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