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