/* * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "FontCache.h" #include "Font.h" #include "FontFallbackList.h" #include "FontPlatformData.h" #include "StringHash.h" #include namespace WebCore { struct FontPlatformDataCacheKey { FontPlatformDataCacheKey(const AtomicString& family = AtomicString(), unsigned size = 0, bool bold = false, bool italic = false, bool isPrinterFont = false) : m_family(family) , m_size(size) , m_bold(bold) , m_italic(italic) , m_printerFont(isPrinterFont) { } bool operator==(const FontPlatformDataCacheKey& other) const { return equalIgnoringCase(m_family, other.m_family) && m_size == other.m_size && m_bold == other.m_bold && m_italic == other.m_italic && m_printerFont == other.m_printerFont; } AtomicString m_family; unsigned m_size; bool m_bold; bool m_italic; bool m_printerFont; }; inline unsigned computeHash(const FontPlatformDataCacheKey& fontKey) { unsigned hashCodes[3] = { CaseInsensitiveHash::hash(fontKey.m_family), fontKey.m_size, static_cast(fontKey.m_bold) << 2 | static_cast(fontKey.m_italic) << 1 | static_cast(fontKey.m_printerFont) }; return StringImpl::computeHash(reinterpret_cast(hashCodes), 3 * sizeof(unsigned) / sizeof(UChar)); } struct FontPlatformDataCacheKeyHash { static unsigned hash(const FontPlatformDataCacheKey& font) { return computeHash(font); } static bool equal(const FontPlatformDataCacheKey& a, const FontPlatformDataCacheKey& b) { return a == b; } }; struct FontPlatformDataCacheKeyTraits : WTF::GenericHashTraits { static const bool emptyValueIsZero = true; static const bool needsDestruction = false; static const FontPlatformDataCacheKey& deletedValue() { static FontPlatformDataCacheKey key(nullAtom, 0xFFFFFFFFU, false, false); return key; } static const FontPlatformDataCacheKey& emptyValue() { static FontPlatformDataCacheKey key(nullAtom, 0, false, false); return key; } }; typedef HashMap FontPlatformDataCache; static FontPlatformDataCache* gFontPlatformDataCache = 0; static const AtomicString& alternateFamilyName(const AtomicString& familyName) { // Alias Courier <-> Courier New static AtomicString courier("Courier"), courierNew("Courier New"); if (equalIgnoringCase(familyName, courier)) return courierNew; if (equalIgnoringCase(familyName, courierNew)) return courier; // Alias Times and Times New Roman. static AtomicString times("Times"), timesNewRoman("Times New Roman"); if (equalIgnoringCase(familyName, times)) return timesNewRoman; if (equalIgnoringCase(familyName, timesNewRoman)) return times; // Alias Arial and Helvetica static AtomicString arial("Arial"), helvetica("Helvetica"); if (equalIgnoringCase(familyName, arial)) return helvetica; if (equalIgnoringCase(familyName, helvetica)) return arial; return emptyAtom; } FontPlatformData* FontCache::getCachedFontPlatformData(const FontDescription& fontDescription, const AtomicString& familyName, bool checkingAlternateName) { if (!gFontPlatformDataCache) { gFontPlatformDataCache = new FontPlatformDataCache; platformInit(); } FontPlatformDataCacheKey key(familyName, fontDescription.computedPixelSize(), fontDescription.bold(), fontDescription.italic(), fontDescription.usePrinterFont()); FontPlatformData* result = 0; bool foundResult; FontPlatformDataCache::iterator it = gFontPlatformDataCache->find(key); if (it == gFontPlatformDataCache->end()) { result = createFontPlatformData(fontDescription, familyName); gFontPlatformDataCache->set(key, result); foundResult = result; } else { result = it->second; foundResult = true; } if (!foundResult && !checkingAlternateName) { // We were unable to find a font. We have a small set of fonts that we alias to other names, // e.g., Arial/Helvetica, Courier/Courier New, etc. Try looking up the font under the aliased name. const AtomicString& alternateName = alternateFamilyName(familyName); if (!alternateName.isEmpty()) result = getCachedFontPlatformData(fontDescription, alternateName, true); if (result) gFontPlatformDataCache->set(key, new FontPlatformData(*result)); // Cache the result under the old name. } return result; } struct FontDataCacheKeyHash { static unsigned hash(const FontPlatformData& platformData) { return platformData.hash(); } static bool equal(const FontPlatformData& a, const FontPlatformData& b) { return a == b; } }; struct FontDataCacheKeyTraits : WTF::GenericHashTraits { static const bool emptyValueIsZero = true; static const bool needsDestruction = false; static const FontPlatformData& deletedValue() { static FontPlatformData key = FontPlatformData::Deleted(); return key; } static const FontPlatformData& emptyValue() { static FontPlatformData key; return key; } }; typedef HashMap FontDataCache; static FontDataCache* gFontDataCache = 0; FontData* FontCache::getCachedFontData(const FontPlatformData* platformData) { if (!platformData) return 0; if (!gFontDataCache) gFontDataCache = new FontDataCache; FontData* result = gFontDataCache->get(*platformData); if (!result) { result = new FontData(*platformData); gFontDataCache->set(*platformData, result); } return result; } const FontData* FontCache::getFontData(const Font& font, int& familyIndex) { FontPlatformData* result = 0; int startIndex = familyIndex; const FontFamily* startFamily = &font.fontDescription().family(); for (int i = 0; startFamily && i < startIndex; i++) startFamily = startFamily->next(); const FontFamily* currFamily = startFamily; while (currFamily && !result) { familyIndex++; if (currFamily->family().length()) result = getCachedFontPlatformData(font.fontDescription(), currFamily->family()); currFamily = currFamily->next(); } if (!currFamily) familyIndex = cAllFamiliesScanned; if (!result) // We didn't find a font. Try to find a similar font using our own specific knowledge about our platform. // For example on OS X, we know to map any families containing the words Arabic, Pashto, or Urdu to the // Geeza Pro font. result = getSimilarFontPlatformData(font); if (!result && startIndex == 0) // We still don't have a result. Hand back our last resort fallback font. We only do the last resort fallback // when trying to find the primary font. Otherwise our fallback will rely on the actual characters used. result = getLastResortFallbackFont(font); // Now that we have a result, we need to go from FontPlatformData -> FontData. return getCachedFontData(result); } } // namespace WebCore