|
@@ -0,0 +1,4118 @@
|
|
|
+# HG changeset patch
|
|
|
+# User Ryan VanderMeulen <ryanvm@gmail.com>
|
|
|
+# Date 1685465808 0
|
|
|
+# Tue May 30 16:56:48 2023 +0000
|
|
|
+# Node ID fdae8692c5f724b7209d50201e52c8efac172cd7
|
|
|
+# Parent 3e815af522af15c2069c3dcdeff415ee4fbc77c7
|
|
|
+Bug 1835833 - Update OTS to 9.1.0. r=jfkthame
|
|
|
+
|
|
|
+Differential Revision: https://phabricator.services.mozilla.com/D179464
|
|
|
+
|
|
|
+diff --git a/gfx/ots/README.mozilla b/gfx/ots/README.mozilla
|
|
|
+--- a/gfx/ots/README.mozilla
|
|
|
++++ b/gfx/ots/README.mozilla
|
|
|
+@@ -1,12 +1,12 @@
|
|
|
+ This is the Sanitiser for OpenType project, from http://code.google.com/p/ots/.
|
|
|
+
|
|
|
+ Our reference repository is https://github.com/khaledhosny/ots/.
|
|
|
+
|
|
|
+-Current revision: 35643038c4904538aa74c2c691f8d792efdbd6c0 (9.0.0)
|
|
|
++Current revision: 75933e1bdc98bdb095f6274b284e1c365f2c510e (9.1.0)
|
|
|
+
|
|
|
+ Upstream files included: LICENSE, src/, include/, tests/*.cc
|
|
|
+
|
|
|
+ Additional files: README.mozilla, src/moz.build
|
|
|
+
|
|
|
+ Additional patch: ots-visibility.patch (bug 711079).
|
|
|
+ Additional patch: ots-lz4.patch
|
|
|
+diff --git a/gfx/ots/src/avar.cc b/gfx/ots/src/avar.cc
|
|
|
+--- a/gfx/ots/src/avar.cc
|
|
|
++++ b/gfx/ots/src/avar.cc
|
|
|
+@@ -1,41 +1,54 @@
|
|
|
+ // Copyright (c) 2018 The OTS Authors. All rights reserved.
|
|
|
+ // Use of this source code is governed by a BSD-style license that can be
|
|
|
+ // found in the LICENSE file.
|
|
|
+
|
|
|
+ #include "avar.h"
|
|
|
+
|
|
|
+ #include "fvar.h"
|
|
|
+
|
|
|
++#include "variations.h"
|
|
|
++
|
|
|
+ namespace ots {
|
|
|
+
|
|
|
+ // -----------------------------------------------------------------------------
|
|
|
+ // OpenTypeAVAR
|
|
|
+ // -----------------------------------------------------------------------------
|
|
|
+
|
|
|
+ bool OpenTypeAVAR::Parse(const uint8_t* data, size_t length) {
|
|
|
+ Buffer table(data, length);
|
|
|
+ if (!table.ReadU16(&this->majorVersion) ||
|
|
|
+ !table.ReadU16(&this->minorVersion) ||
|
|
|
+ !table.ReadU16(&this->reserved) ||
|
|
|
+ !table.ReadU16(&this->axisCount)) {
|
|
|
+ return Drop("Failed to read table header");
|
|
|
+ }
|
|
|
+- if (this->majorVersion != 1) {
|
|
|
++ if (this->majorVersion > 2) {
|
|
|
+ return Drop("Unknown table version");
|
|
|
+ }
|
|
|
+- if (this->minorVersion > 0) {
|
|
|
+- // we only know how to serialize version 1.0
|
|
|
+- Warning("Downgrading minor version to 0");
|
|
|
+- this->minorVersion = 0;
|
|
|
+- }
|
|
|
+- if (this->reserved != 0) {
|
|
|
+- Warning("Expected reserved=0");
|
|
|
+- this->reserved = 0;
|
|
|
++ if (this->majorVersion == 1) {
|
|
|
++ // We can fix table
|
|
|
++ if (this->minorVersion > 0) {
|
|
|
++ // we only know how to serialize version 1.0
|
|
|
++ Warning("Downgrading minor version to 0");
|
|
|
++ this->minorVersion = 0;
|
|
|
++ }
|
|
|
++ if (this->reserved != 0) {
|
|
|
++ Warning("Expected reserved=0");
|
|
|
++ this->reserved = 0;
|
|
|
++ }
|
|
|
++ } else {
|
|
|
++ // We serialize data unchanged, so drop even for minor errors
|
|
|
++ if (this->minorVersion > 0) {
|
|
|
++ return Drop("Unknown minor table version");
|
|
|
++ }
|
|
|
++ if (this->reserved != 0) {
|
|
|
++ return Drop("Expected reserved=0");
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ OpenTypeFVAR* fvar = static_cast<OpenTypeFVAR*>(
|
|
|
+ GetFont()->GetTypedTable(OTS_TAG_FVAR));
|
|
|
+ if (!fvar) {
|
|
|
+ return DropVariations("Required fvar table is missing");
|
|
|
+ }
|
|
|
+ if (axisCount != fvar->AxisCount()) {
|
|
|
+@@ -74,20 +87,62 @@ bool OpenTypeAVAR::Parse(const uint8_t*
|
|
|
+ }
|
|
|
+ this->axisSegmentMaps[i].push_back(map);
|
|
|
+ }
|
|
|
+ if (positionMapCount > 0 && foundRequiredMappings != 3) {
|
|
|
+ return Drop("A required mapping (for -1, 0 or 1) is missing");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
++ if (this->majorVersion < 2)
|
|
|
++ return true;
|
|
|
++
|
|
|
++ uint32_t axisIndexMapOffset;
|
|
|
++ uint32_t varStoreOffset;
|
|
|
++
|
|
|
++ if (!table.ReadU32(&axisIndexMapOffset) ||
|
|
|
++ !table.ReadU32(&varStoreOffset)) {
|
|
|
++ return Drop("Failed to read version 2 offsets");
|
|
|
++ }
|
|
|
++
|
|
|
++ Font *font = GetFont();
|
|
|
++ uint32_t headerSize = table.offset();
|
|
|
++
|
|
|
++ if (axisIndexMapOffset) {
|
|
|
++ if (axisIndexMapOffset < headerSize || axisIndexMapOffset >= length) {
|
|
|
++ return Drop("Bad delta set index offset in table header");
|
|
|
++ }
|
|
|
++ if (!ParseDeltaSetIndexMap(font, data + axisIndexMapOffset, length - axisIndexMapOffset)) {
|
|
|
++ return Drop("Failed to parse delta set index map");
|
|
|
++ }
|
|
|
++ }
|
|
|
++
|
|
|
++ if (varStoreOffset) {
|
|
|
++ if (varStoreOffset < headerSize || varStoreOffset >= length) {
|
|
|
++ return Drop("Bad item variation store offset in table header");
|
|
|
++ }
|
|
|
++ if (!ParseItemVariationStore(font, data + varStoreOffset, length - varStoreOffset)) {
|
|
|
++ return Drop("Failed to parse item variation store");
|
|
|
++ }
|
|
|
++ }
|
|
|
++
|
|
|
++ this->m_data = data;
|
|
|
++ this->m_length = length;
|
|
|
++
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool OpenTypeAVAR::Serialize(OTSStream* out) {
|
|
|
++ if (this->majorVersion >= 2) {
|
|
|
++ if (!out->Write(this->m_data, this->m_length)) {
|
|
|
++ return Error("Failed to write table");
|
|
|
++ }
|
|
|
++ return true;
|
|
|
++ }
|
|
|
++
|
|
|
+ if (!out->WriteU16(this->majorVersion) ||
|
|
|
+ !out->WriteU16(this->minorVersion) ||
|
|
|
+ !out->WriteU16(this->reserved) ||
|
|
|
+ !out->WriteU16(this->axisCount)) {
|
|
|
+ return Error("Failed to write table");
|
|
|
+ }
|
|
|
+
|
|
|
+ for (size_t i = 0; i < this->axisCount; i++) {
|
|
|
+diff --git a/gfx/ots/src/avar.h b/gfx/ots/src/avar.h
|
|
|
+--- a/gfx/ots/src/avar.h
|
|
|
++++ b/gfx/ots/src/avar.h
|
|
|
+@@ -30,13 +30,17 @@ class OpenTypeAVAR : public Table {
|
|
|
+ uint16_t axisCount;
|
|
|
+
|
|
|
+ struct AxisValueMap {
|
|
|
+ int16_t fromCoordinate;
|
|
|
+ int16_t toCoordinate;
|
|
|
+ };
|
|
|
+
|
|
|
+ std::vector<std::vector<AxisValueMap>> axisSegmentMaps;
|
|
|
++
|
|
|
++ // Only used for versions >= 2
|
|
|
++ const uint8_t *m_data;
|
|
|
++ size_t m_length;
|
|
|
+ };
|
|
|
+
|
|
|
+ } // namespace ots
|
|
|
+
|
|
|
+ #endif // OTS_AVAR_H_
|
|
|
+diff --git a/gfx/ots/src/cff_charstring.cc b/gfx/ots/src/cff_charstring.cc
|
|
|
+--- a/gfx/ots/src/cff_charstring.cc
|
|
|
++++ b/gfx/ots/src/cff_charstring.cc
|
|
|
+@@ -379,17 +379,17 @@ bool ExecuteCharStringOperator(ots::Open
|
|
|
+ if (stack_size < 1) {
|
|
|
+ return OTS_FAILURE();
|
|
|
+ }
|
|
|
+ if (cs_ctx.vsindex >= (int32_t)cff.region_index_count.size()) {
|
|
|
+ return OTS_FAILURE();
|
|
|
+ }
|
|
|
+ uint16_t k = cff.region_index_count.at(cs_ctx.vsindex);
|
|
|
+ uint16_t n = argument_stack->top();
|
|
|
+- if (stack_size < n * (k + 1) + 1) {
|
|
|
++ if (stack_size < n * (k + 1u) + 1u) {
|
|
|
+ return OTS_FAILURE();
|
|
|
+ }
|
|
|
+
|
|
|
+ // Keep the 1st n operands on the stack for the next operator to use and
|
|
|
+ // pop the rest. There can be multiple consecutive blend operators, so this
|
|
|
+ // makes sure the operands of all of them are kept on the stack.
|
|
|
+ while (argument_stack->size() > stack_size - ((n * k) + 1))
|
|
|
+ argument_stack->pop();
|
|
|
+diff --git a/gfx/ots/src/cpal.cc b/gfx/ots/src/cpal.cc
|
|
|
+--- a/gfx/ots/src/cpal.cc
|
|
|
++++ b/gfx/ots/src/cpal.cc
|
|
|
+@@ -189,17 +189,19 @@ bool OpenTypeCPAL::Parse(const uint8_t *
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool OpenTypeCPAL::Serialize(OTSStream *out) {
|
|
|
+ uint16_t numPalettes = this->colorRecordIndices.size();
|
|
|
+ uint16_t numColorRecords = this->colorRecords.size();
|
|
|
+
|
|
|
++#ifndef NDEBUG
|
|
|
+ off_t start = out->Tell();
|
|
|
++#endif
|
|
|
+
|
|
|
+ size_t colorRecordsArrayOffset = 4 * sizeof(uint16_t) + sizeof(uint32_t) +
|
|
|
+ numPalettes * sizeof(uint16_t);
|
|
|
+ if (this->version == 1) {
|
|
|
+ colorRecordsArrayOffset += 3 * sizeof(uint32_t);
|
|
|
+ }
|
|
|
+
|
|
|
+ size_t totalLen = colorRecordsArrayOffset + numColorRecords * sizeof(uint32_t);
|
|
|
+diff --git a/gfx/ots/src/glyf.cc b/gfx/ots/src/glyf.cc
|
|
|
+--- a/gfx/ots/src/glyf.cc
|
|
|
++++ b/gfx/ots/src/glyf.cc
|
|
|
+@@ -107,19 +107,19 @@ bool OpenTypeGLYF::ParseSimpleGlyph(Buff
|
|
|
+
|
|
|
+ uint16_t bytecode_length = 0;
|
|
|
+ if (!glyph.ReadU16(&bytecode_length)) {
|
|
|
+ return Error("Can't read bytecode length");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this->maxp->version_1 &&
|
|
|
+ this->maxp->max_size_glyf_instructions < bytecode_length) {
|
|
|
+- this->maxp->max_size_glyf_instructions = bytecode_length;
|
|
|
+ Warning("Bytecode length is bigger than maxp.maxSizeOfInstructions %d: %d",
|
|
|
+ this->maxp->max_size_glyf_instructions, bytecode_length);
|
|
|
++ this->maxp->max_size_glyf_instructions = bytecode_length;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!glyph.Skip(bytecode_length)) {
|
|
|
+ return Error("Can't read bytecode of length %d", bytecode_length);
|
|
|
+ }
|
|
|
+
|
|
|
+ uint32_t coordinates_length = 0;
|
|
|
+ for (uint32_t i = 0; i < num_flags; ++i) {
|
|
|
+@@ -210,20 +210,20 @@ bool OpenTypeGLYF::ParseCompositeGlyph(
|
|
|
+ if (flags & WE_HAVE_INSTRUCTIONS) {
|
|
|
+ uint16_t bytecode_length;
|
|
|
+ if (!glyph.ReadU16(&bytecode_length)) {
|
|
|
+ return Error("Can't read instructions size");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this->maxp->version_1 &&
|
|
|
+ this->maxp->max_size_glyf_instructions < bytecode_length) {
|
|
|
+- this->maxp->max_size_glyf_instructions = bytecode_length;
|
|
|
+ Warning("Bytecode length is bigger than maxp.maxSizeOfInstructions "
|
|
|
+ "%d: %d",
|
|
|
+ this->maxp->max_size_glyf_instructions, bytecode_length);
|
|
|
++ this->maxp->max_size_glyf_instructions = bytecode_length;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!glyph.Skip(bytecode_length)) {
|
|
|
+ return Error("Can't read bytecode of length %d", bytecode_length);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ this->iov.push_back(std::make_pair(glyph.buffer(), glyph.offset()));
|
|
|
+diff --git a/gfx/ots/src/gpos.cc b/gfx/ots/src/gpos.cc
|
|
|
+--- a/gfx/ots/src/gpos.cc
|
|
|
++++ b/gfx/ots/src/gpos.cc
|
|
|
+@@ -25,60 +25,19 @@ enum GPOS_TYPE {
|
|
|
+ GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT = 5,
|
|
|
+ GPOS_TYPE_MARK_TO_MARK_ATTACHMENT = 6,
|
|
|
+ GPOS_TYPE_CONTEXT_POSITIONING = 7,
|
|
|
+ GPOS_TYPE_CHAINED_CONTEXT_POSITIONING = 8,
|
|
|
+ GPOS_TYPE_EXTENSION_POSITIONING = 9,
|
|
|
+ GPOS_TYPE_RESERVED = 10
|
|
|
+ };
|
|
|
+
|
|
|
+-// The size of gpos header, version 1.0.
|
|
|
+-const unsigned kGposHeaderSize_1_0 = 10;
|
|
|
+-// The size of gpos header, version 1.1.
|
|
|
+-const unsigned kGposHeaderSize_1_1 = 14;
|
|
|
+ // The maximum format number for anchor tables.
|
|
|
+ const uint16_t kMaxAnchorFormat = 3;
|
|
|
+
|
|
|
+-// Lookup type parsers.
|
|
|
+-bool ParseSingleAdjustment(const ots::Font *font,
|
|
|
+- const uint8_t *data, const size_t length);
|
|
|
+-bool ParsePairAdjustment(const ots::Font *font,
|
|
|
+- const uint8_t *data, const size_t length);
|
|
|
+-bool ParseCursiveAttachment(const ots::Font *font,
|
|
|
+- const uint8_t *data, const size_t length);
|
|
|
+-bool ParseMarkToBaseAttachment(const ots::Font *font,
|
|
|
+- const uint8_t *data, const size_t length);
|
|
|
+-bool ParseMarkToLigatureAttachment(const ots::Font *font,
|
|
|
+- const uint8_t *data, const size_t length);
|
|
|
+-bool ParseMarkToMarkAttachment(const ots::Font *font,
|
|
|
+- const uint8_t *data, const size_t length);
|
|
|
+-bool ParseContextPositioning(const ots::Font *font,
|
|
|
+- const uint8_t *data, const size_t length);
|
|
|
+-bool ParseChainedContextPositioning(const ots::Font *font,
|
|
|
+- const uint8_t *data, const size_t length);
|
|
|
+-bool ParseExtensionPositioning(const ots::Font *font,
|
|
|
+- const uint8_t *data, const size_t length);
|
|
|
+-
|
|
|
+-const ots::LookupSubtableParser::TypeParser kGposTypeParsers[] = {
|
|
|
+- {GPOS_TYPE_SINGLE_ADJUSTMENT, ParseSingleAdjustment},
|
|
|
+- {GPOS_TYPE_PAIR_ADJUSTMENT, ParsePairAdjustment},
|
|
|
+- {GPOS_TYPE_CURSIVE_ATTACHMENT, ParseCursiveAttachment},
|
|
|
+- {GPOS_TYPE_MARK_TO_BASE_ATTACHMENT, ParseMarkToBaseAttachment},
|
|
|
+- {GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT, ParseMarkToLigatureAttachment},
|
|
|
+- {GPOS_TYPE_MARK_TO_MARK_ATTACHMENT, ParseMarkToMarkAttachment},
|
|
|
+- {GPOS_TYPE_CONTEXT_POSITIONING, ParseContextPositioning},
|
|
|
+- {GPOS_TYPE_CHAINED_CONTEXT_POSITIONING, ParseChainedContextPositioning},
|
|
|
+- {GPOS_TYPE_EXTENSION_POSITIONING, ParseExtensionPositioning}
|
|
|
+-};
|
|
|
+-
|
|
|
+-const ots::LookupSubtableParser kGposLookupSubtableParser = {
|
|
|
+- arraysize(kGposTypeParsers),
|
|
|
+- GPOS_TYPE_EXTENSION_POSITIONING, kGposTypeParsers
|
|
|
+-};
|
|
|
+-
|
|
|
+ // Shared Tables: ValueRecord, Anchor Table, and MarkArray
|
|
|
+
|
|
|
+ size_t CalcValueRecordSize(const uint16_t value_format) {
|
|
|
+ size_t size = 0;
|
|
|
+ for (unsigned i = 0; i < 8; ++i) {
|
|
|
+ if ((value_format >> i) & 0x1) {
|
|
|
+ size += 2;
|
|
|
+ }
|
|
|
+@@ -209,69 +168,16 @@ bool ParseMarkArrayTable(const ots::Font
|
|
|
+ length - offset_mark_anchor)) {
|
|
|
+ return OTS_FAILURE_MSG("Faled to parse anchor table for mark table %d", i);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+-// Lookup Type 1:
|
|
|
+-// Single Adjustment Positioning Subtable
|
|
|
+-bool ParseSingleAdjustment(const ots::Font *font, const uint8_t *data,
|
|
|
+- const size_t length) {
|
|
|
+- ots::Buffer subtable(data, length);
|
|
|
+-
|
|
|
+- ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
|
|
|
+- font->GetTypedTable(OTS_TAG_MAXP));
|
|
|
+- if (!maxp) {
|
|
|
+- return OTS_FAILURE_MSG("Required maxp table missing");
|
|
|
+- }
|
|
|
+-
|
|
|
+- uint16_t format = 0;
|
|
|
+- uint16_t offset_coverage = 0;
|
|
|
+- uint16_t value_format = 0;
|
|
|
+- if (!subtable.ReadU16(&format) ||
|
|
|
+- !subtable.ReadU16(&offset_coverage) ||
|
|
|
+- !subtable.ReadU16(&value_format)) {
|
|
|
+- return OTS_FAILURE_MSG("Can't read single adjustment information");
|
|
|
+- }
|
|
|
+-
|
|
|
+- if (format == 1) {
|
|
|
+- // Format 1 exactly one value record.
|
|
|
+- if (!ParseValueRecord(font, &subtable, value_format)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to parse format 1 single adjustment table");
|
|
|
+- }
|
|
|
+- } else if (format == 2) {
|
|
|
+- uint16_t value_count = 0;
|
|
|
+- if (!subtable.ReadU16(&value_count)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to parse format 2 single adjustment table");
|
|
|
+- }
|
|
|
+- for (unsigned i = 0; i < value_count; ++i) {
|
|
|
+- if (!ParseValueRecord(font, &subtable, value_format)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to parse value record %d in format 2 single adjustment table", i);
|
|
|
+- }
|
|
|
+- }
|
|
|
+- } else {
|
|
|
+- return OTS_FAILURE_MSG("Bad format %d in single adjustment table", format);
|
|
|
+- }
|
|
|
+-
|
|
|
+- if (offset_coverage < subtable.offset() || offset_coverage >= length) {
|
|
|
+- return OTS_FAILURE_MSG("Bad coverage offset %d in single adjustment table", offset_coverage);
|
|
|
+- }
|
|
|
+-
|
|
|
+- if (!ots::ParseCoverageTable(font, data + offset_coverage,
|
|
|
+- length - offset_coverage,
|
|
|
+- maxp->num_glyphs)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to parse coverage table in single adjustment table");
|
|
|
+- }
|
|
|
+-
|
|
|
+- return true;
|
|
|
+-}
|
|
|
+-
|
|
|
+ bool ParsePairSetTable(const ots::Font *font,
|
|
|
+ const uint8_t *data, const size_t length,
|
|
|
+ const uint16_t value_format1,
|
|
|
+ const uint16_t value_format2,
|
|
|
+ const uint16_t num_glyphs) {
|
|
|
+ ots::Buffer subtable(data, length);
|
|
|
+
|
|
|
+ uint16_t value_count = 0;
|
|
|
+@@ -399,138 +305,16 @@ bool ParsePairPosFormat2(const ots::Font
|
|
|
+ length - offset_class_def2,
|
|
|
+ num_glyphs, ots::kMaxClassDefValue)) {
|
|
|
+ return OTS_FAILURE_MSG("Failed to parse class definition table 2");
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+-// Lookup Type 2:
|
|
|
+-// Pair Adjustment Positioning Subtable
|
|
|
+-bool ParsePairAdjustment(const ots::Font *font, const uint8_t *data,
|
|
|
+- const size_t length) {
|
|
|
+- ots::Buffer subtable(data, length);
|
|
|
+-
|
|
|
+- ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
|
|
|
+- font->GetTypedTable(OTS_TAG_MAXP));
|
|
|
+- if (!maxp) {
|
|
|
+- return OTS_FAILURE_MSG("Required maxp table missing");
|
|
|
+- }
|
|
|
+-
|
|
|
+- uint16_t format = 0;
|
|
|
+- uint16_t offset_coverage = 0;
|
|
|
+- uint16_t value_format1 = 0;
|
|
|
+- uint16_t value_format2 = 0;
|
|
|
+- if (!subtable.ReadU16(&format) ||
|
|
|
+- !subtable.ReadU16(&offset_coverage) ||
|
|
|
+- !subtable.ReadU16(&value_format1) ||
|
|
|
+- !subtable.ReadU16(&value_format2)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to read pair adjustment structure");
|
|
|
+- }
|
|
|
+-
|
|
|
+- if (format == 1) {
|
|
|
+- if (!ParsePairPosFormat1(font, data, length, value_format1, value_format2,
|
|
|
+- maxp->num_glyphs)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to parse pair pos format 1");
|
|
|
+- }
|
|
|
+- } else if (format == 2) {
|
|
|
+- if (!ParsePairPosFormat2(font, data, length, value_format1, value_format2,
|
|
|
+- maxp->num_glyphs)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to parse pair format 2");
|
|
|
+- }
|
|
|
+- } else {
|
|
|
+- return OTS_FAILURE_MSG("Bad pos pair format %d", format);
|
|
|
+- }
|
|
|
+-
|
|
|
+- if (offset_coverage < subtable.offset() || offset_coverage >= length) {
|
|
|
+- return OTS_FAILURE_MSG("Bad pair pos offset coverage %d", offset_coverage);
|
|
|
+- }
|
|
|
+- if (!ots::ParseCoverageTable(font, data + offset_coverage,
|
|
|
+- length - offset_coverage,
|
|
|
+- maxp->num_glyphs)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to parse coverage table");
|
|
|
+- }
|
|
|
+-
|
|
|
+- return true;
|
|
|
+-}
|
|
|
+-
|
|
|
+-// Lookup Type 3
|
|
|
+-// Cursive Attachment Positioning Subtable
|
|
|
+-bool ParseCursiveAttachment(const ots::Font *font, const uint8_t *data,
|
|
|
+- const size_t length) {
|
|
|
+- ots::Buffer subtable(data, length);
|
|
|
+-
|
|
|
+- ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
|
|
|
+- font->GetTypedTable(OTS_TAG_MAXP));
|
|
|
+- if (!maxp) {
|
|
|
+- return OTS_FAILURE_MSG("Required maxp table missing");
|
|
|
+- }
|
|
|
+-
|
|
|
+- uint16_t format = 0;
|
|
|
+- uint16_t offset_coverage = 0;
|
|
|
+- uint16_t entry_exit_count = 0;
|
|
|
+- if (!subtable.ReadU16(&format) ||
|
|
|
+- !subtable.ReadU16(&offset_coverage) ||
|
|
|
+- !subtable.ReadU16(&entry_exit_count)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to read cursive attachment structure");
|
|
|
+- }
|
|
|
+-
|
|
|
+- if (format != 1) {
|
|
|
+- return OTS_FAILURE_MSG("Bad cursive attachment format %d", format);
|
|
|
+- }
|
|
|
+-
|
|
|
+- // Check entry exit records.
|
|
|
+- const unsigned entry_exit_records_end =
|
|
|
+- 2 * static_cast<unsigned>(entry_exit_count) + 6;
|
|
|
+- if (entry_exit_records_end > std::numeric_limits<uint16_t>::max()) {
|
|
|
+- return OTS_FAILURE_MSG("Bad entry exit record end %d", entry_exit_records_end);
|
|
|
+- }
|
|
|
+- for (unsigned i = 0; i < entry_exit_count; ++i) {
|
|
|
+- uint16_t offset_entry_anchor = 0;
|
|
|
+- uint16_t offset_exit_anchor = 0;
|
|
|
+- if (!subtable.ReadU16(&offset_entry_anchor) ||
|
|
|
+- !subtable.ReadU16(&offset_exit_anchor)) {
|
|
|
+- return OTS_FAILURE_MSG("Can't read entry exit record %d", i);
|
|
|
+- }
|
|
|
+- // These offsets could be NULL.
|
|
|
+- if (offset_entry_anchor) {
|
|
|
+- if (offset_entry_anchor < entry_exit_records_end ||
|
|
|
+- offset_entry_anchor >= length) {
|
|
|
+- return OTS_FAILURE_MSG("Bad entry anchor offset %d in entry exit record %d", offset_entry_anchor, i);
|
|
|
+- }
|
|
|
+- if (!ParseAnchorTable(font, data + offset_entry_anchor,
|
|
|
+- length - offset_entry_anchor)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to parse entry anchor table in entry exit record %d", i);
|
|
|
+- }
|
|
|
+- }
|
|
|
+- if (offset_exit_anchor) {
|
|
|
+- if (offset_exit_anchor < entry_exit_records_end ||
|
|
|
+- offset_exit_anchor >= length) {
|
|
|
+- return OTS_FAILURE_MSG("Bad exit anchor offset %d in entry exit record %d", offset_exit_anchor, i);
|
|
|
+- }
|
|
|
+- if (!ParseAnchorTable(font, data + offset_exit_anchor,
|
|
|
+- length - offset_exit_anchor)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to parse exit anchor table in entry exit record %d", i);
|
|
|
+- }
|
|
|
+- }
|
|
|
+- }
|
|
|
+-
|
|
|
+- if (offset_coverage < subtable.offset() || offset_coverage >= length) {
|
|
|
+- return OTS_FAILURE_MSG("Bad coverage offset in cursive attachment %d", offset_coverage);
|
|
|
+- }
|
|
|
+- if (!ots::ParseCoverageTable(font, data + offset_coverage,
|
|
|
+- length - offset_coverage,
|
|
|
+- maxp->num_glyphs)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to parse coverage table in cursive attachment");
|
|
|
+- }
|
|
|
+-
|
|
|
+- return true;
|
|
|
+-}
|
|
|
+-
|
|
|
+ bool ParseAnchorArrayTable(const ots::Font *font,
|
|
|
+ const uint8_t *data, const size_t length,
|
|
|
+ const uint16_t class_count) {
|
|
|
+ ots::Buffer subtable(data, length);
|
|
|
+
|
|
|
+ uint16_t record_count = 0;
|
|
|
+ if (!subtable.ReadU16(&record_count)) {
|
|
|
+ return OTS_FAILURE_MSG("Can't read anchor array length");
|
|
|
+@@ -666,176 +450,262 @@ bool ParseMarkToAttachmentSubtables(cons
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ return OTS_FAILURE_MSG("Bad attachment type %d", type);
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
++} // namespace
|
|
|
++
|
|
|
++namespace ots {
|
|
|
++
|
|
|
++// Lookup Type 1:
|
|
|
++// Single Adjustment Positioning Subtable
|
|
|
++bool OpenTypeGPOS::ParseSingleAdjustment(const uint8_t *data,
|
|
|
++ const size_t length) {
|
|
|
++ Font* font = GetFont();
|
|
|
++ Buffer subtable(data, length);
|
|
|
++
|
|
|
++ OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
|
|
|
++ font->GetTypedTable(OTS_TAG_MAXP));
|
|
|
++ if (!maxp) {
|
|
|
++ return Error("Required maxp table missing");
|
|
|
++ }
|
|
|
++
|
|
|
++ uint16_t format = 0;
|
|
|
++ uint16_t offset_coverage = 0;
|
|
|
++ uint16_t value_format = 0;
|
|
|
++ if (!subtable.ReadU16(&format) ||
|
|
|
++ !subtable.ReadU16(&offset_coverage) ||
|
|
|
++ !subtable.ReadU16(&value_format)) {
|
|
|
++ return Error("Can't read single adjustment information");
|
|
|
++ }
|
|
|
++
|
|
|
++ if (format == 1) {
|
|
|
++ // Format 1 exactly one value record.
|
|
|
++ if (!ParseValueRecord(font, &subtable, value_format)) {
|
|
|
++ return Error("Failed to parse format 1 single adjustment table");
|
|
|
++ }
|
|
|
++ } else if (format == 2) {
|
|
|
++ uint16_t value_count = 0;
|
|
|
++ if (!subtable.ReadU16(&value_count)) {
|
|
|
++ return Error("Failed to parse format 2 single adjustment table");
|
|
|
++ }
|
|
|
++ for (unsigned i = 0; i < value_count; ++i) {
|
|
|
++ if (!ParseValueRecord(font, &subtable, value_format)) {
|
|
|
++ return Error("Failed to parse value record %d in format 2 single adjustment table", i);
|
|
|
++ }
|
|
|
++ }
|
|
|
++ } else {
|
|
|
++ return Error("Bad format %d in single adjustment table", format);
|
|
|
++ }
|
|
|
++
|
|
|
++ if (offset_coverage < subtable.offset() || offset_coverage >= length) {
|
|
|
++ return Error("Bad coverage offset %d in single adjustment table", offset_coverage);
|
|
|
++ }
|
|
|
++
|
|
|
++ if (!ots::ParseCoverageTable(font, data + offset_coverage,
|
|
|
++ length - offset_coverage,
|
|
|
++ maxp->num_glyphs)) {
|
|
|
++ return Error("Failed to parse coverage table in single adjustment table");
|
|
|
++ }
|
|
|
++
|
|
|
++ return true;
|
|
|
++}
|
|
|
++
|
|
|
++// Lookup Type 2:
|
|
|
++// Pair Adjustment Positioning Subtable
|
|
|
++bool OpenTypeGPOS::ParsePairAdjustment(const uint8_t *data,
|
|
|
++ const size_t length) {
|
|
|
++ Font* font = GetFont();
|
|
|
++ Buffer subtable(data, length);
|
|
|
++
|
|
|
++ OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
|
|
|
++ font->GetTypedTable(OTS_TAG_MAXP));
|
|
|
++ if (!maxp) {
|
|
|
++ return Error("Required maxp table missing");
|
|
|
++ }
|
|
|
++
|
|
|
++ uint16_t format = 0;
|
|
|
++ uint16_t offset_coverage = 0;
|
|
|
++ uint16_t value_format1 = 0;
|
|
|
++ uint16_t value_format2 = 0;
|
|
|
++ if (!subtable.ReadU16(&format) ||
|
|
|
++ !subtable.ReadU16(&offset_coverage) ||
|
|
|
++ !subtable.ReadU16(&value_format1) ||
|
|
|
++ !subtable.ReadU16(&value_format2)) {
|
|
|
++ return Error("Failed to read pair adjustment structure");
|
|
|
++ }
|
|
|
++
|
|
|
++ if (format == 1) {
|
|
|
++ if (!ParsePairPosFormat1(font, data, length, value_format1, value_format2,
|
|
|
++ maxp->num_glyphs)) {
|
|
|
++ return Error("Failed to parse pair pos format 1");
|
|
|
++ }
|
|
|
++ } else if (format == 2) {
|
|
|
++ if (!ParsePairPosFormat2(font, data, length, value_format1, value_format2,
|
|
|
++ maxp->num_glyphs)) {
|
|
|
++ return Error("Failed to parse pair format 2");
|
|
|
++ }
|
|
|
++ } else {
|
|
|
++ return Error("Bad pos pair format %d", format);
|
|
|
++ }
|
|
|
++
|
|
|
++ if (offset_coverage < subtable.offset() || offset_coverage >= length) {
|
|
|
++ return Error("Bad pair pos offset coverage %d", offset_coverage);
|
|
|
++ }
|
|
|
++ if (!ots::ParseCoverageTable(font, data + offset_coverage,
|
|
|
++ length - offset_coverage,
|
|
|
++ maxp->num_glyphs)) {
|
|
|
++ return Error("Failed to parse coverage table");
|
|
|
++ }
|
|
|
++
|
|
|
++ return true;
|
|
|
++}
|
|
|
++
|
|
|
++// Lookup Type 3
|
|
|
++// Cursive Attachment Positioning Subtable
|
|
|
++bool OpenTypeGPOS::ParseCursiveAttachment(const uint8_t *data,
|
|
|
++ const size_t length) {
|
|
|
++ Font* font = GetFont();
|
|
|
++ Buffer subtable(data, length);
|
|
|
++
|
|
|
++ OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
|
|
|
++ font->GetTypedTable(OTS_TAG_MAXP));
|
|
|
++ if (!maxp) {
|
|
|
++ return Error("Required maxp table missing");
|
|
|
++ }
|
|
|
++
|
|
|
++ uint16_t format = 0;
|
|
|
++ uint16_t offset_coverage = 0;
|
|
|
++ uint16_t entry_exit_count = 0;
|
|
|
++ if (!subtable.ReadU16(&format) ||
|
|
|
++ !subtable.ReadU16(&offset_coverage) ||
|
|
|
++ !subtable.ReadU16(&entry_exit_count)) {
|
|
|
++ return Error("Failed to read cursive attachment structure");
|
|
|
++ }
|
|
|
++
|
|
|
++ if (format != 1) {
|
|
|
++ return Error("Bad cursive attachment format %d", format);
|
|
|
++ }
|
|
|
++
|
|
|
++ // Check entry exit records.
|
|
|
++ const unsigned entry_exit_records_end =
|
|
|
++ 2 * static_cast<unsigned>(entry_exit_count) + 6;
|
|
|
++ if (entry_exit_records_end > std::numeric_limits<uint16_t>::max()) {
|
|
|
++ return Error("Bad entry exit record end %d", entry_exit_records_end);
|
|
|
++ }
|
|
|
++ for (unsigned i = 0; i < entry_exit_count; ++i) {
|
|
|
++ uint16_t offset_entry_anchor = 0;
|
|
|
++ uint16_t offset_exit_anchor = 0;
|
|
|
++ if (!subtable.ReadU16(&offset_entry_anchor) ||
|
|
|
++ !subtable.ReadU16(&offset_exit_anchor)) {
|
|
|
++ return Error("Can't read entry exit record %d", i);
|
|
|
++ }
|
|
|
++ // These offsets could be NULL.
|
|
|
++ if (offset_entry_anchor) {
|
|
|
++ if (offset_entry_anchor < entry_exit_records_end ||
|
|
|
++ offset_entry_anchor >= length) {
|
|
|
++ return Error("Bad entry anchor offset %d in entry exit record %d", offset_entry_anchor, i);
|
|
|
++ }
|
|
|
++ if (!ParseAnchorTable(font, data + offset_entry_anchor,
|
|
|
++ length - offset_entry_anchor)) {
|
|
|
++ return Error("Failed to parse entry anchor table in entry exit record %d", i);
|
|
|
++ }
|
|
|
++ }
|
|
|
++ if (offset_exit_anchor) {
|
|
|
++ if (offset_exit_anchor < entry_exit_records_end ||
|
|
|
++ offset_exit_anchor >= length) {
|
|
|
++ return Error("Bad exit anchor offset %d in entry exit record %d", offset_exit_anchor, i);
|
|
|
++ }
|
|
|
++ if (!ParseAnchorTable(font, data + offset_exit_anchor,
|
|
|
++ length - offset_exit_anchor)) {
|
|
|
++ return Error("Failed to parse exit anchor table in entry exit record %d", i);
|
|
|
++ }
|
|
|
++ }
|
|
|
++ }
|
|
|
++
|
|
|
++ if (offset_coverage < subtable.offset() || offset_coverage >= length) {
|
|
|
++ return Error("Bad coverage offset in cursive attachment %d", offset_coverage);
|
|
|
++ }
|
|
|
++ if (!ots::ParseCoverageTable(font, data + offset_coverage,
|
|
|
++ length - offset_coverage,
|
|
|
++ maxp->num_glyphs)) {
|
|
|
++ return Error("Failed to parse coverage table in cursive attachment");
|
|
|
++ }
|
|
|
++
|
|
|
++ return true;
|
|
|
++}
|
|
|
++
|
|
|
+ // Lookup Type 4:
|
|
|
+ // MarkToBase Attachment Positioning Subtable
|
|
|
+-bool ParseMarkToBaseAttachment(const ots::Font *font,
|
|
|
+- const uint8_t *data, const size_t length) {
|
|
|
+- return ParseMarkToAttachmentSubtables(font, data, length,
|
|
|
++bool OpenTypeGPOS::ParseMarkToBaseAttachment(const uint8_t *data,
|
|
|
++ const size_t length) {
|
|
|
++ return ParseMarkToAttachmentSubtables(GetFont(), data, length,
|
|
|
+ GPOS_TYPE_MARK_TO_BASE_ATTACHMENT);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Lookup Type 5:
|
|
|
+ // MarkToLigature Attachment Positioning Subtable
|
|
|
+-bool ParseMarkToLigatureAttachment(const ots::Font *font,
|
|
|
+- const uint8_t *data, const size_t length) {
|
|
|
+- return ParseMarkToAttachmentSubtables(font, data, length,
|
|
|
++bool OpenTypeGPOS::ParseMarkToLigatureAttachment(const uint8_t *data,
|
|
|
++ const size_t length) {
|
|
|
++ return ParseMarkToAttachmentSubtables(GetFont(), data, length,
|
|
|
+ GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Lookup Type 6:
|
|
|
+ // MarkToMark Attachment Positioning Subtable
|
|
|
+-bool ParseMarkToMarkAttachment(const ots::Font *font,
|
|
|
+- const uint8_t *data, const size_t length) {
|
|
|
+- return ParseMarkToAttachmentSubtables(font, data, length,
|
|
|
++bool OpenTypeGPOS::ParseMarkToMarkAttachment(const uint8_t *data,
|
|
|
++ const size_t length) {
|
|
|
++ return ParseMarkToAttachmentSubtables(GetFont(), data, length,
|
|
|
+ GPOS_TYPE_MARK_TO_MARK_ATTACHMENT);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Lookup Type 7:
|
|
|
+ // Contextual Positioning Subtables
|
|
|
+-bool ParseContextPositioning(const ots::Font *font,
|
|
|
+- const uint8_t *data, const size_t length) {
|
|
|
+- ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
|
|
|
+- font->GetTypedTable(OTS_TAG_MAXP));
|
|
|
+- if (!maxp) {
|
|
|
+- return OTS_FAILURE_MSG("Required maxp table missing");
|
|
|
+- }
|
|
|
+- ots::OpenTypeGPOS *gpos = static_cast<ots::OpenTypeGPOS*>(
|
|
|
+- font->GetTypedTable(OTS_TAG_GPOS));
|
|
|
+- if (!gpos) {
|
|
|
+- return OTS_FAILURE_MSG("Internal error!");
|
|
|
+- }
|
|
|
+- return ots::ParseContextSubtable(font, data, length, maxp->num_glyphs,
|
|
|
+- gpos->num_lookups);
|
|
|
+-}
|
|
|
++// OpenTypeLayoutTable::ParseContextSubtable()
|
|
|
+
|
|
|
+ // Lookup Type 8:
|
|
|
+ // Chaining Contexual Positioning Subtable
|
|
|
+-bool ParseChainedContextPositioning(const ots::Font *font,
|
|
|
+- const uint8_t *data, const size_t length) {
|
|
|
+- ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
|
|
|
+- font->GetTypedTable(OTS_TAG_MAXP));
|
|
|
+- if (!maxp) {
|
|
|
+- return OTS_FAILURE_MSG("Required maxp table missing");
|
|
|
+- }
|
|
|
+- ots::OpenTypeGPOS *gpos = static_cast<ots::OpenTypeGPOS*>(
|
|
|
+- font->GetTypedTable(OTS_TAG_GPOS));
|
|
|
+- if (!gpos) {
|
|
|
+- return OTS_FAILURE_MSG("Internal error!");
|
|
|
+- }
|
|
|
+- return ots::ParseChainingContextSubtable(font, data, length,
|
|
|
+- maxp->num_glyphs,
|
|
|
+- gpos->num_lookups);
|
|
|
+-}
|
|
|
++// OpenTypeLayoutTable::ParseChainingContextSubtable()
|
|
|
+
|
|
|
+ // Lookup Type 9:
|
|
|
+ // Extension Positioning
|
|
|
+-bool ParseExtensionPositioning(const ots::Font *font,
|
|
|
+- const uint8_t *data, const size_t length) {
|
|
|
+- return ots::ParseExtensionSubtable(font, data, length,
|
|
|
+- &kGposLookupSubtableParser);
|
|
|
++// OpenTypeLayoutTable::ParseExtensionSubtable
|
|
|
++
|
|
|
++
|
|
|
++bool OpenTypeGPOS::ValidLookupSubtableType(const uint16_t lookup_type,
|
|
|
++ bool extension) const {
|
|
|
++ if (extension && lookup_type == GPOS_TYPE_EXTENSION_POSITIONING)
|
|
|
++ return false;
|
|
|
++ return lookup_type >= GPOS_TYPE_SINGLE_ADJUSTMENT && lookup_type < GPOS_TYPE_RESERVED;
|
|
|
+ }
|
|
|
+
|
|
|
+-} // namespace
|
|
|
+-
|
|
|
+-namespace ots {
|
|
|
+-
|
|
|
+-bool OpenTypeGPOS::Parse(const uint8_t *data, size_t length) {
|
|
|
+- Font *font = GetFont();
|
|
|
+- Buffer table(data, length);
|
|
|
+-
|
|
|
+- uint16_t version_major = 0, version_minor = 0;
|
|
|
+- uint16_t offset_script_list = 0;
|
|
|
+- uint16_t offset_feature_list = 0;
|
|
|
+- uint16_t offset_lookup_list = 0;
|
|
|
+- uint32_t offset_feature_variations = 0;
|
|
|
+- if (!table.ReadU16(&version_major) ||
|
|
|
+- !table.ReadU16(&version_minor) ||
|
|
|
+- !table.ReadU16(&offset_script_list) ||
|
|
|
+- !table.ReadU16(&offset_feature_list) ||
|
|
|
+- !table.ReadU16(&offset_lookup_list)) {
|
|
|
+- return Error("Incomplete table");
|
|
|
+- }
|
|
|
+-
|
|
|
+- if (version_major != 1 || version_minor > 1) {
|
|
|
+- return Error("Bad version");
|
|
|
+- }
|
|
|
+-
|
|
|
+- if (version_minor > 0) {
|
|
|
+- if (!table.ReadU32(&offset_feature_variations)) {
|
|
|
+- return Error("Incomplete table");
|
|
|
+- }
|
|
|
+- }
|
|
|
+-
|
|
|
+- const size_t header_size =
|
|
|
+- (version_minor == 0) ? kGposHeaderSize_1_0 : kGposHeaderSize_1_1;
|
|
|
+-
|
|
|
+- if (offset_lookup_list) {
|
|
|
+- if (offset_lookup_list < header_size || offset_lookup_list >= length) {
|
|
|
+- return Error("Bad lookup list offset in table header");
|
|
|
+- }
|
|
|
+-
|
|
|
+- if (!ParseLookupListTable(font, data + offset_lookup_list,
|
|
|
+- length - offset_lookup_list,
|
|
|
+- &kGposLookupSubtableParser,
|
|
|
+- &this->num_lookups)) {
|
|
|
+- return Error("Failed to parse lookup list table");
|
|
|
+- }
|
|
|
++bool OpenTypeGPOS::ParseLookupSubtable(const uint8_t *data, const size_t length,
|
|
|
++ const uint16_t lookup_type) {
|
|
|
++ switch (lookup_type) {
|
|
|
++ case GPOS_TYPE_SINGLE_ADJUSTMENT:
|
|
|
++ return ParseSingleAdjustment(data, length);
|
|
|
++ case GPOS_TYPE_PAIR_ADJUSTMENT:
|
|
|
++ return ParsePairAdjustment(data, length);
|
|
|
++ case GPOS_TYPE_CURSIVE_ATTACHMENT:
|
|
|
++ return ParseCursiveAttachment(data, length);
|
|
|
++ case GPOS_TYPE_MARK_TO_BASE_ATTACHMENT:
|
|
|
++ return ParseMarkToBaseAttachment(data, length);
|
|
|
++ case GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT:
|
|
|
++ return ParseMarkToLigatureAttachment(data, length);
|
|
|
++ case GPOS_TYPE_MARK_TO_MARK_ATTACHMENT:
|
|
|
++ return ParseMarkToMarkAttachment(data, length);
|
|
|
++ case GPOS_TYPE_CONTEXT_POSITIONING:
|
|
|
++ return ParseContextSubtable(data, length);
|
|
|
++ case GPOS_TYPE_CHAINED_CONTEXT_POSITIONING:
|
|
|
++ return ParseChainingContextSubtable(data, length);
|
|
|
++ case GPOS_TYPE_EXTENSION_POSITIONING:
|
|
|
++ return ParseExtensionSubtable(data, length);
|
|
|
+ }
|
|
|
+-
|
|
|
+- uint16_t num_features = 0;
|
|
|
+- if (offset_feature_list) {
|
|
|
+- if (offset_feature_list < header_size || offset_feature_list >= length) {
|
|
|
+- return Error("Bad feature list offset in table header");
|
|
|
+- }
|
|
|
+-
|
|
|
+- if (!ParseFeatureListTable(font, data + offset_feature_list,
|
|
|
+- length - offset_feature_list, this->num_lookups,
|
|
|
+- &num_features)) {
|
|
|
+- return Error("Failed to parse feature list table");
|
|
|
+- }
|
|
|
+- }
|
|
|
+-
|
|
|
+- if (offset_script_list) {
|
|
|
+- if (offset_script_list < header_size || offset_script_list >= length) {
|
|
|
+- return Error("Bad script list offset in table header");
|
|
|
+- }
|
|
|
+-
|
|
|
+- if (!ParseScriptListTable(font, data + offset_script_list,
|
|
|
+- length - offset_script_list, num_features)) {
|
|
|
+- return Error("Failed to parse script list table");
|
|
|
+- }
|
|
|
+- }
|
|
|
+-
|
|
|
+- if (offset_feature_variations) {
|
|
|
+- if (offset_feature_variations < header_size || offset_feature_variations >= length) {
|
|
|
+- return Error("Bad feature variations offset in table header");
|
|
|
+- }
|
|
|
+-
|
|
|
+- if (!ParseFeatureVariationsTable(font, data + offset_feature_variations,
|
|
|
+- length - offset_feature_variations,
|
|
|
+- this->num_lookups)) {
|
|
|
+- return Error("Failed to parse feature variations table");
|
|
|
+- }
|
|
|
+- }
|
|
|
+-
|
|
|
+- this->m_data = data;
|
|
|
+- this->m_length = length;
|
|
|
+- return true;
|
|
|
+-}
|
|
|
+-
|
|
|
+-bool OpenTypeGPOS::Serialize(OTSStream *out) {
|
|
|
+- if (!out->Write(this->m_data, this->m_length)) {
|
|
|
+- return Error("Failed to write GPOS table");
|
|
|
+- }
|
|
|
+-
|
|
|
+- return true;
|
|
|
++ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ } // namespace ots
|
|
|
+
|
|
|
+ #undef TABLE_NAME
|
|
|
+diff --git a/gfx/ots/src/gpos.h b/gfx/ots/src/gpos.h
|
|
|
+--- a/gfx/ots/src/gpos.h
|
|
|
++++ b/gfx/ots/src/gpos.h
|
|
|
+@@ -1,35 +1,35 @@
|
|
|
+ // Copyright (c) 2011-2017 The OTS Authors. All rights reserved.
|
|
|
+ // Use of this source code is governed by a BSD-style license that can be
|
|
|
+ // found in the LICENSE file.
|
|
|
+
|
|
|
+ #ifndef OTS_GPOS_H_
|
|
|
+ #define OTS_GPOS_H_
|
|
|
+
|
|
|
+ #include "ots.h"
|
|
|
++#include "layout.h"
|
|
|
+
|
|
|
+ namespace ots {
|
|
|
+
|
|
|
+-class OpenTypeGPOS : public Table {
|
|
|
++class OpenTypeGPOS : public OpenTypeLayoutTable {
|
|
|
+ public:
|
|
|
+ explicit OpenTypeGPOS(Font *font, uint32_t tag)
|
|
|
+- : Table(font, tag, tag),
|
|
|
+- num_lookups(0),
|
|
|
+- m_data(NULL),
|
|
|
+- m_length(0) {
|
|
|
+- }
|
|
|
+-
|
|
|
+- bool Parse(const uint8_t *data, size_t length);
|
|
|
+- bool Serialize(OTSStream *out);
|
|
|
+-
|
|
|
+- // Number of lookups in GPOS table
|
|
|
+- uint16_t num_lookups;
|
|
|
++ : OpenTypeLayoutTable(font, tag, tag) { }
|
|
|
+
|
|
|
+ private:
|
|
|
+- const uint8_t *m_data;
|
|
|
+- size_t m_length;
|
|
|
++ bool ValidLookupSubtableType(const uint16_t lookup_type,
|
|
|
++ bool extension = false) const;
|
|
|
++ bool ParseLookupSubtable(const uint8_t *data, const size_t length,
|
|
|
++ const uint16_t lookup_type);
|
|
|
++
|
|
|
++ bool ParseSingleAdjustment(const uint8_t *data, const size_t length);
|
|
|
++ bool ParsePairAdjustment(const uint8_t *data, const size_t length);
|
|
|
++ bool ParseCursiveAttachment(const uint8_t *data, const size_t length);
|
|
|
++ bool ParseMarkToBaseAttachment(const uint8_t *data, const size_t length);
|
|
|
++ bool ParseMarkToLigatureAttachment(const uint8_t *data, const size_t length);
|
|
|
++ bool ParseMarkToMarkAttachment(const uint8_t *data, const size_t length);
|
|
|
+ };
|
|
|
+
|
|
|
+ } // namespace ots
|
|
|
+
|
|
|
+ #endif
|
|
|
+
|
|
|
+diff --git a/gfx/ots/src/gsub.cc b/gfx/ots/src/gsub.cc
|
|
|
+--- a/gfx/ots/src/gsub.cc
|
|
|
++++ b/gfx/ots/src/gsub.cc
|
|
|
+@@ -12,131 +12,28 @@
|
|
|
+
|
|
|
+ // GSUB - The Glyph Substitution Table
|
|
|
+ // http://www.microsoft.com/typography/otspec/gsub.htm
|
|
|
+
|
|
|
+ #define TABLE_NAME "GSUB"
|
|
|
+
|
|
|
+ namespace {
|
|
|
+
|
|
|
+-// The GSUB header size for table version 1.0
|
|
|
+-const size_t kGsubHeaderSize_1_0 = 4 + 3 * 2;
|
|
|
+-// GSUB header size v1.1
|
|
|
+-const size_t kGsubHeaderSize_1_1 = 4 + 3 * 2 + 4;
|
|
|
+-
|
|
|
+ enum GSUB_TYPE {
|
|
|
+ GSUB_TYPE_SINGLE = 1,
|
|
|
+ GSUB_TYPE_MULTIPLE = 2,
|
|
|
+ GSUB_TYPE_ALTERNATE = 3,
|
|
|
+ GSUB_TYPE_LIGATURE = 4,
|
|
|
+ GSUB_TYPE_CONTEXT = 5,
|
|
|
+ GSUB_TYPE_CHANGING_CONTEXT = 6,
|
|
|
+ GSUB_TYPE_EXTENSION_SUBSTITUTION = 7,
|
|
|
+ GSUB_TYPE_REVERSE_CHAINING_CONTEXT_SINGLE = 8,
|
|
|
+ GSUB_TYPE_RESERVED = 9
|
|
|
+ };
|
|
|
+
|
|
|
+-// Lookup type parsers.
|
|
|
+-bool ParseSingleSubstitution(const ots::Font *font,
|
|
|
+- const uint8_t *data, const size_t length);
|
|
|
+-bool ParseMutipleSubstitution(const ots::Font *font,
|
|
|
+- const uint8_t *data, const size_t length);
|
|
|
+-bool ParseAlternateSubstitution(const ots::Font *font,
|
|
|
+- const uint8_t *data, const size_t length);
|
|
|
+-bool ParseLigatureSubstitution(const ots::Font *font,
|
|
|
+- const uint8_t *data, const size_t length);
|
|
|
+-bool ParseContextSubstitution(const ots::Font *font,
|
|
|
+- const uint8_t *data, const size_t length);
|
|
|
+-bool ParseChainingContextSubstitution(const ots::Font *font,
|
|
|
+- const uint8_t *data,
|
|
|
+- const size_t length);
|
|
|
+-bool ParseExtensionSubstitution(const ots::Font *font,
|
|
|
+- const uint8_t *data, const size_t length);
|
|
|
+-bool ParseReverseChainingContextSingleSubstitution(
|
|
|
+- const ots::Font *font, const uint8_t *data, const size_t length);
|
|
|
+-
|
|
|
+-const ots::LookupSubtableParser::TypeParser kGsubTypeParsers[] = {
|
|
|
+- {GSUB_TYPE_SINGLE, ParseSingleSubstitution},
|
|
|
+- {GSUB_TYPE_MULTIPLE, ParseMutipleSubstitution},
|
|
|
+- {GSUB_TYPE_ALTERNATE, ParseAlternateSubstitution},
|
|
|
+- {GSUB_TYPE_LIGATURE, ParseLigatureSubstitution},
|
|
|
+- {GSUB_TYPE_CONTEXT, ParseContextSubstitution},
|
|
|
+- {GSUB_TYPE_CHANGING_CONTEXT, ParseChainingContextSubstitution},
|
|
|
+- {GSUB_TYPE_EXTENSION_SUBSTITUTION, ParseExtensionSubstitution},
|
|
|
+- {GSUB_TYPE_REVERSE_CHAINING_CONTEXT_SINGLE,
|
|
|
+- ParseReverseChainingContextSingleSubstitution}
|
|
|
+-};
|
|
|
+-
|
|
|
+-const ots::LookupSubtableParser kGsubLookupSubtableParser = {
|
|
|
+- arraysize(kGsubTypeParsers),
|
|
|
+- GSUB_TYPE_EXTENSION_SUBSTITUTION, kGsubTypeParsers
|
|
|
+-};
|
|
|
+-
|
|
|
+-// Lookup Type 1:
|
|
|
+-// Single Substitution Subtable
|
|
|
+-bool ParseSingleSubstitution(const ots::Font *font,
|
|
|
+- const uint8_t *data, const size_t length) {
|
|
|
+- ots::Buffer subtable(data, length);
|
|
|
+-
|
|
|
+- uint16_t format = 0;
|
|
|
+- uint16_t offset_coverage = 0;
|
|
|
+-
|
|
|
+- if (!subtable.ReadU16(&format) ||
|
|
|
+- !subtable.ReadU16(&offset_coverage)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to read single subst table header");
|
|
|
+- }
|
|
|
+-
|
|
|
+- ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
|
|
|
+- font->GetTypedTable(OTS_TAG_MAXP));
|
|
|
+- if (!maxp) {
|
|
|
+- return OTS_FAILURE_MSG("Required maxp table missing");
|
|
|
+- }
|
|
|
+- const uint16_t num_glyphs = maxp->num_glyphs;
|
|
|
+- if (format == 1) {
|
|
|
+- // Parse SingleSubstFormat1
|
|
|
+- int16_t delta_glyph_id = 0;
|
|
|
+- if (!subtable.ReadS16(&delta_glyph_id)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to read glyph shift from format 1 single subst table");
|
|
|
+- }
|
|
|
+- if (std::abs(delta_glyph_id) >= num_glyphs) {
|
|
|
+- return OTS_FAILURE_MSG("bad glyph shift of %d in format 1 single subst table", delta_glyph_id);
|
|
|
+- }
|
|
|
+- } else if (format == 2) {
|
|
|
+- // Parse SingleSubstFormat2
|
|
|
+- uint16_t glyph_count = 0;
|
|
|
+- if (!subtable.ReadU16(&glyph_count)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to read glyph cound in format 2 single subst table");
|
|
|
+- }
|
|
|
+- if (glyph_count > num_glyphs) {
|
|
|
+- return OTS_FAILURE_MSG("Bad glyph count %d > %d in format 2 single subst table", glyph_count, num_glyphs);
|
|
|
+- }
|
|
|
+- for (unsigned i = 0; i < glyph_count; ++i) {
|
|
|
+- uint16_t substitute = 0;
|
|
|
+- if (!subtable.ReadU16(&substitute)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to read substitution %d in format 2 single subst table", i);
|
|
|
+- }
|
|
|
+- if (substitute >= num_glyphs) {
|
|
|
+- return OTS_FAILURE_MSG("too large substitute: %u", substitute);
|
|
|
+- }
|
|
|
+- }
|
|
|
+- } else {
|
|
|
+- return OTS_FAILURE_MSG("Bad single subst table format %d", format);
|
|
|
+- }
|
|
|
+-
|
|
|
+- if (offset_coverage < subtable.offset() || offset_coverage >= length) {
|
|
|
+- return OTS_FAILURE_MSG("Bad coverage offset %x", offset_coverage);
|
|
|
+- }
|
|
|
+- if (!ots::ParseCoverageTable(font, data + offset_coverage,
|
|
|
+- length - offset_coverage, num_glyphs)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to parse coverage table");
|
|
|
+- }
|
|
|
+-
|
|
|
+- return true;
|
|
|
+-}
|
|
|
+-
|
|
|
+ bool ParseSequenceTable(const ots::Font *font,
|
|
|
+ const uint8_t *data, const size_t length,
|
|
|
+ const uint16_t num_glyphs) {
|
|
|
+ ots::Buffer subtable(data, length);
|
|
|
+
|
|
|
+ uint16_t glyph_count = 0;
|
|
|
+ if (!subtable.ReadU16(&glyph_count)) {
|
|
|
+ return OTS_FAILURE_MSG("Failed to read glyph count in sequence table");
|
|
|
+@@ -152,72 +49,16 @@ bool ParseSequenceTable(const ots::Font
|
|
|
+ if (substitute >= num_glyphs) {
|
|
|
+ return OTS_FAILURE_MSG("Bad substitution (%d) %d > %d", i, substitute, num_glyphs);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+-// Lookup Type 2:
|
|
|
+-// Multiple Substitution Subtable
|
|
|
+-bool ParseMutipleSubstitution(const ots::Font *font,
|
|
|
+- const uint8_t *data, const size_t length) {
|
|
|
+- ots::Buffer subtable(data, length);
|
|
|
+-
|
|
|
+- uint16_t format = 0;
|
|
|
+- uint16_t offset_coverage = 0;
|
|
|
+- uint16_t sequence_count = 0;
|
|
|
+-
|
|
|
+- if (!subtable.ReadU16(&format) ||
|
|
|
+- !subtable.ReadU16(&offset_coverage) ||
|
|
|
+- !subtable.ReadU16(&sequence_count)) {
|
|
|
+- return OTS_FAILURE_MSG("Can't read header of multiple subst table");
|
|
|
+- }
|
|
|
+-
|
|
|
+- if (format != 1) {
|
|
|
+- return OTS_FAILURE_MSG("Bad multiple subst table format %d", format);
|
|
|
+- }
|
|
|
+-
|
|
|
+- ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
|
|
|
+- font->GetTypedTable(OTS_TAG_MAXP));
|
|
|
+- if (!maxp) {
|
|
|
+- return OTS_FAILURE_MSG("Required maxp table missing");
|
|
|
+- }
|
|
|
+- const uint16_t num_glyphs = maxp->num_glyphs;
|
|
|
+- const unsigned sequence_end = static_cast<unsigned>(6) +
|
|
|
+- sequence_count * 2;
|
|
|
+- if (sequence_end > std::numeric_limits<uint16_t>::max()) {
|
|
|
+- return OTS_FAILURE_MSG("Bad sequence end %d, in multiple subst", sequence_end);
|
|
|
+- }
|
|
|
+- for (unsigned i = 0; i < sequence_count; ++i) {
|
|
|
+- uint16_t offset_sequence = 0;
|
|
|
+- if (!subtable.ReadU16(&offset_sequence)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to read sequence offset for sequence %d", i);
|
|
|
+- }
|
|
|
+- if (offset_sequence < sequence_end || offset_sequence >= length) {
|
|
|
+- return OTS_FAILURE_MSG("Bad sequence offset %d for sequence %d", offset_sequence, i);
|
|
|
+- }
|
|
|
+- if (!ParseSequenceTable(font, data + offset_sequence, length - offset_sequence,
|
|
|
+- num_glyphs)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to parse sequence table %d", i);
|
|
|
+- }
|
|
|
+- }
|
|
|
+-
|
|
|
+- if (offset_coverage < sequence_end || offset_coverage >= length) {
|
|
|
+- return OTS_FAILURE_MSG("Bad coverage offset %d", offset_coverage);
|
|
|
+- }
|
|
|
+- if (!ots::ParseCoverageTable(font, data + offset_coverage,
|
|
|
+- length - offset_coverage, num_glyphs)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to parse coverage table");
|
|
|
+- }
|
|
|
+-
|
|
|
+- return true;
|
|
|
+-}
|
|
|
+-
|
|
|
+ bool ParseAlternateSetTable(const ots::Font *font,
|
|
|
+ const uint8_t *data, const size_t length,
|
|
|
+ const uint16_t num_glyphs) {
|
|
|
+ ots::Buffer subtable(data, length);
|
|
|
+
|
|
|
+ uint16_t glyph_count = 0;
|
|
|
+ if (!subtable.ReadU16(&glyph_count)) {
|
|
|
+ return OTS_FAILURE_MSG("Failed to read alternate set header");
|
|
|
+@@ -232,74 +73,16 @@ bool ParseAlternateSetTable(const ots::F
|
|
|
+ }
|
|
|
+ if (alternate >= num_glyphs) {
|
|
|
+ return OTS_FAILURE_MSG("Too large alternate: %u", alternate);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+-// Lookup Type 3:
|
|
|
+-// Alternate Substitution Subtable
|
|
|
+-bool ParseAlternateSubstitution(const ots::Font *font,
|
|
|
+- const uint8_t *data, const size_t length) {
|
|
|
+- ots::Buffer subtable(data, length);
|
|
|
+-
|
|
|
+- uint16_t format = 0;
|
|
|
+- uint16_t offset_coverage = 0;
|
|
|
+- uint16_t alternate_set_count = 0;
|
|
|
+-
|
|
|
+- if (!subtable.ReadU16(&format) ||
|
|
|
+- !subtable.ReadU16(&offset_coverage) ||
|
|
|
+- !subtable.ReadU16(&alternate_set_count)) {
|
|
|
+- return OTS_FAILURE_MSG("Can't read alternate subst header");
|
|
|
+- }
|
|
|
+-
|
|
|
+- if (format != 1) {
|
|
|
+- return OTS_FAILURE_MSG("Bad alternate subst table format %d", format);
|
|
|
+- }
|
|
|
+-
|
|
|
+- ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
|
|
|
+- font->GetTypedTable(OTS_TAG_MAXP));
|
|
|
+- if (!maxp) {
|
|
|
+- return OTS_FAILURE_MSG("Required maxp table missing");
|
|
|
+- }
|
|
|
+- const uint16_t num_glyphs = maxp->num_glyphs;
|
|
|
+- const unsigned alternate_set_end = static_cast<unsigned>(6) +
|
|
|
+- alternate_set_count * 2;
|
|
|
+- if (alternate_set_end > std::numeric_limits<uint16_t>::max()) {
|
|
|
+- return OTS_FAILURE_MSG("Bad end of alternate set %d", alternate_set_end);
|
|
|
+- }
|
|
|
+- for (unsigned i = 0; i < alternate_set_count; ++i) {
|
|
|
+- uint16_t offset_alternate_set = 0;
|
|
|
+- if (!subtable.ReadU16(&offset_alternate_set)) {
|
|
|
+- return OTS_FAILURE_MSG("Can't read alternate set offset for set %d", i);
|
|
|
+- }
|
|
|
+- if (offset_alternate_set < alternate_set_end ||
|
|
|
+- offset_alternate_set >= length) {
|
|
|
+- return OTS_FAILURE_MSG("Bad alternate set offset %d for set %d", offset_alternate_set, i);
|
|
|
+- }
|
|
|
+- if (!ParseAlternateSetTable(font, data + offset_alternate_set,
|
|
|
+- length - offset_alternate_set,
|
|
|
+- num_glyphs)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to parse alternate set");
|
|
|
+- }
|
|
|
+- }
|
|
|
+-
|
|
|
+- if (offset_coverage < alternate_set_end || offset_coverage >= length) {
|
|
|
+- return OTS_FAILURE_MSG("Bad coverage offset %d", offset_coverage);
|
|
|
+- }
|
|
|
+- if (!ots::ParseCoverageTable(font, data + offset_coverage,
|
|
|
+- length - offset_coverage, num_glyphs)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to parse coverage table");
|
|
|
+- }
|
|
|
+-
|
|
|
+- return true;
|
|
|
+-}
|
|
|
+-
|
|
|
+ bool ParseLigatureTable(const ots::Font *font,
|
|
|
+ const uint8_t *data, const size_t length,
|
|
|
+ const uint16_t num_glyphs) {
|
|
|
+ ots::Buffer subtable(data, length);
|
|
|
+
|
|
|
+ uint16_t lig_glyph = 0;
|
|
|
+ uint16_t comp_count = 0;
|
|
|
+
|
|
|
+@@ -354,313 +137,397 @@ bool ParseLigatureSetTable(const ots::Fo
|
|
|
+ num_glyphs)) {
|
|
|
+ return OTS_FAILURE_MSG("Failed to parse ligature %d", i);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
++} // namespace
|
|
|
++
|
|
|
++namespace ots {
|
|
|
++
|
|
|
++// Lookup Type 1:
|
|
|
++// Single Substitution Subtable
|
|
|
++bool OpenTypeGSUB::ParseSingleSubstitution(const uint8_t *data,
|
|
|
++ const size_t length) {
|
|
|
++ Font* font = GetFont();
|
|
|
++ Buffer subtable(data, length);
|
|
|
++
|
|
|
++ uint16_t format = 0;
|
|
|
++ uint16_t offset_coverage = 0;
|
|
|
++
|
|
|
++ if (!subtable.ReadU16(&format) ||
|
|
|
++ !subtable.ReadU16(&offset_coverage)) {
|
|
|
++ return Error("Failed to read single subst table header");
|
|
|
++ }
|
|
|
++
|
|
|
++ OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
|
|
|
++ font->GetTypedTable(OTS_TAG_MAXP));
|
|
|
++ if (!maxp) {
|
|
|
++ return Error("Required maxp table missing");
|
|
|
++ }
|
|
|
++ const uint16_t num_glyphs = maxp->num_glyphs;
|
|
|
++ if (format == 1) {
|
|
|
++ // Parse SingleSubstFormat1
|
|
|
++ int16_t delta_glyph_id = 0;
|
|
|
++ if (!subtable.ReadS16(&delta_glyph_id)) {
|
|
|
++ return Error("Failed to read glyph shift from format 1 single subst table");
|
|
|
++ }
|
|
|
++ if (std::abs(delta_glyph_id) >= num_glyphs) {
|
|
|
++ return Error("bad glyph shift of %d in format 1 single subst table", delta_glyph_id);
|
|
|
++ }
|
|
|
++ } else if (format == 2) {
|
|
|
++ // Parse SingleSubstFormat2
|
|
|
++ uint16_t glyph_count = 0;
|
|
|
++ if (!subtable.ReadU16(&glyph_count)) {
|
|
|
++ return Error("Failed to read glyph cound in format 2 single subst table");
|
|
|
++ }
|
|
|
++ if (glyph_count > num_glyphs) {
|
|
|
++ return Error("Bad glyph count %d > %d in format 2 single subst table", glyph_count, num_glyphs);
|
|
|
++ }
|
|
|
++ for (unsigned i = 0; i < glyph_count; ++i) {
|
|
|
++ uint16_t substitute = 0;
|
|
|
++ if (!subtable.ReadU16(&substitute)) {
|
|
|
++ return Error("Failed to read substitution %d in format 2 single subst table", i);
|
|
|
++ }
|
|
|
++ if (substitute >= num_glyphs) {
|
|
|
++ return Error("too large substitute: %u", substitute);
|
|
|
++ }
|
|
|
++ }
|
|
|
++ } else {
|
|
|
++ return Error("Bad single subst table format %d", format);
|
|
|
++ }
|
|
|
++
|
|
|
++ if (offset_coverage < subtable.offset() || offset_coverage >= length) {
|
|
|
++ return Error("Bad coverage offset %x", offset_coverage);
|
|
|
++ }
|
|
|
++ if (!ots::ParseCoverageTable(font, data + offset_coverage,
|
|
|
++ length - offset_coverage, num_glyphs)) {
|
|
|
++ return Error("Failed to parse coverage table");
|
|
|
++ }
|
|
|
++
|
|
|
++ return true;
|
|
|
++}
|
|
|
++
|
|
|
++// Lookup Type 2:
|
|
|
++// Multiple Substitution Subtable
|
|
|
++bool OpenTypeGSUB::ParseMutipleSubstitution(const uint8_t *data,
|
|
|
++ const size_t length) {
|
|
|
++ Font* font = GetFont();
|
|
|
++ Buffer subtable(data, length);
|
|
|
++
|
|
|
++ uint16_t format = 0;
|
|
|
++ uint16_t offset_coverage = 0;
|
|
|
++ uint16_t sequence_count = 0;
|
|
|
++
|
|
|
++ if (!subtable.ReadU16(&format) ||
|
|
|
++ !subtable.ReadU16(&offset_coverage) ||
|
|
|
++ !subtable.ReadU16(&sequence_count)) {
|
|
|
++ return Error("Can't read header of multiple subst table");
|
|
|
++ }
|
|
|
++
|
|
|
++ if (format != 1) {
|
|
|
++ return Error("Bad multiple subst table format %d", format);
|
|
|
++ }
|
|
|
++
|
|
|
++ OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
|
|
|
++ font->GetTypedTable(OTS_TAG_MAXP));
|
|
|
++ if (!maxp) {
|
|
|
++ return Error("Required maxp table missing");
|
|
|
++ }
|
|
|
++ const uint16_t num_glyphs = maxp->num_glyphs;
|
|
|
++ const unsigned sequence_end = static_cast<unsigned>(6) +
|
|
|
++ sequence_count * 2;
|
|
|
++ if (sequence_end > std::numeric_limits<uint16_t>::max()) {
|
|
|
++ return Error("Bad sequence end %d, in multiple subst", sequence_end);
|
|
|
++ }
|
|
|
++ for (unsigned i = 0; i < sequence_count; ++i) {
|
|
|
++ uint16_t offset_sequence = 0;
|
|
|
++ if (!subtable.ReadU16(&offset_sequence)) {
|
|
|
++ return Error("Failed to read sequence offset for sequence %d", i);
|
|
|
++ }
|
|
|
++ if (offset_sequence < sequence_end || offset_sequence >= length) {
|
|
|
++ return Error("Bad sequence offset %d for sequence %d", offset_sequence, i);
|
|
|
++ }
|
|
|
++ if (!ParseSequenceTable(font, data + offset_sequence, length - offset_sequence,
|
|
|
++ num_glyphs)) {
|
|
|
++ return Error("Failed to parse sequence table %d", i);
|
|
|
++ }
|
|
|
++ }
|
|
|
++
|
|
|
++ if (offset_coverage < sequence_end || offset_coverage >= length) {
|
|
|
++ return Error("Bad coverage offset %d", offset_coverage);
|
|
|
++ }
|
|
|
++ if (!ots::ParseCoverageTable(font, data + offset_coverage,
|
|
|
++ length - offset_coverage, num_glyphs)) {
|
|
|
++ return Error("Failed to parse coverage table");
|
|
|
++ }
|
|
|
++
|
|
|
++ return true;
|
|
|
++}
|
|
|
++
|
|
|
++// Lookup Type 3:
|
|
|
++// Alternate Substitution Subtable
|
|
|
++bool OpenTypeGSUB::ParseAlternateSubstitution(const uint8_t *data,
|
|
|
++ const size_t length) {
|
|
|
++ Font* font = GetFont();
|
|
|
++ Buffer subtable(data, length);
|
|
|
++
|
|
|
++ uint16_t format = 0;
|
|
|
++ uint16_t offset_coverage = 0;
|
|
|
++ uint16_t alternate_set_count = 0;
|
|
|
++
|
|
|
++ if (!subtable.ReadU16(&format) ||
|
|
|
++ !subtable.ReadU16(&offset_coverage) ||
|
|
|
++ !subtable.ReadU16(&alternate_set_count)) {
|
|
|
++ return Error("Can't read alternate subst header");
|
|
|
++ }
|
|
|
++
|
|
|
++ if (format != 1) {
|
|
|
++ return Error("Bad alternate subst table format %d", format);
|
|
|
++ }
|
|
|
++
|
|
|
++ OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
|
|
|
++ font->GetTypedTable(OTS_TAG_MAXP));
|
|
|
++ if (!maxp) {
|
|
|
++ return Error("Required maxp table missing");
|
|
|
++ }
|
|
|
++ const uint16_t num_glyphs = maxp->num_glyphs;
|
|
|
++ const unsigned alternate_set_end = static_cast<unsigned>(6) +
|
|
|
++ alternate_set_count * 2;
|
|
|
++ if (alternate_set_end > std::numeric_limits<uint16_t>::max()) {
|
|
|
++ return Error("Bad end of alternate set %d", alternate_set_end);
|
|
|
++ }
|
|
|
++ for (unsigned i = 0; i < alternate_set_count; ++i) {
|
|
|
++ uint16_t offset_alternate_set = 0;
|
|
|
++ if (!subtable.ReadU16(&offset_alternate_set)) {
|
|
|
++ return Error("Can't read alternate set offset for set %d", i);
|
|
|
++ }
|
|
|
++ if (offset_alternate_set < alternate_set_end ||
|
|
|
++ offset_alternate_set >= length) {
|
|
|
++ return Error("Bad alternate set offset %d for set %d", offset_alternate_set, i);
|
|
|
++ }
|
|
|
++ if (!ParseAlternateSetTable(font, data + offset_alternate_set,
|
|
|
++ length - offset_alternate_set,
|
|
|
++ num_glyphs)) {
|
|
|
++ return Error("Failed to parse alternate set");
|
|
|
++ }
|
|
|
++ }
|
|
|
++
|
|
|
++ if (offset_coverage < alternate_set_end || offset_coverage >= length) {
|
|
|
++ return Error("Bad coverage offset %d", offset_coverage);
|
|
|
++ }
|
|
|
++ if (!ots::ParseCoverageTable(font, data + offset_coverage,
|
|
|
++ length - offset_coverage, num_glyphs)) {
|
|
|
++ return Error("Failed to parse coverage table");
|
|
|
++ }
|
|
|
++
|
|
|
++ return true;
|
|
|
++}
|
|
|
++
|
|
|
+ // Lookup Type 4:
|
|
|
+ // Ligature Substitution Subtable
|
|
|
+-bool ParseLigatureSubstitution(const ots::Font *font,
|
|
|
+- const uint8_t *data, const size_t length) {
|
|
|
+- ots::Buffer subtable(data, length);
|
|
|
++bool OpenTypeGSUB::ParseLigatureSubstitution(const uint8_t *data,
|
|
|
++ const size_t length) {
|
|
|
++ Font* font = GetFont();
|
|
|
++ Buffer subtable(data, length);
|
|
|
+
|
|
|
+ uint16_t format = 0;
|
|
|
+ uint16_t offset_coverage = 0;
|
|
|
+ uint16_t lig_set_count = 0;
|
|
|
+
|
|
|
+ if (!subtable.ReadU16(&format) ||
|
|
|
+ !subtable.ReadU16(&offset_coverage) ||
|
|
|
+ !subtable.ReadU16(&lig_set_count)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to read ligature substitution header");
|
|
|
++ return Error("Failed to read ligature substitution header");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (format != 1) {
|
|
|
+- return OTS_FAILURE_MSG("Bad ligature substitution table format %d", format);
|
|
|
++ return Error("Bad ligature substitution table format %d", format);
|
|
|
+ }
|
|
|
+
|
|
|
+- ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
|
|
|
++ OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
|
|
|
+ font->GetTypedTable(OTS_TAG_MAXP));
|
|
|
+ if (!maxp) {
|
|
|
+- return OTS_FAILURE_MSG("Required maxp table missing");
|
|
|
++ return Error("Required maxp table missing");
|
|
|
+ }
|
|
|
+ const uint16_t num_glyphs = maxp->num_glyphs;
|
|
|
+ const unsigned ligature_set_end = static_cast<unsigned>(6) +
|
|
|
+ lig_set_count * 2;
|
|
|
+ if (ligature_set_end > std::numeric_limits<uint16_t>::max()) {
|
|
|
+- return OTS_FAILURE_MSG("Bad end of ligature set %d in ligature substitution table", ligature_set_end);
|
|
|
++ return Error("Bad end of ligature set %d in ligature substitution table", ligature_set_end);
|
|
|
+ }
|
|
|
+ for (unsigned i = 0; i < lig_set_count; ++i) {
|
|
|
+ uint16_t offset_ligature_set = 0;
|
|
|
+ if (!subtable.ReadU16(&offset_ligature_set)) {
|
|
|
+- return OTS_FAILURE_MSG("Can't read ligature set offset %d", i);
|
|
|
++ return Error("Can't read ligature set offset %d", i);
|
|
|
+ }
|
|
|
+ if (offset_ligature_set < ligature_set_end ||
|
|
|
+ offset_ligature_set >= length) {
|
|
|
+- return OTS_FAILURE_MSG("Bad ligature set offset %d for set %d", offset_ligature_set, i);
|
|
|
++ return Error("Bad ligature set offset %d for set %d", offset_ligature_set, i);
|
|
|
+ }
|
|
|
+ if (!ParseLigatureSetTable(font, data + offset_ligature_set,
|
|
|
+ length - offset_ligature_set, num_glyphs)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to parse ligature set %d", i);
|
|
|
++ return Error("Failed to parse ligature set %d", i);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (offset_coverage < ligature_set_end || offset_coverage >= length) {
|
|
|
+- return OTS_FAILURE_MSG("Bad coverage offset %d", offset_coverage);
|
|
|
++ return Error("Bad coverage offset %d", offset_coverage);
|
|
|
+ }
|
|
|
+ if (!ots::ParseCoverageTable(font, data + offset_coverage,
|
|
|
+ length - offset_coverage, num_glyphs)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to parse coverage table");
|
|
|
++ return Error("Failed to parse coverage table");
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Lookup Type 5:
|
|
|
+ // Contextual Substitution Subtable
|
|
|
+-bool ParseContextSubstitution(const ots::Font *font,
|
|
|
+- const uint8_t *data, const size_t length) {
|
|
|
+- ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
|
|
|
+- font->GetTypedTable(OTS_TAG_MAXP));
|
|
|
+- if (!maxp) {
|
|
|
+- return OTS_FAILURE_MSG("Required maxp table missing");
|
|
|
+- }
|
|
|
+- ots::OpenTypeGSUB *gsub = static_cast<ots::OpenTypeGSUB*>(
|
|
|
+- font->GetTypedTable(OTS_TAG_GSUB));
|
|
|
+- if (!gsub) {
|
|
|
+- return OTS_FAILURE_MSG("Internal error!");
|
|
|
+- }
|
|
|
+- return ots::ParseContextSubtable(font, data, length, maxp->num_glyphs,
|
|
|
+- gsub->num_lookups);
|
|
|
+-}
|
|
|
++// OpenTypeLayoutTable::ParseContextSubtable()
|
|
|
+
|
|
|
+ // Lookup Type 6:
|
|
|
+ // Chaining Contextual Substitution Subtable
|
|
|
+-bool ParseChainingContextSubstitution(const ots::Font *font,
|
|
|
+- const uint8_t *data,
|
|
|
+- const size_t length) {
|
|
|
+- ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
|
|
|
+- font->GetTypedTable(OTS_TAG_MAXP));
|
|
|
+- if (!maxp) {
|
|
|
+- return OTS_FAILURE_MSG("Required maxp table missing");
|
|
|
+- }
|
|
|
+- ots::OpenTypeGSUB *gsub = static_cast<ots::OpenTypeGSUB*>(
|
|
|
+- font->GetTypedTable(OTS_TAG_GSUB));
|
|
|
+- if (!gsub) {
|
|
|
+- return OTS_FAILURE_MSG("Internal error!");
|
|
|
+- }
|
|
|
+- return ots::ParseChainingContextSubtable(font, data, length,
|
|
|
+- maxp->num_glyphs,
|
|
|
+- gsub->num_lookups);
|
|
|
+-}
|
|
|
++// OpenTypeLayoutTable::ParseChainingContextSubtable
|
|
|
+
|
|
|
+ // Lookup Type 7:
|
|
|
+ // Extension Substition
|
|
|
+-bool ParseExtensionSubstitution(const ots::Font *font,
|
|
|
+- const uint8_t *data, const size_t length) {
|
|
|
+- return ots::ParseExtensionSubtable(font, data, length,
|
|
|
+- &kGsubLookupSubtableParser);
|
|
|
+-}
|
|
|
++// OpenTypeLayoutTable::ParseExtensionSubtable
|
|
|
+
|
|
|
+ // Lookup Type 8:
|
|
|
+ // Reverse Chaining Contexual Single Substitution Subtable
|
|
|
+-bool ParseReverseChainingContextSingleSubstitution(
|
|
|
+- const ots::Font *font, const uint8_t *data, const size_t length) {
|
|
|
+- ots::Buffer subtable(data, length);
|
|
|
++bool OpenTypeGSUB::ParseReverseChainingContextSingleSubstitution(const uint8_t *data,
|
|
|
++ const size_t length) {
|
|
|
++ Font* font = GetFont();
|
|
|
++ Buffer subtable(data, length);
|
|
|
+
|
|
|
+ uint16_t format = 0;
|
|
|
+ uint16_t offset_coverage = 0;
|
|
|
+
|
|
|
+ if (!subtable.ReadU16(&format) ||
|
|
|
+ !subtable.ReadU16(&offset_coverage)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to read reverse chaining header");
|
|
|
++ return Error("Failed to read reverse chaining header");
|
|
|
+ }
|
|
|
+
|
|
|
+- ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
|
|
|
++ OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
|
|
|
+ font->GetTypedTable(OTS_TAG_MAXP));
|
|
|
+ if (!maxp) {
|
|
|
+- return OTS_FAILURE_MSG("Required maxp table missing");
|
|
|
++ return Error("Required maxp table missing");
|
|
|
+ }
|
|
|
+ const uint16_t num_glyphs = maxp->num_glyphs;
|
|
|
+
|
|
|
+ uint16_t backtrack_glyph_count = 0;
|
|
|
+ if (!subtable.ReadU16(&backtrack_glyph_count)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to read backtrack glyph count in reverse chaining table");
|
|
|
++ return Error("Failed to read backtrack glyph count in reverse chaining table");
|
|
|
+ }
|
|
|
+ std::vector<uint16_t> offsets_backtrack;
|
|
|
+ offsets_backtrack.reserve(backtrack_glyph_count);
|
|
|
+ for (unsigned i = 0; i < backtrack_glyph_count; ++i) {
|
|
|
+ uint16_t offset = 0;
|
|
|
+ if (!subtable.ReadU16(&offset)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to read backtrack offset %d", i);
|
|
|
++ return Error("Failed to read backtrack offset %d", i);
|
|
|
+ }
|
|
|
+ offsets_backtrack.push_back(offset);
|
|
|
+ }
|
|
|
+
|
|
|
+ uint16_t lookahead_glyph_count = 0;
|
|
|
+ if (!subtable.ReadU16(&lookahead_glyph_count)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to read look ahead glyph count");
|
|
|
++ return Error("Failed to read look ahead glyph count");
|
|
|
+ }
|
|
|
+ std::vector<uint16_t> offsets_lookahead;
|
|
|
+ offsets_lookahead.reserve(lookahead_glyph_count);
|
|
|
+ for (unsigned i = 0; i < lookahead_glyph_count; ++i) {
|
|
|
+ uint16_t offset = 0;
|
|
|
+ if (!subtable.ReadU16(&offset)) {
|
|
|
+- return OTS_FAILURE_MSG("Can't read look ahead offset %d", i);
|
|
|
++ return Error("Can't read look ahead offset %d", i);
|
|
|
+ }
|
|
|
+ offsets_lookahead.push_back(offset);
|
|
|
+ }
|
|
|
+
|
|
|
+ uint16_t glyph_count = 0;
|
|
|
+ if (!subtable.ReadU16(&glyph_count)) {
|
|
|
+- return OTS_FAILURE_MSG("Can't read glyph count in reverse chaining table");
|
|
|
++ return Error("Can't read glyph count in reverse chaining table");
|
|
|
+ }
|
|
|
+ for (unsigned i = 0; i < glyph_count; ++i) {
|
|
|
+ uint16_t substitute = 0;
|
|
|
+ if (!subtable.ReadU16(&substitute)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to read substitution %d reverse chaining table", i);
|
|
|
++ return Error("Failed to read substitution %d reverse chaining table", i);
|
|
|
+ }
|
|
|
+ if (substitute >= num_glyphs) {
|
|
|
+- return OTS_FAILURE_MSG("Bad substitute glyph %d in reverse chaining table substitution %d", substitute, i);
|
|
|
++ return Error("Bad substitute glyph %d in reverse chaining table substitution %d", substitute, i);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const unsigned substitute_end = static_cast<unsigned>(10) +
|
|
|
+ (backtrack_glyph_count + lookahead_glyph_count + glyph_count) * 2;
|
|
|
+ if (substitute_end > std::numeric_limits<uint16_t>::max()) {
|
|
|
+- return OTS_FAILURE_MSG("Bad substitute end offset in reverse chaining table");
|
|
|
++ return Error("Bad substitute end offset in reverse chaining table");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (offset_coverage < substitute_end || offset_coverage >= length) {
|
|
|
+- return OTS_FAILURE_MSG("Bad coverage offset %d in reverse chaining table", offset_coverage);
|
|
|
++ return Error("Bad coverage offset %d in reverse chaining table", offset_coverage);
|
|
|
+ }
|
|
|
+ if (!ots::ParseCoverageTable(font, data + offset_coverage,
|
|
|
+ length - offset_coverage, num_glyphs)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to parse coverage table in reverse chaining table");
|
|
|
++ return Error("Failed to parse coverage table in reverse chaining table");
|
|
|
+ }
|
|
|
+
|
|
|
+ for (unsigned i = 0; i < backtrack_glyph_count; ++i) {
|
|
|
+ if (offsets_backtrack[i] < substitute_end ||
|
|
|
+ offsets_backtrack[i] >= length) {
|
|
|
+- return OTS_FAILURE_MSG("Bad backtrack offset %d for backtrack %d in reverse chaining table", offsets_backtrack[i], i);
|
|
|
++ return Error("Bad backtrack offset %d for backtrack %d in reverse chaining table", offsets_backtrack[i], i);
|
|
|
+ }
|
|
|
+ if (!ots::ParseCoverageTable(font, data + offsets_backtrack[i],
|
|
|
+ length - offsets_backtrack[i], num_glyphs)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to parse coverage table for backtrack %d in reverse chaining table", i);
|
|
|
++ return Error("Failed to parse coverage table for backtrack %d in reverse chaining table", i);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ for (unsigned i = 0; i < lookahead_glyph_count; ++i) {
|
|
|
+ if (offsets_lookahead[i] < substitute_end ||
|
|
|
+ offsets_lookahead[i] >= length) {
|
|
|
+- return OTS_FAILURE_MSG("Bad lookahead offset %d for lookahead %d in reverse chaining table", offsets_lookahead[i], i);
|
|
|
++ return Error("Bad lookahead offset %d for lookahead %d in reverse chaining table", offsets_lookahead[i], i);
|
|
|
+ }
|
|
|
+ if (!ots::ParseCoverageTable(font, data + offsets_lookahead[i],
|
|
|
+ length - offsets_lookahead[i], num_glyphs)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to parse lookahead coverage table %d in reverse chaining table", i);
|
|
|
++ return Error("Failed to parse lookahead coverage table %d in reverse chaining table", i);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+-} // namespace
|
|
|
+-
|
|
|
+-namespace ots {
|
|
|
+-
|
|
|
+-bool OpenTypeGSUB::Parse(const uint8_t *data, size_t length) {
|
|
|
+- // Parsing gsub table requires |maxp->num_glyphs|
|
|
|
+- Font *font = GetFont();
|
|
|
+- Buffer table(data, length);
|
|
|
+-
|
|
|
+- uint16_t version_major = 0, version_minor = 0;
|
|
|
+- uint16_t offset_script_list = 0;
|
|
|
+- uint16_t offset_feature_list = 0;
|
|
|
+- uint16_t offset_lookup_list = 0;
|
|
|
+- uint32_t offset_feature_variations = 0;
|
|
|
+- if (!table.ReadU16(&version_major) ||
|
|
|
+- !table.ReadU16(&version_minor) ||
|
|
|
+- !table.ReadU16(&offset_script_list) ||
|
|
|
+- !table.ReadU16(&offset_feature_list) ||
|
|
|
+- !table.ReadU16(&offset_lookup_list)) {
|
|
|
+- return Error("Incomplete table");
|
|
|
+- }
|
|
|
+-
|
|
|
+- if (version_major != 1 || version_minor > 1) {
|
|
|
+- return Error("Bad version");
|
|
|
+- }
|
|
|
+-
|
|
|
+- if (version_minor > 0) {
|
|
|
+- if (!table.ReadU32(&offset_feature_variations)) {
|
|
|
+- return Error("Incomplete table");
|
|
|
+- }
|
|
|
+- }
|
|
|
+-
|
|
|
+- const size_t header_size =
|
|
|
+- (version_minor == 0) ? kGsubHeaderSize_1_0 : kGsubHeaderSize_1_1;
|
|
|
+-
|
|
|
+- if (offset_lookup_list) {
|
|
|
+- if (offset_lookup_list < header_size || offset_lookup_list >= length) {
|
|
|
+- return Error("Bad lookup list offset in table header");
|
|
|
+- }
|
|
|
+-
|
|
|
+- if (!ParseLookupListTable(font, data + offset_lookup_list,
|
|
|
+- length - offset_lookup_list,
|
|
|
+- &kGsubLookupSubtableParser,
|
|
|
+- &this->num_lookups)) {
|
|
|
+- return Error("Failed to parse lookup list table");
|
|
|
+- }
|
|
|
+- }
|
|
|
+-
|
|
|
+- uint16_t num_features = 0;
|
|
|
+- if (offset_feature_list) {
|
|
|
+- if (offset_feature_list < header_size || offset_feature_list >= length) {
|
|
|
+- return Error("Bad feature list offset in table header");
|
|
|
+- }
|
|
|
+-
|
|
|
+- if (!ParseFeatureListTable(font, data + offset_feature_list,
|
|
|
+- length - offset_feature_list, this->num_lookups,
|
|
|
+- &num_features)) {
|
|
|
+- return Error("Failed to parse feature list table");
|
|
|
+- }
|
|
|
+- }
|
|
|
+-
|
|
|
+- if (offset_script_list) {
|
|
|
+- if (offset_script_list < header_size || offset_script_list >= length) {
|
|
|
+- return Error("Bad script list offset in table header");
|
|
|
+- }
|
|
|
+-
|
|
|
+- if (!ParseScriptListTable(font, data + offset_script_list,
|
|
|
+- length - offset_script_list, num_features)) {
|
|
|
+- return Error("Failed to parse script list table");
|
|
|
+- }
|
|
|
+- }
|
|
|
+-
|
|
|
+- if (offset_feature_variations) {
|
|
|
+- if (offset_feature_variations < header_size || offset_feature_variations >= length) {
|
|
|
+- return Error("Bad feature variations offset in table header");
|
|
|
+- }
|
|
|
+-
|
|
|
+- if (!ParseFeatureVariationsTable(font, data + offset_feature_variations,
|
|
|
+- length - offset_feature_variations,
|
|
|
+- this->num_lookups)) {
|
|
|
+- return Error("Failed to parse feature variations table");
|
|
|
+- }
|
|
|
+- }
|
|
|
+-
|
|
|
+- this->m_data = data;
|
|
|
+- this->m_length = length;
|
|
|
+- return true;
|
|
|
++bool OpenTypeGSUB::ValidLookupSubtableType(const uint16_t lookup_type,
|
|
|
++ bool extension) const {
|
|
|
++ if (extension && lookup_type == GSUB_TYPE_EXTENSION_SUBSTITUTION)
|
|
|
++ return false;
|
|
|
++ return lookup_type >= GSUB_TYPE_SINGLE && lookup_type < GSUB_TYPE_RESERVED;
|
|
|
+ }
|
|
|
+
|
|
|
+-bool OpenTypeGSUB::Serialize(OTSStream *out) {
|
|
|
+- if (!out->Write(this->m_data, this->m_length)) {
|
|
|
+- return Error("Failed to write GSUB table");
|
|
|
++bool OpenTypeGSUB::ParseLookupSubtable(const uint8_t *data, const size_t length,
|
|
|
++ const uint16_t lookup_type) {
|
|
|
++ switch (lookup_type) {
|
|
|
++ case GSUB_TYPE_SINGLE:
|
|
|
++ return ParseSingleSubstitution(data, length);
|
|
|
++ case GSUB_TYPE_MULTIPLE:
|
|
|
++ return ParseMutipleSubstitution(data, length);
|
|
|
++ case GSUB_TYPE_ALTERNATE:
|
|
|
++ return ParseAlternateSubstitution(data, length);
|
|
|
++ case GSUB_TYPE_LIGATURE:
|
|
|
++ return ParseLigatureSubstitution(data, length);
|
|
|
++ case GSUB_TYPE_CONTEXT:
|
|
|
++ return ParseContextSubtable(data, length);
|
|
|
++ case GSUB_TYPE_CHANGING_CONTEXT:
|
|
|
++ return ParseChainingContextSubtable(data, length);
|
|
|
++ case GSUB_TYPE_EXTENSION_SUBSTITUTION:
|
|
|
++ return ParseExtensionSubtable(data, length);
|
|
|
++ case GSUB_TYPE_REVERSE_CHAINING_CONTEXT_SINGLE:
|
|
|
++ return ParseReverseChainingContextSingleSubstitution(data, length);
|
|
|
+ }
|
|
|
+-
|
|
|
+- return true;
|
|
|
++ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ } // namespace ots
|
|
|
+
|
|
|
+ #undef TABLE_NAME
|
|
|
+diff --git a/gfx/ots/src/gsub.h b/gfx/ots/src/gsub.h
|
|
|
+--- a/gfx/ots/src/gsub.h
|
|
|
++++ b/gfx/ots/src/gsub.h
|
|
|
+@@ -1,35 +1,34 @@
|
|
|
+ // Copyright (c) 2011-2017 The OTS Authors. All rights reserved.
|
|
|
+ // Use of this source code is governed by a BSD-style license that can be
|
|
|
+ // found in the LICENSE file.
|
|
|
+
|
|
|
+ #ifndef OTS_GSUB_H_
|
|
|
+ #define OTS_GSUB_H_
|
|
|
+
|
|
|
+ #include "ots.h"
|
|
|
++#include "layout.h"
|
|
|
+
|
|
|
+ namespace ots {
|
|
|
+
|
|
|
+-class OpenTypeGSUB : public Table {
|
|
|
++class OpenTypeGSUB : public OpenTypeLayoutTable {
|
|
|
+ public:
|
|
|
+ explicit OpenTypeGSUB(Font *font, uint32_t tag)
|
|
|
+- : Table(font, tag, tag),
|
|
|
+- num_lookups(0),
|
|
|
+- m_data(NULL),
|
|
|
+- m_length(0) {
|
|
|
+- }
|
|
|
++ : OpenTypeLayoutTable(font, tag, tag) { }
|
|
|
+
|
|
|
+- bool Parse(const uint8_t *data, size_t length);
|
|
|
+- bool Serialize(OTSStream *out);
|
|
|
++ private:
|
|
|
++ bool ValidLookupSubtableType(uint16_t lookup_type,
|
|
|
++ bool extension = false) const;
|
|
|
++ bool ParseLookupSubtable(const uint8_t *data, const size_t length,
|
|
|
++ const uint16_t lookup_type);
|
|
|
+
|
|
|
+- // Number of lookups in GPSUB table
|
|
|
+- uint16_t num_lookups;
|
|
|
+-
|
|
|
+- //private:
|
|
|
+- const uint8_t *m_data;
|
|
|
+- size_t m_length;
|
|
|
++ bool ParseSingleSubstitution(const uint8_t *data, const size_t length);
|
|
|
++ bool ParseMutipleSubstitution(const uint8_t *data, const size_t length);
|
|
|
++ bool ParseAlternateSubstitution(const uint8_t *data, const size_t length);
|
|
|
++ bool ParseLigatureSubstitution(const uint8_t *data, const size_t length);
|
|
|
++ bool ParseReverseChainingContextSingleSubstitution(const uint8_t *data, const size_t length);
|
|
|
+ };
|
|
|
+
|
|
|
+ } // namespace ots
|
|
|
+
|
|
|
+ #endif // OTS_GSUB_H_
|
|
|
+
|
|
|
+diff --git a/gfx/ots/src/layout.cc b/gfx/ots/src/layout.cc
|
|
|
+--- a/gfx/ots/src/layout.cc
|
|
|
++++ b/gfx/ots/src/layout.cc
|
|
|
+@@ -4,16 +4,17 @@
|
|
|
+
|
|
|
+ #include "layout.h"
|
|
|
+
|
|
|
+ #include <limits>
|
|
|
+ #include <vector>
|
|
|
+
|
|
|
+ #include "fvar.h"
|
|
|
+ #include "gdef.h"
|
|
|
++#include "maxp.h"
|
|
|
+
|
|
|
+ // OpenType Layout Common Table Formats
|
|
|
+ // http://www.microsoft.com/typography/otspec/chapter2.htm
|
|
|
+
|
|
|
+ #define TABLE_NAME "Layout" // XXX: use individual table names
|
|
|
+
|
|
|
+ namespace {
|
|
|
+
|
|
|
+@@ -169,85 +170,16 @@ bool ParseFeatureTable(const ots::Font *
|
|
|
+ // lookup index starts with 0.
|
|
|
+ if (lookup_index >= num_lookups) {
|
|
|
+ return OTS_FAILURE_MSG("Bad lookup index %d for lookup %d", lookup_index, i);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+-bool ParseLookupTable(ots::Font *font, const uint8_t *data,
|
|
|
+- const size_t length,
|
|
|
+- const ots::LookupSubtableParser* parser) {
|
|
|
+- ots::Buffer subtable(data, length);
|
|
|
+-
|
|
|
+- uint16_t lookup_type = 0;
|
|
|
+- uint16_t lookup_flag = 0;
|
|
|
+- uint16_t subtable_count = 0;
|
|
|
+- if (!subtable.ReadU16(&lookup_type) ||
|
|
|
+- !subtable.ReadU16(&lookup_flag) ||
|
|
|
+- !subtable.ReadU16(&subtable_count)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to read lookup table header");
|
|
|
+- }
|
|
|
+-
|
|
|
+- if (lookup_type == 0 || lookup_type > parser->num_types) {
|
|
|
+- return OTS_FAILURE_MSG("Bad lookup type %d", lookup_type);
|
|
|
+- }
|
|
|
+-
|
|
|
+- bool use_mark_filtering_set = lookup_flag & kUseMarkFilteringSetBit;
|
|
|
+-
|
|
|
+- std::vector<uint16_t> subtables;
|
|
|
+- subtables.reserve(subtable_count);
|
|
|
+- // If the |kUseMarkFilteringSetBit| of |lookup_flag| is set,
|
|
|
+- // extra 2 bytes will follow after subtable offset array.
|
|
|
+- const unsigned lookup_table_end = 2 * static_cast<unsigned>(subtable_count) +
|
|
|
+- (use_mark_filtering_set ? 8 : 6);
|
|
|
+- if (lookup_table_end > std::numeric_limits<uint16_t>::max()) {
|
|
|
+- return OTS_FAILURE_MSG("Bad end of lookup %d", lookup_table_end);
|
|
|
+- }
|
|
|
+- for (unsigned i = 0; i < subtable_count; ++i) {
|
|
|
+- uint16_t offset_subtable = 0;
|
|
|
+- if (!subtable.ReadU16(&offset_subtable)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to read subtable offset %d", i);
|
|
|
+- }
|
|
|
+- if (offset_subtable < lookup_table_end ||
|
|
|
+- offset_subtable >= length) {
|
|
|
+- return OTS_FAILURE_MSG("Bad subtable offset %d for subtable %d", offset_subtable, i);
|
|
|
+- }
|
|
|
+- subtables.push_back(offset_subtable);
|
|
|
+- }
|
|
|
+- if (subtables.size() != subtable_count) {
|
|
|
+- return OTS_FAILURE_MSG("Bad subtable size %ld", subtables.size());
|
|
|
+- }
|
|
|
+-
|
|
|
+- if (use_mark_filtering_set) {
|
|
|
+- uint16_t mark_filtering_set = 0;
|
|
|
+- if (!subtable.ReadU16(&mark_filtering_set)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to read mark filtering set");
|
|
|
+- }
|
|
|
+-
|
|
|
+- ots::OpenTypeGDEF *gdef = static_cast<ots::OpenTypeGDEF*>(
|
|
|
+- font->GetTypedTable(OTS_TAG_GDEF));
|
|
|
+-
|
|
|
+- if (gdef && (gdef->num_mark_glyph_sets == 0 ||
|
|
|
+- mark_filtering_set >= gdef->num_mark_glyph_sets)) {
|
|
|
+- return OTS_FAILURE_MSG("Bad mark filtering set %d", mark_filtering_set);
|
|
|
+- }
|
|
|
+- }
|
|
|
+-
|
|
|
+- // Parse lookup subtables for this lookup type.
|
|
|
+- for (unsigned i = 0; i < subtable_count; ++i) {
|
|
|
+- if (!parser->Parse(font, data + subtables[i], length - subtables[i],
|
|
|
+- lookup_type)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to parse subtable %d", i);
|
|
|
+- }
|
|
|
+- }
|
|
|
+- return true;
|
|
|
+-}
|
|
|
+-
|
|
|
+ bool ParseClassDefFormat1(const ots::Font *font,
|
|
|
+ const uint8_t *data, size_t length,
|
|
|
+ const uint16_t num_glyphs,
|
|
|
+ const uint16_t num_classes) {
|
|
|
+ ots::Buffer subtable(data, length);
|
|
|
+
|
|
|
+ // Skip format field.
|
|
|
+ if (!subtable.Skip(2)) {
|
|
|
+@@ -441,17 +373,17 @@ bool ParseRuleSubtable(const ots::Font *
|
|
|
+
|
|
|
+ uint16_t glyph_count = 0;
|
|
|
+ uint16_t lookup_count = 0;
|
|
|
+ if (!subtable.ReadU16(&glyph_count) ||
|
|
|
+ !subtable.ReadU16(&lookup_count)) {
|
|
|
+ return OTS_FAILURE_MSG("Failed to read rule subtable header");
|
|
|
+ }
|
|
|
+
|
|
|
+- if (glyph_count == 0 || glyph_count >= num_glyphs) {
|
|
|
++ if (glyph_count == 0) {
|
|
|
+ return OTS_FAILURE_MSG("Bad glyph count %d in rule subtable", glyph_count);
|
|
|
+ }
|
|
|
+ for (unsigned i = 0; i < glyph_count - static_cast<unsigned>(1); ++i) {
|
|
|
+ uint16_t glyph_id = 0;
|
|
|
+ if (!subtable.ReadU16(&glyph_id)) {
|
|
|
+ return OTS_FAILURE_MSG("Failed to read glyph %d", i);
|
|
|
+ }
|
|
|
+ if (glyph_id > num_glyphs) {
|
|
|
+@@ -720,53 +652,47 @@ bool ParseChainRuleSubtable(const ots::F
|
|
|
+ const uint16_t num_glyphs,
|
|
|
+ const uint16_t num_lookups) {
|
|
|
+ ots::Buffer subtable(data, length);
|
|
|
+
|
|
|
+ uint16_t backtrack_count = 0;
|
|
|
+ if (!subtable.ReadU16(&backtrack_count)) {
|
|
|
+ return OTS_FAILURE_MSG("Failed to read backtrack count in chain rule subtable");
|
|
|
+ }
|
|
|
+- if (backtrack_count >= num_glyphs) {
|
|
|
+- return OTS_FAILURE_MSG("Bad backtrack count %d in chain rule subtable", backtrack_count);
|
|
|
+- }
|
|
|
+ for (unsigned i = 0; i < backtrack_count; ++i) {
|
|
|
+ uint16_t glyph_id = 0;
|
|
|
+ if (!subtable.ReadU16(&glyph_id)) {
|
|
|
+ return OTS_FAILURE_MSG("Failed to read backtrack glyph %d in chain rule subtable", i);
|
|
|
+ }
|
|
|
+ if (glyph_id > num_glyphs) {
|
|
|
+ return OTS_FAILURE_MSG("Bad glyph id %d for bactrack glyph %d in chain rule subtable", glyph_id, i);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ uint16_t input_count = 0;
|
|
|
+ if (!subtable.ReadU16(&input_count)) {
|
|
|
+ return OTS_FAILURE_MSG("Failed to read input count in chain rule subtable");
|
|
|
+ }
|
|
|
+- if (input_count == 0 || input_count >= num_glyphs) {
|
|
|
++ if (input_count == 0) {
|
|
|
+ return OTS_FAILURE_MSG("Bad input count %d in chain rule subtable", input_count);
|
|
|
+ }
|
|
|
+ for (unsigned i = 0; i < input_count - static_cast<unsigned>(1); ++i) {
|
|
|
+ uint16_t glyph_id = 0;
|
|
|
+ if (!subtable.ReadU16(&glyph_id)) {
|
|
|
+ return OTS_FAILURE_MSG("Failed to read input glyph %d in chain rule subtable", i);
|
|
|
+ }
|
|
|
+ if (glyph_id > num_glyphs) {
|
|
|
+ return OTS_FAILURE_MSG("Bad glyph id %d for input glyph %d in chain rule subtable", glyph_id, i);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ uint16_t lookahead_count = 0;
|
|
|
+ if (!subtable.ReadU16(&lookahead_count)) {
|
|
|
+ return OTS_FAILURE_MSG("Failed to read lookahead count in chain rule subtable");
|
|
|
+ }
|
|
|
+- if (lookahead_count >= num_glyphs) {
|
|
|
+- return OTS_FAILURE_MSG("Bad lookahead count %d in chain rule subtable", lookahead_count);
|
|
|
+- }
|
|
|
+ for (unsigned i = 0; i < lookahead_count; ++i) {
|
|
|
+ uint16_t glyph_id = 0;
|
|
|
+ if (!subtable.ReadU16(&glyph_id)) {
|
|
|
+ return OTS_FAILURE_MSG("Failed to read lookahead glyph %d in chain rule subtable", i);
|
|
|
+ }
|
|
|
+ if (glyph_id > num_glyphs) {
|
|
|
+ return OTS_FAILURE_MSG("Bad glyph id %d for lookadhead glyph %d in chain rule subtable", glyph_id, i);
|
|
|
+ }
|
|
|
+@@ -873,41 +799,35 @@ bool ParseChainClassRuleSubtable(const o
|
|
|
+
|
|
|
+ // In this subtable, we don't check the value of classes for now since
|
|
|
+ // these could take arbitrary values.
|
|
|
+
|
|
|
+ uint16_t backtrack_count = 0;
|
|
|
+ if (!subtable.ReadU16(&backtrack_count)) {
|
|
|
+ return OTS_FAILURE_MSG("Failed to read backtrack count in chain class rule subtable");
|
|
|
+ }
|
|
|
+- if (backtrack_count >= num_glyphs) {
|
|
|
+- return OTS_FAILURE_MSG("Bad backtrack count %d in chain class rule subtable", backtrack_count);
|
|
|
+- }
|
|
|
+ if (!subtable.Skip(2 * backtrack_count)) {
|
|
|
+ return OTS_FAILURE_MSG("Failed to skip backtrack offsets in chain class rule subtable");
|
|
|
+ }
|
|
|
+
|
|
|
+ uint16_t input_count = 0;
|
|
|
+ if (!subtable.ReadU16(&input_count)) {
|
|
|
+ return OTS_FAILURE_MSG("Failed to read input count in chain class rule subtable");
|
|
|
+ }
|
|
|
+- if (input_count == 0 || input_count >= num_glyphs) {
|
|
|
++ if (input_count == 0) {
|
|
|
+ return OTS_FAILURE_MSG("Bad input count %d in chain class rule subtable", input_count);
|
|
|
+ }
|
|
|
+ if (!subtable.Skip(2 * (input_count - 1))) {
|
|
|
+ return OTS_FAILURE_MSG("Failed to skip input offsets in chain class rule subtable");
|
|
|
+ }
|
|
|
+
|
|
|
+ uint16_t lookahead_count = 0;
|
|
|
+ if (!subtable.ReadU16(&lookahead_count)) {
|
|
|
+ return OTS_FAILURE_MSG("Failed to read lookahead count in chain class rule subtable");
|
|
|
+ }
|
|
|
+- if (lookahead_count >= num_glyphs) {
|
|
|
+- return OTS_FAILURE_MSG("Bad lookahead count %d in chain class rule subtable", lookahead_count);
|
|
|
+- }
|
|
|
+ if (!subtable.Skip(2 * lookahead_count)) {
|
|
|
+ return OTS_FAILURE_MSG("Failed to skip lookahead offsets in chain class rule subtable");
|
|
|
+ }
|
|
|
+
|
|
|
+ uint16_t lookup_count = 0;
|
|
|
+ if (!subtable.ReadU16(&lookup_count)) {
|
|
|
+ return OTS_FAILURE_MSG("Failed to read lookup count in chain class rule subtable");
|
|
|
+ }
|
|
|
+@@ -1053,19 +973,16 @@ bool ParseChainContextFormat3(const ots:
|
|
|
+
|
|
|
+ uint16_t backtrack_count = 0;
|
|
|
+ // Skip format field.
|
|
|
+ if (!subtable.Skip(2) ||
|
|
|
+ !subtable.ReadU16(&backtrack_count)) {
|
|
|
+ return OTS_FAILURE_MSG("Failed to read backtrack count in chain context format 3");
|
|
|
+ }
|
|
|
+
|
|
|
+- if (backtrack_count >= num_glyphs) {
|
|
|
+- return OTS_FAILURE_MSG("Bad backtrack count %d in chain context format 3", backtrack_count);
|
|
|
+- }
|
|
|
+ std::vector<uint16_t> offsets_backtrack;
|
|
|
+ offsets_backtrack.reserve(backtrack_count);
|
|
|
+ for (unsigned i = 0; i < backtrack_count; ++i) {
|
|
|
+ uint16_t offset = 0;
|
|
|
+ if (!subtable.ReadU16(&offset)) {
|
|
|
+ return OTS_FAILURE_MSG("Failed to read backtrack offset %d in chain context format 3", i);
|
|
|
+ }
|
|
|
+ offsets_backtrack.push_back(offset);
|
|
|
+@@ -1073,19 +990,16 @@ bool ParseChainContextFormat3(const ots:
|
|
|
+ if (offsets_backtrack.size() != backtrack_count) {
|
|
|
+ return OTS_FAILURE_MSG("Bad backtrack offsets size %ld in chain context format 3", offsets_backtrack.size());
|
|
|
+ }
|
|
|
+
|
|
|
+ uint16_t input_count = 0;
|
|
|
+ if (!subtable.ReadU16(&input_count)) {
|
|
|
+ return OTS_FAILURE_MSG("Failed to read input count in chain context format 3");
|
|
|
+ }
|
|
|
+- if (input_count >= num_glyphs) {
|
|
|
+- return OTS_FAILURE_MSG("Bad input count %d in chain context format 3", input_count);
|
|
|
+- }
|
|
|
+ std::vector<uint16_t> offsets_input;
|
|
|
+ offsets_input.reserve(input_count);
|
|
|
+ for (unsigned i = 0; i < input_count; ++i) {
|
|
|
+ uint16_t offset = 0;
|
|
|
+ if (!subtable.ReadU16(&offset)) {
|
|
|
+ return OTS_FAILURE_MSG("Failed to read input offset %d in chain context format 3", i);
|
|
|
+ }
|
|
|
+ offsets_input.push_back(offset);
|
|
|
+@@ -1093,19 +1007,16 @@ bool ParseChainContextFormat3(const ots:
|
|
|
+ if (offsets_input.size() != input_count) {
|
|
|
+ return OTS_FAILURE_MSG("Bad input offsets size %ld in chain context format 3", offsets_input.size());
|
|
|
+ }
|
|
|
+
|
|
|
+ uint16_t lookahead_count = 0;
|
|
|
+ if (!subtable.ReadU16(&lookahead_count)) {
|
|
|
+ return OTS_FAILURE_MSG("Failed ot read lookahead count in chain context format 3");
|
|
|
+ }
|
|
|
+- if (lookahead_count >= num_glyphs) {
|
|
|
+- return OTS_FAILURE_MSG("Bad lookahead count %d in chain context format 3", lookahead_count);
|
|
|
+- }
|
|
|
+ std::vector<uint16_t> offsets_lookahead;
|
|
|
+ offsets_lookahead.reserve(lookahead_count);
|
|
|
+ for (unsigned i = 0; i < lookahead_count; ++i) {
|
|
|
+ uint16_t offset = 0;
|
|
|
+ if (!subtable.ReadU16(&offset)) {
|
|
|
+ return OTS_FAILURE_MSG("Failed to read lookahead offset %d in chain context format 3", i);
|
|
|
+ }
|
|
|
+ offsets_lookahead.push_back(offset);
|
|
|
+@@ -1160,177 +1071,332 @@ bool ParseChainContextFormat3(const ots:
|
|
|
+ length - offsets_lookahead[i], num_glyphs)) {
|
|
|
+ return OTS_FAILURE_MSG("Failed to parse lookahead coverage table %d in chain context format 3", i);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
++bool ParseFeatureTableSubstitutionTable(const ots::Font *font,
|
|
|
++ const uint8_t *data, const size_t length,
|
|
|
++ const uint16_t num_lookups) {
|
|
|
++ ots::Buffer subtable(data, length);
|
|
|
++
|
|
|
++ uint16_t version_major = 0;
|
|
|
++ uint16_t version_minor = 0;
|
|
|
++ uint16_t substitution_count = 0;
|
|
|
++ const size_t kFeatureTableSubstitutionHeaderSize = 3 * sizeof(uint16_t);
|
|
|
++
|
|
|
++ if (!subtable.ReadU16(&version_major) ||
|
|
|
++ !subtable.ReadU16(&version_minor) ||
|
|
|
++ !subtable.ReadU16(&substitution_count)) {
|
|
|
++ return OTS_FAILURE_MSG("Failed to read feature table substitution table header");
|
|
|
++ }
|
|
|
++
|
|
|
++ for (uint16_t i = 0; i < substitution_count; i++) {
|
|
|
++ uint16_t feature_index = 0;
|
|
|
++ uint32_t alternate_feature_table_offset = 0;
|
|
|
++ const size_t kFeatureTableSubstitutionRecordSize = sizeof(uint16_t) + sizeof(uint32_t);
|
|
|
++
|
|
|
++ if (!subtable.ReadU16(&feature_index) ||
|
|
|
++ !subtable.ReadU32(&alternate_feature_table_offset)) {
|
|
|
++ return OTS_FAILURE_MSG("Failed to read feature table substitution record");
|
|
|
++ }
|
|
|
++
|
|
|
++ if (alternate_feature_table_offset < kFeatureTableSubstitutionHeaderSize +
|
|
|
++ kFeatureTableSubstitutionRecordSize * substitution_count ||
|
|
|
++ alternate_feature_table_offset >= length) {
|
|
|
++ return OTS_FAILURE_MSG("Invalid alternate feature table offset");
|
|
|
++ }
|
|
|
++
|
|
|
++ if (!ParseFeatureTable(font, data + alternate_feature_table_offset,
|
|
|
++ length - alternate_feature_table_offset, num_lookups)) {
|
|
|
++ return OTS_FAILURE_MSG("Failed to parse alternate feature table");
|
|
|
++ }
|
|
|
++ }
|
|
|
++
|
|
|
++ return true;
|
|
|
++}
|
|
|
++
|
|
|
++bool ParseConditionTable(const ots::Font *font,
|
|
|
++ const uint8_t *data, const size_t length,
|
|
|
++ const uint16_t axis_count) {
|
|
|
++ ots::Buffer subtable(data, length);
|
|
|
++
|
|
|
++ uint16_t format = 0;
|
|
|
++ if (!subtable.ReadU16(&format)) {
|
|
|
++ return OTS_FAILURE_MSG("Failed to read condition table format");
|
|
|
++ }
|
|
|
++
|
|
|
++ if (format != 1) {
|
|
|
++ // An unknown format is not an error, but should be ignored per spec.
|
|
|
++ return true;
|
|
|
++ }
|
|
|
++
|
|
|
++ uint16_t axis_index = 0;
|
|
|
++ int16_t filter_range_min_value = 0;
|
|
|
++ int16_t filter_range_max_value = 0;
|
|
|
++ if (!subtable.ReadU16(&axis_index) ||
|
|
|
++ !subtable.ReadS16(&filter_range_min_value) ||
|
|
|
++ !subtable.ReadS16(&filter_range_max_value)) {
|
|
|
++ return OTS_FAILURE_MSG("Failed to read condition table (format 1)");
|
|
|
++ }
|
|
|
++
|
|
|
++ if (axis_index >= axis_count) {
|
|
|
++ return OTS_FAILURE_MSG("Axis index out of range in condition");
|
|
|
++ }
|
|
|
++
|
|
|
++ // Check min/max values are within range -1.0 .. 1.0 and properly ordered
|
|
|
++ if (filter_range_min_value < -0x4000 || // -1.0 in F2DOT14 format
|
|
|
++ filter_range_max_value > 0x4000 || // +1.0 in F2DOT14 format
|
|
|
++ filter_range_min_value > filter_range_max_value) {
|
|
|
++ return OTS_FAILURE_MSG("Invalid filter range in condition");
|
|
|
++ }
|
|
|
++
|
|
|
++ return true;
|
|
|
++}
|
|
|
++
|
|
|
++bool ParseConditionSetTable(const ots::Font *font,
|
|
|
++ const uint8_t *data, const size_t length,
|
|
|
++ const uint16_t axis_count) {
|
|
|
++ ots::Buffer subtable(data, length);
|
|
|
++
|
|
|
++ uint16_t condition_count = 0;
|
|
|
++ if (!subtable.ReadU16(&condition_count)) {
|
|
|
++ return OTS_FAILURE_MSG("Failed to read condition count");
|
|
|
++ }
|
|
|
++
|
|
|
++ for (uint16_t i = 0; i < condition_count; i++) {
|
|
|
++ uint32_t condition_offset = 0;
|
|
|
++ if (!subtable.ReadU32(&condition_offset)) {
|
|
|
++ return OTS_FAILURE_MSG("Failed to read condition offset");
|
|
|
++ }
|
|
|
++ if (condition_offset < subtable.offset() || condition_offset >= length) {
|
|
|
++ return OTS_FAILURE_MSG("Offset out of range");
|
|
|
++ }
|
|
|
++ if (!ParseConditionTable(font, data + condition_offset, length - condition_offset,
|
|
|
++ axis_count)) {
|
|
|
++ return OTS_FAILURE_MSG("Failed to parse condition table");
|
|
|
++ }
|
|
|
++ }
|
|
|
++
|
|
|
++ return true;
|
|
|
++}
|
|
|
++
|
|
|
+ } // namespace
|
|
|
+
|
|
|
+ namespace ots {
|
|
|
+
|
|
|
+-bool LookupSubtableParser::Parse(const Font *font, const uint8_t *data,
|
|
|
+- const size_t length,
|
|
|
+- const uint16_t lookup_type) const {
|
|
|
+- for (unsigned i = 0; i < num_types; ++i) {
|
|
|
+- if (parsers[i].type == lookup_type && parsers[i].parse) {
|
|
|
+- if (!parsers[i].parse(font, data, length)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to parse lookup subtable %d", i);
|
|
|
+- }
|
|
|
+- return true;
|
|
|
+- }
|
|
|
+- }
|
|
|
+- return OTS_FAILURE_MSG("No lookup subtables to parse");
|
|
|
+-}
|
|
|
+-
|
|
|
+ // Parsing ScriptListTable requires number of features so we need to
|
|
|
+ // parse FeatureListTable before calling this function.
|
|
|
+-bool ParseScriptListTable(const ots::Font *font,
|
|
|
+- const uint8_t *data, const size_t length,
|
|
|
+- const uint16_t num_features) {
|
|
|
++bool OpenTypeLayoutTable::ParseScriptListTable(const uint8_t *data, const size_t length) {
|
|
|
++ Font* font = GetFont();
|
|
|
+ Buffer subtable(data, length);
|
|
|
+
|
|
|
+ uint16_t script_count = 0;
|
|
|
+ if (!subtable.ReadU16(&script_count)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to read script count in script list table");
|
|
|
++ return Error("Failed to read script count in script list table");
|
|
|
+ }
|
|
|
+
|
|
|
+ const unsigned script_record_end =
|
|
|
+ 6 * static_cast<unsigned>(script_count) + 2;
|
|
|
+ if (script_record_end > std::numeric_limits<uint16_t>::max()) {
|
|
|
+- return OTS_FAILURE_MSG("Bad end of script record %d in script list table", script_record_end);
|
|
|
++ return Error("Bad end of script record %d in script list table", script_record_end);
|
|
|
+ }
|
|
|
+ std::vector<ScriptRecord> script_list;
|
|
|
+ script_list.reserve(script_count);
|
|
|
+ uint32_t last_tag = 0;
|
|
|
+ for (unsigned i = 0; i < script_count; ++i) {
|
|
|
+ ScriptRecord record;
|
|
|
+ if (!subtable.ReadU32(&record.tag) ||
|
|
|
+ !subtable.ReadU16(&record.offset)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to read script record %d in script list table", i);
|
|
|
++ return Error("Failed to read script record %d in script list table", i);
|
|
|
+ }
|
|
|
+ // Script tags should be arranged alphabetically by tag
|
|
|
+ if (last_tag != 0 && last_tag > record.tag) {
|
|
|
+ // Several fonts don't arrange tags alphabetically.
|
|
|
+ // It seems that the order of tags might not be a security issue
|
|
|
+ // so we just warn it.
|
|
|
+ OTS_WARNING("tags aren't arranged alphabetically.");
|
|
|
+ }
|
|
|
+ last_tag = record.tag;
|
|
|
+ if (record.offset < script_record_end || record.offset >= length) {
|
|
|
+- return OTS_FAILURE_MSG("Bad record offset %d for script %c%c%c%c entry %d in script list table", record.offset, OTS_UNTAG(record.tag), i);
|
|
|
++ return Error("Bad record offset %d for script %c%c%c%c entry %d in script list table", record.offset, OTS_UNTAG(record.tag), i);
|
|
|
+ }
|
|
|
+ script_list.push_back(record);
|
|
|
+ }
|
|
|
+ if (script_list.size() != script_count) {
|
|
|
+- return OTS_FAILURE_MSG("Bad script list size %ld in script list table", script_list.size());
|
|
|
++ return Error("Bad script list size %ld in script list table", script_list.size());
|
|
|
+ }
|
|
|
+
|
|
|
+ // Check script records.
|
|
|
+ for (unsigned i = 0; i < script_count; ++i) {
|
|
|
+ if (!ParseScriptTable(font, data + script_list[i].offset,
|
|
|
+ length - script_list[i].offset,
|
|
|
+- script_list[i].tag, num_features)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to parse script table %d", i);
|
|
|
++ script_list[i].tag, m_num_features)) {
|
|
|
++ return Error("Failed to parse script table %d", i);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Parsing FeatureListTable requires number of lookups so we need to parse
|
|
|
+ // LookupListTable before calling this function.
|
|
|
+-bool ParseFeatureListTable(const ots::Font *font,
|
|
|
+- const uint8_t *data, const size_t length,
|
|
|
+- const uint16_t num_lookups,
|
|
|
+- uint16_t* num_features) {
|
|
|
++bool OpenTypeLayoutTable::ParseFeatureListTable(const uint8_t *data, const size_t length) {
|
|
|
++ Font *font = GetFont();
|
|
|
+ Buffer subtable(data, length);
|
|
|
+
|
|
|
+ uint16_t feature_count = 0;
|
|
|
+ if (!subtable.ReadU16(&feature_count)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to read feature count");
|
|
|
++ return Error("Failed to read feature count");
|
|
|
+ }
|
|
|
+
|
|
|
+ std::vector<FeatureRecord> feature_records;
|
|
|
+ feature_records.resize(feature_count);
|
|
|
+ const unsigned feature_record_end =
|
|
|
+ 6 * static_cast<unsigned>(feature_count) + 2;
|
|
|
+ if (feature_record_end > std::numeric_limits<uint16_t>::max()) {
|
|
|
+- return OTS_FAILURE_MSG("Bad end of feature record %d", feature_record_end);
|
|
|
++ return Error("Bad end of feature record %d", feature_record_end);
|
|
|
+ }
|
|
|
+ uint32_t last_tag = 0;
|
|
|
+ for (unsigned i = 0; i < feature_count; ++i) {
|
|
|
+ if (!subtable.ReadU32(&feature_records[i].tag) ||
|
|
|
+ !subtable.ReadU16(&feature_records[i].offset)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to read feature header %d", i);
|
|
|
++ return Error("Failed to read feature header %d", i);
|
|
|
+ }
|
|
|
+ // Feature record array should be arranged alphabetically by tag
|
|
|
+ if (last_tag != 0 && last_tag > feature_records[i].tag) {
|
|
|
+ // Several fonts don't arrange tags alphabetically.
|
|
|
+ // It seems that the order of tags might not be a security issue
|
|
|
+ // so we just warn it.
|
|
|
+ OTS_WARNING("tags aren't arranged alphabetically.");
|
|
|
+ }
|
|
|
+ last_tag = feature_records[i].tag;
|
|
|
+ if (feature_records[i].offset < feature_record_end ||
|
|
|
+ feature_records[i].offset >= length) {
|
|
|
+- return OTS_FAILURE_MSG("Bad feature offset %d for feature %d %c%c%c%c", feature_records[i].offset, i, OTS_UNTAG(feature_records[i].tag));
|
|
|
++ return Error("Bad feature offset %d for feature %d %c%c%c%c", feature_records[i].offset, i, OTS_UNTAG(feature_records[i].tag));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ for (unsigned i = 0; i < feature_count; ++i) {
|
|
|
+ if (!ParseFeatureTable(font, data + feature_records[i].offset,
|
|
|
+- length - feature_records[i].offset, num_lookups)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to parse feature table %d", i);
|
|
|
++ length - feature_records[i].offset, m_num_lookups)) {
|
|
|
++ return Error("Failed to parse feature table %d", i);
|
|
|
+ }
|
|
|
+ }
|
|
|
+- *num_features = feature_count;
|
|
|
++ m_num_features = feature_count;
|
|
|
++ return true;
|
|
|
++}
|
|
|
++
|
|
|
++bool OpenTypeLayoutTable::ParseLookupTable(const uint8_t *data,
|
|
|
++ const size_t length) {
|
|
|
++ Font* font = GetFont();
|
|
|
++ Buffer subtable(data, length);
|
|
|
++
|
|
|
++ uint16_t lookup_type = 0;
|
|
|
++ uint16_t lookup_flag = 0;
|
|
|
++ uint16_t subtable_count = 0;
|
|
|
++ if (!subtable.ReadU16(&lookup_type) ||
|
|
|
++ !subtable.ReadU16(&lookup_flag) ||
|
|
|
++ !subtable.ReadU16(&subtable_count)) {
|
|
|
++ return Error("Failed to read lookup table header");
|
|
|
++ }
|
|
|
++
|
|
|
++ if (!ValidLookupSubtableType(lookup_type)) {
|
|
|
++ return Error("Bad lookup type %d", lookup_type);
|
|
|
++ }
|
|
|
++
|
|
|
++ bool use_mark_filtering_set = lookup_flag & kUseMarkFilteringSetBit;
|
|
|
++
|
|
|
++ std::vector<uint16_t> subtables;
|
|
|
++ subtables.reserve(subtable_count);
|
|
|
++ // If the |kUseMarkFilteringSetBit| of |lookup_flag| is set,
|
|
|
++ // extra 2 bytes will follow after subtable offset array.
|
|
|
++ const unsigned lookup_table_end = 2 * static_cast<unsigned>(subtable_count) +
|
|
|
++ (use_mark_filtering_set ? 8 : 6);
|
|
|
++ if (lookup_table_end > std::numeric_limits<uint16_t>::max()) {
|
|
|
++ return Error("Bad end of lookup %d", lookup_table_end);
|
|
|
++ }
|
|
|
++ for (unsigned i = 0; i < subtable_count; ++i) {
|
|
|
++ uint16_t offset_subtable = 0;
|
|
|
++ if (!subtable.ReadU16(&offset_subtable)) {
|
|
|
++ return Error("Failed to read subtable offset %d", i);
|
|
|
++ }
|
|
|
++ if (offset_subtable < lookup_table_end ||
|
|
|
++ offset_subtable >= length) {
|
|
|
++ return Error("Bad subtable offset %d for subtable %d", offset_subtable, i);
|
|
|
++ }
|
|
|
++ subtables.push_back(offset_subtable);
|
|
|
++ }
|
|
|
++ if (subtables.size() != subtable_count) {
|
|
|
++ return Error("Bad subtable size %ld", subtables.size());
|
|
|
++ }
|
|
|
++
|
|
|
++ if (use_mark_filtering_set) {
|
|
|
++ uint16_t mark_filtering_set = 0;
|
|
|
++ if (!subtable.ReadU16(&mark_filtering_set)) {
|
|
|
++ return Error("Failed to read mark filtering set");
|
|
|
++ }
|
|
|
++
|
|
|
++ OpenTypeGDEF *gdef = static_cast<OpenTypeGDEF*>(
|
|
|
++ font->GetTypedTable(OTS_TAG_GDEF));
|
|
|
++
|
|
|
++ if (gdef && (gdef->num_mark_glyph_sets == 0 ||
|
|
|
++ mark_filtering_set >= gdef->num_mark_glyph_sets)) {
|
|
|
++ return Error("Bad mark filtering set %d", mark_filtering_set);
|
|
|
++ }
|
|
|
++ }
|
|
|
++
|
|
|
++ // Parse lookup subtables for this lookup type.
|
|
|
++ for (unsigned i = 0; i < subtable_count; ++i) {
|
|
|
++ if (!ParseLookupSubtable(data + subtables[i], length - subtables[i],
|
|
|
++ lookup_type)) {
|
|
|
++ return Error("Failed to parse subtable %d", i);
|
|
|
++ }
|
|
|
++ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // For parsing GPOS/GSUB tables, this function should be called at first to
|
|
|
+ // obtain the number of lookups because parsing FeatureTableList requires
|
|
|
+ // the number.
|
|
|
+-bool ParseLookupListTable(Font *font, const uint8_t *data,
|
|
|
+- const size_t length,
|
|
|
+- const LookupSubtableParser* parser,
|
|
|
+- uint16_t *num_lookups) {
|
|
|
++bool OpenTypeLayoutTable::ParseLookupListTable(const uint8_t *data,
|
|
|
++ const size_t length) {
|
|
|
+ Buffer subtable(data, length);
|
|
|
+
|
|
|
+- if (!subtable.ReadU16(num_lookups)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to read number of lookups");
|
|
|
++ if (!subtable.ReadU16(&m_num_lookups)) {
|
|
|
++ return Error("Failed to read number of lookups");
|
|
|
+ }
|
|
|
+
|
|
|
+ std::vector<uint16_t> lookups;
|
|
|
+- lookups.reserve(*num_lookups);
|
|
|
++ lookups.reserve(m_num_lookups);
|
|
|
+ const unsigned lookup_end =
|
|
|
+- 2 * static_cast<unsigned>(*num_lookups) + 2;
|
|
|
++ 2 * static_cast<unsigned>(m_num_lookups) + 2;
|
|
|
+ if (lookup_end > std::numeric_limits<uint16_t>::max()) {
|
|
|
+- return OTS_FAILURE_MSG("Bad end of lookups %d", lookup_end);
|
|
|
++ return Error("Bad end of lookups %d", lookup_end);
|
|
|
+ }
|
|
|
+- for (unsigned i = 0; i < *num_lookups; ++i) {
|
|
|
++ for (unsigned i = 0; i < m_num_lookups; ++i) {
|
|
|
+ uint16_t offset = 0;
|
|
|
+ if (!subtable.ReadU16(&offset)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to read lookup offset %d", i);
|
|
|
++ return Error("Failed to read lookup offset %d", i);
|
|
|
+ }
|
|
|
+ if (offset < lookup_end || offset >= length) {
|
|
|
+- return OTS_FAILURE_MSG("Bad lookup offset %d for lookup %d", offset, i);
|
|
|
++ return Error("Bad lookup offset %d for lookup %d", offset, i);
|
|
|
+ }
|
|
|
+ lookups.push_back(offset);
|
|
|
+ }
|
|
|
+- if (lookups.size() != *num_lookups) {
|
|
|
+- return OTS_FAILURE_MSG("Bad lookup offsets list size %ld", lookups.size());
|
|
|
++ if (lookups.size() != m_num_lookups) {
|
|
|
++ return Error("Bad lookup offsets list size %ld", lookups.size());
|
|
|
+ }
|
|
|
+
|
|
|
+- for (unsigned i = 0; i < *num_lookups; ++i) {
|
|
|
+- if (!ParseLookupTable(font, data + lookups[i], length - lookups[i],
|
|
|
+- parser)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to parse lookup %d", i);
|
|
|
++ for (unsigned i = 0; i < m_num_lookups; ++i) {
|
|
|
++ if (!ParseLookupTable(data + lookups[i], length - lookups[i])) {
|
|
|
++ return Error("Failed to parse lookup %d", i);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool ParseClassDefTable(const ots::Font *font,
|
|
|
+ const uint8_t *data, size_t length,
|
|
|
+@@ -1401,276 +1467,269 @@ bool ParseDeviceTable(const ots::Font *f
|
|
|
+ // Just skip |num_units| * 2 bytes since the compressed data could take
|
|
|
+ // arbitrary values.
|
|
|
+ if (!subtable.Skip(num_units * 2)) {
|
|
|
+ return OTS_FAILURE_MSG("Failed to skip data in device table");
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+-bool ParseContextSubtable(const ots::Font *font,
|
|
|
+- const uint8_t *data, const size_t length,
|
|
|
+- const uint16_t num_glyphs,
|
|
|
+- const uint16_t num_lookups) {
|
|
|
++bool OpenTypeLayoutTable::ParseContextSubtable(const uint8_t *data,
|
|
|
++ const size_t length) {
|
|
|
++ Font *font = GetFont();
|
|
|
+ Buffer subtable(data, length);
|
|
|
+
|
|
|
+ uint16_t format = 0;
|
|
|
+ if (!subtable.ReadU16(&format)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to read context subtable format");
|
|
|
++ return Error("Failed to read context subtable format");
|
|
|
++ }
|
|
|
++
|
|
|
++ OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
|
|
|
++ font->GetTypedTable(OTS_TAG_MAXP));
|
|
|
++ if (!maxp) {
|
|
|
++ return Error("Required maxp table missing");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (format == 1) {
|
|
|
+- if (!ParseContextFormat1(font, data, length, num_glyphs, num_lookups)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to parse context format 1 subtable");
|
|
|
++ if (!ParseContextFormat1(font, data, length, maxp->num_glyphs, m_num_lookups)) {
|
|
|
++ return Error("Failed to parse context format 1 subtable");
|
|
|
+ }
|
|
|
+ } else if (format == 2) {
|
|
|
+- if (!ParseContextFormat2(font, data, length, num_glyphs, num_lookups)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to parse context format 2 subtable");
|
|
|
++ if (!ParseContextFormat2(font, data, length, maxp->num_glyphs, m_num_lookups)) {
|
|
|
++ return Error("Failed to parse context format 2 subtable");
|
|
|
+ }
|
|
|
+ } else if (format == 3) {
|
|
|
+- if (!ParseContextFormat3(font, data, length, num_glyphs, num_lookups)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to parse context format 3 subtable");
|
|
|
++ if (!ParseContextFormat3(font, data, length, maxp->num_glyphs, m_num_lookups)) {
|
|
|
++ return Error("Failed to parse context format 3 subtable");
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+- return OTS_FAILURE_MSG("Bad context subtable format %d", format);
|
|
|
++ return Error("Bad context subtable format %d", format);
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+-bool ParseChainingContextSubtable(const ots::Font *font,
|
|
|
+- const uint8_t *data, const size_t length,
|
|
|
+- const uint16_t num_glyphs,
|
|
|
+- const uint16_t num_lookups) {
|
|
|
++bool OpenTypeLayoutTable::ParseChainingContextSubtable(const uint8_t *data,
|
|
|
++ const size_t length) {
|
|
|
++ Font *font = GetFont();
|
|
|
+ Buffer subtable(data, length);
|
|
|
+
|
|
|
+ uint16_t format = 0;
|
|
|
+ if (!subtable.ReadU16(&format)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to read chaining context subtable format");
|
|
|
++ return Error("Failed to read chaining context subtable format");
|
|
|
++ }
|
|
|
++
|
|
|
++ OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
|
|
|
++ font->GetTypedTable(OTS_TAG_MAXP));
|
|
|
++ if (!maxp) {
|
|
|
++ return Error("Required maxp table missing");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (format == 1) {
|
|
|
+- if (!ParseChainContextFormat1(font, data, length, num_glyphs, num_lookups)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to parse chaining context format 1 subtable");
|
|
|
++ if (!ParseChainContextFormat1(font, data, length, maxp->num_glyphs, m_num_lookups)) {
|
|
|
++ return Error("Failed to parse chaining context format 1 subtable");
|
|
|
+ }
|
|
|
+ } else if (format == 2) {
|
|
|
+- if (!ParseChainContextFormat2(font, data, length, num_glyphs, num_lookups)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to parse chaining context format 2 subtable");
|
|
|
++ if (!ParseChainContextFormat2(font, data, length, maxp->num_glyphs, m_num_lookups)) {
|
|
|
++ return Error("Failed to parse chaining context format 2 subtable");
|
|
|
+ }
|
|
|
+ } else if (format == 3) {
|
|
|
+- if (!ParseChainContextFormat3(font, data, length, num_glyphs, num_lookups)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to parse chaining context format 3 subtable");
|
|
|
++ if (!ParseChainContextFormat3(font, data, length, maxp->num_glyphs, m_num_lookups)) {
|
|
|
++ return Error("Failed to parse chaining context format 3 subtable");
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+- return OTS_FAILURE_MSG("Bad chaining context subtable format %d", format);
|
|
|
++ return Error("Bad chaining context subtable format %d", format);
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+-bool ParseExtensionSubtable(const Font *font,
|
|
|
+- const uint8_t *data, const size_t length,
|
|
|
+- const LookupSubtableParser* parser) {
|
|
|
++bool OpenTypeLayoutTable::ParseExtensionSubtable(const uint8_t *data,
|
|
|
++ const size_t length) {
|
|
|
+ Buffer subtable(data, length);
|
|
|
+
|
|
|
+ uint16_t format = 0;
|
|
|
+ uint16_t lookup_type = 0;
|
|
|
+ uint32_t offset_extension = 0;
|
|
|
+ if (!subtable.ReadU16(&format) ||
|
|
|
+ !subtable.ReadU16(&lookup_type) ||
|
|
|
+ !subtable.ReadU32(&offset_extension)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to read extension table header");
|
|
|
++ return Error("Failed to read extension table header");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (format != 1) {
|
|
|
+- return OTS_FAILURE_MSG("Bad extension table format %d", format);
|
|
|
++ return Error("Bad extension table format %d", format);
|
|
|
+ }
|
|
|
+ // |lookup_type| should be other than |parser->extension_type|.
|
|
|
+- if (lookup_type < 1 || lookup_type > parser->num_types ||
|
|
|
+- lookup_type == parser->extension_type) {
|
|
|
+- return OTS_FAILURE_MSG("Bad lookup type %d in extension table", lookup_type);
|
|
|
++ if (!ValidLookupSubtableType(lookup_type, true)) {
|
|
|
++ return Error("Bad lookup type %d in extension table", lookup_type);
|
|
|
+ }
|
|
|
+
|
|
|
+ const unsigned format_end = static_cast<unsigned>(8);
|
|
|
+ if (offset_extension < format_end ||
|
|
|
+ offset_extension >= length) {
|
|
|
+- return OTS_FAILURE_MSG("Bad extension offset %d", offset_extension);
|
|
|
++ return Error("Bad extension offset %d", offset_extension);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Parse the extension subtable of |lookup_type|.
|
|
|
+- if (!parser->Parse(font, data + offset_extension, length - offset_extension,
|
|
|
+- lookup_type)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to parse lookup from extension lookup");
|
|
|
+- }
|
|
|
+-
|
|
|
+- return true;
|
|
|
+-}
|
|
|
+-
|
|
|
+-bool ParseConditionTable(const Font *font,
|
|
|
+- const uint8_t *data, const size_t length,
|
|
|
+- const uint16_t axis_count) {
|
|
|
+- Buffer subtable(data, length);
|
|
|
+-
|
|
|
+- uint16_t format = 0;
|
|
|
+- if (!subtable.ReadU16(&format)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to read condition table format");
|
|
|
+- }
|
|
|
+-
|
|
|
+- if (format != 1) {
|
|
|
+- // An unknown format is not an error, but should be ignored per spec.
|
|
|
+- return true;
|
|
|
+- }
|
|
|
+-
|
|
|
+- uint16_t axis_index = 0;
|
|
|
+- int16_t filter_range_min_value = 0;
|
|
|
+- int16_t filter_range_max_value = 0;
|
|
|
+- if (!subtable.ReadU16(&axis_index) ||
|
|
|
+- !subtable.ReadS16(&filter_range_min_value) ||
|
|
|
+- !subtable.ReadS16(&filter_range_max_value)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to read condition table (format 1)");
|
|
|
+- }
|
|
|
+-
|
|
|
+- if (axis_index >= axis_count) {
|
|
|
+- return OTS_FAILURE_MSG("Axis index out of range in condition");
|
|
|
+- }
|
|
|
+-
|
|
|
+- // Check min/max values are within range -1.0 .. 1.0 and properly ordered
|
|
|
+- if (filter_range_min_value < -0x4000 || // -1.0 in F2DOT14 format
|
|
|
+- filter_range_max_value > 0x4000 || // +1.0 in F2DOT14 format
|
|
|
+- filter_range_min_value > filter_range_max_value) {
|
|
|
+- return OTS_FAILURE_MSG("Invalid filter range in condition");
|
|
|
++ if (!ParseLookupSubtable(data + offset_extension, length - offset_extension,
|
|
|
++ lookup_type)) {
|
|
|
++ return Error("Failed to parse lookup from extension lookup");
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+-bool ParseConditionSetTable(const Font *font,
|
|
|
+- const uint8_t *data, const size_t length,
|
|
|
+- const uint16_t axis_count) {
|
|
|
+- Buffer subtable(data, length);
|
|
|
+-
|
|
|
+- uint16_t condition_count = 0;
|
|
|
+- if (!subtable.ReadU16(&condition_count)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to read condition count");
|
|
|
+- }
|
|
|
+-
|
|
|
+- for (uint16_t i = 0; i < condition_count; i++) {
|
|
|
+- uint32_t condition_offset = 0;
|
|
|
+- if (!subtable.ReadU32(&condition_offset)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to read condition offset");
|
|
|
+- }
|
|
|
+- if (condition_offset < subtable.offset() || condition_offset >= length) {
|
|
|
+- return OTS_FAILURE_MSG("Offset out of range");
|
|
|
+- }
|
|
|
+- if (!ParseConditionTable(font, data + condition_offset, length - condition_offset,
|
|
|
+- axis_count)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to parse condition table");
|
|
|
+- }
|
|
|
+- }
|
|
|
+-
|
|
|
+- return true;
|
|
|
+-}
|
|
|
+-
|
|
|
+-bool ParseFeatureTableSubstitutionTable(const Font *font,
|
|
|
+- const uint8_t *data, const size_t length,
|
|
|
+- const uint16_t num_lookups) {
|
|
|
+- Buffer subtable(data, length);
|
|
|
+-
|
|
|
+- uint16_t version_major = 0;
|
|
|
+- uint16_t version_minor = 0;
|
|
|
+- uint16_t substitution_count = 0;
|
|
|
+- const size_t kFeatureTableSubstitutionHeaderSize = 3 * sizeof(uint16_t);
|
|
|
+-
|
|
|
+- if (!subtable.ReadU16(&version_major) ||
|
|
|
+- !subtable.ReadU16(&version_minor) ||
|
|
|
+- !subtable.ReadU16(&substitution_count)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to read feature table substitution table header");
|
|
|
+- }
|
|
|
+-
|
|
|
+- for (uint16_t i = 0; i < substitution_count; i++) {
|
|
|
+- uint16_t feature_index = 0;
|
|
|
+- uint32_t alternate_feature_table_offset = 0;
|
|
|
+- const size_t kFeatureTableSubstitutionRecordSize = sizeof(uint16_t) + sizeof(uint32_t);
|
|
|
+-
|
|
|
+- if (!subtable.ReadU16(&feature_index) ||
|
|
|
+- !subtable.ReadU32(&alternate_feature_table_offset)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to read feature table substitution record");
|
|
|
+- }
|
|
|
+-
|
|
|
+- if (alternate_feature_table_offset < kFeatureTableSubstitutionHeaderSize +
|
|
|
+- kFeatureTableSubstitutionRecordSize * substitution_count ||
|
|
|
+- alternate_feature_table_offset >= length) {
|
|
|
+- return OTS_FAILURE_MSG("Invalid alternate feature table offset");
|
|
|
+- }
|
|
|
+-
|
|
|
+- if (!ParseFeatureTable(font, data + alternate_feature_table_offset,
|
|
|
+- length - alternate_feature_table_offset, num_lookups)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to parse alternate feature table");
|
|
|
+- }
|
|
|
+- }
|
|
|
+-
|
|
|
+- return true;
|
|
|
+-}
|
|
|
+-
|
|
|
+-bool ParseFeatureVariationsTable(const Font *font,
|
|
|
+- const uint8_t *data, const size_t length,
|
|
|
+- const uint16_t num_lookups) {
|
|
|
++// Parsing feature variations table (in GSUB/GPOS v1.1)
|
|
|
++bool OpenTypeLayoutTable::ParseFeatureVariationsTable(const uint8_t *data, const size_t length) {
|
|
|
++ Font *font = GetFont();
|
|
|
+ Buffer subtable(data, length);
|
|
|
+
|
|
|
+ uint16_t version_major = 0;
|
|
|
+ uint16_t version_minor = 0;
|
|
|
+ uint32_t feature_variation_record_count = 0;
|
|
|
+
|
|
|
+ if (!subtable.ReadU16(&version_major) ||
|
|
|
+ !subtable.ReadU16(&version_minor) ||
|
|
|
+ !subtable.ReadU32(&feature_variation_record_count)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to read feature variations table header");
|
|
|
++ return Error("Failed to read feature variations table header");
|
|
|
+ }
|
|
|
+
|
|
|
+ OpenTypeFVAR* fvar = static_cast<OpenTypeFVAR*>(font->GetTypedTable(OTS_TAG_FVAR));
|
|
|
+ if (!fvar) {
|
|
|
+- return OTS_FAILURE_MSG("Not a variation font");
|
|
|
++ return Error("Not a variation font");
|
|
|
+ }
|
|
|
+ const uint16_t axis_count = fvar->AxisCount();
|
|
|
+
|
|
|
+ const size_t kEndOfFeatureVariationRecords =
|
|
|
+ 2 * sizeof(uint16_t) + sizeof(uint32_t) +
|
|
|
+ feature_variation_record_count * 2 * sizeof(uint32_t);
|
|
|
+
|
|
|
+ for (uint32_t i = 0; i < feature_variation_record_count; i++) {
|
|
|
+ uint32_t condition_set_offset = 0;
|
|
|
+ uint32_t feature_table_substitution_offset = 0;
|
|
|
+ if (!subtable.ReadU32(&condition_set_offset) ||
|
|
|
+ !subtable.ReadU32(&feature_table_substitution_offset)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to read feature variation record");
|
|
|
++ return Error("Failed to read feature variation record");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (condition_set_offset) {
|
|
|
+ if (condition_set_offset < kEndOfFeatureVariationRecords ||
|
|
|
+ condition_set_offset >= length) {
|
|
|
+- return OTS_FAILURE_MSG("Condition set offset out of range");
|
|
|
++ return Error("Condition set offset out of range");
|
|
|
+ }
|
|
|
+ if (!ParseConditionSetTable(font, data + condition_set_offset,
|
|
|
+ length - condition_set_offset,
|
|
|
+ axis_count)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to parse condition set table");
|
|
|
++ return Error("Failed to parse condition set table");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (feature_table_substitution_offset) {
|
|
|
+ if (feature_table_substitution_offset < kEndOfFeatureVariationRecords ||
|
|
|
+ feature_table_substitution_offset >= length) {
|
|
|
+- return OTS_FAILURE_MSG("Feature table substitution offset out of range");
|
|
|
++ return Error("Feature table substitution offset out of range");
|
|
|
+ }
|
|
|
+ if (!ParseFeatureTableSubstitutionTable(font, data + feature_table_substitution_offset,
|
|
|
+ length - feature_table_substitution_offset,
|
|
|
+- num_lookups)) {
|
|
|
+- return OTS_FAILURE_MSG("Failed to parse feature table substitution table");
|
|
|
++ m_num_lookups)) {
|
|
|
++ return Error("Failed to parse feature table substitution table");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
++// GSUB/GPOS header size for table version 1.0
|
|
|
++const size_t kHeaderSize_1_0 = 4 + 3 * 2;
|
|
|
++// GSUB/GPOS header size for table versio 1.1
|
|
|
++const size_t kHeaderSize_1_1 = 4 + 3 * 2 + 4;
|
|
|
++
|
|
|
++bool OpenTypeLayoutTable::Parse(const uint8_t *data, size_t length) {
|
|
|
++ Buffer table(data, length);
|
|
|
++
|
|
|
++ uint16_t version_major = 0, version_minor = 0;
|
|
|
++ uint16_t offset_script_list = 0;
|
|
|
++ uint16_t offset_feature_list = 0;
|
|
|
++ uint16_t offset_lookup_list = 0;
|
|
|
++ uint32_t offset_feature_variations = 0;
|
|
|
++ if (!table.ReadU16(&version_major) ||
|
|
|
++ !table.ReadU16(&version_minor) ||
|
|
|
++ !table.ReadU16(&offset_script_list) ||
|
|
|
++ !table.ReadU16(&offset_feature_list) ||
|
|
|
++ !table.ReadU16(&offset_lookup_list)) {
|
|
|
++ return Error("Incomplete table");
|
|
|
++ }
|
|
|
++
|
|
|
++ if (version_major != 1 || version_minor > 1) {
|
|
|
++ return Error("Bad version");
|
|
|
++ }
|
|
|
++
|
|
|
++ if (version_minor > 0) {
|
|
|
++ if (!table.ReadU32(&offset_feature_variations)) {
|
|
|
++ return Error("Incomplete table");
|
|
|
++ }
|
|
|
++ }
|
|
|
++
|
|
|
++ const size_t header_size =
|
|
|
++ (version_minor == 0) ? kHeaderSize_1_0 : kHeaderSize_1_1;
|
|
|
++
|
|
|
++ if (offset_lookup_list) {
|
|
|
++ if (offset_lookup_list < header_size || offset_lookup_list >= length) {
|
|
|
++ return Error("Bad lookup list offset in table header");
|
|
|
++ }
|
|
|
++
|
|
|
++ if (!ParseLookupListTable(data + offset_lookup_list,
|
|
|
++ length - offset_lookup_list)) {
|
|
|
++ return Error("Failed to parse lookup list table");
|
|
|
++ }
|
|
|
++ }
|
|
|
++
|
|
|
++ if (offset_feature_list) {
|
|
|
++ if (offset_feature_list < header_size || offset_feature_list >= length) {
|
|
|
++ return Error("Bad feature list offset in table header");
|
|
|
++ }
|
|
|
++
|
|
|
++ if (!ParseFeatureListTable(data + offset_feature_list,
|
|
|
++ length - offset_feature_list)) {
|
|
|
++ return Error("Failed to parse feature list table");
|
|
|
++ }
|
|
|
++ }
|
|
|
++
|
|
|
++ if (offset_script_list) {
|
|
|
++ if (offset_script_list < header_size || offset_script_list >= length) {
|
|
|
++ return Error("Bad script list offset in table header");
|
|
|
++ }
|
|
|
++
|
|
|
++ if (!ParseScriptListTable(data + offset_script_list,
|
|
|
++ length - offset_script_list)) {
|
|
|
++ return Error("Failed to parse script list table");
|
|
|
++ }
|
|
|
++ }
|
|
|
++
|
|
|
++ if (offset_feature_variations) {
|
|
|
++ if (offset_feature_variations < header_size || offset_feature_variations >= length) {
|
|
|
++ return Error("Bad feature variations offset in table header");
|
|
|
++ }
|
|
|
++
|
|
|
++ if (!ParseFeatureVariationsTable(data + offset_feature_variations,
|
|
|
++ length - offset_feature_variations)) {
|
|
|
++ return Error("Failed to parse feature variations table");
|
|
|
++ }
|
|
|
++ }
|
|
|
++
|
|
|
++ this->m_data = data;
|
|
|
++ this->m_length = length;
|
|
|
++ return true;
|
|
|
++}
|
|
|
++
|
|
|
++bool OpenTypeLayoutTable::Serialize(OTSStream *out) {
|
|
|
++ if (!out->Write(this->m_data, this->m_length)) {
|
|
|
++ return Error("Failed to write table");
|
|
|
++ }
|
|
|
++
|
|
|
++ return true;
|
|
|
++}
|
|
|
++
|
|
|
+ } // namespace ots
|
|
|
+
|
|
|
+ #undef TABLE_NAME
|
|
|
+diff --git a/gfx/ots/src/layout.h b/gfx/ots/src/layout.h
|
|
|
+--- a/gfx/ots/src/layout.h
|
|
|
++++ b/gfx/ots/src/layout.h
|
|
|
+@@ -10,86 +10,56 @@
|
|
|
+ // Utility functions for OpenType layout common table formats.
|
|
|
+ // http://www.microsoft.com/typography/otspec/chapter2.htm
|
|
|
+
|
|
|
+ namespace ots {
|
|
|
+
|
|
|
+ // The maximum number of class value.
|
|
|
+ const uint16_t kMaxClassDefValue = 0xFFFF;
|
|
|
+
|
|
|
+-struct LookupSubtableParser {
|
|
|
+- struct TypeParser {
|
|
|
+- uint16_t type;
|
|
|
+- bool (*parse)(const Font *font, const uint8_t *data,
|
|
|
+- const size_t length);
|
|
|
+- };
|
|
|
+- size_t num_types;
|
|
|
+- uint16_t extension_type;
|
|
|
+- const TypeParser *parsers;
|
|
|
++class OpenTypeLayoutTable : public Table {
|
|
|
++ public:
|
|
|
++ explicit OpenTypeLayoutTable(Font *font, uint32_t tag, uint32_t type)
|
|
|
++ : Table(font, tag, type) { }
|
|
|
+
|
|
|
+- bool Parse(const Font *font, const uint8_t *data,
|
|
|
+- const size_t length, const uint16_t lookup_type) const;
|
|
|
+-};
|
|
|
++ bool Parse(const uint8_t *data, size_t length);
|
|
|
++ bool Serialize(OTSStream *out);
|
|
|
++
|
|
|
++ protected:
|
|
|
++ bool ParseContextSubtable(const uint8_t *data, const size_t length);
|
|
|
++ bool ParseChainingContextSubtable(const uint8_t *data, const size_t length);
|
|
|
++ bool ParseExtensionSubtable(const uint8_t *data, const size_t length);
|
|
|
+
|
|
|
+-bool ParseScriptListTable(const ots::Font *font,
|
|
|
+- const uint8_t *data, const size_t length,
|
|
|
+- const uint16_t num_features);
|
|
|
++ private:
|
|
|
++ bool ParseScriptListTable(const uint8_t *data, const size_t length);
|
|
|
++ bool ParseFeatureListTable(const uint8_t *data, const size_t length);
|
|
|
++ bool ParseLookupListTable(const uint8_t *data, const size_t length);
|
|
|
++ bool ParseFeatureVariationsTable(const uint8_t *data, const size_t length);
|
|
|
++ bool ParseLookupTable(const uint8_t *data, const size_t length);
|
|
|
+
|
|
|
+-bool ParseFeatureListTable(const ots::Font *font,
|
|
|
+- const uint8_t *data, const size_t length,
|
|
|
+- const uint16_t num_lookups,
|
|
|
+- uint16_t *num_features);
|
|
|
++ virtual bool ValidLookupSubtableType(const uint16_t lookup_type,
|
|
|
++ bool extension = false) const = 0;
|
|
|
++ virtual bool ParseLookupSubtable(const uint8_t *data, const size_t length,
|
|
|
++ const uint16_t lookup_type) = 0;
|
|
|
+
|
|
|
+-bool ParseLookupListTable(Font *font, const uint8_t *data,
|
|
|
+- const size_t length,
|
|
|
+- const LookupSubtableParser* parser,
|
|
|
+- uint16_t* num_lookups);
|
|
|
++ const uint8_t *m_data = nullptr;
|
|
|
++ size_t m_length = 0;
|
|
|
++ uint16_t m_num_features = 0;
|
|
|
++ uint16_t m_num_lookups = 0;
|
|
|
++};
|
|
|
+
|
|
|
+ bool ParseClassDefTable(const ots::Font *font,
|
|
|
+ const uint8_t *data, size_t length,
|
|
|
+ const uint16_t num_glyphs,
|
|
|
+ const uint16_t num_classes);
|
|
|
+
|
|
|
+ bool ParseCoverageTable(const ots::Font *font,
|
|
|
+ const uint8_t *data, size_t length,
|
|
|
+ const uint16_t num_glyphs,
|
|
|
+ const uint16_t expected_num_glyphs = 0);
|
|
|
+
|
|
|
+ bool ParseDeviceTable(const ots::Font *font,
|
|
|
+ const uint8_t *data, size_t length);
|
|
|
+
|
|
|
+-// Parser for 'Contextual' subtable shared by GSUB/GPOS tables.
|
|
|
+-bool ParseContextSubtable(const ots::Font *font,
|
|
|
+- const uint8_t *data, const size_t length,
|
|
|
+- const uint16_t num_glyphs,
|
|
|
+- const uint16_t num_lookups);
|
|
|
+-
|
|
|
+-// Parser for 'Chaining Contextual' subtable shared by GSUB/GPOS tables.
|
|
|
+-bool ParseChainingContextSubtable(const ots::Font *font,
|
|
|
+- const uint8_t *data, const size_t length,
|
|
|
+- const uint16_t num_glyphs,
|
|
|
+- const uint16_t num_lookups);
|
|
|
+-
|
|
|
+-bool ParseExtensionSubtable(const Font *font,
|
|
|
+- const uint8_t *data, const size_t length,
|
|
|
+- const LookupSubtableParser* parser);
|
|
|
+-
|
|
|
+-// For feature variations table (in GSUB/GPOS v1.1)
|
|
|
+-bool ParseConditionTable(const Font *font,
|
|
|
+- const uint8_t *data, const size_t length,
|
|
|
+- const uint16_t axis_count);
|
|
|
+-
|
|
|
+-bool ParseConditionSetTable(const Font *font,
|
|
|
+- const uint8_t *data, const size_t length,
|
|
|
+- const uint16_t axis_count);
|
|
|
+-
|
|
|
+-bool ParseFeatureTableSubstitutionTable(const Font *font,
|
|
|
+- const uint8_t *data, const size_t length,
|
|
|
+- const uint16_t num_lookups);
|
|
|
+-
|
|
|
+-bool ParseFeatureVariationsTable(const Font *font,
|
|
|
+- const uint8_t *data, const size_t length,
|
|
|
+- const uint16_t num_lookups);
|
|
|
+-
|
|
|
+ } // namespace ots
|
|
|
+
|
|
|
+ #endif // OTS_LAYOUT_H_
|
|
|
+
|
|
|
+diff --git a/gfx/ots/src/ots.cc b/gfx/ots/src/ots.cc
|
|
|
+--- a/gfx/ots/src/ots.cc
|
|
|
++++ b/gfx/ots/src/ots.cc
|
|
|
+@@ -732,20 +732,29 @@ bool ProcessGeneric(ots::FontFile *heade
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ #ifdef OTS_SYNTHESIZE_MISSING_GVAR
|
|
|
+ // If there was an fvar table but no gvar, synthesize an empty gvar to avoid
|
|
|
+ // issues with rasterizers (e.g. Core Text) that assume it must be present.
|
|
|
+ if (font->GetTable(OTS_TAG_FVAR) && !font->GetTable(OTS_TAG_GVAR)) {
|
|
|
+- ots::OpenTypeGVAR *gvar = new ots::OpenTypeGVAR(font, OTS_TAG_GVAR);
|
|
|
+- if (gvar->InitEmpty()) {
|
|
|
+- table_map[OTS_TAG_GVAR] = { OTS_TAG_GVAR, 0, 0, 0, 0 };
|
|
|
+- font->AddTable(gvar);
|
|
|
++ ots::TableEntry table_entry{ OTS_TAG_GVAR, 0, 0, 0, 0 };
|
|
|
++ const auto &it = font->file->tables.find(table_entry);
|
|
|
++ if (it != font->file->tables.end()) {
|
|
|
++ table_map[OTS_TAG_GVAR] = table_entry;
|
|
|
++ font->AddTable(table_entry, it->second);
|
|
|
++ } else {
|
|
|
++ ots::OpenTypeGVAR *gvar = new ots::OpenTypeGVAR(font, OTS_TAG_GVAR);
|
|
|
++ if (gvar->InitEmpty()) {
|
|
|
++ table_map[OTS_TAG_GVAR] = table_entry;
|
|
|
++ font->AddTable(table_entry, gvar);
|
|
|
++ } else {
|
|
|
++ delete gvar;
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ #endif
|
|
|
+
|
|
|
+ ots::Table *glyf = font->GetTable(OTS_TAG_GLYF);
|
|
|
+ ots::Table *loca = font->GetTable(OTS_TAG_LOCA);
|
|
|
+ ots::Table *cff = font->GetTable(OTS_TAG_CFF);
|
|
|
+ ots::Table *cff2 = font->GetTable(OTS_TAG_CFF2);
|
|
|
+@@ -892,16 +901,42 @@ bool ProcessGeneric(ots::FontFile *heade
|
|
|
+
|
|
|
+ if (!output->Seek(end_of_file)) {
|
|
|
+ return OTS_FAILURE_MSG_HDR("error writing output");
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
++bool IsGraphiteTag(uint32_t tag) {
|
|
|
++ if (tag == OTS_TAG_FEAT ||
|
|
|
++ tag == OTS_TAG_GLAT ||
|
|
|
++ tag == OTS_TAG_GLOC ||
|
|
|
++ tag == OTS_TAG_SILE ||
|
|
|
++ tag == OTS_TAG_SILF ||
|
|
|
++ tag == OTS_TAG_SILL) {
|
|
|
++ return true;
|
|
|
++ }
|
|
|
++ return false;
|
|
|
++}
|
|
|
++
|
|
|
++bool IsVariationsTag(uint32_t tag) {
|
|
|
++ if (tag == OTS_TAG_AVAR ||
|
|
|
++ tag == OTS_TAG_CVAR ||
|
|
|
++ tag == OTS_TAG_FVAR ||
|
|
|
++ tag == OTS_TAG_GVAR ||
|
|
|
++ tag == OTS_TAG_HVAR ||
|
|
|
++ tag == OTS_TAG_MVAR ||
|
|
|
++ tag == OTS_TAG_STAT ||
|
|
|
++ tag == OTS_TAG_VVAR) {
|
|
|
++ return true;
|
|
|
++ }
|
|
|
++ return false;
|
|
|
++}
|
|
|
++
|
|
|
+ } // namespace
|
|
|
+
|
|
|
+ namespace ots {
|
|
|
+
|
|
|
+ FontFile::~FontFile() {
|
|
|
+ for (const auto& it : tables) {
|
|
|
+ delete it.second;
|
|
|
+ }
|
|
|
+@@ -980,24 +1015,19 @@ bool Font::ParseTable(const TableEntry&
|
|
|
+ }
|
|
|
+
|
|
|
+ if (table) {
|
|
|
+ const uint8_t* table_data;
|
|
|
+ size_t table_length;
|
|
|
+
|
|
|
+ ret = GetTableData(data, table_entry, arena, &table_length, &table_data);
|
|
|
+ if (ret) {
|
|
|
+- // FIXME: Parsing some tables will fail if the table is not added to
|
|
|
+- // m_tables first.
|
|
|
+- m_tables[tag] = table;
|
|
|
+ ret = table->Parse(table_data, table_length);
|
|
|
+ if (ret)
|
|
|
+- file->tables[table_entry] = table;
|
|
|
+- else
|
|
|
+- m_tables.erase(tag);
|
|
|
++ AddTable(table_entry, table);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!ret)
|
|
|
+ delete table;
|
|
|
+
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+@@ -1011,48 +1041,37 @@ Table* Font::GetTable(uint32_t tag) cons
|
|
|
+
|
|
|
+ Table* Font::GetTypedTable(uint32_t tag) const {
|
|
|
+ Table* t = GetTable(tag);
|
|
|
+ if (t && t->Type() == tag)
|
|
|
+ return t;
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+-void Font::AddTable(Table* table) {
|
|
|
++void Font::AddTable(TableEntry entry, Table* table) {
|
|
|
+ // Attempting to add a duplicate table would be an error; this should only
|
|
|
+ // be used to add a table that does not already exist.
|
|
|
+ assert(m_tables.find(table->Tag()) == m_tables.end());
|
|
|
+ m_tables[table->Tag()] = table;
|
|
|
++ file->tables[entry] = table;
|
|
|
+ }
|
|
|
+
|
|
|
+ void Font::DropGraphite() {
|
|
|
+ file->context->Message(0, "Dropping all Graphite tables");
|
|
|
+ for (const std::pair<uint32_t, Table*> entry : m_tables) {
|
|
|
+- if (entry.first == OTS_TAG_FEAT ||
|
|
|
+- entry.first == OTS_TAG_GLAT ||
|
|
|
+- entry.first == OTS_TAG_GLOC ||
|
|
|
+- entry.first == OTS_TAG_SILE ||
|
|
|
+- entry.first == OTS_TAG_SILF ||
|
|
|
+- entry.first == OTS_TAG_SILL) {
|
|
|
++ if (IsGraphiteTag(entry.first)) {
|
|
|
+ entry.second->Drop("Discarding Graphite table");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void Font::DropVariations() {
|
|
|
+ file->context->Message(0, "Dropping all Variation tables");
|
|
|
+ for (const std::pair<uint32_t, Table*> entry : m_tables) {
|
|
|
+- if (entry.first == OTS_TAG_AVAR ||
|
|
|
+- entry.first == OTS_TAG_CVAR ||
|
|
|
+- entry.first == OTS_TAG_FVAR ||
|
|
|
+- entry.first == OTS_TAG_GVAR ||
|
|
|
+- entry.first == OTS_TAG_HVAR ||
|
|
|
+- entry.first == OTS_TAG_MVAR ||
|
|
|
+- entry.first == OTS_TAG_STAT ||
|
|
|
+- entry.first == OTS_TAG_VVAR) {
|
|
|
++ if (IsVariationsTag(entry.first)) {
|
|
|
+ entry.second->Drop("Discarding Variations table");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ bool Table::ShouldSerialize() {
|
|
|
+ return m_shouldSerialize;
|
|
|
+ }
|
|
|
+@@ -1095,26 +1114,32 @@ bool Table::Drop(const char *format, ...
|
|
|
+
|
|
|
+ bool Table::DropGraphite(const char *format, ...) {
|
|
|
+ va_list va;
|
|
|
+ va_start(va, format);
|
|
|
+ Message(0, format, va);
|
|
|
+ va_end(va);
|
|
|
+
|
|
|
+ m_font->DropGraphite();
|
|
|
++ if (IsGraphiteTag(m_tag))
|
|
|
++ Drop("Discarding Graphite table");
|
|
|
++
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool Table::DropVariations(const char *format, ...) {
|
|
|
+ va_list va;
|
|
|
+ va_start(va, format);
|
|
|
+ Message(0, format, va);
|
|
|
+ va_end(va);
|
|
|
+
|
|
|
+ m_font->DropVariations();
|
|
|
++ if (IsVariationsTag(m_tag))
|
|
|
++ Drop("Discarding Variations table");
|
|
|
++
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool TablePassthru::Parse(const uint8_t *data, size_t length) {
|
|
|
+ m_data = data;
|
|
|
+ m_length = length;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+diff --git a/gfx/ots/src/ots.h b/gfx/ots/src/ots.h
|
|
|
+--- a/gfx/ots/src/ots.h
|
|
|
++++ b/gfx/ots/src/ots.h
|
|
|
+@@ -314,17 +314,17 @@ struct Font {
|
|
|
+ Table* GetTable(uint32_t tag) const;
|
|
|
+
|
|
|
+ // This checks that the returned Table is actually of the correct subclass
|
|
|
+ // for |tag|, so it can safely be downcast to the corresponding OpenTypeXXXX;
|
|
|
+ // if not (i.e. if the table was treated as Passthru), it will return NULL.
|
|
|
+ Table* GetTypedTable(uint32_t tag) const;
|
|
|
+
|
|
|
+ // Insert a new table. Asserts if a table with the same tag already exists.
|
|
|
+- void AddTable(Table* table);
|
|
|
++ void AddTable(TableEntry entry, Table* table);
|
|
|
+
|
|
|
+ // Drop all Graphite tables and don't parse new ones.
|
|
|
+ void DropGraphite();
|
|
|
+
|
|
|
+ // Drop all Variations tables and don't parse new ones.
|
|
|
+ void DropVariations();
|
|
|
+
|
|
|
+ FontFile *file;
|
|
|
+diff --git a/gfx/ots/tests/layout_common_table_test.cc b/gfx/ots/tests/layout_common_table_test.cc
|
|
|
+deleted file mode 100644
|
|
|
+--- a/gfx/ots/tests/layout_common_table_test.cc
|
|
|
++++ /dev/null
|
|
|
+@@ -1,770 +0,0 @@
|
|
|
+-// Copyright (c) 2011-2017 The OTS Authors. All rights reserved.
|
|
|
+-// Use of this source code is governed by a BSD-style license that can be
|
|
|
+-// found in the LICENSE file.
|
|
|
+-
|
|
|
+-#include <cmath>
|
|
|
+-#include <vector>
|
|
|
+-#include <gtest/gtest.h>
|
|
|
+-
|
|
|
+-#include "layout.h"
|
|
|
+-#include "ots-memory-stream.h"
|
|
|
+-
|
|
|
+-namespace {
|
|
|
+-
|
|
|
+-const uint32_t kFakeTag = 0x00000000;
|
|
|
+-const size_t kScriptRecordSize = 6;
|
|
|
+-const size_t kLangSysRecordSize = 6;
|
|
|
+-
|
|
|
+-bool BuildFakeScriptListTable(ots::OTSStream *out, const uint16_t script_count,
|
|
|
+- const uint16_t langsys_count,
|
|
|
+- const uint16_t feature_count) {
|
|
|
+- if (!out->WriteU16(script_count)) {
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- const off_t script_record_end = out->Tell() +
|
|
|
+- kScriptRecordSize * script_count;
|
|
|
+- const size_t script_table_size = 4 + kLangSysRecordSize * langsys_count;
|
|
|
+- for (unsigned i = 0; i < script_count; ++i) {
|
|
|
+- if (!out->WriteU32(kFakeTag) ||
|
|
|
+- !out->WriteU16(script_record_end + i * script_table_size)) {
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- }
|
|
|
+-
|
|
|
+- // Offsets to LangSys tables are measured from the beginning of each
|
|
|
+- // script table.
|
|
|
+- const off_t langsys_record_end = 4 + kLangSysRecordSize * langsys_count;
|
|
|
+- const size_t langsys_table_size = 6 + 2 * feature_count;
|
|
|
+- // Write Fake Script tables.
|
|
|
+- for (unsigned i = 0; i < script_count; ++i) {
|
|
|
+- if (!out->WriteU16(0x0000) ||
|
|
|
+- !out->WriteU16(langsys_count)) {
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- for (unsigned j = 0; j < langsys_count; ++j) {
|
|
|
+- if (!out->WriteU32(kFakeTag) ||
|
|
|
+- !out->WriteU16(langsys_record_end + j * langsys_table_size)) {
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- }
|
|
|
+- }
|
|
|
+-
|
|
|
+- // Write Fake LangSys tables.
|
|
|
+- for (unsigned i = 0; i < langsys_count; ++i) {
|
|
|
+- if (!out->WriteU16(0x0000) ||
|
|
|
+- !out->WriteU16(0xFFFF) ||
|
|
|
+- !out->WriteU16(feature_count)) {
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- for (unsigned j = 0; j < feature_count; ++j) {
|
|
|
+- if (!out->WriteU16(j)) {
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- }
|
|
|
+- }
|
|
|
+- return true;
|
|
|
+-}
|
|
|
+-
|
|
|
+-const size_t kFeatureRecordSize = 6;
|
|
|
+-
|
|
|
+-bool BuildFakeFeatureListTable(ots::OTSStream *out,
|
|
|
+- const uint16_t feature_count,
|
|
|
+- const uint16_t lookup_count) {
|
|
|
+- if (!out->WriteU16(feature_count)) {
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- const off_t feature_record_end = out->Tell() +
|
|
|
+- kFeatureRecordSize * feature_count;
|
|
|
+- const size_t feature_table_size = 4 + 2 * lookup_count;
|
|
|
+- for (unsigned i = 0; i < feature_count; ++i) {
|
|
|
+- if (!out->WriteU32(kFakeTag) ||
|
|
|
+- !out->WriteU16(feature_record_end + i * feature_table_size)) {
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- }
|
|
|
+-
|
|
|
+- // Write FeatureTable
|
|
|
+- for (unsigned i = 0; i < feature_count; ++i) {
|
|
|
+- if (!out->WriteU16(0x0000) ||
|
|
|
+- !out->WriteU16(lookup_count)) {
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- for (uint16_t j = 0; j < lookup_count; ++j) {
|
|
|
+- if (!out->WriteU16(j)) {
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- }
|
|
|
+- }
|
|
|
+- return true;
|
|
|
+-}
|
|
|
+-
|
|
|
+-bool BuildFakeLookupListTable(ots::OTSStream *out, const uint16_t lookup_count,
|
|
|
+- const uint16_t subtable_count) {
|
|
|
+- if (!out->WriteU16(lookup_count)) {
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- const off_t base_offset_lookup = out->Tell();
|
|
|
+- if (!out->Pad(2 * lookup_count)) {
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+-
|
|
|
+- std::vector<off_t> offsets_lookup(lookup_count, 0);
|
|
|
+- for (uint16_t i = 0; i < lookup_count; ++i) {
|
|
|
+- offsets_lookup[i] = out->Tell();
|
|
|
+- if (!out->WriteU16(i + 1) ||
|
|
|
+- !out->WriteU16(0) ||
|
|
|
+- !out->WriteU16(subtable_count) ||
|
|
|
+- !out->Pad(2 * subtable_count) ||
|
|
|
+- !out->WriteU16(0)) {
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- }
|
|
|
+-
|
|
|
+- const off_t offset_lookup_table_end = out->Tell();
|
|
|
+- // Allocate 256 bytes for each subtable.
|
|
|
+- if (!out->Pad(256 * lookup_count * subtable_count)) {
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+-
|
|
|
+- if (!out->Seek(base_offset_lookup)) {
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- for (unsigned i = 0; i < lookup_count; ++i) {
|
|
|
+- if (!out->WriteU16(offsets_lookup[i])) {
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- }
|
|
|
+-
|
|
|
+- for (unsigned i = 0; i < lookup_count; ++i) {
|
|
|
+- if (!out->Seek(offsets_lookup[i] + 6)) {
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- for (unsigned j = 0; j < subtable_count; ++j) {
|
|
|
+- if (!out->WriteU16(offset_lookup_table_end +
|
|
|
+- 256*i*subtable_count + 256*j)) {
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- }
|
|
|
+- }
|
|
|
+- return true;
|
|
|
+-}
|
|
|
+-
|
|
|
+-bool BuildFakeCoverageFormat1(ots::OTSStream *out, const uint16_t glyph_count) {
|
|
|
+- if (!out->WriteU16(1) || !out->WriteU16(glyph_count)) {
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- for (uint16_t glyph_id = 1; glyph_id <= glyph_count; ++glyph_id) {
|
|
|
+- if (!out->WriteU16(glyph_id)) {
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- }
|
|
|
+- return true;
|
|
|
+-}
|
|
|
+-
|
|
|
+-bool BuildFakeCoverageFormat2(ots::OTSStream *out, const uint16_t range_count) {
|
|
|
+- if (!out->WriteU16(2) || !out->WriteU16(range_count)) {
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- uint16_t glyph_id = 1;
|
|
|
+- uint16_t start_coverage_index = 0;
|
|
|
+- for (unsigned i = 0; i < range_count; ++i) {
|
|
|
+- // Write consecutive ranges in which each range consists of two glyph id.
|
|
|
+- if (!out->WriteU16(glyph_id) ||
|
|
|
+- !out->WriteU16(glyph_id + 1) ||
|
|
|
+- !out->WriteU16(start_coverage_index)) {
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- glyph_id += 2;
|
|
|
+- start_coverage_index += 2;
|
|
|
+- }
|
|
|
+- return true;
|
|
|
+-}
|
|
|
+-
|
|
|
+-bool BuildFakeClassDefFormat1(ots::OTSStream *out, const uint16_t glyph_count) {
|
|
|
+- if (!out->WriteU16(1) ||
|
|
|
+- !out->WriteU16(1) ||
|
|
|
+- !out->WriteU16(glyph_count)) {
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- for (uint16_t class_value = 1; class_value <= glyph_count; ++class_value) {
|
|
|
+- if (!out->WriteU16(class_value)) {
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- }
|
|
|
+- return true;
|
|
|
+-}
|
|
|
+-
|
|
|
+-bool BuildFakeClassDefFormat2(ots::OTSStream *out, const uint16_t range_count) {
|
|
|
+- if (!out->WriteU16(2) || !out->WriteU16(range_count)) {
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- uint16_t glyph_id = 1;
|
|
|
+- for (uint16_t class_value = 1; class_value <= range_count; ++class_value) {
|
|
|
+- // Write consecutive ranges in which each range consists of one glyph id.
|
|
|
+- if (!out->WriteU16(glyph_id) ||
|
|
|
+- !out->WriteU16(glyph_id + 1) ||
|
|
|
+- !out->WriteU16(class_value)) {
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- glyph_id += 2;
|
|
|
+- }
|
|
|
+- return true;
|
|
|
+-}
|
|
|
+-
|
|
|
+-bool BuildFakeDeviceTable(ots::OTSStream *out, const uint16_t start_size,
|
|
|
+- const uint16_t end_size, const uint16_t format) {
|
|
|
+- if (!out->WriteU16(start_size) ||
|
|
|
+- !out->WriteU16(end_size) ||
|
|
|
+- !out->WriteU16(format)) {
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+-
|
|
|
+- const unsigned num_values = std::abs(end_size - start_size) + 1;
|
|
|
+- const unsigned num_bits = (1 << format) * num_values;
|
|
|
+- const unsigned num_units = (num_bits - 1) / 16 + 1;
|
|
|
+- if (!out->Pad(num_units * 2)) {
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- return true;
|
|
|
+-}
|
|
|
+-
|
|
|
+-class TestStream : public ots::MemoryStream {
|
|
|
+- public:
|
|
|
+- TestStream()
|
|
|
+- : ots::MemoryStream(data_, sizeof(data_)), size_(0) {
|
|
|
+- std::memset(reinterpret_cast<char*>(data_), 0, sizeof(data_));
|
|
|
+- }
|
|
|
+-
|
|
|
+- uint8_t* data() { return data_; }
|
|
|
+- size_t size() override { return size_; }
|
|
|
+-
|
|
|
+- bool WriteRaw(const void *d, size_t length) override {
|
|
|
+- if (Tell() + length > size_) {
|
|
|
+- size_ = Tell() + length;
|
|
|
+- }
|
|
|
+- return ots::MemoryStream::WriteRaw(d, length);
|
|
|
+- }
|
|
|
+-
|
|
|
+- private:
|
|
|
+- size_t size_;
|
|
|
+- uint8_t data_[4096];
|
|
|
+-};
|
|
|
+-
|
|
|
+-class TableTest : public ::testing::Test {
|
|
|
+- protected:
|
|
|
+-
|
|
|
+- virtual void SetUp() {
|
|
|
+- ots::FontFile *file = new ots::FontFile();
|
|
|
+- file->context = new ots::OTSContext();
|
|
|
+- font = new ots::Font(file);
|
|
|
+- }
|
|
|
+-
|
|
|
+- virtual void TearDown() {
|
|
|
+- delete font->file->context;
|
|
|
+- delete font->file;
|
|
|
+- delete font;
|
|
|
+- }
|
|
|
+-
|
|
|
+- TestStream out;
|
|
|
+- ots::Font *font;
|
|
|
+-};
|
|
|
+-
|
|
|
+-class ScriptListTableTest : public TableTest { };
|
|
|
+-class DeviceTableTest : public TableTest { };
|
|
|
+-class CoverageTableTest : public TableTest { };
|
|
|
+-class CoverageFormat1Test : public TableTest { };
|
|
|
+-class CoverageFormat2Test : public TableTest { };
|
|
|
+-class ClassDefTableTest : public TableTest { };
|
|
|
+-class ClassDefFormat1Test : public TableTest { };
|
|
|
+-class ClassDefFormat2Test : public TableTest { };
|
|
|
+-class LookupSubtableParserTest : public TableTest { };
|
|
|
+-
|
|
|
+-class FeatureListTableTest : public TableTest {
|
|
|
+- protected:
|
|
|
+-
|
|
|
+- virtual void SetUp() {
|
|
|
+- TableTest::SetUp();
|
|
|
+- num_features = 0;
|
|
|
+- }
|
|
|
+-
|
|
|
+- uint16_t num_features;
|
|
|
+-};
|
|
|
+-
|
|
|
+-bool fakeTypeParserReturnsTrue(const ots::Font*, const uint8_t *,
|
|
|
+- const size_t) {
|
|
|
+- return true;
|
|
|
+-}
|
|
|
+-
|
|
|
+-bool fakeTypeParserReturnsFalse(const ots::Font*, const uint8_t *,
|
|
|
+- const size_t) {
|
|
|
+- return false;
|
|
|
+-}
|
|
|
+-
|
|
|
+-const ots::LookupSubtableParser::TypeParser TypeParsersReturnTrue[] = {
|
|
|
+- {1, fakeTypeParserReturnsTrue},
|
|
|
+- {2, fakeTypeParserReturnsTrue},
|
|
|
+- {3, fakeTypeParserReturnsTrue},
|
|
|
+- {4, fakeTypeParserReturnsTrue},
|
|
|
+- {5, fakeTypeParserReturnsTrue}
|
|
|
+-};
|
|
|
+-
|
|
|
+-// Fake lookup subtable parser which always returns true.
|
|
|
+-const ots::LookupSubtableParser FakeLookupParserReturnsTrue = {
|
|
|
+- 5, 5, TypeParsersReturnTrue,
|
|
|
+-};
|
|
|
+-
|
|
|
+-const ots::LookupSubtableParser::TypeParser TypeParsersReturnFalse[] = {
|
|
|
+- {1, fakeTypeParserReturnsFalse}
|
|
|
+-};
|
|
|
+-
|
|
|
+-// Fake lookup subtable parser which always returns false.
|
|
|
+-const ots::LookupSubtableParser FakeLookupParserReturnsFalse = {
|
|
|
+- 1, 1, TypeParsersReturnFalse
|
|
|
+-};
|
|
|
+-
|
|
|
+-class LookupListTableTest : public TableTest {
|
|
|
+- protected:
|
|
|
+-
|
|
|
+- virtual void SetUp() {
|
|
|
+- TableTest::SetUp();
|
|
|
+- num_lookups = 0;
|
|
|
+- }
|
|
|
+-
|
|
|
+- bool Parse() {
|
|
|
+- return ots::ParseLookupListTable(font, out.data(), out.size(),
|
|
|
+- &FakeLookupParserReturnsTrue,
|
|
|
+- &num_lookups);
|
|
|
+- }
|
|
|
+-
|
|
|
+- uint16_t num_lookups;
|
|
|
+-};
|
|
|
+-
|
|
|
+-} // namespace
|
|
|
+-
|
|
|
+-TEST_F(ScriptListTableTest, TestSuccess) {
|
|
|
+- BuildFakeScriptListTable(&out, 1, 1, 1);
|
|
|
+- EXPECT_TRUE(ots::ParseScriptListTable(font, out.data(), out.size(), 1));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(ScriptListTableTest, TestBadScriptCount) {
|
|
|
+- BuildFakeScriptListTable(&out, 1, 1, 1);
|
|
|
+- // Set too large script count.
|
|
|
+- out.Seek(0);
|
|
|
+- out.WriteU16(2);
|
|
|
+- EXPECT_FALSE(ots::ParseScriptListTable(font, out.data(), out.size(), 1));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(ScriptListTableTest, TestScriptRecordOffsetUnderflow) {
|
|
|
+- BuildFakeScriptListTable(&out, 1, 1, 1);
|
|
|
+- // Set bad offset to ScriptRecord[0].
|
|
|
+- out.Seek(6);
|
|
|
+- out.WriteU16(0);
|
|
|
+- EXPECT_FALSE(ots::ParseScriptListTable(font, out.data(), out.size(), 1));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(ScriptListTableTest, TestScriptRecordOffsetOverflow) {
|
|
|
+- BuildFakeScriptListTable(&out, 1, 1, 1);
|
|
|
+- // Set bad offset to ScriptRecord[0].
|
|
|
+- out.Seek(6);
|
|
|
+- out.WriteU16(out.size());
|
|
|
+- EXPECT_FALSE(ots::ParseScriptListTable(font, out.data(), out.size(), 1));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(ScriptListTableTest, TestBadLangSysCount) {
|
|
|
+- BuildFakeScriptListTable(&out, 1, 1, 1);
|
|
|
+- // Set too large langsys count.
|
|
|
+- out.Seek(10);
|
|
|
+- out.WriteU16(2);
|
|
|
+- EXPECT_FALSE(ots::ParseScriptListTable(font, out.data(), out.size(), 1));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(ScriptListTableTest, TestLangSysRecordOffsetUnderflow) {
|
|
|
+- BuildFakeScriptListTable(&out, 1, 1, 1);
|
|
|
+- // Set bad offset to LangSysRecord[0].
|
|
|
+- out.Seek(16);
|
|
|
+- out.WriteU16(0);
|
|
|
+- EXPECT_FALSE(ots::ParseScriptListTable(font, out.data(), out.size(), 1));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(ScriptListTableTest, TestLangSysRecordOffsetOverflow) {
|
|
|
+- BuildFakeScriptListTable(&out, 1, 1, 1);
|
|
|
+- // Set bad offset to LangSysRecord[0].
|
|
|
+- out.Seek(16);
|
|
|
+- out.WriteU16(out.size());
|
|
|
+- EXPECT_FALSE(ots::ParseScriptListTable(font, out.data(), out.size(), 1));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(ScriptListTableTest, TestBadReqFeatureIndex) {
|
|
|
+- BuildFakeScriptListTable(&out, 1, 1, 1);
|
|
|
+- // Set too large feature index to ReqFeatureIndex of LangSysTable[0].
|
|
|
+- out.Seek(20);
|
|
|
+- out.WriteU16(2);
|
|
|
+- EXPECT_FALSE(ots::ParseScriptListTable(font, out.data(), out.size(), 1));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(ScriptListTableTest, TestBadFeatureCount) {
|
|
|
+- BuildFakeScriptListTable(&out, 1, 1, 1);
|
|
|
+- // Set too large feature count to LangSysTable[0].
|
|
|
+- out.Seek(22);
|
|
|
+- out.WriteU16(2);
|
|
|
+- EXPECT_FALSE(ots::ParseScriptListTable(font, out.data(), out.size(), 1));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(ScriptListTableTest, TestBadFeatureIndex) {
|
|
|
+- BuildFakeScriptListTable(&out, 1, 1, 1);
|
|
|
+- // Set too large feature index to ReatureIndex[0] of LangSysTable[0].
|
|
|
+- out.Seek(24);
|
|
|
+- out.WriteU16(2);
|
|
|
+- EXPECT_FALSE(ots::ParseScriptListTable(font, out.data(), out.size(), 1));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(FeatureListTableTest, TestSuccess) {
|
|
|
+- BuildFakeFeatureListTable(&out, 1, 1);
|
|
|
+- EXPECT_TRUE(ots::ParseFeatureListTable(font, out.data(), out.size(), 1,
|
|
|
+- &num_features));
|
|
|
+- EXPECT_EQ(num_features, 1);
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(FeatureListTableTest, TestSuccess2) {
|
|
|
+- BuildFakeFeatureListTable(&out, 5, 1);
|
|
|
+- EXPECT_TRUE(ots::ParseFeatureListTable(font, out.data(), out.size(), 1,
|
|
|
+- &num_features));
|
|
|
+- EXPECT_EQ(num_features, 5);
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(FeatureListTableTest, TestBadFeatureCount) {
|
|
|
+- BuildFakeFeatureListTable(&out, 1, 1);
|
|
|
+- // Set too large feature count.
|
|
|
+- out.Seek(0);
|
|
|
+- out.WriteU16(2);
|
|
|
+- EXPECT_FALSE(ots::ParseFeatureListTable(font, out.data(), out.size(), 1,
|
|
|
+- &num_features));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(FeatureListTableTest, TestOffsetFeatureUnderflow) {
|
|
|
+- BuildFakeFeatureListTable(&out, 1, 1);
|
|
|
+- // Set bad offset to FeatureRecord[0].
|
|
|
+- out.Seek(6);
|
|
|
+- out.WriteU16(0);
|
|
|
+- EXPECT_FALSE(ots::ParseFeatureListTable(font, out.data(), out.size(), 1,
|
|
|
+- &num_features));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(FeatureListTableTest, TestOffsetFeatureOverflow) {
|
|
|
+- BuildFakeFeatureListTable(&out, 1, 1);
|
|
|
+- // Set bad offset to FeatureRecord[0].
|
|
|
+- out.Seek(6);
|
|
|
+- out.WriteU16(out.size());
|
|
|
+- EXPECT_FALSE(ots::ParseFeatureListTable(font, out.data(), out.size(), 1,
|
|
|
+- &num_features));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(FeatureListTableTest, TestBadLookupCount) {
|
|
|
+- BuildFakeFeatureListTable(&out, 1, 1);
|
|
|
+- // Set too large lookup count to FeatureTable[0].
|
|
|
+- out.Seek(10);
|
|
|
+- out.WriteU16(2);
|
|
|
+- EXPECT_FALSE(ots::ParseFeatureListTable(font, out.data(), out.size(), 1,
|
|
|
+- &num_features));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(LookupListTableTest, TestSuccess) {
|
|
|
+- BuildFakeLookupListTable(&out, 1, 1);
|
|
|
+- EXPECT_TRUE(Parse());
|
|
|
+- EXPECT_EQ(num_lookups, 1);
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(LookupListTableTest, TestSuccess2) {
|
|
|
+- BuildFakeLookupListTable(&out, 5, 1);
|
|
|
+- EXPECT_TRUE(Parse());
|
|
|
+- EXPECT_EQ(num_lookups, 5);
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(LookupListTableTest, TestOffsetLookupTableUnderflow) {
|
|
|
+- BuildFakeLookupListTable(&out, 1, 1);
|
|
|
+- // Set bad offset to Lookup[0].
|
|
|
+- out.Seek(2);
|
|
|
+- out.WriteU16(0);
|
|
|
+- EXPECT_FALSE(Parse());
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(LookupListTableTest, TestOffsetLookupTableOverflow) {
|
|
|
+- BuildFakeLookupListTable(&out, 1, 1);
|
|
|
+- // Set bad offset to Lookup[0].
|
|
|
+- out.Seek(2);
|
|
|
+- out.WriteU16(out.size());
|
|
|
+- EXPECT_FALSE(Parse());
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(LookupListTableTest, TestOffsetSubtableUnderflow) {
|
|
|
+- BuildFakeLookupListTable(&out, 1, 1);
|
|
|
+- // Set bad offset to SubTable[0] of LookupTable[0].
|
|
|
+- out.Seek(10);
|
|
|
+- out.WriteU16(0);
|
|
|
+- EXPECT_FALSE(Parse());
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(LookupListTableTest, TestOffsetSubtableOverflow) {
|
|
|
+- BuildFakeLookupListTable(&out, 1, 1);
|
|
|
+- // Set bad offset to SubTable[0] of LookupTable[0].
|
|
|
+- out.Seek(10);
|
|
|
+- out.WriteU16(out.size());
|
|
|
+- EXPECT_FALSE(Parse());
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(LookupListTableTest, TesBadLookupCount) {
|
|
|
+- BuildFakeLookupListTable(&out, 1, 1);
|
|
|
+- // Set too large lookup count of LookupTable[0].
|
|
|
+- out.Seek(0);
|
|
|
+- out.WriteU16(2);
|
|
|
+- EXPECT_FALSE(Parse());
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(LookupListTableTest, TesBadLookupType) {
|
|
|
+- BuildFakeLookupListTable(&out, 1, 1);
|
|
|
+- // Set too large lookup type of LookupTable[0].
|
|
|
+- out.Seek(4);
|
|
|
+- out.WriteU16(6);
|
|
|
+- EXPECT_FALSE(Parse());
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(LookupListTableTest, TesBadLookupFlag) {
|
|
|
+- BuildFakeLookupListTable(&out, 1, 1);
|
|
|
+- // Set IgnoreBaseGlyphs(0x0002) to the lookup flag of LookupTable[0].
|
|
|
+- out.Seek(6);
|
|
|
+- out.WriteU16(0x0002);
|
|
|
+- EXPECT_TRUE(Parse());
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(LookupListTableTest, TesBadSubtableCount) {
|
|
|
+- BuildFakeLookupListTable(&out, 1, 1);
|
|
|
+- // Set too large sutable count of LookupTable[0].
|
|
|
+- out.Seek(8);
|
|
|
+- out.WriteU16(2);
|
|
|
+- EXPECT_FALSE(Parse());
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(CoverageTableTest, TestSuccessFormat1) {
|
|
|
+- BuildFakeCoverageFormat1(&out, 1);
|
|
|
+- EXPECT_TRUE(ots::ParseCoverageTable(font, out.data(), out.size(), 1));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(CoverageTableTest, TestSuccessFormat2) {
|
|
|
+- BuildFakeCoverageFormat2(&out, 1);
|
|
|
+- EXPECT_TRUE(ots::ParseCoverageTable(font, out.data(), out.size(), 1));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(CoverageTableTest, TestBadFormat) {
|
|
|
+- BuildFakeCoverageFormat1(&out, 1);
|
|
|
+- // Set bad format.
|
|
|
+- out.Seek(0);
|
|
|
+- out.WriteU16(3);
|
|
|
+- EXPECT_FALSE(ots::ParseCoverageTable(font, out.data(), out.size(), 1));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(CoverageFormat1Test, TestBadGlyphCount) {
|
|
|
+- BuildFakeCoverageFormat1(&out, 1);
|
|
|
+- // Set too large glyph count.
|
|
|
+- out.Seek(2);
|
|
|
+- out.WriteU16(2);
|
|
|
+- EXPECT_FALSE(ots::ParseCoverageTable(font, out.data(), out.size(), 1));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(CoverageFormat1Test, TestBadGlyphId) {
|
|
|
+- BuildFakeCoverageFormat1(&out, 1);
|
|
|
+- // Set too large glyph id.
|
|
|
+- out.Seek(4);
|
|
|
+- out.WriteU16(2);
|
|
|
+- EXPECT_FALSE(ots::ParseCoverageTable(font, out.data(), out.size(), 1));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(CoverageFormat2Test, TestBadRangeCount) {
|
|
|
+- BuildFakeCoverageFormat2(&out, 1);
|
|
|
+- // Set too large range count.
|
|
|
+- out.Seek(2);
|
|
|
+- out.WriteU16(2);
|
|
|
+- EXPECT_FALSE(ots::ParseCoverageTable(font, out.data(), out.size(), 1));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(CoverageFormat2Test, TestBadRange) {
|
|
|
+- BuildFakeCoverageFormat2(&out, 1);
|
|
|
+- // Set reverse order glyph id to start/end fields.
|
|
|
+- out.Seek(4);
|
|
|
+- out.WriteU16(2);
|
|
|
+- out.WriteU16(1);
|
|
|
+- EXPECT_FALSE(ots::ParseCoverageTable(font, out.data(), out.size(), 1));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(CoverageFormat2Test, TestRangeOverlap) {
|
|
|
+- BuildFakeCoverageFormat2(&out, 2);
|
|
|
+- // Set overlapping glyph id to an end field.
|
|
|
+- out.Seek(12);
|
|
|
+- out.WriteU16(1);
|
|
|
+- EXPECT_FALSE(ots::ParseCoverageTable(font, out.data(), out.size(), 2));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(CoverageFormat2Test, TestRangeOverlap2) {
|
|
|
+- BuildFakeCoverageFormat2(&out, 2);
|
|
|
+- // Set overlapping range.
|
|
|
+- out.Seek(10);
|
|
|
+- out.WriteU16(1);
|
|
|
+- out.WriteU16(2);
|
|
|
+- EXPECT_FALSE(ots::ParseCoverageTable(font, out.data(), out.size(), 2));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(ClassDefTableTest, TestSuccessFormat1) {
|
|
|
+- BuildFakeClassDefFormat1(&out, 1);
|
|
|
+- EXPECT_TRUE(ots::ParseClassDefTable(font, out.data(), out.size(), 1, 1));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(ClassDefTableTest, TestSuccessFormat2) {
|
|
|
+- BuildFakeClassDefFormat2(&out, 1);
|
|
|
+- EXPECT_TRUE(ots::ParseClassDefTable(font, out.data(), out.size(), 1, 1));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(ClassDefTableTest, TestBadFormat) {
|
|
|
+- BuildFakeClassDefFormat1(&out, 1);
|
|
|
+- // Set bad format.
|
|
|
+- out.Seek(0);
|
|
|
+- out.WriteU16(3);
|
|
|
+- EXPECT_FALSE(ots::ParseClassDefTable(font, out.data(), out.size(), 1, 1));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(ClassDefFormat1Test, TestBadStartGlyph) {
|
|
|
+- BuildFakeClassDefFormat1(&out, 1);
|
|
|
+- // Set too large start glyph id.
|
|
|
+- out.Seek(2);
|
|
|
+- out.WriteU16(2);
|
|
|
+- EXPECT_FALSE(ots::ParseClassDefTable(font, out.data(), out.size(), 1, 1));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(ClassDefFormat1Test, TestBadGlyphCount) {
|
|
|
+- BuildFakeClassDefFormat1(&out, 1);
|
|
|
+- // Set too large glyph count.
|
|
|
+- out.Seek(4);
|
|
|
+- out.WriteU16(2);
|
|
|
+- EXPECT_FALSE(ots::ParseClassDefTable(font, out.data(), out.size(), 1, 1));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(ClassDefFormat1Test, TestBadClassValue) {
|
|
|
+- BuildFakeClassDefFormat1(&out, 1);
|
|
|
+- // Set too large class value.
|
|
|
+- out.Seek(6);
|
|
|
+- out.WriteU16(2);
|
|
|
+- EXPECT_FALSE(ots::ParseClassDefTable(font, out.data(), out.size(), 1, 1));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(ClassDefFormat2Test, TestBadRangeCount) {
|
|
|
+- BuildFakeClassDefFormat2(&out, 1);
|
|
|
+- // Set too large range count.
|
|
|
+- out.Seek(2);
|
|
|
+- out.WriteU16(2);
|
|
|
+- EXPECT_FALSE(ots::ParseClassDefTable(font, out.data(), out.size(), 1, 1));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(ClassDefFormat2Test, TestRangeOverlap) {
|
|
|
+- BuildFakeClassDefFormat2(&out, 2);
|
|
|
+- // Set overlapping glyph id to an end field.
|
|
|
+- out.Seek(12);
|
|
|
+- out.WriteU16(1);
|
|
|
+- EXPECT_FALSE(ots::ParseClassDefTable(font, out.data(), out.size(), 1, 1));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(ClassDefFormat2Test, TestRangeOverlap2) {
|
|
|
+- BuildFakeClassDefFormat2(&out, 2);
|
|
|
+- // Set overlapping range.
|
|
|
+- out.Seek(10);
|
|
|
+- out.WriteU16(1);
|
|
|
+- out.WriteU16(2);
|
|
|
+- EXPECT_FALSE(ots::ParseClassDefTable(font, out.data(), out.size(), 1, 1));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(DeviceTableTest, TestDeltaFormat1Success) {
|
|
|
+- BuildFakeDeviceTable(&out, 1, 8, 1);
|
|
|
+- EXPECT_TRUE(ots::ParseDeviceTable(font, out.data(), out.size()));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(DeviceTableTest, TestDeltaFormat1Success2) {
|
|
|
+- BuildFakeDeviceTable(&out, 1, 9, 1);
|
|
|
+- EXPECT_TRUE(ots::ParseDeviceTable(font, out.data(), out.size()));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(DeviceTableTest, TestDeltaFormat1Fail) {
|
|
|
+- // Pass shorter length than expected.
|
|
|
+- BuildFakeDeviceTable(&out, 1, 8, 1);
|
|
|
+- EXPECT_FALSE(ots::ParseDeviceTable(font, out.data(), out.size() - 1));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(DeviceTableTest, TestDeltaFormat1Fail2) {
|
|
|
+- // Pass shorter length than expected.
|
|
|
+- BuildFakeDeviceTable(&out, 1, 9, 1);
|
|
|
+- EXPECT_FALSE(ots::ParseDeviceTable(font, out.data(), out.size() - 1));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(DeviceTableTest, TestDeltaFormat2Success) {
|
|
|
+- BuildFakeDeviceTable(&out, 1, 1, 2);
|
|
|
+- EXPECT_TRUE(ots::ParseDeviceTable(font, out.data(), out.size()));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(DeviceTableTest, TestDeltaFormat2Success2) {
|
|
|
+- BuildFakeDeviceTable(&out, 1, 8, 2);
|
|
|
+- EXPECT_TRUE(ots::ParseDeviceTable(font, out.data(), out.size()));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(DeviceTableTest, TestDeltaFormat2Fail) {
|
|
|
+- // Pass shorter length than expected.
|
|
|
+- BuildFakeDeviceTable(&out, 1, 8, 2);
|
|
|
+- EXPECT_FALSE(ots::ParseDeviceTable(font, out.data(), out.size() - 1));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(DeviceTableTest, TestDeltaFormat2Fail2) {
|
|
|
+- // Pass shorter length than expected.
|
|
|
+- BuildFakeDeviceTable(&out, 1, 9, 2);
|
|
|
+- EXPECT_FALSE(ots::ParseDeviceTable(font, out.data(), out.size() - 1));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(DeviceTableTest, TestDeltaFormat3Success) {
|
|
|
+- BuildFakeDeviceTable(&out, 1, 1, 3);
|
|
|
+- EXPECT_TRUE(ots::ParseDeviceTable(font, out.data(), out.size()));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(DeviceTableTest, TestDeltaFormat3Success2) {
|
|
|
+- BuildFakeDeviceTable(&out, 1, 8, 3);
|
|
|
+- EXPECT_TRUE(ots::ParseDeviceTable(font, out.data(), out.size()));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(DeviceTableTest, TestDeltaFormat3Fail) {
|
|
|
+- // Pass shorter length than expected.
|
|
|
+- BuildFakeDeviceTable(&out, 1, 8, 3);
|
|
|
+- EXPECT_FALSE(ots::ParseDeviceTable(font, out.data(), out.size() - 1));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(DeviceTableTest, TestDeltaFormat3Fail2) {
|
|
|
+- // Pass shorter length than expected.
|
|
|
+- BuildFakeDeviceTable(&out, 1, 9, 3);
|
|
|
+- EXPECT_FALSE(ots::ParseDeviceTable(font, out.data(), out.size() - 1));
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(LookupSubtableParserTest, TestSuccess) {
|
|
|
+- {
|
|
|
+- EXPECT_TRUE(FakeLookupParserReturnsTrue.Parse(font, 0, 0, 1));
|
|
|
+- }
|
|
|
+- {
|
|
|
+- EXPECT_TRUE(FakeLookupParserReturnsTrue.Parse(font, 0, 0, 5));
|
|
|
+- }
|
|
|
+-}
|
|
|
+-
|
|
|
+-TEST_F(LookupSubtableParserTest, TestFail) {
|
|
|
+- {
|
|
|
+- // Pass bad lookup type which less than the smallest type.
|
|
|
+- EXPECT_FALSE(FakeLookupParserReturnsTrue.Parse(font, 0, 0, 0));
|
|
|
+- }
|
|
|
+- {
|
|
|
+- // Pass bad lookup type which greater than the maximum type.
|
|
|
+- EXPECT_FALSE(FakeLookupParserReturnsTrue.Parse(font, 0, 0, 6));
|
|
|
+- }
|
|
|
+- {
|
|
|
+- // Check the type parser failure.
|
|
|
+- EXPECT_FALSE(FakeLookupParserReturnsFalse.Parse(font, 0, 0, 1));
|
|
|
+- }
|
|
|
+-}
|
|
|
+diff --git a/gfx/tests/gtest/moz.build b/gfx/tests/gtest/moz.build
|
|
|
+--- a/gfx/tests/gtest/moz.build
|
|
|
++++ b/gfx/tests/gtest/moz.build
|
|
|
+@@ -34,24 +34,18 @@ UNIFIED_SOURCES += [
|
|
|
+ UNIFIED_SOURCES += [ '/gfx/2d/unittest/%s' % p for p in [
|
|
|
+ 'TestBase.cpp',
|
|
|
+ 'TestBugs.cpp',
|
|
|
+ 'TestCairo.cpp',
|
|
|
+ 'TestPoint.cpp',
|
|
|
+ 'TestScaling.cpp',
|
|
|
+ ]]
|
|
|
+
|
|
|
+-# not UNIFIED_SOURCES because layout_common_table_test.cc has classes
|
|
|
+-# in an anonymous namespace which result in a GCC error when used in
|
|
|
+-# tests (e g. "error: 'ScriptListTableTest_TestSuccess_Test' has a field
|
|
|
+-# 'ScriptListTableTest_TestSuccess_Test::<anonymous>' whose type uses
|
|
|
+-# the anonymous namespace").
|
|
|
+-SOURCES += [ '/gfx/ots/tests/%s' % p for p in [
|
|
|
+- 'cff_charstring_test.cc',
|
|
|
+- 'layout_common_table_test.cc',
|
|
|
+++UNIFIED_SOURCES += [
|
|
|
+++ "/gfx/ots/tests/cff_charstring_test.cc",
|
|
|
+ ]]
|
|
|
+
|
|
|
+ # ICC profiles used for verifying QCMS transformations. The copyright
|
|
|
+ # notice embedded in the profiles should be reviewed to ensure there are
|
|
|
+ # no known restrictions on distribution.
|
|
|
+ TEST_HARNESS_FILES.gtest += [
|
|
|
+ 'icc_profiles/lcms_samsung_syncmaster.icc',
|
|
|
+ 'icc_profiles/lcms_thinkpad_w540.icc',
|