diff options
Diffstat (limited to 'gfx/ots/src/layout.cc')
-rw-r--r-- | gfx/ots/src/layout.cc | 249 |
1 files changed, 202 insertions, 47 deletions
diff --git a/gfx/ots/src/layout.cc b/gfx/ots/src/layout.cc index 1d87b53d4..8e99f573d 100644 --- a/gfx/ots/src/layout.cc +++ b/gfx/ots/src/layout.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. @@ -7,6 +7,7 @@ #include <limits> #include <vector> +#include "fvar.h" #include "gdef.h" // OpenType Layout Common Table Formats @@ -22,14 +23,11 @@ const uint32_t kScriptTableTagDflt = 0x44464c54; const uint16_t kNoRequiredFeatureIndexDefined = 0xFFFF; // The lookup flag bit which indicates existence of MarkFilteringSet. const uint16_t kUseMarkFilteringSetBit = 0x0010; -// The lookup flags which require GDEF table. -const uint16_t kGdefRequiredFlags = 0x0002 | 0x0004 | 0x0008; -// The mask for MarkAttachmentType. -const uint16_t kMarkAttachmentTypeMask = 0xFF00; // The maximum type number of format for device tables. const uint16_t kMaxDeltaFormatType = 3; -// The maximum number of class value. -const uint16_t kMaxClassDefValue = 0xFFFF; +// In variation fonts, Device Tables are replaced by VariationIndex tables, +// indicated by this flag in the deltaFormat field. +const uint16_t kVariationIndex = 0x8000; struct ScriptRecord { uint32_t tag; @@ -94,15 +92,12 @@ bool ParseScriptTable(const ots::Font *font, } // The spec requires a script table for 'DFLT' tag must contain non-NULL - // |offset_default_lang_sys| and |lang_sys_count| == 0 + // |offset_default_lang_sys|. // https://www.microsoft.com/typography/otspec/chapter2.htm if (tag == kScriptTableTagDflt) { if (offset_default_lang_sys == 0) { return OTS_FAILURE_MSG("DFLT script doesn't satisfy the spec. DefaultLangSys is NULL"); } - if (lang_sys_count != 0) { - return OTS_FAILURE_MSG("DFLT script doesn't satisfy the spec. LangSysCount is not zero: %d", lang_sys_count); - } } const unsigned lang_sys_record_end = @@ -197,27 +192,7 @@ bool ParseLookupTable(ots::Font *font, const uint8_t *data, return OTS_FAILURE_MSG("Bad lookup type %d", lookup_type); } - // Check lookup flags. - if ((lookup_flag & kGdefRequiredFlags) && - (!font->gdef || !font->gdef->has_glyph_class_def)) { - return OTS_FAILURE_MSG("Lookup flags require GDEF table, " - "but none was found: %d", lookup_flag); - } - if ((lookup_flag & kMarkAttachmentTypeMask) && - (!font->gdef || !font->gdef->has_mark_attachment_class_def)) { - return OTS_FAILURE_MSG("Lookup flags ask for mark attachment, " - "but there is no GDEF table or it has no " - "mark attachment classes: %d", lookup_flag); - } - bool use_mark_filtering_set = false; - if (lookup_flag & kUseMarkFilteringSetBit) { - if (!font->gdef || !font->gdef->has_mark_glyph_sets_def) { - return OTS_FAILURE_MSG("Lookup flags ask for mark filtering, " - "but there is no GDEF table or it has no " - "mark filtering sets: %d", lookup_flag); - } - use_mark_filtering_set = true; - } + bool use_mark_filtering_set = lookup_flag & kUseMarkFilteringSetBit; std::vector<uint16_t> subtables; subtables.reserve(subtable_count); @@ -248,8 +223,12 @@ bool ParseLookupTable(ots::Font *font, const uint8_t *data, if (!subtable.ReadU16(&mark_filtering_set)) { return OTS_FAILURE_MSG("Failed to read mark filtering set"); } - if (font->gdef->num_mark_glyph_sets == 0 || - mark_filtering_set >= font->gdef->num_mark_glyph_sets) { + + 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); } } @@ -311,15 +290,15 @@ bool ParseClassDefFormat2(const ots::Font *font, // Skip format field. if (!subtable.Skip(2)) { - return OTS_FAILURE_MSG("Failed to skip format of class defintion header"); + return OTS_FAILURE_MSG("Failed to read class definition format"); } uint16_t range_count = 0; if (!subtable.ReadU16(&range_count)) { - return OTS_FAILURE_MSG("Failed to read range count in class definition"); + return OTS_FAILURE_MSG("Failed to read classRangeCount"); } if (range_count > num_glyphs) { - return OTS_FAILURE_MSG("bad range count: %u", range_count); + return OTS_FAILURE_MSG("classRangeCount > glyph count: %u > %u", range_count, num_glyphs); } uint16_t last_end = 0; @@ -330,13 +309,16 @@ bool ParseClassDefFormat2(const ots::Font *font, if (!subtable.ReadU16(&start) || !subtable.ReadU16(&end) || !subtable.ReadU16(&class_value)) { - return OTS_FAILURE_MSG("Failed to read class definition reange %d", i); + return OTS_FAILURE_MSG("Failed to read ClassRangeRecord %d", i); + } + if (start > end) { + return OTS_FAILURE_MSG("ClassRangeRecord %d, start > end: %u > %u", i, start, end); } - if (start > end || (last_end && start <= last_end)) { - return OTS_FAILURE_MSG("glyph range is overlapping.in range %d", i); + if (last_end && start <= last_end) { + return OTS_FAILURE_MSG("ClassRangeRecord %d start overlaps with end of the previous one: %u <= %u", i, start, last_end); } if (class_value > num_classes) { - return OTS_FAILURE_MSG("bad class value: %u", class_value); + return OTS_FAILURE_MSG("ClassRangeRecord %d class > number of classes: %u > %u", i, class_value, num_classes); } last_end = end; } @@ -661,7 +643,7 @@ bool ParseContextFormat2(const ots::Font *font, } if (!ots::ParseClassDefTable(font, data + offset_class_def, length - offset_class_def, - num_glyphs, kMaxClassDefValue)) { + num_glyphs, ots::kMaxClassDefValue)) { return OTS_FAILURE_MSG("Failed to parse class definition table in context format 2"); } @@ -1014,7 +996,7 @@ bool ParseChainContextFormat2(const ots::Font *font, } if (!ots::ParseClassDefTable(font, data + offset_backtrack_class_def, length - offset_backtrack_class_def, - num_glyphs, kMaxClassDefValue)) { + num_glyphs, ots::kMaxClassDefValue)) { return OTS_FAILURE_MSG("Failed to parse backtrack class defn table in chain context format 2"); } } @@ -1025,7 +1007,7 @@ bool ParseChainContextFormat2(const ots::Font *font, } if (!ots::ParseClassDefTable(font, data + offset_input_class_def, length - offset_input_class_def, - num_glyphs, kMaxClassDefValue)) { + num_glyphs, ots::kMaxClassDefValue)) { return OTS_FAILURE_MSG("Failed to parse input class defn in chain context format 2"); } @@ -1036,7 +1018,7 @@ bool ParseChainContextFormat2(const ots::Font *font, } if (!ots::ParseClassDefTable(font, data + offset_lookahead_class_def, length - offset_lookahead_class_def, - num_glyphs, kMaxClassDefValue)) { + num_glyphs, ots::kMaxClassDefValue)) { return OTS_FAILURE_MSG("Failed to parse lookahead class defn in chain context format 2"); } } @@ -1400,11 +1382,17 @@ bool ParseDeviceTable(const ots::Font *font, !subtable.ReadU16(&delta_format)) { return OTS_FAILURE_MSG("Failed to read device table header"); } + if (delta_format == kVariationIndex) { + // start_size and end_size are replaced by deltaSetOuterIndex + // and deltaSetInnerIndex respectively, but we don't attempt to + // check them here, so nothing more to do. + return true; + } if (start_size > end_size) { - return OTS_FAILURE_MSG("bad size range: %u > %u", start_size, end_size); + return OTS_FAILURE_MSG("Bad device table size range: %u > %u", start_size, end_size); } if (delta_format == 0 || delta_format > kMaxDeltaFormatType) { - return OTS_FAILURE_MSG("bad delta format: %u", delta_format); + return OTS_FAILURE_MSG("Bad device table delta format: 0x%x", delta_format); } // The number of delta values per uint16. The device table should contain // at least |num_units| * 2 bytes compressed data. @@ -1516,6 +1504,173 @@ bool ParseExtensionSubtable(const Font *font, 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"); + } + + 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) { + 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"); + } + + OpenTypeFVAR* fvar = static_cast<OpenTypeFVAR*>(font->GetTypedTable(OTS_TAG_FVAR)); + if (!fvar) { + return OTS_FAILURE_MSG("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"); + } + + if (condition_set_offset) { + if (condition_set_offset < kEndOfFeatureVariationRecords || + condition_set_offset >= length) { + return OTS_FAILURE_MSG("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"); + } + } + + 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"); + } + 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"); + } + } + } + + return true; +} + } // namespace ots #undef TABLE_NAME |