blob: b38ebcb0cbbffe0ae8fd0b8c06329ee6a1416f2f [file] [log] [blame]
bashi@chromium.orga5748662011-03-18 17:48:29 +00001// 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
18namespace {
19
20// The GSUB header size
21const size_t kGsubHeaderSize = 8;
22
23enum GSUB_TYPE {
bashi@chromium.orgc486cfb2011-03-23 06:08:30 +000024 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,
bashi@chromium.orga5748662011-03-18 17:48:29 +000032 GSUB_TYPE_RESERVED = 9
33};
34
35// Lookup type parsers.
36bool ParseSingleSubstitution(const ots::OpenTypeFile *file,
37 const uint8_t *data, const size_t length);
38bool ParseMutipleSubstitution(const ots::OpenTypeFile *file,
39 const uint8_t *data, const size_t length);
40bool ParseAlternateSubstitution(const ots::OpenTypeFile *file,
41 const uint8_t *data, const size_t length);
42bool ParseLigatureSubstitution(const ots::OpenTypeFile *file,
43 const uint8_t *data, const size_t length);
44bool ParseContextSubstitution(const ots::OpenTypeFile *file,
45 const uint8_t *data, const size_t length);
46bool ParseChainingContextSubstitution(const ots::OpenTypeFile *file,
47 const uint8_t *data,
48 const size_t length);
49bool ParseExtensionSubstitution(const ots::OpenTypeFile *file,
50 const uint8_t *data, const size_t length);
51bool ParseReverseChainingContextSingleSubstitution(
52 const ots::OpenTypeFile *file, const uint8_t *data, const size_t length);
53
54const ots::LookupSubtableParser::TypeParser kGsubTypeParsers[] = {
bashi@chromium.orgc486cfb2011-03-23 06:08:30 +000055 {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,
bashi@chromium.orga5748662011-03-18 17:48:29 +000063 ParseReverseChainingContextSingleSubstitution}
64};
65
bashi@chromium.orgbe8c6382012-03-03 11:08:05 +000066// TODO(bashi): Port Chromium's arraysize macro and use it instead of sizeof().
bashi@chromium.orga5748662011-03-18 17:48:29 +000067const ots::LookupSubtableParser kGsubLookupSubtableParser = {
bashi@chromium.orgbe8c6382012-03-03 11:08:05 +000068 sizeof(kGsubTypeParsers) / sizeof(kGsubTypeParsers[0]),
69 GSUB_TYPE_EXTENSION_SUBSTITUTION, kGsubTypeParsers
bashi@chromium.orga5748662011-03-18 17:48:29 +000070};
71
72// Lookup Type 1:
73// Single Substitution Subtable
74bool ParseSingleSubstitution(const ots::OpenTypeFile *file,
75 const uint8_t *data, const size_t length) {
76 ots::Buffer subtable(data, length);
77
78 uint16_t format = 0;
79 uint16_t offset_coverage = 0;
80
81 if (!subtable.ReadU16(&format) ||
82 !subtable.ReadU16(&offset_coverage)) {
83 return OTS_FAILURE();
84 }
85
86 const uint16_t num_glyphs = file->maxp->num_glyphs;
87 if (format == 1) {
88 // Parse SingleSubstFormat1
89 int16_t delta_glyph_id = 0;
90 if (!subtable.ReadS16(&delta_glyph_id)) {
91 return OTS_FAILURE();
92 }
93 if (std::abs(delta_glyph_id) >= num_glyphs) {
94 return OTS_FAILURE();
95 }
96 } else if (format == 2) {
97 // Parse SingleSubstFormat2
98 uint16_t glyph_count = 0;
99 if (!subtable.ReadU16(&glyph_count)) {
100 return OTS_FAILURE();
101 }
102 if (glyph_count > num_glyphs) {
103 return OTS_FAILURE();
104 }
105 for (unsigned i = 0; i < glyph_count; ++i) {
106 uint16_t substitute = 0;
107 if (!subtable.ReadU16(&substitute)) {
108 return OTS_FAILURE();
109 }
110 if (substitute >= num_glyphs) {
111 OTS_WARNING("too large substitute: %u", substitute);
112 return OTS_FAILURE();
113 }
114 }
115 } else {
116 return OTS_FAILURE();
117 }
118
119 if (offset_coverage < subtable.offset() || offset_coverage >= length) {
120 return OTS_FAILURE();
121 }
122 if (!ots::ParseCoverageTable(data + offset_coverage,
123 length - offset_coverage, num_glyphs)) {
124 return OTS_FAILURE();
125 }
126
127 return true;
128}
129
130bool ParseSequenceTable(const uint8_t *data, const size_t length,
131 const uint16_t num_glyphs) {
132 ots::Buffer subtable(data, length);
133
134 uint16_t glyph_count = 0;
135 if (!subtable.ReadU16(&glyph_count)) {
136 return OTS_FAILURE();
137 }
138 if (glyph_count > num_glyphs) {
139 return OTS_FAILURE();
140 }
141 for (unsigned i = 0; i < glyph_count; ++i) {
142 uint16_t substitute = 0;
143 if (!subtable.ReadU16(&substitute)) {
144 return OTS_FAILURE();
145 }
146 if (substitute >= num_glyphs) {
147 return OTS_FAILURE();
148 }
149 }
150
151 return true;
152}
153
154// Lookup Type 2:
155// Multiple Substitution Subtable
156bool ParseMutipleSubstitution(const ots::OpenTypeFile *file,
157 const uint8_t *data, const size_t length) {
158 ots::Buffer subtable(data, length);
159
160 uint16_t format = 0;
161 uint16_t offset_coverage = 0;
162 uint16_t sequence_count = 0;
163
164 if (!subtable.ReadU16(&format) ||
165 !subtable.ReadU16(&offset_coverage) ||
166 !subtable.ReadU16(&sequence_count)) {
167 return OTS_FAILURE();
168 }
169
170 if (format != 1) {
171 return OTS_FAILURE();
172 }
173
174 const uint16_t num_glyphs = file->maxp->num_glyphs;
175 const unsigned sequence_end = static_cast<unsigned>(6) +
176 sequence_count * 2;
177 if (sequence_end > std::numeric_limits<uint16_t>::max()) {
178 return OTS_FAILURE();
179 }
180 for (unsigned i = 0; i < sequence_count; ++i) {
181 uint16_t offset_sequence = 0;
182 if (!subtable.ReadU16(&offset_sequence)) {
183 return OTS_FAILURE();
184 }
185 if (offset_sequence < sequence_end || offset_sequence >= length) {
186 return OTS_FAILURE();
187 }
188 if (!ParseSequenceTable(data + offset_sequence, length - offset_sequence,
189 num_glyphs)) {
190 return OTS_FAILURE();
191 }
192 }
193
194 if (offset_coverage < sequence_end || offset_coverage >= length) {
195 return OTS_FAILURE();
196 }
197 if (!ots::ParseCoverageTable(data + offset_coverage,
198 length - offset_coverage, num_glyphs)) {
199 return OTS_FAILURE();
200 }
201
202 return true;
203}
204
205bool ParseAlternateSetTable(const uint8_t *data, const size_t length,
206 const uint16_t num_glyphs) {
207 ots::Buffer subtable(data, length);
208
209 uint16_t glyph_count = 0;
210 if (!subtable.ReadU16(&glyph_count)) {
211 return OTS_FAILURE();
212 }
213 if (glyph_count > num_glyphs) {
214 return OTS_FAILURE();
215 }
216 for (unsigned i = 0; i < glyph_count; ++i) {
217 uint16_t alternate = 0;
218 if (!subtable.ReadU16(&alternate)) {
219 return OTS_FAILURE();
220 }
221 if (alternate >= num_glyphs) {
222 OTS_WARNING("too arge alternate: %u", alternate);
223 return OTS_FAILURE();
224 }
225 }
226 return true;
227}
228
229// Lookup Type 3:
230// Alternate Substitution Subtable
231bool ParseAlternateSubstitution(const ots::OpenTypeFile *file,
232 const uint8_t *data, const size_t length) {
233 ots::Buffer subtable(data, length);
234
235 uint16_t format = 0;
236 uint16_t offset_coverage = 0;
237 uint16_t alternate_set_count = 0;
238
239 if (!subtable.ReadU16(&format) ||
240 !subtable.ReadU16(&offset_coverage) ||
241 !subtable.ReadU16(&alternate_set_count)) {
242 return OTS_FAILURE();
243 }
244
245 if (format != 1) {
246 return OTS_FAILURE();
247 }
248
249 const uint16_t num_glyphs = file->maxp->num_glyphs;
250 const unsigned alternate_set_end = static_cast<unsigned>(6) +
251 alternate_set_count * 2;
252 if (alternate_set_end > std::numeric_limits<uint16_t>::max()) {
253 return OTS_FAILURE();
254 }
255 for (unsigned i = 0; i < alternate_set_count; ++i) {
256 uint16_t offset_alternate_set = 0;
257 if (!subtable.ReadU16(&offset_alternate_set)) {
258 return OTS_FAILURE();
259 }
260 if (offset_alternate_set < alternate_set_end ||
261 offset_alternate_set >= length) {
262 return OTS_FAILURE();
263 }
264 if (!ParseAlternateSetTable(data + offset_alternate_set,
265 length - offset_alternate_set,
266 num_glyphs)) {
267 return OTS_FAILURE();
268 }
269 }
270
271 if (offset_coverage < alternate_set_end || offset_coverage >= length) {
272 return OTS_FAILURE();
273 }
274 if (!ots::ParseCoverageTable(data + offset_coverage,
275 length - offset_coverage, num_glyphs)) {
276 return OTS_FAILURE();
277 }
278
279 return true;
280}
281
282bool ParseLigatureTable(const uint8_t *data, const size_t length,
283 const uint16_t num_glyphs) {
284 ots::Buffer subtable(data, length);
285
286 uint16_t lig_glyph = 0;
287 uint16_t comp_count = 0;
288
289 if (!subtable.ReadU16(&lig_glyph) ||
290 !subtable.ReadU16(&comp_count)) {
291 return OTS_FAILURE();
292 }
293
294 if (lig_glyph >= num_glyphs) {
295 OTS_WARNING("too large lig_glyph: %u", lig_glyph);
296 return OTS_FAILURE();
297 }
298 if (comp_count == 0 || comp_count > num_glyphs) {
299 return OTS_FAILURE();
300 }
301 for (unsigned i = 0; i < comp_count - static_cast<unsigned>(1); ++i) {
302 uint16_t component = 0;
303 if (!subtable.ReadU16(&component)) {
304 return OTS_FAILURE();
305 }
306 if (component >= num_glyphs) {
307 return OTS_FAILURE();
308 }
309 }
310
311 return true;
312}
313
314bool ParseLigatureSetTable(const uint8_t *data, const size_t length,
315 const uint16_t num_glyphs) {
316 ots::Buffer subtable(data, length);
317
318 uint16_t ligature_count = 0;
319
320 if (!subtable.ReadU16(&ligature_count)) {
321 return OTS_FAILURE();
322 }
323
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();
327 }
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();
332 }
333 if (offset_ligature < ligature_end || offset_ligature >= length) {
334 return OTS_FAILURE();
335 }
336 if (!ParseLigatureTable(data + offset_ligature, length - offset_ligature,
337 num_glyphs)) {
338 return OTS_FAILURE();
339 }
340 }
341
342 return true;
343}
344
345// Lookup Type 4:
346// Ligature Substitution Subtable
347bool ParseLigatureSubstitution(const ots::OpenTypeFile *file,
348 const uint8_t *data, const size_t length) {
349 ots::Buffer subtable(data, length);
350
351 uint16_t format = 0;
352 uint16_t offset_coverage = 0;
353 uint16_t lig_set_count = 0;
354
355 if (!subtable.ReadU16(&format) ||
356 !subtable.ReadU16(&offset_coverage) ||
357 !subtable.ReadU16(&lig_set_count)) {
358 return OTS_FAILURE();
359 }
360
361 if (format != 1) {
362 return OTS_FAILURE();
363 }
364
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();
370 }
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();
375 }
376 if (offset_ligature_set < ligature_set_end ||
377 offset_ligature_set >= length) {
378 return OTS_FAILURE();
379 }
380 if (!ParseLigatureSetTable(data + offset_ligature_set,
381 length - offset_ligature_set, num_glyphs)) {
382 return OTS_FAILURE();
383 }
384 }
385
386 if (offset_coverage < ligature_set_end || offset_coverage >= length) {
387 return OTS_FAILURE();
388 }
389 if (!ots::ParseCoverageTable(data + offset_coverage,
390 length - offset_coverage, num_glyphs)) {
391 return OTS_FAILURE();
392 }
393
394 return true;
395}
396
bashi@chromium.orga5748662011-03-18 17:48:29 +0000397// Lookup Type 5:
398// Contextual Substitution Subtable
399bool 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 file->gsub->num_lookups);
403}
404
405// Lookup Type 6:
406// Chaining Contextual Substitution Subtable
407bool 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 file->gsub->num_lookups);
413}
414
415// Lookup Type 7:
416// Extension Substition
417bool ParseExtensionSubstitution(const ots::OpenTypeFile *file,
418 const uint8_t *data, const size_t length) {
419 return ots::ParseExtensionSubtable(file, data, length,
420 &kGsubLookupSubtableParser);
421}
422
423// Lookup Type 8:
424// Reverse Chaining Contexual Single Substitution Subtable
425bool ParseReverseChainingContextSingleSubstitution(
426 const ots::OpenTypeFile *file, const uint8_t *data, const size_t length) {
427 ots::Buffer subtable(data, length);
428
429 uint16_t format = 0;
430 uint16_t offset_coverage = 0;
431
432 if (!subtable.ReadU16(&format) ||
433 !subtable.ReadU16(&offset_coverage)) {
434 return OTS_FAILURE();
435 }
436
437 const uint16_t num_glyphs = file->maxp->num_glyphs;
438
439 uint16_t backtrack_glyph_count = 0;
440 if (!subtable.ReadU16(&backtrack_glyph_count)) {
441 return OTS_FAILURE();
442 }
443 if (backtrack_glyph_count > num_glyphs) {
444 return OTS_FAILURE();
445 }
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();
452 }
453 offsets_backtrack.push_back(offset);
454 }
455
456 uint16_t lookahead_glyph_count = 0;
457 if (!subtable.ReadU16(&lookahead_glyph_count)) {
458 return OTS_FAILURE();
459 }
460 if (lookahead_glyph_count > num_glyphs) {
461 return OTS_FAILURE();
462 }
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();
469 }
470 offsets_lookahead.push_back(offset);
471 }
472
473 uint16_t glyph_count = 0;
474 if (!subtable.ReadU16(&glyph_count)) {
475 return OTS_FAILURE();
476 }
477 if (glyph_count > num_glyphs) {
478 return OTS_FAILURE();
479 }
480 for (unsigned i = 0; i < glyph_count; ++i) {
481 uint16_t substitute = 0;
482 if (!subtable.ReadU16(&substitute)) {
483 return OTS_FAILURE();
484 }
485 if (substitute >= num_glyphs) {
486 return OTS_FAILURE();
487 }
488 }
489
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();
494 }
495
496 if (offset_coverage < substitute_end || offset_coverage >= length) {
497 return OTS_FAILURE();
498 }
499 if (!ots::ParseCoverageTable(data + offset_coverage,
500 length - offset_coverage, num_glyphs)) {
501 return OTS_FAILURE();
502 }
503
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();
508 }
509 if (!ots::ParseCoverageTable(data + offsets_backtrack[i],
510 length - offsets_backtrack[i], num_glyphs)) {
511 return OTS_FAILURE();
512 }
513 }
514
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();
519 }
520 if (!ots::ParseCoverageTable(data + offsets_lookahead[i],
521 length - offsets_lookahead[i], num_glyphs)) {
522 return OTS_FAILURE();
523 }
524 }
525
526 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
534namespace 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
586bool ots_gsub_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
587 // Parsing gsub table requires |file->maxp->num_glyphs|
588 if (!file->maxp) {
589 return OTS_FAILURE();
590 }
591
592 Buffer table(data, length);
593
594 OpenTypeGSUB *gsub = new OpenTypeGSUB;
595 file->gsub = gsub;
596
597 uint32_t version = 0;
598 uint16_t offset_script_list = 0;
599 uint16_t offset_feature_list = 0;
600 uint16_t offset_lookup_list = 0;
601 if (!table.ReadU32(&version) ||
602 !table.ReadU16(&offset_script_list) ||
603 !table.ReadU16(&offset_feature_list) ||
604 !table.ReadU16(&offset_lookup_list)) {
605 return OTS_FAILURE();
606 }
607
608 if (version != 0x00010000) {
609 OTS_WARNING("bad GSUB version");
610 DROP_THIS_TABLE;
611 return true;
612 }
613 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 DROP_THIS_TABLE;
621 return true;
622 }
623
624 if (!ParseLookupListTable(file, data + offset_lookup_list,
625 length - offset_lookup_list,
626 &kGsubLookupSubtableParser,
627 &gsub->num_lookups)) {
628 OTS_WARNING("faild to parse lookup list table");
629 DROP_THIS_TABLE;
630 return true;
631 }
632
633 uint16_t num_features = 0;
634 if (!ParseFeatureListTable(data + offset_feature_list,
635 length - offset_feature_list, gsub->num_lookups,
636 &num_features)) {
637 OTS_WARNING("faild to parse feature list table");
638 DROP_THIS_TABLE;
639 return true;
640 }
641
642 if (!ParseScriptListTable(data + offset_script_list,
643 length - offset_script_list, num_features)) {
644 OTS_WARNING("faild to parse script list table");
645 DROP_THIS_TABLE;
646 return true;
647 }
648
649 gsub->data = data;
650 gsub->length = length;
651 return true;
652}
653
654bool ots_gsub_should_serialise(OpenTypeFile *file) {
655 const bool needed_tables_dropped =
656 (file->gdef && file->gdef->data == NULL) ||
657 (file->gpos && file->gpos->data == NULL);
658 return file->gsub != NULL && file->gsub->data != NULL
659 && !needed_tables_dropped;
660}
661
662bool ots_gsub_serialise(OTSStream *out, OpenTypeFile *file) {
663 if (!out->Write(file->gsub->data, file->gsub->length)) {
664 return OTS_FAILURE();
665 }
666
667 return true;
668}
669
670void ots_gsub_free(OpenTypeFile *file) {
671 delete file->gsub;
672}
673
674} // namespace ots
675