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