xref: /haiku/src/servers/app/font/FontManager.cpp (revision 5e7964b0a929555415798dea3373db9ac4611caa)
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("NotoSans-Regular.ttf");
338 		_AddDefaultMapping("Noto Sans", "Book", veraFontPath.Path());
339 
340 		veraFontPath.SetTo(ttfontsPath.Path());
341 		veraFontPath.Append("NotoSans-Bold.ttf");
342 		_AddDefaultMapping("Noto Sans", "Bold", veraFontPath.Path());
343 
344 		veraFontPath.SetTo(ttfontsPath.Path());
345 		veraFontPath.Append("NotoMono-Regular.ttf");
346 		_AddDefaultMapping("Noto Mono", "Regular", 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