diff options
author | bungeman <bungeman@google.com> | 2014-07-24 08:05:09 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-07-24 08:05:10 -0700 |
commit | 8d84c995319dd4a82e4f2054bbd19f968c671ca6 (patch) | |
tree | 4b003f8346d3a26632de2c2d132dbf8631e23b67 /src | |
parent | 307bcea49245223a05eea5f818a561b9d3e6cdd6 (diff) |
SkFontMgr for Android.
Committed: https://skia.googlesource.com/skia/+/4f9a01e03ef3bbe7dd8f9bebdcb3555226087e06
R=djsollen@google.com, tomhudson@google.com, robertphillips@google.com
Author: bungeman@google.com
Review URL: https://codereview.chromium.org/414483002
Diffstat (limited to 'src')
-rw-r--r-- | src/ports/SkFontConfigInterface_android.cpp | 5 | ||||
-rw-r--r-- | src/ports/SkFontConfigParser_android.cpp | 35 | ||||
-rw-r--r-- | src/ports/SkFontConfigParser_android.h | 3 | ||||
-rw-r--r-- | src/ports/SkFontMgr_android.cpp | 406 |
4 files changed, 443 insertions, 6 deletions
diff --git a/src/ports/SkFontConfigInterface_android.cpp b/src/ports/SkFontConfigInterface_android.cpp index c4f1753eb7..1323a62d7a 100644 --- a/src/ports/SkFontConfigInterface_android.cpp +++ b/src/ports/SkFontConfigInterface_android.cpp @@ -907,8 +907,3 @@ SkTypeface* SkCreateTypefaceForScript(hb_script_t script, SkTypeface::Style styl #endif -/////////////////////////////////////////////////////////////////////////////// - -SkFontMgr* SkFontMgr::Factory() { - return NULL; -} diff --git a/src/ports/SkFontConfigParser_android.cpp b/src/ports/SkFontConfigParser_android.cpp index 38a6ee6520..fc8e9199c3 100644 --- a/src/ports/SkFontConfigParser_android.cpp +++ b/src/ports/SkFontConfigParser_android.cpp @@ -7,12 +7,15 @@ #include "SkFontConfigParser_android.h" #include "SkTDArray.h" +#include "SkTSearch.h" #include "SkTypeface.h" #include <expat.h> #include <stdio.h> #include <sys/system_properties.h> +#include <limits> + #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" @@ -48,13 +51,14 @@ struct FamilyData { * or file tag. The resulting strings are put into the fNames or FontFileInfo arrays. */ static void textHandler(void *data, const char *s, int len) { + SkAutoAsciiToLC tolc(s); 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)) { switch (familyData->currentTag) { case NAMESET_TAG: - familyData->currentFamily->fNames.push_back().set(s, len); + familyData->currentFamily->fNames.push_back().set(tolc.lc(), len); break; case FILESET_TAG: if (familyData->currentFontInfo) { @@ -68,6 +72,28 @@ static void textHandler(void *data, const char *s, int len) { } } +/** http://www.w3.org/TR/html-markup/datatypes.html#common.data.integer.non-negative-def */ +template <typename T> static bool parseNonNegativeInteger(const char* s, T* value) { + SK_COMPILE_ASSERT(std::numeric_limits<T>::is_integer, T_must_be_integer); + const T nMax = std::numeric_limits<T>::max() / 10; + const T dMax = std::numeric_limits<T>::max() - (nMax * 10); + T n = 0; + for (; *s; ++s) { + // Check if digit + if (*s < '0' || '9' < *s) { + return false; + } + int d = *s - '0'; + // Check for overflow + if (n > nMax || (n == nMax && d > dMax)) { + return false; + } + n = (n * 10) + d; + } + *value = n; + return true; +} + /** * Handler for font files. This processes the attributes for language and * variants then lets textHandler handle the actual file name @@ -90,6 +116,13 @@ static void fontFileElementHandler(FamilyData *familyData, const char **attribut } } else if (strncmp(attributeName, "lang", nameLength) == 0) { newFileInfo.fPaintOptions.setLanguage(attributeValue); + } else if (strncmp(attributeName, "index", nameLength) == 0) { + int value; + if (parseNonNegativeInteger(attributeValue, &value)) { + newFileInfo.fIndex = value; + } else { + SkDebugf("---- SystemFonts index=%s (INVALID)", attributeValue); + } } //each element is a pair of attributeName/attributeValue string pairs currentAttributeIndex += 2; diff --git a/src/ports/SkFontConfigParser_android.h b/src/ports/SkFontConfigParser_android.h index e2247efb0a..8119fd7558 100644 --- a/src/ports/SkFontConfigParser_android.h +++ b/src/ports/SkFontConfigParser_android.h @@ -15,7 +15,10 @@ #include "SkTDArray.h" struct FontFileInfo { + FontFileInfo() : fIndex(0) { } + SkString fFileName; + int fIndex; SkPaintOptionsAndroid fPaintOptions; }; diff --git a/src/ports/SkFontMgr_android.cpp b/src/ports/SkFontMgr_android.cpp new file mode 100644 index 0000000000..ba3846c412 --- /dev/null +++ b/src/ports/SkFontMgr_android.cpp @@ -0,0 +1,406 @@ +/* + * Copyright 2014 Google Inc. + * + * 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 "SkFontDescriptor.h" +#include "SkFontHost_FreeType_common.h" +#include "SkFontMgr.h" +#include "SkFontStyle.h" +#include "SkStream.h" +#include "SkTDArray.h" +#include "SkTSearch.h" +#include "SkTypeface.h" +#include "SkTypefaceCache.h" + +#include <limits> +#include <stdlib.h> + +#ifndef SK_FONT_FILE_PREFIX +# define SK_FONT_FILE_PREFIX "/fonts/" +#endif + +#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 + +class SkTypeface_Android : public SkTypeface_FreeType { +public: + SkTypeface_Android(int index, + Style style, + bool isFixedPitch, + const SkString familyName) + : INHERITED(style, SkTypefaceCache::NewFontID(), isFixedPitch) + , fIndex(index) + , fFamilyName(familyName) + { } + + const SkString& name() const { return fFamilyName; } + +protected: + int fIndex; + SkString fFamilyName; + +private: + typedef SkTypeface_FreeType INHERITED; +}; + +class SkTypeface_AndroidSystem : public SkTypeface_Android { +public: + SkTypeface_AndroidSystem(const SkString pathName, + int index, + Style style, + bool isFixedPitch, + const SkString familyName) + : INHERITED(index, style, isFixedPitch, familyName) + , fPathName(pathName) + { } + + virtual void onGetFontDescriptor(SkFontDescriptor* desc, + bool* serialize) const SK_OVERRIDE + { + SkASSERT(desc); + SkASSERT(serialize); + desc->setFamilyName(fFamilyName.c_str()); + desc->setFontFileName(fPathName.c_str()); + *serialize = false; + } + virtual SkStream* onOpenStream(int* ttcIndex) const SK_OVERRIDE { + *ttcIndex = fIndex; + return SkStream::NewFromFile(fPathName.c_str()); + } + +private: + SkString fPathName; + + typedef SkTypeface_Android INHERITED; +}; + +class SkTypeface_AndroidStream : public SkTypeface_Android { +public: + SkTypeface_AndroidStream(SkStream* stream, + int index, + Style style, + bool isFixedPitch, + const SkString familyName) + : INHERITED(index, style, isFixedPitch, familyName) + , fStream(stream) + { } + + virtual void onGetFontDescriptor(SkFontDescriptor* desc, + bool* serialize) const SK_OVERRIDE { + SkASSERT(desc); + SkASSERT(serialize); + desc->setFamilyName(fFamilyName.c_str()); + desc->setFontFileName(NULL); + *serialize = true; + } + + virtual SkStream* onOpenStream(int* ttcIndex) const SK_OVERRIDE { + *ttcIndex = fIndex; + return fStream->duplicate(); + } + +private: + SkAutoTUnref<SkStream> fStream; + + typedef SkTypeface_Android INHERITED; +}; + +void get_path_for_sys_fonts(SkString* full, const SkString& name) { + full->set(getenv("ANDROID_ROOT")); + full->append(SK_FONT_FILE_PREFIX); + full->append(name); +} + +class SkFontStyleSet_Android : public SkFontStyleSet { +public: + explicit SkFontStyleSet_Android(FontFamily* family) : fFontFamily(family) { + // TODO? make this lazy + for (int i = 0; i < family->fFontFiles.count(); ++i) { + const SkString& fileName = family->fFontFiles[i].fFileName; + + SkString pathName; + get_path_for_sys_fonts(&pathName, fileName); + + SkAutoTUnref<SkStream> stream(SkStream::NewFromFile(pathName.c_str())); + if (!stream.get()) { + DEBUG_FONT(("---- SystemFonts[%d] file=%s (NOT EXIST)", i, filename.c_str())); + continue; + } + + SkString fontName; + SkTypeface::Style style; + bool isFixedWidth; + if (!SkTypeface_FreeType::ScanFont(stream.get(), family->fFontFiles[i].fIndex, + &fontName, &style, &isFixedWidth)) + { + DEBUG_FONT(("---- SystemFonts[%d] file=%s (INVALID)", i, filename.c_str())); + continue; + } + + fStyles.push_back().reset(SkNEW_ARGS(SkTypeface_AndroidSystem, + (pathName, 0, + style, isFixedWidth, fontName))); + } + } + + virtual int count() SK_OVERRIDE { + return fStyles.count(); + } + virtual void getStyle(int index, SkFontStyle* style, SkString* name) SK_OVERRIDE { + if (index < 0 || fStyles.count() <= index) { + return; + } + if (style) { + *style = this->style(index); + } + if (name) { + name->reset(); + } + } + virtual SkTypeface* createTypeface(int index) SK_OVERRIDE { + if (index < 0 || fStyles.count() <= index) { + return NULL; + } + return SkRef(fStyles[index].get()); + } + + /** Find the typeface in this style set that most closely matches the given pattern. + * TODO: consider replacing with SkStyleSet_Indirect::matchStyle(); + * this simpler version using match_score() passes all our tests. + */ + virtual SkTypeface* matchStyle(const SkFontStyle& pattern) SK_OVERRIDE { + if (0 == fStyles.count()) { + return NULL; + } + SkTypeface* closest = fStyles[0]; + int minScore = std::numeric_limits<int>::max(); + for (int i = 0; i < fStyles.count(); ++i) { + SkFontStyle style = this->style(i); + int score = match_score(pattern, style); + if (score < minScore) { + closest = fStyles[i]; + minScore = score; + } + } + return SkRef(closest); + } + +private: + SkFontStyle style(int index) { + return SkFontStyle(this->weight(index), SkFontStyle::kNormal_Width, + this->slant(index)); + } + SkFontStyle::Weight weight(int index) { + if (fStyles[index]->isBold()) return SkFontStyle::kBold_Weight; + return SkFontStyle::kNormal_Weight; + } + SkFontStyle::Slant slant(int index) { + if (fStyles[index]->isItalic()) return SkFontStyle::kItalic_Slant; + return SkFontStyle::kUpright_Slant; + } + static int match_score(const SkFontStyle& pattern, const SkFontStyle& candidate) { + int score = 0; + score += abs((pattern.width() - candidate.width()) * 100); + score += abs((pattern.isItalic() == candidate.isItalic()) ? 0 : 1000); + score += abs(pattern.weight() - candidate.weight()); + return score; + } + + + FontFamily* fFontFamily; + SkTArray<SkAutoTUnref<SkTypeface>, true> fStyles; + + friend struct NameToFamily; + friend class SkFontMgr_Android; + + typedef SkFontStyleSet INHERITED; +}; + +/** On Android a single family can have many names, but our API assumes unique names. + * Map names to the back end so that all names for a given family refer to the same + * (non-replicated) set of typefaces. + * SkTDict<> doesn't let us do index-based lookup, so we write our own mapping. + */ +struct NameToFamily { + SkString name; + SkFontStyleSet_Android* styleSet; +}; + +class SkFontMgr_Android : public SkFontMgr { +public: + SkFontMgr_Android() { + SkTDArray<FontFamily*> fontFamilies; + SkFontConfigParser::GetFontFamilies(fontFamilies); + this->buildNameToFamilyMap(fontFamilies); + this->findDefaultFont(); + } + +protected: + /** Returns not how many families we have, but how many unique names + * exist among the families. + */ + virtual int onCountFamilies() const SK_OVERRIDE { + return fNameToFamilyMap.count(); + } + + virtual void onGetFamilyName(int index, SkString* familyName) const SK_OVERRIDE { + if (index < 0 || fNameToFamilyMap.count() <= index) { + familyName->reset(); + return; + } + familyName->set(fNameToFamilyMap[index].name); + } + + virtual SkFontStyleSet* onCreateStyleSet(int index) const SK_OVERRIDE { + if (index < 0 || fNameToFamilyMap.count() <= index) { + return NULL; + } + return SkRef(fNameToFamilyMap[index].styleSet); + } + + virtual SkFontStyleSet* onMatchFamily(const char familyName[]) const SK_OVERRIDE { + if (!familyName) { + return NULL; + } + SkAutoAsciiToLC tolc(familyName); + for (int i = 0; i < fNameToFamilyMap.count(); ++i) { + if (fNameToFamilyMap[i].name.equals(tolc.lc())) { + return SkRef(fNameToFamilyMap[i].styleSet); + } + } + return NULL; + } + + virtual SkTypeface* onMatchFamilyStyle(const char familyName[], + const SkFontStyle& style) const SK_OVERRIDE { + SkAutoTUnref<SkFontStyleSet> sset(this->matchFamily(familyName)); + return sset->matchStyle(style); + } + + virtual SkTypeface* onMatchFaceStyle(const SkTypeface* typeface, + const SkFontStyle& style) const SK_OVERRIDE { + for (int i = 0; i < fFontStyleSets.count(); ++i) { + for (int j = 0; j < fFontStyleSets[i]->fStyles.count(); ++j) { + if (fFontStyleSets[i]->fStyles[j] == typeface) { + return fFontStyleSets[i]->matchStyle(style); + } + } + } + return NULL; + } + + virtual SkTypeface* onCreateFromData(SkData* data, int ttcIndex) const SK_OVERRIDE { + SkAutoTUnref<SkStream> stream(new SkMemoryStream(data)); + return this->createFromStream(stream, ttcIndex); + } + + virtual SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const SK_OVERRIDE { + SkAutoTUnref<SkStream> stream(SkStream::NewFromFile(path)); + return stream.get() ? this->createFromStream(stream, ttcIndex) : NULL; + } + + virtual SkTypeface* onCreateFromStream(SkStream* s, int ttcIndex) const SK_OVERRIDE { + SkAutoTUnref<SkStream> stream(s); + + bool isFixedPitch; + SkTypeface::Style style; + SkString name; + if (!SkTypeface_FreeType::ScanFont(stream, ttcIndex, &name, &style, &isFixedPitch)) { + return NULL; + } + return SkNEW_ARGS(SkTypeface_AndroidStream, (stream.detach(), ttcIndex, + style, isFixedPitch, name)); + } + + + virtual SkTypeface* onLegacyCreateTypeface(const char familyName[], + unsigned styleBits) const SK_OVERRIDE { + SkTypeface::Style oldStyle = (SkTypeface::Style)styleBits; + SkFontStyle style = SkFontStyle(oldStyle & SkTypeface::kBold + ? SkFontStyle::kBold_Weight + : SkFontStyle::kNormal_Weight, + SkFontStyle::kNormal_Width, + oldStyle & SkTypeface::kItalic + ? SkFontStyle::kItalic_Slant + : SkFontStyle::kUpright_Slant); + SkTypeface* tf = NULL; + + if (NULL != familyName) { + // On Android, we must return NULL when we can't find the requested + // named typeface so that the system/app can provide their own recovery + // mechanism. On other platforms we'd provide a typeface from the + // default family instead. + tf = this->onMatchFamilyStyle(familyName, style); + } else { + tf = fDefaultFamily->matchStyle(style); + } + + // TODO: double ref? qv matchStyle() + return SkSafeRef(tf); + } + + +private: + + SkTArray<SkAutoTUnref<SkFontStyleSet_Android>, true> fFontStyleSets; + SkFontStyleSet* fDefaultFamily; + SkTypeface* fDefaultTypeface; + + SkTDArray<NameToFamily> fNameToFamilyMap; + + void buildNameToFamilyMap(SkTDArray<FontFamily*> families) { + for (int i = 0; i < families.count(); i++) { + fFontStyleSets.push_back().reset( + SkNEW_ARGS(SkFontStyleSet_Android, (families[i]))); + for (int j = 0; j < families[i]->fNames.count(); j++) { + NameToFamily* nextEntry = fNameToFamilyMap.append(); + SkNEW_PLACEMENT_ARGS(&nextEntry->name, SkString, (families[i]->fNames[j])); + nextEntry->styleSet = fFontStyleSets.back().get(); + } + } + } + + void findDefaultFont() { + SkASSERT(!fFontStyleSets.empty()); + + static const char* gDefaultNames[] = { "sans-serif" }; + for (size_t i = 0; i < SK_ARRAY_COUNT(gDefaultNames); ++i) { + SkFontStyleSet* set = this->onMatchFamily(gDefaultNames[i]); + if (NULL == set) { + continue; + } + SkTypeface* tf = set->matchStyle(SkFontStyle()); + if (NULL == tf) { + continue; + } + fDefaultFamily = set; + fDefaultTypeface = tf; + break; + } + if (NULL == fDefaultTypeface) { + fDefaultFamily = fFontStyleSets[0]; + fDefaultTypeface = fDefaultFamily->createTypeface(0); + } + SkASSERT(fDefaultFamily); + SkASSERT(fDefaultTypeface); + } + + typedef SkFontMgr INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +SkFontMgr* SkFontMgr::Factory() { + return SkNEW(SkFontMgr_Android); +} |