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