xref: /haiku/src/kits/interface/Font.cpp (revision 302f62604763c95777d6d04cca456e876f471c4f)
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<uint8>(fSpacing);
1051 	link.Attach<float>(fRotation);
1052 	link.Attach<uint32>(fFlags);
1053 
1054 	link.Attach<float>(delta ? delta->nonspace : 0.0f);
1055 	link.Attach<float>(delta ? delta->space : 0.0f);
1056 	link.Attach<int32>(numChars);
1057 
1058 	// TODO: Should we not worry about the port capacity here?!?
1059 	uint32 bytesInBuffer = UTF8CountBytes(charArray, numChars);
1060 	link.Attach<int32>(bytesInBuffer);
1061 	link.Attach(charArray, bytesInBuffer);
1062 
1063 	int32 code;
1064 	if (link.FlushWithReply(code) != B_OK
1065 		|| code != B_OK)
1066 		return;
1067 
1068 	link.Read(escapementArray, numChars * sizeof(float));
1069 }
1070 
1071 
1072 void
1073 BFont::GetEscapements(const char charArray[], int32 numChars, escapement_delta *delta,
1074 	BPoint escapementArray[]) const
1075 {
1076 	GetEscapements(charArray, numChars, delta, escapementArray, NULL);
1077 }
1078 
1079 
1080 void
1081 BFont::GetEscapements(const char charArray[], int32 numChars, escapement_delta *delta,
1082 	BPoint escapementArray[], BPoint offsetArray[]) const
1083 {
1084 	if (!charArray || numChars < 1 || !escapementArray)
1085 		return;
1086 
1087 	BPrivate::AppServerLink link;
1088 	link.StartMessage(AS_GET_ESCAPEMENTS);
1089 	link.Attach<uint16>(fFamilyID);
1090 	link.Attach<uint16>(fStyleID);
1091 	link.Attach<float>(fSize);
1092 	link.Attach<uint8>(fSpacing);
1093 	link.Attach<float>(fRotation);
1094 	link.Attach<uint32>(fFlags);
1095 
1096 	link.Attach<float>(delta ? delta->nonspace : 0.0);
1097 	link.Attach<float>(delta ? delta->space : 0.0);
1098 	link.Attach<bool>(offsetArray != NULL);
1099 	link.Attach<int32>(numChars);
1100 
1101 	// TODO: Should we not worry about the port capacity here?!?
1102 	uint32 bytesInBuffer = UTF8CountBytes(charArray, numChars);
1103 	link.Attach<int32>(bytesInBuffer);
1104 	link.Attach(charArray, bytesInBuffer);
1105 
1106 	int32 code;
1107 	if (link.FlushWithReply(code) != B_OK
1108 		|| code != B_OK)
1109 		return;
1110 
1111 	link.Read(escapementArray, sizeof(BPoint) * numChars);
1112 	if (offsetArray)
1113 		link.Read(offsetArray, sizeof(BPoint) * numChars);
1114 }
1115 
1116 
1117 void
1118 BFont::GetEdges(const char charArray[], int32 numChars, edge_info edgeArray[]) const
1119 {
1120 	if (!charArray || numChars < 1 || !edgeArray)
1121 		return;
1122 
1123 	int32 code;
1124 	BPrivate::AppServerLink link;
1125 
1126 	link.StartMessage(AS_GET_EDGES);
1127 	link.Attach<int32>(numChars);
1128 
1129 	uint32 bytesInBuffer = UTF8CountBytes(charArray, numChars);
1130 	link.Attach<int32>(bytesInBuffer);
1131 	link.Attach(charArray, bytesInBuffer);
1132 
1133 	if (link.FlushWithReply(code) != B_OK
1134 		|| code != B_OK)
1135 		return;
1136 
1137 	link.Read(edgeArray, sizeof(edge_info) * numChars);
1138 }
1139 
1140 
1141 void
1142 BFont::GetHeight(font_height *_height) const
1143 {
1144 	if (_height == NULL)
1145 		return;
1146 
1147 	if (fHeight.ascent == kUninitializedAscent) {
1148 		// we don't have the font height cached yet
1149 		BPrivate::AppServerLink link;
1150 
1151 		link.StartMessage(AS_GET_FONT_HEIGHT);
1152 		link.Attach<uint16>(fFamilyID);
1153 		link.Attach<uint16>(fStyleID);
1154 		link.Attach<float>(fSize);
1155 
1156 		int32 code;
1157 		if (link.FlushWithReply(code) != B_OK
1158 			|| code != B_OK)
1159 			return;
1160 
1161 		// Who put that "const" to this method? :-)
1162 		// We made fHeight mutable for this, but we should drop the "const" when we can
1163 		link.Read<font_height>(&fHeight);
1164 	}
1165 
1166 	*_height = fHeight;
1167 }
1168 
1169 
1170 void
1171 BFont::GetBoundingBoxesAsGlyphs(const char charArray[], int32 numChars, font_metric_mode mode,
1172 	BRect boundingBoxArray[]) const
1173 {
1174 	_GetBoundingBoxes(charArray, numChars, mode, false, NULL, boundingBoxArray);
1175 }
1176 
1177 
1178 void
1179 BFont::GetBoundingBoxesAsString(const char charArray[], int32 numChars, font_metric_mode mode,
1180 	escapement_delta *delta, BRect boundingBoxArray[]) const
1181 {
1182 	_GetBoundingBoxes(charArray, numChars, mode, true, delta, boundingBoxArray);
1183 }
1184 
1185 
1186 void
1187 BFont::_GetBoundingBoxes(const char charArray[], int32 numChars, font_metric_mode mode,
1188 	bool string_escapement, escapement_delta *delta, BRect boundingBoxArray[]) const
1189 {
1190 	if (!charArray || numChars < 1 || !boundingBoxArray)
1191 		return;
1192 
1193 	int32 code;
1194 	BPrivate::AppServerLink link;
1195 
1196 	link.StartMessage(AS_GET_BOUNDINGBOXES_CHARS);
1197 	link.Attach<uint16>(fFamilyID);
1198 	link.Attach<uint16>(fStyleID);
1199 	link.Attach<float>(fSize);
1200 	link.Attach<float>(fRotation);
1201 	link.Attach<float>(fShear);
1202 	link.Attach<uint8>(fSpacing);
1203 
1204 	link.Attach<uint32>(fFlags);
1205 	link.Attach<font_metric_mode>(mode);
1206 	link.Attach<bool>(string_escapement);
1207 
1208 	if (delta) {
1209 		link.Attach<escapement_delta>(*delta);
1210 	} else {
1211 		escapement_delta emptyDelta = {0, 0};
1212 		link.Attach<escapement_delta>(emptyDelta);
1213 	}
1214 
1215 	link.Attach<int32>(numChars);
1216 	uint32 bytesInBuffer = UTF8CountBytes(charArray, numChars);
1217 	link.Attach<int32>(bytesInBuffer);
1218 	link.Attach(charArray, bytesInBuffer);
1219 
1220 	if (link.FlushWithReply(code) != B_OK
1221 		|| code != B_OK)
1222 		return;
1223 
1224 	link.Read(boundingBoxArray, sizeof(BRect) * numChars);
1225 }
1226 
1227 
1228 void
1229 BFont::GetBoundingBoxesForStrings(const char *stringArray[], int32 numStrings,
1230 	font_metric_mode mode, escapement_delta deltas[], BRect boundingBoxArray[]) const
1231 {
1232 	if (!stringArray || numStrings < 1 || !boundingBoxArray)
1233 		return;
1234 
1235 	int32 code;
1236 	BPrivate::AppServerLink link;
1237 
1238 	link.StartMessage(AS_GET_BOUNDINGBOXES_STRINGS);
1239 	link.Attach<uint16>(fFamilyID);
1240 	link.Attach<uint16>(fStyleID);
1241 	link.Attach<float>(fSize);
1242 	link.Attach<float>(fRotation);
1243 	link.Attach<float>(fShear);
1244 	link.Attach<uint8>(fSpacing);
1245 	link.Attach<uint32>(fFlags);
1246 	link.Attach<font_metric_mode>(mode);
1247 	link.Attach<int32>(numStrings);
1248 
1249 	if (deltas) {
1250 		for (int32 i = 0; i < numStrings; i++) {
1251 			link.AttachString(stringArray[i]);
1252 			link.Attach<escapement_delta>(deltas[i]);
1253 		}
1254 	} else {
1255 		escapement_delta emptyDelta = {0, 0};
1256 
1257 		for (int32 i = 0; i < numStrings; i++) {
1258 			link.AttachString(stringArray[i]);
1259 			link.Attach<escapement_delta>(emptyDelta);
1260 		}
1261 	}
1262 
1263 	if (link.FlushWithReply(code) != B_OK
1264 		|| code != B_OK)
1265 		return;
1266 
1267 	link.Read(boundingBoxArray, sizeof(BRect) * numStrings);
1268 }
1269 
1270 
1271 void
1272 BFont::GetGlyphShapes(const char charArray[], int32 numChars, BShape *glyphShapeArray[]) const
1273 {
1274 	// TODO: implement code specifically for passing BShapes to and from the server
1275 	if (!charArray || numChars < 1 || !glyphShapeArray)
1276 		return;
1277 
1278 	int32 code;
1279 	BPrivate::AppServerLink link;
1280 
1281 	link.StartMessage(AS_GET_GLYPH_SHAPES);
1282 	link.Attach<uint16>(fFamilyID);
1283 	link.Attach<uint16>(fStyleID);
1284 	link.Attach<float>(fSize);
1285 	link.Attach<float>(fShear);
1286 	link.Attach<float>(fRotation);
1287 	link.Attach<uint32>(fFlags);
1288 	link.Attach<int32>(numChars);
1289 
1290 	uint32 bytesInBuffer = UTF8CountBytes(charArray, numChars);
1291 	link.Attach<int32>(bytesInBuffer);
1292 	link.Attach(charArray, bytesInBuffer);
1293 
1294 	if (link.FlushWithReply(code) != B_OK
1295 		|| code != B_OK)
1296 		return;
1297 
1298 	for (int32 i = 0; i < numChars; i++)
1299 		link.ReadShape(glyphShapeArray[i]);
1300 }
1301 
1302 
1303 void
1304 BFont::GetHasGlyphs(const char charArray[], int32 numChars, bool hasArray[]) const
1305 {
1306 	if (!charArray || numChars < 1 || !hasArray)
1307 		return;
1308 
1309 	int32 code;
1310 	BPrivate::AppServerLink link;
1311 
1312 	link.StartMessage(AS_GET_HAS_GLYPHS);
1313 	link.Attach<uint16>(fFamilyID);
1314 	link.Attach<uint16>(fStyleID);
1315 	link.Attach<int32>(numChars);
1316 
1317 	uint32 bytesInBuffer = UTF8CountBytes(charArray, numChars);
1318 	link.Attach<int32>(bytesInBuffer);
1319 	link.Attach(charArray, bytesInBuffer);
1320 
1321 	if (link.FlushWithReply(code) != B_OK
1322 		|| code != B_OK)
1323 		return;
1324 
1325 	link.Read(hasArray, sizeof(bool) * numChars);
1326 }
1327 
1328 
1329 BFont &
1330 BFont::operator=(const BFont &font)
1331 {
1332 	fFamilyID = font.fFamilyID;
1333 	fStyleID = font.fStyleID;
1334 	fSize = font.fSize;
1335 	fShear = font.fShear;
1336 	fRotation = font.fRotation;
1337 	fSpacing = font.fSpacing;
1338 	fEncoding = font.fEncoding;
1339 	fFace = font.fFace;
1340 	fHeight = font.fHeight;
1341 	fFlags = font.fFlags;
1342 	return *this;
1343 }
1344 
1345 
1346 bool
1347 BFont::operator==(const BFont &font) const
1348 {
1349 	return fFamilyID == font.fFamilyID
1350 		&& fStyleID == font.fStyleID
1351 		&& fSize == font.fSize
1352 		&& fShear == font.fShear
1353 		&& fRotation == font.fRotation
1354 		&& fSpacing == font.fSpacing
1355 		&& fEncoding == font.fEncoding
1356 		&& fFace == font.fFace;
1357 }
1358 
1359 
1360 bool
1361 BFont::operator!=(const BFont &font) const
1362 {
1363 	return fFamilyID != font.fFamilyID
1364 		|| fStyleID != font.fStyleID
1365 		|| fSize != font.fSize
1366 		|| fShear != font.fShear
1367 		|| fRotation != font.fRotation
1368 		|| fSpacing != font.fSpacing
1369 		|| fEncoding != font.fEncoding
1370 		|| fFace != font.fFace;
1371 }
1372 
1373 
1374 void
1375 BFont::PrintToStream() const
1376 {
1377 	font_family family;
1378 	font_style style;
1379 	GetFamilyAndStyle(&family, &style);
1380 
1381 	printf("BFont { %s (%d), %s (%d) 0x%x %f/%f %fpt (%f %f %f), %d }\n", family,
1382 		fFamilyID, style, fStyleID, fFace, fShear, fRotation, fSize,
1383 		fHeight.ascent, fHeight.descent, fHeight.leading, fEncoding);
1384 }
1385 
1386 
1387 void
1388 BFont::_GetExtraFlags() const
1389 {
1390 	// TODO: this has to be const in order to allow other font getters to stay const as well
1391 	if (fExtraFlags != kUninitializedExtraFlags)
1392 		return;
1393 
1394 	BPrivate::AppServerLink link;
1395 	link.StartMessage(AS_GET_EXTRA_FONT_FLAGS);
1396 	link.Attach<uint16>(fFamilyID);
1397 	link.Attach<uint16>(fStyleID);
1398 
1399 	status_t status = B_ERROR;
1400 	if (link.FlushWithReply(status) != B_OK
1401 		|| status != B_OK) {
1402 		// use defaut values for the flags
1403 		fExtraFlags = (uint32)B_FONT_LEFT_TO_RIGHT << B_PRIVATE_FONT_DIRECTION_SHIFT;
1404 		return;
1405 	}
1406 
1407 	link.Read<uint32>(&fExtraFlags);
1408 }
1409