Home | History | Annotate | Download | only in opentype
      1 /*
      2  * Copyright (C) 2012 Koji Ishii <kojiishi (at) gmail.com>
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  * 1.  Redistributions of source code must retain the above copyright
      8  *     notice, this list of conditions and the following disclaimer.
      9  * 2.  Redistributions in binary form must reproduce the above copyright
     10  *     notice, this list of conditions and the following disclaimer in the
     11  *     documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
     14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     16  * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     17  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     18  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     19  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
     20  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     22  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     23  */
     24 
     25 #include "config.h"
     26 #if ENABLE(OPENTYPE_VERTICAL)
     27 #include "platform/fonts/opentype/OpenTypeVerticalData.h"
     28 
     29 #include "platform/SharedBuffer.h"
     30 #include "platform/fonts/SimpleFontData.h"
     31 #include "platform/fonts/GlyphPage.h"
     32 #include "platform/fonts/opentype/OpenTypeTypes.h"
     33 #include "platform/geometry/FloatRect.h"
     34 #include "wtf/RefPtr.h"
     35 
     36 namespace blink {
     37 namespace OpenType {
     38 
     39 const uint32_t GSUBTag = OT_MAKE_TAG('G', 'S', 'U', 'B');
     40 const uint32_t HheaTag = OT_MAKE_TAG('h', 'h', 'e', 'a');
     41 const uint32_t HmtxTag = OT_MAKE_TAG('h', 'm', 't', 'x');
     42 const uint32_t VheaTag = OT_MAKE_TAG('v', 'h', 'e', 'a');
     43 const uint32_t VmtxTag = OT_MAKE_TAG('v', 'm', 't', 'x');
     44 const uint32_t VORGTag = OT_MAKE_TAG('V', 'O', 'R', 'G');
     45 
     46 const uint32_t DefaultScriptTag = OT_MAKE_TAG('D', 'F', 'L', 'T');
     47 
     48 const uint32_t VertFeatureTag = OT_MAKE_TAG('v', 'e', 'r', 't');
     49 
     50 #pragma pack(1)
     51 
     52 struct HheaTable {
     53     OpenType::Fixed version;
     54     OpenType::Int16 ascender;
     55     OpenType::Int16 descender;
     56     OpenType::Int16 lineGap;
     57     OpenType::Int16 advanceWidthMax;
     58     OpenType::Int16 minLeftSideBearing;
     59     OpenType::Int16 minRightSideBearing;
     60     OpenType::Int16 xMaxExtent;
     61     OpenType::Int16 caretSlopeRise;
     62     OpenType::Int16 caretSlopeRun;
     63     OpenType::Int16 caretOffset;
     64     OpenType::Int16 reserved[4];
     65     OpenType::Int16 metricDataFormat;
     66     OpenType::UInt16 numberOfHMetrics;
     67 };
     68 
     69 struct VheaTable {
     70     OpenType::Fixed version;
     71     OpenType::Int16 ascent;
     72     OpenType::Int16 descent;
     73     OpenType::Int16 lineGap;
     74     OpenType::Int16 advanceHeightMax;
     75     OpenType::Int16 minTopSideBearing;
     76     OpenType::Int16 minBottomSideBearing;
     77     OpenType::Int16 yMaxExtent;
     78     OpenType::Int16 caretSlopeRise;
     79     OpenType::Int16 caretSlopeRun;
     80     OpenType::Int16 caretOffset;
     81     OpenType::Int16 reserved[4];
     82     OpenType::Int16 metricDataFormat;
     83     OpenType::UInt16 numOfLongVerMetrics;
     84 };
     85 
     86 struct HmtxTable {
     87     struct Entry {
     88         OpenType::UInt16 advanceWidth;
     89         OpenType::Int16 lsb;
     90     } entries[1];
     91 };
     92 
     93 struct VmtxTable {
     94     struct Entry {
     95         OpenType::UInt16 advanceHeight;
     96         OpenType::Int16 topSideBearing;
     97     } entries[1];
     98 };
     99 
    100 struct VORGTable {
    101     OpenType::UInt16 majorVersion;
    102     OpenType::UInt16 minorVersion;
    103     OpenType::Int16 defaultVertOriginY;
    104     OpenType::UInt16 numVertOriginYMetrics;
    105     struct VertOriginYMetrics {
    106         OpenType::UInt16 glyphIndex;
    107         OpenType::Int16 vertOriginY;
    108     } vertOriginYMetrics[1];
    109 
    110     size_t requiredSize() const { return sizeof(*this) + sizeof(VertOriginYMetrics) * (numVertOriginYMetrics - 1); }
    111 };
    112 
    113 struct CoverageTable : TableBase {
    114     OpenType::UInt16 coverageFormat;
    115 };
    116 
    117 struct Coverage1Table : CoverageTable {
    118     OpenType::UInt16 glyphCount;
    119     OpenType::GlyphID glyphArray[1];
    120 };
    121 
    122 struct Coverage2Table : CoverageTable {
    123     OpenType::UInt16 rangeCount;
    124     struct RangeRecord {
    125         OpenType::GlyphID start;
    126         OpenType::GlyphID end;
    127         OpenType::UInt16 startCoverageIndex;
    128     } ranges[1];
    129 };
    130 
    131 struct SubstitutionSubTable : TableBase {
    132     OpenType::UInt16 substFormat;
    133     OpenType::Offset coverageOffset;
    134 
    135     const CoverageTable* coverage(const SharedBuffer& buffer) const { return validateOffset<CoverageTable>(buffer, coverageOffset); }
    136 };
    137 
    138 struct SingleSubstitution2SubTable : SubstitutionSubTable {
    139     OpenType::UInt16 glyphCount;
    140     OpenType::GlyphID substitute[1];
    141 };
    142 
    143 struct LookupTable : TableBase {
    144     OpenType::UInt16 lookupType;
    145     OpenType::UInt16 lookupFlag;
    146     OpenType::UInt16 subTableCount;
    147     OpenType::Offset subTableOffsets[1];
    148     // OpenType::UInt16 markFilteringSet; this field comes after variable length, so offset is determined dynamically.
    149 
    150     bool getSubstitutions(HashMap<Glyph, Glyph>* map, const SharedBuffer& buffer) const
    151     {
    152         uint16_t countSubTable = subTableCount;
    153         if (!isValidEnd(buffer, &subTableOffsets[countSubTable]))
    154             return false;
    155         if (lookupType != 1) // "Single Substitution Subtable" is all what we support
    156             return false;
    157         for (uint16_t i = 0; i < countSubTable; ++i) {
    158             const SubstitutionSubTable* substitution = validateOffset<SubstitutionSubTable>(buffer, subTableOffsets[i]);
    159             if (!substitution)
    160                 return false;
    161             const CoverageTable* coverage = substitution->coverage(buffer);
    162             if (!coverage)
    163                 return false;
    164             if (substitution->substFormat != 2) // "Single Substitution Format 2" is all what we support
    165                 return false;
    166             const SingleSubstitution2SubTable* singleSubstitution2 = validatePtr<SingleSubstitution2SubTable>(buffer, substitution);
    167             if (!singleSubstitution2)
    168                 return false;
    169             uint16_t countTo = singleSubstitution2->glyphCount;
    170             if (!isValidEnd(buffer, &singleSubstitution2->substitute[countTo]))
    171                 return false;
    172             switch (coverage->coverageFormat) {
    173             case 1: { // Coverage Format 1 (e.g., MS Gothic)
    174                 const Coverage1Table* coverage1 = validatePtr<Coverage1Table>(buffer, coverage);
    175                 if (!coverage1)
    176                     return false;
    177                 uint16_t countFrom = coverage1->glyphCount;
    178                 if (!isValidEnd(buffer, &coverage1->glyphArray[countFrom]) || countTo != countFrom)
    179                     return false;
    180                 for (uint16_t i = 0; i < countTo; ++i)
    181                     map->set(coverage1->glyphArray[i], singleSubstitution2->substitute[i]);
    182                 break;
    183             }
    184             case 2: { // Coverage Format 2 (e.g., Adobe Kozuka Gothic)
    185                 const Coverage2Table* coverage2 = validatePtr<Coverage2Table>(buffer, coverage);
    186                 if (!coverage2)
    187                     return false;
    188                 uint16_t countRange = coverage2->rangeCount;
    189                 if (!isValidEnd(buffer, &coverage2->ranges[countRange]))
    190                     return false;
    191                 for (uint16_t i = 0, indexTo = 0; i < countRange; ++i) {
    192                     uint16_t from = coverage2->ranges[i].start;
    193                     uint16_t fromEnd = coverage2->ranges[i].end + 1; // OpenType "end" is inclusive
    194                     if (indexTo + (fromEnd - from) > countTo)
    195                         return false;
    196                     for (; from != fromEnd; ++from, ++indexTo)
    197                         map->set(from, singleSubstitution2->substitute[indexTo]);
    198                 }
    199                 break;
    200             }
    201             default:
    202                 return false;
    203             }
    204         }
    205         return true;
    206     }
    207 };
    208 
    209 struct LookupList : TableBase {
    210     OpenType::UInt16 lookupCount;
    211     OpenType::Offset lookupOffsets[1];
    212 
    213     const LookupTable* lookup(uint16_t index, const SharedBuffer& buffer) const
    214     {
    215         uint16_t count = lookupCount;
    216         if (index >= count || !isValidEnd(buffer, &lookupOffsets[count]))
    217             return 0;
    218         return validateOffset<LookupTable>(buffer, lookupOffsets[index]);
    219     }
    220 };
    221 
    222 struct FeatureTable : TableBase {
    223     OpenType::Offset featureParams;
    224     OpenType::UInt16 lookupCount;
    225     OpenType::UInt16 lookupListIndex[1];
    226 
    227     bool getGlyphSubstitutions(const LookupList* lookups, HashMap<Glyph, Glyph>* map, const SharedBuffer& buffer) const
    228     {
    229         uint16_t count = lookupCount;
    230         if (!isValidEnd(buffer, &lookupListIndex[count]))
    231             return false;
    232         for (uint16_t i = 0; i < count; ++i) {
    233             const LookupTable* lookup = lookups->lookup(lookupListIndex[i], buffer);
    234             if (!lookup || !lookup->getSubstitutions(map, buffer))
    235                 return false;
    236         }
    237         return true;
    238     }
    239 };
    240 
    241 struct FeatureList : TableBase {
    242     OpenType::UInt16 featureCount;
    243     struct FeatureRecord {
    244         OpenType::Tag featureTag;
    245         OpenType::Offset featureOffset;
    246     } features[1];
    247 
    248     const FeatureTable* feature(uint16_t index, OpenType::Tag tag, const SharedBuffer& buffer) const
    249     {
    250         uint16_t count = featureCount;
    251         if (index >= count || !isValidEnd(buffer, &features[count]))
    252             return 0;
    253         if (features[index].featureTag == tag)
    254             return validateOffset<FeatureTable>(buffer, features[index].featureOffset);
    255         return 0;
    256     }
    257 
    258     const FeatureTable* findFeature(OpenType::Tag tag, const SharedBuffer& buffer) const
    259     {
    260         for (uint16_t i = 0; i < featureCount; ++i) {
    261             if (isValidEnd(buffer, &features[i]) && features[i].featureTag == tag)
    262                 return validateOffset<FeatureTable>(buffer, features[i].featureOffset);
    263         }
    264         return 0;
    265     }
    266 };
    267 
    268 struct LangSysTable : TableBase {
    269     OpenType::Offset lookupOrder;
    270     OpenType::UInt16 reqFeatureIndex;
    271     OpenType::UInt16 featureCount;
    272     OpenType::UInt16 featureIndex[1];
    273 
    274     const FeatureTable* feature(OpenType::Tag featureTag, const FeatureList* features, const SharedBuffer& buffer) const
    275     {
    276         uint16_t count = featureCount;
    277         if (!isValidEnd(buffer, &featureIndex[count]))
    278             return 0;
    279         for (uint16_t i = 0; i < count; ++i) {
    280             const FeatureTable* featureTable = features->feature(featureIndex[i], featureTag, buffer);
    281             if (featureTable)
    282                 return featureTable;
    283         }
    284         return 0;
    285     }
    286 };
    287 
    288 struct ScriptTable : TableBase {
    289     OpenType::Offset defaultLangSysOffset;
    290     OpenType::UInt16 langSysCount;
    291     struct LangSysRecord {
    292         OpenType::Tag langSysTag;
    293         OpenType::Offset langSysOffset;
    294     } langSysRecords[1];
    295 
    296     const LangSysTable* defaultLangSys(const SharedBuffer& buffer) const
    297     {
    298         uint16_t count = langSysCount;
    299         if (!isValidEnd(buffer, &langSysRecords[count]))
    300             return 0;
    301         uint16_t offset = defaultLangSysOffset;
    302         if (offset)
    303             return validateOffset<LangSysTable>(buffer, offset);
    304         if (count)
    305             return validateOffset<LangSysTable>(buffer, langSysRecords[0].langSysOffset);
    306         return 0;
    307     }
    308 };
    309 
    310 struct ScriptList : TableBase {
    311     OpenType::UInt16 scriptCount;
    312     struct ScriptRecord {
    313         OpenType::Tag scriptTag;
    314         OpenType::Offset scriptOffset;
    315     } scripts[1];
    316 
    317     const ScriptTable* script(OpenType::Tag tag, const SharedBuffer& buffer) const
    318     {
    319         uint16_t count = scriptCount;
    320         if (!isValidEnd(buffer, &scripts[count]))
    321             return 0;
    322         for (uint16_t i = 0; i < count; ++i) {
    323             if (scripts[i].scriptTag == tag)
    324                 return validateOffset<ScriptTable>(buffer, scripts[i].scriptOffset);
    325         }
    326         return 0;
    327     }
    328 
    329     const ScriptTable* defaultScript(const SharedBuffer& buffer) const
    330     {
    331         uint16_t count = scriptCount;
    332         if (!count || !isValidEnd(buffer, &scripts[count]))
    333             return 0;
    334         const ScriptTable* scriptOfDefaultTag = script(OpenType::DefaultScriptTag, buffer);
    335         if (scriptOfDefaultTag)
    336             return scriptOfDefaultTag;
    337         return validateOffset<ScriptTable>(buffer, scripts[0].scriptOffset);
    338     }
    339 
    340     const LangSysTable* defaultLangSys(const SharedBuffer& buffer) const
    341     {
    342         const ScriptTable* scriptTable = defaultScript(buffer);
    343         if (!scriptTable)
    344             return 0;
    345         return scriptTable->defaultLangSys(buffer);
    346     }
    347 };
    348 
    349 struct GSUBTable : TableBase {
    350     OpenType::Fixed version;
    351     OpenType::Offset scriptListOffset;
    352     OpenType::Offset featureListOffset;
    353     OpenType::Offset lookupListOffset;
    354 
    355     const ScriptList* scriptList(const SharedBuffer& buffer) const { return validateOffset<ScriptList>(buffer, scriptListOffset); }
    356     const FeatureList* featureList(const SharedBuffer& buffer) const { return validateOffset<FeatureList>(buffer, featureListOffset); }
    357     const LookupList* lookupList(const SharedBuffer& buffer) const { return validateOffset<LookupList>(buffer, lookupListOffset); }
    358 
    359     const LangSysTable* defaultLangSys(const SharedBuffer& buffer) const
    360     {
    361         const ScriptList* scripts = scriptList(buffer);
    362         if (!scripts)
    363             return 0;
    364         return scripts->defaultLangSys(buffer);
    365     }
    366 
    367     const FeatureTable* feature(OpenType::Tag featureTag, const SharedBuffer& buffer) const
    368     {
    369         const LangSysTable* langSys = defaultLangSys(buffer);
    370         const FeatureList* features = featureList(buffer);
    371         if (!features)
    372             return 0;
    373         const FeatureTable* feature = 0;
    374         if (langSys)
    375             feature = langSys->feature(featureTag, features, buffer);
    376         if (!feature) {
    377             // If the font has no langSys table, or has no default script and the first script doesn't
    378             // have the requested feature, then use the first matching feature directly.
    379             feature = features->findFeature(featureTag, buffer);
    380         }
    381         return feature;
    382     }
    383 
    384     bool getVerticalGlyphSubstitutions(HashMap<Glyph, Glyph>* map, const SharedBuffer& buffer) const
    385     {
    386         const FeatureTable* verticalFeatureTable = feature(OpenType::VertFeatureTag, buffer);
    387         if (!verticalFeatureTable)
    388             return false;
    389         const LookupList* lookups = lookupList(buffer);
    390         return lookups && verticalFeatureTable->getGlyphSubstitutions(lookups, map, buffer);
    391     }
    392 };
    393 
    394 #pragma pack()
    395 
    396 } // namespace OpenType
    397 
    398 OpenTypeVerticalData::OpenTypeVerticalData(const FontPlatformData& platformData)
    399     : m_defaultVertOriginY(0)
    400 {
    401     loadMetrics(platformData);
    402     loadVerticalGlyphSubstitutions(platformData);
    403 }
    404 
    405 void OpenTypeVerticalData::loadMetrics(const FontPlatformData& platformData)
    406 {
    407     // Load hhea and hmtx to get x-component of vertical origins.
    408     // If these tables are missing, it's not an OpenType font.
    409     RefPtr<SharedBuffer> buffer = platformData.openTypeTable(OpenType::HheaTag);
    410     const OpenType::HheaTable* hhea = OpenType::validateTable<OpenType::HheaTable>(buffer);
    411     if (!hhea)
    412         return;
    413     uint16_t countHmtxEntries = hhea->numberOfHMetrics;
    414     if (!countHmtxEntries) {
    415         WTF_LOG_ERROR("Invalid numberOfHMetrics");
    416         return;
    417     }
    418 
    419     buffer = platformData.openTypeTable(OpenType::HmtxTag);
    420     const OpenType::HmtxTable* hmtx = OpenType::validateTable<OpenType::HmtxTable>(buffer, countHmtxEntries);
    421     if (!hmtx) {
    422         WTF_LOG_ERROR("hhea exists but hmtx does not (or broken)");
    423         return;
    424     }
    425     m_advanceWidths.resize(countHmtxEntries);
    426     for (uint16_t i = 0; i < countHmtxEntries; ++i)
    427         m_advanceWidths[i] = hmtx->entries[i].advanceWidth;
    428 
    429     // Load vhea first. This table is required for fonts that support vertical flow.
    430     buffer = platformData.openTypeTable(OpenType::VheaTag);
    431     const OpenType::VheaTable* vhea = OpenType::validateTable<OpenType::VheaTable>(buffer);
    432     if (!vhea)
    433         return;
    434     uint16_t countVmtxEntries = vhea->numOfLongVerMetrics;
    435     if (!countVmtxEntries) {
    436         WTF_LOG_ERROR("Invalid numOfLongVerMetrics");
    437         return;
    438     }
    439 
    440     // Load VORG. This table is optional.
    441     buffer = platformData.openTypeTable(OpenType::VORGTag);
    442     const OpenType::VORGTable* vorg = OpenType::validateTable<OpenType::VORGTable>(buffer);
    443     if (vorg && buffer->size() >= vorg->requiredSize()) {
    444         m_defaultVertOriginY = vorg->defaultVertOriginY;
    445         uint16_t countVertOriginYMetrics = vorg->numVertOriginYMetrics;
    446         if (!countVertOriginYMetrics) {
    447             // Add one entry so that hasVORG() becomes true
    448             m_vertOriginY.set(0, m_defaultVertOriginY);
    449         } else {
    450             for (uint16_t i = 0; i < countVertOriginYMetrics; ++i) {
    451                 const OpenType::VORGTable::VertOriginYMetrics& metrics = vorg->vertOriginYMetrics[i];
    452                 m_vertOriginY.set(metrics.glyphIndex, metrics.vertOriginY);
    453             }
    454         }
    455     }
    456 
    457     // Load vmtx then. This table is required for fonts that support vertical flow.
    458     buffer = platformData.openTypeTable(OpenType::VmtxTag);
    459     const OpenType::VmtxTable* vmtx = OpenType::validateTable<OpenType::VmtxTable>(buffer, countVmtxEntries);
    460     if (!vmtx) {
    461         WTF_LOG_ERROR("vhea exists but vmtx does not (or broken)");
    462         return;
    463     }
    464     m_advanceHeights.resize(countVmtxEntries);
    465     for (uint16_t i = 0; i < countVmtxEntries; ++i)
    466         m_advanceHeights[i] = vmtx->entries[i].advanceHeight;
    467 
    468     // VORG is preferred way to calculate vertical origin than vmtx,
    469     // so load topSideBearing from vmtx only if VORG is missing.
    470     if (hasVORG())
    471         return;
    472 
    473     size_t sizeExtra = buffer->size() - sizeof(OpenType::VmtxTable::Entry) * countVmtxEntries;
    474     if (sizeExtra % sizeof(OpenType::Int16)) {
    475         WTF_LOG_ERROR("vmtx has incorrect tsb count");
    476         return;
    477     }
    478     size_t countTopSideBearings = countVmtxEntries + sizeExtra / sizeof(OpenType::Int16);
    479     m_topSideBearings.resize(countTopSideBearings);
    480     size_t i;
    481     for (i = 0; i < countVmtxEntries; ++i)
    482         m_topSideBearings[i] = vmtx->entries[i].topSideBearing;
    483     if (i < countTopSideBearings) {
    484         const OpenType::Int16* pTopSideBearingsExtra = reinterpret_cast<const OpenType::Int16*>(&vmtx->entries[countVmtxEntries]);
    485         for (; i < countTopSideBearings; ++i, ++pTopSideBearingsExtra)
    486             m_topSideBearings[i] = *pTopSideBearingsExtra;
    487     }
    488 }
    489 
    490 void OpenTypeVerticalData::loadVerticalGlyphSubstitutions(const FontPlatformData& platformData)
    491 {
    492     RefPtr<SharedBuffer> buffer = platformData.openTypeTable(OpenType::GSUBTag);
    493     const OpenType::GSUBTable* gsub = OpenType::validateTable<OpenType::GSUBTable>(buffer);
    494     if (gsub)
    495         gsub->getVerticalGlyphSubstitutions(&m_verticalGlyphMap, *buffer.get());
    496 }
    497 
    498 float OpenTypeVerticalData::advanceHeight(const SimpleFontData* font, Glyph glyph) const
    499 {
    500     size_t countHeights = m_advanceHeights.size();
    501     if (countHeights) {
    502         uint16_t advanceFUnit = m_advanceHeights[glyph < countHeights ? glyph : countHeights - 1];
    503         float advance = advanceFUnit * font->sizePerUnit();
    504         return advance;
    505     }
    506 
    507     // No vertical info in the font file; use height as advance.
    508     return font->fontMetrics().height();
    509 }
    510 
    511 void OpenTypeVerticalData::getVerticalTranslationsForGlyphs(const SimpleFontData* font, const Glyph* glyphs, size_t count, float* outXYArray) const
    512 {
    513     size_t countWidths = m_advanceWidths.size();
    514     ASSERT(countWidths > 0);
    515     const FontMetrics& metrics = font->fontMetrics();
    516     float sizePerUnit = font->sizePerUnit();
    517     float ascent = metrics.ascent();
    518     bool useVORG = hasVORG();
    519     size_t countTopSideBearings = m_topSideBearings.size();
    520     float defaultVertOriginY = std::numeric_limits<float>::quiet_NaN();
    521     for (float* end = &(outXYArray[count * 2]); outXYArray != end; ++glyphs, outXYArray += 2) {
    522         Glyph glyph = *glyphs;
    523         uint16_t widthFUnit = m_advanceWidths[glyph < countWidths ? glyph : countWidths - 1];
    524         float width = widthFUnit * sizePerUnit;
    525         outXYArray[0] = -width / 2;
    526 
    527         // For Y, try VORG first.
    528         if (useVORG) {
    529             int16_t vertOriginYFUnit = m_vertOriginY.get(glyph);
    530             if (vertOriginYFUnit) {
    531                 outXYArray[1] = -vertOriginYFUnit * sizePerUnit;
    532                 continue;
    533             }
    534             if (std::isnan(defaultVertOriginY))
    535                 defaultVertOriginY = -m_defaultVertOriginY * sizePerUnit;
    536             outXYArray[1] = defaultVertOriginY;
    537             continue;
    538         }
    539 
    540         // If no VORG, try vmtx next.
    541         if (countTopSideBearings) {
    542             int16_t topSideBearingFUnit = m_topSideBearings[glyph < countTopSideBearings ? glyph : countTopSideBearings - 1];
    543             float topSideBearing = topSideBearingFUnit * sizePerUnit;
    544             FloatRect bounds = font->boundsForGlyph(glyph);
    545             outXYArray[1] = bounds.y() - topSideBearing;
    546             continue;
    547         }
    548 
    549         // No vertical info in the font file; use ascent as vertical origin.
    550         outXYArray[1] = -ascent;
    551     }
    552 }
    553 
    554 void OpenTypeVerticalData::substituteWithVerticalGlyphs(const SimpleFontData* font, GlyphPage* glyphPage, unsigned offset, unsigned length) const
    555 {
    556     const HashMap<Glyph, Glyph>& map = m_verticalGlyphMap;
    557     if (map.isEmpty())
    558         return;
    559 
    560     for (unsigned index = offset, end = offset + length; index < end; ++index) {
    561         GlyphData glyphData = glyphPage->glyphDataForIndex(index);
    562         if (glyphData.glyph && glyphData.fontData == font) {
    563             Glyph to = map.get(glyphData.glyph);
    564             if (to)
    565                 glyphPage->setGlyphDataForIndex(index, to, font);
    566         }
    567     }
    568 }
    569 
    570 } // namespace blink
    571 #endif // ENABLE(OPENTYPE_VERTICAL)
    572