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