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.
13 // GSUB - The Glyph Substitution Table
14 // http://www.microsoft.com/typography/otspec/gsub.htm
16 #define TABLE_NAME "GSUB"
20 // The GSUB header size
21 const size_t kGsubHeaderSize
= 4 + 3 * 2;
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
,
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
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
);
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
;
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
);
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");
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
);
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
);
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");
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) +
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
,
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");
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
);
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
);
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");
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
,
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");
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
);
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
,
338 return OTS_FAILURE_MSG("Failed to parse ligature %d", i
);
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
);
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");
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) +
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");
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
);
406 // Chaining Contextual Substitution Subtable
407 bool ParseChainingContextSubstitution(const ots::OpenTypeFile
*file
,
409 const size_t length
) {
410 return ots::ParseChainingContextSubtable(file
, data
, length
,
411 file
->maxp
->num_glyphs
,
412 file
->gsub
->num_lookups
);
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
);
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
);
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
) {
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
) {
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
);
531 #define DROP_THIS_TABLE(msg_) \
533 OTS_FAILURE_MSG(msg_ ", table discarded"); \
534 file->gsub->data = 0; \
535 file->gsub->length = 0; \
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)
549 // GraublauWebBold.otf
551 // # too large alternate (value is 0xFFFF)
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
579 // # glyph range is overlapping
581 // KacstDecorative.ttf
590 bool ots_gsub_parse(OpenTypeFile
*file
, const uint8_t *data
, size_t length
) {
591 // Parsing gsub table requires |file->maxp->num_glyphs|
593 return OTS_FAILURE_MSG("Missing maxp table in font, needed by GSUB");
596 Buffer
table(data
, length
);
598 OpenTypeGSUB
*gsub
= new OpenTypeGSUB
;
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");
613 if (version
!= 0x00010000) {
614 DROP_THIS_TABLE("Bad version");
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");
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");
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");
640 if (!ParseFeatureListTable(file
, data
+ offset_feature_list
,
641 length
- offset_feature_list
, gsub
->num_lookups
,
643 DROP_THIS_TABLE("Failed to parse feature list table");
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");
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");
662 gsub
->length
= length
;
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");
678 void ots_gsub_free(OpenTypeFile
*file
) {
685 #undef DROP_THIS_TABLE