xref: /haiku/src/servers/app/font/FontManager.cpp (revision 47c05920fde47c2618efccd24bd82f1e79cdf05a)
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 <Debug.h>
19 
20 #include "FontFamily.h"
21 
22 
23 //#define TRACE_FONT_MANAGER
24 #ifdef TRACE_FONT_MANAGER
25 #	define FTRACE(x) printf x
26 #else
27 #	define FTRACE(x) ;
28 #endif
29 
30 
31 FT_Library gFreeTypeLibrary;
32 
33 
34 static int
35 compare_font_families(const FontFamily* a, const FontFamily* b)
36 {
37 	return strcmp(a->Name(), b->Name());
38 }
39 
40 
41 //	#pragma mark -
42 
43 
44 FontManager::FontManager()
45 	:
46 	fFamilies(20),
47 	fNextID(0)
48 {
49 }
50 
51 
52 FontManager::~FontManager()
53 {
54 	_RemoveAllFonts();
55 }
56 
57 
58 /*!	\brief Finds and returns the first valid charmap in a font
59 
60 	\param face Font handle obtained from FT_Load_Face()
61 	\return An FT_CharMap or NULL if unsuccessful
62 */
63 FT_CharMap
64 FontManager::_GetSupportedCharmap(const FT_Face& face)
65 {
66 	for (int32 i = 0; i < face->num_charmaps; i++) {
67 		FT_CharMap charmap = face->charmaps[i];
68 
69 		switch (charmap->platform_id) {
70 			case 3:
71 				// if Windows Symbol or Windows Unicode
72 				if (charmap->encoding_id == 0 || charmap->encoding_id == 1)
73 					return charmap;
74 				break;
75 
76 			case 1:
77 				// if Apple Unicode
78 				if (charmap->encoding_id == 0)
79 					return charmap;
80 				break;
81 
82 			case 0:
83 				// if Apple Roman
84 				if (charmap->encoding_id == 0)
85 					return charmap;
86 				break;
87 
88 			default:
89 				break;
90 		}
91 	}
92 
93 	return NULL;
94 }
95 
96 
97 
98 /*!	\brief Counts the number of font families available
99 	\return The number of unique font families currently available
100 */
101 int32
102 FontManager::CountFamilies()
103 {
104 	return fFamilies.CountItems();
105 }
106 
107 
108 /*!	\brief Counts the number of styles available in a font family
109 	\param family Name of the font family to scan
110 	\return The number of font styles currently available for the font family
111 */
112 int32
113 FontManager::CountStyles(const char *familyName)
114 {
115 	FontFamily *family = GetFamily(familyName);
116 	if (family != NULL)
117 		return family->CountStyles();
118 
119 	return 0;
120 }
121 
122 
123 /*!	\brief Counts the number of styles available in a font family
124 	\param family Name of the font family to scan
125 	\return The number of font styles currently available for the font family
126 */
127 int32
128 FontManager::CountStyles(uint16 familyID)
129 {
130 	FontFamily *family = GetFamily(familyID);
131 	if (family != NULL)
132 		return family->CountStyles();
133 
134 	return 0;
135 }
136 
137 
138 FontFamily*
139 FontManager::FamilyAt(int32 index) const
140 {
141 	ASSERT(IsLocked());
142 
143 	return fFamilies.ItemAt(index);
144 }
145 
146 
147 /*!	\brief Locates a FontFamily object by name
148 	\param name The family to find
149 	\return Pointer to the specified family or NULL if not found.
150 */
151 FontFamily*
152 FontManager::GetFamily(const char* name)
153 {
154 	if (name == NULL)
155 		return NULL;
156 
157 	return _FindFamily(name);
158 }
159 
160 
161 FontFamily*
162 FontManager::GetFamily(uint16 familyID) const
163 {
164 	FontKey key(familyID, 0);
165 	FontStyle* style = fStyleHashTable.Get(key);
166 	if (style != NULL)
167 		return style->Family();
168 
169 	// Try the slow route in case style 0 was removed
170 	return _FindFamily(familyID);
171 }
172 
173 
174 FontStyle*
175 FontManager::GetStyleByIndex(const char* familyName, int32 index)
176 {
177 	FontFamily* family = GetFamily(familyName);
178 	if (family != NULL)
179 		return family->StyleAt(index);
180 
181 	return NULL;
182 }
183 
184 
185 FontStyle*
186 FontManager::GetStyleByIndex(uint16 familyID, int32 index)
187 {
188 	FontFamily* family = GetFamily(familyID);
189 	if (family != NULL)
190 		return family->StyleAt(index);
191 
192 	return NULL;
193 }
194 
195 
196 /*!	\brief Retrieves the FontStyle object
197 	\param family ID for the font's family
198 	\param style ID of the font's style
199 	\return The FontStyle having those attributes or NULL if not available
200 */
201 FontStyle*
202 FontManager::GetStyle(uint16 familyID, uint16 styleID) const
203 {
204 	ASSERT(IsLocked());
205 
206 	FontKey key(familyID, styleID);
207 	FontStyle* style = fStyleHashTable.Get(key);
208 	if (style != NULL)
209 		return style;
210 
211 	return fDelistedStyleHashTable.Get(key);
212 }
213 
214 
215 /*!	\brief Retrieves the FontStyle object that comes closest to the one
216 		specified.
217 
218 	\param family The font's family or NULL in which case \a familyID is used
219 	\param style The font's style or NULL in which case \a styleID is used
220 	\param familyID will only be used if \a family is NULL (or empty)
221 	\param styleID will only be used if \a family and \a style are NULL (or empty)
222 	\param face is used to specify the style if both \a style is NULL or empty
223 		and styleID is 0xffff.
224 
225 	\return The FontStyle having those attributes or NULL if not available
226 */
227 FontStyle*
228 FontManager::GetStyle(const char* familyName, const char* styleName,
229 	uint16 familyID, uint16 styleID, uint16 face)
230 {
231 	ASSERT(IsLocked());
232 
233 	FontFamily* family;
234 
235 	if (styleID != 0xffff && (familyName == NULL || !familyName[0])
236 		&& (styleName == NULL || !styleName[0])) {
237 		return GetStyle(familyID, styleID);
238 	}
239 
240 	// find family
241 
242 	if (familyName != NULL && familyName[0])
243 		family = GetFamily(familyName);
244 	else
245 		family = GetFamily(familyID);
246 
247 	if (family == NULL)
248 		return NULL;
249 
250 	// find style
251 
252 	if (styleName != NULL && styleName[0])
253 		return family->GetStyle(styleName);
254 
255 	// try to get from face
256 	return family->GetStyleMatchingFace(face);
257 }
258 
259 
260 /*!	\brief If you don't find your preferred font style, but are anxious
261 		to have one fitting your needs, you may want to use this method.
262 */
263 FontStyle*
264 FontManager::FindStyleMatchingFace(uint16 face) const
265 {
266 	int32 count = fFamilies.CountItems();
267 
268 	for (int32 i = 0; i < count; i++) {
269 		FontFamily* family = fFamilies.ItemAt(i);
270 		FontStyle* style = family->GetStyleMatchingFace(face);
271 		if (style != NULL)
272 			return style;
273 	}
274 
275 	return NULL;
276 }
277 
278 
279 /*!	\brief This call is used by the FontStyle class - and the FontStyle class
280 		only - to remove itself from the font manager.
281 	At this point, the style is already no longer available to the user.
282 */
283 void
284 FontManager::RemoveStyle(FontStyle* style)
285 {
286 	ASSERT(IsLocked());
287 
288 	FontFamily* family = style->Family();
289 	if (family == NULL)
290 		debugger("family is NULL!");
291 
292 	family->RemoveStyle(style);
293 	fDelistedStyleHashTable.Remove(FontKey(family->ID(), style->ID()));
294 }
295 
296 
297 status_t
298 FontManager::_AddFont(FT_Face face, node_ref nodeRef, const char* path,
299 	uint16& familyID, uint16& styleID)
300 {
301 	ASSERT(IsLocked());
302 
303 	BReference<FontFamily> family(_FindFamily(face->family_name));
304 	bool isNewFontFamily = !family.IsSet();
305 
306 	if (family.IsSet() && family->HasStyle(face->style_name)) {
307 		// prevent adding the same style twice
308 		// (this indicates a problem with the installed fonts maybe?)
309 		FT_Done_Face(face);
310 		return B_NAME_IN_USE;
311 	}
312 
313 	if (!family.IsSet()) {
314 		family.SetTo(new (std::nothrow) FontFamily(face->family_name, _NextID()), true);
315 
316 		if (!family.IsSet() || !fFamilies.BinaryInsert(family, compare_font_families)) {
317 			FT_Done_Face(face);
318 			return B_NO_MEMORY;
319 		}
320 	}
321 
322 	FTRACE(("\tadd style: %s, %s\n", face->family_name, face->style_name));
323 
324 	// the FontStyle takes over ownership of the FT_Face object
325 	FontStyle* style = new (std::nothrow) FontStyle(nodeRef, path, face, this);
326 
327 	if (style == NULL || !family->AddStyle(style)) {
328 		delete style;
329 		if (isNewFontFamily)
330 			fFamilies.RemoveItem(family);
331 		return B_NO_MEMORY;
332 	}
333 
334 	familyID = style->Family()->ID();
335 	styleID = style->ID();
336 
337 	fStyleHashTable.Put(FontKey(familyID, styleID), style);
338 	style->ReleaseReference();
339 
340 	return B_OK;
341 }
342 
343 
344 FontStyle*
345 FontManager::_RemoveFont(uint16 familyID, uint16 styleID)
346 {
347 	ASSERT(IsLocked());
348 
349 	FontKey key(familyID, styleID);
350 	FontStyle* style = fStyleHashTable.Get(key);
351 	if (style != NULL) {
352 		fDelistedStyleHashTable.Put(key, style);
353 		FontFamily* family = style->Family();
354 		if (family->RemoveStyle(style) && family->CountStyles() == 0)
355 			fFamilies.RemoveItem(family);
356 		fStyleHashTable.Remove(key);
357 	}
358 	return style;
359 }
360 
361 
362 void
363 FontManager::_RemoveAllFonts()
364 {
365 	fFamilies.MakeEmpty();
366 
367 	// Disconnect the styles from their families before removing them; once we
368 	// get to this point, we are in the dtor and don't want them to call back.
369 
370 	HashMap<FontKey, FontStyle*>::Iterator delisted = fDelistedStyleHashTable.GetIterator();
371 	while (delisted.HasNext())
372 		delisted.Next().value->_SetFontFamily(NULL, -1);
373 	fDelistedStyleHashTable.Clear();
374 
375 	HashMap<FontKey, BReference<FontStyle> >::Iterator referenced = fStyleHashTable.GetIterator();
376 	while (referenced.HasNext())
377 		referenced.Next().value->_SetFontFamily(NULL, -1);
378 	fStyleHashTable.Clear();
379 }
380 
381 
382 FontFamily*
383 FontManager::_FindFamily(const char* name) const
384 {
385 	if (name == NULL)
386 		return NULL;
387 
388 	FontFamily family(name, 0);
389 	return const_cast<FontFamily*>(fFamilies.BinarySearch(family,
390 		compare_font_families));
391 }
392 
393 
394 FontFamily*
395 FontManager::_FindFamily(uint16 familyID) const
396 {
397 	int32 count = fFamilies.CountItems();
398 
399 	for (int32 i = 0; i < count; i++) {
400 		FontFamily* family = fFamilies.ItemAt(i);
401 		if (family->ID() == familyID)
402 			return family;
403 	}
404 
405 	return NULL;
406 }
407 
408 
409 uint16
410 FontManager::_NextID()
411 {
412 	return fNextID++;
413 }
414