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