blob: d63aec20769f43d214b5a97037e211c52f057d5c [file] [log] [blame]
vandebo@chromium.org2a22e102011-01-25 21:01:34 +00001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
reed@android.com9d3a9852010-01-08 14:07:42 +000017#include "SkFontHost.h"
18#include "SkDescriptor.h"
19#include "SkMMapStream.h"
20#include "SkPaint.h"
21#include "SkString.h"
22#include "SkStream.h"
23#include "SkThread.h"
24#include "SkTSearch.h"
25#include <stdio.h>
26
27#define FONT_CACHE_MEMORY_BUDGET (768 * 1024)
28
reed@google.com60169622011-03-14 15:08:44 +000029#ifdef SK_BUILD_FOR_MAC
30 #define SK_FONT_FILE_PREFIX "/Library/Fonts/"
31#else
32 #define SK_FONT_FILE_PREFIX "/skimages/"
33#endif
reed@android.com9d3a9852010-01-08 14:07:42 +000034
reed@google.com60169622011-03-14 15:08:44 +000035SkTypeface::Style find_name_and_attributes(SkStream* stream, SkString* name,
36 bool* isFixedWidth);
reed@android.com9d3a9852010-01-08 14:07:42 +000037
38static void GetFullPathForSysFonts(SkString* full, const char name[]) {
39 full->set(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.com60169622011-03-14 15:08:44 +000055
reed@android.com9d3a9852010-01-08 14:07:42 +000056 void construct(const char name[], FamilyRec* family) {
57 fName = strdup(name);
58 fFamily = family; // we don't own this, so just record the referene
59 }
60
61 void destruct() {
62 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.com60169622011-03-14 15:08:44 +000078
reed@android.com9d3a9852010-01-08 14:07:42 +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,
88 SkTypeface::Style style) {
89 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
114static FamilyRec* find_family(const SkTypeface* member) {
115 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
127/* Returns the matching typeface, or NULL. If a typeface is found, its refcnt
128 is not modified.
129 */
130static SkTypeface* find_from_uniqueID(uint32_t uniqueID) {
131 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*/
147static FamilyRec* remove_from_family(const SkTypeface* face) {
148 FamilyRec* family = find_family(face);
149 SkASSERT(family->fFaces[face->style()] == face);
150 family->fFaces[face->style()] = NULL;
reed@google.com60169622011-03-14 15:08:44 +0000151
reed@android.com9d3a9852010-01-08 14:07:42 +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
161static void detach_and_delete_family(FamilyRec* family) {
162 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
182static SkTypeface* find_typeface(const char name[], SkTypeface::Style style) {
183 NameFamilyPair* list = gNameList.begin();
184 int count = gNameList.count();
reed@google.com60169622011-03-14 15:08:44 +0000185
reed@android.com9d3a9852010-01-08 14:07:42 +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,
195 SkTypeface::Style style) {
196 const FamilyRec* family = find_family(familyMember);
197 return family ? find_best_face(family, style) : NULL;
198}
199
200static void add_name(const char name[], FamilyRec* family) {
201 SkAutoAsciiToLC tolc(name);
202 name = tolc.lc();
203
204 NameFamilyPair* list = gNameList.begin();
205 int count = gNameList.count();
reed@google.com60169622011-03-14 15:08:44 +0000206
reed@android.com9d3a9852010-01-08 14:07:42 +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.com60169622011-03-14 15:08:44 +0000224
reed@android.com9d3a9852010-01-08 14:07:42 +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:
239 FamilyTypeface(Style style, bool sysFont, SkTypeface* familyMember)
240 : SkTypeface(style, sk_atomic_inc(&gUniqueFontID) + 1) {
241 fIsSysFont = sysFont;
reed@google.com60169622011-03-14 15:08:44 +0000242
reed@android.com9d3a9852010-01-08 14:07:42 +0000243 SkAutoMutexAcquire ac(gFamilyMutex);
reed@google.com60169622011-03-14 15:08:44 +0000244
reed@android.com9d3a9852010-01-08 14:07:42 +0000245 FamilyRec* rec = NULL;
246 if (familyMember) {
247 rec = find_family(familyMember);
248 SkASSERT(rec);
249 } else {
250 rec = SkNEW(FamilyRec);
251 }
252 rec->fFaces[style] = this;
253 }
reed@google.com60169622011-03-14 15:08:44 +0000254
reed@android.com9d3a9852010-01-08 14:07:42 +0000255 virtual ~FamilyTypeface() {
256 SkAutoMutexAcquire ac(gFamilyMutex);
reed@google.com60169622011-03-14 15:08:44 +0000257
reed@android.com9d3a9852010-01-08 14:07:42 +0000258 // remove us from our family. If the family is now empty, we return
259 // that and then remove that family from the name list
260 FamilyRec* family = remove_from_family(this);
261 if (NULL != family) {
262 remove_from_names(family);
263 detach_and_delete_family(family);
264 }
265 }
reed@google.com60169622011-03-14 15:08:44 +0000266
reed@android.com9d3a9852010-01-08 14:07:42 +0000267 bool isSysFont() const { return fIsSysFont; }
reed@google.com60169622011-03-14 15:08:44 +0000268
reed@android.com9d3a9852010-01-08 14:07:42 +0000269 virtual SkStream* openStream() = 0;
270 virtual const char* getUniqueString() const = 0;
271 virtual const char* getFilePath() const = 0;
reed@google.com60169622011-03-14 15:08:44 +0000272
reed@android.com9d3a9852010-01-08 14:07:42 +0000273private:
274 bool fIsSysFont;
reed@google.com60169622011-03-14 15:08:44 +0000275
reed@android.com9d3a9852010-01-08 14:07:42 +0000276 typedef SkTypeface INHERITED;
277};
278
279///////////////////////////////////////////////////////////////////////////////
280
281class StreamTypeface : public FamilyTypeface {
282public:
283 StreamTypeface(Style style, bool sysFont, SkTypeface* familyMember,
284 SkStream* stream)
285 : INHERITED(style, sysFont, familyMember) {
286 SkASSERT(stream);
287 stream->ref();
288 fStream = stream;
289 }
290 virtual ~StreamTypeface() {
291 fStream->unref();
292 }
reed@google.com60169622011-03-14 15:08:44 +0000293
reed@android.com9d3a9852010-01-08 14:07:42 +0000294 // overrides
295 virtual SkStream* openStream() {
296 // we just ref our existing stream, since the caller will call unref()
297 // when they are through
298 fStream->ref();
299 return fStream;
300 }
301 virtual const char* getUniqueString() const { return NULL; }
302 virtual const char* getFilePath() const { return NULL; }
303
304private:
305 SkStream* fStream;
reed@google.com60169622011-03-14 15:08:44 +0000306
reed@android.com9d3a9852010-01-08 14:07:42 +0000307 typedef FamilyTypeface INHERITED;
308};
309
310class FileTypeface : public FamilyTypeface {
311public:
312 FileTypeface(Style style, bool sysFont, SkTypeface* familyMember,
313 const char path[])
314 : INHERITED(style, sysFont, familyMember) {
315 SkString fullpath;
reed@google.com60169622011-03-14 15:08:44 +0000316
reed@android.com9d3a9852010-01-08 14:07:42 +0000317 if (sysFont) {
318 GetFullPathForSysFonts(&fullpath, path);
319 path = fullpath.c_str();
320 }
321 fPath.set(path);
322 }
reed@google.com60169622011-03-14 15:08:44 +0000323
reed@android.com9d3a9852010-01-08 14:07:42 +0000324 // overrides
325 virtual SkStream* openStream() {
326 SkStream* stream = SkNEW_ARGS(SkMMAPStream, (fPath.c_str()));
327
328 // check for failure
329 if (stream->getLength() <= 0) {
330 SkDELETE(stream);
331 // maybe MMAP isn't supported. try FILE
332 stream = SkNEW_ARGS(SkFILEStream, (fPath.c_str()));
333 if (stream->getLength() <= 0) {
334 SkDELETE(stream);
335 stream = NULL;
336 }
337 }
338 return stream;
339 }
340 virtual const char* getUniqueString() const {
341 const char* str = strrchr(fPath.c_str(), '/');
342 if (str) {
343 str += 1; // skip the '/'
344 }
345 return str;
346 }
347 virtual const char* getFilePath() const {
348 return fPath.c_str();
349 }
350
351private:
352 SkString fPath;
reed@google.com60169622011-03-14 15:08:44 +0000353
reed@android.com9d3a9852010-01-08 14:07:42 +0000354 typedef FamilyTypeface INHERITED;
355};
356
357///////////////////////////////////////////////////////////////////////////////
358///////////////////////////////////////////////////////////////////////////////
359
360static bool get_name_and_style(const char path[], SkString* name,
361 SkTypeface::Style* style, bool isExpected) {
reed@google.com60169622011-03-14 15:08:44 +0000362 bool isFixedWidth;
reed@android.com9d3a9852010-01-08 14:07:42 +0000363 SkString fullpath;
364 GetFullPathForSysFonts(&fullpath, path);
365
366 SkMMAPStream stream(fullpath.c_str());
367 if (stream.getLength() > 0) {
reed@google.com60169622011-03-14 15:08:44 +0000368 *style = find_name_and_attributes(&stream, name, &isFixedWidth);
reed@android.com9d3a9852010-01-08 14:07:42 +0000369 return true;
370 }
371 else {
372 SkFILEStream stream(fullpath.c_str());
373 if (stream.getLength() > 0) {
reed@google.com60169622011-03-14 15:08:44 +0000374 *style = find_name_and_attributes(&stream, name, &isFixedWidth);
reed@android.com9d3a9852010-01-08 14:07:42 +0000375 return true;
376 }
377 }
378
379 if (isExpected) {
380 SkDebugf("---- failed to open <%s> as a font\n", fullpath.c_str());
381 }
382 return false;
383}
384
385// used to record our notion of the pre-existing fonts
386struct FontInitRec {
387 const char* fFileName;
388 const char* const* fNames; // null-terminated list
389};
390
391static const char* gSansNames[] = {
392 "sans-serif", "arial", "helvetica", "tahoma", "verdana", NULL
393};
394
395static const char* gSerifNames[] = {
396 "serif", "times", "times new roman", "palatino", "georgia", "baskerville",
397 "goudy", "fantasy", "cursive", "ITC Stone Serif", NULL
398};
399
400static const char* gMonoNames[] = {
401 "monospace", "courier", "courier new", "monaco", NULL
402};
403
404// deliberately empty, but we use the address to identify fallback fonts
405static const char* gFBNames[] = { NULL };
406
407/* Fonts must be grouped by family, with the first font in a family having the
408 list of names (even if that list is empty), and the following members having
409 null for the list. The names list must be NULL-terminated
410*/
411static const FontInitRec gSystemFonts[] = {
reed@google.com60169622011-03-14 15:08:44 +0000412 { "Arial.ttf", gSansNames },
413 { "Times.ttf", gSerifNames },
reed@android.com9d3a9852010-01-08 14:07:42 +0000414 { "samplefont.ttf", gSansNames },
415};
416
417#define DEFAULT_NAMES gSansNames
418
419// these globals are assigned (once) by load_system_fonts()
420static FamilyRec* gDefaultFamily;
421static SkTypeface* gDefaultNormal;
422
423/* This is sized conservatively, assuming that it will never be a size issue.
424 It will be initialized in load_system_fonts(), and will be filled with the
425 fontIDs that can be used for fallback consideration, in sorted order (sorted
426 meaning element[0] should be used first, then element[1], etc. When we hit
427 a fontID==0 in the array, the list is done, hence our allocation size is
428 +1 the total number of possible system fonts. Also see NextLogicalFont().
429 */
430static uint32_t gFallbackFonts[SK_ARRAY_COUNT(gSystemFonts)+1];
431
432/* Called once (ensured by the sentinel check at the beginning of our body).
433 Initializes all the globals, and register the system fonts.
434 */
435static void load_system_fonts() {
436 // check if we've already be called
437 if (NULL != gDefaultNormal) {
438 return;
439 }
reed@google.com60169622011-03-14 15:08:44 +0000440
reed@android.com9d3a9852010-01-08 14:07:42 +0000441 const FontInitRec* rec = gSystemFonts;
442 SkTypeface* firstInFamily = NULL;
443 int fallbackCount = 0;
444
445 for (size_t i = 0; i < SK_ARRAY_COUNT(gSystemFonts); i++) {
446 // if we're the first in a new family, clear firstInFamily
447 if (rec[i].fNames != NULL) {
448 firstInFamily = NULL;
449 }
reed@google.com60169622011-03-14 15:08:44 +0000450
reed@android.com9d3a9852010-01-08 14:07:42 +0000451 SkString name;
452 SkTypeface::Style style;
453
454 // we expect all the fonts, except the "fallback" fonts
455 bool isExpected = (rec[i].fNames != gFBNames);
456 if (!get_name_and_style(rec[i].fFileName, &name, &style, isExpected)) {
457 continue;
458 }
459
460 SkTypeface* tf = SkNEW_ARGS(FileTypeface,
461 (style,
462 true, // system-font (cannot delete)
463 firstInFamily, // what family to join
464 rec[i].fFileName) // filename
465 );
466
467 if (rec[i].fNames != NULL) {
468 // see if this is one of our fallback fonts
469 if (rec[i].fNames == gFBNames) {
470 // SkDebugf("---- adding %s as fallback[%d] fontID %d\n",
471 // rec[i].fFileName, fallbackCount, tf->uniqueID());
472 gFallbackFonts[fallbackCount++] = tf->uniqueID();
473 }
474
475 firstInFamily = tf;
476 FamilyRec* family = find_family(tf);
477 const char* const* names = rec[i].fNames;
478
479 // record the default family if this is it
480 if (names == DEFAULT_NAMES) {
481 gDefaultFamily = family;
482 }
483 // add the names to map to this family
484 while (*names) {
485 add_name(*names, family);
486 names += 1;
487 }
488 }
489 }
490
491 // do this after all fonts are loaded. This is our default font, and it
492 // acts as a sentinel so we only execute load_system_fonts() once
493 gDefaultNormal = find_best_face(gDefaultFamily, SkTypeface::kNormal);
494 // now terminate our fallback list with the sentinel value
495 gFallbackFonts[fallbackCount] = 0;
496}
497
498///////////////////////////////////////////////////////////////////////////////
499
500void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
501 const char* name = ((FamilyTypeface*)face)->getUniqueString();
502
503 stream->write8((uint8_t)face->style());
504
505 if (NULL == name || 0 == *name) {
506 stream->writePackedUInt(0);
507// SkDebugf("--- fonthost serialize null\n");
508 } else {
509 uint32_t len = strlen(name);
510 stream->writePackedUInt(len);
511 stream->write(name, len);
512// SkDebugf("--- fonthost serialize <%s> %d\n", name, face->style());
513 }
514}
515
516SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
517 load_system_fonts();
518
519 int style = stream->readU8();
520
521 int len = stream->readPackedUInt();
522 if (len > 0) {
523 SkString str;
524 str.resize(len);
525 stream->read(str.writable_str(), len);
reed@google.com60169622011-03-14 15:08:44 +0000526
reed@android.com9d3a9852010-01-08 14:07:42 +0000527 const FontInitRec* rec = gSystemFonts;
528 for (size_t i = 0; i < SK_ARRAY_COUNT(gSystemFonts); i++) {
529 if (strcmp(rec[i].fFileName, str.c_str()) == 0) {
530 // backup until we hit the fNames
531 for (int j = i; j >= 0; --j) {
532 if (rec[j].fNames != NULL) {
533 return SkFontHost::CreateTypeface(NULL,
agl@chromium.org5f6a0762010-04-20 22:06:40 +0000534 rec[j].fNames[0], NULL, 0, (SkTypeface::Style)style);
reed@android.com9d3a9852010-01-08 14:07:42 +0000535 }
536 }
537 }
538 }
539 }
540 return NULL;
541}
542
543///////////////////////////////////////////////////////////////////////////////
544
545SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
546 const char familyName[],
agl@chromium.org5f6a0762010-04-20 22:06:40 +0000547 const void* data, size_t bytelength,
reed@android.com9d3a9852010-01-08 14:07:42 +0000548 SkTypeface::Style style) {
549 load_system_fonts();
550
551 SkAutoMutexAcquire ac(gFamilyMutex);
reed@google.com60169622011-03-14 15:08:44 +0000552
reed@android.com9d3a9852010-01-08 14:07:42 +0000553 // clip to legal style bits
554 style = (SkTypeface::Style)(style & SkTypeface::kBoldItalic);
reed@google.com60169622011-03-14 15:08:44 +0000555
reed@android.com9d3a9852010-01-08 14:07:42 +0000556 SkTypeface* tf = NULL;
557
558 if (NULL != familyFace) {
559 tf = find_typeface(familyFace, style);
560 } else if (NULL != familyName) {
561// SkDebugf("======= familyName <%s>\n", familyName);
562 tf = find_typeface(familyName, style);
563 }
564
565 if (NULL == tf) {
566 tf = find_best_face(gDefaultFamily, style);
567 }
568
569 // we ref(), since the symantic is to return a new instance
570 tf->ref();
571 return tf;
572}
573
574bool SkFontHost::ValidFontID(uint32_t fontID) {
575 SkAutoMutexAcquire ac(gFamilyMutex);
reed@google.com60169622011-03-14 15:08:44 +0000576
reed@android.com9d3a9852010-01-08 14:07:42 +0000577 return find_from_uniqueID(fontID) != NULL;
578}
579
580SkStream* SkFontHost::OpenStream(uint32_t fontID) {
581 SkAutoMutexAcquire ac(gFamilyMutex);
reed@google.com60169622011-03-14 15:08:44 +0000582
reed@android.com9d3a9852010-01-08 14:07:42 +0000583 FamilyTypeface* tf = (FamilyTypeface*)find_from_uniqueID(fontID);
584 SkStream* stream = tf ? tf->openStream() : NULL;
585
586 if (stream && stream->getLength() == 0) {
587 stream->unref();
588 stream = NULL;
589 }
590 return stream;
591}
592
reed@google.com60169622011-03-14 15:08:44 +0000593#if 0
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000594SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics(
vandebo@chromium.org325cb9a2011-03-30 18:36:29 +0000595 uint32_t fontID,
596 SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo) {
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000597 SkASSERT(!"SkFontHost::GetAdvancedTypefaceMetrics unimplemented");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000598 return NULL;
599}
reed@google.com60169622011-03-14 15:08:44 +0000600#endif
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000601
reed@android.com9d3a9852010-01-08 14:07:42 +0000602size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length,
603 int32_t* index) {
604 SkAutoMutexAcquire ac(gFamilyMutex);
605
606 FamilyTypeface* tf = (FamilyTypeface*)find_from_uniqueID(fontID);
607 const char* src = tf ? tf->getFilePath() : NULL;
608
609 if (src) {
610 size_t size = strlen(src);
611 if (path) {
612 memcpy(path, src, SkMin32(size, length));
613 }
614 if (index) {
615 *index = 0; // we don't have collections (yet)
616 }
617 return size;
618 } else {
619 return 0;
620 }
621}
622
reed@google.com7d26c592011-06-13 13:01:10 +0000623SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) {
reed@android.com9d3a9852010-01-08 14:07:42 +0000624 load_system_fonts();
625
626 /* First see if fontID is already one of our fallbacks. If so, return
627 its successor. If fontID is not in our list, then return the first one
628 in our list. Note: list is zero-terminated, and returning zero means
629 we have no more fonts to use for fallbacks.
630 */
631 const uint32_t* list = gFallbackFonts;
632 for (int i = 0; list[i] != 0; i++) {
reed@google.com7d26c592011-06-13 13:01:10 +0000633 if (list[i] == currFontID) {
reed@android.com9d3a9852010-01-08 14:07:42 +0000634 return list[i+1];
635 }
636 }
637 return list[0];
638}
639
640///////////////////////////////////////////////////////////////////////////////
641
642SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
643 if (NULL == stream || stream->getLength() <= 0) {
644 return NULL;
645 }
reed@google.com60169622011-03-14 15:08:44 +0000646
647 bool isFixedWidth;
reed@android.com9d3a9852010-01-08 14:07:42 +0000648 SkString name;
reed@google.com60169622011-03-14 15:08:44 +0000649 SkTypeface::Style style = find_name_and_attributes(stream, &name,
650 &isFixedWidth);
reed@android.com9d3a9852010-01-08 14:07:42 +0000651
652 return SkNEW_ARGS(StreamTypeface, (style, false, NULL, stream));
653}
654
655SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
656 SkStream* stream = SkNEW_ARGS(SkMMAPStream, (path));
657 SkTypeface* face = SkFontHost::CreateTypefaceFromStream(stream);
658 // since we created the stream, we let go of our ref() here
659 stream->unref();
660 return face;
661}
662
663///////////////////////////////////////////////////////////////////////////////
664
665size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) {
666 if (sizeAllocatedSoFar > FONT_CACHE_MEMORY_BUDGET)
667 return sizeAllocatedSoFar - FONT_CACHE_MEMORY_BUDGET;
668 else
669 return 0; // nothing to do
670}
671