diff options
-rw-r--r-- | gyp/ports.gyp | 5 | ||||
-rw-r--r-- | include/ports/SkTypeface_android.h | 4 | ||||
-rw-r--r-- | src/ports/SkFontConfigInterface_android.cpp | 623 | ||||
-rw-r--r-- | src/ports/SkFontConfigParser_android.cpp | 276 | ||||
-rw-r--r-- | src/ports/SkFontConfigParser_android.h | 65 |
5 files changed, 970 insertions, 3 deletions
diff --git a/gyp/ports.gyp b/gyp/ports.gyp index 6a99268759..abbe16ba69 100644 --- a/gyp/ports.gyp +++ b/gyp/ports.gyp @@ -162,12 +162,13 @@ '../src/ports/SkPurgeableMemoryBlock_none.cpp', ], 'sources': [ - '../src/ports/FontHostConfiguration_android.cpp', '../src/ports/SkDebug_android.cpp', '../src/ports/SkThread_pthread.cpp', - '../src/ports/SkFontHost_android.cpp', + '../src/ports/SkFontConfigInterface_android.cpp', + '../src/ports/SkFontConfigParser_android.cpp', '../src/ports/SkFontHost_FreeType.cpp', '../src/ports/SkFontHost_FreeType_common.cpp', + '../src/ports/SkFontHost_fontconfig.cpp', '../src/ports/SkPurgeableMemoryBlock_android.cpp', ], 'dependencies': [ diff --git a/include/ports/SkTypeface_android.h b/include/ports/SkTypeface_android.h index 0d89736e7e..fa702cb011 100644 --- a/include/ports/SkTypeface_android.h +++ b/include/ports/SkTypeface_android.h @@ -13,9 +13,11 @@ #include "SkTypeface.h" +class SkPaintOptionsAndroid; + /** * Get the family name of the font in the fallback font list containing - * the specified chararacter. if no font is found, returns false. + * the specified character. if no font is found, returns false. */ SK_API bool SkGetFallbackFamilyNameForChar(SkUnichar uni, SkString* name); diff --git a/src/ports/SkFontConfigInterface_android.cpp b/src/ports/SkFontConfigInterface_android.cpp new file mode 100644 index 0000000000..850d902ba4 --- /dev/null +++ b/src/ports/SkFontConfigInterface_android.cpp @@ -0,0 +1,623 @@ + +/* + * 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 "SkPaintOptionsAndroid.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; + +/////////////////////////////////////////////////////////////////////////////// + +// used to record our notion of the pre-existing fonts +struct FontRec { + SkRefPtr<SkTypeface> fTypeface; + SkString fFileName; + SkTypeface::Style fStyle; + SkPaintOptionsAndroid fPaintOptions; + bool fIsFallbackFont; + bool fIsValid; +}; + +typedef int32_t FontRecID; +#define INVALID_FONT_REC_ID -1 + +struct FamilyRec { + FamilyRec() { + memset(fFontRecID, INVALID_FONT_REC_ID, sizeof(fFontRecID)); + } + + static const int FONT_STYLE_COUNT = 4; + FontRecID fFontRecID[FONT_STYLE_COUNT]; +}; + +typedef int32_t FamilyRecID; +#define INVALID_FAMILY_REC_ID -1 + +typedef SkTDArray<FontRecID> 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, 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); + FallbackFontList* findFallbackFontList(const SkLanguage& lang); + + SkTArray<FontRec> fFonts; + SkTArray<FamilyRec> fFontFamilies; + SkTDict<FamilyRecID> fFamilyNameDict; + FamilyRecID fDefaultFamilyRecID; + + // (SkLanguage)<->(fallback chain index) translation + SkTDict<FallbackFontList*> fFallbackFontDict; + FallbackFontList fDefaultFallbackList; +}; + +/////////////////////////////////////////////////////////////////////////////// + +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<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() { + return getSingletonInterface(); +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool has_font(const SkTArray<FontRec>& 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<FamilyRecID>& 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<FontFamily*>& fontFamilies) : + fFonts(fontFamilies.count()), + fFontFamilies(fontFamilies.count() / FamilyRec::FONT_STYLE_COUNT), + fFamilyNameDict(1024), + fDefaultFamilyRecID(INVALID_FAMILY_REC_ID), + fFallbackFontDict(128) { + + 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; + + const FontRecID fontRecID = fFonts.count() - 1; + + SkAutoTUnref<SkStream> 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; + } + + // 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<const char*> 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<FallbackFontList*>::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<FallbackFontList*>::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) SK_OVERRIDE { + // clip to legal style bits + style = (SkTypeface::Style)(style & SkTypeface::kBoldItalic); + + bool exactNameMatch = false; + + FamilyRecID familyRecID = INVALID_FAMILY_REC_ID; + if (NULL != familyName) { + if (fFamilyNameDict.find(familyName, &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; + bool found = fFamilyNameDict.findKey(familyRecID, &familyName); + SkASSERT(found && familyName); + outFamilyName->set(familyName); + } + } + + if (NULL != outStyle) { + *outStyle = fontRec.fStyle; + } + + return true; +} + +SkStream* SkFontConfigInterfaceAndroid::openStream(const FontIdentity& identity) SK_OVERRIDE { + return SkStream::NewFromFile(identity.fString.c_str()); +} + +SkDataTable* SkFontConfigInterfaceAndroid::getFamilyNames() SK_OVERRIDE { + 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>*) SK_OVERRIDE { + return false; +} + +static SkTypeface* get_typeface_for_rec(FontRec& fontRec) { + SkTypeface* face = fontRec.fTypeface.get(); + if (!face) { + // TODO look for it in the typeface cache + + // if it is not in the cache then create it + face = SkTypeface::CreateFromFile(fontRec.fFileName.c_str()); + + // store the result for subsequent lookups + fontRec.fTypeface = face; + } + SkASSERT(face); + return face; +} + +bool SkFontConfigInterfaceAndroid::getFallbackFamilyNameForChar(SkUnichar uni, SkString* name) { + for (int i = 0; i < fDefaultFallbackList.count(); i++) { + FontRecID fontRecID = fDefaultFallbackList[i]; + SkTypeface* face = get_typeface_for_rec(fFonts[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 = get_typeface_for_rec(fFonts[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::findFallbackFontList(const SkLanguage& lang) { + const SkString& langTag = lang.getTag(); + if (langTag.isEmpty()) { + return &fDefaultFallbackList; + } + + FallbackFontList* fallbackFontList; + if (fFallbackFontDict.find(langTag.c_str(), langTag.size(), &fallbackFontList)) { + return fallbackFontList; + } + + // attempt a recursive fuzzy match + // TODO we could cache the intermediate parent so that we don't have to do + // the recursion again. + SkLanguage parent = lang.getParent(); + return findFallbackFontList(parent); +} + +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; + } + + const SkTypeface* origTypeface = SkTypefaceCache::FindByID(origFontID); + const SkTypeface* currTypeface = SkTypefaceCache::FindByID(currFontID); + + FallbackFontList* currentFallbackList = findFallbackFontList(opts.getLanguage()); + SkASSERT(currentFallbackList); + + SkASSERT(origTypeface != 0); + SkASSERT(currTypeface != 0); + + // we must convert currTypeface into a FontRecID + FontRecID currFontRecID = ((FontConfigTypeface*)currTypeface)->getIdentity().fID; + + // TODO lookup the index next font in the chain + int currFallbackFontIndex = currentFallbackList->find(currFontRecID); + int nextFallbackFontIndex = currFallbackFontIndex + 1; + SkASSERT(-1 == nextFallbackFontIndex); + + if(nextFallbackFontIndex >= currentFallbackList->count() && -1 == currFallbackFontIndex) { + 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) { + nextLogicalTypeface = get_typeface_for_rec(fFonts[fontRecID]); + break; + } + nextFallbackFontIndex++; + } + + DEBUG_FONT(("---- nextLogicalFont: currFontID=%d, origFontID=%d, currRecID=%d, " + "lang=%s, variant=%d, nextFallbackIndex=%d => nextLogicalTypeface=%d", + currFontID, origFontID, currFontRecID, opts.getLanguage().getTag().c_str(), + variant, 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); + +} + +/////////////////////////////////////////////////////////////////////////////// + +SkFontMgr* SkFontMgr::Factory() { + return NULL; +} diff --git a/src/ports/SkFontConfigParser_android.cpp b/src/ports/SkFontConfigParser_android.cpp new file mode 100644 index 0000000000..9214d1bf61 --- /dev/null +++ b/src/ports/SkFontConfigParser_android.cpp @@ -0,0 +1,276 @@ +/* + * Copyright 2011 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 "SkFontConfigParser_android.h" +#include "SkTDArray.h" +#include "SkTypeface.h" + +#include <expat.h> +#include <sys/system_properties.h> + +#define SYSTEM_FONTS_FILE "/system/etc/system_fonts.xml" +#define FALLBACK_FONTS_FILE "/system/etc/fallback_fonts.xml" +#define VENDOR_FONTS_FILE "/vendor/etc/fallback_fonts.xml" + +// These defines are used to determine the kind of tag that we're currently +// populating with data. We only care about the sibling tags nameset and fileset +// for now. +#define NO_TAG 0 +#define NAMESET_TAG 1 +#define FILESET_TAG 2 + +/** + * The FamilyData structure is passed around by the parser so that each handler + * can read these variables that are relevant to the current parsing. + */ +struct FamilyData { + FamilyData(XML_Parser *parserRef, SkTDArray<FontFamily*> &familiesRef) : + parser(parserRef), + families(familiesRef), + currentFamily(NULL), + currentFontInfo(NULL), + currentTag(NO_TAG) {}; + + XML_Parser *parser; // The expat parser doing the work + SkTDArray<FontFamily*> &families; // The array that each family is put into as it is parsed + FontFamily *currentFamily; // The current family being created + FontFileInfo *currentFontInfo; // The current fontInfo being created + int currentTag; // A flag to indicate whether we're in nameset/fileset tags +}; + +/** + * Handler for arbitrary text. This is used to parse the text inside each name + * or file tag. The resulting strings are put into the fNames or FontFileInfo arrays. + */ +static void textHandler(void *data, const char *s, int len) { + FamilyData *familyData = (FamilyData*) data; + // Make sure we're in the right state to store this name information + if (familyData->currentFamily && + (familyData->currentTag == NAMESET_TAG || familyData->currentTag == FILESET_TAG)) { + // Malloc new buffer to store the string + char *buff; + buff = (char*) malloc((len + 1) * sizeof(char)); + strncpy(buff, s, len); + buff[len] = '\0'; + switch (familyData->currentTag) { + case NAMESET_TAG: + *(familyData->currentFamily->fNames.append()) = buff; + break; + case FILESET_TAG: + if (familyData->currentFontInfo) { + familyData->currentFontInfo->fFileName = buff; + } + break; + default: + // Noop - don't care about any text that's not in the Fonts or Names list + break; + } + } +} + +/** + * Handler for font files. This processes the attributes for language and + * variants then lets textHandler handle the actual file name + */ +static void fontFileElementHandler(FamilyData *familyData, const char **attributes) { + FontFileInfo* newFileInfo = new FontFileInfo(); + if (attributes) { + int currentAttributeIndex = 0; + while (attributes[currentAttributeIndex]) { + const char* attributeName = attributes[currentAttributeIndex]; + const char* attributeValue = attributes[currentAttributeIndex+1]; + int nameLength = strlen(attributeName); + int valueLength = strlen(attributeValue); + if (strncmp(attributeName, "variant", nameLength) == 0) { + if (strncmp(attributeValue, "elegant", valueLength) == 0) { + newFileInfo->fPaintOptions.setFontVariant(SkPaintOptionsAndroid::kElegant_Variant); + } else if (strncmp(attributeValue, "compact", valueLength) == 0) { + newFileInfo->fPaintOptions.setFontVariant(SkPaintOptionsAndroid::kCompact_Variant); + } + } else if (strncmp(attributeName, "lang", nameLength) == 0) { + newFileInfo->fPaintOptions.setLanguage(attributeValue); + } + //each element is a pair of attributeName/attributeValue string pairs + currentAttributeIndex += 2; + } + } + *(familyData->currentFamily->fFontFiles.append()) = newFileInfo; + familyData->currentFontInfo = newFileInfo; + XML_SetCharacterDataHandler(*familyData->parser, textHandler); +} + +/** + * Handler for the start of a tag. The only tags we expect are family, nameset, + * fileset, name, and file. + */ +static void startElementHandler(void *data, const char *tag, const char **atts) { + FamilyData *familyData = (FamilyData*) data; + int len = strlen(tag); + if (strncmp(tag, "family", len)== 0) { + familyData->currentFamily = new FontFamily(); + familyData->currentFamily->order = -1; + // The Family tag has an optional "order" attribute with an integer value >= 0 + // If this attribute does not exist, the default value is -1 + for (int i = 0; atts[i] != NULL; i += 2) { + const char* valueString = atts[i+1]; + int value; + int len = sscanf(valueString, "%d", &value); + if (len > 0) { + familyData->currentFamily->order = value; + } + } + } else if (len == 7 && strncmp(tag, "nameset", len) == 0) { + familyData->currentTag = NAMESET_TAG; + } else if (len == 7 && strncmp(tag, "fileset", len) == 0) { + familyData->currentTag = FILESET_TAG; + } else if (strncmp(tag, "name", len) == 0 && familyData->currentTag == NAMESET_TAG) { + // If it's a Name, parse the text inside + XML_SetCharacterDataHandler(*familyData->parser, textHandler); + } else if (strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET_TAG) { + // If it's a file, parse the attributes, then parse the text inside + fontFileElementHandler(familyData, atts); + } +} + +/** + * Handler for the end of tags. We only care about family, nameset, fileset, + * name, and file. + */ +static void endElementHandler(void *data, const char *tag) { + FamilyData *familyData = (FamilyData*) data; + int len = strlen(tag); + if (strncmp(tag, "family", len)== 0) { + // Done parsing a Family - store the created currentFamily in the families array + *familyData->families.append() = familyData->currentFamily; + familyData->currentFamily = NULL; + } else if (len == 7 && strncmp(tag, "nameset", len) == 0) { + familyData->currentTag = NO_TAG; + } else if (len == 7 && strncmp(tag, "fileset", len) == 0) { + familyData->currentTag = NO_TAG; + } else if ((strncmp(tag, "name", len) == 0 && familyData->currentTag == NAMESET_TAG) || + (strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET_TAG)) { + // Disable the arbitrary text handler installed to load Name data + XML_SetCharacterDataHandler(*familyData->parser, NULL); + } +} + +/** + * This function parses the given filename and stores the results in the given + * families array. + */ +static void parseConfigFile(const char *filename, SkTDArray<FontFamily*> &families) { + XML_Parser parser = XML_ParserCreate(NULL); + FamilyData *familyData = new FamilyData(&parser, families); + XML_SetUserData(parser, familyData); + XML_SetElementHandler(parser, startElementHandler, endElementHandler); + FILE *file = fopen(filename, "r"); + // Some of the files we attempt to parse (in particular, /vendor/etc/fallback_fonts.xml) + // are optional - failure here is okay because one of these optional files may not exist. + if (file == NULL) { + return; + } + char buffer[512]; + bool done = false; + while (!done) { + fgets(buffer, sizeof(buffer), file); + int len = strlen(buffer); + if (feof(file) != 0) { + done = true; + } + XML_Parse(parser, buffer, len, done); + } + fclose(file); +} + +static void getSystemFontFamilies(SkTDArray<FontFamily*> &fontFamilies) { + parseConfigFile(SYSTEM_FONTS_FILE, fontFamilies); +} + +static void getFallbackFontFamilies(SkTDArray<FontFamily*> &fallbackFonts) { + SkTDArray<FontFamily*> vendorFonts; + parseConfigFile(FALLBACK_FONTS_FILE, fallbackFonts); + parseConfigFile(VENDOR_FONTS_FILE, vendorFonts); + + // This loop inserts the vendor fallback fonts in the correct order in the + // overall fallbacks list. + int currentOrder = -1; + for (int i = 0; i < vendorFonts.count(); ++i) { + FontFamily* family = vendorFonts[i]; + int order = family->order; + if (order < 0) { + if (currentOrder < 0) { + // Default case - just add it to the end of the fallback list + *fallbackFonts.append() = family; + } else { + // no order specified on this font, but we're incrementing the order + // based on an earlier order insertion request + *fallbackFonts.insert(currentOrder++) = family; + } + } else { + // Add the font into the fallback list in the specified order. Set + // currentOrder for correct placement of other fonts in the vendor list. + *fallbackFonts.insert(order) = family; + currentOrder = order + 1; + } + } +} + +/** + * Loads data on font families from various expected configuration files. The + * resulting data is returned in the given fontFamilies array. + */ +void SkFontConfigParser::GetFontFamilies(SkTDArray<FontFamily*> &fontFamilies) { + + getSystemFontFamilies(fontFamilies); + + // Append all the fallback fonts to system fonts + SkTDArray<FontFamily*> fallbackFonts; + getFallbackFontFamilies(fallbackFonts); + for (int i = 0; i < fallbackFonts.count(); ++i) { + fallbackFonts[i]->fIsFallbackFont = true; + *fontFamilies.append() = fallbackFonts[i]; + } +} + +void SkFontConfigParser::GetTestFontFamilies(SkTDArray<FontFamily*> &fontFamilies, + const char* testMainConfigFile, + const char* testFallbackConfigFile) { + parseConfigFile(testMainConfigFile, fontFamilies); + + SkTDArray<FontFamily*> fallbackFonts; + parseConfigFile(testFallbackConfigFile, fallbackFonts); + + // Append all fallback fonts to system fonts + for (int i = 0; i < fallbackFonts.count(); ++i) { + fallbackFonts[i]->fIsFallbackFont = true; + *fontFamilies.append() = fallbackFonts[i]; + } +} + +/** + * Read the persistent locale. + */ +void SkFontConfigParser::GetLocale(AndroidLocale &locale) +{ + char propLang[PROP_VALUE_MAX], propRegn[PROP_VALUE_MAX]; + __system_property_get("persist.sys.language", propLang); + __system_property_get("persist.sys.country", propRegn); + + if (*propLang == 0 && *propRegn == 0) { + /* Set to ro properties, default is en_US */ + __system_property_get("ro.product.locale.language", propLang); + __system_property_get("ro.product.locale.region", propRegn); + if (*propLang == 0 && *propRegn == 0) { + strcpy(propLang, "en"); + strcpy(propRegn, "US"); + } + } + strncpy(locale.language, propLang, 2); + locale.language[2] = '\0'; + strncpy(locale.region, propRegn, 2); + locale.region[2] = '\0'; +} diff --git a/src/ports/SkFontConfigParser_android.h b/src/ports/SkFontConfigParser_android.h new file mode 100644 index 0000000000..c5cd3eb8b7 --- /dev/null +++ b/src/ports/SkFontConfigParser_android.h @@ -0,0 +1,65 @@ +/* + * Copyright 2011 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. + */ + +#ifndef SKFONTCONFIGPARSER_ANDROID_H_ +#define SKFONTCONFIGPARSER_ANDROID_H_ + +#include "SkTypes.h" + +#include "SkPaintOptionsAndroid.h" +#include "SkTDArray.h" + +struct FontFileInfo { + FontFileInfo() : fFileName(NULL) {} + + const char* fFileName; + SkPaintOptionsAndroid fPaintOptions; +}; + +/** + * The FontFamily data structure is created during parsing and handed back to + * Skia to fold into its representation of font families. fNames is the list of + * font names that alias to a font family. fontFileArray is the list of information + * about each file. Order is the priority order for the font. This is + * used internally to determine the order in which to place fallback fonts as + * they are read from the configuration files. + */ +struct FontFamily { + FontFamily() : fIsFallbackFont(false), order(-1) {} + + SkTDArray<const char*> fNames; + SkTDArray<FontFileInfo*> fFontFiles; + bool fIsFallbackFont; + int order; // only used internally by SkFontConfigParser +}; + +namespace SkFontConfigParser { + +/** + * Parses all system font configuration files and returns the results in an + * array of FontFamily structures. + */ +void GetFontFamilies(SkTDArray<FontFamily*> &fontFamilies); + +/** + * Parses all test font configuration files and returns the results in an + * array of FontFamily structures. + */ +void GetTestFontFamilies(SkTDArray<FontFamily*> &fontFamilies, + const char* testMainConfigFile, + const char* testFallbackConfigFile); + +struct AndroidLocale { + char language[3]; + char region[3]; +}; + +void GetLocale(AndroidLocale &locale); + +} // SkFontConfigParser namespace + +#endif /* SKFONTCONFIGPARSER_ANDROID_H_ */ |