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