| Index: Source/platform/fonts/opentype/OpenTypeVerticalData.cpp
|
| diff --git a/Source/platform/fonts/opentype/OpenTypeVerticalData.cpp b/Source/platform/fonts/opentype/OpenTypeVerticalData.cpp
|
| index e058c8129af259d0023b079f261d277ae38ffb3f..45ce79b16d4dad4bf21a178563fa70e740edc4bc 100644
|
| --- a/Source/platform/fonts/opentype/OpenTypeVerticalData.cpp
|
| +++ b/Source/platform/fonts/opentype/OpenTypeVerticalData.cpp
|
| @@ -35,12 +35,17 @@
|
| namespace blink {
|
| namespace OpenType {
|
|
|
| +const uint32_t GSUBTag = OT_MAKE_TAG('G', 'S', 'U', 'B');
|
| const uint32_t HheaTag = OT_MAKE_TAG('h', 'h', 'e', 'a');
|
| const uint32_t HmtxTag = OT_MAKE_TAG('h', 'm', 't', 'x');
|
| const uint32_t VheaTag = OT_MAKE_TAG('v', 'h', 'e', 'a');
|
| const uint32_t VmtxTag = OT_MAKE_TAG('v', 'm', 't', 'x');
|
| const uint32_t VORGTag = OT_MAKE_TAG('V', 'O', 'R', 'G');
|
|
|
| +const uint32_t DefaultScriptTag = OT_MAKE_TAG('D', 'F', 'L', 'T');
|
| +
|
| +const uint32_t VertFeatureTag = OT_MAKE_TAG('v', 'e', 'r', 't');
|
| +
|
| #pragma pack(1)
|
|
|
| struct HheaTable {
|
| @@ -104,6 +109,287 @@ struct VORGTable {
|
| size_t requiredSize() const { return sizeof(*this) + sizeof(VertOriginYMetrics) * (numVertOriginYMetrics - 1); }
|
| };
|
|
|
| +struct CoverageTable : TableBase {
|
| + OpenType::UInt16 coverageFormat;
|
| +};
|
| +
|
| +struct Coverage1Table : CoverageTable {
|
| + OpenType::UInt16 glyphCount;
|
| + OpenType::GlyphID glyphArray[1];
|
| +};
|
| +
|
| +struct Coverage2Table : CoverageTable {
|
| + OpenType::UInt16 rangeCount;
|
| + struct RangeRecord {
|
| + OpenType::GlyphID start;
|
| + OpenType::GlyphID end;
|
| + OpenType::UInt16 startCoverageIndex;
|
| + } ranges[1];
|
| +};
|
| +
|
| +struct SubstitutionSubTable : TableBase {
|
| + OpenType::UInt16 substFormat;
|
| + OpenType::Offset coverageOffset;
|
| +
|
| + const CoverageTable* coverage(const SharedBuffer& buffer) const { return validateOffset<CoverageTable>(buffer, coverageOffset); }
|
| +};
|
| +
|
| +struct SingleSubstitution2SubTable : SubstitutionSubTable {
|
| + OpenType::UInt16 glyphCount;
|
| + OpenType::GlyphID substitute[1];
|
| +};
|
| +
|
| +struct LookupTable : TableBase {
|
| + OpenType::UInt16 lookupType;
|
| + OpenType::UInt16 lookupFlag;
|
| + OpenType::UInt16 subTableCount;
|
| + OpenType::Offset subTableOffsets[1];
|
| + // OpenType::UInt16 markFilteringSet; this field comes after variable length, so offset is determined dynamically.
|
| +
|
| + bool getSubstitutions(HashMap<Glyph, Glyph>* map, const SharedBuffer& buffer) const
|
| + {
|
| + uint16_t countSubTable = subTableCount;
|
| + if (!isValidEnd(buffer, &subTableOffsets[countSubTable]))
|
| + return false;
|
| + if (lookupType != 1) // "Single Substitution Subtable" is all what we support
|
| + return false;
|
| + for (uint16_t i = 0; i < countSubTable; ++i) {
|
| + const SubstitutionSubTable* substitution = validateOffset<SubstitutionSubTable>(buffer, subTableOffsets[i]);
|
| + if (!substitution)
|
| + return false;
|
| + const CoverageTable* coverage = substitution->coverage(buffer);
|
| + if (!coverage)
|
| + return false;
|
| + if (substitution->substFormat != 2) // "Single Substitution Format 2" is all what we support
|
| + return false;
|
| + const SingleSubstitution2SubTable* singleSubstitution2 = validatePtr<SingleSubstitution2SubTable>(buffer, substitution);
|
| + if (!singleSubstitution2)
|
| + return false;
|
| + uint16_t countTo = singleSubstitution2->glyphCount;
|
| + if (!isValidEnd(buffer, &singleSubstitution2->substitute[countTo]))
|
| + return false;
|
| + switch (coverage->coverageFormat) {
|
| + case 1: { // Coverage Format 1 (e.g., MS Gothic)
|
| + const Coverage1Table* coverage1 = validatePtr<Coverage1Table>(buffer, coverage);
|
| + if (!coverage1)
|
| + return false;
|
| + uint16_t countFrom = coverage1->glyphCount;
|
| + if (!isValidEnd(buffer, &coverage1->glyphArray[countFrom]) || countTo != countFrom)
|
| + return false;
|
| + for (uint16_t i = 0; i < countTo; ++i)
|
| + map->set(coverage1->glyphArray[i], singleSubstitution2->substitute[i]);
|
| + break;
|
| + }
|
| + case 2: { // Coverage Format 2 (e.g., Adobe Kozuka Gothic)
|
| + const Coverage2Table* coverage2 = validatePtr<Coverage2Table>(buffer, coverage);
|
| + if (!coverage2)
|
| + return false;
|
| + uint16_t countRange = coverage2->rangeCount;
|
| + if (!isValidEnd(buffer, &coverage2->ranges[countRange]))
|
| + return false;
|
| + for (uint16_t i = 0, indexTo = 0; i < countRange; ++i) {
|
| + uint16_t from = coverage2->ranges[i].start;
|
| + uint16_t fromEnd = coverage2->ranges[i].end + 1; // OpenType "end" is inclusive
|
| + if (indexTo + (fromEnd - from) > countTo)
|
| + return false;
|
| + for (; from != fromEnd; ++from, ++indexTo)
|
| + map->set(from, singleSubstitution2->substitute[indexTo]);
|
| + }
|
| + break;
|
| + }
|
| + default:
|
| + return false;
|
| + }
|
| + }
|
| + return true;
|
| + }
|
| +};
|
| +
|
| +struct LookupList : TableBase {
|
| + OpenType::UInt16 lookupCount;
|
| + OpenType::Offset lookupOffsets[1];
|
| +
|
| + const LookupTable* lookup(uint16_t index, const SharedBuffer& buffer) const
|
| + {
|
| + uint16_t count = lookupCount;
|
| + if (index >= count || !isValidEnd(buffer, &lookupOffsets[count]))
|
| + return 0;
|
| + return validateOffset<LookupTable>(buffer, lookupOffsets[index]);
|
| + }
|
| +};
|
| +
|
| +struct FeatureTable : TableBase {
|
| + OpenType::Offset featureParams;
|
| + OpenType::UInt16 lookupCount;
|
| + OpenType::UInt16 lookupListIndex[1];
|
| +
|
| + bool getGlyphSubstitutions(const LookupList* lookups, HashMap<Glyph, Glyph>* map, const SharedBuffer& buffer) const
|
| + {
|
| + uint16_t count = lookupCount;
|
| + if (!isValidEnd(buffer, &lookupListIndex[count]))
|
| + return false;
|
| + for (uint16_t i = 0; i < count; ++i) {
|
| + const LookupTable* lookup = lookups->lookup(lookupListIndex[i], buffer);
|
| + if (!lookup || !lookup->getSubstitutions(map, buffer))
|
| + return false;
|
| + }
|
| + return true;
|
| + }
|
| +};
|
| +
|
| +struct FeatureList : TableBase {
|
| + OpenType::UInt16 featureCount;
|
| + struct FeatureRecord {
|
| + OpenType::Tag featureTag;
|
| + OpenType::Offset featureOffset;
|
| + } features[1];
|
| +
|
| + const FeatureTable* feature(uint16_t index, OpenType::Tag tag, const SharedBuffer& buffer) const
|
| + {
|
| + uint16_t count = featureCount;
|
| + if (index >= count || !isValidEnd(buffer, &features[count]))
|
| + return 0;
|
| + if (features[index].featureTag == tag)
|
| + return validateOffset<FeatureTable>(buffer, features[index].featureOffset);
|
| + return 0;
|
| + }
|
| +
|
| + const FeatureTable* findFeature(OpenType::Tag tag, const SharedBuffer& buffer) const
|
| + {
|
| + for (uint16_t i = 0; i < featureCount; ++i) {
|
| + if (isValidEnd(buffer, &features[i]) && features[i].featureTag == tag)
|
| + return validateOffset<FeatureTable>(buffer, features[i].featureOffset);
|
| + }
|
| + return 0;
|
| + }
|
| +};
|
| +
|
| +struct LangSysTable : TableBase {
|
| + OpenType::Offset lookupOrder;
|
| + OpenType::UInt16 reqFeatureIndex;
|
| + OpenType::UInt16 featureCount;
|
| + OpenType::UInt16 featureIndex[1];
|
| +
|
| + const FeatureTable* feature(OpenType::Tag featureTag, const FeatureList* features, const SharedBuffer& buffer) const
|
| + {
|
| + uint16_t count = featureCount;
|
| + if (!isValidEnd(buffer, &featureIndex[count]))
|
| + return 0;
|
| + for (uint16_t i = 0; i < count; ++i) {
|
| + const FeatureTable* featureTable = features->feature(featureIndex[i], featureTag, buffer);
|
| + if (featureTable)
|
| + return featureTable;
|
| + }
|
| + return 0;
|
| + }
|
| +};
|
| +
|
| +struct ScriptTable : TableBase {
|
| + OpenType::Offset defaultLangSysOffset;
|
| + OpenType::UInt16 langSysCount;
|
| + struct LangSysRecord {
|
| + OpenType::Tag langSysTag;
|
| + OpenType::Offset langSysOffset;
|
| + } langSysRecords[1];
|
| +
|
| + const LangSysTable* defaultLangSys(const SharedBuffer& buffer) const
|
| + {
|
| + uint16_t count = langSysCount;
|
| + if (!isValidEnd(buffer, &langSysRecords[count]))
|
| + return 0;
|
| + uint16_t offset = defaultLangSysOffset;
|
| + if (offset)
|
| + return validateOffset<LangSysTable>(buffer, offset);
|
| + if (count)
|
| + return validateOffset<LangSysTable>(buffer, langSysRecords[0].langSysOffset);
|
| + return 0;
|
| + }
|
| +};
|
| +
|
| +struct ScriptList : TableBase {
|
| + OpenType::UInt16 scriptCount;
|
| + struct ScriptRecord {
|
| + OpenType::Tag scriptTag;
|
| + OpenType::Offset scriptOffset;
|
| + } scripts[1];
|
| +
|
| + const ScriptTable* script(OpenType::Tag tag, const SharedBuffer& buffer) const
|
| + {
|
| + uint16_t count = scriptCount;
|
| + if (!isValidEnd(buffer, &scripts[count]))
|
| + return 0;
|
| + for (uint16_t i = 0; i < count; ++i) {
|
| + if (scripts[i].scriptTag == tag)
|
| + return validateOffset<ScriptTable>(buffer, scripts[i].scriptOffset);
|
| + }
|
| + return 0;
|
| + }
|
| +
|
| + const ScriptTable* defaultScript(const SharedBuffer& buffer) const
|
| + {
|
| + uint16_t count = scriptCount;
|
| + if (!count || !isValidEnd(buffer, &scripts[count]))
|
| + return 0;
|
| + const ScriptTable* scriptOfDefaultTag = script(OpenType::DefaultScriptTag, buffer);
|
| + if (scriptOfDefaultTag)
|
| + return scriptOfDefaultTag;
|
| + return validateOffset<ScriptTable>(buffer, scripts[0].scriptOffset);
|
| + }
|
| +
|
| + const LangSysTable* defaultLangSys(const SharedBuffer& buffer) const
|
| + {
|
| + const ScriptTable* scriptTable = defaultScript(buffer);
|
| + if (!scriptTable)
|
| + return 0;
|
| + return scriptTable->defaultLangSys(buffer);
|
| + }
|
| +};
|
| +
|
| +struct GSUBTable : TableBase {
|
| + OpenType::Fixed version;
|
| + OpenType::Offset scriptListOffset;
|
| + OpenType::Offset featureListOffset;
|
| + OpenType::Offset lookupListOffset;
|
| +
|
| + const ScriptList* scriptList(const SharedBuffer& buffer) const { return validateOffset<ScriptList>(buffer, scriptListOffset); }
|
| + const FeatureList* featureList(const SharedBuffer& buffer) const { return validateOffset<FeatureList>(buffer, featureListOffset); }
|
| + const LookupList* lookupList(const SharedBuffer& buffer) const { return validateOffset<LookupList>(buffer, lookupListOffset); }
|
| +
|
| + const LangSysTable* defaultLangSys(const SharedBuffer& buffer) const
|
| + {
|
| + const ScriptList* scripts = scriptList(buffer);
|
| + if (!scripts)
|
| + return 0;
|
| + return scripts->defaultLangSys(buffer);
|
| + }
|
| +
|
| + const FeatureTable* feature(OpenType::Tag featureTag, const SharedBuffer& buffer) const
|
| + {
|
| + const LangSysTable* langSys = defaultLangSys(buffer);
|
| + const FeatureList* features = featureList(buffer);
|
| + if (!features)
|
| + return 0;
|
| + const FeatureTable* feature = 0;
|
| + if (langSys)
|
| + feature = langSys->feature(featureTag, features, buffer);
|
| + if (!feature) {
|
| + // If the font has no langSys table, or has no default script and the first script doesn't
|
| + // have the requested feature, then use the first matching feature directly.
|
| + feature = features->findFeature(featureTag, buffer);
|
| + }
|
| + return feature;
|
| + }
|
| +
|
| + bool getVerticalGlyphSubstitutions(HashMap<Glyph, Glyph>* map, const SharedBuffer& buffer) const
|
| + {
|
| + const FeatureTable* verticalFeatureTable = feature(OpenType::VertFeatureTag, buffer);
|
| + if (!verticalFeatureTable)
|
| + return false;
|
| + const LookupList* lookups = lookupList(buffer);
|
| + return lookups && verticalFeatureTable->getGlyphSubstitutions(lookups, map, buffer);
|
| + }
|
| +};
|
| +
|
| #pragma pack()
|
|
|
| } // namespace OpenType
|
| @@ -112,6 +398,7 @@ OpenTypeVerticalData::OpenTypeVerticalData(const FontPlatformData& platformData)
|
| : m_defaultVertOriginY(0)
|
| {
|
| loadMetrics(platformData);
|
| + loadVerticalGlyphSubstitutions(platformData);
|
| }
|
|
|
| void OpenTypeVerticalData::loadMetrics(const FontPlatformData& platformData)
|
| @@ -255,4 +542,28 @@ void OpenTypeVerticalData::getVerticalTranslationsForGlyphs(const SimpleFontData
|
| }
|
| }
|
|
|
| +void OpenTypeVerticalData::loadVerticalGlyphSubstitutions(const FontPlatformData& platformData)
|
| +{
|
| + RefPtr<SharedBuffer> buffer = platformData.openTypeTable(OpenType::GSUBTag);
|
| + const OpenType::GSUBTable* gsub = OpenType::validateTable<OpenType::GSUBTable>(buffer);
|
| + if (gsub)
|
| + gsub->getVerticalGlyphSubstitutions(&m_verticalGlyphMap, *buffer.get());
|
| +}
|
| +
|
| +void OpenTypeVerticalData::substituteWithVerticalGlyphs(const SimpleFontData* font, GlyphPage* glyphPage, unsigned offset, unsigned length) const
|
| +{
|
| + const HashMap<Glyph, Glyph>& map = m_verticalGlyphMap;
|
| + if (map.isEmpty())
|
| + return;
|
| +
|
| + for (unsigned index = offset, end = offset + length; index < end; ++index) {
|
| + GlyphData glyphData = glyphPage->glyphDataForIndex(index);
|
| + if (glyphData.glyph && glyphData.fontData == font) {
|
| + Glyph to = map.get(glyphData.glyph);
|
| + if (to)
|
| + glyphPage->setGlyphDataForIndex(index, to, font);
|
| + }
|
| + }
|
| +}
|
| +
|
| } // namespace blink
|
|
|