1/*
2 * Copyright 2001-2008, 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/**	Classes to represent font styles and families */
11
12
13#include "FontFamily.h"
14#include "FontManager.h"
15#include "ServerFont.h"
16
17#include <FontPrivate.h>
18
19#include <Entry.h>
20
21
22static BLocker sFontLock("font lock");
23
24
25/*!
26	\brief Constructor
27	\param filepath path to a font file
28	\param face FreeType handle for the font file after it is loaded - it will
29		   be kept open until the FontStyle is destroyed
30*/
31FontStyle::FontStyle(node_ref& nodeRef, const char* path, FT_Face face,
32	FontManager* fontManager)
33	:
34	fFreeTypeFace(face),
35	fName(face->style_name),
36	fPath(path),
37	fNodeRef(nodeRef),
38	fFamily(NULL),
39	fID(0),
40	fBounds(0, 0, 0, 0),
41	fFace(_TranslateStyleToFace(face->style_name)),
42	fFullAndHalfFixed(false),
43	fFontData(NULL),
44	fFontManager(fontManager)
45{
46	fName.Truncate(B_FONT_STYLE_LENGTH);
47		// make sure this style can be found using the Be API
48
49	if (IsScalable()) {
50		fHeight.ascent = (double)face->ascender / face->units_per_EM;
51		fHeight.descent = (double)-face->descender / face->units_per_EM;
52			// FT2's descent numbers are negative. Be's is positive
53
54		// FT2 doesn't provide a linegap, but according to the docs, we can
55		// calculate it because height = ascending + descending + leading
56		fHeight.leading = (double)(face->height - face->ascender
57			+ face->descender) / face->units_per_EM;
58	} else {
59		// We don't have global metrics, get them from a bitmap
60		FT_Pos size = face->available_sizes[0].size;
61		for (int i = 1; i < face->num_fixed_sizes; i++)
62			size = max_c(size, face->available_sizes[i].size);
63		FT_Set_Pixel_Sizes(face, 0, size / 64);
64			// Size is encoded as 26.6 fixed point, while FT_Set_Pixel_Sizes
65			// uses the integer unencoded value
66
67		FT_Size_Metrics metrics = face->size->metrics;
68		fHeight.ascent = (double)metrics.ascender / size;
69		fHeight.descent = (double)-metrics.descender / size;
70		fHeight.leading = (double)(metrics.height - metrics.ascender
71			+ metrics.descender) / size;
72	}
73
74	if (IsFixedWidth())
75		return;
76
77	// manually check if all applicable chars are the same width
78
79	FT_Int32 loadFlags = FT_LOAD_NO_SCALE | FT_LOAD_TARGET_NORMAL;
80	if (FT_Load_Char(face, (uint32)' ', loadFlags) != 0)
81		return;
82
83	int firstWidth = face->glyph->advance.x;
84	for (uint32 c = ' ' + 1; c <= 0x7e; c++) {
85		if (FT_Load_Char(face, c, loadFlags) != 0)
86			return;
87
88		if (face->glyph->advance.x != firstWidth)
89			return;
90	}
91
92	fFullAndHalfFixed = true;
93}
94
95
96FontStyle::~FontStyle()
97{
98	// make sure the font server is ours
99	if (fFamily != NULL && fFontManager->Lock()) {
100		fFontManager->RemoveStyle(this);
101		fFontManager->Unlock();
102	}
103
104	FT_Done_Face(fFreeTypeFace);
105
106	if (fFontData != NULL)
107		free(fFontData);
108}
109
110
111bool
112FontStyle::Lock()
113{
114	return sFontLock.Lock();
115}
116
117
118void
119FontStyle::Unlock()
120{
121	sFontLock.Unlock();
122}
123
124
125void
126FontStyle::GetHeight(float size, font_height& height) const
127{
128	height.ascent = fHeight.ascent * size;
129	height.descent = fHeight.descent * size;
130	height.leading = fHeight.leading * size;
131}
132
133
134/*!
135	\brief Returns the path to the style's font file
136	\return The style's font file path
137*/
138const char*
139FontStyle::Path() const
140{
141	return fPath.Path();
142}
143
144
145/*!
146	\brief Updates the path of the font style in case the style
147		has been moved around.
148*/
149void
150FontStyle::UpdatePath(const node_ref& parentNodeRef)
151{
152	entry_ref ref;
153	ref.device = parentNodeRef.device;
154	ref.directory = parentNodeRef.node;
155	ref.set_name(fPath.Leaf());
156
157	fPath.SetTo(&ref);
158}
159
160
161/*!
162	\brief Unlike BFont::Flags() this returns the extra flags field as used
163		in the private part of BFont.
164*/
165uint32
166FontStyle::Flags() const
167{
168	uint32 flags = uint32(Direction()) << B_PRIVATE_FONT_DIRECTION_SHIFT;
169
170	if (IsFixedWidth())
171		flags |= B_IS_FIXED;
172	if (IsFullAndHalfFixed())
173		flags |= B_PRIVATE_FONT_IS_FULL_AND_HALF_FIXED;
174	if (TunedCount() > 0)
175		flags |= B_HAS_TUNED_FONT;
176	if (HasKerning())
177		flags |= B_PRIVATE_FONT_HAS_KERNING;
178
179	return flags;
180}
181
182
183/*!
184	\brief Updates the given face to match the one from this style
185
186	The specified font face often doesn't match the exact face of
187	a style. This method will preserve the attributes of the face
188	that this style does not alter, and will only update the
189	attributes that matter to this style.
190	The font renderer could then emulate the other face attributes
191	taking this style as a base.
192*/
193uint16
194FontStyle::PreservedFace(uint16 face) const
195{
196	// TODO: make this better
197	face &= ~(B_REGULAR_FACE | B_BOLD_FACE | B_ITALIC_FACE | B_CONDENSED_FACE
198		| B_LIGHT_FACE | B_HEAVY_FACE);
199	face |= Face();
200
201	return face;
202}
203
204
205status_t
206FontStyle::UpdateFace(FT_Face face)
207{
208	if (!sFontLock.IsLocked()) {
209		debugger("UpdateFace() called without having locked FontStyle!");
210		return B_ERROR;
211	}
212
213	// we only accept the face if it hasn't change its style
214
215	BString name = face->style_name;
216	name.Truncate(B_FONT_STYLE_LENGTH);
217
218	if (name != fName)
219		return B_BAD_VALUE;
220
221	FT_Done_Face(fFreeTypeFace);
222	fFreeTypeFace = face;
223	return B_OK;
224}
225
226
227void
228FontStyle::_SetFontFamily(FontFamily* family, uint16 id)
229{
230	fFamily = family;
231	fID = id;
232}
233
234
235uint16
236FontStyle::_TranslateStyleToFace(const char* name) const
237{
238	if (name == NULL)
239		return 0;
240
241	BString string(name);
242	uint16 face = 0;
243
244	if (string.IFindFirst("bold") >= 0)
245		face |= B_BOLD_FACE;
246
247	if (string.IFindFirst("italic") >= 0
248		|| string.IFindFirst("oblique") >= 0)
249		face |= B_ITALIC_FACE;
250
251	if (string.IFindFirst("condensed") >= 0)
252		face |= B_CONDENSED_FACE;
253
254	if (string.IFindFirst("light") >= 0
255		|| string.IFindFirst("thin") >= 0)
256		face |= B_LIGHT_FACE;
257
258	if (string.IFindFirst("heavy") >= 0
259		|| string.IFindFirst("black") >= 0)
260		face |= B_HEAVY_FACE;
261
262	if (face == 0)
263		return B_REGULAR_FACE;
264
265	return face;
266}
267
268
269void
270FontStyle::SetFontData(FT_Byte* location, uint32 size)
271{
272	// if memory was already allocated here, we should free it so it's not leaked
273	if (fFontData != NULL)
274		free(fFontData);
275
276	fFontDataSize = size;
277	fFontData = location;
278}
279