blob: 0624d359a2470b95ef932d7dbf4bffc839ad1d18 [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
reed@google.com60169622011-03-14 15:08:44 +000020#ifdef SK_BUILD_FOR_MAC
21 #define SK_FONT_FILE_PREFIX "/Library/Fonts/"
22#else
23 #define SK_FONT_FILE_PREFIX "/skimages/"
24#endif
reed@android.com9d3a9852010-01-08 14:07:42 +000025
reed@google.com60169622011-03-14 15:08:44 +000026SkTypeface::Style find_name_and_attributes(SkStream* stream, SkString* name,
27 bool* isFixedWidth);
reed@android.com9d3a9852010-01-08 14:07:42 +000028
29static void GetFullPathForSysFonts(SkString* full, const char name[]) {
30 full->set(SK_FONT_FILE_PREFIX);
31 full->append(name);
32}
33
34///////////////////////////////////////////////////////////////////////////////
35
36struct FamilyRec;
37
38/* This guy holds a mapping of a name -> family, used for looking up fonts.
39 Since it is stored in a stretchy array that doesn't preserve object
40 semantics, we don't use constructor/destructors, but just have explicit
41 helpers to manage our internal bookkeeping.
42*/
43struct NameFamilyPair {
44 const char* fName; // we own this
45 FamilyRec* fFamily; // we don't own this, we just reference it
reed@google.com60169622011-03-14 15:08:44 +000046
reed@android.com9d3a9852010-01-08 14:07:42 +000047 void construct(const char name[], FamilyRec* family) {
48 fName = strdup(name);
49 fFamily = family; // we don't own this, so just record the referene
50 }
51
52 void destruct() {
53 free((char*)fName);
54 // we don't own family, so just ignore our reference
55 }
56};
57
58// we use atomic_inc to grow this for each typeface we create
59static int32_t gUniqueFontID;
60
61// this is the mutex that protects these globals
62static SkMutex gFamilyMutex;
63static FamilyRec* gFamilyHead;
64static SkTDArray<NameFamilyPair> gNameList;
65
66struct FamilyRec {
67 FamilyRec* fNext;
68 SkTypeface* fFaces[4];
reed@google.com60169622011-03-14 15:08:44 +000069
reed@android.com9d3a9852010-01-08 14:07:42 +000070 FamilyRec()
71 {
72 fNext = gFamilyHead;
73 memset(fFaces, 0, sizeof(fFaces));
74 gFamilyHead = this;
75 }
76};
77
78static SkTypeface* find_best_face(const FamilyRec* family,
79 SkTypeface::Style style) {
80 SkTypeface* const* faces = family->fFaces;
81
82 if (faces[style] != NULL) { // exact match
83 return faces[style];
84 }
85 // look for a matching bold
86 style = (SkTypeface::Style)(style ^ SkTypeface::kItalic);
87 if (faces[style] != NULL) {
88 return faces[style];
89 }
90 // look for the plain
91 if (faces[SkTypeface::kNormal] != NULL) {
92 return faces[SkTypeface::kNormal];
93 }
94 // look for anything
95 for (int i = 0; i < 4; i++) {
96 if (faces[i] != NULL) {
97 return faces[i];
98 }
99 }
100 // should never get here, since the faces list should not be empty
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000101 SkDEBUGFAIL("faces list is empty");
reed@android.com9d3a9852010-01-08 14:07:42 +0000102 return NULL;
103}
104
105static FamilyRec* find_family(const SkTypeface* member) {
106 FamilyRec* curr = gFamilyHead;
107 while (curr != NULL) {
108 for (int i = 0; i < 4; i++) {
109 if (curr->fFaces[i] == member) {
110 return curr;
111 }
112 }
113 curr = curr->fNext;
114 }
115 return NULL;
116}
117
118/* Returns the matching typeface, or NULL. If a typeface is found, its refcnt
119 is not modified.
120 */
121static SkTypeface* find_from_uniqueID(uint32_t uniqueID) {
122 FamilyRec* curr = gFamilyHead;
123 while (curr != NULL) {
124 for (int i = 0; i < 4; i++) {
125 SkTypeface* face = curr->fFaces[i];
126 if (face != NULL && face->uniqueID() == uniqueID) {
127 return face;
128 }
129 }
130 curr = curr->fNext;
131 }
132 return NULL;
133}
134
135/* Remove reference to this face from its family. If the resulting family
136 is empty (has no faces), return that family, otherwise return NULL
137*/
138static FamilyRec* remove_from_family(const SkTypeface* face) {
139 FamilyRec* family = find_family(face);
140 SkASSERT(family->fFaces[face->style()] == face);
141 family->fFaces[face->style()] = NULL;
reed@google.com60169622011-03-14 15:08:44 +0000142
reed@android.com9d3a9852010-01-08 14:07:42 +0000143 for (int i = 0; i < 4; i++) {
144 if (family->fFaces[i] != NULL) { // family is non-empty
145 return NULL;
146 }
147 }
148 return family; // return the empty family
149}
150
151// maybe we should make FamilyRec be doubly-linked
152static void detach_and_delete_family(FamilyRec* family) {
153 FamilyRec* curr = gFamilyHead;
154 FamilyRec* prev = NULL;
155
156 while (curr != NULL) {
157 FamilyRec* next = curr->fNext;
158 if (curr == family) {
159 if (prev == NULL) {
160 gFamilyHead = next;
161 } else {
162 prev->fNext = next;
163 }
164 SkDELETE(family);
165 return;
166 }
167 prev = curr;
168 curr = next;
169 }
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000170 SkDEBUGFAIL("Yikes, couldn't find family in our list to remove/delete");
reed@android.com9d3a9852010-01-08 14:07:42 +0000171}
172
173static SkTypeface* find_typeface(const char name[], SkTypeface::Style style) {
174 NameFamilyPair* list = gNameList.begin();
175 int count = gNameList.count();
reed@google.com60169622011-03-14 15:08:44 +0000176
reed@android.com9d3a9852010-01-08 14:07:42 +0000177 int index = SkStrLCSearch(&list[0].fName, count, name, sizeof(list[0]));
178
179 if (index >= 0) {
180 return find_best_face(list[index].fFamily, style);
181 }
182 return NULL;
183}
184
185static SkTypeface* find_typeface(const SkTypeface* familyMember,
186 SkTypeface::Style style) {
187 const FamilyRec* family = find_family(familyMember);
188 return family ? find_best_face(family, style) : NULL;
189}
190
191static void add_name(const char name[], FamilyRec* family) {
192 SkAutoAsciiToLC tolc(name);
193 name = tolc.lc();
194
195 NameFamilyPair* list = gNameList.begin();
196 int count = gNameList.count();
reed@google.com60169622011-03-14 15:08:44 +0000197
reed@android.com9d3a9852010-01-08 14:07:42 +0000198 int index = SkStrLCSearch(&list[0].fName, count, name, sizeof(list[0]));
199
200 if (index < 0) {
201 list = gNameList.insert(~index);
202 list->construct(name, family);
203 }
204}
205
206static void remove_from_names(FamilyRec* emptyFamily)
207{
208#ifdef SK_DEBUG
209 for (int i = 0; i < 4; i++) {
210 SkASSERT(emptyFamily->fFaces[i] == NULL);
211 }
212#endif
213
214 SkTDArray<NameFamilyPair>& list = gNameList;
reed@google.com60169622011-03-14 15:08:44 +0000215
reed@android.com9d3a9852010-01-08 14:07:42 +0000216 // must go backwards when removing
217 for (int i = list.count() - 1; i >= 0; --i) {
218 NameFamilyPair* pair = &list[i];
219 if (pair->fFamily == emptyFamily) {
220 pair->destruct();
221 list.remove(i);
222 }
223 }
224}
225
226///////////////////////////////////////////////////////////////////////////////
227
228class FamilyTypeface : public SkTypeface {
229public:
230 FamilyTypeface(Style style, bool sysFont, SkTypeface* familyMember)
231 : SkTypeface(style, sk_atomic_inc(&gUniqueFontID) + 1) {
232 fIsSysFont = sysFont;
reed@google.com60169622011-03-14 15:08:44 +0000233
reed@android.com9d3a9852010-01-08 14:07:42 +0000234 SkAutoMutexAcquire ac(gFamilyMutex);
reed@google.com60169622011-03-14 15:08:44 +0000235
reed@android.com9d3a9852010-01-08 14:07:42 +0000236 FamilyRec* rec = NULL;
237 if (familyMember) {
238 rec = find_family(familyMember);
239 SkASSERT(rec);
240 } else {
241 rec = SkNEW(FamilyRec);
242 }
243 rec->fFaces[style] = this;
244 }
reed@google.com60169622011-03-14 15:08:44 +0000245
reed@android.com9d3a9852010-01-08 14:07:42 +0000246 virtual ~FamilyTypeface() {
247 SkAutoMutexAcquire ac(gFamilyMutex);
reed@google.com60169622011-03-14 15:08:44 +0000248
reed@android.com9d3a9852010-01-08 14:07:42 +0000249 // remove us from our family. If the family is now empty, we return
250 // that and then remove that family from the name list
251 FamilyRec* family = remove_from_family(this);
252 if (NULL != family) {
253 remove_from_names(family);
254 detach_and_delete_family(family);
255 }
256 }
reed@google.com60169622011-03-14 15:08:44 +0000257
reed@android.com9d3a9852010-01-08 14:07:42 +0000258 bool isSysFont() const { return fIsSysFont; }
reed@google.com60169622011-03-14 15:08:44 +0000259
reed@android.com9d3a9852010-01-08 14:07:42 +0000260 virtual SkStream* openStream() = 0;
261 virtual const char* getUniqueString() const = 0;
262 virtual const char* getFilePath() const = 0;
reed@google.com60169622011-03-14 15:08:44 +0000263
reed@android.com9d3a9852010-01-08 14:07:42 +0000264private:
265 bool fIsSysFont;
reed@google.com60169622011-03-14 15:08:44 +0000266
reed@android.com9d3a9852010-01-08 14:07:42 +0000267 typedef SkTypeface INHERITED;
268};
269
270///////////////////////////////////////////////////////////////////////////////
271
272class StreamTypeface : public FamilyTypeface {
273public:
274 StreamTypeface(Style style, bool sysFont, SkTypeface* familyMember,
275 SkStream* stream)
276 : INHERITED(style, sysFont, familyMember) {
277 SkASSERT(stream);
278 stream->ref();
279 fStream = stream;
280 }
281 virtual ~StreamTypeface() {
282 fStream->unref();
283 }
reed@google.com60169622011-03-14 15:08:44 +0000284
reed@android.com9d3a9852010-01-08 14:07:42 +0000285 // overrides
286 virtual SkStream* openStream() {
287 // we just ref our existing stream, since the caller will call unref()
288 // when they are through
289 fStream->ref();
290 return fStream;
291 }
292 virtual const char* getUniqueString() const { return NULL; }
293 virtual const char* getFilePath() const { return NULL; }
294
295private:
296 SkStream* fStream;
reed@google.com60169622011-03-14 15:08:44 +0000297
reed@android.com9d3a9852010-01-08 14:07:42 +0000298 typedef FamilyTypeface INHERITED;
299};
300
301class FileTypeface : public FamilyTypeface {
302public:
303 FileTypeface(Style style, bool sysFont, SkTypeface* familyMember,
304 const char path[])
305 : INHERITED(style, sysFont, familyMember) {
306 SkString fullpath;
reed@google.com60169622011-03-14 15:08:44 +0000307
reed@android.com9d3a9852010-01-08 14:07:42 +0000308 if (sysFont) {
309 GetFullPathForSysFonts(&fullpath, path);
310 path = fullpath.c_str();
311 }
312 fPath.set(path);
313 }
reed@google.com60169622011-03-14 15:08:44 +0000314
reed@android.com9d3a9852010-01-08 14:07:42 +0000315 // overrides
316 virtual SkStream* openStream() {
317 SkStream* stream = SkNEW_ARGS(SkMMAPStream, (fPath.c_str()));
318
319 // check for failure
320 if (stream->getLength() <= 0) {
321 SkDELETE(stream);
322 // maybe MMAP isn't supported. try FILE
323 stream = SkNEW_ARGS(SkFILEStream, (fPath.c_str()));
324 if (stream->getLength() <= 0) {
325 SkDELETE(stream);
326 stream = NULL;
327 }
328 }
329 return stream;
330 }
331 virtual const char* getUniqueString() const {
332 const char* str = strrchr(fPath.c_str(), '/');
333 if (str) {
334 str += 1; // skip the '/'
335 }
336 return str;
337 }
338 virtual const char* getFilePath() const {
339 return fPath.c_str();
340 }
341
342private:
343 SkString fPath;
reed@google.com60169622011-03-14 15:08:44 +0000344
reed@android.com9d3a9852010-01-08 14:07:42 +0000345 typedef FamilyTypeface INHERITED;
346};
347
348///////////////////////////////////////////////////////////////////////////////
349///////////////////////////////////////////////////////////////////////////////
350
351static bool get_name_and_style(const char path[], SkString* name,
352 SkTypeface::Style* style, bool isExpected) {
reed@google.com60169622011-03-14 15:08:44 +0000353 bool isFixedWidth;
reed@android.com9d3a9852010-01-08 14:07:42 +0000354 SkString fullpath;
355 GetFullPathForSysFonts(&fullpath, path);
356
357 SkMMAPStream stream(fullpath.c_str());
358 if (stream.getLength() > 0) {
reed@google.com60169622011-03-14 15:08:44 +0000359 *style = find_name_and_attributes(&stream, name, &isFixedWidth);
reed@android.com9d3a9852010-01-08 14:07:42 +0000360 return true;
361 }
362 else {
363 SkFILEStream stream(fullpath.c_str());
364 if (stream.getLength() > 0) {
reed@google.com60169622011-03-14 15:08:44 +0000365 *style = find_name_and_attributes(&stream, name, &isFixedWidth);
reed@android.com9d3a9852010-01-08 14:07:42 +0000366 return true;
367 }
368 }
369
370 if (isExpected) {
371 SkDebugf("---- failed to open <%s> as a font\n", fullpath.c_str());
372 }
373 return false;
374}
375
376// used to record our notion of the pre-existing fonts
377struct FontInitRec {
378 const char* fFileName;
379 const char* const* fNames; // null-terminated list
380};
381
382static const char* gSansNames[] = {
383 "sans-serif", "arial", "helvetica", "tahoma", "verdana", NULL
384};
385
386static const char* gSerifNames[] = {
387 "serif", "times", "times new roman", "palatino", "georgia", "baskerville",
388 "goudy", "fantasy", "cursive", "ITC Stone Serif", NULL
389};
390
391static const char* gMonoNames[] = {
392 "monospace", "courier", "courier new", "monaco", NULL
393};
394
395// deliberately empty, but we use the address to identify fallback fonts
396static const char* gFBNames[] = { NULL };
397
398/* Fonts must be grouped by family, with the first font in a family having the
399 list of names (even if that list is empty), and the following members having
400 null for the list. The names list must be NULL-terminated
401*/
402static const FontInitRec gSystemFonts[] = {
reed@google.com60169622011-03-14 15:08:44 +0000403 { "Arial.ttf", gSansNames },
404 { "Times.ttf", gSerifNames },
reed@android.com9d3a9852010-01-08 14:07:42 +0000405 { "samplefont.ttf", gSansNames },
406};
407
408#define DEFAULT_NAMES gSansNames
409
410// these globals are assigned (once) by load_system_fonts()
411static FamilyRec* gDefaultFamily;
412static SkTypeface* gDefaultNormal;
413
414/* This is sized conservatively, assuming that it will never be a size issue.
415 It will be initialized in load_system_fonts(), and will be filled with the
416 fontIDs that can be used for fallback consideration, in sorted order (sorted
417 meaning element[0] should be used first, then element[1], etc. When we hit
418 a fontID==0 in the array, the list is done, hence our allocation size is
419 +1 the total number of possible system fonts. Also see NextLogicalFont().
420 */
421static uint32_t gFallbackFonts[SK_ARRAY_COUNT(gSystemFonts)+1];
422
423/* Called once (ensured by the sentinel check at the beginning of our body).
424 Initializes all the globals, and register the system fonts.
425 */
426static void load_system_fonts() {
427 // check if we've already be called
428 if (NULL != gDefaultNormal) {
429 return;
430 }
reed@google.com60169622011-03-14 15:08:44 +0000431
reed@android.com9d3a9852010-01-08 14:07:42 +0000432 const FontInitRec* rec = gSystemFonts;
433 SkTypeface* firstInFamily = NULL;
434 int fallbackCount = 0;
435
436 for (size_t i = 0; i < SK_ARRAY_COUNT(gSystemFonts); i++) {
437 // if we're the first in a new family, clear firstInFamily
438 if (rec[i].fNames != NULL) {
439 firstInFamily = NULL;
440 }
reed@google.com60169622011-03-14 15:08:44 +0000441
reed@android.com9d3a9852010-01-08 14:07:42 +0000442 SkString name;
443 SkTypeface::Style style;
444
445 // we expect all the fonts, except the "fallback" fonts
446 bool isExpected = (rec[i].fNames != gFBNames);
447 if (!get_name_and_style(rec[i].fFileName, &name, &style, isExpected)) {
448 continue;
449 }
450
451 SkTypeface* tf = SkNEW_ARGS(FileTypeface,
452 (style,
453 true, // system-font (cannot delete)
454 firstInFamily, // what family to join
455 rec[i].fFileName) // filename
456 );
457
458 if (rec[i].fNames != NULL) {
459 // see if this is one of our fallback fonts
460 if (rec[i].fNames == gFBNames) {
461 // SkDebugf("---- adding %s as fallback[%d] fontID %d\n",
462 // rec[i].fFileName, fallbackCount, tf->uniqueID());
463 gFallbackFonts[fallbackCount++] = tf->uniqueID();
464 }
465
466 firstInFamily = tf;
467 FamilyRec* family = find_family(tf);
468 const char* const* names = rec[i].fNames;
469
470 // record the default family if this is it
471 if (names == DEFAULT_NAMES) {
472 gDefaultFamily = family;
473 }
474 // add the names to map to this family
475 while (*names) {
476 add_name(*names, family);
477 names += 1;
478 }
479 }
480 }
481
482 // do this after all fonts are loaded. This is our default font, and it
483 // acts as a sentinel so we only execute load_system_fonts() once
484 gDefaultNormal = find_best_face(gDefaultFamily, SkTypeface::kNormal);
485 // now terminate our fallback list with the sentinel value
486 gFallbackFonts[fallbackCount] = 0;
487}
488
489///////////////////////////////////////////////////////////////////////////////
490
491void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
492 const char* name = ((FamilyTypeface*)face)->getUniqueString();
493
494 stream->write8((uint8_t)face->style());
495
496 if (NULL == name || 0 == *name) {
497 stream->writePackedUInt(0);
498// SkDebugf("--- fonthost serialize null\n");
499 } else {
500 uint32_t len = strlen(name);
501 stream->writePackedUInt(len);
502 stream->write(name, len);
503// SkDebugf("--- fonthost serialize <%s> %d\n", name, face->style());
504 }
505}
506
507SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
508 load_system_fonts();
509
510 int style = stream->readU8();
511
512 int len = stream->readPackedUInt();
513 if (len > 0) {
514 SkString str;
515 str.resize(len);
516 stream->read(str.writable_str(), len);
reed@google.com60169622011-03-14 15:08:44 +0000517
reed@android.com9d3a9852010-01-08 14:07:42 +0000518 const FontInitRec* rec = gSystemFonts;
519 for (size_t i = 0; i < SK_ARRAY_COUNT(gSystemFonts); i++) {
520 if (strcmp(rec[i].fFileName, str.c_str()) == 0) {
521 // backup until we hit the fNames
522 for (int j = i; j >= 0; --j) {
523 if (rec[j].fNames != NULL) {
524 return SkFontHost::CreateTypeface(NULL,
agl@chromium.org5f6a0762010-04-20 22:06:40 +0000525 rec[j].fNames[0], NULL, 0, (SkTypeface::Style)style);
reed@android.com9d3a9852010-01-08 14:07:42 +0000526 }
527 }
528 }
529 }
530 }
531 return NULL;
532}
533
534///////////////////////////////////////////////////////////////////////////////
535
536SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
537 const char familyName[],
agl@chromium.org5f6a0762010-04-20 22:06:40 +0000538 const void* data, size_t bytelength,
reed@android.com9d3a9852010-01-08 14:07:42 +0000539 SkTypeface::Style style) {
540 load_system_fonts();
541
542 SkAutoMutexAcquire ac(gFamilyMutex);
reed@google.com60169622011-03-14 15:08:44 +0000543
reed@android.com9d3a9852010-01-08 14:07:42 +0000544 // clip to legal style bits
545 style = (SkTypeface::Style)(style & SkTypeface::kBoldItalic);
reed@google.com60169622011-03-14 15:08:44 +0000546
reed@android.com9d3a9852010-01-08 14:07:42 +0000547 SkTypeface* tf = NULL;
548
549 if (NULL != familyFace) {
550 tf = find_typeface(familyFace, style);
551 } else if (NULL != familyName) {
552// SkDebugf("======= familyName <%s>\n", familyName);
553 tf = find_typeface(familyName, style);
554 }
555
556 if (NULL == tf) {
557 tf = find_best_face(gDefaultFamily, style);
558 }
559
560 // we ref(), since the symantic is to return a new instance
561 tf->ref();
562 return tf;
563}
564
565bool SkFontHost::ValidFontID(uint32_t fontID) {
566 SkAutoMutexAcquire ac(gFamilyMutex);
reed@google.com60169622011-03-14 15:08:44 +0000567
reed@android.com9d3a9852010-01-08 14:07:42 +0000568 return find_from_uniqueID(fontID) != NULL;
569}
570
571SkStream* SkFontHost::OpenStream(uint32_t fontID) {
572 SkAutoMutexAcquire ac(gFamilyMutex);
reed@google.com60169622011-03-14 15:08:44 +0000573
reed@android.com9d3a9852010-01-08 14:07:42 +0000574 FamilyTypeface* tf = (FamilyTypeface*)find_from_uniqueID(fontID);
575 SkStream* stream = tf ? tf->openStream() : NULL;
576
577 if (stream && stream->getLength() == 0) {
578 stream->unref();
579 stream = NULL;
580 }
581 return stream;
582}
583
reed@google.com60169622011-03-14 15:08:44 +0000584#if 0
vandebo@chromium.orgc48b2b32011-02-02 02:11:22 +0000585SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics(
vandebo@chromium.org325cb9a2011-03-30 18:36:29 +0000586 uint32_t fontID,
587 SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo) {
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000588 SkDEBUGFAIL("SkFontHost::GetAdvancedTypefaceMetrics unimplemented");
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000589 return NULL;
590}
reed@google.com60169622011-03-14 15:08:44 +0000591#endif
vandebo@chromium.org2a22e102011-01-25 21:01:34 +0000592
reed@android.com9d3a9852010-01-08 14:07:42 +0000593size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length,
594 int32_t* index) {
595 SkAutoMutexAcquire ac(gFamilyMutex);
596
597 FamilyTypeface* tf = (FamilyTypeface*)find_from_uniqueID(fontID);
598 const char* src = tf ? tf->getFilePath() : NULL;
599
600 if (src) {
601 size_t size = strlen(src);
602 if (path) {
603 memcpy(path, src, SkMin32(size, length));
604 }
605 if (index) {
606 *index = 0; // we don't have collections (yet)
607 }
608 return size;
609 } else {
610 return 0;
611 }
612}
613
reed@google.com7d26c592011-06-13 13:01:10 +0000614SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) {
reed@android.com9d3a9852010-01-08 14:07:42 +0000615 load_system_fonts();
616
617 /* First see if fontID is already one of our fallbacks. If so, return
618 its successor. If fontID is not in our list, then return the first one
619 in our list. Note: list is zero-terminated, and returning zero means
620 we have no more fonts to use for fallbacks.
621 */
622 const uint32_t* list = gFallbackFonts;
623 for (int i = 0; list[i] != 0; i++) {
reed@google.com7d26c592011-06-13 13:01:10 +0000624 if (list[i] == currFontID) {
reed@android.com9d3a9852010-01-08 14:07:42 +0000625 return list[i+1];
626 }
627 }
628 return list[0];
629}
630
631///////////////////////////////////////////////////////////////////////////////
632
633SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
634 if (NULL == stream || stream->getLength() <= 0) {
635 return NULL;
636 }
reed@google.com60169622011-03-14 15:08:44 +0000637
638 bool isFixedWidth;
reed@android.com9d3a9852010-01-08 14:07:42 +0000639 SkString name;
reed@google.com60169622011-03-14 15:08:44 +0000640 SkTypeface::Style style = find_name_and_attributes(stream, &name,
641 &isFixedWidth);
reed@android.com9d3a9852010-01-08 14:07:42 +0000642
643 return SkNEW_ARGS(StreamTypeface, (style, false, NULL, stream));
644}
645
646SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
647 SkStream* stream = SkNEW_ARGS(SkMMAPStream, (path));
648 SkTypeface* face = SkFontHost::CreateTypefaceFromStream(stream);
649 // since we created the stream, we let go of our ref() here
650 stream->unref();
651 return face;
652}
653