Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(17)

Unified Diff: src/ports/SkFontMgr_android.cpp

Issue 384503002: WIP SkFontMgrAndroid (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Cleanup main file Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/ports/SkFontConfigParser_android.cpp ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/ports/SkFontMgr_android.cpp
diff --git a/src/ports/SkFontMgr_android.cpp b/src/ports/SkFontMgr_android.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f44f093b7a9f168563bdf60d33825b5b1c3156a7
--- /dev/null
+++ b/src/ports/SkFontMgr_android.cpp
@@ -0,0 +1,399 @@
+/*
+ * 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>
+
+// TODO: document isLocal in getFontDescriptor:
bungeman-skia 2014/07/21 15:48:00 We need to just change the name to 'shouldSerializ
bungeman-skia 2014/07/22 17:26:53 Acknowledged.
+// isLocal TRUE: this is a copy of the font local to my installation,
+// I need to serialize it.
+// isLocal FALSE: this font is globally available - installed as part
+// of the system image - so I can just refer to it and the recipient is
+// guaranteed to have it available.
+
+// TODO?
+bool find_name_and_attributes(SkStream* stream, SkString* name,
bungeman-skia 2014/07/21 15:48:00 This is now gone, replaced by SkTypeface_FreeType:
bungeman-skia 2014/07/22 17:26:52 Done.
+ SkTypeface::Style* style, bool* isFixedPitch);
+
+class SkTypeface_Android : public SkTypeface_FreeType {
+public:
+ SkTypeface_Android(Style style,
+ bool isFixedPitch,
+ const SkString familyName)
+ : INHERITED(style, SkTypefaceCache::NewFontID(), isFixedPitch)
+ , fFamilyName(familyName) {
+ }
+
+ const SkString& name () const { return fFamilyName; }
bungeman-skia 2014/07/21 15:48:00 nit: name()
bungeman-skia 2014/07/22 17:26:53 Done.
+
+protected:
+ SkString fFamilyName;
+
bungeman-skia 2014/07/21 15:48:00 We can put an fTtcIndex right here, and use it as
bungeman-skia 2014/07/22 17:26:53 Done.
+private:
+ typedef SkTypeface_FreeType INHERITED;
+};
+
+/// On Android, system fonts are installed on the device; the XML catalogs
+/// are read by SkFontConfigParser_android. We can't safely duplicate()
+/// them, so re-open them every time we need a fresh stream
bungeman-skia 2014/07/21 15:48:00 This comment (or at least most of it) seems more a
bungeman-skia 2014/07/22 17:26:53 Done.
+class SkTypeface_AndroidSystem : public SkTypeface_Android {
+public:
+ SkTypeface_AndroidSystem(Style style,
+ bool isFixedPitch,
+ const SkString pathName,
+ const SkString familyName)
+ : INHERITED(style, isFixedPitch, familyName)
+ , fPathName(pathName) {
+ }
+
+ void onGetFontDescriptor(SkFontDescriptor* desc, bool* isLocal)
+ const SK_OVERRIDE {
+ SkASSERT(desc);
+ SkASSERT(isLocal);
+ desc->setFamilyName(fFamilyName.c_str());
+ desc->setFontFileName(fPathName.c_str());
+ *isLocal = false;
+ }
+ SkStream* onOpenStream(int* ttcIndex) const SK_OVERRIDE {
+ *ttcIndex = 0; // TODO
+ return SkStream::NewFromFile(fPathName.c_str());
+ }
+
+private:
+ SkString fPathName;
+
+ typedef SkTypeface_Android INHERITED;
+};
+
+/// On Android, non-system fonts can be supplied through a SkStream,
+/// which we can cache in memory and duplicate() when necessary. We
+/// don't have a pathname for these fonts, however.
+class SkTypeface_AndroidStream : public SkTypeface_Android {
+public:
+ SkTypeface_AndroidStream(Style style,
+ bool isFixedPitch,
+ SkStream* stream,
+ const SkString familyName)
+ : INHERITED(style, isFixedPitch, familyName)
+ , fStream(stream) {
+ }
+
+ void onGetFontDescriptor(SkFontDescriptor* desc, bool* isLocal)
+ const SK_OVERRIDE {
+ SkASSERT(desc);
+ SkASSERT(isLocal);
+ desc->setFamilyName(fFamilyName.c_str());
+ desc->setFontFileName(NULL);
+ *isLocal = true;
+ }
+
+ SkStream* onOpenStream(int* ttcIndex) const SK_OVERRIDE {
+ *ttcIndex = 0; // TODO
+ return fStream->duplicate();
+ }
+
+private:
+ SkAutoTUnref<SkStream> fStream;
+
+ typedef SkTypeface_Android INHERITED;
+};
+
+// TODO - need another implementation of Typeface_Android to hold faces
+// created from Streams; that one can call duplicate().
bungeman-skia 2014/07/21 15:48:00 Looks like you've done this todo?
bungeman-skia 2014/07/22 17:26:53 Done.
+
+namespace {
+
+#ifndef SK_FONT_FILE_PREFIX
+#define SK_FONT_FILE_PREFIX "/fonts/"
+#endif
+
+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;
bungeman-skia 2014/07/21 15:48:00 We have SkTTCFHeader.h and can write some simple c
bungeman-skia 2014/07/22 17:26:53 Done.
+ SkString pathName;
+ get_path_for_sys_fonts(&pathName, fileName);
+
+ SkAutoTUnref<SkStream> stream(SkStream::NewFromFile(pathName.c_str()));
+ SkASSERT(stream.get());
+ bool isFixedWidth;
+ SkTypeface::Style style;
+ SkString fontName;
+ find_name_and_attributes(stream.get(), &fontName, &style, &isFixedWidth);
bungeman-skia 2014/07/21 15:48:00 We should check (at least assert or message) the r
bungeman-skia 2014/07/22 17:26:53 Done.
+ fStyles.push_back().reset(SkNEW_ARGS(SkTypeface_AndroidSystem,
+ (style, isFixedWidth,
+ pathName, fontName)));
+ }
+ }
+
+ int count() SK_OVERRIDE {
+ return fStyles.count();
+ }
+ void getStyle(int index, SkFontStyle* style, SkString* name) SK_OVERRIDE {
+ SkASSERT(index >= 0 && index < fStyles.count());
bungeman-skia 2014/07/21 15:48:00 Since this is public API, I think we should probab
bungeman-skia 2014/07/22 17:26:52 Done.
+ SkASSERT(style);
+ SkASSERT(name);
bungeman-skia 2014/07/21 15:48:01 These need to be like 'if (style) ...'. Any subset
bungeman-skia 2014/07/22 17:26:53 Done.
+ *style = this->style(index);
+ name->reset(); // UNDEFINED SEMANTICS?
bungeman-skia 2014/07/21 15:48:00 I think this is right for when it isn't supported
bungeman-skia 2014/07/22 17:26:52 Acknowledged.
+ }
+ SkTypeface* createTypeface(int index) SK_OVERRIDE {
+ SkASSERT(index >= 0 && index < fStyles.count());
+ 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.
+ 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:
+ /// On Android: 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 {
+ SkASSERT(index >= 0 && index < fNameToFamilyMap.count());
+ familyName->set(fNameToFamilyMap[index].name);
+ }
+ virtual SkFontStyleSet* onCreateStyleSet(int index) const SK_OVERRIDE {
+ SkASSERT(index >= 0 && index < fNameToFamilyMap.count());
+ 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* onCreateFromStream(SkStream* stream, int ttcIndex) const SK_OVERRIDE {
+ if (NULL == stream || stream->getLength() <= 0) {
+ SkDELETE(stream);
+ return NULL;
+ }
+
+ bool isFixedPitch;
+ SkTypeface::Style style;
+ SkString name;
+ if (find_name_and_attributes(stream, &name, &style, &isFixedPitch)) {
+ return SkNEW_ARGS(SkTypeface_AndroidStream,
+ (style, isFixedPitch, stream, name));
+ } else {
+ return NULL;
+ }
+ }
+
+ 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* 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 = gDefaultFamily->matchStyle(style);
+ }
+
+ // TODO: double ref? qv matchStyle()
+ return SkSafeRef(tf);
+ }
+
+
+private:
+
+ SkTArray<SkAutoTUnref<SkFontStyleSet_Android>, true> fFontStyleSets;
+ SkFontStyleSet* gDefaultFamily;
+ SkTypeface* gDefaultTypeface;
+
+ 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()); // TODO?
+
+ // TODO: hardwired list of names - should there be more?
+ // TODO: Previous version was NULL-terminated; why??
+ // Currently passing NULL to onMatchFamily crashes.
+ static const char* gDefaultNames[] = { "Droid Sans" };
+ 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;
+ }
+ gDefaultFamily = set;
+ gDefaultTypeface = tf;
+ break;
+ }
+ if (NULL == gDefaultTypeface) {
+ gDefaultFamily = fFontStyleSets[0];
+ gDefaultTypeface = gDefaultFamily->createTypeface(0);
+ }
+ SkASSERT(gDefaultFamily);
+ SkASSERT(gDefaultTypeface);
+ }
+
+ typedef SkFontMgr INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkFontMgr* SkFontMgr::Factory() {
+ return SkNEW(SkFontMgr_Android);
+}
+
+
+
« no previous file with comments | « src/ports/SkFontConfigParser_android.cpp ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698