blob: fba14562195e8ebf51d6100f66acbe047323faea [file] [log] [blame]
bashi@chromium.orgced71122011-02-17 10:23:47 +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 "gpos.h"
6
7#include <limits>
8#include <vector>
9
10#include "layout.h"
11#include "maxp.h"
12
13// GPOS - The Glyph Positioning Table
14// http://www.microsoft.com/typography/otspec/gpos.htm
15
16namespace {
17
18enum GPOS_TYPE {
19 SINGLE_ADJUSTMENT = 1,
20 PAIR_ADJUSTMENT = 2,
21 CURSIVE_ATTACHMENT = 3,
22 MARK_TO_BASE_ATTACHMENT = 4,
23 MARK_TO_LIGATURE_ATTACHMENT = 5,
24 MARK_TO_MARK_ATTACHMENT = 6,
25 CONTEXT_POSITIONING = 7,
26 CHAINED_CONTEXT_POSITIONING = 8,
27 EXTENSION_POSITIONING = 9,
28 GPOS_TYPE_RESERVED = 10
29};
30
31// The size of gpos header.
32const unsigned kGposHeaderSize = 10;
33// The maximum format number for anchor tables.
34const uint16_t kMaxAnchorFormat = 3;
35// The maximum number of class value.
36const uint16_t kMaxClassDefValue = 0xFFFF;
37
38// Lookup type parsers.
39bool ParseSingleAdjustment(const ots::OpenTypeFile *file,
40 const uint8_t *data, const size_t length);
41bool ParsePairAdjustment(const ots::OpenTypeFile *file,
42 const uint8_t *data, const size_t length);
43bool ParseCursiveAttachment(const ots::OpenTypeFile *file,
44 const uint8_t *data, const size_t length);
45bool ParseMarkToBaseAttachment(const ots::OpenTypeFile *file,
46 const uint8_t *data, const size_t length);
47bool ParseMarkToLigatureAttachment(const ots::OpenTypeFile *file,
48 const uint8_t *data, const size_t length);
49bool ParseMarkToMarkAttachment(const ots::OpenTypeFile *file,
50 const uint8_t *data, const size_t length);
51bool ParseContextPositioning(const ots::OpenTypeFile *file,
52 const uint8_t *data, const size_t length);
53bool ParseChainedContextPositioning(const ots::OpenTypeFile *file,
54 const uint8_t *data, const size_t length);
55bool ParseExtensionPositioning(const ots::OpenTypeFile *file,
56 const uint8_t *data, const size_t length);
57
58const ots::LookupSubtableParser::TypeParser kGposTypeParsers[] = {
59 {SINGLE_ADJUSTMENT, ParseSingleAdjustment},
60 {PAIR_ADJUSTMENT, ParsePairAdjustment},
61 {CURSIVE_ATTACHMENT, ParseCursiveAttachment},
62 {MARK_TO_BASE_ATTACHMENT, ParseMarkToBaseAttachment},
63 {MARK_TO_LIGATURE_ATTACHMENT, ParseMarkToLigatureAttachment},
64 {MARK_TO_MARK_ATTACHMENT, ParseMarkToMarkAttachment},
65 {CONTEXT_POSITIONING, ParseContextPositioning},
66 {CHAINED_CONTEXT_POSITIONING, ParseChainedContextPositioning},
67 {EXTENSION_POSITIONING, ParseExtensionPositioning}
68};
69
70const ots::LookupSubtableParser kGposLookupSubtableParser = {
71 GPOS_TYPE_RESERVED, EXTENSION_POSITIONING, kGposTypeParsers
72};
73
74// Shared Tables: ValueRecord, Anchor Table, and MarkArray
75
76bool ParseValueRecord(ots::Buffer* subtable, const uint8_t *data,
77 const size_t length, const uint16_t value_format) {
78 // Check existence of adjustment fields.
79 for (unsigned i = 0; i < 4; ++i) {
80 if ((value_format >> i) & 0x1) {
81 // Just read the field since these fileds could take an arbitrary values.
82 if (!subtable->Skip(2)) {
83 return OTS_FAILURE();
84 }
85 }
86 }
87
88 // Check existence of offsets to device table.
89 for (unsigned i = 0; i < 4; ++i) {
90 if ((value_format >> (i + 4)) & 0x1) {
91 uint16_t offset = 0;
92 if (!subtable->ReadU16(&offset)) {
93 return OTS_FAILURE();
94 }
95 if (offset) {
96 // TODO(bashi): Is it possible that device tables locate before
97 // this record? No fonts contain such offset AKAIF.
98 if (offset >= length) {
99 return OTS_FAILURE();
100 }
101 if (!ots::ParseDeviceTable(data + offset, length - offset)) {
102 return OTS_FAILURE();
103 }
104 }
105 }
106 }
107 return true;
108}
109
110bool ParseAnchorTable(const uint8_t *data, const size_t length) {
111 ots::Buffer subtable(data, length);
112
113 uint16_t format = 0;
114 // Read format and skip 2 2-byte fields that could be arbitrary values.
115 if (!subtable.ReadU16(&format) ||
116 !subtable.Skip(4)) {
117 return OTS_FAILURE();
118 }
119
120 if (format == 0 || format > kMaxAnchorFormat) {
121 return OTS_FAILURE();
122 }
123
124 // Format 2 and 3 has additional fields.
125 if (format == 2) {
126 // Format 2 provides an index to a glyph contour point, which will take
127 // arbitrary value.
128 uint16_t anchor_point = 0;
129 if (!subtable.ReadU16(&anchor_point)) {
130 return OTS_FAILURE();
131 }
132 } else if (format == 3) {
133 uint16_t offset_x_device = 0;
134 uint16_t offset_y_device = 0;
135 if (!subtable.ReadU16(&offset_x_device) ||
136 !subtable.ReadU16(&offset_y_device)) {
137 return OTS_FAILURE();
138 }
139 const unsigned format_end = static_cast<unsigned>(10);
140 if (offset_x_device) {
141 if (offset_x_device < format_end || offset_x_device >= length) {
142 return OTS_FAILURE();
143 }
144 if (!ots::ParseDeviceTable(data + offset_x_device,
145 length - offset_x_device)) {
146 return OTS_FAILURE();
147 }
148 }
149 if (offset_y_device) {
150 if (offset_y_device < format_end || offset_y_device >= length) {
151 return OTS_FAILURE();
152 }
153 if (!ots::ParseDeviceTable(data + offset_y_device,
154 length - offset_y_device)) {
155 return OTS_FAILURE();
156 }
157 }
158 }
159 return true;
160}
161
162bool ParseMarkArrayTable(const uint8_t *data, const size_t length,
163 const uint16_t class_count) {
164 ots::Buffer subtable(data, length);
165
166 uint16_t mark_count = 0;
167 if (!subtable.ReadU16(&mark_count)) {
168 return OTS_FAILURE();
169 }
170
171 // MarkRecord consists of 4-bytes.
172 const unsigned mark_records_end = static_cast<unsigned>(2) + mark_count * 4;
173 if (mark_records_end > std::numeric_limits<uint16_t>::max()) {
174 return OTS_FAILURE();
175 }
176 for (unsigned i = 0; i < mark_count; ++i) {
177 uint16_t class_value = 0;
178 uint16_t offset_mark_anchor = 0;
179 if (!subtable.ReadU16(&class_value) ||
180 !subtable.ReadU16(&offset_mark_anchor)) {
181 return OTS_FAILURE();
182 }
183 // |class_value| may take arbitrary values including 0 here so we don't
184 // check the value.
185 if (offset_mark_anchor < mark_records_end ||
186 offset_mark_anchor >= length) {
187 return OTS_FAILURE();
188 }
189 if (!ParseAnchorTable(data + offset_mark_anchor,
190 length - offset_mark_anchor)) {
191 return OTS_FAILURE();
192 }
193 }
194
195 return true;
196}
197
198// Lookup Type 1:
199// Single Adjustment Positioning Subtable
200bool ParseSingleAdjustment(const ots::OpenTypeFile *file, const uint8_t *data,
201 const size_t length) {
202 ots::Buffer subtable(data, length);
203
204 uint16_t format = 0;
205 uint16_t offset_coverage = 0;
206 uint16_t value_format = 0;
207 if (!subtable.ReadU16(&format) ||
208 !subtable.ReadU16(&offset_coverage) ||
209 !subtable.ReadU16(&value_format)) {
210 return OTS_FAILURE();
211 }
212
213 if (format == 1) {
214 // Format 1 exactly one value record.
215 if (!ParseValueRecord(&subtable, data, length, value_format)) {
216 return OTS_FAILURE();
217 }
218 } else if (format == 2) {
219 uint16_t value_count = 0;
220 if (!subtable.ReadU16(&value_count)) {
221 return OTS_FAILURE();
222 }
223 for (unsigned i = 0; i < value_count; ++i) {
224 if (!ParseValueRecord(&subtable, data, length, value_format)) {
225 return OTS_FAILURE();
226 }
227 }
228 } else {
229 return OTS_FAILURE();
230 }
231
232 if (offset_coverage < subtable.offset() || offset_coverage >= length) {
233 return OTS_FAILURE();
234 }
235
236 if (!ots::ParseCoverageTable(data + offset_coverage,
237 length - offset_coverage,
238 file->maxp->num_glyphs)) {
239 return OTS_FAILURE();
240 }
241
242 return true;
243}
244
245bool ParsePairSetTable(const uint8_t *data, const size_t length,
246 const uint16_t value_format1,
247 const uint16_t value_format2,
248 const uint16_t num_glyphs) {
249 ots::Buffer subtable(data, length);
250
251 uint16_t value_count = 0;
252 if (!subtable.ReadU16(&value_count)) {
253 return OTS_FAILURE();
254 }
255 for (unsigned i = 0; i < value_count; ++i) {
256 // Check pair value record.
257 uint16_t glyph_id = 0;
258 if (!subtable.ReadU16(&glyph_id)) {
259 return OTS_FAILURE();
260 }
261 if (glyph_id >= num_glyphs) {
262 return OTS_FAILURE();
263 }
264 if (!ParseValueRecord(&subtable, data, length, value_format1)) {
265 return OTS_FAILURE();
266 }
267 if (!ParseValueRecord(&subtable, data, length, value_format2)) {
268 return OTS_FAILURE();
269 }
270 }
271 return true;
272}
273
274bool ParsePairPosFormat1(const uint8_t *data, const size_t length,
275 const uint16_t value_format1,
276 const uint16_t value_format2,
277 const uint16_t num_glyphs) {
278 ots::Buffer subtable(data, length);
279
280 // Skip 8 bytes that are already read before.
281 if (!subtable.Skip(8)) {
282 return OTS_FAILURE();
283 }
284
285 uint16_t pair_set_count = 0;
286 if (!subtable.ReadU16(&pair_set_count)) {
287 return OTS_FAILURE();
288 }
289
290 const unsigned pair_pos_end = static_cast<unsigned>(10) +
291 pair_set_count * 2;
292 if (pair_pos_end > std::numeric_limits<uint16_t>::max()) {
293 return OTS_FAILURE();
294 }
295 for (unsigned i = 0; i < pair_set_count; ++i) {
296 uint16_t pair_set_offset = 0;
297 if (!subtable.ReadU16(&pair_set_offset)) {
298 return OTS_FAILURE();
299 }
300 if (pair_set_offset < pair_pos_end || pair_set_offset >= length) {
301 return OTS_FAILURE();
302 }
303 // Check pair set tables
304 if (!ParsePairSetTable(data + pair_set_offset, length - pair_set_offset,
305 value_format1, value_format2,
306 num_glyphs)) {
307 return OTS_FAILURE();
308 }
309 }
310
311 return true;
312}
313
314bool ParsePairPosFormat2(const uint8_t *data, const size_t length,
315 const uint16_t value_format1,
316 const uint16_t value_format2,
317 const uint16_t num_glyphs) {
318 ots::Buffer subtable(data, length);
319
320 // Skip 8 bytes that are already read before.
321 if (!subtable.Skip(8)) {
322 return OTS_FAILURE();
323 }
324
325 uint16_t offset_class_def1 = 0;
326 uint16_t offset_class_def2 = 0;
327 uint16_t class1_count = 0;
328 uint16_t class2_count = 0;
329 if (!subtable.ReadU16(&offset_class_def1) ||
330 !subtable.ReadU16(&offset_class_def2) ||
331 !subtable.ReadU16(&class1_count) ||
332 !subtable.ReadU16(&class2_count)) {
333 return OTS_FAILURE();
334 }
335
336 // Check class 1 records.
337 for (unsigned i = 0; i < class1_count; ++i) {
338 // Check class 2 records.
339 for (unsigned j = 0; j < class2_count; ++j) {
340 if (value_format1 && !ParseValueRecord(&subtable, data, length,
341 value_format1)) {
342 return OTS_FAILURE();
343 }
344 if (value_format2 && !ParseValueRecord(&subtable, data, length,
345 value_format2)) {
346 return OTS_FAILURE();
347 }
348 }
349 }
350
351 // Check class definition tables.
352 if (offset_class_def1 < subtable.offset() || offset_class_def1 >= length ||
353 offset_class_def2 < subtable.offset() || offset_class_def2 >= length) {
354 return OTS_FAILURE();
355 }
356 if (!ots::ParseClassDefTable(data + offset_class_def1,
357 length - offset_class_def1,
358 num_glyphs, kMaxClassDefValue)) {
359 return OTS_FAILURE();
360 }
361 if (!ots::ParseClassDefTable(data + offset_class_def2,
362 length - offset_class_def2,
363 num_glyphs, kMaxClassDefValue)) {
364 return OTS_FAILURE();
365 }
366
367 return true;
368}
369
370// Lookup Type 2:
371// Pair Adjustment Positioning Subtable
372bool ParsePairAdjustment(const ots::OpenTypeFile *file, const uint8_t *data,
373 const size_t length) {
374 ots::Buffer subtable(data, length);
375
376 uint16_t format = 0;
377 uint16_t offset_coverage = 0;
378 uint16_t value_format1 = 0;
379 uint16_t value_format2 = 0;
380 if (!subtable.ReadU16(&format) ||
381 !subtable.ReadU16(&offset_coverage) ||
382 !subtable.ReadU16(&value_format1) ||
383 !subtable.ReadU16(&value_format2)) {
384 return OTS_FAILURE();
385 }
386
387 if (format == 1) {
388 if (!ParsePairPosFormat1(data, length, value_format1, value_format2,
389 file->maxp->num_glyphs)) {
390 return OTS_FAILURE();
391 }
392 } else if (format == 2) {
393 if (!ParsePairPosFormat2(data, length, value_format1, value_format2,
394 file->maxp->num_glyphs)) {
395 return OTS_FAILURE();
396 }
397 } else {
398 return OTS_FAILURE();
399 }
400
401 if (offset_coverage < subtable.offset() || offset_coverage >= length) {
402 return OTS_FAILURE();
403 }
404 if (!ots::ParseCoverageTable(data + offset_coverage,
405 length - offset_coverage,
406 file->maxp->num_glyphs)) {
407 return OTS_FAILURE();
408 }
409
410 return true;
411}
412
413// Lookup Type 3
414// Cursive Attachment Positioning Subtable
415bool ParseCursiveAttachment(const ots::OpenTypeFile *file, const uint8_t *data,
416 const size_t length) {
417 ots::Buffer subtable(data, length);
418
419 uint16_t format = 0;
420 uint16_t offset_coverage = 0;
421 uint16_t entry_exit_count = 0;
422 if (!subtable.ReadU16(&format) ||
423 !subtable.ReadU16(&offset_coverage) ||
424 !subtable.ReadU16(&entry_exit_count)) {
425 return OTS_FAILURE();
426 }
427
428 if (format != 1) {
429 return OTS_FAILURE();
430 }
431
432 // Check entry exit records.
433 const unsigned entry_exit_records_end = static_cast<unsigned>(6) +
434 entry_exit_count * 2;
435 if (entry_exit_records_end > std::numeric_limits<uint16_t>::max()) {
436 return OTS_FAILURE();
437 }
438 for (unsigned i = 0; i < entry_exit_count; ++i) {
439 uint16_t offset_entry_anchor = 0;
440 uint16_t offset_exit_anchor = 0;
441 if (!subtable.ReadU16(&offset_entry_anchor) ||
442 !subtable.ReadU16(&offset_exit_anchor)) {
443 return OTS_FAILURE();
444 }
445 // These offsets could be NULL.
446 if (offset_entry_anchor) {
447 if (offset_entry_anchor < entry_exit_records_end ||
448 offset_entry_anchor >= length) {
449 return OTS_FAILURE();
450 }
451 if (!ParseAnchorTable(data + offset_entry_anchor,
452 length - offset_entry_anchor)) {
453 return OTS_FAILURE();
454 }
455 }
456 if (offset_exit_anchor) {
457 if (offset_exit_anchor < entry_exit_records_end ||
458 offset_exit_anchor >= length) {
459 return OTS_FAILURE();
460 }
461 if (!ParseAnchorTable(data + offset_exit_anchor,
462 length - offset_exit_anchor)) {
463 return OTS_FAILURE();
464 }
465 }
466 }
467
468 if (offset_coverage < subtable.offset() || offset_coverage >= length) {
469 return OTS_FAILURE();
470 }
471 if (!ots::ParseCoverageTable(data + offset_coverage,
472 length - offset_coverage,
473 file->maxp->num_glyphs)) {
474 return OTS_FAILURE();
475 }
476
477 return true;
478}
479
480bool ParseAnchorArrayTable(const uint8_t *data, const size_t length,
481 const uint16_t class_count) {
482 ots::Buffer subtable(data, length);
483
484 uint16_t record_count = 0;
485 if (!subtable.ReadU16(&record_count)) {
486 return OTS_FAILURE();
487 }
488
489 const unsigned anchor_array_end = static_cast<unsigned>(2) +
490 record_count * class_count * 2;
491 if (anchor_array_end > std::numeric_limits<uint16_t>::max()) {
492 return OTS_FAILURE();
493 }
494 for (unsigned i = 0; i < record_count; ++i) {
495 for (unsigned j = 0; j < class_count; ++j) {
496 uint16_t offset_record = 0;
497 if (!subtable.ReadU16(&offset_record)) {
498 return OTS_FAILURE();
499 }
500 // |offset_record| could be NULL.
501 if (offset_record) {
502 if (offset_record < anchor_array_end || offset_record >= length) {
503 return OTS_FAILURE();
504 }
505 if (!ParseAnchorTable(data + offset_record,
506 length - offset_record)) {
507 return OTS_FAILURE();
508 }
509 }
510 }
511 }
512 return true;
513}
514
515bool ParseLigatureArrayTable(const uint8_t *data, const size_t length,
516 const uint16_t class_count) {
517 ots::Buffer subtable(data, length);
518
519 uint16_t ligature_count = 0;
520 if (!subtable.ReadU16(&ligature_count)) {
521 return OTS_FAILURE();
522 }
523 for (unsigned i = 0; i < ligature_count; ++i) {
524 uint16_t offset_ligature_attach = 0;
525 if (!subtable.ReadU16(&offset_ligature_attach)) {
526 return OTS_FAILURE();
527 }
528 if (offset_ligature_attach < 2 || offset_ligature_attach >= length) {
529 return OTS_FAILURE();
530 }
531 if (!ParseAnchorArrayTable(data + offset_ligature_attach,
532 length - offset_ligature_attach, class_count)) {
533 return OTS_FAILURE();
534 }
535 }
536 return true;
537}
538
539// Common parser for Lookup Type 4, 5 and 6.
540bool ParseMarkToAttachmentSubtables(const ots::OpenTypeFile *file,
541 const uint8_t *data, const size_t length,
542 const GPOS_TYPE type) {
543 ots::Buffer subtable(data, length);
544
545 uint16_t format = 0;
546 uint16_t offset_coverage1 = 0;
547 uint16_t offset_coverage2 = 0;
548 uint16_t class_count = 0;
549 uint16_t offset_mark_array = 0;
550 uint16_t offset_type_specific_array = 0;
551 if (!subtable.ReadU16(&format) ||
552 !subtable.ReadU16(&offset_coverage1) ||
553 !subtable.ReadU16(&offset_coverage2) ||
554 !subtable.ReadU16(&class_count) ||
555 !subtable.ReadU16(&offset_mark_array) ||
556 !subtable.ReadU16(&offset_type_specific_array)) {
557 return OTS_FAILURE();
558 }
559
560 if (format != 1) {
561 return OTS_FAILURE();
562 }
563
564 const unsigned header_end = static_cast<unsigned>(subtable.offset());
565 if (header_end > std::numeric_limits<uint16_t>::max()) {
566 return OTS_FAILURE();
567 }
568 if (offset_coverage1 < header_end || offset_coverage1 >= length) {
569 return OTS_FAILURE();
570 }
571 if (!ots::ParseCoverageTable(data + offset_coverage1,
572 length - offset_coverage1,
573 file->maxp->num_glyphs)) {
574 return OTS_FAILURE();
575 }
576 if (offset_coverage2 < header_end || offset_coverage2 >= length) {
577 return OTS_FAILURE();
578 }
579 if (!ots::ParseCoverageTable(data + offset_coverage2,
580 length - offset_coverage2,
581 file->maxp->num_glyphs)) {
582 return OTS_FAILURE();
583 }
584
585 if (offset_mark_array < header_end || offset_mark_array >= length) {
586 return OTS_FAILURE();
587 }
588 if (!ParseMarkArrayTable(data + offset_mark_array,
589 length - offset_mark_array, class_count)) {
590 return OTS_FAILURE();
591 }
592
593 if (offset_type_specific_array < header_end ||
594 offset_type_specific_array >= length) {
595 return OTS_FAILURE();
596 }
597 if (type == MARK_TO_BASE_ATTACHMENT || type == MARK_TO_MARK_ATTACHMENT) {
598 if (!ParseAnchorArrayTable(data + offset_type_specific_array,
599 length - offset_type_specific_array,
600 class_count)) {
601 return OTS_FAILURE();
602 }
603 } else if (type == MARK_TO_LIGATURE_ATTACHMENT) {
604 if (!ParseLigatureArrayTable(data + offset_type_specific_array,
605 length - offset_type_specific_array,
606 class_count)) {
607 return OTS_FAILURE();
608 }
609 } else {
610 return OTS_FAILURE();
611 }
612
613 return true;
614}
615
616// Lookup Type 4:
617// MarkToBase Attachment Positioning Subtable
618bool ParseMarkToBaseAttachment(const ots::OpenTypeFile *file,
619 const uint8_t *data, const size_t length) {
620 return ParseMarkToAttachmentSubtables(file, data, length,
621 MARK_TO_BASE_ATTACHMENT);
622}
623
624// Lookup Type 5:
625// MarkToLigature Attachment Positioning Subtable
626bool ParseMarkToLigatureAttachment(const ots::OpenTypeFile *file,
627 const uint8_t *data, const size_t length) {
628 return ParseMarkToAttachmentSubtables(file, data, length,
629 MARK_TO_LIGATURE_ATTACHMENT);
630}
631
632// Lookup Type 6:
633// MarkToMark Attachment Positioning Subtable
634bool ParseMarkToMarkAttachment(const ots::OpenTypeFile *file,
635 const uint8_t *data, const size_t length) {
636 return ParseMarkToAttachmentSubtables(file, data, length,
637 MARK_TO_MARK_ATTACHMENT);
638}
639
640// Lookup Type 7:
641// Contextual Positioning Subtables
642bool ParseContextPositioning(const ots::OpenTypeFile *file,
643 const uint8_t *data, const size_t length) {
644 return ots::ParseContextSubtable(data, length, file->maxp->num_glyphs,
645 file->gpos->num_lookups);
646}
647
648// Lookup Type 8:
649// Chaining Contexual Positioning Subtable
650bool ParseChainedContextPositioning(const ots::OpenTypeFile *file,
651 const uint8_t *data, const size_t length) {
652 return ots::ParseChainingContextSubtable(data, length,
653 file->maxp->num_glyphs,
654 file->gpos->num_lookups);
655}
656
657// Lookup Type 9:
658// Extension Positioning
659bool ParseExtensionPositioning(const ots::OpenTypeFile *file,
660 const uint8_t *data, const size_t length) {
661 return ots::ParseExtensionSubtable(file, data, length,
662 &kGposLookupSubtableParser);
663}
664
665} // namespace
666
667#define DROP_THIS_TABLE \
668 do { delete file->gpos; file->gpos = 0; } while (0)
669
670namespace ots {
671
672// As far as I checked, following fonts contain invalid GPOS table and
673// OTS will drop their GPOS table.
674//
675// # invalid delta format in device table
676// samanata.ttf
677//
678// # bad size range in device table
679// Sarai_07.ttf
680//
681// # bad offset to PairSetTable
682// chandas1-2.ttf
683//
684// # bad offset to FeatureTable
685// glrso12.ttf
686// gllr12.ttf
687// glbo12.ttf
688// glb12.ttf
689// glro12.ttf
690// glbso12.ttf
691// glrc12.ttf
692// glrsc12.ttf
693// glbs12.ttf
694// glrs12.ttf
695// glr12.ttf
696//
697// # ScriptRecords aren't sorted by tag
698// Garogier_unhinted.otf
699//
700// # bad start coverage index in CoverageFormat2
701// AndBasR.ttf
702// CharisSILB.ttf
703// CharisSILBI.ttf
704// CharisSILI.ttf
705// CharisSILR.ttf
706// DoulosSILR.ttf
707// GenBasBI.ttf
708// GenBasI.ttf
709// GenBkBasI.ttf
710// GenBkBasB.ttf
711// GenBkBasR.ttf
712// Padauk-Bold.ttf
713// Padauk.ttf
714//
715// # Contour point indexes aren't sorted
716// Arial Unicode.ttf
717
718bool ots_gpos_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
719 // Parsing GPOS table requires num_glyphs which is contained in maxp table.
720 if (!file->maxp) {
721 return OTS_FAILURE();
722 }
723
724 Buffer table(data, length);
725
726 OpenTypeGPOS *gpos = new OpenTypeGPOS;
727 file->gpos = gpos;
728
729 uint32_t version = 0;
730 uint16_t offset_script_list = 0;
731 uint16_t offset_feature_list = 0;
732 uint16_t offset_lookup_list = 0;
733 if (!table.ReadU32(&version) ||
734 !table.ReadU16(&offset_script_list) ||
735 !table.ReadU16(&offset_feature_list) ||
736 !table.ReadU16(&offset_lookup_list)) {
737 return OTS_FAILURE();
738 }
739
740 if (version != 0x00010000) {
741 OTS_WARNING("bad GPOS version");
742 DROP_THIS_TABLE;
743 return true;
744 }
745 if ((offset_script_list < kGposHeaderSize ||
746 offset_script_list >= length) ||
747 (offset_feature_list < kGposHeaderSize ||
748 offset_feature_list >= length) ||
749 (offset_lookup_list < kGposHeaderSize ||
750 offset_lookup_list >= length)) {
751 OTS_WARNING("bad offset in GPOS header");
752 DROP_THIS_TABLE;
753 return true;
754 }
755
756 if (!ParseLookupListTable(file, data + offset_lookup_list,
757 length - offset_lookup_list,
758 &kGposLookupSubtableParser,
759 &gpos->num_lookups)) {
760 OTS_WARNING("faild to parse lookup list table");
761 DROP_THIS_TABLE;
762 return true;
763 }
764
765 uint16_t num_features = 0;
766 if (!ParseFeatureListTable(data + offset_feature_list,
767 length - offset_feature_list, gpos->num_lookups,
768 &num_features)) {
769 OTS_WARNING("faild to parse feature list table");
770 DROP_THIS_TABLE;
771 return true;
772 }
773
774 if (!ParseScriptListTable(data + offset_script_list,
775 length - offset_script_list, num_features)) {
776 OTS_WARNING("faild to parse script list table");
777 DROP_THIS_TABLE;
778 return true;
779 }
780
781 gpos->data = data;
782 gpos->length = length;
783 return true;
784}
785
786bool ots_gpos_should_serialise(OpenTypeFile *file) {
787 return file->gpos != NULL;
788}
789
790bool ots_gpos_serialise(OTSStream *out, OpenTypeFile *file) {
791 if (!out->Write(file->gpos->data, file->gpos->length)) {
792 return OTS_FAILURE();
793 }
794
795 return true;
796}
797
798void ots_gpos_free(OpenTypeFile *file) {
799 delete file->gpos;
800}
801
802} // namespace ots
803