1 /*
2 * Copyright 2014 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "SkTypes.h"
9
10 #include "SkData.h"
11 #include "SkFixed.h"
12 #include "SkFontDescriptor.h"
13 #include "SkFontHost_FreeType_common.h"
14 #include "SkFontMgr.h"
15 #include "SkFontMgr_android.h"
16 #include "SkFontMgr_android_parser.h"
17 #include "SkFontStyle.h"
18 #include "SkOSFile.h"
19 #include "SkPaint.h"
20 #include "SkRefCnt.h"
21 #include "SkString.h"
22 #include "SkStream.h"
23 #include "SkTArray.h"
24 #include "SkTDArray.h"
25 #include "SkTSearch.h"
26 #include "SkTemplates.h"
27 #include "SkTypefaceCache.h"
28
29 #include <limits>
30
31 class SkData;
32
33 class SkTypeface_Android : public SkTypeface_FreeType {
34 public:
SkTypeface_Android(const SkFontStyle & style,bool isFixedPitch,const SkString & familyName)35 SkTypeface_Android(const SkFontStyle& style,
36 bool isFixedPitch,
37 const SkString& familyName)
38 : INHERITED(style, SkTypefaceCache::NewFontID(), isFixedPitch)
39 , fFamilyName(familyName)
40 { }
41
42 protected:
onGetFamilyName(SkString * familyName) const43 void onGetFamilyName(SkString* familyName) const override {
44 *familyName = fFamilyName;
45 }
46
47 SkString fFamilyName;
48
49 private:
50 typedef SkTypeface_FreeType INHERITED;
51 };
52
53 class SkTypeface_AndroidSystem : public SkTypeface_Android {
54 public:
SkTypeface_AndroidSystem(const SkString & pathName,const bool cacheFontFiles,int index,const SkFixed * axes,int axesCount,const SkFontStyle & style,bool isFixedPitch,const SkString & familyName,const SkLanguage & lang,FontVariant variantStyle)55 SkTypeface_AndroidSystem(const SkString& pathName,
56 const bool cacheFontFiles,
57 int index,
58 const SkFixed* axes, int axesCount,
59 const SkFontStyle& style,
60 bool isFixedPitch,
61 const SkString& familyName,
62 const SkLanguage& lang,
63 FontVariant variantStyle)
64 : INHERITED(style, isFixedPitch, familyName)
65 , fPathName(pathName)
66 , fIndex(index)
67 , fAxes(axes, axesCount)
68 , fLang(lang)
69 , fVariantStyle(variantStyle)
70 , fFile(cacheFontFiles ? sk_fopen(fPathName.c_str(), kRead_SkFILE_Flag) : nullptr) {
71 if (cacheFontFiles) {
72 SkASSERT(fFile);
73 }
74 }
75
createStream() const76 SkStreamAsset* createStream() const {
77 if (fFile) {
78 SkData* data = SkData::NewFromFILE(fFile);
79 return data ? new SkMemoryStream(data) : nullptr;
80 }
81 return SkStream::NewFromFile(fPathName.c_str());
82 }
83
onGetFontDescriptor(SkFontDescriptor * desc,bool * serialize) const84 virtual void onGetFontDescriptor(SkFontDescriptor* desc, bool* serialize) const override {
85 SkASSERT(desc);
86 SkASSERT(serialize);
87 desc->setFamilyName(fFamilyName.c_str());
88 *serialize = false;
89 }
onOpenStream(int * ttcIndex) const90 SkStreamAsset* onOpenStream(int* ttcIndex) const override {
91 *ttcIndex = fIndex;
92 return this->createStream();
93 }
onCreateFontData() const94 SkFontData* onCreateFontData() const override {
95 return new SkFontData(this->createStream(), fIndex, fAxes.begin(), fAxes.count());
96 }
97
98 const SkString fPathName;
99 int fIndex;
100 const SkSTArray<4, SkFixed, true> fAxes;
101 const SkLanguage fLang;
102 const FontVariant fVariantStyle;
103 SkAutoTCallVProc<FILE, sk_fclose> fFile;
104
105 typedef SkTypeface_Android INHERITED;
106 };
107
108 class SkTypeface_AndroidStream : public SkTypeface_Android {
109 public:
SkTypeface_AndroidStream(SkFontData * data,const SkFontStyle & style,bool isFixedPitch,const SkString & familyName)110 SkTypeface_AndroidStream(SkFontData* data,
111 const SkFontStyle& style,
112 bool isFixedPitch,
113 const SkString& familyName)
114 : INHERITED(style, isFixedPitch, familyName)
115 , fData(data)
116 { }
117
onGetFontDescriptor(SkFontDescriptor * desc,bool * serialize) const118 virtual void onGetFontDescriptor(SkFontDescriptor* desc,
119 bool* serialize) const override {
120 SkASSERT(desc);
121 SkASSERT(serialize);
122 desc->setFamilyName(fFamilyName.c_str());
123 *serialize = true;
124 }
125
onOpenStream(int * ttcIndex) const126 SkStreamAsset* onOpenStream(int* ttcIndex) const override {
127 *ttcIndex = fData->getIndex();
128 return fData->duplicateStream();
129 }
130
onCreateFontData() const131 SkFontData* onCreateFontData() const override {
132 return new SkFontData(*fData.get());
133 }
134
135 private:
136 const SkAutoTDelete<const SkFontData> fData;
137 typedef SkTypeface_Android INHERITED;
138 };
139
140 class SkFontStyleSet_Android : public SkFontStyleSet {
141 typedef SkTypeface_FreeType::Scanner Scanner;
142
143 public:
SkFontStyleSet_Android(const FontFamily & family,const Scanner & scanner,const bool cacheFontFiles)144 explicit SkFontStyleSet_Android(const FontFamily& family, const Scanner& scanner,
145 const bool cacheFontFiles) {
146 const SkString* cannonicalFamilyName = nullptr;
147 if (family.fNames.count() > 0) {
148 cannonicalFamilyName = &family.fNames[0];
149 }
150 // TODO? make this lazy
151 for (int i = 0; i < family.fFonts.count(); ++i) {
152 const FontFileInfo& fontFile = family.fFonts[i];
153
154 SkString pathName(family.fBasePath);
155 pathName.append(fontFile.fFileName);
156
157 SkAutoTDelete<SkStream> stream(SkStream::NewFromFile(pathName.c_str()));
158 if (!stream.get()) {
159 SkDEBUGF(("Requested font file %s does not exist or cannot be opened.\n",
160 pathName.c_str()));
161 continue;
162 }
163
164 const int ttcIndex = fontFile.fIndex;
165 SkString familyName;
166 SkFontStyle style;
167 bool isFixedWidth;
168 Scanner::AxisDefinitions axisDefinitions;
169 if (!scanner.scanFont(stream.get(), ttcIndex,
170 &familyName, &style, &isFixedWidth, &axisDefinitions))
171 {
172 SkDEBUGF(("Requested font file %s exists, but is not a valid font.\n",
173 pathName.c_str()));
174 continue;
175 }
176
177 int weight = fontFile.fWeight != 0 ? fontFile.fWeight : style.weight();
178 SkFontStyle::Slant slant = style.slant();
179 switch (fontFile.fStyle) {
180 case FontFileInfo::Style::kAuto: slant = style.slant(); break;
181 case FontFileInfo::Style::kNormal: slant = SkFontStyle::kUpright_Slant; break;
182 case FontFileInfo::Style::kItalic: slant = SkFontStyle::kItalic_Slant; break;
183 default: SkASSERT(false); break;
184 }
185 style = SkFontStyle(weight, style.width(), slant);
186
187 const SkLanguage& lang = family.fLanguage;
188 uint32_t variant = family.fVariant;
189 if (kDefault_FontVariant == variant) {
190 variant = kCompact_FontVariant | kElegant_FontVariant;
191 }
192
193 // The first specified family name overrides the family name found in the font.
194 // TODO: SkTypeface_AndroidSystem::onCreateFamilyNameIterator should return
195 // all of the specified family names in addition to the names found in the font.
196 if (cannonicalFamilyName != nullptr) {
197 familyName = *cannonicalFamilyName;
198 }
199
200 SkAutoSTMalloc<4, SkFixed> axisValues(axisDefinitions.count());
201 Scanner::computeAxisValues(axisDefinitions,
202 fontFile.fAxes.begin(), fontFile.fAxes.count(),
203 axisValues, familyName);
204
205 fStyles.push_back().reset(new SkTypeface_AndroidSystem(
206 pathName, cacheFontFiles, ttcIndex, axisValues.get(), axisDefinitions.count(),
207 style, isFixedWidth, familyName, lang, variant));
208 }
209 }
210
count()211 int count() override {
212 return fStyles.count();
213 }
getStyle(int index,SkFontStyle * style,SkString * name)214 void getStyle(int index, SkFontStyle* style, SkString* name) override {
215 if (index < 0 || fStyles.count() <= index) {
216 return;
217 }
218 if (style) {
219 *style = this->style(index);
220 }
221 if (name) {
222 name->reset();
223 }
224 }
createTypeface(int index)225 SkTypeface_AndroidSystem* createTypeface(int index) override {
226 if (index < 0 || fStyles.count() <= index) {
227 return nullptr;
228 }
229 return SkRef(fStyles[index].get());
230 }
231
232 /** Find the typeface in this style set that most closely matches the given pattern.
233 * TODO: consider replacing with SkStyleSet_Indirect::matchStyle();
234 * this simpler version using match_score() passes all our tests.
235 */
matchStyle(const SkFontStyle & pattern)236 SkTypeface_AndroidSystem* matchStyle(const SkFontStyle& pattern) override {
237 if (0 == fStyles.count()) {
238 return nullptr;
239 }
240 SkTypeface_AndroidSystem* closest = fStyles[0];
241 int minScore = std::numeric_limits<int>::max();
242 for (int i = 0; i < fStyles.count(); ++i) {
243 SkFontStyle style = this->style(i);
244 int score = match_score(pattern, style);
245 if (score < minScore) {
246 closest = fStyles[i];
247 minScore = score;
248 }
249 }
250 return SkRef(closest);
251 }
252
253 private:
style(int index)254 SkFontStyle style(int index) {
255 return fStyles[index]->fontStyle();
256 }
match_score(const SkFontStyle & pattern,const SkFontStyle & candidate)257 static int match_score(const SkFontStyle& pattern, const SkFontStyle& candidate) {
258 int score = 0;
259 score += SkTAbs((pattern.width() - candidate.width()) * 100);
260 score += SkTAbs((pattern.isItalic() == candidate.isItalic()) ? 0 : 1000);
261 score += SkTAbs(pattern.weight() - candidate.weight());
262 return score;
263 }
264
265 SkTArray<SkAutoTUnref<SkTypeface_AndroidSystem>, true> fStyles;
266
267 friend struct NameToFamily;
268 friend class SkFontMgr_Android;
269
270 typedef SkFontStyleSet INHERITED;
271 };
272
273 /** On Android a single family can have many names, but our API assumes unique names.
274 * Map names to the back end so that all names for a given family refer to the same
275 * (non-replicated) set of typefaces.
276 * SkTDict<> doesn't let us do index-based lookup, so we write our own mapping.
277 */
278 struct NameToFamily {
279 SkString name;
280 SkFontStyleSet_Android* styleSet;
281 };
282
283 class SkFontMgr_Android : public SkFontMgr {
284 public:
SkFontMgr_Android(const SkFontMgr_Android_CustomFonts * custom)285 SkFontMgr_Android(const SkFontMgr_Android_CustomFonts* custom) {
286 SkTDArray<FontFamily*> families;
287 if (custom && SkFontMgr_Android_CustomFonts::kPreferSystem != custom->fSystemFontUse) {
288 SkString base(custom->fBasePath);
289 SkFontMgr_Android_Parser::GetCustomFontFamilies(
290 families, base, custom->fFontsXml, custom->fFallbackFontsXml);
291 }
292 if (!custom ||
293 (custom && SkFontMgr_Android_CustomFonts::kOnlyCustom != custom->fSystemFontUse))
294 {
295 SkFontMgr_Android_Parser::GetSystemFontFamilies(families);
296 }
297 if (custom && SkFontMgr_Android_CustomFonts::kPreferSystem == custom->fSystemFontUse) {
298 SkString base(custom->fBasePath);
299 SkFontMgr_Android_Parser::GetCustomFontFamilies(
300 families, base, custom->fFontsXml, custom->fFallbackFontsXml);
301 }
302 this->buildNameToFamilyMap(families, custom ? custom->fIsolated : false);
303 this->findDefaultFont();
304 families.deleteAll();
305 }
306
307 protected:
308 /** Returns not how many families we have, but how many unique names
309 * exist among the families.
310 */
onCountFamilies() const311 int onCountFamilies() const override {
312 return fNameToFamilyMap.count();
313 }
314
onGetFamilyName(int index,SkString * familyName) const315 void onGetFamilyName(int index, SkString* familyName) const override {
316 if (index < 0 || fNameToFamilyMap.count() <= index) {
317 familyName->reset();
318 return;
319 }
320 familyName->set(fNameToFamilyMap[index].name);
321 }
322
onCreateStyleSet(int index) const323 SkFontStyleSet* onCreateStyleSet(int index) const override {
324 if (index < 0 || fNameToFamilyMap.count() <= index) {
325 return nullptr;
326 }
327 return SkRef(fNameToFamilyMap[index].styleSet);
328 }
329
onMatchFamily(const char familyName[]) const330 SkFontStyleSet* onMatchFamily(const char familyName[]) const override {
331 if (!familyName) {
332 return nullptr;
333 }
334 SkAutoAsciiToLC tolc(familyName);
335 for (int i = 0; i < fNameToFamilyMap.count(); ++i) {
336 if (fNameToFamilyMap[i].name.equals(tolc.lc())) {
337 return SkRef(fNameToFamilyMap[i].styleSet);
338 }
339 }
340 // TODO: eventually we should not need to name fallback families.
341 for (int i = 0; i < fFallbackNameToFamilyMap.count(); ++i) {
342 if (fFallbackNameToFamilyMap[i].name.equals(tolc.lc())) {
343 return SkRef(fFallbackNameToFamilyMap[i].styleSet);
344 }
345 }
346 return nullptr;
347 }
348
onMatchFamilyStyle(const char familyName[],const SkFontStyle & style) const349 virtual SkTypeface* onMatchFamilyStyle(const char familyName[],
350 const SkFontStyle& style) const override {
351 SkAutoTUnref<SkFontStyleSet> sset(this->matchFamily(familyName));
352 return sset->matchStyle(style);
353 }
354
onMatchFaceStyle(const SkTypeface * typeface,const SkFontStyle & style) const355 virtual SkTypeface* onMatchFaceStyle(const SkTypeface* typeface,
356 const SkFontStyle& style) const override {
357 for (int i = 0; i < fFontStyleSets.count(); ++i) {
358 for (int j = 0; j < fFontStyleSets[i]->fStyles.count(); ++j) {
359 if (fFontStyleSets[i]->fStyles[j] == typeface) {
360 return fFontStyleSets[i]->matchStyle(style);
361 }
362 }
363 }
364 return nullptr;
365 }
366
find_family_style_character(const SkTDArray<NameToFamily> & fallbackNameToFamilyMap,const SkFontStyle & style,bool elegant,const SkString & langTag,SkUnichar character)367 static SkTypeface_AndroidSystem* find_family_style_character(
368 const SkTDArray<NameToFamily>& fallbackNameToFamilyMap,
369 const SkFontStyle& style, bool elegant,
370 const SkString& langTag, SkUnichar character)
371 {
372 for (int i = 0; i < fallbackNameToFamilyMap.count(); ++i) {
373 SkFontStyleSet_Android* family = fallbackNameToFamilyMap[i].styleSet;
374 SkAutoTUnref<SkTypeface_AndroidSystem> face(family->matchStyle(style));
375
376 if (!langTag.isEmpty() && !face->fLang.getTag().startsWith(langTag.c_str())) {
377 continue;
378 }
379
380 if (SkToBool(face->fVariantStyle & kElegant_FontVariant) != elegant) {
381 continue;
382 }
383
384 SkPaint paint;
385 paint.setTypeface(face);
386 paint.setTextEncoding(SkPaint::kUTF32_TextEncoding);
387
388 uint16_t glyphID;
389 paint.textToGlyphs(&character, sizeof(character), &glyphID);
390 if (glyphID != 0) {
391 return face.detach();
392 }
393 }
394 return nullptr;
395 }
396
onMatchFamilyStyleCharacter(const char familyName[],const SkFontStyle & style,const char * bcp47[],int bcp47Count,SkUnichar character) const397 virtual SkTypeface* onMatchFamilyStyleCharacter(const char familyName[],
398 const SkFontStyle& style,
399 const char* bcp47[],
400 int bcp47Count,
401 SkUnichar character) const override
402 {
403 // The variant 'elegant' is 'not squashed', 'compact' is 'stays in ascent/descent'.
404 // The variant 'default' means 'compact and elegant'.
405 // As a result, it is not possible to know the variant context from the font alone.
406 // TODO: add 'is_elegant' and 'is_compact' bits to 'style' request.
407
408 // The first time match anything elegant, second time anything not elegant.
409 for (int elegant = 2; elegant --> 0;) {
410 for (int bcp47Index = bcp47Count; bcp47Index --> 0;) {
411 SkLanguage lang(bcp47[bcp47Index]);
412 while (!lang.getTag().isEmpty()) {
413 SkTypeface_AndroidSystem* matchingTypeface =
414 find_family_style_character(fFallbackNameToFamilyMap,
415 style, SkToBool(elegant),
416 lang.getTag(), character);
417 if (matchingTypeface) {
418 return matchingTypeface;
419 }
420
421 lang = lang.getParent();
422 }
423 }
424 SkTypeface_AndroidSystem* matchingTypeface =
425 find_family_style_character(fFallbackNameToFamilyMap,
426 style, SkToBool(elegant),
427 SkString(), character);
428 if (matchingTypeface) {
429 return matchingTypeface;
430 }
431 }
432 return nullptr;
433 }
434
onCreateFromData(SkData * data,int ttcIndex) const435 SkTypeface* onCreateFromData(SkData* data, int ttcIndex) const override {
436 return this->createFromStream(new SkMemoryStream(data), ttcIndex);
437 }
438
onCreateFromFile(const char path[],int ttcIndex) const439 SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const override {
440 SkAutoTDelete<SkStreamAsset> stream(SkStream::NewFromFile(path));
441 return stream.get() ? this->createFromStream(stream.detach(), ttcIndex) : nullptr;
442 }
443
onCreateFromStream(SkStreamAsset * bareStream,int ttcIndex) const444 SkTypeface* onCreateFromStream(SkStreamAsset* bareStream, int ttcIndex) const override {
445 SkAutoTDelete<SkStreamAsset> stream(bareStream);
446 bool isFixedPitch;
447 SkFontStyle style;
448 SkString name;
449 if (!fScanner.scanFont(stream, ttcIndex, &name, &style, &isFixedPitch, nullptr)) {
450 return nullptr;
451 }
452 SkFontData* data(new SkFontData(stream.detach(), ttcIndex, nullptr, 0));
453 return new SkTypeface_AndroidStream(data, style, isFixedPitch, name);
454 }
455
onCreateFromStream(SkStreamAsset * s,const FontParameters & params) const456 SkTypeface* onCreateFromStream(SkStreamAsset* s, const FontParameters& params) const override {
457 using Scanner = SkTypeface_FreeType::Scanner;
458 SkAutoTDelete<SkStreamAsset> stream(s);
459 bool isFixedPitch;
460 SkFontStyle style;
461 SkString name;
462 Scanner::AxisDefinitions axisDefinitions;
463 if (!fScanner.scanFont(stream, params.getCollectionIndex(), &name, &style, &isFixedPitch,
464 &axisDefinitions))
465 {
466 return nullptr;
467 }
468
469 int paramAxisCount;
470 const FontParameters::Axis* paramAxes = params.getAxes(¶mAxisCount);
471 SkAutoSTMalloc<4, SkFixed> axisValues(axisDefinitions.count());
472 Scanner::computeAxisValues(axisDefinitions, paramAxes, paramAxisCount, axisValues, name);
473
474 SkFontData* data(new SkFontData(stream.detach(), params.getCollectionIndex(),
475 axisValues.get(), axisDefinitions.count()));
476 return new SkTypeface_AndroidStream(data, style, isFixedPitch, name);
477 }
478
onCreateFromFontData(SkFontData * data) const479 SkTypeface* onCreateFromFontData(SkFontData* data) const override {
480 SkStreamAsset* stream(data->getStream());
481 bool isFixedPitch;
482 SkFontStyle style;
483 SkString name;
484 if (!fScanner.scanFont(stream, data->getIndex(), &name, &style, &isFixedPitch, nullptr)) {
485 return nullptr;
486 }
487 return new SkTypeface_AndroidStream(data, style, isFixedPitch, name);
488 }
489
490
onLegacyCreateTypeface(const char familyName[],unsigned styleBits) const491 virtual SkTypeface* onLegacyCreateTypeface(const char familyName[],
492 unsigned styleBits) const override {
493 SkFontStyle style = SkFontStyle(styleBits);
494
495 if (familyName) {
496 // On Android, we must return nullptr when we can't find the requested
497 // named typeface so that the system/app can provide their own recovery
498 // mechanism. On other platforms we'd provide a typeface from the
499 // default family instead.
500 return this->onMatchFamilyStyle(familyName, style);
501 }
502 return fDefaultFamily->matchStyle(style);
503 }
504
505
506 private:
507
508 SkTypeface_FreeType::Scanner fScanner;
509
510 SkTArray<SkAutoTUnref<SkFontStyleSet_Android>, true> fFontStyleSets;
511 SkFontStyleSet* fDefaultFamily;
512 SkTypeface* fDefaultTypeface;
513
514 SkTDArray<NameToFamily> fNameToFamilyMap;
515 SkTDArray<NameToFamily> fFallbackNameToFamilyMap;
516
buildNameToFamilyMap(SkTDArray<FontFamily * > families,const bool isolated)517 void buildNameToFamilyMap(SkTDArray<FontFamily*> families, const bool isolated) {
518 for (int i = 0; i < families.count(); i++) {
519 FontFamily& family = *families[i];
520
521 SkTDArray<NameToFamily>* nameToFamily = &fNameToFamilyMap;
522 if (family.fIsFallbackFont) {
523 nameToFamily = &fFallbackNameToFamilyMap;
524
525 if (0 == family.fNames.count()) {
526 SkString& fallbackName = family.fNames.push_back();
527 fallbackName.printf("%.2x##fallback", i);
528 }
529 }
530
531 SkFontStyleSet_Android* newSet = new SkFontStyleSet_Android(family, fScanner, isolated);
532 if (0 == newSet->count()) {
533 delete newSet;
534 continue;
535 }
536 fFontStyleSets.push_back().reset(newSet);
537
538 for (int j = 0; j < family.fNames.count(); j++) {
539 NameToFamily* nextEntry = nameToFamily->append();
540 new (&nextEntry->name) SkString(family.fNames[j]);
541 nextEntry->styleSet = newSet;
542 }
543 }
544 }
545
findDefaultFont()546 void findDefaultFont() {
547 SkASSERT(!fFontStyleSets.empty());
548
549 static const char* gDefaultNames[] = { "sans-serif" };
550 for (size_t i = 0; i < SK_ARRAY_COUNT(gDefaultNames); ++i) {
551 SkFontStyleSet* set = this->onMatchFamily(gDefaultNames[i]);
552 if (nullptr == set) {
553 continue;
554 }
555 SkTypeface* tf = set->matchStyle(SkFontStyle());
556 if (nullptr == tf) {
557 continue;
558 }
559 fDefaultFamily = set;
560 fDefaultTypeface = tf;
561 break;
562 }
563 if (nullptr == fDefaultTypeface) {
564 fDefaultFamily = fFontStyleSets[0];
565 fDefaultTypeface = fDefaultFamily->createTypeface(0);
566 }
567 SkASSERT(fDefaultFamily);
568 SkASSERT(fDefaultTypeface);
569 }
570
571 typedef SkFontMgr INHERITED;
572 };
573
574 #ifdef SK_DEBUG
575 static char const * const gSystemFontUseStrings[] = {
576 "OnlyCustom", "PreferCustom", "PreferSystem"
577 };
578 #endif
SkFontMgr_New_Android(const SkFontMgr_Android_CustomFonts * custom)579 SkFontMgr* SkFontMgr_New_Android(const SkFontMgr_Android_CustomFonts* custom) {
580 if (custom) {
581 SkASSERT(0 <= custom->fSystemFontUse);
582 SkASSERT(custom->fSystemFontUse < SK_ARRAY_COUNT(gSystemFontUseStrings));
583 SkDEBUGF(("SystemFontUse: %s BasePath: %s Fonts: %s FallbackFonts: %s\n",
584 gSystemFontUseStrings[custom->fSystemFontUse],
585 custom->fBasePath,
586 custom->fFontsXml,
587 custom->fFallbackFontsXml));
588 }
589
590 return new SkFontMgr_Android(custom);
591 }
592