aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar djsollen@google.com <djsollen@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-05-21 16:53:50 +0000
committerGravatar djsollen@google.com <djsollen@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-05-21 16:53:50 +0000
commitbfae9d373ccc9cf47fd70757092962c7850fadf4 (patch)
treebb2dae453daf82743682a8323d273af418d1a145 /src
parent7768751b8bec9e50fcbdad67b82e1962e71c3eb9 (diff)
Add a fontConfig interface for android.
The contents of this CL are based on the SkFontHost_android.cpp found in the android repository. BUG= R=reed@google.com Review URL: https://codereview.chromium.org/14731025 git-svn-id: http://skia.googlecode.com/svn/trunk@9219 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'src')
-rw-r--r--src/ports/SkFontConfigInterface_android.cpp623
-rw-r--r--src/ports/SkFontConfigParser_android.cpp276
-rw-r--r--src/ports/SkFontConfigParser_android.h65
3 files changed, 964 insertions, 0 deletions
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_ */