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 "platform/fonts/FontCache.h" 32 33 #include "FontFamilyNames.h" 34 35 #include "RuntimeEnabledFeatures.h" 36 #include "platform/fonts/AlternateFontFamily.h" 37 #include "platform/fonts/FontCacheKey.h" 38 #include "platform/fonts/FontDataCache.h" 39 #include "platform/fonts/FontDescription.h" 40 #include "platform/fonts/FontFallbackList.h" 41 #include "platform/fonts/FontPlatformData.h" 42 #include "platform/fonts/FontSelector.h" 43 #include "platform/fonts/FontSmoothingMode.h" 44 #include "platform/fonts/TextRenderingMode.h" 45 #include "platform/fonts/opentype/OpenTypeVerticalData.h" 46 #include "wtf/HashMap.h" 47 #include "wtf/ListHashSet.h" 48 #include "wtf/StdLibExtras.h" 49 #include "wtf/text/AtomicStringHash.h" 50 #include "wtf/text/StringHash.h" 51 52 using namespace WTF; 53 54 namespace WebCore { 55 56 #if !OS(WIN) || ENABLE(GDI_FONTS_ON_WINDOWS) 57 FontCache::FontCache() 58 : m_purgePreventCount(0) 59 { 60 } 61 #endif // !OS(WIN) || ENABLE(GDI_FONTS_ON_WINDOWS) 62 63 typedef HashMap<FontCacheKey, OwnPtr<FontPlatformData>, FontCacheKeyHash, FontCacheKeyTraits> FontPlatformDataCache; 64 65 static FontPlatformDataCache* gFontPlatformDataCache = 0; 66 67 FontCache* FontCache::fontCache() 68 { 69 DEFINE_STATIC_LOCAL(FontCache, globalFontCache, ()); 70 return &globalFontCache; 71 } 72 73 FontPlatformData* FontCache::getFontPlatformData(const FontDescription& fontDescription, 74 const AtomicString& passedFamilyName, bool checkingAlternateName) 75 { 76 #if OS(WIN) && ENABLE(OPENTYPE_VERTICAL) 77 // Leading "@" in the font name enables Windows vertical flow flag for the font. 78 // Because we do vertical flow by ourselves, we don't want to use the Windows feature. 79 // IE disregards "@" regardless of the orientatoin, so we follow the behavior. 80 const AtomicString& familyName = (passedFamilyName.isEmpty() || passedFamilyName[0] != '@') ? 81 passedFamilyName : AtomicString(passedFamilyName.impl()->substring(1)); 82 #else 83 const AtomicString& familyName = passedFamilyName; 84 #endif 85 86 if (!gFontPlatformDataCache) { 87 gFontPlatformDataCache = new FontPlatformDataCache; 88 platformInit(); 89 } 90 91 FontCacheKey key = fontDescription.cacheKey(familyName); 92 FontPlatformData* result = 0; 93 bool foundResult; 94 FontPlatformDataCache::iterator it = gFontPlatformDataCache->find(key); 95 if (it == gFontPlatformDataCache->end()) { 96 result = createFontPlatformData(fontDescription, familyName, fontDescription.effectiveFontSize()); 97 gFontPlatformDataCache->set(key, adoptPtr(result)); 98 foundResult = result; 99 } else { 100 result = it->value.get(); 101 foundResult = true; 102 } 103 104 if (!foundResult && !checkingAlternateName) { 105 // We were unable to find a font. We have a small set of fonts that we alias to other names, 106 // e.g., Arial/Helvetica, Courier/Courier New, etc. Try looking up the font under the aliased name. 107 const AtomicString& alternateName = alternateFamilyName(familyName); 108 if (!alternateName.isEmpty()) 109 result = getFontPlatformData(fontDescription, alternateName, true); 110 if (result) 111 gFontPlatformDataCache->set(key, adoptPtr(new FontPlatformData(*result))); // Cache the result under the old name. 112 } 113 114 return result; 115 } 116 117 #if ENABLE(OPENTYPE_VERTICAL) 118 typedef HashMap<FontCache::FontFileKey, RefPtr<OpenTypeVerticalData>, WTF::IntHash<FontCache::FontFileKey>, WTF::UnsignedWithZeroKeyHashTraits<FontCache::FontFileKey> > FontVerticalDataCache; 119 120 FontVerticalDataCache& fontVerticalDataCacheInstance() 121 { 122 DEFINE_STATIC_LOCAL(FontVerticalDataCache, fontVerticalDataCache, ()); 123 return fontVerticalDataCache; 124 } 125 126 PassRefPtr<OpenTypeVerticalData> FontCache::getVerticalData(const FontFileKey& key, const FontPlatformData& platformData) 127 { 128 FontVerticalDataCache& fontVerticalDataCache = fontVerticalDataCacheInstance(); 129 FontVerticalDataCache::iterator result = fontVerticalDataCache.find(key); 130 if (result != fontVerticalDataCache.end()) 131 return result.get()->value; 132 133 RefPtr<OpenTypeVerticalData> verticalData = OpenTypeVerticalData::create(platformData); 134 if (!verticalData->isOpenType()) 135 verticalData.clear(); 136 fontVerticalDataCache.set(key, verticalData); 137 return verticalData; 138 } 139 #endif 140 141 static FontDataCache* gFontDataCache = 0; 142 143 PassRefPtr<SimpleFontData> FontCache::getFontData(const FontDescription& fontDescription, const AtomicString& family, bool checkingAlternateName, ShouldRetain shouldRetain) 144 { 145 if (FontPlatformData* platformData = getFontPlatformData(fontDescription, adjustFamilyNameToAvoidUnsupportedFonts(family), checkingAlternateName)) 146 return fontDataFromFontPlatformData(platformData, shouldRetain); 147 148 return 0; 149 } 150 151 PassRefPtr<SimpleFontData> FontCache::fontDataFromFontPlatformData(const FontPlatformData* platformData, ShouldRetain shouldRetain) 152 { 153 if (!gFontDataCache) 154 gFontDataCache = new FontDataCache; 155 156 #if !ASSERT_DISABLED 157 if (shouldRetain == DoNotRetain) 158 ASSERT(m_purgePreventCount); 159 #endif 160 161 return gFontDataCache->get(platformData, shouldRetain); 162 } 163 164 bool FontCache::isPlatformFontAvailable(const FontDescription& fontDescription, const AtomicString& family) 165 { 166 bool checkingAlternateName = true; 167 return getFontPlatformData(fontDescription, family, checkingAlternateName); 168 } 169 170 SimpleFontData* FontCache::getNonRetainedLastResortFallbackFont(const FontDescription& fontDescription) 171 { 172 return getLastResortFallbackFont(fontDescription, DoNotRetain).leakRef(); 173 } 174 175 void FontCache::releaseFontData(const SimpleFontData* fontData) 176 { 177 ASSERT(gFontDataCache); 178 179 gFontDataCache->release(fontData); 180 } 181 182 static inline void purgePlatformFontDataCache() 183 { 184 if (!gFontPlatformDataCache) 185 return; 186 187 Vector<FontCacheKey> keysToRemove; 188 keysToRemove.reserveInitialCapacity(gFontPlatformDataCache->size()); 189 FontPlatformDataCache::iterator platformDataEnd = gFontPlatformDataCache->end(); 190 for (FontPlatformDataCache::iterator platformData = gFontPlatformDataCache->begin(); platformData != platformDataEnd; ++platformData) { 191 if (platformData->value && !gFontDataCache->contains(platformData->value.get())) 192 keysToRemove.append(platformData->key); 193 } 194 195 size_t keysToRemoveCount = keysToRemove.size(); 196 for (size_t i = 0; i < keysToRemoveCount; ++i) 197 gFontPlatformDataCache->remove(keysToRemove[i]); 198 } 199 200 static inline void purgeFontVerticalDataCache() 201 { 202 #if ENABLE(OPENTYPE_VERTICAL) 203 FontVerticalDataCache& fontVerticalDataCache = fontVerticalDataCacheInstance(); 204 if (!fontVerticalDataCache.isEmpty()) { 205 // Mark & sweep unused verticalData 206 FontVerticalDataCache::iterator verticalDataEnd = fontVerticalDataCache.end(); 207 for (FontVerticalDataCache::iterator verticalData = fontVerticalDataCache.begin(); verticalData != verticalDataEnd; ++verticalData) { 208 if (verticalData->value) 209 verticalData->value->setInFontCache(false); 210 } 211 212 gFontDataCache->markAllVerticalData(); 213 214 Vector<FontCache::FontFileKey> keysToRemove; 215 keysToRemove.reserveInitialCapacity(fontVerticalDataCache.size()); 216 for (FontVerticalDataCache::iterator verticalData = fontVerticalDataCache.begin(); verticalData != verticalDataEnd; ++verticalData) { 217 if (!verticalData->value || !verticalData->value->inFontCache()) 218 keysToRemove.append(verticalData->key); 219 } 220 for (size_t i = 0, count = keysToRemove.size(); i < count; ++i) 221 fontVerticalDataCache.take(keysToRemove[i]); 222 } 223 #endif 224 } 225 226 void FontCache::purge(PurgeSeverity PurgeSeverity) 227 { 228 // We should never be forcing the purge while the FontCachePurgePreventer is in scope. 229 ASSERT(!m_purgePreventCount || PurgeSeverity == PurgeIfNeeded); 230 if (m_purgePreventCount) 231 return; 232 233 if (!gFontDataCache || !gFontDataCache->purge(PurgeSeverity)) 234 return; 235 236 purgePlatformFontDataCache(); 237 purgeFontVerticalDataCache(); 238 } 239 240 static HashSet<FontSelector*>* gClients; 241 242 void FontCache::addClient(FontSelector* client) 243 { 244 if (!gClients) 245 gClients = new HashSet<FontSelector*>; 246 247 ASSERT(!gClients->contains(client)); 248 gClients->add(client); 249 } 250 251 void FontCache::removeClient(FontSelector* client) 252 { 253 ASSERT(gClients); 254 ASSERT(gClients->contains(client)); 255 256 gClients->remove(client); 257 } 258 259 static unsigned short gGeneration = 0; 260 261 unsigned short FontCache::generation() 262 { 263 return gGeneration; 264 } 265 266 void FontCache::invalidate() 267 { 268 if (!gClients) { 269 ASSERT(!gFontPlatformDataCache); 270 return; 271 } 272 273 if (gFontPlatformDataCache) { 274 delete gFontPlatformDataCache; 275 gFontPlatformDataCache = new FontPlatformDataCache; 276 } 277 278 gGeneration++; 279 280 Vector<RefPtr<FontSelector> > clients; 281 size_t numClients = gClients->size(); 282 clients.reserveInitialCapacity(numClients); 283 HashSet<FontSelector*>::iterator end = gClients->end(); 284 for (HashSet<FontSelector*>::iterator it = gClients->begin(); it != end; ++it) 285 clients.append(*it); 286 287 ASSERT(numClients == clients.size()); 288 for (size_t i = 0; i < numClients; ++i) 289 clients[i]->fontCacheInvalidated(); 290 291 purge(ForcePurge); 292 } 293 294 } // namespace WebCore 295