Coverage Report

Created: 2026-07-14 06:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ots/src/gpos.cc
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