blob: 2c31dacd97b47072ce96379590fc37d267779375 [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/* libs/graphics/ports/SkFontHost_android.cpp
2**
3** Copyright 2006, The Android Open Source Project
4**
reed@google.com4f809512011-01-04 21:25:43 +00005** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
reed@android.com8a1c16f2008-12-17 15:59:43 +00008**
reed@google.com4f809512011-01-04 21:25:43 +00009** http://www.apache.org/licenses/LICENSE-2.0
reed@android.com8a1c16f2008-12-17 15:59:43 +000010**
reed@google.com4f809512011-01-04 21:25:43 +000011** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
reed@android.com8a1c16f2008-12-17 15:59:43 +000015** limitations under the License.
16*/
17
18#include "SkFontHost.h"
19#include "SkDescriptor.h"
20#include "SkMMapStream.h"
21#include "SkPaint.h"
22#include "SkString.h"
23#include "SkStream.h"
24#include "SkThread.h"
25#include "SkTSearch.h"
26#include <stdio.h>
27
28#define FONT_CACHE_MEMORY_BUDGET (768 * 1024)
29
30#ifndef SK_FONT_FILE_PREFIX
31 #define SK_FONT_FILE_PREFIX "/fonts/"
32#endif
33
reed@google.com5b31b0f2011-02-23 14:41:42 +000034SkTypeface::Style find_name_and_attributes(SkStream* stream, SkString* name,
35 bool* isFixedWidth);
reed@android.com8a1c16f2008-12-17 15:59:43 +000036
reed@android.comfcce6472009-03-20 12:23:07 +000037static void GetFullPathForSysFonts(SkString* full, const char name[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000038 full->set(getenv("ANDROID_ROOT"));
39 full->append(SK_FONT_FILE_PREFIX);
40 full->append(name);
41}
42
43///////////////////////////////////////////////////////////////////////////////
44
45struct FamilyRec;
46
47/* This guy holds a mapping of a name -> family, used for looking up fonts.
48 Since it is stored in a stretchy array that doesn't preserve object
49 semantics, we don't use constructor/destructors, but just have explicit
50 helpers to manage our internal bookkeeping.
51*/
52struct NameFamilyPair {
53 const char* fName; // we own this
54 FamilyRec* fFamily; // we don't own this, we just reference it
reed@google.com4f809512011-01-04 21:25:43 +000055
reed@android.comfcce6472009-03-20 12:23:07 +000056 void construct(const char name[], FamilyRec* family) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000057 fName = strdup(name);
58 fFamily = family; // we don't own this, so just record the referene
59 }
reed@android.comfcce6472009-03-20 12:23:07 +000060
61 void destruct() {
reed@android.com8a1c16f2008-12-17 15:59:43 +000062 free((char*)fName);
63 // we don't own family, so just ignore our reference
64 }
65};
66
67// we use atomic_inc to grow this for each typeface we create
68static int32_t gUniqueFontID;
69
70// this is the mutex that protects these globals
71static SkMutex gFamilyMutex;
72static FamilyRec* gFamilyHead;
73static SkTDArray<NameFamilyPair> gNameList;
74
75struct FamilyRec {
76 FamilyRec* fNext;
77 SkTypeface* fFaces[4];
reed@google.com4f809512011-01-04 21:25:43 +000078
reed@android.com8a1c16f2008-12-17 15:59:43 +000079 FamilyRec()
80 {
81 fNext = gFamilyHead;
82 memset(fFaces, 0, sizeof(fFaces));
83 gFamilyHead = this;
84 }
85};
86
87static SkTypeface* find_best_face(const FamilyRec* family,
reed@android.comfcce6472009-03-20 12:23:07 +000088 SkTypeface::Style style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000089 SkTypeface* const* faces = family->fFaces;
90
91 if (faces[style] != NULL) { // exact match
92 return faces[style];
93 }
94 // look for a matching bold
95 style = (SkTypeface::Style)(style ^ SkTypeface::kItalic);
96 if (faces[style] != NULL) {
97 return faces[style];
98 }
99 // look for the plain
100 if (faces[SkTypeface::kNormal] != NULL) {
101 return faces[SkTypeface::kNormal];
102 }
103 // look for anything
104 for (int i = 0; i < 4; i++) {
105 if (faces[i] != NULL) {
106 return faces[i];
107 }
108 }
109 // should never get here, since the faces list should not be empty
110 SkASSERT(!"faces list is empty");
111 return NULL;
112}
113
reed@android.comfcce6472009-03-20 12:23:07 +0000114static FamilyRec* find_family(const SkTypeface* member) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000115 FamilyRec* curr = gFamilyHead;
116 while (curr != NULL) {
117 for (int i = 0; i < 4; i++) {
118 if (curr->fFaces[i] == member) {
119 return curr;
120 }
121 }
122 curr = curr->fNext;
123 }
124 return NULL;
125}
126
reed@android.comb1d9d2e2009-03-04 17:37:51 +0000127/* Returns the matching typeface, or NULL. If a typeface is found, its refcnt
128 is not modified.
129 */
reed@android.comfcce6472009-03-20 12:23:07 +0000130static SkTypeface* find_from_uniqueID(uint32_t uniqueID) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000131 FamilyRec* curr = gFamilyHead;
132 while (curr != NULL) {
133 for (int i = 0; i < 4; i++) {
134 SkTypeface* face = curr->fFaces[i];
135 if (face != NULL && face->uniqueID() == uniqueID) {
136 return face;
137 }
138 }
139 curr = curr->fNext;
140 }
141 return NULL;
142}
143
144/* Remove reference to this face from its family. If the resulting family
145 is empty (has no faces), return that family, otherwise return NULL
146*/
reed@android.comfcce6472009-03-20 12:23:07 +0000147static FamilyRec* remove_from_family(const SkTypeface* face) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000148 FamilyRec* family = find_family(face);
149 SkASSERT(family->fFaces[face->style()] == face);
150 family->fFaces[face->style()] = NULL;
reed@google.com4f809512011-01-04 21:25:43 +0000151
reed@android.com8a1c16f2008-12-17 15:59:43 +0000152 for (int i = 0; i < 4; i++) {
153 if (family->fFaces[i] != NULL) { // family is non-empty
154 return NULL;
155 }
156 }
157 return family; // return the empty family
158}
159
160// maybe we should make FamilyRec be doubly-linked
reed@android.comfcce6472009-03-20 12:23:07 +0000161static void detach_and_delete_family(FamilyRec* family) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000162 FamilyRec* curr = gFamilyHead;
163 FamilyRec* prev = NULL;
164
165 while (curr != NULL) {
166 FamilyRec* next = curr->fNext;
167 if (curr == family) {
168 if (prev == NULL) {
169 gFamilyHead = next;
170 } else {
171 prev->fNext = next;
172 }
173 SkDELETE(family);
174 return;
175 }
176 prev = curr;
177 curr = next;
178 }
179 SkASSERT(!"Yikes, couldn't find family in our list to remove/delete");
180}
181
reed@android.comfcce6472009-03-20 12:23:07 +0000182static SkTypeface* find_typeface(const char name[], SkTypeface::Style style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000183 NameFamilyPair* list = gNameList.begin();
184 int count = gNameList.count();
reed@google.com4f809512011-01-04 21:25:43 +0000185
reed@android.com8a1c16f2008-12-17 15:59:43 +0000186 int index = SkStrLCSearch(&list[0].fName, count, name, sizeof(list[0]));
187
188 if (index >= 0) {
189 return find_best_face(list[index].fFamily, style);
190 }
191 return NULL;
192}
193
194static SkTypeface* find_typeface(const SkTypeface* familyMember,
reed@android.comfcce6472009-03-20 12:23:07 +0000195 SkTypeface::Style style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000196 const FamilyRec* family = find_family(familyMember);
197 return family ? find_best_face(family, style) : NULL;
198}
199
reed@android.comfcce6472009-03-20 12:23:07 +0000200static void add_name(const char name[], FamilyRec* family) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000201 SkAutoAsciiToLC tolc(name);
202 name = tolc.lc();
203
204 NameFamilyPair* list = gNameList.begin();
205 int count = gNameList.count();
reed@google.com4f809512011-01-04 21:25:43 +0000206
reed@android.com8a1c16f2008-12-17 15:59:43 +0000207 int index = SkStrLCSearch(&list[0].fName, count, name, sizeof(list[0]));
208
209 if (index < 0) {
210 list = gNameList.insert(~index);
211 list->construct(name, family);
212 }
213}
214
215static void remove_from_names(FamilyRec* emptyFamily)
216{
217#ifdef SK_DEBUG
218 for (int i = 0; i < 4; i++) {
219 SkASSERT(emptyFamily->fFaces[i] == NULL);
220 }
221#endif
222
223 SkTDArray<NameFamilyPair>& list = gNameList;
reed@google.com4f809512011-01-04 21:25:43 +0000224
reed@android.com8a1c16f2008-12-17 15:59:43 +0000225 // must go backwards when removing
226 for (int i = list.count() - 1; i >= 0; --i) {
227 NameFamilyPair* pair = &list[i];
228 if (pair->fFamily == emptyFamily) {
229 pair->destruct();
230 list.remove(i);
231 }
232 }
233}
234
235///////////////////////////////////////////////////////////////////////////////
236
237class FamilyTypeface : public SkTypeface {
238public:
reed@google.com5b31b0f2011-02-23 14:41:42 +0000239 FamilyTypeface(Style style, bool sysFont, SkTypeface* familyMember,
240 bool isFixedWidth)
241 : SkTypeface(style, sk_atomic_inc(&gUniqueFontID) + 1, isFixedWidth) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242 fIsSysFont = sysFont;
reed@google.com4f809512011-01-04 21:25:43 +0000243
reed@android.com8a1c16f2008-12-17 15:59:43 +0000244 SkAutoMutexAcquire ac(gFamilyMutex);
reed@google.com4f809512011-01-04 21:25:43 +0000245
reed@android.com8a1c16f2008-12-17 15:59:43 +0000246 FamilyRec* rec = NULL;
247 if (familyMember) {
248 rec = find_family(familyMember);
249 SkASSERT(rec);
250 } else {
251 rec = SkNEW(FamilyRec);
252 }
253 rec->fFaces[style] = this;
254 }
reed@google.com4f809512011-01-04 21:25:43 +0000255
reed@android.comfcce6472009-03-20 12:23:07 +0000256 virtual ~FamilyTypeface() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000257 SkAutoMutexAcquire ac(gFamilyMutex);
reed@google.com4f809512011-01-04 21:25:43 +0000258
reed@android.com8a1c16f2008-12-17 15:59:43 +0000259 // remove us from our family. If the family is now empty, we return
260 // that and then remove that family from the name list
261 FamilyRec* family = remove_from_family(this);
262 if (NULL != family) {
263 remove_from_names(family);
264 detach_and_delete_family(family);
265 }
266 }
reed@google.com4f809512011-01-04 21:25:43 +0000267
reed@android.com8a1c16f2008-12-17 15:59:43 +0000268 bool isSysFont() const { return fIsSysFont; }
reed@google.com4f809512011-01-04 21:25:43 +0000269
reed@android.com8a1c16f2008-12-17 15:59:43 +0000270 virtual SkStream* openStream() = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000271 virtual const char* getUniqueString() const = 0;
reed@android.comac981542009-07-31 16:17:01 +0000272 virtual const char* getFilePath() const = 0;
reed@google.com4f809512011-01-04 21:25:43 +0000273
reed@android.com8a1c16f2008-12-17 15:59:43 +0000274private:
275 bool fIsSysFont;
reed@google.com4f809512011-01-04 21:25:43 +0000276
reed@android.com8a1c16f2008-12-17 15:59:43 +0000277 typedef SkTypeface INHERITED;
278};
279
280///////////////////////////////////////////////////////////////////////////////
281
282class StreamTypeface : public FamilyTypeface {
283public:
284 StreamTypeface(Style style, bool sysFont, SkTypeface* familyMember,
reed@google.com5b31b0f2011-02-23 14:41:42 +0000285 SkStream* stream, bool isFixedWidth)
286 : INHERITED(style, sysFont, familyMember, isFixedWidth) {
reed@android.comb1d9d2e2009-03-04 17:37:51 +0000287 SkASSERT(stream);
288 stream->ref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000289 fStream = stream;
290 }
reed@android.comb1d9d2e2009-03-04 17:37:51 +0000291 virtual ~StreamTypeface() {
292 fStream->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000293 }
reed@google.com4f809512011-01-04 21:25:43 +0000294
reed@android.com8a1c16f2008-12-17 15:59:43 +0000295 // overrides
reed@android.comb1d9d2e2009-03-04 17:37:51 +0000296 virtual SkStream* openStream() {
297 // we just ref our existing stream, since the caller will call unref()
298 // when they are through
299 fStream->ref();
reed@google.com4f809512011-01-04 21:25:43 +0000300 // must rewind each time, since the caller assumes a "new" stream
301 fStream->rewind();
reed@android.comb1d9d2e2009-03-04 17:37:51 +0000302 return fStream;
303 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000304 virtual const char* getUniqueString() const { return NULL; }
reed@android.comac981542009-07-31 16:17:01 +0000305 virtual const char* getFilePath() const { return NULL; }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000306
307private:
308 SkStream* fStream;
reed@google.com4f809512011-01-04 21:25:43 +0000309
reed@android.com8a1c16f2008-12-17 15:59:43 +0000310 typedef FamilyTypeface INHERITED;
311};
312
313class FileTypeface : public FamilyTypeface {
314public:
315 FileTypeface(Style style, bool sysFont, SkTypeface* familyMember,
reed@google.com5b31b0f2011-02-23 14:41:42 +0000316 const char path[], bool isFixedWidth)
317 : INHERITED(style, sysFont, familyMember, isFixedWidth) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000318 SkString fullpath;
reed@google.com4f809512011-01-04 21:25:43 +0000319
reed@android.com8a1c16f2008-12-17 15:59:43 +0000320 if (sysFont) {
321 GetFullPathForSysFonts(&fullpath, path);
322 path = fullpath.c_str();
323 }
324 fPath.set(path);
325 }
reed@google.com4f809512011-01-04 21:25:43 +0000326
reed@android.com8a1c16f2008-12-17 15:59:43 +0000327 // overrides
reed@android.comfcce6472009-03-20 12:23:07 +0000328 virtual SkStream* openStream() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000329 SkStream* stream = SkNEW_ARGS(SkMMAPStream, (fPath.c_str()));
330
331 // check for failure
332 if (stream->getLength() <= 0) {
333 SkDELETE(stream);
334 // maybe MMAP isn't supported. try FILE
335 stream = SkNEW_ARGS(SkFILEStream, (fPath.c_str()));
336 if (stream->getLength() <= 0) {
337 SkDELETE(stream);
338 stream = NULL;
339 }
340 }
341 return stream;
342 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000343 virtual const char* getUniqueString() const {
344 const char* str = strrchr(fPath.c_str(), '/');
345 if (str) {
346 str += 1; // skip the '/'
347 }
348 return str;
349 }
reed@android.comac981542009-07-31 16:17:01 +0000350 virtual const char* getFilePath() const {
351 return fPath.c_str();
352 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000353
354private:
355 SkString fPath;
reed@google.com4f809512011-01-04 21:25:43 +0000356
reed@android.com8a1c16f2008-12-17 15:59:43 +0000357 typedef FamilyTypeface INHERITED;
358};
359
360///////////////////////////////////////////////////////////////////////////////
361///////////////////////////////////////////////////////////////////////////////
362
363static bool get_name_and_style(const char path[], SkString* name,
reed@google.com5b31b0f2011-02-23 14:41:42 +0000364 SkTypeface::Style* style,
365 bool* isFixedWidth, bool isExpected) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000366 SkString fullpath;
367 GetFullPathForSysFonts(&fullpath, path);
368
369 SkMMAPStream stream(fullpath.c_str());
370 if (stream.getLength() > 0) {
reed@google.com5b31b0f2011-02-23 14:41:42 +0000371 *style = find_name_and_attributes(&stream, name, isFixedWidth);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000372 return true;
373 }
374 else {
375 SkFILEStream stream(fullpath.c_str());
376 if (stream.getLength() > 0) {
reed@google.com5b31b0f2011-02-23 14:41:42 +0000377 *style = find_name_and_attributes(&stream, name, isFixedWidth);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000378 return true;
379 }
380 }
381
reed@android.comfcce6472009-03-20 12:23:07 +0000382 if (isExpected) {
383 SkDebugf("---- failed to open <%s> as a font\n", fullpath.c_str());
384 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000385 return false;
386}
387
reed@android.comfcce6472009-03-20 12:23:07 +0000388// used to record our notion of the pre-existing fonts
reed@android.com8a1c16f2008-12-17 15:59:43 +0000389struct FontInitRec {
390 const char* fFileName;
391 const char* const* fNames; // null-terminated list
392};
393
394static const char* gSansNames[] = {
395 "sans-serif", "arial", "helvetica", "tahoma", "verdana", NULL
396};
397
398static const char* gSerifNames[] = {
399 "serif", "times", "times new roman", "palatino", "georgia", "baskerville",
400 "goudy", "fantasy", "cursive", "ITC Stone Serif", NULL
401};
402
403static const char* gMonoNames[] = {
404 "monospace", "courier", "courier new", "monaco", NULL
405};
406
reed@android.comfcce6472009-03-20 12:23:07 +0000407// deliberately empty, but we use the address to identify fallback fonts
reed@android.com8a1c16f2008-12-17 15:59:43 +0000408static const char* gFBNames[] = { NULL };
409
410/* Fonts must be grouped by family, with the first font in a family having the
411 list of names (even if that list is empty), and the following members having
412 null for the list. The names list must be NULL-terminated
413*/
414static const FontInitRec gSystemFonts[] = {
415 { "DroidSans.ttf", gSansNames },
416 { "DroidSans-Bold.ttf", NULL },
417 { "DroidSerif-Regular.ttf", gSerifNames },
418 { "DroidSerif-Bold.ttf", NULL },
419 { "DroidSerif-Italic.ttf", NULL },
420 { "DroidSerif-BoldItalic.ttf", NULL },
421 { "DroidSansMono.ttf", gMonoNames },
reed@android.comfcce6472009-03-20 12:23:07 +0000422 /* These are optional, and can be ignored if not found in the file system.
423 These are appended to gFallbackFonts[] as they are seen, so we list
424 them in the order we want them to be accessed by NextLogicalFont().
425 */
reed@android.comfaea3552010-03-02 19:50:40 +0000426 { "DroidSansArabic.ttf", gFBNames },
427 { "DroidSansHebrew.ttf", gFBNames },
428 { "DroidSansThai.ttf", gFBNames },
reed@android.comfcce6472009-03-20 12:23:07 +0000429 { "DroidSansJapanese.ttf", gFBNames },
reed@android.com8a1c16f2008-12-17 15:59:43 +0000430 { "DroidSansFallback.ttf", gFBNames }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000431};
432
433#define DEFAULT_NAMES gSansNames
434
435// these globals are assigned (once) by load_system_fonts()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000436static FamilyRec* gDefaultFamily;
437static SkTypeface* gDefaultNormal;
438
reed@android.comfcce6472009-03-20 12:23:07 +0000439/* This is sized conservatively, assuming that it will never be a size issue.
440 It will be initialized in load_system_fonts(), and will be filled with the
441 fontIDs that can be used for fallback consideration, in sorted order (sorted
442 meaning element[0] should be used first, then element[1], etc. When we hit
443 a fontID==0 in the array, the list is done, hence our allocation size is
444 +1 the total number of possible system fonts. Also see NextLogicalFont().
445 */
446static uint32_t gFallbackFonts[SK_ARRAY_COUNT(gSystemFonts)+1];
447
448/* Called once (ensured by the sentinel check at the beginning of our body).
449 Initializes all the globals, and register the system fonts.
450 */
451static void load_system_fonts() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000452 // check if we've already be called
453 if (NULL != gDefaultNormal) {
454 return;
455 }
reed@google.com4f809512011-01-04 21:25:43 +0000456
reed@android.com8a1c16f2008-12-17 15:59:43 +0000457 const FontInitRec* rec = gSystemFonts;
458 SkTypeface* firstInFamily = NULL;
reed@android.comfcce6472009-03-20 12:23:07 +0000459 int fallbackCount = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000460
461 for (size_t i = 0; i < SK_ARRAY_COUNT(gSystemFonts); i++) {
462 // if we're the first in a new family, clear firstInFamily
463 if (rec[i].fNames != NULL) {
464 firstInFamily = NULL;
465 }
reed@google.com4f809512011-01-04 21:25:43 +0000466
reed@google.com5b31b0f2011-02-23 14:41:42 +0000467 bool isFixedWidth;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000468 SkString name;
469 SkTypeface::Style style;
reed@android.comfcce6472009-03-20 12:23:07 +0000470
471 // we expect all the fonts, except the "fallback" fonts
472 bool isExpected = (rec[i].fNames != gFBNames);
reed@google.com5b31b0f2011-02-23 14:41:42 +0000473 if (!get_name_and_style(rec[i].fFileName, &name, &style,
474 &isFixedWidth, isExpected)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000475 continue;
476 }
477
478 SkTypeface* tf = SkNEW_ARGS(FileTypeface,
479 (style,
480 true, // system-font (cannot delete)
481 firstInFamily, // what family to join
reed@google.com5b31b0f2011-02-23 14:41:42 +0000482 rec[i].fFileName,
483 isFixedWidth) // filename
reed@android.com8a1c16f2008-12-17 15:59:43 +0000484 );
485
486 if (rec[i].fNames != NULL) {
reed@android.comfcce6472009-03-20 12:23:07 +0000487 // see if this is one of our fallback fonts
488 if (rec[i].fNames == gFBNames) {
489 // SkDebugf("---- adding %s as fallback[%d] fontID %d\n",
490 // rec[i].fFileName, fallbackCount, tf->uniqueID());
491 gFallbackFonts[fallbackCount++] = tf->uniqueID();
492 }
493
reed@android.com8a1c16f2008-12-17 15:59:43 +0000494 firstInFamily = tf;
reed@android.comfcce6472009-03-20 12:23:07 +0000495 FamilyRec* family = find_family(tf);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000496 const char* const* names = rec[i].fNames;
497
reed@android.com8a1c16f2008-12-17 15:59:43 +0000498 // record the default family if this is it
499 if (names == DEFAULT_NAMES) {
reed@android.comfcce6472009-03-20 12:23:07 +0000500 gDefaultFamily = family;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000501 }
502 // add the names to map to this family
reed@android.com8a1c16f2008-12-17 15:59:43 +0000503 while (*names) {
504 add_name(*names, family);
505 names += 1;
506 }
507 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000508 }
509
510 // do this after all fonts are loaded. This is our default font, and it
511 // acts as a sentinel so we only execute load_system_fonts() once
512 gDefaultNormal = find_best_face(gDefaultFamily, SkTypeface::kNormal);
reed@android.comfcce6472009-03-20 12:23:07 +0000513 // now terminate our fallback list with the sentinel value
514 gFallbackFonts[fallbackCount] = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000515}
516
517///////////////////////////////////////////////////////////////////////////////
518
519void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
520 const char* name = ((FamilyTypeface*)face)->getUniqueString();
521
reed@android.comb1d9d2e2009-03-04 17:37:51 +0000522 stream->write8((uint8_t)face->style());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000523
524 if (NULL == name || 0 == *name) {
525 stream->writePackedUInt(0);
526// SkDebugf("--- fonthost serialize null\n");
527 } else {
528 uint32_t len = strlen(name);
529 stream->writePackedUInt(len);
530 stream->write(name, len);
reed@android.comb1d9d2e2009-03-04 17:37:51 +0000531// SkDebugf("--- fonthost serialize <%s> %d\n", name, face->style());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000532 }
533}
534
535SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
536 load_system_fonts();
537
538 int style = stream->readU8();
539
540 int len = stream->readPackedUInt();
541 if (len > 0) {
542 SkString str;
543 str.resize(len);
544 stream->read(str.writable_str(), len);
reed@google.com4f809512011-01-04 21:25:43 +0000545
reed@android.com8a1c16f2008-12-17 15:59:43 +0000546 const FontInitRec* rec = gSystemFonts;
547 for (size_t i = 0; i < SK_ARRAY_COUNT(gSystemFonts); i++) {
548 if (strcmp(rec[i].fFileName, str.c_str()) == 0) {
549 // backup until we hit the fNames
550 for (int j = i; j >= 0; --j) {
551 if (rec[j].fNames != NULL) {
reed@android.comb1d9d2e2009-03-04 17:37:51 +0000552 return SkFontHost::CreateTypeface(NULL,
agl@chromium.org5f6a0762010-04-20 22:06:40 +0000553 rec[j].fNames[0], NULL, 0, (SkTypeface::Style)style);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000554 }
555 }
556 }
557 }
558 }
reed@android.com62533ed2009-03-06 15:57:26 +0000559 return NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000560}
561
562///////////////////////////////////////////////////////////////////////////////
563
reed@android.comb1d9d2e2009-03-04 17:37:51 +0000564SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
565 const char familyName[],
agl@chromium.org5f6a0762010-04-20 22:06:40 +0000566 const void* data, size_t bytelength,
reed@android.comfcce6472009-03-20 12:23:07 +0000567 SkTypeface::Style style) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000568 load_system_fonts();
569
570 SkAutoMutexAcquire ac(gFamilyMutex);
reed@google.com4f809512011-01-04 21:25:43 +0000571
reed@android.com8a1c16f2008-12-17 15:59:43 +0000572 // clip to legal style bits
573 style = (SkTypeface::Style)(style & SkTypeface::kBoldItalic);
reed@google.com4f809512011-01-04 21:25:43 +0000574
reed@android.com8a1c16f2008-12-17 15:59:43 +0000575 SkTypeface* tf = NULL;
576
577 if (NULL != familyFace) {
578 tf = find_typeface(familyFace, style);
579 } else if (NULL != familyName) {
580// SkDebugf("======= familyName <%s>\n", familyName);
581 tf = find_typeface(familyName, style);
582 }
583
584 if (NULL == tf) {
585 tf = find_best_face(gDefaultFamily, style);
586 }
587
reed@android.comfb12c3e2009-03-05 20:43:42 +0000588 // we ref(), since the symantic is to return a new instance
589 tf->ref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000590 return tf;
591}
592
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000593// static
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000594SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics(
595 uint32_t fontID, bool perGlyphInfo) {
596 SkASSERT(!"SkFontHost::GetAdvancedTypefaceMetrics unimplemented");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000597 return NULL;
598}
599
reed@android.comfcce6472009-03-20 12:23:07 +0000600bool SkFontHost::ValidFontID(uint32_t fontID) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000601 SkAutoMutexAcquire ac(gFamilyMutex);
reed@google.com4f809512011-01-04 21:25:43 +0000602
reed@android.comb1d9d2e2009-03-04 17:37:51 +0000603 return find_from_uniqueID(fontID) != NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000604}
605
reed@android.comfcce6472009-03-20 12:23:07 +0000606SkStream* SkFontHost::OpenStream(uint32_t fontID) {
reed@android.comfb12c3e2009-03-05 20:43:42 +0000607 SkAutoMutexAcquire ac(gFamilyMutex);
reed@google.com4f809512011-01-04 21:25:43 +0000608
reed@android.comb1d9d2e2009-03-04 17:37:51 +0000609 FamilyTypeface* tf = (FamilyTypeface*)find_from_uniqueID(fontID);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000610 SkStream* stream = tf ? tf->openStream() : NULL;
611
reed@android.comb1d9d2e2009-03-04 17:37:51 +0000612 if (stream && stream->getLength() == 0) {
613 stream->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000614 stream = NULL;
615 }
616 return stream;
617}
618
reed@android.comac981542009-07-31 16:17:01 +0000619size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length,
620 int32_t* index) {
621 SkAutoMutexAcquire ac(gFamilyMutex);
622
623 FamilyTypeface* tf = (FamilyTypeface*)find_from_uniqueID(fontID);
624 const char* src = tf ? tf->getFilePath() : NULL;
625
626 if (src) {
627 size_t size = strlen(src);
628 if (path) {
629 memcpy(path, src, SkMin32(size, length));
630 }
631 if (index) {
632 *index = 0; // we don't have collections (yet)
633 }
634 return size;
635 } else {
636 return 0;
637 }
638}
639
reed@android.coma14ea0e2009-03-17 17:59:53 +0000640uint32_t SkFontHost::NextLogicalFont(uint32_t fontID) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000641 load_system_fonts();
642
reed@android.comfcce6472009-03-20 12:23:07 +0000643 /* First see if fontID is already one of our fallbacks. If so, return
644 its successor. If fontID is not in our list, then return the first one
645 in our list. Note: list is zero-terminated, and returning zero means
646 we have no more fonts to use for fallbacks.
647 */
648 const uint32_t* list = gFallbackFonts;
649 for (int i = 0; list[i] != 0; i++) {
650 if (list[i] == fontID) {
651 return list[i+1];
652 }
reed@android.coma14ea0e2009-03-17 17:59:53 +0000653 }
reed@android.comfcce6472009-03-20 12:23:07 +0000654 return list[0];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000655}
656
657///////////////////////////////////////////////////////////////////////////////
658
reed@android.comfcce6472009-03-20 12:23:07 +0000659SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000660 if (NULL == stream || stream->getLength() <= 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000661 return NULL;
662 }
reed@google.com4f809512011-01-04 21:25:43 +0000663
reed@google.com5b31b0f2011-02-23 14:41:42 +0000664 bool isFixedWidth;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000665 SkString name;
reed@google.com5b31b0f2011-02-23 14:41:42 +0000666 SkTypeface::Style style = find_name_and_attributes(stream, &name, &isFixedWidth);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000667
reed@google.com5b31b0f2011-02-23 14:41:42 +0000668 return SkNEW_ARGS(StreamTypeface, (style, false, NULL, stream, isFixedWidth));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000669}
670
reed@android.comfcce6472009-03-20 12:23:07 +0000671SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
reed@android.comb1d9d2e2009-03-04 17:37:51 +0000672 SkStream* stream = SkNEW_ARGS(SkMMAPStream, (path));
673 SkTypeface* face = SkFontHost::CreateTypefaceFromStream(stream);
674 // since we created the stream, we let go of our ref() here
675 stream->unref();
676 return face;
reed@android.com1550a422008-12-23 20:10:57 +0000677}
678
reed@android.com8a1c16f2008-12-17 15:59:43 +0000679///////////////////////////////////////////////////////////////////////////////
680
reed@android.comfcce6472009-03-20 12:23:07 +0000681size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000682 if (sizeAllocatedSoFar > FONT_CACHE_MEMORY_BUDGET)
683 return sizeAllocatedSoFar - FONT_CACHE_MEMORY_BUDGET;
684 else
685 return 0; // nothing to do
686}
687