1From 6982ad469adcdfa2b7bdbf8bbd843bc22d3832fc Mon Sep 17 00:00:00 2001
2From: George Wright <gwright@mozilla.com>
3Date: Fri, 18 May 2012 14:52:40 -0400
4Subject: [PATCH 07/10]     Bug 755869 - [10] Re-apply bug 719872 - Fix crash
5 on Android by reverting to older FontHost impl
6 r=mattwoodrow
7
8---
9 gfx/skia/Makefile.in                          |    5 +-
10 gfx/skia/src/ports/SkFontHost_android_old.cpp |  664 +++++++++++++++++++++++++
11 2 files changed, 668 insertions(+), 1 deletions(-)
12 create mode 100644 gfx/skia/src/ports/SkFontHost_android_old.cpp
13
14diff --git a/gfx/skia/Makefile.in b/gfx/skia/Makefile.in
15index 9da098a..8184f1c 100644
16--- a/gfx/skia/Makefile.in
17+++ b/gfx/skia/Makefile.in
18@@ -327,7 +327,10 @@ endif
19 ifeq (android,$(MOZ_WIDGET_TOOLKIT))
20 CPPSRCS += \
21 	SkDebug_android.cpp \
22-	SkFontHost_none.cpp \
23+	SkFontHost_android_old.cpp \
24+	SkFontHost_gamma.cpp \
25+	SkFontHost_FreeType.cpp \
26+	SkFontHost_tables.cpp \
27 	SkMMapStream.cpp \
28 	SkTime_Unix.cpp \
29 	SkThread_pthread.cpp \
30diff --git a/gfx/skia/src/ports/SkFontHost_android_old.cpp b/gfx/skia/src/ports/SkFontHost_android_old.cpp
31new file mode 100644
32index 0000000..b5c4f3c
33--- /dev/null
34+++ b/gfx/skia/src/ports/SkFontHost_android_old.cpp
35@@ -0,0 +1,664 @@
36+
37+/*
38+ * Copyright 2006 The Android Open Source Project
39+ *
40+ * Use of this source code is governed by a BSD-style license that can be
41+ * found in the LICENSE file.
42+ */
43+
44+
45+#include "SkFontHost.h"
46+#include "SkDescriptor.h"
47+#include "SkMMapStream.h"
48+#include "SkPaint.h"
49+#include "SkString.h"
50+#include "SkStream.h"
51+#include "SkThread.h"
52+#include "SkTSearch.h"
53+#include <stdio.h>
54+
55+#define FONT_CACHE_MEMORY_BUDGET    (768 * 1024)
56+
57+#ifndef SK_FONT_FILE_PREFIX
58+    #define SK_FONT_FILE_PREFIX          "/fonts/"
59+#endif
60+
61+bool find_name_and_attributes(SkStream* stream, SkString* name, SkTypeface::Style* style,
62+                                           bool* isFixedWidth);
63+
64+static void GetFullPathForSysFonts(SkString* full, const char name[]) {
65+    full->set(getenv("ANDROID_ROOT"));
66+    full->append(SK_FONT_FILE_PREFIX);
67+    full->append(name);
68+}
69+
70+///////////////////////////////////////////////////////////////////////////////
71+
72+struct FamilyRec;
73+
74+/*  This guy holds a mapping of a name -> family, used for looking up fonts.
75+    Since it is stored in a stretchy array that doesn't preserve object
76+    semantics, we don't use constructor/destructors, but just have explicit
77+    helpers to manage our internal bookkeeping.
78+*/
79+struct NameFamilyPair {
80+    const char* fName;      // we own this
81+    FamilyRec*  fFamily;    // we don't own this, we just reference it
82+
83+    void construct(const char name[], FamilyRec* family) {
84+        fName = strdup(name);
85+        fFamily = family;   // we don't own this, so just record the referene
86+    }
87+
88+    void destruct() {
89+        free((char*)fName);
90+        // we don't own family, so just ignore our reference
91+    }
92+};
93+
94+// we use atomic_inc to grow this for each typeface we create
95+static int32_t gUniqueFontID;
96+
97+// this is the mutex that protects these globals
98+static SkMutex gFamilyMutex;
99+static FamilyRec* gFamilyHead;
100+static SkTDArray<NameFamilyPair> gNameList;
101+
102+struct FamilyRec {
103+    FamilyRec*  fNext;
104+    SkTypeface* fFaces[4];
105+
106+    FamilyRec()
107+    {
108+        fNext = gFamilyHead;
109+        memset(fFaces, 0, sizeof(fFaces));
110+        gFamilyHead = this;
111+    }
112+};
113+
114+static SkTypeface* find_best_face(const FamilyRec* family,
115+                                  SkTypeface::Style style) {
116+    SkTypeface* const* faces = family->fFaces;
117+
118+    if (faces[style] != NULL) { // exact match
119+        return faces[style];
120+    }
121+    // look for a matching bold
122+    style = (SkTypeface::Style)(style ^ SkTypeface::kItalic);
123+    if (faces[style] != NULL) {
124+        return faces[style];
125+    }
126+    // look for the plain
127+    if (faces[SkTypeface::kNormal] != NULL) {
128+        return faces[SkTypeface::kNormal];
129+    }
130+    // look for anything
131+    for (int i = 0; i < 4; i++) {
132+        if (faces[i] != NULL) {
133+            return faces[i];
134+        }
135+    }
136+    // should never get here, since the faces list should not be empty
137+    SkASSERT(!"faces list is empty");
138+    return NULL;
139+}
140+
141+static FamilyRec* find_family(const SkTypeface* member) {
142+    FamilyRec* curr = gFamilyHead;
143+    while (curr != NULL) {
144+        for (int i = 0; i < 4; i++) {
145+            if (curr->fFaces[i] == member) {
146+                return curr;
147+            }
148+        }
149+        curr = curr->fNext;
150+    }
151+    return NULL;
152+}
153+
154+/*  Returns the matching typeface, or NULL. If a typeface is found, its refcnt
155+    is not modified.
156+ */
157+static SkTypeface* find_from_uniqueID(uint32_t uniqueID) {
158+    FamilyRec* curr = gFamilyHead;
159+    while (curr != NULL) {
160+        for (int i = 0; i < 4; i++) {
161+            SkTypeface* face = curr->fFaces[i];
162+            if (face != NULL && face->uniqueID() == uniqueID) {
163+                return face;
164+            }
165+        }
166+        curr = curr->fNext;
167+    }
168+    return NULL;
169+}
170+
171+/*  Remove reference to this face from its family. If the resulting family
172+    is empty (has no faces), return that family, otherwise return NULL
173+*/
174+static FamilyRec* remove_from_family(const SkTypeface* face) {
175+    FamilyRec* family = find_family(face);
176+    SkASSERT(family->fFaces[face->style()] == face);
177+    family->fFaces[face->style()] = NULL;
178+
179+    for (int i = 0; i < 4; i++) {
180+        if (family->fFaces[i] != NULL) {    // family is non-empty
181+            return NULL;
182+        }
183+    }
184+    return family;  // return the empty family
185+}
186+
187+// maybe we should make FamilyRec be doubly-linked
188+static void detach_and_delete_family(FamilyRec* family) {
189+    FamilyRec* curr = gFamilyHead;
190+    FamilyRec* prev = NULL;
191+
192+    while (curr != NULL) {
193+        FamilyRec* next = curr->fNext;
194+        if (curr == family) {
195+            if (prev == NULL) {
196+                gFamilyHead = next;
197+            } else {
198+                prev->fNext = next;
199+            }
200+            SkDELETE(family);
201+            return;
202+        }
203+        prev = curr;
204+        curr = next;
205+    }
206+    SkASSERT(!"Yikes, couldn't find family in our list to remove/delete");
207+}
208+
209+static SkTypeface* find_typeface(const char name[], SkTypeface::Style style) {
210+    NameFamilyPair* list = gNameList.begin();
211+    int             count = gNameList.count();
212+
213+    int index = SkStrLCSearch(&list[0].fName, count, name, sizeof(list[0]));
214+
215+    if (index >= 0) {
216+        return find_best_face(list[index].fFamily, style);
217+    }
218+    return NULL;
219+}
220+
221+static SkTypeface* find_typeface(const SkTypeface* familyMember,
222+                                 SkTypeface::Style style) {
223+    const FamilyRec* family = find_family(familyMember);
224+    return family ? find_best_face(family, style) : NULL;
225+}
226+
227+static void add_name(const char name[], FamilyRec* family) {
228+    SkAutoAsciiToLC tolc(name);
229+    name = tolc.lc();
230+
231+    NameFamilyPair* list = gNameList.begin();
232+    int             count = gNameList.count();
233+
234+    int index = SkStrLCSearch(&list[0].fName, count, name, sizeof(list[0]));
235+
236+    if (index < 0) {
237+        list = gNameList.insert(~index);
238+        list->construct(name, family);
239+    }
240+}
241+
242+static void remove_from_names(FamilyRec* emptyFamily)
243+{
244+#ifdef SK_DEBUG
245+    for (int i = 0; i < 4; i++) {
246+        SkASSERT(emptyFamily->fFaces[i] == NULL);
247+    }
248+#endif
249+
250+    SkTDArray<NameFamilyPair>& list = gNameList;
251+
252+    // must go backwards when removing
253+    for (int i = list.count() - 1; i >= 0; --i) {
254+        NameFamilyPair* pair = &list[i];
255+        if (pair->fFamily == emptyFamily) {
256+            pair->destruct();
257+            list.remove(i);
258+        }
259+    }
260+}
261+
262+///////////////////////////////////////////////////////////////////////////////
263+
264+class FamilyTypeface : public SkTypeface {
265+public:
266+    FamilyTypeface(Style style, bool sysFont, SkTypeface* familyMember,
267+                   bool isFixedWidth)
268+    : SkTypeface(style, sk_atomic_inc(&gUniqueFontID) + 1, isFixedWidth) {
269+        fIsSysFont = sysFont;
270+
271+        SkAutoMutexAcquire  ac(gFamilyMutex);
272+
273+        FamilyRec* rec = NULL;
274+        if (familyMember) {
275+            rec = find_family(familyMember);
276+            SkASSERT(rec);
277+        } else {
278+            rec = SkNEW(FamilyRec);
279+        }
280+        rec->fFaces[style] = this;
281+    }
282+
283+    virtual ~FamilyTypeface() {
284+        SkAutoMutexAcquire  ac(gFamilyMutex);
285+
286+        // remove us from our family. If the family is now empty, we return
287+        // that and then remove that family from the name list
288+        FamilyRec* family = remove_from_family(this);
289+        if (NULL != family) {
290+            remove_from_names(family);
291+            detach_and_delete_family(family);
292+        }
293+    }
294+
295+    bool isSysFont() const { return fIsSysFont; }
296+
297+    virtual SkStream* openStream() = 0;
298+    virtual const char* getUniqueString() const = 0;
299+    virtual const char* getFilePath() const = 0;
300+
301+private:
302+    bool    fIsSysFont;
303+
304+    typedef SkTypeface INHERITED;
305+};
306+
307+///////////////////////////////////////////////////////////////////////////////
308+
309+class StreamTypeface : public FamilyTypeface {
310+public:
311+    StreamTypeface(Style style, bool sysFont, SkTypeface* familyMember,
312+                   SkStream* stream, bool isFixedWidth)
313+    : INHERITED(style, sysFont, familyMember, isFixedWidth) {
314+        SkASSERT(stream);
315+        stream->ref();
316+        fStream = stream;
317+    }
318+    virtual ~StreamTypeface() {
319+        fStream->unref();
320+    }
321+
322+    // overrides
323+    virtual SkStream* openStream() {
324+        // we just ref our existing stream, since the caller will call unref()
325+        // when they are through
326+        fStream->ref();
327+        // must rewind each time, since the caller assumes a "new" stream
328+        fStream->rewind();
329+        return fStream;
330+    }
331+    virtual const char* getUniqueString() const { return NULL; }
332+    virtual const char* getFilePath() const { return NULL; }
333+
334+private:
335+    SkStream* fStream;
336+
337+    typedef FamilyTypeface INHERITED;
338+};
339+
340+class FileTypeface : public FamilyTypeface {
341+public:
342+    FileTypeface(Style style, bool sysFont, SkTypeface* familyMember,
343+                 const char path[], bool isFixedWidth)
344+    : INHERITED(style, sysFont, familyMember, isFixedWidth) {
345+        SkString fullpath;
346+
347+        if (sysFont) {
348+            GetFullPathForSysFonts(&fullpath, path);
349+            path = fullpath.c_str();
350+        }
351+        fPath.set(path);
352+    }
353+
354+    // overrides
355+    virtual SkStream* openStream() {
356+        SkStream* stream = SkNEW_ARGS(SkMMAPStream, (fPath.c_str()));
357+
358+        // check for failure
359+        if (stream->getLength() <= 0) {
360+            SkDELETE(stream);
361+            // maybe MMAP isn't supported. try FILE
362+            stream = SkNEW_ARGS(SkFILEStream, (fPath.c_str()));
363+            if (stream->getLength() <= 0) {
364+                SkDELETE(stream);
365+                stream = NULL;
366+            }
367+        }
368+        return stream;
369+    }
370+    virtual const char* getUniqueString() const {
371+        const char* str = strrchr(fPath.c_str(), '/');
372+        if (str) {
373+            str += 1;   // skip the '/'
374+        }
375+        return str;
376+    }
377+    virtual const char* getFilePath() const {
378+        return fPath.c_str();
379+    }
380+
381+private:
382+    SkString fPath;
383+
384+    typedef FamilyTypeface INHERITED;
385+};
386+
387+///////////////////////////////////////////////////////////////////////////////
388+///////////////////////////////////////////////////////////////////////////////
389+
390+static bool get_name_and_style(const char path[], SkString* name,
391+                               SkTypeface::Style* style,
392+                               bool* isFixedWidth, bool isExpected) {
393+    SkString        fullpath;
394+    GetFullPathForSysFonts(&fullpath, path);
395+
396+    SkMMAPStream stream(fullpath.c_str());
397+    if (stream.getLength() > 0) {
398+        find_name_and_attributes(&stream, name, style, isFixedWidth);
399+        return true;
400+    }
401+    else {
402+        SkFILEStream stream(fullpath.c_str());
403+        if (stream.getLength() > 0) {
404+            find_name_and_attributes(&stream, name, style, isFixedWidth);
405+            return true;
406+        }
407+    }
408+
409+    if (isExpected) {
410+        SkDebugf("---- failed to open <%s> as a font\n", fullpath.c_str());
411+    }
412+    return false;
413+}
414+
415+// used to record our notion of the pre-existing fonts
416+struct FontInitRec {
417+    const char*         fFileName;
418+    const char* const*  fNames;     // null-terminated list
419+};
420+
421+static const char* gSansNames[] = {
422+    "sans-serif", "arial", "helvetica", "tahoma", "verdana", NULL
423+};
424+
425+static const char* gSerifNames[] = {
426+    "serif", "times", "times new roman", "palatino", "georgia", "baskerville",
427+    "goudy", "fantasy", "cursive", "ITC Stone Serif", NULL
428+};
429+
430+static const char* gMonoNames[] = {
431+    "monospace", "courier", "courier new", "monaco", NULL
432+};
433+
434+// deliberately empty, but we use the address to identify fallback fonts
435+static const char* gFBNames[] = { NULL };
436+
437+/*  Fonts must be grouped by family, with the first font in a family having the
438+    list of names (even if that list is empty), and the following members having
439+    null for the list. The names list must be NULL-terminated
440+*/
441+static const FontInitRec gSystemFonts[] = {
442+    { "DroidSans.ttf",              gSansNames  },
443+    { "DroidSans-Bold.ttf",         NULL        },
444+    { "DroidSerif-Regular.ttf",     gSerifNames },
445+    { "DroidSerif-Bold.ttf",        NULL        },
446+    { "DroidSerif-Italic.ttf",      NULL        },
447+    { "DroidSerif-BoldItalic.ttf",  NULL        },
448+    { "DroidSansMono.ttf",          gMonoNames  },
449+    /*  These are optional, and can be ignored if not found in the file system.
450+        These are appended to gFallbackFonts[] as they are seen, so we list
451+        them in the order we want them to be accessed by NextLogicalFont().
452+     */
453+    { "DroidSansArabic.ttf",        gFBNames    },
454+    { "DroidSansHebrew.ttf",        gFBNames    },
455+    { "DroidSansThai.ttf",          gFBNames    },
456+    { "MTLmr3m.ttf",                gFBNames    }, // Motoya Japanese Font
457+    { "MTLc3m.ttf",                 gFBNames    }, // Motoya Japanese Font
458+    { "DroidSansJapanese.ttf",      gFBNames    },
459+    { "DroidSansFallback.ttf",      gFBNames    }
460+};
461+
462+#define DEFAULT_NAMES   gSansNames
463+
464+// these globals are assigned (once) by load_system_fonts()
465+static FamilyRec* gDefaultFamily;
466+static SkTypeface* gDefaultNormal;
467+
468+/*  This is sized conservatively, assuming that it will never be a size issue.
469+    It will be initialized in load_system_fonts(), and will be filled with the
470+    fontIDs that can be used for fallback consideration, in sorted order (sorted
471+    meaning element[0] should be used first, then element[1], etc. When we hit
472+    a fontID==0 in the array, the list is done, hence our allocation size is
473+    +1 the total number of possible system fonts. Also see NextLogicalFont().
474+ */
475+static uint32_t gFallbackFonts[SK_ARRAY_COUNT(gSystemFonts)+1];
476+
477+/*  Called once (ensured by the sentinel check at the beginning of our body).
478+    Initializes all the globals, and register the system fonts.
479+ */
480+static void load_system_fonts() {
481+    // check if we've already be called
482+    if (NULL != gDefaultNormal) {
483+        return;
484+    }
485+
486+    const FontInitRec* rec = gSystemFonts;
487+    SkTypeface* firstInFamily = NULL;
488+    int fallbackCount = 0;
489+
490+    for (size_t i = 0; i < SK_ARRAY_COUNT(gSystemFonts); i++) {
491+        // if we're the first in a new family, clear firstInFamily
492+        if (rec[i].fNames != NULL) {
493+            firstInFamily = NULL;
494+        }
495+
496+        bool isFixedWidth;
497+        SkString name;
498+        SkTypeface::Style style;
499+
500+        // we expect all the fonts, except the "fallback" fonts
501+        bool isExpected = (rec[i].fNames != gFBNames);
502+        if (!get_name_and_style(rec[i].fFileName, &name, &style,
503+                                &isFixedWidth, isExpected)) {
504+            continue;
505+        }
506+
507+        SkTypeface* tf = SkNEW_ARGS(FileTypeface,
508+                                    (style,
509+                                     true,  // system-font (cannot delete)
510+                                     firstInFamily, // what family to join
511+                                     rec[i].fFileName,
512+                                     isFixedWidth) // filename
513+                                    );
514+
515+        if (rec[i].fNames != NULL) {
516+            // see if this is one of our fallback fonts
517+            if (rec[i].fNames == gFBNames) {
518+            //    SkDebugf("---- adding %s as fallback[%d] fontID %d\n",
519+            //             rec[i].fFileName, fallbackCount, tf->uniqueID());
520+                gFallbackFonts[fallbackCount++] = tf->uniqueID();
521+            }
522+
523+            firstInFamily = tf;
524+            FamilyRec* family = find_family(tf);
525+            const char* const* names = rec[i].fNames;
526+
527+            // record the default family if this is it
528+            if (names == DEFAULT_NAMES) {
529+                gDefaultFamily = family;
530+            }
531+            // add the names to map to this family
532+            while (*names) {
533+                add_name(*names, family);
534+                names += 1;
535+            }
536+        }
537+    }
538+
539+    // do this after all fonts are loaded. This is our default font, and it
540+    // acts as a sentinel so we only execute load_system_fonts() once
541+    gDefaultNormal = find_best_face(gDefaultFamily, SkTypeface::kNormal);
542+    // now terminate our fallback list with the sentinel value
543+    gFallbackFonts[fallbackCount] = 0;
544+}
545+
546+///////////////////////////////////////////////////////////////////////////////
547+
548+void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
549+    const char* name = ((FamilyTypeface*)face)->getUniqueString();
550+
551+    stream->write8((uint8_t)face->style());
552+
553+    if (NULL == name || 0 == *name) {
554+        stream->writePackedUInt(0);
555+//        SkDebugf("--- fonthost serialize null\n");
556+    } else {
557+        uint32_t len = strlen(name);
558+        stream->writePackedUInt(len);
559+        stream->write(name, len);
560+//      SkDebugf("--- fonthost serialize <%s> %d\n", name, face->style());
561+    }
562+}
563+
564+SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
565+    load_system_fonts();
566+
567+    int style = stream->readU8();
568+
569+    int len = stream->readPackedUInt();
570+    if (len > 0) {
571+        SkString str;
572+        str.resize(len);
573+        stream->read(str.writable_str(), len);
574+
575+        const FontInitRec* rec = gSystemFonts;
576+        for (size_t i = 0; i < SK_ARRAY_COUNT(gSystemFonts); i++) {
577+            if (strcmp(rec[i].fFileName, str.c_str()) == 0) {
578+                // backup until we hit the fNames
579+                for (int j = i; j >= 0; --j) {
580+                    if (rec[j].fNames != NULL) {
581+                        return SkFontHost::CreateTypeface(NULL,
582+                                    rec[j].fNames[0], (SkTypeface::Style)style);
583+                    }
584+                }
585+            }
586+        }
587+    }
588+    return NULL;
589+}
590+
591+///////////////////////////////////////////////////////////////////////////////
592+
593+SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
594+                                       const char familyName[],
595+                                       SkTypeface::Style style) {
596+    load_system_fonts();
597+
598+    SkAutoMutexAcquire  ac(gFamilyMutex);
599+
600+    // clip to legal style bits
601+    style = (SkTypeface::Style)(style & SkTypeface::kBoldItalic);
602+
603+    SkTypeface* tf = NULL;
604+
605+    if (NULL != familyFace) {
606+        tf = find_typeface(familyFace, style);
607+    } else if (NULL != familyName) {
608+//        SkDebugf("======= familyName <%s>\n", familyName);
609+        tf = find_typeface(familyName, style);
610+    }
611+
612+    if (NULL == tf) {
613+        tf = find_best_face(gDefaultFamily, style);
614+    }
615+
616+    // we ref(), since the symantic is to return a new instance
617+    tf->ref();
618+    return tf;
619+}
620+
621+SkStream* SkFontHost::OpenStream(uint32_t fontID) {
622+    SkAutoMutexAcquire  ac(gFamilyMutex);
623+
624+    FamilyTypeface* tf = (FamilyTypeface*)find_from_uniqueID(fontID);
625+    SkStream* stream = tf ? tf->openStream() : NULL;
626+
627+    if (stream && stream->getLength() == 0) {
628+        stream->unref();
629+        stream = NULL;
630+    }
631+    return stream;
632+}
633+
634+size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length,
635+                               int32_t* index) {
636+    SkAutoMutexAcquire  ac(gFamilyMutex);
637+
638+    FamilyTypeface* tf = (FamilyTypeface*)find_from_uniqueID(fontID);
639+    const char* src = tf ? tf->getFilePath() : NULL;
640+
641+    if (src) {
642+        size_t size = strlen(src);
643+        if (path) {
644+            memcpy(path, src, SkMin32(size, length));
645+        }
646+        if (index) {
647+            *index = 0; // we don't have collections (yet)
648+        }
649+        return size;
650+    } else {
651+        return 0;
652+    }
653+}
654+
655+SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) {
656+    load_system_fonts();
657+
658+    /*  First see if fontID is already one of our fallbacks. If so, return
659+        its successor. If fontID is not in our list, then return the first one
660+        in our list. Note: list is zero-terminated, and returning zero means
661+        we have no more fonts to use for fallbacks.
662+     */
663+    const uint32_t* list = gFallbackFonts;
664+    for (int i = 0; list[i] != 0; i++) {
665+        if (list[i] == currFontID) {
666+            return list[i+1];
667+        }
668+    }
669+    return list[0];
670+}
671+
672+///////////////////////////////////////////////////////////////////////////////
673+
674+SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
675+    if (NULL == stream || stream->getLength() <= 0) {
676+        return NULL;
677+    }
678+
679+    bool isFixedWidth;
680+    SkString name;
681+    SkTypeface::Style style;
682+    find_name_and_attributes(stream, &name, &style, &isFixedWidth);
683+
684+    if (!name.isEmpty()) {
685+        return SkNEW_ARGS(StreamTypeface, (style, false, NULL, stream, isFixedWidth));
686+    } else {
687+        return NULL;
688+    }
689+}
690+
691+SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
692+    SkStream* stream = SkNEW_ARGS(SkMMAPStream, (path));
693+    SkTypeface* face = SkFontHost::CreateTypefaceFromStream(stream);
694+    // since we created the stream, we let go of our ref() here
695+    stream->unref();
696+    return face;
697+}
698+
699+///////////////////////////////////////////////////////////////////////////////
700--
7011.7.5.4
702
703