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