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 "StringHash.h"
38 #include <wtf/HashMap.h>
39 #include <wtf/ListHashSet.h>
40 #include <wtf/StdLibExtras.h>
46 FontCache* fontCache()
48 DEFINE_STATIC_LOCAL(FontCache, globalFontCache, ());
49 return &globalFontCache;
52 FontCache::FontCache()
56 struct FontPlatformDataCacheKey {
57 FontPlatformDataCacheKey(const AtomicString& family = AtomicString(), unsigned size = 0, unsigned weight = 0, bool italic = false,
58 bool isPrinterFont = false, FontRenderingMode renderingMode = NormalRenderingMode)
63 , m_printerFont(isPrinterFont)
64 , m_renderingMode(renderingMode)
68 FontPlatformDataCacheKey(HashTableDeletedValueType) : m_size(hashTableDeletedSize()) { }
69 bool isHashTableDeletedValue() const { return m_size == hashTableDeletedSize(); }
71 bool operator==(const FontPlatformDataCacheKey& other) const
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;
78 AtomicString m_family;
83 FontRenderingMode m_renderingMode;
86 static unsigned hashTableDeletedSize() { return 0xFFFFFFFFU; }
89 inline unsigned computeHash(const FontPlatformDataCacheKey& fontKey)
91 unsigned hashCodes[4] = {
92 CaseFoldingHash::hash(fontKey.m_family),
95 static_cast<unsigned>(fontKey.m_italic) << 2 | static_cast<unsigned>(fontKey.m_printerFont) << 1 | static_cast<unsigned>(fontKey.m_renderingMode)
97 return StringImpl::computeHash(reinterpret_cast<UChar*>(hashCodes), sizeof(hashCodes) / sizeof(UChar));
100 struct FontPlatformDataCacheKeyHash {
101 static unsigned hash(const FontPlatformDataCacheKey& font)
103 return computeHash(font);
106 static bool equal(const FontPlatformDataCacheKey& a, const FontPlatformDataCacheKey& b)
111 static const bool safeToCompareToEmptyOrDeleted = true;
114 struct FontPlatformDataCacheKeyTraits : WTF::GenericHashTraits<FontPlatformDataCacheKey> {
115 static const bool emptyValueIsZero = true;
116 static const FontPlatformDataCacheKey& emptyValue()
118 DEFINE_STATIC_LOCAL(FontPlatformDataCacheKey, key, (nullAtom));
121 static void constructDeletedValue(FontPlatformDataCacheKey& slot)
123 new (&slot) FontPlatformDataCacheKey(HashTableDeletedValue);
125 static bool isDeletedValue(const FontPlatformDataCacheKey& value)
127 return value.isHashTableDeletedValue();
131 typedef HashMap<FontPlatformDataCacheKey, FontPlatformData*, FontPlatformDataCacheKeyHash, FontPlatformDataCacheKeyTraits> FontPlatformDataCache;
133 static FontPlatformDataCache* gFontPlatformDataCache = 0;
135 static const AtomicString& alternateFamilyName(const AtomicString& familyName)
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))
142 #if !PLATFORM(WIN_OS)
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
146 if (equalIgnoringCase(familyName, courierNew))
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))
158 // Alias Arial and Helvetica
159 DEFINE_STATIC_LOCAL(AtomicString, arial, ("Arial"));
160 DEFINE_STATIC_LOCAL(AtomicString, helvetica, ("Helvetica"));
161 if (equalIgnoringCase(familyName, arial))
163 if (equalIgnoringCase(familyName, helvetica))
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;
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;
184 FontPlatformData* FontCache::getCachedFontPlatformData(const FontDescription& fontDescription,
185 const AtomicString& familyName,
186 bool checkingAlternateName)
188 if (!gFontPlatformDataCache) {
189 gFontPlatformDataCache = new FontPlatformDataCache;
193 FontPlatformDataCacheKey key(familyName, fontDescription.computedPixelSize(), fontDescription.weight(), fontDescription.italic(),
194 fontDescription.usePrinterFont(), fontDescription.renderingMode());
195 FontPlatformData* result = 0;
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;
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);
214 gFontPlatformDataCache->set(key, new FontPlatformData(*result)); // Cache the result under the old name.
220 struct FontDataCacheKeyHash {
221 static unsigned hash(const FontPlatformData& platformData)
223 return platformData.hash();
226 static bool equal(const FontPlatformData& a, const FontPlatformData& b)
231 static const bool safeToCompareToEmptyOrDeleted = true;
234 struct FontDataCacheKeyTraits : WTF::GenericHashTraits<FontPlatformData> {
235 static const bool emptyValueIsZero = true;
236 static const bool needsDestruction = true;
237 static const FontPlatformData& emptyValue()
239 DEFINE_STATIC_LOCAL(FontPlatformData, key, ());
242 static void constructDeletedValue(FontPlatformData& slot)
244 new (&slot) FontPlatformData(HashTableDeletedValue);
246 static bool isDeletedValue(const FontPlatformData& value)
248 return value.isHashTableDeletedValue();
252 typedef HashMap<FontPlatformData, pair<SimpleFontData*, unsigned>, FontDataCacheKeyHash, FontDataCacheKeyTraits> FontDataCache;
254 static FontDataCache* gFontDataCache = 0;
256 const int cMaxInactiveFontData = 120; // Pretty Low Threshold
257 const float cTargetInactiveFontData = 100;
258 static ListHashSet<const SimpleFontData*>* gInactiveFontData = 0;
260 SimpleFontData* FontCache::getCachedFontData(const FontPlatformData* platformData)
265 if (!gFontDataCache) {
266 gFontDataCache = new FontDataCache;
267 gInactiveFontData = new ListHashSet<const SimpleFontData*>;
270 FontDataCache::iterator result = gFontDataCache->find(*platformData);
271 if (result == gFontDataCache->end()) {
272 pair<SimpleFontData*, unsigned> newValue(new SimpleFontData(*platformData), 1);
273 gFontDataCache->set(*platformData, newValue);
274 return newValue.first;
276 if (!result.get()->second.second++) {
277 ASSERT(gInactiveFontData->contains(result.get()->second.first));
278 gInactiveFontData->remove(result.get()->second.first);
281 return result.get()->second.first;
284 void FontCache::releaseFontData(const SimpleFontData* fontData)
286 ASSERT(gFontDataCache);
287 ASSERT(!fontData->isCustomFont());
289 FontDataCache::iterator it = gFontDataCache->find(fontData->platformData());
290 ASSERT(it != gFontDataCache->end());
292 if (!--it->second.second) {
293 gInactiveFontData->add(fontData);
294 if (gInactiveFontData->size() > cMaxInactiveFontData)
295 purgeInactiveFontData(gInactiveFontData->size() - cTargetInactiveFontData);
299 void FontCache::purgeInactiveFontData(int count)
301 if (!gInactiveFontData)
304 static bool isPurging; // Guard against reentry when e.g. a deleted FontData releases its small caps FontData.
310 ListHashSet<const SimpleFontData*>::iterator end = gInactiveFontData->end();
311 ListHashSet<const SimpleFontData*>::iterator it = gInactiveFontData->begin();
312 for (int i = 0; i < count && it != end; ++it, ++i) {
313 const SimpleFontData* fontData = *it.get();
314 gFontDataCache->remove(fontData->platformData());
319 // Removed everything
320 gInactiveFontData->clear();
322 for (int i = 0; i < count; ++i)
323 gInactiveFontData->remove(gInactiveFontData->begin());
326 Vector<FontPlatformDataCacheKey> keysToRemove;
327 keysToRemove.reserveInitialCapacity(gFontPlatformDataCache->size());
328 FontPlatformDataCache::iterator platformDataEnd = gFontPlatformDataCache->end();
329 for (FontPlatformDataCache::iterator platformData = gFontPlatformDataCache->begin(); platformData != platformDataEnd; ++platformData) {
330 if (platformData->second && !gFontDataCache->contains(*platformData->second))
331 keysToRemove.append(platformData->first);
334 size_t keysToRemoveCount = keysToRemove.size();
335 for (size_t i = 0; i < keysToRemoveCount; ++i)
336 delete gFontPlatformDataCache->take(keysToRemove[i]);
341 size_t FontCache::fontDataCount()
344 return gFontDataCache->size();
348 size_t FontCache::inactiveFontDataCount()
350 if (gInactiveFontData)
351 return gInactiveFontData->size();
355 const FontData* FontCache::getFontData(const Font& font, int& familyIndex, FontSelector* fontSelector)
357 FontPlatformData* result = 0;
359 int startIndex = familyIndex;
360 const FontFamily* startFamily = &font.fontDescription().family();
361 for (int i = 0; startFamily && i < startIndex; i++)
362 startFamily = startFamily->next();
363 const FontFamily* currFamily = startFamily;
364 while (currFamily && !result) {
366 if (currFamily->family().length()) {
368 FontData* data = fontSelector->getFontData(font.fontDescription(), currFamily->family());
372 result = getCachedFontPlatformData(font.fontDescription(), currFamily->family());
374 currFamily = currFamily->next();
378 familyIndex = cAllFamiliesScanned;
381 // We didn't find a font. Try to find a similar font using our own specific knowledge about our platform.
382 // For example on OS X, we know to map any families containing the words Arabic, Pashto, or Urdu to the
384 result = getSimilarFontPlatformData(font);
386 if (!result && startIndex == 0) {
387 // If it's the primary font that we couldn't find, we try the following. In all other cases, we will
388 // just use per-character system fallback.
391 // Try the user's preferred standard font.
392 if (FontData* data = fontSelector->getFontData(font.fontDescription(), "-webkit-standard"))
396 // Still no result. Hand back our last resort fallback font.
397 result = getLastResortFallbackFont(font.fontDescription());
400 // Now that we have a result, we need to go from FontPlatformData -> FontData.
401 return getCachedFontData(result);
404 static HashSet<FontSelector*>* gClients;
406 void FontCache::addClient(FontSelector* client)
409 gClients = new HashSet<FontSelector*>;
411 ASSERT(!gClients->contains(client));
412 gClients->add(client);
415 void FontCache::removeClient(FontSelector* client)
418 ASSERT(gClients->contains(client));
420 gClients->remove(client);
423 static unsigned gGeneration = 0;
425 unsigned FontCache::generation()
430 void FontCache::invalidate()
433 ASSERT(!gFontPlatformDataCache);
437 if (gFontPlatformDataCache) {
438 deleteAllValues(*gFontPlatformDataCache);
439 delete gFontPlatformDataCache;
440 gFontPlatformDataCache = new FontPlatformDataCache;
445 Vector<RefPtr<FontSelector> > clients;
446 size_t numClients = gClients->size();
447 clients.reserveInitialCapacity(numClients);
448 HashSet<FontSelector*>::iterator end = gClients->end();
449 for (HashSet<FontSelector*>::iterator it = gClients->begin(); it != end; ++it)
452 ASSERT(numClients == clients.size());
453 for (size_t i = 0; i < numClients; ++i)
454 clients[i]->fontCacheInvalidated();
456 purgeInactiveFontData();
459 } // namespace WebCore