xref: /haiku/src/servers/app/ServerFont.cpp (revision 0b2dbe7d46ee888392907c60131b7f7652314175)
1 /*
2  * Copyright 2001-2005, Haiku.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		DarkWyrm <bpmagic@columbus.rr.com>
7  */
8 
9 
10 #include <ByteOrder.h>
11 #include <Shape.h>
12 #include <String.h>
13 #include <UTF8.h>
14 
15 #include "Angle.h"
16 #include "FontServer.h"
17 #include "moreUTF8.h"
18 #include "truncate_string.h"
19 
20 #include FT_FREETYPE_H
21 #include FT_OUTLINE_H
22 
23 #include "ServerFont.h"
24 
25 
26 // functions needed to convert a freetype vector graphics to a BShape
27 inline BPoint
28 VectorToPoint(FT_Vector *vector)
29 {
30 	BPoint result;
31 	result.x = float(int32(vector->x)) / 2097152;
32 	result.y = -float(int32(vector->y)) / 2097152;
33 	return result;
34 }
35 
36 int
37 MoveToFunc(FT_Vector *to, void *user)
38 {
39 	((BShape *)user)->MoveTo(VectorToPoint(to));
40 	return 0;
41 }
42 
43 int
44 LineToFunc(FT_Vector *to, void *user)
45 {
46 	((BShape *)user)->LineTo(VectorToPoint(to));
47 	return 0;
48 }
49 
50 int
51 ConicToFunc(FT_Vector *control, FT_Vector *to, void *user)
52 {
53 	BPoint controls[3];
54 
55 	controls[0] = VectorToPoint(control);
56 	controls[1] = VectorToPoint(to);
57 	controls[2] = controls[1];
58 
59 	((BShape *)user)->BezierTo(controls);
60 	return 0;
61 }
62 
63 int
64 CubicToFunc(FT_Vector *control1, FT_Vector *control2, FT_Vector *to, void *user)
65 {
66 	BPoint controls[3];
67 
68 	controls[0] = VectorToPoint(control1);
69 	controls[1] = VectorToPoint(control2);
70 	controls[2] = VectorToPoint(to);
71 
72 	((BShape *)user)->BezierTo(controls);
73 	return 0;
74 }
75 
76 
77 // is_white_space
78 inline bool
79 is_white_space(uint16 glyph)
80 {
81 	// TODO: handle them all!
82 	if (glyph == ' ' || glyph == B_TAB)
83 		return true;
84 	return false;
85 }
86 
87 
88 //	#pragma mark -
89 
90 
91 /*!
92 	\brief Constructor
93 	\param style Style object to which the ServerFont belongs
94 	\param size Character size in points
95 	\param rotation Rotation in degrees
96 	\param shear Shear (slant) in degrees. 45 <= shear <= 135
97 	\param flags Style flags as defined in <Font.h>
98 	\param spacing String spacing flag as defined in <Font.h>
99 */
100 ServerFont::ServerFont(FontStyle *style, float size,
101 					   float rotation, float shear,
102 					   uint16 flags, uint8 spacing)
103 	: fStyle(style),
104 	  fSize(size),
105 	  fRotation(rotation),
106 	  fShear(shear),
107 	  fBounds(0, 0, 0, 0),
108 	  fFlags(flags),
109 	  fSpacing(spacing),
110 	  fDirection(B_FONT_LEFT_TO_RIGHT),
111 	  fFace(B_REGULAR_FACE),
112 	  fEncoding(B_UNICODE_UTF8)
113 
114 {
115 	if (fStyle)
116 		fStyle->AddDependent();
117 }
118 
119 // TODO: fStyle should not be NULL. There should be another FontStyle
120 // constructor, that initializes without actually interfacing with
121 // freetype, so that a ServerFont can be guaranteed to be "valid".
122 
123 ServerFont::ServerFont()
124 	: fStyle(NULL),
125 	  fSize(0.0),
126 	  fRotation(0.0),
127 	  fShear(90.0),
128 	  fBounds(0, 0, 0, 0),
129 	  fFlags(0),
130 	  fSpacing(B_STRING_SPACING),
131 	  fDirection(B_FONT_LEFT_TO_RIGHT),
132 	  fFace(B_REGULAR_FACE),
133 	  fEncoding(B_UNICODE_UTF8)
134 {
135 }
136 
137 /*!
138 	\brief Copy Constructor
139 	\param font ServerFont to copy
140 */
141 ServerFont::ServerFont(const ServerFont &font)
142 	: fStyle(font.fStyle),
143 	  fSize(font.fSize),
144 	  fRotation(font.fRotation),
145 	  fShear(font.fShear),
146 	  fBounds(0, 0, 0, 0),
147 	  fFlags(font.fFlags),
148 	  fSpacing(font.fSpacing),
149 	  fDirection(font.fDirection),
150 	  fFace(font.fFace),
151 	  fEncoding(font.fEncoding)
152 {
153 	if (fStyle)
154 		fStyle->AddDependent();
155 }
156 
157 /*!
158 	\brief Removes itself as a dependency of its owning style.
159 */
160 ServerFont::~ServerFont()
161 {
162 	if (fStyle)
163 		fStyle->RemoveDependent();
164 }
165 
166 /*!
167 	\brief Returns a copy of the specified font
168 	\param The font to copy from.
169 	\return A copy of the specified font
170 */
171 ServerFont&
172 ServerFont::operator=(const ServerFont& font)
173 {
174 	fSize		= font.fSize;
175 	fRotation	= font.fRotation;
176 	fShear		= font.fShear;
177 	fFlags		= font.fFlags;
178 	fSpacing	= font.fSpacing;
179 	fDirection	= font.fDirection;
180 	fFace		= font.fFace;
181 	fEncoding	= font.fEncoding;
182 	fBounds		= font.fBounds;
183 
184 	_SetStyle(font.fStyle);
185 
186 	return *this;
187 }
188 
189 /*!
190 	\brief Returns the number of strikes in the font
191 	\return The number of strikes in the font
192 */
193 int32
194 ServerFont::CountTuned()
195 {
196 	if (fStyle)
197 		return fStyle->TunedCount();
198 
199 	return 0;
200 }
201 
202 /*!
203 	\brief Returns the file format of the font. Currently unimplemented.
204 	\return B_TRUETYPE_WINDOWS
205 */
206 font_file_format
207 ServerFont::FileFormat()
208 {
209 	// TODO: implement ServerFont::FileFormat
210 	return B_TRUETYPE_WINDOWS;
211 }
212 
213 const char*
214 ServerFont::GetStyle() const
215 {
216 	if (fStyle)
217 		return fStyle->Name();
218 
219 	return NULL;
220 }
221 
222 const char*
223 ServerFont::GetFamily() const
224 {
225 	if (fStyle)
226 		return fStyle->Family()->Name();
227 
228 	return NULL;
229 }
230 
231 /*!
232 	\brief Sets the ServerFont instance to whatever font is specified
233 	\param familyID ID number of the family to set
234 	\param styleID ID number of the style to set
235 	\return B_OK if successful, B_ERROR if not
236 */
237 status_t
238 ServerFont::SetFamilyAndStyle(uint16 familyID, uint16 styleID)
239 {
240 	FontStyle* style = NULL;
241 
242 	if (gFontServer->Lock()) {
243 		 style = gFontServer->GetStyle(familyID, styleID);
244 		gFontServer->Unlock();
245 	}
246 
247 	if (!style)
248 		return B_ERROR;
249 
250 	_SetStyle(style);
251 
252 	return B_OK;
253 }
254 
255 /*!
256 	\brief Sets the ServerFont instance to whatever font is specified
257 	\param fontID the combination of family and style ID numbers
258 	\return B_OK if successful, B_ERROR if not
259 */
260 status_t
261 ServerFont::SetFamilyAndStyle(uint32 fontID)
262 {
263 	uint16 style = fontID & 0xFFFF;
264 	uint16 family = (fontID & 0xFFFF0000) >> 16;
265 
266 	return SetFamilyAndStyle(family, style);
267 }
268 
269 /*!
270 	\brief Gets the ID values for the ServerFont instance in one shot
271 	\return the combination of family and style ID numbers
272 */
273 uint32
274 ServerFont::GetFamilyAndStyle(void) const
275 {
276 	return (FamilyID() << 16) | StyleID();
277 }
278 
279 
280 BShape **
281 ServerFont::GetGlyphShapes(const char charArray[], int32 numChars) const
282 {
283 	if (!charArray || numChars <= 0)
284 		return NULL;
285 
286 	FT_Outline_Funcs funcs;
287 	funcs.move_to = MoveToFunc;
288 	funcs.line_to = LineToFunc;
289 	funcs.conic_to = ConicToFunc;
290 	funcs.cubic_to = CubicToFunc;
291 
292 	FT_Face face = fStyle->GetFTFace();
293 	if (!face)
294 		return NULL;
295 
296 	FT_Set_Char_Size(face, 0, int32(fSize * 64), 72, 72);
297 
298 	Angle rotation(fRotation);
299 	Angle shear(fShear);
300 
301 	// First, rotate
302 	FT_Matrix rmatrix;
303 	rmatrix.xx = (FT_Fixed)( rotation.Cosine()*0x10000);
304 	rmatrix.xy = (FT_Fixed)(-rotation.Sine()*0x10000);
305 	rmatrix.yx = (FT_Fixed)( rotation.Sine()*0x10000);
306 	rmatrix.yy = (FT_Fixed)( rotation.Cosine()*0x10000);
307 
308 	// Next, shear
309 	FT_Matrix smatrix;
310 	smatrix.xx = (FT_Fixed)(0x10000);
311 	smatrix.xy = (FT_Fixed)(-shear.Cosine()*0x10000);
312 	smatrix.yx = (FT_Fixed)(0);
313 	smatrix.yy = (FT_Fixed)(0x10000);
314 
315 	// Multiply togheter
316 	FT_Matrix_Multiply(&rmatrix, &smatrix);
317 
318 	//FT_Vector pen;
319 	//FT_Set_Transform(face, &smatrix, &pen);
320 
321 	BShape **shapes = (BShape **)malloc(sizeof(BShape *) * numChars);
322 	for (int i = 0; i < numChars; i++) {
323 		shapes[i] = new BShape();
324 		shapes[i]->Clear();
325 		FT_Load_Char(face, charArray[i], FT_LOAD_NO_BITMAP);
326 		FT_Outline outline = face->glyph->outline;
327 		FT_Outline_Decompose(&outline, &funcs, shapes[i]);
328 		shapes[i]->Close();
329 	}
330 
331 	return shapes;
332 }
333 
334 // GetEscapements
335 BPoint*
336 ServerFont::GetEscapements(const char charArray[], int32 numChars,
337 						   BPoint offsetArray[]) const
338 {
339 	if (!fStyle || !charArray || numChars <= 0 || !offsetArray)
340 		return NULL;
341 
342 	FT_Face face = fStyle->GetFTFace();
343 	if (!face)
344 		return NULL;
345 
346 	FT_Set_Char_Size(face, 0, int32(fSize * 64), 72, 72);
347 
348 	Angle rotation(fRotation);
349 	Angle shear(fShear);
350 
351 	// First, rotate
352 	FT_Matrix rmatrix;
353 	rmatrix.xx = (FT_Fixed)( rotation.Cosine()*0x10000);
354 	rmatrix.xy = (FT_Fixed)(-rotation.Sine()*0x10000);
355 	rmatrix.yx = (FT_Fixed)( rotation.Sine()*0x10000);
356 	rmatrix.yy = (FT_Fixed)( rotation.Cosine()*0x10000);
357 
358 	// Next, shear
359 	FT_Matrix smatrix;
360 	smatrix.xx = (FT_Fixed)(0x10000);
361 	smatrix.xy = (FT_Fixed)(-shear.Cosine()*0x10000);
362 	smatrix.yx = (FT_Fixed)(0);
363 	smatrix.yy = (FT_Fixed)(0x10000);
364 
365 	// Multiply togheter
366 	FT_Matrix_Multiply(&rmatrix, &smatrix);
367 
368 	//FT_Vector pen;
369 	//FT_Set_Transform(face, &smatrix, &pen);
370 
371 	// TODO: I'm not sure if this the correct interpretation
372 	// of the BeBook. Have actual tests been done here?
373 
374 	// TODO: handle UTF8... see below!!
375 	BPoint *escapements = (BPoint *)malloc(sizeof(BPoint) * numChars);
376 	for (int i = 0; i < numChars; i++) {
377 		FT_Load_Char(face, charArray[i], FT_LOAD_NO_BITMAP);
378 //		escapements[i].x = float(face->glyph->metrics.width / 64) / fSize;
379 //		escapements[i].y = 0;
380 		escapements[i].x = float(face->glyph->metrics.horiAdvance / 64) / fSize;
381 		escapements[i].y = float(face->glyph->metrics.vertAdvance / 64) / fSize;
382 		escapements[i] += offsetArray[i];
383 	}
384 
385 	return escapements;
386 }
387 
388 
389 // GetEscapements
390 bool
391 ServerFont::GetEscapements(const char charArray[], int32 numChars,
392 						   float widthArray[], escapement_delta delta) const
393 {
394 	if (!fStyle || !charArray || numChars <= 0)
395 		return false;
396 
397 	FT_Face face = fStyle->GetFTFace();
398 	if (!face)
399 		return false;
400 
401 	FT_Set_Char_Size(face, 0, int32(fSize * 64), 72, 72);
402 
403 	// UTF8 handling...this can probably be smarter
404 	// Here is what I do in the AGGTextRenderer to handle UTF8...
405 	// It is probably highly inefficient, so it should be reviewed.
406 	int32 numBytes = UTF8CountBytes(charArray, numChars);
407 	int32 convertedLength = numBytes * 2;
408 	char* convertedBuffer = new char[convertedLength];
409 
410 	int32 state = 0;
411 	status_t ret;
412 	if ((ret = convert_from_utf8(B_UNICODE_CONVERSION,
413 								 charArray, &numBytes,
414 								 convertedBuffer, &convertedLength,
415 								 &state, B_SUBSTITUTE)) >= B_OK
416 		&& (ret = swap_data(B_INT16_TYPE, convertedBuffer, convertedLength,
417 							B_SWAP_BENDIAN_TO_HOST)) >= B_OK) {
418 
419 		uint16* glyphIndex = (uint16*)convertedBuffer;
420 		// just to be sure
421 		numChars = min_c((uint32)numChars, convertedLength / sizeof(uint16));
422 
423 		for (int i = 0; i < numChars; i++) {
424 			FT_Load_Char(face, glyphIndex[i], FT_LOAD_NO_BITMAP);
425 			if (face->glyph) {
426 				widthArray[i] = ((float)face->glyph->metrics.horiAdvance / 64.0) / fSize;
427 				widthArray[i] += is_white_space(glyphIndex[i]) ? delta.space : delta.nonspace;
428 			}
429 		}
430 	}
431 	delete[] convertedBuffer;
432 
433 	return ret >= B_OK;
434 }
435 
436 // StringWidth
437 float
438 ServerFont::StringWidth(const char* string, int32 numBytes) const
439 {
440 	if (!fStyle || !string || numBytes <= 0)
441 		return 0.0;
442 
443 	FT_Face face = fStyle->GetFTFace();
444 	if (!face)
445 		return 0.0;
446 
447 	float width = 0.0;
448 
449 	int32 convertedLength = numBytes * 2;
450 	char* convertedBuffer = new char[convertedLength];
451 
452 	int32 state = 0;
453 	status_t ret;
454 	if ((ret = convert_from_utf8(B_UNICODE_CONVERSION,
455 								 string, &numBytes,
456 								 convertedBuffer, &convertedLength,
457 								 &state, B_SUBSTITUTE)) >= B_OK
458 		&& (ret = swap_data(B_INT16_TYPE, convertedBuffer, convertedLength,
459 							B_SWAP_BENDIAN_TO_HOST)) >= B_OK) {
460 
461 		uint16* glyphIndex = (uint16*)convertedBuffer;
462 		// just to be sure
463 		int numChars = convertedLength / sizeof(uint16);
464 
465 		for (int i = 0; i < numChars; i++) {
466 			FT_Load_Char(face, glyphIndex[i], FT_LOAD_NO_BITMAP);
467 			width += (float)face->glyph->metrics.horiAdvance / 64.0;
468 		}
469 	}
470 	delete[] convertedBuffer;
471 
472 	return width;
473 }
474 
475 /*!
476 	\brief Returns a BRect which encloses the entire font
477 	\return A BRect which encloses the entire font
478 */
479 BRect
480 ServerFont::BoundingBox()
481 {
482 	// TODO: fBounds is nowhere calculated!
483 	return fBounds;
484 }
485 
486 /*!
487 	\brief Obtains the height values for characters in the font in its current state
488 	\param fh pointer to a font_height object to receive the values for the font
489 */
490 void
491 ServerFont::Height(font_height *fh) const
492 {
493 	if (fh && fStyle)
494 		*fh = fStyle->GetHeight(fSize);
495 }
496 
497 // TruncateString
498 void
499 ServerFont::TruncateString(BString* inOut, uint32 mode, float width) const
500 {
501 	if (inOut) {
502 		// the width of the "…" glyph
503 		float ellipsisWidth = StringWidth(B_UTF8_ELLIPSIS, 3);
504 		const char* string = inOut->String();
505 		int32 length = strlen(string);
506 		// temporary array to hold result
507 		char* result = new char[length + 3];
508 		// count the individual glyphs
509 		int32 numChars = UTF8CountChars(string, length);
510 		// get the escapement of each glyph in font units
511 		float* escapementArray = new float[numChars];
512 		static escapement_delta delta = (escapement_delta){ 0.0, 0.0 };
513 		GetEscapements(string, numChars, escapementArray, delta);
514 
515 		truncate_string(string, mode, width, result,
516 						escapementArray, fSize, ellipsisWidth, length, numChars);
517 
518 		inOut->SetTo(result);
519 
520 		delete[] escapementArray;
521 		delete[] result;
522 	}
523 }
524 
525 // _SetStyle
526 void
527 ServerFont::_SetStyle(FontStyle* style)
528 {
529 	if (style != fStyle) {
530 		// detach from old style
531 		if (fStyle)
532 			fStyle->RemoveDependent();
533 
534 		fStyle = style;
535 
536 		// attach to new style
537 		if (fStyle)
538 			fStyle->AddDependent();
539 	}
540 }
541