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