/* * 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 "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 #include #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 { SkRefPtr fTypeface; SkString fFileName; SkTypeface::Style fStyle; SkPaintOptionsAndroid fPaintOptions; bool fIsFallbackFont; 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]; }; typedef SkTDArray FallbackFontList; class SkFontConfigInterfaceAndroid : public SkFontConfigInterface { public: SkFontConfigInterfaceAndroid(SkTDArray& 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*) 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, SkString* name); /** * */ SkTypeface* getTypefaceForChar(SkUnichar uni, SkTypeface::Style style, SkPaintOptionsAndroid::FontVariant fontVariant); SkTypeface* nextLogicalTypeface(SkFontID currFontID, SkFontID origFontID, const SkPaintOptionsAndroid& options); private: void addFallbackFont(FontRecID fontRecID); SkTypeface* getTypefaceForFontRec(FontRecID fontRecID); FallbackFontList* getCurrentLocaleFallbackFontList(); FallbackFontList* findFallbackFontList(const SkLanguage& lang, bool isOriginal = true); SkTArray fFonts; SkTArray fFontFamilies; SkTDict fFamilyNameDict; FamilyRecID fDefaultFamilyRecID; // (SkLanguage)<->(fallback chain index) translation SkTDict fFallbackFontDict; SkTDict fFallbackFontAliasDict; FallbackFontList fDefaultFallbackList; // fallback info for current locale SkString fCachedLocale; FallbackFontList* fLocaleFallbackFontList; }; /////////////////////////////////////////////////////////////////////////////// static SkFontConfigInterfaceAndroid* getSingletonInterface() { SK_DECLARE_STATIC_MUTEX(gMutex); static SkFontConfigInterfaceAndroid* gFontConfigInterface; SkAutoMutexAcquire ac(gMutex); if (NULL == gFontConfigInterface) { // load info from a configuration file that we can use to populate the // system/fallback font structures SkTDArray 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() { return getSingletonInterface(); } /////////////////////////////////////////////////////////////////////////////// static bool has_font(const SkTArray& 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 char 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& familyNameDict, const char* name, FamilyRecID familyRecID) { SkAutoAsciiToLC tolc(name); familyNameDict.set(tolc.lc(), familyRecID); } // Defined in SkFontHost_FreeType.cpp bool find_name_and_attributes(SkStream* stream, SkString* name, SkTypeface::Style* style, bool* isFixedWidth); /////////////////////////////////////////////////////////////////////////////// SkFontConfigInterfaceAndroid::SkFontConfigInterfaceAndroid(SkTDArray& 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.fPaintOptions = family->fFontFiles[j]->fPaintOptions; fontRec.fIsFallbackFont = family->fIsFallbackFont; fontRec.fIsValid = false; fontRec.fFamilyRecID = familyRecID; const FontRecID fontRecID = fFonts.count() - 1; SkAutoTUnref stream(SkStream::NewFromFile(filename.c_str())); if (stream.get() != NULL) { bool isFixedWidth; SkString name; fontRec.fIsValid = find_name_and_attributes(stream.get(), &name, &fontRec.fStyle, &isFixedWidth); } else { if (!fontRec.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, fontRec.fIsFallbackFont, filename.c_str())); } else { DEBUG_FONT(("---- SystemFonts[%d][%d] fallback=%d file=%s (INVALID)", i, fFonts.count() - 1, fontRec.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; } // 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 this is a fallback font then add it to the appropriate fallback chains if (fontRec.fIsFallbackFont) { addFallbackFont(fontRecID); } // add the fallback file name to the name dictionary. This is needed // by getFallbackFamilyNameForChar() so that fallback families can be // requested by the filenames of the fonts they contain. if (family->fIsFallbackFont && familyRec) { insert_into_name_dict(fFamilyNameDict, fontRec.fFileName.c_str(), familyRecID); } } // add the names that map to this family to the dictionary for easy lookup if (familyRec && !family->fIsFallbackFont) { SkTDArray names = family->fNames; if (names.isEmpty()) { SkDEBUGFAIL("ERROR: non-fallback font with no name"); continue; } for (int i = 0; i < names.count(); i++) { insert_into_name_dict(fFamilyNameDict, names[i], 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::Iter iter(fFallbackFontDict); const char* fallbackLang = iter.next(&fallbackList); while(fallbackLang != NULL) { for (int i = 0; i < fDefaultFallbackList.count(); i++) { FontRecID fontRecID = fDefaultFallbackList[i]; const SkString& fontLang = fFonts[fontRecID].fPaintOptions.getLanguage().getTag(); if (strcmp(fallbackLang, fontLang.c_str()) != 0) { fallbackList->push(fontRecID); } } // move to the next fallback list in the dictionary fallbackLang = iter.next(&fallbackList); } } SkFontConfigInterfaceAndroid::~SkFontConfigInterfaceAndroid() { // iterate through and cleanup fFallbackFontDict SkTDict::Iter iter(fFallbackFontDict); FallbackFontList* fallbackList; while(iter.next(&fallbackList) != NULL) { SkDELETE(fallbackList); } } void SkFontConfigInterfaceAndroid::addFallbackFont(FontRecID fontRecID) { SkASSERT(fontRecID < fFonts.count()); const FontRec& fontRec = fFonts[fontRecID]; SkASSERT(fontRec.fIsFallbackFont); // add to the default fallback list fDefaultFallbackList.push(fontRecID); // stop here if it is the default language tag const SkString& languageTag = fontRec.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(fontRecID); } 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 (INVALID_FAMILY_REC_ID == familyRecID) { //TODO this ensures that we always return something familyRecID = fDefaultFamilyRecID; //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 names; SkTDArray sizes; SkTDict::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*) { 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 = face; } SkASSERT(face); return face; } bool SkFontConfigInterfaceAndroid::getFallbackFamilyNameForChar(SkUnichar uni, SkString* name) { FallbackFontList* fallbackFontList = this->getCurrentLocaleFallbackFontList(); for (int i = 0; i < fallbackFontList->count(); i++) { FontRecID fontRecID = fallbackFontList->getAt(i); 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(fFonts[fontRecID].fFileName); 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); // 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 && currFontID == origFontID) { currFontRecID = ((FontConfigTypeface*)currTypeface)->getIdentity().fID; SkASSERT(INVALID_FONT_REC_ID != currFontRecID); } // lookup the index next font in the chain int currFallbackFontIndex = currentFallbackList->find(currFontRecID); // 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()) { FontRecID fontRecID = currentFallbackList->getAt(nextFallbackFontIndex); if ((fFonts[fontRecID].fPaintOptions.getFontVariant() & acceptedVariants) != 0) { nextLogicalTypeface = this->getTypefaceForFontRec(fontRecID); 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); } /////////////////////////////////////////////////////////////////////////////// bool SkGetFallbackFamilyNameForChar(SkUnichar uni, SkString* name) { SkFontConfigInterfaceAndroid* fontConfig = getSingletonInterface(); return fontConfig->getFallbackFamilyNameForChar(uni, 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); } /////////////////////////////////////////////////////////////////////////////// #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK struct HB_UnicodeMapping { // TODO: when the WebView no longer needs harfbuzz_old, remove HB_Script script_old; 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, HB_SCRIPT_ARMENIAN, 0x0531}, {HB_Script_Hebrew, HB_SCRIPT_HEBREW, 0x0591}, {HB_Script_Arabic, HB_SCRIPT_ARABIC, 0x0600}, {HB_Script_Syriac, HB_SCRIPT_SYRIAC, 0x0710}, {HB_Script_Thaana, HB_SCRIPT_THAANA, 0x0780}, {HB_Script_Nko, HB_SCRIPT_NKO, 0x07C0}, {HB_Script_Devanagari, HB_SCRIPT_DEVANAGARI, 0x0901}, {HB_Script_Bengali, HB_SCRIPT_BENGALI, 0x0981}, {HB_Script_Gurmukhi, HB_SCRIPT_GURMUKHI, 0x0A10}, {HB_Script_Gujarati, HB_SCRIPT_GUJARATI, 0x0A90}, {HB_Script_Oriya, HB_SCRIPT_ORIYA, 0x0B10}, {HB_Script_Tamil, HB_SCRIPT_TAMIL, 0x0B82}, {HB_Script_Telugu, HB_SCRIPT_TELUGU, 0x0C10}, {HB_Script_Kannada, HB_SCRIPT_KANNADA, 0x0C90}, {HB_Script_Malayalam, HB_SCRIPT_MALAYALAM, 0x0D10}, {HB_Script_Sinhala, HB_SCRIPT_SINHALA, 0x0D90}, {HB_Script_Thai, HB_SCRIPT_THAI, 0x0E01}, {HB_Script_Lao, HB_SCRIPT_LAO, 0x0E81}, {HB_Script_Tibetan, HB_SCRIPT_TIBETAN, 0x0F00}, {HB_Script_Myanmar, HB_SCRIPT_MYANMAR, 0x1000}, {HB_Script_Georgian, HB_SCRIPT_GEORGIAN, 0x10A0}, {HB_Script_Unknown, HB_SCRIPT_ETHIOPIC, 0x1200}, {HB_Script_Unknown, HB_SCRIPT_CHEROKEE, 0x13A0}, {HB_Script_Ogham, HB_SCRIPT_OGHAM, 0x1680}, {HB_Script_Runic, HB_SCRIPT_RUNIC, 0x16A0}, {HB_Script_Khmer, HB_SCRIPT_KHMER, 0x1780}, {HB_Script_Unknown, HB_SCRIPT_TAI_LE, 0x1950}, {HB_Script_Unknown, HB_SCRIPT_NEW_TAI_LUE, 0x1980}, {HB_Script_Unknown, HB_SCRIPT_TAI_THAM, 0x1A20}, {HB_Script_Unknown, HB_SCRIPT_CHAM, 0xAA00}, }; static hb_script_t getHBScriptFromHBScriptOld(HB_Script script_old) { hb_script_t script = HB_SCRIPT_INVALID; int numSupportedFonts = sizeof(HB_UnicodeMappingArray) / sizeof(HB_UnicodeMapping); for (int i = 0; i < numSupportedFonts; i++) { if (script_old == HB_UnicodeMappingArray[i].script_old) { script = HB_UnicodeMappingArray[i].script; break; } } return script; } // 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 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* SkCreateTypefaceForScriptNG(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( (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); } SkTypeface* SkCreateTypefaceForScript(HB_Script script, SkTypeface::Style style, SkPaintOptionsAndroid::FontVariant fontVariant) { return SkCreateTypefaceForScriptNG(getHBScriptFromHBScriptOld(script), style, fontVariant); } #endif /////////////////////////////////////////////////////////////////////////////// SkFontMgr* SkFontMgr::Factory() { return NULL; }