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