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 <wtf/HashMap.h>
38 #include <wtf/ListHashSet.h>
39 #include <wtf/StdLibExtras.h>
40 #include <wtf/text/StringHash.h>
46 FontCache* fontCache()
48 DEFINE_STATIC_LOCAL(FontCache, globalFontCache, ());
49 return &globalFontCache;
52 FontCache::FontCache()
56 struct FontPlatformDataCacheKey {
57 WTF_MAKE_FAST_ALLOCATED;
59 FontPlatformDataCacheKey(const AtomicString& family = AtomicString(), unsigned size = 0, unsigned weight = 0, bool italic = false,
60 bool isPrinterFont = false, FontRenderingMode renderingMode = NormalRenderingMode, FontOrientation orientation = Horizontal, FontWidthVariant widthVariant = RegularWidth)
65 , m_printerFont(isPrinterFont)
66 , m_renderingMode(renderingMode)
67 , m_orientation(orientation)
68 , m_widthVariant(widthVariant)
72 FontPlatformDataCacheKey(HashTableDeletedValueType) : m_size(hashTableDeletedSize()) { }
73 bool isHashTableDeletedValue() const { return m_size == hashTableDeletedSize(); }
75 bool operator==(const FontPlatformDataCacheKey& other) const
77 return equalIgnoringCase(m_family, other.m_family) && m_size == other.m_size &&
78 m_weight == other.m_weight && m_italic == other.m_italic && m_printerFont == other.m_printerFont &&
79 m_renderingMode == other.m_renderingMode && m_orientation == other.m_orientation && m_widthVariant == other.m_widthVariant;
84 AtomicString m_family;
87 FontRenderingMode m_renderingMode;
88 FontOrientation m_orientation;
89 FontWidthVariant m_widthVariant;
92 static unsigned hashTableDeletedSize() { return 0xFFFFFFFFU; }
95 inline unsigned computeHash(const FontPlatformDataCacheKey& fontKey)
97 unsigned hashCodes[5] = {
98 CaseFoldingHash::hash(fontKey.m_family),
101 fontKey.m_widthVariant,
102 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)
104 return WTF::StringHasher::createBlobHash<sizeof(hashCodes)>(hashCodes);
107 struct FontPlatformDataCacheKeyHash {
108 static unsigned hash(const FontPlatformDataCacheKey& font)
110 return computeHash(font);
113 static bool equal(const FontPlatformDataCacheKey& a, const FontPlatformDataCacheKey& b)
118 static const bool safeToCompareToEmptyOrDeleted = true;
121 struct FontPlatformDataCacheKeyTraits : WTF::GenericHashTraits<FontPlatformDataCacheKey> {
122 static const bool emptyValueIsZero = true;
123 static const FontPlatformDataCacheKey& emptyValue()
125 DEFINE_STATIC_LOCAL(FontPlatformDataCacheKey, key, (nullAtom));
128 static void constructDeletedValue(FontPlatformDataCacheKey& slot)
130 new (&slot) FontPlatformDataCacheKey(HashTableDeletedValue);
132 static bool isDeletedValue(const FontPlatformDataCacheKey& value)
134 return value.isHashTableDeletedValue();
138 typedef HashMap<FontPlatformDataCacheKey, FontPlatformData*, FontPlatformDataCacheKeyHash, FontPlatformDataCacheKeyTraits> FontPlatformDataCache;
140 static FontPlatformDataCache* gFontPlatformDataCache = 0;
142 static const AtomicString& alternateFamilyName(const AtomicString& familyName)
144 // Alias Courier <-> Courier New
145 DEFINE_STATIC_LOCAL(AtomicString, courier, ("Courier"));
146 DEFINE_STATIC_LOCAL(AtomicString, courierNew, ("Courier New"));
147 if (equalIgnoringCase(familyName, courier))
150 // On Windows, Courier New (truetype font) is always present and
151 // Courier is a bitmap font. So, we don't want to map Courier New to
153 if (equalIgnoringCase(familyName, courierNew))
157 // Alias Times and Times New Roman.
158 DEFINE_STATIC_LOCAL(AtomicString, times, ("Times"));
159 DEFINE_STATIC_LOCAL(AtomicString, timesNewRoman, ("Times New Roman"));
160 if (equalIgnoringCase(familyName, times))
161 return timesNewRoman;
162 if (equalIgnoringCase(familyName, timesNewRoman))
165 // Alias Arial and Helvetica
166 DEFINE_STATIC_LOCAL(AtomicString, arial, ("Arial"));
167 DEFINE_STATIC_LOCAL(AtomicString, helvetica, ("Helvetica"));
168 if (equalIgnoringCase(familyName, arial))
170 if (equalIgnoringCase(familyName, helvetica))
174 // On Windows, bitmap fonts are blocked altogether so that we have to
175 // alias MS Sans Serif (bitmap font) -> Microsoft Sans Serif (truetype font)
176 DEFINE_STATIC_LOCAL(AtomicString, msSans, ("MS Sans Serif"));
177 DEFINE_STATIC_LOCAL(AtomicString, microsoftSans, ("Microsoft Sans Serif"));
178 if (equalIgnoringCase(familyName, msSans))
179 return microsoftSans;
181 // Alias MS Serif (bitmap) -> Times New Roman (truetype font). There's no
182 // 'Microsoft Sans Serif-equivalent' for Serif.
183 static AtomicString msSerif("MS Serif");
184 if (equalIgnoringCase(familyName, msSerif))
185 return timesNewRoman;
191 FontPlatformData* FontCache::getCachedFontPlatformData(const FontDescription& fontDescription,
192 const AtomicString& familyName,
193 bool checkingAlternateName)
195 if (!gFontPlatformDataCache) {
196 gFontPlatformDataCache = new FontPlatformDataCache;
200 FontPlatformDataCacheKey key(familyName, fontDescription.computedPixelSize(), fontDescription.weight(), fontDescription.italic(),
201 fontDescription.usePrinterFont(), fontDescription.renderingMode(), fontDescription.orientation(), fontDescription.widthVariant());
202 FontPlatformData* result = 0;
204 FontPlatformDataCache::iterator it = gFontPlatformDataCache->find(key);
205 if (it == gFontPlatformDataCache->end()) {
206 result = createFontPlatformData(fontDescription, familyName);
207 gFontPlatformDataCache->set(key, result);
208 foundResult = result;
214 if (!foundResult && !checkingAlternateName) {
215 // We were unable to find a font. We have a small set of fonts that we alias to other names,
216 // e.g., Arial/Helvetica, Courier/Courier New, etc. Try looking up the font under the aliased name.
217 const AtomicString& alternateName = alternateFamilyName(familyName);
218 if (!alternateName.isEmpty())
219 result = getCachedFontPlatformData(fontDescription, alternateName, true);
221 gFontPlatformDataCache->set(key, new FontPlatformData(*result)); // Cache the result under the old name.
227 struct FontDataCacheKeyHash {
228 static unsigned hash(const FontPlatformData& platformData)
230 return platformData.hash();
233 static bool equal(const FontPlatformData& a, const FontPlatformData& b)
238 static const bool safeToCompareToEmptyOrDeleted = true;
241 struct FontDataCacheKeyTraits : WTF::GenericHashTraits<FontPlatformData> {
242 static const bool emptyValueIsZero = true;
243 static const bool needsDestruction = true;
244 static const FontPlatformData& emptyValue()
246 DEFINE_STATIC_LOCAL(FontPlatformData, key, (0.f, false, false));
249 static void constructDeletedValue(FontPlatformData& slot)
251 new (&slot) FontPlatformData(HashTableDeletedValue);
253 static bool isDeletedValue(const FontPlatformData& value)
255 return value.isHashTableDeletedValue();
259 typedef HashMap<FontPlatformData, pair<SimpleFontData*, unsigned>, FontDataCacheKeyHash, FontDataCacheKeyTraits> FontDataCache;
261 static FontDataCache* gFontDataCache = 0;
263 const int cMaxInactiveFontData = 120; // Pretty Low Threshold
264 const int cTargetInactiveFontData = 100;
265 static ListHashSet<const SimpleFontData*>* gInactiveFontData = 0;
267 SimpleFontData* FontCache::getCachedFontData(const FontDescription& fontDescription, const AtomicString& family, bool checkingAlternateName)
269 FontPlatformData* platformData = getCachedFontPlatformData(fontDescription, family, checkingAlternateName);
273 return getCachedFontData(platformData);
276 SimpleFontData* FontCache::getCachedFontData(const FontPlatformData* platformData)
281 if (!gFontDataCache) {
282 gFontDataCache = new FontDataCache;
283 gInactiveFontData = new ListHashSet<const SimpleFontData*>;
286 FontDataCache::iterator result = gFontDataCache->find(*platformData);
287 if (result == gFontDataCache->end()) {
288 pair<SimpleFontData*, unsigned> newValue(new SimpleFontData(*platformData), 1);
289 gFontDataCache->set(*platformData, newValue);
290 return newValue.first;
292 if (!result.get()->second.second++) {
293 ASSERT(gInactiveFontData->contains(result.get()->second.first));
294 gInactiveFontData->remove(result.get()->second.first);
297 return result.get()->second.first;
300 void FontCache::releaseFontData(const SimpleFontData* fontData)
302 ASSERT(gFontDataCache);
303 ASSERT(!fontData->isCustomFont());
305 FontDataCache::iterator it = gFontDataCache->find(fontData->platformData());
306 ASSERT(it != gFontDataCache->end());
308 if (!--it->second.second) {
309 gInactiveFontData->add(fontData);
310 if (gInactiveFontData->size() > cMaxInactiveFontData)
311 purgeInactiveFontData(gInactiveFontData->size() - cTargetInactiveFontData);
315 void FontCache::purgeInactiveFontData(int count)
317 if (!gInactiveFontData)
320 static bool isPurging; // Guard against reentry when e.g. a deleted FontData releases its small caps FontData.
326 Vector<const SimpleFontData*, 20> fontDataToDelete;
327 ListHashSet<const SimpleFontData*>::iterator end = gInactiveFontData->end();
328 ListHashSet<const SimpleFontData*>::iterator it = gInactiveFontData->begin();
329 for (int i = 0; i < count && it != end; ++it, ++i) {
330 const SimpleFontData* fontData = *it.get();
331 gFontDataCache->remove(fontData->platformData());
332 fontDataToDelete.append(fontData);
336 // Removed everything
337 gInactiveFontData->clear();
339 for (int i = 0; i < count; ++i)
340 gInactiveFontData->remove(gInactiveFontData->begin());
343 size_t fontDataToDeleteCount = fontDataToDelete.size();
344 for (size_t i = 0; i < fontDataToDeleteCount; ++i)
345 delete fontDataToDelete[i];
347 if (gFontPlatformDataCache) {
348 Vector<FontPlatformDataCacheKey> keysToRemove;
349 keysToRemove.reserveInitialCapacity(gFontPlatformDataCache->size());
350 FontPlatformDataCache::iterator platformDataEnd = gFontPlatformDataCache->end();
351 for (FontPlatformDataCache::iterator platformData = gFontPlatformDataCache->begin(); platformData != platformDataEnd; ++platformData) {
352 if (platformData->second && !gFontDataCache->contains(*platformData->second))
353 keysToRemove.append(platformData->first);
356 size_t keysToRemoveCount = keysToRemove.size();
357 for (size_t i = 0; i < keysToRemoveCount; ++i)
358 delete gFontPlatformDataCache->take(keysToRemove[i]);
364 size_t FontCache::fontDataCount()
367 return gFontDataCache->size();
371 size_t FontCache::inactiveFontDataCount()
373 if (gInactiveFontData)
374 return gInactiveFontData->size();
378 const FontData* FontCache::getFontData(const Font& font, int& familyIndex, FontSelector* fontSelector)
380 SimpleFontData* result = 0;
382 int startIndex = familyIndex;
383 const FontFamily* startFamily = &font.fontDescription().family();
384 for (int i = 0; startFamily && i < startIndex; i++)
385 startFamily = startFamily->next();
386 const FontFamily* currFamily = startFamily;
387 while (currFamily && !result) {
389 if (currFamily->family().length()) {
391 FontData* data = fontSelector->getFontData(font.fontDescription(), currFamily->family());
395 result = getCachedFontData(font.fontDescription(), currFamily->family());
397 currFamily = currFamily->next();
401 familyIndex = cAllFamiliesScanned;
404 // We didn't find a font. Try to find a similar font using our own specific knowledge about our platform.
405 // For example on OS X, we know to map any families containing the words Arabic, Pashto, or Urdu to the
407 result = getSimilarFontPlatformData(font);
409 if (!result && startIndex == 0) {
410 // If it's the primary font that we couldn't find, we try the following. In all other cases, we will
411 // just use per-character system fallback.
414 // Try the user's preferred standard font.
415 if (FontData* data = fontSelector->getFontData(font.fontDescription(), "-webkit-standard"))
419 // Still no result. Hand back our last resort fallback font.
420 result = getLastResortFallbackFont(font.fontDescription());
425 static HashSet<FontSelector*>* gClients;
427 void FontCache::addClient(FontSelector* client)
430 gClients = new HashSet<FontSelector*>;
432 ASSERT(!gClients->contains(client));
433 gClients->add(client);
436 void FontCache::removeClient(FontSelector* client)
439 ASSERT(gClients->contains(client));
441 gClients->remove(client);
444 static unsigned gGeneration = 0;
446 unsigned FontCache::generation()
451 void FontCache::invalidate()
454 ASSERT(!gFontPlatformDataCache);
458 if (gFontPlatformDataCache) {
459 deleteAllValues(*gFontPlatformDataCache);
460 delete gFontPlatformDataCache;
461 gFontPlatformDataCache = new FontPlatformDataCache;
466 Vector<RefPtr<FontSelector> > clients;
467 size_t numClients = gClients->size();
468 clients.reserveInitialCapacity(numClients);
469 HashSet<FontSelector*>::iterator end = gClients->end();
470 for (HashSet<FontSelector*>::iterator it = gClients->begin(); it != end; ++it)
473 ASSERT(numClients == clients.size());
474 for (size_t i = 0; i < numClients; ++i)
475 clients[i]->fontCacheInvalidated();
477 purgeInactiveFontData();
480 } // namespace WebCore