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
31FT_Library gFreeTypeLibrary;
32
33
34static int
35compare_font_families(const FontFamily* a, const FontFamily* b)
36{
37	return strcmp(a->Name(), b->Name());
38}
39
40
41//	#pragma mark -
42
43
44FontManager::FontManager()
45	:
46	fFamilies(20),
47	fNextID(0)
48{
49}
50
51
52FontManager::~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*/
63FT_CharMap
64FontManager::_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*/
101int32
102FontManager::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*/
112int32
113FontManager::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*/
127int32
128FontManager::CountStyles(uint16 familyID)
129{
130	FontFamily *family = GetFamily(familyID);
131	if (family != NULL)
132		return family->CountStyles();
133
134	return 0;
135}
136
137
138FontFamily*
139FontManager::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*/
151FontFamily*
152FontManager::GetFamily(const char* name)
153{
154	if (name == NULL)
155		return NULL;
156
157	return _FindFamily(name);
158}
159
160
161FontFamily*
162FontManager::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
174FontStyle*
175FontManager::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
185FontStyle*
186FontManager::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*/
201FontStyle*
202FontManager::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*/
227FontStyle*
228FontManager::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*/
263FontStyle*
264FontManager::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*/
283void
284FontManager::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
297status_t
298FontManager::_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
344FontStyle*
345FontManager::_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
362void
363FontManager::_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
382FontFamily*
383FontManager::_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
394FontFamily*
395FontManager::_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
409uint16
410FontManager::_NextID()
411{
412	return fNextID++;
413}
414