blob: 6abbb2ea43d5a64c1fa663fc4a4e908dc96935b3 [file] [log] [blame]
/*
* 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 "FontSelector.h"
#include "StringHash.h"
#include <wtf/HashMap.h>
namespace WebCore {
struct FontPlatformDataCacheKey {
FontPlatformDataCacheKey(const AtomicString& family = AtomicString(), unsigned size = 0, bool bold = false, bool italic = false,
bool isPrinterFont = false, FontRenderingMode renderingMode = NormalRenderingMode)
: m_family(family)
, m_size(size)
, m_bold(bold)
, m_italic(italic)
, m_printerFont(isPrinterFont)
, m_renderingMode(renderingMode)
{
}
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 &&
m_renderingMode == other.m_renderingMode;
}
AtomicString m_family;
unsigned m_size;
bool m_bold;
bool m_italic;
bool m_printerFont;
FontRenderingMode m_renderingMode;
};
inline unsigned computeHash(const FontPlatformDataCacheKey& fontKey)
{
unsigned hashCodes[4] = {
CaseFoldingHash::hash(fontKey.m_family),
fontKey.m_size,
static_cast<unsigned>(fontKey.m_bold) << 3 | static_cast<unsigned>(fontKey.m_italic) << 2 | static_cast<unsigned>(fontKey.m_printerFont) << 1 |
static_cast<unsigned>(fontKey.m_renderingMode)
};
return StringImpl::computeHash(reinterpret_cast<UChar*>(hashCodes), 4 * 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;
}
static const bool safeToCompareToEmptyOrDeleted = true;
};
struct FontPlatformDataCacheKeyTraits : WTF::GenericHashTraits<FontPlatformDataCacheKey> {
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<FontPlatformDataCacheKey, FontPlatformData*, FontPlatformDataCacheKeyHash, FontPlatformDataCacheKeyTraits> 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(), fontDescription.renderingMode());
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;
}
static const bool safeToCompareToEmptyOrDeleted = true;
};
struct FontDataCacheKeyTraits : WTF::GenericHashTraits<FontPlatformData> {
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<FontPlatformData, SimpleFontData*, FontDataCacheKeyHash, FontDataCacheKeyTraits> FontDataCache;
static FontDataCache* gFontDataCache = 0;
SimpleFontData* FontCache::getCachedFontData(const FontPlatformData* platformData)
{
if (!platformData)
return 0;
if (!gFontDataCache)
gFontDataCache = new FontDataCache;
SimpleFontData* result = gFontDataCache->get(*platformData);
if (!result) {
result = new SimpleFontData(*platformData);
gFontDataCache->set(*platformData, result);
}
return result;
}
const FontData* FontCache::getFontData(const Font& font, int& familyIndex, FontSelector* fontSelector)
{
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()) {
if (fontSelector) {
FontData* data = fontSelector->getFontData(font.fontDescription(), currFamily->family());
if (data)
return data;
}
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) {
// If it's the primary font that we couldn't find, we try the following. In all other cases, we will
// just use per-character system fallback.
if (fontSelector) {
// Try the user's preferred standard font.
if (FontData* data = fontSelector->getFontData(font.fontDescription(), "-webkit-standard"))
return data;
}
// Still no result. Hand back our last resort fallback font.
result = getLastResortFallbackFont(font.fontDescription());
}
// Now that we have a result, we need to go from FontPlatformData -> FontData.
return getCachedFontData(result);
}
} // namespace WebCore