xref: /haiku/src/kits/interface/Font.cpp (revision e433b3cfc3f089f7681f6d4e81d43f950ca6a440)
1 /*
2  * Copyright 2001-2015, Haiku, Inc.
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  *		Axel Dörfler, axeld@pinc-software.de
9  *		Stephan Aßmus <superstippi@gmx.de>
10  *		Andrej Spielmann, <andrej.spielmann@seh.ox.ac.uk>
11  */
12 
13 
14 #include <AppServerLink.h>
15 #include <FontPrivate.h>
16 #include <ObjectList.h>
17 #include <ServerProtocol.h>
18 #include <truncate_string.h>
19 #include <utf8_functions.h>
20 
21 #include <Autolock.h>
22 #include <Font.h>
23 #include <Locker.h>
24 #include <Message.h>
25 #include <PortLink.h>
26 #include <Rect.h>
27 #include <Shape.h>
28 #include <String.h>
29 
30 #include <new>
31 #include <stdio.h>
32 #include <stdlib.h>
33 
34 using namespace std;
35 
36 const float kUninitializedAscent = INFINITY;
37 const uint32 kUninitializedExtraFlags = 0xffffffff;
38 
39 // The actual objects which the globals point to
40 static BFont sPlainFont;
41 static BFont sBoldFont;
42 static BFont sFixedFont;
43 
44 const BFont* be_plain_font = &sPlainFont;
45 const BFont* be_bold_font = &sBoldFont;
46 const BFont* be_fixed_font = &sFixedFont;
47 
48 
49 struct style {
50 	BString	name;
51 	uint16	face;
52 	uint32	flags;
53 };
54 
55 struct family {
56 	BString	name;
57 	uint32	flags;
58 	BObjectList<style> styles;
59 };
60 
61 namespace {
62 
63 class FontList : public BLocker {
64 public:
65 								FontList();
66 	virtual						~FontList();
67 
68 	static	FontList*			Default();
69 
70 			bool				UpdatedOnServer();
71 
72 			status_t			FamilyAt(int32 index, font_family* _family,
73 									uint32* _flags);
74 			status_t			StyleAt(font_family family, int32 index,
75 									font_style* _style, uint16* _face,
76 									uint32* _flags);
77 
78 			int32				CountFamilies();
79 			int32				CountStyles(font_family family);
80 
81 private:
82 			status_t			_UpdateIfNecessary();
83 			status_t			_Update();
84 			int32				_RevisionOnServer();
85 			family*				_FindFamily(font_family name);
86 	static	void				_InitSingleton();
87 
88 private:
89 			BObjectList<family>	fFamilies;
90 			family*				fLastFamily;
91 			bigtime_t			fLastUpdate;
92 			int32				fRevision;
93 
94 	static	pthread_once_t		sDefaultInitOnce;
95 	static	FontList*			sDefaultInstance;
96 };
97 
98 pthread_once_t FontList::sDefaultInitOnce = PTHREAD_ONCE_INIT;
99 FontList* FontList::sDefaultInstance = NULL;
100 
101 }	// unnamed namespace
102 
103 
104 //	#pragma mark -
105 
106 
107 static int
108 compare_families(const family* a, const family* b)
109 {
110 	// TODO: compare font names according to the user's locale settings
111 	return strcmp(a->name.String(), b->name.String());
112 }
113 
114 
115 namespace {
116 
117 FontList::FontList()
118 	: BLocker("font list"),
119 	fLastFamily(NULL),
120 	fLastUpdate(0),
121 	fRevision(0)
122 {
123 }
124 
125 
126 FontList::~FontList()
127 {
128 }
129 
130 
131 /*static*/ FontList*
132 FontList::Default()
133 {
134 	if (sDefaultInstance == NULL)
135 		pthread_once(&sDefaultInitOnce, &_InitSingleton);
136 
137 	return sDefaultInstance;
138 }
139 
140 
141 bool
142 FontList::UpdatedOnServer()
143 {
144 	return _RevisionOnServer() != fRevision;
145 }
146 
147 
148 status_t
149 FontList::FamilyAt(int32 index, font_family* _family, uint32* _flags)
150 {
151 	BAutolock locker(this);
152 
153 	status_t status = _UpdateIfNecessary();
154 	if (status < B_OK)
155 		return status;
156 
157 	::family* family = fFamilies.ItemAt(index);
158 	if (family == NULL)
159 		return B_BAD_VALUE;
160 
161 	memcpy(*_family, family->name.String(), family->name.Length() + 1);
162 	if (_flags)
163 		*_flags = family->flags;
164 	return B_OK;
165 }
166 
167 
168 status_t
169 FontList::StyleAt(font_family familyName, int32 index, font_style* _style,
170 	uint16* _face, uint32* _flags)
171 {
172 	BAutolock locker(this);
173 
174 	status_t status = _UpdateIfNecessary();
175 	if (status < B_OK)
176 		return status;
177 
178 	::family* family = _FindFamily(familyName);
179 	if (family == NULL)
180 		return B_BAD_VALUE;
181 
182 	::style* style = family->styles.ItemAt(index);
183 	if (style == NULL)
184 		return B_BAD_VALUE;
185 
186 	memcpy(*_style, style->name.String(), style->name.Length() + 1);
187 	if (_face)
188 		*_face = style->face;
189 	if (_flags)
190 		*_flags = style->flags;
191 	return B_OK;
192 }
193 
194 
195 int32
196 FontList::CountFamilies()
197 {
198 	BAutolock locker(this);
199 
200 	_UpdateIfNecessary();
201 	return fFamilies.CountItems();
202 }
203 
204 
205 int32
206 FontList::CountStyles(font_family familyName)
207 {
208 	BAutolock locker(this);
209 
210 	_UpdateIfNecessary();
211 
212 	::family* family = _FindFamily(familyName);
213 	if (family == NULL)
214 		return 0;
215 
216 	return family->styles.CountItems();
217 }
218 
219 
220 status_t
221 FontList::_Update()
222 {
223 	// check version
224 
225 	int32 revision = _RevisionOnServer();
226 	fLastUpdate = system_time();
227 
228 	// are we up-to-date already?
229 	if (revision == fRevision)
230 		return B_OK;
231 
232 	fFamilies.MakeEmpty();
233 	fLastFamily = NULL;
234 
235 	BPrivate::AppServerLink link;
236 
237 	for (int32 index = 0;; index++) {
238 		link.StartMessage(AS_GET_FAMILY_AND_STYLES);
239 		link.Attach<int32>(index);
240 
241 		int32 status;
242 		if (link.FlushWithReply(status) != B_OK
243 			|| status != B_OK)
244 			break;
245 
246 		::family* family = new (nothrow) ::family;
247 		if (family == NULL)
248 			return B_NO_MEMORY;
249 
250 		link.ReadString(family->name);
251 		link.Read<uint32>(&family->flags);
252 
253 		int32 styleCount;
254 		link.Read<int32>(&styleCount);
255 
256 		for (int32 i = 0; i < styleCount; i++) {
257 			::style* style = new (nothrow) ::style;
258 			if (style == NULL) {
259 				delete family;
260 				return B_NO_MEMORY;
261 			}
262 
263 			link.ReadString(style->name);
264 			link.Read<uint16>(&style->face);
265 			link.Read<uint32>(&style->flags);
266 
267 			family->styles.AddItem(style);
268 		}
269 
270 		fFamilies.BinaryInsert(family, compare_families);
271 	}
272 
273 	fRevision = revision;
274 
275 	// if the font list has been changed in the mean time, just update again
276 	if (UpdatedOnServer())
277 		_Update();
278 
279 	return B_OK;
280 }
281 
282 
283 status_t
284 FontList::_UpdateIfNecessary()
285 {
286 	// an updated font list is at least valid for 1 second
287 	if (fLastUpdate > system_time() - 1000000)
288 		return B_OK;
289 
290 	return _Update();
291 }
292 
293 
294 int32
295 FontList::_RevisionOnServer()
296 {
297 	BPrivate::AppServerLink link;
298 	link.StartMessage(AS_GET_FONT_LIST_REVISION);
299 
300 	int32 code;
301 	if (link.FlushWithReply(code) != B_OK || code != B_OK)
302 		return B_ERROR;
303 
304 	int32 revision;
305 	link.Read<int32>(&revision);
306 
307 	return revision;
308 }
309 
310 
311 family*
312 FontList::_FindFamily(font_family name)
313 {
314 	if (fLastFamily != NULL && fLastFamily->name == name)
315 		return fLastFamily;
316 
317 	::family family;
318 	family.name = name;
319 	fLastFamily = const_cast< ::family*>(fFamilies.BinarySearch(family,
320 		compare_families));
321 	return fLastFamily;
322 }
323 
324 
325 /*static*/ void
326 FontList::_InitSingleton()
327 {
328 	sDefaultInstance = new FontList;
329 }
330 
331 }	// unnamed namespace
332 
333 
334 //	#pragma mark -
335 
336 
337 void
338 _init_global_fonts_()
339 {
340 	BPrivate::AppServerLink link;
341 	link.StartMessage(AS_GET_SYSTEM_FONTS);
342 
343 	int32 code;
344 	if (link.FlushWithReply(code) != B_OK
345 		|| code != B_OK) {
346 		printf("DEBUG: Couldn't initialize global fonts!\n");
347 		return;
348 	}
349 
350 	char type[B_OS_NAME_LENGTH];
351 
352 	while (link.ReadString(type, sizeof(type)) >= B_OK && type[0]) {
353 		BFont dummy;
354 		BFont* font = &dummy;
355 
356 		if (!strcmp(type, "plain"))
357 			font = &sPlainFont;
358 		else if (!strcmp(type, "bold"))
359 			font = &sBoldFont;
360 		else if (!strcmp(type, "fixed"))
361 			font = &sFixedFont;
362 
363 		link.Read<uint16>(&font->fFamilyID);
364 		link.Read<uint16>(&font->fStyleID);
365 		link.Read<float>(&font->fSize);
366 		link.Read<uint16>(&font->fFace);
367 		link.Read<uint32>(&font->fFlags);
368 
369 		font->fHeight.ascent = kUninitializedAscent;
370 		font->fExtraFlags = kUninitializedExtraFlags;
371 	}
372 }
373 
374 
375 void
376 _font_control_(BFont* font, int32 cmd, void* data)
377 {
378 }
379 
380 
381 status_t
382 get_font_cache_info(uint32 id, void* set)
383 {
384 	return B_ERROR;
385 }
386 
387 
388 status_t
389 set_font_cache_info(uint32 id, void* set)
390 {
391 	return B_ERROR;
392 }
393 
394 
395 // Private function used to replace the R5 hack which sets a system font
396 void
397 _set_system_font_(const char* which, font_family family, font_style style,
398 	float size)
399 {
400 	// R5 used a global area offset table to set the system fonts in the Font
401 	// preferences panel. Bleah.
402 	BPrivate::AppServerLink link;
403 
404 	link.StartMessage(AS_SET_SYSTEM_FONT);
405 	link.AttachString(which, B_OS_NAME_LENGTH);
406 	link.AttachString(family, sizeof(font_family));
407 	link.AttachString(style, sizeof(font_style));
408 	link.Attach<float>(size);
409 	link.Flush();
410 }
411 
412 
413 status_t
414 _get_system_default_font_(const char* which, font_family family,
415 	font_style style, float* _size)
416 {
417 	BPrivate::AppServerLink link;
418 
419 	link.StartMessage(AS_GET_SYSTEM_DEFAULT_FONT);
420 	link.AttachString(which, B_OS_NAME_LENGTH);
421 
422 	int32 status = B_ERROR;
423 	if (link.FlushWithReply(status) != B_OK
424 		|| status < B_OK)
425 		return status;
426 
427 	link.ReadString(family, sizeof(font_family));
428 	link.ReadString(style, sizeof(font_style));
429 	link.Read<float>(_size);
430 	return B_OK;
431 }
432 
433 
434 // Returns the number of installed font families
435 int32
436 count_font_families()
437 {
438 	return FontList::Default()->CountFamilies();
439 }
440 
441 
442 // Returns the number of styles available for a font family
443 int32
444 count_font_styles(font_family family)
445 {
446 	return FontList::Default()->CountStyles(family);
447 }
448 
449 
450 // Retrieves the family name at the specified index
451 status_t
452 get_font_family(int32 index, font_family* _name, uint32* _flags)
453 {
454 	if (_name == NULL)
455 		return B_BAD_VALUE;
456 
457 	return FontList::Default()->FamilyAt(index, _name, _flags);
458 }
459 
460 
461 // Retrieves the family name at the specified index
462 status_t
463 get_font_style(font_family family, int32 index, font_style* _name,
464 	uint32* _flags)
465 {
466 	return get_font_style(family, index, _name, NULL, _flags);
467 }
468 
469 
470 // Retrieves the family name at the specified index
471 status_t
472 get_font_style(font_family family, int32 index, font_style* _name,
473 	uint16* _face, uint32* _flags)
474 {
475 	// The face value returned by this function is not very reliable. At the
476 	// same time, the value returned should be fairly reliable, returning the
477 	// proper flag for 90%-99% of font names.
478 
479 	if (_name == NULL)
480 		return B_BAD_VALUE;
481 
482 	return FontList::Default()->StyleAt(family, index, _name, _face, _flags);
483 }
484 
485 
486 // Updates the font family list
487 bool
488 update_font_families(bool /*checkOnly*/)
489 {
490 	return FontList::Default()->UpdatedOnServer();
491 }
492 
493 
494 //	#pragma mark -
495 
496 
497 BFont::BFont()
498 	:
499 	// initialise for be_plain_font (avoid circular definition)
500 	fFamilyID(0),
501 	fStyleID(0),
502 	fSize(10.0),
503 	fShear(90.0),
504 	fRotation(0.0),
505 	fFalseBoldWidth(0.0),
506 	fSpacing(B_BITMAP_SPACING),
507 	fEncoding(B_UNICODE_UTF8),
508 	fFace(0),
509 	fFlags(0),
510 	fExtraFlags(kUninitializedExtraFlags)
511 {
512 	if (be_plain_font != NULL && this != &sPlainFont)
513 		*this = *be_plain_font;
514 	else {
515 		fHeight.ascent = 7.0;
516 		fHeight.descent = 2.0;
517 		fHeight.leading = 13.0;
518 	}
519 }
520 
521 
522 BFont::BFont(const BFont& font)
523 {
524 	*this = font;
525 }
526 
527 
528 BFont::BFont(const BFont* font)
529 {
530 	if (font != NULL)
531 		*this = *font;
532 	else
533 		*this = *be_plain_font;
534 }
535 
536 
537 // Sets the font's family and style all at once
538 status_t
539 BFont::SetFamilyAndStyle(const font_family family, const font_style style)
540 {
541 	if (family == NULL && style == NULL)
542 		return B_BAD_VALUE;
543 
544 	BPrivate::AppServerLink link;
545 
546 	link.StartMessage(AS_GET_FAMILY_AND_STYLE_IDS);
547 	link.AttachString(family, sizeof(font_family));
548 	link.AttachString(style, sizeof(font_style));
549 	link.Attach<uint16>(fFamilyID);
550 	link.Attach<uint16>(0xffff);
551 	link.Attach<uint16>(fFace);
552 
553 	int32 status = B_ERROR;
554 	if (link.FlushWithReply(status) != B_OK || status != B_OK)
555 		return status;
556 
557 	link.Read<uint16>(&fFamilyID);
558 	link.Read<uint16>(&fStyleID);
559 	link.Read<uint16>(&fFace);
560 	fHeight.ascent = kUninitializedAscent;
561 	fExtraFlags = kUninitializedExtraFlags;
562 
563 	return B_OK;
564 }
565 
566 
567 // Sets the font's family and style all at once
568 void
569 BFont::SetFamilyAndStyle(uint32 code)
570 {
571 	// R5 has a bug here: the face is not updated even though the IDs are set.
572 	// This is a problem because the face flag includes Regular/Bold/Italic
573 	// information in addition to stuff like underlining and strikethrough.
574 	// As a result, this will need a trip to the server and, thus, be slower
575 	// than R5's in order to be correct
576 
577 	uint16 family, style;
578 	style = code & 0xFFFF;
579 	family = (code & 0xFFFF0000) >> 16;
580 
581 	BPrivate::AppServerLink link;
582 	link.StartMessage(AS_GET_FAMILY_AND_STYLE_IDS);
583 	link.AttachString(NULL);	// no family and style name
584 	link.AttachString(NULL);
585 	link.Attach<uint16>(family);
586 	link.Attach<uint16>(style);
587 	link.Attach<uint16>(fFace);
588 
589 	int32 fontcode;
590 	if (link.FlushWithReply(fontcode) != B_OK || fontcode != B_OK)
591 		return;
592 
593 	link.Read<uint16>(&fFamilyID);
594 	link.Read<uint16>(&fStyleID);
595 	link.Read<uint16>(&fFace);
596 	fHeight.ascent = kUninitializedAscent;
597 	fExtraFlags = kUninitializedExtraFlags;
598 }
599 
600 
601 // Sets the font's family and face all at once
602 status_t
603 BFont::SetFamilyAndFace(const font_family family, uint16 face)
604 {
605 	// To comply with the BeBook, this function will only set valid values
606 	// i.e. passing a nonexistent family will cause only the face to be set.
607 	// Additionally, if a particular  face does not exist in a family, the
608 	// closest match will be chosen.
609 
610 	BPrivate::AppServerLink link;
611 	link.StartMessage(AS_GET_FAMILY_AND_STYLE_IDS);
612 	link.AttachString(family, sizeof(font_family));
613 	link.AttachString(NULL);	// no style given
614 	link.Attach<uint16>(fFamilyID);
615 	link.Attach<uint16>(0xffff);
616 	link.Attach<uint16>(face);
617 
618 	int32 status = B_ERROR;
619 	if (link.FlushWithReply(status) != B_OK || status != B_OK)
620 		return status;
621 
622 	link.Read<uint16>(&fFamilyID);
623 	link.Read<uint16>(&fStyleID);
624 	link.Read<uint16>(&fFace);
625 	fHeight.ascent = kUninitializedAscent;
626 	fExtraFlags = kUninitializedExtraFlags;
627 
628 	return B_OK;
629 }
630 
631 
632 void
633 BFont::SetSize(float size)
634 {
635 	fSize = size;
636 	fHeight.ascent = kUninitializedAscent;
637 }
638 
639 
640 void
641 BFont::SetShear(float shear)
642 {
643 	fShear = shear;
644 	fHeight.ascent = kUninitializedAscent;
645 }
646 
647 
648 void
649 BFont::SetRotation(float rotation)
650 {
651 	fRotation = rotation;
652 	fHeight.ascent = kUninitializedAscent;
653 }
654 
655 
656 void
657 BFont::SetFalseBoldWidth(float width)
658 {
659 	fFalseBoldWidth = width;
660 }
661 
662 
663 void
664 BFont::SetSpacing(uint8 spacing)
665 {
666 	fSpacing = spacing;
667 }
668 
669 
670 void
671 BFont::SetEncoding(uint8 encoding)
672 {
673 	fEncoding = encoding;
674 }
675 
676 
677 void
678 BFont::SetFace(uint16 face)
679 {
680 	if (face == fFace)
681 		return;
682 
683 	SetFamilyAndFace(NULL, face);
684 }
685 
686 
687 void
688 BFont::SetFlags(uint32 flags)
689 {
690 	fFlags = flags;
691 }
692 
693 
694 void
695 BFont::GetFamilyAndStyle(font_family* family, font_style* style) const
696 {
697 	if (family == NULL && style == NULL)
698 		return;
699 
700 	// it's okay to call this function with either family or style set to NULL
701 
702 	font_family familyBuffer;
703 	font_style styleBuffer;
704 
705 	if (family == NULL)
706 		family = &familyBuffer;
707 	if (style == NULL)
708 		style = &styleBuffer;
709 
710 	BPrivate::AppServerLink link;
711 	link.StartMessage(AS_GET_FAMILY_AND_STYLE);
712 	link.Attach<uint16>(fFamilyID);
713 	link.Attach<uint16>(fStyleID);
714 
715 	int32 code;
716 	if (link.FlushWithReply(code) != B_OK || code != B_OK) {
717 		// the least we can do is to clear the buffers
718 		memset(*family, 0, sizeof(font_family));
719 		memset(*style, 0, sizeof(font_style));
720 		return;
721 	}
722 
723 	link.ReadString(*family, sizeof(font_family));
724 	link.ReadString(*style, sizeof(font_style));
725 }
726 
727 
728 uint32
729 BFont::FamilyAndStyle() const
730 {
731 	return (fFamilyID << 16UL) | fStyleID;
732 }
733 
734 
735 float
736 BFont::Size() const
737 {
738 	return fSize;
739 }
740 
741 
742 float
743 BFont::Shear() const
744 {
745 	return fShear;
746 }
747 
748 
749 float
750 BFont::Rotation() const
751 {
752 	return fRotation;
753 }
754 
755 
756 float
757 BFont::FalseBoldWidth() const
758 {
759 	return fFalseBoldWidth;
760 }
761 
762 
763 uint8
764 BFont::Spacing() const
765 {
766 	return fSpacing;
767 }
768 
769 
770 uint8
771 BFont::Encoding() const
772 {
773 	return fEncoding;
774 }
775 
776 
777 uint16
778 BFont::Face() const
779 {
780 	return fFace;
781 }
782 
783 
784 uint32
785 BFont::Flags() const
786 {
787 	return fFlags;
788 }
789 
790 
791 font_direction
792 BFont::Direction() const
793 {
794 	_GetExtraFlags();
795 	return (font_direction)(fExtraFlags >> B_PRIVATE_FONT_DIRECTION_SHIFT);
796 }
797 
798 
799 bool
800 BFont::IsFixed() const
801 {
802 	_GetExtraFlags();
803 	return (fExtraFlags & B_IS_FIXED) != 0;
804 }
805 
806 
807 // Returns whether or not the font is fixed-width and contains both
808 // full and half-width characters.
809 bool
810 BFont::IsFullAndHalfFixed() const
811 {
812 	// This was left unimplemented as of R5. It is a way to work with both
813 	// Kanji and Roman characters in the same fixed-width font.
814 
815 	_GetExtraFlags();
816 	return (fExtraFlags & B_PRIVATE_FONT_IS_FULL_AND_HALF_FIXED) != 0;
817 }
818 
819 
820 BRect
821 BFont::BoundingBox() const
822 {
823 	BPrivate::AppServerLink link;
824 	link.StartMessage(AS_GET_FONT_BOUNDING_BOX);
825 	link.Attach<uint16>(fFamilyID);
826 	link.Attach<uint16>(fStyleID);
827 
828 	int32 code;
829 	if (link.FlushWithReply(code) != B_OK
830 		|| code != B_OK)
831 		return BRect(0, 0, 0 ,0);
832 
833 	BRect box;
834 	link.Read<BRect>(&box);
835 	return box;
836 }
837 
838 
839 unicode_block
840 BFont::Blocks() const
841 {
842 	BPrivate::AppServerLink link;
843 	link.StartMessage(AS_GET_UNICODE_BLOCKS);
844 	link.Attach<uint16>(fFamilyID);
845 	link.Attach<uint16>(fStyleID);
846 
847 	int32 status;
848 	if (link.FlushWithReply(status) != B_OK
849 		|| status != B_OK) {
850 		return unicode_block(~0LL, ~0LL);
851 	}
852 
853 	unicode_block blocksForFont;
854 	link.Read<unicode_block>(&blocksForFont);
855 
856 	return blocksForFont;
857 }
858 
859 bool
860 BFont::IncludesBlock(uint32 start, uint32 end) const
861 {
862 	BPrivate::AppServerLink link;
863 	link.StartMessage(AS_GET_HAS_UNICODE_BLOCK);
864 	link.Attach<uint16>(fFamilyID);
865 	link.Attach<uint16>(fStyleID);
866 	link.Attach<uint32>(start);
867 	link.Attach<uint32>(end);
868 
869 	int32 status;
870 	if (link.FlushWithReply(status) != B_OK
871 		|| status != B_OK) {
872 		return false;
873 	}
874 
875 	bool hasBlock;
876 	link.Read<bool>(&hasBlock);
877 
878 	return hasBlock;
879 }
880 
881 
882 font_file_format
883 BFont::FileFormat() const
884 {
885 	BPrivate::AppServerLink link;
886 	link.StartMessage(AS_GET_FONT_FILE_FORMAT);
887 	link.Attach<uint16>(fFamilyID);
888 	link.Attach<uint16>(fStyleID);
889 
890 	int32 status;
891 	if (link.FlushWithReply(status) != B_OK
892 		|| status != B_OK) {
893 		// just take a safe bet...
894 		return B_TRUETYPE_WINDOWS;
895 	}
896 
897 	uint16 format;
898 	link.Read<uint16>(&format);
899 
900 	return (font_file_format)format;
901 }
902 
903 
904 int32
905 BFont::CountTuned() const
906 {
907 	BPrivate::AppServerLink link;
908 	link.StartMessage(AS_GET_TUNED_COUNT);
909 	link.Attach<uint16>(fFamilyID);
910 	link.Attach<uint16>(fStyleID);
911 
912 	int32 code;
913 	if (link.FlushWithReply(code) != B_OK
914 		|| code != B_OK)
915 		return -1;
916 
917 	int32 count;
918 	link.Read<int32>(&count);
919 	return count;
920 }
921 
922 
923 void
924 BFont::GetTunedInfo(int32 index, tuned_font_info* info) const
925 {
926 	if (info == NULL)
927 		return;
928 
929 	BPrivate::AppServerLink link;
930 	link.StartMessage(AS_GET_TUNED_INFO);
931 	link.Attach<uint16>(fFamilyID);
932 	link.Attach<uint16>(fStyleID);
933 	link.Attach<uint32>(index);
934 
935 	int32 code;
936 	if (link.FlushWithReply(code) != B_OK || code != B_OK)
937 		return;
938 
939 	link.Read<tuned_font_info>(info);
940 }
941 
942 
943 void
944 BFont::TruncateString(BString* inOut, uint32 mode, float width) const
945 {
946 	if (mode == B_NO_TRUNCATION)
947 		return;
948 
949 	// NOTE: Careful, we cannot directly use "inOut->String()" as result
950 	// array, because the string length increases by 3 bytes in the worst
951 	// case scenario.
952 	const char* string = inOut->String();
953 	GetTruncatedStrings(&string, 1, mode, width, inOut);
954 }
955 
956 
957 void
958 BFont::GetTruncatedStrings(const char* stringArray[], int32 numStrings,
959 	uint32 mode, float width, BString resultArray[]) const
960 {
961 	if (stringArray != NULL && numStrings > 0) {
962 		// the width of the "…" glyph
963 		float ellipsisWidth = StringWidth(B_UTF8_ELLIPSIS);
964 
965 		for (int32 i = 0; i < numStrings; i++) {
966 			resultArray[i] = stringArray[i];
967 			int32 numChars = resultArray[i].CountChars();
968 
969 			// get the escapement of each glyph in font units
970 			float* escapementArray = new float[numChars];
971 			GetEscapements(stringArray[i], numChars, NULL, escapementArray);
972 
973 			truncate_string(resultArray[i], mode, width, escapementArray,
974 				fSize, ellipsisWidth, numChars);
975 
976 			delete[] escapementArray;
977 		}
978 	}
979 }
980 
981 
982 void
983 BFont::GetTruncatedStrings(const char* stringArray[], int32 numStrings,
984 	uint32 mode, float width, char* resultArray[]) const
985 {
986 	if (stringArray != NULL && numStrings > 0) {
987 		for (int32 i = 0; i < numStrings; i++) {
988 			BString* strings = new BString[numStrings];
989 			GetTruncatedStrings(stringArray, numStrings, mode, width, strings);
990 
991 			for (int32 i = 0; i < numStrings; i++)
992 				strcpy(resultArray[i], strings[i].String());
993 
994 			delete[] strings;
995 		}
996 	}
997 }
998 
999 
1000 float
1001 BFont::StringWidth(const char* string) const
1002 {
1003 	if (string == NULL)
1004 		return 0.0;
1005 
1006 	int32 length = strlen(string);
1007 	float width;
1008 	GetStringWidths(&string, &length, 1, &width);
1009 
1010 	return width;
1011 }
1012 
1013 
1014 float
1015 BFont::StringWidth(const char* string, int32 length) const
1016 {
1017 	if (!string || length < 1)
1018 		return 0.0f;
1019 
1020 	float width = 0.0f;
1021 	GetStringWidths(&string, &length, 1, &width);
1022 
1023 	return width;
1024 }
1025 
1026 
1027 void
1028 BFont::GetStringWidths(const char* stringArray[], const int32 lengthArray[],
1029 	int32 numStrings, float widthArray[]) const
1030 {
1031 	if (stringArray == NULL || lengthArray == NULL || numStrings < 1
1032 		|| widthArray == NULL) {
1033 		return;
1034 	}
1035 
1036 	BPrivate::AppServerLink link;
1037 	link.StartMessage(AS_GET_STRING_WIDTHS);
1038 	link.Attach<uint16>(fFamilyID);
1039 	link.Attach<uint16>(fStyleID);
1040 	link.Attach<float>(fSize);
1041 	link.Attach<uint8>(fSpacing);
1042 	link.Attach<int32>(numStrings);
1043 
1044 	// TODO: all strings into a single array???
1045 	// we do have a maximum message length, and it could be easily touched
1046 	// here...
1047 	for (int32 i = 0; i < numStrings; i++)
1048 		link.AttachString(stringArray[i], lengthArray[i]);
1049 
1050 	status_t status;
1051 	if (link.FlushWithReply(status) != B_OK || status != B_OK)
1052 		return;
1053 
1054 	link.Read(widthArray, sizeof(float) * numStrings);
1055 }
1056 
1057 
1058 void
1059 BFont::GetEscapements(const char charArray[], int32 numChars,
1060 	float escapementArray[]) const
1061 {
1062 	GetEscapements(charArray, numChars, NULL, escapementArray);
1063 }
1064 
1065 
1066 void
1067 BFont::GetEscapements(const char charArray[], int32 numChars,
1068 	escapement_delta* delta, float escapementArray[]) const
1069 {
1070 	if (charArray == NULL || numChars < 1 || escapementArray == NULL)
1071 		return;
1072 
1073 	BPrivate::AppServerLink link;
1074 	link.StartMessage(AS_GET_ESCAPEMENTS_AS_FLOATS);
1075 	link.Attach<uint16>(fFamilyID);
1076 	link.Attach<uint16>(fStyleID);
1077 	link.Attach<float>(fSize);
1078 	link.Attach<uint8>(fSpacing);
1079 	link.Attach<float>(fRotation);
1080 	link.Attach<uint32>(fFlags);
1081 
1082 	link.Attach<float>(delta ? delta->nonspace : 0.0f);
1083 	link.Attach<float>(delta ? delta->space : 0.0f);
1084 	link.Attach<int32>(numChars);
1085 
1086 	// TODO: Should we not worry about the port capacity here?!?
1087 	uint32 bytesInBuffer = UTF8CountBytes(charArray, numChars);
1088 	link.Attach<int32>(bytesInBuffer);
1089 	link.Attach(charArray, bytesInBuffer);
1090 
1091 	int32 code;
1092 	if (link.FlushWithReply(code) != B_OK || code != B_OK)
1093 		return;
1094 
1095 	link.Read(escapementArray, numChars * sizeof(float));
1096 }
1097 
1098 
1099 void
1100 BFont::GetEscapements(const char charArray[], int32 numChars,
1101 	escapement_delta* delta, BPoint escapementArray[]) const
1102 {
1103 	GetEscapements(charArray, numChars, delta, escapementArray, NULL);
1104 }
1105 
1106 
1107 void
1108 BFont::GetEscapements(const char charArray[], int32 numChars,
1109 	escapement_delta* delta, BPoint escapementArray[],
1110 	BPoint offsetArray[]) const
1111 {
1112 	if (charArray == NULL || numChars < 1 || escapementArray == NULL)
1113 		return;
1114 
1115 	BPrivate::AppServerLink link;
1116 	link.StartMessage(AS_GET_ESCAPEMENTS);
1117 	link.Attach<uint16>(fFamilyID);
1118 	link.Attach<uint16>(fStyleID);
1119 	link.Attach<float>(fSize);
1120 	link.Attach<uint8>(fSpacing);
1121 	link.Attach<float>(fRotation);
1122 	link.Attach<uint32>(fFlags);
1123 
1124 	link.Attach<float>(delta ? delta->nonspace : 0.0);
1125 	link.Attach<float>(delta ? delta->space : 0.0);
1126 	link.Attach<bool>(offsetArray != NULL);
1127 	link.Attach<int32>(numChars);
1128 
1129 	// TODO: Should we not worry about the port capacity here?!?
1130 	uint32 bytesInBuffer = UTF8CountBytes(charArray, numChars);
1131 	link.Attach<int32>(bytesInBuffer);
1132 	link.Attach(charArray, bytesInBuffer);
1133 
1134 	int32 code;
1135 	if (link.FlushWithReply(code) != B_OK || code != B_OK)
1136 		return;
1137 
1138 	link.Read(escapementArray, sizeof(BPoint) * numChars);
1139 	if (offsetArray)
1140 		link.Read(offsetArray, sizeof(BPoint) * numChars);
1141 }
1142 
1143 
1144 void
1145 BFont::GetEdges(const char charArray[], int32 numChars,
1146 	edge_info edgeArray[]) const
1147 {
1148 	if (!charArray || numChars < 1 || !edgeArray)
1149 		return;
1150 
1151 	int32 code;
1152 	BPrivate::AppServerLink link;
1153 
1154 	link.StartMessage(AS_GET_EDGES);
1155 	link.Attach<uint16>(fFamilyID);
1156 	link.Attach<uint16>(fStyleID);
1157 	link.Attach<int32>(numChars);
1158 
1159 	uint32 bytesInBuffer = UTF8CountBytes(charArray, numChars);
1160 	link.Attach<int32>(bytesInBuffer);
1161 	link.Attach(charArray, bytesInBuffer);
1162 
1163 	if (link.FlushWithReply(code) != B_OK || code != B_OK)
1164 		return;
1165 
1166 	link.Read(edgeArray, sizeof(edge_info) * numChars);
1167 }
1168 
1169 
1170 void
1171 BFont::GetHeight(font_height* _height) const
1172 {
1173 	if (_height == NULL)
1174 		return;
1175 
1176 	if (fHeight.ascent == kUninitializedAscent) {
1177 		// we don't have the font height cached yet
1178 		BPrivate::AppServerLink link;
1179 
1180 		link.StartMessage(AS_GET_FONT_HEIGHT);
1181 		link.Attach<uint16>(fFamilyID);
1182 		link.Attach<uint16>(fStyleID);
1183 		link.Attach<float>(fSize);
1184 
1185 		int32 code;
1186 		if (link.FlushWithReply(code) != B_OK || code != B_OK)
1187 			return;
1188 
1189 		// Who put that "const" to this method? :-)
1190 		// We made fHeight mutable for this, but we should drop the "const"
1191 		// when we can
1192 		link.Read<font_height>(&fHeight);
1193 	}
1194 
1195 	*_height = fHeight;
1196 }
1197 
1198 
1199 void
1200 BFont::GetBoundingBoxesAsGlyphs(const char charArray[], int32 numChars,
1201 	font_metric_mode mode, BRect boundingBoxArray[]) const
1202 {
1203 	_GetBoundingBoxes(charArray, numChars, mode, false, NULL,
1204 		boundingBoxArray, false);
1205 }
1206 
1207 
1208 void
1209 BFont::GetBoundingBoxesAsString(const char charArray[], int32 numChars,
1210 	font_metric_mode mode, escapement_delta* delta,
1211 	BRect boundingBoxArray[]) const
1212 {
1213 	_GetBoundingBoxes(charArray, numChars, mode, true, delta,
1214 		boundingBoxArray, true);
1215 }
1216 
1217 
1218 void
1219 BFont::_GetBoundingBoxes(const char charArray[], int32 numChars,
1220 	font_metric_mode mode, bool string_escapement, escapement_delta* delta,
1221 	BRect boundingBoxArray[], bool asString) const
1222 {
1223 	if (charArray == NULL || numChars < 1 || boundingBoxArray == NULL)
1224 		return;
1225 
1226 	int32 code;
1227 	BPrivate::AppServerLink link;
1228 
1229 	link.StartMessage(asString
1230 		? AS_GET_BOUNDINGBOXES_STRING : AS_GET_BOUNDINGBOXES_CHARS);
1231 	link.Attach<uint16>(fFamilyID);
1232 	link.Attach<uint16>(fStyleID);
1233 	link.Attach<float>(fSize);
1234 	link.Attach<float>(fRotation);
1235 	link.Attach<float>(fShear);
1236 	link.Attach<float>(fFalseBoldWidth);
1237 	link.Attach<uint8>(fSpacing);
1238 
1239 	link.Attach<uint32>(fFlags);
1240 	link.Attach<font_metric_mode>(mode);
1241 	link.Attach<bool>(string_escapement);
1242 
1243 	if (delta != NULL) {
1244 		link.Attach<escapement_delta>(*delta);
1245 	} else {
1246 		escapement_delta emptyDelta = {0, 0};
1247 		link.Attach<escapement_delta>(emptyDelta);
1248 	}
1249 
1250 	link.Attach<int32>(numChars);
1251 	uint32 bytesInBuffer = UTF8CountBytes(charArray, numChars);
1252 	link.Attach<int32>(bytesInBuffer);
1253 	link.Attach(charArray, bytesInBuffer);
1254 
1255 	if (link.FlushWithReply(code) != B_OK || code != B_OK)
1256 		return;
1257 
1258 	link.Read(boundingBoxArray, sizeof(BRect) * numChars);
1259 }
1260 
1261 
1262 void
1263 BFont::GetBoundingBoxesForStrings(const char* stringArray[], int32 numStrings,
1264 	font_metric_mode mode, escapement_delta deltas[],
1265 	BRect boundingBoxArray[]) const
1266 {
1267 	if (!stringArray || numStrings < 1 || !boundingBoxArray)
1268 		return;
1269 
1270 	int32 code;
1271 	BPrivate::AppServerLink link;
1272 
1273 	link.StartMessage(AS_GET_BOUNDINGBOXES_STRINGS);
1274 	link.Attach<uint16>(fFamilyID);
1275 	link.Attach<uint16>(fStyleID);
1276 	link.Attach<float>(fSize);
1277 	link.Attach<float>(fRotation);
1278 	link.Attach<float>(fShear);
1279 	link.Attach<float>(fFalseBoldWidth);
1280 	link.Attach<uint8>(fSpacing);
1281 	link.Attach<uint32>(fFlags);
1282 	link.Attach<font_metric_mode>(mode);
1283 	link.Attach<int32>(numStrings);
1284 
1285 	if (deltas) {
1286 		for (int32 i = 0; i < numStrings; i++) {
1287 			link.AttachString(stringArray[i]);
1288 			link.Attach<escapement_delta>(deltas[i]);
1289 		}
1290 	} else {
1291 		escapement_delta emptyDelta = {0, 0};
1292 
1293 		for (int32 i = 0; i < numStrings; i++) {
1294 			link.AttachString(stringArray[i]);
1295 			link.Attach<escapement_delta>(emptyDelta);
1296 		}
1297 	}
1298 
1299 	if (link.FlushWithReply(code) != B_OK || code != B_OK)
1300 		return;
1301 
1302 	link.Read(boundingBoxArray, sizeof(BRect) * numStrings);
1303 }
1304 
1305 
1306 void
1307 BFont::GetGlyphShapes(const char charArray[], int32 numChars,
1308 	BShape* glyphShapeArray[]) const
1309 {
1310 	// TODO: implement code specifically for passing BShapes to and
1311 	// from the server
1312 	if (!charArray || numChars < 1 || !glyphShapeArray)
1313 		return;
1314 
1315 	int32 code;
1316 	BPrivate::AppServerLink link;
1317 
1318 	link.StartMessage(AS_GET_GLYPH_SHAPES);
1319 	link.Attach<uint16>(fFamilyID);
1320 	link.Attach<uint16>(fStyleID);
1321 	link.Attach<float>(fSize);
1322 	link.Attach<float>(fShear);
1323 	link.Attach<float>(fRotation);
1324 	link.Attach<float>(fFalseBoldWidth);
1325 	link.Attach<uint32>(fFlags);
1326 	link.Attach<int32>(numChars);
1327 
1328 	uint32 bytesInBuffer = UTF8CountBytes(charArray, numChars);
1329 	link.Attach<int32>(bytesInBuffer);
1330 	link.Attach(charArray, bytesInBuffer);
1331 
1332 	if (link.FlushWithReply(code) != B_OK || code != B_OK)
1333 		return;
1334 
1335 	for (int32 i = 0; i < numChars; i++)
1336 		link.ReadShape(glyphShapeArray[i]);
1337 }
1338 
1339 
1340 void
1341 BFont::GetHasGlyphs(const char charArray[], int32 numChars,
1342 	bool hasArray[]) const
1343 {
1344 	if (!charArray || numChars < 1 || !hasArray)
1345 		return;
1346 
1347 	int32 code;
1348 	BPrivate::AppServerLink link;
1349 
1350 	link.StartMessage(AS_GET_HAS_GLYPHS);
1351 	link.Attach<uint16>(fFamilyID);
1352 	link.Attach<uint16>(fStyleID);
1353 	link.Attach<int32>(numChars);
1354 
1355 	uint32 bytesInBuffer = UTF8CountBytes(charArray, numChars);
1356 	link.Attach<int32>(bytesInBuffer);
1357 	link.Attach(charArray, bytesInBuffer);
1358 
1359 	if (link.FlushWithReply(code) != B_OK || code != B_OK)
1360 		return;
1361 
1362 	link.Read(hasArray, sizeof(bool) * numChars);
1363 }
1364 
1365 
1366 BFont&
1367 BFont::operator=(const BFont& font)
1368 {
1369 	fFamilyID = font.fFamilyID;
1370 	fStyleID = font.fStyleID;
1371 	fSize = font.fSize;
1372 	fShear = font.fShear;
1373 	fRotation = font.fRotation;
1374 	fFalseBoldWidth = font.fFalseBoldWidth;
1375 	fSpacing = font.fSpacing;
1376 	fEncoding = font.fEncoding;
1377 	fFace = font.fFace;
1378 	fHeight = font.fHeight;
1379 	fFlags = font.fFlags;
1380 	fExtraFlags = font.fExtraFlags;
1381 
1382 	return *this;
1383 }
1384 
1385 
1386 bool
1387 BFont::operator==(const BFont& font) const
1388 {
1389 	return fFamilyID == font.fFamilyID
1390 		&& fStyleID == font.fStyleID
1391 		&& fSize == font.fSize
1392 		&& fShear == font.fShear
1393 		&& fRotation == font.fRotation
1394 		&& fFalseBoldWidth == font.fFalseBoldWidth
1395 		&& fSpacing == font.fSpacing
1396 		&& fEncoding == font.fEncoding
1397 		&& fFace == font.fFace;
1398 }
1399 
1400 
1401 bool
1402 BFont::operator!=(const BFont& font) const
1403 {
1404 	return fFamilyID != font.fFamilyID
1405 		|| fStyleID != font.fStyleID
1406 		|| fSize != font.fSize
1407 		|| fShear != font.fShear
1408 		|| fRotation != font.fRotation
1409 		|| fFalseBoldWidth != font.fFalseBoldWidth
1410 		|| fSpacing != font.fSpacing
1411 		|| fEncoding != font.fEncoding
1412 		|| fFace != font.fFace;
1413 }
1414 
1415 
1416 void
1417 BFont::PrintToStream() const
1418 {
1419 	font_family family;
1420 	font_style style;
1421 	GetFamilyAndStyle(&family, &style);
1422 
1423 	printf("BFont { %s (%d), %s (%d) 0x%x %f/%f %fpt (%f %f %f), %d }\n",
1424 		family, fFamilyID, style, fStyleID, fFace, fShear, fRotation, fSize,
1425 		fHeight.ascent, fHeight.descent, fHeight.leading, fEncoding);
1426 }
1427 
1428 
1429 void
1430 BFont::_GetExtraFlags() const
1431 {
1432 	// TODO: this has to be const in order to allow other font getters to
1433 	// stay const as well
1434 	if (fExtraFlags != kUninitializedExtraFlags)
1435 		return;
1436 
1437 	BPrivate::AppServerLink link;
1438 	link.StartMessage(AS_GET_EXTRA_FONT_FLAGS);
1439 	link.Attach<uint16>(fFamilyID);
1440 	link.Attach<uint16>(fStyleID);
1441 
1442 	status_t status = B_ERROR;
1443 	if (link.FlushWithReply(status) != B_OK || status != B_OK) {
1444 		// use defaut values for the flags
1445 		fExtraFlags = (uint32)B_FONT_LEFT_TO_RIGHT
1446 			<< B_PRIVATE_FONT_DIRECTION_SHIFT;
1447 		return;
1448 	}
1449 
1450 	link.Read<uint32>(&fExtraFlags);
1451 }
1452