xref: /haiku/src/kits/interface/Font.cpp (revision cfc3fa87da824bdf593eb8b817a83b6376e77935)
1 /*
2  * Copyright 2001-2008, 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,
227 		compare_families));
228 	return fLastFamily;
229 }
230 
231 
232 status_t
233 FontList::FamilyAt(int32 index, font_family *_family, uint32 *_flags)
234 {
235 	BAutolock locker(this);
236 
237 	status_t status = _UpdateIfNecessary();
238 	if (status < B_OK)
239 		return status;
240 
241 	::family* family = fFamilies.ItemAt(index);
242 	if (family == NULL)
243 		return B_BAD_VALUE;
244 
245 	memcpy(*_family, family->name.String(), family->name.Length() + 1);
246 	if (_flags)
247 		*_flags = family->flags;
248 	return B_OK;
249 }
250 
251 
252 status_t
253 FontList::StyleAt(font_family familyName, int32 index, font_style *_style,
254 	uint16 *_face, uint32 *_flags)
255 {
256 	BAutolock locker(this);
257 
258 	status_t status = _UpdateIfNecessary();
259 	if (status < B_OK)
260 		return status;
261 
262 	::family* family = _FindFamily(familyName);
263 	if (family == NULL)
264 		return B_BAD_VALUE;
265 
266 	::style* style = family->styles.ItemAt(index);
267 	if (style == NULL)
268 		return B_BAD_VALUE;
269 
270 	memcpy(*_style, style->name.String(), style->name.Length() + 1);
271 	if (_face)
272 		*_face = style->face;
273 	if (_flags)
274 		*_flags = style->flags;
275 	return B_OK;
276 }
277 
278 
279 int32
280 FontList::CountFamilies()
281 {
282 	BAutolock locker(this);
283 
284 	_UpdateIfNecessary();
285 	return fFamilies.CountItems();
286 }
287 
288 
289 int32
290 FontList::CountStyles(font_family familyName)
291 {
292 	BAutolock locker(this);
293 
294 	_UpdateIfNecessary();
295 
296 	::family* family = _FindFamily(familyName);
297 	if (family == NULL)
298 		return 0;
299 
300 	return family->styles.CountItems();
301 }
302 
303 }	// namespace BPrivate
304 
305 
306 //	#pragma mark -
307 
308 
309 void
310 _init_global_fonts_()
311 {
312 	BPrivate::AppServerLink link;
313 	link.StartMessage(AS_GET_SYSTEM_FONTS);
314 
315 	int32 code;
316 	if (link.FlushWithReply(code) != B_OK
317 		|| code != B_OK) {
318 		printf("DEBUG: Couldn't initialize global fonts!\n");
319 		return;
320 	}
321 
322 	char type[B_OS_NAME_LENGTH];
323 
324 	while (link.ReadString(type, sizeof(type)) >= B_OK && type[0]) {
325 		BFont dummy;
326 		BFont *font = &dummy;
327 
328 		if (!strcmp(type, "plain"))
329 			font = &sPlainFont;
330 		else if (!strcmp(type, "bold"))
331 			font = &sBoldFont;
332 		else if (!strcmp(type, "fixed"))
333 			font = &sFixedFont;
334 
335 		link.Read<uint16>(&font->fFamilyID);
336 		link.Read<uint16>(&font->fStyleID);
337 		link.Read<float>(&font->fSize);
338 		link.Read<uint16>(&font->fFace);
339 		link.Read<uint32>(&font->fFlags);
340 
341 		font->fHeight.ascent = kUninitializedAscent;
342 		font->fExtraFlags = kUninitializedExtraFlags;
343 	}
344 }
345 
346 
347 void _font_control_(BFont *font, int32 cmd, void *data)
348 {
349 }
350 
351 status_t get_font_cache_info(uint32 id, void *set)
352 {
353 	return B_ERROR;
354 }
355 
356 status_t set_font_cache_info(uint32 id, void *set)
357 {
358 	return B_ERROR;
359 }
360 
361 
362 /*!
363 	\brief Private function used to replace the R5 hack which sets a system font
364 	\param which string denoting which font to set
365 	\param family the new family for the system font
366 	\param style the new style for the system font
367 	\param size the size for the system font to have
368 
369 	R5 used a global area offset table to set the system fonts in the Font
370 	preferences panel. Bleah.
371 */
372 void
373 _set_system_font_(const char *which, font_family family, font_style style,
374 	float size)
375 {
376 	BPrivate::AppServerLink link;
377 
378 	link.StartMessage(AS_SET_SYSTEM_FONT);
379 	link.AttachString(which, B_OS_NAME_LENGTH);
380 	link.AttachString(family, sizeof(font_family));
381 	link.AttachString(style, sizeof(font_style));
382 	link.Attach<float>(size);
383 	link.Flush();
384 }
385 
386 
387 status_t
388 _get_system_default_font_(const char* which, font_family family,
389 	font_style style, float* _size)
390 {
391 	BPrivate::AppServerLink link;
392 
393 	link.StartMessage(AS_GET_SYSTEM_DEFAULT_FONT);
394 	link.AttachString(which, B_OS_NAME_LENGTH);
395 
396 	int32 status = B_ERROR;
397 	if (link.FlushWithReply(status) != B_OK
398 		|| status < B_OK)
399 		return status;
400 
401 	link.ReadString(family, sizeof(font_family));
402 	link.ReadString(style, sizeof(font_style));
403 	link.Read<float>(_size);
404 	return B_OK;
405 }
406 
407 
408 /*!
409 	\brief Returns the number of installed font families
410 	\return The number of installed font families
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 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
435 		B_HAS_TUNED_FONT are returned
436 	\return B_ERROR if the index does not correspond to a font family
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
453 		B_HAS_TUNED_FONT are returned
454 	\return B_ERROR if the index does not correspond to a font style
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
470 		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
474 	time, the value returned should be fairly reliable, returning the proper
475 	flag for 90%-99% of font names.
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 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 	fFalseBoldWidth(0.0),
512 	fSpacing(0),
513 	fEncoding(0),
514 	fFace(0),
515 	fFlags(0),
516 	fExtraFlags(kUninitializedExtraFlags)
517 {
518 	if (be_plain_font != NULL && this != &sPlainFont)
519 		*this = *be_plain_font;
520 	else {
521 		fHeight.ascent = 7.0;
522 		fHeight.descent = 2.0;
523 		fHeight.leading = 13.0;
524 	}
525 }
526 
527 
528 BFont::BFont(const BFont &font)
529 {
530 	*this = font;
531 }
532 
533 
534 BFont::BFont(const BFont *font)
535 {
536 	if (font)
537 		*this = *font;
538 	else
539 		*this = *be_plain_font;
540 }
541 
542 
543 /*!
544 	\brief Sets the font's family and style all at once
545 	\param family Font family to set
546 	\param style Font style to set
547 	\return B_NAME_NOT_FOUND if family or style do not exist.
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 	fExtraFlags = kUninitializedExtraFlags;
574 
575 	return B_OK;
576 }
577 
578 
579 /*!
580 	\brief Sets the font's family and style all at once
581 	\param code Unique font identifier obtained from the server.
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.
587 	// This is a problem because the face flag includes Regular/Bold/Italic
588 	// information in addition to stuff like underlining and strikethrough.
589 	// As a result, this will need a trip to the server and, thus, be slower
590 	// than R5's in order to be correct
591 
592 	uint16 family, style;
593 	style = fontcode & 0xFFFF;
594 	family = (fontcode & 0xFFFF0000) >> 16;
595 
596 	BPrivate::AppServerLink link;
597 	link.StartMessage(AS_GET_FAMILY_AND_STYLE_IDS);
598 	link.AttachString(NULL);	// no family and style name
599 	link.AttachString(NULL);
600 	link.Attach<uint16>(family);
601 	link.Attach<uint16>(style);
602 	link.Attach<uint16>(fFace);
603 
604 	int32 code;
605 	if (link.FlushWithReply(code) != B_OK
606 		|| code != B_OK)
607 		return;
608 
609 	link.Read<uint16>(&fFamilyID);
610 	link.Read<uint16>(&fStyleID);
611 	link.Read<uint16>(&fFace);
612 	fHeight.ascent = kUninitializedAscent;
613 	fExtraFlags = kUninitializedExtraFlags;
614 }
615 
616 
617 /*!
618 	\brief Sets the font's family and face all at once
619 	\param family Font family to set
620 	\param face Font face to set.
621 	\return B_ERROR if family does not exists or face is an invalid value.
622 
623 	To comply with the BeBook, this function will only set valid values - i.e.
624 	passing a nonexistent family will cause only the face to be set.
625 	Additionally, if a particular  face does not exist in a family, the closest
626 	match will be chosen.
627 */
628 status_t
629 BFont::SetFamilyAndFace(const font_family family, uint16 face)
630 {
631 	BPrivate::AppServerLink link;
632 	link.StartMessage(AS_GET_FAMILY_AND_STYLE_IDS);
633 	link.AttachString(family, sizeof(font_family));
634 	link.AttachString(NULL);	// no style given
635 	link.Attach<uint16>(fFamilyID);
636 	link.Attach<uint16>(0xffff);
637 	link.Attach<uint16>(face);
638 
639 	int32 status = B_ERROR;
640 	if (link.FlushWithReply(status) != B_OK
641 		|| status != B_OK)
642 		return status;
643 
644 	link.Read<uint16>(&fFamilyID);
645 	link.Read<uint16>(&fStyleID);
646 	link.Read<uint16>(&fFace);
647 	fHeight.ascent = kUninitializedAscent;
648 	fExtraFlags = kUninitializedExtraFlags;
649 
650 	return B_OK;
651 }
652 
653 
654 void
655 BFont::SetSize(float size)
656 {
657 	fSize = size;
658 	fHeight.ascent = kUninitializedAscent;
659 }
660 
661 
662 void
663 BFont::SetShear(float shear)
664 {
665 	fShear = shear;
666 	fHeight.ascent = kUninitializedAscent;
667 }
668 
669 
670 void
671 BFont::SetRotation(float rotation)
672 {
673 	fRotation = rotation;
674 	fHeight.ascent = kUninitializedAscent;
675 }
676 
677 
678 void
679 BFont::SetFalseBoldWidth(float width)
680 {
681 	fFalseBoldWidth = width;
682 }
683 
684 
685 void
686 BFont::SetSpacing(uint8 spacing)
687 {
688 	fSpacing = spacing;
689 }
690 
691 
692 void
693 BFont::SetEncoding(uint8 encoding)
694 {
695 	fEncoding = encoding;
696 }
697 
698 
699 void
700 BFont::SetFace(uint16 face)
701 {
702 	if (face == fFace)
703 		return;
704 
705 	SetFamilyAndFace(NULL, face);
706 }
707 
708 
709 void
710 BFont::SetFlags(uint32 flags)
711 {
712 	fFlags = flags;
713 }
714 
715 
716 void
717 BFont::GetFamilyAndStyle(font_family *family, font_style *style) const
718 {
719 	if (family == NULL && style == NULL)
720 		return;
721 
722 	// it's okay to call this function with either family or style set to NULL
723 
724 	font_family familyBuffer;
725 	font_style styleBuffer;
726 
727 	if (family == NULL)
728 		family = &familyBuffer;
729 	if (style == NULL)
730 		style = &styleBuffer;
731 
732 	BPrivate::AppServerLink link;
733 	link.StartMessage(AS_GET_FAMILY_AND_STYLE);
734 	link.Attach<uint16>(fFamilyID);
735 	link.Attach<uint16>(fStyleID);
736 
737 	int32 code;
738 	if (link.FlushWithReply(code) != B_OK
739 		|| code != B_OK) {
740 		// the least we can do is to clear the buffers
741 		memset(*family, 0, sizeof(font_family));
742 		memset(*style, 0, sizeof(font_style));
743 		return;
744 	}
745 
746 	link.ReadString(*family, sizeof(font_family));
747 	link.ReadString(*style, sizeof(font_style));
748 }
749 
750 
751 uint32
752 BFont::FamilyAndStyle() const
753 {
754 	return (fFamilyID << 16UL) | fStyleID;
755 }
756 
757 
758 float
759 BFont::Size() const
760 {
761 	return fSize;
762 }
763 
764 
765 float
766 BFont::Shear() const
767 {
768 	return fShear;
769 }
770 
771 
772 float
773 BFont::Rotation() const
774 {
775 	return fRotation;
776 }
777 
778 
779 float
780 BFont::FalseBoldWidth() const
781 {
782 	return fFalseBoldWidth;
783 }
784 
785 
786 uint8
787 BFont::Spacing() const
788 {
789 	return fSpacing;
790 }
791 
792 
793 uint8
794 BFont::Encoding() const
795 {
796 	return fEncoding;
797 }
798 
799 
800 uint16
801 BFont::Face() const
802 {
803 	return fFace;
804 }
805 
806 
807 uint32
808 BFont::Flags() const
809 {
810 	return fFlags;
811 }
812 
813 
814 font_direction
815 BFont::Direction() const
816 {
817 	_GetExtraFlags();
818 	return (font_direction)(fExtraFlags >> B_PRIVATE_FONT_DIRECTION_SHIFT);
819 }
820 
821 
822 bool
823 BFont::IsFixed() const
824 {
825 	_GetExtraFlags();
826 	return (fExtraFlags & B_IS_FIXED) != 0;
827 }
828 
829 
830 /*!
831 	\brief Returns true if the font is fixed-width and contains both full
832 		   and half-width characters
833 
834 	This was left unimplemented as of R5. It was a way to work with both
835 	Kanji and Roman characters in the same fixed-width font.
836 */
837 bool
838 BFont::IsFullAndHalfFixed() const
839 {
840 	_GetExtraFlags();
841 	return (fExtraFlags & B_PRIVATE_FONT_IS_FULL_AND_HALF_FIXED) != 0;
842 }
843 
844 
845 BRect
846 BFont::BoundingBox() const
847 {
848 	BPrivate::AppServerLink link;
849 	link.StartMessage(AS_GET_FONT_BOUNDING_BOX);
850 	link.Attach<uint16>(fFamilyID);
851 	link.Attach<uint16>(fStyleID);
852 
853 	int32 code;
854 	if (link.FlushWithReply(code) != B_OK
855 		|| code != B_OK)
856 		return BRect(0, 0, 0 ,0);
857 
858 	BRect box;
859 	link.Read<BRect>(&box);
860 	return box;
861 }
862 
863 
864 unicode_block
865 BFont::Blocks() const
866 {
867 	// TODO: Add Block support
868 	return unicode_block();
869 }
870 
871 
872 font_file_format
873 BFont::FileFormat() const
874 {
875 	BPrivate::AppServerLink link;
876 	link.StartMessage(AS_GET_FONT_FILE_FORMAT);
877 	link.Attach<uint16>(fFamilyID);
878 	link.Attach<uint16>(fStyleID);
879 
880 	int32 status;
881 	if (link.FlushWithReply(status) != B_OK
882 		|| status != B_OK) {
883 		// just take a safe bet...
884 		return B_TRUETYPE_WINDOWS;
885 	}
886 
887 	uint16 format;
888 	link.Read<uint16>(&format);
889 
890 	return (font_file_format)format;
891 }
892 
893 
894 int32
895 BFont::CountTuned() const
896 {
897 	BPrivate::AppServerLink link;
898 	link.StartMessage(AS_GET_TUNED_COUNT);
899 	link.Attach<uint16>(fFamilyID);
900 	link.Attach<uint16>(fStyleID);
901 
902 	int32 code;
903 	if (link.FlushWithReply(code) != B_OK
904 		|| code != B_OK)
905 		return -1;
906 
907 	int32 count;
908 	link.Read<int32>(&count);
909 	return count;
910 }
911 
912 
913 void
914 BFont::GetTunedInfo(int32 index, tuned_font_info *info) const
915 {
916 	if (!info)
917 		return;
918 
919 	BPrivate::AppServerLink link;
920 	link.StartMessage(AS_GET_TUNED_INFO);
921 	link.Attach<uint16>(fFamilyID);
922 	link.Attach<uint16>(fStyleID);
923 	link.Attach<uint32>(index);
924 
925 	int32 code;
926 	if (link.FlushWithReply(code) != B_OK
927 		|| code != B_OK)
928 		return;
929 
930 	link.Read<tuned_font_info>(info);
931 }
932 
933 
934 void
935 BFont::TruncateString(BString *inOut, uint32 mode, float width) const
936 {
937 	// NOTE: Careful, we cannot directly use "inOut->String()" as result
938 	// array, because the string length increases by 3 bytes in the worst
939 	// case scenario.
940 	const char *string = inOut->String();
941 	GetTruncatedStrings(&string, 1, mode, width, inOut);
942 }
943 
944 
945 void
946 BFont::GetTruncatedStrings(const char *stringArray[], int32 numStrings,
947 	uint32 mode, float width, BString resultArray[]) const
948 {
949 	if (stringArray && resultArray && numStrings > 0) {
950 		// allocate storage, see BeBook for "+ 3" (make space for ellipsis)
951 		char** truncatedStrings = new char*[numStrings];
952 		for (int32 i = 0; i < numStrings; i++) {
953 			truncatedStrings[i] = new char[strlen(stringArray[i]) + 3];
954 		}
955 
956 		GetTruncatedStrings(stringArray, numStrings, mode, width,
957 			truncatedStrings);
958 
959 		// copy the strings into the BString array and free each one
960 		for (int32 i = 0; i < numStrings; i++) {
961 			resultArray[i].SetTo(truncatedStrings[i]);
962 			delete[] truncatedStrings[i];
963 		}
964 		delete[] truncatedStrings;
965 	}
966 }
967 
968 
969 void
970 BFont::GetTruncatedStrings(const char *stringArray[], int32 numStrings,
971 	uint32 mode, float width, char *resultArray[]) const
972 {
973 	if (stringArray && numStrings > 0) {
974 		// the width of the "…" glyph
975 		float ellipsisWidth = StringWidth(B_UTF8_ELLIPSIS);
976 		for (int32 i = 0; i < numStrings; i++) {
977 			int32 length = strlen(stringArray[i]);
978 			// count the individual glyphs
979 			int32 numChars = UTF8CountChars(stringArray[i], length);
980 			// get the escapement of each glyph in font units
981 			float* escapementArray = new float[numChars];
982 			GetEscapements(stringArray[i], numChars, NULL, escapementArray);
983 
984 			truncate_string(stringArray[i], mode, width, resultArray[i],
985 				escapementArray, fSize, ellipsisWidth, length, numChars);
986 
987 			delete[] escapementArray;
988 		}
989 	}
990 }
991 
992 
993 float
994 BFont::StringWidth(const char *string) const
995 {
996 	if (!string)
997 		return 0.0;
998 
999 	int32 length = strlen(string);
1000 	float width;
1001 	GetStringWidths(&string, &length, 1, &width);
1002 
1003 	return width;
1004 }
1005 
1006 
1007 float
1008 BFont::StringWidth(const char *string, int32 length) const
1009 {
1010 	if (!string || length < 1)
1011 		return 0.0f;
1012 
1013 	float width = 0.0f;
1014 	GetStringWidths(&string, &length, 1, &width);
1015 
1016 	return width;
1017 }
1018 
1019 
1020 void
1021 BFont::GetStringWidths(const char *stringArray[], const int32 lengthArray[],
1022 	int32 numStrings, float widthArray[]) const
1023 {
1024 	if (!stringArray || !lengthArray || numStrings < 1 || !widthArray)
1025 		return;
1026 
1027 	BPrivate::AppServerLink link;
1028 	link.StartMessage(AS_GET_STRING_WIDTHS);
1029 	link.Attach<uint16>(fFamilyID);
1030 	link.Attach<uint16>(fStyleID);
1031 	link.Attach<float>(fSize);
1032 	link.Attach<uint8>(fSpacing);
1033 	link.Attach<int32>(numStrings);
1034 
1035 	// TODO: all strings into a single array???
1036 	//	we do have a maximum message length, and it could be easily touched
1037 	//	here...
1038 	for (int32 i = 0; i < numStrings; i++) {
1039 		link.AttachString(stringArray[i], lengthArray[i]);
1040 	}
1041 
1042 	status_t status;
1043 	if (link.FlushWithReply(status) != B_OK
1044 		|| status != B_OK)
1045 		return;
1046 
1047 	link.Read(widthArray, sizeof(float) * numStrings);
1048 }
1049 
1050 
1051 void
1052 BFont::GetEscapements(const char charArray[], int32 numChars,
1053 	float escapementArray[]) const
1054 {
1055 	GetEscapements(charArray, numChars, NULL, escapementArray);
1056 }
1057 
1058 
1059 void
1060 BFont::GetEscapements(const char charArray[], int32 numChars,
1061 	escapement_delta *delta, float escapementArray[]) const
1062 {
1063 	if (!charArray || numChars < 1 || !escapementArray)
1064 		return;
1065 
1066 	BPrivate::AppServerLink link;
1067 	link.StartMessage(AS_GET_ESCAPEMENTS_AS_FLOATS);
1068 	link.Attach<uint16>(fFamilyID);
1069 	link.Attach<uint16>(fStyleID);
1070 	link.Attach<float>(fSize);
1071 	link.Attach<uint8>(fSpacing);
1072 	link.Attach<float>(fRotation);
1073 	link.Attach<uint32>(fFlags);
1074 
1075 	link.Attach<float>(delta ? delta->nonspace : 0.0f);
1076 	link.Attach<float>(delta ? delta->space : 0.0f);
1077 	link.Attach<int32>(numChars);
1078 
1079 	// TODO: Should we not worry about the port capacity here?!?
1080 	uint32 bytesInBuffer = UTF8CountBytes(charArray, numChars);
1081 	link.Attach<int32>(bytesInBuffer);
1082 	link.Attach(charArray, bytesInBuffer);
1083 
1084 	int32 code;
1085 	if (link.FlushWithReply(code) != B_OK
1086 		|| code != B_OK)
1087 		return;
1088 
1089 	link.Read(escapementArray, numChars * sizeof(float));
1090 }
1091 
1092 
1093 void
1094 BFont::GetEscapements(const char charArray[], int32 numChars,
1095 	escapement_delta *delta, BPoint escapementArray[]) const
1096 {
1097 	GetEscapements(charArray, numChars, delta, escapementArray, NULL);
1098 }
1099 
1100 
1101 void
1102 BFont::GetEscapements(const char charArray[], int32 numChars,
1103 	escapement_delta *delta, BPoint escapementArray[],
1104 	BPoint offsetArray[]) const
1105 {
1106 	if (!charArray || numChars < 1 || !escapementArray)
1107 		return;
1108 
1109 	BPrivate::AppServerLink link;
1110 	link.StartMessage(AS_GET_ESCAPEMENTS);
1111 	link.Attach<uint16>(fFamilyID);
1112 	link.Attach<uint16>(fStyleID);
1113 	link.Attach<float>(fSize);
1114 	link.Attach<uint8>(fSpacing);
1115 	link.Attach<float>(fRotation);
1116 	link.Attach<uint32>(fFlags);
1117 
1118 	link.Attach<float>(delta ? delta->nonspace : 0.0);
1119 	link.Attach<float>(delta ? delta->space : 0.0);
1120 	link.Attach<bool>(offsetArray != NULL);
1121 	link.Attach<int32>(numChars);
1122 
1123 	// TODO: Should we not worry about the port capacity here?!?
1124 	uint32 bytesInBuffer = UTF8CountBytes(charArray, numChars);
1125 	link.Attach<int32>(bytesInBuffer);
1126 	link.Attach(charArray, bytesInBuffer);
1127 
1128 	int32 code;
1129 	if (link.FlushWithReply(code) != B_OK
1130 		|| code != B_OK)
1131 		return;
1132 
1133 	link.Read(escapementArray, sizeof(BPoint) * numChars);
1134 	if (offsetArray)
1135 		link.Read(offsetArray, sizeof(BPoint) * numChars);
1136 }
1137 
1138 
1139 void
1140 BFont::GetEdges(const char charArray[], int32 numChars,
1141 	edge_info edgeArray[]) const
1142 {
1143 	if (!charArray || numChars < 1 || !edgeArray)
1144 		return;
1145 
1146 	int32 code;
1147 	BPrivate::AppServerLink link;
1148 
1149 	link.StartMessage(AS_GET_EDGES);
1150 	link.Attach<uint16>(fFamilyID);
1151 	link.Attach<uint16>(fStyleID);
1152 	link.Attach<int32>(numChars);
1153 
1154 	uint32 bytesInBuffer = UTF8CountBytes(charArray, numChars);
1155 	link.Attach<int32>(bytesInBuffer);
1156 	link.Attach(charArray, bytesInBuffer);
1157 
1158 	if (link.FlushWithReply(code) != B_OK
1159 		|| code != B_OK)
1160 		return;
1161 
1162 	link.Read(edgeArray, sizeof(edge_info) * numChars);
1163 }
1164 
1165 
1166 void
1167 BFont::GetHeight(font_height *_height) const
1168 {
1169 	if (_height == NULL)
1170 		return;
1171 
1172 	if (fHeight.ascent == kUninitializedAscent) {
1173 		// we don't have the font height cached yet
1174 		BPrivate::AppServerLink link;
1175 
1176 		link.StartMessage(AS_GET_FONT_HEIGHT);
1177 		link.Attach<uint16>(fFamilyID);
1178 		link.Attach<uint16>(fStyleID);
1179 		link.Attach<float>(fSize);
1180 
1181 		int32 code;
1182 		if (link.FlushWithReply(code) != B_OK
1183 			|| code != B_OK)
1184 			return;
1185 
1186 		// Who put that "const" to this method? :-)
1187 		// We made fHeight mutable for this, but we should drop the "const"
1188 		// when we can
1189 		link.Read<font_height>(&fHeight);
1190 	}
1191 
1192 	*_height = fHeight;
1193 }
1194 
1195 
1196 void
1197 BFont::GetBoundingBoxesAsGlyphs(const char charArray[], int32 numChars,
1198 	font_metric_mode mode, BRect boundingBoxArray[]) const
1199 {
1200 	_GetBoundingBoxes(charArray, numChars, mode, false, NULL, boundingBoxArray,
1201 		false);
1202 }
1203 
1204 
1205 void
1206 BFont::GetBoundingBoxesAsString(const char charArray[], int32 numChars,
1207 	font_metric_mode mode, escapement_delta *delta,
1208 	BRect boundingBoxArray[]) const
1209 {
1210 	_GetBoundingBoxes(charArray, numChars, mode, true, delta, boundingBoxArray,
1211 		true);
1212 }
1213 
1214 
1215 void
1216 BFont::_GetBoundingBoxes(const char charArray[], int32 numChars,
1217 	font_metric_mode mode, bool string_escapement, escapement_delta *delta,
1218 	BRect boundingBoxArray[], bool asString) const
1219 {
1220 	if (!charArray || numChars < 1 || !boundingBoxArray)
1221 		return;
1222 
1223 	int32 code;
1224 	BPrivate::AppServerLink link;
1225 
1226 	link.StartMessage(asString
1227 		? AS_GET_BOUNDINGBOXES_STRING : AS_GET_BOUNDINGBOXES_CHARS);
1228 	link.Attach<uint16>(fFamilyID);
1229 	link.Attach<uint16>(fStyleID);
1230 	link.Attach<float>(fSize);
1231 	link.Attach<float>(fRotation);
1232 	link.Attach<float>(fShear);
1233 	link.Attach<float>(fFalseBoldWidth);
1234 	link.Attach<uint8>(fSpacing);
1235 
1236 	link.Attach<uint32>(fFlags);
1237 	link.Attach<font_metric_mode>(mode);
1238 	link.Attach<bool>(string_escapement);
1239 
1240 	if (delta) {
1241 		link.Attach<escapement_delta>(*delta);
1242 	} else {
1243 		escapement_delta emptyDelta = {0, 0};
1244 		link.Attach<escapement_delta>(emptyDelta);
1245 	}
1246 
1247 	link.Attach<int32>(numChars);
1248 	uint32 bytesInBuffer = UTF8CountBytes(charArray, numChars);
1249 	link.Attach<int32>(bytesInBuffer);
1250 	link.Attach(charArray, bytesInBuffer);
1251 
1252 	if (link.FlushWithReply(code) != B_OK
1253 		|| code != B_OK)
1254 		return;
1255 
1256 	link.Read(boundingBoxArray, sizeof(BRect) * numChars);
1257 }
1258 
1259 
1260 void
1261 BFont::GetBoundingBoxesForStrings(const char *stringArray[], int32 numStrings,
1262 	font_metric_mode mode, escapement_delta deltas[],
1263 	BRect boundingBoxArray[]) const
1264 {
1265 	if (!stringArray || numStrings < 1 || !boundingBoxArray)
1266 		return;
1267 
1268 	int32 code;
1269 	BPrivate::AppServerLink link;
1270 
1271 	link.StartMessage(AS_GET_BOUNDINGBOXES_STRINGS);
1272 	link.Attach<uint16>(fFamilyID);
1273 	link.Attach<uint16>(fStyleID);
1274 	link.Attach<float>(fSize);
1275 	link.Attach<float>(fRotation);
1276 	link.Attach<float>(fShear);
1277 	link.Attach<float>(fFalseBoldWidth);
1278 	link.Attach<uint8>(fSpacing);
1279 	link.Attach<uint32>(fFlags);
1280 	link.Attach<font_metric_mode>(mode);
1281 	link.Attach<int32>(numStrings);
1282 
1283 	if (deltas) {
1284 		for (int32 i = 0; i < numStrings; i++) {
1285 			link.AttachString(stringArray[i]);
1286 			link.Attach<escapement_delta>(deltas[i]);
1287 		}
1288 	} else {
1289 		escapement_delta emptyDelta = {0, 0};
1290 
1291 		for (int32 i = 0; i < numStrings; i++) {
1292 			link.AttachString(stringArray[i]);
1293 			link.Attach<escapement_delta>(emptyDelta);
1294 		}
1295 	}
1296 
1297 	if (link.FlushWithReply(code) != B_OK
1298 		|| code != B_OK)
1299 		return;
1300 
1301 	link.Read(boundingBoxArray, sizeof(BRect) * numStrings);
1302 }
1303 
1304 
1305 void
1306 BFont::GetGlyphShapes(const char charArray[], int32 numChars,
1307 	BShape *glyphShapeArray[]) const
1308 {
1309 	// TODO: implement code specifically for passing BShapes to and
1310 	// from the server
1311 	if (!charArray || numChars < 1 || !glyphShapeArray)
1312 		return;
1313 
1314 	int32 code;
1315 	BPrivate::AppServerLink link;
1316 
1317 	link.StartMessage(AS_GET_GLYPH_SHAPES);
1318 	link.Attach<uint16>(fFamilyID);
1319 	link.Attach<uint16>(fStyleID);
1320 	link.Attach<float>(fSize);
1321 	link.Attach<float>(fShear);
1322 	link.Attach<float>(fRotation);
1323 	link.Attach<float>(fFalseBoldWidth);
1324 	link.Attach<uint32>(fFlags);
1325 	link.Attach<int32>(numChars);
1326 
1327 	uint32 bytesInBuffer = UTF8CountBytes(charArray, numChars);
1328 	link.Attach<int32>(bytesInBuffer);
1329 	link.Attach(charArray, bytesInBuffer);
1330 
1331 	if (link.FlushWithReply(code) != B_OK
1332 		|| code != B_OK)
1333 		return;
1334 
1335 	for (int32 i = 0; i < numChars; i++)
1336 		link.ReadShape(glyphShapeArray[i]);
1337 }
1338 
1339 
1340 void
1341 BFont::GetHasGlyphs(const char charArray[], int32 numChars,
1342 	bool hasArray[]) const
1343 {
1344 	if (!charArray || numChars < 1 || !hasArray)
1345 		return;
1346 
1347 	int32 code;
1348 	BPrivate::AppServerLink link;
1349 
1350 	link.StartMessage(AS_GET_HAS_GLYPHS);
1351 	link.Attach<uint16>(fFamilyID);
1352 	link.Attach<uint16>(fStyleID);
1353 	link.Attach<int32>(numChars);
1354 
1355 	uint32 bytesInBuffer = UTF8CountBytes(charArray, numChars);
1356 	link.Attach<int32>(bytesInBuffer);
1357 	link.Attach(charArray, bytesInBuffer);
1358 
1359 	if (link.FlushWithReply(code) != B_OK
1360 		|| code != B_OK)
1361 		return;
1362 
1363 	link.Read(hasArray, sizeof(bool) * numChars);
1364 }
1365 
1366 
1367 BFont &
1368 BFont::operator=(const BFont &font)
1369 {
1370 	fFamilyID = font.fFamilyID;
1371 	fStyleID = font.fStyleID;
1372 	fSize = font.fSize;
1373 	fShear = font.fShear;
1374 	fRotation = font.fRotation;
1375 	fFalseBoldWidth = font.fFalseBoldWidth;
1376 	fSpacing = font.fSpacing;
1377 	fEncoding = font.fEncoding;
1378 	fFace = font.fFace;
1379 	fHeight = font.fHeight;
1380 	fFlags = font.fFlags;
1381 	fExtraFlags = font.fExtraFlags;
1382 
1383 	return *this;
1384 }
1385 
1386 
1387 bool
1388 BFont::operator==(const BFont &font) const
1389 {
1390 	return fFamilyID == font.fFamilyID
1391 		&& fStyleID == font.fStyleID
1392 		&& fSize == font.fSize
1393 		&& fShear == font.fShear
1394 		&& fRotation == font.fRotation
1395 		&& fFalseBoldWidth == font.fFalseBoldWidth
1396 		&& fSpacing == font.fSpacing
1397 		&& fEncoding == font.fEncoding
1398 		&& fFace == font.fFace;
1399 }
1400 
1401 
1402 bool
1403 BFont::operator!=(const BFont &font) const
1404 {
1405 	return fFamilyID != font.fFamilyID
1406 		|| fStyleID != font.fStyleID
1407 		|| fSize != font.fSize
1408 		|| fShear != font.fShear
1409 		|| fRotation != font.fRotation
1410 		|| fFalseBoldWidth != font.fFalseBoldWidth
1411 		|| fSpacing != font.fSpacing
1412 		|| fEncoding != font.fEncoding
1413 		|| fFace != font.fFace;
1414 }
1415 
1416 
1417 void
1418 BFont::PrintToStream() const
1419 {
1420 	font_family family;
1421 	font_style style;
1422 	GetFamilyAndStyle(&family, &style);
1423 
1424 	printf("BFont { %s (%d), %s (%d) 0x%x %f/%f %fpt (%f %f %f), %d }\n",
1425 		family, fFamilyID, style, fStyleID, fFace, fShear, fRotation, fSize,
1426 		fHeight.ascent, fHeight.descent, fHeight.leading, fEncoding);
1427 }
1428 
1429 
1430 void
1431 BFont::_GetExtraFlags() const
1432 {
1433 	// TODO: this has to be const in order to allow other font getters to
1434 	// stay const as well
1435 	if (fExtraFlags != kUninitializedExtraFlags)
1436 		return;
1437 
1438 	BPrivate::AppServerLink link;
1439 	link.StartMessage(AS_GET_EXTRA_FONT_FLAGS);
1440 	link.Attach<uint16>(fFamilyID);
1441 	link.Attach<uint16>(fStyleID);
1442 
1443 	status_t status = B_ERROR;
1444 	if (link.FlushWithReply(status) != B_OK
1445 		|| status != B_OK) {
1446 		// use defaut values for the flags
1447 		fExtraFlags = (uint32)B_FONT_LEFT_TO_RIGHT
1448 			<< B_PRIVATE_FONT_DIRECTION_SHIFT;
1449 		return;
1450 	}
1451 
1452 	link.Read<uint32>(&fExtraFlags);
1453 }
1454 
1455