blob: 4e435cd616f4a99395b775742e5c76ba1e8af14c [file] [log] [blame]
/*
* Copyright 2013 The Android Open Source Project
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkFontConfigInterface.h"
#include "SkTypeface_android.h"
#include "SkFontConfigParser_android.h"
#include "SkFontConfigTypeface.h"
#include "SkFontHost_FreeType_common.h"
#include "SkFontMgr.h"
#include "SkGlyphCache.h"
#include "SkPaint.h"
#include "SkString.h"
#include "SkStream.h"
#include "SkThread.h"
#include "SkTypefaceCache.h"
#include "SkTArray.h"
#include "SkTDict.h"
#include "SkTSearch.h"
#include <stdio.h>
#include <string.h>
#ifndef SK_DEBUG_FONTS
#define SK_DEBUG_FONTS 0
#endif
#if SK_DEBUG_FONTS
#define DEBUG_FONT(args) SkDebugf args
#else
#define DEBUG_FONT(args)
#endif
///////////////////////////////////////////////////////////////////////////////
// For test only.
static const char* gTestMainConfigFile = NULL;
static const char* gTestFallbackConfigFile = NULL;
static const char* gTestFontFilePrefix = NULL;
///////////////////////////////////////////////////////////////////////////////
typedef int32_t FontRecID;
#define INVALID_FONT_REC_ID -1
typedef int32_t FamilyRecID;
#define INVALID_FAMILY_REC_ID -1
// used to record our notion of the pre-existing fonts
struct FontRec {
SkAutoTUnref<SkTypeface> fTypeface;
SkString fFileName;
SkTypeface::Style fStyle;
bool fIsValid;
FamilyRecID fFamilyRecID;
};
struct FamilyRec {
FamilyRec() {
memset(fFontRecID, INVALID_FONT_REC_ID, sizeof(fFontRecID));
}
static const int FONT_STYLE_COUNT = 4;
FontRecID fFontRecID[FONT_STYLE_COUNT];
bool fIsFallbackFont;
SkString fFallbackName;
SkPaintOptionsAndroid fPaintOptions;
};
typedef SkTDArray<FamilyRecID> FallbackFontList;
class SkFontConfigInterfaceAndroid : public SkFontConfigInterface {
public:
SkFontConfigInterfaceAndroid(SkTDArray<FontFamily*>& fontFamilies);
virtual ~SkFontConfigInterfaceAndroid();
virtual bool matchFamilyName(const char familyName[],
SkTypeface::Style requested,
FontIdentity* outFontIdentifier,
SkString* outFamilyName,
SkTypeface::Style* outStyle) SK_OVERRIDE;
virtual SkStream* openStream(const FontIdentity&) SK_OVERRIDE;
// new APIs
virtual SkDataTable* getFamilyNames() SK_OVERRIDE;
virtual bool matchFamilySet(const char inFamilyName[],
SkString* outFamilyName,
SkTArray<FontIdentity>*) SK_OVERRIDE;
/**
* Get the family name of the font in the default fallback font list that
* contains the specified chararacter. if no font is found, returns false.
*/
bool getFallbackFamilyNameForChar(SkUnichar uni, const char* lang, SkString* name);
private:
void addFallbackFamily(FamilyRecID fontRecID);
SkTypeface* getTypefaceForFontRec(FontRecID fontRecID);
FallbackFontList* findFallbackFontList(const SkLanguage& lang, bool isOriginal = true);
SkTArray<FontRec, true> fFonts;
SkTArray<FamilyRec, true> fFontFamilies;
SkTDict<FamilyRecID> fFamilyNameDict;
FamilyRecID fDefaultFamilyRecID;
// (SkLanguage)<->(fallback chain index) translation
SkTDict<FallbackFontList*> fFallbackFontDict;
SkTDict<FallbackFontList*> fFallbackFontAliasDict;
FallbackFontList fDefaultFallbackList;
// fallback info for current locale
SkString fCachedLocale;
FallbackFontList* fLocaleFallbackFontList;
};
///////////////////////////////////////////////////////////////////////////////
SK_DECLARE_STATIC_MUTEX(gGetSingletonInterfaceMutex);
static SkFontConfigInterfaceAndroid* getSingletonInterface() {
static SkFontConfigInterfaceAndroid* gFontConfigInterface;
SkAutoMutexAcquire ac(gGetSingletonInterfaceMutex);
if (NULL == gFontConfigInterface) {
// load info from a configuration file that we can use to populate the
// system/fallback font structures
SkTDArray<FontFamily*> fontFamilies;
if (!gTestMainConfigFile) {
SkFontConfigParser::GetFontFamilies(fontFamilies);
} else {
SkFontConfigParser::GetTestFontFamilies(fontFamilies, gTestMainConfigFile,
gTestFallbackConfigFile);
}
gFontConfigInterface = new SkFontConfigInterfaceAndroid(fontFamilies);
// cleanup the data we received from the parser
fontFamilies.deleteAll();
}
return gFontConfigInterface;
}
SkFontConfigInterface* SkFontConfigInterface::GetSingletonDirectInterface(SkBaseMutex*) {
// Doesn't need passed-in mutex because getSingletonInterface() uses one
return getSingletonInterface();
}
///////////////////////////////////////////////////////////////////////////////
static bool has_font(const SkTArray<FontRec, true>& array, const SkString& filename) {
for (int i = 0; i < array.count(); i++) {
if (array[i].fFileName == filename) {
return true;
}
}
return false;
}
#ifndef SK_FONT_FILE_PREFIX
#define SK_FONT_FILE_PREFIX "/fonts/"
#endif
static void get_path_for_sys_fonts(SkString* full, const SkString& name) {
if (gTestFontFilePrefix) {
full->set(gTestFontFilePrefix);
} else {
full->set(getenv("ANDROID_ROOT"));
full->append(SK_FONT_FILE_PREFIX);
}
full->append(name);
}
static void insert_into_name_dict(SkTDict<FamilyRecID>& familyNameDict,
const char* name, FamilyRecID familyRecID) {
SkAutoAsciiToLC tolc(name);
if (familyNameDict.find(tolc.lc())) {
SkDebugf("---- system font attempting to use a the same name [%s] for"
"multiple families. skipping subsequent occurrences", tolc.lc());
} else {
familyNameDict.set(tolc.lc(), familyRecID);
}
}
///////////////////////////////////////////////////////////////////////////////
SkFontConfigInterfaceAndroid::SkFontConfigInterfaceAndroid(SkTDArray<FontFamily*>& fontFamilies) :
fFonts(fontFamilies.count()),
fFontFamilies(fontFamilies.count() / FamilyRec::FONT_STYLE_COUNT),
fFamilyNameDict(1024),
fDefaultFamilyRecID(INVALID_FAMILY_REC_ID),
fFallbackFontDict(128),
fFallbackFontAliasDict(128),
fLocaleFallbackFontList(NULL) {
for (int i = 0; i < fontFamilies.count(); ++i) {
FontFamily* family = fontFamilies[i];
// defer initializing the familyRec until we can be sure that at least
// one of it's children contains a valid font file
FamilyRec* familyRec = NULL;
FamilyRecID familyRecID = INVALID_FAMILY_REC_ID;
for (int j = 0; j < family->fFonts.count(); ++j) {
SkString filename;
get_path_for_sys_fonts(&filename, family->fFonts[j].fFileName);
if (has_font(fFonts, filename)) {
DEBUG_FONT(("---- system font and fallback font files specify a duplicate "
"font %s, skipping the second occurrence", filename.c_str()));
}
FontRec& fontRec = fFonts.push_back();
fontRec.fFileName = filename;
fontRec.fStyle = SkTypeface::kNormal;
fontRec.fIsValid = false;
fontRec.fFamilyRecID = familyRecID;
const FontRecID fontRecID = fFonts.count() - 1;
SkAutoTUnref<SkStream> stream(SkStream::NewFromFile(filename.c_str()));
if (stream.get() != NULL) {
bool isFixedWidth;
SkString name;
fontRec.fIsValid = SkTypeface_FreeType::ScanFont(stream.get(), 0,
&name, &fontRec.fStyle,
&isFixedWidth);
} else {
if (!family->fIsFallbackFont) {
SkDebugf("---- failed to open <%s> as a font\n", filename.c_str());
}
}
if (fontRec.fIsValid) {
DEBUG_FONT(("---- SystemFonts[%d][%d] fallback=%d file=%s",
i, fFonts.count() - 1, family->fIsFallbackFont, filename.c_str()));
} else {
DEBUG_FONT(("---- SystemFonts[%d][%d] fallback=%d file=%s (INVALID)",
i, fFonts.count() - 1, family->fIsFallbackFont, filename.c_str()));
continue;
}
// create a familyRec now that we know that at least one font in
// the family is valid
if (familyRec == NULL) {
familyRec = &fFontFamilies.push_back();
familyRecID = fFontFamilies.count() - 1;
fontRec.fFamilyRecID = familyRecID;
familyRec->fIsFallbackFont = family->fIsFallbackFont;
familyRec->fPaintOptions = family->fFonts[j].fPaintOptions;
} else if (familyRec->fPaintOptions != family->fFonts[j].fPaintOptions) {
SkDebugf("Every font file within a family must have identical"
"language and variant attributes");
sk_throw();
}
// add this font to the current familyRec
if (INVALID_FONT_REC_ID != familyRec->fFontRecID[fontRec.fStyle]) {
DEBUG_FONT(("Overwriting familyRec for style[%d] old,new:(%d,%d)",
fontRec.fStyle, familyRec->fFontRecID[fontRec.fStyle],
fontRecID));
}
familyRec->fFontRecID[fontRec.fStyle] = fontRecID;
}
if (familyRec) {
if (familyRec->fIsFallbackFont) {
// add the font to the appropriate fallback chains and also insert a
// unique name into the familyNameDict for internal usage
addFallbackFamily(familyRecID);
} else {
// add the names that map to this family to the dictionary for easy lookup
const SkTArray<SkString>& names = family->fNames;
if (names.empty()) {
SkDEBUGFAIL("ERROR: non-fallback font with no name");
continue;
}
for (int i = 0; i < names.count(); i++) {
insert_into_name_dict(fFamilyNameDict, names[i].c_str(), familyRecID);
}
}
}
}
DEBUG_FONT(("---- We have %d system fonts", fFonts.count()));
if (fFontFamilies.count() > 0) {
fDefaultFamilyRecID = 0;
}
// scans the default fallback font chain, adding every entry to every other
// fallback font chain to which it does not belong. this results in every
// language-specific fallback font chain having all of its fallback fonts at
// the front of the chain, and everything else at the end.
FallbackFontList* fallbackList;
SkTDict<FallbackFontList*>::Iter iter(fFallbackFontDict);
const char* fallbackLang = iter.next(&fallbackList);
while(fallbackLang != NULL) {
for (int i = 0; i < fDefaultFallbackList.count(); i++) {
FamilyRecID familyRecID = fDefaultFallbackList[i];
const SkString& fontLang = fFontFamilies[familyRecID].fPaintOptions.getLanguage().getTag();
if (strcmp(fallbackLang, fontLang.c_str()) != 0) {
fallbackList->push(familyRecID);
}
}
// move to the next fallback list in the dictionary
fallbackLang = iter.next(&fallbackList);
}
}
SkFontConfigInterfaceAndroid::~SkFontConfigInterfaceAndroid() {
// iterate through and cleanup fFallbackFontDict
SkTDict<FallbackFontList*>::Iter iter(fFallbackFontDict);
FallbackFontList* fallbackList;
while(iter.next(&fallbackList) != NULL) {
SkDELETE(fallbackList);
}
}
void SkFontConfigInterfaceAndroid::addFallbackFamily(FamilyRecID familyRecID) {
SkASSERT(familyRecID < fFontFamilies.count());
FamilyRec& familyRec = fFontFamilies[familyRecID];
SkASSERT(familyRec.fIsFallbackFont);
// add the fallback family to the name dictionary. This is
// needed by getFallbackFamilyNameForChar() so that fallback
// families can be identified by a unique name. The unique
// identifier that we've chosen is the familyID in hex (e.g. '0F##fallback').
familyRec.fFallbackName.printf("%.2x##fallback", familyRecID);
insert_into_name_dict(fFamilyNameDict, familyRec.fFallbackName.c_str(), familyRecID);
// add to the default fallback list
fDefaultFallbackList.push(familyRecID);
// stop here if it is the default language tag
const SkString& languageTag = familyRec.fPaintOptions.getLanguage().getTag();
if (languageTag.isEmpty()) {
return;
}
// add to the appropriate language's custom fallback list
FallbackFontList* customList = NULL;
if (!fFallbackFontDict.find(languageTag.c_str(), &customList)) {
DEBUG_FONT(("---- Created fallback list for \"%s\"", languageTag.c_str()));
customList = SkNEW(FallbackFontList);
fFallbackFontDict.set(languageTag.c_str(), customList);
}
SkASSERT(customList != NULL);
customList->push(familyRecID);
}
static FontRecID find_best_style(const FamilyRec& family, SkTypeface::Style style) {
const FontRecID* fontRecIDs = family.fFontRecID;
if (fontRecIDs[style] != INVALID_FONT_REC_ID) { // exact match
return fontRecIDs[style];
}
// look for a matching bold
style = (SkTypeface::Style)(style ^ SkTypeface::kItalic);
if (fontRecIDs[style] != INVALID_FONT_REC_ID) {
return fontRecIDs[style];
}
// look for the plain
if (fontRecIDs[SkTypeface::kNormal] != INVALID_FONT_REC_ID) {
return fontRecIDs[SkTypeface::kNormal];
}
// look for anything
for (int i = 0; i < FamilyRec::FONT_STYLE_COUNT; i++) {
if (fontRecIDs[i] != INVALID_FONT_REC_ID) {
return fontRecIDs[i];
}
}
// should never get here, since the fontRecID list should not be empty
SkDEBUGFAIL("No valid fonts exist for this family");
return -1;
}
bool SkFontConfigInterfaceAndroid::matchFamilyName(const char familyName[],
SkTypeface::Style style,
FontIdentity* outFontIdentifier,
SkString* outFamilyName,
SkTypeface::Style* outStyle) {
// clip to legal style bits
style = (SkTypeface::Style)(style & SkTypeface::kBoldItalic);
bool exactNameMatch = false;
FamilyRecID familyRecID = INVALID_FAMILY_REC_ID;
if (NULL != familyName) {
SkAutoAsciiToLC tolc(familyName);
if (fFamilyNameDict.find(tolc.lc(), &familyRecID)) {
exactNameMatch = true;
}
} else {
familyRecID = fDefaultFamilyRecID;
}
// If no matching family name is found then return false. This allows clients
// to be able to search for other fonts instead of forcing them to use the
// default font.
if (INVALID_FAMILY_REC_ID == familyRecID) {
return false;
}
FontRecID fontRecID = find_best_style(fFontFamilies[familyRecID], style);
FontRec& fontRec = fFonts[fontRecID];
if (NULL != outFontIdentifier) {
outFontIdentifier->fID = fontRecID;
outFontIdentifier->fTTCIndex = 0;
outFontIdentifier->fString.set(fontRec.fFileName);
// outFontIdentifier->fStyle = fontRec.fStyle;
}
if (NULL != outFamilyName) {
if (exactNameMatch) {
outFamilyName->set(familyName);
} else {
// find familyName from list of names
const char* familyName = NULL;
SkAssertResult(fFamilyNameDict.findKey(familyRecID, &familyName));
SkASSERT(familyName);
outFamilyName->set(familyName);
}
}
if (NULL != outStyle) {
*outStyle = fontRec.fStyle;
}
return true;
}
SkStream* SkFontConfigInterfaceAndroid::openStream(const FontIdentity& identity) {
return SkStream::NewFromFile(identity.fString.c_str());
}
SkDataTable* SkFontConfigInterfaceAndroid::getFamilyNames() {
SkTDArray<const char*> names;
SkTDArray<size_t> sizes;
SkTDict<FamilyRecID>::Iter iter(fFamilyNameDict);
const char* familyName = iter.next(NULL);
while(familyName != NULL) {
*names.append() = familyName;
*sizes.append() = strlen(familyName) + 1;
// move to the next familyName in the dictionary
familyName = iter.next(NULL);
}
return SkDataTable::NewCopyArrays((const void*const*)names.begin(),
sizes.begin(), names.count());
}
bool SkFontConfigInterfaceAndroid::matchFamilySet(const char inFamilyName[],
SkString* outFamilyName,
SkTArray<FontIdentity>*) {
return false;
}
static bool find_proc(SkTypeface* face, SkTypeface::Style style, void* ctx) {
const FontRecID* fontRecID = (const FontRecID*)ctx;
FontRecID currFontRecID = ((FontConfigTypeface*)face)->getIdentity().fID;
return currFontRecID == *fontRecID;
}
SkTypeface* SkFontConfigInterfaceAndroid::getTypefaceForFontRec(FontRecID fontRecID) {
FontRec& fontRec = fFonts[fontRecID];
SkTypeface* face = fontRec.fTypeface.get();
if (!face) {
// look for it in the typeface cache
face = SkTypefaceCache::FindByProcAndRef(find_proc, &fontRecID);
// if it is not in the cache then create it
if (!face) {
const char* familyName = NULL;
SkAssertResult(fFamilyNameDict.findKey(fontRec.fFamilyRecID, &familyName));
SkASSERT(familyName);
face = SkTypeface::CreateFromName(familyName, fontRec.fStyle);
}
// store the result for subsequent lookups
fontRec.fTypeface.reset(face);
}
SkASSERT(face);
return face;
}
bool SkFontConfigInterfaceAndroid::getFallbackFamilyNameForChar(SkUnichar uni,
const char* lang,
SkString* name) {
const SkString locale(lang);
if (NULL == fLocaleFallbackFontList || locale != fCachedLocale) {
fCachedLocale = locale;
fLocaleFallbackFontList = this->findFallbackFontList(locale);
}
FallbackFontList* fallbackFontList = fLocaleFallbackFontList;
for (int i = 0; i < fallbackFontList->count(); i++) {
FamilyRecID familyRecID = fallbackFontList->getAt(i);
// if it is not one of the accepted variants then move to the next family
int32_t acceptedVariants = SkPaintOptionsAndroid::kDefault_Variant |
SkPaintOptionsAndroid::kElegant_Variant;
if (!(fFontFamilies[familyRecID].fPaintOptions.getFontVariant() & acceptedVariants)) {
continue;
}
FontRecID fontRecID = find_best_style(fFontFamilies[familyRecID], SkTypeface::kNormal);
SkTypeface* face = this->getTypefaceForFontRec(fontRecID);
SkPaint paint;
paint.setTypeface(face);
paint.setTextEncoding(SkPaint::kUTF32_TextEncoding);
uint16_t glyphID;
paint.textToGlyphs(&uni, sizeof(uni), &glyphID);
if (glyphID != 0) {
name->set(fFontFamilies[familyRecID].fFallbackName);
return true;
}
}
return false;
}
FallbackFontList* SkFontConfigInterfaceAndroid::findFallbackFontList(const SkLanguage& lang,
bool isOriginal) {
const SkString& langTag = lang.getTag();
if (langTag.isEmpty()) {
return &fDefaultFallbackList;
}
FallbackFontList* fallbackFontList;
if (fFallbackFontDict.find(langTag.c_str(), langTag.size(), &fallbackFontList) ||
fFallbackFontAliasDict.find(langTag.c_str(), langTag.size(), &fallbackFontList)) {
return fallbackFontList;
}
// attempt a recursive fuzzy match
SkLanguage parent = lang.getParent();
fallbackFontList = findFallbackFontList(parent, false);
// cache the original lang so we don't have to do the recursion again.
if (isOriginal) {
DEBUG_FONT(("---- Created fallback list alias for \"%s\"", langTag.c_str()));
fFallbackFontAliasDict.set(langTag.c_str(), fallbackFontList);
}
return fallbackFontList;
}
///////////////////////////////////////////////////////////////////////////////
bool SkGetFallbackFamilyNameForChar(SkUnichar uni, const char* lang, SkString* name) {
SkFontConfigInterfaceAndroid* fontConfig = getSingletonInterface();
return fontConfig->getFallbackFamilyNameForChar(uni, lang, name);
}
void SkUseTestFontConfigFile(const char* mainconf, const char* fallbackconf,
const char* fontsdir) {
gTestMainConfigFile = mainconf;
gTestFallbackConfigFile = fallbackconf;
gTestFontFilePrefix = fontsdir;
SkASSERT(gTestMainConfigFile);
SkASSERT(gTestFallbackConfigFile);
SkASSERT(gTestFontFilePrefix);
SkDEBUGF(("Use Test Config File Main %s, Fallback %s, Font Dir %s",
gTestMainConfigFile, gTestFallbackConfigFile, gTestFontFilePrefix));
}
void SkGetTestFontConfiguration(const char** mainconf, const char** fallbackconf,
const char** fontsdir) {
*mainconf = gTestMainConfigFile;
*fallbackconf = gTestFallbackConfigFile;
*fontsdir = gTestFontFilePrefix;
}