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 "FontCache.h" 32 33 #include "Font.h" 34 #include "FontFallbackList.h" 35 #include "FontPlatformData.h" 36 #include "FontSelector.h" 37 #include "StringHash.h" 38 #include <wtf/HashMap.h> 39 #include <wtf/ListHashSet.h> 40 #include <wtf/StdLibExtras.h> 41 42 using namespace WTF; 43 44 namespace WebCore { 45 46 FontCache* fontCache() 47 { 48 DEFINE_STATIC_LOCAL(FontCache, globalFontCache, ()); 49 return &globalFontCache; 50 } 51 52 FontCache::FontCache() 53 { 54 } 55 56 struct FontPlatformDataCacheKey : FastAllocBase { 57 FontPlatformDataCacheKey(const AtomicString& family = AtomicString(), unsigned size = 0, unsigned weight = 0, bool italic = false, 58 bool isPrinterFont = false, FontRenderingMode renderingMode = NormalRenderingMode) 59 : m_family(family) 60 , m_size(size) 61 , m_weight(weight) 62 , m_italic(italic) 63 , m_printerFont(isPrinterFont) 64 , m_renderingMode(renderingMode) 65 { 66 } 67 68 FontPlatformDataCacheKey(HashTableDeletedValueType) : m_size(hashTableDeletedSize()) { } 69 bool isHashTableDeletedValue() const { return m_size == hashTableDeletedSize(); } 70 71 bool operator==(const FontPlatformDataCacheKey& other) const 72 { 73 return equalIgnoringCase(m_family, other.m_family) && m_size == other.m_size && 74 m_weight == other.m_weight && m_italic == other.m_italic && m_printerFont == other.m_printerFont && 75 m_renderingMode == other.m_renderingMode; 76 } 77 78 AtomicString m_family; 79 unsigned m_size; 80 unsigned m_weight; 81 bool m_italic; 82 bool m_printerFont; 83 FontRenderingMode m_renderingMode; 84 85 private: 86 static unsigned hashTableDeletedSize() { return 0xFFFFFFFFU; } 87 }; 88 89 inline unsigned computeHash(const FontPlatformDataCacheKey& fontKey) 90 { 91 unsigned hashCodes[4] = { 92 CaseFoldingHash::hash(fontKey.m_family), 93 fontKey.m_size, 94 fontKey.m_weight, 95 static_cast<unsigned>(fontKey.m_italic) << 2 | static_cast<unsigned>(fontKey.m_printerFont) << 1 | static_cast<unsigned>(fontKey.m_renderingMode) 96 }; 97 return StringImpl::computeHash(reinterpret_cast<UChar*>(hashCodes), sizeof(hashCodes) / sizeof(UChar)); 98 } 99 100 struct FontPlatformDataCacheKeyHash { 101 static unsigned hash(const FontPlatformDataCacheKey& font) 102 { 103 return computeHash(font); 104 } 105 106 static bool equal(const FontPlatformDataCacheKey& a, const FontPlatformDataCacheKey& b) 107 { 108 return a == b; 109 } 110 111 static const bool safeToCompareToEmptyOrDeleted = true; 112 }; 113 114 struct FontPlatformDataCacheKeyTraits : WTF::GenericHashTraits<FontPlatformDataCacheKey> { 115 static const bool emptyValueIsZero = true; 116 static const FontPlatformDataCacheKey& emptyValue() 117 { 118 DEFINE_STATIC_LOCAL(FontPlatformDataCacheKey, key, (nullAtom)); 119 return key; 120 } 121 static void constructDeletedValue(FontPlatformDataCacheKey& slot) 122 { 123 new (&slot) FontPlatformDataCacheKey(HashTableDeletedValue); 124 } 125 static bool isDeletedValue(const FontPlatformDataCacheKey& value) 126 { 127 return value.isHashTableDeletedValue(); 128 } 129 }; 130 131 typedef HashMap<FontPlatformDataCacheKey, FontPlatformData*, FontPlatformDataCacheKeyHash, FontPlatformDataCacheKeyTraits> FontPlatformDataCache; 132 133 static FontPlatformDataCache* gFontPlatformDataCache = 0; 134 135 static const AtomicString& alternateFamilyName(const AtomicString& familyName) 136 { 137 // Alias Courier <-> Courier New 138 DEFINE_STATIC_LOCAL(AtomicString, courier, ("Courier")); 139 DEFINE_STATIC_LOCAL(AtomicString, courierNew, ("Courier New")); 140 if (equalIgnoringCase(familyName, courier)) 141 return courierNew; 142 #if !OS(WINDOWS) 143 // On Windows, Courier New (truetype font) is always present and 144 // Courier is a bitmap font. So, we don't want to map Courier New to 145 // Courier. 146 if (equalIgnoringCase(familyName, courierNew)) 147 return courier; 148 #endif 149 150 // Alias Times and Times New Roman. 151 DEFINE_STATIC_LOCAL(AtomicString, times, ("Times")); 152 DEFINE_STATIC_LOCAL(AtomicString, timesNewRoman, ("Times New Roman")); 153 if (equalIgnoringCase(familyName, times)) 154 return timesNewRoman; 155 if (equalIgnoringCase(familyName, timesNewRoman)) 156 return times; 157 158 // Alias Arial and Helvetica 159 DEFINE_STATIC_LOCAL(AtomicString, arial, ("Arial")); 160 DEFINE_STATIC_LOCAL(AtomicString, helvetica, ("Helvetica")); 161 if (equalIgnoringCase(familyName, arial)) 162 return helvetica; 163 if (equalIgnoringCase(familyName, helvetica)) 164 return arial; 165 166 #if OS(WINDOWS) 167 // On Windows, bitmap fonts are blocked altogether so that we have to 168 // alias MS Sans Serif (bitmap font) -> Microsoft Sans Serif (truetype font) 169 DEFINE_STATIC_LOCAL(AtomicString, msSans, ("MS Sans Serif")); 170 DEFINE_STATIC_LOCAL(AtomicString, microsoftSans, ("Microsoft Sans Serif")); 171 if (equalIgnoringCase(familyName, msSans)) 172 return microsoftSans; 173 174 // Alias MS Serif (bitmap) -> Times New Roman (truetype font). There's no 175 // 'Microsoft Sans Serif-equivalent' for Serif. 176 static AtomicString msSerif("MS Serif"); 177 if (equalIgnoringCase(familyName, msSerif)) 178 return timesNewRoman; 179 #endif 180 181 return emptyAtom; 182 } 183 184 FontPlatformData* FontCache::getCachedFontPlatformData(const FontDescription& fontDescription, 185 const AtomicString& familyName, 186 bool checkingAlternateName) 187 { 188 if (!gFontPlatformDataCache) { 189 gFontPlatformDataCache = new FontPlatformDataCache; 190 platformInit(); 191 } 192 193 FontPlatformDataCacheKey key(familyName, fontDescription.computedPixelSize(), fontDescription.weight(), fontDescription.italic(), 194 fontDescription.usePrinterFont(), fontDescription.renderingMode()); 195 FontPlatformData* result = 0; 196 bool foundResult; 197 FontPlatformDataCache::iterator it = gFontPlatformDataCache->find(key); 198 if (it == gFontPlatformDataCache->end()) { 199 result = createFontPlatformData(fontDescription, familyName); 200 gFontPlatformDataCache->set(key, result); 201 foundResult = result; 202 } else { 203 result = it->second; 204 foundResult = true; 205 } 206 207 if (!foundResult && !checkingAlternateName) { 208 // We were unable to find a font. We have a small set of fonts that we alias to other names, 209 // e.g., Arial/Helvetica, Courier/Courier New, etc. Try looking up the font under the aliased name. 210 const AtomicString& alternateName = alternateFamilyName(familyName); 211 if (!alternateName.isEmpty()) 212 result = getCachedFontPlatformData(fontDescription, alternateName, true); 213 if (result) 214 gFontPlatformDataCache->set(key, new FontPlatformData(*result)); // Cache the result under the old name. 215 } 216 217 return result; 218 } 219 220 struct FontDataCacheKeyHash { 221 static unsigned hash(const FontPlatformData& platformData) 222 { 223 return platformData.hash(); 224 } 225 226 static bool equal(const FontPlatformData& a, const FontPlatformData& b) 227 { 228 return a == b; 229 } 230 231 static const bool safeToCompareToEmptyOrDeleted = true; 232 }; 233 234 struct FontDataCacheKeyTraits : WTF::GenericHashTraits<FontPlatformData> { 235 static const bool emptyValueIsZero = true; 236 static const bool needsDestruction = true; 237 static const FontPlatformData& emptyValue() 238 { 239 DEFINE_STATIC_LOCAL(FontPlatformData, key, (0.f, false, false)); 240 return key; 241 } 242 static void constructDeletedValue(FontPlatformData& slot) 243 { 244 new (&slot) FontPlatformData(HashTableDeletedValue); 245 } 246 static bool isDeletedValue(const FontPlatformData& value) 247 { 248 return value.isHashTableDeletedValue(); 249 } 250 }; 251 252 typedef HashMap<FontPlatformData, pair<SimpleFontData*, unsigned>, FontDataCacheKeyHash, FontDataCacheKeyTraits> FontDataCache; 253 254 static FontDataCache* gFontDataCache = 0; 255 256 const int cMaxInactiveFontData = 120; // Pretty Low Threshold 257 const float cTargetInactiveFontData = 100; 258 static ListHashSet<const SimpleFontData*>* gInactiveFontData = 0; 259 260 SimpleFontData* FontCache::getCachedFontData(const FontDescription& fontDescription, const AtomicString& family, bool checkingAlternateName) 261 { 262 FontPlatformData* platformData = getCachedFontPlatformData(fontDescription, family, checkingAlternateName); 263 if (!platformData) 264 return 0; 265 266 return getCachedFontData(platformData); 267 } 268 269 SimpleFontData* FontCache::getCachedFontData(const FontPlatformData* platformData) 270 { 271 if (!platformData) 272 return 0; 273 274 if (!gFontDataCache) { 275 gFontDataCache = new FontDataCache; 276 gInactiveFontData = new ListHashSet<const SimpleFontData*>; 277 } 278 279 FontDataCache::iterator result = gFontDataCache->find(*platformData); 280 if (result == gFontDataCache->end()) { 281 pair<SimpleFontData*, unsigned> newValue(new SimpleFontData(*platformData), 1); 282 gFontDataCache->set(*platformData, newValue); 283 return newValue.first; 284 } 285 if (!result.get()->second.second++) { 286 ASSERT(gInactiveFontData->contains(result.get()->second.first)); 287 gInactiveFontData->remove(result.get()->second.first); 288 } 289 290 return result.get()->second.first; 291 } 292 293 void FontCache::releaseFontData(const SimpleFontData* fontData) 294 { 295 ASSERT(gFontDataCache); 296 ASSERT(!fontData->isCustomFont()); 297 298 FontDataCache::iterator it = gFontDataCache->find(fontData->platformData()); 299 ASSERT(it != gFontDataCache->end()); 300 301 if (!--it->second.second) { 302 gInactiveFontData->add(fontData); 303 if (gInactiveFontData->size() > cMaxInactiveFontData) 304 purgeInactiveFontData(gInactiveFontData->size() - cTargetInactiveFontData); 305 } 306 } 307 308 void FontCache::purgeInactiveFontData(int count) 309 { 310 if (!gInactiveFontData) 311 return; 312 313 static bool isPurging; // Guard against reentry when e.g. a deleted FontData releases its small caps FontData. 314 if (isPurging) 315 return; 316 317 isPurging = true; 318 319 Vector<const SimpleFontData*, 20> fontDataToDelete; 320 ListHashSet<const SimpleFontData*>::iterator end = gInactiveFontData->end(); 321 ListHashSet<const SimpleFontData*>::iterator it = gInactiveFontData->begin(); 322 for (int i = 0; i < count && it != end; ++it, ++i) { 323 const SimpleFontData* fontData = *it.get(); 324 gFontDataCache->remove(fontData->platformData()); 325 fontDataToDelete.append(fontData); 326 } 327 328 if (it == end) { 329 // Removed everything 330 gInactiveFontData->clear(); 331 } else { 332 for (int i = 0; i < count; ++i) 333 gInactiveFontData->remove(gInactiveFontData->begin()); 334 } 335 336 size_t fontDataToDeleteCount = fontDataToDelete.size(); 337 for (size_t i = 0; i < fontDataToDeleteCount; ++i) 338 delete fontDataToDelete[i]; 339 340 if (gFontPlatformDataCache) { 341 Vector<FontPlatformDataCacheKey> keysToRemove; 342 keysToRemove.reserveInitialCapacity(gFontPlatformDataCache->size()); 343 FontPlatformDataCache::iterator platformDataEnd = gFontPlatformDataCache->end(); 344 for (FontPlatformDataCache::iterator platformData = gFontPlatformDataCache->begin(); platformData != platformDataEnd; ++platformData) { 345 if (platformData->second && !gFontDataCache->contains(*platformData->second)) 346 keysToRemove.append(platformData->first); 347 } 348 349 size_t keysToRemoveCount = keysToRemove.size(); 350 for (size_t i = 0; i < keysToRemoveCount; ++i) 351 delete gFontPlatformDataCache->take(keysToRemove[i]); 352 } 353 354 isPurging = false; 355 } 356 357 size_t FontCache::fontDataCount() 358 { 359 if (gFontDataCache) 360 return gFontDataCache->size(); 361 return 0; 362 } 363 364 size_t FontCache::inactiveFontDataCount() 365 { 366 if (gInactiveFontData) 367 return gInactiveFontData->size(); 368 return 0; 369 } 370 371 const FontData* FontCache::getFontData(const Font& font, int& familyIndex, FontSelector* fontSelector) 372 { 373 SimpleFontData* result = 0; 374 375 int startIndex = familyIndex; 376 const FontFamily* startFamily = &font.fontDescription().family(); 377 for (int i = 0; startFamily && i < startIndex; i++) 378 startFamily = startFamily->next(); 379 const FontFamily* currFamily = startFamily; 380 while (currFamily && !result) { 381 familyIndex++; 382 if (currFamily->family().length()) { 383 if (fontSelector) { 384 FontData* data = fontSelector->getFontData(font.fontDescription(), currFamily->family()); 385 if (data) 386 return data; 387 } 388 result = getCachedFontData(font.fontDescription(), currFamily->family()); 389 } 390 currFamily = currFamily->next(); 391 } 392 393 if (!currFamily) 394 familyIndex = cAllFamiliesScanned; 395 396 if (!result) 397 // We didn't find a font. Try to find a similar font using our own specific knowledge about our platform. 398 // For example on OS X, we know to map any families containing the words Arabic, Pashto, or Urdu to the 399 // Geeza Pro font. 400 result = getSimilarFontPlatformData(font); 401 402 if (!result && startIndex == 0) { 403 // If it's the primary font that we couldn't find, we try the following. In all other cases, we will 404 // just use per-character system fallback. 405 406 if (fontSelector) { 407 // Try the user's preferred standard font. 408 if (FontData* data = fontSelector->getFontData(font.fontDescription(), "-webkit-standard")) 409 return data; 410 } 411 412 // Still no result. Hand back our last resort fallback font. 413 result = getLastResortFallbackFont(font.fontDescription()); 414 } 415 return result; 416 } 417 418 static HashSet<FontSelector*>* gClients; 419 420 void FontCache::addClient(FontSelector* client) 421 { 422 if (!gClients) 423 gClients = new HashSet<FontSelector*>; 424 425 ASSERT(!gClients->contains(client)); 426 gClients->add(client); 427 } 428 429 void FontCache::removeClient(FontSelector* client) 430 { 431 ASSERT(gClients); 432 ASSERT(gClients->contains(client)); 433 434 gClients->remove(client); 435 } 436 437 static unsigned gGeneration = 0; 438 439 unsigned FontCache::generation() 440 { 441 return gGeneration; 442 } 443 444 void FontCache::invalidate() 445 { 446 if (!gClients) { 447 ASSERT(!gFontPlatformDataCache); 448 return; 449 } 450 451 if (gFontPlatformDataCache) { 452 deleteAllValues(*gFontPlatformDataCache); 453 delete gFontPlatformDataCache; 454 gFontPlatformDataCache = new FontPlatformDataCache; 455 } 456 457 gGeneration++; 458 459 Vector<RefPtr<FontSelector> > clients; 460 size_t numClients = gClients->size(); 461 clients.reserveInitialCapacity(numClients); 462 HashSet<FontSelector*>::iterator end = gClients->end(); 463 for (HashSet<FontSelector*>::iterator it = gClients->begin(); it != end; ++it) 464 clients.append(*it); 465 466 ASSERT(numClients == clients.size()); 467 for (size_t i = 0; i < numClients; ++i) 468 clients[i]->fontCacheInvalidated(); 469 470 purgeInactiveFontData(); 471 } 472 473 } // namespace WebCore 474