Upstream version 10.39.225.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 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)