| Index: src/layout.cc
|
| ===================================================================
|
| --- src/layout.cc (revision 50)
|
| +++ src/layout.cc (working copy)
|
| @@ -4,11 +4,249 @@
|
|
|
| #include "layout.h"
|
|
|
| +#include <limits>
|
| +#include <vector>
|
| +
|
| +#include "gdef.h"
|
| +
|
| // OpenType Layout Common Table Formats
|
| // http://www.microsoft.com/typography/otspec/chapter2.htm
|
|
|
| namespace {
|
|
|
| +// The 'DFLT' tag of script table.
|
| +const uint32_t kScriptTableTagDflt = 0x44464c54;
|
| +// The value which represents there is no required feature index.
|
| +const uint16_t kNoRequiredFeatureIndexDefined = 0xFFFF;
|
| +// The lookup flag bit which indicates existence of MarkFilteringSet.
|
| +const uint16_t kUseMarkFilteringSetBit = 0x0010;
|
| +// The mask for MarkAttachmentType.
|
| +const uint16_t kMarkAttachmentTypeMask = 0xFF00;
|
| +// The maximum type number of format for device tables.
|
| +const uint16_t kMaxDeltaFormatType = 3;
|
| +
|
| +struct ScriptRecord {
|
| + uint32_t tag;
|
| + uint16_t offset;
|
| +};
|
| +
|
| +struct LangSysRecord {
|
| + uint32_t tag;
|
| + uint16_t offset;
|
| +};
|
| +
|
| +struct FeatureRecord {
|
| + uint32_t tag;
|
| + uint16_t offset;
|
| +};
|
| +
|
| +bool ParseLangSysTable(ots::Buffer *subtable, const uint32_t tag,
|
| + const uint16_t num_features) {
|
| + uint16_t offset_lookup_order = 0;
|
| + uint16_t req_feature_index = 0;
|
| + uint16_t feature_count = 0;
|
| + if (!subtable->ReadU16(&offset_lookup_order) ||
|
| + !subtable->ReadU16(&req_feature_index) ||
|
| + !subtable->ReadU16(&feature_count)) {
|
| + return OTS_FAILURE();
|
| + }
|
| + // |offset_lookup_order| is reserved and should be NULL.
|
| + if (offset_lookup_order != 0) {
|
| + return OTS_FAILURE();
|
| + }
|
| + if (req_feature_index != kNoRequiredFeatureIndexDefined &&
|
| + req_feature_index >= num_features) {
|
| + return OTS_FAILURE();
|
| + }
|
| + if (feature_count > num_features) {
|
| + return OTS_FAILURE();
|
| + }
|
| +
|
| + for (unsigned i = 0; i < feature_count; ++i) {
|
| + uint16_t feature_index = 0;
|
| + if (!subtable->ReadU16(&feature_index)) {
|
| + return OTS_FAILURE();
|
| + }
|
| + if (feature_index >= num_features) {
|
| + return OTS_FAILURE();
|
| + }
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +bool ParseScriptTable(const uint8_t *data, const size_t length,
|
| + const uint32_t tag, const uint16_t num_features) {
|
| + ots::Buffer subtable(data, length);
|
| +
|
| + uint16_t offset_default_lang_sys = 0;
|
| + uint16_t lang_sys_count = 0;
|
| + if (!subtable.ReadU16(&offset_default_lang_sys) ||
|
| + !subtable.ReadU16(&lang_sys_count)) {
|
| + return OTS_FAILURE();
|
| + }
|
| +
|
| + // The spec requires a script table for 'DFLT' tag must contain non-NULL
|
| + // |offset_default_lang_sys| and |lang_sys_count| == 0
|
| + if (tag == kScriptTableTagDflt &&
|
| + (offset_default_lang_sys == 0 || lang_sys_count != 0)) {
|
| + OTS_WARNING("DFLT table doesn't satisfy the spec.");
|
| + return OTS_FAILURE();
|
| + }
|
| +
|
| + const unsigned lang_sys_record_end = static_cast<unsigned>(4) +
|
| + lang_sys_count * 6;
|
| + if (lang_sys_record_end > std::numeric_limits<uint16_t>::max()) {
|
| + return OTS_FAILURE();
|
| + }
|
| +
|
| + std::vector<LangSysRecord> lang_sys_records;
|
| + lang_sys_records.resize(lang_sys_count);
|
| + uint32_t last_tag = 0;
|
| + for (unsigned i = 0; i < lang_sys_count; ++i) {
|
| + if (!subtable.ReadU32(&lang_sys_records[i].tag) ||
|
| + !subtable.ReadU16(&lang_sys_records[i].offset)) {
|
| + return OTS_FAILURE();
|
| + }
|
| + // The record array must store the records alphabetically by tag
|
| + if (last_tag != 0 && last_tag > lang_sys_records[i].tag) {
|
| + return OTS_FAILURE();
|
| + }
|
| + if (lang_sys_records[i].offset < lang_sys_record_end ||
|
| + lang_sys_records[i].offset >= length) {
|
| + return OTS_FAILURE();
|
| + }
|
| + last_tag = lang_sys_records[i].tag;
|
| + }
|
| +
|
| + // Check lang sys tables
|
| + for (unsigned i = 0; i < lang_sys_count; ++i) {
|
| + subtable.set_offset(lang_sys_records[i].offset);
|
| + if (!ParseLangSysTable(&subtable, lang_sys_records[i].tag, num_features)) {
|
| + return OTS_FAILURE();
|
| + }
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool ParseFeatureTable(const uint8_t *data, const size_t length,
|
| + const uint16_t num_lookups) {
|
| + ots::Buffer subtable(data, length);
|
| +
|
| + uint16_t offset_feature_params = 0;
|
| + uint16_t lookup_count = 0;
|
| + if (!subtable.ReadU16(&offset_feature_params) ||
|
| + !subtable.ReadU16(&lookup_count)) {
|
| + return OTS_FAILURE();
|
| + }
|
| +
|
| + const unsigned feature_table_end = static_cast<unsigned>(4) +
|
| + num_lookups * 2;
|
| + if (feature_table_end > std::numeric_limits<uint16_t>::max()) {
|
| + return OTS_FAILURE();
|
| + }
|
| + // |offset_feature_params| is generally set to NULL.
|
| + if (offset_feature_params != 0 &&
|
| + (offset_feature_params < feature_table_end ||
|
| + offset_feature_params >= length)) {
|
| + return OTS_FAILURE();
|
| + }
|
| +
|
| + for (unsigned i = 0; i < lookup_count; ++i) {
|
| + uint16_t lookup_index = 0;
|
| + if (!subtable.ReadU16(&lookup_index)) {
|
| + return OTS_FAILURE();
|
| + }
|
| + // lookup index starts with 0.
|
| + if (lookup_index >= num_lookups) {
|
| + return OTS_FAILURE();
|
| + }
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +bool LookupTypeParserLess(const ots::LookupTypeParser& parser,
|
| + const uint16_t type) {
|
| + return parser.type < type;
|
| +}
|
| +
|
| +bool ParseLookupTable(ots::OpenTypeFile *file, const uint8_t *data,
|
| + const size_t length, const size_t num_types,
|
| + const ots::LookupTypeParser* parsers) {
|
| + 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();
|
| + }
|
| +
|
| + if (lookup_type == 0 || lookup_type > num_types) {
|
| + return OTS_FAILURE();
|
| + }
|
| +
|
| + // Check lookup flags.
|
| + if ((lookup_flag & kMarkAttachmentTypeMask) &&
|
| + (!file->gdef || !file->gdef->has_mark_attachment_class_def)) {
|
| + return OTS_FAILURE();
|
| + }
|
| + bool use_mark_filtering_set = false;
|
| + if (lookup_flag & kUseMarkFilteringSetBit) {
|
| + if (!file->gdef || !file->gdef->has_mark_glyph_sets_def) {
|
| + return OTS_FAILURE();
|
| + }
|
| + use_mark_filtering_set = true;
|
| + }
|
| +
|
| + 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 =
|
| + static_cast<unsigned>(use_mark_filtering_set ? 8 : 6) +
|
| + subtable_count * 2;
|
| + if (lookup_table_end > std::numeric_limits<uint16_t>::max()) {
|
| + return OTS_FAILURE();
|
| + }
|
| + for (unsigned i = 0; i < subtable_count; ++i) {
|
| + if (!subtable.ReadU16(&subtables[i])) {
|
| + return OTS_FAILURE();
|
| + }
|
| + if (subtables[i] < lookup_table_end || subtables[i] >= length) {
|
| + return OTS_FAILURE();
|
| + }
|
| + }
|
| +
|
| + if (use_mark_filtering_set) {
|
| + uint16_t mark_filtering_set = 0;
|
| + if (!subtable.ReadU16(&mark_filtering_set)) {
|
| + return OTS_FAILURE();
|
| + }
|
| + if (file->gdef->num_mark_glyph_sets == 0 ||
|
| + mark_filtering_set >= file->gdef->num_mark_glyph_sets) {
|
| + return OTS_FAILURE();
|
| + }
|
| + }
|
| +
|
| + // Parse lookup subtables for this lookup type.
|
| + for (unsigned i = 0; i < subtable_count; ++i) {
|
| + const ots::LookupTypeParser *parser =
|
| + std::lower_bound(parsers, parsers + num_types, lookup_type,
|
| + LookupTypeParserLess);
|
| + if (parser == parsers + num_types || parser->type != lookup_type ||
|
| + !parser->parse) {
|
| + return OTS_FAILURE();
|
| + }
|
| + if (!parser->parse(file, data + subtables[i], length - subtables[i])) {
|
| + return OTS_FAILURE();
|
| + }
|
| + }
|
| + return true;
|
| +}
|
| +
|
| bool ParseClassDefFormat1(const uint8_t *data, size_t length,
|
| const uint16_t num_glyphs,
|
| const uint16_t num_classes) {
|
| @@ -87,6 +325,7 @@
|
| OTS_WARNING("bad class value: %u", class_value);
|
| return OTS_FAILURE();
|
| }
|
| + last_end = end;
|
| }
|
|
|
| return true;
|
| @@ -136,11 +375,12 @@
|
| if (!subtable.ReadU16(&range_count)) {
|
| return OTS_FAILURE();
|
| }
|
| - if (range_count >= num_glyphs) {
|
| + if (range_count > num_glyphs) {
|
| OTS_WARNING("bad range count: %u", range_count);
|
| return OTS_FAILURE();
|
| }
|
| uint16_t last_end = 0;
|
| + uint16_t last_start_coverage_index = 0;
|
| for (unsigned i = 0; i < range_count; ++i) {
|
| uint16_t start = 0;
|
| uint16_t end = 0;
|
| @@ -154,6 +394,12 @@
|
| OTS_WARNING("glyph range is overlapping.");
|
| return OTS_FAILURE();
|
| }
|
| + if (start_coverage_index != last_start_coverage_index) {
|
| + OTS_WARNING("bad start coverage index.");
|
| + return OTS_FAILURE();
|
| + }
|
| + last_end = end;
|
| + last_start_coverage_index += end - start + 1;
|
| }
|
|
|
| return true;
|
| @@ -163,6 +409,138 @@
|
|
|
| namespace ots {
|
|
|
| +// Parsing ScriptListTable requires number of features so we need to
|
| +// parse FeatureListTable before calling this function.
|
| +bool ParseScriptListTable(const uint8_t *data, const size_t length,
|
| + const uint16_t num_features) {
|
| + Buffer subtable(data, length);
|
| +
|
| + uint16_t script_count = 0;
|
| + if (!subtable.ReadU16(&script_count)) {
|
| + return OTS_FAILURE();
|
| + }
|
| +
|
| + const unsigned script_record_end = static_cast<unsigned>(2) +
|
| + script_count * 6;
|
| + if (script_record_end > std::numeric_limits<uint16_t>::max()) {
|
| + return OTS_FAILURE();
|
| + }
|
| + std::vector<ScriptRecord> script_list;
|
| + script_list.reserve(script_count);
|
| + uint32_t last_tag = 0;
|
| + for (unsigned i = 0; i < script_count; ++i) {
|
| + if (!subtable.ReadU32(&script_list[i].tag) ||
|
| + !subtable.ReadU16(&script_list[i].offset)) {
|
| + return OTS_FAILURE();
|
| + }
|
| + // Script tags should be arranged alphabetically by tag
|
| + if (last_tag != 0 && last_tag > script_list[i].tag) {
|
| + return OTS_FAILURE();
|
| + }
|
| + last_tag = script_list[i].tag;
|
| + if (script_list[i].offset < script_record_end ||
|
| + script_list[i].offset >= length) {
|
| + return OTS_FAILURE();
|
| + }
|
| + }
|
| +
|
| + // Check script records.
|
| + for (unsigned i = 0; i < script_count; ++i) {
|
| + if (!ParseScriptTable(data + script_list[i].offset,
|
| + length - script_list[i].offset,
|
| + script_list[i].tag, num_features)) {
|
| + return OTS_FAILURE();
|
| + }
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +// Parsing FeatureListTable requires number of lookups so we need to parse
|
| +// LookupListTable before calling this function.
|
| +bool ParseFeatureListTable(const uint8_t *data, const size_t length,
|
| + const uint16_t num_lookups,
|
| + uint16_t* num_features) {
|
| + Buffer subtable(data, length);
|
| +
|
| + uint16_t feature_count = 0;
|
| + if (!subtable.ReadU16(&feature_count)) {
|
| + return OTS_FAILURE();
|
| + }
|
| +
|
| + std::vector<FeatureRecord> feature_records;
|
| + feature_records.resize(feature_count);
|
| + const unsigned feature_record_end = static_cast<unsigned>(2) +
|
| + feature_count * 6;
|
| + if (feature_record_end > std::numeric_limits<uint16_t>::max()) {
|
| + return OTS_FAILURE();
|
| + }
|
| + 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();
|
| + }
|
| + // Feature record array should be arranged alphabetically by tag
|
| + if (last_tag != 0 && last_tag > feature_records[i].tag) {
|
| + return OTS_FAILURE();
|
| + }
|
| + last_tag = feature_records[i].tag;
|
| + if (feature_records[i].offset < feature_record_end ||
|
| + feature_records[i].offset >= length) {
|
| + return OTS_FAILURE();
|
| + }
|
| + }
|
| +
|
| + for (unsigned i = 0; i < feature_count; ++i) {
|
| + if (!ParseFeatureTable(data + feature_records[i].offset,
|
| + length - feature_records[i].offset, num_lookups)) {
|
| + return OTS_FAILURE();
|
| + }
|
| + }
|
| + *num_features = feature_count;
|
| + 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(OpenTypeFile *file, const uint8_t *data,
|
| + const size_t length, const size_t num_types,
|
| + const LookupTypeParser* parsers,
|
| + uint16_t *num_lookups) {
|
| + Buffer subtable(data, length);
|
| +
|
| + if (!subtable.ReadU16(num_lookups)) {
|
| + return OTS_FAILURE();
|
| + }
|
| +
|
| + std::vector<uint16_t> lookups;
|
| + lookups.reserve(*num_lookups);
|
| + const unsigned lookup_end = static_cast<unsigned>(2) +
|
| + (*num_lookups) * 2;
|
| + if (lookup_end > std::numeric_limits<uint16_t>::max()) {
|
| + return OTS_FAILURE();
|
| + }
|
| + for (unsigned i = 0; i < *num_lookups; ++i) {
|
| + if (!subtable.ReadU16(&lookups[i])) {
|
| + return OTS_FAILURE();
|
| + }
|
| + if (lookups[i] < lookup_end || lookups[i] >= length) {
|
| + return OTS_FAILURE();
|
| + }
|
| + }
|
| +
|
| + for (unsigned i = 0; i < *num_lookups; ++i) {
|
| + if (!ParseLookupTable(file, data + lookups[i], length - lookups[i],
|
| + num_types, parsers)) {
|
| + return OTS_FAILURE();
|
| + }
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| bool ParseClassDefTable(const uint8_t *data, size_t length,
|
| const uint16_t num_glyphs,
|
| const uint16_t num_classes) {
|
| @@ -198,5 +576,36 @@
|
| return OTS_FAILURE();
|
| }
|
|
|
| +bool ParseDeviceTable(const uint8_t *data, size_t length) {
|
| + Buffer subtable(data, length);
|
| +
|
| + uint16_t start_size = 0;
|
| + uint16_t end_size = 0;
|
| + uint16_t delta_format = 0;
|
| + if (!subtable.ReadU16(&start_size) ||
|
| + !subtable.ReadU16(&end_size) ||
|
| + !subtable.ReadU16(&delta_format)) {
|
| + return OTS_FAILURE();
|
| + }
|
| + if (start_size > end_size) {
|
| + OTS_WARNING("bad size range: %u > %u", start_size, end_size);
|
| + return OTS_FAILURE();
|
| + }
|
| + if (delta_format == 0 || delta_format > kMaxDeltaFormatType) {
|
| + OTS_WARNING("bad delta format: %u", delta_format);
|
| + return OTS_FAILURE();
|
| + }
|
| + // The number of delta values per uint16. The device table should contain
|
| + // at least |num_units| * 2 bytes compressed data.
|
| + const unsigned num_units = (end_size - start_size) /
|
| + (1 << (4 - delta_format)) + 1;
|
| + // Just skip |num_units| * 2 bytes since the compressed data could take
|
| + // arbitrary values.
|
| + if (!subtable.Skip(num_units * 2)) {
|
| + return OTS_FAILURE();
|
| + }
|
| + return true;
|
| +}
|
| +
|
| } // namespace ots
|
|
|
|
|