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