[GTK] Glyphs in vertical text tests are rotated 90 degrees clockwise
[WebKit-https.git] / Source / WebCore / platform / graphics / FontCache.cpp
1 /*
2  * Copyright (C) 2006, 2008, 2013 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 "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>
43
44 #if ENABLE(OPENTYPE_VERTICAL)
45 #include "OpenTypeVerticalData.h"
46 #endif
47
48 using namespace WTF;
49
50 namespace WebCore {
51
52 FontCache* fontCache()
53 {
54     DEFINE_STATIC_LOCAL(FontCache, globalFontCache, ());
55     return &globalFontCache;
56 }
57
58 FontCache::FontCache()
59     : m_purgePreventCount(0)
60 {
61 }
62
63 struct FontPlatformDataCacheKey {
64     WTF_MAKE_FAST_ALLOCATED;
65 public:
66     FontPlatformDataCacheKey() { }
67     FontPlatformDataCacheKey(const AtomicString& family, const FontDescription& description)
68         : m_fontDescriptionKey(description)
69         , m_family(family)
70     { }
71
72     FontPlatformDataCacheKey(HashTableDeletedValueType) : m_fontDescriptionKey(hashTableDeletedSize()) { }
73     bool isHashTableDeletedValue() const { return m_fontDescriptionKey.size == hashTableDeletedSize(); }
74
75     bool operator==(const FontPlatformDataCacheKey& other) const
76     {
77         return equalIgnoringCase(m_family, other.m_family) && m_fontDescriptionKey == other.m_fontDescriptionKey;
78     }
79
80     FontDescriptionFontDataCacheKey m_fontDescriptionKey;
81     AtomicString m_family;
82
83 private:
84     static unsigned hashTableDeletedSize() { return 0xFFFFFFFFU; }
85 };
86
87 inline unsigned computeHash(const FontPlatformDataCacheKey& fontKey)
88 {
89     return pairIntHash(CaseFoldingHash::hash(fontKey.m_family), fontKey.m_fontDescriptionKey.computeHash());
90 }
91
92 struct FontPlatformDataCacheKeyHash {
93     static unsigned hash(const FontPlatformDataCacheKey& font)
94     {
95         return computeHash(font);
96     }
97          
98     static bool equal(const FontPlatformDataCacheKey& a, const FontPlatformDataCacheKey& b)
99     {
100         return a == b;
101     }
102
103     static const bool safeToCompareToEmptyOrDeleted = true;
104 };
105
106 struct FontPlatformDataCacheKeyTraits : WTF::SimpleClassHashTraits<FontPlatformDataCacheKey> { };
107
108 typedef HashMap<FontPlatformDataCacheKey, OwnPtr<FontPlatformData>, FontPlatformDataCacheKeyHash, FontPlatformDataCacheKeyTraits> FontPlatformDataCache;
109
110 static FontPlatformDataCache* gFontPlatformDataCache = 0;
111
112 static bool familyNameEqualIgnoringCase(const AtomicString& familyName, const char* reference, unsigned length)
113 {
114     ASSERT(length > 0);
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);
121 }
122
123 template<size_t length>
124 static inline bool familyNameEqualIgnoringCase(const AtomicString& familyName, const char (&reference)[length])
125 {
126     return familyNameEqualIgnoringCase(familyName, reference, length - 1);
127 }
128
129 static const AtomicString alternateFamilyName(const AtomicString& familyName)
130 {
131     // Alias Courier and Courier New.
132     // Alias Times and Times New Roman.
133     // Alias Arial and Helvetica.
134     switch (familyName.length()) {
135     case 5:
136         if (familyNameEqualIgnoringCase(familyName, "Arial"))
137             return AtomicString("Helvetica", AtomicString::ConstructFromLiteral);
138         if (familyNameEqualIgnoringCase(familyName, "Times"))
139             return AtomicString("Times New Roman", AtomicString::ConstructFromLiteral);
140         break;
141     case 7:
142         if (familyNameEqualIgnoringCase(familyName, "Courier"))
143             return AtomicString("Courier New", AtomicString::ConstructFromLiteral);
144         break;
145     case 9:
146         if (familyNameEqualIgnoringCase(familyName, "Helvetica"))
147             return AtomicString("Arial", AtomicString::ConstructFromLiteral);
148         break;
149 #if !OS(WINDOWS)
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
152     // Courier.
153     case 11:
154         if (familyNameEqualIgnoringCase(familyName, "Courier New"))
155             return AtomicString("Courier", AtomicString::ConstructFromLiteral);
156         break;
157 #endif // !OS(WINDOWS)
158     case 15:
159         if (familyNameEqualIgnoringCase(familyName, "Times New Roman"))
160             return AtomicString("Times", AtomicString::ConstructFromLiteral);
161         break;
162 #if OS(WINDOWS)
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)
165     case 13:
166         if (familyNameEqualIgnoringCase(familyName, "MS Sans Serif"))
167             return AtomicString("Microsoft Sans Serif", AtomicString::ConstructFromLiteral);
168         break;
169
170     // Alias MS Serif (bitmap) -> Times New Roman (truetype font). There's no 
171     // 'Microsoft Sans Serif-equivalent' for Serif.
172     case 8:
173         if (familyNameEqualIgnoringCase(familyName, "MS Serif"))
174             return AtomicString("Times New Roman", AtomicString::ConstructFromLiteral);
175         break;
176 #endif // OS(WINDOWS)
177
178     }
179
180     return nullAtom;
181 }
182
183 FontPlatformData* FontCache::getCachedFontPlatformData(const FontDescription& fontDescription,
184                                                        const AtomicString& passedFamilyName,
185                                                        bool checkingAlternateName)
186 {
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));
193 #else
194     const AtomicString& familyName = passedFamilyName;
195 #endif
196
197     if (!gFontPlatformDataCache) {
198         gFontPlatformDataCache = new FontPlatformDataCache;
199         platformInit();
200     }
201
202     FontPlatformDataCacheKey key(familyName, fontDescription);
203
204     FontPlatformDataCache::AddResult result = gFontPlatformDataCache->add(key, nullptr);
205     FontPlatformDataCache::iterator it = result.iterator;
206     if (result.isNewEntry) {
207         it->value = createFontPlatformData(fontDescription, familyName);
208
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));
221             }
222         }
223     }
224
225     return it->value.get();
226 }
227
228 #if ENABLE(OPENTYPE_VERTICAL)
229 struct FontVerticalDataCacheKeyHash {
230     static unsigned hash(const FontCache::FontFileKey& fontFileKey)
231     {
232         return PtrHash<const FontCache::FontFileKey*>::hash(&fontFileKey);
233     }
234
235     static bool equal(const FontCache::FontFileKey& a, const FontCache::FontFileKey& b)
236     {
237         return a == b;
238     }
239
240     static const bool safeToCompareToEmptyOrDeleted = true;
241 };
242
243 struct FontVerticalDataCacheKeyTraits : WTF::GenericHashTraits<FontCache::FontFileKey> {
244     static const bool emptyValueIsZero = true;
245     static const bool needsDestruction = true;
246     static const FontCache::FontFileKey& emptyValue()
247     {
248         DEFINE_STATIC_LOCAL(FontCache::FontFileKey, key, (nullAtom));
249         return key;
250     }
251     static void constructDeletedValue(FontCache::FontFileKey& slot)
252     {
253         new (NotNull, &slot) FontCache::FontFileKey(HashTableDeletedValue);
254     }
255     static bool isDeletedValue(const FontCache::FontFileKey& value)
256     {
257         return value.isHashTableDeletedValue();
258     }
259 };
260
261 typedef HashMap<FontCache::FontFileKey, RefPtr<OpenTypeVerticalData>, FontVerticalDataCacheKeyHash, FontVerticalDataCacheKeyTraits> FontVerticalDataCache;
262
263 FontVerticalDataCache& fontVerticalDataCacheInstance()
264 {
265     DEFINE_STATIC_LOCAL(FontVerticalDataCache, fontVerticalDataCache, ());
266     return fontVerticalDataCache;
267 }
268
269 PassRefPtr<OpenTypeVerticalData> FontCache::getVerticalData(const FontFileKey& key, const FontPlatformData& platformData)
270 {
271     FontVerticalDataCache& fontVerticalDataCache = fontVerticalDataCacheInstance();
272     FontVerticalDataCache::iterator result = fontVerticalDataCache.find(key);
273     if (result != fontVerticalDataCache.end())
274         return result.get()->value;
275
276     RefPtr<OpenTypeVerticalData> verticalData = OpenTypeVerticalData::create(platformData);
277     if (!verticalData->isOpenType())
278         verticalData.clear();
279     fontVerticalDataCache.set(key, verticalData);
280     return verticalData;
281 }
282 #endif
283
284 struct FontDataCacheKeyHash {
285     static unsigned hash(const FontPlatformData& platformData)
286     {
287         return platformData.hash();
288     }
289          
290     static bool equal(const FontPlatformData& a, const FontPlatformData& b)
291     {
292         return a == b;
293     }
294
295     static const bool safeToCompareToEmptyOrDeleted = true;
296 };
297
298 struct FontDataCacheKeyTraits : WTF::GenericHashTraits<FontPlatformData> {
299     static const bool emptyValueIsZero = true;
300     static const bool needsDestruction = true;
301     static const FontPlatformData& emptyValue()
302     {
303         DEFINE_STATIC_LOCAL(FontPlatformData, key, (0.f, false, false));
304         return key;
305     }
306     static void constructDeletedValue(FontPlatformData& slot)
307     {
308         new (NotNull, &slot) FontPlatformData(HashTableDeletedValue);
309     }
310     static bool isDeletedValue(const FontPlatformData& value)
311     {
312         return value.isHashTableDeletedValue();
313     }
314 };
315
316 typedef HashMap<FontPlatformData, pair<RefPtr<SimpleFontData>, unsigned>, FontDataCacheKeyHash, FontDataCacheKeyTraits> FontDataCache;
317
318 static FontDataCache* gFontDataCache = 0;
319
320 const int cMaxInactiveFontData = 225;
321 const int cTargetInactiveFontData = 200;
322 static ListHashSet<RefPtr<SimpleFontData>>* gInactiveFontData = 0;
323
324 PassRefPtr<SimpleFontData> FontCache::getCachedFontData(const FontDescription& fontDescription, const AtomicString& family, bool checkingAlternateName, ShouldRetain shouldRetain)
325 {
326     FontPlatformData* platformData = getCachedFontPlatformData(fontDescription, family, checkingAlternateName);
327     if (!platformData)
328         return 0;
329
330     return getCachedFontData(platformData, shouldRetain);
331 }
332
333 PassRefPtr<SimpleFontData> FontCache::getCachedFontData(const FontPlatformData* platformData, ShouldRetain shouldRetain)
334 {
335     if (!platformData)
336         return 0;
337
338 #if !ASSERT_DISABLED
339     if (shouldRetain == DoNotRetain)
340         ASSERT(m_purgePreventCount);
341 #endif
342
343     if (!gFontDataCache) {
344         gFontDataCache = new FontDataCache;
345         gInactiveFontData = new ListHashSet<RefPtr<SimpleFontData>>;
346     }
347
348     FontDataCache::iterator result = gFontDataCache->find(*platformData);
349     if (result == gFontDataCache->end()) {
350         pair<RefPtr<SimpleFontData>, unsigned> newValue(SimpleFontData::create(*platformData), shouldRetain == Retain ? 1 : 0);
351         gFontDataCache->set(*platformData, newValue);
352         if (shouldRetain == DoNotRetain)
353             gInactiveFontData->add(newValue.first);
354         return newValue.first.release();
355     }
356
357     if (!result.get()->value.second) {
358         ASSERT(gInactiveFontData->contains(result.get()->value.first));
359         gInactiveFontData->remove(result.get()->value.first);
360     }
361
362     if (shouldRetain == Retain)
363         result.get()->value.second++;
364     else if (!result.get()->value.second) {
365         // If shouldRetain is DoNotRetain and count is 0, we want to remove the fontData from 
366         // gInactiveFontData (above) and re-add here to update LRU position.
367         gInactiveFontData->add(result.get()->value.first);
368     }
369
370     return result.get()->value.first;
371 }
372
373 SimpleFontData* FontCache::getNonRetainedLastResortFallbackFont(const FontDescription& fontDescription)
374 {
375     return getLastResortFallbackFont(fontDescription, DoNotRetain).leakRef();
376 }
377
378 void FontCache::releaseFontData(const SimpleFontData* fontData)
379 {
380     ASSERT(gFontDataCache);
381     ASSERT(!fontData->isCustomFont());
382
383     FontDataCache::iterator it = gFontDataCache->find(fontData->platformData());
384     ASSERT(it != gFontDataCache->end());
385     if (it == gFontDataCache->end())
386         return;
387
388     ASSERT(it->value.second);
389     if (!--it->value.second)
390         gInactiveFontData->add(it->value.first);
391 }
392
393 void FontCache::purgeInactiveFontDataIfNeeded()
394 {
395     if (gInactiveFontData && !m_purgePreventCount && gInactiveFontData->size() > cMaxInactiveFontData)
396         purgeInactiveFontData(gInactiveFontData->size() - cTargetInactiveFontData);
397 }
398
399 void FontCache::purgeInactiveFontData(int count)
400 {
401     pruneUnreferencedEntriesFromFontGlyphsCache();
402
403     if (!gInactiveFontData || m_purgePreventCount)
404         return;
405
406     static bool isPurging;  // Guard against reentry when e.g. a deleted FontData releases its small caps FontData.
407     if (isPurging)
408         return;
409
410     isPurging = true;
411
412     Vector<RefPtr<SimpleFontData>, 20> fontDataToDelete;
413     ListHashSet<RefPtr<SimpleFontData>>::iterator end = gInactiveFontData->end();
414     ListHashSet<RefPtr<SimpleFontData>>::iterator it = gInactiveFontData->begin();
415     for (int i = 0; i < count && it != end; ++it, ++i) {
416         RefPtr<SimpleFontData>& fontData = *it.get();
417         gFontDataCache->remove(fontData->platformData());
418         // We should not delete SimpleFontData here because deletion can modify gInactiveFontData. See http://trac.webkit.org/changeset/44011
419         fontDataToDelete.append(fontData);
420     }
421
422     if (it == end) {
423         // Removed everything
424         gInactiveFontData->clear();
425     } else {
426         for (int i = 0; i < count; ++i)
427             gInactiveFontData->remove(gInactiveFontData->begin());
428     }
429
430     fontDataToDelete.clear();
431
432     if (gFontPlatformDataCache) {
433         Vector<FontPlatformDataCacheKey> keysToRemove;
434         keysToRemove.reserveInitialCapacity(gFontPlatformDataCache->size());
435         FontPlatformDataCache::iterator platformDataEnd = gFontPlatformDataCache->end();
436         for (FontPlatformDataCache::iterator platformData = gFontPlatformDataCache->begin(); platformData != platformDataEnd; ++platformData) {
437             if (platformData->value && !gFontDataCache->contains(*platformData->value))
438                 keysToRemove.append(platformData->key);
439         }
440         
441         size_t keysToRemoveCount = keysToRemove.size();
442         for (size_t i = 0; i < keysToRemoveCount; ++i)
443             gFontPlatformDataCache->remove(keysToRemove[i]);
444     }
445
446 #if ENABLE(OPENTYPE_VERTICAL)
447     FontVerticalDataCache& fontVerticalDataCache = fontVerticalDataCacheInstance();
448     if (!fontVerticalDataCache.isEmpty()) {
449         // Mark & sweep unused verticalData
450         FontVerticalDataCache::iterator verticalDataEnd = fontVerticalDataCache.end();
451         for (FontVerticalDataCache::iterator verticalData = fontVerticalDataCache.begin(); verticalData != verticalDataEnd; ++verticalData) {
452             if (verticalData->value)
453                 verticalData->value->m_inFontCache = false;
454         }
455         FontDataCache::iterator fontDataEnd = gFontDataCache->end();
456         for (FontDataCache::iterator fontData = gFontDataCache->begin(); fontData != fontDataEnd; ++fontData) {
457             OpenTypeVerticalData* verticalData = const_cast<OpenTypeVerticalData*>(fontData->value.first->verticalData());
458             if (verticalData)
459                 verticalData->m_inFontCache = true;
460         }
461         Vector<FontFileKey> keysToRemove;
462         keysToRemove.reserveInitialCapacity(fontVerticalDataCache.size());
463         for (FontVerticalDataCache::iterator verticalData = fontVerticalDataCache.begin(); verticalData != verticalDataEnd; ++verticalData) {
464             if (!verticalData->value || !verticalData->value->m_inFontCache)
465                 keysToRemove.append(verticalData->key);
466         }
467         for (size_t i = 0, count = keysToRemove.size(); i < count; ++i)
468             fontVerticalDataCache.take(keysToRemove[i]);
469     }
470 #endif
471
472     isPurging = false;
473 }
474
475 size_t FontCache::fontDataCount()
476 {
477     if (gFontDataCache)
478         return gFontDataCache->size();
479     return 0;
480 }
481
482 size_t FontCache::inactiveFontDataCount()
483 {
484     if (gInactiveFontData)
485         return gInactiveFontData->size();
486     return 0;
487 }
488
489 PassRefPtr<FontData> FontCache::getFontData(const FontDescription& description, int& familyIndex, FontSelector* fontSelector)
490 {
491     ASSERT(familyIndex != cAllFamiliesScanned);
492     RefPtr<FontData> result;
493
494     bool isFirst = !familyIndex;
495     int familyCount = description.familyCount();
496     for (;familyIndex < familyCount && !result; ++familyIndex) {
497         const AtomicString& family = description.familyAt(familyIndex);
498         if (family.isEmpty())
499             continue;
500         if (fontSelector)
501             result = fontSelector->getFontData(description, family);
502         if (!result)
503             result = getCachedFontData(description, family);
504     }
505     if (familyIndex == familyCount)
506         familyIndex = cAllFamiliesScanned;
507
508 #if PLATFORM(MAC)
509     if (!result) {
510         // We didn't find a font. Try to find a similar font using our own specific knowledge about our platform.
511         // For example on OS X, we know to map any families containing the words Arabic, Pashto, or Urdu to the
512         // Geeza Pro font.
513         result = similarFontPlatformData(description);
514     }
515 #endif
516
517     if (!result && isFirst) {
518         // If it's the primary font that we couldn't find, we try the following. In all other cases, we will
519         // just use per-character system fallback.
520         if (fontSelector) {
521             // Try the user's preferred standard font.
522             if (RefPtr<FontData> data = fontSelector->getFontData(description, standardFamily))
523                 return data.release();
524         }
525         // Still no result.  Hand back our last resort fallback font.
526         result = getLastResortFallbackFont(description);
527     }
528     return result.release();
529 }
530
531 static HashSet<FontSelector*>* gClients;
532
533 void FontCache::addClient(FontSelector* client)
534 {
535     if (!gClients)
536         gClients = new HashSet<FontSelector*>;
537
538     ASSERT(!gClients->contains(client));
539     gClients->add(client);
540 }
541
542 void FontCache::removeClient(FontSelector* client)
543 {
544     ASSERT(gClients);
545     ASSERT(gClients->contains(client));
546
547     gClients->remove(client);
548 }
549
550 static unsigned short gGeneration = 0;
551
552 unsigned short FontCache::generation()
553 {
554     return gGeneration;
555 }
556
557 void FontCache::invalidate()
558 {
559     if (!gClients) {
560         ASSERT(!gFontPlatformDataCache);
561         return;
562     }
563
564     if (gFontPlatformDataCache)
565         gFontPlatformDataCache->clear();
566     invalidateFontGlyphsCache();
567
568     gGeneration++;
569
570     Vector<Ref<FontSelector>> clients;
571     clients.reserveInitialCapacity(gClients->size());
572     for (auto it = gClients->begin(), end = gClients->end(); it != end; ++it)
573         clients.uncheckedAppend(**it);
574
575     for (unsigned i = 0; i < clients.size(); ++i)
576         clients[i]->fontCacheInvalidated();
577
578     purgeInactiveFontData();
579 }
580
581 } // namespace WebCore