blob: 1f901b9fa9f7457e876c12039656d950551a8153 [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);
djsollen@google.com5df5e612013-10-03 14:42:24 +0000107 SkTypeface* getTypefaceForGlyphID(uint16_t glyphID, const SkTypeface* origTypeface,
108 const SkPaintOptionsAndroid& options,
109 int* lowerBounds, int* upperBounds);
djsollen@google.combfae9d32013-05-21 16:53:50 +0000110
111private:
djsollen@google.comb27eba72013-09-06 12:59:50 +0000112 void addFallbackFamily(FamilyRecID fontRecID);
djsollen@google.com40078cb2013-05-24 20:31:57 +0000113 SkTypeface* getTypefaceForFontRec(FontRecID fontRecID);
djsollen@google.com9a70f342013-06-25 18:07:45 +0000114 FallbackFontList* getCurrentLocaleFallbackFontList();
djsollen@google.com40078cb2013-05-24 20:31:57 +0000115 FallbackFontList* findFallbackFontList(const SkLanguage& lang, bool isOriginal = true);
djsollen@google.combfae9d32013-05-21 16:53:50 +0000116
117 SkTArray<FontRec> fFonts;
118 SkTArray<FamilyRec> fFontFamilies;
119 SkTDict<FamilyRecID> fFamilyNameDict;
120 FamilyRecID fDefaultFamilyRecID;
121
122 // (SkLanguage)<->(fallback chain index) translation
123 SkTDict<FallbackFontList*> fFallbackFontDict;
djsollen@google.com40078cb2013-05-24 20:31:57 +0000124 SkTDict<FallbackFontList*> fFallbackFontAliasDict;
djsollen@google.combfae9d32013-05-21 16:53:50 +0000125 FallbackFontList fDefaultFallbackList;
djsollen@google.com9a70f342013-06-25 18:07:45 +0000126
127 // fallback info for current locale
128 SkString fCachedLocale;
129 FallbackFontList* fLocaleFallbackFontList;
djsollen@google.combfae9d32013-05-21 16:53:50 +0000130};
131
132///////////////////////////////////////////////////////////////////////////////
133
134static SkFontConfigInterfaceAndroid* getSingletonInterface() {
135 SK_DECLARE_STATIC_MUTEX(gMutex);
136 static SkFontConfigInterfaceAndroid* gFontConfigInterface;
137
138 SkAutoMutexAcquire ac(gMutex);
139 if (NULL == gFontConfigInterface) {
140 // load info from a configuration file that we can use to populate the
141 // system/fallback font structures
142 SkTDArray<FontFamily*> fontFamilies;
143 if (!gTestMainConfigFile) {
144 SkFontConfigParser::GetFontFamilies(fontFamilies);
145 } else {
146 SkFontConfigParser::GetTestFontFamilies(fontFamilies, gTestMainConfigFile,
147 gTestFallbackConfigFile);
148 }
149
150 gFontConfigInterface = new SkFontConfigInterfaceAndroid(fontFamilies);
151
152 // cleanup the data we received from the parser
153 fontFamilies.deleteAll();
154 }
155 return gFontConfigInterface;
156}
157
tomhudsone438ddb2014-07-01 18:54:41 -0700158SkFontConfigInterface* SkFontConfigInterface::GetSingletonDirectInterface(SkBaseMutex*) {
159 // Doesn't need passed-in mutex because getSingletonInterface() uses one
djsollen@google.combfae9d32013-05-21 16:53:50 +0000160 return getSingletonInterface();
161}
162
163///////////////////////////////////////////////////////////////////////////////
164
165static bool has_font(const SkTArray<FontRec>& array, const SkString& filename) {
166 for (int i = 0; i < array.count(); i++) {
167 if (array[i].fFileName == filename) {
168 return true;
169 }
170 }
171 return false;
172}
173
174#ifndef SK_FONT_FILE_PREFIX
175 #define SK_FONT_FILE_PREFIX "/fonts/"
176#endif
177
commit-bot@chromium.org31db71d2014-04-04 18:14:39 +0000178static void get_path_for_sys_fonts(SkString* full, const SkString& name) {
djsollen@google.combfae9d32013-05-21 16:53:50 +0000179 if (gTestFontFilePrefix) {
180 full->set(gTestFontFilePrefix);
181 } else {
182 full->set(getenv("ANDROID_ROOT"));
183 full->append(SK_FONT_FILE_PREFIX);
184 }
185 full->append(name);
186}
187
188static void insert_into_name_dict(SkTDict<FamilyRecID>& familyNameDict,
189 const char* name, FamilyRecID familyRecID) {
190 SkAutoAsciiToLC tolc(name);
djsollen@google.com92e3f082013-08-27 17:40:03 +0000191 if (familyNameDict.find(tolc.lc())) {
192 SkDebugf("---- system font attempting to use a the same name [%s] for"
193 "multiple families. skipping subsequent occurrences", tolc.lc());
194 } else {
195 familyNameDict.set(tolc.lc(), familyRecID);
196 }
djsollen@google.combfae9d32013-05-21 16:53:50 +0000197}
198
199// Defined in SkFontHost_FreeType.cpp
200bool find_name_and_attributes(SkStream* stream, SkString* name,
201 SkTypeface::Style* style, bool* isFixedWidth);
202
203///////////////////////////////////////////////////////////////////////////////
204
205SkFontConfigInterfaceAndroid::SkFontConfigInterfaceAndroid(SkTDArray<FontFamily*>& fontFamilies) :
206 fFonts(fontFamilies.count()),
207 fFontFamilies(fontFamilies.count() / FamilyRec::FONT_STYLE_COUNT),
208 fFamilyNameDict(1024),
209 fDefaultFamilyRecID(INVALID_FAMILY_REC_ID),
djsollen@google.com40078cb2013-05-24 20:31:57 +0000210 fFallbackFontDict(128),
djsollen@google.com9a70f342013-06-25 18:07:45 +0000211 fFallbackFontAliasDict(128),
212 fLocaleFallbackFontList(NULL) {
djsollen@google.combfae9d32013-05-21 16:53:50 +0000213
214 for (int i = 0; i < fontFamilies.count(); ++i) {
215 FontFamily* family = fontFamilies[i];
216
217 // defer initializing the familyRec until we can be sure that at least
218 // one of it's children contains a valid font file
219 FamilyRec* familyRec = NULL;
220 FamilyRecID familyRecID = INVALID_FAMILY_REC_ID;
221
222 for (int j = 0; j < family->fFontFiles.count(); ++j) {
223 SkString filename;
commit-bot@chromium.org31db71d2014-04-04 18:14:39 +0000224 get_path_for_sys_fonts(&filename, family->fFontFiles[j].fFileName);
djsollen@google.combfae9d32013-05-21 16:53:50 +0000225
226 if (has_font(fFonts, filename)) {
227 SkDebugf("---- system font and fallback font files specify a duplicate "
228 "font %s, skipping the second occurrence", filename.c_str());
229 continue;
230 }
231
232 FontRec& fontRec = fFonts.push_back();
233 fontRec.fFileName = filename;
234 fontRec.fStyle = SkTypeface::kNormal;
djsollen@google.combfae9d32013-05-21 16:53:50 +0000235 fontRec.fIsValid = false;
djsollen@google.com40078cb2013-05-24 20:31:57 +0000236 fontRec.fFamilyRecID = familyRecID;
djsollen@google.combfae9d32013-05-21 16:53:50 +0000237
238 const FontRecID fontRecID = fFonts.count() - 1;
239
240 SkAutoTUnref<SkStream> stream(SkStream::NewFromFile(filename.c_str()));
241 if (stream.get() != NULL) {
242 bool isFixedWidth;
243 SkString name;
244 fontRec.fIsValid = find_name_and_attributes(stream.get(), &name,
245 &fontRec.fStyle, &isFixedWidth);
246 } else {
djsollen@google.comb27eba72013-09-06 12:59:50 +0000247 if (!family->fIsFallbackFont) {
djsollen@google.combfae9d32013-05-21 16:53:50 +0000248 SkDebugf("---- failed to open <%s> as a font\n", filename.c_str());
249 }
250 }
251
252 if (fontRec.fIsValid) {
253 DEBUG_FONT(("---- SystemFonts[%d][%d] fallback=%d file=%s",
djsollen@google.com9902c382013-09-19 18:22:30 +0000254 i, fFonts.count() - 1, family->fIsFallbackFont, filename.c_str()));
djsollen@google.combfae9d32013-05-21 16:53:50 +0000255 } else {
256 DEBUG_FONT(("---- SystemFonts[%d][%d] fallback=%d file=%s (INVALID)",
djsollen@google.com9902c382013-09-19 18:22:30 +0000257 i, fFonts.count() - 1, family->fIsFallbackFont, filename.c_str()));
djsollen@google.combfae9d32013-05-21 16:53:50 +0000258 continue;
259 }
260
261 // create a familyRec now that we know that at least one font in
262 // the family is valid
263 if (familyRec == NULL) {
264 familyRec = &fFontFamilies.push_back();
265 familyRecID = fFontFamilies.count() - 1;
djsollen@google.com40078cb2013-05-24 20:31:57 +0000266 fontRec.fFamilyRecID = familyRecID;
djsollen@google.comb27eba72013-09-06 12:59:50 +0000267
268 familyRec->fIsFallbackFont = family->fIsFallbackFont;
commit-bot@chromium.org31db71d2014-04-04 18:14:39 +0000269 familyRec->fPaintOptions = family->fFontFiles[j].fPaintOptions;
djsollen@google.comb27eba72013-09-06 12:59:50 +0000270
commit-bot@chromium.org31db71d2014-04-04 18:14:39 +0000271 } else if (familyRec->fPaintOptions != family->fFontFiles[j].fPaintOptions) {
djsollen@google.comb27eba72013-09-06 12:59:50 +0000272 SkDebugf("Every font file within a family must have identical"
273 "language and variant attributes");
274 sk_throw();
djsollen@google.combfae9d32013-05-21 16:53:50 +0000275 }
276
277 // add this font to the current familyRec
278 if (INVALID_FONT_REC_ID != familyRec->fFontRecID[fontRec.fStyle]) {
279 DEBUG_FONT(("Overwriting familyRec for style[%d] old,new:(%d,%d)",
280 fontRec.fStyle, familyRec->fFontRecID[fontRec.fStyle],
281 fontRecID));
282 }
283 familyRec->fFontRecID[fontRec.fStyle] = fontRecID;
djsollen@google.combfae9d32013-05-21 16:53:50 +0000284 }
285
djsollen@google.com39a7c702013-09-24 20:08:47 +0000286 if (familyRec) {
287 if (familyRec->fIsFallbackFont) {
288 // add the font to the appropriate fallback chains and also insert a
289 // unique name into the familyNameDict for internal usage
290 addFallbackFamily(familyRecID);
291 } else {
292 // add the names that map to this family to the dictionary for easy lookup
commit-bot@chromium.org31db71d2014-04-04 18:14:39 +0000293 const SkTArray<SkString>& names = family->fNames;
294 if (names.empty()) {
djsollen@google.com39a7c702013-09-24 20:08:47 +0000295 SkDEBUGFAIL("ERROR: non-fallback font with no name");
296 continue;
297 }
djsollen@google.combfae9d32013-05-21 16:53:50 +0000298
djsollen@google.com39a7c702013-09-24 20:08:47 +0000299 for (int i = 0; i < names.count(); i++) {
commit-bot@chromium.org31db71d2014-04-04 18:14:39 +0000300 insert_into_name_dict(fFamilyNameDict, names[i].c_str(), familyRecID);
djsollen@google.com39a7c702013-09-24 20:08:47 +0000301 }
djsollen@google.combfae9d32013-05-21 16:53:50 +0000302 }
303 }
djsollen@google.combfae9d32013-05-21 16:53:50 +0000304 }
305
306 DEBUG_FONT(("---- We have %d system fonts", fFonts.count()));
307
308 if (fFontFamilies.count() > 0) {
309 fDefaultFamilyRecID = 0;
310 }
311
312 // scans the default fallback font chain, adding every entry to every other
313 // fallback font chain to which it does not belong. this results in every
314 // language-specific fallback font chain having all of its fallback fonts at
315 // the front of the chain, and everything else at the end.
316 FallbackFontList* fallbackList;
317 SkTDict<FallbackFontList*>::Iter iter(fFallbackFontDict);
318 const char* fallbackLang = iter.next(&fallbackList);
319 while(fallbackLang != NULL) {
320 for (int i = 0; i < fDefaultFallbackList.count(); i++) {
djsollen@google.comb27eba72013-09-06 12:59:50 +0000321 FamilyRecID familyRecID = fDefaultFallbackList[i];
322 const SkString& fontLang = fFontFamilies[familyRecID].fPaintOptions.getLanguage().getTag();
djsollen@google.combfae9d32013-05-21 16:53:50 +0000323 if (strcmp(fallbackLang, fontLang.c_str()) != 0) {
djsollen@google.comb27eba72013-09-06 12:59:50 +0000324 fallbackList->push(familyRecID);
djsollen@google.combfae9d32013-05-21 16:53:50 +0000325 }
326 }
327 // move to the next fallback list in the dictionary
328 fallbackLang = iter.next(&fallbackList);
329 }
330}
331
332SkFontConfigInterfaceAndroid::~SkFontConfigInterfaceAndroid() {
333 // iterate through and cleanup fFallbackFontDict
334 SkTDict<FallbackFontList*>::Iter iter(fFallbackFontDict);
335 FallbackFontList* fallbackList;
336 while(iter.next(&fallbackList) != NULL) {
337 SkDELETE(fallbackList);
338 }
339}
340
djsollen@google.comb27eba72013-09-06 12:59:50 +0000341void SkFontConfigInterfaceAndroid::addFallbackFamily(FamilyRecID familyRecID) {
342 SkASSERT(familyRecID < fFontFamilies.count());
djsollen@google.com39a7c702013-09-24 20:08:47 +0000343 FamilyRec& familyRec = fFontFamilies[familyRecID];
djsollen@google.comb27eba72013-09-06 12:59:50 +0000344 SkASSERT(familyRec.fIsFallbackFont);
djsollen@google.combfae9d32013-05-21 16:53:50 +0000345
djsollen@google.com39a7c702013-09-24 20:08:47 +0000346 // add the fallback family to the name dictionary. This is
347 // needed by getFallbackFamilyNameForChar() so that fallback
348 // families can be identified by a unique name. The unique
349 // identifier that we've chosen is the familyID in hex (e.g. '0F##fallback').
350 familyRec.fFallbackName.printf("%.2x##fallback", familyRecID);
351 insert_into_name_dict(fFamilyNameDict, familyRec.fFallbackName.c_str(), familyRecID);
352
djsollen@google.combfae9d32013-05-21 16:53:50 +0000353 // add to the default fallback list
djsollen@google.comb27eba72013-09-06 12:59:50 +0000354 fDefaultFallbackList.push(familyRecID);
djsollen@google.combfae9d32013-05-21 16:53:50 +0000355
356 // stop here if it is the default language tag
djsollen@google.comb27eba72013-09-06 12:59:50 +0000357 const SkString& languageTag = familyRec.fPaintOptions.getLanguage().getTag();
djsollen@google.combfae9d32013-05-21 16:53:50 +0000358 if (languageTag.isEmpty()) {
359 return;
360 }
361
362 // add to the appropriate language's custom fallback list
363 FallbackFontList* customList = NULL;
364 if (!fFallbackFontDict.find(languageTag.c_str(), &customList)) {
365 DEBUG_FONT(("---- Created fallback list for \"%s\"", languageTag.c_str()));
366 customList = SkNEW(FallbackFontList);
367 fFallbackFontDict.set(languageTag.c_str(), customList);
368 }
369 SkASSERT(customList != NULL);
djsollen@google.comb27eba72013-09-06 12:59:50 +0000370 customList->push(familyRecID);
djsollen@google.combfae9d32013-05-21 16:53:50 +0000371}
372
373
374static FontRecID find_best_style(const FamilyRec& family, SkTypeface::Style style) {
375
376 const FontRecID* fontRecIDs = family.fFontRecID;
377
378 if (fontRecIDs[style] != INVALID_FONT_REC_ID) { // exact match
379 return fontRecIDs[style];
380 }
381 // look for a matching bold
382 style = (SkTypeface::Style)(style ^ SkTypeface::kItalic);
383 if (fontRecIDs[style] != INVALID_FONT_REC_ID) {
384 return fontRecIDs[style];
385 }
386 // look for the plain
387 if (fontRecIDs[SkTypeface::kNormal] != INVALID_FONT_REC_ID) {
388 return fontRecIDs[SkTypeface::kNormal];
389 }
390 // look for anything
391 for (int i = 0; i < FamilyRec::FONT_STYLE_COUNT; i++) {
392 if (fontRecIDs[i] != INVALID_FONT_REC_ID) {
393 return fontRecIDs[i];
394 }
395 }
396 // should never get here, since the fontRecID list should not be empty
397 SkDEBUGFAIL("No valid fonts exist for this family");
398 return -1;
399}
400
401bool SkFontConfigInterfaceAndroid::matchFamilyName(const char familyName[],
402 SkTypeface::Style style,
403 FontIdentity* outFontIdentifier,
404 SkString* outFamilyName,
robertphillips@google.comb7457d02013-05-22 00:12:43 +0000405 SkTypeface::Style* outStyle) {
djsollen@google.combfae9d32013-05-21 16:53:50 +0000406 // clip to legal style bits
407 style = (SkTypeface::Style)(style & SkTypeface::kBoldItalic);
408
409 bool exactNameMatch = false;
410
411 FamilyRecID familyRecID = INVALID_FAMILY_REC_ID;
412 if (NULL != familyName) {
djsollen@google.com2e08f192013-05-21 20:08:10 +0000413 SkAutoAsciiToLC tolc(familyName);
414 if (fFamilyNameDict.find(tolc.lc(), &familyRecID)) {
djsollen@google.combfae9d32013-05-21 16:53:50 +0000415 exactNameMatch = true;
416 }
417 } else {
418 familyRecID = fDefaultFamilyRecID;
419
420 }
421
djsollen@google.com1f584ed2013-09-19 12:08:40 +0000422 // If no matching family name is found then return false. This allows clients
423 // to be able to search for other fonts instead of forcing them to use the
424 // default font.
djsollen@google.combfae9d32013-05-21 16:53:50 +0000425 if (INVALID_FAMILY_REC_ID == familyRecID) {
djsollen@google.com1f584ed2013-09-19 12:08:40 +0000426 return false;
djsollen@google.combfae9d32013-05-21 16:53:50 +0000427 }
428
429 FontRecID fontRecID = find_best_style(fFontFamilies[familyRecID], style);
430 FontRec& fontRec = fFonts[fontRecID];
431
432 if (NULL != outFontIdentifier) {
433 outFontIdentifier->fID = fontRecID;
434 outFontIdentifier->fTTCIndex = 0;
435 outFontIdentifier->fString.set(fontRec.fFileName);
436// outFontIdentifier->fStyle = fontRec.fStyle;
437 }
438
439 if (NULL != outFamilyName) {
440 if (exactNameMatch) {
441 outFamilyName->set(familyName);
442 } else {
443 // find familyName from list of names
444 const char* familyName = NULL;
djsollen@google.comab6eeb92013-05-21 17:15:27 +0000445 SkAssertResult(fFamilyNameDict.findKey(familyRecID, &familyName));
446 SkASSERT(familyName);
djsollen@google.combfae9d32013-05-21 16:53:50 +0000447 outFamilyName->set(familyName);
448 }
449 }
450
451 if (NULL != outStyle) {
452 *outStyle = fontRec.fStyle;
453 }
454
455 return true;
456}
457
robertphillips@google.comb7457d02013-05-22 00:12:43 +0000458SkStream* SkFontConfigInterfaceAndroid::openStream(const FontIdentity& identity) {
djsollen@google.combfae9d32013-05-21 16:53:50 +0000459 return SkStream::NewFromFile(identity.fString.c_str());
460}
461
robertphillips@google.comb7457d02013-05-22 00:12:43 +0000462SkDataTable* SkFontConfigInterfaceAndroid::getFamilyNames() {
djsollen@google.combfae9d32013-05-21 16:53:50 +0000463 SkTDArray<const char*> names;
464 SkTDArray<size_t> sizes;
465
466 SkTDict<FamilyRecID>::Iter iter(fFamilyNameDict);
467 const char* familyName = iter.next(NULL);
468 while(familyName != NULL) {
469 *names.append() = familyName;
470 *sizes.append() = strlen(familyName) + 1;
471
472 // move to the next familyName in the dictionary
473 familyName = iter.next(NULL);
474 }
475
476 return SkDataTable::NewCopyArrays((const void*const*)names.begin(),
477 sizes.begin(), names.count());
478}
479
480bool SkFontConfigInterfaceAndroid::matchFamilySet(const char inFamilyName[],
481 SkString* outFamilyName,
robertphillips@google.comb7457d02013-05-22 00:12:43 +0000482 SkTArray<FontIdentity>*) {
djsollen@google.combfae9d32013-05-21 16:53:50 +0000483 return false;
484}
485
djsollen@google.com40078cb2013-05-24 20:31:57 +0000486static bool find_proc(SkTypeface* face, SkTypeface::Style style, void* ctx) {
487 const FontRecID* fontRecID = (const FontRecID*)ctx;
488 FontRecID currFontRecID = ((FontConfigTypeface*)face)->getIdentity().fID;
489 return currFontRecID == *fontRecID;
490}
491
492SkTypeface* SkFontConfigInterfaceAndroid::getTypefaceForFontRec(FontRecID fontRecID) {
493 FontRec& fontRec = fFonts[fontRecID];
djsollen@google.combfae9d32013-05-21 16:53:50 +0000494 SkTypeface* face = fontRec.fTypeface.get();
495 if (!face) {
djsollen@google.com40078cb2013-05-24 20:31:57 +0000496 // look for it in the typeface cache
497 face = SkTypefaceCache::FindByProcAndRef(find_proc, &fontRecID);
djsollen@google.combfae9d32013-05-21 16:53:50 +0000498
499 // if it is not in the cache then create it
djsollen@google.com40078cb2013-05-24 20:31:57 +0000500 if (!face) {
501 const char* familyName = NULL;
502 SkAssertResult(fFamilyNameDict.findKey(fontRec.fFamilyRecID, &familyName));
503 SkASSERT(familyName);
504 face = SkTypeface::CreateFromName(familyName, fontRec.fStyle);
505 }
djsollen@google.combfae9d32013-05-21 16:53:50 +0000506
507 // store the result for subsequent lookups
508 fontRec.fTypeface = face;
509 }
510 SkASSERT(face);
511 return face;
512}
513
djsollen@google.com9902c382013-09-19 18:22:30 +0000514bool SkFontConfigInterfaceAndroid::getFallbackFamilyNameForChar(SkUnichar uni,
515 const char* lang,
516 SkString* name) {
commit-bot@chromium.orgc0445fe2013-10-07 19:49:13 +0000517 FallbackFontList* fallbackFontList = NULL;
518 const SkString langTag(lang);
519 if (langTag.isEmpty()) {
520 fallbackFontList = this->getCurrentLocaleFallbackFontList();
521 } else {
522 fallbackFontList = this->findFallbackFontList(langTag);
523 }
524
djsollen@google.com9a70f342013-06-25 18:07:45 +0000525 for (int i = 0; i < fallbackFontList->count(); i++) {
djsollen@google.comb27eba72013-09-06 12:59:50 +0000526 FamilyRecID familyRecID = fallbackFontList->getAt(i);
djsollen@google.com9902c382013-09-19 18:22:30 +0000527
528 // if it is not one of the accepted variants then move to the next family
529 int32_t acceptedVariants = SkPaintOptionsAndroid::kDefault_Variant |
530 SkPaintOptionsAndroid::kElegant_Variant;
531 if (!(fFontFamilies[familyRecID].fPaintOptions.getFontVariant() & acceptedVariants)) {
532 continue;
533 }
534
djsollen@google.comb27eba72013-09-06 12:59:50 +0000535 FontRecID fontRecID = find_best_style(fFontFamilies[familyRecID], SkTypeface::kNormal);
djsollen@google.com40078cb2013-05-24 20:31:57 +0000536 SkTypeface* face = this->getTypefaceForFontRec(fontRecID);
djsollen@google.combfae9d32013-05-21 16:53:50 +0000537
538 SkPaint paint;
539 paint.setTypeface(face);
540 paint.setTextEncoding(SkPaint::kUTF32_TextEncoding);
541
542 uint16_t glyphID;
543 paint.textToGlyphs(&uni, sizeof(uni), &glyphID);
544 if (glyphID != 0) {
djsollen@google.com39a7c702013-09-24 20:08:47 +0000545 name->set(fFontFamilies[familyRecID].fFallbackName);
djsollen@google.combfae9d32013-05-21 16:53:50 +0000546 return true;
547 }
548 }
549 return false;
550}
551
552SkTypeface* SkFontConfigInterfaceAndroid::getTypefaceForChar(SkUnichar uni,
553 SkTypeface::Style style,
554 SkPaintOptionsAndroid::FontVariant fontVariant) {
555 FontRecID fontRecID = find_best_style(fFontFamilies[fDefaultFamilyRecID], style);
djsollen@google.com40078cb2013-05-24 20:31:57 +0000556 SkTypeface* face = this->getTypefaceForFontRec(fontRecID);
djsollen@google.combfae9d32013-05-21 16:53:50 +0000557
558 SkPaintOptionsAndroid paintOptions;
559 paintOptions.setFontVariant(fontVariant);
560 paintOptions.setUseFontFallbacks(true);
561
562 SkPaint paint;
563 paint.setTypeface(face);
564 paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
565 paint.setPaintOptionsAndroid(paintOptions);
566
567 SkAutoGlyphCache autoCache(paint, NULL, NULL);
568 SkGlyphCache* cache = autoCache.getCache();
569
570 SkScalerContext* ctx = cache->getScalerContext();
571 if (ctx) {
572 SkFontID fontID = ctx->findTypefaceIdForChar(uni);
573 return SkTypefaceCache::FindByID(fontID);
574 }
575 return NULL;
576}
577
djsollen@google.com9a70f342013-06-25 18:07:45 +0000578FallbackFontList* SkFontConfigInterfaceAndroid::getCurrentLocaleFallbackFontList() {
579 SkString locale = SkFontConfigParser::GetLocale();
580 if (NULL == fLocaleFallbackFontList || locale != fCachedLocale) {
581 fCachedLocale = locale;
582 fLocaleFallbackFontList = this->findFallbackFontList(locale);
583 }
584 return fLocaleFallbackFontList;
585}
586
djsollen@google.com40078cb2013-05-24 20:31:57 +0000587FallbackFontList* SkFontConfigInterfaceAndroid::findFallbackFontList(const SkLanguage& lang,
588 bool isOriginal) {
djsollen@google.combfae9d32013-05-21 16:53:50 +0000589 const SkString& langTag = lang.getTag();
590 if (langTag.isEmpty()) {
591 return &fDefaultFallbackList;
592 }
593
594 FallbackFontList* fallbackFontList;
djsollen@google.com40078cb2013-05-24 20:31:57 +0000595 if (fFallbackFontDict.find(langTag.c_str(), langTag.size(), &fallbackFontList) ||
596 fFallbackFontAliasDict.find(langTag.c_str(), langTag.size(), &fallbackFontList)) {
djsollen@google.combfae9d32013-05-21 16:53:50 +0000597 return fallbackFontList;
598 }
599
600 // attempt a recursive fuzzy match
djsollen@google.combfae9d32013-05-21 16:53:50 +0000601 SkLanguage parent = lang.getParent();
djsollen@google.com40078cb2013-05-24 20:31:57 +0000602 fallbackFontList = findFallbackFontList(parent, false);
603
604 // cache the original lang so we don't have to do the recursion again.
605 if (isOriginal) {
606 DEBUG_FONT(("---- Created fallback list alias for \"%s\"", langTag.c_str()));
607 fFallbackFontAliasDict.set(langTag.c_str(), fallbackFontList);
608 }
609 return fallbackFontList;
djsollen@google.combfae9d32013-05-21 16:53:50 +0000610}
611
612SkTypeface* SkFontConfigInterfaceAndroid::nextLogicalTypeface(SkFontID currFontID,
613 SkFontID origFontID,
614 const SkPaintOptionsAndroid& opts) {
615 // Skia does not support font fallback by default. This enables clients such
616 // as WebKit to customize their font selection. In any case, clients can use
617 // GetFallbackFamilyNameForChar() to get the fallback font for individual
618 // characters.
619 if (!opts.isUsingFontFallbacks()) {
620 return NULL;
621 }
622
djsollen@google.combfae9d32013-05-21 16:53:50 +0000623 FallbackFontList* currentFallbackList = findFallbackFontList(opts.getLanguage());
624 SkASSERT(currentFallbackList);
625
djsollen@google.comb27eba72013-09-06 12:59:50 +0000626 SkTypeface::Style origStyle = SkTypeface::kNormal;
627 const SkTypeface* origTypeface = SkTypefaceCache::FindByID(origFontID);
628 if (NULL != origTypeface) {
629 origStyle = origTypeface->style();
630 }
631
djsollen@google.combfae9d32013-05-21 16:53:50 +0000632 // we must convert currTypeface into a FontRecID
djsollen@google.come47e7d12013-06-06 21:25:09 +0000633 FontRecID currFontRecID = INVALID_FONT_REC_ID;
634 const SkTypeface* currTypeface = SkTypefaceCache::FindByID(currFontID);
635 // non-system fonts are not in the font cache so if we are asked to fallback
636 // for a non-system font we will start at the front of the chain.
commit-bot@chromium.org94da31d2014-01-23 17:21:28 +0000637 if (NULL != currTypeface) {
djsollen@google.come47e7d12013-06-06 21:25:09 +0000638 currFontRecID = ((FontConfigTypeface*)currTypeface)->getIdentity().fID;
639 SkASSERT(INVALID_FONT_REC_ID != currFontRecID);
640 }
djsollen@google.combfae9d32013-05-21 16:53:50 +0000641
djsollen@google.comb27eba72013-09-06 12:59:50 +0000642 FamilyRecID currFamilyRecID = INVALID_FAMILY_REC_ID;
643 if (INVALID_FONT_REC_ID != currFontRecID) {
644 currFamilyRecID = fFonts[currFontRecID].fFamilyRecID;
645 }
646
djsollen@google.come47e7d12013-06-06 21:25:09 +0000647 // lookup the index next font in the chain
djsollen@google.comb27eba72013-09-06 12:59:50 +0000648 int currFallbackFontIndex = currentFallbackList->find(currFamilyRecID);
djsollen@google.come47e7d12013-06-06 21:25:09 +0000649 // We add 1 to the returned index for 2 reasons: (1) if find succeeds it moves
650 // our index to the next entry in the list; (2) if find() fails it returns
651 // -1 and incrementing it will set our starting index to 0 (the head of the list)
djsollen@google.combfae9d32013-05-21 16:53:50 +0000652 int nextFallbackFontIndex = currFallbackFontIndex + 1;
djsollen@google.combfae9d32013-05-21 16:53:50 +0000653
djsollen@google.com40078cb2013-05-24 20:31:57 +0000654 if(nextFallbackFontIndex >= currentFallbackList->count()) {
djsollen@google.combfae9d32013-05-21 16:53:50 +0000655 return NULL;
656 }
657
658 // If a rec object is set to prefer "kDefault_Variant" it means they have no preference
659 // In this case, we set the value to "kCompact_Variant"
660 SkPaintOptionsAndroid::FontVariant variant = opts.getFontVariant();
661 if (variant == SkPaintOptionsAndroid::kDefault_Variant) {
662 variant = SkPaintOptionsAndroid::kCompact_Variant;
663 }
664
665 int32_t acceptedVariants = SkPaintOptionsAndroid::kDefault_Variant | variant;
666
667 SkTypeface* nextLogicalTypeface = 0;
668 while (nextFallbackFontIndex < currentFallbackList->count()) {
djsollen@google.comb27eba72013-09-06 12:59:50 +0000669 FamilyRecID familyRecID = currentFallbackList->getAt(nextFallbackFontIndex);
670 if ((fFontFamilies[familyRecID].fPaintOptions.getFontVariant() & acceptedVariants) != 0) {
671 FontRecID matchedFont = find_best_style(fFontFamilies[familyRecID], origStyle);
672 nextLogicalTypeface = this->getTypefaceForFontRec(matchedFont);
djsollen@google.combfae9d32013-05-21 16:53:50 +0000673 break;
674 }
675 nextFallbackFontIndex++;
676 }
677
678 DEBUG_FONT(("---- nextLogicalFont: currFontID=%d, origFontID=%d, currRecID=%d, "
djsollen@google.com40078cb2013-05-24 20:31:57 +0000679 "lang=%s, variant=%d, nextFallbackIndex[%d,%d] => nextLogicalTypeface=%d",
djsollen@google.combfae9d32013-05-21 16:53:50 +0000680 currFontID, origFontID, currFontRecID, opts.getLanguage().getTag().c_str(),
djsollen@google.com40078cb2013-05-24 20:31:57 +0000681 variant, nextFallbackFontIndex, currentFallbackList->getAt(nextFallbackFontIndex),
djsollen@google.combfae9d32013-05-21 16:53:50 +0000682 (nextLogicalTypeface) ? nextLogicalTypeface->uniqueID() : 0));
683 return SkSafeRef(nextLogicalTypeface);
684}
685
djsollen@google.com5df5e612013-10-03 14:42:24 +0000686SkTypeface* SkFontConfigInterfaceAndroid::getTypefaceForGlyphID(uint16_t glyphID,
687 const SkTypeface* origTypeface,
688 const SkPaintOptionsAndroid& opts,
689 int* lBounds, int* uBounds) {
690 // If we aren't using fallbacks then we shouldn't be calling this
691 SkASSERT(opts.isUsingFontFallbacks());
692 SkASSERT(origTypeface);
693
694 SkTypeface* currentTypeface = NULL;
695 int lowerBounds = 0; //inclusive
696 int upperBounds = origTypeface->countGlyphs(); //exclusive
697
698 // check to see if the glyph is in the bounds of the origTypeface
699 if (glyphID < upperBounds) {
700 currentTypeface = const_cast<SkTypeface*>(origTypeface);
701 } else {
702 FallbackFontList* currentFallbackList = findFallbackFontList(opts.getLanguage());
703 SkASSERT(currentFallbackList);
704
705 // If an object is set to prefer "kDefault_Variant" it means they have no preference
706 // In this case, we set the value to "kCompact_Variant"
707 SkPaintOptionsAndroid::FontVariant variant = opts.getFontVariant();
708 if (variant == SkPaintOptionsAndroid::kDefault_Variant) {
709 variant = SkPaintOptionsAndroid::kCompact_Variant;
710 }
711
712 int32_t acceptedVariants = SkPaintOptionsAndroid::kDefault_Variant | variant;
713 SkTypeface::Style origStyle = origTypeface->style();
714
715 for (int x = 0; x < currentFallbackList->count(); ++x) {
716 const FamilyRecID familyRecID = currentFallbackList->getAt(x);
717 const SkPaintOptionsAndroid& familyOptions = fFontFamilies[familyRecID].fPaintOptions;
718 if ((familyOptions.getFontVariant() & acceptedVariants) != 0) {
719 FontRecID matchedFont = find_best_style(fFontFamilies[familyRecID], origStyle);
720 currentTypeface = this->getTypefaceForFontRec(matchedFont);
721 lowerBounds = upperBounds;
722 upperBounds += currentTypeface->countGlyphs();
723 if (glyphID < upperBounds) {
724 break;
725 }
726 }
727 }
728 }
729
730 if (NULL != currentTypeface) {
731 if (lBounds) {
732 *lBounds = lowerBounds;
733 }
734 if (uBounds) {
735 *uBounds = upperBounds;
736 }
737 }
738 return currentTypeface;
739}
740
djsollen@google.combfae9d32013-05-21 16:53:50 +0000741///////////////////////////////////////////////////////////////////////////////
742
743bool SkGetFallbackFamilyNameForChar(SkUnichar uni, SkString* name) {
744 SkFontConfigInterfaceAndroid* fontConfig = getSingletonInterface();
commit-bot@chromium.orgc0445fe2013-10-07 19:49:13 +0000745 return fontConfig->getFallbackFamilyNameForChar(uni, NULL, name);
djsollen@google.com9902c382013-09-19 18:22:30 +0000746}
747
748bool SkGetFallbackFamilyNameForChar(SkUnichar uni, const char* lang, SkString* name) {
749 SkFontConfigInterfaceAndroid* fontConfig = getSingletonInterface();
750 return fontConfig->getFallbackFamilyNameForChar(uni, lang, name);
djsollen@google.combfae9d32013-05-21 16:53:50 +0000751}
752
753void SkUseTestFontConfigFile(const char* mainconf, const char* fallbackconf,
754 const char* fontsdir) {
755 gTestMainConfigFile = mainconf;
756 gTestFallbackConfigFile = fallbackconf;
757 gTestFontFilePrefix = fontsdir;
758 SkASSERT(gTestMainConfigFile);
759 SkASSERT(gTestFallbackConfigFile);
760 SkASSERT(gTestFontFilePrefix);
761 SkDEBUGF(("Use Test Config File Main %s, Fallback %s, Font Dir %s",
762 gTestMainConfigFile, gTestFallbackConfigFile, gTestFontFilePrefix));
763}
764
765SkTypeface* SkAndroidNextLogicalTypeface(SkFontID currFontID, SkFontID origFontID,
766 const SkPaintOptionsAndroid& options) {
767 SkFontConfigInterfaceAndroid* fontConfig = getSingletonInterface();
768 return fontConfig->nextLogicalTypeface(currFontID, origFontID, options);
769
770}
771
djsollen@google.com5df5e612013-10-03 14:42:24 +0000772SkTypeface* SkGetTypefaceForGlyphID(uint16_t glyphID, const SkTypeface* origTypeface,
773 const SkPaintOptionsAndroid& options,
774 int* lowerBounds, int* upperBounds) {
775 SkFontConfigInterfaceAndroid* fontConfig = getSingletonInterface();
776 return fontConfig->getTypefaceForGlyphID(glyphID, origTypeface, options,
777 lowerBounds, upperBounds);
778}
779
djsollen@google.combfae9d32013-05-21 16:53:50 +0000780///////////////////////////////////////////////////////////////////////////////
781
djsollen@google.com40078cb2013-05-24 20:31:57 +0000782#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
783
784struct HB_UnicodeMapping {
djsollen@google.com40078cb2013-05-24 20:31:57 +0000785 hb_script_t script;
786 const SkUnichar unicode;
787};
788
789/*
790 * The following scripts are not complex fonts and we do not expect them to be parsed by this table
791 * HB_SCRIPT_COMMON,
792 * HB_SCRIPT_GREEK,
793 * HB_SCRIPT_CYRILLIC,
794 * HB_SCRIPT_HANGUL
795 * HB_SCRIPT_INHERITED
796 */
797
798/* Harfbuzz (old) is missing a number of scripts in its table. For these,
799 * we include a value which can never happen. We won't get complex script
800 * shaping in these cases, but the library wouldn't know how to shape
801 * them anyway. */
802#define HB_Script_Unknown HB_ScriptCount
803
804static HB_UnicodeMapping HB_UnicodeMappingArray[] = {
commit-bot@chromium.org98000ef2013-12-18 19:49:27 +0000805 {HB_SCRIPT_ARMENIAN, 0x0531},
806 {HB_SCRIPT_HEBREW, 0x0591},
807 {HB_SCRIPT_ARABIC, 0x0600},
808 {HB_SCRIPT_SYRIAC, 0x0710},
809 {HB_SCRIPT_THAANA, 0x0780},
810 {HB_SCRIPT_NKO, 0x07C0},
811 {HB_SCRIPT_DEVANAGARI, 0x0901},
812 {HB_SCRIPT_BENGALI, 0x0981},
813 {HB_SCRIPT_GURMUKHI, 0x0A10},
814 {HB_SCRIPT_GUJARATI, 0x0A90},
815 {HB_SCRIPT_ORIYA, 0x0B10},
816 {HB_SCRIPT_TAMIL, 0x0B82},
817 {HB_SCRIPT_TELUGU, 0x0C10},
818 {HB_SCRIPT_KANNADA, 0x0C90},
819 {HB_SCRIPT_MALAYALAM, 0x0D10},
820 {HB_SCRIPT_SINHALA, 0x0D90},
821 {HB_SCRIPT_THAI, 0x0E01},
822 {HB_SCRIPT_LAO, 0x0E81},
823 {HB_SCRIPT_TIBETAN, 0x0F00},
824 {HB_SCRIPT_MYANMAR, 0x1000},
825 {HB_SCRIPT_GEORGIAN, 0x10A0},
826 {HB_SCRIPT_ETHIOPIC, 0x1200},
827 {HB_SCRIPT_CHEROKEE, 0x13A0},
828 {HB_SCRIPT_OGHAM, 0x1680},
829 {HB_SCRIPT_RUNIC, 0x16A0},
830 {HB_SCRIPT_KHMER, 0x1780},
831 {HB_SCRIPT_TAI_LE, 0x1950},
832 {HB_SCRIPT_NEW_TAI_LUE, 0x1980},
833 {HB_SCRIPT_TAI_THAM, 0x1A20},
834 {HB_SCRIPT_CHAM, 0xAA00},
djsollen@google.com40078cb2013-05-24 20:31:57 +0000835};
836
djsollen@google.com40078cb2013-05-24 20:31:57 +0000837// returns 0 for "Not Found"
838static SkUnichar getUnicodeFromHBScript(hb_script_t script) {
839 SkUnichar unichar = 0;
840 int numSupportedFonts = sizeof(HB_UnicodeMappingArray) / sizeof(HB_UnicodeMapping);
841 for (int i = 0; i < numSupportedFonts; i++) {
842 if (script == HB_UnicodeMappingArray[i].script) {
843 unichar = HB_UnicodeMappingArray[i].unicode;
844 break;
845 }
846 }
847 return unichar;
848}
849
850struct TypefaceLookupStruct {
851 hb_script_t script;
852 SkTypeface::Style style;
853 SkPaintOptionsAndroid::FontVariant fontVariant;
854 SkTypeface* typeface;
855};
856
857SK_DECLARE_STATIC_MUTEX(gTypefaceTableMutex); // This is the mutex for gTypefaceTable
858static SkTDArray<TypefaceLookupStruct> gTypefaceTable; // This is protected by gTypefaceTableMutex
859
860static int typefaceLookupCompare(const TypefaceLookupStruct& first,
861 const TypefaceLookupStruct& second) {
862 if (first.script != second.script) {
863 return (first.script > second.script) ? 1 : -1;
864 }
865 if (first.style != second.style) {
866 return (first.style > second.style) ? 1 : -1;
867 }
868 if (first.fontVariant != second.fontVariant) {
869 return (first.fontVariant > second.fontVariant) ? 1 : -1;
870 }
871 return 0;
872}
873
commit-bot@chromium.org98000ef2013-12-18 19:49:27 +0000874SkTypeface* SkCreateTypefaceForScript(hb_script_t script, SkTypeface::Style style,
875 SkPaintOptionsAndroid::FontVariant fontVariant) {
djsollen@google.com40078cb2013-05-24 20:31:57 +0000876 SkAutoMutexAcquire ac(gTypefaceTableMutex);
877
878 TypefaceLookupStruct key;
879 key.script = script;
880 key.style = style;
881 key.fontVariant = fontVariant;
882
883 int index = SkTSearch<TypefaceLookupStruct>(
884 (const TypefaceLookupStruct*) gTypefaceTable.begin(),
885 gTypefaceTable.count(), key, sizeof(TypefaceLookupStruct),
886 typefaceLookupCompare);
887
888 SkTypeface* retTypeface = NULL;
889 if (index >= 0) {
890 retTypeface = gTypefaceTable[index].typeface;
891 }
892 else {
893 SkUnichar unichar = getUnicodeFromHBScript(script);
894 if (!unichar) {
895 return NULL;
896 }
897
898 SkFontConfigInterfaceAndroid* fontConfig = getSingletonInterface();
899 retTypeface = fontConfig->getTypefaceForChar(unichar, style, fontVariant);
900
901 // add to the lookup table
902 key.typeface = retTypeface;
903 *gTypefaceTable.insert(~index) = key;
904 }
905
906 // we ref(), the caller is expected to unref when they are done
907 return SkSafeRef(retTypeface);
908}
909
djsollen@google.com40078cb2013-05-24 20:31:57 +0000910#endif
911
912///////////////////////////////////////////////////////////////////////////////
913
djsollen@google.combfae9d32013-05-21 16:53:50 +0000914SkFontMgr* SkFontMgr::Factory() {
bungeman@google.com451b5962013-11-11 18:36:46 +0000915 return NULL;
djsollen@google.combfae9d32013-05-21 16:53:50 +0000916}