https://bugs.webkit.org/show_bug.cgi?id=48540, support the text-orientation CSS property.
[WebKit-https.git] / Source / WebCore / platform / graphics / FontCache.cpp
1 /*
2  * Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
3  * Copyright (C) 2007 Nicholas Shanks <webkit@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 "FontCache.h"
32
33 #include "Font.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>
41
42 using namespace WTF;
43
44 namespace WebCore {
45
46 FontCache* fontCache()
47 {
48     DEFINE_STATIC_LOCAL(FontCache, globalFontCache, ());
49     return &globalFontCache;
50 }
51
52 FontCache::FontCache()
53 {
54 }
55
56 struct FontPlatformDataCacheKey {
57     WTF_MAKE_FAST_ALLOCATED;
58 public:
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,
61                              TextOrientation textOrientation = TextOrientationVerticalRight, FontWidthVariant widthVariant = RegularWidth)
62         : m_size(size)
63         , m_weight(weight)
64         , m_family(family)
65         , m_italic(italic)
66         , m_printerFont(isPrinterFont)
67         , m_renderingMode(renderingMode)
68         , m_orientation(orientation)
69         , m_textOrientation(textOrientation)
70         , m_widthVariant(widthVariant)
71     {
72     }
73
74     FontPlatformDataCacheKey(HashTableDeletedValueType) : m_size(hashTableDeletedSize()) { }
75     bool isHashTableDeletedValue() const { return m_size == hashTableDeletedSize(); }
76
77     bool operator==(const FontPlatformDataCacheKey& other) const
78     {
79         return equalIgnoringCase(m_family, other.m_family) && m_size == other.m_size && 
80                m_weight == other.m_weight && m_italic == other.m_italic && m_printerFont == other.m_printerFont &&
81                m_renderingMode == other.m_renderingMode && m_orientation == other.m_orientation && m_textOrientation == other.m_textOrientation && m_widthVariant == other.m_widthVariant;
82     }
83
84     unsigned m_size;
85     unsigned m_weight;
86     AtomicString m_family;
87     bool m_italic;
88     bool m_printerFont;
89     FontRenderingMode m_renderingMode;
90     FontOrientation m_orientation;
91     TextOrientation m_textOrientation;
92     FontWidthVariant m_widthVariant;
93
94 private:
95     static unsigned hashTableDeletedSize() { return 0xFFFFFFFFU; }
96 };
97
98 inline unsigned computeHash(const FontPlatformDataCacheKey& fontKey)
99 {
100     unsigned hashCodes[5] = {
101         CaseFoldingHash::hash(fontKey.m_family),
102         fontKey.m_size,
103         fontKey.m_weight,
104         fontKey.m_widthVariant,
105         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)
106     };
107     return WTF::StringHasher::createBlobHash<sizeof(hashCodes)>(hashCodes);
108 }
109
110 struct FontPlatformDataCacheKeyHash {
111     static unsigned hash(const FontPlatformDataCacheKey& font)
112     {
113         return computeHash(font);
114     }
115          
116     static bool equal(const FontPlatformDataCacheKey& a, const FontPlatformDataCacheKey& b)
117     {
118         return a == b;
119     }
120
121     static const bool safeToCompareToEmptyOrDeleted = true;
122 };
123
124 struct FontPlatformDataCacheKeyTraits : WTF::GenericHashTraits<FontPlatformDataCacheKey> {
125     static const bool emptyValueIsZero = true;
126     static const FontPlatformDataCacheKey& emptyValue()
127     {
128         DEFINE_STATIC_LOCAL(FontPlatformDataCacheKey, key, (nullAtom));
129         return key;
130     }
131     static void constructDeletedValue(FontPlatformDataCacheKey& slot)
132     {
133         new (&slot) FontPlatformDataCacheKey(HashTableDeletedValue);
134     }
135     static bool isDeletedValue(const FontPlatformDataCacheKey& value)
136     {
137         return value.isHashTableDeletedValue();
138     }
139 };
140
141 typedef HashMap<FontPlatformDataCacheKey, FontPlatformData*, FontPlatformDataCacheKeyHash, FontPlatformDataCacheKeyTraits> FontPlatformDataCache;
142
143 static FontPlatformDataCache* gFontPlatformDataCache = 0;
144
145 static const AtomicString& alternateFamilyName(const AtomicString& familyName)
146 {
147     // Alias Courier <-> Courier New
148     DEFINE_STATIC_LOCAL(AtomicString, courier, ("Courier"));
149     DEFINE_STATIC_LOCAL(AtomicString, courierNew, ("Courier New"));
150     if (equalIgnoringCase(familyName, courier))
151         return courierNew;
152 #if !OS(WINDOWS)
153     // On Windows, Courier New (truetype font) is always present and
154     // Courier is a bitmap font. So, we don't want to map Courier New to
155     // Courier.
156     if (equalIgnoringCase(familyName, courierNew))
157         return courier;
158 #endif
159
160     // Alias Times and Times New Roman.
161     DEFINE_STATIC_LOCAL(AtomicString, times, ("Times"));
162     DEFINE_STATIC_LOCAL(AtomicString, timesNewRoman, ("Times New Roman"));
163     if (equalIgnoringCase(familyName, times))
164         return timesNewRoman;
165     if (equalIgnoringCase(familyName, timesNewRoman))
166         return times;
167     
168     // Alias Arial and Helvetica
169     DEFINE_STATIC_LOCAL(AtomicString, arial, ("Arial"));
170     DEFINE_STATIC_LOCAL(AtomicString, helvetica, ("Helvetica"));
171     if (equalIgnoringCase(familyName, arial))
172         return helvetica;
173     if (equalIgnoringCase(familyName, helvetica))
174         return arial;
175
176 #if OS(WINDOWS)
177     // On Windows, bitmap fonts are blocked altogether so that we have to 
178     // alias MS Sans Serif (bitmap font) -> Microsoft Sans Serif (truetype font)
179     DEFINE_STATIC_LOCAL(AtomicString, msSans, ("MS Sans Serif"));
180     DEFINE_STATIC_LOCAL(AtomicString, microsoftSans, ("Microsoft Sans Serif"));
181     if (equalIgnoringCase(familyName, msSans))
182         return microsoftSans;
183
184     // Alias MS Serif (bitmap) -> Times New Roman (truetype font). There's no 
185     // 'Microsoft Sans Serif-equivalent' for Serif. 
186     static AtomicString msSerif("MS Serif");
187     if (equalIgnoringCase(familyName, msSerif))
188         return timesNewRoman;
189 #endif
190
191     return emptyAtom;
192 }
193
194 FontPlatformData* FontCache::getCachedFontPlatformData(const FontDescription& fontDescription, 
195                                                        const AtomicString& familyName,
196                                                        bool checkingAlternateName)
197 {
198     if (!gFontPlatformDataCache) {
199         gFontPlatformDataCache = new FontPlatformDataCache;
200         platformInit();
201     }
202
203     FontPlatformDataCacheKey key(familyName, fontDescription.computedPixelSize(), fontDescription.weight(), fontDescription.italic(),
204                                  fontDescription.usePrinterFont(), fontDescription.renderingMode(), fontDescription.orientation(),
205                                  fontDescription.textOrientation(), fontDescription.widthVariant());
206     FontPlatformData* result = 0;
207     bool foundResult;
208     FontPlatformDataCache::iterator it = gFontPlatformDataCache->find(key);
209     if (it == gFontPlatformDataCache->end()) {
210         result = createFontPlatformData(fontDescription, familyName);
211         gFontPlatformDataCache->set(key, result);
212         foundResult = result;
213     } else {
214         result = it->second;
215         foundResult = true;
216     }
217
218     if (!foundResult && !checkingAlternateName) {
219         // We were unable to find a font.  We have a small set of fonts that we alias to other names, 
220         // e.g., Arial/Helvetica, Courier/Courier New, etc.  Try looking up the font under the aliased name.
221         const AtomicString& alternateName = alternateFamilyName(familyName);
222         if (!alternateName.isEmpty())
223             result = getCachedFontPlatformData(fontDescription, alternateName, true);
224         if (result)
225             gFontPlatformDataCache->set(key, new FontPlatformData(*result)); // Cache the result under the old name.
226     }
227
228     return result;
229 }
230
231 struct FontDataCacheKeyHash {
232     static unsigned hash(const FontPlatformData& platformData)
233     {
234         return platformData.hash();
235     }
236          
237     static bool equal(const FontPlatformData& a, const FontPlatformData& b)
238     {
239         return a == b;
240     }
241
242     static const bool safeToCompareToEmptyOrDeleted = true;
243 };
244
245 struct FontDataCacheKeyTraits : WTF::GenericHashTraits<FontPlatformData> {
246     static const bool emptyValueIsZero = true;
247     static const bool needsDestruction = true;
248     static const FontPlatformData& emptyValue()
249     {
250         DEFINE_STATIC_LOCAL(FontPlatformData, key, (0.f, false, false));
251         return key;
252     }
253     static void constructDeletedValue(FontPlatformData& slot)
254     {
255         new (&slot) FontPlatformData(HashTableDeletedValue);
256     }
257     static bool isDeletedValue(const FontPlatformData& value)
258     {
259         return value.isHashTableDeletedValue();
260     }
261 };
262
263 typedef HashMap<FontPlatformData, pair<SimpleFontData*, unsigned>, FontDataCacheKeyHash, FontDataCacheKeyTraits> FontDataCache;
264
265 static FontDataCache* gFontDataCache = 0;
266
267 const int cMaxInactiveFontData = 120;  // Pretty Low Threshold
268 const int cTargetInactiveFontData = 100;
269 static ListHashSet<const SimpleFontData*>* gInactiveFontData = 0;
270
271 SimpleFontData* FontCache::getCachedFontData(const FontDescription& fontDescription, const AtomicString& family, bool checkingAlternateName)
272 {
273     FontPlatformData* platformData = getCachedFontPlatformData(fontDescription, family, checkingAlternateName);
274     if (!platformData)
275         return 0;
276
277     return getCachedFontData(platformData);
278 }
279
280 SimpleFontData* FontCache::getCachedFontData(const FontPlatformData* platformData)
281 {
282     if (!platformData)
283         return 0;
284
285     if (!gFontDataCache) {
286         gFontDataCache = new FontDataCache;
287         gInactiveFontData = new ListHashSet<const SimpleFontData*>;
288     }
289     
290     FontDataCache::iterator result = gFontDataCache->find(*platformData);
291     if (result == gFontDataCache->end()) {
292         pair<SimpleFontData*, unsigned> newValue(new SimpleFontData(*platformData), 1);
293         gFontDataCache->set(*platformData, newValue);
294         return newValue.first;
295     }
296     if (!result.get()->second.second++) {
297         ASSERT(gInactiveFontData->contains(result.get()->second.first));
298         gInactiveFontData->remove(result.get()->second.first);
299     }
300
301     return result.get()->second.first;
302 }
303
304 void FontCache::releaseFontData(const SimpleFontData* fontData)
305 {
306     ASSERT(gFontDataCache);
307     ASSERT(!fontData->isCustomFont());
308
309     FontDataCache::iterator it = gFontDataCache->find(fontData->platformData());
310     ASSERT(it != gFontDataCache->end());
311
312     if (!--it->second.second) {
313         gInactiveFontData->add(fontData);
314         if (gInactiveFontData->size() > cMaxInactiveFontData)
315             purgeInactiveFontData(gInactiveFontData->size() - cTargetInactiveFontData);
316     }
317 }
318
319 void FontCache::purgeInactiveFontData(int count)
320 {
321     if (!gInactiveFontData)
322         return;
323
324     static bool isPurging;  // Guard against reentry when e.g. a deleted FontData releases its small caps FontData.
325     if (isPurging)
326         return;
327
328     isPurging = true;
329
330     Vector<const SimpleFontData*, 20> fontDataToDelete;
331     ListHashSet<const SimpleFontData*>::iterator end = gInactiveFontData->end();
332     ListHashSet<const SimpleFontData*>::iterator it = gInactiveFontData->begin();
333     for (int i = 0; i < count && it != end; ++it, ++i) {
334         const SimpleFontData* fontData = *it.get();
335         gFontDataCache->remove(fontData->platformData());
336         fontDataToDelete.append(fontData);
337     }
338
339     if (it == end) {
340         // Removed everything
341         gInactiveFontData->clear();
342     } else {
343         for (int i = 0; i < count; ++i)
344             gInactiveFontData->remove(gInactiveFontData->begin());
345     }
346
347     size_t fontDataToDeleteCount = fontDataToDelete.size();
348     for (size_t i = 0; i < fontDataToDeleteCount; ++i)
349         delete fontDataToDelete[i];
350
351     if (gFontPlatformDataCache) {
352         Vector<FontPlatformDataCacheKey> keysToRemove;
353         keysToRemove.reserveInitialCapacity(gFontPlatformDataCache->size());
354         FontPlatformDataCache::iterator platformDataEnd = gFontPlatformDataCache->end();
355         for (FontPlatformDataCache::iterator platformData = gFontPlatformDataCache->begin(); platformData != platformDataEnd; ++platformData) {
356             if (platformData->second && !gFontDataCache->contains(*platformData->second))
357                 keysToRemove.append(platformData->first);
358         }
359         
360         size_t keysToRemoveCount = keysToRemove.size();
361         for (size_t i = 0; i < keysToRemoveCount; ++i)
362             delete gFontPlatformDataCache->take(keysToRemove[i]);
363     }
364
365     isPurging = false;
366 }
367
368 size_t FontCache::fontDataCount()
369 {
370     if (gFontDataCache)
371         return gFontDataCache->size();
372     return 0;
373 }
374
375 size_t FontCache::inactiveFontDataCount()
376 {
377     if (gInactiveFontData)
378         return gInactiveFontData->size();
379     return 0;
380 }
381
382 const FontData* FontCache::getFontData(const Font& font, int& familyIndex, FontSelector* fontSelector)
383 {
384     SimpleFontData* result = 0;
385
386     int startIndex = familyIndex;
387     const FontFamily* startFamily = &font.fontDescription().family();
388     for (int i = 0; startFamily && i < startIndex; i++)
389         startFamily = startFamily->next();
390     const FontFamily* currFamily = startFamily;
391     while (currFamily && !result) {
392         familyIndex++;
393         if (currFamily->family().length()) {
394             if (fontSelector) {
395                 FontData* data = fontSelector->getFontData(font.fontDescription(), currFamily->family());
396                 if (data)
397                     return data;
398             }
399             result = getCachedFontData(font.fontDescription(), currFamily->family());
400         }
401         currFamily = currFamily->next();
402     }
403
404     if (!currFamily)
405         familyIndex = cAllFamiliesScanned;
406
407     if (!result)
408         // We didn't find a font. Try to find a similar font using our own specific knowledge about our platform.
409         // For example on OS X, we know to map any families containing the words Arabic, Pashto, or Urdu to the
410         // Geeza Pro font.
411         result = getSimilarFontPlatformData(font);
412
413     if (!result && startIndex == 0) {
414         // If it's the primary font that we couldn't find, we try the following. In all other cases, we will
415         // just use per-character system fallback.
416
417         if (fontSelector) {
418             // Try the user's preferred standard font.
419             if (FontData* data = fontSelector->getFontData(font.fontDescription(), "-webkit-standard"))
420                 return data;
421         }
422
423         // Still no result.  Hand back our last resort fallback font.
424         result = getLastResortFallbackFont(font.fontDescription());
425     }
426     return result;
427 }
428
429 static HashSet<FontSelector*>* gClients;
430
431 void FontCache::addClient(FontSelector* client)
432 {
433     if (!gClients)
434         gClients = new HashSet<FontSelector*>;
435
436     ASSERT(!gClients->contains(client));
437     gClients->add(client);
438 }
439
440 void FontCache::removeClient(FontSelector* client)
441 {
442     ASSERT(gClients);
443     ASSERT(gClients->contains(client));
444
445     gClients->remove(client);
446 }
447
448 static unsigned gGeneration = 0;
449
450 unsigned FontCache::generation()
451 {
452     return gGeneration;
453 }
454
455 void FontCache::invalidate()
456 {
457     if (!gClients) {
458         ASSERT(!gFontPlatformDataCache);
459         return;
460     }
461
462     if (gFontPlatformDataCache) {
463         deleteAllValues(*gFontPlatformDataCache);
464         delete gFontPlatformDataCache;
465         gFontPlatformDataCache = new FontPlatformDataCache;
466     }
467
468     gGeneration++;
469
470     Vector<RefPtr<FontSelector> > clients;
471     size_t numClients = gClients->size();
472     clients.reserveInitialCapacity(numClients);
473     HashSet<FontSelector*>::iterator end = gClients->end();
474     for (HashSet<FontSelector*>::iterator it = gClients->begin(); it != end; ++it)
475         clients.append(*it);
476
477     ASSERT(numClients == clients.size());
478     for (size_t i = 0; i < numClients; ++i)
479         clients[i]->fontCacheInvalidated();
480
481     purgeInactiveFontData();
482 }
483
484 } // namespace WebCore