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