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