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 bool scanned;
56 BObjectList<FontStyle> styles;
57
58 FontStyle* FindStyle(const node_ref& nodeRef) const;
59 };
60
61
62 struct GlobalFontManager::font_mapping {
63 BString family;
64 BString style;
65 entry_ref ref;
66 };
67
68
69 FontStyle*
FindStyle(const node_ref & nodeRef) const70 GlobalFontManager::font_directory::FindStyle(const node_ref& nodeRef) const
71 {
72 for (int32 i = styles.CountItems(); i-- > 0;) {
73 FontStyle* style = styles.ItemAt(i);
74
75 if (nodeRef == style->NodeRef())
76 return style;
77 }
78
79 return NULL;
80 }
81
82
83 static status_t
set_entry(node_ref & nodeRef,const char * name,BEntry & entry)84 set_entry(node_ref& nodeRef, const char* name, BEntry& entry)
85 {
86 entry_ref ref;
87 ref.device = nodeRef.device;
88 ref.directory = nodeRef.node;
89
90 status_t status = ref.set_name(name);
91 if (status != B_OK)
92 return status;
93
94 return entry.SetTo(&ref);
95 }
96
97
98 // #pragma mark -
99
100
101 //! Does basic set up so that directories can be scanned
GlobalFontManager()102 GlobalFontManager::GlobalFontManager()
103 : BLooper("GlobalFontManager"),
104 fDirectories(10, true),
105 fMappings(10, true),
106
107 fDefaultPlainFont(NULL),
108 fDefaultBoldFont(NULL),
109 fDefaultFixedFont(NULL),
110
111 fScanned(false)
112 {
113 fInitStatus = FT_Init_FreeType(&gFreeTypeLibrary) == 0 ? B_OK : B_ERROR;
114 if (fInitStatus == B_OK) {
115 _AddSystemPaths();
116 _AddUserPaths();
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
~GlobalFontManager()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
MessageReceived(BMessage * message)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;
243 while ((style = fromDirectory->FindStyle(nodeRef)) != 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 uint32
Revision()302 GlobalFontManager::Revision()
303 {
304 BAutolock locker(this);
305
306 _ScanFontsIfNecessary();
307
308 return FontManager::Revision();
309 }
310
311
312 void
SaveRecentFontMappings()313 GlobalFontManager::SaveRecentFontMappings()
314 {
315 }
316
317
318 void
_AddDefaultMapping(const char * family,const char * style,const char * path)319 GlobalFontManager::_AddDefaultMapping(const char* family, const char* style,
320 const char* path)
321 {
322 font_mapping* mapping = new (std::nothrow) font_mapping;
323 if (mapping == NULL)
324 return;
325
326 mapping->family = family;
327 mapping->style = style;
328 BEntry entry(path);
329
330 if (entry.GetRef(&mapping->ref) != B_OK
331 || !entry.Exists()
332 || !fMappings.AddItem(mapping))
333 delete mapping;
334 }
335
336
337 bool
_LoadRecentFontMappings()338 GlobalFontManager::_LoadRecentFontMappings()
339 {
340 // default known mappings
341 // TODO: load them for real, and use these as a fallback
342
343 BPath ttfontsPath;
344 if (find_directory(B_BEOS_FONTS_DIRECTORY, &ttfontsPath) == B_OK) {
345 ttfontsPath.Append("ttfonts");
346
347 BPath veraFontPath = ttfontsPath;
348 veraFontPath.Append("NotoSans-Regular.ttf");
349 _AddDefaultMapping("Noto Sans", "Book", veraFontPath.Path());
350
351 veraFontPath.SetTo(ttfontsPath.Path());
352 veraFontPath.Append("NotoSans-Bold.ttf");
353 _AddDefaultMapping("Noto Sans", "Bold", veraFontPath.Path());
354
355 veraFontPath.SetTo(ttfontsPath.Path());
356 veraFontPath.Append("NotoSansMono-Regular.ttf");
357 _AddDefaultMapping("Noto Sans Mono", "Regular", veraFontPath.Path());
358
359 return true;
360 }
361
362 return false;
363 }
364
365
366 status_t
_AddMappedFont(const char * familyName,const char * styleName)367 GlobalFontManager::_AddMappedFont(const char* familyName, const char* styleName)
368 {
369 FTRACE(("_AddMappedFont(family = \"%s\", style = \"%s\")\n",
370 familyName, styleName));
371
372 for (int32 i = 0; i < fMappings.CountItems(); i++) {
373 font_mapping* mapping = fMappings.ItemAt(i);
374
375 if (mapping->family == familyName) {
376 if (styleName != NULL && mapping->style != styleName)
377 continue;
378
379 BEntry entry(&mapping->ref);
380 if (entry.InitCheck() != B_OK)
381 continue;
382
383 // find parent directory
384
385 node_ref nodeRef;
386 nodeRef.device = mapping->ref.device;
387 nodeRef.node = mapping->ref.directory;
388 font_directory* directory = _FindDirectory(nodeRef);
389 if (directory == NULL) {
390 // unknown directory, maybe this is a user font - try
391 // to create the missing directory
392 BPath path(&entry);
393 if (path.GetParent(&path) != B_OK
394 || _CreateDirectories(path.Path()) != B_OK
395 || (directory = _FindDirectory(nodeRef)) == NULL)
396 continue;
397 }
398
399 return _AddFont(*directory, entry);
400 }
401 }
402
403 return B_ENTRY_NOT_FOUND;
404 }
405
406
407 FontStyle*
_GetDefaultStyle(const char * familyName,const char * styleName,const char * fallbackFamily,const char * fallbackStyle,uint16 fallbackFace)408 GlobalFontManager::_GetDefaultStyle(const char* familyName, const char* styleName,
409 const char* fallbackFamily, const char* fallbackStyle,
410 uint16 fallbackFace)
411 {
412 // try to find a matching font
413 FontStyle* style = GetStyle(familyName, styleName);
414 if (style == NULL) {
415 style = GetStyle(fallbackFamily, fallbackStyle);
416 if (style == NULL) {
417 style = FindStyleMatchingFace(fallbackFace);
418 if (style == NULL && FamilyAt(0) != NULL)
419 style = FamilyAt(0)->StyleAt(0);
420 }
421 }
422
423 return style;
424 }
425
426
427 /*! \brief Sets the fonts that will be used when you create an empty
428 ServerFont without specifying a style, as well as the default
429 Desktop fonts if there are no settings available.
430 */
431 status_t
_SetDefaultFonts()432 GlobalFontManager::_SetDefaultFonts()
433 {
434 FontStyle* style = NULL;
435
436 // plain font
437 style = _GetDefaultStyle(DEFAULT_PLAIN_FONT_FAMILY, DEFAULT_PLAIN_FONT_STYLE,
438 FALLBACK_PLAIN_FONT_FAMILY, FALLBACK_PLAIN_FONT_STYLE, B_REGULAR_FACE);
439 if (style == NULL)
440 return B_ERROR;
441
442 fDefaultPlainFont.SetTo(new (std::nothrow) ServerFont(*style,
443 DEFAULT_FONT_SIZE));
444 if (!fDefaultPlainFont.IsSet())
445 return B_NO_MEMORY;
446
447 // bold font
448 style = _GetDefaultStyle(DEFAULT_BOLD_FONT_FAMILY, DEFAULT_BOLD_FONT_STYLE,
449 FALLBACK_BOLD_FONT_FAMILY, FALLBACK_BOLD_FONT_STYLE, B_BOLD_FACE);
450
451 fDefaultBoldFont.SetTo(new (std::nothrow) ServerFont(*style,
452 DEFAULT_FONT_SIZE));
453 if (!fDefaultBoldFont.IsSet())
454 return B_NO_MEMORY;
455
456 // fixed font
457 style = _GetDefaultStyle(DEFAULT_FIXED_FONT_FAMILY, DEFAULT_FIXED_FONT_STYLE,
458 FALLBACK_FIXED_FONT_FAMILY, FALLBACK_FIXED_FONT_STYLE, B_REGULAR_FACE);
459
460 fDefaultFixedFont.SetTo(new (std::nothrow) ServerFont(*style,
461 DEFAULT_FONT_SIZE));
462 if (!fDefaultFixedFont.IsSet())
463 return B_NO_MEMORY;
464
465 fDefaultFixedFont->SetSpacing(B_FIXED_SPACING);
466
467 return B_OK;
468 }
469
470
471 /*! \brief Removes the style from the font directory.
472
473 It doesn't necessary delete the font style, if it's still
474 in use, though.
475 */
476 void
_RemoveStyle(font_directory & directory,FontStyle * style)477 GlobalFontManager::_RemoveStyle(font_directory& directory, FontStyle* style)
478 {
479 FTRACE(("font removed: %s\n", style->Name()));
480
481 directory.styles.RemoveItem(style);
482
483 _RemoveFont(style->Family()->ID(), style->ID());
484 }
485
486
487 void
_RemoveStyle(dev_t device,uint64 directoryNode,uint64 node)488 GlobalFontManager::_RemoveStyle(dev_t device, uint64 directoryNode, uint64 node)
489 {
490 // remove font style from directory
491 node_ref nodeRef;
492 nodeRef.device = device;
493 nodeRef.node = directoryNode;
494
495 font_directory* directory = _FindDirectory(nodeRef);
496 if (directory != NULL) {
497 // find style in directory and remove it
498 nodeRef.node = node;
499 FontStyle* style;
500 while ((style = directory->FindStyle(nodeRef)) != NULL)
501 _RemoveStyle(*directory, style);
502 }
503 }
504
505
506 /*! \brief Counts the number of font families available
507 \return The number of unique font families currently available
508 */
509 int32
CountFamilies()510 GlobalFontManager::CountFamilies()
511 {
512 _ScanFontsIfNecessary();
513
514 return FontManager::CountFamilies();
515 }
516
517
518 /*! \brief Counts the number of styles available in a font family
519 \param family Name of the font family to scan
520 \return The number of font styles currently available for the font family
521 */
522 int32
CountStyles(const char * familyName)523 GlobalFontManager::CountStyles(const char* familyName)
524 {
525 _ScanFontsIfNecessary();
526
527 FontFamily* family = GetFamily(familyName);
528 if (family)
529 return family->CountStyles();
530
531 return 0;
532 }
533
534
535 /*! \brief Counts the number of styles available in a font family
536 \param family Name of the font family to scan
537 \return The number of font styles currently available for the font family
538 */
539 int32
CountStyles(uint16 familyID)540 GlobalFontManager::CountStyles(uint16 familyID)
541 {
542 _ScanFontsIfNecessary();
543
544 FontFamily* family = GetFamily(familyID);
545 if (family)
546 return family->CountStyles();
547
548 return 0;
549 }
550
551
552 FontStyle*
GetStyle(uint16 familyID,uint16 styleID) const553 GlobalFontManager::GetStyle(uint16 familyID, uint16 styleID) const
554 {
555 return FontManager::GetStyle(familyID, styleID);
556 }
557
558
559 /*! \brief Retrieves the FontStyle object that comes closest to the one
560 specified.
561
562 \param family The font's family or NULL in which case \a familyID is used
563 \param style The font's style or NULL in which case \a styleID is used
564 \param familyID will only be used if \a family is NULL (or empty)
565 \param styleID will only be used if \a family and \a style are NULL (or empty)
566 \param face is used to specify the style if both \a style is NULL or empty
567 and styleID is 0xffff.
568
569 \return The FontStyle having those attributes or NULL if not available
570 */
571 FontStyle*
GetStyle(const char * familyName,const char * styleName,uint16 familyID,uint16 styleID,uint16 face)572 GlobalFontManager::GetStyle(const char* familyName, const char* styleName,
573 uint16 familyID, uint16 styleID, uint16 face)
574 {
575 ASSERT(IsLocked());
576
577 if (styleID != 0xffff && (familyName == NULL || !familyName[0])
578 && (styleName == NULL || !styleName[0])) {
579 return GetStyle(familyID, styleID);
580 }
581
582 // find family
583
584 FontFamily* family;
585 if (familyName != NULL && familyName[0])
586 family = GetFamily(familyName);
587 else
588 family = GetFamily(familyID);
589
590 if (family == NULL)
591 return NULL;
592
593 // find style
594
595 if (styleName != NULL && styleName[0]) {
596 FontStyle* fontStyle = family->GetStyle(styleName);
597 if (fontStyle != NULL)
598 return fontStyle;
599
600 // before we fail, we try the mappings for a match
601 if (_AddMappedFont(family->Name(), styleName) == B_OK) {
602 fontStyle = family->GetStyle(styleName);
603 if (fontStyle != NULL)
604 return fontStyle;
605 }
606
607 _ScanFonts();
608 return family->GetStyle(styleName);
609 }
610
611 // try to get from face
612 return family->GetStyleMatchingFace(face);
613 }
614
615
616 void
_PrecacheFontFile(const ServerFont * font)617 GlobalFontManager::_PrecacheFontFile(const ServerFont* font)
618 {
619 if (font == NULL)
620 return;
621
622 size_t bufferSize = 32768;
623 uint8* buffer = new (std::nothrow) uint8[bufferSize];
624 if (buffer == NULL) {
625 // We don't care. Pre-caching doesn't make sense anyways when there
626 // is not enough RAM...
627 return;
628 }
629
630 BFile file(font->Path(), B_READ_ONLY);
631 if (file.InitCheck() != B_OK) {
632 delete[] buffer;
633 return;
634 }
635
636 while (true) {
637 // We just want the file in the kernel file cache...
638 ssize_t read = file.Read(buffer, bufferSize);
639 if (read < (ssize_t)bufferSize)
640 break;
641 }
642
643 delete[] buffer;
644 }
645
646
647 void
_AddSystemPaths()648 GlobalFontManager::_AddSystemPaths()
649 {
650 BPath path;
651 if (find_directory(B_SYSTEM_FONTS_DIRECTORY, &path, true) == B_OK)
652 _AddPath(path.Path());
653
654 // We don't scan these in test mode to help shave off some startup time
655 #if !TEST_MODE
656 if (find_directory(B_SYSTEM_NONPACKAGED_FONTS_DIRECTORY, &path, true) == B_OK)
657 _AddPath(path.Path());
658 #endif
659 }
660
661
662 void
_AddUserPaths()663 GlobalFontManager::_AddUserPaths()
664 {
665 #if !TEST_MODE
666 // TODO: avoids user fonts in safe mode
667 BPath path;
668 if (find_directory(B_USER_FONTS_DIRECTORY, &path, true) == B_OK)
669 _AddPath(path.Path());
670 if (find_directory(B_USER_NONPACKAGED_FONTS_DIRECTORY, &path, true) == B_OK)
671 _AddPath(path.Path());
672 #endif
673 }
674
675
676 void
_ScanFontsIfNecessary()677 GlobalFontManager::_ScanFontsIfNecessary()
678 {
679 if (!fScanned)
680 _ScanFonts();
681 }
682
683
684 //! Scans all currently known font directories
685 void
_ScanFonts()686 GlobalFontManager::_ScanFonts()
687 {
688 if (fScanned)
689 return;
690
691 for (int32 i = fDirectories.CountItems(); i-- > 0;) {
692 font_directory* directory = fDirectories.ItemAt(i);
693
694 if (directory->scanned)
695 continue;
696
697 _ScanFontDirectory(*directory);
698 }
699
700 fScanned = true;
701 }
702
703
704 /*! \brief Adds the FontFamily/FontStyle that is represented by this path.
705 */
706 status_t
_AddFont(font_directory & directory,BEntry & entry)707 GlobalFontManager::_AddFont(font_directory& directory, BEntry& entry)
708 {
709 node_ref nodeRef;
710 status_t status = entry.GetNodeRef(&nodeRef);
711 if (status < B_OK)
712 return status;
713
714 BPath path;
715 status = entry.GetPath(&path);
716 if (status < B_OK)
717 return status;
718
719 FT_Face face;
720 FT_Error error = FT_New_Face(gFreeTypeLibrary, path.Path(), -1, &face);
721 if (error != 0)
722 return B_ERROR;
723 FT_Long count = face->num_faces;
724 FT_Done_Face(face);
725
726 for (FT_Long i = 0; i < count; i++) {
727 FT_Error error = FT_New_Face(gFreeTypeLibrary, path.Path(), -(i + 1), &face);
728 if (error != 0)
729 return B_ERROR;
730 uint32 variableCount = (face->style_flags & 0x7fff0000) >> 16;
731 FT_Done_Face(face);
732
733 uint32 j = variableCount == 0 ? 0 : 1;
734 do {
735 FT_Long faceIndex = i | (j << 16);
736 error = FT_New_Face(gFreeTypeLibrary, path.Path(), faceIndex, &face);
737 if (error != 0)
738 return B_ERROR;
739
740 uint16 familyID, styleID;
741 status = FontManager::_AddFont(face, nodeRef, path.Path(), familyID, styleID);
742 if (status == B_NAME_IN_USE) {
743 status = B_OK;
744 j++;
745 continue;
746 }
747 if (status < B_OK)
748 return status;
749 directory.styles.AddItem(GetStyle(familyID, styleID));
750 j++;
751 } while (j <= variableCount);
752 }
753
754 return B_OK;
755 }
756
757
758 GlobalFontManager::font_directory*
_FindDirectory(node_ref & nodeRef)759 GlobalFontManager::_FindDirectory(node_ref& nodeRef)
760 {
761 for (int32 i = fDirectories.CountItems(); i-- > 0;) {
762 font_directory* directory = fDirectories.ItemAt(i);
763
764 if (directory->directory == nodeRef)
765 return directory;
766 }
767
768 return NULL;
769 }
770
771
772 void
_RemoveDirectory(font_directory * directory)773 GlobalFontManager::_RemoveDirectory(font_directory* directory)
774 {
775 FTRACE(("FontManager: Remove directory (%" B_PRIdINO ")!\n",
776 directory->directory.node));
777
778 fDirectories.RemoveItem(directory, false);
779
780 // TODO: remove styles from this directory!
781
782 watch_node(&directory->directory, B_STOP_WATCHING, this);
783 delete directory;
784 }
785
786
787 status_t
_AddPath(const char * path)788 GlobalFontManager::_AddPath(const char* path)
789 {
790 BEntry entry;
791 status_t status = entry.SetTo(path);
792 if (status != B_OK)
793 return status;
794
795 return _AddPath(entry);
796 }
797
798
799 status_t
_AddPath(BEntry & entry,font_directory ** _newDirectory)800 GlobalFontManager::_AddPath(BEntry& entry, font_directory** _newDirectory)
801 {
802 node_ref nodeRef;
803 status_t status = entry.GetNodeRef(&nodeRef);
804 if (status != B_OK)
805 return status;
806
807 // check if we are already know this directory
808
809 font_directory* directory = _FindDirectory(nodeRef);
810 if (directory != NULL) {
811 if (_newDirectory)
812 *_newDirectory = directory;
813 return B_OK;
814 }
815
816 // it's a new one, so let's add it
817
818 directory = new (std::nothrow) font_directory;
819 if (directory == NULL)
820 return B_NO_MEMORY;
821
822 struct stat stat;
823 status = entry.GetStat(&stat);
824 if (status != B_OK) {
825 delete directory;
826 return status;
827 }
828
829 directory->directory = nodeRef;
830 directory->user = stat.st_uid;
831 directory->group = stat.st_gid;
832 directory->scanned = false;
833
834 status = watch_node(&nodeRef, B_WATCH_DIRECTORY, this);
835 if (status != B_OK) {
836 // we cannot watch this directory - while this is unfortunate,
837 // it's not a critical error
838 printf("could not watch directory %" B_PRIdDEV ":%" B_PRIdINO "\n",
839 nodeRef.device, nodeRef.node);
840 // TODO: should go into syslog()
841 } else {
842 BPath path(&entry);
843 FTRACE(("FontManager: now watching: %s\n", path.Path()));
844 }
845
846 fDirectories.AddItem(directory);
847
848 if (_newDirectory)
849 *_newDirectory = directory;
850
851 fScanned = false;
852 return B_OK;
853 }
854
855
856 /*! \brief Creates all unknown font_directories of the specified path - but
857 only if one of its parent directories is already known.
858
859 This method is used to create the font_directories for font_mappings.
860 It recursively walks upwards in the directory hierarchy until it finds
861 a known font_directory (or hits the root directory, in which case it
862 bails out).
863 */
864 status_t
_CreateDirectories(const char * path)865 GlobalFontManager::_CreateDirectories(const char* path)
866 {
867 FTRACE(("_CreateDirectories(path = %s)\n", path));
868
869 if (!strcmp(path, "/")) {
870 // we walked our way up to the root
871 return B_ENTRY_NOT_FOUND;
872 }
873
874 BEntry entry;
875 status_t status = entry.SetTo(path);
876 if (status != B_OK)
877 return status;
878
879 node_ref nodeRef;
880 status = entry.GetNodeRef(&nodeRef);
881 if (status != B_OK)
882 return status;
883
884 // check if we are already know this directory
885
886 font_directory* directory = _FindDirectory(nodeRef);
887 if (directory != NULL)
888 return B_OK;
889
890 // We don't know this one yet - keep walking the path upwards
891 // and try to find a match.
892
893 BPath parent(path);
894 status = parent.GetParent(&parent);
895 if (status != B_OK)
896 return status;
897
898 status = _CreateDirectories(parent.Path());
899 if (status != B_OK)
900 return status;
901
902 // We have our match, create sub directory
903
904 return _AddPath(path);
905 }
906
907
908 /*! \brief Scan a folder for all valid fonts
909 \param directoryPath Path of the folder to scan.
910 */
911 status_t
_ScanFontDirectory(font_directory & fontDirectory)912 GlobalFontManager::_ScanFontDirectory(font_directory& fontDirectory)
913 {
914 // This bad boy does all the real work. It loads each entry in the
915 // directory. If a valid font file, it adds both the family and the style.
916
917 if (fontDirectory.scanned)
918 return B_OK;
919
920 BDirectory directory;
921 status_t status = directory.SetTo(&fontDirectory.directory);
922 if (status != B_OK)
923 return status;
924
925 BEntry entry;
926 while (directory.GetNextEntry(&entry) == B_OK) {
927 if (entry.IsDirectory()) {
928 // scan this directory recursively
929 font_directory* newDirectory;
930 if (_AddPath(entry, &newDirectory) == B_OK && newDirectory != NULL
931 && !newDirectory->scanned) {
932 _ScanFontDirectory(*newDirectory);
933 }
934
935 continue;
936 }
937
938 // TODO: Commenting this out makes my "Unicode glyph lookup"
939 // work with our default fonts. The real fix is to select the
940 // Unicode char map (if supported), and/or adjust the
941 // utf8 -> glyph-index mapping everywhere to handle other
942 // char maps. We could also ignore fonts that don't support
943 // the Unicode lookup as a temporary "solution".
944 #if 0
945 FT_CharMap charmap = _GetSupportedCharmap(face);
946 if (!charmap) {
947 FT_Done_Face(face);
948 continue;
949 }
950
951 face->charmap = charmap;
952 #endif
953
954 _AddFont(fontDirectory, entry);
955 // takes over ownership of the FT_Face object
956 }
957
958 fontDirectory.scanned = true;
959 return B_OK;
960 }
961
962
963 /*! \brief Locates a FontFamily object by name
964 \param name The family to find
965 \return Pointer to the specified family or NULL if not found.
966 */
967 FontFamily*
GetFamily(const char * name)968 GlobalFontManager::GetFamily(const char* name)
969 {
970 if (name == NULL)
971 return NULL;
972
973 FontFamily* family = _FindFamily(name);
974 if (family != NULL)
975 return family;
976
977 if (fScanned)
978 return NULL;
979
980 // try font mappings before failing
981 if (_AddMappedFont(name) == B_OK)
982 return _FindFamily(name);
983
984 _ScanFonts();
985 return _FindFamily(name);
986 }
987
988
989 FontFamily*
GetFamily(uint16 familyID) const990 GlobalFontManager::GetFamily(uint16 familyID) const
991 {
992 return FontManager::GetFamily(familyID);
993 }
994
995
996 const ServerFont*
DefaultPlainFont() const997 GlobalFontManager::DefaultPlainFont() const
998 {
999 return fDefaultPlainFont.Get();
1000 }
1001
1002
1003 const ServerFont*
DefaultBoldFont() const1004 GlobalFontManager::DefaultBoldFont() const
1005 {
1006 return fDefaultBoldFont.Get();
1007 }
1008
1009
1010 const ServerFont*
DefaultFixedFont() const1011 GlobalFontManager::DefaultFixedFont() const
1012 {
1013 return fDefaultFixedFont.Get();
1014 }
1015