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