2 * Copyright (C) 2006, 2008, 2013 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 "FontGlyphs.h"
35 #include "FontPlatformData.h"
36 #include "FontSelector.h"
37 #include "WebKitFontFamilyNames.h"
38 #include <wtf/HashMap.h>
39 #include <wtf/ListHashSet.h>
40 #include <wtf/StdLibExtras.h>
41 #include <wtf/text/AtomicStringHash.h>
42 #include <wtf/text/StringHash.h>
44 #if ENABLE(OPENTYPE_VERTICAL)
45 #include "OpenTypeVerticalData.h"
52 FontCache* fontCache()
54 DEFINE_STATIC_LOCAL(FontCache, globalFontCache, ());
55 return &globalFontCache;
58 FontCache::FontCache()
59 : m_purgePreventCount(0)
63 struct FontPlatformDataCacheKey {
64 WTF_MAKE_FAST_ALLOCATED;
66 FontPlatformDataCacheKey() { }
67 FontPlatformDataCacheKey(const AtomicString& family, const FontDescription& description)
68 : m_fontDescriptionKey(description)
72 FontPlatformDataCacheKey(HashTableDeletedValueType) : m_fontDescriptionKey(hashTableDeletedSize()) { }
73 bool isHashTableDeletedValue() const { return m_fontDescriptionKey.size == hashTableDeletedSize(); }
75 bool operator==(const FontPlatformDataCacheKey& other) const
77 return equalIgnoringCase(m_family, other.m_family) && m_fontDescriptionKey == other.m_fontDescriptionKey;
80 FontDescriptionFontDataCacheKey m_fontDescriptionKey;
81 AtomicString m_family;
84 static unsigned hashTableDeletedSize() { return 0xFFFFFFFFU; }
87 inline unsigned computeHash(const FontPlatformDataCacheKey& fontKey)
89 return pairIntHash(CaseFoldingHash::hash(fontKey.m_family), fontKey.m_fontDescriptionKey.computeHash());
92 struct FontPlatformDataCacheKeyHash {
93 static unsigned hash(const FontPlatformDataCacheKey& font)
95 return computeHash(font);
98 static bool equal(const FontPlatformDataCacheKey& a, const FontPlatformDataCacheKey& b)
103 static const bool safeToCompareToEmptyOrDeleted = true;
106 struct FontPlatformDataCacheKeyTraits : WTF::SimpleClassHashTraits<FontPlatformDataCacheKey> { };
108 typedef HashMap<FontPlatformDataCacheKey, OwnPtr<FontPlatformData>, FontPlatformDataCacheKeyHash, FontPlatformDataCacheKeyTraits> FontPlatformDataCache;
110 static FontPlatformDataCache* gFontPlatformDataCache = 0;
112 static bool familyNameEqualIgnoringCase(const AtomicString& familyName, const char* reference, unsigned length)
115 ASSERT(familyName.length() == length);
116 ASSERT(strlen(reference) == length);
117 const AtomicStringImpl* familyNameImpl = familyName.impl();
118 if (familyNameImpl->is8Bit())
119 return equalIgnoringCase(familyNameImpl->characters8(), reinterpret_cast<const LChar*>(reference), length);
120 return equalIgnoringCase(familyNameImpl->characters16(), reinterpret_cast<const LChar*>(reference), length);
123 template<size_t length>
124 static inline bool familyNameEqualIgnoringCase(const AtomicString& familyName, const char (&reference)[length])
126 return familyNameEqualIgnoringCase(familyName, reference, length - 1);
129 static const AtomicString alternateFamilyName(const AtomicString& familyName)
131 // Alias Courier and Courier New.
132 // Alias Times and Times New Roman.
133 // Alias Arial and Helvetica.
134 switch (familyName.length()) {
136 if (familyNameEqualIgnoringCase(familyName, "Arial"))
137 return AtomicString("Helvetica", AtomicString::ConstructFromLiteral);
138 if (familyNameEqualIgnoringCase(familyName, "Times"))
139 return AtomicString("Times New Roman", AtomicString::ConstructFromLiteral);
142 if (familyNameEqualIgnoringCase(familyName, "Courier"))
143 return AtomicString("Courier New", AtomicString::ConstructFromLiteral);
146 if (familyNameEqualIgnoringCase(familyName, "Helvetica"))
147 return AtomicString("Arial", AtomicString::ConstructFromLiteral);
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
154 if (familyNameEqualIgnoringCase(familyName, "Courier New"))
155 return AtomicString("Courier", AtomicString::ConstructFromLiteral);
157 #endif // !OS(WINDOWS)
159 if (familyNameEqualIgnoringCase(familyName, "Times New Roman"))
160 return AtomicString("Times", AtomicString::ConstructFromLiteral);
163 // On Windows, bitmap fonts are blocked altogether so that we have to
164 // alias MS Sans Serif (bitmap font) -> Microsoft Sans Serif (truetype font)
166 if (familyNameEqualIgnoringCase(familyName, "MS Sans Serif"))
167 return AtomicString("Microsoft Sans Serif", AtomicString::ConstructFromLiteral);
170 // Alias MS Serif (bitmap) -> Times New Roman (truetype font). There's no
171 // 'Microsoft Sans Serif-equivalent' for Serif.
173 if (familyNameEqualIgnoringCase(familyName, "MS Serif"))
174 return AtomicString("Times New Roman", AtomicString::ConstructFromLiteral);
176 #endif // OS(WINDOWS)
183 FontPlatformData* FontCache::getCachedFontPlatformData(const FontDescription& fontDescription,
184 const AtomicString& passedFamilyName,
185 bool checkingAlternateName)
187 #if OS(WINDOWS) && ENABLE(OPENTYPE_VERTICAL)
188 // Leading "@" in the font name enables Windows vertical flow flag for the font.
189 // Because we do vertical flow by ourselves, we don't want to use the Windows feature.
190 // IE disregards "@" regardless of the orientatoin, so we follow the behavior.
191 const AtomicString& familyName = (passedFamilyName.isEmpty() || passedFamilyName[0] != '@') ?
192 passedFamilyName : AtomicString(passedFamilyName.impl()->substring(1));
194 const AtomicString& familyName = passedFamilyName;
197 if (!gFontPlatformDataCache) {
198 gFontPlatformDataCache = new FontPlatformDataCache;
202 FontPlatformDataCacheKey key(familyName, fontDescription);
204 FontPlatformDataCache::AddResult result = gFontPlatformDataCache->add(key, nullptr);
205 FontPlatformDataCache::iterator it = result.iterator;
206 if (result.isNewEntry) {
207 it->value = createFontPlatformData(fontDescription, familyName);
209 if (!it->value && !checkingAlternateName) {
210 // We were unable to find a font. We have a small set of fonts that we alias to other names,
211 // e.g., Arial/Helvetica, Courier/Courier New, etc. Try looking up the font under the aliased name.
212 const AtomicString alternateName = alternateFamilyName(familyName);
213 if (!alternateName.isNull()) {
214 FontPlatformData* fontPlatformDataForAlternateName = getCachedFontPlatformData(fontDescription, alternateName, true);
215 // Lookup the key in the hash table again as the previous iterator may have
216 // been invalidated by the recursive call to getCachedFontPlatformData().
217 it = gFontPlatformDataCache->find(key);
218 ASSERT(it != gFontPlatformDataCache->end());
219 if (fontPlatformDataForAlternateName)
220 it->value = adoptPtr(new FontPlatformData(*fontPlatformDataForAlternateName));
225 return it->value.get();
228 #if ENABLE(OPENTYPE_VERTICAL)
229 typedef HashMap<FontCache::FontFileKey, RefPtr<OpenTypeVerticalData>, WTF::IntHash<FontCache::FontFileKey>, WTF::UnsignedWithZeroKeyHashTraits<FontCache::FontFileKey> > FontVerticalDataCache;
231 FontVerticalDataCache& fontVerticalDataCacheInstance()
233 DEFINE_STATIC_LOCAL(FontVerticalDataCache, fontVerticalDataCache, ());
234 return fontVerticalDataCache;
237 PassRefPtr<OpenTypeVerticalData> FontCache::getVerticalData(const FontFileKey& key, const FontPlatformData& platformData)
239 FontVerticalDataCache& fontVerticalDataCache = fontVerticalDataCacheInstance();
240 FontVerticalDataCache::iterator result = fontVerticalDataCache.find(key);
241 if (result != fontVerticalDataCache.end())
242 return result.get()->value;
244 RefPtr<OpenTypeVerticalData> verticalData = OpenTypeVerticalData::create(platformData);
245 if (!verticalData->isOpenType())
246 verticalData.clear();
247 fontVerticalDataCache.set(key, verticalData);
252 struct FontDataCacheKeyHash {
253 static unsigned hash(const FontPlatformData& platformData)
255 return platformData.hash();
258 static bool equal(const FontPlatformData& a, const FontPlatformData& b)
263 static const bool safeToCompareToEmptyOrDeleted = true;
266 struct FontDataCacheKeyTraits : WTF::GenericHashTraits<FontPlatformData> {
267 static const bool emptyValueIsZero = true;
268 static const bool needsDestruction = true;
269 static const FontPlatformData& emptyValue()
271 DEFINE_STATIC_LOCAL(FontPlatformData, key, (0.f, false, false));
274 static void constructDeletedValue(FontPlatformData& slot)
276 new (NotNull, &slot) FontPlatformData(HashTableDeletedValue);
278 static bool isDeletedValue(const FontPlatformData& value)
280 return value.isHashTableDeletedValue();
284 typedef HashMap<FontPlatformData, pair<RefPtr<SimpleFontData>, unsigned>, FontDataCacheKeyHash, FontDataCacheKeyTraits> FontDataCache;
286 static FontDataCache* gFontDataCache = 0;
288 const int cMaxInactiveFontData = 225;
289 const int cTargetInactiveFontData = 200;
290 static ListHashSet<RefPtr<SimpleFontData> >* gInactiveFontData = 0;
292 PassRefPtr<SimpleFontData> FontCache::getCachedFontData(const FontDescription& fontDescription, const AtomicString& family, bool checkingAlternateName, ShouldRetain shouldRetain)
294 FontPlatformData* platformData = getCachedFontPlatformData(fontDescription, family, checkingAlternateName);
298 return getCachedFontData(platformData, shouldRetain);
301 PassRefPtr<SimpleFontData> FontCache::getCachedFontData(const FontPlatformData* platformData, ShouldRetain shouldRetain)
307 if (shouldRetain == DoNotRetain)
308 ASSERT(m_purgePreventCount);
311 if (!gFontDataCache) {
312 gFontDataCache = new FontDataCache;
313 gInactiveFontData = new ListHashSet<RefPtr<SimpleFontData> >;
316 FontDataCache::iterator result = gFontDataCache->find(*platformData);
317 if (result == gFontDataCache->end()) {
318 pair<RefPtr<SimpleFontData>, unsigned> newValue(SimpleFontData::create(*platformData), shouldRetain == Retain ? 1 : 0);
319 gFontDataCache->set(*platformData, newValue);
320 if (shouldRetain == DoNotRetain)
321 gInactiveFontData->add(newValue.first);
322 return newValue.first.release();
325 if (!result.get()->value.second) {
326 ASSERT(gInactiveFontData->contains(result.get()->value.first));
327 gInactiveFontData->remove(result.get()->value.first);
330 if (shouldRetain == Retain)
331 result.get()->value.second++;
332 else if (!result.get()->value.second) {
333 // If shouldRetain is DoNotRetain and count is 0, we want to remove the fontData from
334 // gInactiveFontData (above) and re-add here to update LRU position.
335 gInactiveFontData->add(result.get()->value.first);
338 return result.get()->value.first;
341 SimpleFontData* FontCache::getNonRetainedLastResortFallbackFont(const FontDescription& fontDescription)
343 return getLastResortFallbackFont(fontDescription, DoNotRetain).leakRef();
346 void FontCache::releaseFontData(const SimpleFontData* fontData)
348 ASSERT(gFontDataCache);
349 ASSERT(!fontData->isCustomFont());
351 FontDataCache::iterator it = gFontDataCache->find(fontData->platformData());
352 ASSERT(it != gFontDataCache->end());
353 if (it == gFontDataCache->end())
356 ASSERT(it->value.second);
357 if (!--it->value.second)
358 gInactiveFontData->add(it->value.first);
361 void FontCache::purgeInactiveFontDataIfNeeded()
363 if (gInactiveFontData && !m_purgePreventCount && gInactiveFontData->size() > cMaxInactiveFontData)
364 purgeInactiveFontData(gInactiveFontData->size() - cTargetInactiveFontData);
367 void FontCache::purgeInactiveFontData(int count)
369 pruneUnreferencedEntriesFromFontGlyphsCache();
371 if (!gInactiveFontData || m_purgePreventCount)
374 static bool isPurging; // Guard against reentry when e.g. a deleted FontData releases its small caps FontData.
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);
391 // Removed everything
392 gInactiveFontData->clear();
394 for (int i = 0; i < count; ++i)
395 gInactiveFontData->remove(gInactiveFontData->begin());
398 fontDataToDelete.clear();
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);
409 size_t keysToRemoveCount = keysToRemove.size();
410 for (size_t i = 0; i < keysToRemoveCount; ++i)
411 gFontPlatformDataCache->remove(keysToRemove[i]);
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;
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());
427 verticalData->m_inFontCache = true;
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);
435 for (size_t i = 0, count = keysToRemove.size(); i < count; ++i)
436 fontVerticalDataCache.take(keysToRemove[i]);
443 size_t FontCache::fontDataCount()
446 return gFontDataCache->size();
450 size_t FontCache::inactiveFontDataCount()
452 if (gInactiveFontData)
453 return gInactiveFontData->size();
457 PassRefPtr<FontData> FontCache::getFontData(const FontDescription& description, int& familyIndex, FontSelector* fontSelector)
459 ASSERT(familyIndex != cAllFamiliesScanned);
460 RefPtr<FontData> result;
462 bool isFirst = !familyIndex;
463 int familyCount = description.familyCount();
464 for (;familyIndex < familyCount && !result; ++familyIndex) {
465 const AtomicString& family = description.familyAt(familyIndex);
466 if (family.isEmpty())
469 result = fontSelector->getFontData(description, family);
471 result = getCachedFontData(description, family);
473 if (familyIndex == familyCount)
474 familyIndex = cAllFamiliesScanned;
478 // We didn't find a font. Try to find a similar font using our own specific knowledge about our platform.
479 // For example on OS X, we know to map any families containing the words Arabic, Pashto, or Urdu to the
481 result = similarFontPlatformData(description);
485 if (!result && isFirst) {
486 // If it's the primary font that we couldn't find, we try the following. In all other cases, we will
487 // just use per-character system fallback.
489 // Try the user's preferred standard font.
490 if (RefPtr<FontData> data = fontSelector->getFontData(description, standardFamily))
491 return data.release();
493 // Still no result. Hand back our last resort fallback font.
494 result = getLastResortFallbackFont(description);
496 return result.release();
499 static HashSet<FontSelector*>* gClients;
501 void FontCache::addClient(FontSelector* client)
504 gClients = new HashSet<FontSelector*>;
506 ASSERT(!gClients->contains(client));
507 gClients->add(client);
510 void FontCache::removeClient(FontSelector* client)
513 ASSERT(gClients->contains(client));
515 gClients->remove(client);
518 static unsigned short gGeneration = 0;
520 unsigned short FontCache::generation()
525 void FontCache::invalidate()
528 ASSERT(!gFontPlatformDataCache);
532 if (gFontPlatformDataCache)
533 gFontPlatformDataCache->clear();
534 invalidateFontGlyphsCache();
538 Vector<RefPtr<FontSelector> > clients;
539 size_t numClients = gClients->size();
540 clients.reserveInitialCapacity(numClients);
541 HashSet<FontSelector*>::iterator end = gClients->end();
542 for (HashSet<FontSelector*>::iterator it = gClients->begin(); it != end; ++it)
545 ASSERT(numClients == clients.size());
546 for (size_t i = 0; i < numClients; ++i)
547 clients[i]->fontCacheInvalidated();
549 purgeInactiveFontData();
552 } // namespace WebCore