diff options
Diffstat (limited to 'gfx/ots/src/gpos.cc')
-rw-r--r-- | gfx/ots/src/gpos.cc | 277 |
1 files changed, 151 insertions, 126 deletions
diff --git a/gfx/ots/src/gpos.cc b/gfx/ots/src/gpos.cc index 83d9ab053..034f9799d 100644 --- a/gfx/ots/src/gpos.cc +++ b/gfx/ots/src/gpos.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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. @@ -30,12 +30,12 @@ enum GPOS_TYPE { GPOS_TYPE_RESERVED = 10 }; -// The size of gpos header. -const unsigned kGposHeaderSize = 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; -// The maximum number of class value. -const uint16_t kMaxClassDefValue = 0xFFFF; // Lookup type parsers. bool ParseSingleAdjustment(const ots::Font *font, @@ -76,9 +76,23 @@ const ots::LookupSubtableParser kGposLookupSubtableParser = { // 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; + } + } + + return size; +} + bool ParseValueRecord(const ots::Font *font, - ots::Buffer* subtable, const uint8_t *data, - const size_t length, const uint16_t value_format) { + ots::Buffer* subtable, + const uint16_t value_format) { + const uint8_t *data = subtable->buffer(); + const size_t length = subtable->length(); + // Check existence of adjustment fields. for (unsigned i = 0; i < 4; ++i) { if ((value_format >> i) & 0x1) { @@ -207,6 +221,12 @@ 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; @@ -218,7 +238,7 @@ bool ParseSingleAdjustment(const ots::Font *font, const uint8_t *data, if (format == 1) { // Format 1 exactly one value record. - if (!ParseValueRecord(font, &subtable, data, length, value_format)) { + if (!ParseValueRecord(font, &subtable, value_format)) { return OTS_FAILURE_MSG("Failed to parse format 1 single adjustment table"); } } else if (format == 2) { @@ -227,7 +247,7 @@ bool ParseSingleAdjustment(const ots::Font *font, const uint8_t *data, return OTS_FAILURE_MSG("Failed to parse format 2 single adjustment table"); } for (unsigned i = 0; i < value_count; ++i) { - if (!ParseValueRecord(font, &subtable, data, length, value_format)) { + if (!ParseValueRecord(font, &subtable, value_format)) { return OTS_FAILURE_MSG("Failed to parse value record %d in format 2 single adjustment table", i); } } @@ -241,7 +261,7 @@ bool ParseSingleAdjustment(const ots::Font *font, const uint8_t *data, if (!ots::ParseCoverageTable(font, data + offset_coverage, length - offset_coverage, - font->maxp->num_glyphs)) { + maxp->num_glyphs)) { return OTS_FAILURE_MSG("Failed to parse coverage table in single adjustment table"); } @@ -268,10 +288,10 @@ bool ParsePairSetTable(const ots::Font *font, if (glyph_id >= num_glyphs) { return OTS_FAILURE_MSG("glyph id %d too high >= %d", glyph_id, num_glyphs); } - if (!ParseValueRecord(font, &subtable, data, length, value_format1)) { + if (!ParseValueRecord(font, &subtable, value_format1)) { return OTS_FAILURE_MSG("Failed to parse value record in format 1 pair set table"); } - if (!ParseValueRecord(font, &subtable, data, length, value_format2)) { + if (!ParseValueRecord(font, &subtable, value_format2)) { return OTS_FAILURE_MSG("Failed to parse value record in format 2 pair set table"); } } @@ -341,34 +361,44 @@ bool ParsePairPosFormat2(const ots::Font *font, return OTS_FAILURE_MSG("Failed to read pair pos format 2 data"); } + size_t value_record1_size = CalcValueRecordSize(value_format1); + size_t value_record2_size = CalcValueRecordSize(value_format2); + size_t value_records_size = size_t(class1_count) * size_t(class2_count) * + (value_record1_size + value_record2_size); + + // Check the validity of class definition offsets. + if (offset_class_def1 < subtable.offset() + value_records_size || + offset_class_def2 < subtable.offset() + value_records_size || + offset_class_def1 >= length || offset_class_def2 >= length) { + return OTS_FAILURE_MSG("Bad ParsePairPosFormat2 class definition offsets %d or %d", offset_class_def1, offset_class_def2); + } + // Check class 1 records. - for (unsigned i = 0; i < class1_count; ++i) { - // Check class 2 records. - for (unsigned j = 0; j < class2_count; ++j) { - if (value_format1 && !ParseValueRecord(font, &subtable, data, length, - value_format1)) { - return OTS_FAILURE_MSG("Failed to parse value record 1 %d and %d", j, i); - } - if (value_format2 && !ParseValueRecord(font, &subtable, data, length, - value_format2)) { - return OTS_FAILURE_MSG("Falied to parse value record 2 %d and %d", j, i); + if (value_record1_size || value_record2_size) { + for (unsigned i = 0; i < class1_count; ++i) { + // Check class 2 records. + for (unsigned j = 0; j < class2_count; ++j) { + if (value_format1 && value_record2_size && + !ParseValueRecord(font, &subtable, value_format1)) { + return OTS_FAILURE_MSG("Failed to parse value record 1 %d and %d", j, i); + } + if (value_format2 && value_record2_size && + !ParseValueRecord(font, &subtable, value_format2)) { + return OTS_FAILURE_MSG("Falied to parse value record 2 %d and %d", j, i); + } } } } // Check class definition tables. - if (offset_class_def1 < subtable.offset() || offset_class_def1 >= length || - offset_class_def2 < subtable.offset() || offset_class_def2 >= length) { - return OTS_FAILURE_MSG("Bad class definition table offsets %d or %d", offset_class_def1, offset_class_def2); - } if (!ots::ParseClassDefTable(font, data + offset_class_def1, length - offset_class_def1, - num_glyphs, kMaxClassDefValue)) { + num_glyphs, ots::kMaxClassDefValue)) { return OTS_FAILURE_MSG("Failed to parse class definition table 1"); } if (!ots::ParseClassDefTable(font, data + offset_class_def2, length - offset_class_def2, - num_glyphs, kMaxClassDefValue)) { + num_glyphs, ots::kMaxClassDefValue)) { return OTS_FAILURE_MSG("Failed to parse class definition table 2"); } @@ -381,6 +411,12 @@ 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; @@ -394,12 +430,12 @@ bool ParsePairAdjustment(const ots::Font *font, const uint8_t *data, if (format == 1) { if (!ParsePairPosFormat1(font, data, length, value_format1, value_format2, - font->maxp->num_glyphs)) { + 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, - font->maxp->num_glyphs)) { + maxp->num_glyphs)) { return OTS_FAILURE_MSG("Failed to parse pair format 2"); } } else { @@ -411,7 +447,7 @@ bool ParsePairAdjustment(const ots::Font *font, const uint8_t *data, } if (!ots::ParseCoverageTable(font, data + offset_coverage, length - offset_coverage, - font->maxp->num_glyphs)) { + maxp->num_glyphs)) { return OTS_FAILURE_MSG("Failed to parse coverage table"); } @@ -424,6 +460,12 @@ 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; @@ -478,7 +520,7 @@ bool ParseCursiveAttachment(const ots::Font *font, const uint8_t *data, } if (!ots::ParseCoverageTable(font, data + offset_coverage, length - offset_coverage, - font->maxp->num_glyphs)) { + maxp->num_glyphs)) { return OTS_FAILURE_MSG("Failed to parse coverage table in cursive attachment"); } @@ -552,6 +594,12 @@ bool ParseMarkToAttachmentSubtables(const ots::Font *font, const GPOS_TYPE type) { 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_coverage1 = 0; uint16_t offset_coverage2 = 0; @@ -580,7 +628,7 @@ bool ParseMarkToAttachmentSubtables(const ots::Font *font, } if (!ots::ParseCoverageTable(font, data + offset_coverage1, length - offset_coverage1, - font->maxp->num_glyphs)) { + maxp->num_glyphs)) { return OTS_FAILURE_MSG("Failed to parse converge 1 table"); } if (offset_coverage2 < header_end || offset_coverage2 >= length) { @@ -588,7 +636,7 @@ bool ParseMarkToAttachmentSubtables(const ots::Font *font, } if (!ots::ParseCoverageTable(font, data + offset_coverage2, length - offset_coverage2, - font->maxp->num_glyphs)) { + maxp->num_glyphs)) { return OTS_FAILURE_MSG("Failed to parse coverage table 2"); } @@ -652,17 +700,37 @@ bool ParseMarkToMarkAttachment(const ots::Font *font, // Contextual Positioning Subtables bool ParseContextPositioning(const ots::Font *font, const uint8_t *data, const size_t length) { - return ots::ParseContextSubtable(font, data, length, font->maxp->num_glyphs, - font->gpos->num_lookups); + 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); } // 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, - font->maxp->num_glyphs, - font->gpos->num_lookups); + maxp->num_glyphs, + gpos->num_lookups); } // Lookup Type 9: @@ -677,139 +745,96 @@ bool ParseExtensionPositioning(const ots::Font *font, namespace ots { -// As far as I checked, following fonts contain invalid GPOS table and -// OTS will drop their GPOS table. -// -// # invalid delta format in device table -// samanata.ttf -// -// # bad size range in device table -// Sarai_07.ttf -// -// # bad offset to PairSetTable -// chandas1-2.ttf -// -// # bad offset to FeatureTable -// glrso12.ttf -// gllr12.ttf -// glbo12.ttf -// glb12.ttf -// glro12.ttf -// glbso12.ttf -// glrc12.ttf -// glrsc12.ttf -// glbs12.ttf -// glrs12.ttf -// glr12.ttf -// -// # ScriptRecords aren't sorted by tag -// Garogier_unhinted.otf -// -// # bad start coverage index in CoverageFormat2 -// AndBasR.ttf -// CharisSILB.ttf -// CharisSILBI.ttf -// CharisSILI.ttf -// CharisSILR.ttf -// DoulosSILR.ttf -// GenBasBI.ttf -// GenBasI.ttf -// GenBkBasI.ttf -// GenBkBasB.ttf -// GenBkBasR.ttf -// Padauk-Bold.ttf -// Padauk.ttf -// -// # Contour point indexes aren't sorted -// Arial Unicode.ttf - -bool ots_gpos_parse(Font *font, const uint8_t *data, size_t length) { - // Parsing GPOS table requires num_glyphs which is contained in maxp table. - if (!font->maxp) { - return OTS_FAILURE_MSG("missing maxp table needed in GPOS"); - } - +bool OpenTypeGPOS::Parse(const uint8_t *data, size_t length) { + Font *font = GetFont(); Buffer table(data, length); - OpenTypeGPOS *gpos = new OpenTypeGPOS; - font->gpos = gpos; - - uint32_t version = 0; + 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; - if (!table.ReadU32(&version) || + 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 OTS_FAILURE_MSG("Incomplete table"); + return Error("Incomplete table"); } - if (version != 0x00010000) { - return OTS_FAILURE_MSG("Bad version"); + 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 < kGposHeaderSize || offset_lookup_list >= length) { - return OTS_FAILURE_MSG("Bad lookup list offset in table header"); + 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, - &gpos->num_lookups)) { - return OTS_FAILURE_MSG("Failed to parse lookup list table"); + &this->num_lookups)) { + return Error("Failed to parse lookup list table"); } } uint16_t num_features = 0; if (offset_feature_list) { - if (offset_feature_list < kGposHeaderSize || offset_feature_list >= length) { - return OTS_FAILURE_MSG("Bad feature list offset in table header"); + 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, gpos->num_lookups, + length - offset_feature_list, this->num_lookups, &num_features)) { - return OTS_FAILURE_MSG("Failed to parse feature list table"); + return Error("Failed to parse feature list table"); } } if (offset_script_list) { - if (offset_script_list < kGposHeaderSize || offset_script_list >= length) { - return OTS_FAILURE_MSG("Bad script list offset in table header"); + 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 OTS_FAILURE_MSG("Failed to parse script list table"); + return Error("Failed to parse script list table"); } } - gpos->data = data; - gpos->length = length; - return true; -} - -bool ots_gpos_should_serialise(Font *font) { - return font->gpos != NULL && font->gpos->data != NULL; -} + if (offset_feature_variations) { + if (offset_feature_variations < header_size || offset_feature_variations >= length) { + return Error("Bad feature variations offset in table header"); + } -bool ots_gpos_serialise(OTSStream *out, Font *font) { - if (!out->Write(font->gpos->data, font->gpos->length)) { - return OTS_FAILURE_MSG("Failed to write GPOS table"); + 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; } -void ots_gpos_reuse(Font *font, Font *other) { - font->gpos = other->gpos; - font->gpos_reused = true; -} +bool OpenTypeGPOS::Serialize(OTSStream *out) { + if (!out->Write(this->m_data, this->m_length)) { + return Error("Failed to write GPOS table"); + } -void ots_gpos_free(Font *font) { - delete font->gpos; + return true; } } // namespace ots |