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