xref: /haiku/src/servers/app/ServerFont.cpp (revision 1214ef1b2100f2b3299fc9d8d6142e46f70a4c3f)
1 /*
2  * Copyright 2001-2007, 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 void
296 ServerFont::SetFace(uint32 face)
297 {
298 	// TODO: change font style as requested!
299 	fFace = face;
300 }
301 
302 
303 /*!
304 	\brief Gets the ID values for the ServerFont instance in one shot
305 	\return the combination of family and style ID numbers
306 */
307 uint32
308 ServerFont::GetFamilyAndStyle() const
309 {
310 	return (FamilyID() << 16) | StyleID();
311 }
312 
313 
314 FT_Face
315 ServerFont::GetTransformedFace(bool rotate, bool shear) const
316 {
317 	fStyle->Lock();
318 	FT_Face face = fStyle->FreeTypeFace();
319 	if (!face) {
320 		fStyle->Unlock();
321 		return NULL;
322 	}
323 
324 	FT_Set_Char_Size(face, 0, int32(fSize * 64), 72, 72);
325 
326 	if ((rotate && fRotation != 0) || (shear && fShear != 90)) {
327 		FT_Matrix rmatrix, smatrix;
328 
329 		Angle rotationAngle(fRotation);
330 		rmatrix.xx = (FT_Fixed)( rotationAngle.Cosine() * 0x10000);
331 		rmatrix.xy = (FT_Fixed)(-rotationAngle.Sine() * 0x10000);
332 		rmatrix.yx = (FT_Fixed)( rotationAngle.Sine() * 0x10000);
333 		rmatrix.yy = (FT_Fixed)( rotationAngle.Cosine() * 0x10000);
334 
335 		Angle shearAngle(fShear);
336 		smatrix.xx = (FT_Fixed)(0x10000);
337 		smatrix.xy = (FT_Fixed)(-shearAngle.Cosine() * 0x10000);
338 		smatrix.yx = (FT_Fixed)(0);
339 		smatrix.yy = (FT_Fixed)(0x10000);
340 
341 		// Multiply togheter and apply transform
342 		FT_Matrix_Multiply(&rmatrix, &smatrix);
343 		FT_Set_Transform(face, &smatrix, NULL);
344 	}
345 
346 	// fStyle will be unlocked in PutTransformedFace()
347 	return face;
348 }
349 
350 
351 void
352 ServerFont::PutTransformedFace(FT_Face face) const
353 {
354 	// Reset transformation
355 	FT_Set_Transform(face, NULL, NULL);
356 	fStyle->Unlock();
357 }
358 
359 
360 status_t
361 ServerFont::GetGlyphShapes(const char charArray[], int32 numChars,
362 	BShape *shapeArray[]) const
363 {
364 	if (!charArray || numChars <= 0 || !shapeArray)
365 		return B_BAD_DATA;
366 
367 	FT_Face face = GetTransformedFace(true, true);
368 	if (!face)
369 		return B_ERROR;
370 
371 	FT_Outline_Funcs funcs;
372 	funcs.move_to = MoveToFunc;
373 	funcs.line_to = LineToFunc;
374 	funcs.conic_to = ConicToFunc;
375 	funcs.cubic_to = CubicToFunc;
376 	funcs.shift = 0;
377 	funcs.delta = 0;
378 
379 	const char *string = charArray;
380 	for (int i = 0; i < numChars; i++) {
381 		shapeArray[i] = new BShape();
382 		FT_Load_Char(face, UTF8ToCharCode(&string), FT_LOAD_NO_BITMAP);
383 		FT_Outline outline = face->glyph->outline;
384 		FT_Outline_Decompose(&outline, &funcs, shapeArray[i]);
385 		shapeArray[i]->Close();
386 	}
387 
388 	PutTransformedFace(face);
389 	return B_OK;
390 }
391 
392 
393 class HasGlyphsConsumer {
394  public:
395 	HasGlyphsConsumer(bool* hasArray)
396 		: fHasArray(hasArray)
397 	{
398 	}
399 	void Start() {}
400 	void Finish(double x, double y) {}
401 	void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y)
402 	{
403 		fHasArray[index] = false;
404 	}
405 	bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph,
406 		FontCacheEntry* entry, double x, double y)
407 	{
408 		fHasArray[index] = glyph->glyph_index >= 0;
409 		return true;
410 	}
411 
412  private:
413 	bool* fHasArray;
414 };
415 
416 
417 status_t
418 ServerFont::GetHasGlyphs(const char* string, int32 numBytes,
419 	bool* hasArray) const
420 {
421 	if (!string || numBytes <= 0 || !hasArray)
422 		return B_BAD_DATA;
423 
424 	bool kerning = true; // TODO make this a property?
425 
426 	HasGlyphsConsumer consumer(hasArray);
427 	if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes,
428 		NULL, kerning, fSpacing))
429 		return B_OK;
430 
431 	return B_ERROR;
432 }
433 
434 
435 class EdgesConsumer {
436  public:
437 	EdgesConsumer(edge_info* edges, float size)
438 		: fEdges(edges)
439 		, fSize(size)
440 	{
441 	}
442 	void Start() {}
443 	void Finish(double x, double y) {}
444 	void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y)
445 	{
446 		fEdges[index].left = 0.0;
447 		fEdges[index].right = 0.0;
448 	}
449 	bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph,
450 		FontCacheEntry* entry, double x, double y)
451 	{
452 		fEdges[index].left = glyph->inset_left / fSize;
453 		fEdges[index].right = glyph->inset_right / fSize;
454 		return true;
455 	}
456 
457  private:
458 	edge_info* fEdges;
459 	float fSize;
460 };
461 
462 
463 status_t
464 ServerFont::GetEdges(const char* string, int32 numBytes,
465 	edge_info* edges) const
466 {
467 	if (!string || numBytes <= 0 || !edges)
468 		return B_BAD_DATA;
469 
470 	bool kerning = true; // TODO make this a property?
471 
472 	EdgesConsumer consumer(edges, fSize);
473 	if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes,
474 		NULL, kerning, fSpacing))
475 		return B_OK;
476 
477 	return B_ERROR;
478 
479 //	FT_Face face = GetTransformedFace(false, false);
480 //	if (!face)
481 //		return B_ERROR;
482 //
483 //	const char *string = charArray;
484 //	for (int i = 0; i < numChars; i++) {
485 //		FT_Load_Char(face, UTF8ToCharCode(&string), FT_LOAD_NO_BITMAP);
486 //		edgeArray[i].left = float(face->glyph->metrics.horiBearingX)
487 //			/ 64 / fSize;
488 //		edgeArray[i].right = float(face->glyph->metrics.horiBearingX
489 //			+ face->glyph->metrics.width - face->glyph->metrics.horiAdvance)
490 //			/ 64 / fSize;
491 //	}
492 //
493 //	PutTransformedFace(face);
494 //	return B_OK;
495 }
496 
497 
498 class BPointEscapementConsumer {
499  public:
500 	BPointEscapementConsumer(BPoint* escapements, BPoint* offsets, float size)
501 		: fEscapements(escapements)
502 		, fOffsets(offsets)
503 		, fSize(size)
504 	{
505 	}
506 	void Start() {}
507 	void Finish(double x, double y) {}
508 	void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y)
509 	{
510 		_Set(index, 0, 0);
511 	}
512 	bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph,
513 		FontCacheEntry* entry, double x, double y)
514 	{
515 		_Set(index, glyph->advance_x, glyph->advance_y);
516 		return true;
517 	}
518  private:
519 	inline void _Set(int32 index, double x, double y)
520 	{
521 		fEscapements[index].x = x / fSize;
522 		fEscapements[index].y = y / fSize;
523 		if (fOffsets) {
524 			// ToDo: According to the BeBook: "The offsetArray is applied by
525 			// the dynamic spacing in order to improve the relative position
526 			// of the character's width with relation to another character,
527 			// without altering the width." So this will probably depend on
528 			// the spacing mode.
529 			fOffsets[index].x = 0;
530 			fOffsets[index].y = 0;
531 		}
532 	}
533 
534 	BPoint* fEscapements;
535 	BPoint* fOffsets;
536  	float fSize;
537 };
538 
539 
540 status_t
541 ServerFont::GetEscapements(const char* string, int32 numBytes,
542 	escapement_delta delta, BPoint escapementArray[],
543 	BPoint offsetArray[]) const
544 {
545 	if (!string || numBytes <= 0 || !escapementArray)
546 		return B_BAD_DATA;
547 
548 	bool kerning = true; // TODO make this a property?
549 
550 	BPointEscapementConsumer consumer(escapementArray, offsetArray, fSize);
551 	if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes,
552 		&delta, kerning, fSpacing))
553 		return B_OK;
554 
555 	return B_ERROR;
556 }
557 
558 
559 class WidthEscapementConsumer {
560  public:
561 	WidthEscapementConsumer(float* widths, float size)
562 		: fWidths(widths)
563 		, fSize(size)
564 	{
565 	}
566 	void Start() {}
567 	void Finish(double x, double y) {}
568 	void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y)
569 	{
570 		fWidths[index] = 0.0;
571 	}
572 	bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph,
573 		FontCacheEntry* entry, double x, double y)
574 	{
575 		fWidths[index] = glyph->advance_x / fSize;
576 		return true;
577 	}
578 
579  private:
580 	float* fWidths;
581 	float fSize;
582 };
583 
584 
585 
586 status_t
587 ServerFont::GetEscapements(const char* string, int32 numBytes,
588 	escapement_delta delta, float widthArray[]) const
589 {
590 	if (!string || numBytes <= 0 || !widthArray)
591 		return B_BAD_DATA;
592 
593 	bool kerning = true; // TODO make this a property?
594 
595 	WidthEscapementConsumer consumer(widthArray, fSize);
596 	if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes,
597 		&delta, kerning, fSpacing))
598 		return B_OK;
599 	return B_ERROR;
600 }
601 
602 
603 class BoundingBoxConsumer {
604  public:
605 	BoundingBoxConsumer(Transformable& transform, BRect* rectArray,
606 			bool asString)
607 		: rectArray(rectArray)
608 		, stringBoundingBox(LONG_MAX, LONG_MAX, LONG_MIN, LONG_MIN)
609 		, fAsString(asString)
610 		, fCurves(fPathAdaptor)
611 		, fContour(fCurves)
612 		, fTransformedOutline(fCurves, transform)
613 		, fTransformedContourOutline(fContour, transform)
614 		, fTransform(transform)
615 	{
616 	}
617 
618 	void Start() {}
619 	void Finish(double x, double y) {}
620 	void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) {}
621 	bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph,
622 		FontCacheEntry* entry, double x, double y)
623 	{
624 		if (glyph->data_type != glyph_data_outline) {
625 			const agg::rect_i& r = glyph->bounds;
626 			if (fAsString) {
627 				if (rectArray) {
628 					rectArray[index].left = r.x1 + x;
629 					rectArray[index].top = r.y1 + y;
630 					rectArray[index].right = r.x2 + x + 1;
631 					rectArray[index].bottom = r.y2 + y + 1;
632 				} else {
633 					stringBoundingBox = stringBoundingBox
634 						| BRect(r.x1 + x, r.y1 + y,
635 							r.x2 + x + 1, r.y2 + y + 1);
636 				}
637 			} else {
638 				rectArray[index].left = r.x1;
639 				rectArray[index].top = r.y1;
640 				rectArray[index].right = r.x2 + 1;
641 				rectArray[index].bottom = r.y2 + 1;
642 			}
643 		} else {
644 			if (fAsString) {
645 				entry->InitAdaptors(glyph, x, y,
646 						fMonoAdaptor, fGray8Adaptor, fPathAdaptor);
647 			} else {
648 				entry->InitAdaptors(glyph, 0, 0,
649 						fMonoAdaptor, fGray8Adaptor, fPathAdaptor);
650 			}
651 			double left = 0.0;
652 			double top = 0.0;
653 			double right = -1.0;
654 			double bottom = -1.0;
655 			uint32 pathID[1];
656 			pathID[0] = 0;
657 			// TODO: use fContour if falseboldwidth is > 0
658 			agg::bounding_rect(fTransformedOutline, pathID, 0, 1,
659 				&left, &top, &right, &bottom);
660 
661 			if (rectArray) {
662 				rectArray[index] = BRect(left, top, right, bottom);
663 			} else {
664 				stringBoundingBox = stringBoundingBox
665 					| BRect(left, top, right, bottom);
666 			}
667 		}
668 		return true;
669 	}
670 
671 	BRect*								rectArray;
672 	BRect								stringBoundingBox;
673 
674  private:
675 	bool								fAsString;
676 	FontCacheEntry::GlyphPathAdapter	fPathAdaptor;
677 	FontCacheEntry::GlyphGray8Adapter	fGray8Adaptor;
678 	FontCacheEntry::GlyphMonoAdapter	fMonoAdaptor;
679 
680 	FontCacheEntry::CurveConverter		fCurves;
681 	FontCacheEntry::ContourConverter	fContour;
682 
683 	FontCacheEntry::TransformedOutline	fTransformedOutline;
684 	FontCacheEntry::TransformedContourOutline fTransformedContourOutline;
685 
686 	Transformable&						fTransform;
687 };
688 
689 
690 status_t
691 ServerFont::GetBoundingBoxes(const char* string, int32 numBytes,
692 	BRect rectArray[], bool stringEscapement, font_metric_mode mode,
693 	escapement_delta delta, bool asString)
694 {
695 	// TODO: The font_metric_mode is not used
696 	if (!string || numBytes <= 0 || !rectArray)
697 		return B_BAD_DATA;
698 
699 	bool kerning = true; // TODO make this a property?
700 
701 	Transformable transform(EmbeddedTransformation());
702 
703 	BoundingBoxConsumer consumer(transform, rectArray, asString);
704 	if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes,
705 		stringEscapement ? &delta : NULL, kerning, fSpacing))
706 		return B_OK;
707 	return B_ERROR;
708 }
709 
710 
711 status_t
712 ServerFont::GetBoundingBoxesForStrings(char *charArray[], int32 lengthArray[],
713 	int32 numStrings, BRect rectArray[], font_metric_mode mode,
714 	escapement_delta deltaArray[])
715 {
716 	// TODO: The font_metric_mode is never used
717 	if (!charArray || !lengthArray|| numStrings <= 0 || !rectArray || !deltaArray)
718 		return B_BAD_DATA;
719 
720 	bool kerning = true; // TODO make this a property?
721 
722 	Transformable transform(EmbeddedTransformation());
723 
724 	for (int32 i = 0; i < numStrings; i++) {
725 		int32 numBytes = lengthArray[i];
726 		const char* string = charArray[i];
727 		escapement_delta delta = deltaArray[i];
728 
729 		BoundingBoxConsumer consumer(transform, NULL, true);
730 		if (!GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes,
731 			&delta, kerning, fSpacing))
732 			return B_ERROR;
733 
734 		rectArray[i] = consumer.stringBoundingBox;
735 	}
736 
737 	return B_OK;
738 }
739 
740 
741 class StringWidthConsumer {
742  public:
743 	StringWidthConsumer() : width(0.0) {}
744 	void Start() {}
745 	void Finish(double x, double y) { width = x; }
746 	void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) {}
747 	bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph,
748 		FontCacheEntry* entry, double x, double y)
749 	{ return true; }
750 
751 	float width;
752 };
753 
754 
755 float
756 ServerFont::StringWidth(const char *string, int32 numBytes,
757 	const escapement_delta* deltaArray) const
758 {
759 	if (!string || numBytes <= 0)
760 		return 0.0;
761 
762 	bool kerning = true; // TODO make this a property?
763 
764 	StringWidthConsumer consumer;
765 	if (!GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes,
766 		deltaArray, kerning, fSpacing))
767 		return 0.0;
768 
769 	return consumer.width;
770 }
771 
772 
773 /*!
774 	\brief Returns a BRect which encloses the entire font
775 	\return A BRect which encloses the entire font
776 */
777 BRect
778 ServerFont::BoundingBox()
779 {
780 	// TODO: fBounds is nowhere calculated!
781 	return fBounds;
782 }
783 
784 
785 /*!
786 	\brief Obtains the height values for characters in the font in its current state
787 	\param fh pointer to a font_height object to receive the values for the font
788 */
789 void
790 ServerFont::GetHeight(font_height& height) const
791 {
792 	fStyle->GetHeight(fSize, height);
793 }
794 
795 
796 void
797 ServerFont::TruncateString(BString* inOut, uint32 mode, float width) const
798 {
799 	if (!inOut)
800 		return;
801 
802 	// the width of the "…" glyph
803 	float ellipsisWidth = StringWidth(B_UTF8_ELLIPSIS, 1);
804 	const char *string = inOut->String();
805 	int32 length = inOut->Length();
806 
807 	// temporary array to hold result
808 	char *result = new char[length + 3];
809 
810 	// count the individual glyphs
811 	int32 numChars = UTF8CountChars(string, -1);
812 
813 	// get the escapement of each glyph in font units
814 	float *escapementArray = new float[numChars];
815 	static escapement_delta delta = (escapement_delta){ 0.0, 0.0 };
816 	if (GetEscapements(string, length, delta, escapementArray) == B_OK) {
817 		truncate_string(string, mode, width, result, escapementArray, fSize,
818 			ellipsisWidth, length, numChars);
819 
820 		inOut->SetTo(result);
821 	}
822 
823 	delete[] escapementArray;
824 	delete[] result;
825 }
826 
827 
828 Transformable
829 ServerFont::EmbeddedTransformation() const
830 {
831 	// TODO: cache this?
832 	Transformable transform;
833 
834 	transform.ShearBy(B_ORIGIN, (90.0 - fShear) * PI / 180.0, 0.0);
835 	transform.RotateBy(B_ORIGIN, -fRotation * PI / 180.0);
836 
837 	return transform;
838 }
839 
840