Line | Count | Source |
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 maximum format number for anchor tables. |
34 | | const uint16_t kMaxAnchorFormat = 3; |
35 | | |
36 | | // Shared Tables: ValueRecord, Anchor Table, and MarkArray |
37 | | |
38 | 2.64k | size_t CalcValueRecordSize(const uint16_t value_format) { |
39 | 2.64k | size_t size = 0; |
40 | 23.7k | for (unsigned i = 0; i < 8; ++i) { |
41 | 21.1k | if ((value_format >> i) & 0x1) { |
42 | 5.65k | size += 2; |
43 | 5.65k | } |
44 | 21.1k | } |
45 | | |
46 | 2.64k | return size; |
47 | 2.64k | } |
48 | | |
49 | | bool ParseValueRecord(const ots::Font *font, |
50 | | ots::Buffer* subtable, |
51 | 10.3M | const uint16_t value_format) { |
52 | 10.3M | const uint8_t *data = subtable->buffer(); |
53 | 10.3M | const size_t length = subtable->length(); |
54 | | |
55 | | // Check existence of adjustment fields. |
56 | 51.6M | for (unsigned i = 0; i < 4; ++i) { |
57 | 41.3M | if ((value_format >> i) & 0x1) { |
58 | | // Just read the field since these fileds could take an arbitrary values. |
59 | 231k | if (!subtable->Skip(2)) { |
60 | 45 | return OTS_FAILURE_MSG("Failed to read value reacord component"); |
61 | 45 | } |
62 | 231k | } |
63 | 41.3M | } |
64 | | |
65 | | // Check existence of offsets to device table. |
66 | 51.6M | for (unsigned i = 0; i < 4; ++i) { |
67 | 41.3M | if ((value_format >> (i + 4)) & 0x1) { |
68 | 44.8k | uint16_t offset = 0; |
69 | 44.8k | if (!subtable->ReadU16(&offset)) { |
70 | 45 | return OTS_FAILURE_MSG("Failed to read value record offset"); |
71 | 45 | } |
72 | 44.7k | if (offset) { |
73 | | // TODO(bashi): Is it possible that device tables locate before |
74 | | // this record? No fonts contain such offset AKAIF. |
75 | 41.3k | if (offset >= length) { |
76 | 135 | return OTS_FAILURE_MSG("Value record offset too high %d >= %ld", offset, length); |
77 | 135 | } |
78 | 41.2k | if (!ots::ParseDeviceTable(font, data + offset, length - offset)) { |
79 | 175 | return OTS_FAILURE_MSG("Failed to parse device table in value record"); |
80 | 175 | } |
81 | 41.2k | } |
82 | 44.7k | } |
83 | 41.3M | } |
84 | 10.3M | return true; |
85 | 10.3M | } |
86 | | |
87 | | bool ParseAnchorTable(const ots::Font *font, |
88 | 110k | const uint8_t *data, const size_t length) { |
89 | 110k | ots::Buffer subtable(data, length); |
90 | | |
91 | 110k | uint16_t format = 0; |
92 | | // Read format and skip 2 2-byte fields that could be arbitrary values. |
93 | 110k | if (!subtable.ReadU16(&format) || |
94 | 110k | !subtable.Skip(4)) { |
95 | 63 | return OTS_FAILURE_MSG("Faled to read anchor table"); |
96 | 63 | } |
97 | | |
98 | 110k | if (format == 0 || format > kMaxAnchorFormat) { |
99 | 143 | return OTS_FAILURE_MSG("Bad Anchor table format %d", format); |
100 | 143 | } |
101 | | |
102 | | // Format 2 and 3 has additional fields. |
103 | 109k | if (format == 2) { |
104 | | // Format 2 provides an index to a glyph contour point, which will take |
105 | | // arbitrary value. |
106 | 310 | uint16_t anchor_point = 0; |
107 | 310 | if (!subtable.ReadU16(&anchor_point)) { |
108 | 32 | return OTS_FAILURE_MSG("Failed to read anchor point in format 2 Anchor Table"); |
109 | 32 | } |
110 | 109k | } else if (format == 3) { |
111 | 9.49k | uint16_t offset_x_device = 0; |
112 | 9.49k | uint16_t offset_y_device = 0; |
113 | 9.49k | if (!subtable.ReadU16(&offset_x_device) || |
114 | 9.45k | !subtable.ReadU16(&offset_y_device)) { |
115 | 39 | return OTS_FAILURE_MSG("Failed to read device table offsets in format 3 anchor table"); |
116 | 39 | } |
117 | 9.45k | const unsigned format_end = static_cast<unsigned>(10); |
118 | 9.45k | if (offset_x_device) { |
119 | 9.04k | if (offset_x_device < format_end || offset_x_device >= length) { |
120 | 80 | return OTS_FAILURE_MSG("Bad x device table offset %d", offset_x_device); |
121 | 80 | } |
122 | 8.96k | if (!ots::ParseDeviceTable(font, data + offset_x_device, |
123 | 8.96k | length - offset_x_device)) { |
124 | 42 | return OTS_FAILURE_MSG("Failed to parse device table in anchor table"); |
125 | 42 | } |
126 | 8.96k | } |
127 | 9.33k | if (offset_y_device) { |
128 | 3.47k | if (offset_y_device < format_end || offset_y_device >= length) { |
129 | 80 | return OTS_FAILURE_MSG("Bad y device table offset %d", offset_y_device); |
130 | 80 | } |
131 | 3.39k | if (!ots::ParseDeviceTable(font, data + offset_y_device, |
132 | 3.39k | length - offset_y_device)) { |
133 | 41 | return OTS_FAILURE_MSG("Failed to parse device table in anchor table"); |
134 | 41 | } |
135 | 3.39k | } |
136 | 9.33k | } |
137 | 109k | return true; |
138 | 109k | } |
139 | | |
140 | | bool ParseMarkArrayTable(const ots::Font *font, |
141 | 1.43k | const uint8_t *data, const size_t length) { |
142 | 1.43k | ots::Buffer subtable(data, length); |
143 | | |
144 | 1.43k | uint16_t mark_count = 0; |
145 | 1.43k | if (!subtable.ReadU16(&mark_count)) { |
146 | 15 | return OTS_FAILURE_MSG("Can't read mark table length"); |
147 | 15 | } |
148 | | |
149 | | // MarkRecord consists of 4-bytes. |
150 | 1.42k | const unsigned mark_records_end = 4 * static_cast<unsigned>(mark_count) + 2; |
151 | 1.42k | if (mark_records_end > std::numeric_limits<uint16_t>::max()) { |
152 | 56 | return OTS_FAILURE_MSG("Bad mark table length"); |
153 | 56 | } |
154 | 9.87k | for (unsigned i = 0; i < mark_count; ++i) { |
155 | 8.75k | uint16_t class_value = 0; |
156 | 8.75k | uint16_t offset_mark_anchor = 0; |
157 | 8.75k | if (!subtable.ReadU16(&class_value) || |
158 | 8.72k | !subtable.ReadU16(&offset_mark_anchor)) { |
159 | 45 | return OTS_FAILURE_MSG("Can't read mark table %d", i); |
160 | 45 | } |
161 | | // |class_value| may take arbitrary values including 0 here so we don't |
162 | | // check the value. |
163 | 8.70k | if (offset_mark_anchor < mark_records_end || |
164 | 8.64k | offset_mark_anchor >= length) { |
165 | 125 | return OTS_FAILURE_MSG("Bad mark anchor offset %d for mark table %d", offset_mark_anchor, i); |
166 | 125 | } |
167 | 8.58k | if (!ParseAnchorTable(font, data + offset_mark_anchor, |
168 | 8.58k | length - offset_mark_anchor)) { |
169 | 71 | return OTS_FAILURE_MSG("Faled to parse anchor table for mark table %d", i); |
170 | 71 | } |
171 | 8.58k | } |
172 | | |
173 | 1.12k | return true; |
174 | 1.36k | } |
175 | | |
176 | | bool ParsePairSetTable(const ots::Font *font, |
177 | | const uint8_t *data, const size_t length, |
178 | | const uint16_t value_format1, |
179 | | const uint16_t value_format2, |
180 | 11.1k | const uint16_t num_glyphs) { |
181 | 11.1k | ots::Buffer subtable(data, length); |
182 | | |
183 | 11.1k | uint16_t value_count = 0; |
184 | 11.1k | if (!subtable.ReadU16(&value_count)) { |
185 | 9 | return OTS_FAILURE_MSG("Failed to read pair set table structure"); |
186 | 9 | } |
187 | 217k | for (unsigned i = 0; i < value_count; ++i) { |
188 | | // Check pair value record. |
189 | 206k | uint16_t glyph_id = 0; |
190 | 206k | if (!subtable.ReadU16(&glyph_id)) { |
191 | 38 | return OTS_FAILURE_MSG("Failed to read glyph in pair value record %d", i); |
192 | 38 | } |
193 | 206k | if (glyph_id >= num_glyphs) { |
194 | 49 | return OTS_FAILURE_MSG("glyph id %d too high >= %d", glyph_id, num_glyphs); |
195 | 49 | } |
196 | 206k | if (!ParseValueRecord(font, &subtable, value_format1)) { |
197 | 121 | return OTS_FAILURE_MSG("Failed to parse value record in format 1 pair set table"); |
198 | 121 | } |
199 | 206k | if (!ParseValueRecord(font, &subtable, value_format2)) { |
200 | 100 | return OTS_FAILURE_MSG("Failed to parse value record in format 2 pair set table"); |
201 | 100 | } |
202 | 206k | } |
203 | 10.8k | return true; |
204 | 11.1k | } |
205 | | |
206 | | bool ParsePairPosFormat1(const ots::Font *font, |
207 | | const uint8_t *data, const size_t length, |
208 | | const uint16_t value_format1, |
209 | | const uint16_t value_format2, |
210 | 727 | const uint16_t num_glyphs) { |
211 | 727 | ots::Buffer subtable(data, length); |
212 | | |
213 | | // Skip 8 bytes that are already read before. |
214 | 727 | if (!subtable.Skip(8)) { |
215 | 0 | return OTS_FAILURE_MSG("Failed to read pair pos table structure"); |
216 | 0 | } |
217 | | |
218 | 727 | uint16_t pair_set_count = 0; |
219 | 727 | if (!subtable.ReadU16(&pair_set_count)) { |
220 | 26 | return OTS_FAILURE_MSG("Failed to read pair pos set count"); |
221 | 26 | } |
222 | | |
223 | 701 | const unsigned pair_pos_end = 2 * static_cast<unsigned>(pair_set_count) + 10; |
224 | 701 | if (pair_pos_end > std::numeric_limits<uint16_t>::max()) { |
225 | 35 | return OTS_FAILURE_MSG("Bad pair set length %d", pair_pos_end); |
226 | 35 | } |
227 | 11.5k | for (unsigned i = 0; i < pair_set_count; ++i) { |
228 | 11.2k | uint16_t pair_set_offset = 0; |
229 | 11.2k | if (!subtable.ReadU16(&pair_set_offset)) { |
230 | 28 | return OTS_FAILURE_MSG("Failed to read pair set offset for pair set %d", i); |
231 | 28 | } |
232 | 11.2k | if (pair_set_offset < pair_pos_end || pair_set_offset >= length) { |
233 | 100 | return OTS_FAILURE_MSG("Bad pair set offset %d for pair set %d", pair_set_offset, i); |
234 | 100 | } |
235 | | // Check pair set tables |
236 | 11.1k | if (!ParsePairSetTable(font, data + pair_set_offset, length - pair_set_offset, |
237 | 11.1k | value_format1, value_format2, |
238 | 11.1k | num_glyphs)) { |
239 | 317 | return OTS_FAILURE_MSG("Failed to parse pair set table %d", i); |
240 | 317 | } |
241 | 11.1k | } |
242 | | |
243 | 221 | return true; |
244 | 666 | } |
245 | | |
246 | | bool ParsePairPosFormat2(const ots::Font *font, |
247 | | const uint8_t *data, const size_t length, |
248 | | const uint16_t value_format1, |
249 | | const uint16_t value_format2, |
250 | 1.40k | const uint16_t num_glyphs) { |
251 | 1.40k | ots::Buffer subtable(data, length); |
252 | | |
253 | | // Skip 8 bytes that are already read before. |
254 | 1.40k | if (!subtable.Skip(8)) { |
255 | 0 | return OTS_FAILURE_MSG("Failed to read pair pos format 2 structure"); |
256 | 0 | } |
257 | | |
258 | 1.40k | uint16_t offset_class_def1 = 0; |
259 | 1.40k | uint16_t offset_class_def2 = 0; |
260 | 1.40k | uint16_t class1_count = 0; |
261 | 1.40k | uint16_t class2_count = 0; |
262 | 1.40k | if (!subtable.ReadU16(&offset_class_def1) || |
263 | 1.38k | !subtable.ReadU16(&offset_class_def2) || |
264 | 1.35k | !subtable.ReadU16(&class1_count) || |
265 | 1.33k | !subtable.ReadU16(&class2_count)) { |
266 | 84 | return OTS_FAILURE_MSG("Failed to read pair pos format 2 data"); |
267 | 84 | } |
268 | | |
269 | 1.32k | size_t value_record1_size = CalcValueRecordSize(value_format1); |
270 | 1.32k | size_t value_record2_size = CalcValueRecordSize(value_format2); |
271 | 1.32k | size_t value_records_size = size_t(class1_count) * size_t(class2_count) * |
272 | 1.32k | (value_record1_size + value_record2_size); |
273 | | |
274 | | // Check the validity of class definition offsets. |
275 | 1.32k | if (offset_class_def1 < subtable.offset() + value_records_size || |
276 | 1.26k | offset_class_def2 < subtable.offset() + value_records_size || |
277 | 1.23k | offset_class_def1 >= length || offset_class_def2 >= length) { |
278 | 125 | return OTS_FAILURE_MSG("Bad ParsePairPosFormat2 class definition offsets %d or %d", offset_class_def1, offset_class_def2); |
279 | 125 | } |
280 | | |
281 | | // Check class 1 records. |
282 | 1.19k | if (value_record1_size || value_record2_size) { |
283 | 1.00M | for (unsigned i = 0; i < class1_count; ++i) { |
284 | | // Check class 2 records. |
285 | 1.17M | for (unsigned j = 0; j < class2_count; ++j) { |
286 | 177k | if (value_format1 && value_record2_size && |
287 | 913 | !ParseValueRecord(font, &subtable, value_format1)) { |
288 | 35 | return OTS_FAILURE_MSG("Failed to parse value record 1 %d and %d", j, i); |
289 | 35 | } |
290 | 177k | if (value_format2 && value_record2_size && |
291 | 972 | !ParseValueRecord(font, &subtable, value_format2)) { |
292 | 32 | return OTS_FAILURE_MSG("Falied to parse value record 2 %d and %d", j, i); |
293 | 32 | } |
294 | 177k | } |
295 | 1.00M | } |
296 | 1.01k | } |
297 | | |
298 | | // Check class definition tables. |
299 | 1.12k | if (!ots::ParseClassDefTable(font, data + offset_class_def1, |
300 | 1.12k | length - offset_class_def1, |
301 | 1.12k | num_glyphs, ots::kMaxClassDefValue)) { |
302 | 60 | return OTS_FAILURE_MSG("Failed to parse class definition table 1"); |
303 | 60 | } |
304 | 1.06k | if (!ots::ParseClassDefTable(font, data + offset_class_def2, |
305 | 1.06k | length - offset_class_def2, |
306 | 1.06k | num_glyphs, ots::kMaxClassDefValue)) { |
307 | 60 | return OTS_FAILURE_MSG("Failed to parse class definition table 2"); |
308 | 60 | } |
309 | | |
310 | 1.00k | return true; |
311 | 1.06k | } |
312 | | |
313 | | bool ParseAnchorArrayTable(const ots::Font *font, |
314 | | const uint8_t *data, const size_t length, |
315 | 2.96k | const uint16_t class_count) { |
316 | 2.96k | ots::Buffer subtable(data, length); |
317 | | |
318 | 2.96k | uint16_t record_count = 0; |
319 | 2.96k | if (!subtable.ReadU16(&record_count)) { |
320 | 42 | return OTS_FAILURE_MSG("Can't read anchor array length"); |
321 | 42 | } |
322 | | |
323 | 2.92k | const unsigned anchor_array_end = 2 * static_cast<unsigned>(record_count) * |
324 | 2.92k | static_cast<unsigned>(class_count) + 2; |
325 | 2.92k | if (anchor_array_end > std::numeric_limits<uint16_t>::max()) { |
326 | 75 | return OTS_FAILURE_MSG("Bad end of anchor array %d", anchor_array_end); |
327 | 75 | } |
328 | 10.5M | for (unsigned i = 0; i < record_count; ++i) { |
329 | 10.6M | for (unsigned j = 0; j < class_count; ++j) { |
330 | 107k | uint16_t offset_record = 0; |
331 | 107k | if (!subtable.ReadU16(&offset_record)) { |
332 | 24 | return OTS_FAILURE_MSG("Can't read anchor array record offset for class %d and record %d", j, i); |
333 | 24 | } |
334 | | // |offset_record| could be NULL. |
335 | 107k | if (offset_record) { |
336 | 99.4k | if (offset_record < anchor_array_end || offset_record >= length) { |
337 | 98 | return OTS_FAILURE_MSG("Bad record offset %d in class %d, record %d", offset_record, j, i); |
338 | 98 | } |
339 | 99.3k | if (!ParseAnchorTable(font, data + offset_record, |
340 | 99.3k | length - offset_record)) { |
341 | 15 | return OTS_FAILURE_MSG("Failed to parse anchor table for class %d, record %d", j, i); |
342 | 15 | } |
343 | 99.3k | } |
344 | 107k | } |
345 | 10.5M | } |
346 | 2.70k | return true; |
347 | 2.84k | } |
348 | | |
349 | | bool ParseLigatureArrayTable(const ots::Font *font, |
350 | | const uint8_t *data, const size_t length, |
351 | 419 | const uint16_t class_count) { |
352 | 419 | ots::Buffer subtable(data, length); |
353 | | |
354 | 419 | uint16_t ligature_count = 0; |
355 | 419 | if (!subtable.ReadU16(&ligature_count)) { |
356 | 10 | return OTS_FAILURE_MSG("Failed to read ligature count"); |
357 | 10 | } |
358 | 2.57k | for (unsigned i = 0; i < ligature_count; ++i) { |
359 | 2.47k | uint16_t offset_ligature_attach = 0; |
360 | 2.47k | if (!subtable.ReadU16(&offset_ligature_attach)) { |
361 | 16 | return OTS_FAILURE_MSG("Can't read ligature offset %d", i); |
362 | 16 | } |
363 | 2.45k | if (offset_ligature_attach < 2 || offset_ligature_attach >= length) { |
364 | 131 | return OTS_FAILURE_MSG("Bad ligature attachment offset %d in ligature %d", offset_ligature_attach, i); |
365 | 131 | } |
366 | 2.32k | if (!ParseAnchorArrayTable(font, data + offset_ligature_attach, |
367 | 2.32k | length - offset_ligature_attach, class_count)) { |
368 | 162 | return OTS_FAILURE_MSG("Failed to parse anchor table for ligature %d", i); |
369 | 162 | } |
370 | 2.32k | } |
371 | 100 | return true; |
372 | 409 | } |
373 | | |
374 | | // Common parser for Lookup Type 4, 5 and 6. |
375 | | bool ParseMarkToAttachmentSubtables(const ots::Font *font, |
376 | | const uint8_t *data, const size_t length, |
377 | 1.95k | const GPOS_TYPE type) { |
378 | 1.95k | ots::Buffer subtable(data, length); |
379 | | |
380 | 1.95k | ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>( |
381 | 1.95k | font->GetTypedTable(OTS_TAG_MAXP)); |
382 | 1.95k | if (!maxp) { |
383 | 0 | return OTS_FAILURE_MSG("Required maxp table missing"); |
384 | 0 | } |
385 | | |
386 | 1.95k | uint16_t format = 0; |
387 | 1.95k | uint16_t offset_coverage1 = 0; |
388 | 1.95k | uint16_t offset_coverage2 = 0; |
389 | 1.95k | uint16_t class_count = 0; |
390 | 1.95k | uint16_t offset_mark_array = 0; |
391 | 1.95k | uint16_t offset_type_specific_array = 0; |
392 | 1.95k | if (!subtable.ReadU16(&format) || |
393 | 1.94k | !subtable.ReadU16(&offset_coverage1) || |
394 | 1.91k | !subtable.ReadU16(&offset_coverage2) || |
395 | 1.90k | !subtable.ReadU16(&class_count) || |
396 | 1.86k | !subtable.ReadU16(&offset_mark_array) || |
397 | 1.83k | !subtable.ReadU16(&offset_type_specific_array)) { |
398 | 129 | return OTS_FAILURE_MSG("Failed to read mark attachment subtable header"); |
399 | 129 | } |
400 | | |
401 | 1.82k | if (format != 1) { |
402 | 57 | return OTS_FAILURE_MSG("bad mark attachment subtable format %d", format); |
403 | 57 | } |
404 | | |
405 | 1.77k | const unsigned header_end = static_cast<unsigned>(subtable.offset()); |
406 | 1.77k | if (header_end > std::numeric_limits<uint16_t>::max()) { |
407 | 0 | return OTS_FAILURE_MSG("Bad mark attachment subtable size ending at %d", header_end); |
408 | 0 | } |
409 | 1.77k | if (offset_coverage1 < header_end || offset_coverage1 >= length) { |
410 | 70 | return OTS_FAILURE_MSG("Bad coverage 1 offset %d", offset_coverage1); |
411 | 70 | } |
412 | 1.70k | if (!ots::ParseCoverageTable(font, data + offset_coverage1, |
413 | 1.70k | length - offset_coverage1, |
414 | 1.70k | maxp->num_glyphs)) { |
415 | 63 | return OTS_FAILURE_MSG("Failed to parse converge 1 table"); |
416 | 63 | } |
417 | 1.63k | if (offset_coverage2 < header_end || offset_coverage2 >= length) { |
418 | 80 | return OTS_FAILURE_MSG("Bad coverage 2 offset %d", offset_coverage2); |
419 | 80 | } |
420 | 1.55k | if (!ots::ParseCoverageTable(font, data + offset_coverage2, |
421 | 1.55k | length - offset_coverage2, |
422 | 1.55k | maxp->num_glyphs)) { |
423 | 49 | return OTS_FAILURE_MSG("Failed to parse coverage table 2"); |
424 | 49 | } |
425 | | |
426 | 1.50k | if (offset_mark_array < header_end || offset_mark_array >= length) { |
427 | 71 | return OTS_FAILURE_MSG("Bad mark array offset %d", offset_mark_array); |
428 | 71 | } |
429 | 1.43k | if (!ParseMarkArrayTable(font, data + offset_mark_array, |
430 | 1.43k | length - offset_mark_array)) { |
431 | 312 | return OTS_FAILURE_MSG("Failed to parse mark array"); |
432 | 312 | } |
433 | | |
434 | 1.12k | if (offset_type_specific_array < header_end || |
435 | 1.08k | offset_type_specific_array >= length) { |
436 | 69 | return OTS_FAILURE_MSG("Bad type specific array offset %d", offset_type_specific_array); |
437 | 69 | } |
438 | 1.05k | if (type == GPOS_TYPE_MARK_TO_BASE_ATTACHMENT || |
439 | 667 | type == GPOS_TYPE_MARK_TO_MARK_ATTACHMENT) { |
440 | 637 | if (!ParseAnchorArrayTable(font, data + offset_type_specific_array, |
441 | 637 | length - offset_type_specific_array, |
442 | 637 | class_count)) { |
443 | 92 | return OTS_FAILURE_MSG("Failed to parse anchor array"); |
444 | 92 | } |
445 | 637 | } else if (type == GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT) { |
446 | 419 | if (!ParseLigatureArrayTable(font, data + offset_type_specific_array, |
447 | 419 | length - offset_type_specific_array, |
448 | 419 | class_count)) { |
449 | 319 | return OTS_FAILURE_MSG("Failed to parse ligature array"); |
450 | 319 | } |
451 | 419 | } else { |
452 | 0 | return OTS_FAILURE_MSG("Bad attachment type %d", type); |
453 | 0 | } |
454 | | |
455 | 645 | return true; |
456 | 1.05k | } |
457 | | |
458 | | } // namespace |
459 | | |
460 | | namespace ots { |
461 | | |
462 | | // Lookup Type 1: |
463 | | // Single Adjustment Positioning Subtable |
464 | | bool OpenTypeGPOS::ParseSingleAdjustment(const uint8_t *data, |
465 | 2.56k | const size_t length) { |
466 | 2.56k | Font* font = GetFont(); |
467 | 2.56k | Buffer subtable(data, length); |
468 | | |
469 | 2.56k | OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>( |
470 | 2.56k | font->GetTypedTable(OTS_TAG_MAXP)); |
471 | 2.56k | if (!maxp) { |
472 | 0 | return Error("Required maxp table missing"); |
473 | 0 | } |
474 | | |
475 | 2.56k | uint16_t format = 0; |
476 | 2.56k | uint16_t offset_coverage = 0; |
477 | 2.56k | uint16_t value_format = 0; |
478 | 2.56k | if (!subtable.ReadU16(&format) || |
479 | 2.55k | !subtable.ReadU16(&offset_coverage) || |
480 | 2.52k | !subtable.ReadU16(&value_format)) { |
481 | 76 | return Error("Can't read single adjustment information"); |
482 | 76 | } |
483 | | |
484 | 2.48k | if (format == 1) { |
485 | | // Format 1 exactly one value record. |
486 | 1.60k | if (!ParseValueRecord(font, &subtable, value_format)) { |
487 | 62 | return Error("Failed to parse format 1 single adjustment table"); |
488 | 62 | } |
489 | 1.60k | } else if (format == 2) { |
490 | 848 | uint16_t value_count = 0; |
491 | 848 | if (!subtable.ReadU16(&value_count)) { |
492 | 1 | return Error("Failed to parse format 2 single adjustment table"); |
493 | 1 | } |
494 | 9.91M | for (unsigned i = 0; i < value_count; ++i) { |
495 | 9.91M | if (!ParseValueRecord(font, &subtable, value_format)) { |
496 | 50 | return Error("Failed to parse value record %d in format 2 single adjustment table", i); |
497 | 50 | } |
498 | 9.91M | } |
499 | 847 | } else { |
500 | 41 | return Error("Bad format %d in single adjustment table", format); |
501 | 41 | } |
502 | | |
503 | 2.33k | if (offset_coverage < subtable.offset() || offset_coverage >= length) { |
504 | 97 | return Error("Bad coverage offset %d in single adjustment table", offset_coverage); |
505 | 97 | } |
506 | | |
507 | 2.23k | if (!ots::ParseCoverageTable(font, data + offset_coverage, |
508 | 2.23k | length - offset_coverage, |
509 | 2.23k | maxp->num_glyphs)) { |
510 | 28 | return Error("Failed to parse coverage table in single adjustment table"); |
511 | 28 | } |
512 | | |
513 | 2.21k | return true; |
514 | 2.23k | } |
515 | | |
516 | | // Lookup Type 2: |
517 | | // Pair Adjustment Positioning Subtable |
518 | | bool OpenTypeGPOS::ParsePairAdjustment(const uint8_t *data, |
519 | 2.25k | const size_t length) { |
520 | 2.25k | Font* font = GetFont(); |
521 | 2.25k | Buffer subtable(data, length); |
522 | | |
523 | 2.25k | OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>( |
524 | 2.25k | font->GetTypedTable(OTS_TAG_MAXP)); |
525 | 2.25k | if (!maxp) { |
526 | 0 | return Error("Required maxp table missing"); |
527 | 0 | } |
528 | | |
529 | 2.25k | uint16_t format = 0; |
530 | 2.25k | uint16_t offset_coverage = 0; |
531 | 2.25k | uint16_t value_format1 = 0; |
532 | 2.25k | uint16_t value_format2 = 0; |
533 | 2.25k | if (!subtable.ReadU16(&format) || |
534 | 2.21k | !subtable.ReadU16(&offset_coverage) || |
535 | 2.20k | !subtable.ReadU16(&value_format1) || |
536 | 2.16k | !subtable.ReadU16(&value_format2)) { |
537 | 87 | return Error("Failed to read pair adjustment structure"); |
538 | 87 | } |
539 | | |
540 | 2.16k | if (format == 1) { |
541 | 727 | if (!ParsePairPosFormat1(font, data, length, value_format1, value_format2, |
542 | 727 | maxp->num_glyphs)) { |
543 | 506 | return Error("Failed to parse pair pos format 1"); |
544 | 506 | } |
545 | 1.44k | } else if (format == 2) { |
546 | 1.40k | if (!ParsePairPosFormat2(font, data, length, value_format1, value_format2, |
547 | 1.40k | maxp->num_glyphs)) { |
548 | 396 | return Error("Failed to parse pair format 2"); |
549 | 396 | } |
550 | 1.40k | } else { |
551 | 37 | return Error("Bad pos pair format %d", format); |
552 | 37 | } |
553 | | |
554 | 1.22k | if (offset_coverage < subtable.offset() || offset_coverage >= length) { |
555 | 76 | return Error("Bad pair pos offset coverage %d", offset_coverage); |
556 | 76 | } |
557 | 1.15k | if (!ots::ParseCoverageTable(font, data + offset_coverage, |
558 | 1.15k | length - offset_coverage, |
559 | 1.15k | maxp->num_glyphs)) { |
560 | 53 | return Error("Failed to parse coverage table"); |
561 | 53 | } |
562 | | |
563 | 1.10k | return true; |
564 | 1.15k | } |
565 | | |
566 | | // Lookup Type 3 |
567 | | // Cursive Attachment Positioning Subtable |
568 | | bool OpenTypeGPOS::ParseCursiveAttachment(const uint8_t *data, |
569 | 1.14k | const size_t length) { |
570 | 1.14k | Font* font = GetFont(); |
571 | 1.14k | Buffer subtable(data, length); |
572 | | |
573 | 1.14k | OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>( |
574 | 1.14k | font->GetTypedTable(OTS_TAG_MAXP)); |
575 | 1.14k | if (!maxp) { |
576 | 0 | return Error("Required maxp table missing"); |
577 | 0 | } |
578 | | |
579 | 1.14k | uint16_t format = 0; |
580 | 1.14k | uint16_t offset_coverage = 0; |
581 | 1.14k | uint16_t entry_exit_count = 0; |
582 | 1.14k | if (!subtable.ReadU16(&format) || |
583 | 1.14k | !subtable.ReadU16(&offset_coverage) || |
584 | 1.12k | !subtable.ReadU16(&entry_exit_count)) { |
585 | 46 | return Error("Failed to read cursive attachment structure"); |
586 | 46 | } |
587 | | |
588 | 1.09k | if (format != 1) { |
589 | 22 | return Error("Bad cursive attachment format %d", format); |
590 | 22 | } |
591 | | |
592 | | // Check entry exit records. |
593 | 1.07k | const unsigned entry_exit_records_end = |
594 | 1.07k | 2 * static_cast<unsigned>(entry_exit_count) + 6; |
595 | 1.07k | if (entry_exit_records_end > std::numeric_limits<uint16_t>::max()) { |
596 | 34 | return Error("Bad entry exit record end %d", entry_exit_records_end); |
597 | 34 | } |
598 | 4.52k | for (unsigned i = 0; i < entry_exit_count; ++i) { |
599 | 4.11k | uint16_t offset_entry_anchor = 0; |
600 | 4.11k | uint16_t offset_exit_anchor = 0; |
601 | 4.11k | if (!subtable.ReadU16(&offset_entry_anchor) || |
602 | 4.07k | !subtable.ReadU16(&offset_exit_anchor)) { |
603 | 66 | return Error("Can't read entry exit record %d", i); |
604 | 66 | } |
605 | | // These offsets could be NULL. |
606 | 4.05k | if (offset_entry_anchor) { |
607 | 1.26k | if (offset_entry_anchor < entry_exit_records_end || |
608 | 1.22k | offset_entry_anchor >= length) { |
609 | 53 | return Error("Bad entry anchor offset %d in entry exit record %d", offset_entry_anchor, i); |
610 | 53 | } |
611 | 1.20k | if (!ParseAnchorTable(font, data + offset_entry_anchor, |
612 | 1.20k | length - offset_entry_anchor)) { |
613 | 313 | return Error("Failed to parse entry anchor table in entry exit record %d", i); |
614 | 313 | } |
615 | 1.20k | } |
616 | 3.68k | if (offset_exit_anchor) { |
617 | 989 | if (offset_exit_anchor < entry_exit_records_end || |
618 | 949 | offset_exit_anchor >= length) { |
619 | 82 | return Error("Bad exit anchor offset %d in entry exit record %d", offset_exit_anchor, i); |
620 | 82 | } |
621 | 907 | if (!ParseAnchorTable(font, data + offset_exit_anchor, |
622 | 907 | length - offset_exit_anchor)) { |
623 | 121 | return Error("Failed to parse exit anchor table in entry exit record %d", i); |
624 | 121 | } |
625 | 907 | } |
626 | 3.68k | } |
627 | | |
628 | 406 | if (offset_coverage < subtable.offset() || offset_coverage >= length) { |
629 | 59 | return Error("Bad coverage offset in cursive attachment %d", offset_coverage); |
630 | 59 | } |
631 | 347 | if (!ots::ParseCoverageTable(font, data + offset_coverage, |
632 | 347 | length - offset_coverage, |
633 | 347 | maxp->num_glyphs)) { |
634 | 27 | return Error("Failed to parse coverage table in cursive attachment"); |
635 | 27 | } |
636 | | |
637 | 320 | return true; |
638 | 347 | } |
639 | | |
640 | | // Lookup Type 4: |
641 | | // MarkToBase Attachment Positioning Subtable |
642 | | bool OpenTypeGPOS::ParseMarkToBaseAttachment(const uint8_t *data, |
643 | 487 | const size_t length) { |
644 | 487 | return ParseMarkToAttachmentSubtables(GetFont(), data, length, |
645 | 487 | GPOS_TYPE_MARK_TO_BASE_ATTACHMENT); |
646 | 487 | } |
647 | | |
648 | | // Lookup Type 5: |
649 | | // MarkToLigature Attachment Positioning Subtable |
650 | | bool OpenTypeGPOS::ParseMarkToLigatureAttachment(const uint8_t *data, |
651 | 1.09k | const size_t length) { |
652 | 1.09k | return ParseMarkToAttachmentSubtables(GetFont(), data, length, |
653 | 1.09k | GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT); |
654 | 1.09k | } |
655 | | |
656 | | // Lookup Type 6: |
657 | | // MarkToMark Attachment Positioning Subtable |
658 | | bool OpenTypeGPOS::ParseMarkToMarkAttachment(const uint8_t *data, |
659 | 377 | const size_t length) { |
660 | 377 | return ParseMarkToAttachmentSubtables(GetFont(), data, length, |
661 | 377 | GPOS_TYPE_MARK_TO_MARK_ATTACHMENT); |
662 | 377 | } |
663 | | |
664 | | // Lookup Type 7: |
665 | | // Contextual Positioning Subtables |
666 | | // OpenTypeLayoutTable::ParseContextSubtable() |
667 | | |
668 | | // Lookup Type 8: |
669 | | // Chaining Contexual Positioning Subtable |
670 | | // OpenTypeLayoutTable::ParseChainingContextSubtable() |
671 | | |
672 | | // Lookup Type 9: |
673 | | // Extension Positioning |
674 | | // OpenTypeLayoutTable::ParseExtensionSubtable |
675 | | |
676 | | |
677 | | bool OpenTypeGPOS::ValidLookupSubtableType(const uint16_t lookup_type, |
678 | 12.5k | bool extension) const { |
679 | 12.5k | if (extension && lookup_type == GPOS_TYPE_EXTENSION_POSITIONING) |
680 | 2 | return false; |
681 | 12.5k | return lookup_type >= GPOS_TYPE_SINGLE_ADJUSTMENT && lookup_type < GPOS_TYPE_RESERVED; |
682 | 12.5k | } |
683 | | |
684 | | bool OpenTypeGPOS::ParseLookupSubtable(const uint8_t *data, const size_t length, |
685 | 15.3k | const uint16_t lookup_type) { |
686 | 15.3k | switch (lookup_type) { |
687 | 2.56k | case GPOS_TYPE_SINGLE_ADJUSTMENT: |
688 | 2.56k | return ParseSingleAdjustment(data, length); |
689 | 2.25k | case GPOS_TYPE_PAIR_ADJUSTMENT: |
690 | 2.25k | return ParsePairAdjustment(data, length); |
691 | 1.14k | case GPOS_TYPE_CURSIVE_ATTACHMENT: |
692 | 1.14k | return ParseCursiveAttachment(data, length); |
693 | 487 | case GPOS_TYPE_MARK_TO_BASE_ATTACHMENT: |
694 | 487 | return ParseMarkToBaseAttachment(data, length); |
695 | 1.09k | case GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT: |
696 | 1.09k | return ParseMarkToLigatureAttachment(data, length); |
697 | 377 | case GPOS_TYPE_MARK_TO_MARK_ATTACHMENT: |
698 | 377 | return ParseMarkToMarkAttachment(data, length); |
699 | 1.69k | case GPOS_TYPE_CONTEXT_POSITIONING: |
700 | 1.69k | return ParseContextSubtable(data, length); |
701 | 5.18k | case GPOS_TYPE_CHAINED_CONTEXT_POSITIONING: |
702 | 5.18k | return ParseChainingContextSubtable(data, length); |
703 | 583 | case GPOS_TYPE_EXTENSION_POSITIONING: |
704 | 583 | return ParseExtensionSubtable(data, length); |
705 | 15.3k | } |
706 | 0 | return false; |
707 | 15.3k | } |
708 | | |
709 | | } // namespace ots |
710 | | |
711 | | #undef TABLE_NAME |