1/*
2 * Copyright (C) 2012 Koji Ishii <kojiishi@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 "OpenTypeVerticalData.h"
28
29#include "FloatRect.h"
30#include "GlyphPage.h"
31#include "OpenTypeTypes.h"
32#include "SharedBuffer.h"
33#include "SimpleFontData.h"
34#include <wtf/RefPtr.h>
35
36using namespace std;
37
38namespace WebCore {
39namespace OpenType {
40
41const uint32_t GSUBTag = OT_MAKE_TAG('G', 'S', 'U', 'B');
42const uint32_t HheaTag = OT_MAKE_TAG('h', 'h', 'e', 'a');
43const uint32_t HmtxTag = OT_MAKE_TAG('h', 'm', 't', 'x');
44const uint32_t VheaTag = OT_MAKE_TAG('v', 'h', 'e', 'a');
45const uint32_t VmtxTag = OT_MAKE_TAG('v', 'm', 't', 'x');
46const uint32_t VORGTag = OT_MAKE_TAG('V', 'O', 'R', 'G');
47
48const uint32_t DefaultScriptTag = OT_MAKE_TAG('D', 'F', 'L', 'T');
49
50const uint32_t VertFeatureTag = OT_MAKE_TAG('v', 'e', 'r', 't');
51
52#pragma pack(1)
53
54struct 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
71struct 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
88struct HmtxTable {
89    struct Entry {
90        OpenType::UInt16 advanceWidth;
91        OpenType::Int16 lsb;
92    } entries[1];
93};
94
95struct VmtxTable {
96    struct Entry {
97        OpenType::UInt16 advanceHeight;
98        OpenType::Int16 topSideBearing;
99    } entries[1];
100};
101
102struct 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
115struct CoverageTable : TableBase {
116    OpenType::UInt16 coverageFormat;
117};
118
119struct Coverage1Table : CoverageTable {
120    OpenType::UInt16 glyphCount;
121    OpenType::GlyphID glyphArray[1];
122};
123
124struct 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
133struct 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
140struct SingleSubstitution2SubTable : SubstitutionSubTable {
141    OpenType::UInt16 glyphCount;
142    OpenType::GlyphID substitute[1];
143};
144
145struct 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
211struct 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
224struct 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
243struct 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
270struct 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
290struct 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
312struct 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
351struct 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
400OpenTypeVerticalData::OpenTypeVerticalData(const FontPlatformData& platformData)
401    : m_defaultVertOriginY(0)
402{
403    loadMetrics(platformData);
404    loadVerticalGlyphSubstitutions(platformData);
405}
406
407void 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
492void 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
500float 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
513void 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
556void 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