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