1 /* 2 * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. 3 * Copyright (C) 2007 Nicholas Shanks <webkit (at) nickshanks.com> 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 15 * its contributors may be used to endorse or promote products derived 16 * from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 #include "config.h" 31 #include "core/platform/graphics/FontCache.h" 32 33 #include "FontFamilyNames.h" 34 #include "core/platform/graphics/Font.h" 35 #include "core/platform/graphics/FontFallbackList.h" 36 #include "core/platform/graphics/FontPlatformData.h" 37 #include "core/platform/graphics/FontSelector.h" 38 #include "core/platform/graphics/opentype/OpenTypeVerticalData.h" 39 #include "wtf/HashMap.h" 40 #include "wtf/HashTableDeletedValueType.h" 41 #include "wtf/ListHashSet.h" 42 #include "wtf/StdLibExtras.h" 43 #include "wtf/text/AtomicStringHash.h" 44 #include "wtf/text/StringHash.h" 45 46 using namespace WTF; 47 48 namespace WebCore { 49 50 FontCache* fontCache() 51 { 52 DEFINE_STATIC_LOCAL(FontCache, globalFontCache, ()); 53 return &globalFontCache; 54 } 55 56 FontCache::FontCache() 57 : m_purgePreventCount(0) 58 { 59 } 60 61 struct FontPlatformDataCacheKey { 62 WTF_MAKE_FAST_ALLOCATED; 63 public: 64 FontPlatformDataCacheKey(const AtomicString& family = AtomicString(), unsigned size = 0, unsigned weight = 0, bool italic = false, 65 bool isPrinterFont = false, FontOrientation orientation = Horizontal, FontWidthVariant widthVariant = RegularWidth) 66 : m_size(size) 67 , m_weight(weight) 68 , m_family(family) 69 , m_italic(italic) 70 , m_printerFont(isPrinterFont) 71 , m_orientation(orientation) 72 , m_widthVariant(widthVariant) 73 { 74 } 75 76 FontPlatformDataCacheKey(HashTableDeletedValueType) : m_size(hashTableDeletedSize()) { } 77 bool isHashTableDeletedValue() const { return m_size == hashTableDeletedSize(); } 78 79 bool operator==(const FontPlatformDataCacheKey& other) const 80 { 81 return equalIgnoringCase(m_family, other.m_family) && m_size == other.m_size 82 && m_weight == other.m_weight && m_italic == other.m_italic && m_printerFont == other.m_printerFont 83 && m_orientation == other.m_orientation && m_widthVariant == other.m_widthVariant; 84 } 85 86 unsigned m_size; 87 unsigned m_weight; 88 AtomicString m_family; 89 bool m_italic; 90 bool m_printerFont; 91 FontOrientation m_orientation; 92 FontWidthVariant m_widthVariant; 93 94 private: 95 static unsigned hashTableDeletedSize() { return 0xFFFFFFFFU; } 96 }; 97 98 inline unsigned computeHash(const FontPlatformDataCacheKey& fontKey) 99 { 100 unsigned hashCodes[5] = { 101 CaseFoldingHash::hash(fontKey.m_family), 102 fontKey.m_size, 103 fontKey.m_weight, 104 fontKey.m_widthVariant, 105 static_cast<unsigned>(fontKey.m_orientation) << 2 | static_cast<unsigned>(fontKey.m_italic) << 1 | static_cast<unsigned>(fontKey.m_printerFont) 106 }; 107 return StringHasher::hashMemory<sizeof(hashCodes)>(hashCodes); 108 } 109 110 struct FontPlatformDataCacheKeyHash { 111 static unsigned hash(const FontPlatformDataCacheKey& font) 112 { 113 return computeHash(font); 114 } 115 116 static bool equal(const FontPlatformDataCacheKey& a, const FontPlatformDataCacheKey& b) 117 { 118 return a == b; 119 } 120 121 static const bool safeToCompareToEmptyOrDeleted = true; 122 }; 123 124 struct FontPlatformDataCacheKeyTraits : WTF::SimpleClassHashTraits<FontPlatformDataCacheKey> { }; 125 126 typedef HashMap<FontPlatformDataCacheKey, FontPlatformData*, FontPlatformDataCacheKeyHash, FontPlatformDataCacheKeyTraits> FontPlatformDataCache; 127 128 static FontPlatformDataCache* gFontPlatformDataCache = 0; 129 130 static const AtomicString& alternateFamilyName(const AtomicString& familyName) 131 { 132 // Alias Courier <-> Courier New 133 DEFINE_STATIC_LOCAL(AtomicString, courier, ("Courier", AtomicString::ConstructFromLiteral)); 134 DEFINE_STATIC_LOCAL(AtomicString, courierNew, ("Courier New", AtomicString::ConstructFromLiteral)); 135 if (equalIgnoringCase(familyName, courier)) 136 return courierNew; 137 #if !OS(WINDOWS) 138 // On Windows, Courier New (truetype font) is always present and 139 // Courier is a bitmap font. So, we don't want to map Courier New to 140 // Courier. 141 if (equalIgnoringCase(familyName, courierNew)) 142 return courier; 143 #endif 144 145 // Alias Times and Times New Roman. 146 DEFINE_STATIC_LOCAL(AtomicString, times, ("Times", AtomicString::ConstructFromLiteral)); 147 DEFINE_STATIC_LOCAL(AtomicString, timesNewRoman, ("Times New Roman", AtomicString::ConstructFromLiteral)); 148 if (equalIgnoringCase(familyName, times)) 149 return timesNewRoman; 150 if (equalIgnoringCase(familyName, timesNewRoman)) 151 return times; 152 153 // Alias Arial and Helvetica 154 DEFINE_STATIC_LOCAL(AtomicString, arial, ("Arial", AtomicString::ConstructFromLiteral)); 155 DEFINE_STATIC_LOCAL(AtomicString, helvetica, ("Helvetica", AtomicString::ConstructFromLiteral)); 156 if (equalIgnoringCase(familyName, arial)) 157 return helvetica; 158 if (equalIgnoringCase(familyName, helvetica)) 159 return arial; 160 161 #if OS(WINDOWS) 162 // On Windows, bitmap fonts are blocked altogether so that we have to 163 // alias MS Sans Serif (bitmap font) -> Microsoft Sans Serif (truetype font) 164 DEFINE_STATIC_LOCAL(AtomicString, msSans, ("MS Sans Serif", AtomicString::ConstructFromLiteral)); 165 DEFINE_STATIC_LOCAL(AtomicString, microsoftSans, ("Microsoft Sans Serif", AtomicString::ConstructFromLiteral)); 166 if (equalIgnoringCase(familyName, msSans)) 167 return microsoftSans; 168 169 // Alias MS Serif (bitmap) -> Times New Roman (truetype font). There's no 170 // 'Microsoft Sans Serif-equivalent' for Serif. 171 DEFINE_STATIC_LOCAL(AtomicString, msSerif, ("MS Serif", AtomicString::ConstructFromLiteral)); 172 if (equalIgnoringCase(familyName, msSerif)) 173 return timesNewRoman; 174 #endif 175 176 return emptyAtom; 177 } 178 179 FontPlatformData* FontCache::getFontResourcePlatformData(const FontDescription& fontDescription, 180 const AtomicString& passedFamilyName, 181 bool checkingAlternateName) 182 { 183 #if OS(WINDOWS) && ENABLE(OPENTYPE_VERTICAL) 184 // Leading "@" in the font name enables Windows vertical flow flag for the font. 185 // Because we do vertical flow by ourselves, we don't want to use the Windows feature. 186 // IE disregards "@" regardless of the orientatoin, so we follow the behavior. 187 const AtomicString& familyName = (passedFamilyName.isEmpty() || passedFamilyName[0] != '@') ? 188 passedFamilyName : AtomicString(passedFamilyName.impl()->substring(1)); 189 #else 190 const AtomicString& familyName = passedFamilyName; 191 #endif 192 193 if (!gFontPlatformDataCache) { 194 gFontPlatformDataCache = new FontPlatformDataCache; 195 platformInit(); 196 } 197 198 FontPlatformDataCacheKey key(familyName, fontDescription.computedPixelSize(), fontDescription.weight(), fontDescription.italic(), 199 fontDescription.usePrinterFont(), fontDescription.orientation(), fontDescription.widthVariant()); 200 FontPlatformData* result = 0; 201 bool foundResult; 202 FontPlatformDataCache::iterator it = gFontPlatformDataCache->find(key); 203 if (it == gFontPlatformDataCache->end()) { 204 result = createFontPlatformData(fontDescription, familyName); 205 gFontPlatformDataCache->set(key, result); 206 foundResult = result; 207 } else { 208 result = it->value; 209 foundResult = true; 210 } 211 212 if (!foundResult && !checkingAlternateName) { 213 // We were unable to find a font. We have a small set of fonts that we alias to other names, 214 // e.g., Arial/Helvetica, Courier/Courier New, etc. Try looking up the font under the aliased name. 215 const AtomicString& alternateName = alternateFamilyName(familyName); 216 if (!alternateName.isEmpty()) 217 result = getFontResourcePlatformData(fontDescription, alternateName, true); 218 if (result) 219 gFontPlatformDataCache->set(key, new FontPlatformData(*result)); // Cache the result under the old name. 220 } 221 222 return result; 223 } 224 225 #if ENABLE(OPENTYPE_VERTICAL) 226 typedef HashMap<FontCache::FontFileKey, RefPtr<OpenTypeVerticalData>, WTF::IntHash<FontCache::FontFileKey>, WTF::UnsignedWithZeroKeyHashTraits<FontCache::FontFileKey> > FontVerticalDataCache; 227 228 FontVerticalDataCache& fontVerticalDataCacheInstance() 229 { 230 DEFINE_STATIC_LOCAL(FontVerticalDataCache, fontVerticalDataCache, ()); 231 return fontVerticalDataCache; 232 } 233 234 PassRefPtr<OpenTypeVerticalData> FontCache::getVerticalData(const FontFileKey& key, const FontPlatformData& platformData) 235 { 236 FontVerticalDataCache& fontVerticalDataCache = fontVerticalDataCacheInstance(); 237 FontVerticalDataCache::iterator result = fontVerticalDataCache.find(key); 238 if (result != fontVerticalDataCache.end()) 239 return result.get()->value; 240 241 RefPtr<OpenTypeVerticalData> verticalData = OpenTypeVerticalData::create(platformData); 242 if (!verticalData->isOpenType()) 243 verticalData.clear(); 244 fontVerticalDataCache.set(key, verticalData); 245 return verticalData; 246 } 247 #endif 248 249 struct FontDataCacheKeyHash { 250 static unsigned hash(const FontPlatformData& platformData) 251 { 252 return platformData.hash(); 253 } 254 255 static bool equal(const FontPlatformData& a, const FontPlatformData& b) 256 { 257 return a == b; 258 } 259 260 static const bool safeToCompareToEmptyOrDeleted = true; 261 }; 262 263 struct FontDataCacheKeyTraits : WTF::GenericHashTraits<FontPlatformData> { 264 static const bool emptyValueIsZero = true; 265 static const bool needsDestruction = true; 266 static const FontPlatformData& emptyValue() 267 { 268 DEFINE_STATIC_LOCAL(FontPlatformData, key, (0.f, false, false)); 269 return key; 270 } 271 static void constructDeletedValue(FontPlatformData& slot) 272 { 273 new (NotNull, &slot) FontPlatformData(HashTableDeletedValue); 274 } 275 static bool isDeletedValue(const FontPlatformData& value) 276 { 277 return value.isHashTableDeletedValue(); 278 } 279 }; 280 281 typedef HashMap<FontPlatformData, pair<RefPtr<SimpleFontData>, unsigned>, FontDataCacheKeyHash, FontDataCacheKeyTraits> FontDataCache; 282 283 static FontDataCache* gFontDataCache = 0; 284 285 #if !OS(ANDROID) 286 const int cMaxInactiveFontData = 250; 287 const int cTargetInactiveFontData = 200; 288 #else 289 const int cMaxInactiveFontData = 225; 290 const int cTargetInactiveFontData = 200; 291 #endif 292 static ListHashSet<RefPtr<SimpleFontData> >* gInactiveFontData = 0; 293 294 PassRefPtr<SimpleFontData> FontCache::getFontResourceData(const FontDescription& fontDescription, const AtomicString& family, bool checkingAlternateName, ShouldRetain shouldRetain) 295 { 296 FontPlatformData* platformData = getFontResourcePlatformData(fontDescription, family, checkingAlternateName); 297 if (!platformData) 298 return 0; 299 300 return getFontResourceData(platformData, shouldRetain); 301 } 302 303 PassRefPtr<SimpleFontData> FontCache::getFontResourceData(const FontPlatformData* platformData, ShouldRetain shouldRetain) 304 { 305 if (!platformData) 306 return 0; 307 308 #if !ASSERT_DISABLED 309 if (shouldRetain == DoNotRetain) 310 ASSERT(m_purgePreventCount); 311 #endif 312 313 if (!gFontDataCache) { 314 gFontDataCache = new FontDataCache; 315 gInactiveFontData = new ListHashSet<RefPtr<SimpleFontData> >; 316 } 317 318 FontDataCache::iterator result = gFontDataCache->find(*platformData); 319 if (result == gFontDataCache->end()) { 320 pair<RefPtr<SimpleFontData>, unsigned> newValue(SimpleFontData::create(*platformData), shouldRetain == Retain ? 1 : 0); 321 gFontDataCache->set(*platformData, newValue); 322 if (shouldRetain == DoNotRetain) 323 gInactiveFontData->add(newValue.first); 324 return newValue.first.release(); 325 } 326 327 if (!result.get()->value.second) { 328 ASSERT(gInactiveFontData->contains(result.get()->value.first)); 329 gInactiveFontData->remove(result.get()->value.first); 330 } 331 332 if (shouldRetain == Retain) 333 result.get()->value.second++; 334 else if (!result.get()->value.second) { 335 // If shouldRetain is DoNotRetain and count is 0, we want to remove the fontData from 336 // gInactiveFontData (above) and re-add here to update LRU position. 337 gInactiveFontData->add(result.get()->value.first); 338 } 339 340 return result.get()->value.first; 341 } 342 343 SimpleFontData* FontCache::getNonRetainedLastResortFallbackFont(const FontDescription& fontDescription) 344 { 345 return getLastResortFallbackFont(fontDescription, DoNotRetain).leakRef(); 346 } 347 348 void FontCache::releaseFontData(const SimpleFontData* fontData) 349 { 350 ASSERT(gFontDataCache); 351 ASSERT(!fontData->isCustomFont()); 352 353 FontDataCache::iterator it = gFontDataCache->find(fontData->platformData()); 354 ASSERT(it != gFontDataCache->end()); 355 if (it == gFontDataCache->end()) 356 return; 357 358 ASSERT(it->value.second); 359 if (!--it->value.second) 360 gInactiveFontData->add(it->value.first); 361 } 362 363 void FontCache::purgeInactiveFontDataIfNeeded() 364 { 365 if (gInactiveFontData && !m_purgePreventCount && gInactiveFontData->size() > cMaxInactiveFontData) 366 purgeInactiveFontData(gInactiveFontData->size() - cTargetInactiveFontData); 367 } 368 369 void FontCache::purgeInactiveFontData(int count) 370 { 371 if (!gInactiveFontData || m_purgePreventCount) 372 return; 373 374 static bool isPurging; // Guard against reentry when e.g. a deleted FontData releases its small caps FontData. 375 if (isPurging) 376 return; 377 378 isPurging = true; 379 380 Vector<RefPtr<SimpleFontData>, 20> fontDataToDelete; 381 ListHashSet<RefPtr<SimpleFontData> >::iterator end = gInactiveFontData->end(); 382 ListHashSet<RefPtr<SimpleFontData> >::iterator it = gInactiveFontData->begin(); 383 for (int i = 0; i < count && it != end; ++it, ++i) { 384 RefPtr<SimpleFontData>& fontData = *it.get(); 385 gFontDataCache->remove(fontData->platformData()); 386 // We should not delete SimpleFontData here because deletion can modify gInactiveFontData. See http://trac.webkit.org/changeset/44011 387 fontDataToDelete.append(fontData); 388 } 389 390 if (it == end) { 391 // Removed everything 392 gInactiveFontData->clear(); 393 } else { 394 for (int i = 0; i < count; ++i) 395 gInactiveFontData->remove(gInactiveFontData->begin()); 396 } 397 398 fontDataToDelete.clear(); 399 400 if (gFontPlatformDataCache) { 401 Vector<FontPlatformDataCacheKey> keysToRemove; 402 keysToRemove.reserveInitialCapacity(gFontPlatformDataCache->size()); 403 FontPlatformDataCache::iterator platformDataEnd = gFontPlatformDataCache->end(); 404 for (FontPlatformDataCache::iterator platformData = gFontPlatformDataCache->begin(); platformData != platformDataEnd; ++platformData) { 405 if (platformData->value && !gFontDataCache->contains(*platformData->value)) 406 keysToRemove.append(platformData->key); 407 } 408 409 size_t keysToRemoveCount = keysToRemove.size(); 410 for (size_t i = 0; i < keysToRemoveCount; ++i) 411 delete gFontPlatformDataCache->take(keysToRemove[i]); 412 } 413 414 #if ENABLE(OPENTYPE_VERTICAL) 415 FontVerticalDataCache& fontVerticalDataCache = fontVerticalDataCacheInstance(); 416 if (!fontVerticalDataCache.isEmpty()) { 417 // Mark & sweep unused verticalData 418 FontVerticalDataCache::iterator verticalDataEnd = fontVerticalDataCache.end(); 419 for (FontVerticalDataCache::iterator verticalData = fontVerticalDataCache.begin(); verticalData != verticalDataEnd; ++verticalData) { 420 if (verticalData->value) 421 verticalData->value->m_inFontCache = false; 422 } 423 FontDataCache::iterator fontDataEnd = gFontDataCache->end(); 424 for (FontDataCache::iterator fontData = gFontDataCache->begin(); fontData != fontDataEnd; ++fontData) { 425 OpenTypeVerticalData* verticalData = const_cast<OpenTypeVerticalData*>(fontData->value.first->verticalData()); 426 if (verticalData) 427 verticalData->m_inFontCache = true; 428 } 429 Vector<FontFileKey> keysToRemove; 430 keysToRemove.reserveInitialCapacity(fontVerticalDataCache.size()); 431 for (FontVerticalDataCache::iterator verticalData = fontVerticalDataCache.begin(); verticalData != verticalDataEnd; ++verticalData) { 432 if (!verticalData->value || !verticalData->value->m_inFontCache) 433 keysToRemove.append(verticalData->key); 434 } 435 for (size_t i = 0, count = keysToRemove.size(); i < count; ++i) 436 fontVerticalDataCache.take(keysToRemove[i]); 437 } 438 #endif 439 440 isPurging = false; 441 } 442 443 size_t FontCache::fontDataCount() 444 { 445 if (gFontDataCache) 446 return gFontDataCache->size(); 447 return 0; 448 } 449 450 size_t FontCache::inactiveFontDataCount() 451 { 452 if (gInactiveFontData) 453 return gInactiveFontData->size(); 454 return 0; 455 } 456 457 PassRefPtr<FontData> FontCache::getFontData(const Font& font, int& familyIndex, FontSelector* fontSelector) 458 { 459 RefPtr<FontData> result; 460 461 int startIndex = familyIndex; 462 const FontFamily* startFamily = &font.fontDescription().family(); 463 for (int i = 0; startFamily && i < startIndex; i++) 464 startFamily = startFamily->next(); 465 const FontFamily* currFamily = startFamily; 466 while (currFamily && !result) { 467 familyIndex++; 468 if (currFamily->family().length()) { 469 if (fontSelector) 470 result = fontSelector->getFontData(font.fontDescription(), currFamily->family()); 471 472 if (!result) 473 result = getFontResourceData(font.fontDescription(), currFamily->family()); 474 } 475 currFamily = currFamily->next(); 476 } 477 478 if (!currFamily) 479 familyIndex = cAllFamiliesScanned; 480 481 if (!result) 482 // We didn't find a font. Try to find a similar font using our own specific knowledge about our platform. 483 // For example on OS X, we know to map any families containing the words Arabic, Pashto, or Urdu to the 484 // Geeza Pro font. 485 result = getSimilarFontPlatformData(font); 486 487 if (!result && startIndex == 0) { 488 // If it's the primary font that we couldn't find, we try the following. In all other cases, we will 489 // just use per-character system fallback. 490 491 if (fontSelector) { 492 // Try the user's preferred standard font. 493 if (RefPtr<FontData> data = fontSelector->getFontData(font.fontDescription(), standardFamily)) 494 return data.release(); 495 } 496 497 // Still no result. Hand back our last resort fallback font. 498 result = getLastResortFallbackFont(font.fontDescription()); 499 } 500 return result.release(); 501 } 502 503 static HashSet<FontSelector*>* gClients; 504 505 void FontCache::addClient(FontSelector* client) 506 { 507 if (!gClients) 508 gClients = new HashSet<FontSelector*>; 509 510 ASSERT(!gClients->contains(client)); 511 gClients->add(client); 512 } 513 514 void FontCache::removeClient(FontSelector* client) 515 { 516 ASSERT(gClients); 517 ASSERT(gClients->contains(client)); 518 519 gClients->remove(client); 520 } 521 522 static unsigned short gGeneration = 0; 523 524 unsigned short FontCache::generation() 525 { 526 return gGeneration; 527 } 528 529 void FontCache::invalidate() 530 { 531 if (!gClients) { 532 ASSERT(!gFontPlatformDataCache); 533 return; 534 } 535 536 if (gFontPlatformDataCache) { 537 deleteAllValues(*gFontPlatformDataCache); 538 delete gFontPlatformDataCache; 539 gFontPlatformDataCache = new FontPlatformDataCache; 540 } 541 542 gGeneration++; 543 544 Vector<RefPtr<FontSelector> > clients; 545 size_t numClients = gClients->size(); 546 clients.reserveInitialCapacity(numClients); 547 HashSet<FontSelector*>::iterator end = gClients->end(); 548 for (HashSet<FontSelector*>::iterator it = gClients->begin(); it != end; ++it) 549 clients.append(*it); 550 551 ASSERT(numClients == clients.size()); 552 for (size_t i = 0; i < numClients; ++i) 553 clients[i]->fontCacheInvalidated(); 554 555 purgeInactiveFontData(); 556 } 557 558 const FontPlatformData* FontCache::getFallbackFontData(const FontDescription& description) 559 { 560 DEFINE_STATIC_LOCAL(const AtomicString, sansStr, ("Sans", AtomicString::ConstructFromLiteral)); 561 DEFINE_STATIC_LOCAL(const AtomicString, serifStr, ("Serif", AtomicString::ConstructFromLiteral)); 562 DEFINE_STATIC_LOCAL(const AtomicString, monospaceStr, ("Monospace", AtomicString::ConstructFromLiteral)); 563 564 FontPlatformData* fontPlatformData = 0; 565 switch (description.genericFamily()) { 566 case FontDescription::SerifFamily: 567 fontPlatformData = getFontResourcePlatformData(description, serifStr); 568 break; 569 case FontDescription::MonospaceFamily: 570 fontPlatformData = getFontResourcePlatformData(description, monospaceStr); 571 break; 572 case FontDescription::SansSerifFamily: 573 default: 574 fontPlatformData = getFontResourcePlatformData(description, sansStr); 575 break; 576 } 577 578 return fontPlatformData; 579 } 580 581 } // namespace WebCore 582