Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / platform / fonts / opentype / OpenTypeVerticalData.cpp
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 "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 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         WTF_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         WTF_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         WTF_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         WTF_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         WTF_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         GlyphData glyphData = glyphPage->glyphDataForIndex(index);
564         if (glyphData.glyph && glyphData.fontData == font) {
565             Glyph to = map.get(glyphData.glyph);
566             if (to)
567                 glyphPage->setGlyphDataForIndex(index, to, font);
568         }
569     }
570 }
571
572 } // namespace WebCore
573 #endif // ENABLE(OPENTYPE_VERTICAL)