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