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