Coverage Report

Created: 2026-08-13 06:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ots/src/math.cc
Line
Count
Source
1
// Copyright (c) 2014-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
// We use an underscore to avoid confusion with the standard math.h library.
6
#include "math_.h"
7
8
#include <limits>
9
#include <vector>
10
11
#include "layout.h"
12
#include "maxp.h"
13
14
// MATH - The MATH Table
15
// http://www.microsoft.com/typography/otspec/math.htm
16
17
namespace {
18
19
// The size of MATH header.
20
// Version
21
// MathConstants
22
// MathGlyphInfo
23
// MathVariants
24
const unsigned kMathHeaderSize = 4 + 3 * 2;
25
26
// The size of the MathGlyphInfo header.
27
// MathItalicsCorrectionInfo
28
// MathTopAccentAttachment
29
// ExtendedShapeCoverage
30
// MathKernInfo
31
const unsigned kMathGlyphInfoHeaderSize = 4 * 2;
32
33
// The size of the MathValueRecord.
34
// Value
35
// DeviceTable
36
const unsigned kMathValueRecordSize = 2 * 2;
37
38
// The size of the GlyphPartRecord.
39
// glyph
40
// StartConnectorLength
41
// EndConnectorLength
42
// FullAdvance
43
// PartFlags
44
const unsigned kGlyphPartRecordSize = 5 * 2;
45
46
}  // namespace
47
48
namespace ots {
49
50
// Shared Table: MathValueRecord
51
52
bool OpenTypeMATH::ParseMathValueRecord(ots::Buffer* subtable,
53
                                        const uint8_t *data,
54
95.0k
                                        const size_t length) {
55
  // Check the Value field.
56
95.0k
  if (!subtable->Skip(2)) {
57
32
    return OTS_FAILURE();
58
32
  }
59
60
  // Check the offset to device table.
61
95.0k
  uint16_t offset = 0;
62
95.0k
  if (!subtable->ReadU16(&offset)) {
63
21
    return OTS_FAILURE();
64
21
  }
65
94.9k
  if (offset) {
66
711
    if (offset >= length) {
67
155
      return OTS_FAILURE();
68
155
    }
69
556
    if (!ots::ParseDeviceTable(GetFont(), data + offset, length - offset)) {
70
215
      return OTS_FAILURE();
71
215
    }
72
556
  }
73
74
94.6k
  return true;
75
94.9k
}
76
77
bool OpenTypeMATH::ParseMathConstantsTable(const uint8_t *data,
78
1.86k
                                           size_t length) {
79
1.86k
  ots::Buffer subtable(data, length);
80
81
  // Part 1: int16 or uint16 constants.
82
  //  ScriptPercentScaleDown
83
  //  ScriptScriptPercentScaleDown
84
  //  DelimitedSubFormulaMinHeight
85
  //  DisplayOperatorMinHeight
86
1.86k
  if (!subtable.Skip(4 * 2)) {
87
21
    return OTS_FAILURE();
88
21
  }
89
90
  // Part 2: MathValueRecord constants.
91
  // MathLeading
92
  // AxisHeight
93
  // AccentBaseHeight
94
  // FlattenedAccentBaseHeight
95
  // SubscriptShiftDown
96
  // SubscriptTopMax
97
  // SubscriptBaselineDropMin
98
  // SuperscriptShiftUp
99
  // SuperscriptShiftUpCramped
100
  // SuperscriptBottomMin
101
  //
102
  // SuperscriptBaselineDropMax
103
  // SubSuperscriptGapMin
104
  // SuperscriptBottomMaxWithSubscript
105
  // SpaceAfterScript
106
  // UpperLimitGapMin
107
  // UpperLimitBaselineRiseMin
108
  // LowerLimitGapMin
109
  // LowerLimitBaselineDropMin
110
  // StackTopShiftUp
111
  // StackTopDisplayStyleShiftUp
112
  //
113
  // StackBottomShiftDown
114
  // StackBottomDisplayStyleShiftDown
115
  // StackGapMin
116
  // StackDisplayStyleGapMin
117
  // StretchStackTopShiftUp
118
  // StretchStackBottomShiftDown
119
  // StretchStackGapAboveMin
120
  // StretchStackGapBelowMin
121
  // FractionNumeratorShiftUp
122
  // FractionNumeratorDisplayStyleShiftUp
123
  //
124
  // FractionDenominatorShiftDown
125
  // FractionDenominatorDisplayStyleShiftDown
126
  // FractionNumeratorGapMin
127
  // FractionNumDisplayStyleGapMin
128
  // FractionRuleThickness
129
  // FractionDenominatorGapMin
130
  // FractionDenomDisplayStyleGapMin
131
  // SkewedFractionHorizontalGap
132
  // SkewedFractionVerticalGap
133
  // OverbarVerticalGap
134
  //
135
  // OverbarRuleThickness
136
  // OverbarExtraAscender
137
  // UnderbarVerticalGap
138
  // UnderbarRuleThickness
139
  // UnderbarExtraDescender
140
  // RadicalVerticalGap
141
  // RadicalDisplayStyleVerticalGap
142
  // RadicalRuleThickness
143
  // RadicalExtraAscender
144
  // RadicalKernBeforeDegree
145
  //
146
  // RadicalKernAfterDegree
147
88.5k
  for (unsigned i = 0; i < static_cast<unsigned>(51); ++i) {
148
86.9k
    if (!ParseMathValueRecord(&subtable, data, length)) {
149
229
      return OTS_FAILURE();
150
229
    }
151
86.9k
  }
152
153
  // Part 3: uint16 constant
154
  // RadicalDegreeBottomRaisePercent
155
1.61k
  if (!subtable.Skip(2)) {
156
0
    return OTS_FAILURE();
157
0
  }
158
159
1.61k
  return true;
160
1.61k
}
161
162
bool OpenTypeMATH::ParseMathValueRecordSequenceForGlyphs(ots::Buffer* subtable,
163
                                                         const uint8_t *data,
164
                                                         const size_t length,
165
255
                                                         const uint16_t num_glyphs) {
166
  // Check the header.
167
255
  uint16_t offset_coverage = 0;
168
255
  uint16_t sequence_count = 0;
169
255
  if (!subtable->ReadU16(&offset_coverage) ||
170
253
      !subtable->ReadU16(&sequence_count)) {
171
9
    return OTS_FAILURE();
172
9
  }
173
174
246
  const unsigned sequence_end = static_cast<unsigned>(2 * 2) +
175
246
      sequence_count * kMathValueRecordSize;
176
246
  if (sequence_end > std::numeric_limits<uint16_t>::max()) {
177
27
    return OTS_FAILURE();
178
27
  }
179
180
  // Check coverage table.
181
219
  if (offset_coverage < sequence_end || offset_coverage >= length) {
182
103
    return OTS_FAILURE();
183
103
  }
184
116
  if (!ots::ParseCoverageTable(GetFont(), data + offset_coverage,
185
116
                               length - offset_coverage,
186
116
                               num_glyphs, sequence_count)) {
187
49
    return OTS_FAILURE();
188
49
  }
189
190
  // Check sequence.
191
119
  for (unsigned i = 0; i < sequence_count; ++i) {
192
62
    if (!ParseMathValueRecord(subtable, data, length)) {
193
10
      return OTS_FAILURE();
194
10
    }
195
62
  }
196
197
57
  return true;
198
67
}
199
200
bool OpenTypeMATH::ParseMathItalicsCorrectionInfoTable(const uint8_t *data,
201
                                                       size_t length,
202
129
                                                       const uint16_t num_glyphs) {
203
129
  ots::Buffer subtable(data, length);
204
129
  return ParseMathValueRecordSequenceForGlyphs(&subtable, data, length,
205
129
                                               num_glyphs);
206
129
}
207
208
bool OpenTypeMATH::ParseMathTopAccentAttachmentTable(const uint8_t *data,
209
                                                     size_t length,
210
126
                                                     const uint16_t num_glyphs) {
211
126
  ots::Buffer subtable(data, length);
212
126
  return ParseMathValueRecordSequenceForGlyphs(&subtable, data, length,
213
126
                                               num_glyphs);
214
126
}
215
216
508
bool OpenTypeMATH::ParseMathKernTable(const uint8_t *data, size_t length) {
217
508
  ots::Buffer subtable(data, length);
218
219
  // Check the Height count.
220
508
  uint16_t height_count = 0;
221
508
  if (!subtable.ReadU16(&height_count)) {
222
0
    return OTS_FAILURE();
223
0
  }
224
225
  // Check the Correction Heights.
226
7.07k
  for (unsigned i = 0; i < height_count; ++i) {
227
6.65k
    if (!ParseMathValueRecord(&subtable, data, length)) {
228
95
      return OTS_FAILURE();
229
95
    }
230
6.65k
  }
231
232
  // Check the Kern Values.
233
1.61k
  for (unsigned i = 0; i <= height_count; ++i) {
234
1.24k
    if (!ParseMathValueRecord(&subtable, data, length)) {
235
48
      return OTS_FAILURE();
236
48
    }
237
1.24k
  }
238
239
365
  return true;
240
413
}
241
242
bool OpenTypeMATH::ParseMathKernInfoTable(const uint8_t *data,
243
                                          size_t length,
244
502
                                          const uint16_t num_glyphs) {
245
502
  ots::Buffer subtable(data, length);
246
247
  // Check the header.
248
502
  uint16_t offset_coverage = 0;
249
502
  uint16_t sequence_count = 0;
250
502
  if (!subtable.ReadU16(&offset_coverage) ||
251
462
      !subtable.ReadU16(&sequence_count)) {
252
45
    return OTS_FAILURE();
253
45
  }
254
255
457
  const unsigned sequence_end = static_cast<unsigned>(2 * 2) +
256
457
    sequence_count * 4 * 2;
257
457
  if (sequence_end > std::numeric_limits<uint16_t>::max()) {
258
31
    return OTS_FAILURE();
259
31
  }
260
261
  // Check coverage table.
262
426
  if (offset_coverage < sequence_end || offset_coverage >= length) {
263
67
    return OTS_FAILURE();
264
67
  }
265
359
  if (!ots::ParseCoverageTable(GetFont(), data + offset_coverage, length - offset_coverage,
266
359
                               num_glyphs, sequence_count)) {
267
41
    return OTS_FAILURE();
268
41
  }
269
270
  // Check sequence of MathKernInfoRecord
271
348
  for (unsigned i = 0; i < sequence_count; ++i) {
272
    // Check TopRight, TopLeft, BottomRight and BottomLeft Math Kern.
273
793
    for (unsigned j = 0; j < 4; ++j) {
274
763
      uint16_t offset_math_kern = 0;
275
763
      if (!subtable.ReadU16(&offset_math_kern)) {
276
0
        return OTS_FAILURE();
277
0
      }
278
763
      if (offset_math_kern) {
279
644
        if (offset_math_kern < sequence_end || offset_math_kern >= length ||
280
508
            !ParseMathKernTable(data + offset_math_kern,
281
508
                                length - offset_math_kern)) {
282
279
          return OTS_FAILURE();
283
279
        }
284
644
      }
285
763
    }
286
309
  }
287
288
39
  return true;
289
318
}
290
291
bool OpenTypeMATH::ParseMathGlyphInfoTable(const uint8_t *data,
292
                                           size_t length,
293
1.61k
                                           const uint16_t num_glyphs) {
294
1.61k
  ots::Buffer subtable(data, length);
295
296
  // Check Header.
297
1.61k
  uint16_t offset_math_italics_correction_info = 0;
298
1.61k
  uint16_t offset_math_top_accent_attachment = 0;
299
1.61k
  uint16_t offset_extended_shaped_coverage = 0;
300
1.61k
  uint16_t offset_math_kern_info = 0;
301
1.61k
  if (!subtable.ReadU16(&offset_math_italics_correction_info) ||
302
1.59k
      !subtable.ReadU16(&offset_math_top_accent_attachment) ||
303
1.59k
      !subtable.ReadU16(&offset_extended_shaped_coverage) ||
304
1.55k
      !subtable.ReadU16(&offset_math_kern_info)) {
305
88
    return OTS_FAILURE();
306
88
  }
307
308
  // Check subtables.
309
  // The specification does not say whether the offsets for
310
  // MathItalicsCorrectionInfo, MathTopAccentAttachment and MathKernInfo may
311
  // be NULL, but that's the case in some fonts (e.g STIX) so we accept that.
312
1.53k
  if (offset_math_italics_correction_info) {
313
160
    if (offset_math_italics_correction_info >= length ||
314
139
        offset_math_italics_correction_info < kMathGlyphInfoHeaderSize ||
315
129
        !ParseMathItalicsCorrectionInfoTable(
316
129
            data + offset_math_italics_correction_info,
317
129
            length - offset_math_italics_correction_info,
318
136
            num_glyphs)) {
319
136
      return OTS_FAILURE();
320
136
    }
321
160
  }
322
1.39k
  if (offset_math_top_accent_attachment) {
323
179
    if (offset_math_top_accent_attachment >= length ||
324
164
        offset_math_top_accent_attachment < kMathGlyphInfoHeaderSize ||
325
126
        !ParseMathTopAccentAttachmentTable(data +
326
126
                                           offset_math_top_accent_attachment,
327
126
                                           length -
328
126
                                           offset_math_top_accent_attachment,
329
146
                                           num_glyphs)) {
330
146
      return OTS_FAILURE();
331
146
    }
332
179
  }
333
1.24k
  if (offset_extended_shaped_coverage) {
334
376
    if (offset_extended_shaped_coverage >= length ||
335
368
        offset_extended_shaped_coverage < kMathGlyphInfoHeaderSize ||
336
342
        !ots::ParseCoverageTable(GetFont(), data + offset_extended_shaped_coverage,
337
342
                                 length - offset_extended_shaped_coverage,
338
342
                                 num_glyphs)) {
339
119
      return OTS_FAILURE();
340
119
    }
341
376
  }
342
1.13k
  if (offset_math_kern_info) {
343
542
    if (offset_math_kern_info >= length ||
344
528
        offset_math_kern_info < kMathGlyphInfoHeaderSize ||
345
502
        !ParseMathKernInfoTable(data + offset_math_kern_info,
346
503
                                length - offset_math_kern_info, num_glyphs)) {
347
503
      return OTS_FAILURE();
348
503
    }
349
542
  }
350
351
627
  return true;
352
1.13k
}
353
354
bool OpenTypeMATH::ParseGlyphAssemblyTable(const uint8_t *data,
355
                                           size_t length,
356
107
                                           const uint16_t num_glyphs) {
357
107
  ots::Buffer subtable(data, length);
358
359
  // Check the header.
360
107
  uint16_t part_count = 0;
361
107
  if (!ParseMathValueRecord(&subtable, data, length) ||
362
66
      !subtable.ReadU16(&part_count)) {
363
44
    return OTS_FAILURE();
364
44
  }
365
366
63
  const unsigned sequence_end = kMathValueRecordSize +
367
63
    static_cast<unsigned>(2) + part_count * kGlyphPartRecordSize;
368
63
  if (sequence_end > std::numeric_limits<uint16_t>::max()) {
369
10
    return OTS_FAILURE();
370
10
  }
371
372
  // Check the sequence of GlyphPartRecord.
373
735
  for (unsigned i = 0; i < part_count; ++i) {
374
720
    uint16_t glyph = 0;
375
720
    uint16_t part_flags = 0;
376
720
    if (!subtable.ReadU16(&glyph) ||
377
705
        !subtable.Skip(2 * 3) ||
378
692
        !subtable.ReadU16(&part_flags)) {
379
29
      return OTS_FAILURE();
380
29
    }
381
691
    if (glyph >= num_glyphs) {
382
5
      return Error("bad glyph ID: %u", glyph);
383
5
    }
384
686
    if (part_flags & ~0x00000001) {
385
4
      return Error("unknown part flag: %u", part_flags);
386
4
    }
387
686
  }
388
389
15
  return true;
390
53
}
391
392
bool OpenTypeMATH::ParseMathGlyphConstructionTable(const uint8_t *data,
393
                                                   size_t length,
394
259
                                                   const uint16_t num_glyphs) {
395
259
  ots::Buffer subtable(data, length);
396
397
  // Check the header.
398
259
  uint16_t offset_glyph_assembly = 0;
399
259
  uint16_t variant_count = 0;
400
259
  if (!subtable.ReadU16(&offset_glyph_assembly) ||
401
251
      !subtable.ReadU16(&variant_count)) {
402
10
    return OTS_FAILURE();
403
10
  }
404
405
249
  const unsigned sequence_end = static_cast<unsigned>(2 * 2) +
406
249
    variant_count * 2 * 2;
407
249
  if (sequence_end > std::numeric_limits<uint16_t>::max()) {
408
26
    return OTS_FAILURE();
409
26
  }
410
411
  // Check the GlyphAssembly offset.
412
223
  if (offset_glyph_assembly) {
413
142
    if (offset_glyph_assembly >= length ||
414
123
        offset_glyph_assembly < sequence_end) {
415
35
      return OTS_FAILURE();
416
35
    }
417
107
    if (!ParseGlyphAssemblyTable(data + offset_glyph_assembly,
418
107
                                 length - offset_glyph_assembly, num_glyphs)) {
419
92
      return OTS_FAILURE();
420
92
    }
421
107
  }
422
423
  // Check the sequence of MathGlyphVariantRecord.
424
422
  for (unsigned i = 0; i < variant_count; ++i) {
425
403
    uint16_t glyph = 0;
426
403
    if (!subtable.ReadU16(&glyph) ||
427
390
        !subtable.Skip(2)) {
428
20
      return OTS_FAILURE();
429
20
    }
430
383
    if (glyph >= num_glyphs) {
431
57
      return Error("bad glyph ID: %u", glyph);
432
57
    }
433
383
  }
434
435
19
  return true;
436
96
}
437
438
bool OpenTypeMATH::ParseMathGlyphConstructionSequence(ots::Buffer* subtable,
439
                                                      const uint8_t *data,
440
                                                      size_t length,
441
                                                      const uint16_t num_glyphs,
442
                                                      uint16_t offset_coverage,
443
                                                      uint16_t glyph_count,
444
715
                                                      const unsigned sequence_end) {
445
  // Zero glyph count, nothing to parse.
446
715
  if (!glyph_count) {
447
217
    return true;
448
217
  }
449
450
  // Check coverage table.
451
498
  if (offset_coverage < sequence_end || offset_coverage >= length) {
452
108
    return OTS_FAILURE();
453
108
  }
454
390
  if (!ots::ParseCoverageTable(GetFont(), data + offset_coverage,
455
390
                               length - offset_coverage,
456
390
                               num_glyphs, glyph_count)) {
457
81
    return OTS_FAILURE();
458
81
  }
459
460
  // Check sequence of MathGlyphConstruction.
461
328
  for (unsigned i = 0; i < glyph_count; ++i) {
462
309
      uint16_t offset_glyph_construction = 0;
463
309
      if (!subtable->ReadU16(&offset_glyph_construction)) {
464
0
        return OTS_FAILURE();
465
0
      }
466
309
      if (offset_glyph_construction < sequence_end ||
467
295
          offset_glyph_construction >= length ||
468
259
          !ParseMathGlyphConstructionTable(data + offset_glyph_construction,
469
259
                                           length - offset_glyph_construction,
470
290
                                           num_glyphs)) {
471
290
        return OTS_FAILURE();
472
290
      }
473
309
  }
474
475
19
  return true;
476
309
}
477
478
bool OpenTypeMATH::ParseMathVariantsTable(const uint8_t *data,
479
                                          size_t length,
480
627
                                          const uint16_t num_glyphs) {
481
627
  ots::Buffer subtable(data, length);
482
483
  // Check the header.
484
627
  uint16_t offset_vert_glyph_coverage = 0;
485
627
  uint16_t offset_horiz_glyph_coverage = 0;
486
627
  uint16_t vert_glyph_count = 0;
487
627
  uint16_t horiz_glyph_count = 0;
488
627
  if (!subtable.Skip(2) ||  // MinConnectorOverlap
489
627
      !subtable.ReadU16(&offset_vert_glyph_coverage) ||
490
619
      !subtable.ReadU16(&offset_horiz_glyph_coverage) ||
491
614
      !subtable.ReadU16(&vert_glyph_count) ||
492
603
      !subtable.ReadU16(&horiz_glyph_count)) {
493
41
    return OTS_FAILURE();
494
41
  }
495
496
586
  const unsigned sequence_end = 5 * 2 + vert_glyph_count * 2 +
497
586
    horiz_glyph_count * 2;
498
586
  if (sequence_end > std::numeric_limits<uint16_t>::max()) {
499
35
    return OTS_FAILURE();
500
35
  }
501
502
551
  if (!ParseMathGlyphConstructionSequence(&subtable, data, length, num_glyphs,
503
551
                                          offset_vert_glyph_coverage,
504
551
                                          vert_glyph_count,
505
551
                                          sequence_end) ||
506
164
      !ParseMathGlyphConstructionSequence(&subtable, data, length, num_glyphs,
507
164
                                          offset_horiz_glyph_coverage,
508
164
                                          horiz_glyph_count,
509
479
                                          sequence_end)) {
510
479
    return OTS_FAILURE();
511
479
  }
512
513
72
  return true;
514
551
}
515
516
2.93k
bool OpenTypeMATH::Parse(const uint8_t *data, size_t length) {
517
  // Grab the number of glyphs in the font from the maxp table to check
518
  // GlyphIDs in MATH table.
519
2.93k
  OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
520
2.93k
      GetFont()->GetTypedTable(OTS_TAG_MAXP));
521
2.93k
  if (!maxp) {
522
0
    return Error("Required maxp table missing");
523
0
  }
524
2.93k
  const uint16_t num_glyphs = maxp->num_glyphs;
525
526
2.93k
  Buffer table(data, length);
527
528
2.93k
  uint32_t version = 0;
529
2.93k
  if (!table.ReadU32(&version)) {
530
1
    return OTS_FAILURE();
531
1
  }
532
2.93k
  if (version != 0x00010000) {
533
844
    return Drop("bad MATH version");
534
844
  }
535
536
2.08k
  uint16_t offset_math_constants = 0;
537
2.08k
  uint16_t offset_math_glyph_info = 0;
538
2.08k
  uint16_t offset_math_variants = 0;
539
2.08k
  if (!table.ReadU16(&offset_math_constants) ||
540
2.07k
      !table.ReadU16(&offset_math_glyph_info) ||
541
2.06k
      !table.ReadU16(&offset_math_variants)) {
542
34
    return OTS_FAILURE();
543
34
  }
544
545
2.05k
  if (offset_math_constants >= length ||
546
2.01k
      offset_math_constants < kMathHeaderSize ||
547
1.97k
      offset_math_glyph_info >= length ||
548
1.95k
      offset_math_glyph_info < kMathHeaderSize ||
549
1.93k
      offset_math_variants >= length ||
550
1.90k
      offset_math_variants < kMathHeaderSize) {
551
184
    return Drop("bad offset in MATH header");
552
184
  }
553
554
1.86k
  if (!ParseMathConstantsTable(data + offset_math_constants,
555
1.86k
                               length - offset_math_constants)) {
556
250
    return Drop("failed to parse MathConstants table");
557
250
  }
558
1.61k
  if (!ParseMathGlyphInfoTable(data + offset_math_glyph_info,
559
1.61k
                               length - offset_math_glyph_info, num_glyphs)) {
560
992
    return Drop("failed to parse MathGlyphInfo table");
561
992
  }
562
627
  if (!ParseMathVariantsTable(data + offset_math_variants,
563
627
                              length - offset_math_variants, num_glyphs)) {
564
555
    return Drop("failed to parse MathVariants table");
565
555
  }
566
567
72
  this->m_data = data;
568
72
  this->m_length = length;
569
72
  return true;
570
627
}
571
572
70
bool OpenTypeMATH::Serialize(OTSStream *out) {
573
70
  if (!out->Write(this->m_data, this->m_length)) {
574
0
    return OTS_FAILURE();
575
0
  }
576
577
70
  return true;
578
70
}
579
580
15.6k
bool OpenTypeMATH::ShouldSerialize() {
581
15.6k
  return Table::ShouldSerialize() && this->m_data != NULL;
582
15.6k
}
583
584
}  // namespace ots