Index: Source/platform/exported/linux/WebFontInfo.cpp |
diff --git a/Source/platform/exported/linux/WebFontInfo.cpp b/Source/platform/exported/linux/WebFontInfo.cpp |
index 10b2467cf31ee590531118c6c4662d04446a4ed0..acdf5958dd54bee1341aad2dc790838986b38a34 100644 |
--- a/Source/platform/exported/linux/WebFontInfo.cpp |
+++ b/Source/platform/exported/linux/WebFontInfo.cpp |
@@ -33,6 +33,13 @@ |
#include "public/platform/linux/WebFallbackFont.h" |
#include "public/platform/linux/WebFontRenderStyle.h" |
+#include "wtf/HashMap.h" |
+#include "wtf/Noncopyable.h" |
+#include "wtf/OwnPtr.h" |
+#include "wtf/PassOwnPtr.h" |
+#include "wtf/Vector.h" |
+#include "wtf/text/AtomicString.h" |
+#include "wtf/text/AtomicStringHash.h" |
#include <fontconfig/fontconfig.h> |
#include <string.h> |
#include <unicode/utf16.h> |
@@ -46,94 +53,223 @@ void WebFontInfo::setSubpixelPositioning(bool subpixelPositioning) |
useSubpixelPositioning = subpixelPositioning; |
} |
-void WebFontInfo::fallbackFontForChar(WebUChar32 c, const char* preferredLocale, WebFallbackFont* fallbackFont) |
-{ |
- FcCharSet* cset = FcCharSetCreate(); |
- FcCharSetAddChar(cset, c); |
- FcPattern* pattern = FcPatternCreate(); |
+class CachedFont { |
+public: |
+ // Note: We pass the charset explicitly as callers |
+ // should not create CachedFont entries without knowing |
+ // that the FcPattern contains a valid charset. |
+ CachedFont(FcPattern* pattern, FcCharSet* charSet) |
+ : m_supportedCharacters(charSet) |
+ { |
+ ASSERT(pattern); |
+ ASSERT(charSet); |
+ m_fallbackFont.name = fontName(pattern); |
+ m_fallbackFont.filename = fontFilename(pattern); |
+ m_fallbackFont.ttcIndex = fontTtcIndex(pattern); |
+ m_fallbackFont.isBold = fontIsBold(pattern); |
+ m_fallbackFont.isItalic = fontIsItalic(pattern); |
+ } |
+ const WebFallbackFont& fallbackFont() const { return m_fallbackFont; } |
+ bool hasGlyphForCharacter(WebUChar32 c) |
+ { |
+ return m_supportedCharacters && FcCharSetHasChar(m_supportedCharacters, c); |
+ } |
- FcValue fcvalue; |
- fcvalue.type = FcTypeCharSet; |
- fcvalue.u.c = cset; |
- FcPatternAdd(pattern, FC_CHARSET, fcvalue, FcFalse); |
+private: |
+ static WebCString fontName(FcPattern* pattern) |
+ { |
+ FcChar8* familyName; |
+ if (FcPatternGetString(pattern, FC_FAMILY, 0, &familyName) != FcResultMatch) |
+ return WebCString(); |
- fcvalue.type = FcTypeBool; |
- fcvalue.u.b = FcTrue; |
- FcPatternAdd(pattern, FC_SCALABLE, fcvalue, FcFalse); |
+ // FCChar8 is unsigned char, so we cast to char for WebCString. |
+ const char* charFamily = reinterpret_cast<char*>(familyName); |
+ return WebCString(charFamily, strlen(charFamily)); |
+ } |
- if (preferredLocale && strlen(preferredLocale)) { |
- FcLangSet* langset = FcLangSetCreate(); |
- FcLangSetAdd(langset, reinterpret_cast<const FcChar8 *>(preferredLocale)); |
- FcPatternAddLangSet(pattern, FC_LANG, langset); |
- FcLangSetDestroy(langset); |
+ static WebCString fontFilename(FcPattern* pattern) |
+ { |
+ FcChar8* cFilename; |
+ if (FcPatternGetString(pattern, FC_FILE, 0, &cFilename) != FcResultMatch) |
+ return WebCString(); |
+ const char* fontFilename = reinterpret_cast<char*>(cFilename); |
+ return WebCString(fontFilename, strlen(fontFilename)); |
} |
- FcConfigSubstitute(0, pattern, FcMatchPattern); |
- FcDefaultSubstitute(pattern); |
+ static int fontTtcIndex(FcPattern* pattern) |
+ { |
+ int ttcIndex; |
+ if (FcPatternGetInteger(pattern, FC_INDEX, 0, &ttcIndex) != FcResultMatch && ttcIndex < 0) |
+ return 0; |
+ return ttcIndex; |
+ } |
- // Default substitution reintroduces an FC_LANG entry, |
- // which causes sorting order to change. |
- if (!preferredLocale || !strlen(preferredLocale)) |
- FcPatternDel(pattern, FC_LANG); |
+ static bool fontIsBold(FcPattern* pattern) |
+ { |
+ int weight; |
+ if (FcPatternGetInteger(pattern, FC_WEIGHT, 0, &weight) != FcResultMatch) |
+ return false; |
+ return weight >= FC_WEIGHT_BOLD; |
+ } |
- FcResult result; |
- FcFontSet* fontSet = FcFontSort(0, pattern, 0, 0, &result); |
- FcPatternDestroy(pattern); |
- FcCharSetDestroy(cset); |
+ static bool fontIsItalic(FcPattern* pattern) |
+ { |
+ int slant; |
+ if (FcPatternGetInteger(pattern, FC_SLANT, 0, &slant) != FcResultMatch) |
+ return false; |
+ return slant != FC_SLANT_ROMAN; |
+ } |
- if (!fontSet) { |
- fallbackFont->name = WebCString(); |
- fallbackFont->isBold = false; |
- fallbackFont->isItalic = false; |
- return; |
+ WebFallbackFont m_fallbackFont; |
+ // m_supportedCharaters is owned by the parent |
+ // FcFontSet and should never be freed. |
+ FcCharSet* m_supportedCharacters; |
+}; |
+ |
+ |
+class CachedFontSet { |
+ WTF_MAKE_NONCOPYABLE(CachedFontSet); |
+public: |
+ // CachedFontSet takes ownership of the passed FcFontSet. |
+ static PassOwnPtr<CachedFontSet> createForLocale(const char* locale) |
+ { |
+ FcFontSet* fontSet = createFcFontSetForLocale(locale); |
+ return adoptPtr(new CachedFontSet(fontSet)); |
} |
- // Older versions of fontconfig have a bug where they cannot select |
- // only scalable fonts so we have to manually filter the results. |
- for (int i = 0; i < fontSet->nfont; ++i) { |
- FcPattern* current = fontSet->fonts[i]; |
- FcBool isScalable; |
- if (FcPatternGetBool(current, FC_SCALABLE, 0, &isScalable) != FcResultMatch |
- || !isScalable) |
- continue; |
+ ~CachedFontSet() |
+ { |
+ m_fallbackList.clear(); |
+ FcFontSetDestroy(m_fontSet); |
+ } |
- // fontconfig can also return fonts which are unreadable |
- FcChar8* cFilename; |
- if (FcPatternGetString(current, FC_FILE, 0, &cFilename) != FcResultMatch) |
- continue; |
+ WebFallbackFont fallbackFontForChar(WebUChar32 c) |
+ { |
+ Vector<CachedFont>::iterator itr = m_fallbackList.begin(); |
+ for (; itr != m_fallbackList.end(); itr++) { |
+ if (itr->hasGlyphForCharacter(c)) |
+ return itr->fallbackFont(); |
+ } |
+ // The previous code just returned garbage if the user didn't |
+ // have the necessary fonts, this seems better than garbage. |
+ // Current callers happen to ignore any values with an empty family string. |
+ return WebFallbackFont(); |
+ } |
- if (access(reinterpret_cast<char*>(cFilename), R_OK)) |
- continue; |
+private: |
+ static FcFontSet* createFcFontSetForLocale(const char* locale) |
+ { |
+ FcPattern* pattern = FcPatternCreate(); |
- const char* fontFilename = reinterpret_cast<char*>(cFilename); |
- fallbackFont->filename = WebCString(fontFilename, strlen(fontFilename)); |
+ if (locale) { |
+ // FcChar* is unsigned char* so we have to cast. |
+ FcPatternAddString(pattern, FC_LANG, reinterpret_cast<const FcChar8*>(locale)); |
+ } |
- // Index into font collection. |
- int ttcIndex; |
- if (FcPatternGetInteger(current, FC_INDEX, 0, &ttcIndex) != FcResultMatch && ttcIndex < 0) |
- continue; |
- fallbackFont->ttcIndex = ttcIndex; |
+ FcValue fcvalue; |
+ fcvalue.type = FcTypeBool; |
+ fcvalue.u.b = FcTrue; |
+ FcPatternAdd(pattern, FC_SCALABLE, fcvalue, FcFalse); |
- FcChar8* familyName; |
- if (FcPatternGetString(current, FC_FAMILY, 0, &familyName) == FcResultMatch) { |
- const char* charFamily = reinterpret_cast<char*>(familyName); |
- fallbackFont->name = WebCString(charFamily, strlen(charFamily)); |
+ FcConfigSubstitute(0, pattern, FcMatchPattern); |
+ FcDefaultSubstitute(pattern); |
+ |
+ if (!locale) |
+ FcPatternDel(pattern, FC_LANG); |
+ |
+ // The result parameter returns if any fonts were found. |
+ // We already handle 0 fonts correctly, so we ignore the param. |
+ FcResult result; |
+ FcFontSet* fontSet = FcFontSort(0, pattern, 0, 0, &result); |
+ FcPatternDestroy(pattern); |
+ |
+ // The caller will take ownership of this FcFontSet. |
+ return fontSet; |
+ } |
+ |
+ CachedFontSet(FcFontSet* fontSet) |
+ : m_fontSet(fontSet) |
+ { |
+ fillFallbackList(); |
+ } |
+ |
+ void fillFallbackList() |
+ { |
+ ASSERT(m_fallbackList.isEmpty()); |
+ if (!m_fontSet) |
+ return; |
+ |
+ for (int i = 0; i < m_fontSet->nfont; ++i) { |
+ FcPattern* pattern = m_fontSet->fonts[i]; |
+ |
+ // Ignore any bitmap fonts users may still have installed from last century. |
+ FcBool isScalable; |
+ if (FcPatternGetBool(pattern, FC_SCALABLE, 0, &isScalable) != FcResultMatch || !isScalable) |
+ continue; |
+ |
+ // Ignore any fonts FontConfig knows about, but that we don't have permission to read. |
+ FcChar8* cFilename; |
+ if (FcPatternGetString(pattern, FC_FILE, 0, &cFilename) != FcResultMatch) |
+ continue; |
+ if (access(reinterpret_cast<char*>(cFilename), R_OK)) |
+ continue; |
+ |
+ // Make sure this font can tell us what characters it has glyphs for. |
+ FcCharSet* charSet; |
+ if (FcPatternGetCharSet(pattern, FC_CHARSET, 0, &charSet) != FcResultMatch) |
+ continue; |
+ |
+ m_fallbackList.append(CachedFont(pattern, charSet)); |
} |
- int weight; |
- if (FcPatternGetInteger(current, FC_WEIGHT, 0, &weight) == FcResultMatch) |
- fallbackFont->isBold = weight >= FC_WEIGHT_BOLD; |
- else |
- fallbackFont->isBold = false; |
- int slant; |
- if (FcPatternGetInteger(current, FC_SLANT, 0, &slant) == FcResultMatch) |
- fallbackFont->isItalic = slant != FC_SLANT_ROMAN; |
- else |
- fallbackFont->isItalic = false; |
- FcFontSetDestroy(fontSet); |
- return; |
} |
- FcFontSetDestroy(fontSet); |
+ FcFontSet* m_fontSet; // Owned by this object. |
+ // CachedFont has a FcCharset* which points into the FcFontSet. |
+ // If the FcFontSet is ever destroyed, the fallbackList |
+ // must be cleared first. |
+ Vector<CachedFont> m_fallbackList; |
+}; |
+ |
+class FontSetCache { |
+public: |
+ static FontSetCache& shared() |
+ { |
+ DEFINE_STATIC_LOCAL(FontSetCache, cache, ()); |
+ return cache; |
+ } |
+ |
+ WebFallbackFont fallbackFontForCharInLocale(WebUChar32 c, const char* locale) |
+ { |
+ DEFINE_STATIC_LOCAL(AtomicString, kNoLocale, ("NO_LOCALE_SPECIFIED")); |
+ AtomicString localeKey; |
+ if (locale && strlen(locale)) { |
+ localeKey = AtomicString(locale); |
+ } else { |
+ // String hash computation the m_setsByLocale map needs |
+ // a non-empty string. |
+ localeKey = kNoLocale; |
+ } |
+ |
+ LocaleToCachedFont::iterator itr = m_setsByLocale.find(localeKey); |
+ if (itr == m_setsByLocale.end()) { |
+ OwnPtr<CachedFontSet> newEntry = CachedFontSet::createForLocale(strlen(locale) ? locale : 0); |
+ return m_setsByLocale.add(localeKey, newEntry.release()).storedValue->value->fallbackFontForChar(c); |
+ } |
+ return itr.get()->value->fallbackFontForChar(c); |
+ } |
+ // FIXME: We may wish to add a way to prune the cache at a later time. |
+ |
+private: |
+ // FIXME: This shouldn't need to be AtomicString, but |
+ // currently HashTraits<const char*> isn't smart enough |
+ // to hash the string (only does pointer compares). |
+ typedef HashMap<AtomicString, OwnPtr<CachedFontSet> > LocaleToCachedFont; |
+ LocaleToCachedFont m_setsByLocale; |
+}; |
+ |
+void WebFontInfo::fallbackFontForChar(WebUChar32 c, const char* locale, WebFallbackFont* fallbackFont) |
+{ |
+ *fallbackFont = FontSetCache::shared().fallbackFontForCharInLocale(c, locale); |
} |
void WebFontInfo::renderStyleForStrike(const char* family, int sizeAndStyle, WebFontRenderStyle* out) |