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