xref: /haiku/src/servers/app/ServerFont.cpp (revision e81a954787e50e56a7f06f72705b7859b6ab06d1)
1 /*
2  * Copyright 2001-2016, 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  *		Stephan Aßmus <superstippi@gmx.de>
10  */
11 
12 
13 #include "ServerFont.h"
14 
15 #include "Angle.h"
16 #include "GlyphLayoutEngine.h"
17 #include "FontManager.h"
18 #include "truncate_string.h"
19 #include "utf8_functions.h"
20 
21 #include FT_FREETYPE_H
22 #include FT_GLYPH_H
23 #include FT_OUTLINE_H
24 
25 #ifdef FONTCONFIG_ENABLED
26 
27 #include <fontconfig.h>
28 #include <fcfreetype.h>
29 
30 #endif // FONTCONFIG_ENABLED
31 
32 #include <Shape.h>
33 #include <String.h>
34 #include <UnicodeBlockObjects.h>
35 #include <UTF8.h>
36 
37 #include <agg_bounding_rect.h>
38 
39 #include <stdio.h>
40 #include <string.h>
41 
42 
43 // functions needed to convert a freetype vector graphics to a BShape
44 inline BPoint
45 VectorToPoint(const FT_Vector *vector)
46 {
47 	BPoint result;
48 	result.x = float(vector->x) / 64;
49 	result.y = -float(vector->y) / 64;
50 	return result;
51 }
52 
53 
54 int
55 MoveToFunc(const FT_Vector *to, void *user)
56 {
57 	((BShape *)user)->MoveTo(VectorToPoint(to));
58 	return 0;
59 }
60 
61 
62 int
63 LineToFunc(const FT_Vector *to, void *user)
64 {
65 	((BShape *)user)->LineTo(VectorToPoint(to));
66 	return 0;
67 }
68 
69 
70 int
71 ConicToFunc(const FT_Vector *control, const FT_Vector *to, void *user)
72 {
73 	BPoint controls[3];
74 
75 	controls[0] = VectorToPoint(control);
76 	controls[1] = VectorToPoint(to);
77 	controls[2] = controls[1];
78 
79 	((BShape *)user)->BezierTo(controls);
80 	return 0;
81 }
82 
83 
84 int
85 CubicToFunc(const FT_Vector *control1, const FT_Vector *control2, const FT_Vector *to, void *user)
86 {
87 	BPoint controls[3];
88 
89 	controls[0] = VectorToPoint(control1);
90 	controls[1] = VectorToPoint(control2);
91 	controls[2] = VectorToPoint(to);
92 
93 	((BShape *)user)->BezierTo(controls);
94 	return 0;
95 }
96 
97 
98 inline bool
99 is_white_space(uint32 charCode)
100 {
101 	switch (charCode) {
102 		case 0x0009:	/* tab */
103 		case 0x000b:	/* vertical tab */
104 		case 0x000c:	/* form feed */
105 		case 0x0020:	/* space */
106 		case 0x00a0:	/* non breaking space */
107 		case 0x000a:	/* line feed */
108 		case 0x000d:	/* carriage return */
109 		case 0x2028:	/* line separator */
110 		case 0x2029:	/* paragraph separator */
111 			return true;
112 	}
113 
114 	return false;
115 }
116 
117 
118 //	#pragma mark -
119 
120 
121 /*!
122 	\brief Constructor
123 	\param style Style object to which the ServerFont belongs
124 	\param size Character size in points
125 	\param rotation Rotation in degrees
126 	\param shear Shear (slant) in degrees. 45 <= shear <= 135
127 	\param flags Style flags as defined in <Font.h>
128 	\param spacing String spacing flag as defined in <Font.h>
129 */
130 ServerFont::ServerFont(FontStyle& style, float size, float rotation,
131 		float shear, float falseBoldWidth, uint16 flags, uint8 spacing)
132 	:
133 	fStyle(&style),
134 	fSize(size),
135 	fRotation(rotation),
136 	fShear(shear),
137 	fFalseBoldWidth(falseBoldWidth),
138 	fBounds(0, 0, 0, 0),
139 	fFlags(flags),
140 	fSpacing(spacing),
141 	fDirection(style.Direction()),
142 	fFace(style.Face()),
143 	fEncoding(B_UNICODE_UTF8)
144 {
145 	fStyle->Acquire();
146 }
147 
148 
149 ServerFont::ServerFont()
150 	:
151 	fStyle(NULL)
152 {
153 	*this = *gFontManager->DefaultPlainFont();
154 }
155 
156 
157 /*!
158 	\brief Copy Constructor
159 	\param font ServerFont to copy
160 */
161 ServerFont::ServerFont(const ServerFont &font)
162 	:
163 	fStyle(NULL)
164 {
165 	*this = font;
166 }
167 
168 
169 /*!
170 	\brief Removes itself as a dependency of its owning style.
171 */
172 ServerFont::~ServerFont()
173 {
174 	fStyle->Release();
175 }
176 
177 
178 /*!
179 	\brief Returns a copy of the specified font
180 	\param The font to copy from.
181 	\return A copy of the specified font
182 */
183 ServerFont&
184 ServerFont::operator=(const ServerFont& font)
185 {
186 	fSize = font.fSize;
187 	fRotation = font.fRotation;
188 	fShear = font.fShear;
189 	fFalseBoldWidth = font.fFalseBoldWidth;
190 	fFlags = font.fFlags;
191 	fSpacing = font.fSpacing;
192 	fEncoding = font.fEncoding;
193 	fBounds = font.fBounds;
194 
195 	SetStyle(font.fStyle);
196 
197 	return *this;
198 }
199 
200 
201 bool
202 ServerFont::operator==(const ServerFont& other) const
203 {
204 	if (GetFamilyAndStyle() != other.GetFamilyAndStyle())
205 		return false;
206 
207 	return fSize == other.fSize && fRotation == other.fRotation
208 		&& fShear == other.fShear && fFalseBoldWidth == other.fFalseBoldWidth
209 		&& fFlags == other.fFlags && fSpacing == other.fSpacing
210 		&& fEncoding == other.fEncoding && fBounds == other.fBounds
211 		&& fDirection == other.fDirection && fFace == other.fFace;
212 }
213 
214 
215 /*!
216 	\brief Returns the number of strikes in the font
217 	\return The number of strikes in the font
218 */
219 int32
220 ServerFont::CountTuned()
221 {
222 	return fStyle->TunedCount();
223 }
224 
225 
226 /*!
227 	\brief Returns the file format of the font.
228 	\return Mostly B_TRUETYPE_WINDOWS :)
229 */
230 font_file_format
231 ServerFont::FileFormat()
232 {
233 	return fStyle->FileFormat();
234 }
235 
236 
237 const char*
238 ServerFont::Style() const
239 {
240 	return fStyle->Name();
241 }
242 
243 
244 const char*
245 ServerFont::Family() const
246 {
247 	return fStyle->Family()->Name();
248 }
249 
250 
251 void
252 ServerFont::SetStyle(FontStyle* style)
253 {
254 	if (style && style != fStyle) {
255 		// detach from old style
256 		if (fStyle != NULL)
257 			fStyle->Release();
258 
259 		// attach to new style
260 		fStyle = style;
261 
262 		fStyle->Acquire();
263 
264 		fFace = fStyle->Face();
265 		fDirection = fStyle->Direction();
266 	}
267 }
268 
269 
270 /*!
271 	\brief Sets the ServerFont instance to whatever font is specified
272 	This method will lock the font manager.
273 
274 	\param familyID ID number of the family to set
275 	\param styleID ID number of the style to set
276 	\return B_OK if successful, B_ERROR if not
277 */
278 status_t
279 ServerFont::SetFamilyAndStyle(uint16 familyID, uint16 styleID)
280 {
281 	FontStyle* style = NULL;
282 
283 	if (gFontManager->Lock()) {
284 		style = gFontManager->GetStyle(familyID, styleID);
285 		if (style != NULL)
286 			style->Acquire();
287 
288 		gFontManager->Unlock();
289 	}
290 
291 	if (style == NULL)
292 		return B_ERROR;
293 
294 	SetStyle(style);
295 	style->Release();
296 
297 	return B_OK;
298 }
299 
300 
301 /*!
302 	\brief Sets the ServerFont instance to whatever font is specified
303 	\param fontID the combination of family and style ID numbers
304 	\return B_OK if successful, B_ERROR if not
305 */
306 status_t
307 ServerFont::SetFamilyAndStyle(uint32 fontID)
308 {
309 	uint16 style = fontID & 0xFFFF;
310 	uint16 family = (fontID & 0xFFFF0000) >> 16;
311 
312 	return SetFamilyAndStyle(family, style);
313 }
314 
315 
316 status_t
317 ServerFont::SetFace(uint16 face)
318 {
319 	// TODO: This needs further investigation. The face variable is actually
320 	// flags, but some of them are not enforcable at the same time. Also don't
321 	// confuse the Be API "face" with the Freetype face, which is just an
322 	// index in case a single font file exports multiple font faces. The
323 	// FontStyle class takes care of mapping the font style name to the Be
324 	// API face flags in FontStyle::_TranslateStyleToFace().
325 
326 	FontStyle* style = NULL;
327 	uint16 familyID = FamilyID();
328 	if (gFontManager->Lock()) {
329 		int32 count = gFontManager->CountStyles(familyID);
330 		for (int32 i = 0; i < count; i++) {
331 			style = gFontManager->GetStyleByIndex(familyID, i);
332 			if (style == NULL)
333 				break;
334 			if (style->Face() == face) {
335 				style->Acquire();
336 				break;
337 			} else
338 				style = NULL;
339 		}
340 
341 		gFontManager->Unlock();
342 	}
343 
344 	if (!style)
345 		return B_ERROR;
346 
347 	SetStyle(style);
348 	style->Release();
349 
350 	return B_OK;
351 }
352 
353 
354 /*!
355 	\brief Gets the ID values for the ServerFont instance in one shot
356 	\return the combination of family and style ID numbers
357 */
358 uint32
359 ServerFont::GetFamilyAndStyle() const
360 {
361 	return (FamilyID() << 16) | StyleID();
362 }
363 
364 
365 FT_Face
366 ServerFont::GetTransformedFace(bool rotate, bool shear) const
367 {
368 	fStyle->Lock();
369 	FT_Face face = fStyle->FreeTypeFace();
370 	if (!face) {
371 		fStyle->Unlock();
372 		return NULL;
373 	}
374 
375 	FT_Set_Char_Size(face, 0, int32(fSize * 64), 72, 72);
376 
377 	if ((rotate && fRotation != 0) || (shear && fShear != 90)) {
378 		FT_Matrix rmatrix, smatrix;
379 
380 		Angle rotationAngle(fRotation);
381 		rmatrix.xx = (FT_Fixed)( rotationAngle.Cosine() * 0x10000);
382 		rmatrix.xy = (FT_Fixed)(-rotationAngle.Sine() * 0x10000);
383 		rmatrix.yx = (FT_Fixed)( rotationAngle.Sine() * 0x10000);
384 		rmatrix.yy = (FT_Fixed)( rotationAngle.Cosine() * 0x10000);
385 
386 		Angle shearAngle(fShear);
387 		smatrix.xx = (FT_Fixed)(0x10000);
388 		smatrix.xy = (FT_Fixed)(-shearAngle.Cosine() * 0x10000);
389 		smatrix.yx = (FT_Fixed)(0);
390 		smatrix.yy = (FT_Fixed)(0x10000);
391 
392 		// Multiply togheter and apply transform
393 		FT_Matrix_Multiply(&rmatrix, &smatrix);
394 		FT_Set_Transform(face, &smatrix, NULL);
395 	}
396 
397 	// fStyle will be unlocked in PutTransformedFace()
398 	return face;
399 }
400 
401 
402 void
403 ServerFont::PutTransformedFace(FT_Face face) const
404 {
405 	// Reset transformation
406 	FT_Set_Transform(face, NULL, NULL);
407 	fStyle->Unlock();
408 }
409 
410 
411 status_t
412 ServerFont::GetGlyphShapes(const char charArray[], int32 numChars,
413 	BShape* shapeArray[]) const
414 {
415 	if (!charArray || numChars <= 0 || !shapeArray)
416 		return B_BAD_DATA;
417 
418 	FT_Face face = GetTransformedFace(true, true);
419 	if (!face)
420 		return B_ERROR;
421 
422 	FT_Outline_Funcs funcs;
423 	funcs.move_to = MoveToFunc;
424 	funcs.line_to = LineToFunc;
425 	funcs.conic_to = ConicToFunc;
426 	funcs.cubic_to = CubicToFunc;
427 	funcs.shift = 0;
428 	funcs.delta = 0;
429 
430 	const char* string = charArray;
431 	for (int i = 0; i < numChars; i++) {
432 		shapeArray[i] = new (std::nothrow) BShape();
433 		if (shapeArray[i] == NULL) {
434 			PutTransformedFace(face);
435 			return B_NO_MEMORY;
436 		}
437 		FT_Load_Char(face, UTF8ToCharCode(&string), FT_LOAD_NO_BITMAP);
438 		FT_Outline outline = face->glyph->outline;
439 		FT_Outline_Decompose(&outline, &funcs, shapeArray[i]);
440 		shapeArray[i]->Close();
441 	}
442 
443 	PutTransformedFace(face);
444 	return B_OK;
445 }
446 
447 
448 #ifdef FONTCONFIG_ENABLED
449 
450 /*!
451 	\brief For a given codepoint, do a binary search of the defined unicode
452 	blocks to figure out which one contains the codepoint.
453 	\param codePoint is the point to find
454 	\param startGuess is the starting point for the binary search (default 0)
455 */
456 static
457 int32
458 FindBlockForCodepoint(uint32 codePoint, uint32 startGuess)
459 {
460 	uint32 min = 0;
461 	uint32 max = kNumUnicodeBlockRanges;
462 	uint32 guess = (max + min) / 2;
463 
464 	if (startGuess > 0)
465 		guess = startGuess;
466 
467 	if (codePoint > kUnicodeBlockMap[max-1].end)
468 		return -1;
469 
470 	while ((max >= min) && (guess < kNumUnicodeBlockRanges)) {
471 		uint32 start = kUnicodeBlockMap[guess].start;
472 		uint32 end = kUnicodeBlockMap[guess].end;
473 
474 		if (start <= codePoint && end >= codePoint)
475 			return guess;
476 
477 		if (end < codePoint) {
478 			min = guess + 1;
479 		} else {
480 			max = guess - 1;
481 		}
482 
483 		guess = (max + min) / 2;
484 	}
485 
486 	return -1;
487 }
488 
489 /*!
490 	\brief parses charmap from fontconfig.  See fontconfig docs for FcCharSetFirstPage
491 	and FcCharSetNextPage for details on format.
492 	\param charMap is a fontconfig character map
493 	\param baseCodePoint is the base codepoint returned by fontconfig
494 	\param blocksForMap is a unicode_block to store the bitmap of contained blocks
495 */
496 static
497 void
498 ParseFcMap(FcChar32 charMap[], FcChar32 baseCodePoint, unicode_block& blocksForMap)
499 {
500 	uint32 block = 0;
501 	const uint8 BITS_PER_BLOCK = 32;
502 	uint32 currentCodePoint = 0;
503 
504 	if (baseCodePoint > kUnicodeBlockMap[kNumUnicodeBlockRanges-1].end)
505 		return;
506 
507 	for (int i = 0; i < FC_CHARSET_MAP_SIZE; ++i) {
508 		FcChar32 curMapBlock = charMap[i];
509 		int32 rangeStart = -1;
510 		int32 startBlock = -1;
511 		int32 endBlock = -1;
512 		uint32 startPoint = 0;
513 
514 		currentCodePoint = baseCodePoint + block;
515 
516 		for (int bit = 0; bit < BITS_PER_BLOCK; ++bit) {
517 			if (curMapBlock == 0 && startBlock < 0)
518 				// if no more bits are set then short-circuit the loop
519 				break;
520 
521 			if ((curMapBlock & 0x1) != 0 && rangeStart < 0) {
522 				rangeStart = bit;
523 				startPoint = currentCodePoint + rangeStart;
524 				startBlock = FindBlockForCodepoint(startPoint, 0);
525 				if (startBlock >= 0) {
526 					blocksForMap = blocksForMap
527 						| kUnicodeBlockMap[startBlock].block;
528 				}
529 			} else if (rangeStart >= 0 && startBlock >= 0) {
530 					// when we find an empty bit, that's the end of the range
531 				uint32 endPoint = currentCodePoint + (bit - 1);
532 
533 				endBlock = FindBlockForCodepoint(endPoint,
534 					startBlock);
535 					// start the binary search at the block where we found the
536 					// start codepoint to ideally find the end in the same
537 					// block.
538 				++startBlock;
539 
540 				while (startBlock <= endBlock) {
541 					// if the starting codepoint is found in a different block
542 					// than the ending codepoint, we should add all the blocks
543 					// inbetween.
544 					blocksForMap = blocksForMap
545 						| kUnicodeBlockMap[startBlock].block;
546 					++startBlock;
547 				}
548 
549 				startBlock = -1;
550 				endBlock = -1;
551 				rangeStart = -1;
552 			}
553 
554 			curMapBlock >>= 1;
555 		}
556 
557 		if (rangeStart >= 0 && startBlock >= 0) {
558 				// if we hit the end of the block and had
559 				// found a start of the range then we
560 				// should end the range at the end of the block
561 			uint32 endPoint = currentCodePoint + BITS_PER_BLOCK - 1;
562 
563 			endBlock = FindBlockForCodepoint(endPoint,
564 				startBlock);
565 				// start the binary search at the block where we found the
566 				// start codepoint to ideally find the end in the same
567 				// block.
568 			++startBlock;
569 
570 			while (startBlock <= endBlock) {
571 				// if the starting codepoint is found in a different block
572 				// than the ending codepoint, we should add all the blocks
573 				// inbetween.
574 				blocksForMap = blocksForMap
575 					| kUnicodeBlockMap[startBlock].block;
576 				++startBlock;
577 			}
578 		}
579 
580 		block += BITS_PER_BLOCK;
581 	}
582 }
583 
584 #endif // FONTCONFIG_ENABLED
585 
586 
587 /*!
588 	\brief Gets a bitmap that indicates which Unicode blocks are in the font.
589 	\param unicode_block to store bitmap in
590 	\return B_OK; bitmap will be empty if something went wrong
591 */
592 status_t
593 ServerFont::GetUnicodeBlocks(unicode_block& blocksForFont)
594 {
595 	blocksForFont = unicode_block();
596 
597 #ifdef FONTCONFIG_ENABLED
598 	FT_Face face = GetTransformedFace(true, true);
599 	if (face == NULL)
600 		return B_ERROR;
601 
602 	FcCharSet *charSet = FcFreeTypeCharSet(face, NULL);
603 	if (charSet == NULL) {
604 		PutTransformedFace(face);
605 		return B_ERROR;
606 	}
607 
608 	FcChar32 charMap[FC_CHARSET_MAP_SIZE];
609 	FcChar32 next = 0;
610 	FcChar32 baseCodePoint = FcCharSetFirstPage(charSet, charMap, &next);
611 
612 	while ((baseCodePoint != FC_CHARSET_DONE) && (next != FC_CHARSET_DONE)) {
613 		ParseFcMap(charMap, baseCodePoint, blocksForFont);
614 		baseCodePoint = FcCharSetNextPage(charSet, charMap, &next);
615 	}
616 
617 	FcCharSetDestroy(charSet);
618 	PutTransformedFace(face);
619 #endif // FONTCONFIG_ENABLED
620 
621 	return B_OK;
622 }
623 
624 /*!
625 	\brief Checks if a unicode block specified by a start and end point is defined
626 	in the current font
627 	\param start of unicode block
628 	\param end of unicode block
629 	\param hasBlock boolean to store whether the font contains the specified block
630 	\return B_OK; hasBlock will be false if something goes wrong
631 */
632 status_t
633 ServerFont::IncludesUnicodeBlock(uint32 start, uint32 end, bool& hasBlock)
634 {
635 	hasBlock = false;
636 
637 #ifdef FONTCONFIG_ENABLED
638 	FT_Face face = GetTransformedFace(true, true);
639 	if (face == NULL)
640 		return B_ERROR;
641 
642 	FcCharSet *charSet = FcFreeTypeCharSet(face, NULL);
643 	if (charSet == NULL) {
644 		PutTransformedFace(face);
645 		return B_ERROR;
646 	}
647 
648 	uint32 curCodePoint = start;
649 
650 	while (curCodePoint <= end && hasBlock == false) {
651 		// loop through range; if any character in the range is in the charset
652 		// then the block is represented.
653 		if (FcCharSetHasChar(charSet, (FcChar32)curCodePoint) == FcTrue) {
654 			hasBlock = true;
655 			break;
656 		}
657 
658 		++curCodePoint;
659 	}
660 
661 	FcCharSetDestroy(charSet);
662 	PutTransformedFace(face);
663 #endif // FONTCONFIG_ENABLED
664 
665 	return B_OK;
666 }
667 
668 
669 class HasGlyphsConsumer {
670  public:
671 	HasGlyphsConsumer(bool* hasArray)
672 		: fHasArray(hasArray)
673 	{
674 	}
675 	bool NeedsVector() { return false; }
676 	void Start() {}
677 	void Finish(double x, double y) {}
678 	void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y)
679 	{
680 		fHasArray[index] = false;
681 	}
682 	bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph,
683 		FontCacheEntry* entry, double x, double y, double advanceX,
684 			double advanceY)
685 	{
686 		fHasArray[index] = glyph->glyph_index != 0;
687 		return true;
688 	}
689 
690  private:
691 	bool* fHasArray;
692 };
693 
694 
695 status_t
696 ServerFont::GetHasGlyphs(const char* string, int32 numBytes,
697 	bool* hasArray) const
698 {
699 	if (!string || numBytes <= 0 || !hasArray)
700 		return B_BAD_DATA;
701 
702 	HasGlyphsConsumer consumer(hasArray);
703 	if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes,
704 		NULL, fSpacing))
705 		return B_OK;
706 
707 	return B_ERROR;
708 }
709 
710 
711 class EdgesConsumer {
712  public:
713 	EdgesConsumer(edge_info* edges, float size)
714 		: fEdges(edges)
715 		, fSize(size)
716 	{
717 	}
718 	bool NeedsVector() { return false; }
719 	void Start() {}
720 	void Finish(double x, double y) {}
721 	void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y)
722 	{
723 		fEdges[index].left = 0.0;
724 		fEdges[index].right = 0.0;
725 	}
726 	bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph,
727 		FontCacheEntry* entry, double x, double y, double advanceX,
728 			double advanceY)
729 	{
730 		fEdges[index].left = glyph->inset_left / fSize;
731 		fEdges[index].right = glyph->inset_right / fSize;
732 		return true;
733 	}
734 
735  private:
736 	edge_info* fEdges;
737 	float fSize;
738 };
739 
740 
741 status_t
742 ServerFont::GetEdges(const char* string, int32 numBytes,
743 	edge_info* edges) const
744 {
745 	if (!string || numBytes <= 0 || !edges)
746 		return B_BAD_DATA;
747 
748 	EdgesConsumer consumer(edges, fSize);
749 	if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes,
750 		NULL, fSpacing)) {
751 		return B_OK;
752 	}
753 
754 	return B_ERROR;
755 
756 //	FT_Face face = GetTransformedFace(false, false);
757 //	if (!face)
758 //		return B_ERROR;
759 //
760 //	const char *string = charArray;
761 //	for (int i = 0; i < numChars; i++) {
762 //		FT_Load_Char(face, UTF8ToCharCode(&string), FT_LOAD_NO_BITMAP);
763 //		edgeArray[i].left = float(face->glyph->metrics.horiBearingX)
764 //			/ 64 / fSize;
765 //		edgeArray[i].right = float(face->glyph->metrics.horiBearingX
766 //			+ face->glyph->metrics.width - face->glyph->metrics.horiAdvance)
767 //			/ 64 / fSize;
768 //	}
769 //
770 //	PutTransformedFace(face);
771 //	return B_OK;
772 }
773 
774 
775 class BPointEscapementConsumer {
776 public:
777 	BPointEscapementConsumer(BPoint* escapements, BPoint* offsets,
778 			int32 numChars, float size)
779 		:
780 		fEscapements(escapements),
781 		fOffsets(offsets),
782 		fNumChars(numChars),
783 		fSize(size)
784 	{
785 	}
786 
787 	bool NeedsVector() { return false; }
788 	void Start() {}
789 	void Finish(double x, double y) {}
790 	void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y)
791 	{
792 		_Set(index, 0, 0);
793 	}
794 
795 	bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph,
796 		FontCacheEntry* entry, double x, double y, double advanceX,
797 			double advanceY)
798 	{
799 		return _Set(index, advanceX, advanceY);
800 	}
801 
802 private:
803 	inline bool _Set(int32 index, double x, double y)
804 	{
805 		if (index >= fNumChars)
806 			return false;
807 
808 		fEscapements[index].x = x / fSize;
809 		fEscapements[index].y = y / fSize;
810 		if (fOffsets) {
811 			// ToDo: According to the BeBook: "The offsetArray is applied by
812 			// the dynamic spacing in order to improve the relative position
813 			// of the character's width with relation to another character,
814 			// without altering the width." So this will probably depend on
815 			// the spacing mode.
816 			fOffsets[index].x = 0;
817 			fOffsets[index].y = 0;
818 		}
819 		return true;
820 	}
821 
822 	BPoint* fEscapements;
823 	BPoint* fOffsets;
824 	int32 fNumChars;
825  	float fSize;
826 };
827 
828 
829 status_t
830 ServerFont::GetEscapements(const char* string, int32 numBytes, int32 numChars,
831 	escapement_delta delta, BPoint escapementArray[],
832 	BPoint offsetArray[]) const
833 {
834 	if (!string || numBytes <= 0 || !escapementArray)
835 		return B_BAD_DATA;
836 
837 	BPointEscapementConsumer consumer(escapementArray, offsetArray, numChars,
838 		fSize);
839 	if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes,
840 		&delta, fSpacing)) {
841 		return B_OK;
842 	}
843 
844 	return B_ERROR;
845 }
846 
847 
848 class WidthEscapementConsumer {
849 public:
850 	WidthEscapementConsumer(float* widths, int32 numChars, float size)
851 		:
852 		fWidths(widths),
853 		fNumChars(numChars),
854 		fSize(size)
855 	{
856 	}
857 
858 	bool NeedsVector() { return false; }
859 	void Start() {}
860 	void Finish(double x, double y) {}
861 	void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y)
862 	{
863 		fWidths[index] = 0.0;
864 	}
865 
866 	bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph,
867 		FontCacheEntry* entry, double x, double y, double advanceX,
868 			double advanceY)
869 	{
870 		if (index >= fNumChars)
871 			return false;
872 
873 		fWidths[index] = advanceX / fSize;
874 		return true;
875 	}
876 
877  private:
878 	float* fWidths;
879 	int32 fNumChars;
880 	float fSize;
881 };
882 
883 
884 
885 status_t
886 ServerFont::GetEscapements(const char* string, int32 numBytes, int32 numChars,
887 	escapement_delta delta, float widthArray[]) const
888 {
889 	if (!string || numBytes <= 0 || !widthArray)
890 		return B_BAD_DATA;
891 
892 	WidthEscapementConsumer consumer(widthArray, numChars, fSize);
893 	if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes,
894 		&delta, fSpacing)) {
895 		return B_OK;
896 	}
897 	return B_ERROR;
898 }
899 
900 
901 class BoundingBoxConsumer {
902  public:
903 	BoundingBoxConsumer(Transformable& transform, BRect* rectArray,
904 			bool asString)
905 		: rectArray(rectArray)
906 		, stringBoundingBox(INT32_MAX, INT32_MAX, INT32_MIN, INT32_MIN)
907 		, fAsString(asString)
908 		, fCurves(fPathAdaptor)
909 		, fContour(fCurves)
910 		, fTransformedOutline(fCurves, transform)
911 		, fTransformedContourOutline(fContour, transform)
912 		, fTransform(transform)
913 	{
914 	}
915 
916 	bool NeedsVector() { return false; }
917 	void Start() {}
918 	void Finish(double x, double y) {}
919 	void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) {}
920 	bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph,
921 		FontCacheEntry* entry, double x, double y, double advanceX,
922 			double advanceY)
923 	{
924 		if (glyph->data_type != glyph_data_outline) {
925 			const agg::rect_i& r = glyph->bounds;
926 			if (fAsString) {
927 				if (rectArray) {
928 					rectArray[index].left = r.x1 + x;
929 					rectArray[index].top = r.y1 + y;
930 					rectArray[index].right = r.x2 + x + 1;
931 					rectArray[index].bottom = r.y2 + y + 1;
932 				} else {
933 					stringBoundingBox = stringBoundingBox
934 						| BRect(r.x1 + x, r.y1 + y,
935 							r.x2 + x + 1, r.y2 + y + 1);
936 				}
937 			} else {
938 				rectArray[index].left = r.x1;
939 				rectArray[index].top = r.y1;
940 				rectArray[index].right = r.x2 + 1;
941 				rectArray[index].bottom = r.y2 + 1;
942 			}
943 		} else {
944 			if (fAsString) {
945 				entry->InitAdaptors(glyph, x, y,
946 						fMonoAdaptor, fGray8Adaptor, fPathAdaptor);
947 			} else {
948 				entry->InitAdaptors(glyph, 0, 0,
949 						fMonoAdaptor, fGray8Adaptor, fPathAdaptor);
950 			}
951 			double left = 0.0;
952 			double top = 0.0;
953 			double right = -1.0;
954 			double bottom = -1.0;
955 			uint32 pathID[1];
956 			pathID[0] = 0;
957 			// TODO: use fContour if falseboldwidth is > 0
958 			agg::bounding_rect(fTransformedOutline, pathID, 0, 1,
959 				&left, &top, &right, &bottom);
960 
961 			if (rectArray) {
962 				rectArray[index] = BRect(left, top, right, bottom);
963 			} else {
964 				stringBoundingBox = stringBoundingBox
965 					| BRect(left, top, right, bottom);
966 			}
967 		}
968 		return true;
969 	}
970 
971 	BRect*								rectArray;
972 	BRect								stringBoundingBox;
973 
974  private:
975 	bool								fAsString;
976 	FontCacheEntry::GlyphPathAdapter	fPathAdaptor;
977 	FontCacheEntry::GlyphGray8Adapter	fGray8Adaptor;
978 	FontCacheEntry::GlyphMonoAdapter	fMonoAdaptor;
979 
980 	FontCacheEntry::CurveConverter		fCurves;
981 	FontCacheEntry::ContourConverter	fContour;
982 
983 	FontCacheEntry::TransformedOutline	fTransformedOutline;
984 	FontCacheEntry::TransformedContourOutline fTransformedContourOutline;
985 
986 	Transformable&						fTransform;
987 };
988 
989 
990 status_t
991 ServerFont::GetBoundingBoxes(const char* string, int32 numBytes,
992 	BRect rectArray[], bool stringEscapement, font_metric_mode mode,
993 	escapement_delta delta, bool asString)
994 {
995 	// TODO: The font_metric_mode is not used
996 	if (!string || numBytes <= 0 || !rectArray)
997 		return B_BAD_DATA;
998 
999 	Transformable transform(EmbeddedTransformation());
1000 
1001 	BoundingBoxConsumer consumer(transform, rectArray, asString);
1002 	if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes,
1003 		stringEscapement ? &delta : NULL, fSpacing)) {
1004 		return B_OK;
1005 	}
1006 	return B_ERROR;
1007 }
1008 
1009 
1010 status_t
1011 ServerFont::GetBoundingBoxesForStrings(char *charArray[], int32 lengthArray[],
1012 	int32 numStrings, BRect rectArray[], font_metric_mode mode,
1013 	escapement_delta deltaArray[])
1014 {
1015 	// TODO: The font_metric_mode is never used
1016 	if (!charArray || !lengthArray|| numStrings <= 0 || !rectArray || !deltaArray)
1017 		return B_BAD_DATA;
1018 
1019 	Transformable transform(EmbeddedTransformation());
1020 
1021 	for (int32 i = 0; i < numStrings; i++) {
1022 		int32 numBytes = lengthArray[i];
1023 		const char* string = charArray[i];
1024 		escapement_delta delta = deltaArray[i];
1025 
1026 		BoundingBoxConsumer consumer(transform, NULL, true);
1027 		if (!GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes,
1028 			&delta, fSpacing)) {
1029 			return B_ERROR;
1030 		}
1031 
1032 		rectArray[i] = consumer.stringBoundingBox;
1033 	}
1034 
1035 	return B_OK;
1036 }
1037 
1038 
1039 class StringWidthConsumer {
1040  public:
1041 	StringWidthConsumer() : width(0.0) {}
1042 	bool NeedsVector() { return false; }
1043 	void Start() {}
1044 	void Finish(double x, double y) { width = x; }
1045 	void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) {}
1046 	bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph,
1047 		FontCacheEntry* entry, double x, double y, double advanceX,
1048 			double advanceY)
1049 	{ return true; }
1050 
1051 	float width;
1052 };
1053 
1054 
1055 float
1056 ServerFont::StringWidth(const char *string, int32 numBytes,
1057 	const escapement_delta* deltaArray) const
1058 {
1059 	if (!string || numBytes <= 0)
1060 		return 0.0;
1061 
1062 	StringWidthConsumer consumer;
1063 	if (!GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes,
1064 			deltaArray, fSpacing)) {
1065 		return 0.0;
1066 	}
1067 
1068 	return consumer.width;
1069 }
1070 
1071 
1072 /*!
1073 	\brief Returns a BRect which encloses the entire font
1074 	\return A BRect which encloses the entire font
1075 */
1076 BRect
1077 ServerFont::BoundingBox()
1078 {
1079 	// TODO: fBounds is nowhere calculated!
1080 	return fBounds;
1081 }
1082 
1083 
1084 /*!
1085 	\brief Obtains the height values for characters in the font in its current state
1086 	\param fh pointer to a font_height object to receive the values for the font
1087 */
1088 void
1089 ServerFont::GetHeight(font_height& height) const
1090 {
1091 	fStyle->GetHeight(fSize, height);
1092 }
1093 
1094 
1095 void
1096 ServerFont::TruncateString(BString* inOut, uint32 mode, float width) const
1097 {
1098 	if (!inOut)
1099 		return;
1100 
1101 	// the width of the "…" glyph
1102 	float ellipsisWidth = StringWidth(B_UTF8_ELLIPSIS, strlen(B_UTF8_ELLIPSIS));
1103 
1104 	// count the individual glyphs
1105 	int32 numChars = inOut->CountChars();
1106 
1107 	// get the escapement of each glyph in font units
1108 	float* escapementArray = new (std::nothrow) float[numChars];
1109 	if (escapementArray == NULL)
1110 		return;
1111 
1112 	static escapement_delta delta = (escapement_delta){ 0.0, 0.0 };
1113 	if (GetEscapements(inOut->String(), inOut->Length(), numChars, delta,
1114 		escapementArray) == B_OK) {
1115 		truncate_string(*inOut, mode, width, escapementArray, fSize,
1116 			ellipsisWidth, numChars);
1117 	}
1118 
1119 	delete[] escapementArray;
1120 }
1121 
1122 
1123 Transformable
1124 ServerFont::EmbeddedTransformation() const
1125 {
1126 	// TODO: cache this?
1127 	Transformable transform;
1128 
1129 	transform.ShearBy(B_ORIGIN, (90.0 - fShear) * M_PI / 180.0, 0.0);
1130 	transform.RotateBy(B_ORIGIN, -fRotation * M_PI / 180.0);
1131 
1132 	return transform;
1133 }
1134 
1135