Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / third_party / ots / src / gsub.cc
blobaf31144635f6ae9596881833097b17b9d44f99fe
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "gsub.h"
7 #include <limits>
8 #include <vector>
10 #include "layout.h"
11 #include "maxp.h"
13 // GSUB - The Glyph Substitution Table
14 // http://www.microsoft.com/typography/otspec/gsub.htm
16 #define TABLE_NAME "GSUB"
18 namespace {
20 // The GSUB header size
21 const size_t kGsubHeaderSize = 4 + 3 * 2;
23 enum GSUB_TYPE {
24 GSUB_TYPE_SINGLE = 1,
25 GSUB_TYPE_MULTIPLE = 2,
26 GSUB_TYPE_ALTERNATE = 3,
27 GSUB_TYPE_LIGATURE = 4,
28 GSUB_TYPE_CONTEXT = 5,
29 GSUB_TYPE_CHANGING_CONTEXT = 6,
30 GSUB_TYPE_EXTENSION_SUBSTITUTION = 7,
31 GSUB_TYPE_REVERSE_CHAINING_CONTEXT_SINGLE = 8,
32 GSUB_TYPE_RESERVED = 9
35 // Lookup type parsers.
36 bool ParseSingleSubstitution(const ots::OpenTypeFile *file,
37 const uint8_t *data, const size_t length);
38 bool ParseMutipleSubstitution(const ots::OpenTypeFile *file,
39 const uint8_t *data, const size_t length);
40 bool ParseAlternateSubstitution(const ots::OpenTypeFile *file,
41 const uint8_t *data, const size_t length);
42 bool ParseLigatureSubstitution(const ots::OpenTypeFile *file,
43 const uint8_t *data, const size_t length);
44 bool ParseContextSubstitution(const ots::OpenTypeFile *file,
45 const uint8_t *data, const size_t length);
46 bool ParseChainingContextSubstitution(const ots::OpenTypeFile *file,
47 const uint8_t *data,
48 const size_t length);
49 bool ParseExtensionSubstitution(const ots::OpenTypeFile *file,
50 const uint8_t *data, const size_t length);
51 bool ParseReverseChainingContextSingleSubstitution(
52 const ots::OpenTypeFile *file, const uint8_t *data, const size_t length);
54 const ots::LookupSubtableParser::TypeParser kGsubTypeParsers[] = {
55 {GSUB_TYPE_SINGLE, ParseSingleSubstitution},
56 {GSUB_TYPE_MULTIPLE, ParseMutipleSubstitution},
57 {GSUB_TYPE_ALTERNATE, ParseAlternateSubstitution},
58 {GSUB_TYPE_LIGATURE, ParseLigatureSubstitution},
59 {GSUB_TYPE_CONTEXT, ParseContextSubstitution},
60 {GSUB_TYPE_CHANGING_CONTEXT, ParseChainingContextSubstitution},
61 {GSUB_TYPE_EXTENSION_SUBSTITUTION, ParseExtensionSubstitution},
62 {GSUB_TYPE_REVERSE_CHAINING_CONTEXT_SINGLE,
63 ParseReverseChainingContextSingleSubstitution}
66 const ots::LookupSubtableParser kGsubLookupSubtableParser = {
67 arraysize(kGsubTypeParsers),
68 GSUB_TYPE_EXTENSION_SUBSTITUTION, kGsubTypeParsers
71 // Lookup Type 1:
72 // Single Substitution Subtable
73 bool ParseSingleSubstitution(const ots::OpenTypeFile *file,
74 const uint8_t *data, const size_t length) {
75 ots::Buffer subtable(data, length);
77 uint16_t format = 0;
78 uint16_t offset_coverage = 0;
80 if (!subtable.ReadU16(&format) ||
81 !subtable.ReadU16(&offset_coverage)) {
82 return OTS_FAILURE_MSG("Failed to read single subst table header");
85 const uint16_t num_glyphs = file->maxp->num_glyphs;
86 if (format == 1) {
87 // Parse SingleSubstFormat1
88 int16_t delta_glyph_id = 0;
89 if (!subtable.ReadS16(&delta_glyph_id)) {
90 return OTS_FAILURE_MSG("Failed to read glyph shift from format 1 single subst table");
92 if (std::abs(delta_glyph_id) >= num_glyphs) {
93 return OTS_FAILURE_MSG("bad glyph shift of %d in format 1 single subst table", delta_glyph_id);
95 } else if (format == 2) {
96 // Parse SingleSubstFormat2
97 uint16_t glyph_count = 0;
98 if (!subtable.ReadU16(&glyph_count)) {
99 return OTS_FAILURE_MSG("Failed to read glyph cound in format 2 single subst table");
101 if (glyph_count > num_glyphs) {
102 return OTS_FAILURE_MSG("Bad glyph count %d > %d in format 2 single subst table", glyph_count, num_glyphs);
104 for (unsigned i = 0; i < glyph_count; ++i) {
105 uint16_t substitute = 0;
106 if (!subtable.ReadU16(&substitute)) {
107 return OTS_FAILURE_MSG("Failed to read substitution %d in format 2 single subst table", i);
109 if (substitute >= num_glyphs) {
110 return OTS_FAILURE_MSG("too large substitute: %u", substitute);
113 } else {
114 return OTS_FAILURE_MSG("Bad single subst table format %d", format);
117 if (offset_coverage < subtable.offset() || offset_coverage >= length) {
118 return OTS_FAILURE_MSG("Bad coverage offset %x", offset_coverage);
120 if (!ots::ParseCoverageTable(file, data + offset_coverage,
121 length - offset_coverage, num_glyphs)) {
122 return OTS_FAILURE_MSG("Failed to parse coverage table");
125 return true;
128 bool ParseSequenceTable(const ots::OpenTypeFile *file,
129 const uint8_t *data, const size_t length,
130 const uint16_t num_glyphs) {
131 ots::Buffer subtable(data, length);
133 uint16_t glyph_count = 0;
134 if (!subtable.ReadU16(&glyph_count)) {
135 return OTS_FAILURE_MSG("Failed to read glyph count in sequence table");
137 if (glyph_count > num_glyphs) {
138 return OTS_FAILURE_MSG("bad glyph count %d > %d", glyph_count, num_glyphs);
140 for (unsigned i = 0; i < glyph_count; ++i) {
141 uint16_t substitute = 0;
142 if (!subtable.ReadU16(&substitute)) {
143 return OTS_FAILURE_MSG("Failedt o read substitution %d in sequence table", i);
145 if (substitute >= num_glyphs) {
146 return OTS_FAILURE_MSG("Bad subsitution (%d) %d > %d", i, substitute, num_glyphs);
150 return true;
153 // Lookup Type 2:
154 // Multiple Substitution Subtable
155 bool ParseMutipleSubstitution(const ots::OpenTypeFile *file,
156 const uint8_t *data, const size_t length) {
157 ots::Buffer subtable(data, length);
159 uint16_t format = 0;
160 uint16_t offset_coverage = 0;
161 uint16_t sequence_count = 0;
163 if (!subtable.ReadU16(&format) ||
164 !subtable.ReadU16(&offset_coverage) ||
165 !subtable.ReadU16(&sequence_count)) {
166 return OTS_FAILURE_MSG("Can't read header of multiple subst table");
169 if (format != 1) {
170 return OTS_FAILURE_MSG("Bad multiple subst table format %d", format);
173 const uint16_t num_glyphs = file->maxp->num_glyphs;
174 const unsigned sequence_end = static_cast<unsigned>(6) +
175 sequence_count * 2;
176 if (sequence_end > std::numeric_limits<uint16_t>::max()) {
177 return OTS_FAILURE_MSG("Bad segence end %d, in multiple subst", sequence_end);
179 for (unsigned i = 0; i < sequence_count; ++i) {
180 uint16_t offset_sequence = 0;
181 if (!subtable.ReadU16(&offset_sequence)) {
182 return OTS_FAILURE_MSG("Failed to read sequence offset for sequence %d", i);
184 if (offset_sequence < sequence_end || offset_sequence >= length) {
185 return OTS_FAILURE_MSG("Bad sequence offset %d for sequence %d", offset_sequence, i);
187 if (!ParseSequenceTable(file, data + offset_sequence, length - offset_sequence,
188 num_glyphs)) {
189 return OTS_FAILURE_MSG("Failed to parse sequence table %d", i);
193 if (offset_coverage < sequence_end || offset_coverage >= length) {
194 return OTS_FAILURE_MSG("Bad coverage offset %d", offset_coverage);
196 if (!ots::ParseCoverageTable(file, data + offset_coverage,
197 length - offset_coverage, num_glyphs)) {
198 return OTS_FAILURE_MSG("Failed to parse coverage table");
201 return true;
204 bool ParseAlternateSetTable(const ots::OpenTypeFile *file,
205 const uint8_t *data, const size_t length,
206 const uint16_t num_glyphs) {
207 ots::Buffer subtable(data, length);
209 uint16_t glyph_count = 0;
210 if (!subtable.ReadU16(&glyph_count)) {
211 return OTS_FAILURE_MSG("Failed to read alternate set header");
213 if (glyph_count > num_glyphs) {
214 return OTS_FAILURE_MSG("Bad glyph count %d > %d in alternate set table", glyph_count, num_glyphs);
216 for (unsigned i = 0; i < glyph_count; ++i) {
217 uint16_t alternate = 0;
218 if (!subtable.ReadU16(&alternate)) {
219 return OTS_FAILURE_MSG("Can't read alternate %d", i);
221 if (alternate >= num_glyphs) {
222 return OTS_FAILURE_MSG("Too large alternate: %u", alternate);
225 return true;
228 // Lookup Type 3:
229 // Alternate Substitution Subtable
230 bool ParseAlternateSubstitution(const ots::OpenTypeFile *file,
231 const uint8_t *data, const size_t length) {
232 ots::Buffer subtable(data, length);
234 uint16_t format = 0;
235 uint16_t offset_coverage = 0;
236 uint16_t alternate_set_count = 0;
238 if (!subtable.ReadU16(&format) ||
239 !subtable.ReadU16(&offset_coverage) ||
240 !subtable.ReadU16(&alternate_set_count)) {
241 return OTS_FAILURE_MSG("Can't read alternate subst header");
244 if (format != 1) {
245 return OTS_FAILURE_MSG("Bad alternate subst table format %d", format);
248 const uint16_t num_glyphs = file->maxp->num_glyphs;
249 const unsigned alternate_set_end = static_cast<unsigned>(6) +
250 alternate_set_count * 2;
251 if (alternate_set_end > std::numeric_limits<uint16_t>::max()) {
252 return OTS_FAILURE_MSG("Bad end of alternate set %d", alternate_set_end);
254 for (unsigned i = 0; i < alternate_set_count; ++i) {
255 uint16_t offset_alternate_set = 0;
256 if (!subtable.ReadU16(&offset_alternate_set)) {
257 return OTS_FAILURE_MSG("Can't read alternate set offset for set %d", i);
259 if (offset_alternate_set < alternate_set_end ||
260 offset_alternate_set >= length) {
261 return OTS_FAILURE_MSG("Bad alternate set offset %d for set %d", offset_alternate_set, i);
263 if (!ParseAlternateSetTable(file, data + offset_alternate_set,
264 length - offset_alternate_set,
265 num_glyphs)) {
266 return OTS_FAILURE_MSG("Failed to parse alternate set");
270 if (offset_coverage < alternate_set_end || offset_coverage >= length) {
271 return OTS_FAILURE_MSG("Bad coverage offset %d", offset_coverage);
273 if (!ots::ParseCoverageTable(file, data + offset_coverage,
274 length - offset_coverage, num_glyphs)) {
275 return OTS_FAILURE_MSG("Failed to parse coverage table");
278 return true;
281 bool ParseLigatureTable(const ots::OpenTypeFile *file,
282 const uint8_t *data, const size_t length,
283 const uint16_t num_glyphs) {
284 ots::Buffer subtable(data, length);
286 uint16_t lig_glyph = 0;
287 uint16_t comp_count = 0;
289 if (!subtable.ReadU16(&lig_glyph) ||
290 !subtable.ReadU16(&comp_count)) {
291 return OTS_FAILURE_MSG("Failed to read ligatuer table header");
294 if (lig_glyph >= num_glyphs) {
295 return OTS_FAILURE_MSG("too large lig_glyph: %u", lig_glyph);
297 if (comp_count == 0 || comp_count > num_glyphs) {
298 return OTS_FAILURE_MSG("Bad component count of %d", comp_count);
300 for (unsigned i = 0; i < comp_count - static_cast<unsigned>(1); ++i) {
301 uint16_t component = 0;
302 if (!subtable.ReadU16(&component)) {
303 return OTS_FAILURE_MSG("Can't read ligature component %d", i);
305 if (component >= num_glyphs) {
306 return OTS_FAILURE_MSG("Bad ligature component %d of %d", i, component);
310 return true;
313 bool ParseLigatureSetTable(const ots::OpenTypeFile *file,
314 const uint8_t *data, const size_t length,
315 const uint16_t num_glyphs) {
316 ots::Buffer subtable(data, length);
318 uint16_t ligature_count = 0;
320 if (!subtable.ReadU16(&ligature_count)) {
321 return OTS_FAILURE_MSG("Can't read ligature count in ligature set");
324 const unsigned ligature_end = static_cast<unsigned>(2) + ligature_count * 2;
325 if (ligature_end > std::numeric_limits<uint16_t>::max()) {
326 return OTS_FAILURE_MSG("Bad end of ligature %d in ligature set", ligature_end);
328 for (unsigned i = 0; i < ligature_count; ++i) {
329 uint16_t offset_ligature = 0;
330 if (!subtable.ReadU16(&offset_ligature)) {
331 return OTS_FAILURE_MSG("Failed to read ligature offset %d", i);
333 if (offset_ligature < ligature_end || offset_ligature >= length) {
334 return OTS_FAILURE_MSG("Bad ligature offset %d for ligature %d", offset_ligature, i);
336 if (!ParseLigatureTable(file, data + offset_ligature, length - offset_ligature,
337 num_glyphs)) {
338 return OTS_FAILURE_MSG("Failed to parse ligature %d", i);
342 return true;
345 // Lookup Type 4:
346 // Ligature Substitution Subtable
347 bool ParseLigatureSubstitution(const ots::OpenTypeFile *file,
348 const uint8_t *data, const size_t length) {
349 ots::Buffer subtable(data, length);
351 uint16_t format = 0;
352 uint16_t offset_coverage = 0;
353 uint16_t lig_set_count = 0;
355 if (!subtable.ReadU16(&format) ||
356 !subtable.ReadU16(&offset_coverage) ||
357 !subtable.ReadU16(&lig_set_count)) {
358 return OTS_FAILURE_MSG("Failed to read ligature substitution header");
361 if (format != 1) {
362 return OTS_FAILURE_MSG("Bad ligature substitution table format %d", format);
365 const uint16_t num_glyphs = file->maxp->num_glyphs;
366 const unsigned ligature_set_end = static_cast<unsigned>(6) +
367 lig_set_count * 2;
368 if (ligature_set_end > std::numeric_limits<uint16_t>::max()) {
369 return OTS_FAILURE_MSG("Bad end of ligature set %d in ligature substitution table", ligature_set_end);
371 for (unsigned i = 0; i < lig_set_count; ++i) {
372 uint16_t offset_ligature_set = 0;
373 if (!subtable.ReadU16(&offset_ligature_set)) {
374 return OTS_FAILURE_MSG("Can't read ligature set offset %d", i);
376 if (offset_ligature_set < ligature_set_end ||
377 offset_ligature_set >= length) {
378 return OTS_FAILURE_MSG("Bad ligature set offset %d for set %d", offset_ligature_set, i);
380 if (!ParseLigatureSetTable(file, data + offset_ligature_set,
381 length - offset_ligature_set, num_glyphs)) {
382 return OTS_FAILURE_MSG("Failed to parse ligature set %d", i);
386 if (offset_coverage < ligature_set_end || offset_coverage >= length) {
387 return OTS_FAILURE_MSG("Bad coverage offset %d", offset_coverage);
389 if (!ots::ParseCoverageTable(file, data + offset_coverage,
390 length - offset_coverage, num_glyphs)) {
391 return OTS_FAILURE_MSG("Failed to parse coverage table");
394 return true;
397 // Lookup Type 5:
398 // Contextual Substitution Subtable
399 bool ParseContextSubstitution(const ots::OpenTypeFile *file,
400 const uint8_t *data, const size_t length) {
401 return ots::ParseContextSubtable(file, data, length, file->maxp->num_glyphs,
402 file->gsub->num_lookups);
405 // Lookup Type 6:
406 // Chaining Contextual Substitution Subtable
407 bool ParseChainingContextSubstitution(const ots::OpenTypeFile *file,
408 const uint8_t *data,
409 const size_t length) {
410 return ots::ParseChainingContextSubtable(file, data, length,
411 file->maxp->num_glyphs,
412 file->gsub->num_lookups);
415 // Lookup Type 7:
416 // Extension Substition
417 bool ParseExtensionSubstitution(const ots::OpenTypeFile *file,
418 const uint8_t *data, const size_t length) {
419 return ots::ParseExtensionSubtable(file, data, length,
420 &kGsubLookupSubtableParser);
423 // Lookup Type 8:
424 // Reverse Chaining Contexual Single Substitution Subtable
425 bool ParseReverseChainingContextSingleSubstitution(
426 const ots::OpenTypeFile *file, const uint8_t *data, const size_t length) {
427 ots::Buffer subtable(data, length);
429 uint16_t format = 0;
430 uint16_t offset_coverage = 0;
432 if (!subtable.ReadU16(&format) ||
433 !subtable.ReadU16(&offset_coverage)) {
434 return OTS_FAILURE_MSG("Failed to read reverse chaining header");
437 const uint16_t num_glyphs = file->maxp->num_glyphs;
439 uint16_t backtrack_glyph_count = 0;
440 if (!subtable.ReadU16(&backtrack_glyph_count)) {
441 return OTS_FAILURE_MSG("Failed to read backtrack glyph count in reverse chaining table");
443 if (backtrack_glyph_count > num_glyphs) {
444 return OTS_FAILURE_MSG("Bad backtrack glyph count of %d", backtrack_glyph_count);
446 std::vector<uint16_t> offsets_backtrack;
447 offsets_backtrack.reserve(backtrack_glyph_count);
448 for (unsigned i = 0; i < backtrack_glyph_count; ++i) {
449 uint16_t offset = 0;
450 if (!subtable.ReadU16(&offset)) {
451 return OTS_FAILURE_MSG("Failed to read backtrack offset %d", i);
453 offsets_backtrack.push_back(offset);
456 uint16_t lookahead_glyph_count = 0;
457 if (!subtable.ReadU16(&lookahead_glyph_count)) {
458 return OTS_FAILURE_MSG("Failed to read look ahead glyph count");
460 if (lookahead_glyph_count > num_glyphs) {
461 return OTS_FAILURE_MSG("Bad look ahead glyph count %d", lookahead_glyph_count);
463 std::vector<uint16_t> offsets_lookahead;
464 offsets_lookahead.reserve(lookahead_glyph_count);
465 for (unsigned i = 0; i < lookahead_glyph_count; ++i) {
466 uint16_t offset = 0;
467 if (!subtable.ReadU16(&offset)) {
468 return OTS_FAILURE_MSG("Can't read look ahead offset %d", i);
470 offsets_lookahead.push_back(offset);
473 uint16_t glyph_count = 0;
474 if (!subtable.ReadU16(&glyph_count)) {
475 return OTS_FAILURE_MSG("Can't read glyph count in reverse chaining table");
477 if (glyph_count > num_glyphs) {
478 return OTS_FAILURE_MSG("Bad glyph count of %d", glyph_count);
480 for (unsigned i = 0; i < glyph_count; ++i) {
481 uint16_t substitute = 0;
482 if (!subtable.ReadU16(&substitute)) {
483 return OTS_FAILURE_MSG("Failed to read substitution %d reverse chaining table", i);
485 if (substitute >= num_glyphs) {
486 return OTS_FAILURE_MSG("Bad substitute glyph %d in reverse chaining table substitution %d", substitute, i);
490 const unsigned substitute_end = static_cast<unsigned>(10) +
491 (backtrack_glyph_count + lookahead_glyph_count + glyph_count) * 2;
492 if (substitute_end > std::numeric_limits<uint16_t>::max()) {
493 return OTS_FAILURE_MSG("Bad substitute end offset in reverse chaining table");
496 if (offset_coverage < substitute_end || offset_coverage >= length) {
497 return OTS_FAILURE_MSG("Bad coverage offset %d in reverse chaining table", offset_coverage);
499 if (!ots::ParseCoverageTable(file, data + offset_coverage,
500 length - offset_coverage, num_glyphs)) {
501 return OTS_FAILURE_MSG("Failed to parse coverage table in reverse chaining table");
504 for (unsigned i = 0; i < backtrack_glyph_count; ++i) {
505 if (offsets_backtrack[i] < substitute_end ||
506 offsets_backtrack[i] >= length) {
507 return OTS_FAILURE_MSG("Bad backtrack offset %d for backtrack %d in reverse chaining table", offsets_backtrack[i], i);
509 if (!ots::ParseCoverageTable(file, data + offsets_backtrack[i],
510 length - offsets_backtrack[i], num_glyphs)) {
511 return OTS_FAILURE_MSG("Failed to parse coverage table for backtrack %d in reverse chaining table", i);
515 for (unsigned i = 0; i < lookahead_glyph_count; ++i) {
516 if (offsets_lookahead[i] < substitute_end ||
517 offsets_lookahead[i] >= length) {
518 return OTS_FAILURE_MSG("Bad lookahead offset %d for lookahead %d in reverse chaining table", offsets_lookahead[i], i);
520 if (!ots::ParseCoverageTable(file, data + offsets_lookahead[i],
521 length - offsets_lookahead[i], num_glyphs)) {
522 return OTS_FAILURE_MSG("Failed to parse lookahead coverage table %d in reverse chaining table", i);
526 return true;
529 } // namespace
531 #define DROP_THIS_TABLE(msg_) \
532 do { \
533 OTS_FAILURE_MSG(msg_ ", table discarded"); \
534 file->gsub->data = 0; \
535 file->gsub->length = 0; \
536 } while (0)
538 namespace ots {
540 // As far as I checked, following fonts contain invalid values in GSUB table.
541 // OTS will drop their GSUB table.
543 // # too large substitute (value is 0xFFFF)
544 // kaiu.ttf
545 // mingliub2.ttf
546 // mingliub1.ttf
547 // mingliub0.ttf
548 // GraublauWeb.otf
549 // GraublauWebBold.otf
551 // # too large alternate (value is 0xFFFF)
552 // ManchuFont.ttf
554 // # bad offset to lang sys table (NULL offset)
555 // DejaVuMonoSansBold.ttf
556 // DejaVuMonoSansBoldOblique.ttf
557 // DejaVuMonoSansOblique.ttf
558 // DejaVuSansMono-BoldOblique.ttf
559 // DejaVuSansMono-Oblique.ttf
560 // DejaVuSansMono-Bold.ttf
562 // # bad start coverage index
563 // GenBasBI.ttf
564 // GenBasI.ttf
565 // AndBasR.ttf
566 // GenBkBasI.ttf
567 // CharisSILR.ttf
568 // CharisSILBI.ttf
569 // CharisSILI.ttf
570 // CharisSILB.ttf
571 // DoulosSILR.ttf
572 // CharisSILBI.ttf
573 // GenBkBasB.ttf
574 // GenBkBasR.ttf
575 // GenBkBasBI.ttf
576 // GenBasB.ttf
577 // GenBasR.ttf
579 // # glyph range is overlapping
580 // KacstTitleL.ttf
581 // KacstDecorative.ttf
582 // KacstTitle.ttf
583 // KacstArt.ttf
584 // KacstPoster.ttf
585 // KacstQurn.ttf
586 // KacstDigital.ttf
587 // KacstBook.ttf
588 // KacstFarsi.ttf
590 bool ots_gsub_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
591 // Parsing gsub table requires |file->maxp->num_glyphs|
592 if (!file->maxp) {
593 return OTS_FAILURE_MSG("Missing maxp table in font, needed by GSUB");
596 Buffer table(data, length);
598 OpenTypeGSUB *gsub = new OpenTypeGSUB;
599 file->gsub = gsub;
601 uint32_t version = 0;
602 uint16_t offset_script_list = 0;
603 uint16_t offset_feature_list = 0;
604 uint16_t offset_lookup_list = 0;
605 if (!table.ReadU32(&version) ||
606 !table.ReadU16(&offset_script_list) ||
607 !table.ReadU16(&offset_feature_list) ||
608 !table.ReadU16(&offset_lookup_list)) {
609 DROP_THIS_TABLE("Incomplete table");
610 return true;
613 if (version != 0x00010000) {
614 DROP_THIS_TABLE("Bad version");
615 return true;
618 if (offset_lookup_list) {
619 if (offset_lookup_list < kGsubHeaderSize || offset_lookup_list >= length) {
620 DROP_THIS_TABLE("Bad lookup list offset in table header");
621 return true;
624 if (!ParseLookupListTable(file, data + offset_lookup_list,
625 length - offset_lookup_list,
626 &kGsubLookupSubtableParser,
627 &gsub->num_lookups)) {
628 DROP_THIS_TABLE("Failed to parse lookup list table");
629 return true;
633 uint16_t num_features = 0;
634 if (offset_feature_list) {
635 if (offset_feature_list < kGsubHeaderSize || offset_feature_list >= length) {
636 DROP_THIS_TABLE("Bad feature list offset in table header");
637 return true;
640 if (!ParseFeatureListTable(file, data + offset_feature_list,
641 length - offset_feature_list, gsub->num_lookups,
642 &num_features)) {
643 DROP_THIS_TABLE("Failed to parse feature list table");
644 return true;
648 if (offset_script_list) {
649 if (offset_script_list < kGsubHeaderSize || offset_script_list >= length) {
650 DROP_THIS_TABLE("Bad script list offset in table header");
651 return true;
654 if (!ParseScriptListTable(file, data + offset_script_list,
655 length - offset_script_list, num_features)) {
656 DROP_THIS_TABLE("Failed to parse script list table");
657 return true;
661 gsub->data = data;
662 gsub->length = length;
663 return true;
666 bool ots_gsub_should_serialise(OpenTypeFile *file) {
667 return file->gsub != NULL && file->gsub->data != NULL;
670 bool ots_gsub_serialise(OTSStream *out, OpenTypeFile *file) {
671 if (!out->Write(file->gsub->data, file->gsub->length)) {
672 return OTS_FAILURE_MSG("Failed to write GSUB table");
675 return true;
678 void ots_gsub_free(OpenTypeFile *file) {
679 delete file->gsub;
682 } // namespace ots
684 #undef TABLE_NAME
685 #undef DROP_THIS_TABLE