xref: /haiku/src/servers/app/ServerFont.cpp (revision b46615c55ad2c8fe6de54412055a0713da3d610a)
1 /*
2  * Copyright 2001-2009, 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 #include <Shape.h>
26 #include <String.h>
27 #include <UTF8.h>
28 
29 #include <agg_bounding_rect.h>
30 
31 #include <stdio.h>
32 #include <string.h>
33 
34 // functions needed to convert a freetype vector graphics to a BShape
35 inline BPoint
36 VectorToPoint(const FT_Vector *vector)
37 {
38 	BPoint result;
39 	result.x = float(vector->x) / 64;
40 	result.y = -float(vector->y) / 64;
41 	return result;
42 }
43 
44 
45 int
46 MoveToFunc(const FT_Vector *to, void *user)
47 {
48 	((BShape *)user)->MoveTo(VectorToPoint(to));
49 	return 0;
50 }
51 
52 
53 int
54 LineToFunc(const FT_Vector *to, void *user)
55 {
56 	((BShape *)user)->LineTo(VectorToPoint(to));
57 	return 0;
58 }
59 
60 
61 int
62 ConicToFunc(const FT_Vector *control, const FT_Vector *to, void *user)
63 {
64 	BPoint controls[3];
65 
66 	controls[0] = VectorToPoint(control);
67 	controls[1] = VectorToPoint(to);
68 	controls[2] = controls[1];
69 
70 	((BShape *)user)->BezierTo(controls);
71 	return 0;
72 }
73 
74 
75 int
76 CubicToFunc(const FT_Vector *control1, const FT_Vector *control2, const FT_Vector *to, void *user)
77 {
78 	BPoint controls[3];
79 
80 	controls[0] = VectorToPoint(control1);
81 	controls[1] = VectorToPoint(control2);
82 	controls[2] = VectorToPoint(to);
83 
84 	((BShape *)user)->BezierTo(controls);
85 	return 0;
86 }
87 
88 
89 inline bool
90 is_white_space(uint32 charCode)
91 {
92 	switch (charCode) {
93 		case 0x0009:	/* tab */
94 		case 0x000b:	/* vertical tab */
95 		case 0x000c:	/* form feed */
96 		case 0x0020:	/* space */
97 		case 0x00a0:	/* non breaking space */
98 		case 0x000a:	/* line feed */
99 		case 0x000d:	/* carriage return */
100 		case 0x2028:	/* line separator */
101 		case 0x2029:	/* paragraph separator */
102 			return true;
103 	}
104 
105 	return false;
106 }
107 
108 
109 //	#pragma mark -
110 
111 
112 /*!
113 	\brief Constructor
114 	\param style Style object to which the ServerFont belongs
115 	\param size Character size in points
116 	\param rotation Rotation in degrees
117 	\param shear Shear (slant) in degrees. 45 <= shear <= 135
118 	\param flags Style flags as defined in <Font.h>
119 	\param spacing String spacing flag as defined in <Font.h>
120 */
121 ServerFont::ServerFont(FontStyle& style, float size,
122 					   float rotation, float shear, float falseBoldWidth,
123 					   uint16 flags, uint8 spacing)
124 	: fStyle(&style),
125 	  fSize(size),
126 	  fRotation(rotation),
127 	  fShear(shear),
128 	  fFalseBoldWidth(falseBoldWidth),
129 	  fBounds(0, 0, 0, 0),
130 	  fFlags(flags),
131 	  fSpacing(spacing),
132 	  fDirection(style.Direction()),
133 	  fFace(style.Face()),
134 	  fEncoding(B_UNICODE_UTF8)
135 {
136 	fStyle->Acquire();
137 }
138 
139 
140 ServerFont::ServerFont()
141 	:
142 	fStyle(NULL)
143 {
144 	*this = *gFontManager->DefaultPlainFont();
145 }
146 
147 
148 /*!
149 	\brief Copy Constructor
150 	\param font ServerFont to copy
151 */
152 ServerFont::ServerFont(const ServerFont &font)
153 	:
154 	fStyle(NULL)
155 {
156 	*this = font;
157 }
158 
159 
160 /*!
161 	\brief Removes itself as a dependency of its owning style.
162 */
163 ServerFont::~ServerFont()
164 {
165 	fStyle->Release();
166 }
167 
168 
169 /*!
170 	\brief Returns a copy of the specified font
171 	\param The font to copy from.
172 	\return A copy of the specified font
173 */
174 ServerFont&
175 ServerFont::operator=(const ServerFont& font)
176 {
177 	fSize = font.fSize;
178 	fRotation = font.fRotation;
179 	fShear = font.fShear;
180 	fFalseBoldWidth = font.fFalseBoldWidth;
181 	fFlags = font.fFlags;
182 	fSpacing = font.fSpacing;
183 	fEncoding = font.fEncoding;
184 	fBounds = font.fBounds;
185 
186 	SetStyle(font.fStyle);
187 
188 	return *this;
189 }
190 
191 
192 bool
193 ServerFont::operator==(const ServerFont& other) const
194 {
195 	if (GetFamilyAndStyle() != other.GetFamilyAndStyle())
196 		return false;
197 
198 	return fSize == other.fSize && fRotation == other.fRotation
199 		&& fShear == other.fShear && fFalseBoldWidth == other.fFalseBoldWidth
200 		&& fFlags == other.fFlags && fSpacing == other.fSpacing
201 		&& fEncoding == other.fEncoding && fBounds == other.fBounds
202 		&& fDirection == other.fDirection && fFace == other.fFace;
203 }
204 
205 
206 /*!
207 	\brief Returns the number of strikes in the font
208 	\return The number of strikes in the font
209 */
210 int32
211 ServerFont::CountTuned()
212 {
213 	return fStyle->TunedCount();
214 }
215 
216 
217 /*!
218 	\brief Returns the file format of the font.
219 	\return Mostly B_TRUETYPE_WINDOWS :)
220 */
221 font_file_format
222 ServerFont::FileFormat()
223 {
224 	return fStyle->FileFormat();
225 }
226 
227 
228 const char*
229 ServerFont::Style() const
230 {
231 	return fStyle->Name();
232 }
233 
234 
235 const char*
236 ServerFont::Family() const
237 {
238 	return fStyle->Family()->Name();
239 }
240 
241 
242 void
243 ServerFont::SetStyle(FontStyle* style)
244 {
245 	if (style && style != fStyle) {
246 		// detach from old style
247 		if (fStyle != NULL)
248 			fStyle->Release();
249 
250 		// attach to new style
251 		fStyle = style;
252 
253 		fStyle->Acquire();
254 
255 		fFace = fStyle->Face();
256 		fDirection = fStyle->Direction();
257 	}
258 }
259 
260 
261 /*!
262 	\brief Sets the ServerFont instance to whatever font is specified
263 	This method will lock the font manager.
264 
265 	\param familyID ID number of the family to set
266 	\param styleID ID number of the style to set
267 	\return B_OK if successful, B_ERROR if not
268 */
269 status_t
270 ServerFont::SetFamilyAndStyle(uint16 familyID, uint16 styleID)
271 {
272 	FontStyle* style = NULL;
273 
274 	if (gFontManager->Lock()) {
275 		style = gFontManager->GetStyle(familyID, styleID);
276 		if (style != NULL)
277 			style->Acquire();
278 
279 		gFontManager->Unlock();
280 	}
281 
282 	if (style == NULL)
283 		return B_ERROR;
284 
285 	SetStyle(style);
286 	style->Release();
287 
288 	return B_OK;
289 }
290 
291 
292 /*!
293 	\brief Sets the ServerFont instance to whatever font is specified
294 	\param fontID the combination of family and style ID numbers
295 	\return B_OK if successful, B_ERROR if not
296 */
297 status_t
298 ServerFont::SetFamilyAndStyle(uint32 fontID)
299 {
300 	uint16 style = fontID & 0xFFFF;
301 	uint16 family = (fontID & 0xFFFF0000) >> 16;
302 
303 	return SetFamilyAndStyle(family, style);
304 }
305 
306 
307 status_t
308 ServerFont::SetFace(uint16 face)
309 {
310 	// TODO: This needs further investigation. The face variable is actually
311 	// flags, but some of them are not enforcable at the same time. Also don't
312 	// confuse the Be API "face" with the Freetype face, which is just an
313 	// index in case a single font file exports multiple font faces. The
314 	// FontStyle class takes care of mapping the font style name to the Be
315 	// API face flags in FontStyle::_TranslateStyleToFace().
316 
317 	FontStyle* style = NULL;
318 	uint16 familyID = FamilyID();
319 	if (gFontManager->Lock()) {
320 		int32 count = gFontManager->CountStyles(familyID);
321 		for (int32 i = 0; i < count; i++) {
322 			style = gFontManager->GetStyleByIndex(familyID, i);
323 			if (style == NULL)
324 				break;
325 			if (style->Face() == face) {
326 				style->Acquire();
327 				break;
328 			} else
329 				style = NULL;
330 		}
331 
332 		gFontManager->Unlock();
333 	}
334 
335 	if (!style)
336 		return B_ERROR;
337 
338 	SetStyle(style);
339 	style->Release();
340 
341 	return B_OK;
342 }
343 
344 
345 /*!
346 	\brief Gets the ID values for the ServerFont instance in one shot
347 	\return the combination of family and style ID numbers
348 */
349 uint32
350 ServerFont::GetFamilyAndStyle() const
351 {
352 	return (FamilyID() << 16) | StyleID();
353 }
354 
355 
356 FT_Face
357 ServerFont::GetTransformedFace(bool rotate, bool shear) const
358 {
359 	fStyle->Lock();
360 	FT_Face face = fStyle->FreeTypeFace();
361 	if (!face) {
362 		fStyle->Unlock();
363 		return NULL;
364 	}
365 
366 	FT_Set_Char_Size(face, 0, int32(fSize * 64), 72, 72);
367 
368 	if ((rotate && fRotation != 0) || (shear && fShear != 90)) {
369 		FT_Matrix rmatrix, smatrix;
370 
371 		Angle rotationAngle(fRotation);
372 		rmatrix.xx = (FT_Fixed)( rotationAngle.Cosine() * 0x10000);
373 		rmatrix.xy = (FT_Fixed)(-rotationAngle.Sine() * 0x10000);
374 		rmatrix.yx = (FT_Fixed)( rotationAngle.Sine() * 0x10000);
375 		rmatrix.yy = (FT_Fixed)( rotationAngle.Cosine() * 0x10000);
376 
377 		Angle shearAngle(fShear);
378 		smatrix.xx = (FT_Fixed)(0x10000);
379 		smatrix.xy = (FT_Fixed)(-shearAngle.Cosine() * 0x10000);
380 		smatrix.yx = (FT_Fixed)(0);
381 		smatrix.yy = (FT_Fixed)(0x10000);
382 
383 		// Multiply togheter and apply transform
384 		FT_Matrix_Multiply(&rmatrix, &smatrix);
385 		FT_Set_Transform(face, &smatrix, NULL);
386 	}
387 
388 	// fStyle will be unlocked in PutTransformedFace()
389 	return face;
390 }
391 
392 
393 void
394 ServerFont::PutTransformedFace(FT_Face face) const
395 {
396 	// Reset transformation
397 	FT_Set_Transform(face, NULL, NULL);
398 	fStyle->Unlock();
399 }
400 
401 
402 status_t
403 ServerFont::GetGlyphShapes(const char charArray[], int32 numChars,
404 	BShape *shapeArray[]) const
405 {
406 	if (!charArray || numChars <= 0 || !shapeArray)
407 		return B_BAD_DATA;
408 
409 	FT_Face face = GetTransformedFace(true, true);
410 	if (!face)
411 		return B_ERROR;
412 
413 	FT_Outline_Funcs funcs;
414 	funcs.move_to = MoveToFunc;
415 	funcs.line_to = LineToFunc;
416 	funcs.conic_to = ConicToFunc;
417 	funcs.cubic_to = CubicToFunc;
418 	funcs.shift = 0;
419 	funcs.delta = 0;
420 
421 	const char *string = charArray;
422 	for (int i = 0; i < numChars; i++) {
423 		shapeArray[i] = new BShape();
424 		FT_Load_Char(face, UTF8ToCharCode(&string), FT_LOAD_NO_BITMAP);
425 		FT_Outline outline = face->glyph->outline;
426 		FT_Outline_Decompose(&outline, &funcs, shapeArray[i]);
427 		shapeArray[i]->Close();
428 	}
429 
430 	PutTransformedFace(face);
431 	return B_OK;
432 }
433 
434 
435 class HasGlyphsConsumer {
436  public:
437 	HasGlyphsConsumer(bool* hasArray)
438 		: fHasArray(hasArray)
439 	{
440 	}
441 	void Start() {}
442 	void Finish(double x, double y) {}
443 	void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y)
444 	{
445 		fHasArray[index] = false;
446 	}
447 	bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph,
448 		FontCacheEntry* entry, double x, double y)
449 	{
450 		fHasArray[index] = glyph->glyph_index != 0;
451 		return true;
452 	}
453 
454  private:
455 	bool* fHasArray;
456 };
457 
458 
459 status_t
460 ServerFont::GetHasGlyphs(const char* string, int32 numBytes,
461 	bool* hasArray) const
462 {
463 	if (!string || numBytes <= 0 || !hasArray)
464 		return B_BAD_DATA;
465 
466 	bool kerning = true; // TODO make this a property?
467 
468 	HasGlyphsConsumer consumer(hasArray);
469 	if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes,
470 		NULL, kerning, fSpacing))
471 		return B_OK;
472 
473 	return B_ERROR;
474 }
475 
476 
477 class EdgesConsumer {
478  public:
479 	EdgesConsumer(edge_info* edges, float size)
480 		: fEdges(edges)
481 		, fSize(size)
482 	{
483 	}
484 	void Start() {}
485 	void Finish(double x, double y) {}
486 	void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y)
487 	{
488 		fEdges[index].left = 0.0;
489 		fEdges[index].right = 0.0;
490 	}
491 	bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph,
492 		FontCacheEntry* entry, double x, double y)
493 	{
494 		fEdges[index].left = glyph->inset_left / fSize;
495 		fEdges[index].right = glyph->inset_right / fSize;
496 		return true;
497 	}
498 
499  private:
500 	edge_info* fEdges;
501 	float fSize;
502 };
503 
504 
505 status_t
506 ServerFont::GetEdges(const char* string, int32 numBytes,
507 	edge_info* edges) const
508 {
509 	if (!string || numBytes <= 0 || !edges)
510 		return B_BAD_DATA;
511 
512 	bool kerning = true; // TODO make this a property?
513 
514 	EdgesConsumer consumer(edges, fSize);
515 	if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes,
516 		NULL, kerning, fSpacing))
517 		return B_OK;
518 
519 	return B_ERROR;
520 
521 //	FT_Face face = GetTransformedFace(false, false);
522 //	if (!face)
523 //		return B_ERROR;
524 //
525 //	const char *string = charArray;
526 //	for (int i = 0; i < numChars; i++) {
527 //		FT_Load_Char(face, UTF8ToCharCode(&string), FT_LOAD_NO_BITMAP);
528 //		edgeArray[i].left = float(face->glyph->metrics.horiBearingX)
529 //			/ 64 / fSize;
530 //		edgeArray[i].right = float(face->glyph->metrics.horiBearingX
531 //			+ face->glyph->metrics.width - face->glyph->metrics.horiAdvance)
532 //			/ 64 / fSize;
533 //	}
534 //
535 //	PutTransformedFace(face);
536 //	return B_OK;
537 }
538 
539 
540 class BPointEscapementConsumer {
541 public:
542 	BPointEscapementConsumer(BPoint* escapements, BPoint* offsets,
543 			int32 numChars, float size)
544 		:
545 		fEscapements(escapements),
546 		fOffsets(offsets),
547 		fNumChars(numChars),
548 		fSize(size)
549 	{
550 	}
551 
552 	void Start() {}
553 	void Finish(double x, double y) {}
554 	void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y)
555 	{
556 		_Set(index, 0, 0);
557 	}
558 
559 	bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph,
560 		FontCacheEntry* entry, double x, double y)
561 	{
562 		return _Set(index, glyph->advance_x, glyph->advance_y);
563 	}
564 
565 private:
566 	inline bool _Set(int32 index, double x, double y)
567 	{
568 		if (index >= fNumChars)
569 			return false;
570 
571 		fEscapements[index].x = x / fSize;
572 		fEscapements[index].y = y / fSize;
573 		if (fOffsets) {
574 			// ToDo: According to the BeBook: "The offsetArray is applied by
575 			// the dynamic spacing in order to improve the relative position
576 			// of the character's width with relation to another character,
577 			// without altering the width." So this will probably depend on
578 			// the spacing mode.
579 			fOffsets[index].x = 0;
580 			fOffsets[index].y = 0;
581 		}
582 		return true;
583 	}
584 
585 	BPoint* fEscapements;
586 	BPoint* fOffsets;
587 	int32 fNumChars;
588  	float fSize;
589 };
590 
591 
592 status_t
593 ServerFont::GetEscapements(const char* string, int32 numBytes, int32 numChars,
594 	escapement_delta delta, BPoint escapementArray[],
595 	BPoint offsetArray[]) const
596 {
597 	if (!string || numBytes <= 0 || !escapementArray)
598 		return B_BAD_DATA;
599 
600 	bool kerning = true; // TODO make this a property?
601 
602 	BPointEscapementConsumer consumer(escapementArray, offsetArray, numChars,
603 		fSize);
604 	if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes,
605 		&delta, kerning, fSpacing))
606 		return B_OK;
607 
608 	return B_ERROR;
609 }
610 
611 
612 class WidthEscapementConsumer {
613 public:
614 	WidthEscapementConsumer(float* widths, int32 numChars, float size)
615 		:
616 		fWidths(widths),
617 		fNumChars(numChars),
618 		fSize(size)
619 	{
620 	}
621 
622 	void Start() {}
623 	void Finish(double x, double y) {}
624 	void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y)
625 	{
626 		fWidths[index] = 0.0;
627 	}
628 
629 	bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph,
630 		FontCacheEntry* entry, double x, double y)
631 	{
632 		if (index >= fNumChars)
633 			return false;
634 
635 		fWidths[index] = glyph->advance_x / fSize;
636 		return true;
637 	}
638 
639  private:
640 	float* fWidths;
641 	int32 fNumChars;
642 	float fSize;
643 };
644 
645 
646 
647 status_t
648 ServerFont::GetEscapements(const char* string, int32 numBytes, int32 numChars,
649 	escapement_delta delta, float widthArray[]) const
650 {
651 	if (!string || numBytes <= 0 || !widthArray)
652 		return B_BAD_DATA;
653 
654 	bool kerning = true; // TODO make this a property?
655 
656 	WidthEscapementConsumer consumer(widthArray, numChars, fSize);
657 	if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes,
658 		&delta, kerning, fSpacing))
659 		return B_OK;
660 	return B_ERROR;
661 }
662 
663 
664 class BoundingBoxConsumer {
665  public:
666 	BoundingBoxConsumer(Transformable& transform, BRect* rectArray,
667 			bool asString)
668 		: rectArray(rectArray)
669 		, stringBoundingBox(LONG_MAX, LONG_MAX, LONG_MIN, LONG_MIN)
670 		, fAsString(asString)
671 		, fCurves(fPathAdaptor)
672 		, fContour(fCurves)
673 		, fTransformedOutline(fCurves, transform)
674 		, fTransformedContourOutline(fContour, transform)
675 		, fTransform(transform)
676 	{
677 	}
678 
679 	void Start() {}
680 	void Finish(double x, double y) {}
681 	void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) {}
682 	bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph,
683 		FontCacheEntry* entry, double x, double y)
684 	{
685 		if (glyph->data_type != glyph_data_outline) {
686 			const agg::rect_i& r = glyph->bounds;
687 			if (fAsString) {
688 				if (rectArray) {
689 					rectArray[index].left = r.x1 + x;
690 					rectArray[index].top = r.y1 + y;
691 					rectArray[index].right = r.x2 + x + 1;
692 					rectArray[index].bottom = r.y2 + y + 1;
693 				} else {
694 					stringBoundingBox = stringBoundingBox
695 						| BRect(r.x1 + x, r.y1 + y,
696 							r.x2 + x + 1, r.y2 + y + 1);
697 				}
698 			} else {
699 				rectArray[index].left = r.x1;
700 				rectArray[index].top = r.y1;
701 				rectArray[index].right = r.x2 + 1;
702 				rectArray[index].bottom = r.y2 + 1;
703 			}
704 		} else {
705 			if (fAsString) {
706 				entry->InitAdaptors(glyph, x, y,
707 						fMonoAdaptor, fGray8Adaptor, fPathAdaptor);
708 			} else {
709 				entry->InitAdaptors(glyph, 0, 0,
710 						fMonoAdaptor, fGray8Adaptor, fPathAdaptor);
711 			}
712 			double left = 0.0;
713 			double top = 0.0;
714 			double right = -1.0;
715 			double bottom = -1.0;
716 			uint32 pathID[1];
717 			pathID[0] = 0;
718 			// TODO: use fContour if falseboldwidth is > 0
719 			agg::bounding_rect(fTransformedOutline, pathID, 0, 1,
720 				&left, &top, &right, &bottom);
721 
722 			if (rectArray) {
723 				rectArray[index] = BRect(left, top, right, bottom);
724 			} else {
725 				stringBoundingBox = stringBoundingBox
726 					| BRect(left, top, right, bottom);
727 			}
728 		}
729 		return true;
730 	}
731 
732 	BRect*								rectArray;
733 	BRect								stringBoundingBox;
734 
735  private:
736 	bool								fAsString;
737 	FontCacheEntry::GlyphPathAdapter	fPathAdaptor;
738 	FontCacheEntry::GlyphGray8Adapter	fGray8Adaptor;
739 	FontCacheEntry::GlyphMonoAdapter	fMonoAdaptor;
740 
741 	FontCacheEntry::CurveConverter		fCurves;
742 	FontCacheEntry::ContourConverter	fContour;
743 
744 	FontCacheEntry::TransformedOutline	fTransformedOutline;
745 	FontCacheEntry::TransformedContourOutline fTransformedContourOutline;
746 
747 	Transformable&						fTransform;
748 };
749 
750 
751 status_t
752 ServerFont::GetBoundingBoxes(const char* string, int32 numBytes,
753 	BRect rectArray[], bool stringEscapement, font_metric_mode mode,
754 	escapement_delta delta, bool asString)
755 {
756 	// TODO: The font_metric_mode is not used
757 	if (!string || numBytes <= 0 || !rectArray)
758 		return B_BAD_DATA;
759 
760 	bool kerning = true; // TODO make this a property?
761 
762 	Transformable transform(EmbeddedTransformation());
763 
764 	BoundingBoxConsumer consumer(transform, rectArray, asString);
765 	if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes,
766 		stringEscapement ? &delta : NULL, kerning, fSpacing))
767 		return B_OK;
768 	return B_ERROR;
769 }
770 
771 
772 status_t
773 ServerFont::GetBoundingBoxesForStrings(char *charArray[], int32 lengthArray[],
774 	int32 numStrings, BRect rectArray[], font_metric_mode mode,
775 	escapement_delta deltaArray[])
776 {
777 	// TODO: The font_metric_mode is never used
778 	if (!charArray || !lengthArray|| numStrings <= 0 || !rectArray || !deltaArray)
779 		return B_BAD_DATA;
780 
781 	bool kerning = true; // TODO make this a property?
782 
783 	Transformable transform(EmbeddedTransformation());
784 
785 	for (int32 i = 0; i < numStrings; i++) {
786 		int32 numBytes = lengthArray[i];
787 		const char* string = charArray[i];
788 		escapement_delta delta = deltaArray[i];
789 
790 		BoundingBoxConsumer consumer(transform, NULL, true);
791 		if (!GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes,
792 			&delta, kerning, fSpacing))
793 			return B_ERROR;
794 
795 		rectArray[i] = consumer.stringBoundingBox;
796 	}
797 
798 	return B_OK;
799 }
800 
801 
802 class StringWidthConsumer {
803  public:
804 	StringWidthConsumer() : width(0.0) {}
805 	void Start() {}
806 	void Finish(double x, double y) { width = x; }
807 	void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) {}
808 	bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph,
809 		FontCacheEntry* entry, double x, double y)
810 	{ return true; }
811 
812 	float width;
813 };
814 
815 
816 float
817 ServerFont::StringWidth(const char *string, int32 numBytes,
818 	const escapement_delta* deltaArray) const
819 {
820 	if (!string || numBytes <= 0)
821 		return 0.0;
822 
823 	bool kerning = true; // TODO make this a property?
824 
825 	StringWidthConsumer consumer;
826 	if (!GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes,
827 			deltaArray, kerning, fSpacing))
828 		return 0.0;
829 
830 	return consumer.width;
831 }
832 
833 
834 /*!
835 	\brief Returns a BRect which encloses the entire font
836 	\return A BRect which encloses the entire font
837 */
838 BRect
839 ServerFont::BoundingBox()
840 {
841 	// TODO: fBounds is nowhere calculated!
842 	return fBounds;
843 }
844 
845 
846 /*!
847 	\brief Obtains the height values for characters in the font in its current state
848 	\param fh pointer to a font_height object to receive the values for the font
849 */
850 void
851 ServerFont::GetHeight(font_height& height) const
852 {
853 	fStyle->GetHeight(fSize, height);
854 }
855 
856 
857 void
858 ServerFont::TruncateString(BString* inOut, uint32 mode, float width) const
859 {
860 	if (!inOut)
861 		return;
862 
863 	// the width of the "…" glyph
864 	float ellipsisWidth = StringWidth(B_UTF8_ELLIPSIS, strlen(B_UTF8_ELLIPSIS));
865 
866 	// count the individual glyphs
867 	int32 numChars = inOut->CountChars();
868 
869 	// get the escapement of each glyph in font units
870 	float* escapementArray = new float[numChars];
871 	static escapement_delta delta = (escapement_delta){ 0.0, 0.0 };
872 	if (GetEscapements(inOut->String(), inOut->Length(), numChars, delta,
873 		escapementArray) == B_OK) {
874 		truncate_string(*inOut, mode, width, escapementArray, fSize,
875 			ellipsisWidth, numChars);
876 	}
877 
878 	delete[] escapementArray;
879 }
880 
881 
882 Transformable
883 ServerFont::EmbeddedTransformation() const
884 {
885 	// TODO: cache this?
886 	Transformable transform;
887 
888 	transform.ShearBy(B_ORIGIN, (90.0 - fShear) * M_PI / 180.0, 0.0);
889 	transform.RotateBy(B_ORIGIN, -fRotation * M_PI / 180.0);
890 
891 	return transform;
892 }
893 
894