Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
michael@0 | 1 | // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
michael@0 | 2 | // Use of this source code is governed by a BSD-style license that can be |
michael@0 | 3 | // found in the LICENSE file. |
michael@0 | 4 | |
michael@0 | 5 | #include "gsub.h" |
michael@0 | 6 | |
michael@0 | 7 | #include <limits> |
michael@0 | 8 | #include <vector> |
michael@0 | 9 | |
michael@0 | 10 | #include "layout.h" |
michael@0 | 11 | #include "maxp.h" |
michael@0 | 12 | |
michael@0 | 13 | // GSUB - The Glyph Substitution Table |
michael@0 | 14 | // http://www.microsoft.com/typography/otspec/gsub.htm |
michael@0 | 15 | |
michael@0 | 16 | #define TABLE_NAME "GSUB" |
michael@0 | 17 | |
michael@0 | 18 | namespace { |
michael@0 | 19 | |
michael@0 | 20 | // The GSUB header size |
michael@0 | 21 | const size_t kGsubHeaderSize = 4 + 3 * 2; |
michael@0 | 22 | |
michael@0 | 23 | enum GSUB_TYPE { |
michael@0 | 24 | GSUB_TYPE_SINGLE = 1, |
michael@0 | 25 | GSUB_TYPE_MULTIPLE = 2, |
michael@0 | 26 | GSUB_TYPE_ALTERNATE = 3, |
michael@0 | 27 | GSUB_TYPE_LIGATURE = 4, |
michael@0 | 28 | GSUB_TYPE_CONTEXT = 5, |
michael@0 | 29 | GSUB_TYPE_CHANGING_CONTEXT = 6, |
michael@0 | 30 | GSUB_TYPE_EXTENSION_SUBSTITUTION = 7, |
michael@0 | 31 | GSUB_TYPE_REVERSE_CHAINING_CONTEXT_SINGLE = 8, |
michael@0 | 32 | GSUB_TYPE_RESERVED = 9 |
michael@0 | 33 | }; |
michael@0 | 34 | |
michael@0 | 35 | // Lookup type parsers. |
michael@0 | 36 | bool ParseSingleSubstitution(const ots::OpenTypeFile *file, |
michael@0 | 37 | const uint8_t *data, const size_t length); |
michael@0 | 38 | bool ParseMutipleSubstitution(const ots::OpenTypeFile *file, |
michael@0 | 39 | const uint8_t *data, const size_t length); |
michael@0 | 40 | bool ParseAlternateSubstitution(const ots::OpenTypeFile *file, |
michael@0 | 41 | const uint8_t *data, const size_t length); |
michael@0 | 42 | bool ParseLigatureSubstitution(const ots::OpenTypeFile *file, |
michael@0 | 43 | const uint8_t *data, const size_t length); |
michael@0 | 44 | bool ParseContextSubstitution(const ots::OpenTypeFile *file, |
michael@0 | 45 | const uint8_t *data, const size_t length); |
michael@0 | 46 | bool ParseChainingContextSubstitution(const ots::OpenTypeFile *file, |
michael@0 | 47 | const uint8_t *data, |
michael@0 | 48 | const size_t length); |
michael@0 | 49 | bool ParseExtensionSubstitution(const ots::OpenTypeFile *file, |
michael@0 | 50 | const uint8_t *data, const size_t length); |
michael@0 | 51 | bool ParseReverseChainingContextSingleSubstitution( |
michael@0 | 52 | const ots::OpenTypeFile *file, const uint8_t *data, const size_t length); |
michael@0 | 53 | |
michael@0 | 54 | const ots::LookupSubtableParser::TypeParser kGsubTypeParsers[] = { |
michael@0 | 55 | {GSUB_TYPE_SINGLE, ParseSingleSubstitution}, |
michael@0 | 56 | {GSUB_TYPE_MULTIPLE, ParseMutipleSubstitution}, |
michael@0 | 57 | {GSUB_TYPE_ALTERNATE, ParseAlternateSubstitution}, |
michael@0 | 58 | {GSUB_TYPE_LIGATURE, ParseLigatureSubstitution}, |
michael@0 | 59 | {GSUB_TYPE_CONTEXT, ParseContextSubstitution}, |
michael@0 | 60 | {GSUB_TYPE_CHANGING_CONTEXT, ParseChainingContextSubstitution}, |
michael@0 | 61 | {GSUB_TYPE_EXTENSION_SUBSTITUTION, ParseExtensionSubstitution}, |
michael@0 | 62 | {GSUB_TYPE_REVERSE_CHAINING_CONTEXT_SINGLE, |
michael@0 | 63 | ParseReverseChainingContextSingleSubstitution} |
michael@0 | 64 | }; |
michael@0 | 65 | |
michael@0 | 66 | const ots::LookupSubtableParser kGsubLookupSubtableParser = { |
michael@0 | 67 | arraysize(kGsubTypeParsers), |
michael@0 | 68 | GSUB_TYPE_EXTENSION_SUBSTITUTION, kGsubTypeParsers |
michael@0 | 69 | }; |
michael@0 | 70 | |
michael@0 | 71 | // Lookup Type 1: |
michael@0 | 72 | // Single Substitution Subtable |
michael@0 | 73 | bool ParseSingleSubstitution(const ots::OpenTypeFile *file, |
michael@0 | 74 | const uint8_t *data, const size_t length) { |
michael@0 | 75 | ots::Buffer subtable(data, length); |
michael@0 | 76 | |
michael@0 | 77 | uint16_t format = 0; |
michael@0 | 78 | uint16_t offset_coverage = 0; |
michael@0 | 79 | |
michael@0 | 80 | if (!subtable.ReadU16(&format) || |
michael@0 | 81 | !subtable.ReadU16(&offset_coverage)) { |
michael@0 | 82 | return OTS_FAILURE_MSG("Failed to read single subst table header"); |
michael@0 | 83 | } |
michael@0 | 84 | |
michael@0 | 85 | const uint16_t num_glyphs = file->maxp->num_glyphs; |
michael@0 | 86 | if (format == 1) { |
michael@0 | 87 | // Parse SingleSubstFormat1 |
michael@0 | 88 | int16_t delta_glyph_id = 0; |
michael@0 | 89 | if (!subtable.ReadS16(&delta_glyph_id)) { |
michael@0 | 90 | return OTS_FAILURE_MSG("Failed to read glyph shift from format 1 single subst table"); |
michael@0 | 91 | } |
michael@0 | 92 | if (std::abs(delta_glyph_id) >= num_glyphs) { |
michael@0 | 93 | return OTS_FAILURE_MSG("bad glyph shift of %d in format 1 single subst table", delta_glyph_id); |
michael@0 | 94 | } |
michael@0 | 95 | } else if (format == 2) { |
michael@0 | 96 | // Parse SingleSubstFormat2 |
michael@0 | 97 | uint16_t glyph_count = 0; |
michael@0 | 98 | if (!subtable.ReadU16(&glyph_count)) { |
michael@0 | 99 | return OTS_FAILURE_MSG("Failed to read glyph cound in format 2 single subst table"); |
michael@0 | 100 | } |
michael@0 | 101 | if (glyph_count > num_glyphs) { |
michael@0 | 102 | return OTS_FAILURE_MSG("Bad glyph count %d > %d in format 2 single subst table", glyph_count, num_glyphs); |
michael@0 | 103 | } |
michael@0 | 104 | for (unsigned i = 0; i < glyph_count; ++i) { |
michael@0 | 105 | uint16_t substitute = 0; |
michael@0 | 106 | if (!subtable.ReadU16(&substitute)) { |
michael@0 | 107 | return OTS_FAILURE_MSG("Failed to read substitution %d in format 2 single subst table", i); |
michael@0 | 108 | } |
michael@0 | 109 | if (substitute >= num_glyphs) { |
michael@0 | 110 | return OTS_FAILURE_MSG("too large substitute: %u", substitute); |
michael@0 | 111 | } |
michael@0 | 112 | } |
michael@0 | 113 | } else { |
michael@0 | 114 | return OTS_FAILURE_MSG("Bad single subst table format %d", format); |
michael@0 | 115 | } |
michael@0 | 116 | |
michael@0 | 117 | if (offset_coverage < subtable.offset() || offset_coverage >= length) { |
michael@0 | 118 | return OTS_FAILURE_MSG("Bad coverage offset %x", offset_coverage); |
michael@0 | 119 | } |
michael@0 | 120 | if (!ots::ParseCoverageTable(file, data + offset_coverage, |
michael@0 | 121 | length - offset_coverage, num_glyphs)) { |
michael@0 | 122 | return OTS_FAILURE_MSG("Failed to parse coverage table"); |
michael@0 | 123 | } |
michael@0 | 124 | |
michael@0 | 125 | return true; |
michael@0 | 126 | } |
michael@0 | 127 | |
michael@0 | 128 | bool ParseSequenceTable(const ots::OpenTypeFile *file, |
michael@0 | 129 | const uint8_t *data, const size_t length, |
michael@0 | 130 | const uint16_t num_glyphs) { |
michael@0 | 131 | ots::Buffer subtable(data, length); |
michael@0 | 132 | |
michael@0 | 133 | uint16_t glyph_count = 0; |
michael@0 | 134 | if (!subtable.ReadU16(&glyph_count)) { |
michael@0 | 135 | return OTS_FAILURE_MSG("Failed to read glyph count in sequence table"); |
michael@0 | 136 | } |
michael@0 | 137 | if (glyph_count > num_glyphs) { |
michael@0 | 138 | return OTS_FAILURE_MSG("bad glyph count %d > %d", glyph_count, num_glyphs); |
michael@0 | 139 | } |
michael@0 | 140 | for (unsigned i = 0; i < glyph_count; ++i) { |
michael@0 | 141 | uint16_t substitute = 0; |
michael@0 | 142 | if (!subtable.ReadU16(&substitute)) { |
michael@0 | 143 | return OTS_FAILURE_MSG("Failedt o read substitution %d in sequence table", i); |
michael@0 | 144 | } |
michael@0 | 145 | if (substitute >= num_glyphs) { |
michael@0 | 146 | return OTS_FAILURE_MSG("Bad subsitution (%d) %d > %d", i, substitute, num_glyphs); |
michael@0 | 147 | } |
michael@0 | 148 | } |
michael@0 | 149 | |
michael@0 | 150 | return true; |
michael@0 | 151 | } |
michael@0 | 152 | |
michael@0 | 153 | // Lookup Type 2: |
michael@0 | 154 | // Multiple Substitution Subtable |
michael@0 | 155 | bool ParseMutipleSubstitution(const ots::OpenTypeFile *file, |
michael@0 | 156 | const uint8_t *data, const size_t length) { |
michael@0 | 157 | ots::Buffer subtable(data, length); |
michael@0 | 158 | |
michael@0 | 159 | uint16_t format = 0; |
michael@0 | 160 | uint16_t offset_coverage = 0; |
michael@0 | 161 | uint16_t sequence_count = 0; |
michael@0 | 162 | |
michael@0 | 163 | if (!subtable.ReadU16(&format) || |
michael@0 | 164 | !subtable.ReadU16(&offset_coverage) || |
michael@0 | 165 | !subtable.ReadU16(&sequence_count)) { |
michael@0 | 166 | return OTS_FAILURE_MSG("Can't read header of multiple subst table"); |
michael@0 | 167 | } |
michael@0 | 168 | |
michael@0 | 169 | if (format != 1) { |
michael@0 | 170 | return OTS_FAILURE_MSG("Bad multiple subst table format %d", format); |
michael@0 | 171 | } |
michael@0 | 172 | |
michael@0 | 173 | const uint16_t num_glyphs = file->maxp->num_glyphs; |
michael@0 | 174 | const unsigned sequence_end = static_cast<unsigned>(6) + |
michael@0 | 175 | sequence_count * 2; |
michael@0 | 176 | if (sequence_end > std::numeric_limits<uint16_t>::max()) { |
michael@0 | 177 | return OTS_FAILURE_MSG("Bad segence end %d, in multiple subst", sequence_end); |
michael@0 | 178 | } |
michael@0 | 179 | for (unsigned i = 0; i < sequence_count; ++i) { |
michael@0 | 180 | uint16_t offset_sequence = 0; |
michael@0 | 181 | if (!subtable.ReadU16(&offset_sequence)) { |
michael@0 | 182 | return OTS_FAILURE_MSG("Failed to read sequence offset for sequence %d", i); |
michael@0 | 183 | } |
michael@0 | 184 | if (offset_sequence < sequence_end || offset_sequence >= length) { |
michael@0 | 185 | return OTS_FAILURE_MSG("Bad sequence offset %d for sequence %d", offset_sequence, i); |
michael@0 | 186 | } |
michael@0 | 187 | if (!ParseSequenceTable(file, data + offset_sequence, length - offset_sequence, |
michael@0 | 188 | num_glyphs)) { |
michael@0 | 189 | return OTS_FAILURE_MSG("Failed to parse sequence table %d", i); |
michael@0 | 190 | } |
michael@0 | 191 | } |
michael@0 | 192 | |
michael@0 | 193 | if (offset_coverage < sequence_end || offset_coverage >= length) { |
michael@0 | 194 | return OTS_FAILURE_MSG("Bad coverage offset %d", offset_coverage); |
michael@0 | 195 | } |
michael@0 | 196 | if (!ots::ParseCoverageTable(file, data + offset_coverage, |
michael@0 | 197 | length - offset_coverage, num_glyphs)) { |
michael@0 | 198 | return OTS_FAILURE_MSG("Failed to parse coverage table"); |
michael@0 | 199 | } |
michael@0 | 200 | |
michael@0 | 201 | return true; |
michael@0 | 202 | } |
michael@0 | 203 | |
michael@0 | 204 | bool ParseAlternateSetTable(const ots::OpenTypeFile *file, |
michael@0 | 205 | const uint8_t *data, const size_t length, |
michael@0 | 206 | const uint16_t num_glyphs) { |
michael@0 | 207 | ots::Buffer subtable(data, length); |
michael@0 | 208 | |
michael@0 | 209 | uint16_t glyph_count = 0; |
michael@0 | 210 | if (!subtable.ReadU16(&glyph_count)) { |
michael@0 | 211 | return OTS_FAILURE_MSG("Failed to read alternate set header"); |
michael@0 | 212 | } |
michael@0 | 213 | if (glyph_count > num_glyphs) { |
michael@0 | 214 | return OTS_FAILURE_MSG("Bad glyph count %d > %d in alternate set table", glyph_count, num_glyphs); |
michael@0 | 215 | } |
michael@0 | 216 | for (unsigned i = 0; i < glyph_count; ++i) { |
michael@0 | 217 | uint16_t alternate = 0; |
michael@0 | 218 | if (!subtable.ReadU16(&alternate)) { |
michael@0 | 219 | return OTS_FAILURE_MSG("Can't read alternate %d", i); |
michael@0 | 220 | } |
michael@0 | 221 | if (alternate >= num_glyphs) { |
michael@0 | 222 | return OTS_FAILURE_MSG("Too large alternate: %u", alternate); |
michael@0 | 223 | } |
michael@0 | 224 | } |
michael@0 | 225 | return true; |
michael@0 | 226 | } |
michael@0 | 227 | |
michael@0 | 228 | // Lookup Type 3: |
michael@0 | 229 | // Alternate Substitution Subtable |
michael@0 | 230 | bool ParseAlternateSubstitution(const ots::OpenTypeFile *file, |
michael@0 | 231 | const uint8_t *data, const size_t length) { |
michael@0 | 232 | ots::Buffer subtable(data, length); |
michael@0 | 233 | |
michael@0 | 234 | uint16_t format = 0; |
michael@0 | 235 | uint16_t offset_coverage = 0; |
michael@0 | 236 | uint16_t alternate_set_count = 0; |
michael@0 | 237 | |
michael@0 | 238 | if (!subtable.ReadU16(&format) || |
michael@0 | 239 | !subtable.ReadU16(&offset_coverage) || |
michael@0 | 240 | !subtable.ReadU16(&alternate_set_count)) { |
michael@0 | 241 | return OTS_FAILURE_MSG("Can't read alternate subst header"); |
michael@0 | 242 | } |
michael@0 | 243 | |
michael@0 | 244 | if (format != 1) { |
michael@0 | 245 | return OTS_FAILURE_MSG("Bad alternate subst table format %d", format); |
michael@0 | 246 | } |
michael@0 | 247 | |
michael@0 | 248 | const uint16_t num_glyphs = file->maxp->num_glyphs; |
michael@0 | 249 | const unsigned alternate_set_end = static_cast<unsigned>(6) + |
michael@0 | 250 | alternate_set_count * 2; |
michael@0 | 251 | if (alternate_set_end > std::numeric_limits<uint16_t>::max()) { |
michael@0 | 252 | return OTS_FAILURE_MSG("Bad end of alternate set %d", alternate_set_end); |
michael@0 | 253 | } |
michael@0 | 254 | for (unsigned i = 0; i < alternate_set_count; ++i) { |
michael@0 | 255 | uint16_t offset_alternate_set = 0; |
michael@0 | 256 | if (!subtable.ReadU16(&offset_alternate_set)) { |
michael@0 | 257 | return OTS_FAILURE_MSG("Can't read alternate set offset for set %d", i); |
michael@0 | 258 | } |
michael@0 | 259 | if (offset_alternate_set < alternate_set_end || |
michael@0 | 260 | offset_alternate_set >= length) { |
michael@0 | 261 | return OTS_FAILURE_MSG("Bad alternate set offset %d for set %d", offset_alternate_set, i); |
michael@0 | 262 | } |
michael@0 | 263 | if (!ParseAlternateSetTable(file, data + offset_alternate_set, |
michael@0 | 264 | length - offset_alternate_set, |
michael@0 | 265 | num_glyphs)) { |
michael@0 | 266 | return OTS_FAILURE_MSG("Failed to parse alternate set"); |
michael@0 | 267 | } |
michael@0 | 268 | } |
michael@0 | 269 | |
michael@0 | 270 | if (offset_coverage < alternate_set_end || offset_coverage >= length) { |
michael@0 | 271 | return OTS_FAILURE_MSG("Bad coverage offset %d", offset_coverage); |
michael@0 | 272 | } |
michael@0 | 273 | if (!ots::ParseCoverageTable(file, data + offset_coverage, |
michael@0 | 274 | length - offset_coverage, num_glyphs)) { |
michael@0 | 275 | return OTS_FAILURE_MSG("Failed to parse coverage table"); |
michael@0 | 276 | } |
michael@0 | 277 | |
michael@0 | 278 | return true; |
michael@0 | 279 | } |
michael@0 | 280 | |
michael@0 | 281 | bool ParseLigatureTable(const ots::OpenTypeFile *file, |
michael@0 | 282 | const uint8_t *data, const size_t length, |
michael@0 | 283 | const uint16_t num_glyphs) { |
michael@0 | 284 | ots::Buffer subtable(data, length); |
michael@0 | 285 | |
michael@0 | 286 | uint16_t lig_glyph = 0; |
michael@0 | 287 | uint16_t comp_count = 0; |
michael@0 | 288 | |
michael@0 | 289 | if (!subtable.ReadU16(&lig_glyph) || |
michael@0 | 290 | !subtable.ReadU16(&comp_count)) { |
michael@0 | 291 | return OTS_FAILURE_MSG("Failed to read ligatuer table header"); |
michael@0 | 292 | } |
michael@0 | 293 | |
michael@0 | 294 | if (lig_glyph >= num_glyphs) { |
michael@0 | 295 | return OTS_FAILURE_MSG("too large lig_glyph: %u", lig_glyph); |
michael@0 | 296 | } |
michael@0 | 297 | if (comp_count == 0 || comp_count > num_glyphs) { |
michael@0 | 298 | return OTS_FAILURE_MSG("Bad component count of %d", comp_count); |
michael@0 | 299 | } |
michael@0 | 300 | for (unsigned i = 0; i < comp_count - static_cast<unsigned>(1); ++i) { |
michael@0 | 301 | uint16_t component = 0; |
michael@0 | 302 | if (!subtable.ReadU16(&component)) { |
michael@0 | 303 | return OTS_FAILURE_MSG("Can't read ligature component %d", i); |
michael@0 | 304 | } |
michael@0 | 305 | if (component >= num_glyphs) { |
michael@0 | 306 | return OTS_FAILURE_MSG("Bad ligature component %d of %d", i, component); |
michael@0 | 307 | } |
michael@0 | 308 | } |
michael@0 | 309 | |
michael@0 | 310 | return true; |
michael@0 | 311 | } |
michael@0 | 312 | |
michael@0 | 313 | bool ParseLigatureSetTable(const ots::OpenTypeFile *file, |
michael@0 | 314 | const uint8_t *data, const size_t length, |
michael@0 | 315 | const uint16_t num_glyphs) { |
michael@0 | 316 | ots::Buffer subtable(data, length); |
michael@0 | 317 | |
michael@0 | 318 | uint16_t ligature_count = 0; |
michael@0 | 319 | |
michael@0 | 320 | if (!subtable.ReadU16(&ligature_count)) { |
michael@0 | 321 | return OTS_FAILURE_MSG("Can't read ligature count in ligature set"); |
michael@0 | 322 | } |
michael@0 | 323 | |
michael@0 | 324 | const unsigned ligature_end = static_cast<unsigned>(2) + ligature_count * 2; |
michael@0 | 325 | if (ligature_end > std::numeric_limits<uint16_t>::max()) { |
michael@0 | 326 | return OTS_FAILURE_MSG("Bad end of ligature %d in ligature set", ligature_end); |
michael@0 | 327 | } |
michael@0 | 328 | for (unsigned i = 0; i < ligature_count; ++i) { |
michael@0 | 329 | uint16_t offset_ligature = 0; |
michael@0 | 330 | if (!subtable.ReadU16(&offset_ligature)) { |
michael@0 | 331 | return OTS_FAILURE_MSG("Failed to read ligature offset %d", i); |
michael@0 | 332 | } |
michael@0 | 333 | if (offset_ligature < ligature_end || offset_ligature >= length) { |
michael@0 | 334 | return OTS_FAILURE_MSG("Bad ligature offset %d for ligature %d", offset_ligature, i); |
michael@0 | 335 | } |
michael@0 | 336 | if (!ParseLigatureTable(file, data + offset_ligature, length - offset_ligature, |
michael@0 | 337 | num_glyphs)) { |
michael@0 | 338 | return OTS_FAILURE_MSG("Failed to parse ligature %d", i); |
michael@0 | 339 | } |
michael@0 | 340 | } |
michael@0 | 341 | |
michael@0 | 342 | return true; |
michael@0 | 343 | } |
michael@0 | 344 | |
michael@0 | 345 | // Lookup Type 4: |
michael@0 | 346 | // Ligature Substitution Subtable |
michael@0 | 347 | bool ParseLigatureSubstitution(const ots::OpenTypeFile *file, |
michael@0 | 348 | const uint8_t *data, const size_t length) { |
michael@0 | 349 | ots::Buffer subtable(data, length); |
michael@0 | 350 | |
michael@0 | 351 | uint16_t format = 0; |
michael@0 | 352 | uint16_t offset_coverage = 0; |
michael@0 | 353 | uint16_t lig_set_count = 0; |
michael@0 | 354 | |
michael@0 | 355 | if (!subtable.ReadU16(&format) || |
michael@0 | 356 | !subtable.ReadU16(&offset_coverage) || |
michael@0 | 357 | !subtable.ReadU16(&lig_set_count)) { |
michael@0 | 358 | return OTS_FAILURE_MSG("Failed to read ligature substitution header"); |
michael@0 | 359 | } |
michael@0 | 360 | |
michael@0 | 361 | if (format != 1) { |
michael@0 | 362 | return OTS_FAILURE_MSG("Bad ligature substitution table format %d", format); |
michael@0 | 363 | } |
michael@0 | 364 | |
michael@0 | 365 | const uint16_t num_glyphs = file->maxp->num_glyphs; |
michael@0 | 366 | const unsigned ligature_set_end = static_cast<unsigned>(6) + |
michael@0 | 367 | lig_set_count * 2; |
michael@0 | 368 | if (ligature_set_end > std::numeric_limits<uint16_t>::max()) { |
michael@0 | 369 | return OTS_FAILURE_MSG("Bad end of ligature set %d in ligature substitution table", ligature_set_end); |
michael@0 | 370 | } |
michael@0 | 371 | for (unsigned i = 0; i < lig_set_count; ++i) { |
michael@0 | 372 | uint16_t offset_ligature_set = 0; |
michael@0 | 373 | if (!subtable.ReadU16(&offset_ligature_set)) { |
michael@0 | 374 | return OTS_FAILURE_MSG("Can't read ligature set offset %d", i); |
michael@0 | 375 | } |
michael@0 | 376 | if (offset_ligature_set < ligature_set_end || |
michael@0 | 377 | offset_ligature_set >= length) { |
michael@0 | 378 | return OTS_FAILURE_MSG("Bad ligature set offset %d for set %d", offset_ligature_set, i); |
michael@0 | 379 | } |
michael@0 | 380 | if (!ParseLigatureSetTable(file, data + offset_ligature_set, |
michael@0 | 381 | length - offset_ligature_set, num_glyphs)) { |
michael@0 | 382 | return OTS_FAILURE_MSG("Failed to parse ligature set %d", i); |
michael@0 | 383 | } |
michael@0 | 384 | } |
michael@0 | 385 | |
michael@0 | 386 | if (offset_coverage < ligature_set_end || offset_coverage >= length) { |
michael@0 | 387 | return OTS_FAILURE_MSG("Bad coverage offset %d", offset_coverage); |
michael@0 | 388 | } |
michael@0 | 389 | if (!ots::ParseCoverageTable(file, data + offset_coverage, |
michael@0 | 390 | length - offset_coverage, num_glyphs)) { |
michael@0 | 391 | return OTS_FAILURE_MSG("Failed to parse coverage table"); |
michael@0 | 392 | } |
michael@0 | 393 | |
michael@0 | 394 | return true; |
michael@0 | 395 | } |
michael@0 | 396 | |
michael@0 | 397 | // Lookup Type 5: |
michael@0 | 398 | // Contextual Substitution Subtable |
michael@0 | 399 | bool ParseContextSubstitution(const ots::OpenTypeFile *file, |
michael@0 | 400 | const uint8_t *data, const size_t length) { |
michael@0 | 401 | return ots::ParseContextSubtable(file, data, length, file->maxp->num_glyphs, |
michael@0 | 402 | file->gsub->num_lookups); |
michael@0 | 403 | } |
michael@0 | 404 | |
michael@0 | 405 | // Lookup Type 6: |
michael@0 | 406 | // Chaining Contextual Substitution Subtable |
michael@0 | 407 | bool ParseChainingContextSubstitution(const ots::OpenTypeFile *file, |
michael@0 | 408 | const uint8_t *data, |
michael@0 | 409 | const size_t length) { |
michael@0 | 410 | return ots::ParseChainingContextSubtable(file, data, length, |
michael@0 | 411 | file->maxp->num_glyphs, |
michael@0 | 412 | file->gsub->num_lookups); |
michael@0 | 413 | } |
michael@0 | 414 | |
michael@0 | 415 | // Lookup Type 7: |
michael@0 | 416 | // Extension Substition |
michael@0 | 417 | bool ParseExtensionSubstitution(const ots::OpenTypeFile *file, |
michael@0 | 418 | const uint8_t *data, const size_t length) { |
michael@0 | 419 | return ots::ParseExtensionSubtable(file, data, length, |
michael@0 | 420 | &kGsubLookupSubtableParser); |
michael@0 | 421 | } |
michael@0 | 422 | |
michael@0 | 423 | // Lookup Type 8: |
michael@0 | 424 | // Reverse Chaining Contexual Single Substitution Subtable |
michael@0 | 425 | bool ParseReverseChainingContextSingleSubstitution( |
michael@0 | 426 | const ots::OpenTypeFile *file, const uint8_t *data, const size_t length) { |
michael@0 | 427 | ots::Buffer subtable(data, length); |
michael@0 | 428 | |
michael@0 | 429 | uint16_t format = 0; |
michael@0 | 430 | uint16_t offset_coverage = 0; |
michael@0 | 431 | |
michael@0 | 432 | if (!subtable.ReadU16(&format) || |
michael@0 | 433 | !subtable.ReadU16(&offset_coverage)) { |
michael@0 | 434 | return OTS_FAILURE_MSG("Failed to read reverse chaining header"); |
michael@0 | 435 | } |
michael@0 | 436 | |
michael@0 | 437 | const uint16_t num_glyphs = file->maxp->num_glyphs; |
michael@0 | 438 | |
michael@0 | 439 | uint16_t backtrack_glyph_count = 0; |
michael@0 | 440 | if (!subtable.ReadU16(&backtrack_glyph_count)) { |
michael@0 | 441 | return OTS_FAILURE_MSG("Failed to read backtrack glyph count in reverse chaining table"); |
michael@0 | 442 | } |
michael@0 | 443 | if (backtrack_glyph_count > num_glyphs) { |
michael@0 | 444 | return OTS_FAILURE_MSG("Bad backtrack glyph count of %d", backtrack_glyph_count); |
michael@0 | 445 | } |
michael@0 | 446 | std::vector<uint16_t> offsets_backtrack; |
michael@0 | 447 | offsets_backtrack.reserve(backtrack_glyph_count); |
michael@0 | 448 | for (unsigned i = 0; i < backtrack_glyph_count; ++i) { |
michael@0 | 449 | uint16_t offset = 0; |
michael@0 | 450 | if (!subtable.ReadU16(&offset)) { |
michael@0 | 451 | return OTS_FAILURE_MSG("Failed to read backtrack offset %d", i); |
michael@0 | 452 | } |
michael@0 | 453 | offsets_backtrack.push_back(offset); |
michael@0 | 454 | } |
michael@0 | 455 | |
michael@0 | 456 | uint16_t lookahead_glyph_count = 0; |
michael@0 | 457 | if (!subtable.ReadU16(&lookahead_glyph_count)) { |
michael@0 | 458 | return OTS_FAILURE_MSG("Failed to read look ahead glyph count"); |
michael@0 | 459 | } |
michael@0 | 460 | if (lookahead_glyph_count > num_glyphs) { |
michael@0 | 461 | return OTS_FAILURE_MSG("Bad look ahead glyph count %d", lookahead_glyph_count); |
michael@0 | 462 | } |
michael@0 | 463 | std::vector<uint16_t> offsets_lookahead; |
michael@0 | 464 | offsets_lookahead.reserve(lookahead_glyph_count); |
michael@0 | 465 | for (unsigned i = 0; i < lookahead_glyph_count; ++i) { |
michael@0 | 466 | uint16_t offset = 0; |
michael@0 | 467 | if (!subtable.ReadU16(&offset)) { |
michael@0 | 468 | return OTS_FAILURE_MSG("Can't read look ahead offset %d", i); |
michael@0 | 469 | } |
michael@0 | 470 | offsets_lookahead.push_back(offset); |
michael@0 | 471 | } |
michael@0 | 472 | |
michael@0 | 473 | uint16_t glyph_count = 0; |
michael@0 | 474 | if (!subtable.ReadU16(&glyph_count)) { |
michael@0 | 475 | return OTS_FAILURE_MSG("Can't read glyph count in reverse chaining table"); |
michael@0 | 476 | } |
michael@0 | 477 | if (glyph_count > num_glyphs) { |
michael@0 | 478 | return OTS_FAILURE_MSG("Bad glyph count of %d", glyph_count); |
michael@0 | 479 | } |
michael@0 | 480 | for (unsigned i = 0; i < glyph_count; ++i) { |
michael@0 | 481 | uint16_t substitute = 0; |
michael@0 | 482 | if (!subtable.ReadU16(&substitute)) { |
michael@0 | 483 | return OTS_FAILURE_MSG("Failed to read substitution %d reverse chaining table", i); |
michael@0 | 484 | } |
michael@0 | 485 | if (substitute >= num_glyphs) { |
michael@0 | 486 | return OTS_FAILURE_MSG("Bad substitute glyph %d in reverse chaining table substitution %d", substitute, i); |
michael@0 | 487 | } |
michael@0 | 488 | } |
michael@0 | 489 | |
michael@0 | 490 | const unsigned substitute_end = static_cast<unsigned>(10) + |
michael@0 | 491 | (backtrack_glyph_count + lookahead_glyph_count + glyph_count) * 2; |
michael@0 | 492 | if (substitute_end > std::numeric_limits<uint16_t>::max()) { |
michael@0 | 493 | return OTS_FAILURE_MSG("Bad substitute end offset in reverse chaining table"); |
michael@0 | 494 | } |
michael@0 | 495 | |
michael@0 | 496 | if (offset_coverage < substitute_end || offset_coverage >= length) { |
michael@0 | 497 | return OTS_FAILURE_MSG("Bad coverage offset %d in reverse chaining table", offset_coverage); |
michael@0 | 498 | } |
michael@0 | 499 | if (!ots::ParseCoverageTable(file, data + offset_coverage, |
michael@0 | 500 | length - offset_coverage, num_glyphs)) { |
michael@0 | 501 | return OTS_FAILURE_MSG("Failed to parse coverage table in reverse chaining table"); |
michael@0 | 502 | } |
michael@0 | 503 | |
michael@0 | 504 | for (unsigned i = 0; i < backtrack_glyph_count; ++i) { |
michael@0 | 505 | if (offsets_backtrack[i] < substitute_end || |
michael@0 | 506 | offsets_backtrack[i] >= length) { |
michael@0 | 507 | return OTS_FAILURE_MSG("Bad backtrack offset %d for backtrack %d in reverse chaining table", offsets_backtrack[i], i); |
michael@0 | 508 | } |
michael@0 | 509 | if (!ots::ParseCoverageTable(file, data + offsets_backtrack[i], |
michael@0 | 510 | length - offsets_backtrack[i], num_glyphs)) { |
michael@0 | 511 | return OTS_FAILURE_MSG("Failed to parse coverage table for backtrack %d in reverse chaining table", i); |
michael@0 | 512 | } |
michael@0 | 513 | } |
michael@0 | 514 | |
michael@0 | 515 | for (unsigned i = 0; i < lookahead_glyph_count; ++i) { |
michael@0 | 516 | if (offsets_lookahead[i] < substitute_end || |
michael@0 | 517 | offsets_lookahead[i] >= length) { |
michael@0 | 518 | return OTS_FAILURE_MSG("Bad lookahead offset %d for lookahead %d in reverse chaining table", offsets_lookahead[i], i); |
michael@0 | 519 | } |
michael@0 | 520 | if (!ots::ParseCoverageTable(file, data + offsets_lookahead[i], |
michael@0 | 521 | length - offsets_lookahead[i], num_glyphs)) { |
michael@0 | 522 | return OTS_FAILURE_MSG("Failed to parse lookahead coverage table %d in reverse chaining table", i); |
michael@0 | 523 | } |
michael@0 | 524 | } |
michael@0 | 525 | |
michael@0 | 526 | return true; |
michael@0 | 527 | } |
michael@0 | 528 | |
michael@0 | 529 | } // namespace |
michael@0 | 530 | |
michael@0 | 531 | #define DROP_THIS_TABLE(msg_) \ |
michael@0 | 532 | do { \ |
michael@0 | 533 | file->gsub->data = 0; \ |
michael@0 | 534 | file->gsub->length = 0; \ |
michael@0 | 535 | OTS_FAILURE_MSG(msg_ ", table discarded"); \ |
michael@0 | 536 | } while (0) |
michael@0 | 537 | |
michael@0 | 538 | namespace ots { |
michael@0 | 539 | |
michael@0 | 540 | // As far as I checked, following fonts contain invalid values in GSUB table. |
michael@0 | 541 | // OTS will drop their GSUB table. |
michael@0 | 542 | // |
michael@0 | 543 | // # too large substitute (value is 0xFFFF) |
michael@0 | 544 | // kaiu.ttf |
michael@0 | 545 | // mingliub2.ttf |
michael@0 | 546 | // mingliub1.ttf |
michael@0 | 547 | // mingliub0.ttf |
michael@0 | 548 | // GraublauWeb.otf |
michael@0 | 549 | // GraublauWebBold.otf |
michael@0 | 550 | // |
michael@0 | 551 | // # too large alternate (value is 0xFFFF) |
michael@0 | 552 | // ManchuFont.ttf |
michael@0 | 553 | // |
michael@0 | 554 | // # bad offset to lang sys table (NULL offset) |
michael@0 | 555 | // DejaVuMonoSansBold.ttf |
michael@0 | 556 | // DejaVuMonoSansBoldOblique.ttf |
michael@0 | 557 | // DejaVuMonoSansOblique.ttf |
michael@0 | 558 | // DejaVuSansMono-BoldOblique.ttf |
michael@0 | 559 | // DejaVuSansMono-Oblique.ttf |
michael@0 | 560 | // DejaVuSansMono-Bold.ttf |
michael@0 | 561 | // |
michael@0 | 562 | // # bad start coverage index |
michael@0 | 563 | // GenBasBI.ttf |
michael@0 | 564 | // GenBasI.ttf |
michael@0 | 565 | // AndBasR.ttf |
michael@0 | 566 | // GenBkBasI.ttf |
michael@0 | 567 | // CharisSILR.ttf |
michael@0 | 568 | // CharisSILBI.ttf |
michael@0 | 569 | // CharisSILI.ttf |
michael@0 | 570 | // CharisSILB.ttf |
michael@0 | 571 | // DoulosSILR.ttf |
michael@0 | 572 | // CharisSILBI.ttf |
michael@0 | 573 | // GenBkBasB.ttf |
michael@0 | 574 | // GenBkBasR.ttf |
michael@0 | 575 | // GenBkBasBI.ttf |
michael@0 | 576 | // GenBasB.ttf |
michael@0 | 577 | // GenBasR.ttf |
michael@0 | 578 | // |
michael@0 | 579 | // # glyph range is overlapping |
michael@0 | 580 | // KacstTitleL.ttf |
michael@0 | 581 | // KacstDecorative.ttf |
michael@0 | 582 | // KacstTitle.ttf |
michael@0 | 583 | // KacstArt.ttf |
michael@0 | 584 | // KacstPoster.ttf |
michael@0 | 585 | // KacstQurn.ttf |
michael@0 | 586 | // KacstDigital.ttf |
michael@0 | 587 | // KacstBook.ttf |
michael@0 | 588 | // KacstFarsi.ttf |
michael@0 | 589 | |
michael@0 | 590 | bool ots_gsub_parse(OpenTypeFile *file, const uint8_t *data, size_t length) { |
michael@0 | 591 | // Parsing gsub table requires |file->maxp->num_glyphs| |
michael@0 | 592 | if (!file->maxp) { |
michael@0 | 593 | return OTS_FAILURE_MSG("Missing maxp table in font, needed by GSUB"); |
michael@0 | 594 | } |
michael@0 | 595 | |
michael@0 | 596 | Buffer table(data, length); |
michael@0 | 597 | |
michael@0 | 598 | OpenTypeGSUB *gsub = new OpenTypeGSUB; |
michael@0 | 599 | file->gsub = gsub; |
michael@0 | 600 | |
michael@0 | 601 | uint32_t version = 0; |
michael@0 | 602 | uint16_t offset_script_list = 0; |
michael@0 | 603 | uint16_t offset_feature_list = 0; |
michael@0 | 604 | uint16_t offset_lookup_list = 0; |
michael@0 | 605 | if (!table.ReadU32(&version) || |
michael@0 | 606 | !table.ReadU16(&offset_script_list) || |
michael@0 | 607 | !table.ReadU16(&offset_feature_list) || |
michael@0 | 608 | !table.ReadU16(&offset_lookup_list)) { |
michael@0 | 609 | DROP_THIS_TABLE("Incomplete table"); |
michael@0 | 610 | return true; |
michael@0 | 611 | } |
michael@0 | 612 | |
michael@0 | 613 | if (version != 0x00010000) { |
michael@0 | 614 | DROP_THIS_TABLE("Bad version"); |
michael@0 | 615 | return true; |
michael@0 | 616 | } |
michael@0 | 617 | if ((offset_script_list < kGsubHeaderSize || |
michael@0 | 618 | offset_script_list >= length) || |
michael@0 | 619 | (offset_feature_list < kGsubHeaderSize || |
michael@0 | 620 | offset_feature_list >= length) || |
michael@0 | 621 | (offset_lookup_list < kGsubHeaderSize || |
michael@0 | 622 | offset_lookup_list >= length)) { |
michael@0 | 623 | DROP_THIS_TABLE("Bad offset in table header"); |
michael@0 | 624 | return true; |
michael@0 | 625 | } |
michael@0 | 626 | |
michael@0 | 627 | if (!ParseLookupListTable(file, data + offset_lookup_list, |
michael@0 | 628 | length - offset_lookup_list, |
michael@0 | 629 | &kGsubLookupSubtableParser, |
michael@0 | 630 | &gsub->num_lookups)) { |
michael@0 | 631 | DROP_THIS_TABLE("Failed to parse lookup list table"); |
michael@0 | 632 | return true; |
michael@0 | 633 | } |
michael@0 | 634 | |
michael@0 | 635 | uint16_t num_features = 0; |
michael@0 | 636 | if (!ParseFeatureListTable(file, data + offset_feature_list, |
michael@0 | 637 | length - offset_feature_list, gsub->num_lookups, |
michael@0 | 638 | &num_features)) { |
michael@0 | 639 | DROP_THIS_TABLE("Failed to parse feature list table"); |
michael@0 | 640 | return true; |
michael@0 | 641 | } |
michael@0 | 642 | |
michael@0 | 643 | if (!ParseScriptListTable(file, data + offset_script_list, |
michael@0 | 644 | length - offset_script_list, num_features)) { |
michael@0 | 645 | DROP_THIS_TABLE("Failed to parse script list table"); |
michael@0 | 646 | return true; |
michael@0 | 647 | } |
michael@0 | 648 | |
michael@0 | 649 | gsub->data = data; |
michael@0 | 650 | gsub->length = length; |
michael@0 | 651 | return true; |
michael@0 | 652 | } |
michael@0 | 653 | |
michael@0 | 654 | bool ots_gsub_should_serialise(OpenTypeFile *file) { |
michael@0 | 655 | return file->gsub != NULL && file->gsub->data != NULL; |
michael@0 | 656 | } |
michael@0 | 657 | |
michael@0 | 658 | bool ots_gsub_serialise(OTSStream *out, OpenTypeFile *file) { |
michael@0 | 659 | if (!out->Write(file->gsub->data, file->gsub->length)) { |
michael@0 | 660 | return OTS_FAILURE_MSG("Failed to write GSUB table"); |
michael@0 | 661 | } |
michael@0 | 662 | |
michael@0 | 663 | return true; |
michael@0 | 664 | } |
michael@0 | 665 | |
michael@0 | 666 | void ots_gsub_free(OpenTypeFile *file) { |
michael@0 | 667 | delete file->gsub; |
michael@0 | 668 | } |
michael@0 | 669 | |
michael@0 | 670 | } // namespace ots |
michael@0 | 671 |