xref: /haiku/src/servers/app/ServerFont.cpp (revision 89755088d790ff4fe36f8aa77dacb2bd15507108)
1 /*
2  * Copyright 2001-2008, 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,
501 			int32 numChars, float size)
502 		:
503 		fEscapements(escapements),
504 		fOffsets(offsets),
505 		fNumChars(numChars),
506 		fSize(size)
507 	{
508 	}
509 
510 	void Start() {}
511 	void Finish(double x, double y) {}
512 	void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y)
513 	{
514 		_Set(index, 0, 0);
515 	}
516 
517 	bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph,
518 		FontCacheEntry* entry, double x, double y)
519 	{
520 		return _Set(index, glyph->advance_x, glyph->advance_y);
521 	}
522 
523 private:
524 	inline bool _Set(int32 index, double x, double y)
525 	{
526 		if (index >= fNumChars)
527 			return false;
528 
529 		fEscapements[index].x = x / fSize;
530 		fEscapements[index].y = y / fSize;
531 		if (fOffsets) {
532 			// ToDo: According to the BeBook: "The offsetArray is applied by
533 			// the dynamic spacing in order to improve the relative position
534 			// of the character's width with relation to another character,
535 			// without altering the width." So this will probably depend on
536 			// the spacing mode.
537 			fOffsets[index].x = 0;
538 			fOffsets[index].y = 0;
539 		}
540 		return true;
541 	}
542 
543 	BPoint* fEscapements;
544 	BPoint* fOffsets;
545 	int32 fNumChars;
546  	float fSize;
547 };
548 
549 
550 status_t
551 ServerFont::GetEscapements(const char* string, int32 numBytes, int32 numChars,
552 	escapement_delta delta, BPoint escapementArray[],
553 	BPoint offsetArray[]) const
554 {
555 	if (!string || numBytes <= 0 || !escapementArray)
556 		return B_BAD_DATA;
557 
558 	bool kerning = true; // TODO make this a property?
559 
560 	BPointEscapementConsumer consumer(escapementArray, offsetArray, numChars,
561 		fSize);
562 	if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes,
563 		&delta, kerning, fSpacing))
564 		return B_OK;
565 
566 	return B_ERROR;
567 }
568 
569 
570 class WidthEscapementConsumer {
571 public:
572 	WidthEscapementConsumer(float* widths, int32 numChars, float size)
573 		:
574 		fWidths(widths),
575 		fNumChars(numChars),
576 		fSize(size)
577 	{
578 	}
579 
580 	void Start() {}
581 	void Finish(double x, double y) {}
582 	void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y)
583 	{
584 		fWidths[index] = 0.0;
585 	}
586 
587 	bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph,
588 		FontCacheEntry* entry, double x, double y)
589 	{
590 		if (index >= fNumChars)
591 			return false;
592 
593 		fWidths[index] = glyph->advance_x / fSize;
594 		return true;
595 	}
596 
597  private:
598 	float* fWidths;
599 	int32 fNumChars;
600 	float fSize;
601 };
602 
603 
604 
605 status_t
606 ServerFont::GetEscapements(const char* string, int32 numBytes, int32 numChars,
607 	escapement_delta delta, float widthArray[]) const
608 {
609 	if (!string || numBytes <= 0 || !widthArray)
610 		return B_BAD_DATA;
611 
612 	bool kerning = true; // TODO make this a property?
613 
614 	WidthEscapementConsumer consumer(widthArray, numChars, fSize);
615 	if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes,
616 		&delta, kerning, fSpacing))
617 		return B_OK;
618 	return B_ERROR;
619 }
620 
621 
622 class BoundingBoxConsumer {
623  public:
624 	BoundingBoxConsumer(Transformable& transform, BRect* rectArray,
625 			bool asString)
626 		: rectArray(rectArray)
627 		, stringBoundingBox(LONG_MAX, LONG_MAX, LONG_MIN, LONG_MIN)
628 		, fAsString(asString)
629 		, fCurves(fPathAdaptor)
630 		, fContour(fCurves)
631 		, fTransformedOutline(fCurves, transform)
632 		, fTransformedContourOutline(fContour, transform)
633 		, fTransform(transform)
634 	{
635 	}
636 
637 	void Start() {}
638 	void Finish(double x, double y) {}
639 	void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) {}
640 	bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph,
641 		FontCacheEntry* entry, double x, double y)
642 	{
643 		if (glyph->data_type != glyph_data_outline) {
644 			const agg::rect_i& r = glyph->bounds;
645 			if (fAsString) {
646 				if (rectArray) {
647 					rectArray[index].left = r.x1 + x;
648 					rectArray[index].top = r.y1 + y;
649 					rectArray[index].right = r.x2 + x + 1;
650 					rectArray[index].bottom = r.y2 + y + 1;
651 				} else {
652 					stringBoundingBox = stringBoundingBox
653 						| BRect(r.x1 + x, r.y1 + y,
654 							r.x2 + x + 1, r.y2 + y + 1);
655 				}
656 			} else {
657 				rectArray[index].left = r.x1;
658 				rectArray[index].top = r.y1;
659 				rectArray[index].right = r.x2 + 1;
660 				rectArray[index].bottom = r.y2 + 1;
661 			}
662 		} else {
663 			if (fAsString) {
664 				entry->InitAdaptors(glyph, x, y,
665 						fMonoAdaptor, fGray8Adaptor, fPathAdaptor);
666 			} else {
667 				entry->InitAdaptors(glyph, 0, 0,
668 						fMonoAdaptor, fGray8Adaptor, fPathAdaptor);
669 			}
670 			double left = 0.0;
671 			double top = 0.0;
672 			double right = -1.0;
673 			double bottom = -1.0;
674 			uint32 pathID[1];
675 			pathID[0] = 0;
676 			// TODO: use fContour if falseboldwidth is > 0
677 			agg::bounding_rect(fTransformedOutline, pathID, 0, 1,
678 				&left, &top, &right, &bottom);
679 
680 			if (rectArray) {
681 				rectArray[index] = BRect(left, top, right, bottom);
682 			} else {
683 				stringBoundingBox = stringBoundingBox
684 					| BRect(left, top, right, bottom);
685 			}
686 		}
687 		return true;
688 	}
689 
690 	BRect*								rectArray;
691 	BRect								stringBoundingBox;
692 
693  private:
694 	bool								fAsString;
695 	FontCacheEntry::GlyphPathAdapter	fPathAdaptor;
696 	FontCacheEntry::GlyphGray8Adapter	fGray8Adaptor;
697 	FontCacheEntry::GlyphMonoAdapter	fMonoAdaptor;
698 
699 	FontCacheEntry::CurveConverter		fCurves;
700 	FontCacheEntry::ContourConverter	fContour;
701 
702 	FontCacheEntry::TransformedOutline	fTransformedOutline;
703 	FontCacheEntry::TransformedContourOutline fTransformedContourOutline;
704 
705 	Transformable&						fTransform;
706 };
707 
708 
709 status_t
710 ServerFont::GetBoundingBoxes(const char* string, int32 numBytes,
711 	BRect rectArray[], bool stringEscapement, font_metric_mode mode,
712 	escapement_delta delta, bool asString)
713 {
714 	// TODO: The font_metric_mode is not used
715 	if (!string || numBytes <= 0 || !rectArray)
716 		return B_BAD_DATA;
717 
718 	bool kerning = true; // TODO make this a property?
719 
720 	Transformable transform(EmbeddedTransformation());
721 
722 	BoundingBoxConsumer consumer(transform, rectArray, asString);
723 	if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes,
724 		stringEscapement ? &delta : NULL, kerning, fSpacing))
725 		return B_OK;
726 	return B_ERROR;
727 }
728 
729 
730 status_t
731 ServerFont::GetBoundingBoxesForStrings(char *charArray[], int32 lengthArray[],
732 	int32 numStrings, BRect rectArray[], font_metric_mode mode,
733 	escapement_delta deltaArray[])
734 {
735 	// TODO: The font_metric_mode is never used
736 	if (!charArray || !lengthArray|| numStrings <= 0 || !rectArray || !deltaArray)
737 		return B_BAD_DATA;
738 
739 	bool kerning = true; // TODO make this a property?
740 
741 	Transformable transform(EmbeddedTransformation());
742 
743 	for (int32 i = 0; i < numStrings; i++) {
744 		int32 numBytes = lengthArray[i];
745 		const char* string = charArray[i];
746 		escapement_delta delta = deltaArray[i];
747 
748 		BoundingBoxConsumer consumer(transform, NULL, true);
749 		if (!GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes,
750 			&delta, kerning, fSpacing))
751 			return B_ERROR;
752 
753 		rectArray[i] = consumer.stringBoundingBox;
754 	}
755 
756 	return B_OK;
757 }
758 
759 
760 class StringWidthConsumer {
761  public:
762 	StringWidthConsumer() : width(0.0) {}
763 	void Start() {}
764 	void Finish(double x, double y) { width = x; }
765 	void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) {}
766 	bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph,
767 		FontCacheEntry* entry, double x, double y)
768 	{ return true; }
769 
770 	float width;
771 };
772 
773 
774 float
775 ServerFont::StringWidth(const char *string, int32 numBytes,
776 	const escapement_delta* deltaArray) const
777 {
778 	if (!string || numBytes <= 0)
779 		return 0.0;
780 
781 	bool kerning = true; // TODO make this a property?
782 
783 	StringWidthConsumer consumer;
784 	if (!GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes,
785 		deltaArray, kerning, fSpacing))
786 		return 0.0;
787 
788 	return consumer.width;
789 }
790 
791 
792 /*!
793 	\brief Returns a BRect which encloses the entire font
794 	\return A BRect which encloses the entire font
795 */
796 BRect
797 ServerFont::BoundingBox()
798 {
799 	// TODO: fBounds is nowhere calculated!
800 	return fBounds;
801 }
802 
803 
804 /*!
805 	\brief Obtains the height values for characters in the font in its current state
806 	\param fh pointer to a font_height object to receive the values for the font
807 */
808 void
809 ServerFont::GetHeight(font_height& height) const
810 {
811 	fStyle->GetHeight(fSize, height);
812 }
813 
814 
815 void
816 ServerFont::TruncateString(BString* inOut, uint32 mode, float width) const
817 {
818 	if (!inOut)
819 		return;
820 
821 	// the width of the "…" glyph
822 	float ellipsisWidth = StringWidth(B_UTF8_ELLIPSIS, 1);
823 	const char *string = inOut->String();
824 	int32 length = inOut->Length();
825 
826 	// temporary array to hold result
827 	char *result = new char[length + 3];
828 
829 	// count the individual glyphs
830 	int32 numChars = UTF8CountChars(string, -1);
831 
832 	// get the escapement of each glyph in font units
833 	float *escapementArray = new float[numChars];
834 	static escapement_delta delta = (escapement_delta){ 0.0, 0.0 };
835 	if (GetEscapements(string, length, numChars, delta, escapementArray)
836 			== B_OK) {
837 		truncate_string(string, mode, width, result, escapementArray, fSize,
838 			ellipsisWidth, length, numChars);
839 
840 		inOut->SetTo(result);
841 	}
842 
843 	delete[] escapementArray;
844 	delete[] result;
845 }
846 
847 
848 Transformable
849 ServerFont::EmbeddedTransformation() const
850 {
851 	// TODO: cache this?
852 	Transformable transform;
853 
854 	transform.ShearBy(B_ORIGIN, (90.0 - fShear) * PI / 180.0, 0.0);
855 	transform.RotateBy(B_ORIGIN, -fRotation * PI / 180.0);
856 
857 	return transform;
858 }
859 
860