Coverage Report

Created: 2026-01-09 06:55

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
33.0k
                                        const size_t length) {
55
  // Check the Value field.
56
33.0k
  if (!subtable->Skip(2)) {
57
4
    return OTS_FAILURE();
58
4
  }
59
60
  // Check the offset to device table.
61
33.0k
  uint16_t offset = 0;
62
33.0k
  if (!subtable->ReadU16(&offset)) {
63
0
    return OTS_FAILURE();
64
0
  }
65
33.0k
  if (offset) {
66
632
    if (offset >= length) {
67
81
      return OTS_FAILURE();
68
81
    }
69
551
    if (!ots::ParseDeviceTable(GetFont(), data + offset, length - offset)) {
70
87
      return OTS_FAILURE();
71
87
    }
72
551
  }
73
74
32.8k
  return true;
75
33.0k
}
76
77
bool OpenTypeMATH::ParseMathConstantsTable(const uint8_t *data,
78
717
                                           size_t length) {
79
717
  ots::Buffer subtable(data, length);
80
81
  // Part 1: int16 or uint16 constants.
82
  //  ScriptPercentScaleDown
83
  //  ScriptScriptPercentScaleDown
84
  //  DelimitedSubFormulaMinHeight
85
  //  DisplayOperatorMinHeight
86
717
  if (!subtable.Skip(4 * 2)) {
87
1
    return OTS_FAILURE();
88
1
  }
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
33.2k
  for (unsigned i = 0; i < static_cast<unsigned>(51); ++i) {
148
32.6k
    if (!ParseMathValueRecord(&subtable, data, length)) {
149
100
      return OTS_FAILURE();
150
100
    }
151
32.6k
  }
152
153
  // Part 3: uint16 constant
154
  // RadicalDegreeBottomRaisePercent
155
616
  if (!subtable.Skip(2)) {
156
0
    return OTS_FAILURE();
157
0
  }
158
159
616
  return true;
160
616
}
161
162
bool OpenTypeMATH::ParseMathValueRecordSequenceForGlyphs(ots::Buffer* subtable,
163
                                                         const uint8_t *data,
164
                                                         const size_t length,
165
151
                                                         const uint16_t num_glyphs) {
166
  // Check the header.
167
151
  uint16_t offset_coverage = 0;
168
151
  uint16_t sequence_count = 0;
169
151
  if (!subtable->ReadU16(&offset_coverage) ||
170
148
      !subtable->ReadU16(&sequence_count)) {
171
21
    return OTS_FAILURE();
172
21
  }
173
174
130
  const unsigned sequence_end = static_cast<unsigned>(2 * 2) +
175
130
      sequence_count * kMathValueRecordSize;
176
130
  if (sequence_end > std::numeric_limits<uint16_t>::max()) {
177
13
    return OTS_FAILURE();
178
13
  }
179
180
  // Check coverage table.
181
117
  if (offset_coverage < sequence_end || offset_coverage >= length) {
182
59
    return OTS_FAILURE();
183
59
  }
184
58
  if (!ots::ParseCoverageTable(GetFont(), data + offset_coverage,
185
58
                               length - offset_coverage,
186
58
                               num_glyphs, sequence_count)) {
187
19
    return OTS_FAILURE();
188
19
  }
189
190
  // Check sequence.
191
58
  for (unsigned i = 0; i < sequence_count; ++i) {
192
19
    if (!ParseMathValueRecord(subtable, data, length)) {
193
0
      return OTS_FAILURE();
194
0
    }
195
19
  }
196
197
39
  return true;
198
39
}
199
200
bool OpenTypeMATH::ParseMathItalicsCorrectionInfoTable(const uint8_t *data,
201
                                                       size_t length,
202
110
                                                       const uint16_t num_glyphs) {
203
110
  ots::Buffer subtable(data, length);
204
110
  return ParseMathValueRecordSequenceForGlyphs(&subtable, data, length,
205
110
                                               num_glyphs);
206
110
}
207
208
bool OpenTypeMATH::ParseMathTopAccentAttachmentTable(const uint8_t *data,
209
                                                     size_t length,
210
41
                                                     const uint16_t num_glyphs) {
211
41
  ots::Buffer subtable(data, length);
212
41
  return ParseMathValueRecordSequenceForGlyphs(&subtable, data, length,
213
41
                                               num_glyphs);
214
41
}
215
216
151
bool OpenTypeMATH::ParseMathKernTable(const uint8_t *data, size_t length) {
217
151
  ots::Buffer subtable(data, length);
218
219
  // Check the Height count.
220
151
  uint16_t height_count = 0;
221
151
  if (!subtable.ReadU16(&height_count)) {
222
1
    return OTS_FAILURE();
223
1
  }
224
225
  // Check the Correction Heights.
226
248
  for (unsigned i = 0; i < height_count; ++i) {
227
124
    if (!ParseMathValueRecord(&subtable, data, length)) {
228
26
      return OTS_FAILURE();
229
26
    }
230
124
  }
231
232
  // Check the Kern Values.
233
238
  for (unsigned i = 0; i <= height_count; ++i) {
234
141
    if (!ParseMathValueRecord(&subtable, data, length)) {
235
27
      return OTS_FAILURE();
236
27
    }
237
141
  }
238
239
97
  return true;
240
124
}
241
242
bool OpenTypeMATH::ParseMathKernInfoTable(const uint8_t *data,
243
                                          size_t length,
244
236
                                          const uint16_t num_glyphs) {
245
236
  ots::Buffer subtable(data, length);
246
247
  // Check the header.
248
236
  uint16_t offset_coverage = 0;
249
236
  uint16_t sequence_count = 0;
250
236
  if (!subtable.ReadU16(&offset_coverage) ||
251
229
      !subtable.ReadU16(&sequence_count)) {
252
7
    return OTS_FAILURE();
253
7
  }
254
255
229
  const unsigned sequence_end = static_cast<unsigned>(2 * 2) +
256
229
    sequence_count * 4 * 2;
257
229
  if (sequence_end > std::numeric_limits<uint16_t>::max()) {
258
18
    return OTS_FAILURE();
259
18
  }
260
261
  // Check coverage table.
262
211
  if (offset_coverage < sequence_end || offset_coverage >= length) {
263
58
    return OTS_FAILURE();
264
58
  }
265
153
  if (!ots::ParseCoverageTable(GetFont(), data + offset_coverage, length - offset_coverage,
266
153
                               num_glyphs, sequence_count)) {
267
25
    return OTS_FAILURE();
268
25
  }
269
270
  // Check sequence of MathKernInfoRecord
271
134
  for (unsigned i = 0; i < sequence_count; ++i) {
272
    // Check TopRight, TopLeft, BottomRight and BottomLeft Math Kern.
273
341
    for (unsigned j = 0; j < 4; ++j) {
274
335
      uint16_t offset_math_kern = 0;
275
335
      if (!subtable.ReadU16(&offset_math_kern)) {
276
0
        return OTS_FAILURE();
277
0
      }
278
335
      if (offset_math_kern) {
279
204
        if (offset_math_kern < sequence_end || offset_math_kern >= length ||
280
151
            !ParseMathKernTable(data + offset_math_kern,
281
151
                                length - offset_math_kern)) {
282
107
          return OTS_FAILURE();
283
107
        }
284
204
      }
285
335
    }
286
113
  }
287
288
21
  return true;
289
128
}
290
291
bool OpenTypeMATH::ParseMathGlyphInfoTable(const uint8_t *data,
292
                                           size_t length,
293
616
                                           const uint16_t num_glyphs) {
294
616
  ots::Buffer subtable(data, length);
295
296
  // Check Header.
297
616
  uint16_t offset_math_italics_correction_info = 0;
298
616
  uint16_t offset_math_top_accent_attachment = 0;
299
616
  uint16_t offset_extended_shaped_coverage = 0;
300
616
  uint16_t offset_math_kern_info = 0;
301
616
  if (!subtable.ReadU16(&offset_math_italics_correction_info) ||
302
614
      !subtable.ReadU16(&offset_math_top_accent_attachment) ||
303
614
      !subtable.ReadU16(&offset_extended_shaped_coverage) ||
304
613
      !subtable.ReadU16(&offset_math_kern_info)) {
305
3
    return OTS_FAILURE();
306
3
  }
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
613
  if (offset_math_italics_correction_info) {
313
164
    if (offset_math_italics_correction_info >= length ||
314
135
        offset_math_italics_correction_info < kMathGlyphInfoHeaderSize ||
315
110
        !ParseMathItalicsCorrectionInfoTable(
316
110
            data + offset_math_italics_correction_info,
317
110
            length - offset_math_italics_correction_info,
318
156
            num_glyphs)) {
319
156
      return OTS_FAILURE();
320
156
    }
321
164
  }
322
457
  if (offset_math_top_accent_attachment) {
323
65
    if (offset_math_top_accent_attachment >= length ||
324
55
        offset_math_top_accent_attachment < kMathGlyphInfoHeaderSize ||
325
41
        !ParseMathTopAccentAttachmentTable(data +
326
41
                                           offset_math_top_accent_attachment,
327
41
                                           length -
328
41
                                           offset_math_top_accent_attachment,
329
41
                                           num_glyphs)) {
330
34
      return OTS_FAILURE();
331
34
    }
332
65
  }
333
423
  if (offset_extended_shaped_coverage) {
334
27
    if (offset_extended_shaped_coverage >= length ||
335
22
        offset_extended_shaped_coverage < kMathGlyphInfoHeaderSize ||
336
3
        !ots::ParseCoverageTable(GetFont(), data + offset_extended_shaped_coverage,
337
3
                                 length - offset_extended_shaped_coverage,
338
24
                                 num_glyphs)) {
339
24
      return OTS_FAILURE();
340
24
    }
341
27
  }
342
399
  if (offset_math_kern_info) {
343
237
    if (offset_math_kern_info >= length ||
344
236
        offset_math_kern_info < kMathGlyphInfoHeaderSize ||
345
236
        !ParseMathKernInfoTable(data + offset_math_kern_info,
346
236
                                length - offset_math_kern_info, num_glyphs)) {
347
216
      return OTS_FAILURE();
348
216
    }
349
237
  }
350
351
183
  return true;
352
399
}
353
354
bool OpenTypeMATH::ParseGlyphAssemblyTable(const uint8_t *data,
355
                                           size_t length,
356
72
                                           const uint16_t num_glyphs) {
357
72
  ots::Buffer subtable(data, length);
358
359
  // Check the header.
360
72
  uint16_t part_count = 0;
361
72
  if (!ParseMathValueRecord(&subtable, data, length) ||
362
53
      !subtable.ReadU16(&part_count)) {
363
29
    return OTS_FAILURE();
364
29
  }
365
366
43
  const unsigned sequence_end = kMathValueRecordSize +
367
43
    static_cast<unsigned>(2) + part_count * kGlyphPartRecordSize;
368
43
  if (sequence_end > std::numeric_limits<uint16_t>::max()) {
369
5
    return OTS_FAILURE();
370
5
  }
371
372
  // Check the sequence of GlyphPartRecord.
373
53
  for (unsigned i = 0; i < part_count; ++i) {
374
47
    uint16_t glyph = 0;
375
47
    uint16_t part_flags = 0;
376
47
    if (!subtable.ReadU16(&glyph) ||
377
47
        !subtable.Skip(2 * 3) ||
378
41
        !subtable.ReadU16(&part_flags)) {
379
6
      return OTS_FAILURE();
380
6
    }
381
41
    if (glyph >= num_glyphs) {
382
14
      return Error("bad glyph ID: %u", glyph);
383
14
    }
384
27
    if (part_flags & ~0x00000001) {
385
12
      return Error("unknown part flag: %u", part_flags);
386
12
    }
387
27
  }
388
389
6
  return true;
390
38
}
391
392
bool OpenTypeMATH::ParseMathGlyphConstructionTable(const uint8_t *data,
393
                                                   size_t length,
394
134
                                                   const uint16_t num_glyphs) {
395
134
  ots::Buffer subtable(data, length);
396
397
  // Check the header.
398
134
  uint16_t offset_glyph_assembly = 0;
399
134
  uint16_t variant_count = 0;
400
134
  if (!subtable.ReadU16(&offset_glyph_assembly) ||
401
133
      !subtable.ReadU16(&variant_count)) {
402
10
    return OTS_FAILURE();
403
10
  }
404
405
124
  const unsigned sequence_end = static_cast<unsigned>(2 * 2) +
406
124
    variant_count * 2 * 2;
407
124
  if (sequence_end > std::numeric_limits<uint16_t>::max()) {
408
0
    return OTS_FAILURE();
409
0
  }
410
411
  // Check the GlyphAssembly offset.
412
124
  if (offset_glyph_assembly) {
413
84
    if (offset_glyph_assembly >= length ||
414
75
        offset_glyph_assembly < sequence_end) {
415
12
      return OTS_FAILURE();
416
12
    }
417
72
    if (!ParseGlyphAssemblyTable(data + offset_glyph_assembly,
418
72
                                 length - offset_glyph_assembly, num_glyphs)) {
419
66
      return OTS_FAILURE();
420
66
    }
421
72
  }
422
423
  // Check the sequence of MathGlyphVariantRecord.
424
107
  for (unsigned i = 0; i < variant_count; ++i) {
425
93
    uint16_t glyph = 0;
426
93
    if (!subtable.ReadU16(&glyph) ||
427
93
        !subtable.Skip(2)) {
428
4
      return OTS_FAILURE();
429
4
    }
430
89
    if (glyph >= num_glyphs) {
431
28
      return Error("bad glyph ID: %u", glyph);
432
28
    }
433
89
  }
434
435
14
  return true;
436
46
}
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
186
                                                      const unsigned sequence_end) {
445
  // Zero glyph count, nothing to parse.
446
186
  if (!glyph_count) {
447
28
    return true;
448
28
  }
449
450
  // Check coverage table.
451
158
  if (offset_coverage < sequence_end || offset_coverage >= length) {
452
7
    return OTS_FAILURE();
453
7
  }
454
151
  if (!ots::ParseCoverageTable(GetFont(), data + offset_coverage,
455
151
                               length - offset_coverage,
456
151
                               num_glyphs, glyph_count)) {
457
9
    return OTS_FAILURE();
458
9
  }
459
460
  // Check sequence of MathGlyphConstruction.
461
156
  for (unsigned i = 0; i < glyph_count; ++i) {
462
142
      uint16_t offset_glyph_construction = 0;
463
142
      if (!subtable->ReadU16(&offset_glyph_construction)) {
464
0
        return OTS_FAILURE();
465
0
      }
466
142
      if (offset_glyph_construction < sequence_end ||
467
141
          offset_glyph_construction >= length ||
468
134
          !ParseMathGlyphConstructionTable(data + offset_glyph_construction,
469
134
                                           length - offset_glyph_construction,
470
134
                                           num_glyphs)) {
471
128
        return OTS_FAILURE();
472
128
      }
473
142
  }
474
475
14
  return true;
476
142
}
477
478
bool OpenTypeMATH::ParseMathVariantsTable(const uint8_t *data,
479
                                          size_t length,
480
183
                                          const uint16_t num_glyphs) {
481
183
  ots::Buffer subtable(data, length);
482
483
  // Check the header.
484
183
  uint16_t offset_vert_glyph_coverage = 0;
485
183
  uint16_t offset_horiz_glyph_coverage = 0;
486
183
  uint16_t vert_glyph_count = 0;
487
183
  uint16_t horiz_glyph_count = 0;
488
183
  if (!subtable.Skip(2) ||  // MinConnectorOverlap
489
183
      !subtable.ReadU16(&offset_vert_glyph_coverage) ||
490
182
      !subtable.ReadU16(&offset_horiz_glyph_coverage) ||
491
181
      !subtable.ReadU16(&vert_glyph_count) ||
492
178
      !subtable.ReadU16(&horiz_glyph_count)) {
493
6
    return OTS_FAILURE();
494
6
  }
495
496
177
  const unsigned sequence_end = 5 * 2 + vert_glyph_count * 2 +
497
177
    horiz_glyph_count * 2;
498
177
  if (sequence_end > std::numeric_limits<uint16_t>::max()) {
499
14
    return OTS_FAILURE();
500
14
  }
501
502
163
  if (!ParseMathGlyphConstructionSequence(&subtable, data, length, num_glyphs,
503
163
                                          offset_vert_glyph_coverage,
504
163
                                          vert_glyph_count,
505
163
                                          sequence_end) ||
506
23
      !ParseMathGlyphConstructionSequence(&subtable, data, length, num_glyphs,
507
23
                                          offset_horiz_glyph_coverage,
508
23
                                          horiz_glyph_count,
509
144
                                          sequence_end)) {
510
144
    return OTS_FAILURE();
511
144
  }
512
513
19
  return true;
514
163
}
515
516
1.13k
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
1.13k
  OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
520
1.13k
      GetFont()->GetTypedTable(OTS_TAG_MAXP));
521
1.13k
  if (!maxp) {
522
0
    return Error("Required maxp table missing");
523
0
  }
524
1.13k
  const uint16_t num_glyphs = maxp->num_glyphs;
525
526
1.13k
  Buffer table(data, length);
527
528
1.13k
  uint32_t version = 0;
529
1.13k
  if (!table.ReadU32(&version)) {
530
1
    return OTS_FAILURE();
531
1
  }
532
1.13k
  if (version != 0x00010000) {
533
318
    return Drop("bad MATH version");
534
318
  }
535
536
812
  uint16_t offset_math_constants = 0;
537
812
  uint16_t offset_math_glyph_info = 0;
538
812
  uint16_t offset_math_variants = 0;
539
812
  if (!table.ReadU16(&offset_math_constants) ||
540
811
      !table.ReadU16(&offset_math_glyph_info) ||
541
810
      !table.ReadU16(&offset_math_variants)) {
542
2
    return OTS_FAILURE();
543
2
  }
544
545
810
  if (offset_math_constants >= length ||
546
806
      offset_math_constants < kMathHeaderSize ||
547
772
      offset_math_glyph_info >= length ||
548
768
      offset_math_glyph_info < kMathHeaderSize ||
549
748
      offset_math_variants >= length ||
550
724
      offset_math_variants < kMathHeaderSize) {
551
93
    return Drop("bad offset in MATH header");
552
93
  }
553
554
717
  if (!ParseMathConstantsTable(data + offset_math_constants,
555
717
                               length - offset_math_constants)) {
556
101
    return Drop("failed to parse MathConstants table");
557
101
  }
558
616
  if (!ParseMathGlyphInfoTable(data + offset_math_glyph_info,
559
616
                               length - offset_math_glyph_info, num_glyphs)) {
560
433
    return Drop("failed to parse MathGlyphInfo table");
561
433
  }
562
183
  if (!ParseMathVariantsTable(data + offset_math_variants,
563
183
                              length - offset_math_variants, num_glyphs)) {
564
164
    return Drop("failed to parse MathVariants table");
565
164
  }
566
567
19
  this->m_data = data;
568
19
  this->m_length = length;
569
19
  return true;
570
183
}
571
572
17
bool OpenTypeMATH::Serialize(OTSStream *out) {
573
17
  if (!out->Write(this->m_data, this->m_length)) {
574
0
    return OTS_FAILURE();
575
0
  }
576
577
17
  return true;
578
17
}
579
580
6.26k
bool OpenTypeMATH::ShouldSerialize() {
581
6.26k
  return Table::ShouldSerialize() && this->m_data != NULL;
582
6.26k
}
583
584
}  // namespace ots