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