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