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