michael@0: michael@0: /* michael@0: * Copyright 2013 The Android Open Source Project michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: #include "SkFontConfigInterface.h" michael@0: #include "SkTypeface_android.h" michael@0: michael@0: #include "SkFontConfigParser_android.h" michael@0: #include "SkFontConfigTypeface.h" michael@0: #include "SkFontMgr.h" michael@0: #include "SkGlyphCache.h" michael@0: #include "SkPaint.h" michael@0: #include "SkString.h" michael@0: #include "SkStream.h" michael@0: #include "SkThread.h" michael@0: #include "SkTypefaceCache.h" michael@0: #include "SkTArray.h" michael@0: #include "SkTDict.h" michael@0: #include "SkTSearch.h" michael@0: michael@0: #include michael@0: #include michael@0: michael@0: #ifndef SK_DEBUG_FONTS michael@0: #define SK_DEBUG_FONTS 0 michael@0: #endif michael@0: michael@0: #if SK_DEBUG_FONTS michael@0: #define DEBUG_FONT(args) SkDebugf args michael@0: #else michael@0: #define DEBUG_FONT(args) michael@0: #endif michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: // For test only. michael@0: static const char* gTestMainConfigFile = NULL; michael@0: static const char* gTestFallbackConfigFile = NULL; michael@0: static const char* gTestFontFilePrefix = NULL; michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: typedef int32_t FontRecID; michael@0: #define INVALID_FONT_REC_ID -1 michael@0: michael@0: typedef int32_t FamilyRecID; michael@0: #define INVALID_FAMILY_REC_ID -1 michael@0: michael@0: // used to record our notion of the pre-existing fonts michael@0: struct FontRec { michael@0: SkRefPtr fTypeface; michael@0: SkString fFileName; michael@0: SkTypeface::Style fStyle; michael@0: bool fIsValid; michael@0: FamilyRecID fFamilyRecID; michael@0: }; michael@0: michael@0: struct FamilyRec { michael@0: FamilyRec() { michael@0: memset(fFontRecID, INVALID_FONT_REC_ID, sizeof(fFontRecID)); michael@0: } michael@0: michael@0: static const int FONT_STYLE_COUNT = 4; michael@0: FontRecID fFontRecID[FONT_STYLE_COUNT]; michael@0: bool fIsFallbackFont; michael@0: SkString fFallbackName; michael@0: SkPaintOptionsAndroid fPaintOptions; michael@0: }; michael@0: michael@0: michael@0: typedef SkTDArray FallbackFontList; michael@0: michael@0: class SkFontConfigInterfaceAndroid : public SkFontConfigInterface { michael@0: public: michael@0: SkFontConfigInterfaceAndroid(SkTDArray& fontFamilies); michael@0: virtual ~SkFontConfigInterfaceAndroid(); michael@0: michael@0: virtual bool matchFamilyName(const char familyName[], michael@0: SkTypeface::Style requested, michael@0: FontIdentity* outFontIdentifier, michael@0: SkString* outFamilyName, michael@0: SkTypeface::Style* outStyle) SK_OVERRIDE; michael@0: virtual SkStream* openStream(const FontIdentity&) SK_OVERRIDE; michael@0: michael@0: // new APIs michael@0: virtual SkDataTable* getFamilyNames() SK_OVERRIDE; michael@0: virtual bool matchFamilySet(const char inFamilyName[], michael@0: SkString* outFamilyName, michael@0: SkTArray*) SK_OVERRIDE; michael@0: michael@0: /** michael@0: * Get the family name of the font in the default fallback font list that michael@0: * contains the specified chararacter. if no font is found, returns false. michael@0: */ michael@0: bool getFallbackFamilyNameForChar(SkUnichar uni, const char* lang, SkString* name); michael@0: /** michael@0: * michael@0: */ michael@0: SkTypeface* getTypefaceForChar(SkUnichar uni, SkTypeface::Style style, michael@0: SkPaintOptionsAndroid::FontVariant fontVariant); michael@0: SkTypeface* nextLogicalTypeface(SkFontID currFontID, SkFontID origFontID, michael@0: const SkPaintOptionsAndroid& options); michael@0: SkTypeface* getTypefaceForGlyphID(uint16_t glyphID, const SkTypeface* origTypeface, michael@0: const SkPaintOptionsAndroid& options, michael@0: int* lowerBounds, int* upperBounds); michael@0: michael@0: private: michael@0: void addFallbackFamily(FamilyRecID fontRecID); michael@0: SkTypeface* getTypefaceForFontRec(FontRecID fontRecID); michael@0: FallbackFontList* getCurrentLocaleFallbackFontList(); michael@0: FallbackFontList* findFallbackFontList(const SkLanguage& lang, bool isOriginal = true); michael@0: michael@0: SkTArray fFonts; michael@0: SkTArray fFontFamilies; michael@0: SkTDict fFamilyNameDict; michael@0: FamilyRecID fDefaultFamilyRecID; michael@0: michael@0: // (SkLanguage)<->(fallback chain index) translation michael@0: SkTDict fFallbackFontDict; michael@0: SkTDict fFallbackFontAliasDict; michael@0: FallbackFontList fDefaultFallbackList; michael@0: michael@0: // fallback info for current locale michael@0: SkString fCachedLocale; michael@0: FallbackFontList* fLocaleFallbackFontList; michael@0: }; michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: static SkFontConfigInterfaceAndroid* getSingletonInterface() { michael@0: SK_DECLARE_STATIC_MUTEX(gMutex); michael@0: static SkFontConfigInterfaceAndroid* gFontConfigInterface; michael@0: michael@0: SkAutoMutexAcquire ac(gMutex); michael@0: if (NULL == gFontConfigInterface) { michael@0: // load info from a configuration file that we can use to populate the michael@0: // system/fallback font structures michael@0: SkTDArray fontFamilies; michael@0: if (!gTestMainConfigFile) { michael@0: SkFontConfigParser::GetFontFamilies(fontFamilies); michael@0: } else { michael@0: SkFontConfigParser::GetTestFontFamilies(fontFamilies, gTestMainConfigFile, michael@0: gTestFallbackConfigFile); michael@0: } michael@0: michael@0: gFontConfigInterface = new SkFontConfigInterfaceAndroid(fontFamilies); michael@0: michael@0: // cleanup the data we received from the parser michael@0: fontFamilies.deleteAll(); michael@0: } michael@0: return gFontConfigInterface; michael@0: } michael@0: michael@0: SkFontConfigInterface* SkFontConfigInterface::GetSingletonDirectInterface() { michael@0: return getSingletonInterface(); michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: static bool has_font(const SkTArray& array, const SkString& filename) { michael@0: for (int i = 0; i < array.count(); i++) { michael@0: if (array[i].fFileName == filename) { michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: #ifndef SK_FONT_FILE_PREFIX michael@0: #define SK_FONT_FILE_PREFIX "/fonts/" michael@0: #endif michael@0: michael@0: static void get_path_for_sys_fonts(SkString* full, const char name[]) { michael@0: if (gTestFontFilePrefix) { michael@0: full->set(gTestFontFilePrefix); michael@0: } else { michael@0: full->set(getenv("ANDROID_ROOT")); michael@0: full->append(SK_FONT_FILE_PREFIX); michael@0: } michael@0: full->append(name); michael@0: } michael@0: michael@0: static void insert_into_name_dict(SkTDict& familyNameDict, michael@0: const char* name, FamilyRecID familyRecID) { michael@0: SkAutoAsciiToLC tolc(name); michael@0: if (familyNameDict.find(tolc.lc())) { michael@0: SkDebugf("---- system font attempting to use a the same name [%s] for" michael@0: "multiple families. skipping subsequent occurrences", tolc.lc()); michael@0: } else { michael@0: familyNameDict.set(tolc.lc(), familyRecID); michael@0: } michael@0: } michael@0: michael@0: // Defined in SkFontHost_FreeType.cpp michael@0: bool find_name_and_attributes(SkStream* stream, SkString* name, michael@0: SkTypeface::Style* style, bool* isFixedWidth); michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: SkFontConfigInterfaceAndroid::SkFontConfigInterfaceAndroid(SkTDArray& fontFamilies) : michael@0: fFonts(fontFamilies.count()), michael@0: fFontFamilies(fontFamilies.count() / FamilyRec::FONT_STYLE_COUNT), michael@0: fFamilyNameDict(1024), michael@0: fDefaultFamilyRecID(INVALID_FAMILY_REC_ID), michael@0: fFallbackFontDict(128), michael@0: fFallbackFontAliasDict(128), michael@0: fLocaleFallbackFontList(NULL) { michael@0: michael@0: for (int i = 0; i < fontFamilies.count(); ++i) { michael@0: FontFamily* family = fontFamilies[i]; michael@0: michael@0: // defer initializing the familyRec until we can be sure that at least michael@0: // one of it's children contains a valid font file michael@0: FamilyRec* familyRec = NULL; michael@0: FamilyRecID familyRecID = INVALID_FAMILY_REC_ID; michael@0: michael@0: for (int j = 0; j < family->fFontFiles.count(); ++j) { michael@0: SkString filename; michael@0: get_path_for_sys_fonts(&filename, family->fFontFiles[j]->fFileName); michael@0: michael@0: if (has_font(fFonts, filename)) { michael@0: SkDebugf("---- system font and fallback font files specify a duplicate " michael@0: "font %s, skipping the second occurrence", filename.c_str()); michael@0: continue; michael@0: } michael@0: michael@0: FontRec& fontRec = fFonts.push_back(); michael@0: fontRec.fFileName = filename; michael@0: fontRec.fStyle = SkTypeface::kNormal; michael@0: fontRec.fIsValid = false; michael@0: fontRec.fFamilyRecID = familyRecID; michael@0: michael@0: const FontRecID fontRecID = fFonts.count() - 1; michael@0: michael@0: SkAutoTUnref stream(SkStream::NewFromFile(filename.c_str())); michael@0: if (stream.get() != NULL) { michael@0: bool isFixedWidth; michael@0: SkString name; michael@0: fontRec.fIsValid = find_name_and_attributes(stream.get(), &name, michael@0: &fontRec.fStyle, &isFixedWidth); michael@0: } else { michael@0: if (!family->fIsFallbackFont) { michael@0: SkDebugf("---- failed to open <%s> as a font\n", filename.c_str()); michael@0: } michael@0: } michael@0: michael@0: if (fontRec.fIsValid) { michael@0: DEBUG_FONT(("---- SystemFonts[%d][%d] fallback=%d file=%s", michael@0: i, fFonts.count() - 1, family->fIsFallbackFont, filename.c_str())); michael@0: } else { michael@0: DEBUG_FONT(("---- SystemFonts[%d][%d] fallback=%d file=%s (INVALID)", michael@0: i, fFonts.count() - 1, family->fIsFallbackFont, filename.c_str())); michael@0: continue; michael@0: } michael@0: michael@0: // create a familyRec now that we know that at least one font in michael@0: // the family is valid michael@0: if (familyRec == NULL) { michael@0: familyRec = &fFontFamilies.push_back(); michael@0: familyRecID = fFontFamilies.count() - 1; michael@0: fontRec.fFamilyRecID = familyRecID; michael@0: michael@0: familyRec->fIsFallbackFont = family->fIsFallbackFont; michael@0: familyRec->fPaintOptions = family->fFontFiles[j]->fPaintOptions; michael@0: michael@0: } else if (familyRec->fPaintOptions != family->fFontFiles[j]->fPaintOptions) { michael@0: SkDebugf("Every font file within a family must have identical" michael@0: "language and variant attributes"); michael@0: sk_throw(); michael@0: } michael@0: michael@0: // add this font to the current familyRec michael@0: if (INVALID_FONT_REC_ID != familyRec->fFontRecID[fontRec.fStyle]) { michael@0: DEBUG_FONT(("Overwriting familyRec for style[%d] old,new:(%d,%d)", michael@0: fontRec.fStyle, familyRec->fFontRecID[fontRec.fStyle], michael@0: fontRecID)); michael@0: } michael@0: familyRec->fFontRecID[fontRec.fStyle] = fontRecID; michael@0: } michael@0: michael@0: if (familyRec) { michael@0: if (familyRec->fIsFallbackFont) { michael@0: // add the font to the appropriate fallback chains and also insert a michael@0: // unique name into the familyNameDict for internal usage michael@0: addFallbackFamily(familyRecID); michael@0: } else { michael@0: // add the names that map to this family to the dictionary for easy lookup michael@0: const SkTDArray& names = family->fNames; michael@0: if (names.isEmpty()) { michael@0: SkDEBUGFAIL("ERROR: non-fallback font with no name"); michael@0: continue; michael@0: } michael@0: michael@0: for (int i = 0; i < names.count(); i++) { michael@0: insert_into_name_dict(fFamilyNameDict, names[i], familyRecID); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: DEBUG_FONT(("---- We have %d system fonts", fFonts.count())); michael@0: michael@0: if (fFontFamilies.count() > 0) { michael@0: fDefaultFamilyRecID = 0; michael@0: } michael@0: michael@0: // scans the default fallback font chain, adding every entry to every other michael@0: // fallback font chain to which it does not belong. this results in every michael@0: // language-specific fallback font chain having all of its fallback fonts at michael@0: // the front of the chain, and everything else at the end. michael@0: FallbackFontList* fallbackList; michael@0: SkTDict::Iter iter(fFallbackFontDict); michael@0: const char* fallbackLang = iter.next(&fallbackList); michael@0: while(fallbackLang != NULL) { michael@0: for (int i = 0; i < fDefaultFallbackList.count(); i++) { michael@0: FamilyRecID familyRecID = fDefaultFallbackList[i]; michael@0: const SkString& fontLang = fFontFamilies[familyRecID].fPaintOptions.getLanguage().getTag(); michael@0: if (strcmp(fallbackLang, fontLang.c_str()) != 0) { michael@0: fallbackList->push(familyRecID); michael@0: } michael@0: } michael@0: // move to the next fallback list in the dictionary michael@0: fallbackLang = iter.next(&fallbackList); michael@0: } michael@0: } michael@0: michael@0: SkFontConfigInterfaceAndroid::~SkFontConfigInterfaceAndroid() { michael@0: // iterate through and cleanup fFallbackFontDict michael@0: SkTDict::Iter iter(fFallbackFontDict); michael@0: FallbackFontList* fallbackList; michael@0: while(iter.next(&fallbackList) != NULL) { michael@0: SkDELETE(fallbackList); michael@0: } michael@0: } michael@0: michael@0: void SkFontConfigInterfaceAndroid::addFallbackFamily(FamilyRecID familyRecID) { michael@0: SkASSERT(familyRecID < fFontFamilies.count()); michael@0: FamilyRec& familyRec = fFontFamilies[familyRecID]; michael@0: SkASSERT(familyRec.fIsFallbackFont); michael@0: michael@0: // add the fallback family to the name dictionary. This is michael@0: // needed by getFallbackFamilyNameForChar() so that fallback michael@0: // families can be identified by a unique name. The unique michael@0: // identifier that we've chosen is the familyID in hex (e.g. '0F##fallback'). michael@0: familyRec.fFallbackName.printf("%.2x##fallback", familyRecID); michael@0: insert_into_name_dict(fFamilyNameDict, familyRec.fFallbackName.c_str(), familyRecID); michael@0: michael@0: // add to the default fallback list michael@0: fDefaultFallbackList.push(familyRecID); michael@0: michael@0: // stop here if it is the default language tag michael@0: const SkString& languageTag = familyRec.fPaintOptions.getLanguage().getTag(); michael@0: if (languageTag.isEmpty()) { michael@0: return; michael@0: } michael@0: michael@0: // add to the appropriate language's custom fallback list michael@0: FallbackFontList* customList = NULL; michael@0: if (!fFallbackFontDict.find(languageTag.c_str(), &customList)) { michael@0: DEBUG_FONT(("---- Created fallback list for \"%s\"", languageTag.c_str())); michael@0: customList = SkNEW(FallbackFontList); michael@0: fFallbackFontDict.set(languageTag.c_str(), customList); michael@0: } michael@0: SkASSERT(customList != NULL); michael@0: customList->push(familyRecID); michael@0: } michael@0: michael@0: michael@0: static FontRecID find_best_style(const FamilyRec& family, SkTypeface::Style style) { michael@0: michael@0: const FontRecID* fontRecIDs = family.fFontRecID; michael@0: michael@0: if (fontRecIDs[style] != INVALID_FONT_REC_ID) { // exact match michael@0: return fontRecIDs[style]; michael@0: } michael@0: // look for a matching bold michael@0: style = (SkTypeface::Style)(style ^ SkTypeface::kItalic); michael@0: if (fontRecIDs[style] != INVALID_FONT_REC_ID) { michael@0: return fontRecIDs[style]; michael@0: } michael@0: // look for the plain michael@0: if (fontRecIDs[SkTypeface::kNormal] != INVALID_FONT_REC_ID) { michael@0: return fontRecIDs[SkTypeface::kNormal]; michael@0: } michael@0: // look for anything michael@0: for (int i = 0; i < FamilyRec::FONT_STYLE_COUNT; i++) { michael@0: if (fontRecIDs[i] != INVALID_FONT_REC_ID) { michael@0: return fontRecIDs[i]; michael@0: } michael@0: } michael@0: // should never get here, since the fontRecID list should not be empty michael@0: SkDEBUGFAIL("No valid fonts exist for this family"); michael@0: return -1; michael@0: } michael@0: michael@0: bool SkFontConfigInterfaceAndroid::matchFamilyName(const char familyName[], michael@0: SkTypeface::Style style, michael@0: FontIdentity* outFontIdentifier, michael@0: SkString* outFamilyName, michael@0: SkTypeface::Style* outStyle) { michael@0: // clip to legal style bits michael@0: style = (SkTypeface::Style)(style & SkTypeface::kBoldItalic); michael@0: michael@0: bool exactNameMatch = false; michael@0: michael@0: FamilyRecID familyRecID = INVALID_FAMILY_REC_ID; michael@0: if (NULL != familyName) { michael@0: SkAutoAsciiToLC tolc(familyName); michael@0: if (fFamilyNameDict.find(tolc.lc(), &familyRecID)) { michael@0: exactNameMatch = true; michael@0: } michael@0: } else { michael@0: familyRecID = fDefaultFamilyRecID; michael@0: michael@0: } michael@0: michael@0: // If no matching family name is found then return false. This allows clients michael@0: // to be able to search for other fonts instead of forcing them to use the michael@0: // default font. michael@0: if (INVALID_FAMILY_REC_ID == familyRecID) { michael@0: return false; michael@0: } michael@0: michael@0: FontRecID fontRecID = find_best_style(fFontFamilies[familyRecID], style); michael@0: FontRec& fontRec = fFonts[fontRecID]; michael@0: michael@0: if (NULL != outFontIdentifier) { michael@0: outFontIdentifier->fID = fontRecID; michael@0: outFontIdentifier->fTTCIndex = 0; michael@0: outFontIdentifier->fString.set(fontRec.fFileName); michael@0: // outFontIdentifier->fStyle = fontRec.fStyle; michael@0: } michael@0: michael@0: if (NULL != outFamilyName) { michael@0: if (exactNameMatch) { michael@0: outFamilyName->set(familyName); michael@0: } else { michael@0: // find familyName from list of names michael@0: const char* familyName = NULL; michael@0: SkAssertResult(fFamilyNameDict.findKey(familyRecID, &familyName)); michael@0: SkASSERT(familyName); michael@0: outFamilyName->set(familyName); michael@0: } michael@0: } michael@0: michael@0: if (NULL != outStyle) { michael@0: *outStyle = fontRec.fStyle; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: SkStream* SkFontConfigInterfaceAndroid::openStream(const FontIdentity& identity) { michael@0: return SkStream::NewFromFile(identity.fString.c_str()); michael@0: } michael@0: michael@0: SkDataTable* SkFontConfigInterfaceAndroid::getFamilyNames() { michael@0: SkTDArray names; michael@0: SkTDArray sizes; michael@0: michael@0: SkTDict::Iter iter(fFamilyNameDict); michael@0: const char* familyName = iter.next(NULL); michael@0: while(familyName != NULL) { michael@0: *names.append() = familyName; michael@0: *sizes.append() = strlen(familyName) + 1; michael@0: michael@0: // move to the next familyName in the dictionary michael@0: familyName = iter.next(NULL); michael@0: } michael@0: michael@0: return SkDataTable::NewCopyArrays((const void*const*)names.begin(), michael@0: sizes.begin(), names.count()); michael@0: } michael@0: michael@0: bool SkFontConfigInterfaceAndroid::matchFamilySet(const char inFamilyName[], michael@0: SkString* outFamilyName, michael@0: SkTArray*) { michael@0: return false; michael@0: } michael@0: michael@0: static bool find_proc(SkTypeface* face, SkTypeface::Style style, void* ctx) { michael@0: const FontRecID* fontRecID = (const FontRecID*)ctx; michael@0: FontRecID currFontRecID = ((FontConfigTypeface*)face)->getIdentity().fID; michael@0: return currFontRecID == *fontRecID; michael@0: } michael@0: michael@0: SkTypeface* SkFontConfigInterfaceAndroid::getTypefaceForFontRec(FontRecID fontRecID) { michael@0: FontRec& fontRec = fFonts[fontRecID]; michael@0: SkTypeface* face = fontRec.fTypeface.get(); michael@0: if (!face) { michael@0: // look for it in the typeface cache michael@0: face = SkTypefaceCache::FindByProcAndRef(find_proc, &fontRecID); michael@0: michael@0: // if it is not in the cache then create it michael@0: if (!face) { michael@0: const char* familyName = NULL; michael@0: SkAssertResult(fFamilyNameDict.findKey(fontRec.fFamilyRecID, &familyName)); michael@0: SkASSERT(familyName); michael@0: face = SkTypeface::CreateFromName(familyName, fontRec.fStyle); michael@0: } michael@0: michael@0: // store the result for subsequent lookups michael@0: fontRec.fTypeface = face; michael@0: } michael@0: SkASSERT(face); michael@0: return face; michael@0: } michael@0: michael@0: bool SkFontConfigInterfaceAndroid::getFallbackFamilyNameForChar(SkUnichar uni, michael@0: const char* lang, michael@0: SkString* name) { michael@0: FallbackFontList* fallbackFontList = NULL; michael@0: const SkString langTag(lang); michael@0: if (langTag.isEmpty()) { michael@0: fallbackFontList = this->getCurrentLocaleFallbackFontList(); michael@0: } else { michael@0: fallbackFontList = this->findFallbackFontList(langTag); michael@0: } michael@0: michael@0: for (int i = 0; i < fallbackFontList->count(); i++) { michael@0: FamilyRecID familyRecID = fallbackFontList->getAt(i); michael@0: michael@0: // if it is not one of the accepted variants then move to the next family michael@0: int32_t acceptedVariants = SkPaintOptionsAndroid::kDefault_Variant | michael@0: SkPaintOptionsAndroid::kElegant_Variant; michael@0: if (!(fFontFamilies[familyRecID].fPaintOptions.getFontVariant() & acceptedVariants)) { michael@0: continue; michael@0: } michael@0: michael@0: FontRecID fontRecID = find_best_style(fFontFamilies[familyRecID], SkTypeface::kNormal); michael@0: SkTypeface* face = this->getTypefaceForFontRec(fontRecID); michael@0: michael@0: SkPaint paint; michael@0: paint.setTypeface(face); michael@0: paint.setTextEncoding(SkPaint::kUTF32_TextEncoding); michael@0: michael@0: uint16_t glyphID; michael@0: paint.textToGlyphs(&uni, sizeof(uni), &glyphID); michael@0: if (glyphID != 0) { michael@0: name->set(fFontFamilies[familyRecID].fFallbackName); michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: SkTypeface* SkFontConfigInterfaceAndroid::getTypefaceForChar(SkUnichar uni, michael@0: SkTypeface::Style style, michael@0: SkPaintOptionsAndroid::FontVariant fontVariant) { michael@0: FontRecID fontRecID = find_best_style(fFontFamilies[fDefaultFamilyRecID], style); michael@0: SkTypeface* face = this->getTypefaceForFontRec(fontRecID); michael@0: michael@0: SkPaintOptionsAndroid paintOptions; michael@0: paintOptions.setFontVariant(fontVariant); michael@0: paintOptions.setUseFontFallbacks(true); michael@0: michael@0: SkPaint paint; michael@0: paint.setTypeface(face); michael@0: paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); michael@0: paint.setPaintOptionsAndroid(paintOptions); michael@0: michael@0: SkAutoGlyphCache autoCache(paint, NULL, NULL); michael@0: SkGlyphCache* cache = autoCache.getCache(); michael@0: michael@0: SkScalerContext* ctx = cache->getScalerContext(); michael@0: if (ctx) { michael@0: SkFontID fontID = ctx->findTypefaceIdForChar(uni); michael@0: return SkTypefaceCache::FindByID(fontID); michael@0: } michael@0: return NULL; michael@0: } michael@0: michael@0: FallbackFontList* SkFontConfigInterfaceAndroid::getCurrentLocaleFallbackFontList() { michael@0: SkString locale = SkFontConfigParser::GetLocale(); michael@0: if (NULL == fLocaleFallbackFontList || locale != fCachedLocale) { michael@0: fCachedLocale = locale; michael@0: fLocaleFallbackFontList = this->findFallbackFontList(locale); michael@0: } michael@0: return fLocaleFallbackFontList; michael@0: } michael@0: michael@0: FallbackFontList* SkFontConfigInterfaceAndroid::findFallbackFontList(const SkLanguage& lang, michael@0: bool isOriginal) { michael@0: const SkString& langTag = lang.getTag(); michael@0: if (langTag.isEmpty()) { michael@0: return &fDefaultFallbackList; michael@0: } michael@0: michael@0: FallbackFontList* fallbackFontList; michael@0: if (fFallbackFontDict.find(langTag.c_str(), langTag.size(), &fallbackFontList) || michael@0: fFallbackFontAliasDict.find(langTag.c_str(), langTag.size(), &fallbackFontList)) { michael@0: return fallbackFontList; michael@0: } michael@0: michael@0: // attempt a recursive fuzzy match michael@0: SkLanguage parent = lang.getParent(); michael@0: fallbackFontList = findFallbackFontList(parent, false); michael@0: michael@0: // cache the original lang so we don't have to do the recursion again. michael@0: if (isOriginal) { michael@0: DEBUG_FONT(("---- Created fallback list alias for \"%s\"", langTag.c_str())); michael@0: fFallbackFontAliasDict.set(langTag.c_str(), fallbackFontList); michael@0: } michael@0: return fallbackFontList; michael@0: } michael@0: michael@0: SkTypeface* SkFontConfigInterfaceAndroid::nextLogicalTypeface(SkFontID currFontID, michael@0: SkFontID origFontID, michael@0: const SkPaintOptionsAndroid& opts) { michael@0: // Skia does not support font fallback by default. This enables clients such michael@0: // as WebKit to customize their font selection. In any case, clients can use michael@0: // GetFallbackFamilyNameForChar() to get the fallback font for individual michael@0: // characters. michael@0: if (!opts.isUsingFontFallbacks()) { michael@0: return NULL; michael@0: } michael@0: michael@0: FallbackFontList* currentFallbackList = findFallbackFontList(opts.getLanguage()); michael@0: SkASSERT(currentFallbackList); michael@0: michael@0: SkTypeface::Style origStyle = SkTypeface::kNormal; michael@0: const SkTypeface* origTypeface = SkTypefaceCache::FindByID(origFontID); michael@0: if (NULL != origTypeface) { michael@0: origStyle = origTypeface->style(); michael@0: } michael@0: michael@0: // we must convert currTypeface into a FontRecID michael@0: FontRecID currFontRecID = INVALID_FONT_REC_ID; michael@0: const SkTypeface* currTypeface = SkTypefaceCache::FindByID(currFontID); michael@0: // non-system fonts are not in the font cache so if we are asked to fallback michael@0: // for a non-system font we will start at the front of the chain. michael@0: if (NULL != currTypeface) { michael@0: currFontRecID = ((FontConfigTypeface*)currTypeface)->getIdentity().fID; michael@0: SkASSERT(INVALID_FONT_REC_ID != currFontRecID); michael@0: } michael@0: michael@0: FamilyRecID currFamilyRecID = INVALID_FAMILY_REC_ID; michael@0: if (INVALID_FONT_REC_ID != currFontRecID) { michael@0: currFamilyRecID = fFonts[currFontRecID].fFamilyRecID; michael@0: } michael@0: michael@0: // lookup the index next font in the chain michael@0: int currFallbackFontIndex = currentFallbackList->find(currFamilyRecID); michael@0: // We add 1 to the returned index for 2 reasons: (1) if find succeeds it moves michael@0: // our index to the next entry in the list; (2) if find() fails it returns michael@0: // -1 and incrementing it will set our starting index to 0 (the head of the list) michael@0: int nextFallbackFontIndex = currFallbackFontIndex + 1; michael@0: michael@0: if(nextFallbackFontIndex >= currentFallbackList->count()) { michael@0: return NULL; michael@0: } michael@0: michael@0: // If a rec object is set to prefer "kDefault_Variant" it means they have no preference michael@0: // In this case, we set the value to "kCompact_Variant" michael@0: SkPaintOptionsAndroid::FontVariant variant = opts.getFontVariant(); michael@0: if (variant == SkPaintOptionsAndroid::kDefault_Variant) { michael@0: variant = SkPaintOptionsAndroid::kCompact_Variant; michael@0: } michael@0: michael@0: int32_t acceptedVariants = SkPaintOptionsAndroid::kDefault_Variant | variant; michael@0: michael@0: SkTypeface* nextLogicalTypeface = 0; michael@0: while (nextFallbackFontIndex < currentFallbackList->count()) { michael@0: FamilyRecID familyRecID = currentFallbackList->getAt(nextFallbackFontIndex); michael@0: if ((fFontFamilies[familyRecID].fPaintOptions.getFontVariant() & acceptedVariants) != 0) { michael@0: FontRecID matchedFont = find_best_style(fFontFamilies[familyRecID], origStyle); michael@0: nextLogicalTypeface = this->getTypefaceForFontRec(matchedFont); michael@0: break; michael@0: } michael@0: nextFallbackFontIndex++; michael@0: } michael@0: michael@0: DEBUG_FONT(("---- nextLogicalFont: currFontID=%d, origFontID=%d, currRecID=%d, " michael@0: "lang=%s, variant=%d, nextFallbackIndex[%d,%d] => nextLogicalTypeface=%d", michael@0: currFontID, origFontID, currFontRecID, opts.getLanguage().getTag().c_str(), michael@0: variant, nextFallbackFontIndex, currentFallbackList->getAt(nextFallbackFontIndex), michael@0: (nextLogicalTypeface) ? nextLogicalTypeface->uniqueID() : 0)); michael@0: return SkSafeRef(nextLogicalTypeface); michael@0: } michael@0: michael@0: SkTypeface* SkFontConfigInterfaceAndroid::getTypefaceForGlyphID(uint16_t glyphID, michael@0: const SkTypeface* origTypeface, michael@0: const SkPaintOptionsAndroid& opts, michael@0: int* lBounds, int* uBounds) { michael@0: // If we aren't using fallbacks then we shouldn't be calling this michael@0: SkASSERT(opts.isUsingFontFallbacks()); michael@0: SkASSERT(origTypeface); michael@0: michael@0: SkTypeface* currentTypeface = NULL; michael@0: int lowerBounds = 0; //inclusive michael@0: int upperBounds = origTypeface->countGlyphs(); //exclusive michael@0: michael@0: // check to see if the glyph is in the bounds of the origTypeface michael@0: if (glyphID < upperBounds) { michael@0: currentTypeface = const_cast(origTypeface); michael@0: } else { michael@0: FallbackFontList* currentFallbackList = findFallbackFontList(opts.getLanguage()); michael@0: SkASSERT(currentFallbackList); michael@0: michael@0: // If an object is set to prefer "kDefault_Variant" it means they have no preference michael@0: // In this case, we set the value to "kCompact_Variant" michael@0: SkPaintOptionsAndroid::FontVariant variant = opts.getFontVariant(); michael@0: if (variant == SkPaintOptionsAndroid::kDefault_Variant) { michael@0: variant = SkPaintOptionsAndroid::kCompact_Variant; michael@0: } michael@0: michael@0: int32_t acceptedVariants = SkPaintOptionsAndroid::kDefault_Variant | variant; michael@0: SkTypeface::Style origStyle = origTypeface->style(); michael@0: michael@0: for (int x = 0; x < currentFallbackList->count(); ++x) { michael@0: const FamilyRecID familyRecID = currentFallbackList->getAt(x); michael@0: const SkPaintOptionsAndroid& familyOptions = fFontFamilies[familyRecID].fPaintOptions; michael@0: if ((familyOptions.getFontVariant() & acceptedVariants) != 0) { michael@0: FontRecID matchedFont = find_best_style(fFontFamilies[familyRecID], origStyle); michael@0: currentTypeface = this->getTypefaceForFontRec(matchedFont); michael@0: lowerBounds = upperBounds; michael@0: upperBounds += currentTypeface->countGlyphs(); michael@0: if (glyphID < upperBounds) { michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (NULL != currentTypeface) { michael@0: if (lBounds) { michael@0: *lBounds = lowerBounds; michael@0: } michael@0: if (uBounds) { michael@0: *uBounds = upperBounds; michael@0: } michael@0: } michael@0: return currentTypeface; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: bool SkGetFallbackFamilyNameForChar(SkUnichar uni, SkString* name) { michael@0: SkFontConfigInterfaceAndroid* fontConfig = getSingletonInterface(); michael@0: return fontConfig->getFallbackFamilyNameForChar(uni, NULL, name); michael@0: } michael@0: michael@0: bool SkGetFallbackFamilyNameForChar(SkUnichar uni, const char* lang, SkString* name) { michael@0: SkFontConfigInterfaceAndroid* fontConfig = getSingletonInterface(); michael@0: return fontConfig->getFallbackFamilyNameForChar(uni, lang, name); michael@0: } michael@0: michael@0: void SkUseTestFontConfigFile(const char* mainconf, const char* fallbackconf, michael@0: const char* fontsdir) { michael@0: gTestMainConfigFile = mainconf; michael@0: gTestFallbackConfigFile = fallbackconf; michael@0: gTestFontFilePrefix = fontsdir; michael@0: SkASSERT(gTestMainConfigFile); michael@0: SkASSERT(gTestFallbackConfigFile); michael@0: SkASSERT(gTestFontFilePrefix); michael@0: SkDEBUGF(("Use Test Config File Main %s, Fallback %s, Font Dir %s", michael@0: gTestMainConfigFile, gTestFallbackConfigFile, gTestFontFilePrefix)); michael@0: } michael@0: michael@0: SkTypeface* SkAndroidNextLogicalTypeface(SkFontID currFontID, SkFontID origFontID, michael@0: const SkPaintOptionsAndroid& options) { michael@0: SkFontConfigInterfaceAndroid* fontConfig = getSingletonInterface(); michael@0: return fontConfig->nextLogicalTypeface(currFontID, origFontID, options); michael@0: michael@0: } michael@0: michael@0: SkTypeface* SkGetTypefaceForGlyphID(uint16_t glyphID, const SkTypeface* origTypeface, michael@0: const SkPaintOptionsAndroid& options, michael@0: int* lowerBounds, int* upperBounds) { michael@0: SkFontConfigInterfaceAndroid* fontConfig = getSingletonInterface(); michael@0: return fontConfig->getTypefaceForGlyphID(glyphID, origTypeface, options, michael@0: lowerBounds, upperBounds); michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK michael@0: michael@0: struct HB_UnicodeMapping { michael@0: hb_script_t script; michael@0: const SkUnichar unicode; michael@0: }; michael@0: michael@0: /* michael@0: * The following scripts are not complex fonts and we do not expect them to be parsed by this table michael@0: * HB_SCRIPT_COMMON, michael@0: * HB_SCRIPT_GREEK, michael@0: * HB_SCRIPT_CYRILLIC, michael@0: * HB_SCRIPT_HANGUL michael@0: * HB_SCRIPT_INHERITED michael@0: */ michael@0: michael@0: /* Harfbuzz (old) is missing a number of scripts in its table. For these, michael@0: * we include a value which can never happen. We won't get complex script michael@0: * shaping in these cases, but the library wouldn't know how to shape michael@0: * them anyway. */ michael@0: #define HB_Script_Unknown HB_ScriptCount michael@0: michael@0: static HB_UnicodeMapping HB_UnicodeMappingArray[] = { michael@0: {HB_SCRIPT_ARMENIAN, 0x0531}, michael@0: {HB_SCRIPT_HEBREW, 0x0591}, michael@0: {HB_SCRIPT_ARABIC, 0x0600}, michael@0: {HB_SCRIPT_SYRIAC, 0x0710}, michael@0: {HB_SCRIPT_THAANA, 0x0780}, michael@0: {HB_SCRIPT_NKO, 0x07C0}, michael@0: {HB_SCRIPT_DEVANAGARI, 0x0901}, michael@0: {HB_SCRIPT_BENGALI, 0x0981}, michael@0: {HB_SCRIPT_GURMUKHI, 0x0A10}, michael@0: {HB_SCRIPT_GUJARATI, 0x0A90}, michael@0: {HB_SCRIPT_ORIYA, 0x0B10}, michael@0: {HB_SCRIPT_TAMIL, 0x0B82}, michael@0: {HB_SCRIPT_TELUGU, 0x0C10}, michael@0: {HB_SCRIPT_KANNADA, 0x0C90}, michael@0: {HB_SCRIPT_MALAYALAM, 0x0D10}, michael@0: {HB_SCRIPT_SINHALA, 0x0D90}, michael@0: {HB_SCRIPT_THAI, 0x0E01}, michael@0: {HB_SCRIPT_LAO, 0x0E81}, michael@0: {HB_SCRIPT_TIBETAN, 0x0F00}, michael@0: {HB_SCRIPT_MYANMAR, 0x1000}, michael@0: {HB_SCRIPT_GEORGIAN, 0x10A0}, michael@0: {HB_SCRIPT_ETHIOPIC, 0x1200}, michael@0: {HB_SCRIPT_CHEROKEE, 0x13A0}, michael@0: {HB_SCRIPT_OGHAM, 0x1680}, michael@0: {HB_SCRIPT_RUNIC, 0x16A0}, michael@0: {HB_SCRIPT_KHMER, 0x1780}, michael@0: {HB_SCRIPT_TAI_LE, 0x1950}, michael@0: {HB_SCRIPT_NEW_TAI_LUE, 0x1980}, michael@0: {HB_SCRIPT_TAI_THAM, 0x1A20}, michael@0: {HB_SCRIPT_CHAM, 0xAA00}, michael@0: }; michael@0: michael@0: // returns 0 for "Not Found" michael@0: static SkUnichar getUnicodeFromHBScript(hb_script_t script) { michael@0: SkUnichar unichar = 0; michael@0: int numSupportedFonts = sizeof(HB_UnicodeMappingArray) / sizeof(HB_UnicodeMapping); michael@0: for (int i = 0; i < numSupportedFonts; i++) { michael@0: if (script == HB_UnicodeMappingArray[i].script) { michael@0: unichar = HB_UnicodeMappingArray[i].unicode; michael@0: break; michael@0: } michael@0: } michael@0: return unichar; michael@0: } michael@0: michael@0: struct TypefaceLookupStruct { michael@0: hb_script_t script; michael@0: SkTypeface::Style style; michael@0: SkPaintOptionsAndroid::FontVariant fontVariant; michael@0: SkTypeface* typeface; michael@0: }; michael@0: michael@0: SK_DECLARE_STATIC_MUTEX(gTypefaceTableMutex); // This is the mutex for gTypefaceTable michael@0: static SkTDArray gTypefaceTable; // This is protected by gTypefaceTableMutex michael@0: michael@0: static int typefaceLookupCompare(const TypefaceLookupStruct& first, michael@0: const TypefaceLookupStruct& second) { michael@0: if (first.script != second.script) { michael@0: return (first.script > second.script) ? 1 : -1; michael@0: } michael@0: if (first.style != second.style) { michael@0: return (first.style > second.style) ? 1 : -1; michael@0: } michael@0: if (first.fontVariant != second.fontVariant) { michael@0: return (first.fontVariant > second.fontVariant) ? 1 : -1; michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: SkTypeface* SkCreateTypefaceForScript(hb_script_t script, SkTypeface::Style style, michael@0: SkPaintOptionsAndroid::FontVariant fontVariant) { michael@0: SkAutoMutexAcquire ac(gTypefaceTableMutex); michael@0: michael@0: TypefaceLookupStruct key; michael@0: key.script = script; michael@0: key.style = style; michael@0: key.fontVariant = fontVariant; michael@0: michael@0: int index = SkTSearch( michael@0: (const TypefaceLookupStruct*) gTypefaceTable.begin(), michael@0: gTypefaceTable.count(), key, sizeof(TypefaceLookupStruct), michael@0: typefaceLookupCompare); michael@0: michael@0: SkTypeface* retTypeface = NULL; michael@0: if (index >= 0) { michael@0: retTypeface = gTypefaceTable[index].typeface; michael@0: } michael@0: else { michael@0: SkUnichar unichar = getUnicodeFromHBScript(script); michael@0: if (!unichar) { michael@0: return NULL; michael@0: } michael@0: michael@0: SkFontConfigInterfaceAndroid* fontConfig = getSingletonInterface(); michael@0: retTypeface = fontConfig->getTypefaceForChar(unichar, style, fontVariant); michael@0: michael@0: // add to the lookup table michael@0: key.typeface = retTypeface; michael@0: *gTypefaceTable.insert(~index) = key; michael@0: } michael@0: michael@0: // we ref(), the caller is expected to unref when they are done michael@0: return SkSafeRef(retTypeface); michael@0: } michael@0: michael@0: #endif michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: SkFontMgr* SkFontMgr::Factory() { michael@0: return NULL; michael@0: }