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