mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
265 lines
8.9 KiB
C++
265 lines
8.9 KiB
C++
/*
|
|
* Copyright (C) 2012 Google 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.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "flutter/sky/engine/platform/fonts/FontCache.h"
|
|
|
|
#include <fontconfig/fontconfig.h>
|
|
#include <string.h>
|
|
#include <unicode/utf16.h>
|
|
#include <unistd.h>
|
|
|
|
#include "flutter/sky/engine/public/platform/Platform.h"
|
|
#include "flutter/sky/engine/wtf/HashMap.h"
|
|
#include "flutter/sky/engine/wtf/Noncopyable.h"
|
|
#include "flutter/sky/engine/wtf/OwnPtr.h"
|
|
#include "flutter/sky/engine/wtf/PassOwnPtr.h"
|
|
#include "flutter/sky/engine/wtf/Vector.h"
|
|
#include "flutter/sky/engine/wtf/text/AtomicString.h"
|
|
#include "flutter/sky/engine/wtf/text/AtomicStringHash.h"
|
|
#include "flutter/sky/engine/wtf/text/CString.h"
|
|
|
|
namespace blink {
|
|
|
|
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 FontCache::PlatformFallbackFont& fallbackFont() const {
|
|
return m_fallbackFont;
|
|
}
|
|
bool hasGlyphForCharacter(UChar32 c) {
|
|
return m_supportedCharacters && FcCharSetHasChar(m_supportedCharacters, c);
|
|
}
|
|
|
|
private:
|
|
static String fontName(FcPattern* pattern) {
|
|
FcChar8* familyName = nullptr;
|
|
if (FcPatternGetString(pattern, FC_FAMILY, 0, &familyName) != FcResultMatch)
|
|
return String();
|
|
|
|
// FCChar8 is unsigned char, so we cast to char for CString.
|
|
const char* charFamily = reinterpret_cast<char*>(familyName);
|
|
return String::fromUTF8(charFamily, strlen(charFamily));
|
|
}
|
|
|
|
static CString fontFilename(FcPattern* pattern) {
|
|
FcChar8* cFilename = nullptr;
|
|
if (FcPatternGetString(pattern, FC_FILE, 0, &cFilename) != FcResultMatch)
|
|
return CString();
|
|
const char* fontFilename = reinterpret_cast<char*>(cFilename);
|
|
return CString(fontFilename, strlen(fontFilename));
|
|
}
|
|
|
|
static int fontTtcIndex(FcPattern* pattern) {
|
|
int ttcIndex = -1;
|
|
if (FcPatternGetInteger(pattern, FC_INDEX, 0, &ttcIndex) != FcResultMatch ||
|
|
ttcIndex < 0)
|
|
return 0;
|
|
return ttcIndex;
|
|
}
|
|
|
|
static bool fontIsBold(FcPattern* pattern) {
|
|
int weight = 0;
|
|
if (FcPatternGetInteger(pattern, FC_WEIGHT, 0, &weight) != FcResultMatch)
|
|
return false;
|
|
return weight >= FC_WEIGHT_BOLD;
|
|
}
|
|
|
|
static bool fontIsItalic(FcPattern* pattern) {
|
|
int slant = 0;
|
|
if (FcPatternGetInteger(pattern, FC_SLANT, 0, &slant) != FcResultMatch)
|
|
return false;
|
|
return slant != FC_SLANT_ROMAN;
|
|
}
|
|
|
|
FontCache::PlatformFallbackFont 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));
|
|
}
|
|
|
|
~CachedFontSet() {
|
|
m_fallbackList.clear();
|
|
FcFontSetDestroy(m_fontSet);
|
|
}
|
|
|
|
FontCache::PlatformFallbackFont fallbackFontForChar(UChar32 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 FontCache::PlatformFallbackFont();
|
|
}
|
|
|
|
private:
|
|
static FcFontSet* createFcFontSetForLocale(const char* locale) {
|
|
FcPattern* pattern = FcPatternCreate();
|
|
|
|
if (locale) {
|
|
// FcChar* is unsigned char* so we have to cast.
|
|
FcPatternAddString(pattern, FC_LANG,
|
|
reinterpret_cast<const FcChar8*>(locale));
|
|
}
|
|
|
|
FcValue fcvalue;
|
|
fcvalue.type = FcTypeBool;
|
|
fcvalue.u.b = FcTrue;
|
|
FcPatternAdd(pattern, FC_SCALABLE, fcvalue, FcFalse);
|
|
|
|
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));
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
FontCache::PlatformFallbackFont fallbackFontForCharInLocale(
|
|
UChar32 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 FontCache::getFontForCharacter(
|
|
UChar32 c,
|
|
const char* locale,
|
|
FontCache::PlatformFallbackFont* fallbackFont) {
|
|
*fallbackFont = FontSetCache::shared().fallbackFontForCharInLocale(c, locale);
|
|
}
|
|
|
|
} // namespace blink
|