xref: /haiku/src/servers/app/ServerFont.cpp (revision b8a45b3a2df2379b4301bf3bd5949b9a105be4ba)
1 /*
2  * Copyright 2001-2016, 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 "AppFontManager.h"
17 #include "GlyphLayoutEngine.h"
18 #include "GlobalFontManager.h"
19 #include "truncate_string.h"
20 #include "utf8_functions.h"
21 
22 #include FT_FREETYPE_H
23 #include FT_GLYPH_H
24 #include FT_OUTLINE_H
25 
26 #ifdef FONTCONFIG_ENABLED
27 
28 #include <fontconfig.h>
29 #include <fcfreetype.h>
30 
31 #endif // FONTCONFIG_ENABLED
32 
33 #include <Shape.h>
34 #include <String.h>
35 #include <UnicodeBlockObjects.h>
36 #include <UTF8.h>
37 
38 #include <agg_bounding_rect.h>
39 
40 #include <stdio.h>
41 #include <string.h>
42 
43 
44 // functions needed to convert a freetype vector graphics to a BShape
45 inline BPoint
VectorToPoint(const FT_Vector * vector)46 VectorToPoint(const FT_Vector *vector)
47 {
48 	BPoint result;
49 	result.x = float(vector->x) / 64;
50 	result.y = -float(vector->y) / 64;
51 	return result;
52 }
53 
54 
55 int
MoveToFunc(const FT_Vector * to,void * user)56 MoveToFunc(const FT_Vector *to, void *user)
57 {
58 	((BShape *)user)->MoveTo(VectorToPoint(to));
59 	return 0;
60 }
61 
62 
63 int
LineToFunc(const FT_Vector * to,void * user)64 LineToFunc(const FT_Vector *to, void *user)
65 {
66 	((BShape *)user)->LineTo(VectorToPoint(to));
67 	return 0;
68 }
69 
70 
71 int
ConicToFunc(const FT_Vector * control,const FT_Vector * to,void * user)72 ConicToFunc(const FT_Vector *control, const FT_Vector *to, void *user)
73 {
74 	BPoint controls[3];
75 
76 	controls[0] = VectorToPoint(control);
77 	controls[1] = controls[0];
78 	controls[2] = VectorToPoint(to);
79 
80 	((BShape *)user)->BezierTo(controls);
81 	return 0;
82 }
83 
84 
85 int
CubicToFunc(const FT_Vector * control1,const FT_Vector * control2,const FT_Vector * to,void * user)86 CubicToFunc(const FT_Vector *control1, const FT_Vector *control2, const FT_Vector *to, void *user)
87 {
88 	BPoint controls[3];
89 
90 	controls[0] = VectorToPoint(control1);
91 	controls[1] = VectorToPoint(control2);
92 	controls[2] = VectorToPoint(to);
93 
94 	((BShape *)user)->BezierTo(controls);
95 	return 0;
96 }
97 
98 
99 inline bool
is_white_space(uint32 charCode)100 is_white_space(uint32 charCode)
101 {
102 	switch (charCode) {
103 		case 0x0009:	/* tab */
104 		case 0x000b:	/* vertical tab */
105 		case 0x000c:	/* form feed */
106 		case 0x0020:	/* space */
107 		case 0x00a0:	/* non breaking space */
108 		case 0x000a:	/* line feed */
109 		case 0x000d:	/* carriage return */
110 		case 0x2028:	/* line separator */
111 		case 0x2029:	/* paragraph separator */
112 			return true;
113 	}
114 
115 	return false;
116 }
117 
118 
119 //	#pragma mark -
120 
121 
122 /*!
123 	\brief Constructor
124 	\param style Style object to which the ServerFont belongs
125 	\param size Character size in points
126 	\param rotation Rotation in degrees
127 	\param shear Shear (slant) in degrees. 45 <= shear <= 135
128 	\param flags Style flags as defined in <Font.h>
129 	\param spacing String spacing flag as defined in <Font.h>
130 */
ServerFont(FontStyle & style,float size,float rotation,float shear,float falseBoldWidth,uint16 flags,uint8 spacing)131 ServerFont::ServerFont(FontStyle& style, float size, float rotation,
132 		float shear, float falseBoldWidth, uint16 flags, uint8 spacing)
133 	:
134 	fStyle(&style, false),
135 	fSize(size),
136 	fRotation(rotation),
137 	fShear(shear),
138 	fFalseBoldWidth(falseBoldWidth),
139 	fBounds(0, 0, 0, 0),
140 	fFlags(flags),
141 	fSpacing(spacing),
142 	fDirection(style.Direction()),
143 	fFace(style.Face()),
144 	fEncoding(B_UNICODE_UTF8)
145 {
146 }
147 
148 
ServerFont()149 ServerFont::ServerFont()
150 	:
151 	fStyle(NULL)
152 {
153 	*this = *gFontManager->DefaultPlainFont();
154 }
155 
156 
157 /*!
158 	\brief Copy Constructor
159 	\param font ServerFont to copy
160 */
ServerFont(const ServerFont & font)161 ServerFont::ServerFont(const ServerFont &font)
162 	:
163 	fStyle(NULL)
164 {
165 	*this = font;
166 }
167 
168 
169 /*!
170 	\brief Removes itself as a dependency of its owning style.
171 */
~ServerFont()172 ServerFont::~ServerFont()
173 {
174 }
175 
176 
177 /*!
178 	\brief Returns a copy of the specified font
179 	\param The font to copy from.
180 	\return A copy of the specified font
181 */
182 ServerFont&
operator =(const ServerFont & font)183 ServerFont::operator=(const ServerFont& font)
184 {
185 	fSize = font.fSize;
186 	fRotation = font.fRotation;
187 	fShear = font.fShear;
188 	fFalseBoldWidth = font.fFalseBoldWidth;
189 	fFlags = font.fFlags;
190 	fSpacing = font.fSpacing;
191 	fEncoding = font.fEncoding;
192 	fBounds = font.fBounds;
193 
194 	SetStyle(font.fStyle);
195 
196 	fFace = font.fFace;
197 
198 	return *this;
199 }
200 
201 
202 bool
operator ==(const ServerFont & other) const203 ServerFont::operator==(const ServerFont& other) const
204 {
205 	if (GetFamilyAndStyle() != other.GetFamilyAndStyle())
206 		return false;
207 
208 	return fSize == other.fSize && fRotation == other.fRotation
209 		&& fShear == other.fShear && fFalseBoldWidth == other.fFalseBoldWidth
210 		&& fFlags == other.fFlags && fSpacing == other.fSpacing
211 		&& fEncoding == other.fEncoding && fBounds == other.fBounds
212 		&& fDirection == other.fDirection && fFace == other.fFace;
213 }
214 
215 
216 /*!
217 	\brief Returns the number of strikes in the font
218 	\return The number of strikes in the font
219 */
220 int32
CountTuned()221 ServerFont::CountTuned()
222 {
223 	return fStyle->TunedCount();
224 }
225 
226 
227 /*!
228 	\brief Returns the file format of the font.
229 	\return Mostly B_TRUETYPE_WINDOWS :)
230 */
231 font_file_format
FileFormat()232 ServerFont::FileFormat()
233 {
234 	return fStyle->FileFormat();
235 }
236 
237 
238 const char*
Style() const239 ServerFont::Style() const
240 {
241 	return fStyle->Name();
242 }
243 
244 
245 const char*
Family() const246 ServerFont::Family() const
247 {
248 	return fStyle->Family()->Name();
249 }
250 
251 
252 void
SetStyle(FontStyle * style)253 ServerFont::SetStyle(FontStyle* style)
254 {
255 	if (style && style != fStyle) {
256 		fStyle.SetTo(style, false);
257 
258 		fFace = fStyle->PreservedFace(fFace);
259 		fDirection = fStyle->Direction();
260 
261 		// invalidate fBounds
262 		fBounds.Set(0, -1, 0, -1);
263 	}
264 }
265 
266 
267 /*!
268 	\brief Sets the ServerFont instance to whatever font is specified
269 	This method will lock the font manager.
270 
271 	\param familyID ID number of the family to set
272 	\param styleID ID number of the style to set
273 	\return B_OK if successful, B_ERROR if not
274 */
275 status_t
SetFamilyAndStyle(uint16 familyID,uint16 styleID,AppFontManager * fontManager)276 ServerFont::SetFamilyAndStyle(uint16 familyID, uint16 styleID,
277 	AppFontManager* fontManager)
278 {
279 
280 	BReference<FontStyle> style;
281 
282 	if (gFontManager->Lock()) {
283 		style.SetTo(gFontManager->GetStyle(familyID, styleID), false);
284 
285 		gFontManager->Unlock();
286 	}
287 
288 	if (style == NULL) {
289 		if (fontManager != NULL && fontManager->Lock()) {
290 			style.SetTo(fontManager->GetStyle(familyID, styleID), false);
291 
292 			fontManager->Unlock();
293 		}
294 
295 		if (style == NULL)
296 			return B_ERROR;
297 	}
298 
299 	SetStyle(style);
300 
301 	// invalidate fBounds
302 	fBounds.Set(0, -1, 0, -1);
303 
304 	return B_OK;
305 }
306 
307 
308 /*!
309 	\brief Sets the ServerFont instance to whatever font is specified
310 	\param fontID the combination of family and style ID numbers
311 	\return B_OK if successful, B_ERROR if not
312 */
313 status_t
SetFamilyAndStyle(uint32 fontID,AppFontManager * fontManager)314 ServerFont::SetFamilyAndStyle(uint32 fontID, AppFontManager* fontManager)
315 {
316 	uint16 style = fontID & 0xFFFF;
317 	uint16 family = (fontID & 0xFFFF0000) >> 16;
318 
319 	return SetFamilyAndStyle(family, style, fontManager);
320 }
321 
322 
323 void
SetSize(float value)324 ServerFont::SetSize(float value)
325 {
326 	fSize = value;
327 
328 	// invalidate fBounds
329 	fBounds.Set(0, -1, 0, -1);
330 }
331 
332 
333 status_t
SetFace(uint16 face)334 ServerFont::SetFace(uint16 face)
335 {
336 	// Don't confuse the Be API "face" with the Freetype face, which is just
337 	// an index in case a single font file exports multiple font faces. The
338 	// FontStyle class takes care of mapping the font style name to the Be
339 	// API face flags in FontStyle::_TranslateStyleToFace().
340 
341 	if (fStyle->PreservedFace(face) == face) {
342 		fFace = face;
343 		return B_OK;
344 	}
345 
346 	BReference <FontStyle> style;
347 	uint16 familyID = FamilyID();
348 	if (gFontManager->Lock()) {
349 		int32 count = gFontManager->CountStyles(familyID);
350 		for (int32 i = 0; i < count; i++) {
351 			style.SetTo(gFontManager->GetStyleByIndex(familyID, i), false);
352 			if (style == NULL)
353 				break;
354 			if (style->PreservedFace(face) == face)
355 				break;
356 			else
357 				style = NULL;
358 		}
359 
360 		gFontManager->Unlock();
361 	}
362 
363 	if (!style)
364 		return B_ERROR;
365 
366 	fFace = face;
367 	SetStyle(style);
368 
369 	// invalidate fBounds
370 	fBounds.Set(0, -1, 0, -1);
371 
372 	return B_OK;
373 }
374 
375 
376 /*!
377 	\brief Gets the ID values for the ServerFont instance in one shot
378 	\return the combination of family and style ID numbers
379 */
380 uint32
GetFamilyAndStyle() const381 ServerFont::GetFamilyAndStyle() const
382 {
383 	if (fStyle == NULL || fStyle->Family() == NULL)
384 		return 0;
385 
386 	return (FamilyID() << 16) | StyleID();
387 }
388 
389 
390 FT_Face
GetTransformedFace(bool rotate,bool shear) const391 ServerFont::GetTransformedFace(bool rotate, bool shear) const
392 {
393 	fStyle->Lock();
394 	FT_Face face = fStyle->FreeTypeFace();
395 	if (!face) {
396 		fStyle->Unlock();
397 		return NULL;
398 	}
399 
400 	FT_Set_Char_Size(face, 0, int32(fSize * 64), 72, 72);
401 
402 	if ((rotate && fRotation != 0) || (shear && fShear != 90)) {
403 		FT_Matrix rmatrix, smatrix;
404 
405 		Angle rotationAngle(fRotation);
406 		rmatrix.xx = (FT_Fixed)( rotationAngle.Cosine() * 0x10000);
407 		rmatrix.xy = (FT_Fixed)(-rotationAngle.Sine() * 0x10000);
408 		rmatrix.yx = (FT_Fixed)( rotationAngle.Sine() * 0x10000);
409 		rmatrix.yy = (FT_Fixed)( rotationAngle.Cosine() * 0x10000);
410 
411 		Angle shearAngle(fShear);
412 		smatrix.xx = (FT_Fixed)(0x10000);
413 		smatrix.xy = (FT_Fixed)(-shearAngle.Cosine() * 0x10000);
414 		smatrix.yx = (FT_Fixed)(0);
415 		smatrix.yy = (FT_Fixed)(0x10000);
416 
417 		// Multiply togheter and apply transform
418 		FT_Matrix_Multiply(&rmatrix, &smatrix);
419 		FT_Set_Transform(face, &smatrix, NULL);
420 	}
421 
422 	// fStyle will be unlocked in PutTransformedFace()
423 	return face;
424 }
425 
426 
427 void
PutTransformedFace(FT_Face face) const428 ServerFont::PutTransformedFace(FT_Face face) const
429 {
430 	// Reset transformation
431 	FT_Set_Transform(face, NULL, NULL);
432 	fStyle->Unlock();
433 }
434 
435 
436 status_t
GetGlyphShapes(const char charArray[],int32 numChars,BShape * shapeArray[]) const437 ServerFont::GetGlyphShapes(const char charArray[], int32 numChars,
438 	BShape* shapeArray[]) const
439 {
440 	if (!charArray || numChars <= 0 || !shapeArray)
441 		return B_BAD_DATA;
442 
443 	FT_Face face = GetTransformedFace(true, true);
444 	if (!face)
445 		return B_ERROR;
446 
447 	FT_Outline_Funcs funcs;
448 	funcs.move_to = MoveToFunc;
449 	funcs.line_to = LineToFunc;
450 	funcs.conic_to = ConicToFunc;
451 	funcs.cubic_to = CubicToFunc;
452 	funcs.shift = 0;
453 	funcs.delta = 0;
454 
455 	const char* string = charArray;
456 	for (int i = 0; i < numChars; i++) {
457 		shapeArray[i] = new (std::nothrow) BShape();
458 		if (shapeArray[i] == NULL) {
459 			PutTransformedFace(face);
460 			return B_NO_MEMORY;
461 		}
462 		FT_Load_Char(face, UTF8ToCharCode(&string), FT_LOAD_NO_BITMAP);
463 		FT_Outline outline = face->glyph->outline;
464 		FT_Outline_Decompose(&outline, &funcs, shapeArray[i]);
465 		shapeArray[i]->Close();
466 	}
467 
468 	PutTransformedFace(face);
469 	return B_OK;
470 }
471 
472 
473 #ifdef FONTCONFIG_ENABLED
474 
475 /*!
476 	\brief For a given codepoint, do a binary search of the defined unicode
477 	blocks to figure out which one contains the codepoint.
478 	\param codePoint is the point to find
479 	\param startGuess is the starting point for the binary search (default 0)
480 */
481 static
482 int32
FindBlockForCodepoint(uint32 codePoint,uint32 startGuess)483 FindBlockForCodepoint(uint32 codePoint, uint32 startGuess)
484 {
485 	uint32 min = 0;
486 	uint32 max = kNumUnicodeBlockRanges;
487 	uint32 guess = (max + min) / 2;
488 
489 	if (startGuess > 0)
490 		guess = startGuess;
491 
492 	if (codePoint > kUnicodeBlockMap[max-1].end)
493 		return -1;
494 
495 	while ((max >= min) && (guess < kNumUnicodeBlockRanges)) {
496 		uint32 start = kUnicodeBlockMap[guess].start;
497 		uint32 end = kUnicodeBlockMap[guess].end;
498 
499 		if (start <= codePoint && end >= codePoint)
500 			return guess;
501 
502 		if (end < codePoint) {
503 			min = guess + 1;
504 		} else {
505 			max = guess - 1;
506 		}
507 
508 		guess = (max + min) / 2;
509 	}
510 
511 	return -1;
512 }
513 
514 /*!
515 	\brief parses charmap from fontconfig.  See fontconfig docs for FcCharSetFirstPage
516 	and FcCharSetNextPage for details on format.
517 	\param charMap is a fontconfig character map
518 	\param baseCodePoint is the base codepoint returned by fontconfig
519 	\param blocksForMap is a unicode_block to store the bitmap of contained blocks
520 */
521 static
522 void
ParseFcMap(FcChar32 charMap[],FcChar32 baseCodePoint,unicode_block & blocksForMap)523 ParseFcMap(FcChar32 charMap[], FcChar32 baseCodePoint, unicode_block& blocksForMap)
524 {
525 	uint32 block = 0;
526 	const uint8 BITS_PER_BLOCK = 32;
527 	uint32 currentCodePoint = 0;
528 
529 	if (baseCodePoint > kUnicodeBlockMap[kNumUnicodeBlockRanges-1].end)
530 		return;
531 
532 	for (int i = 0; i < FC_CHARSET_MAP_SIZE; ++i) {
533 		FcChar32 curMapBlock = charMap[i];
534 		int32 rangeStart = -1;
535 		int32 startBlock = -1;
536 		int32 endBlock = -1;
537 		uint32 startPoint = 0;
538 
539 		currentCodePoint = baseCodePoint + block;
540 
541 		for (int bit = 0; bit < BITS_PER_BLOCK; ++bit) {
542 			if (curMapBlock == 0 && startBlock < 0)
543 				// if no more bits are set then short-circuit the loop
544 				break;
545 
546 			if ((curMapBlock & 0x1) != 0 && rangeStart < 0) {
547 				rangeStart = bit;
548 				startPoint = currentCodePoint + rangeStart;
549 				startBlock = FindBlockForCodepoint(startPoint, 0);
550 				if (startBlock >= 0) {
551 					blocksForMap = blocksForMap
552 						| kUnicodeBlockMap[startBlock].block;
553 				}
554 			} else if (rangeStart >= 0 && startBlock >= 0) {
555 					// when we find an empty bit, that's the end of the range
556 				uint32 endPoint = currentCodePoint + (bit - 1);
557 
558 				endBlock = FindBlockForCodepoint(endPoint,
559 					startBlock);
560 					// start the binary search at the block where we found the
561 					// start codepoint to ideally find the end in the same
562 					// block.
563 				++startBlock;
564 
565 				while (startBlock <= endBlock) {
566 					// if the starting codepoint is found in a different block
567 					// than the ending codepoint, we should add all the blocks
568 					// inbetween.
569 					blocksForMap = blocksForMap
570 						| kUnicodeBlockMap[startBlock].block;
571 					++startBlock;
572 				}
573 
574 				startBlock = -1;
575 				endBlock = -1;
576 				rangeStart = -1;
577 			}
578 
579 			curMapBlock >>= 1;
580 		}
581 
582 		if (rangeStart >= 0 && startBlock >= 0) {
583 				// if we hit the end of the block and had
584 				// found a start of the range then we
585 				// should end the range at the end of the block
586 			uint32 endPoint = currentCodePoint + BITS_PER_BLOCK - 1;
587 
588 			endBlock = FindBlockForCodepoint(endPoint,
589 				startBlock);
590 				// start the binary search at the block where we found the
591 				// start codepoint to ideally find the end in the same
592 				// block.
593 			++startBlock;
594 
595 			while (startBlock <= endBlock) {
596 				// if the starting codepoint is found in a different block
597 				// than the ending codepoint, we should add all the blocks
598 				// inbetween.
599 				blocksForMap = blocksForMap
600 					| kUnicodeBlockMap[startBlock].block;
601 				++startBlock;
602 			}
603 		}
604 
605 		block += BITS_PER_BLOCK;
606 	}
607 }
608 
609 #endif // FONTCONFIG_ENABLED
610 
611 
612 /*!
613 	\brief Gets a bitmap that indicates which Unicode blocks are in the font.
614 	\param unicode_block to store bitmap in
615 	\return B_OK; bitmap will be empty if something went wrong
616 */
617 status_t
GetUnicodeBlocks(unicode_block & blocksForFont)618 ServerFont::GetUnicodeBlocks(unicode_block& blocksForFont)
619 {
620 	blocksForFont = unicode_block();
621 
622 #ifdef FONTCONFIG_ENABLED
623 	FT_Face face = GetTransformedFace(true, true);
624 	if (face == NULL)
625 		return B_ERROR;
626 
627 	FcCharSet *charSet = FcFreeTypeCharSet(face, NULL);
628 	if (charSet == NULL) {
629 		PutTransformedFace(face);
630 		return B_ERROR;
631 	}
632 
633 	FcChar32 charMap[FC_CHARSET_MAP_SIZE];
634 	FcChar32 next = 0;
635 	FcChar32 baseCodePoint = FcCharSetFirstPage(charSet, charMap, &next);
636 
637 	while ((baseCodePoint != FC_CHARSET_DONE) && (next != FC_CHARSET_DONE)) {
638 		ParseFcMap(charMap, baseCodePoint, blocksForFont);
639 		baseCodePoint = FcCharSetNextPage(charSet, charMap, &next);
640 	}
641 
642 	FcCharSetDestroy(charSet);
643 	PutTransformedFace(face);
644 #endif // FONTCONFIG_ENABLED
645 
646 	return B_OK;
647 }
648 
649 /*!
650 	\brief Checks if a unicode block specified by a start and end point is defined
651 	in the current font
652 	\param start of unicode block
653 	\param end of unicode block
654 	\param hasBlock boolean to store whether the font contains the specified block
655 	\return B_OK; hasBlock will be false if something goes wrong
656 */
657 status_t
IncludesUnicodeBlock(uint32 start,uint32 end,bool & hasBlock)658 ServerFont::IncludesUnicodeBlock(uint32 start, uint32 end, bool& hasBlock)
659 {
660 	hasBlock = false;
661 
662 #ifdef FONTCONFIG_ENABLED
663 	FT_Face face = GetTransformedFace(true, true);
664 	if (face == NULL)
665 		return B_ERROR;
666 
667 	FcCharSet *charSet = FcFreeTypeCharSet(face, NULL);
668 	if (charSet == NULL) {
669 		PutTransformedFace(face);
670 		return B_ERROR;
671 	}
672 
673 	uint32 curCodePoint = start;
674 
675 	while (curCodePoint <= end && hasBlock == false) {
676 		// loop through range; if any character in the range is in the charset
677 		// then the block is represented.
678 		if (FcCharSetHasChar(charSet, (FcChar32)curCodePoint) == FcTrue) {
679 			hasBlock = true;
680 			break;
681 		}
682 
683 		++curCodePoint;
684 	}
685 
686 	FcCharSetDestroy(charSet);
687 	PutTransformedFace(face);
688 #endif // FONTCONFIG_ENABLED
689 
690 	return B_OK;
691 }
692 
693 
694 status_t
GetHasGlyphs(const char * string,int32 numBytes,int32 numChars,bool * hasArray,bool useFallbacks) const695 ServerFont::GetHasGlyphs(const char* string, int32 numBytes, int32 numChars, bool* hasArray,
696 	bool useFallbacks) const
697 {
698 	if (string == NULL || numBytes <= 0 || numChars <= 0 || hasArray == NULL)
699 		return B_BAD_DATA;
700 
701 	FontCacheEntry* entry = NULL;
702 	FontCacheReference cacheReference;
703 	BObjectList<FontCacheReference> fallbacks(21, true);
704 
705 	entry = GlyphLayoutEngine::FontCacheEntryFor(*this, false);
706 	if (entry == NULL)
707 		return B_ERROR;
708 
709 	cacheReference.SetTo(entry);
710 
711 	uint32 charCode;
712 	int32 charIndex = 0;
713 	const char* start = string;
714 	while (charIndex < numChars && (charCode = UTF8ToCharCode(&string)) != 0) {
715 		hasArray[charIndex] = entry->CanCreateGlyph(charCode);
716 
717 		if (hasArray[charIndex] == false && useFallbacks) {
718 			if (fallbacks.IsEmpty())
719 				GlyphLayoutEngine::PopulateFallbacks(fallbacks, *this, false);
720 
721 			if (GlyphLayoutEngine::GetFallbackReference(fallbacks, charCode) != NULL)
722 				hasArray[charIndex] = true;
723 		}
724 
725 		charIndex++;
726 		if (string - start + 1 > numBytes)
727 			break;
728 	}
729 
730 	return B_OK;
731 }
732 
733 
734 class EdgesConsumer {
735  public:
EdgesConsumer(edge_info * edges,float size)736 	EdgesConsumer(edge_info* edges, float size)
737 		:
738 		fEdges(edges),
739 		fSize(size)
740 	{
741 	}
742 
NeedsVector()743 	bool NeedsVector() { return false; }
Start()744 	void Start() {}
Finish(double x,double y)745 	void Finish(double x, double y) {}
ConsumeEmptyGlyph(int32 index,uint32 charCode,double x,double y)746 	void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y)
747 	{
748 		fEdges[index].left = 0.0;
749 		fEdges[index].right = 0.0;
750 	}
751 
ConsumeGlyph(int32 index,uint32 charCode,const GlyphCache * glyph,FontCacheEntry * entry,double x,double y,double advanceX,double advanceY)752 	bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph,
753 		FontCacheEntry* entry, double x, double y, double advanceX,
754 			double advanceY)
755 	{
756 		fEdges[index].left = glyph->inset_left / fSize;
757 		fEdges[index].right = glyph->inset_right / fSize;
758 		return true;
759 	}
760 
761  private:
762 	edge_info* fEdges;
763 	float fSize;
764 };
765 
766 
767 status_t
GetEdges(const char * string,int32 numBytes,int32 numChars,edge_info * edges) const768 ServerFont::GetEdges(const char* string, int32 numBytes, int32 numChars,
769 	edge_info* edges) const
770 {
771 	if (string == NULL || numBytes <= 0 || numChars <= 0 || edges == NULL)
772 		return B_BAD_DATA;
773 
774 	EdgesConsumer consumer(edges, fSize);
775 	if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes,
776 			numChars, NULL, fSpacing)) {
777 		return B_OK;
778 	}
779 
780 	return B_ERROR;
781 
782 //	FT_Face face = GetTransformedFace(false, false);
783 //	if (!face)
784 //		return B_ERROR;
785 //
786 //	const char *string = charArray;
787 //	for (int i = 0; i < numChars; i++) {
788 //		FT_Load_Char(face, UTF8ToCharCode(&string), FT_LOAD_NO_BITMAP);
789 //		edgeArray[i].left = float(face->glyph->metrics.horiBearingX)
790 //			/ 64 / fSize;
791 //		edgeArray[i].right = float(face->glyph->metrics.horiBearingX
792 //			+ face->glyph->metrics.width - face->glyph->metrics.horiAdvance)
793 //			/ 64 / fSize;
794 //	}
795 //
796 //	PutTransformedFace(face);
797 //	return B_OK;
798 }
799 
800 
801 class BPointEscapementConsumer {
802 public:
BPointEscapementConsumer(BPoint * escapements,BPoint * offsets,float size)803 	BPointEscapementConsumer(BPoint* escapements, BPoint* offsets, float size)
804 		:
805 		fEscapements(escapements),
806 		fOffsets(offsets),
807 		fSize(size)
808 	{
809 	}
810 
NeedsVector()811 	bool NeedsVector() { return false; }
Start()812 	void Start() {}
Finish(double x,double y)813 	void Finish(double x, double y) {}
ConsumeEmptyGlyph(int32 index,uint32 charCode,double x,double y)814 	void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y)
815 	{
816 		_Set(index, 0, 0);
817 	}
818 
ConsumeGlyph(int32 index,uint32 charCode,const GlyphCache * glyph,FontCacheEntry * entry,double x,double y,double advanceX,double advanceY)819 	bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph,
820 		FontCacheEntry* entry, double x, double y, double advanceX,
821 			double advanceY)
822 	{
823 		return _Set(index, advanceX, advanceY);
824 	}
825 
826 private:
_Set(int32 index,double x,double y)827 	inline bool _Set(int32 index, double x, double y)
828 	{
829 		fEscapements[index].x = x / fSize;
830 		fEscapements[index].y = y / fSize;
831 		if (fOffsets) {
832 			// ToDo: According to the BeBook: "The offsetArray is applied by
833 			// the dynamic spacing in order to improve the relative position
834 			// of the character's width with relation to another character,
835 			// without altering the width." So this will probably depend on
836 			// the spacing mode.
837 			fOffsets[index].x = 0;
838 			fOffsets[index].y = 0;
839 		}
840 		return true;
841 	}
842 
843 	BPoint* fEscapements;
844 	BPoint* fOffsets;
845 	float fSize;
846 };
847 
848 
849 status_t
GetEscapements(const char * string,int32 numBytes,int32 numChars,escapement_delta delta,BPoint escapementArray[],BPoint offsetArray[]) const850 ServerFont::GetEscapements(const char* string, int32 numBytes, int32 numChars,
851 	escapement_delta delta, BPoint escapementArray[],
852 	BPoint offsetArray[]) const
853 {
854 	if (string == NULL || numBytes <= 0 || numChars <= 0
855 		|| escapementArray == NULL) {
856 		return B_BAD_DATA;
857 	}
858 
859 	BPointEscapementConsumer consumer(escapementArray, offsetArray, fSize);
860 	if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes,
861 			numChars, &delta, fSpacing)) {
862 		return B_OK;
863 	}
864 
865 	return B_ERROR;
866 }
867 
868 
869 class WidthEscapementConsumer {
870 public:
WidthEscapementConsumer(float * widths,float size)871 	WidthEscapementConsumer(float* widths, float size)
872 		:
873 		fWidths(widths),
874 		fSize(size)
875 	{
876 	}
877 
NeedsVector()878 	bool NeedsVector() { return false; }
Start()879 	void Start() {}
Finish(double x,double y)880 	void Finish(double x, double y) {}
ConsumeEmptyGlyph(int32 index,uint32 charCode,double x,double y)881 	void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y)
882 	{
883 		fWidths[index] = 0.0;
884 	}
885 
ConsumeGlyph(int32 index,uint32 charCode,const GlyphCache * glyph,FontCacheEntry * entry,double x,double y,double advanceX,double advanceY)886 	bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph,
887 		FontCacheEntry* entry, double x, double y, double advanceX,
888 			double advanceY)
889 	{
890 		fWidths[index] = advanceX / fSize;
891 		return true;
892 	}
893 
894  private:
895 	float* fWidths;
896 	float fSize;
897 };
898 
899 
900 
901 status_t
GetEscapements(const char * string,int32 numBytes,int32 numChars,escapement_delta delta,float widthArray[]) const902 ServerFont::GetEscapements(const char* string, int32 numBytes, int32 numChars,
903 	escapement_delta delta, float widthArray[]) const
904 {
905 	if (string == NULL || numBytes <= 0 || numChars <= 0 || widthArray == NULL)
906 		return B_BAD_DATA;
907 
908 	WidthEscapementConsumer consumer(widthArray, fSize);
909 	if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes,
910 			numChars, &delta, fSpacing)) {
911 		return B_OK;
912 	}
913 
914 	return B_ERROR;
915 }
916 
917 
918 class BoundingBoxConsumer {
919  public:
BoundingBoxConsumer(Transformable & transform,BRect * rectArray,bool asString)920 	BoundingBoxConsumer(Transformable& transform, BRect* rectArray,
921 			bool asString)
922 		:
923 		rectArray(rectArray),
924 		stringBoundingBox(INT32_MAX, INT32_MAX, INT32_MIN, INT32_MIN),
925 		fAsString(asString),
926 		fCurves(fPathAdaptor),
927 		fContour(fCurves),
928 		fTransformedOutline(fCurves, transform),
929 		fTransformedContourOutline(fContour, transform),
930 		fTransform(transform)
931 	{
932 	}
933 
NeedsVector()934 	bool NeedsVector() { return false; }
Start()935 	void Start() {}
Finish(double x,double y)936 	void Finish(double x, double y) {}
ConsumeEmptyGlyph(int32 index,uint32 charCode,double x,double y)937 	void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) {}
938 
ConsumeGlyph(int32 index,uint32 charCode,const GlyphCache * glyph,FontCacheEntry * entry,double x,double y,double advanceX,double advanceY)939 	bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph,
940 		FontCacheEntry* entry, double x, double y, double advanceX,
941 			double advanceY)
942 	{
943 		if (glyph->data_type != glyph_data_outline) {
944 			const agg::rect_i& r = glyph->bounds;
945 			if (fAsString) {
946 				if (rectArray) {
947 					rectArray[index].left = r.x1 + x;
948 					rectArray[index].top = r.y1 + y;
949 					rectArray[index].right = r.x2 + x + 1;
950 					rectArray[index].bottom = r.y2 + y + 1;
951 				} else {
952 					stringBoundingBox = stringBoundingBox
953 						| BRect(r.x1 + x, r.y1 + y,
954 							r.x2 + x + 1, r.y2 + y + 1);
955 				}
956 			} else {
957 				rectArray[index].left = r.x1;
958 				rectArray[index].top = r.y1;
959 				rectArray[index].right = r.x2 + 1;
960 				rectArray[index].bottom = r.y2 + 1;
961 			}
962 		} else {
963 			if (fAsString) {
964 				entry->InitAdaptors(glyph, x, y,
965 						fMonoAdaptor, fGray8Adaptor, fPathAdaptor);
966 			} else {
967 				entry->InitAdaptors(glyph, 0, 0,
968 						fMonoAdaptor, fGray8Adaptor, fPathAdaptor);
969 			}
970 			double left = 0.0;
971 			double top = 0.0;
972 			double right = -1.0;
973 			double bottom = -1.0;
974 			uint32 pathID[1];
975 			pathID[0] = 0;
976 			// TODO: use fContour if falseboldwidth is > 0
977 			agg::bounding_rect(fTransformedOutline, pathID, 0, 1,
978 				&left, &top, &right, &bottom);
979 
980 			if (rectArray) {
981 				rectArray[index] = BRect(left, top, right, bottom);
982 			} else {
983 				stringBoundingBox = stringBoundingBox
984 					| BRect(left, top, right, bottom);
985 			}
986 		}
987 		return true;
988 	}
989 
990 	BRect*								rectArray;
991 	BRect								stringBoundingBox;
992 
993  private:
994 	bool								fAsString;
995 	FontCacheEntry::GlyphPathAdapter	fPathAdaptor;
996 	FontCacheEntry::GlyphGray8Adapter	fGray8Adaptor;
997 	FontCacheEntry::GlyphMonoAdapter	fMonoAdaptor;
998 
999 	FontCacheEntry::CurveConverter		fCurves;
1000 	FontCacheEntry::ContourConverter	fContour;
1001 
1002 	FontCacheEntry::TransformedOutline	fTransformedOutline;
1003 	FontCacheEntry::TransformedContourOutline fTransformedContourOutline;
1004 
1005 	Transformable&						fTransform;
1006 };
1007 
1008 
1009 status_t
GetBoundingBoxes(const char * string,int32 numBytes,int32 numChars,BRect rectArray[],bool stringEscapement,font_metric_mode mode,escapement_delta delta,bool asString)1010 ServerFont::GetBoundingBoxes(const char* string, int32 numBytes, int32 numChars,
1011 	BRect rectArray[], bool stringEscapement, font_metric_mode mode,
1012 	escapement_delta delta, bool asString)
1013 {
1014 	// TODO: The font_metric_mode is not used
1015 	if (string == NULL || numBytes <= 0 || numChars <= 0 || rectArray == NULL)
1016 		return B_BAD_DATA;
1017 
1018 	Transformable transform(EmbeddedTransformation());
1019 
1020 	BoundingBoxConsumer consumer(transform, rectArray, asString);
1021 	if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes,
1022 			numChars, stringEscapement ? &delta : NULL, fSpacing)) {
1023 		return B_OK;
1024 	}
1025 	return B_ERROR;
1026 }
1027 
1028 
1029 status_t
GetBoundingBoxesForStrings(char * charArray[],size_t lengthArray[],int32 numStrings,BRect rectArray[],font_metric_mode mode,escapement_delta deltaArray[])1030 ServerFont::GetBoundingBoxesForStrings(char *charArray[], size_t lengthArray[],
1031 	int32 numStrings, BRect rectArray[], font_metric_mode mode,
1032 	escapement_delta deltaArray[])
1033 {
1034 	// TODO: The font_metric_mode is never used
1035 	if (charArray == NULL || lengthArray == NULL || numStrings <= 0
1036 		|| rectArray == NULL || deltaArray == NULL) {
1037 		return B_BAD_DATA;
1038 	}
1039 
1040 	Transformable transform(EmbeddedTransformation());
1041 
1042 	for (int32 i = 0; i < numStrings; i++) {
1043 		size_t numBytes = lengthArray[i];
1044 		const char* string = charArray[i];
1045 		escapement_delta delta = deltaArray[i];
1046 
1047 		BoundingBoxConsumer consumer(transform, NULL, true);
1048 		if (!GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes,
1049 				INT32_MAX, &delta, fSpacing)) {
1050 			return B_ERROR;
1051 		}
1052 
1053 		rectArray[i] = consumer.stringBoundingBox;
1054 	}
1055 
1056 	return B_OK;
1057 }
1058 
1059 
1060 class StringWidthConsumer {
1061  public:
StringWidthConsumer()1062 	StringWidthConsumer()
1063 		:
1064 		width(0.0)
1065 	{
1066 	}
1067 
NeedsVector()1068 	bool NeedsVector() { return false; }
Start()1069 	void Start() {}
Finish(double x,double y)1070 	void Finish(double x, double y) { width = x; }
ConsumeEmptyGlyph(int32 index,uint32 charCode,double x,double y)1071 	void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) {}
ConsumeGlyph(int32 index,uint32 charCode,const GlyphCache * glyph,FontCacheEntry * entry,double x,double y,double advanceX,double advanceY)1072 	bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph,
1073 		FontCacheEntry* entry, double x, double y, double advanceX,
1074 			double advanceY)
1075 	{
1076 		return true;
1077 	}
1078 
1079 	float width;
1080 };
1081 
1082 
1083 float
StringWidth(const char * string,int32 numBytes,const escapement_delta * deltaArray) const1084 ServerFont::StringWidth(const char *string, int32 numBytes,
1085 	const escapement_delta* deltaArray) const
1086 {
1087 	if (!string || numBytes <= 0)
1088 		return 0.0;
1089 
1090 	StringWidthConsumer consumer;
1091 	if (!GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes,
1092 			INT32_MAX, deltaArray, fSpacing)) {
1093 		return 0.0;
1094 	}
1095 
1096 	return consumer.width;
1097 }
1098 
1099 
1100 /*!
1101 	\brief Returns a BRect which encloses the entire font
1102 	\return A BRect which encloses the entire font
1103 */
1104 BRect
BoundingBox()1105 ServerFont::BoundingBox()
1106 {
1107 	FT_Face face = fStyle->FreeTypeFace();
1108 
1109 	if (fBounds.IsValid() &&
1110 		fBounds.IntegerWidth() > 0 &&
1111 		fBounds.IntegerHeight() > 0)
1112 		return fBounds;
1113 
1114 	// if font has vector outlines, get the bounding box
1115 	// from freetype and scale it by the font size
1116 	if (IsScalable()) {
1117 		FT_BBox bounds = face->bbox;
1118 		fBounds.left = (float)bounds.xMin / (float)face->units_per_EM;
1119 		fBounds.right = (float)bounds.xMax / (float)face->units_per_EM;
1120 		fBounds.top = (float)bounds.yMin / (float)face->units_per_EM;
1121 		fBounds.bottom = (float)bounds.yMax / (float)face->units_per_EM;
1122 
1123 		float scaledWidth = fBounds.Width() * fSize;
1124 		float scaledHeight = fBounds.Height() * fSize;
1125 
1126 		fBounds.InsetBy((fBounds.Width() - scaledWidth) / 2.f,
1127 			(fBounds.Height() - scaledHeight) / 2.f);
1128 	} else {
1129 		// otherwise find the bitmap that is closest in size
1130 		// to the requested size
1131 		float pixelSize = fSize * 64.f;
1132 		float minDelta = abs(face->available_sizes[0].size - pixelSize);
1133 		float width = face->available_sizes[0].x_ppem;
1134 		float height = face->available_sizes[0].y_ppem;
1135 
1136 		for (int i = 1; i < face->num_fixed_sizes; ++i) {
1137 			float delta = abs(face->available_sizes[i].size - pixelSize);
1138 			if (delta < minDelta) {
1139 				width = face->available_sizes[i].x_ppem;
1140 				height = face->available_sizes[i].y_ppem;
1141 			}
1142 		}
1143 
1144 		fBounds.top = 0;
1145 		fBounds.left = 0;
1146 		fBounds.right = width / 64.f;
1147 		fBounds.bottom = height / 64.f;
1148 	}
1149 
1150 	return fBounds;
1151 }
1152 
1153 
1154 /*!
1155 	\brief Obtains the height values for characters in the font in its current state
1156 	\param fh pointer to a font_height object to receive the values for the font
1157 */
1158 void
GetHeight(font_height & height) const1159 ServerFont::GetHeight(font_height& height) const
1160 {
1161 	fStyle->GetHeight(fSize, height);
1162 }
1163 
1164 
1165 void
TruncateString(BString * inOut,uint32 mode,float width) const1166 ServerFont::TruncateString(BString* inOut, uint32 mode, float width) const
1167 {
1168 	if (!inOut)
1169 		return;
1170 
1171 	// the width of the "…" glyph
1172 	float ellipsisWidth = StringWidth(B_UTF8_ELLIPSIS, strlen(B_UTF8_ELLIPSIS));
1173 
1174 	// count the individual glyphs
1175 	int32 numChars = inOut->CountChars();
1176 
1177 	// get the escapement of each glyph in font units
1178 	float* escapementArray = new (std::nothrow) float[numChars];
1179 	if (escapementArray == NULL)
1180 		return;
1181 
1182 	static escapement_delta delta = (escapement_delta){ 0.0, 0.0 };
1183 	if (GetEscapements(inOut->String(), inOut->Length(), numChars, delta,
1184 		escapementArray) == B_OK) {
1185 		truncate_string(*inOut, mode, width, escapementArray, fSize,
1186 			ellipsisWidth, numChars);
1187 	}
1188 
1189 	delete[] escapementArray;
1190 }
1191 
1192 
1193 Transformable
EmbeddedTransformation() const1194 ServerFont::EmbeddedTransformation() const
1195 {
1196 	// TODO: cache this?
1197 	Transformable transform;
1198 
1199 	transform.ShearBy(B_ORIGIN, (90.0 - fShear) * M_PI / 180.0, 0.0);
1200 	transform.RotateBy(B_ORIGIN, -fRotation * M_PI / 180.0);
1201 
1202 	return transform;
1203 }
1204 
1205 
1206 void
SetFontData(FT_Byte * location,uint32 size)1207 ServerFont::SetFontData(FT_Byte* location, uint32 size)
1208 {
1209 	if (fStyle != NULL)
1210 		fStyle->SetFontData(location, size);
1211 }
1212