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