| Index: third_party/ots/src/math.cc
 | 
| diff --git a/third_party/ots/src/math.cc b/third_party/ots/src/math.cc
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..9124a88c512de0492566811a8463f407a65e64bd
 | 
| --- /dev/null
 | 
| +++ b/third_party/ots/src/math.cc
 | 
| @@ -0,0 +1,609 @@
 | 
| +// Copyright (c) 2014 The Chromium Authors. All rights reserved.
 | 
| +// Use of this source code is governed by a BSD-style license that can be
 | 
| +// found in the LICENSE file.
 | 
| +
 | 
| +// We use an underscore to avoid confusion with the standard math.h library.
 | 
| +#include "math_.h"
 | 
| +
 | 
| +#include <limits>
 | 
| +#include <vector>
 | 
| +
 | 
| +#include "layout.h"
 | 
| +#include "maxp.h"
 | 
| +
 | 
| +// MATH - The MATH Table
 | 
| +// The specification is not yet public but has been submitted to the MPEG group
 | 
| +// in response to the 'Call for Proposals for ISO/IEC 14496-22 "Open Font
 | 
| +// Format" Color Font Technology and MATH layout support'. Meanwhile, you can
 | 
| +// contact Microsoft's engineer Murray Sargent to obtain a copy.
 | 
| +
 | 
| +#define TABLE_NAME "MATH"
 | 
| +
 | 
| +namespace {
 | 
| +
 | 
| +// The size of MATH header.
 | 
| +// Version
 | 
| +// MathConstants
 | 
| +// MathGlyphInfo
 | 
| +// MathVariants
 | 
| +const unsigned kMathHeaderSize = 4 + 3 * 2;
 | 
| +
 | 
| +// The size of the MathGlyphInfo header.
 | 
| +// MathItalicsCorrectionInfo
 | 
| +// MathTopAccentAttachment
 | 
| +// ExtendedShapeCoverage
 | 
| +// MathKernInfo
 | 
| +const unsigned kMathGlyphInfoHeaderSize = 4 * 2;
 | 
| +
 | 
| +// The size of the MathValueRecord.
 | 
| +// Value
 | 
| +// DeviceTable
 | 
| +const unsigned kMathValueRecordSize = 2 * 2;
 | 
| +
 | 
| +// The size of the GlyphPartRecord.
 | 
| +// glyph
 | 
| +// StartConnectorLength
 | 
| +// EndConnectorLength
 | 
| +// FullAdvance
 | 
| +// PartFlags
 | 
| +const unsigned kGlyphPartRecordSize = 5 * 2;
 | 
| +
 | 
| +// Shared Table: MathValueRecord
 | 
| +
 | 
| +bool ParseMathValueRecord(const ots::OpenTypeFile *file,
 | 
| +                          ots::Buffer* subtable, const uint8_t *data,
 | 
| +                          const size_t length) {
 | 
| +  // Check the Value field.
 | 
| +  if (!subtable->Skip(2)) {
 | 
| +    return OTS_FAILURE();
 | 
| +  }
 | 
| +
 | 
| +  // Check the offset to device table.
 | 
| +  uint16_t offset = 0;
 | 
| +  if (!subtable->ReadU16(&offset)) {
 | 
| +    return OTS_FAILURE();
 | 
| +  }
 | 
| +  if (offset) {
 | 
| +    if (offset >= length) {
 | 
| +      return OTS_FAILURE();
 | 
| +    }
 | 
| +    if (!ots::ParseDeviceTable(file, data + offset, length - offset)) {
 | 
| +      return OTS_FAILURE();
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
| +bool ParseMathConstantsTable(const ots::OpenTypeFile *file,
 | 
| +                             const uint8_t *data, size_t length) {
 | 
| +  ots::Buffer subtable(data, length);
 | 
| +
 | 
| +  // Part 1: int16 or uint16 constants.
 | 
| +  //  ScriptPercentScaleDown
 | 
| +  //  ScriptScriptPercentScaleDown
 | 
| +  //  DelimitedSubFormulaMinHeight
 | 
| +  //  DisplayOperatorMinHeight
 | 
| +  if (!subtable.Skip(4 * 2)) {
 | 
| +    return OTS_FAILURE();
 | 
| +  }
 | 
| +
 | 
| +  // Part 2: MathValueRecord constants.
 | 
| +  // MathLeading
 | 
| +  // AxisHeight
 | 
| +  // AccentBaseHeight
 | 
| +  // FlattenedAccentBaseHeight
 | 
| +  // SubscriptShiftDown
 | 
| +  // SubscriptTopMax
 | 
| +  // SubscriptBaselineDropMin
 | 
| +  // SuperscriptShiftUp
 | 
| +  // SuperscriptShiftUpCramped
 | 
| +  // SuperscriptBottomMin
 | 
| +  //
 | 
| +  // SuperscriptBaselineDropMax
 | 
| +  // SubSuperscriptGapMin
 | 
| +  // SuperscriptBottomMaxWithSubscript
 | 
| +  // SpaceAfterScript
 | 
| +  // UpperLimitGapMin
 | 
| +  // UpperLimitBaselineRiseMin
 | 
| +  // LowerLimitGapMin
 | 
| +  // LowerLimitBaselineDropMin
 | 
| +  // StackTopShiftUp
 | 
| +  // StackTopDisplayStyleShiftUp
 | 
| +  //
 | 
| +  // StackBottomShiftDown
 | 
| +  // StackBottomDisplayStyleShiftDown
 | 
| +  // StackGapMin
 | 
| +  // StackDisplayStyleGapMin
 | 
| +  // StretchStackTopShiftUp
 | 
| +  // StretchStackBottomShiftDown
 | 
| +  // StretchStackGapAboveMin
 | 
| +  // StretchStackGapBelowMin
 | 
| +  // FractionNumeratorShiftUp
 | 
| +  // FractionNumeratorDisplayStyleShiftUp
 | 
| +  //
 | 
| +  // FractionDenominatorShiftDown
 | 
| +  // FractionDenominatorDisplayStyleShiftDown
 | 
| +  // FractionNumeratorGapMin
 | 
| +  // FractionNumDisplayStyleGapMin
 | 
| +  // FractionRuleThickness
 | 
| +  // FractionDenominatorGapMin
 | 
| +  // FractionDenomDisplayStyleGapMin
 | 
| +  // SkewedFractionHorizontalGap
 | 
| +  // SkewedFractionVerticalGap
 | 
| +  // OverbarVerticalGap
 | 
| +  //
 | 
| +  // OverbarRuleThickness
 | 
| +  // OverbarExtraAscender
 | 
| +  // UnderbarVerticalGap
 | 
| +  // UnderbarRuleThickness
 | 
| +  // UnderbarExtraDescender
 | 
| +  // RadicalVerticalGap
 | 
| +  // RadicalDisplayStyleVerticalGap
 | 
| +  // RadicalRuleThickness
 | 
| +  // RadicalExtraAscender
 | 
| +  // RadicalKernBeforeDegree
 | 
| +  //
 | 
| +  // RadicalKernAfterDegree
 | 
| +  for (unsigned i = 0; i < static_cast<unsigned>(51); ++i) {
 | 
| +    if (!ParseMathValueRecord(file, &subtable, data, length)) {
 | 
| +      return OTS_FAILURE();
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  // Part 3: uint16 constant
 | 
| +  // RadicalDegreeBottomRaisePercent
 | 
| +  if (!subtable.Skip(2)) {
 | 
| +    return OTS_FAILURE();
 | 
| +  }
 | 
| +
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
| +bool ParseMathValueRecordSequenceForGlyphs(const ots::OpenTypeFile *file,
 | 
| +                                           ots::Buffer* subtable,
 | 
| +                                           const uint8_t *data,
 | 
| +                                           const size_t length,
 | 
| +                                           const uint16_t num_glyphs) {
 | 
| +  // Check the header.
 | 
| +  uint16_t offset_coverage = 0;
 | 
| +  uint16_t sequence_count = 0;
 | 
| +  if (!subtable->ReadU16(&offset_coverage) ||
 | 
| +      !subtable->ReadU16(&sequence_count)) {
 | 
| +    return OTS_FAILURE();
 | 
| +  }
 | 
| +
 | 
| +  const unsigned sequence_end = static_cast<unsigned>(2 * 2) +
 | 
| +      sequence_count * kMathValueRecordSize;
 | 
| +  if (sequence_end > std::numeric_limits<uint16_t>::max()) {
 | 
| +    return OTS_FAILURE();
 | 
| +  }
 | 
| +
 | 
| +  // Check coverage table.
 | 
| +  if (offset_coverage < sequence_end || offset_coverage >= length) {
 | 
| +    return OTS_FAILURE();
 | 
| +  }
 | 
| +  if (!ots::ParseCoverageTable(file, data + offset_coverage,
 | 
| +                               length - offset_coverage,
 | 
| +                               num_glyphs, sequence_count)) {
 | 
| +    return OTS_FAILURE();
 | 
| +  }
 | 
| +
 | 
| +  // Check sequence.
 | 
| +  for (unsigned i = 0; i < sequence_count; ++i) {
 | 
| +    if (!ParseMathValueRecord(file, subtable, data, length)) {
 | 
| +      return OTS_FAILURE();
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
| +bool ParseMathItalicsCorrectionInfoTable(const ots::OpenTypeFile *file,
 | 
| +                                         const uint8_t *data,
 | 
| +                                         size_t length,
 | 
| +                                         const uint16_t num_glyphs) {
 | 
| +  ots::Buffer subtable(data, length);
 | 
| +  return ParseMathValueRecordSequenceForGlyphs(file, &subtable, data, length,
 | 
| +                                               num_glyphs);
 | 
| +}
 | 
| +
 | 
| +bool ParseMathTopAccentAttachmentTable(const ots::OpenTypeFile *file,
 | 
| +                                       const uint8_t *data,
 | 
| +                                       size_t length,
 | 
| +                                       const uint16_t num_glyphs) {
 | 
| +  ots::Buffer subtable(data, length);
 | 
| +  return ParseMathValueRecordSequenceForGlyphs(file, &subtable, data, length,
 | 
| +                                               num_glyphs);
 | 
| +}
 | 
| +
 | 
| +bool ParseMathKernTable(const ots::OpenTypeFile *file,
 | 
| +                        const uint8_t *data, size_t length) {
 | 
| +  ots::Buffer subtable(data, length);
 | 
| +
 | 
| +  // Check the Height count.
 | 
| +  uint16_t height_count = 0;
 | 
| +  if (!subtable.ReadU16(&height_count)) {
 | 
| +    return OTS_FAILURE();
 | 
| +  }
 | 
| +
 | 
| +  // Check the Correction Heights.
 | 
| +  for (unsigned i = 0; i < height_count; ++i) {
 | 
| +    if (!ParseMathValueRecord(file, &subtable, data, length)) {
 | 
| +      return OTS_FAILURE();
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  // Check the Kern Values.
 | 
| +  for (unsigned i = 0; i <= height_count; ++i) {
 | 
| +    if (!ParseMathValueRecord(file, &subtable, data, length)) {
 | 
| +      return OTS_FAILURE();
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
| +bool ParseMathKernInfoTable(const ots::OpenTypeFile *file,
 | 
| +                            const uint8_t *data, size_t length,
 | 
| +                            const uint16_t num_glyphs) {
 | 
| +  ots::Buffer subtable(data, length);
 | 
| +
 | 
| +  // Check the header.
 | 
| +  uint16_t offset_coverage = 0;
 | 
| +  uint16_t sequence_count = 0;
 | 
| +  if (!subtable.ReadU16(&offset_coverage) ||
 | 
| +      !subtable.ReadU16(&sequence_count)) {
 | 
| +    return OTS_FAILURE();
 | 
| +  }
 | 
| +
 | 
| +  const unsigned sequence_end = static_cast<unsigned>(2 * 2) +
 | 
| +    sequence_count * 4 * 2;
 | 
| +  if (sequence_end > std::numeric_limits<uint16_t>::max()) {
 | 
| +    return OTS_FAILURE();
 | 
| +  }
 | 
| +
 | 
| +  // Check coverage table.
 | 
| +  if (offset_coverage < sequence_end || offset_coverage >= length) {
 | 
| +    return OTS_FAILURE();
 | 
| +  }
 | 
| +  if (!ots::ParseCoverageTable(file, data + offset_coverage, length - offset_coverage,
 | 
| +                               num_glyphs, sequence_count)) {
 | 
| +    return OTS_FAILURE();
 | 
| +  }
 | 
| +
 | 
| +  // Check sequence of MathKernInfoRecord
 | 
| +  for (unsigned i = 0; i < sequence_count; ++i) {
 | 
| +    // Check TopRight, TopLeft, BottomRight and BottomLeft Math Kern.
 | 
| +    for (unsigned j = 0; j < 4; ++j) {
 | 
| +      uint16_t offset_math_kern = 0;
 | 
| +      if (!subtable.ReadU16(&offset_math_kern)) {
 | 
| +        return OTS_FAILURE();
 | 
| +      }
 | 
| +      if (offset_math_kern) {
 | 
| +        if (offset_math_kern < sequence_end || offset_math_kern >= length ||
 | 
| +            !ParseMathKernTable(file, data + offset_math_kern,
 | 
| +                                length - offset_math_kern)) {
 | 
| +          return OTS_FAILURE();
 | 
| +        }
 | 
| +      }
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
| +bool ParseMathGlyphInfoTable(const ots::OpenTypeFile *file,
 | 
| +                             const uint8_t *data, size_t length,
 | 
| +                             const uint16_t num_glyphs) {
 | 
| +  ots::Buffer subtable(data, length);
 | 
| +
 | 
| +  // Check Header.
 | 
| +  uint16_t offset_math_italics_correction_info = 0;
 | 
| +  uint16_t offset_math_top_accent_attachment = 0;
 | 
| +  uint16_t offset_extended_shaped_coverage = 0;
 | 
| +  uint16_t offset_math_kern_info = 0;
 | 
| +  if (!subtable.ReadU16(&offset_math_italics_correction_info) ||
 | 
| +      !subtable.ReadU16(&offset_math_top_accent_attachment) ||
 | 
| +      !subtable.ReadU16(&offset_extended_shaped_coverage) ||
 | 
| +      !subtable.ReadU16(&offset_math_kern_info)) {
 | 
| +    return OTS_FAILURE();
 | 
| +  }
 | 
| +
 | 
| +  // Check subtables.
 | 
| +  // The specification does not say whether the offsets for
 | 
| +  // MathItalicsCorrectionInfo, MathTopAccentAttachment and MathKernInfo may
 | 
| +  // be NULL, but that's the case in some fonts (e.g STIX) so we accept that.
 | 
| +  if (offset_math_italics_correction_info) {
 | 
| +    if (offset_math_italics_correction_info >= length ||
 | 
| +        offset_math_italics_correction_info < kMathGlyphInfoHeaderSize ||
 | 
| +        !ParseMathItalicsCorrectionInfoTable(
 | 
| +            file, data + offset_math_italics_correction_info,
 | 
| +            length - offset_math_italics_correction_info,
 | 
| +            num_glyphs)) {
 | 
| +      return OTS_FAILURE();
 | 
| +    }
 | 
| +  }
 | 
| +  if (offset_math_top_accent_attachment) {
 | 
| +    if (offset_math_top_accent_attachment >= length ||
 | 
| +        offset_math_top_accent_attachment < kMathGlyphInfoHeaderSize ||
 | 
| +        !ParseMathTopAccentAttachmentTable(file, data +
 | 
| +                                           offset_math_top_accent_attachment,
 | 
| +                                           length -
 | 
| +                                           offset_math_top_accent_attachment,
 | 
| +                                           num_glyphs)) {
 | 
| +      return OTS_FAILURE();
 | 
| +    }
 | 
| +  }
 | 
| +  if (offset_extended_shaped_coverage) {
 | 
| +    if (offset_extended_shaped_coverage >= length ||
 | 
| +        offset_extended_shaped_coverage < kMathGlyphInfoHeaderSize ||
 | 
| +        !ots::ParseCoverageTable(file, data + offset_extended_shaped_coverage,
 | 
| +                                 length - offset_extended_shaped_coverage,
 | 
| +                                 num_glyphs)) {
 | 
| +      return OTS_FAILURE();
 | 
| +    }
 | 
| +  }
 | 
| +  if (offset_math_kern_info) {
 | 
| +    if (offset_math_kern_info >= length ||
 | 
| +        offset_math_kern_info < kMathGlyphInfoHeaderSize ||
 | 
| +        !ParseMathKernInfoTable(file, data + offset_math_kern_info,
 | 
| +                                length - offset_math_kern_info, num_glyphs)) {
 | 
| +      return OTS_FAILURE();
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
| +bool ParseGlyphAssemblyTable(const ots::OpenTypeFile *file,
 | 
| +                             const uint8_t *data,
 | 
| +                             size_t length, const uint16_t num_glyphs) {
 | 
| +  ots::Buffer subtable(data, length);
 | 
| +
 | 
| +  // Check the header.
 | 
| +  uint16_t part_count = 0;
 | 
| +  if (!ParseMathValueRecord(file, &subtable, data, length) ||
 | 
| +      !subtable.ReadU16(&part_count)) {
 | 
| +    return OTS_FAILURE();
 | 
| +  }
 | 
| +
 | 
| +  const unsigned sequence_end = kMathValueRecordSize +
 | 
| +    static_cast<unsigned>(2) + part_count * kGlyphPartRecordSize;
 | 
| +  if (sequence_end > std::numeric_limits<uint16_t>::max()) {
 | 
| +    return OTS_FAILURE();
 | 
| +  }
 | 
| +
 | 
| +  // Check the sequence of GlyphPartRecord.
 | 
| +  for (unsigned i = 0; i < part_count; ++i) {
 | 
| +    uint16_t glyph = 0;
 | 
| +    uint16_t part_flags = 0;
 | 
| +    if (!subtable.ReadU16(&glyph) ||
 | 
| +        !subtable.Skip(2 * 3) ||
 | 
| +        !subtable.ReadU16(&part_flags)) {
 | 
| +      return OTS_FAILURE();
 | 
| +    }
 | 
| +    if (glyph >= num_glyphs) {
 | 
| +      return OTS_FAILURE_MSG("bad glyph ID: %u", glyph);
 | 
| +    }
 | 
| +    if (part_flags & ~0x00000001) {
 | 
| +      return OTS_FAILURE_MSG("unknown part flag: %u", part_flags);
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
| +bool ParseMathGlyphConstructionTable(const ots::OpenTypeFile *file,
 | 
| +                                     const uint8_t *data,
 | 
| +                                     size_t length, const uint16_t num_glyphs) {
 | 
| +  ots::Buffer subtable(data, length);
 | 
| +
 | 
| +  // Check the header.
 | 
| +  uint16_t offset_glyph_assembly = 0;
 | 
| +  uint16_t variant_count = 0;
 | 
| +  if (!subtable.ReadU16(&offset_glyph_assembly) ||
 | 
| +      !subtable.ReadU16(&variant_count)) {
 | 
| +    return OTS_FAILURE();
 | 
| +  }
 | 
| +
 | 
| +  const unsigned sequence_end = static_cast<unsigned>(2 * 2) +
 | 
| +    variant_count * 2 * 2;
 | 
| +  if (sequence_end > std::numeric_limits<uint16_t>::max()) {
 | 
| +    return OTS_FAILURE();
 | 
| +  }
 | 
| +
 | 
| +  // Check the GlyphAssembly offset.
 | 
| +  if (offset_glyph_assembly) {
 | 
| +    if (offset_glyph_assembly >= length ||
 | 
| +        offset_glyph_assembly < sequence_end) {
 | 
| +      return OTS_FAILURE();
 | 
| +    }
 | 
| +    if (!ParseGlyphAssemblyTable(file, data + offset_glyph_assembly,
 | 
| +                                 length - offset_glyph_assembly, num_glyphs)) {
 | 
| +      return OTS_FAILURE();
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  // Check the sequence of MathGlyphVariantRecord.
 | 
| +  for (unsigned i = 0; i < variant_count; ++i) {
 | 
| +    uint16_t glyph = 0;
 | 
| +    if (!subtable.ReadU16(&glyph) ||
 | 
| +        !subtable.Skip(2)) {
 | 
| +      return OTS_FAILURE();
 | 
| +    }
 | 
| +    if (glyph >= num_glyphs) {
 | 
| +      return OTS_FAILURE_MSG("bad glyph ID: %u", glyph);
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
| +bool ParseMathGlyphConstructionSequence(const ots::OpenTypeFile *file,
 | 
| +                                        ots::Buffer* subtable,
 | 
| +                                        const uint8_t *data,
 | 
| +                                        size_t length,
 | 
| +                                        const uint16_t num_glyphs,
 | 
| +                                        uint16_t offset_coverage,
 | 
| +                                        uint16_t glyph_count,
 | 
| +                                        const unsigned sequence_end) {
 | 
| +  // Check coverage table.
 | 
| +  if (offset_coverage < sequence_end || offset_coverage >= length) {
 | 
| +    return OTS_FAILURE();
 | 
| +  }
 | 
| +  if (!ots::ParseCoverageTable(file, data + offset_coverage,
 | 
| +                               length - offset_coverage,
 | 
| +                               num_glyphs, glyph_count)) {
 | 
| +    return OTS_FAILURE();
 | 
| +  }
 | 
| +
 | 
| +  // Check sequence of MathGlyphConstruction.
 | 
| +  for (unsigned i = 0; i < glyph_count; ++i) {
 | 
| +      uint16_t offset_glyph_construction = 0;
 | 
| +      if (!subtable->ReadU16(&offset_glyph_construction)) {
 | 
| +        return OTS_FAILURE();
 | 
| +      }
 | 
| +      if (offset_glyph_construction < sequence_end ||
 | 
| +          offset_glyph_construction >= length ||
 | 
| +          !ParseMathGlyphConstructionTable(file, data + offset_glyph_construction,
 | 
| +                                           length - offset_glyph_construction,
 | 
| +                                           num_glyphs)) {
 | 
| +        return OTS_FAILURE();
 | 
| +      }
 | 
| +  }
 | 
| +
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
| +bool ParseMathVariantsTable(const ots::OpenTypeFile *file,
 | 
| +                            const uint8_t *data,
 | 
| +                            size_t length, const uint16_t num_glyphs) {
 | 
| +  ots::Buffer subtable(data, length);
 | 
| +
 | 
| +  // Check the header.
 | 
| +  uint16_t offset_vert_glyph_coverage = 0;
 | 
| +  uint16_t offset_horiz_glyph_coverage = 0;
 | 
| +  uint16_t vert_glyph_count = 0;
 | 
| +  uint16_t horiz_glyph_count = 0;
 | 
| +  if (!subtable.Skip(2) ||  // MinConnectorOverlap
 | 
| +      !subtable.ReadU16(&offset_vert_glyph_coverage) ||
 | 
| +      !subtable.ReadU16(&offset_horiz_glyph_coverage) ||
 | 
| +      !subtable.ReadU16(&vert_glyph_count) ||
 | 
| +      !subtable.ReadU16(&horiz_glyph_count)) {
 | 
| +    return OTS_FAILURE();
 | 
| +  }
 | 
| +
 | 
| +  const unsigned sequence_end = 5 * 2 + vert_glyph_count * 2 +
 | 
| +    horiz_glyph_count * 2;
 | 
| +  if (sequence_end > std::numeric_limits<uint16_t>::max()) {
 | 
| +    return OTS_FAILURE();
 | 
| +  }
 | 
| +
 | 
| +  if (!ParseMathGlyphConstructionSequence(file, &subtable, data, length, num_glyphs,
 | 
| +                                          offset_vert_glyph_coverage,
 | 
| +                                          vert_glyph_count,
 | 
| +                                          sequence_end) ||
 | 
| +      !ParseMathGlyphConstructionSequence(file, &subtable, data, length, num_glyphs,
 | 
| +                                          offset_horiz_glyph_coverage,
 | 
| +                                          horiz_glyph_count,
 | 
| +                                          sequence_end)) {
 | 
| +    return OTS_FAILURE();
 | 
| +  }
 | 
| +
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
| +}  // namespace
 | 
| +
 | 
| +#define DROP_THIS_TABLE(msg_) \
 | 
| +  do { \
 | 
| +    OTS_FAILURE_MSG(msg_ ", table discarded"); \
 | 
| +    file->math->data = 0; \
 | 
| +    file->math->length = 0; \
 | 
| +  } while (0)
 | 
| +
 | 
| +namespace ots {
 | 
| +
 | 
| +bool ots_math_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
 | 
| +  // Grab the number of glyphs in the file from the maxp table to check
 | 
| +  // GlyphIDs in MATH table.
 | 
| +  if (!file->maxp) {
 | 
| +    return OTS_FAILURE();
 | 
| +  }
 | 
| +  const uint16_t num_glyphs = file->maxp->num_glyphs;
 | 
| +
 | 
| +  Buffer table(data, length);
 | 
| +
 | 
| +  OpenTypeMATH* math = new OpenTypeMATH;
 | 
| +  file->math = math;
 | 
| +
 | 
| +  uint32_t version = 0;
 | 
| +  if (!table.ReadU32(&version)) {
 | 
| +    return OTS_FAILURE();
 | 
| +  }
 | 
| +  if (version != 0x00010000) {
 | 
| +    DROP_THIS_TABLE("bad MATH version");
 | 
| +    return true;
 | 
| +  }
 | 
| +
 | 
| +  uint16_t offset_math_constants = 0;
 | 
| +  uint16_t offset_math_glyph_info = 0;
 | 
| +  uint16_t offset_math_variants = 0;
 | 
| +  if (!table.ReadU16(&offset_math_constants) ||
 | 
| +      !table.ReadU16(&offset_math_glyph_info) ||
 | 
| +      !table.ReadU16(&offset_math_variants)) {
 | 
| +    return OTS_FAILURE();
 | 
| +  }
 | 
| +
 | 
| +  if (offset_math_constants >= length ||
 | 
| +      offset_math_constants < kMathHeaderSize ||
 | 
| +      offset_math_glyph_info >= length ||
 | 
| +      offset_math_glyph_info < kMathHeaderSize ||
 | 
| +      offset_math_variants >= length ||
 | 
| +      offset_math_variants < kMathHeaderSize) {
 | 
| +    DROP_THIS_TABLE("bad offset in MATH header");
 | 
| +    return true;
 | 
| +  }
 | 
| +
 | 
| +  if (!ParseMathConstantsTable(file, data + offset_math_constants,
 | 
| +                               length - offset_math_constants)) {
 | 
| +    DROP_THIS_TABLE("failed to parse MathConstants table");
 | 
| +    return true;
 | 
| +  }
 | 
| +  if (!ParseMathGlyphInfoTable(file, data + offset_math_glyph_info,
 | 
| +                               length - offset_math_glyph_info, num_glyphs)) {
 | 
| +    DROP_THIS_TABLE("failed to parse MathGlyphInfo table");
 | 
| +    return true;
 | 
| +  }
 | 
| +  if (!ParseMathVariantsTable(file, data + offset_math_variants,
 | 
| +                              length - offset_math_variants, num_glyphs)) {
 | 
| +    DROP_THIS_TABLE("failed to parse MathVariants table");
 | 
| +    return true;
 | 
| +  }
 | 
| +
 | 
| +  math->data = data;
 | 
| +  math->length = length;
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
| +bool ots_math_should_serialise(OpenTypeFile *file) {
 | 
| +  return file->math != NULL && file->math->data != NULL;
 | 
| +}
 | 
| +
 | 
| +bool ots_math_serialise(OTSStream *out, OpenTypeFile *file) {
 | 
| +  if (!out->Write(file->math->data, file->math->length)) {
 | 
| +    return OTS_FAILURE();
 | 
| +  }
 | 
| +
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
| +void ots_math_free(OpenTypeFile *file) {
 | 
| +  delete file->math;
 | 
| +}
 | 
| +
 | 
| +}  // namespace ots
 | 
| +
 | 
| +#undef TABLE_NAME
 | 
| +#undef DROP_THIS_TABLE
 | 
| 
 |