Chromium Code Reviews| Index: src/math.cc |
| diff --git a/src/math.cc b/src/math.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..29e7b972bc3fe958fcc4d789230cddc413b35a89 |
| --- /dev/null |
| +++ b/src/math.cc |
| @@ -0,0 +1,592 @@ |
| +// 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. |
| + |
| +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(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(data + offset, length - offset)) { |
| + return OTS_FAILURE(); |
| + } |
| + } |
| + |
| + return true; |
| +} |
| + |
| +bool ParseMathConstantsTable(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(&subtable, data, length)) { |
| + return OTS_FAILURE(); |
| + } |
| + } |
| + |
| + // Part 3: uint16 constant |
| + // RadicalDegreeBottomRaisePercent |
| + if (!subtable.Skip(2)) { |
| + return OTS_FAILURE(); |
| + } |
| + |
| + return true; |
| +} |
| + |
| +bool ParseMathValueRecordSequenceForGlyphs(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(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(subtable, data, length)) { |
| + return OTS_FAILURE(); |
| + } |
| + } |
| + |
| + return true; |
| +} |
| + |
| +bool ParseMathItalicsCorrectionInfoTable(const uint8_t *data, |
| + size_t length, |
| + const uint16_t num_glyphs) { |
| + ots::Buffer subtable(data, length); |
| + return ParseMathValueRecordSequenceForGlyphs(&subtable, data, length, |
| + num_glyphs); |
| +} |
| + |
| +bool ParseMathTopAccentAttachmentTable(const uint8_t *data, |
| + size_t length, |
| + const uint16_t num_glyphs) { |
| + ots::Buffer subtable(data, length); |
| + return ParseMathValueRecordSequenceForGlyphs(&subtable, data, length, |
| + num_glyphs); |
| +} |
| + |
| +bool ParseMathKernTable(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(&subtable, data, length)) { |
| + return OTS_FAILURE(); |
| + } |
| + } |
| + |
| + // Check the Kern Values. |
| + for (unsigned i = 0; i <= height_count; ++i) { |
| + if (!ParseMathValueRecord(&subtable, data, length)) { |
| + return OTS_FAILURE(); |
| + } |
| + } |
| + |
| + return true; |
| +} |
| + |
| +bool ParseMathKernInfoTable(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(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(data + offset_math_kern, |
| + length - offset_math_kern)) { |
| + return OTS_FAILURE(); |
| + } |
| + } |
| + } |
| + } |
| + |
| + return true; |
| +} |
| + |
| +bool ParseMathGlyphInfoTable(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( |
| + data + offset_math_italics_correction_info, |
|
Kunihiko Sakamoto
2014/01/17 13:01:55
Oh, my comment wasn't very clear. I meant 4 spaces
|
| + 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(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(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(data + offset_math_kern_info, |
| + length - offset_math_kern_info, num_glyphs)) { |
| + return OTS_FAILURE(); |
| + } |
| + } |
| + |
| + return true; |
| +} |
| + |
| +bool ParseGlyphAssemblyTable(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(&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) { |
| + OTS_WARNING("bad glyph ID: %u", glyph); |
| + return OTS_FAILURE(); |
| + } |
| + if (part_flags & ~0x00000001) { |
| + OTS_WARNING("unknown part flag: %u", part_flags); |
| + return OTS_FAILURE(); |
| + } |
| + } |
| + |
| + return true; |
| +} |
| + |
| +bool ParseMathGlyphConstructionTable(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(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) { |
| + OTS_WARNING("bad glyph ID: %u", glyph); |
| + return OTS_FAILURE(); |
| + } |
| + } |
| + |
| + return true; |
| +} |
| + |
| +bool ParseMathGlyphConstructionSequence(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(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(data + offset_glyph_construction, |
| + length - offset_glyph_construction, |
| + num_glyphs)) { |
| + return OTS_FAILURE(); |
| + } |
| + } |
| + |
| + return true; |
| +} |
| + |
| +bool ParseMathVariantsTable(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(&subtable, data, length, num_glyphs, |
| + offset_vert_glyph_coverage, |
| + vert_glyph_count, |
| + sequence_end) || |
| + !ParseMathGlyphConstructionSequence(&subtable, data, length, num_glyphs, |
| + offset_horiz_glyph_coverage, |
| + horiz_glyph_count, |
| + sequence_end)) { |
| + return OTS_FAILURE(); |
| + } |
| + |
| + return true; |
| +} |
| + |
| +} // namespace |
| + |
| +#define DROP_THIS_TABLE \ |
| + do { 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) { |
| + OTS_WARNING("bad MATH version"); |
| + DROP_THIS_TABLE; |
| + 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; |
|
Kunihiko Sakamoto
2014/01/17 13:01:55
Can you add
OTS_WARNING("bad offset in MATH head
|
| + return true; |
| + } |
| + |
| + if (!ParseMathConstantsTable(data + offset_math_constants, |
| + length - offset_math_constants)) { |
| + DROP_THIS_TABLE; |
| + return true; |
| + } |
| + if (!ParseMathGlyphInfoTable(data + offset_math_glyph_info, |
| + length - offset_math_glyph_info, num_glyphs)) { |
| + DROP_THIS_TABLE; |
| + return true; |
| + } |
| + if (!ParseMathVariantsTable(data + offset_math_variants, |
| + length - offset_math_variants, num_glyphs)) { |
| + DROP_THIS_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 |
| + |