Coverage Report

Created: 2025-08-03 06:45

/src/ots/src/gsub.cc
Line
Count
Source (jump to first uncovered line)
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 "gsub.h"
6
7
#include <limits>
8
#include <vector>
9
10
#include "layout.h"
11
#include "maxp.h"
12
13
// GSUB - The Glyph Substitution Table
14
// http://www.microsoft.com/typography/otspec/gsub.htm
15
16
#define TABLE_NAME "GSUB"
17
18
namespace {
19
20
enum GSUB_TYPE {
21
  GSUB_TYPE_SINGLE = 1,
22
  GSUB_TYPE_MULTIPLE = 2,
23
  GSUB_TYPE_ALTERNATE = 3,
24
  GSUB_TYPE_LIGATURE = 4,
25
  GSUB_TYPE_CONTEXT = 5,
26
  GSUB_TYPE_CHANGING_CONTEXT = 6,
27
  GSUB_TYPE_EXTENSION_SUBSTITUTION = 7,
28
  GSUB_TYPE_REVERSE_CHAINING_CONTEXT_SINGLE = 8,
29
  GSUB_TYPE_RESERVED = 9
30
};
31
32
bool ParseSequenceTable(const ots::Font *font,
33
                        const uint8_t *data, const size_t length,
34
27.5k
                        const uint16_t num_glyphs) {
35
27.5k
  ots::Buffer subtable(data, length);
36
37
27.5k
  uint16_t glyph_count = 0;
38
27.5k
  if (!subtable.ReadU16(&glyph_count)) {
39
1
    return OTS_FAILURE_MSG("Failed to read glyph count in sequence table");
40
1
  }
41
27.5k
  if (glyph_count > num_glyphs) {
42
36
    return OTS_FAILURE_MSG("bad glyph count %d > %d", glyph_count, num_glyphs);
43
36
  }
44
91.6k
  for (unsigned i = 0; i < glyph_count; ++i) {
45
64.1k
    uint16_t substitute = 0;
46
64.1k
    if (!subtable.ReadU16(&substitute)) {
47
1
      return OTS_FAILURE_MSG("Failed to read substitution %d in sequence table", i);
48
1
    }
49
64.1k
    if (substitute >= num_glyphs) {
50
12
      return OTS_FAILURE_MSG("Bad substitution (%d) %d > %d", i, substitute, num_glyphs);
51
12
    }
52
64.1k
  }
53
54
27.5k
  return true;
55
27.5k
}
56
57
bool ParseAlternateSetTable(const ots::Font *font,
58
                            const uint8_t *data, const size_t length,
59
1.24k
                            const uint16_t num_glyphs) {
60
1.24k
  ots::Buffer subtable(data, length);
61
62
1.24k
  uint16_t glyph_count = 0;
63
1.24k
  if (!subtable.ReadU16(&glyph_count)) {
64
1
    return OTS_FAILURE_MSG("Failed to read alternate set header");
65
1
  }
66
1.24k
  if (glyph_count > num_glyphs) {
67
19
    return OTS_FAILURE_MSG("Bad glyph count %d > %d in alternate set table", glyph_count, num_glyphs);
68
19
  }
69
4.42k
  for (unsigned i = 0; i < glyph_count; ++i) {
70
3.21k
    uint16_t alternate = 0;
71
3.21k
    if (!subtable.ReadU16(&alternate)) {
72
1
      return OTS_FAILURE_MSG("Can't read alternate %d", i);
73
1
    }
74
3.21k
    if (alternate >= num_glyphs) {
75
18
      return OTS_FAILURE_MSG("Too large alternate: %u", alternate);
76
18
    }
77
3.21k
  }
78
1.20k
  return true;
79
1.22k
}
80
81
bool ParseLigatureTable(const ots::Font *font,
82
                        const uint8_t *data, const size_t length,
83
36.2k
                        const uint16_t num_glyphs) {
84
36.2k
  ots::Buffer subtable(data, length);
85
86
36.2k
  uint16_t lig_glyph = 0;
87
36.2k
  uint16_t comp_count = 0;
88
89
36.2k
  if (!subtable.ReadU16(&lig_glyph) ||
90
36.2k
      !subtable.ReadU16(&comp_count)) {
91
2
    return OTS_FAILURE_MSG("Failed to read ligature table header");
92
2
  }
93
94
36.2k
  if (lig_glyph >= num_glyphs) {
95
6
    return OTS_FAILURE_MSG("too large lig_glyph: %u", lig_glyph);
96
6
  }
97
36.2k
  if (comp_count == 0) {
98
5
    return OTS_FAILURE_MSG("Component count cannot be 0");
99
5
  }
100
96.0k
  for (unsigned i = 0; i < comp_count - static_cast<unsigned>(1); ++i) {
101
59.8k
    uint16_t component = 0;
102
59.8k
    if (!subtable.ReadU16(&component)) {
103
4
      return OTS_FAILURE_MSG("Can't read ligature component %d", i);
104
4
    }
105
59.8k
    if (component >= num_glyphs) {
106
9
      return OTS_FAILURE_MSG("Bad ligature component %d of %d", i, component);
107
9
    }
108
59.8k
  }
109
110
36.2k
  return true;
111
36.2k
}
112
113
bool ParseLigatureSetTable(const ots::Font *font,
114
                           const uint8_t *data, const size_t length,
115
18.5k
                           const uint16_t num_glyphs) {
116
18.5k
  ots::Buffer subtable(data, length);
117
118
18.5k
  uint16_t ligature_count = 0;
119
120
18.5k
  if (!subtable.ReadU16(&ligature_count)) {
121
0
    return OTS_FAILURE_MSG("Can't read ligature count in ligature set");
122
0
  }
123
124
18.5k
  const unsigned ligature_end = static_cast<unsigned>(2) + ligature_count * 2;
125
18.5k
  if (ligature_end > std::numeric_limits<uint16_t>::max()) {
126
23
    return OTS_FAILURE_MSG("Bad end of ligature %d in ligature set", ligature_end);
127
23
  }
128
54.7k
  for (unsigned i = 0; i < ligature_count; ++i) {
129
36.2k
    uint16_t offset_ligature = 0;
130
36.2k
    if (!subtable.ReadU16(&offset_ligature)) {
131
1
      return OTS_FAILURE_MSG("Failed to read ligature offset %d", i);
132
1
    }
133
36.2k
    if (offset_ligature < ligature_end || offset_ligature >= length) {
134
30
      return OTS_FAILURE_MSG("Bad ligature offset %d for ligature %d", offset_ligature, i);
135
30
    }
136
36.2k
    if (!ParseLigatureTable(font, data + offset_ligature, length - offset_ligature,
137
36.2k
                            num_glyphs)) {
138
26
      return OTS_FAILURE_MSG("Failed to parse ligature %d", i);
139
26
    }
140
36.2k
  }
141
142
18.4k
  return true;
143
18.5k
}
144
145
}  // namespace
146
147
namespace ots {
148
149
// Lookup Type 1:
150
// Single Substitution Subtable
151
bool OpenTypeGSUB::ParseSingleSubstitution(const uint8_t *data,
152
2.44k
                                           const size_t length) {
153
2.44k
  Font* font = GetFont();
154
2.44k
  Buffer subtable(data, length);
155
156
2.44k
  uint16_t format = 0;
157
2.44k
  uint16_t offset_coverage = 0;
158
159
2.44k
  if (!subtable.ReadU16(&format) ||
160
2.44k
      !subtable.ReadU16(&offset_coverage)) {
161
2
    return Error("Failed to read single subst table header");
162
2
  }
163
164
2.44k
  OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
165
2.44k
      font->GetTypedTable(OTS_TAG_MAXP));
166
2.44k
  if (!maxp) {
167
0
    return Error("Required maxp table missing");
168
0
  }
169
2.44k
  const uint16_t num_glyphs = maxp->num_glyphs;
170
2.44k
  if (format == 1) {
171
    // Parse SingleSubstFormat1
172
1.41k
    int16_t delta_glyph_id = 0;
173
1.41k
    if (!subtable.ReadS16(&delta_glyph_id)) {
174
1
      return Error("Failed to read glyph shift from format 1 single subst table");
175
1
    }
176
1.41k
    if (std::abs(delta_glyph_id) >= num_glyphs) {
177
14
      return Error("bad glyph shift of %d in format 1 single subst table", delta_glyph_id);
178
14
    }
179
1.41k
  } else if (format == 2) {
180
    // Parse SingleSubstFormat2
181
1.01k
    uint16_t glyph_count = 0;
182
1.01k
    if (!subtable.ReadU16(&glyph_count)) {
183
0
      return Error("Failed to read glyph cound in format 2 single subst table");
184
0
    }
185
1.01k
    if (glyph_count > num_glyphs) {
186
5
      return Error("Bad glyph count %d > %d in format 2 single subst table", glyph_count, num_glyphs);
187
5
    }
188
22.0k
    for (unsigned i = 0; i < glyph_count; ++i) {
189
21.0k
      uint16_t substitute = 0;
190
21.0k
      if (!subtable.ReadU16(&substitute)) {
191
1
        return Error("Failed to read substitution %d in format 2 single subst table", i);
192
1
      }
193
21.0k
      if (substitute >= num_glyphs) {
194
10
        return Error("too large substitute: %u", substitute);
195
10
      }
196
21.0k
    }
197
1.01k
  } else {
198
12
    return Error("Bad single subst table format %d", format);
199
12
  }
200
201
2.40k
  if (offset_coverage < subtable.offset() || offset_coverage >= length) {
202
30
    return Error("Bad coverage offset %x", offset_coverage);
203
30
  }
204
2.37k
  if (!ots::ParseCoverageTable(font, data + offset_coverage,
205
2.37k
                               length - offset_coverage, num_glyphs)) {
206
11
    return Error("Failed to parse coverage table");
207
11
  }
208
209
2.36k
  return true;
210
2.37k
}
211
212
// Lookup Type 2:
213
// Multiple Substitution Subtable
214
bool OpenTypeGSUB::ParseMutipleSubstitution(const uint8_t *data,
215
622
                                            const size_t length) {
216
622
  Font* font = GetFont();
217
622
  Buffer subtable(data, length);
218
219
622
  uint16_t format = 0;
220
622
  uint16_t offset_coverage = 0;
221
622
  uint16_t sequence_count = 0;
222
223
622
  if (!subtable.ReadU16(&format) ||
224
622
      !subtable.ReadU16(&offset_coverage) ||
225
622
      !subtable.ReadU16(&sequence_count)) {
226
4
    return Error("Can't read header of multiple subst table");
227
4
  }
228
229
618
  if (format != 1) {
230
6
    return Error("Bad multiple subst table format %d", format);
231
6
  }
232
233
612
  OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
234
612
      font->GetTypedTable(OTS_TAG_MAXP));
235
612
  if (!maxp) {
236
0
    return Error("Required maxp table missing");
237
0
  }
238
612
  const uint16_t num_glyphs = maxp->num_glyphs;
239
612
  const unsigned sequence_end = static_cast<unsigned>(6) +
240
612
      sequence_count * 2;
241
612
  if (sequence_end > std::numeric_limits<uint16_t>::max()) {
242
8
    return Error("Bad sequence end %d, in multiple subst", sequence_end);
243
8
  }
244
28.1k
  for (unsigned i = 0; i < sequence_count; ++i) {
245
27.6k
    uint16_t offset_sequence = 0;
246
27.6k
    if (!subtable.ReadU16(&offset_sequence)) {
247
1
      return Error("Failed to read sequence offset for sequence %d", i);
248
1
    }
249
27.5k
    if (offset_sequence < sequence_end || offset_sequence >= length) {
250
35
      return Error("Bad sequence offset %d for sequence %d", offset_sequence, i);
251
35
    }
252
27.5k
    if (!ParseSequenceTable(font, data + offset_sequence, length - offset_sequence,
253
27.5k
                            num_glyphs)) {
254
50
      return Error("Failed to parse sequence table %d", i);
255
50
    }
256
27.5k
  }
257
258
518
  if (offset_coverage < sequence_end || offset_coverage >= length) {
259
39
    return Error("Bad coverage offset %d", offset_coverage);
260
39
  }
261
479
  if (!ots::ParseCoverageTable(font, data + offset_coverage,
262
479
                               length - offset_coverage, num_glyphs)) {
263
16
    return Error("Failed to parse coverage table");
264
16
  }
265
266
463
  return true;
267
479
}
268
269
// Lookup Type 3:
270
// Alternate Substitution Subtable
271
bool OpenTypeGSUB::ParseAlternateSubstitution(const uint8_t *data,
272
251
                                              const size_t length) {
273
251
  Font* font = GetFont();
274
251
  Buffer subtable(data, length);
275
276
251
  uint16_t format = 0;
277
251
  uint16_t offset_coverage = 0;
278
251
  uint16_t alternate_set_count = 0;
279
280
251
  if (!subtable.ReadU16(&format) ||
281
251
      !subtable.ReadU16(&offset_coverage) ||
282
251
      !subtable.ReadU16(&alternate_set_count)) {
283
3
    return Error("Can't read alternate subst header");
284
3
  }
285
286
248
  if (format != 1) {
287
10
    return Error("Bad alternate subst table format %d", format);
288
10
  }
289
290
238
  OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
291
238
      font->GetTypedTable(OTS_TAG_MAXP));
292
238
  if (!maxp) {
293
0
    return Error("Required maxp table missing");
294
0
  }
295
238
  const uint16_t num_glyphs = maxp->num_glyphs;
296
238
  const unsigned alternate_set_end = static_cast<unsigned>(6) +
297
238
      alternate_set_count * 2;
298
238
  if (alternate_set_end > std::numeric_limits<uint16_t>::max()) {
299
9
    return Error("Bad end of alternate set %d", alternate_set_end);
300
9
  }
301
1.43k
  for (unsigned i = 0; i < alternate_set_count; ++i) {
302
1.26k
    uint16_t offset_alternate_set = 0;
303
1.26k
    if (!subtable.ReadU16(&offset_alternate_set)) {
304
1
      return Error("Can't read alternate set offset for set %d", i);
305
1
    }
306
1.26k
    if (offset_alternate_set < alternate_set_end ||
307
1.26k
        offset_alternate_set >= length) {
308
20
      return Error("Bad alternate set offset %d for set %d", offset_alternate_set, i);
309
20
    }
310
1.24k
    if (!ParseAlternateSetTable(font, data + offset_alternate_set,
311
1.24k
                                length - offset_alternate_set,
312
1.24k
                                num_glyphs)) {
313
39
      return Error("Failed to parse alternate set");
314
39
    }
315
1.24k
  }
316
317
169
  if (offset_coverage < alternate_set_end || offset_coverage >= length) {
318
24
    return Error("Bad coverage offset %d", offset_coverage);
319
24
  }
320
145
  if (!ots::ParseCoverageTable(font, data + offset_coverage,
321
145
                               length - offset_coverage, num_glyphs)) {
322
9
    return Error("Failed to parse coverage table");
323
9
  }
324
325
136
  return true;
326
145
}
327
328
// Lookup Type 4:
329
// Ligature Substitution Subtable
330
bool OpenTypeGSUB::ParseLigatureSubstitution(const uint8_t *data,
331
1.89k
                                             const size_t length) {
332
1.89k
  Font* font = GetFont();
333
1.89k
  Buffer subtable(data, length);
334
335
1.89k
  uint16_t format = 0;
336
1.89k
  uint16_t offset_coverage = 0;
337
1.89k
  uint16_t lig_set_count = 0;
338
339
1.89k
  if (!subtable.ReadU16(&format) ||
340
1.89k
      !subtable.ReadU16(&offset_coverage) ||
341
1.89k
      !subtable.ReadU16(&lig_set_count)) {
342
5
    return Error("Failed to read ligature substitution header");
343
5
  }
344
345
1.88k
  if (format != 1) {
346
16
    return Error("Bad ligature substitution table format %d", format);
347
16
  }
348
349
1.87k
  OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
350
1.87k
      font->GetTypedTable(OTS_TAG_MAXP));
351
1.87k
  if (!maxp) {
352
0
    return Error("Required maxp table missing");
353
0
  }
354
1.87k
  const uint16_t num_glyphs = maxp->num_glyphs;
355
1.87k
  const unsigned ligature_set_end = static_cast<unsigned>(6) +
356
1.87k
      lig_set_count * 2;
357
1.87k
  if (ligature_set_end > std::numeric_limits<uint16_t>::max()) {
358
2
    return Error("Bad end of ligature set %d in ligature substitution table", ligature_set_end);
359
2
  }
360
20.3k
  for (unsigned i = 0; i < lig_set_count; ++i) {
361
18.5k
    uint16_t offset_ligature_set = 0;
362
18.5k
    if (!subtable.ReadU16(&offset_ligature_set)) {
363
0
      return Error("Can't read ligature set offset %d", i);
364
0
    }
365
18.5k
    if (offset_ligature_set < ligature_set_end ||
366
18.5k
        offset_ligature_set >= length) {
367
43
      return Error("Bad ligature set offset %d for set %d", offset_ligature_set, i);
368
43
    }
369
18.5k
    if (!ParseLigatureSetTable(font, data + offset_ligature_set,
370
18.5k
                               length - offset_ligature_set, num_glyphs)) {
371
80
      return Error("Failed to parse ligature set %d", i);
372
80
    }
373
18.5k
  }
374
375
1.74k
  if (offset_coverage < ligature_set_end || offset_coverage >= length) {
376
25
    return Error("Bad coverage offset %d", offset_coverage);
377
25
  }
378
1.72k
  if (!ots::ParseCoverageTable(font, data + offset_coverage,
379
1.72k
                               length - offset_coverage, num_glyphs)) {
380
29
    return Error("Failed to parse coverage table");
381
29
  }
382
383
1.69k
  return true;
384
1.72k
}
385
386
// Lookup Type 5:
387
// Contextual Substitution Subtable
388
// OpenTypeLayoutTable::ParseContextSubtable()
389
390
// Lookup Type 6:
391
// Chaining Contextual Substitution Subtable
392
// OpenTypeLayoutTable::ParseChainingContextSubtable
393
394
// Lookup Type 7:
395
// Extension Substition
396
// OpenTypeLayoutTable::ParseExtensionSubtable
397
398
// Lookup Type 8:
399
// Reverse Chaining Contexual Single Substitution Subtable
400
bool OpenTypeGSUB::ParseReverseChainingContextSingleSubstitution(const uint8_t *data,
401
247
                                                                 const size_t length) {
402
247
  Font* font = GetFont();
403
247
  Buffer subtable(data, length);
404
405
247
  uint16_t format = 0;
406
247
  uint16_t offset_coverage = 0;
407
408
247
  if (!subtable.ReadU16(&format) ||
409
247
      !subtable.ReadU16(&offset_coverage)) {
410
2
    return Error("Failed to read reverse chaining header");
411
2
  }
412
413
245
  OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
414
245
      font->GetTypedTable(OTS_TAG_MAXP));
415
245
  if (!maxp) {
416
0
    return Error("Required maxp table missing");
417
0
  }
418
245
  const uint16_t num_glyphs = maxp->num_glyphs;
419
420
245
  uint16_t backtrack_glyph_count = 0;
421
245
  if (!subtable.ReadU16(&backtrack_glyph_count)) {
422
1
    return Error("Failed to read backtrack glyph count in reverse chaining table");
423
1
  }
424
244
  std::vector<uint16_t> offsets_backtrack;
425
244
  offsets_backtrack.reserve(backtrack_glyph_count);
426
327k
  for (unsigned i = 0; i < backtrack_glyph_count; ++i) {
427
327k
    uint16_t offset = 0;
428
327k
    if (!subtable.ReadU16(&offset)) {
429
24
      return Error("Failed to read backtrack offset %d", i);
430
24
    }
431
327k
    offsets_backtrack.push_back(offset);
432
327k
  }
433
434
220
  uint16_t lookahead_glyph_count = 0;
435
220
  if (!subtable.ReadU16(&lookahead_glyph_count)) {
436
1
    return Error("Failed to read look ahead glyph count");
437
1
  }
438
219
  std::vector<uint16_t> offsets_lookahead;
439
219
  offsets_lookahead.reserve(lookahead_glyph_count);
440
55.2k
  for (unsigned i = 0; i < lookahead_glyph_count; ++i) {
441
55.0k
    uint16_t offset = 0;
442
55.0k
    if (!subtable.ReadU16(&offset)) {
443
2
      return Error("Can't read look ahead offset %d", i);
444
2
    }
445
55.0k
    offsets_lookahead.push_back(offset);
446
55.0k
  }
447
448
217
  uint16_t glyph_count = 0;
449
217
  if (!subtable.ReadU16(&glyph_count)) {
450
13
    return Error("Can't read glyph count in reverse chaining table");
451
13
  }
452
687
  for (unsigned i = 0; i < glyph_count; ++i) {
453
499
    uint16_t substitute = 0;
454
499
    if (!subtable.ReadU16(&substitute)) {
455
3
      return Error("Failed to read substitution %d reverse chaining table", i);
456
3
    }
457
496
    if (substitute >= num_glyphs) {
458
13
      return Error("Bad substitute glyph %d in reverse chaining table substitution %d", substitute, i);
459
13
    }
460
496
  }
461
462
188
  const unsigned substitute_end = static_cast<unsigned>(10) +
463
188
      (backtrack_glyph_count + lookahead_glyph_count + glyph_count) * 2;
464
188
  if (substitute_end > std::numeric_limits<uint16_t>::max()) {
465
0
    return Error("Bad substitute end offset in reverse chaining table");
466
0
  }
467
468
188
  if (offset_coverage < substitute_end || offset_coverage >= length) {
469
51
    return Error("Bad coverage offset %d in reverse chaining table", offset_coverage);
470
51
  }
471
137
  if (!ots::ParseCoverageTable(font, data + offset_coverage,
472
137
                               length - offset_coverage, num_glyphs)) {
473
22
    return Error("Failed to parse coverage table in reverse chaining table");
474
22
  }
475
476
120
  for (unsigned i = 0; i < backtrack_glyph_count; ++i) {
477
56
    if (offsets_backtrack[i] < substitute_end ||
478
56
        offsets_backtrack[i] >= length) {
479
39
      return Error("Bad backtrack offset %d for backtrack %d in reverse chaining table", offsets_backtrack[i], i);
480
39
    }
481
17
    if (!ots::ParseCoverageTable(font, data + offsets_backtrack[i],
482
17
                                 length - offsets_backtrack[i], num_glyphs)) {
483
12
      return Error("Failed to parse coverage table for backtrack %d in reverse chaining table", i);
484
12
    }
485
17
  }
486
487
69
  for (unsigned i = 0; i < lookahead_glyph_count; ++i) {
488
12
    if (offsets_lookahead[i] < substitute_end ||
489
12
        offsets_lookahead[i] >= length) {
490
6
      return Error("Bad lookahead offset %d for lookahead %d in reverse chaining table", offsets_lookahead[i], i);
491
6
    }
492
6
    if (!ots::ParseCoverageTable(font, data + offsets_lookahead[i],
493
6
                                 length - offsets_lookahead[i], num_glyphs)) {
494
1
      return Error("Failed to parse lookahead coverage table %d in reverse chaining table", i);
495
1
    }
496
6
  }
497
498
57
  return true;
499
64
}
500
501
bool OpenTypeGSUB::ValidLookupSubtableType(const uint16_t lookup_type,
502
8.79k
                                           bool extension) const {
503
8.79k
  if (extension && lookup_type == GSUB_TYPE_EXTENSION_SUBSTITUTION)
504
10
    return false;
505
8.78k
  return lookup_type >= GSUB_TYPE_SINGLE && lookup_type < GSUB_TYPE_RESERVED;
506
8.79k
}
507
508
bool OpenTypeGSUB::ParseLookupSubtable(const uint8_t *data, const size_t length,
509
12.2k
                                       const uint16_t lookup_type) {
510
12.2k
  switch (lookup_type) {
511
2.44k
    case GSUB_TYPE_SINGLE:
512
2.44k
      return ParseSingleSubstitution(data, length);
513
622
    case GSUB_TYPE_MULTIPLE:
514
622
      return ParseMutipleSubstitution(data, length);
515
251
    case GSUB_TYPE_ALTERNATE:
516
251
      return ParseAlternateSubstitution(data, length);
517
1.89k
    case GSUB_TYPE_LIGATURE:
518
1.89k
      return ParseLigatureSubstitution(data, length);
519
667
    case GSUB_TYPE_CONTEXT:
520
667
      return ParseContextSubtable(data, length);
521
4.77k
    case GSUB_TYPE_CHANGING_CONTEXT:
522
4.77k
      return ParseChainingContextSubtable(data, length);
523
1.37k
    case GSUB_TYPE_EXTENSION_SUBSTITUTION:
524
1.37k
      return ParseExtensionSubtable(data, length);
525
247
    case GSUB_TYPE_REVERSE_CHAINING_CONTEXT_SINGLE:
526
247
      return ParseReverseChainingContextSingleSubstitution(data, length);
527
12.2k
  }
528
0
  return false;
529
12.2k
}
530
531
}  // namespace ots
532
533
#undef TABLE_NAME