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