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