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 |