blob: 94662f5d5949844ec9a77fee69dcfd924c73f1ff [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);
/**
*
*/
SkTypeface* getTypefaceForChar(SkUnichar uni, SkTypeface::Style style,
SkPaintOptionsAndroid::FontVariant fontVariant);
SkTypeface* nextLogicalTypeface(SkFontID currFontID, SkFontID origFontID,
const SkPaintOptionsAndroid& options);
SkTypeface* getTypefaceForGlyphID(uint16_t glyphID, const SkTypeface* origTypeface,
const SkPaintOptionsAndroid& options,
int* lowerBounds, int* upperBounds);
private:
void addFallbackFamily(FamilyRecID fontRecID);
SkTypeface* getTypefaceForFontRec(FontRecID fontRecID);
FallbackFontList* getCurrentLocaleFallbackFontList();
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->fFontFiles.count(); ++j) {
SkString filename;
get_path_for_sys_fonts(&filename, family->fFontFiles[j].fFileName);
if (has_font(fFonts, filename)) {
SkDebugf("---- system font and fallback font files specify a duplicate "
"font %s, skipping the second occurrence", filename.c_str());
continue;
}
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->fFontFiles[j].fPaintOptions;
} else if (familyRec->fPaintOptions != family->fFontFiles[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) {
FallbackFontList* fallbackFontList = NULL;
const SkString langTag(lang);
if (langTag.isEmpty()) {
fallbackFontList = this->getCurrentLocaleFallbackFontList();
} else {
fallbackFontList = this->findFallbackFontList(langTag);
}
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;
}
SkTypeface* SkFontConfigInterfaceAndroid::getTypefaceForChar(SkUnichar uni,
SkTypeface::Style style,
SkPaintOptionsAndroid::FontVariant fontVariant) {
FontRecID fontRecID = find_best_style(fFontFamilies[fDefaultFamilyRecID], style);
SkTypeface* face = this->getTypefaceForFontRec(fontRecID);
SkPaintOptionsAndroid paintOptions;
paintOptions.setFontVariant(fontVariant);
paintOptions.setUseFontFallbacks(true);
SkPaint paint;
paint.setTypeface(face);
paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
paint.setPaintOptionsAndroid(paintOptions);
SkAutoGlyphCache autoCache(paint, NULL, NULL);
SkGlyphCache* cache = autoCache.getCache();
SkScalerContext* ctx = cache->getScalerContext();
if (ctx) {
SkFontID fontID = ctx->findTypefaceIdForChar(uni);
return SkTypefaceCache::FindByID(fontID);
}
return NULL;
}
FallbackFontList* SkFontConfigInterfaceAndroid::getCurrentLocaleFallbackFontList() {
SkString locale = SkFontConfigParser::GetLocale();
if (NULL == fLocaleFallbackFontList || locale != fCachedLocale) {
fCachedLocale = locale;
fLocaleFallbackFontList = this->findFallbackFontList(locale);
}
return fLocaleFallbackFontList;
}
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;
}
SkTypeface* SkFontConfigInterfaceAndroid::nextLogicalTypeface(SkFontID currFontID,
SkFontID origFontID,
const SkPaintOptionsAndroid& opts) {
// Skia does not support font fallback by default. This enables clients such
// as WebKit to customize their font selection. In any case, clients can use
// GetFallbackFamilyNameForChar() to get the fallback font for individual
// characters.
if (!opts.isUsingFontFallbacks()) {
return NULL;
}
FallbackFontList* currentFallbackList = findFallbackFontList(opts.getLanguage());
SkASSERT(currentFallbackList);
SkTypeface::Style origStyle = SkTypeface::kNormal;
const SkTypeface* origTypeface = SkTypefaceCache::FindByID(origFontID);
if (NULL != origTypeface) {
origStyle = origTypeface->style();
}
// we must convert currTypeface into a FontRecID
FontRecID currFontRecID = INVALID_FONT_REC_ID;
const SkTypeface* currTypeface = SkTypefaceCache::FindByID(currFontID);
// non-system fonts are not in the font cache so if we are asked to fallback
// for a non-system font we will start at the front of the chain.
if (NULL != currTypeface) {
currFontRecID = ((FontConfigTypeface*)currTypeface)->getIdentity().fID;
SkASSERT(INVALID_FONT_REC_ID != currFontRecID);
}
FamilyRecID currFamilyRecID = INVALID_FAMILY_REC_ID;
if (INVALID_FONT_REC_ID != currFontRecID) {
currFamilyRecID = fFonts[currFontRecID].fFamilyRecID;
}
// lookup the index next font in the chain
int currFallbackFontIndex = currentFallbackList->find(currFamilyRecID);
// We add 1 to the returned index for 2 reasons: (1) if find succeeds it moves
// our index to the next entry in the list; (2) if find() fails it returns
// -1 and incrementing it will set our starting index to 0 (the head of the list)
int nextFallbackFontIndex = currFallbackFontIndex + 1;
if(nextFallbackFontIndex >= currentFallbackList->count()) {
return NULL;
}
// If a rec object is set to prefer "kDefault_Variant" it means they have no preference
// In this case, we set the value to "kCompact_Variant"
SkPaintOptionsAndroid::FontVariant variant = opts.getFontVariant();
if (variant == SkPaintOptionsAndroid::kDefault_Variant) {
variant = SkPaintOptionsAndroid::kCompact_Variant;
}
int32_t acceptedVariants = SkPaintOptionsAndroid::kDefault_Variant | variant;
SkTypeface* nextLogicalTypeface = 0;
while (nextFallbackFontIndex < currentFallbackList->count()) {
FamilyRecID familyRecID = currentFallbackList->getAt(nextFallbackFontIndex);
if ((fFontFamilies[familyRecID].fPaintOptions.getFontVariant() & acceptedVariants) != 0) {
FontRecID matchedFont = find_best_style(fFontFamilies[familyRecID], origStyle);
nextLogicalTypeface = this->getTypefaceForFontRec(matchedFont);
break;
}
nextFallbackFontIndex++;
}
DEBUG_FONT(("---- nextLogicalFont: currFontID=%d, origFontID=%d, currRecID=%d, "
"lang=%s, variant=%d, nextFallbackIndex[%d,%d] => nextLogicalTypeface=%d",
currFontID, origFontID, currFontRecID, opts.getLanguage().getTag().c_str(),
variant, nextFallbackFontIndex, currentFallbackList->getAt(nextFallbackFontIndex),
(nextLogicalTypeface) ? nextLogicalTypeface->uniqueID() : 0));
return SkSafeRef(nextLogicalTypeface);
}
SkTypeface* SkFontConfigInterfaceAndroid::getTypefaceForGlyphID(uint16_t glyphID,
const SkTypeface* origTypeface,
const SkPaintOptionsAndroid& opts,
int* lBounds, int* uBounds) {
// If we aren't using fallbacks then we shouldn't be calling this
SkASSERT(opts.isUsingFontFallbacks());
SkASSERT(origTypeface);
SkTypeface* currentTypeface = NULL;
int lowerBounds = 0; //inclusive
int upperBounds = origTypeface->countGlyphs(); //exclusive
// check to see if the glyph is in the bounds of the origTypeface
if (glyphID < upperBounds) {
currentTypeface = const_cast<SkTypeface*>(origTypeface);
} else {
FallbackFontList* currentFallbackList = findFallbackFontList(opts.getLanguage());
SkASSERT(currentFallbackList);
// If an object is set to prefer "kDefault_Variant" it means they have no preference
// In this case, we set the value to "kCompact_Variant"
SkPaintOptionsAndroid::FontVariant variant = opts.getFontVariant();
if (variant == SkPaintOptionsAndroid::kDefault_Variant) {
variant = SkPaintOptionsAndroid::kCompact_Variant;
}
int32_t acceptedVariants = SkPaintOptionsAndroid::kDefault_Variant | variant;
SkTypeface::Style origStyle = origTypeface->style();
for (int x = 0; x < currentFallbackList->count(); ++x) {
const FamilyRecID familyRecID = currentFallbackList->getAt(x);
const SkPaintOptionsAndroid& familyOptions = fFontFamilies[familyRecID].fPaintOptions;
if ((familyOptions.getFontVariant() & acceptedVariants) != 0) {
FontRecID matchedFont = find_best_style(fFontFamilies[familyRecID], origStyle);
currentTypeface = this->getTypefaceForFontRec(matchedFont);
lowerBounds = upperBounds;
upperBounds += currentTypeface->countGlyphs();
if (glyphID < upperBounds) {
break;
}
}
}
}
if (NULL != currentTypeface) {
if (lBounds) {
*lBounds = lowerBounds;
}
if (uBounds) {
*uBounds = upperBounds;
}
}
return currentTypeface;
}
///////////////////////////////////////////////////////////////////////////////
bool SkGetFallbackFamilyNameForChar(SkUnichar uni, SkString* name) {
SkFontConfigInterfaceAndroid* fontConfig = getSingletonInterface();
return fontConfig->getFallbackFamilyNameForChar(uni, NULL, name);
}
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));
}
SkTypeface* SkAndroidNextLogicalTypeface(SkFontID currFontID, SkFontID origFontID,
const SkPaintOptionsAndroid& options) {
SkFontConfigInterfaceAndroid* fontConfig = getSingletonInterface();
return fontConfig->nextLogicalTypeface(currFontID, origFontID, options);
}
SkTypeface* SkGetTypefaceForGlyphID(uint16_t glyphID, const SkTypeface* origTypeface,
const SkPaintOptionsAndroid& options,
int* lowerBounds, int* upperBounds) {
SkFontConfigInterfaceAndroid* fontConfig = getSingletonInterface();
return fontConfig->getTypefaceForGlyphID(glyphID, origTypeface, options,
lowerBounds, upperBounds);
}
///////////////////////////////////////////////////////////////////////////////
#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
struct HB_UnicodeMapping {
hb_script_t script;
const SkUnichar unicode;
};
/*
* The following scripts are not complex fonts and we do not expect them to be parsed by this table
* HB_SCRIPT_COMMON,
* HB_SCRIPT_GREEK,
* HB_SCRIPT_CYRILLIC,
* HB_SCRIPT_HANGUL
* HB_SCRIPT_INHERITED
*/
/* Harfbuzz (old) is missing a number of scripts in its table. For these,
* we include a value which can never happen. We won't get complex script
* shaping in these cases, but the library wouldn't know how to shape
* them anyway. */
#define HB_Script_Unknown HB_ScriptCount
static HB_UnicodeMapping HB_UnicodeMappingArray[] = {
{HB_SCRIPT_ARMENIAN, 0x0531},
{HB_SCRIPT_HEBREW, 0x0591},
{HB_SCRIPT_ARABIC, 0x0600},
{HB_SCRIPT_SYRIAC, 0x0710},
{HB_SCRIPT_THAANA, 0x0780},
{HB_SCRIPT_NKO, 0x07C0},
{HB_SCRIPT_DEVANAGARI, 0x0901},
{HB_SCRIPT_BENGALI, 0x0981},
{HB_SCRIPT_GURMUKHI, 0x0A10},
{HB_SCRIPT_GUJARATI, 0x0A90},
{HB_SCRIPT_ORIYA, 0x0B10},
{HB_SCRIPT_TAMIL, 0x0B82},
{HB_SCRIPT_TELUGU, 0x0C10},
{HB_SCRIPT_KANNADA, 0x0C90},
{HB_SCRIPT_MALAYALAM, 0x0D10},
{HB_SCRIPT_SINHALA, 0x0D90},
{HB_SCRIPT_THAI, 0x0E01},
{HB_SCRIPT_LAO, 0x0E81},
{HB_SCRIPT_TIBETAN, 0x0F00},
{HB_SCRIPT_MYANMAR, 0x1000},
{HB_SCRIPT_GEORGIAN, 0x10A0},
{HB_SCRIPT_ETHIOPIC, 0x1200},
{HB_SCRIPT_CHEROKEE, 0x13A0},
{HB_SCRIPT_OGHAM, 0x1680},
{HB_SCRIPT_RUNIC, 0x16A0},
{HB_SCRIPT_KHMER, 0x1780},
{HB_SCRIPT_TAI_LE, 0x1950},
{HB_SCRIPT_NEW_TAI_LUE, 0x1980},
{HB_SCRIPT_TAI_THAM, 0x1A20},
{HB_SCRIPT_CHAM, 0xAA00},
};
// returns 0 for "Not Found"
static SkUnichar getUnicodeFromHBScript(hb_script_t script) {
SkUnichar unichar = 0;
int numSupportedFonts = sizeof(HB_UnicodeMappingArray) / sizeof(HB_UnicodeMapping);
for (int i = 0; i < numSupportedFonts; i++) {
if (script == HB_UnicodeMappingArray[i].script) {
unichar = HB_UnicodeMappingArray[i].unicode;
break;
}
}
return unichar;
}
struct TypefaceLookupStruct {
hb_script_t script;
SkTypeface::Style style;
SkPaintOptionsAndroid::FontVariant fontVariant;
SkTypeface* typeface;
};
SK_DECLARE_STATIC_MUTEX(gTypefaceTableMutex); // This is the mutex for gTypefaceTable
static SkTDArray<TypefaceLookupStruct> gTypefaceTable; // This is protected by gTypefaceTableMutex
static int typefaceLookupCompare(const TypefaceLookupStruct& first,
const TypefaceLookupStruct& second) {
if (first.script != second.script) {
return (first.script > second.script) ? 1 : -1;
}
if (first.style != second.style) {
return (first.style > second.style) ? 1 : -1;
}
if (first.fontVariant != second.fontVariant) {
return (first.fontVariant > second.fontVariant) ? 1 : -1;
}
return 0;
}
SkTypeface* SkCreateTypefaceForScript(hb_script_t script, SkTypeface::Style style,
SkPaintOptionsAndroid::FontVariant fontVariant) {
SkAutoMutexAcquire ac(gTypefaceTableMutex);
TypefaceLookupStruct key;
key.script = script;
key.style = style;
key.fontVariant = fontVariant;
int index = SkTSearch<TypefaceLookupStruct>(
(const TypefaceLookupStruct*) gTypefaceTable.begin(),
gTypefaceTable.count(), key, sizeof(TypefaceLookupStruct),
typefaceLookupCompare);
SkTypeface* retTypeface = NULL;
if (index >= 0) {
retTypeface = gTypefaceTable[index].typeface;
}
else {
SkUnichar unichar = getUnicodeFromHBScript(script);
if (!unichar) {
return NULL;
}
SkFontConfigInterfaceAndroid* fontConfig = getSingletonInterface();
retTypeface = fontConfig->getTypefaceForChar(unichar, style, fontVariant);
// add to the lookup table
key.typeface = retTypeface;
*gTypefaceTable.insert(~index) = key;
}
// we ref(), the caller is expected to unref when they are done
return SkSafeRef(retTypeface);
}
#endif