/gfx/ots/src/gsub.cc
C++ | 683 lines | 520 code | 87 blank | 76 comment | 118 complexity | 23d1b52b7dbd9f09711d4ee0974a7a72 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, LGPL-3.0, AGPL-1.0, MIT, LGPL-2.1, 0BSD, BSD-2-Clause, MPL-2.0, BSD-3-Clause, Apache-2.0, GPL-2.0, JSON
- // Copyright (c) 2011 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "gsub.h"
- #include <limits>
- #include <vector>
- #include "gdef.h"
- #include "gpos.h"
- #include "layout.h"
- #include "maxp.h"
- // GSUB - The Glyph Substitution Table
- // http://www.microsoft.com/typography/otspec/gsub.htm
- #define TABLE_NAME "GSUB"
- namespace {
- // The GSUB header size
- const size_t kGsubHeaderSize = 8;
- 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::OpenTypeFile *file,
- const uint8_t *data, const size_t length);
- bool ParseMutipleSubstitution(const ots::OpenTypeFile *file,
- const uint8_t *data, const size_t length);
- bool ParseAlternateSubstitution(const ots::OpenTypeFile *file,
- const uint8_t *data, const size_t length);
- bool ParseLigatureSubstitution(const ots::OpenTypeFile *file,
- const uint8_t *data, const size_t length);
- bool ParseContextSubstitution(const ots::OpenTypeFile *file,
- const uint8_t *data, const size_t length);
- bool ParseChainingContextSubstitution(const ots::OpenTypeFile *file,
- const uint8_t *data,
- const size_t length);
- bool ParseExtensionSubstitution(const ots::OpenTypeFile *file,
- const uint8_t *data, const size_t length);
- bool ParseReverseChainingContextSingleSubstitution(
- const ots::OpenTypeFile *file, 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}
- };
- // TODO(bashi): Port Chromium's arraysize macro and use it instead of sizeof().
- const ots::LookupSubtableParser kGsubLookupSubtableParser = {
- sizeof(kGsubTypeParsers) / sizeof(kGsubTypeParsers[0]),
- GSUB_TYPE_EXTENSION_SUBSTITUTION, kGsubTypeParsers
- };
- // Lookup Type 1:
- // Single Substitution Subtable
- bool ParseSingleSubstitution(const ots::OpenTypeFile *file,
- 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();
- }
- const uint16_t num_glyphs = file->maxp->num_glyphs;
- if (format == 1) {
- // Parse SingleSubstFormat1
- int16_t delta_glyph_id = 0;
- if (!subtable.ReadS16(&delta_glyph_id)) {
- return OTS_FAILURE();
- }
- if (std::abs(delta_glyph_id) >= num_glyphs) {
- return OTS_FAILURE();
- }
- } else if (format == 2) {
- // Parse SingleSubstFormat2
- uint16_t glyph_count = 0;
- if (!subtable.ReadU16(&glyph_count)) {
- return OTS_FAILURE();
- }
- if (glyph_count > num_glyphs) {
- return OTS_FAILURE();
- }
- for (unsigned i = 0; i < glyph_count; ++i) {
- uint16_t substitute = 0;
- if (!subtable.ReadU16(&substitute)) {
- return OTS_FAILURE();
- }
- if (substitute >= num_glyphs) {
- OTS_WARNING("too large substitute: %u", substitute);
- return OTS_FAILURE();
- }
- }
- } else {
- return OTS_FAILURE();
- }
- if (offset_coverage < subtable.offset() || offset_coverage >= length) {
- return OTS_FAILURE();
- }
- if (!ots::ParseCoverageTable(data + offset_coverage,
- length - offset_coverage, num_glyphs)) {
- return OTS_FAILURE();
- }
- return true;
- }
- bool ParseSequenceTable(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();
- }
- if (glyph_count > num_glyphs) {
- return OTS_FAILURE();
- }
- for (unsigned i = 0; i < glyph_count; ++i) {
- uint16_t substitute = 0;
- if (!subtable.ReadU16(&substitute)) {
- return OTS_FAILURE();
- }
- if (substitute >= num_glyphs) {
- return OTS_FAILURE();
- }
- }
- return true;
- }
- // Lookup Type 2:
- // Multiple Substitution Subtable
- bool ParseMutipleSubstitution(const ots::OpenTypeFile *file,
- 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();
- }
- if (format != 1) {
- return OTS_FAILURE();
- }
- const uint16_t num_glyphs = file->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();
- }
- for (unsigned i = 0; i < sequence_count; ++i) {
- uint16_t offset_sequence = 0;
- if (!subtable.ReadU16(&offset_sequence)) {
- return OTS_FAILURE();
- }
- if (offset_sequence < sequence_end || offset_sequence >= length) {
- return OTS_FAILURE();
- }
- if (!ParseSequenceTable(data + offset_sequence, length - offset_sequence,
- num_glyphs)) {
- return OTS_FAILURE();
- }
- }
- if (offset_coverage < sequence_end || offset_coverage >= length) {
- return OTS_FAILURE();
- }
- if (!ots::ParseCoverageTable(data + offset_coverage,
- length - offset_coverage, num_glyphs)) {
- return OTS_FAILURE();
- }
- return true;
- }
- bool ParseAlternateSetTable(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();
- }
- if (glyph_count > num_glyphs) {
- return OTS_FAILURE();
- }
- for (unsigned i = 0; i < glyph_count; ++i) {
- uint16_t alternate = 0;
- if (!subtable.ReadU16(&alternate)) {
- return OTS_FAILURE();
- }
- if (alternate >= num_glyphs) {
- OTS_WARNING("too large alternate: %u", alternate);
- return OTS_FAILURE();
- }
- }
- return true;
- }
- // Lookup Type 3:
- // Alternate Substitution Subtable
- bool ParseAlternateSubstitution(const ots::OpenTypeFile *file,
- 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();
- }
- if (format != 1) {
- return OTS_FAILURE();
- }
- const uint16_t num_glyphs = file->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();
- }
- for (unsigned i = 0; i < alternate_set_count; ++i) {
- uint16_t offset_alternate_set = 0;
- if (!subtable.ReadU16(&offset_alternate_set)) {
- return OTS_FAILURE();
- }
- if (offset_alternate_set < alternate_set_end ||
- offset_alternate_set >= length) {
- return OTS_FAILURE();
- }
- if (!ParseAlternateSetTable(data + offset_alternate_set,
- length - offset_alternate_set,
- num_glyphs)) {
- return OTS_FAILURE();
- }
- }
- if (offset_coverage < alternate_set_end || offset_coverage >= length) {
- return OTS_FAILURE();
- }
- if (!ots::ParseCoverageTable(data + offset_coverage,
- length - offset_coverage, num_glyphs)) {
- return OTS_FAILURE();
- }
- return true;
- }
- bool ParseLigatureTable(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;
- if (!subtable.ReadU16(&lig_glyph) ||
- !subtable.ReadU16(&comp_count)) {
- return OTS_FAILURE();
- }
- if (lig_glyph >= num_glyphs) {
- OTS_WARNING("too large lig_glyph: %u", lig_glyph);
- return OTS_FAILURE();
- }
- if (comp_count == 0 || comp_count > num_glyphs) {
- return OTS_FAILURE();
- }
- for (unsigned i = 0; i < comp_count - static_cast<unsigned>(1); ++i) {
- uint16_t component = 0;
- if (!subtable.ReadU16(&component)) {
- return OTS_FAILURE();
- }
- if (component >= num_glyphs) {
- return OTS_FAILURE();
- }
- }
- return true;
- }
- bool ParseLigatureSetTable(const uint8_t *data, const size_t length,
- const uint16_t num_glyphs) {
- ots::Buffer subtable(data, length);
- uint16_t ligature_count = 0;
- if (!subtable.ReadU16(&ligature_count)) {
- return OTS_FAILURE();
- }
- const unsigned ligature_end = static_cast<unsigned>(2) + ligature_count * 2;
- if (ligature_end > std::numeric_limits<uint16_t>::max()) {
- return OTS_FAILURE();
- }
- for (unsigned i = 0; i < ligature_count; ++i) {
- uint16_t offset_ligature = 0;
- if (!subtable.ReadU16(&offset_ligature)) {
- return OTS_FAILURE();
- }
- if (offset_ligature < ligature_end || offset_ligature >= length) {
- return OTS_FAILURE();
- }
- if (!ParseLigatureTable(data + offset_ligature, length - offset_ligature,
- num_glyphs)) {
- return OTS_FAILURE();
- }
- }
- return true;
- }
- // Lookup Type 4:
- // Ligature Substitution Subtable
- bool ParseLigatureSubstitution(const ots::OpenTypeFile *file,
- const uint8_t *data, const size_t length) {
- ots::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();
- }
- if (format != 1) {
- return OTS_FAILURE();
- }
- const uint16_t num_glyphs = file->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();
- }
- for (unsigned i = 0; i < lig_set_count; ++i) {
- uint16_t offset_ligature_set = 0;
- if (!subtable.ReadU16(&offset_ligature_set)) {
- return OTS_FAILURE();
- }
- if (offset_ligature_set < ligature_set_end ||
- offset_ligature_set >= length) {
- return OTS_FAILURE();
- }
- if (!ParseLigatureSetTable(data + offset_ligature_set,
- length - offset_ligature_set, num_glyphs)) {
- return OTS_FAILURE();
- }
- }
- if (offset_coverage < ligature_set_end || offset_coverage >= length) {
- return OTS_FAILURE();
- }
- if (!ots::ParseCoverageTable(data + offset_coverage,
- length - offset_coverage, num_glyphs)) {
- return OTS_FAILURE();
- }
- return true;
- }
- // Lookup Type 5:
- // Contextual Substitution Subtable
- bool ParseContextSubstitution(const ots::OpenTypeFile *file,
- const uint8_t *data, const size_t length) {
- return ots::ParseContextSubtable(data, length, file->maxp->num_glyphs,
- file->gsub->num_lookups);
- }
- // Lookup Type 6:
- // Chaining Contextual Substitution Subtable
- bool ParseChainingContextSubstitution(const ots::OpenTypeFile *file,
- const uint8_t *data,
- const size_t length) {
- return ots::ParseChainingContextSubtable(data, length,
- file->maxp->num_glyphs,
- file->gsub->num_lookups);
- }
- // Lookup Type 7:
- // Extension Substition
- bool ParseExtensionSubstitution(const ots::OpenTypeFile *file,
- const uint8_t *data, const size_t length) {
- return ots::ParseExtensionSubtable(file, data, length,
- &kGsubLookupSubtableParser);
- }
- // Lookup Type 8:
- // Reverse Chaining Contexual Single Substitution Subtable
- bool ParseReverseChainingContextSingleSubstitution(
- const ots::OpenTypeFile *file, 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();
- }
- const uint16_t num_glyphs = file->maxp->num_glyphs;
- uint16_t backtrack_glyph_count = 0;
- if (!subtable.ReadU16(&backtrack_glyph_count)) {
- return OTS_FAILURE();
- }
- if (backtrack_glyph_count > num_glyphs) {
- return OTS_FAILURE();
- }
- 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();
- }
- offsets_backtrack.push_back(offset);
- }
- uint16_t lookahead_glyph_count = 0;
- if (!subtable.ReadU16(&lookahead_glyph_count)) {
- return OTS_FAILURE();
- }
- if (lookahead_glyph_count > num_glyphs) {
- return OTS_FAILURE();
- }
- 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();
- }
- offsets_lookahead.push_back(offset);
- }
- uint16_t glyph_count = 0;
- if (!subtable.ReadU16(&glyph_count)) {
- return OTS_FAILURE();
- }
- if (glyph_count > num_glyphs) {
- return OTS_FAILURE();
- }
- for (unsigned i = 0; i < glyph_count; ++i) {
- uint16_t substitute = 0;
- if (!subtable.ReadU16(&substitute)) {
- return OTS_FAILURE();
- }
- if (substitute >= num_glyphs) {
- return OTS_FAILURE();
- }
- }
- 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();
- }
- if (offset_coverage < substitute_end || offset_coverage >= length) {
- return OTS_FAILURE();
- }
- if (!ots::ParseCoverageTable(data + offset_coverage,
- length - offset_coverage, num_glyphs)) {
- return OTS_FAILURE();
- }
- for (unsigned i = 0; i < backtrack_glyph_count; ++i) {
- if (offsets_backtrack[i] < substitute_end ||
- offsets_backtrack[i] >= length) {
- return OTS_FAILURE();
- }
- if (!ots::ParseCoverageTable(data + offsets_backtrack[i],
- length - offsets_backtrack[i], num_glyphs)) {
- return OTS_FAILURE();
- }
- }
- for (unsigned i = 0; i < lookahead_glyph_count; ++i) {
- if (offsets_lookahead[i] < substitute_end ||
- offsets_lookahead[i] >= length) {
- return OTS_FAILURE();
- }
- if (!ots::ParseCoverageTable(data + offsets_lookahead[i],
- length - offsets_lookahead[i], num_glyphs)) {
- return OTS_FAILURE();
- }
- }
- return true;
- }
- } // namespace
- #define DROP_THIS_TABLE \
- do { \
- file->gsub->data = 0; \
- file->gsub->length = 0; \
- OTS_FAILURE_MSG("OpenType layout data discarded"); \
- } while (0)
- namespace ots {
- // As far as I checked, following fonts contain invalid values in GSUB table.
- // OTS will drop their GSUB table.
- //
- // # too large substitute (value is 0xFFFF)
- // kaiu.ttf
- // mingliub2.ttf
- // mingliub1.ttf
- // mingliub0.ttf
- // GraublauWeb.otf
- // GraublauWebBold.otf
- //
- // # too large alternate (value is 0xFFFF)
- // ManchuFont.ttf
- //
- // # bad offset to lang sys table (NULL offset)
- // DejaVuMonoSansBold.ttf
- // DejaVuMonoSansBoldOblique.ttf
- // DejaVuMonoSansOblique.ttf
- // DejaVuSansMono-BoldOblique.ttf
- // DejaVuSansMono-Oblique.ttf
- // DejaVuSansMono-Bold.ttf
- //
- // # bad start coverage index
- // GenBasBI.ttf
- // GenBasI.ttf
- // AndBasR.ttf
- // GenBkBasI.ttf
- // CharisSILR.ttf
- // CharisSILBI.ttf
- // CharisSILI.ttf
- // CharisSILB.ttf
- // DoulosSILR.ttf
- // CharisSILBI.ttf
- // GenBkBasB.ttf
- // GenBkBasR.ttf
- // GenBkBasBI.ttf
- // GenBasB.ttf
- // GenBasR.ttf
- //
- // # glyph range is overlapping
- // KacstTitleL.ttf
- // KacstDecorative.ttf
- // KacstTitle.ttf
- // KacstArt.ttf
- // KacstPoster.ttf
- // KacstQurn.ttf
- // KacstDigital.ttf
- // KacstBook.ttf
- // KacstFarsi.ttf
- bool ots_gsub_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
- // Parsing gsub table requires |file->maxp->num_glyphs|
- if (!file->maxp) {
- return OTS_FAILURE();
- }
- Buffer table(data, length);
- OpenTypeGSUB *gsub = new OpenTypeGSUB;
- file->gsub = gsub;
- uint32_t version = 0;
- uint16_t offset_script_list = 0;
- uint16_t offset_feature_list = 0;
- uint16_t offset_lookup_list = 0;
- if (!table.ReadU32(&version) ||
- !table.ReadU16(&offset_script_list) ||
- !table.ReadU16(&offset_feature_list) ||
- !table.ReadU16(&offset_lookup_list)) {
- OTS_WARNING("incomplete GSUB table");
- DROP_THIS_TABLE;
- return true;
- }
- if (version != 0x00010000) {
- OTS_WARNING("bad GSUB version");
- DROP_THIS_TABLE;
- return true;
- }
- if ((offset_script_list < kGsubHeaderSize ||
- offset_script_list >= length) ||
- (offset_feature_list < kGsubHeaderSize ||
- offset_feature_list >= length) ||
- (offset_lookup_list < kGsubHeaderSize ||
- offset_lookup_list >= length)) {
- OTS_WARNING("bad offset in GSUB header");
- DROP_THIS_TABLE;
- return true;
- }
- if (!ParseLookupListTable(file, data + offset_lookup_list,
- length - offset_lookup_list,
- &kGsubLookupSubtableParser,
- &gsub->num_lookups)) {
- OTS_WARNING("failed to parse lookup list table");
- DROP_THIS_TABLE;
- return true;
- }
- uint16_t num_features = 0;
- if (!ParseFeatureListTable(data + offset_feature_list,
- length - offset_feature_list, gsub->num_lookups,
- &num_features)) {
- OTS_WARNING("failed to parse feature list table");
- DROP_THIS_TABLE;
- return true;
- }
- if (!ParseScriptListTable(data + offset_script_list,
- length - offset_script_list, num_features)) {
- OTS_WARNING("failed to parse script list table");
- DROP_THIS_TABLE;
- return true;
- }
- gsub->data = data;
- gsub->length = length;
- return true;
- }
- bool ots_gsub_should_serialise(OpenTypeFile *file) {
- const bool needed_tables_dropped =
- (file->gdef && file->gdef->data == NULL) ||
- (file->gpos && file->gpos->data == NULL);
- return file->gsub != NULL && file->gsub->data != NULL
- && !needed_tables_dropped;
- }
- bool ots_gsub_serialise(OTSStream *out, OpenTypeFile *file) {
- if (!out->Write(file->gsub->data, file->gsub->length)) {
- return OTS_FAILURE();
- }
- return true;
- }
- void ots_gsub_free(OpenTypeFile *file) {
- delete file->gsub;
- }
- } // namespace ots