Home | History | Annotate | Download | only in ports
      1 /* libs/graphics/ports/SkFontHost_android.cpp
      2 **
      3 ** Copyright 2006, The Android Open Source Project
      4 **
      5 ** 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
      8 **
      9 **     http://www.apache.org/licenses/LICENSE-2.0
     10 **
     11 ** 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
     15 ** 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 "FontHostConfiguration_android.h"
     27 #include <stdio.h>
     28 
     29 #define FONT_CACHE_MEMORY_BUDGET    (768 * 1024)
     30 
     31 #ifndef SK_FONT_FILE_PREFIX
     32     #define SK_FONT_FILE_PREFIX          "/fonts/"
     33 #endif
     34 
     35 SkTypeface::Style find_name_and_attributes(SkStream* stream, SkString* name,
     36                                            bool* isFixedWidth);
     37 
     38 static void GetFullPathForSysFonts(SkString* full, const char name[]) {
     39     full->set(getenv("ANDROID_ROOT"));
     40     full->append(SK_FONT_FILE_PREFIX);
     41     full->append(name);
     42 }
     43 
     44 ///////////////////////////////////////////////////////////////////////////////
     45 
     46 struct FamilyRec;
     47 
     48 /*  This guy holds a mapping of a name -> family, used for looking up fonts.
     49     Since it is stored in a stretchy array that doesn't preserve object
     50     semantics, we don't use constructor/destructors, but just have explicit
     51     helpers to manage our internal bookkeeping.
     52 */
     53 struct NameFamilyPair {
     54     const char* fName;      // we own this
     55     FamilyRec*  fFamily;    // we don't own this, we just reference it
     56 
     57     void construct(const char name[], FamilyRec* family) {
     58         fName = strdup(name);
     59         fFamily = family;   // we don't own this, so just record the referene
     60     }
     61 
     62     void destruct() {
     63         free((char*)fName);
     64         // we don't own family, so just ignore our reference
     65     }
     66 };
     67 
     68 // we use atomic_inc to grow this for each typeface we create
     69 static int32_t gUniqueFontID;
     70 
     71 // this is the mutex that protects these globals
     72 static SkMutex gFamilyMutex;
     73 static FamilyRec* gFamilyHead;
     74 static SkTDArray<NameFamilyPair> gNameList;
     75 
     76 struct FamilyRec {
     77     FamilyRec*  fNext;
     78     SkTypeface* fFaces[4];
     79 
     80     FamilyRec()
     81     {
     82         fNext = gFamilyHead;
     83         memset(fFaces, 0, sizeof(fFaces));
     84         gFamilyHead = this;
     85     }
     86 };
     87 
     88 static SkTypeface* find_best_face(const FamilyRec* family,
     89                                   SkTypeface::Style style) {
     90     SkTypeface* const* faces = family->fFaces;
     91 
     92     if (faces[style] != NULL) { // exact match
     93         return faces[style];
     94     }
     95     // look for a matching bold
     96     style = (SkTypeface::Style)(style ^ SkTypeface::kItalic);
     97     if (faces[style] != NULL) {
     98         return faces[style];
     99     }
    100     // look for the plain
    101     if (faces[SkTypeface::kNormal] != NULL) {
    102         return faces[SkTypeface::kNormal];
    103     }
    104     // look for anything
    105     for (int i = 0; i < 4; i++) {
    106         if (faces[i] != NULL) {
    107             return faces[i];
    108         }
    109     }
    110     // should never get here, since the faces list should not be empty
    111     SkASSERT(!"faces list is empty");
    112     return NULL;
    113 }
    114 
    115 static FamilyRec* find_family(const SkTypeface* member) {
    116     FamilyRec* curr = gFamilyHead;
    117     while (curr != NULL) {
    118         for (int i = 0; i < 4; i++) {
    119             if (curr->fFaces[i] == member) {
    120                 return curr;
    121             }
    122         }
    123         curr = curr->fNext;
    124     }
    125     return NULL;
    126 }
    127 
    128 /*  Returns the matching typeface, or NULL. If a typeface is found, its refcnt
    129     is not modified.
    130  */
    131 static SkTypeface* find_from_uniqueID(uint32_t uniqueID) {
    132     FamilyRec* curr = gFamilyHead;
    133     while (curr != NULL) {
    134         for (int i = 0; i < 4; i++) {
    135             SkTypeface* face = curr->fFaces[i];
    136             if (face != NULL && face->uniqueID() == uniqueID) {
    137                 return face;
    138             }
    139         }
    140         curr = curr->fNext;
    141     }
    142     return NULL;
    143 }
    144 
    145 /*  Remove reference to this face from its family. If the resulting family
    146     is empty (has no faces), return that family, otherwise return NULL
    147 */
    148 static FamilyRec* remove_from_family(const SkTypeface* face) {
    149     FamilyRec* family = find_family(face);
    150     if (family) {
    151         SkASSERT(family->fFaces[face->style()] == face);
    152         family->fFaces[face->style()] = NULL;
    153 
    154         for (int i = 0; i < 4; i++) {
    155             if (family->fFaces[i] != NULL) {    // family is non-empty
    156                 return NULL;
    157             }
    158         }
    159     } else {
    160 //        SkDebugf("remove_from_family(%p) face not found", face);
    161     }
    162     return family;  // return the empty family
    163 }
    164 
    165 // maybe we should make FamilyRec be doubly-linked
    166 static void detach_and_delete_family(FamilyRec* family) {
    167     FamilyRec* curr = gFamilyHead;
    168     FamilyRec* prev = NULL;
    169 
    170     while (curr != NULL) {
    171         FamilyRec* next = curr->fNext;
    172         if (curr == family) {
    173             if (prev == NULL) {
    174                 gFamilyHead = next;
    175             } else {
    176                 prev->fNext = next;
    177             }
    178             SkDELETE(family);
    179             return;
    180         }
    181         prev = curr;
    182         curr = next;
    183     }
    184     SkASSERT(!"Yikes, couldn't find family in our list to remove/delete");
    185 }
    186 
    187 static SkTypeface* find_typeface(const char name[], SkTypeface::Style style) {
    188     NameFamilyPair* list = gNameList.begin();
    189     int             count = gNameList.count();
    190 
    191     int index = SkStrLCSearch(&list[0].fName, count, name, sizeof(list[0]));
    192 
    193     if (index >= 0) {
    194         return find_best_face(list[index].fFamily, style);
    195     }
    196     return NULL;
    197 }
    198 
    199 static SkTypeface* find_typeface(const SkTypeface* familyMember,
    200                                  SkTypeface::Style style) {
    201     const FamilyRec* family = find_family(familyMember);
    202     return family ? find_best_face(family, style) : NULL;
    203 }
    204 
    205 static void add_name(const char name[], FamilyRec* family) {
    206     SkAutoAsciiToLC tolc(name);
    207     name = tolc.lc();
    208 
    209     NameFamilyPair* list = gNameList.begin();
    210     int             count = gNameList.count();
    211 
    212     int index = SkStrLCSearch(&list[0].fName, count, name, sizeof(list[0]));
    213 
    214     if (index < 0) {
    215         list = gNameList.insert(~index);
    216         list->construct(name, family);
    217     }
    218 }
    219 
    220 static void remove_from_names(FamilyRec* emptyFamily)
    221 {
    222 #ifdef SK_DEBUG
    223     for (int i = 0; i < 4; i++) {
    224         SkASSERT(emptyFamily->fFaces[i] == NULL);
    225     }
    226 #endif
    227 
    228     SkTDArray<NameFamilyPair>& list = gNameList;
    229 
    230     // must go backwards when removing
    231     for (int i = list.count() - 1; i >= 0; --i) {
    232         NameFamilyPair* pair = &list[i];
    233         if (pair->fFamily == emptyFamily) {
    234             pair->destruct();
    235             list.remove(i);
    236         }
    237     }
    238 }
    239 
    240 ///////////////////////////////////////////////////////////////////////////////
    241 
    242 class FamilyTypeface : public SkTypeface {
    243 public:
    244     FamilyTypeface(Style style, bool sysFont, SkTypeface* familyMember,
    245                    bool isFixedWidth)
    246     : SkTypeface(style, sk_atomic_inc(&gUniqueFontID) + 1, isFixedWidth) {
    247         fIsSysFont = sysFont;
    248 
    249         SkAutoMutexAcquire  ac(gFamilyMutex);
    250 
    251         FamilyRec* rec = NULL;
    252         if (familyMember) {
    253             rec = find_family(familyMember);
    254             SkASSERT(rec);
    255         } else {
    256             rec = SkNEW(FamilyRec);
    257         }
    258         rec->fFaces[style] = this;
    259     }
    260 
    261     virtual ~FamilyTypeface() {
    262         SkAutoMutexAcquire  ac(gFamilyMutex);
    263 
    264         // remove us from our family. If the family is now empty, we return
    265         // that and then remove that family from the name list
    266         FamilyRec* family = remove_from_family(this);
    267         if (NULL != family) {
    268             remove_from_names(family);
    269             detach_and_delete_family(family);
    270         }
    271     }
    272 
    273     bool isSysFont() const { return fIsSysFont; }
    274 
    275     virtual SkStream* openStream() = 0;
    276     virtual const char* getUniqueString() const = 0;
    277     virtual const char* getFilePath() const = 0;
    278 
    279 private:
    280     bool    fIsSysFont;
    281 
    282     typedef SkTypeface INHERITED;
    283 };
    284 
    285 ///////////////////////////////////////////////////////////////////////////////
    286 
    287 class StreamTypeface : public FamilyTypeface {
    288 public:
    289     StreamTypeface(Style style, bool sysFont, SkTypeface* familyMember,
    290                    SkStream* stream, bool isFixedWidth)
    291     : INHERITED(style, sysFont, familyMember, isFixedWidth) {
    292         SkASSERT(stream);
    293         stream->ref();
    294         fStream = stream;
    295     }
    296     virtual ~StreamTypeface() {
    297         fStream->unref();
    298     }
    299 
    300     // overrides
    301     virtual SkStream* openStream() {
    302         // we just ref our existing stream, since the caller will call unref()
    303         // when they are through
    304         fStream->ref();
    305         // must rewind each time, since the caller assumes a "new" stream
    306         fStream->rewind();
    307         return fStream;
    308     }
    309     virtual const char* getUniqueString() const { return NULL; }
    310     virtual const char* getFilePath() const { return NULL; }
    311 
    312 private:
    313     SkStream* fStream;
    314 
    315     typedef FamilyTypeface INHERITED;
    316 };
    317 
    318 class FileTypeface : public FamilyTypeface {
    319 public:
    320     FileTypeface(Style style, bool sysFont, SkTypeface* familyMember,
    321                  const char path[], bool isFixedWidth)
    322     : INHERITED(style, sysFont, familyMember, isFixedWidth) {
    323         SkString fullpath;
    324 
    325         if (sysFont) {
    326             GetFullPathForSysFonts(&fullpath, path);
    327             path = fullpath.c_str();
    328         }
    329         fPath.set(path);
    330     }
    331 
    332     // overrides
    333     virtual SkStream* openStream() {
    334         SkStream* stream = SkNEW_ARGS(SkMMAPStream, (fPath.c_str()));
    335 
    336         // check for failure
    337         if (stream->getLength() <= 0) {
    338             SkDELETE(stream);
    339             // maybe MMAP isn't supported. try FILE
    340             stream = SkNEW_ARGS(SkFILEStream, (fPath.c_str()));
    341             if (stream->getLength() <= 0) {
    342                 SkDELETE(stream);
    343                 stream = NULL;
    344             }
    345         }
    346         return stream;
    347     }
    348     virtual const char* getUniqueString() const {
    349         const char* str = strrchr(fPath.c_str(), '/');
    350         if (str) {
    351             str += 1;   // skip the '/'
    352         }
    353         return str;
    354     }
    355     virtual const char* getFilePath() const {
    356         return fPath.c_str();
    357     }
    358 
    359 private:
    360     SkString fPath;
    361 
    362     typedef FamilyTypeface INHERITED;
    363 };
    364 
    365 ///////////////////////////////////////////////////////////////////////////////
    366 ///////////////////////////////////////////////////////////////////////////////
    367 
    368 static bool get_name_and_style(const char path[], SkString* name,
    369                                SkTypeface::Style* style,
    370                                bool* isFixedWidth, bool isExpected) {
    371     SkString        fullpath;
    372     GetFullPathForSysFonts(&fullpath, path);
    373 
    374     SkMMAPStream stream(fullpath.c_str());
    375     if (stream.getLength() > 0) {
    376         *style = find_name_and_attributes(&stream, name, isFixedWidth);
    377         return true;
    378     }
    379     else {
    380         SkFILEStream stream(fullpath.c_str());
    381         if (stream.getLength() > 0) {
    382             *style = find_name_and_attributes(&stream, name, isFixedWidth);
    383             return true;
    384         }
    385     }
    386 
    387     if (isExpected) {
    388         SkDebugf("---- failed to open <%s> as a font\n", fullpath.c_str());
    389     }
    390     return false;
    391 }
    392 
    393 // used to record our notion of the pre-existing fonts
    394 struct FontInitRec {
    395     const char*         fFileName;
    396     const char* const*  fNames;     // null-terminated list
    397 };
    398 
    399 // deliberately empty, but we use the address to identify fallback fonts
    400 static const char* gFBNames[] = { NULL };
    401 
    402 
    403 /*  Fonts are grouped by family, with the first font in a family having the
    404     list of names (even if that list is empty), and the following members having
    405     null for the list. The names list must be NULL-terminated.
    406 */
    407 static FontInitRec *gSystemFonts;
    408 static size_t gNumSystemFonts = 0;
    409 
    410 #define SYSTEM_FONTS_FILE "/system/etc/system_fonts.cfg"
    411 
    412 // these globals are assigned (once) by load_system_fonts()
    413 static FamilyRec* gDefaultFamily;
    414 static SkTypeface* gDefaultNormal;
    415 static char** gDefaultNames = NULL;
    416 static uint32_t *gFallbackFonts;
    417 
    418 /*  Load info from a configuration file that populates the system/fallback font structures
    419 */
    420 static void load_font_info() {
    421 //    load_font_info_xml("/system/etc/system_fonts.xml");
    422     SkTDArray<FontFamily*> fontFamilies;
    423     getFontFamilies(fontFamilies);
    424 
    425     SkTDArray<FontInitRec> fontInfo;
    426     bool firstInFamily = false;
    427     for (int i = 0; i < fontFamilies.count(); ++i) {
    428         FontFamily *family = fontFamilies[i];
    429         firstInFamily = true;
    430         for (int j = 0; j < family->fFileNames.count(); ++j) {
    431             FontInitRec fontInfoRecord;
    432             fontInfoRecord.fFileName = family->fFileNames[j];
    433             if (j == 0) {
    434                 if (family->fNames.count() == 0) {
    435                     // Fallback font
    436                     fontInfoRecord.fNames = (char **)gFBNames;
    437                 } else {
    438                     SkTDArray<const char*> names = family->fNames;
    439                     const char **nameList = (const char**)
    440                             malloc((names.count() + 1) * sizeof(char*));
    441                     if (nameList == NULL) {
    442                         // shouldn't get here
    443                         break;
    444                     }
    445                     if (gDefaultNames == NULL) {
    446                         gDefaultNames = (char**) nameList;
    447                     }
    448                     for (int i = 0; i < names.count(); ++i) {
    449                         nameList[i] = names[i];
    450                     }
    451                     nameList[names.count()] = NULL;
    452                     fontInfoRecord.fNames = nameList;
    453                 }
    454             } else {
    455                 fontInfoRecord.fNames = NULL;
    456             }
    457             *fontInfo.append() = fontInfoRecord;
    458         }
    459     }
    460     gNumSystemFonts = fontInfo.count();
    461     gSystemFonts = (FontInitRec*) malloc(gNumSystemFonts * sizeof(FontInitRec));
    462     gFallbackFonts = (uint32_t*) malloc((gNumSystemFonts + 1) * sizeof(uint32_t));
    463     if (gSystemFonts == NULL) {
    464         // shouldn't get here
    465         gNumSystemFonts = 0;
    466     }
    467     for (size_t i = 0; i < gNumSystemFonts; ++i) {
    468         gSystemFonts[i].fFileName = fontInfo[i].fFileName;
    469         gSystemFonts[i].fNames = fontInfo[i].fNames;
    470     }
    471     fontFamilies.deleteAll();
    472 }
    473 
    474 /*  Called once (ensured by the sentinel check at the beginning of our body).
    475     Initializes all the globals, and register the system fonts.
    476  */
    477 static void load_system_fonts() {
    478     // check if we've already be called
    479     if (NULL != gDefaultNormal) {
    480         return;
    481     }
    482 
    483     load_font_info();
    484 
    485     const FontInitRec* rec = gSystemFonts;
    486     SkTypeface* firstInFamily = NULL;
    487     int fallbackCount = 0;
    488 
    489     for (size_t i = 0; i < gNumSystemFonts; i++) {
    490         // if we're the first in a new family, clear firstInFamily
    491         if (rec[i].fNames != NULL) {
    492             firstInFamily = NULL;
    493         }
    494 
    495         bool isFixedWidth;
    496         SkString name;
    497         SkTypeface::Style style;
    498 
    499         // we expect all the fonts, except the "fallback" fonts
    500         bool isExpected = (rec[i].fNames != gFBNames);
    501         if (!get_name_and_style(rec[i].fFileName, &name, &style,
    502                                 &isFixedWidth, isExpected)) {
    503             continue;
    504         }
    505 
    506         SkTypeface* tf = SkNEW_ARGS(FileTypeface,
    507                                     (style,
    508                                      true,  // system-font (cannot delete)
    509                                      firstInFamily, // what family to join
    510                                      rec[i].fFileName,
    511                                      isFixedWidth) // filename
    512                                     );
    513 
    514         if (rec[i].fNames != NULL) {
    515             // see if this is one of our fallback fonts
    516             if (rec[i].fNames == gFBNames) {
    517             //    SkDebugf("---- adding %s as fallback[%d] fontID %d\n",
    518             //             rec[i].fFileName, fallbackCount, tf->uniqueID());
    519                 gFallbackFonts[fallbackCount++] = tf->uniqueID();
    520             }
    521 
    522             firstInFamily = tf;
    523             FamilyRec* family = find_family(tf);
    524             const char* const* names = rec[i].fNames;
    525 
    526             // record the default family if this is it
    527             if (names == gDefaultNames) {
    528                 gDefaultFamily = family;
    529             }
    530             // add the names to map to this family
    531             while (*names) {
    532                 add_name(*names, family);
    533                 names += 1;
    534             }
    535         }
    536     }
    537 
    538     // do this after all fonts are loaded. This is our default font, and it
    539     // acts as a sentinel so we only execute load_system_fonts() once
    540     gDefaultNormal = find_best_face(gDefaultFamily, SkTypeface::kNormal);
    541     // now terminate our fallback list with the sentinel value
    542     gFallbackFonts[fallbackCount] = 0;
    543 }
    544 
    545 ///////////////////////////////////////////////////////////////////////////////
    546 
    547 void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
    548     // lookup and record if the font is custom (i.e. not a system font)
    549     bool isCustomFont = !((FamilyTypeface*)face)->isSysFont();
    550     stream->writeBool(isCustomFont);
    551 
    552     if (isCustomFont) {
    553         SkStream* fontStream = ((FamilyTypeface*)face)->openStream();
    554 
    555         // store the length of the custom font
    556         uint32_t len = fontStream->getLength();
    557         stream->write32(len);
    558 
    559         // store the entire font in the serialized stream
    560         void* fontData = malloc(len);
    561 
    562         fontStream->read(fontData, len);
    563         stream->write(fontData, len);
    564 
    565         fontStream->unref();
    566         free(fontData);
    567 //      SkDebugf("--- fonthost custom serialize %d %d\n", face->style(), len);
    568 
    569     } else {
    570         const char* name = ((FamilyTypeface*)face)->getUniqueString();
    571 
    572         stream->write8((uint8_t)face->style());
    573 
    574         if (NULL == name || 0 == *name) {
    575             stream->writePackedUInt(0);
    576 //          SkDebugf("--- fonthost serialize null\n");
    577         } else {
    578             uint32_t len = strlen(name);
    579             stream->writePackedUInt(len);
    580             stream->write(name, len);
    581 //          SkDebugf("--- fonthost serialize <%s> %d\n", name, face->style());
    582         }
    583     }
    584 }
    585 
    586 SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
    587     load_system_fonts();
    588 
    589     // check if the font is a custom or system font
    590     bool isCustomFont = stream->readBool();
    591 
    592     if (isCustomFont) {
    593 
    594         // read the length of the custom font from the stream
    595         uint32_t len = stream->readU32();
    596 
    597         // generate a new stream to store the custom typeface
    598         SkMemoryStream* fontStream = new SkMemoryStream(len);
    599         stream->read((void*)fontStream->getMemoryBase(), len);
    600 
    601         SkTypeface* face = CreateTypefaceFromStream(fontStream);
    602 
    603         fontStream->unref();
    604 
    605 //      SkDebugf("--- fonthost custom deserialize %d %d\n", face->style(), len);
    606         return face;
    607 
    608     } else {
    609         int style = stream->readU8();
    610 
    611         int len = stream->readPackedUInt();
    612         if (len > 0) {
    613             SkString str;
    614             str.resize(len);
    615             stream->read(str.writable_str(), len);
    616 
    617             const FontInitRec* rec = gSystemFonts;
    618             for (size_t i = 0; i < gNumSystemFonts; i++) {
    619                 if (strcmp(rec[i].fFileName, str.c_str()) == 0) {
    620                     // backup until we hit the fNames
    621                     for (int j = i; j >= 0; --j) {
    622                         if (rec[j].fNames != NULL) {
    623                             return SkFontHost::CreateTypeface(NULL,
    624                                         rec[j].fNames[0], NULL, 0,
    625                                         (SkTypeface::Style)style);
    626                         }
    627                     }
    628                 }
    629             }
    630         }
    631     }
    632     return NULL;
    633 }
    634 
    635 ///////////////////////////////////////////////////////////////////////////////
    636 
    637 SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
    638                                        const char familyName[],
    639                                        const void* data, size_t bytelength,
    640                                        SkTypeface::Style style) {
    641     load_system_fonts();
    642 
    643     SkAutoMutexAcquire  ac(gFamilyMutex);
    644 
    645     // clip to legal style bits
    646     style = (SkTypeface::Style)(style & SkTypeface::kBoldItalic);
    647 
    648     SkTypeface* tf = NULL;
    649 
    650     if (NULL != familyFace) {
    651         tf = find_typeface(familyFace, style);
    652     } else if (NULL != familyName) {
    653 //        SkDebugf("======= familyName <%s>\n", familyName);
    654         tf = find_typeface(familyName, style);
    655     }
    656 
    657     if (NULL == tf) {
    658         tf = find_best_face(gDefaultFamily, style);
    659     }
    660 
    661     // we ref(), since the symantic is to return a new instance
    662     tf->ref();
    663     return tf;
    664 }
    665 
    666 bool SkFontHost::ValidFontID(uint32_t fontID) {
    667     SkAutoMutexAcquire  ac(gFamilyMutex);
    668 
    669     return find_from_uniqueID(fontID) != NULL;
    670 }
    671 
    672 SkStream* SkFontHost::OpenStream(uint32_t fontID) {
    673     SkAutoMutexAcquire  ac(gFamilyMutex);
    674 
    675     FamilyTypeface* tf = (FamilyTypeface*)find_from_uniqueID(fontID);
    676     SkStream* stream = tf ? tf->openStream() : NULL;
    677 
    678     if (stream && stream->getLength() == 0) {
    679         stream->unref();
    680         stream = NULL;
    681     }
    682     return stream;
    683 }
    684 
    685 size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length,
    686                                int32_t* index) {
    687     SkAutoMutexAcquire  ac(gFamilyMutex);
    688 
    689     FamilyTypeface* tf = (FamilyTypeface*)find_from_uniqueID(fontID);
    690     const char* src = tf ? tf->getFilePath() : NULL;
    691 
    692     if (src) {
    693         size_t size = strlen(src);
    694         if (path) {
    695             memcpy(path, src, SkMin32(size, length));
    696         }
    697         if (index) {
    698             *index = 0; // we don't have collections (yet)
    699         }
    700         return size;
    701     } else {
    702         return 0;
    703     }
    704 }
    705 
    706 SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) {
    707     load_system_fonts();
    708 
    709     const SkTypeface* origTypeface = find_from_uniqueID(origFontID);
    710     const SkTypeface* currTypeface = find_from_uniqueID(currFontID);
    711 
    712     SkASSERT(origTypeface != 0);
    713     SkASSERT(currTypeface != 0);
    714 
    715     // Our fallback list always stores the id of the plain in each fallback
    716     // family, so we transform currFontID to its plain equivalent.
    717     currFontID = find_typeface(currTypeface, SkTypeface::kNormal)->uniqueID();
    718 
    719     /*  First see if fontID is already one of our fallbacks. If so, return
    720         its successor. If fontID is not in our list, then return the first one
    721         in our list. Note: list is zero-terminated, and returning zero means
    722         we have no more fonts to use for fallbacks.
    723      */
    724     const uint32_t* list = gFallbackFonts;
    725     for (int i = 0; list[i] != 0; i++) {
    726         if (list[i] == currFontID) {
    727             if (list[i+1] == 0)
    728                 return 0;
    729             const SkTypeface* nextTypeface = find_from_uniqueID(list[i+1]);
    730             return find_typeface(nextTypeface, origTypeface->style())->uniqueID();
    731         }
    732     }
    733 
    734     // If we get here, currFontID was not a fallback, so we start at the
    735     // beginning of our list.
    736     const SkTypeface* firstTypeface = find_from_uniqueID(list[0]);
    737     return find_typeface(firstTypeface, origTypeface->style())->uniqueID();
    738 }
    739 
    740 ///////////////////////////////////////////////////////////////////////////////
    741 
    742 SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
    743     if (NULL == stream || stream->getLength() <= 0) {
    744         return NULL;
    745     }
    746 
    747     bool isFixedWidth;
    748     SkString name;
    749     SkTypeface::Style style = find_name_and_attributes(stream, &name, &isFixedWidth);
    750 
    751     if (!name.isEmpty()) {
    752         return SkNEW_ARGS(StreamTypeface, (style, false, NULL, stream, isFixedWidth));
    753     } else {
    754         return NULL;
    755     }
    756 }
    757 
    758 SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
    759     SkStream* stream = SkNEW_ARGS(SkMMAPStream, (path));
    760     SkTypeface* face = SkFontHost::CreateTypefaceFromStream(stream);
    761     // since we created the stream, we let go of our ref() here
    762     stream->unref();
    763     return face;
    764 }
    765 
    766 ///////////////////////////////////////////////////////////////////////////////
    767 
    768 size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) {
    769     if (sizeAllocatedSoFar > FONT_CACHE_MEMORY_BUDGET)
    770         return sizeAllocatedSoFar - FONT_CACHE_MEMORY_BUDGET;
    771     else
    772         return 0;   // nothing to do
    773 }
    774 
    775