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