mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
This caused us to lose our gn check certification. :( Turns out gn check was just ignoring all the header paths it didn't understand and so gn check passing for sky wasn't meaning much. I tried to straighten out some of the mess in this CL, but its going to take several more rounds of massaging before gn check passes again. On the bright side (almost) all of our headers are absolute now. Turns out my script (attached to the bug) didn't notice ../ includes but I'll fix that in the next patch. R=abarth@chromium.org BUG=435361 Review URL: https://codereview.chromium.org/746023002
268 lines
9.4 KiB
C++
268 lines
9.4 KiB
C++
/*
|
|
* Copyright (C) 2009 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:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * 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.
|
|
* * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
|
|
* OWNER OR 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 "sky/engine/config.h"
|
|
#include "sky/engine/public/platform/linux/WebFontInfo.h"
|
|
|
|
#include <fontconfig/fontconfig.h>
|
|
#include <string.h>
|
|
#include <unicode/utf16.h>
|
|
#include "sky/engine/public/platform/linux/WebFallbackFont.h"
|
|
#include "sky/engine/wtf/HashMap.h"
|
|
#include "sky/engine/wtf/Noncopyable.h"
|
|
#include "sky/engine/wtf/OwnPtr.h"
|
|
#include "sky/engine/wtf/PassOwnPtr.h"
|
|
#include "sky/engine/wtf/Vector.h"
|
|
#include "sky/engine/wtf/text/AtomicString.h"
|
|
#include "sky/engine/wtf/text/AtomicStringHash.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 WebFallbackFont& fallbackFont() const { return m_fallbackFont; }
|
|
bool hasGlyphForCharacter(WebUChar32 c)
|
|
{
|
|
return m_supportedCharacters && FcCharSetHasChar(m_supportedCharacters, c);
|
|
}
|
|
|
|
private:
|
|
static WebCString fontName(FcPattern* pattern)
|
|
{
|
|
FcChar8* familyName = nullptr;
|
|
if (FcPatternGetString(pattern, FC_FAMILY, 0, &familyName) != FcResultMatch)
|
|
return WebCString();
|
|
|
|
// FCChar8 is unsigned char, so we cast to char for WebCString.
|
|
const char* charFamily = reinterpret_cast<char*>(familyName);
|
|
return WebCString(charFamily, strlen(charFamily));
|
|
}
|
|
|
|
static WebCString fontFilename(FcPattern* pattern)
|
|
{
|
|
FcChar8* cFilename = nullptr;
|
|
if (FcPatternGetString(pattern, FC_FILE, 0, &cFilename) != FcResultMatch)
|
|
return WebCString();
|
|
const char* fontFilename = reinterpret_cast<char*>(cFilename);
|
|
return WebCString(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;
|
|
}
|
|
|
|
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));
|
|
}
|
|
|
|
~CachedFontSet()
|
|
{
|
|
m_fallbackList.clear();
|
|
FcFontSetDestroy(m_fontSet);
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
} // namespace blink
|