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