2 * Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
3 * Copyright (C) 2007 Nicholas Shanks <webkit@nickshanks.com>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
31 #include "FontCache.h"
34 #include "FontFallbackList.h"
35 #include "FontPlatformData.h"
36 #include "FontSelector.h"
37 #include "GlyphPageTreeNode.h"
38 #include "WebKitFontFamilyNames.h"
39 #include <wtf/HashMap.h>
40 #include <wtf/ListHashSet.h>
41 #include <wtf/StdLibExtras.h>
42 #include <wtf/text/StringHash.h>
48 FontCache* fontCache()
50 DEFINE_STATIC_LOCAL(FontCache, globalFontCache, ());
51 return &globalFontCache;
54 FontCache::FontCache()
55 : m_purgePreventCount(0)
59 struct FontPlatformDataCacheKey {
60 WTF_MAKE_FAST_ALLOCATED;
62 FontPlatformDataCacheKey(const AtomicString& family = AtomicString(), unsigned size = 0, unsigned weight = 0, bool italic = false,
63 bool isPrinterFont = false, FontRenderingMode renderingMode = NormalRenderingMode, FontOrientation orientation = Horizontal,
64 TextOrientation textOrientation = TextOrientationVerticalRight, FontWidthVariant widthVariant = RegularWidth)
69 , m_printerFont(isPrinterFont)
70 , m_renderingMode(renderingMode)
71 , m_orientation(orientation)
72 , m_textOrientation(textOrientation)
73 , m_widthVariant(widthVariant)
77 FontPlatformDataCacheKey(HashTableDeletedValueType) : m_size(hashTableDeletedSize()) { }
78 bool isHashTableDeletedValue() const { return m_size == hashTableDeletedSize(); }
80 bool operator==(const FontPlatformDataCacheKey& other) const
82 return equalIgnoringCase(m_family, other.m_family) && m_size == other.m_size &&
83 m_weight == other.m_weight && m_italic == other.m_italic && m_printerFont == other.m_printerFont &&
84 m_renderingMode == other.m_renderingMode && m_orientation == other.m_orientation && m_textOrientation == other.m_textOrientation && m_widthVariant == other.m_widthVariant;
89 AtomicString m_family;
92 FontRenderingMode m_renderingMode;
93 FontOrientation m_orientation;
94 TextOrientation m_textOrientation;
95 FontWidthVariant m_widthVariant;
98 static unsigned hashTableDeletedSize() { return 0xFFFFFFFFU; }
101 inline unsigned computeHash(const FontPlatformDataCacheKey& fontKey)
103 unsigned hashCodes[5] = {
104 CaseFoldingHash::hash(fontKey.m_family),
107 fontKey.m_widthVariant,
108 static_cast<unsigned>(fontKey.m_textOrientation) << 4 | static_cast<unsigned>(fontKey.m_orientation) << 3 | static_cast<unsigned>(fontKey.m_italic) << 2 | static_cast<unsigned>(fontKey.m_printerFont) << 1 | static_cast<unsigned>(fontKey.m_renderingMode)
110 return StringHasher::hashMemory<sizeof(hashCodes)>(hashCodes);
113 struct FontPlatformDataCacheKeyHash {
114 static unsigned hash(const FontPlatformDataCacheKey& font)
116 return computeHash(font);
119 static bool equal(const FontPlatformDataCacheKey& a, const FontPlatformDataCacheKey& b)
124 static const bool safeToCompareToEmptyOrDeleted = true;
127 struct FontPlatformDataCacheKeyTraits : WTF::SimpleClassHashTraits<FontPlatformDataCacheKey> { };
129 typedef HashMap<FontPlatformDataCacheKey, FontPlatformData*, FontPlatformDataCacheKeyHash, FontPlatformDataCacheKeyTraits> FontPlatformDataCache;
131 static FontPlatformDataCache* gFontPlatformDataCache = 0;
133 static const AtomicString& alternateFamilyName(const AtomicString& familyName)
135 // Alias Courier <-> Courier New
136 DEFINE_STATIC_LOCAL(AtomicString, courier, ("Courier"));
137 DEFINE_STATIC_LOCAL(AtomicString, courierNew, ("Courier New"));
138 if (equalIgnoringCase(familyName, courier))
141 // On Windows, Courier New (truetype font) is always present and
142 // Courier is a bitmap font. So, we don't want to map Courier New to
144 if (equalIgnoringCase(familyName, courierNew))
148 // Alias Times and Times New Roman.
149 DEFINE_STATIC_LOCAL(AtomicString, times, ("Times"));
150 DEFINE_STATIC_LOCAL(AtomicString, timesNewRoman, ("Times New Roman"));
151 if (equalIgnoringCase(familyName, times))
152 return timesNewRoman;
153 if (equalIgnoringCase(familyName, timesNewRoman))
156 // Alias Arial and Helvetica
157 DEFINE_STATIC_LOCAL(AtomicString, arial, ("Arial"));
158 DEFINE_STATIC_LOCAL(AtomicString, helvetica, ("Helvetica"));
159 if (equalIgnoringCase(familyName, arial))
161 if (equalIgnoringCase(familyName, helvetica))
165 // On Windows, bitmap fonts are blocked altogether so that we have to
166 // alias MS Sans Serif (bitmap font) -> Microsoft Sans Serif (truetype font)
167 DEFINE_STATIC_LOCAL(AtomicString, msSans, ("MS Sans Serif"));
168 DEFINE_STATIC_LOCAL(AtomicString, microsoftSans, ("Microsoft Sans Serif"));
169 if (equalIgnoringCase(familyName, msSans))
170 return microsoftSans;
172 // Alias MS Serif (bitmap) -> Times New Roman (truetype font). There's no
173 // 'Microsoft Sans Serif-equivalent' for Serif.
174 static AtomicString msSerif("MS Serif");
175 if (equalIgnoringCase(familyName, msSerif))
176 return timesNewRoman;
182 FontPlatformData* FontCache::getCachedFontPlatformData(const FontDescription& fontDescription,
183 const AtomicString& familyName,
184 bool checkingAlternateName)
186 if (!gFontPlatformDataCache) {
187 gFontPlatformDataCache = new FontPlatformDataCache;
191 FontPlatformDataCacheKey key(familyName, fontDescription.computedPixelSize(), fontDescription.weight(), fontDescription.italic(),
192 fontDescription.usePrinterFont(), fontDescription.renderingMode(), fontDescription.orientation(),
193 fontDescription.textOrientation(), fontDescription.widthVariant());
194 FontPlatformData* result = 0;
196 FontPlatformDataCache::iterator it = gFontPlatformDataCache->find(key);
197 if (it == gFontPlatformDataCache->end()) {
198 result = createFontPlatformData(fontDescription, familyName);
199 gFontPlatformDataCache->set(key, result);
200 foundResult = result;
206 if (!foundResult && !checkingAlternateName) {
207 // We were unable to find a font. We have a small set of fonts that we alias to other names,
208 // e.g., Arial/Helvetica, Courier/Courier New, etc. Try looking up the font under the aliased name.
209 const AtomicString& alternateName = alternateFamilyName(familyName);
210 if (!alternateName.isEmpty())
211 result = getCachedFontPlatformData(fontDescription, alternateName, true);
213 gFontPlatformDataCache->set(key, new FontPlatformData(*result)); // Cache the result under the old name.
219 struct FontDataCacheKeyHash {
220 static unsigned hash(const FontPlatformData& platformData)
222 return platformData.hash();
225 static bool equal(const FontPlatformData& a, const FontPlatformData& b)
230 static const bool safeToCompareToEmptyOrDeleted = true;
233 struct FontDataCacheKeyTraits : WTF::GenericHashTraits<FontPlatformData> {
234 static const bool emptyValueIsZero = true;
235 static const bool needsDestruction = true;
236 static const FontPlatformData& emptyValue()
238 DEFINE_STATIC_LOCAL(FontPlatformData, key, (0.f, false, false));
241 static void constructDeletedValue(FontPlatformData& slot)
243 new (NotNull, &slot) FontPlatformData(HashTableDeletedValue);
245 static bool isDeletedValue(const FontPlatformData& value)
247 return value.isHashTableDeletedValue();
251 typedef HashMap<FontPlatformData, pair<SimpleFontData*, unsigned>, FontDataCacheKeyHash, FontDataCacheKeyTraits> FontDataCache;
253 static FontDataCache* gFontDataCache = 0;
255 #if PLATFORM(CHROMIUM) && !OS(ANDROID)
256 const int cMaxInactiveFontData = 250;
257 const int cTargetInactiveFontData = 200;
259 const int cMaxInactiveFontData = 50; // Pretty Low Threshold
260 const int cTargetInactiveFontData = 30;
262 static ListHashSet<const SimpleFontData*>* gInactiveFontData = 0;
264 SimpleFontData* FontCache::getCachedFontData(const FontDescription& fontDescription, const AtomicString& family, bool checkingAlternateName, ShouldRetain shouldRetain)
266 FontPlatformData* platformData = getCachedFontPlatformData(fontDescription, family, checkingAlternateName);
270 return getCachedFontData(platformData, shouldRetain);
273 SimpleFontData* FontCache::getCachedFontData(const FontPlatformData* platformData, ShouldRetain shouldRetain)
279 if (shouldRetain == DoNotRetain)
280 ASSERT(m_purgePreventCount);
283 if (!gFontDataCache) {
284 gFontDataCache = new FontDataCache;
285 gInactiveFontData = new ListHashSet<const SimpleFontData*>;
288 FontDataCache::iterator result = gFontDataCache->find(*platformData);
289 if (result == gFontDataCache->end()) {
290 pair<SimpleFontData*, unsigned> newValue(new SimpleFontData(*platformData), shouldRetain == Retain ? 1 : 0);
291 gFontDataCache->set(*platformData, newValue);
292 if (shouldRetain == DoNotRetain)
293 gInactiveFontData->add(newValue.first);
294 return newValue.first;
297 if (!result.get()->second.second) {
298 ASSERT(gInactiveFontData->contains(result.get()->second.first));
299 gInactiveFontData->remove(result.get()->second.first);
302 if (shouldRetain == Retain)
303 result.get()->second.second++;
304 else if (!result.get()->second.second) {
305 // If shouldRetain is DoNotRetain and count is 0, we want to remove the fontData from
306 // gInactiveFontData (above) and re-add here to update LRU position.
307 gInactiveFontData->add(result.get()->second.first);
310 return result.get()->second.first;
313 SimpleFontData* FontCache::getNonRetainedLastResortFallbackFont(const FontDescription& fontDescription)
315 return getLastResortFallbackFont(fontDescription, DoNotRetain);
318 void FontCache::releaseFontData(const SimpleFontData* fontData)
320 ASSERT(gFontDataCache);
321 ASSERT(!fontData->isCustomFont());
323 FontDataCache::iterator it = gFontDataCache->find(fontData->platformData());
324 ASSERT(it != gFontDataCache->end());
326 ASSERT(it->second.second);
327 if (!--it->second.second)
328 gInactiveFontData->add(fontData);
331 void FontCache::purgeInactiveFontDataIfNeeded()
333 if (gInactiveFontData && !m_purgePreventCount && gInactiveFontData->size() > cMaxInactiveFontData)
334 purgeInactiveFontData(gInactiveFontData->size() - cTargetInactiveFontData);
337 void FontCache::purgeInactiveFontData(int count)
339 if (!gInactiveFontData || m_purgePreventCount)
342 static bool isPurging; // Guard against reentry when e.g. a deleted FontData releases its small caps FontData.
348 Vector<const SimpleFontData*, 20> fontDataToDelete;
349 ListHashSet<const SimpleFontData*>::iterator end = gInactiveFontData->end();
350 ListHashSet<const SimpleFontData*>::iterator it = gInactiveFontData->begin();
351 for (int i = 0; i < count && it != end; ++it, ++i) {
352 const SimpleFontData* fontData = *it.get();
353 gFontDataCache->remove(fontData->platformData());
354 // We should not delete SimpleFontData here because deletion can modify gInactiveFontData. See http://trac.webkit.org/changeset/44011
355 fontDataToDelete.append(fontData);
359 // Removed everything
360 gInactiveFontData->clear();
362 for (int i = 0; i < count; ++i)
363 gInactiveFontData->remove(gInactiveFontData->begin());
366 size_t fontDataToDeleteCount = fontDataToDelete.size();
367 for (size_t i = 0; i < fontDataToDeleteCount; ++i)
368 delete fontDataToDelete[i];
370 if (gFontPlatformDataCache) {
371 Vector<FontPlatformDataCacheKey> keysToRemove;
372 keysToRemove.reserveInitialCapacity(gFontPlatformDataCache->size());
373 FontPlatformDataCache::iterator platformDataEnd = gFontPlatformDataCache->end();
374 for (FontPlatformDataCache::iterator platformData = gFontPlatformDataCache->begin(); platformData != platformDataEnd; ++platformData) {
375 if (platformData->second && !gFontDataCache->contains(*platformData->second))
376 keysToRemove.append(platformData->first);
379 size_t keysToRemoveCount = keysToRemove.size();
380 for (size_t i = 0; i < keysToRemoveCount; ++i)
381 delete gFontPlatformDataCache->take(keysToRemove[i]);
387 size_t FontCache::fontDataCount()
390 return gFontDataCache->size();
394 size_t FontCache::inactiveFontDataCount()
396 if (gInactiveFontData)
397 return gInactiveFontData->size();
401 const FontData* FontCache::getFontData(const Font& font, int& familyIndex, FontSelector* fontSelector)
403 FontData* result = 0;
405 int startIndex = familyIndex;
406 const FontFamily* startFamily = &font.fontDescription().family();
407 for (int i = 0; startFamily && i < startIndex; i++)
408 startFamily = startFamily->next();
409 const FontFamily* currFamily = startFamily;
410 while (currFamily && !result) {
412 if (currFamily->family().length()) {
414 result = fontSelector->getFontData(font.fontDescription(), currFamily->family());
417 result = getCachedFontData(font.fontDescription(), currFamily->family());
419 currFamily = currFamily->next();
423 familyIndex = cAllFamiliesScanned;
426 // We didn't find a font. Try to find a similar font using our own specific knowledge about our platform.
427 // For example on OS X, we know to map any families containing the words Arabic, Pashto, or Urdu to the
429 result = getSimilarFontPlatformData(font);
431 if (!result && startIndex == 0) {
432 // If it's the primary font that we couldn't find, we try the following. In all other cases, we will
433 // just use per-character system fallback.
436 // Try the user's preferred standard font.
437 if (FontData* data = fontSelector->getFontData(font.fontDescription(), standardFamily))
441 // Still no result. Hand back our last resort fallback font.
442 result = getLastResortFallbackFont(font.fontDescription());
447 static HashSet<FontSelector*>* gClients;
449 void FontCache::addClient(FontSelector* client)
452 gClients = new HashSet<FontSelector*>;
454 ASSERT(!gClients->contains(client));
455 gClients->add(client);
458 void FontCache::removeClient(FontSelector* client)
461 ASSERT(gClients->contains(client));
463 gClients->remove(client);
466 static unsigned short gGeneration = 0;
468 unsigned short FontCache::generation()
473 void FontCache::invalidate()
476 ASSERT(!gFontPlatformDataCache);
480 if (gFontPlatformDataCache) {
481 deleteAllValues(*gFontPlatformDataCache);
482 delete gFontPlatformDataCache;
483 gFontPlatformDataCache = new FontPlatformDataCache;
488 Vector<RefPtr<FontSelector> > clients;
489 size_t numClients = gClients->size();
490 clients.reserveInitialCapacity(numClients);
491 HashSet<FontSelector*>::iterator end = gClients->end();
492 for (HashSet<FontSelector*>::iterator it = gClients->begin(); it != end; ++it)
495 ASSERT(numClients == clients.size());
496 for (size_t i = 0; i < numClients; ++i)
497 clients[i]->fontCacheInvalidated();
499 purgeInactiveFontData();
502 } // namespace WebCore