1 /* 2 * Copyright 2001-2016, Haiku. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * DarkWyrm <bpmagic@columbus.rr.com> 7 * Axel Dörfler, axeld@pinc-software.de 8 */ 9 10 11 /*! Manages font families and styles */ 12 13 14 #include "FontManager.h" 15 16 #include <new> 17 18 #include <Autolock.h> 19 #include <Directory.h> 20 #include <Entry.h> 21 #include <File.h> 22 #include <FindDirectory.h> 23 #include <Message.h> 24 #include <NodeMonitor.h> 25 #include <Path.h> 26 #include <String.h> 27 28 #include "FontFamily.h" 29 #include "ServerConfig.h" 30 #include "ServerFont.h" 31 32 33 //#define TRACE_FONT_MANAGER 34 #ifdef TRACE_FONT_MANAGER 35 # define FTRACE(x) printf x 36 #else 37 # define FTRACE(x) ; 38 #endif 39 40 41 // TODO: needs some more work for multi-user support 42 43 FT_Library gFreeTypeLibrary; 44 FontManager *gFontManager = NULL; 45 46 struct FontManager::font_directory { 47 node_ref directory; 48 uid_t user; 49 gid_t group; 50 uint32 revision; 51 BObjectList<FontStyle> styles; 52 53 bool AlreadyScanned() const { return revision != 0; } 54 FontStyle* FindStyle(const node_ref& nodeRef) const; 55 }; 56 57 struct FontManager::font_mapping { 58 BString family; 59 BString style; 60 entry_ref ref; 61 }; 62 63 64 FontStyle* 65 FontManager::font_directory::FindStyle(const node_ref& nodeRef) const 66 { 67 for (int32 i = styles.CountItems(); i-- > 0;) { 68 FontStyle* style = styles.ItemAt(i); 69 70 if (nodeRef == style->NodeRef()) 71 return style; 72 } 73 74 return NULL; 75 } 76 77 78 static status_t 79 set_entry(node_ref& nodeRef, const char* name, BEntry& entry) 80 { 81 entry_ref ref; 82 ref.device = nodeRef.device; 83 ref.directory = nodeRef.node; 84 85 status_t status = ref.set_name(name); 86 if (status != B_OK) 87 return status; 88 89 return entry.SetTo(&ref); 90 } 91 92 93 static int 94 compare_font_families(const FontFamily* a, const FontFamily* b) 95 { 96 return strcmp(a->Name(), b->Name()); 97 } 98 99 100 // #pragma mark - 101 102 103 //! Does basic set up so that directories can be scanned 104 FontManager::FontManager() 105 : BLooper("Font Manager"), 106 fDirectories(10, true), 107 fMappings(10, true), 108 fFamilies(20), 109 fDefaultPlainFont(NULL), 110 fDefaultBoldFont(NULL), 111 fDefaultFixedFont(NULL), 112 fScanned(false), 113 fNextID(0) 114 { 115 fInitStatus = FT_Init_FreeType(&gFreeTypeLibrary) == 0 ? B_OK : B_ERROR; 116 117 if (fInitStatus == B_OK) { 118 _AddSystemPaths(); 119 _LoadRecentFontMappings(); 120 121 fInitStatus = _SetDefaultFonts(); 122 123 if (fInitStatus == B_OK) { 124 // Precache the plain and bold fonts 125 _PrecacheFontFile(fDefaultPlainFont.Get()); 126 _PrecacheFontFile(fDefaultBoldFont.Get()); 127 128 // Post a message so we scan the initial paths. 129 PostMessage(B_PULSE); 130 } 131 } 132 } 133 134 135 //! Frees items allocated in the constructor and shuts down FreeType 136 FontManager::~FontManager() 137 { 138 fDefaultPlainFont.Unset(); 139 fDefaultBoldFont.Unset(); 140 fDefaultFixedFont.Unset(); 141 142 // free families before we're done with FreeType 143 144 for (int32 i = fFamilies.CountItems(); i-- > 0;) 145 delete fFamilies.ItemAt(i); 146 147 FT_Done_FreeType(gFreeTypeLibrary); 148 } 149 150 151 void 152 FontManager::MessageReceived(BMessage* message) 153 { 154 switch (message->what) { 155 case B_NODE_MONITOR: 156 { 157 int32 opcode; 158 if (message->FindInt32("opcode", &opcode) != B_OK) 159 return; 160 161 switch (opcode) { 162 case B_ENTRY_CREATED: 163 { 164 const char* name; 165 node_ref nodeRef; 166 if (message->FindInt32("device", &nodeRef.device) != B_OK 167 || message->FindInt64("directory", &nodeRef.node) != B_OK 168 || message->FindString("name", &name) != B_OK) 169 break; 170 171 // TODO: make this better (possible under Haiku) 172 snooze(100000); 173 // let the font be written completely before trying to open it 174 175 BEntry entry; 176 if (set_entry(nodeRef, name, entry) != B_OK) 177 break; 178 179 if (entry.IsDirectory()) { 180 // a new directory to watch for us 181 _AddPath(entry); 182 } else { 183 // a new font 184 font_directory* directory = _FindDirectory(nodeRef); 185 if (directory == NULL) { 186 // unknown directory? how come? 187 break; 188 } 189 190 _AddFont(*directory, entry); 191 } 192 break; 193 } 194 195 case B_ENTRY_MOVED: 196 { 197 // has the entry been moved into a monitored directory or has 198 // it been removed from one? 199 const char* name; 200 node_ref nodeRef; 201 uint64 fromNode; 202 uint64 node; 203 if (message->FindInt32("device", &nodeRef.device) != B_OK 204 || message->FindInt64("to directory", &nodeRef.node) != B_OK 205 || message->FindInt64("from directory", (int64 *)&fromNode) != B_OK 206 || message->FindInt64("node", (int64 *)&node) != B_OK 207 || message->FindString("name", &name) != B_OK) 208 break; 209 210 font_directory* directory = _FindDirectory(nodeRef); 211 212 BEntry entry; 213 if (set_entry(nodeRef, name, entry) != B_OK) 214 break; 215 216 if (directory != NULL) { 217 // something has been added to our watched font directories 218 219 // test, if the source directory is one of ours as well 220 nodeRef.node = fromNode; 221 font_directory* fromDirectory = _FindDirectory(nodeRef); 222 223 if (entry.IsDirectory()) { 224 if (fromDirectory == NULL) { 225 // there is a new directory to watch for us 226 _AddPath(entry); 227 FTRACE(("new directory moved in")); 228 } else { 229 // A directory from our watched directories has 230 // been renamed or moved within the watched 231 // directories - we only need to update the 232 // path names of the styles in that directory 233 nodeRef.node = node; 234 directory = _FindDirectory(nodeRef); 235 if (directory != NULL) { 236 for (int32 i = 0; i < directory->styles.CountItems(); i++) { 237 FontStyle* style = directory->styles.ItemAt(i); 238 style->UpdatePath(directory->directory); 239 } 240 } 241 FTRACE(("directory renamed")); 242 } 243 } else { 244 if (fromDirectory != NULL) { 245 // find style in source and move it to the target 246 nodeRef.node = node; 247 FontStyle* style = fromDirectory->FindStyle(nodeRef); 248 if (style != NULL) { 249 fromDirectory->styles.RemoveItem(style, false); 250 directory->styles.AddItem(style); 251 style->UpdatePath(directory->directory); 252 } 253 FTRACE(("font moved")); 254 } else { 255 FTRACE(("font added: %s\n", name)); 256 _AddFont(*directory, entry); 257 } 258 } 259 } else { 260 // and entry has been removed from our font directories 261 if (entry.IsDirectory()) { 262 if (entry.GetNodeRef(&nodeRef) == B_OK 263 && (directory = _FindDirectory(nodeRef)) != NULL) 264 _RemoveDirectory(directory); 265 } else { 266 // remove font style from directory 267 _RemoveStyle(nodeRef.device, fromNode, node); 268 } 269 } 270 break; 271 } 272 273 case B_ENTRY_REMOVED: 274 { 275 node_ref nodeRef; 276 uint64 directoryNode; 277 if (message->FindInt32("device", &nodeRef.device) != B_OK 278 || message->FindInt64("directory", (int64 *)&directoryNode) != B_OK 279 || message->FindInt64("node", &nodeRef.node) != B_OK) 280 break; 281 282 font_directory* directory = _FindDirectory(nodeRef); 283 if (directory != NULL) { 284 // the directory has been removed, so we remove it as well 285 _RemoveDirectory(directory); 286 } else { 287 // remove font style from directory 288 _RemoveStyle(nodeRef.device, directoryNode, nodeRef.node); 289 } 290 break; 291 } 292 } 293 break; 294 } 295 296 default: 297 BLooper::MessageReceived(message); 298 break; 299 } 300 301 // Scan fonts here if we need to, preventing other threads from having to do so. 302 _ScanFontsIfNecessary(); 303 } 304 305 306 void 307 FontManager::SaveRecentFontMappings() 308 { 309 } 310 311 312 void 313 FontManager::_AddDefaultMapping(const char* family, const char* style, 314 const char* path) 315 { 316 font_mapping* mapping = new (std::nothrow) font_mapping; 317 if (mapping == NULL) 318 return; 319 320 mapping->family = family; 321 mapping->style = style; 322 BEntry entry(path); 323 324 if (entry.GetRef(&mapping->ref) != B_OK 325 || !entry.Exists() 326 || !fMappings.AddItem(mapping)) 327 delete mapping; 328 } 329 330 331 bool 332 FontManager::_LoadRecentFontMappings() 333 { 334 // default known mappings 335 // TODO: load them for real, and use these as a fallback 336 337 BPath ttfontsPath; 338 if (find_directory(B_BEOS_FONTS_DIRECTORY, &ttfontsPath) == B_OK) { 339 ttfontsPath.Append("ttfonts"); 340 341 BPath veraFontPath = ttfontsPath; 342 veraFontPath.Append("NotoSansDisplay-Regular.ttf"); 343 _AddDefaultMapping("Noto Sans Display", "Book", veraFontPath.Path()); 344 345 veraFontPath.SetTo(ttfontsPath.Path()); 346 veraFontPath.Append("NotoSansDisplay-Bold.ttf"); 347 _AddDefaultMapping("Noto Sans Display", "Bold", veraFontPath.Path()); 348 349 veraFontPath.SetTo(ttfontsPath.Path()); 350 veraFontPath.Append("NotoSansMono-Regular.ttf"); 351 _AddDefaultMapping("Noto Sans Mono", "Regular", veraFontPath.Path()); 352 353 return true; 354 } 355 356 return false; 357 } 358 359 360 status_t 361 FontManager::_AddMappedFont(const char* familyName, const char* styleName) 362 { 363 FTRACE(("_AddMappedFont(family = \"%s\", style = \"%s\")\n", 364 familyName, styleName)); 365 366 for (int32 i = 0; i < fMappings.CountItems(); i++) { 367 font_mapping* mapping = fMappings.ItemAt(i); 368 369 if (mapping->family == familyName) { 370 if (styleName != NULL && mapping->style != styleName) 371 continue; 372 373 BEntry entry(&mapping->ref); 374 if (entry.InitCheck() != B_OK) 375 continue; 376 377 // find parent directory 378 379 node_ref nodeRef; 380 nodeRef.device = mapping->ref.device; 381 nodeRef.node = mapping->ref.directory; 382 font_directory* directory = _FindDirectory(nodeRef); 383 if (directory == NULL) { 384 // unknown directory, maybe this is a user font - try 385 // to create the missing directory 386 BPath path(&entry); 387 if (path.GetParent(&path) != B_OK 388 || _CreateDirectories(path.Path()) != B_OK 389 || (directory = _FindDirectory(nodeRef)) == NULL) 390 continue; 391 } 392 393 return _AddFont(*directory, entry); 394 } 395 } 396 397 return B_ENTRY_NOT_FOUND; 398 } 399 400 401 /*! \brief Removes the style from the font directory. 402 403 It doesn't necessary delete the font style, if it's still 404 in use, though. 405 */ 406 void 407 FontManager::_RemoveStyle(font_directory& directory, FontStyle* style) 408 { 409 FTRACE(("font removed: %s\n", style->Name())); 410 411 directory.styles.RemoveItem(style); 412 directory.revision++; 413 414 fStyleHashTable.Remove(FontKey(style->Family()->ID(), style->ID())); 415 416 style->ReleaseReference(); 417 } 418 419 420 void 421 FontManager::_RemoveStyle(dev_t device, uint64 directoryNode, uint64 node) 422 { 423 // remove font style from directory 424 node_ref nodeRef; 425 nodeRef.device = device; 426 nodeRef.node = directoryNode; 427 428 font_directory* directory = _FindDirectory(nodeRef); 429 if (directory != NULL) { 430 // find style in directory and remove it 431 nodeRef.node = node; 432 FontStyle* style = directory->FindStyle(nodeRef); 433 if (style != NULL) 434 _RemoveStyle(*directory, style); 435 } 436 } 437 438 439 FontStyle* 440 FontManager::_GetDefaultStyle(const char *familyName, const char *styleName, 441 const char *fallbackFamily, const char *fallbackStyle, 442 uint16 fallbackFace) 443 { 444 // try to find a matching font 445 446 FontStyle* style = GetStyle(familyName, styleName); 447 if (style == NULL) { 448 style = GetStyle(fallbackFamily, fallbackStyle); 449 if (style == NULL) { 450 style = FindStyleMatchingFace(fallbackFace); 451 if (style == NULL && FamilyAt(0) != NULL) 452 style = FamilyAt(0)->StyleAt(0); 453 } 454 } 455 456 return style; 457 } 458 459 460 /*! \brief Sets the fonts that will be used when you create an empty 461 ServerFont without specifying a style, as well as the default 462 Desktop fonts if there are no settings available. 463 */ 464 status_t 465 FontManager::_SetDefaultFonts() 466 { 467 FontStyle* style = NULL; 468 469 // plain font 470 style = _GetDefaultStyle(DEFAULT_PLAIN_FONT_FAMILY, DEFAULT_PLAIN_FONT_STYLE, 471 FALLBACK_PLAIN_FONT_FAMILY, FALLBACK_PLAIN_FONT_STYLE, B_REGULAR_FACE); 472 if (style == NULL) 473 return B_ERROR; 474 475 fDefaultPlainFont.SetTo(new (std::nothrow) ServerFont(*style, DEFAULT_FONT_SIZE)); 476 if (!fDefaultPlainFont.IsSet()) 477 return B_NO_MEMORY; 478 479 // bold font 480 style = _GetDefaultStyle(DEFAULT_BOLD_FONT_FAMILY, DEFAULT_BOLD_FONT_STYLE, 481 FALLBACK_BOLD_FONT_FAMILY, FALLBACK_BOLD_FONT_STYLE, B_BOLD_FACE); 482 483 fDefaultBoldFont.SetTo(new (std::nothrow) ServerFont(*style, DEFAULT_FONT_SIZE)); 484 if (!fDefaultBoldFont.IsSet()) 485 return B_NO_MEMORY; 486 487 // fixed font 488 style = _GetDefaultStyle(DEFAULT_FIXED_FONT_FAMILY, DEFAULT_FIXED_FONT_STYLE, 489 FALLBACK_FIXED_FONT_FAMILY, DEFAULT_FIXED_FONT_STYLE, B_REGULAR_FACE); 490 491 fDefaultFixedFont.SetTo(new (std::nothrow) ServerFont(*style, DEFAULT_FONT_SIZE)); 492 if (!fDefaultFixedFont.IsSet()) 493 return B_NO_MEMORY; 494 495 fDefaultFixedFont->SetSpacing(B_FIXED_SPACING); 496 497 return B_OK; 498 } 499 500 501 void 502 FontManager::_PrecacheFontFile(const ServerFont* font) 503 { 504 if (font == NULL) 505 return; 506 507 size_t bufferSize = 32768; 508 uint8* buffer = new (std::nothrow) uint8[bufferSize]; 509 if (buffer == NULL) { 510 // We don't care. Pre-caching doesn't make sense anyways when there 511 // is not enough RAM... 512 return; 513 } 514 515 BFile file(font->Path(), B_READ_ONLY); 516 if (file.InitCheck() != B_OK) { 517 delete[] buffer; 518 return; 519 } 520 521 while (true) { 522 // We just want the file in the kernel file cache... 523 ssize_t read = file.Read(buffer, bufferSize); 524 if (read < (ssize_t)bufferSize) 525 break; 526 } 527 528 delete[] buffer; 529 } 530 531 532 void 533 FontManager::_AddSystemPaths() 534 { 535 BPath path; 536 if (find_directory(B_SYSTEM_FONTS_DIRECTORY, &path, true) == B_OK) 537 _AddPath(path.Path()); 538 539 // We don't scan these in test mode to help shave off some startup time 540 #if !TEST_MODE 541 if (find_directory(B_SYSTEM_NONPACKAGED_FONTS_DIRECTORY, &path, true) == B_OK) 542 _AddPath(path.Path()); 543 #endif 544 } 545 546 547 void 548 FontManager::_ScanFontsIfNecessary() 549 { 550 if (!fScanned) 551 _ScanFonts(); 552 } 553 554 555 //! Scans all currently known font directories 556 void 557 FontManager::_ScanFonts() 558 { 559 if (fScanned) 560 return; 561 562 for (int32 i = fDirectories.CountItems(); i-- > 0;) { 563 font_directory* directory = fDirectories.ItemAt(i); 564 565 if (directory->AlreadyScanned()) 566 continue; 567 568 _ScanFontDirectory(*directory); 569 } 570 571 fScanned = true; 572 } 573 574 575 /*! \brief Adds the FontFamily/FontStyle that is represented by this path. 576 */ 577 status_t 578 FontManager::_AddFont(font_directory& directory, BEntry& entry) 579 { 580 node_ref nodeRef; 581 status_t status = entry.GetNodeRef(&nodeRef); 582 if (status < B_OK) 583 return status; 584 585 BPath path; 586 status = entry.GetPath(&path); 587 if (status < B_OK) 588 return status; 589 590 FT_Face face; 591 FT_Error error = FT_New_Face(gFreeTypeLibrary, path.Path(), 0, &face); 592 if (error != 0) 593 return B_ERROR; 594 595 FontFamily *family = _FindFamily(face->family_name); 596 if (family != NULL && family->HasStyle(face->style_name)) { 597 // prevent adding the same style twice 598 // (this indicates a problem with the installed fonts maybe?) 599 FT_Done_Face(face); 600 return B_OK; 601 } 602 603 if (family == NULL) { 604 family = new (std::nothrow) FontFamily(face->family_name, fNextID++); 605 if (family == NULL 606 || !fFamilies.BinaryInsert(family, compare_font_families)) { 607 delete family; 608 FT_Done_Face(face); 609 return B_NO_MEMORY; 610 } 611 } 612 613 FTRACE(("\tadd style: %s, %s\n", face->family_name, face->style_name)); 614 615 // the FontStyle takes over ownership of the FT_Face object 616 FontStyle *style = new (std::nothrow) FontStyle(nodeRef, path.Path(), face); 617 if (style == NULL || !family->AddStyle(style)) { 618 delete style; 619 delete family; 620 return B_NO_MEMORY; 621 } 622 623 directory.styles.AddItem(style); 624 fStyleHashTable.Put(FontKey(style->Family()->ID(), style->ID()), style); 625 626 if (directory.AlreadyScanned()) 627 directory.revision++; 628 629 return B_OK; 630 } 631 632 633 FontManager::font_directory* 634 FontManager::_FindDirectory(node_ref& nodeRef) 635 { 636 for (int32 i = fDirectories.CountItems(); i-- > 0;) { 637 font_directory* directory = fDirectories.ItemAt(i); 638 639 if (directory->directory == nodeRef) 640 return directory; 641 } 642 643 return NULL; 644 } 645 646 647 void 648 FontManager::_RemoveDirectory(font_directory* directory) 649 { 650 FTRACE(("FontManager: Remove directory (%" B_PRIdINO ")!\n", 651 directory->directory.node)); 652 653 fDirectories.RemoveItem(directory, false); 654 655 // TODO: remove styles from this directory! 656 657 watch_node(&directory->directory, B_STOP_WATCHING, this); 658 delete directory; 659 } 660 661 662 status_t 663 FontManager::_AddPath(const char* path) 664 { 665 BEntry entry; 666 status_t status = entry.SetTo(path); 667 if (status != B_OK) 668 return status; 669 670 return _AddPath(entry); 671 } 672 673 674 status_t 675 FontManager::_AddPath(BEntry& entry, font_directory** _newDirectory) 676 { 677 node_ref nodeRef; 678 status_t status = entry.GetNodeRef(&nodeRef); 679 if (status != B_OK) 680 return status; 681 682 // check if we are already know this directory 683 684 font_directory* directory = _FindDirectory(nodeRef); 685 if (directory != NULL) { 686 if (_newDirectory) 687 *_newDirectory = directory; 688 return B_OK; 689 } 690 691 // it's a new one, so let's add it 692 693 directory = new (std::nothrow) font_directory; 694 if (directory == NULL) 695 return B_NO_MEMORY; 696 697 struct stat stat; 698 status = entry.GetStat(&stat); 699 if (status != B_OK) { 700 delete directory; 701 return status; 702 } 703 704 directory->directory = nodeRef; 705 directory->user = stat.st_uid; 706 directory->group = stat.st_gid; 707 directory->revision = 0; 708 709 status = watch_node(&nodeRef, B_WATCH_DIRECTORY, this); 710 if (status != B_OK) { 711 // we cannot watch this directory - while this is unfortunate, 712 // it's not a critical error 713 printf("could not watch directory %" B_PRIdDEV ":%" B_PRIdINO "\n", 714 nodeRef.device, nodeRef.node); 715 // TODO: should go into syslog() 716 } else { 717 BPath path(&entry); 718 FTRACE(("FontManager: now watching: %s\n", path.Path())); 719 } 720 721 fDirectories.AddItem(directory); 722 723 if (_newDirectory) 724 *_newDirectory = directory; 725 726 fScanned = false; 727 return B_OK; 728 } 729 730 731 /*! \brief Creates all unknown font_directories of the specified path - but 732 only if one of its parent directories is already known. 733 734 This method is used to create the font_directories for font_mappings. 735 It recursively walks upwards in the directory hierarchy until it finds 736 a known font_directory (or hits the root directory, in which case it 737 bails out). 738 */ 739 status_t 740 FontManager::_CreateDirectories(const char* path) 741 { 742 FTRACE(("_CreateDirectories(path = %s)\n", path)); 743 744 if (!strcmp(path, "/")) { 745 // we walked our way up to the root 746 return B_ENTRY_NOT_FOUND; 747 } 748 749 BEntry entry; 750 status_t status = entry.SetTo(path); 751 if (status != B_OK) 752 return status; 753 754 node_ref nodeRef; 755 status = entry.GetNodeRef(&nodeRef); 756 if (status != B_OK) 757 return status; 758 759 // check if we are already know this directory 760 761 font_directory* directory = _FindDirectory(nodeRef); 762 if (directory != NULL) 763 return B_OK; 764 765 // We don't know this one yet - keep walking the path upwards 766 // and try to find a match. 767 768 BPath parent(path); 769 status = parent.GetParent(&parent); 770 if (status != B_OK) 771 return status; 772 773 status = _CreateDirectories(parent.Path()); 774 if (status != B_OK) 775 return status; 776 777 // We have our match, create sub directory 778 779 return _AddPath(path); 780 } 781 782 783 /*! \brief Scan a folder for all valid fonts 784 \param directoryPath Path of the folder to scan. 785 */ 786 status_t 787 FontManager::_ScanFontDirectory(font_directory& fontDirectory) 788 { 789 // This bad boy does all the real work. It loads each entry in the 790 // directory. If a valid font file, it adds both the family and the style. 791 792 BDirectory directory; 793 status_t status = directory.SetTo(&fontDirectory.directory); 794 if (status != B_OK) 795 return status; 796 797 BEntry entry; 798 while (directory.GetNextEntry(&entry) == B_OK) { 799 if (entry.IsDirectory()) { 800 // scan this directory recursively 801 font_directory* newDirectory; 802 if (_AddPath(entry, &newDirectory) == B_OK && newDirectory != NULL) 803 _ScanFontDirectory(*newDirectory); 804 805 continue; 806 } 807 808 // TODO: Commenting this out makes my "Unicode glyph lookup" 809 // work with our default fonts. The real fix is to select the 810 // Unicode char map (if supported), and/or adjust the 811 // utf8 -> glyph-index mapping everywhere to handle other 812 // char maps. We could also ignore fonts that don't support 813 // the Unicode lookup as a temporary "solution". 814 #if 0 815 FT_CharMap charmap = _GetSupportedCharmap(face); 816 if (!charmap) { 817 FT_Done_Face(face); 818 continue; 819 } 820 821 face->charmap = charmap; 822 #endif 823 824 _AddFont(fontDirectory, entry); 825 // takes over ownership of the FT_Face object 826 } 827 828 fontDirectory.revision = 1; 829 return B_OK; 830 } 831 832 833 /*! \brief Finds and returns the first valid charmap in a font 834 835 \param face Font handle obtained from FT_Load_Face() 836 \return An FT_CharMap or NULL if unsuccessful 837 */ 838 FT_CharMap 839 FontManager::_GetSupportedCharmap(const FT_Face& face) 840 { 841 for (int32 i = 0; i < face->num_charmaps; i++) { 842 FT_CharMap charmap = face->charmaps[i]; 843 844 switch (charmap->platform_id) { 845 case 3: 846 // if Windows Symbol or Windows Unicode 847 if (charmap->encoding_id == 0 || charmap->encoding_id == 1) 848 return charmap; 849 break; 850 851 case 1: 852 // if Apple Unicode 853 if (charmap->encoding_id == 0) 854 return charmap; 855 break; 856 857 case 0: 858 // if Apple Roman 859 if (charmap->encoding_id == 0) 860 return charmap; 861 break; 862 863 default: 864 break; 865 } 866 } 867 868 return NULL; 869 } 870 871 872 int32 873 FontManager::CheckRevision(uid_t user) 874 { 875 BAutolock locker(this); 876 int32 revision = 0; 877 878 _ScanFontsIfNecessary(); 879 880 for (int32 i = 0; i < fDirectories.CountItems(); i++) { 881 font_directory* directory = fDirectories.ItemAt(i); 882 883 // TODO: for now, add all directories 884 revision += directory->revision; 885 } 886 887 return revision; 888 } 889 890 891 /*! \brief Counts the number of font families available 892 \return The number of unique font families currently available 893 */ 894 int32 895 FontManager::CountFamilies() 896 { 897 _ScanFontsIfNecessary(); 898 899 return fFamilies.CountItems(); 900 } 901 902 903 /*! \brief Counts the number of styles available in a font family 904 \param family Name of the font family to scan 905 \return The number of font styles currently available for the font family 906 */ 907 int32 908 FontManager::CountStyles(const char *familyName) 909 { 910 _ScanFontsIfNecessary(); 911 912 FontFamily *family = GetFamily(familyName); 913 if (family) 914 return family->CountStyles(); 915 916 return 0; 917 } 918 919 920 /*! \brief Counts the number of styles available in a font family 921 \param family Name of the font family to scan 922 \return The number of font styles currently available for the font family 923 */ 924 int32 925 FontManager::CountStyles(uint16 familyID) 926 { 927 _ScanFontsIfNecessary(); 928 929 FontFamily *family = GetFamily(familyID); 930 if (family) 931 return family->CountStyles(); 932 933 return 0; 934 } 935 936 937 FontFamily* 938 FontManager::FamilyAt(int32 index) const 939 { 940 return fFamilies.ItemAt(index); 941 } 942 943 944 FontFamily* 945 FontManager::_FindFamily(const char* name) const 946 { 947 if (name == NULL) 948 return NULL; 949 950 FontFamily family(name, 0); 951 return const_cast<FontFamily*>(fFamilies.BinarySearch(family, 952 compare_font_families)); 953 } 954 955 956 /*! \brief Locates a FontFamily object by name 957 \param name The family to find 958 \return Pointer to the specified family or NULL if not found. 959 */ 960 FontFamily* 961 FontManager::GetFamily(const char* name) 962 { 963 if (name == NULL) 964 return NULL; 965 966 FontFamily* family = _FindFamily(name); 967 if (family != NULL) 968 return family; 969 970 if (fScanned) 971 return NULL; 972 973 // try font mappings before failing 974 if (_AddMappedFont(name) == B_OK) 975 return _FindFamily(name); 976 977 _ScanFonts(); 978 return _FindFamily(name); 979 } 980 981 982 FontFamily* 983 FontManager::GetFamily(uint16 familyID) const 984 { 985 FontKey key(familyID, 0); 986 FontStyle* style = fStyleHashTable.Get(key); 987 if (style != NULL) 988 return style->Family(); 989 990 return NULL; 991 } 992 993 994 FontStyle* 995 FontManager::GetStyleByIndex(const char* familyName, int32 index) 996 { 997 FontFamily* family = GetFamily(familyName); 998 if (family != NULL) 999 return family->StyleAt(index); 1000 1001 return NULL; 1002 } 1003 1004 1005 FontStyle* 1006 FontManager::GetStyleByIndex(uint16 familyID, int32 index) 1007 { 1008 FontFamily* family = GetFamily(familyID); 1009 if (family != NULL) 1010 return family->StyleAt(index); 1011 1012 return NULL; 1013 } 1014 1015 1016 /*! \brief Retrieves the FontStyle object that comes closest to the one 1017 specified. 1018 1019 \param family The font's family or NULL in which case \a familyID is used 1020 \param style The font's style or NULL in which case \a styleID is used 1021 \param familyID will only be used if \a family is NULL (or empty) 1022 \param styleID will only be used if \a style is NULL (or empty) 1023 \param face is used to specify the style if both \a style is NULL or empty 1024 and styleID is 0xffff. 1025 1026 \return The FontStyle having those attributes or NULL if not available 1027 */ 1028 FontStyle* 1029 FontManager::GetStyle(const char* familyName, const char* styleName, 1030 uint16 familyID, uint16 styleID, uint16 face) 1031 { 1032 FontFamily* family; 1033 1034 // find family 1035 1036 if (familyName != NULL && familyName[0]) 1037 family = GetFamily(familyName); 1038 else 1039 family = GetFamily(familyID); 1040 1041 if (family == NULL) 1042 return NULL; 1043 1044 // find style 1045 1046 if (styleName != NULL && styleName[0]) { 1047 FontStyle* fontStyle = family->GetStyle(styleName); 1048 if (fontStyle != NULL) 1049 return fontStyle; 1050 1051 // before we fail, we try the mappings for a match 1052 if (_AddMappedFont(family->Name(), styleName) == B_OK) { 1053 fontStyle = family->GetStyle(styleName); 1054 if (fontStyle != NULL) 1055 return fontStyle; 1056 } 1057 1058 _ScanFonts(); 1059 return family->GetStyle(styleName); 1060 } 1061 1062 if (styleID != 0xffff) 1063 return family->GetStyleByID(styleID); 1064 1065 // try to get from face 1066 return family->GetStyleMatchingFace(face); 1067 } 1068 1069 1070 /*! \brief Retrieves the FontStyle object 1071 \param family ID for the font's family 1072 \param style ID of the font's style 1073 \return The FontStyle having those attributes or NULL if not available 1074 */ 1075 FontStyle* 1076 FontManager::GetStyle(uint16 familyID, uint16 styleID) const 1077 { 1078 FontKey key(familyID, styleID); 1079 return fStyleHashTable.Get(key); 1080 } 1081 1082 1083 /*! \brief If you don't find your preferred font style, but are anxious 1084 to have one fitting your needs, you may want to use this method. 1085 */ 1086 FontStyle* 1087 FontManager::FindStyleMatchingFace(uint16 face) const 1088 { 1089 int32 count = fFamilies.CountItems(); 1090 1091 for (int32 i = 0; i < count; i++) { 1092 FontFamily* family = fFamilies.ItemAt(i); 1093 FontStyle* style = family->GetStyleMatchingFace(face); 1094 if (style != NULL) 1095 return style; 1096 } 1097 1098 return NULL; 1099 } 1100 1101 1102 /*! \brief This call is used by the FontStyle class - and the FontStyle class 1103 only - to remove itself from the font manager. 1104 At this point, the style is already no longer available to the user. 1105 */ 1106 void 1107 FontManager::RemoveStyle(FontStyle* style) 1108 { 1109 FontFamily* family = style->Family(); 1110 if (family == NULL) 1111 debugger("family is NULL!"); 1112 1113 FontStyle* check = GetStyle(family->ID(), style->ID()); 1114 if (check != NULL) 1115 debugger("style removed but still available!"); 1116 1117 if (family->RemoveStyle(style) 1118 && family->CountStyles() == 0) 1119 fFamilies.RemoveItem(family); 1120 } 1121 1122 1123 const ServerFont* 1124 FontManager::DefaultPlainFont() const 1125 { 1126 return fDefaultPlainFont.Get(); 1127 } 1128 1129 1130 const ServerFont* 1131 FontManager::DefaultBoldFont() const 1132 { 1133 return fDefaultBoldFont.Get(); 1134 } 1135 1136 1137 const ServerFont* 1138 FontManager::DefaultFixedFont() const 1139 { 1140 return fDefaultFixedFont.Get(); 1141 } 1142 1143 1144 void 1145 FontManager::AttachUser(uid_t userID) 1146 { 1147 BAutolock locker(this); 1148 1149 #if !TEST_MODE 1150 // TODO: actually, find_directory() cannot know which user ID we want here 1151 // TODO: avoids user fonts in safe mode 1152 BPath path; 1153 if (find_directory(B_USER_FONTS_DIRECTORY, &path, true) == B_OK) 1154 _AddPath(path.Path()); 1155 if (find_directory(B_USER_NONPACKAGED_FONTS_DIRECTORY, &path, true) 1156 == B_OK) { 1157 _AddPath(path.Path()); 1158 } 1159 #endif 1160 } 1161 1162 1163 void 1164 FontManager::DetachUser(uid_t userID) 1165 { 1166 BAutolock locker(this); 1167 1168 // TODO! 1169 } 1170 1171