xref: /haiku/src/servers/app/ServerFont.cpp (revision 0e50eab75e25d0d82090e22dbff766dfaa6f5e86)
1 /*
2  * Copyright 2001-2006, Haiku.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		DarkWyrm <bpmagic@columbus.rr.com>
7  *		Jérôme Duval, jerome.duval@free.fr
8  *		Michael Lotz <mmlr@mlotz.ch>
9  */
10 
11 
12 #include "ServerFont.h"
13 
14 #include "Angle.h"
15 #include "FontManager.h"
16 #include "truncate_string.h"
17 #include "utf8_functions.h"
18 
19 #include FT_FREETYPE_H
20 #include FT_GLYPH_H
21 #include FT_OUTLINE_H
22 
23 #include <Shape.h>
24 #include <String.h>
25 #include <UTF8.h>
26 
27 
28 // functions needed to convert a freetype vector graphics to a BShape
29 inline BPoint
30 VectorToPoint(FT_Vector *vector)
31 {
32 	BPoint result;
33 	result.x = float(vector->x) / 64;
34 	result.y = -float(vector->y) / 64;
35 	return result;
36 }
37 
38 
39 int
40 MoveToFunc(FT_Vector *to, void *user)
41 {
42 	((BShape *)user)->MoveTo(VectorToPoint(to));
43 	return 0;
44 }
45 
46 
47 int
48 LineToFunc(FT_Vector *to, void *user)
49 {
50 	((BShape *)user)->LineTo(VectorToPoint(to));
51 	return 0;
52 }
53 
54 
55 int
56 ConicToFunc(FT_Vector *control, FT_Vector *to, void *user)
57 {
58 	BPoint controls[3];
59 
60 	controls[0] = VectorToPoint(control);
61 	controls[1] = VectorToPoint(to);
62 	controls[2] = controls[1];
63 
64 	((BShape *)user)->BezierTo(controls);
65 	return 0;
66 }
67 
68 
69 int
70 CubicToFunc(FT_Vector *control1, FT_Vector *control2, FT_Vector *to, void *user)
71 {
72 	BPoint controls[3];
73 
74 	controls[0] = VectorToPoint(control1);
75 	controls[1] = VectorToPoint(control2);
76 	controls[2] = VectorToPoint(to);
77 
78 	((BShape *)user)->BezierTo(controls);
79 	return 0;
80 }
81 
82 
83 inline bool
84 is_white_space(uint32 charCode)
85 {
86 	switch (charCode) {
87 		case 0x0009:	/* tab */
88 		case 0x000b:	/* vertical tab */
89 		case 0x000c:	/* form feed */
90 		case 0x0020:	/* space */
91 		case 0x00a0:	/* non breaking space */
92 		case 0x000a:	/* line feed */
93 		case 0x000d:	/* carriage return */
94 		case 0x2028:	/* line separator */
95 		case 0x2029:	/* paragraph separator */
96 			return true;
97 	}
98 
99 	return false;
100 }
101 
102 
103 //	#pragma mark -
104 
105 
106 /*!
107 	\brief Constructor
108 	\param style Style object to which the ServerFont belongs
109 	\param size Character size in points
110 	\param rotation Rotation in degrees
111 	\param shear Shear (slant) in degrees. 45 <= shear <= 135
112 	\param flags Style flags as defined in <Font.h>
113 	\param spacing String spacing flag as defined in <Font.h>
114 */
115 ServerFont::ServerFont(FontStyle& style, float size,
116 					   float rotation, float shear,
117 					   uint16 flags, uint8 spacing)
118 	: fStyle(&style),
119 	  fSize(size),
120 	  fRotation(rotation),
121 	  fShear(shear),
122 	  fBounds(0, 0, 0, 0),
123 	  fFlags(flags),
124 	  fSpacing(spacing),
125 	  fDirection(style.Direction()),
126 	  fFace(style.Face()),
127 	  fEncoding(B_UNICODE_UTF8)
128 {
129 	fStyle->Acquire();
130 }
131 
132 
133 ServerFont::ServerFont()
134 	:
135 	fStyle(NULL)
136 {
137 	*this = *gFontManager->DefaultPlainFont();
138 }
139 
140 
141 /*!
142 	\brief Copy Constructor
143 	\param font ServerFont to copy
144 */
145 ServerFont::ServerFont(const ServerFont &font)
146 	:
147 	fStyle(NULL)
148 {
149 	*this = font;
150 }
151 
152 
153 /*!
154 	\brief Removes itself as a dependency of its owning style.
155 */
156 ServerFont::~ServerFont()
157 {
158 	fStyle->Release();
159 }
160 
161 
162 /*!
163 	\brief Returns a copy of the specified font
164 	\param The font to copy from.
165 	\return A copy of the specified font
166 */
167 ServerFont&
168 ServerFont::operator=(const ServerFont& font)
169 {
170 	if (font.fStyle) {
171 		fSize		= font.fSize;
172 		fRotation	= font.fRotation;
173 		fShear		= font.fShear;
174 		fFlags		= font.fFlags;
175 		fSpacing	= font.fSpacing;
176 		fEncoding	= font.fEncoding;
177 		fBounds		= font.fBounds;
178 
179 		SetStyle(font.fStyle);
180 	}
181 
182 	return *this;
183 }
184 
185 
186 /*!
187 	\brief Returns the number of strikes in the font
188 	\return The number of strikes in the font
189 */
190 int32
191 ServerFont::CountTuned()
192 {
193 	return fStyle->TunedCount();
194 }
195 
196 
197 /*!
198 	\brief Returns the file format of the font.
199 	\return Mostly B_TRUETYPE_WINDOWS :)
200 */
201 font_file_format
202 ServerFont::FileFormat()
203 {
204 	return fStyle->FileFormat();
205 }
206 
207 
208 const char*
209 ServerFont::Style() const
210 {
211 	return fStyle->Name();
212 }
213 
214 
215 const char*
216 ServerFont::Family() const
217 {
218 	return fStyle->Family()->Name();
219 }
220 
221 
222 void
223 ServerFont::SetStyle(FontStyle* style)
224 {
225 	if (style && style != fStyle) {
226 		// detach from old style
227 		if (fStyle)
228 			fStyle->Release();
229 
230 		// attach to new style
231 		fStyle = style;
232 
233 		fStyle->Acquire();
234 
235 		fFace = fStyle->Face();
236 		fDirection = fStyle->Direction();
237 	}
238 }
239 
240 
241 /*!
242 	\brief Sets the ServerFont instance to whatever font is specified
243 	\param familyID ID number of the family to set
244 	\param styleID ID number of the style to set
245 	\return B_OK if successful, B_ERROR if not
246 */
247 status_t
248 ServerFont::SetFamilyAndStyle(uint16 familyID, uint16 styleID)
249 {
250 	FontStyle* style = NULL;
251 
252 	if (gFontManager->Lock()) {
253 		style = gFontManager->GetStyle(familyID, styleID);
254 		if (style != NULL)
255 			style->Acquire();
256 
257 		gFontManager->Unlock();
258 	}
259 
260 	if (!style)
261 		return B_ERROR;
262 
263 	SetStyle(style);
264 	style->Release();
265 
266 	return B_OK;
267 }
268 
269 
270 /*!
271 	\brief Sets the ServerFont instance to whatever font is specified
272 	\param fontID the combination of family and style ID numbers
273 	\return B_OK if successful, B_ERROR if not
274 */
275 status_t
276 ServerFont::SetFamilyAndStyle(uint32 fontID)
277 {
278 	uint16 style = fontID & 0xFFFF;
279 	uint16 family = (fontID & 0xFFFF0000) >> 16;
280 
281 	return SetFamilyAndStyle(family, style);
282 }
283 
284 
285 void
286 ServerFont::SetFace(uint32 face)
287 {
288 	// TODO: change font style as requested!
289 	fFace = face;
290 }
291 
292 
293 /*!
294 	\brief Gets the ID values for the ServerFont instance in one shot
295 	\return the combination of family and style ID numbers
296 */
297 uint32
298 ServerFont::GetFamilyAndStyle() const
299 {
300 	return (FamilyID() << 16) | StyleID();
301 }
302 
303 
304 FT_Face
305 ServerFont::GetTransformedFace(bool rotate, bool shear) const
306 {
307 	fStyle->Lock();
308 	FT_Face face = fStyle->FreeTypeFace();
309 	if (!face) {
310 		fStyle->Unlock();
311 		return NULL;
312 	}
313 
314 	FT_Set_Char_Size(face, 0, int32(fSize * 64), 72, 72);
315 
316 	if ((rotate && fRotation != 0) || (shear && fShear != 90)) {
317 		FT_Matrix rmatrix, smatrix;
318 
319 		Angle rotationAngle(fRotation);
320 		rmatrix.xx = (FT_Fixed)( rotationAngle.Cosine() * 0x10000);
321 		rmatrix.xy = (FT_Fixed)(-rotationAngle.Sine() * 0x10000);
322 		rmatrix.yx = (FT_Fixed)( rotationAngle.Sine() * 0x10000);
323 		rmatrix.yy = (FT_Fixed)( rotationAngle.Cosine() * 0x10000);
324 
325 		Angle shearAngle(fShear);
326 		smatrix.xx = (FT_Fixed)(0x10000);
327 		smatrix.xy = (FT_Fixed)(-shearAngle.Cosine() * 0x10000);
328 		smatrix.yx = (FT_Fixed)(0);
329 		smatrix.yy = (FT_Fixed)(0x10000);
330 
331 		// Multiply togheter and apply transform
332 		FT_Matrix_Multiply(&rmatrix, &smatrix);
333 		FT_Set_Transform(face, &smatrix, NULL);
334 	}
335 
336 	return face;
337 }
338 
339 
340 void
341 ServerFont::PutTransformedFace(FT_Face face) const
342 {
343 	// Reset transformation
344 	FT_Set_Transform(face, NULL, NULL);
345 	fStyle->Unlock();
346 }
347 
348 
349 status_t
350 ServerFont::GetGlyphShapes(const char charArray[], int32 numChars,
351 	BShape *shapeArray[]) const
352 {
353 	if (!charArray || numChars <= 0 || !shapeArray)
354 		return B_BAD_DATA;
355 
356 	FT_Face face = GetTransformedFace(true, true);
357 	if (!face)
358 		return B_ERROR;
359 
360 	FT_Outline_Funcs funcs;
361 	funcs.move_to = MoveToFunc;
362 	funcs.line_to = LineToFunc;
363 	funcs.conic_to = ConicToFunc;
364 	funcs.cubic_to = CubicToFunc;
365 	funcs.shift = 0;
366 	funcs.delta = 0;
367 
368 	const char *string = charArray;
369 	for (int i = 0; i < numChars; i++) {
370 		shapeArray[i] = new BShape();
371 		FT_Load_Char(face, UTF8ToCharCode(&string), FT_LOAD_NO_BITMAP);
372 		FT_Outline outline = face->glyph->outline;
373 		FT_Outline_Decompose(&outline, &funcs, shapeArray[i]);
374 		shapeArray[i]->Close();
375 	}
376 
377 	PutTransformedFace(face);
378 	return B_OK;
379 }
380 
381 
382 status_t
383 ServerFont::GetHasGlyphs(const char charArray[], int32 numChars,
384 	bool hasArray[]) const
385 {
386 	if (!charArray || numChars <= 0 || !hasArray)
387 		return B_BAD_DATA;
388 
389 	FT_Face face = GetTransformedFace(false, false);
390 	if (!face)
391 		return B_ERROR;
392 
393 	const char *string = charArray;
394 	for (int i = 0; i < numChars; i++)
395 		hasArray[i] = FT_Get_Char_Index(face, UTF8ToCharCode(&string)) > 0;
396 
397 	PutTransformedFace(face);
398 	return B_OK;
399 }
400 
401 
402 status_t
403 ServerFont::GetEdges(const char charArray[], int32 numChars,
404 	edge_info edgeArray[]) const
405 {
406 	if (!charArray || numChars <= 0 || !edgeArray)
407 		return B_BAD_DATA;
408 
409 	FT_Face face = GetTransformedFace(false, false);
410 	if (!face)
411 		return B_ERROR;
412 
413 	const char *string = charArray;
414 	for (int i = 0; i < numChars; i++) {
415 		FT_Load_Char(face, UTF8ToCharCode(&string), FT_LOAD_NO_BITMAP);
416 		edgeArray[i].left = float(face->glyph->metrics.horiBearingX)
417 			/ 64 / fSize;
418 		edgeArray[i].right = float(face->glyph->metrics.horiBearingX
419 			+ face->glyph->metrics.width - face->glyph->metrics.horiAdvance)
420 			/ 64 / fSize;
421 	}
422 
423 	PutTransformedFace(face);
424 	return B_OK;
425 }
426 
427 
428 status_t
429 ServerFont::GetEscapements(const char charArray[], int32 numChars,
430 	escapement_delta delta, BPoint escapementArray[], BPoint offsetArray[]) const
431 {
432 	if (!charArray || numChars <= 0 || !escapementArray)
433 		return B_BAD_DATA;
434 
435 	FT_Face face = GetTransformedFace(true, false);
436 	if (!face)
437 		return B_ERROR;
438 
439 	const char *string = charArray;
440 	for (int i = 0; i < numChars; i++) {
441 		uint32 charCode = UTF8ToCharCode(&string);
442 		FT_Load_Char(face, charCode, FT_LOAD_NO_BITMAP);
443 		escapementArray[i].x = is_white_space(charCode) ? delta.space : delta.nonspace;
444 		escapementArray[i].x += float(face->glyph->advance.x) / 64;
445 		escapementArray[i].y = -float(face->glyph->advance.y) / 64;
446 		escapementArray[i].x /= fSize;
447 		escapementArray[i].y /= fSize;
448 
449 		if (offsetArray) {
450 			// ToDo: According to the BeBook: "The offsetArray is applied by
451 			// the dynamic spacing in order to improve the relative position
452 			// of the character's width with relation to another character,
453 			// without altering the width." So this will probably depend on
454 			// the spacing mode.
455 			offsetArray[i].x = 0;
456 			offsetArray[i].y = 0;
457 		}
458 	}
459 
460 	PutTransformedFace(face);
461 	return B_OK;
462 }
463 
464 
465 status_t
466 ServerFont::GetEscapements(const char charArray[], int32 numChars,
467 	escapement_delta delta, float widthArray[]) const
468 {
469 	if (!charArray || numChars <= 0 || !widthArray)
470 		return B_BAD_DATA;
471 
472 	FT_Face face = GetTransformedFace(false, false);
473 	if (!face)
474 		return B_ERROR;
475 
476 	const char *string = charArray;
477 	for (int i = 0; i < numChars; i++) {
478 		uint32 charCode = UTF8ToCharCode(&string);
479 		FT_Load_Char(face, charCode, FT_LOAD_NO_BITMAP);
480 		widthArray[i] = is_white_space(charCode) ? delta.space : delta.nonspace;
481 		widthArray[i] += float(face->glyph->metrics.horiAdvance) / 64.0;
482 		widthArray[i] /= fSize;
483 	}
484 
485 	PutTransformedFace(face);
486 	return B_OK;
487 }
488 
489 
490 status_t
491 ServerFont::GetBoundingBoxesAsString(const char charArray[], int32 numChars,
492 	BRect rectArray[], bool stringEscapement, font_metric_mode mode,
493 	escapement_delta delta)
494 {
495 	if (!charArray || numChars <= 0 || !rectArray)
496 		return B_BAD_DATA;
497 
498 	FT_Face face = GetTransformedFace(true, true);
499 	if (!face)
500 		return B_ERROR;
501 
502 	const char *string = charArray;
503 	for (int i = 0; i < numChars; i++) {
504 		uint32 charCode = UTF8ToCharCode(&string);
505 		if (stringEscapement) {
506 			if (i > 0)
507 				rectArray[i].OffsetBy(is_white_space(charCode) ? delta.space / 2.0 : delta.nonspace / 2.0, 0.0);
508 
509 			rectArray[i].OffsetBy(is_white_space(charCode) ? delta.space / 2.0 : delta.nonspace / 2.0, 0.0);
510 		}
511 
512 		FT_Load_Char(face, charCode, FT_LOAD_NO_BITMAP);
513 		if (i < numChars - 1) {
514 			rectArray[i + 1].left = rectArray[i + 1].right = rectArray[i].left
515 				+ face->glyph->metrics.horiAdvance / 64.0;
516 		}
517 
518 		rectArray[i].left += float(face->glyph->metrics.horiBearingX) /64.0;
519 		rectArray[i].right += float(face->glyph->metrics.horiBearingX
520 			+ face->glyph->metrics.width) / 64.0;
521 		rectArray[i].top = -float(face->glyph->metrics.horiBearingY) / 64.0;
522 		rectArray[i].bottom = float(face->glyph->metrics.height
523 			- face->glyph->metrics.horiBearingY) /64.0;
524 	}
525 
526 	PutTransformedFace(face);
527 	return B_OK;
528 }
529 
530 
531 status_t
532 ServerFont::GetBoundingBoxesForStrings(char *charArray[], int32 lengthArray[],
533 	int32 numStrings, BRect rectArray[], font_metric_mode mode, escapement_delta deltaArray[])
534 {
535 	if (!charArray || !lengthArray|| numStrings <= 0 || !rectArray || !deltaArray)
536 		return B_BAD_DATA;
537 
538 	FT_Face face = GetTransformedFace(true, true);
539 	if (!face)
540 		return B_ERROR;
541 
542 	for (int32 i = 0; i < numStrings; i++) {
543 		// TODO: ...
544 	}
545 
546 	PutTransformedFace(face);
547 	return B_OK;
548 }
549 
550 
551 float
552 ServerFont::StringWidth(const char *_string, int32 numChars) const
553 {
554 	if (!_string || numChars <= 0)
555 		return 0.0;
556 
557 	FT_Face face = GetTransformedFace(false, false);
558 	if (!face)
559 		return 0.0;
560 
561 	float width = 0.0;
562 	const char *string = _string;
563 	for (int i = 0; i < numChars; i++) {
564 		FT_Load_Char(face, UTF8ToCharCode(&string), FT_LOAD_NO_BITMAP);
565 		width += face->glyph->advance.x / 64.0;
566 	}
567 
568 	PutTransformedFace(face);
569 	return width;
570 }
571 
572 
573 /*!
574 	\brief Returns a BRect which encloses the entire font
575 	\return A BRect which encloses the entire font
576 */
577 BRect
578 ServerFont::BoundingBox()
579 {
580 	// TODO: fBounds is nowhere calculated!
581 	return fBounds;
582 }
583 
584 
585 /*!
586 	\brief Obtains the height values for characters in the font in its current state
587 	\param fh pointer to a font_height object to receive the values for the font
588 */
589 void
590 ServerFont::GetHeight(font_height& height) const
591 {
592 	fStyle->GetHeight(fSize, height);
593 }
594 
595 
596 void
597 ServerFont::TruncateString(BString* inOut, uint32 mode, float width) const
598 {
599 	if (!inOut)
600 		return;
601 
602 	// the width of the "…" glyph
603 	float ellipsisWidth = StringWidth(B_UTF8_ELLIPSIS, 1);
604 	const char *string = inOut->String();
605 	int32 length = inOut->Length();
606 
607 	// temporary array to hold result
608 	char *result = new char[length + 3];
609 
610 	// count the individual glyphs
611 	int32 numChars = UTF8ToLength(string);
612 
613 	// get the escapement of each glyph in font units
614 	float *escapementArray = new float[numChars];
615 	static escapement_delta delta = (escapement_delta){ 0.0, 0.0 };
616 	if (GetEscapements(string, numChars, delta, escapementArray) == B_OK) {
617 		truncate_string(string, mode, width, result, escapementArray, fSize,
618 			ellipsisWidth, length, numChars);
619 
620 		inOut->SetTo(result);
621 	}
622 
623 	delete[] escapementArray;
624 	delete[] result;
625 }
626 
627