blob: 7382d00b4546ab0cc8317db1b9bd137d52916739 [file] [log] [blame]
djsollen@google.combfae9d32013-05-21 16:53:50 +00001
2/*
3 * Copyright 2013 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9#include "SkFontConfigInterface.h"
10#include "SkTypeface_android.h"
11
12#include "SkFontConfigParser_android.h"
13#include "SkFontConfigTypeface.h"
14#include "SkFontMgr.h"
15#include "SkGlyphCache.h"
16#include "SkPaint.h"
djsollen@google.combfae9d32013-05-21 16:53:50 +000017#include "SkString.h"
18#include "SkStream.h"
19#include "SkThread.h"
20#include "SkTypefaceCache.h"
21#include "SkTArray.h"
22#include "SkTDict.h"
23#include "SkTSearch.h"
24
25#include <stdio.h>
26#include <string.h>
27
28#ifndef SK_DEBUG_FONTS
29 #define SK_DEBUG_FONTS 0
30#endif
31
32#if SK_DEBUG_FONTS
33 #define DEBUG_FONT(args) SkDebugf args
34#else
35 #define DEBUG_FONT(args)
36#endif
37
38///////////////////////////////////////////////////////////////////////////////
39
40// For test only.
41static const char* gTestMainConfigFile = NULL;
42static const char* gTestFallbackConfigFile = NULL;
43static const char* gTestFontFilePrefix = NULL;
44
45///////////////////////////////////////////////////////////////////////////////
46
djsollen@google.com40078cb2013-05-24 20:31:57 +000047typedef int32_t FontRecID;
48#define INVALID_FONT_REC_ID -1
49
50typedef int32_t FamilyRecID;
51#define INVALID_FAMILY_REC_ID -1
52
djsollen@google.combfae9d32013-05-21 16:53:50 +000053// used to record our notion of the pre-existing fonts
54struct FontRec {
55 SkRefPtr<SkTypeface> fTypeface;
56 SkString fFileName;
57 SkTypeface::Style fStyle;
djsollen@google.combfae9d32013-05-21 16:53:50 +000058 bool fIsValid;
djsollen@google.com40078cb2013-05-24 20:31:57 +000059 FamilyRecID fFamilyRecID;
djsollen@google.combfae9d32013-05-21 16:53:50 +000060};
61
djsollen@google.combfae9d32013-05-21 16:53:50 +000062struct FamilyRec {
63 FamilyRec() {
64 memset(fFontRecID, INVALID_FONT_REC_ID, sizeof(fFontRecID));
65 }
66
67 static const int FONT_STYLE_COUNT = 4;
68 FontRecID fFontRecID[FONT_STYLE_COUNT];
djsollen@google.comb27eba72013-09-06 12:59:50 +000069 bool fIsFallbackFont;
djsollen@google.com39a7c702013-09-24 20:08:47 +000070 SkString fFallbackName;
djsollen@google.comb27eba72013-09-06 12:59:50 +000071 SkPaintOptionsAndroid fPaintOptions;
djsollen@google.combfae9d32013-05-21 16:53:50 +000072};
73
djsollen@google.combfae9d32013-05-21 16:53:50 +000074
djsollen@google.comb27eba72013-09-06 12:59:50 +000075typedef SkTDArray<FamilyRecID> FallbackFontList;
djsollen@google.combfae9d32013-05-21 16:53:50 +000076
77class SkFontConfigInterfaceAndroid : public SkFontConfigInterface {
78public:
79 SkFontConfigInterfaceAndroid(SkTDArray<FontFamily*>& fontFamilies);
80 virtual ~SkFontConfigInterfaceAndroid();
81
82 virtual bool matchFamilyName(const char familyName[],
83 SkTypeface::Style requested,
84 FontIdentity* outFontIdentifier,
85 SkString* outFamilyName,
86 SkTypeface::Style* outStyle) SK_OVERRIDE;
87 virtual SkStream* openStream(const FontIdentity&) SK_OVERRIDE;
88
89 // new APIs
90 virtual SkDataTable* getFamilyNames() SK_OVERRIDE;
91 virtual bool matchFamilySet(const char inFamilyName[],
92 SkString* outFamilyName,
93 SkTArray<FontIdentity>*) SK_OVERRIDE;
94
95 /**
96 * Get the family name of the font in the default fallback font list that
97 * contains the specified chararacter. if no font is found, returns false.
98 */
djsollen@google.com9902c382013-09-19 18:22:30 +000099 bool getFallbackFamilyNameForChar(SkUnichar uni, const char* lang, SkString* name);
djsollen@google.combfae9d32013-05-21 16:53:50 +0000100 /**
101 *
102 */
103 SkTypeface* getTypefaceForChar(SkUnichar uni, SkTypeface::Style style,
104 SkPaintOptionsAndroid::FontVariant fontVariant);
105 SkTypeface* nextLogicalTypeface(SkFontID currFontID, SkFontID origFontID,
106 const SkPaintOptionsAndroid& options);
107
108private:
djsollen@google.comb27eba72013-09-06 12:59:50 +0000109 void addFallbackFamily(FamilyRecID fontRecID);
djsollen@google.com40078cb2013-05-24 20:31:57 +0000110 SkTypeface* getTypefaceForFontRec(FontRecID fontRecID);
djsollen@google.com9a70f342013-06-25 18:07:45 +0000111 FallbackFontList* getCurrentLocaleFallbackFontList();
djsollen@google.com40078cb2013-05-24 20:31:57 +0000112 FallbackFontList* findFallbackFontList(const SkLanguage& lang, bool isOriginal = true);
djsollen@google.combfae9d32013-05-21 16:53:50 +0000113
114 SkTArray<FontRec> fFonts;
115 SkTArray<FamilyRec> fFontFamilies;
116 SkTDict<FamilyRecID> fFamilyNameDict;
117 FamilyRecID fDefaultFamilyRecID;
118
119 // (SkLanguage)<->(fallback chain index) translation
120 SkTDict<FallbackFontList*> fFallbackFontDict;
djsollen@google.com40078cb2013-05-24 20:31:57 +0000121 SkTDict<FallbackFontList*> fFallbackFontAliasDict;
djsollen@google.combfae9d32013-05-21 16:53:50 +0000122 FallbackFontList fDefaultFallbackList;
djsollen@google.com9a70f342013-06-25 18:07:45 +0000123
124 // fallback info for current locale
125 SkString fCachedLocale;
126 FallbackFontList* fLocaleFallbackFontList;
djsollen@google.combfae9d32013-05-21 16:53:50 +0000127};
128
129///////////////////////////////////////////////////////////////////////////////
130
131static SkFontConfigInterfaceAndroid* getSingletonInterface() {
132 SK_DECLARE_STATIC_MUTEX(gMutex);
133 static SkFontConfigInterfaceAndroid* gFontConfigInterface;
134
135 SkAutoMutexAcquire ac(gMutex);
136 if (NULL == gFontConfigInterface) {
137 // load info from a configuration file that we can use to populate the
138 // system/fallback font structures
139 SkTDArray<FontFamily*> fontFamilies;
140 if (!gTestMainConfigFile) {
141 SkFontConfigParser::GetFontFamilies(fontFamilies);
142 } else {
143 SkFontConfigParser::GetTestFontFamilies(fontFamilies, gTestMainConfigFile,
144 gTestFallbackConfigFile);
145 }
146
147 gFontConfigInterface = new SkFontConfigInterfaceAndroid(fontFamilies);
148
149 // cleanup the data we received from the parser
150 fontFamilies.deleteAll();
151 }
152 return gFontConfigInterface;
153}
154
155SkFontConfigInterface* SkFontConfigInterface::GetSingletonDirectInterface() {
156 return getSingletonInterface();
157}
158
159///////////////////////////////////////////////////////////////////////////////
160
161static bool has_font(const SkTArray<FontRec>& array, const SkString& filename) {
162 for (int i = 0; i < array.count(); i++) {
163 if (array[i].fFileName == filename) {
164 return true;
165 }
166 }
167 return false;
168}
169
170#ifndef SK_FONT_FILE_PREFIX
171 #define SK_FONT_FILE_PREFIX "/fonts/"
172#endif
173
174static void get_path_for_sys_fonts(SkString* full, const char name[]) {
175 if (gTestFontFilePrefix) {
176 full->set(gTestFontFilePrefix);
177 } else {
178 full->set(getenv("ANDROID_ROOT"));
179 full->append(SK_FONT_FILE_PREFIX);
180 }
181 full->append(name);
182}
183
184static void insert_into_name_dict(SkTDict<FamilyRecID>& familyNameDict,
185 const char* name, FamilyRecID familyRecID) {
186 SkAutoAsciiToLC tolc(name);
djsollen@google.com92e3f082013-08-27 17:40:03 +0000187 if (familyNameDict.find(tolc.lc())) {
188 SkDebugf("---- system font attempting to use a the same name [%s] for"
189 "multiple families. skipping subsequent occurrences", tolc.lc());
190 } else {
191 familyNameDict.set(tolc.lc(), familyRecID);
192 }
djsollen@google.combfae9d32013-05-21 16:53:50 +0000193}
194
195// Defined in SkFontHost_FreeType.cpp
196bool find_name_and_attributes(SkStream* stream, SkString* name,
197 SkTypeface::Style* style, bool* isFixedWidth);
198
199///////////////////////////////////////////////////////////////////////////////
200
201SkFontConfigInterfaceAndroid::SkFontConfigInterfaceAndroid(SkTDArray<FontFamily*>& fontFamilies) :
202 fFonts(fontFamilies.count()),
203 fFontFamilies(fontFamilies.count() / FamilyRec::FONT_STYLE_COUNT),
204 fFamilyNameDict(1024),
205 fDefaultFamilyRecID(INVALID_FAMILY_REC_ID),
djsollen@google.com40078cb2013-05-24 20:31:57 +0000206 fFallbackFontDict(128),
djsollen@google.com9a70f342013-06-25 18:07:45 +0000207 fFallbackFontAliasDict(128),
208 fLocaleFallbackFontList(NULL) {
djsollen@google.combfae9d32013-05-21 16:53:50 +0000209
210 for (int i = 0; i < fontFamilies.count(); ++i) {
211 FontFamily* family = fontFamilies[i];
212
213 // defer initializing the familyRec until we can be sure that at least
214 // one of it's children contains a valid font file
215 FamilyRec* familyRec = NULL;
216 FamilyRecID familyRecID = INVALID_FAMILY_REC_ID;
217
218 for (int j = 0; j < family->fFontFiles.count(); ++j) {
219 SkString filename;
220 get_path_for_sys_fonts(&filename, family->fFontFiles[j]->fFileName);
221
222 if (has_font(fFonts, filename)) {
223 SkDebugf("---- system font and fallback font files specify a duplicate "
224 "font %s, skipping the second occurrence", filename.c_str());
225 continue;
226 }
227
228 FontRec& fontRec = fFonts.push_back();
229 fontRec.fFileName = filename;
230 fontRec.fStyle = SkTypeface::kNormal;
djsollen@google.combfae9d32013-05-21 16:53:50 +0000231 fontRec.fIsValid = false;
djsollen@google.com40078cb2013-05-24 20:31:57 +0000232 fontRec.fFamilyRecID = familyRecID;
djsollen@google.combfae9d32013-05-21 16:53:50 +0000233
234 const FontRecID fontRecID = fFonts.count() - 1;
235
236 SkAutoTUnref<SkStream> stream(SkStream::NewFromFile(filename.c_str()));
237 if (stream.get() != NULL) {
238 bool isFixedWidth;
239 SkString name;
240 fontRec.fIsValid = find_name_and_attributes(stream.get(), &name,
241 &fontRec.fStyle, &isFixedWidth);
242 } else {
djsollen@google.comb27eba72013-09-06 12:59:50 +0000243 if (!family->fIsFallbackFont) {
djsollen@google.combfae9d32013-05-21 16:53:50 +0000244 SkDebugf("---- failed to open <%s> as a font\n", filename.c_str());
245 }
246 }
247
248 if (fontRec.fIsValid) {
249 DEBUG_FONT(("---- SystemFonts[%d][%d] fallback=%d file=%s",
djsollen@google.com9902c382013-09-19 18:22:30 +0000250 i, fFonts.count() - 1, family->fIsFallbackFont, filename.c_str()));
djsollen@google.combfae9d32013-05-21 16:53:50 +0000251 } else {
252 DEBUG_FONT(("---- SystemFonts[%d][%d] fallback=%d file=%s (INVALID)",
djsollen@google.com9902c382013-09-19 18:22:30 +0000253 i, fFonts.count() - 1, family->fIsFallbackFont, filename.c_str()));
djsollen@google.combfae9d32013-05-21 16:53:50 +0000254 continue;
255 }
256
257 // create a familyRec now that we know that at least one font in
258 // the family is valid
259 if (familyRec == NULL) {
260 familyRec = &fFontFamilies.push_back();
261 familyRecID = fFontFamilies.count() - 1;
djsollen@google.com40078cb2013-05-24 20:31:57 +0000262 fontRec.fFamilyRecID = familyRecID;
djsollen@google.comb27eba72013-09-06 12:59:50 +0000263
264 familyRec->fIsFallbackFont = family->fIsFallbackFont;
265 familyRec->fPaintOptions = family->fFontFiles[j]->fPaintOptions;
266
djsollen@google.comb27eba72013-09-06 12:59:50 +0000267 } else if (familyRec->fPaintOptions != family->fFontFiles[j]->fPaintOptions) {
268 SkDebugf("Every font file within a family must have identical"
269 "language and variant attributes");
270 sk_throw();
djsollen@google.combfae9d32013-05-21 16:53:50 +0000271 }
272
273 // add this font to the current familyRec
274 if (INVALID_FONT_REC_ID != familyRec->fFontRecID[fontRec.fStyle]) {
275 DEBUG_FONT(("Overwriting familyRec for style[%d] old,new:(%d,%d)",
276 fontRec.fStyle, familyRec->fFontRecID[fontRec.fStyle],
277 fontRecID));
278 }
279 familyRec->fFontRecID[fontRec.fStyle] = fontRecID;
djsollen@google.combfae9d32013-05-21 16:53:50 +0000280 }
281
djsollen@google.com39a7c702013-09-24 20:08:47 +0000282 if (familyRec) {
283 if (familyRec->fIsFallbackFont) {
284 // add the font to the appropriate fallback chains and also insert a
285 // unique name into the familyNameDict for internal usage
286 addFallbackFamily(familyRecID);
287 } else {
288 // add the names that map to this family to the dictionary for easy lookup
289 const SkTDArray<const char*>& names = family->fNames;
290 if (names.isEmpty()) {
291 SkDEBUGFAIL("ERROR: non-fallback font with no name");
292 continue;
293 }
djsollen@google.combfae9d32013-05-21 16:53:50 +0000294
djsollen@google.com39a7c702013-09-24 20:08:47 +0000295 for (int i = 0; i < names.count(); i++) {
296 insert_into_name_dict(fFamilyNameDict, names[i], familyRecID);
297 }
djsollen@google.combfae9d32013-05-21 16:53:50 +0000298 }
299 }
djsollen@google.combfae9d32013-05-21 16:53:50 +0000300 }
301
302 DEBUG_FONT(("---- We have %d system fonts", fFonts.count()));
303
304 if (fFontFamilies.count() > 0) {
305 fDefaultFamilyRecID = 0;
306 }
307
308 // scans the default fallback font chain, adding every entry to every other
309 // fallback font chain to which it does not belong. this results in every
310 // language-specific fallback font chain having all of its fallback fonts at
311 // the front of the chain, and everything else at the end.
312 FallbackFontList* fallbackList;
313 SkTDict<FallbackFontList*>::Iter iter(fFallbackFontDict);
314 const char* fallbackLang = iter.next(&fallbackList);
315 while(fallbackLang != NULL) {
316 for (int i = 0; i < fDefaultFallbackList.count(); i++) {
djsollen@google.comb27eba72013-09-06 12:59:50 +0000317 FamilyRecID familyRecID = fDefaultFallbackList[i];
318 const SkString& fontLang = fFontFamilies[familyRecID].fPaintOptions.getLanguage().getTag();
djsollen@google.combfae9d32013-05-21 16:53:50 +0000319 if (strcmp(fallbackLang, fontLang.c_str()) != 0) {
djsollen@google.comb27eba72013-09-06 12:59:50 +0000320 fallbackList->push(familyRecID);
djsollen@google.combfae9d32013-05-21 16:53:50 +0000321 }
322 }
323 // move to the next fallback list in the dictionary
324 fallbackLang = iter.next(&fallbackList);
325 }
326}
327
328SkFontConfigInterfaceAndroid::~SkFontConfigInterfaceAndroid() {
329 // iterate through and cleanup fFallbackFontDict
330 SkTDict<FallbackFontList*>::Iter iter(fFallbackFontDict);
331 FallbackFontList* fallbackList;
332 while(iter.next(&fallbackList) != NULL) {
333 SkDELETE(fallbackList);
334 }
335}
336
djsollen@google.comb27eba72013-09-06 12:59:50 +0000337void SkFontConfigInterfaceAndroid::addFallbackFamily(FamilyRecID familyRecID) {
338 SkASSERT(familyRecID < fFontFamilies.count());
djsollen@google.com39a7c702013-09-24 20:08:47 +0000339 FamilyRec& familyRec = fFontFamilies[familyRecID];
djsollen@google.comb27eba72013-09-06 12:59:50 +0000340 SkASSERT(familyRec.fIsFallbackFont);
djsollen@google.combfae9d32013-05-21 16:53:50 +0000341
djsollen@google.com39a7c702013-09-24 20:08:47 +0000342 // add the fallback family to the name dictionary. This is
343 // needed by getFallbackFamilyNameForChar() so that fallback
344 // families can be identified by a unique name. The unique
345 // identifier that we've chosen is the familyID in hex (e.g. '0F##fallback').
346 familyRec.fFallbackName.printf("%.2x##fallback", familyRecID);
347 insert_into_name_dict(fFamilyNameDict, familyRec.fFallbackName.c_str(), familyRecID);
348
djsollen@google.combfae9d32013-05-21 16:53:50 +0000349 // add to the default fallback list
djsollen@google.comb27eba72013-09-06 12:59:50 +0000350 fDefaultFallbackList.push(familyRecID);
djsollen@google.combfae9d32013-05-21 16:53:50 +0000351
352 // stop here if it is the default language tag
djsollen@google.comb27eba72013-09-06 12:59:50 +0000353 const SkString& languageTag = familyRec.fPaintOptions.getLanguage().getTag();
djsollen@google.combfae9d32013-05-21 16:53:50 +0000354 if (languageTag.isEmpty()) {
355 return;
356 }
357
358 // add to the appropriate language's custom fallback list
359 FallbackFontList* customList = NULL;
360 if (!fFallbackFontDict.find(languageTag.c_str(), &customList)) {
361 DEBUG_FONT(("---- Created fallback list for \"%s\"", languageTag.c_str()));
362 customList = SkNEW(FallbackFontList);
363 fFallbackFontDict.set(languageTag.c_str(), customList);
364 }
365 SkASSERT(customList != NULL);
djsollen@google.comb27eba72013-09-06 12:59:50 +0000366 customList->push(familyRecID);
djsollen@google.combfae9d32013-05-21 16:53:50 +0000367}
368
369
370static FontRecID find_best_style(const FamilyRec& family, SkTypeface::Style style) {
371
372 const FontRecID* fontRecIDs = family.fFontRecID;
373
374 if (fontRecIDs[style] != INVALID_FONT_REC_ID) { // exact match
375 return fontRecIDs[style];
376 }
377 // look for a matching bold
378 style = (SkTypeface::Style)(style ^ SkTypeface::kItalic);
379 if (fontRecIDs[style] != INVALID_FONT_REC_ID) {
380 return fontRecIDs[style];
381 }
382 // look for the plain
383 if (fontRecIDs[SkTypeface::kNormal] != INVALID_FONT_REC_ID) {
384 return fontRecIDs[SkTypeface::kNormal];
385 }
386 // look for anything
387 for (int i = 0; i < FamilyRec::FONT_STYLE_COUNT; i++) {
388 if (fontRecIDs[i] != INVALID_FONT_REC_ID) {
389 return fontRecIDs[i];
390 }
391 }
392 // should never get here, since the fontRecID list should not be empty
393 SkDEBUGFAIL("No valid fonts exist for this family");
394 return -1;
395}
396
397bool SkFontConfigInterfaceAndroid::matchFamilyName(const char familyName[],
398 SkTypeface::Style style,
399 FontIdentity* outFontIdentifier,
400 SkString* outFamilyName,
robertphillips@google.comb7457d02013-05-22 00:12:43 +0000401 SkTypeface::Style* outStyle) {
djsollen@google.combfae9d32013-05-21 16:53:50 +0000402 // clip to legal style bits
403 style = (SkTypeface::Style)(style & SkTypeface::kBoldItalic);
404
405 bool exactNameMatch = false;
406
407 FamilyRecID familyRecID = INVALID_FAMILY_REC_ID;
408 if (NULL != familyName) {
djsollen@google.com2e08f192013-05-21 20:08:10 +0000409 SkAutoAsciiToLC tolc(familyName);
410 if (fFamilyNameDict.find(tolc.lc(), &familyRecID)) {
djsollen@google.combfae9d32013-05-21 16:53:50 +0000411 exactNameMatch = true;
412 }
413 } else {
414 familyRecID = fDefaultFamilyRecID;
415
416 }
417
djsollen@google.com1f584ed2013-09-19 12:08:40 +0000418 // If no matching family name is found then return false. This allows clients
419 // to be able to search for other fonts instead of forcing them to use the
420 // default font.
djsollen@google.combfae9d32013-05-21 16:53:50 +0000421 if (INVALID_FAMILY_REC_ID == familyRecID) {
djsollen@google.com1f584ed2013-09-19 12:08:40 +0000422 return false;
djsollen@google.combfae9d32013-05-21 16:53:50 +0000423 }
424
425 FontRecID fontRecID = find_best_style(fFontFamilies[familyRecID], style);
426 FontRec& fontRec = fFonts[fontRecID];
427
428 if (NULL != outFontIdentifier) {
429 outFontIdentifier->fID = fontRecID;
430 outFontIdentifier->fTTCIndex = 0;
431 outFontIdentifier->fString.set(fontRec.fFileName);
432// outFontIdentifier->fStyle = fontRec.fStyle;
433 }
434
435 if (NULL != outFamilyName) {
436 if (exactNameMatch) {
437 outFamilyName->set(familyName);
438 } else {
439 // find familyName from list of names
440 const char* familyName = NULL;
djsollen@google.comab6eeb92013-05-21 17:15:27 +0000441 SkAssertResult(fFamilyNameDict.findKey(familyRecID, &familyName));
442 SkASSERT(familyName);
djsollen@google.combfae9d32013-05-21 16:53:50 +0000443 outFamilyName->set(familyName);
444 }
445 }
446
447 if (NULL != outStyle) {
448 *outStyle = fontRec.fStyle;
449 }
450
451 return true;
452}
453
robertphillips@google.comb7457d02013-05-22 00:12:43 +0000454SkStream* SkFontConfigInterfaceAndroid::openStream(const FontIdentity& identity) {
djsollen@google.combfae9d32013-05-21 16:53:50 +0000455 return SkStream::NewFromFile(identity.fString.c_str());
456}
457
robertphillips@google.comb7457d02013-05-22 00:12:43 +0000458SkDataTable* SkFontConfigInterfaceAndroid::getFamilyNames() {
djsollen@google.combfae9d32013-05-21 16:53:50 +0000459 SkTDArray<const char*> names;
460 SkTDArray<size_t> sizes;
461
462 SkTDict<FamilyRecID>::Iter iter(fFamilyNameDict);
463 const char* familyName = iter.next(NULL);
464 while(familyName != NULL) {
465 *names.append() = familyName;
466 *sizes.append() = strlen(familyName) + 1;
467
468 // move to the next familyName in the dictionary
469 familyName = iter.next(NULL);
470 }
471
472 return SkDataTable::NewCopyArrays((const void*const*)names.begin(),
473 sizes.begin(), names.count());
474}
475
476bool SkFontConfigInterfaceAndroid::matchFamilySet(const char inFamilyName[],
477 SkString* outFamilyName,
robertphillips@google.comb7457d02013-05-22 00:12:43 +0000478 SkTArray<FontIdentity>*) {
djsollen@google.combfae9d32013-05-21 16:53:50 +0000479 return false;
480}
481
djsollen@google.com40078cb2013-05-24 20:31:57 +0000482static bool find_proc(SkTypeface* face, SkTypeface::Style style, void* ctx) {
483 const FontRecID* fontRecID = (const FontRecID*)ctx;
484 FontRecID currFontRecID = ((FontConfigTypeface*)face)->getIdentity().fID;
485 return currFontRecID == *fontRecID;
486}
487
488SkTypeface* SkFontConfigInterfaceAndroid::getTypefaceForFontRec(FontRecID fontRecID) {
489 FontRec& fontRec = fFonts[fontRecID];
djsollen@google.combfae9d32013-05-21 16:53:50 +0000490 SkTypeface* face = fontRec.fTypeface.get();
491 if (!face) {
djsollen@google.com40078cb2013-05-24 20:31:57 +0000492 // look for it in the typeface cache
493 face = SkTypefaceCache::FindByProcAndRef(find_proc, &fontRecID);
djsollen@google.combfae9d32013-05-21 16:53:50 +0000494
495 // if it is not in the cache then create it
djsollen@google.com40078cb2013-05-24 20:31:57 +0000496 if (!face) {
497 const char* familyName = NULL;
498 SkAssertResult(fFamilyNameDict.findKey(fontRec.fFamilyRecID, &familyName));
499 SkASSERT(familyName);
500 face = SkTypeface::CreateFromName(familyName, fontRec.fStyle);
501 }
djsollen@google.combfae9d32013-05-21 16:53:50 +0000502
503 // store the result for subsequent lookups
504 fontRec.fTypeface = face;
505 }
506 SkASSERT(face);
507 return face;
508}
509
djsollen@google.com9902c382013-09-19 18:22:30 +0000510bool SkFontConfigInterfaceAndroid::getFallbackFamilyNameForChar(SkUnichar uni,
511 const char* lang,
512 SkString* name) {
513 FallbackFontList* fallbackFontList = this->findFallbackFontList(lang);
djsollen@google.com9a70f342013-06-25 18:07:45 +0000514 for (int i = 0; i < fallbackFontList->count(); i++) {
djsollen@google.comb27eba72013-09-06 12:59:50 +0000515 FamilyRecID familyRecID = fallbackFontList->getAt(i);
djsollen@google.com9902c382013-09-19 18:22:30 +0000516
517 // if it is not one of the accepted variants then move to the next family
518 int32_t acceptedVariants = SkPaintOptionsAndroid::kDefault_Variant |
519 SkPaintOptionsAndroid::kElegant_Variant;
520 if (!(fFontFamilies[familyRecID].fPaintOptions.getFontVariant() & acceptedVariants)) {
521 continue;
522 }
523
djsollen@google.comb27eba72013-09-06 12:59:50 +0000524 FontRecID fontRecID = find_best_style(fFontFamilies[familyRecID], SkTypeface::kNormal);
djsollen@google.com40078cb2013-05-24 20:31:57 +0000525 SkTypeface* face = this->getTypefaceForFontRec(fontRecID);
djsollen@google.combfae9d32013-05-21 16:53:50 +0000526
527 SkPaint paint;
528 paint.setTypeface(face);
529 paint.setTextEncoding(SkPaint::kUTF32_TextEncoding);
530
531 uint16_t glyphID;
532 paint.textToGlyphs(&uni, sizeof(uni), &glyphID);
533 if (glyphID != 0) {
djsollen@google.com39a7c702013-09-24 20:08:47 +0000534 name->set(fFontFamilies[familyRecID].fFallbackName);
djsollen@google.combfae9d32013-05-21 16:53:50 +0000535 return true;
536 }
537 }
538 return false;
539}
540
541SkTypeface* SkFontConfigInterfaceAndroid::getTypefaceForChar(SkUnichar uni,
542 SkTypeface::Style style,
543 SkPaintOptionsAndroid::FontVariant fontVariant) {
544 FontRecID fontRecID = find_best_style(fFontFamilies[fDefaultFamilyRecID], style);
djsollen@google.com40078cb2013-05-24 20:31:57 +0000545 SkTypeface* face = this->getTypefaceForFontRec(fontRecID);
djsollen@google.combfae9d32013-05-21 16:53:50 +0000546
547 SkPaintOptionsAndroid paintOptions;
548 paintOptions.setFontVariant(fontVariant);
549 paintOptions.setUseFontFallbacks(true);
550
551 SkPaint paint;
552 paint.setTypeface(face);
553 paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
554 paint.setPaintOptionsAndroid(paintOptions);
555
556 SkAutoGlyphCache autoCache(paint, NULL, NULL);
557 SkGlyphCache* cache = autoCache.getCache();
558
559 SkScalerContext* ctx = cache->getScalerContext();
560 if (ctx) {
561 SkFontID fontID = ctx->findTypefaceIdForChar(uni);
562 return SkTypefaceCache::FindByID(fontID);
563 }
564 return NULL;
565}
566
djsollen@google.com9a70f342013-06-25 18:07:45 +0000567FallbackFontList* SkFontConfigInterfaceAndroid::getCurrentLocaleFallbackFontList() {
568 SkString locale = SkFontConfigParser::GetLocale();
569 if (NULL == fLocaleFallbackFontList || locale != fCachedLocale) {
570 fCachedLocale = locale;
571 fLocaleFallbackFontList = this->findFallbackFontList(locale);
572 }
573 return fLocaleFallbackFontList;
574}
575
djsollen@google.com40078cb2013-05-24 20:31:57 +0000576FallbackFontList* SkFontConfigInterfaceAndroid::findFallbackFontList(const SkLanguage& lang,
577 bool isOriginal) {
djsollen@google.combfae9d32013-05-21 16:53:50 +0000578 const SkString& langTag = lang.getTag();
579 if (langTag.isEmpty()) {
580 return &fDefaultFallbackList;
581 }
582
583 FallbackFontList* fallbackFontList;
djsollen@google.com40078cb2013-05-24 20:31:57 +0000584 if (fFallbackFontDict.find(langTag.c_str(), langTag.size(), &fallbackFontList) ||
585 fFallbackFontAliasDict.find(langTag.c_str(), langTag.size(), &fallbackFontList)) {
djsollen@google.combfae9d32013-05-21 16:53:50 +0000586 return fallbackFontList;
587 }
588
589 // attempt a recursive fuzzy match
djsollen@google.combfae9d32013-05-21 16:53:50 +0000590 SkLanguage parent = lang.getParent();
djsollen@google.com40078cb2013-05-24 20:31:57 +0000591 fallbackFontList = findFallbackFontList(parent, false);
592
593 // cache the original lang so we don't have to do the recursion again.
594 if (isOriginal) {
595 DEBUG_FONT(("---- Created fallback list alias for \"%s\"", langTag.c_str()));
596 fFallbackFontAliasDict.set(langTag.c_str(), fallbackFontList);
597 }
598 return fallbackFontList;
djsollen@google.combfae9d32013-05-21 16:53:50 +0000599}
600
601SkTypeface* SkFontConfigInterfaceAndroid::nextLogicalTypeface(SkFontID currFontID,
602 SkFontID origFontID,
603 const SkPaintOptionsAndroid& opts) {
604 // Skia does not support font fallback by default. This enables clients such
605 // as WebKit to customize their font selection. In any case, clients can use
606 // GetFallbackFamilyNameForChar() to get the fallback font for individual
607 // characters.
608 if (!opts.isUsingFontFallbacks()) {
609 return NULL;
610 }
611
djsollen@google.combfae9d32013-05-21 16:53:50 +0000612 FallbackFontList* currentFallbackList = findFallbackFontList(opts.getLanguage());
613 SkASSERT(currentFallbackList);
614
djsollen@google.comb27eba72013-09-06 12:59:50 +0000615 SkTypeface::Style origStyle = SkTypeface::kNormal;
616 const SkTypeface* origTypeface = SkTypefaceCache::FindByID(origFontID);
617 if (NULL != origTypeface) {
618 origStyle = origTypeface->style();
619 }
620
djsollen@google.combfae9d32013-05-21 16:53:50 +0000621 // we must convert currTypeface into a FontRecID
djsollen@google.come47e7d12013-06-06 21:25:09 +0000622 FontRecID currFontRecID = INVALID_FONT_REC_ID;
623 const SkTypeface* currTypeface = SkTypefaceCache::FindByID(currFontID);
624 // non-system fonts are not in the font cache so if we are asked to fallback
625 // for a non-system font we will start at the front of the chain.
djsollen@google.com29bf8622013-07-31 15:48:10 +0000626 if (NULL != currTypeface && currFontID != origFontID) {
djsollen@google.come47e7d12013-06-06 21:25:09 +0000627 currFontRecID = ((FontConfigTypeface*)currTypeface)->getIdentity().fID;
628 SkASSERT(INVALID_FONT_REC_ID != currFontRecID);
629 }
djsollen@google.combfae9d32013-05-21 16:53:50 +0000630
djsollen@google.comb27eba72013-09-06 12:59:50 +0000631 FamilyRecID currFamilyRecID = INVALID_FAMILY_REC_ID;
632 if (INVALID_FONT_REC_ID != currFontRecID) {
633 currFamilyRecID = fFonts[currFontRecID].fFamilyRecID;
634 }
635
djsollen@google.come47e7d12013-06-06 21:25:09 +0000636 // lookup the index next font in the chain
djsollen@google.comb27eba72013-09-06 12:59:50 +0000637 int currFallbackFontIndex = currentFallbackList->find(currFamilyRecID);
djsollen@google.come47e7d12013-06-06 21:25:09 +0000638 // We add 1 to the returned index for 2 reasons: (1) if find succeeds it moves
639 // our index to the next entry in the list; (2) if find() fails it returns
640 // -1 and incrementing it will set our starting index to 0 (the head of the list)
djsollen@google.combfae9d32013-05-21 16:53:50 +0000641 int nextFallbackFontIndex = currFallbackFontIndex + 1;
djsollen@google.combfae9d32013-05-21 16:53:50 +0000642
djsollen@google.com40078cb2013-05-24 20:31:57 +0000643 if(nextFallbackFontIndex >= currentFallbackList->count()) {
djsollen@google.combfae9d32013-05-21 16:53:50 +0000644 return NULL;
645 }
646
647 // If a rec object is set to prefer "kDefault_Variant" it means they have no preference
648 // In this case, we set the value to "kCompact_Variant"
649 SkPaintOptionsAndroid::FontVariant variant = opts.getFontVariant();
650 if (variant == SkPaintOptionsAndroid::kDefault_Variant) {
651 variant = SkPaintOptionsAndroid::kCompact_Variant;
652 }
653
654 int32_t acceptedVariants = SkPaintOptionsAndroid::kDefault_Variant | variant;
655
656 SkTypeface* nextLogicalTypeface = 0;
657 while (nextFallbackFontIndex < currentFallbackList->count()) {
djsollen@google.comb27eba72013-09-06 12:59:50 +0000658 FamilyRecID familyRecID = currentFallbackList->getAt(nextFallbackFontIndex);
659 if ((fFontFamilies[familyRecID].fPaintOptions.getFontVariant() & acceptedVariants) != 0) {
660 FontRecID matchedFont = find_best_style(fFontFamilies[familyRecID], origStyle);
661 nextLogicalTypeface = this->getTypefaceForFontRec(matchedFont);
djsollen@google.combfae9d32013-05-21 16:53:50 +0000662 break;
663 }
664 nextFallbackFontIndex++;
665 }
666
667 DEBUG_FONT(("---- nextLogicalFont: currFontID=%d, origFontID=%d, currRecID=%d, "
djsollen@google.com40078cb2013-05-24 20:31:57 +0000668 "lang=%s, variant=%d, nextFallbackIndex[%d,%d] => nextLogicalTypeface=%d",
djsollen@google.combfae9d32013-05-21 16:53:50 +0000669 currFontID, origFontID, currFontRecID, opts.getLanguage().getTag().c_str(),
djsollen@google.com40078cb2013-05-24 20:31:57 +0000670 variant, nextFallbackFontIndex, currentFallbackList->getAt(nextFallbackFontIndex),
djsollen@google.combfae9d32013-05-21 16:53:50 +0000671 (nextLogicalTypeface) ? nextLogicalTypeface->uniqueID() : 0));
672 return SkSafeRef(nextLogicalTypeface);
673}
674
675///////////////////////////////////////////////////////////////////////////////
676
677bool SkGetFallbackFamilyNameForChar(SkUnichar uni, SkString* name) {
djsollen@google.com9902c382013-09-19 18:22:30 +0000678 SkString locale = SkFontConfigParser::GetLocale();
djsollen@google.combfae9d32013-05-21 16:53:50 +0000679 SkFontConfigInterfaceAndroid* fontConfig = getSingletonInterface();
djsollen@google.com9902c382013-09-19 18:22:30 +0000680 return fontConfig->getFallbackFamilyNameForChar(uni, locale.c_str(), name);
681}
682
683bool SkGetFallbackFamilyNameForChar(SkUnichar uni, const char* lang, SkString* name) {
684 SkFontConfigInterfaceAndroid* fontConfig = getSingletonInterface();
685 return fontConfig->getFallbackFamilyNameForChar(uni, lang, name);
djsollen@google.combfae9d32013-05-21 16:53:50 +0000686}
687
688void SkUseTestFontConfigFile(const char* mainconf, const char* fallbackconf,
689 const char* fontsdir) {
690 gTestMainConfigFile = mainconf;
691 gTestFallbackConfigFile = fallbackconf;
692 gTestFontFilePrefix = fontsdir;
693 SkASSERT(gTestMainConfigFile);
694 SkASSERT(gTestFallbackConfigFile);
695 SkASSERT(gTestFontFilePrefix);
696 SkDEBUGF(("Use Test Config File Main %s, Fallback %s, Font Dir %s",
697 gTestMainConfigFile, gTestFallbackConfigFile, gTestFontFilePrefix));
698}
699
700SkTypeface* SkAndroidNextLogicalTypeface(SkFontID currFontID, SkFontID origFontID,
701 const SkPaintOptionsAndroid& options) {
702 SkFontConfigInterfaceAndroid* fontConfig = getSingletonInterface();
703 return fontConfig->nextLogicalTypeface(currFontID, origFontID, options);
704
705}
706
707///////////////////////////////////////////////////////////////////////////////
708
djsollen@google.com40078cb2013-05-24 20:31:57 +0000709#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
710
711struct HB_UnicodeMapping {
712 // TODO: when the WebView no longer needs harfbuzz_old, remove
713 HB_Script script_old;
714 hb_script_t script;
715 const SkUnichar unicode;
716};
717
718/*
719 * The following scripts are not complex fonts and we do not expect them to be parsed by this table
720 * HB_SCRIPT_COMMON,
721 * HB_SCRIPT_GREEK,
722 * HB_SCRIPT_CYRILLIC,
723 * HB_SCRIPT_HANGUL
724 * HB_SCRIPT_INHERITED
725 */
726
727/* Harfbuzz (old) is missing a number of scripts in its table. For these,
728 * we include a value which can never happen. We won't get complex script
729 * shaping in these cases, but the library wouldn't know how to shape
730 * them anyway. */
731#define HB_Script_Unknown HB_ScriptCount
732
733static HB_UnicodeMapping HB_UnicodeMappingArray[] = {
734 {HB_Script_Armenian, HB_SCRIPT_ARMENIAN, 0x0531},
735 {HB_Script_Hebrew, HB_SCRIPT_HEBREW, 0x0591},
736 {HB_Script_Arabic, HB_SCRIPT_ARABIC, 0x0600},
737 {HB_Script_Syriac, HB_SCRIPT_SYRIAC, 0x0710},
738 {HB_Script_Thaana, HB_SCRIPT_THAANA, 0x0780},
739 {HB_Script_Nko, HB_SCRIPT_NKO, 0x07C0},
740 {HB_Script_Devanagari, HB_SCRIPT_DEVANAGARI, 0x0901},
741 {HB_Script_Bengali, HB_SCRIPT_BENGALI, 0x0981},
742 {HB_Script_Gurmukhi, HB_SCRIPT_GURMUKHI, 0x0A10},
743 {HB_Script_Gujarati, HB_SCRIPT_GUJARATI, 0x0A90},
744 {HB_Script_Oriya, HB_SCRIPT_ORIYA, 0x0B10},
745 {HB_Script_Tamil, HB_SCRIPT_TAMIL, 0x0B82},
746 {HB_Script_Telugu, HB_SCRIPT_TELUGU, 0x0C10},
747 {HB_Script_Kannada, HB_SCRIPT_KANNADA, 0x0C90},
748 {HB_Script_Malayalam, HB_SCRIPT_MALAYALAM, 0x0D10},
749 {HB_Script_Sinhala, HB_SCRIPT_SINHALA, 0x0D90},
750 {HB_Script_Thai, HB_SCRIPT_THAI, 0x0E01},
751 {HB_Script_Lao, HB_SCRIPT_LAO, 0x0E81},
752 {HB_Script_Tibetan, HB_SCRIPT_TIBETAN, 0x0F00},
753 {HB_Script_Myanmar, HB_SCRIPT_MYANMAR, 0x1000},
754 {HB_Script_Georgian, HB_SCRIPT_GEORGIAN, 0x10A0},
755 {HB_Script_Unknown, HB_SCRIPT_ETHIOPIC, 0x1200},
756 {HB_Script_Unknown, HB_SCRIPT_CHEROKEE, 0x13A0},
757 {HB_Script_Ogham, HB_SCRIPT_OGHAM, 0x1680},
758 {HB_Script_Runic, HB_SCRIPT_RUNIC, 0x16A0},
759 {HB_Script_Khmer, HB_SCRIPT_KHMER, 0x1780},
760 {HB_Script_Unknown, HB_SCRIPT_TAI_LE, 0x1950},
761 {HB_Script_Unknown, HB_SCRIPT_NEW_TAI_LUE, 0x1980},
762 {HB_Script_Unknown, HB_SCRIPT_TAI_THAM, 0x1A20},
763 {HB_Script_Unknown, HB_SCRIPT_CHAM, 0xAA00},
764};
765
766static hb_script_t getHBScriptFromHBScriptOld(HB_Script script_old) {
767 hb_script_t script = HB_SCRIPT_INVALID;
768 int numSupportedFonts = sizeof(HB_UnicodeMappingArray) / sizeof(HB_UnicodeMapping);
769 for (int i = 0; i < numSupportedFonts; i++) {
770 if (script_old == HB_UnicodeMappingArray[i].script_old) {
771 script = HB_UnicodeMappingArray[i].script;
772 break;
773 }
774 }
775 return script;
776}
777
778// returns 0 for "Not Found"
779static SkUnichar getUnicodeFromHBScript(hb_script_t script) {
780 SkUnichar unichar = 0;
781 int numSupportedFonts = sizeof(HB_UnicodeMappingArray) / sizeof(HB_UnicodeMapping);
782 for (int i = 0; i < numSupportedFonts; i++) {
783 if (script == HB_UnicodeMappingArray[i].script) {
784 unichar = HB_UnicodeMappingArray[i].unicode;
785 break;
786 }
787 }
788 return unichar;
789}
790
791struct TypefaceLookupStruct {
792 hb_script_t script;
793 SkTypeface::Style style;
794 SkPaintOptionsAndroid::FontVariant fontVariant;
795 SkTypeface* typeface;
796};
797
798SK_DECLARE_STATIC_MUTEX(gTypefaceTableMutex); // This is the mutex for gTypefaceTable
799static SkTDArray<TypefaceLookupStruct> gTypefaceTable; // This is protected by gTypefaceTableMutex
800
801static int typefaceLookupCompare(const TypefaceLookupStruct& first,
802 const TypefaceLookupStruct& second) {
803 if (first.script != second.script) {
804 return (first.script > second.script) ? 1 : -1;
805 }
806 if (first.style != second.style) {
807 return (first.style > second.style) ? 1 : -1;
808 }
809 if (first.fontVariant != second.fontVariant) {
810 return (first.fontVariant > second.fontVariant) ? 1 : -1;
811 }
812 return 0;
813}
814
815SkTypeface* SkCreateTypefaceForScriptNG(hb_script_t script, SkTypeface::Style style,
816 SkPaintOptionsAndroid::FontVariant fontVariant) {
817 SkAutoMutexAcquire ac(gTypefaceTableMutex);
818
819 TypefaceLookupStruct key;
820 key.script = script;
821 key.style = style;
822 key.fontVariant = fontVariant;
823
824 int index = SkTSearch<TypefaceLookupStruct>(
825 (const TypefaceLookupStruct*) gTypefaceTable.begin(),
826 gTypefaceTable.count(), key, sizeof(TypefaceLookupStruct),
827 typefaceLookupCompare);
828
829 SkTypeface* retTypeface = NULL;
830 if (index >= 0) {
831 retTypeface = gTypefaceTable[index].typeface;
832 }
833 else {
834 SkUnichar unichar = getUnicodeFromHBScript(script);
835 if (!unichar) {
836 return NULL;
837 }
838
839 SkFontConfigInterfaceAndroid* fontConfig = getSingletonInterface();
840 retTypeface = fontConfig->getTypefaceForChar(unichar, style, fontVariant);
841
842 // add to the lookup table
843 key.typeface = retTypeface;
844 *gTypefaceTable.insert(~index) = key;
845 }
846
847 // we ref(), the caller is expected to unref when they are done
848 return SkSafeRef(retTypeface);
849}
850
851SkTypeface* SkCreateTypefaceForScript(HB_Script script, SkTypeface::Style style,
852 SkPaintOptionsAndroid::FontVariant fontVariant) {
853 return SkCreateTypefaceForScriptNG(getHBScriptFromHBScriptOld(script), style, fontVariant);
854}
855
856#endif
857
858///////////////////////////////////////////////////////////////////////////////
859
djsollen@google.combfae9d32013-05-21 16:53:50 +0000860SkFontMgr* SkFontMgr::Factory() {
861 return NULL;
862}