Coverage Report

Created: 2025-12-05 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ots/src/gsub.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 "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
24.6k
                        const uint16_t num_glyphs) {
35
24.6k
  ots::Buffer subtable(data, length);
36
37
24.6k
  uint16_t glyph_count = 0;
38
24.6k
  if (!subtable.ReadU16(&glyph_count)) {
39
1
    return OTS_FAILURE_MSG("Failed to read glyph count in sequence table");
40
1
  }
41
24.6k
  if (glyph_count > num_glyphs) {
42
35
    return OTS_FAILURE_MSG("bad glyph count %d > %d", glyph_count, num_glyphs);
43
35
  }
44
81.7k
  for (unsigned i = 0; i < glyph_count; ++i) {
45
57.1k
    uint16_t substitute = 0;
46
57.1k
    if (!subtable.ReadU16(&substitute)) {
47
1
      return OTS_FAILURE_MSG("Failed to read substitution %d in sequence table", i);
48
1
    }
49
57.1k
    if (substitute >= num_glyphs) {
50
11
      return OTS_FAILURE_MSG("Bad substitution (%d) %d > %d", i, substitute, num_glyphs);
51
11
    }
52
57.1k
  }
53
54
24.6k
  return true;
55
24.6k
}
56
57
bool ParseAlternateSetTable(const ots::Font *font,
58
                            const uint8_t *data, const size_t length,
59
1.22k
                            const uint16_t num_glyphs) {
60
1.22k
  ots::Buffer subtable(data, length);
61
62
1.22k
  uint16_t glyph_count = 0;
63
1.22k
  if (!subtable.ReadU16(&glyph_count)) {
64
1
    return OTS_FAILURE_MSG("Failed to read alternate set header");
65
1
  }
66
1.21k
  if (glyph_count > num_glyphs) {
67
12
    return OTS_FAILURE_MSG("Bad glyph count %d > %d in alternate set table", glyph_count, num_glyphs);
68
12
  }
69
4.32k
  for (unsigned i = 0; i < glyph_count; ++i) {
70
3.13k
    uint16_t alternate = 0;
71
3.13k
    if (!subtable.ReadU16(&alternate)) {
72
1
      return OTS_FAILURE_MSG("Can't read alternate %d", i);
73
1
    }
74
3.13k
    if (alternate >= num_glyphs) {
75
15
      return OTS_FAILURE_MSG("Too large alternate: %u", alternate);
76
15
    }
77
3.13k
  }
78
1.19k
  return true;
79
1.20k
}
80
81
bool ParseLigatureTable(const ots::Font *font,
82
                        const uint8_t *data, const size_t length,
83
33.1k
                        const uint16_t num_glyphs) {
84
33.1k
  ots::Buffer subtable(data, length);
85
86
33.1k
  uint16_t lig_glyph = 0;
87
33.1k
  uint16_t comp_count = 0;
88
89
33.1k
  if (!subtable.ReadU16(&lig_glyph) ||
90
33.1k
      !subtable.ReadU16(&comp_count)) {
91
2
    return OTS_FAILURE_MSG("Failed to read ligature table header");
92
2
  }
93
94
33.1k
  if (lig_glyph >= num_glyphs) {
95
6
    return OTS_FAILURE_MSG("too large lig_glyph: %u", lig_glyph);
96
6
  }
97
33.1k
  if (comp_count == 0) {
98
5
    return OTS_FAILURE_MSG("Component count cannot be 0");
99
5
  }
100
87.5k
  for (unsigned i = 0; i < comp_count - static_cast<unsigned>(1); ++i) {
101
54.3k
    uint16_t component = 0;
102
54.3k
    if (!subtable.ReadU16(&component)) {
103
4
      return OTS_FAILURE_MSG("Can't read ligature component %d", i);
104
4
    }
105
54.3k
    if (component >= num_glyphs) {
106
4
      return OTS_FAILURE_MSG("Bad ligature component %d of %d", i, component);
107
4
    }
108
54.3k
  }
109
110
33.1k
  return true;
111
33.1k
}
112
113
bool ParseLigatureSetTable(const ots::Font *font,
114
                           const uint8_t *data, const size_t length,
115
17.0k
                           const uint16_t num_glyphs) {
116
17.0k
  ots::Buffer subtable(data, length);
117
118
17.0k
  uint16_t ligature_count = 0;
119
120
17.0k
  if (!subtable.ReadU16(&ligature_count)) {
121
0
    return OTS_FAILURE_MSG("Can't read ligature count in ligature set");
122
0
  }
123
124
17.0k
  const unsigned ligature_end = static_cast<unsigned>(2) + ligature_count * 2;
125
17.0k
  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
50.1k
  for (unsigned i = 0; i < ligature_count; ++i) {
129
33.1k
    uint16_t offset_ligature = 0;
130
33.1k
    if (!subtable.ReadU16(&offset_ligature)) {
131
1
      return OTS_FAILURE_MSG("Failed to read ligature offset %d", i);
132
1
    }
133
33.1k
    if (offset_ligature < ligature_end || offset_ligature >= length) {
134
28
      return OTS_FAILURE_MSG("Bad ligature offset %d for ligature %d", offset_ligature, i);
135
28
    }
136
33.1k
    if (!ParseLigatureTable(font, data + offset_ligature, length - offset_ligature,
137
33.1k
                            num_glyphs)) {
138
21
      return OTS_FAILURE_MSG("Failed to parse ligature %d", i);
139
21
    }
140
33.1k
  }
141
142
16.9k
  return true;
143
16.9k
}
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.28k
                                           const size_t length) {
153
2.28k
  Font* font = GetFont();
154
2.28k
  Buffer subtable(data, length);
155
156
2.28k
  uint16_t format = 0;
157
2.28k
  uint16_t offset_coverage = 0;
158
159
2.28k
  if (!subtable.ReadU16(&format) ||
160
2.28k
      !subtable.ReadU16(&offset_coverage)) {
161
2
    return Error("Failed to read single subst table header");
162
2
  }
163
164
2.27k
  OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
165
2.27k
      font->GetTypedTable(OTS_TAG_MAXP));
166
2.27k
  if (!maxp) {
167
0
    return Error("Required maxp table missing");
168
0
  }
169
2.27k
  const uint16_t num_glyphs = maxp->num_glyphs;
170
2.27k
  if (format == 1) {
171
    // Parse SingleSubstFormat1
172
1.24k
    int16_t delta_glyph_id = 0;
173
1.24k
    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.24k
    if (std::abs(delta_glyph_id) >= num_glyphs) {
177
12
      return Error("bad glyph shift of %d in format 1 single subst table", delta_glyph_id);
178
12
    }
179
1.24k
  } else if (format == 2) {
180
    // Parse SingleSubstFormat2
181
1.02k
    uint16_t glyph_count = 0;
182
1.02k
    if (!subtable.ReadU16(&glyph_count)) {
183
0
      return Error("Failed to read glyph cound in format 2 single subst table");
184
0
    }
185
1.02k
    if (glyph_count > num_glyphs) {
186
2
      return Error("Bad glyph count %d > %d in format 2 single subst table", glyph_count, num_glyphs);
187
2
    }
188
21.4k
    for (unsigned i = 0; i < glyph_count; ++i) {
189
20.4k
      uint16_t substitute = 0;
190
20.4k
      if (!subtable.ReadU16(&substitute)) {
191
1
        return Error("Failed to read substitution %d in format 2 single subst table", i);
192
1
      }
193
20.4k
      if (substitute >= num_glyphs) {
194
5
        return Error("too large substitute: %u", substitute);
195
5
      }
196
20.4k
    }
197
1.02k
  } else {
198
8
    return Error("Bad single subst table format %d", format);
199
8
  }
200
201
2.25k
  if (offset_coverage < subtable.offset() || offset_coverage >= length) {
202
30
    return Error("Bad coverage offset %x", offset_coverage);
203
30
  }
204
2.22k
  if (!ots::ParseCoverageTable(font, data + offset_coverage,
205
2.22k
                               length - offset_coverage, num_glyphs)) {
206
11
    return Error("Failed to parse coverage table");
207
11
  }
208
209
2.20k
  return true;
210
2.22k
}
211
212
// Lookup Type 2:
213
// Multiple Substitution Subtable
214
bool OpenTypeGSUB::ParseMutipleSubstitution(const uint8_t *data,
215
574
                                            const size_t length) {
216
574
  Font* font = GetFont();
217
574
  Buffer subtable(data, length);
218
219
574
  uint16_t format = 0;
220
574
  uint16_t offset_coverage = 0;
221
574
  uint16_t sequence_count = 0;
222
223
574
  if (!subtable.ReadU16(&format) ||
224
572
      !subtable.ReadU16(&offset_coverage) ||
225
571
      !subtable.ReadU16(&sequence_count)) {
226
4
    return Error("Can't read header of multiple subst table");
227
4
  }
228
229
570
  if (format != 1) {
230
6
    return Error("Bad multiple subst table format %d", format);
231
6
  }
232
233
564
  OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
234
564
      font->GetTypedTable(OTS_TAG_MAXP));
235
564
  if (!maxp) {
236
0
    return Error("Required maxp table missing");
237
0
  }
238
564
  const uint16_t num_glyphs = maxp->num_glyphs;
239
564
  const unsigned sequence_end = static_cast<unsigned>(6) +
240
564
      sequence_count * 2;
241
564
  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
25.1k
  for (unsigned i = 0; i < sequence_count; ++i) {
245
24.6k
    uint16_t offset_sequence = 0;
246
24.6k
    if (!subtable.ReadU16(&offset_sequence)) {
247
1
      return Error("Failed to read sequence offset for sequence %d", i);
248
1
    }
249
24.6k
    if (offset_sequence < sequence_end || offset_sequence >= length) {
250
32
      return Error("Bad sequence offset %d for sequence %d", offset_sequence, i);
251
32
    }
252
24.6k
    if (!ParseSequenceTable(font, data + offset_sequence, length - offset_sequence,
253
24.6k
                            num_glyphs)) {
254
48
      return Error("Failed to parse sequence table %d", i);
255
48
    }
256
24.6k
  }
257
258
475
  if (offset_coverage < sequence_end || offset_coverage >= length) {
259
39
    return Error("Bad coverage offset %d", offset_coverage);
260
39
  }
261
436
  if (!ots::ParseCoverageTable(font, data + offset_coverage,
262
436
                               length - offset_coverage, num_glyphs)) {
263
16
    return Error("Failed to parse coverage table");
264
16
  }
265
266
420
  return true;
267
436
}
268
269
// Lookup Type 3:
270
// Alternate Substitution Subtable
271
bool OpenTypeGSUB::ParseAlternateSubstitution(const uint8_t *data,
272
243
                                              const size_t length) {
273
243
  Font* font = GetFont();
274
243
  Buffer subtable(data, length);
275
276
243
  uint16_t format = 0;
277
243
  uint16_t offset_coverage = 0;
278
243
  uint16_t alternate_set_count = 0;
279
280
243
  if (!subtable.ReadU16(&format) ||
281
242
      !subtable.ReadU16(&offset_coverage) ||
282
241
      !subtable.ReadU16(&alternate_set_count)) {
283
3
    return Error("Can't read alternate subst header");
284
3
  }
285
286
240
  if (format != 1) {
287
10
    return Error("Bad alternate subst table format %d", format);
288
10
  }
289
290
230
  OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
291
230
      font->GetTypedTable(OTS_TAG_MAXP));
292
230
  if (!maxp) {
293
0
    return Error("Required maxp table missing");
294
0
  }
295
230
  const uint16_t num_glyphs = maxp->num_glyphs;
296
230
  const unsigned alternate_set_end = static_cast<unsigned>(6) +
297
230
      alternate_set_count * 2;
298
230
  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.41k
  for (unsigned i = 0; i < alternate_set_count; ++i) {
302
1.24k
    uint16_t offset_alternate_set = 0;
303
1.24k
    if (!subtable.ReadU16(&offset_alternate_set)) {
304
1
      return Error("Can't read alternate set offset for set %d", i);
305
1
    }
306
1.24k
    if (offset_alternate_set < alternate_set_end ||
307
1.23k
        offset_alternate_set >= length) {
308
20
      return Error("Bad alternate set offset %d for set %d", offset_alternate_set, i);
309
20
    }
310
1.22k
    if (!ParseAlternateSetTable(font, data + offset_alternate_set,
311
1.22k
                                length - offset_alternate_set,
312
1.22k
                                num_glyphs)) {
313
29
      return Error("Failed to parse alternate set");
314
29
    }
315
1.22k
  }
316
317
171
  if (offset_coverage < alternate_set_end || offset_coverage >= length) {
318
24
    return Error("Bad coverage offset %d", offset_coverage);
319
24
  }
320
147
  if (!ots::ParseCoverageTable(font, data + offset_coverage,
321
147
                               length - offset_coverage, num_glyphs)) {
322
9
    return Error("Failed to parse coverage table");
323
9
  }
324
325
138
  return true;
326
147
}
327
328
// Lookup Type 4:
329
// Ligature Substitution Subtable
330
bool OpenTypeGSUB::ParseLigatureSubstitution(const uint8_t *data,
331
1.65k
                                             const size_t length) {
332
1.65k
  Font* font = GetFont();
333
1.65k
  Buffer subtable(data, length);
334
335
1.65k
  uint16_t format = 0;
336
1.65k
  uint16_t offset_coverage = 0;
337
1.65k
  uint16_t lig_set_count = 0;
338
339
1.65k
  if (!subtable.ReadU16(&format) ||
340
1.65k
      !subtable.ReadU16(&offset_coverage) ||
341
1.65k
      !subtable.ReadU16(&lig_set_count)) {
342
5
    return Error("Failed to read ligature substitution header");
343
5
  }
344
345
1.65k
  if (format != 1) {
346
16
    return Error("Bad ligature substitution table format %d", format);
347
16
  }
348
349
1.63k
  OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
350
1.63k
      font->GetTypedTable(OTS_TAG_MAXP));
351
1.63k
  if (!maxp) {
352
0
    return Error("Required maxp table missing");
353
0
  }
354
1.63k
  const uint16_t num_glyphs = maxp->num_glyphs;
355
1.63k
  const unsigned ligature_set_end = static_cast<unsigned>(6) +
356
1.63k
      lig_set_count * 2;
357
1.63k
  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
18.5k
  for (unsigned i = 0; i < lig_set_count; ++i) {
361
17.0k
    uint16_t offset_ligature_set = 0;
362
17.0k
    if (!subtable.ReadU16(&offset_ligature_set)) {
363
0
      return Error("Can't read ligature set offset %d", i);
364
0
    }
365
17.0k
    if (offset_ligature_set < ligature_set_end ||
366
17.0k
        offset_ligature_set >= length) {
367
42
      return Error("Bad ligature set offset %d for set %d", offset_ligature_set, i);
368
42
    }
369
17.0k
    if (!ParseLigatureSetTable(font, data + offset_ligature_set,
370
17.0k
                               length - offset_ligature_set, num_glyphs)) {
371
73
      return Error("Failed to parse ligature set %d", i);
372
73
    }
373
17.0k
  }
374
375
1.52k
  if (offset_coverage < ligature_set_end || offset_coverage >= length) {
376
24
    return Error("Bad coverage offset %d", offset_coverage);
377
24
  }
378
1.49k
  if (!ots::ParseCoverageTable(font, data + offset_coverage,
379
1.49k
                               length - offset_coverage, num_glyphs)) {
380
27
    return Error("Failed to parse coverage table");
381
27
  }
382
383
1.46k
  return true;
384
1.49k
}
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
243
                                                                 const size_t length) {
402
243
  Font* font = GetFont();
403
243
  Buffer subtable(data, length);
404
405
243
  uint16_t format = 0;
406
243
  uint16_t offset_coverage = 0;
407
408
243
  if (!subtable.ReadU16(&format) ||
409
242
      !subtable.ReadU16(&offset_coverage)) {
410
2
    return Error("Failed to read reverse chaining header");
411
2
  }
412
413
241
  OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
414
241
      font->GetTypedTable(OTS_TAG_MAXP));
415
241
  if (!maxp) {
416
0
    return Error("Required maxp table missing");
417
0
  }
418
241
  const uint16_t num_glyphs = maxp->num_glyphs;
419
420
241
  uint16_t backtrack_glyph_count = 0;
421
241
  if (!subtable.ReadU16(&backtrack_glyph_count)) {
422
1
    return Error("Failed to read backtrack glyph count in reverse chaining table");
423
1
  }
424
240
  std::vector<uint16_t> offsets_backtrack;
425
240
  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
21
      return Error("Failed to read backtrack offset %d", i);
430
21
    }
431
327k
    offsets_backtrack.push_back(offset);
432
327k
  }
433
434
219
  uint16_t lookahead_glyph_count = 0;
435
219
  if (!subtable.ReadU16(&lookahead_glyph_count)) {
436
1
    return Error("Failed to read look ahead glyph count");
437
1
  }
438
218
  std::vector<uint16_t> offsets_lookahead;
439
218
  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
216
  uint16_t glyph_count = 0;
449
216
  if (!subtable.ReadU16(&glyph_count)) {
450
13
    return Error("Can't read glyph count in reverse chaining table");
451
13
  }
452
682
  for (unsigned i = 0; i < glyph_count; ++i) {
453
494
    uint16_t substitute = 0;
454
494
    if (!subtable.ReadU16(&substitute)) {
455
2
      return Error("Failed to read substitution %d reverse chaining table", i);
456
2
    }
457
492
    if (substitute >= num_glyphs) {
458
13
      return Error("Bad substitute glyph %d in reverse chaining table substitution %d", substitute, i);
459
13
    }
460
492
  }
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
39
        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
9
        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
7.92k
                                           bool extension) const {
503
7.92k
  if (extension && lookup_type == GSUB_TYPE_EXTENSION_SUBSTITUTION)
504
10
    return false;
505
7.91k
  return lookup_type >= GSUB_TYPE_SINGLE && lookup_type < GSUB_TYPE_RESERVED;
506
7.92k
}
507
508
bool OpenTypeGSUB::ParseLookupSubtable(const uint8_t *data, const size_t length,
509
11.0k
                                       const uint16_t lookup_type) {
510
11.0k
  switch (lookup_type) {
511
2.28k
    case GSUB_TYPE_SINGLE:
512
2.28k
      return ParseSingleSubstitution(data, length);
513
574
    case GSUB_TYPE_MULTIPLE:
514
574
      return ParseMutipleSubstitution(data, length);
515
243
    case GSUB_TYPE_ALTERNATE:
516
243
      return ParseAlternateSubstitution(data, length);
517
1.65k
    case GSUB_TYPE_LIGATURE:
518
1.65k
      return ParseLigatureSubstitution(data, length);
519
587
    case GSUB_TYPE_CONTEXT:
520
587
      return ParseContextSubtable(data, length);
521
4.32k
    case GSUB_TYPE_CHANGING_CONTEXT:
522
4.32k
      return ParseChainingContextSubtable(data, length);
523
1.18k
    case GSUB_TYPE_EXTENSION_SUBSTITUTION:
524
1.18k
      return ParseExtensionSubtable(data, length);
525
243
    case GSUB_TYPE_REVERSE_CHAINING_CONTEXT_SINGLE:
526
243
      return ParseReverseChainingContextSingleSubstitution(data, length);
527
11.0k
  }
528
0
  return false;
529
11.0k
}
530
531
}  // namespace ots
532
533
#undef TABLE_NAME