blob: d94edfca7cdc3c7b553c951fdeca840ff7a0befc [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
vandebo@chromium.org2a22e102011-01-25 21:01:34 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * Copyright 2011 The Android Open Source Project
vandebo@chromium.org2a22e102011-01-25 21:01:34 +00004 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00005 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
vandebo@chromium.org2a22e102011-01-25 21:01:34 +00007 */
8
epoger@google.comec3ed6a2011-07-28 14:26:00 +00009
reed@android.com9d3a9852010-01-08 14:07:42 +000010#include "SkFontHost.h"
11#include "SkDescriptor.h"
12#include "SkMMapStream.h"
13#include "SkPaint.h"
14#include "SkString.h"
15#include "SkStream.h"
16#include "SkThread.h"
17#include "SkTSearch.h"
18#include <stdio.h>
19
20#define FONT_CACHE_MEMORY_BUDGET (768 * 1024)
21
reed@google.com60169622011-03-14 15:08:44 +000022#ifdef SK_BUILD_FOR_MAC
23 #define SK_FONT_FILE_PREFIX "/Library/Fonts/"
24#else
25 #define SK_FONT_FILE_PREFIX "/skimages/"
26#endif
reed@android.com9d3a9852010-01-08 14:07:42 +000027
reed@google.com60169622011-03-14 15:08:44 +000028SkTypeface::Style find_name_and_attributes(SkStream* stream, SkString* name,
29 bool* isFixedWidth);
reed@android.com9d3a9852010-01-08 14:07:42 +000030
31static void GetFullPathForSysFonts(SkString* full, const char name[]) {
32 full->set(SK_FONT_FILE_PREFIX);
33 full->append(name);
34}
35
36///////////////////////////////////////////////////////////////////////////////
37
38struct FamilyRec;
39
40/* This guy holds a mapping of a name -> family, used for looking up fonts.
41 Since it is stored in a stretchy array that doesn't preserve object
42 semantics, we don't use constructor/destructors, but just have explicit
43 helpers to manage our internal bookkeeping.
44*/
45struct NameFamilyPair {
46 const char* fName; // we own this
47 FamilyRec* fFamily; // we don't own this, we just reference it
reed@google.com60169622011-03-14 15:08:44 +000048
reed@android.com9d3a9852010-01-08 14:07:42 +000049 void construct(const char name[], FamilyRec* family) {
50 fName = strdup(name);
51 fFamily = family; // we don't own this, so just record the referene
52 }
53
54 void destruct() {
55 free((char*)fName);
56 // we don't own family, so just ignore our reference
57 }
58};
59
60// we use atomic_inc to grow this for each typeface we create
61static int32_t gUniqueFontID;
62
63// this is the mutex that protects these globals
64static SkMutex gFamilyMutex;
65static FamilyRec* gFamilyHead;
66static SkTDArray<NameFamilyPair> gNameList;
67
68struct FamilyRec {
69 FamilyRec* fNext;
70 SkTypeface* fFaces[4];
reed@google.com60169622011-03-14 15:08:44 +000071
reed@android.com9d3a9852010-01-08 14:07:42 +000072 FamilyRec()
73 {
74 fNext = gFamilyHead;
75 memset(fFaces, 0, sizeof(fFaces));
76 gFamilyHead = this;
77 }
78};
79
80static SkTypeface* find_best_face(const FamilyRec* family,
81 SkTypeface::Style style) {
82 SkTypeface* const* faces = family->fFaces;
83
84 if (faces[style] != NULL) { // exact match
85 return faces[style];
86 }
87 // look for a matching bold
88 style = (SkTypeface::Style)(style ^ SkTypeface::kItalic);
89 if (faces[style] != NULL) {
90 return faces[style];
91 }
92 // look for the plain
93 if (faces[SkTypeface::kNormal] != NULL) {
94 return faces[SkTypeface::kNormal];
95 }
96 // look for anything
97 for (int i = 0; i < 4; i++) {
98 if (faces[i] != NULL) {
99 return faces[i];
100 }
101 }
102 // should never get here, since the faces list should not be empty
103 SkASSERT(!"faces list is empty");
104 return NULL;
105}
106
107static FamilyRec* find_family(const SkTypeface* member) {
108 FamilyRec* curr = gFamilyHead;
109 while (curr != NULL) {
110 for (int i = 0; i < 4; i++) {
111 if (curr->fFaces[i] == member) {
112 return curr;
113 }
114 }
115 curr = curr->fNext;
116 }
117 return NULL;
118}
119
120/* Returns the matching typeface, or NULL. If a typeface is found, its refcnt
121 is not modified.
122 */
123static SkTypeface* find_from_uniqueID(uint32_t uniqueID) {
124 FamilyRec* curr = gFamilyHead;
125 while (curr != NULL) {
126 for (int i = 0; i < 4; i++) {
127 SkTypeface* face = curr->fFaces[i];
128 if (face != NULL && face->uniqueID() == uniqueID) {
129 return face;
130 }
131 }
132 curr = curr->fNext;
133 }
134 return NULL;
135}
136
137/* Remove reference to this face from its family. If the resulting family
138 is empty (has no faces), return that family, otherwise return NULL
139*/
140static FamilyRec* remove_from_family(const SkTypeface* face) {
141 FamilyRec* family = find_family(face);
142 SkASSERT(family->fFaces[face->style()] == face);
143 family->fFaces[face->style()] = NULL;
reed@google.com60169622011-03-14 15:08:44 +0000144
reed@android.com9d3a9852010-01-08 14:07:42 +0000145 for (int i = 0; i < 4; i++) {
146 if (family->fFaces[i] != NULL) { // family is non-empty
147 return NULL;
148 }
149 }
150 return family; // return the empty family
151}
152
153// maybe we should make FamilyRec be doubly-linked
154static void detach_and_delete_family(FamilyRec* family) {
155 FamilyRec* curr = gFamilyHead;
156 FamilyRec* prev = NULL;
157
158 while (curr != NULL) {
159 FamilyRec* next = curr->fNext;
160 if (curr == family) {
161 if (prev == NULL) {
162 gFamilyHead = next;
163 } else {
164 prev->fNext = next;
165 }
166 SkDELETE(family);
167 return;
168 }
169 prev = curr;
170 curr = next;
171 }
172 SkASSERT(!"Yikes, couldn't find family in our list to remove/delete");
173}
174
175static SkTypeface* find_typeface(const char name[], SkTypeface::Style style) {
176 NameFamilyPair* list = gNameList.begin();
177 int count = gNameList.count();
reed@google.com60169622011-03-14 15:08:44 +0000178
reed@android.com9d3a9852010-01-08 14:07:42 +0000179 int index = SkStrLCSearch(&list[0].fName, count, name, sizeof(list[0]));
180
181 if (index >= 0) {
182 return find_best_face(list[index].fFamily, style);
183 }
184 return NULL;
185}
186
187static SkTypeface* find_typeface(const SkTypeface* familyMember,
188 SkTypeface::Style style) {
189 const FamilyRec* family = find_family(familyMember);
190 return family ? find_best_face(family, style) : NULL;
191}
192
193static void add_name(const char name[], FamilyRec* family) {
194 SkAutoAsciiToLC tolc(name);
195 name = tolc.lc();
196
197 NameFamilyPair* list = gNameList.begin();
198 int count = gNameList.count();
reed@google.com60169622011-03-14 15:08:44 +0000199
reed@android.com9d3a9852010-01-08 14:07:42 +0000200 int index = SkStrLCSearch(&list[0].fName, count, name, sizeof(list[0]));
201
202 if (index < 0) {
203 list = gNameList.insert(~index);
204 list->construct(name, family);
205 }
206}
207
208static void remove_from_names(FamilyRec* emptyFamily)
209{
210#ifdef SK_DEBUG
211 for (int i = 0; i < 4; i++) {
212 SkASSERT(emptyFamily->fFaces[i] == NULL);
213 }
214#endif
215
216 SkTDArray<NameFamilyPair>& list = gNameList;
reed@google.com60169622011-03-14 15:08:44 +0000217
reed@android.com9d3a9852010-01-08 14:07:42 +0000218 // must go backwards when removing
219 for (int i = list.count() - 1; i >= 0; --i) {
220 NameFamilyPair* pair = &list[i];
221 if (pair->fFamily == emptyFamily) {
222 pair->destruct();
223 list.remove(i);
224 }
225 }
226}
227
228///////////////////////////////////////////////////////////////////////////////
229
230class FamilyTypeface : public SkTypeface {
231public:
232 FamilyTypeface(Style style, bool sysFont, SkTypeface* familyMember)
233 : SkTypeface(style, sk_atomic_inc(&gUniqueFontID) + 1) {
234 fIsSysFont = sysFont;
reed@google.com60169622011-03-14 15:08:44 +0000235
reed@android.com9d3a9852010-01-08 14:07:42 +0000236 SkAutoMutexAcquire ac(gFamilyMutex);
reed@google.com60169622011-03-14 15:08:44 +0000237
reed@android.com9d3a9852010-01-08 14:07:42 +0000238 FamilyRec* rec = NULL;
239 if (familyMember) {
240 rec = find_family(familyMember);
241 SkASSERT(rec);
242 } else {
243 rec = SkNEW(FamilyRec);
244 }
245 rec->fFaces[style] = this;
246 }
reed@google.com60169622011-03-14 15:08:44 +0000247
reed@android.com9d3a9852010-01-08 14:07:42 +0000248 virtual ~FamilyTypeface() {
249 SkAutoMutexAcquire ac(gFamilyMutex);
reed@google.com60169622011-03-14 15:08:44 +0000250
reed@android.com9d3a9852010-01-08 14:07:42 +0000251 // remove us from our family. If the family is now empty, we return
252 // that and then remove that family from the name list
253 FamilyRec* family = remove_from_family(this);
254 if (NULL != family) {
255 remove_from_names(family);
256 detach_and_delete_family(family);
257 }
258 }
reed@google.com60169622011-03-14 15:08:44 +0000259
reed@android.com9d3a9852010-01-08 14:07:42 +0000260 bool isSysFont() const { return fIsSysFont; }
reed@google.com60169622011-03-14 15:08:44 +0000261
reed@android.com9d3a9852010-01-08 14:07:42 +0000262 virtual SkStream* openStream() = 0;
263 virtual const char* getUniqueString() const = 0;
264 virtual const char* getFilePath() const = 0;
reed@google.com60169622011-03-14 15:08:44 +0000265
reed@android.com9d3a9852010-01-08 14:07:42 +0000266private:
267 bool fIsSysFont;
reed@google.com60169622011-03-14 15:08:44 +0000268
reed@android.com9d3a9852010-01-08 14:07:42 +0000269 typedef SkTypeface INHERITED;
270};
271
272///////////////////////////////////////////////////////////////////////////////
273
274class StreamTypeface : public FamilyTypeface {
275public:
276 StreamTypeface(Style style, bool sysFont, SkTypeface* familyMember,
277 SkStream* stream)
278 : INHERITED(style, sysFont, familyMember) {
279 SkASSERT(stream);
280 stream->ref();
281 fStream = stream;
282 }
283 virtual ~StreamTypeface() {
284 fStream->unref();
285 }
reed@google.com60169622011-03-14 15:08:44 +0000286
reed@android.com9d3a9852010-01-08 14:07:42 +0000287 // overrides
288 virtual SkStream* openStream() {
289 // we just ref our existing stream, since the caller will call unref()
290 // when they are through
291 fStream->ref();
292 return fStream;
293 }
294 virtual const char* getUniqueString() const { return NULL; }
295 virtual const char* getFilePath() const { return NULL; }
296
297private:
298 SkStream* fStream;
reed@google.com60169622011-03-14 15:08:44 +0000299
reed@android.com9d3a9852010-01-08 14:07:42 +0000300 typedef FamilyTypeface INHERITED;
301};
302
303class FileTypeface : public FamilyTypeface {
304public:
305 FileTypeface(Style style, bool sysFont, SkTypeface* familyMember,
306 const char path[])
307 : INHERITED(style, sysFont, familyMember) {
308 SkString fullpath;
reed@google.com60169622011-03-14 15:08:44 +0000309
reed@android.com9d3a9852010-01-08 14:07:42 +0000310 if (sysFont) {
311 GetFullPathForSysFonts(&fullpath, path);
312 path = fullpath.c_str();
313 }
314 fPath.set(path);
315 }
reed@google.com60169622011-03-14 15:08:44 +0000316
reed@android.com9d3a9852010-01-08 14:07:42 +0000317 // overrides
318 virtual SkStream* openStream() {
319 SkStream* stream = SkNEW_ARGS(SkMMAPStream, (fPath.c_str()));
320
321 // check for failure
322 if (stream->getLength() <= 0) {
323 SkDELETE(stream);
324 // maybe MMAP isn't supported. try FILE
325 stream = SkNEW_ARGS(SkFILEStream, (fPath.c_str()));
326 if (stream->getLength() <= 0) {
327 SkDELETE(stream);
328 stream = NULL;
329 }
330 }
331 return stream;
332 }
333 virtual const char* getUniqueString() const {
334 const char* str = strrchr(fPath.c_str(), '/');
335 if (str) {
336 str += 1; // skip the '/'
337 }
338 return str;
339 }
340 virtual const char* getFilePath() const {
341 return fPath.c_str();
342 }
343
344private:
345 SkString fPath;
reed@google.com60169622011-03-14 15:08:44 +0000346
reed@android.com9d3a9852010-01-08 14:07:42 +0000347 typedef FamilyTypeface INHERITED;
348};
349
350///////////////////////////////////////////////////////////////////////////////
351///////////////////////////////////////////////////////////////////////////////
352
353static bool get_name_and_style(const char path[], SkString* name,
354 SkTypeface::Style* style, bool isExpected) {
reed@google.com60169622011-03-14 15:08:44 +0000355 bool isFixedWidth;
reed@android.com9d3a9852010-01-08 14:07:42 +0000356 SkString fullpath;
357 GetFullPathForSysFonts(&fullpath, path);
358
359 SkMMAPStream stream(fullpath.c_str());
360 if (stream.getLength() > 0) {
reed@google.com60169622011-03-14 15:08:44 +0000361 *style = find_name_and_attributes(&stream, name, &isFixedWidth);
reed@android.com9d3a9852010-01-08 14:07:42 +0000362 return true;
363 }
364 else {
365 SkFILEStream stream(fullpath.c_str());
366 if (stream.getLength() > 0) {
reed@google.com60169622011-03-14 15:08:44 +0000367 *style = find_name_and_attributes(&stream, name, &isFixedWidth);
reed@android.com9d3a9852010-01-08 14:07:42 +0000368 return true;
369 }
370 }
371
372 if (isExpected) {
373 SkDebugf("---- failed to open <%s> as a font\n", fullpath.c_str());
374 }
375 return false;
376}
377
378// used to record our notion of the pre-existing fonts
379struct FontInitRec {
380 const char* fFileName;
381 const char* const* fNames; // null-terminated list
382};
383
384static const char* gSansNames[] = {
385 "sans-serif", "arial", "helvetica", "tahoma", "verdana", NULL
386};
387
388static const char* gSerifNames[] = {
389 "serif", "times", "times new roman", "palatino", "georgia", "baskerville",
390 "goudy", "fantasy", "cursive", "ITC Stone Serif", NULL
391};
392
393static const char* gMonoNames[] = {
394 "monospace", "courier", "courier new", "monaco", NULL
395};
396
397// deliberately empty, but we use the address to identify fallback fonts
398static const char* gFBNames[] = { NULL };
399
400/* Fonts must be grouped by family, with the first font in a family having the
401 list of names (even if that list is empty), and the following members having
402 null for the list. The names list must be NULL-terminated
403*/
404static const FontInitRec gSystemFonts[] = {
reed@google.com60169622011-03-14 15:08:44 +0000405 { "Arial.ttf", gSansNames },
406 { "Times.ttf", gSerifNames },
reed@android.com9d3a9852010-01-08 14:07:42 +0000407 { "samplefont.ttf", gSansNames },
408};
409
410#define DEFAULT_NAMES gSansNames
411
412// these globals are assigned (once) by load_system_fonts()
413static FamilyRec* gDefaultFamily;
414static SkTypeface* gDefaultNormal;
415
416/* This is sized conservatively, assuming that it will never be a size issue.
417 It will be initialized in load_system_fonts(), and will be filled with the
418 fontIDs that can be used for fallback consideration, in sorted order (sorted
419 meaning element[0] should be used first, then element[1], etc. When we hit
420 a fontID==0 in the array, the list is done, hence our allocation size is
421 +1 the total number of possible system fonts. Also see NextLogicalFont().
422 */
423static uint32_t gFallbackFonts[SK_ARRAY_COUNT(gSystemFonts)+1];
424
425/* Called once (ensured by the sentinel check at the beginning of our body).
426 Initializes all the globals, and register the system fonts.
427 */
428static void load_system_fonts() {
429 // check if we've already be called
430 if (NULL != gDefaultNormal) {
431 return;
432 }
reed@google.com60169622011-03-14 15:08:44 +0000433
reed@android.com9d3a9852010-01-08 14:07:42 +0000434 const FontInitRec* rec = gSystemFonts;
435 SkTypeface* firstInFamily = NULL;
436 int fallbackCount = 0;
437
438 for (size_t i = 0; i < SK_ARRAY_COUNT(gSystemFonts); i++) {
439 // if we're the first in a new family, clear firstInFamily
440 if (rec[i].fNames != NULL) {
441 firstInFamily = NULL;
442 }
reed@google.com60169622011-03-14 15:08:44 +0000443
reed@android.com9d3a9852010-01-08 14:07:42 +0000444 SkString name;
445 SkTypeface::Style style;
446
447 // we expect all the fonts, except the "fallback" fonts
448 bool isExpected = (rec[i].fNames != gFBNames);
449 if (!get_name_and_style(rec[i].fFileName, &name, &style, isExpected)) {
450 continue;
451 }
452
453 SkTypeface* tf = SkNEW_ARGS(FileTypeface,
454 (style,
455 true, // system-font (cannot delete)
456 firstInFamily, // what family to join
457 rec[i].fFileName) // filename
458 );
459
460 if (rec[i].fNames != NULL) {
461 // see if this is one of our fallback fonts
462 if (rec[i].fNames == gFBNames) {
463 // SkDebugf("---- adding %s as fallback[%d] fontID %d\n",
464 // rec[i].fFileName, fallbackCount, tf->uniqueID());
465 gFallbackFonts[fallbackCount++] = tf->uniqueID();
466 }
467
468 firstInFamily = tf;
469 FamilyRec* family = find_family(tf);
470 const char* const* names = rec[i].fNames;
471
472 // record the default family if this is it
473 if (names == DEFAULT_NAMES) {
474 gDefaultFamily = family;
475 }
476 // add the names to map to this family
477 while (*names) {
478 add_name(*names, family);
479 names += 1;
480 }
481 }
482 }
483
484 // do this after all fonts are loaded. This is our default font, and it
485 // acts as a sentinel so we only execute load_system_fonts() once
486 gDefaultNormal = find_best_face(gDefaultFamily, SkTypeface::kNormal);
487 // now terminate our fallback list with the sentinel value
488 gFallbackFonts[fallbackCount] = 0;
489}
490
491///////////////////////////////////////////////////////////////////////////////
492
493void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
494 const char* name = ((FamilyTypeface*)face)->getUniqueString();
495
496 stream->write8((uint8_t)face->style());
497
498 if (NULL == name || 0 == *name) {
499 stream->writePackedUInt(0);
500// SkDebugf("--- fonthost serialize null\n");
501 } else {
502 uint32_t len = strlen(name);
503 stream->writePackedUInt(len);
504 stream->write(name, len);
505// SkDebugf("--- fonthost serialize <%s> %d\n", name, face->style());
506 }
507}
508
509SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
510 load_system_fonts();
511
512 int style = stream->readU8();
513
514 int len = stream->readPackedUInt();
515 if (len > 0) {
516 SkString str;
517 str.resize(len);
518 stream->read(str.writable_str(), len);
reed@google.com60169622011-03-14 15:08:44 +0000519
reed@android.com9d3a9852010-01-08 14:07:42 +0000520 const FontInitRec* rec = gSystemFonts;
521 for (size_t i = 0; i < SK_ARRAY_COUNT(gSystemFonts); i++) {
522 if (strcmp(rec[i].fFileName, str.c_str()) == 0) {
523 // backup until we hit the fNames
524 for (int j = i; j >= 0; --j) {
525 if (rec[j].fNames != NULL) {
526 return SkFontHost::CreateTypeface(NULL,
agl@chromium.org5f6a0762010-04-20 22:06:40 +0000527 rec[j].fNames[0], NULL, 0, (SkTypeface::Style)style);
reed@android.com9d3a9852010-01-08 14:07:42 +0000528 }
529 }
530 }
531 }
532 }
533 return NULL;
534}
535
536///////////////////////////////////////////////////////////////////////////////
537
538SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
539 const char familyName[],
agl@chromium.org5f6a0762010-04-20 22:06:40 +0000540 const void* data, size_t bytelength,
reed@android.com9d3a9852010-01-08 14:07:42 +0000541 SkTypeface::Style style) {
542 load_system_fonts();
543
544 SkAutoMutexAcquire ac(gFamilyMutex);
reed@google.com60169622011-03-14 15:08:44 +0000545
reed@android.com9d3a9852010-01-08 14:07:42 +0000546 // clip to legal style bits
547 style = (SkTypeface::Style)(style & SkTypeface::kBoldItalic);
reed@google.com60169622011-03-14 15:08:44 +0000548
reed@android.com9d3a9852010-01-08 14:07:42 +0000549 SkTypeface* tf = NULL;
550
551 if (NULL != familyFace) {
552 tf = find_typeface(familyFace, style);
553 } else if (NULL != familyName) {
554// SkDebugf("======= familyName <%s>\n", familyName);
555 tf = find_typeface(familyName, style);
556 }
557
558 if (NULL == tf) {
559 tf = find_best_face(gDefaultFamily, style);
560 }
561
562 // we ref(), since the symantic is to return a new instance
563 tf->ref();
564 return tf;
565}
566
567bool SkFontHost::ValidFontID(uint32_t fontID) {
568 SkAutoMutexAcquire ac(gFamilyMutex);
reed@google.com60169622011-03-14 15:08:44 +0000569
reed@android.com9d3a9852010-01-08 14:07:42 +0000570 return find_from_uniqueID(fontID) != NULL;
571}
572
573SkStream* SkFontHost::OpenStream(uint32_t fontID) {
574 SkAutoMutexAcquire ac(gFamilyMutex);
reed@google.com60169622011-03-14 15:08:44 +0000575
reed@android.com9d3a9852010-01-08 14:07:42 +0000576 FamilyTypeface* tf = (FamilyTypeface*)find_from_uniqueID(fontID);
577 SkStream* stream = tf ? tf->openStream() : NULL;
578
579 if (stream && stream->getLength() == 0) {
580 stream->unref();
581 stream = NULL;
582 }
583 return stream;
584}
585
reed@google.com60169622011-03-14 15:08:44 +0000586#if 0
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000587SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics(
vandebo@chromium.org325cb9a2011-03-30 18:36:29 +0000588 uint32_t fontID,
589 SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo) {
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000590 SkASSERT(!"SkFontHost::GetAdvancedTypefaceMetrics unimplemented");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000591 return NULL;
592}
reed@google.com60169622011-03-14 15:08:44 +0000593#endif
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000594
reed@android.com9d3a9852010-01-08 14:07:42 +0000595size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length,
596 int32_t* index) {
597 SkAutoMutexAcquire ac(gFamilyMutex);
598
599 FamilyTypeface* tf = (FamilyTypeface*)find_from_uniqueID(fontID);
600 const char* src = tf ? tf->getFilePath() : NULL;
601
602 if (src) {
603 size_t size = strlen(src);
604 if (path) {
605 memcpy(path, src, SkMin32(size, length));
606 }
607 if (index) {
608 *index = 0; // we don't have collections (yet)
609 }
610 return size;
611 } else {
612 return 0;
613 }
614}
615
reed@google.com7d26c592011-06-13 13:01:10 +0000616SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) {
reed@android.com9d3a9852010-01-08 14:07:42 +0000617 load_system_fonts();
618
619 /* First see if fontID is already one of our fallbacks. If so, return
620 its successor. If fontID is not in our list, then return the first one
621 in our list. Note: list is zero-terminated, and returning zero means
622 we have no more fonts to use for fallbacks.
623 */
624 const uint32_t* list = gFallbackFonts;
625 for (int i = 0; list[i] != 0; i++) {
reed@google.com7d26c592011-06-13 13:01:10 +0000626 if (list[i] == currFontID) {
reed@android.com9d3a9852010-01-08 14:07:42 +0000627 return list[i+1];
628 }
629 }
630 return list[0];
631}
632
633///////////////////////////////////////////////////////////////////////////////
634
635SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
636 if (NULL == stream || stream->getLength() <= 0) {
637 return NULL;
638 }
reed@google.com60169622011-03-14 15:08:44 +0000639
640 bool isFixedWidth;
reed@android.com9d3a9852010-01-08 14:07:42 +0000641 SkString name;
reed@google.com60169622011-03-14 15:08:44 +0000642 SkTypeface::Style style = find_name_and_attributes(stream, &name,
643 &isFixedWidth);
reed@android.com9d3a9852010-01-08 14:07:42 +0000644
645 return SkNEW_ARGS(StreamTypeface, (style, false, NULL, stream));
646}
647
648SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
649 SkStream* stream = SkNEW_ARGS(SkMMAPStream, (path));
650 SkTypeface* face = SkFontHost::CreateTypefaceFromStream(stream);
651 // since we created the stream, we let go of our ref() here
652 stream->unref();
653 return face;
654}
655
656///////////////////////////////////////////////////////////////////////////////
657
658size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) {
659 if (sizeAllocatedSoFar > FONT_CACHE_MEMORY_BUDGET)
660 return sizeAllocatedSoFar - FONT_CACHE_MEMORY_BUDGET;
661 else
662 return 0; // nothing to do
663}
664