xref: /haiku/src/kits/interface/Font.cpp (revision 4f00613311d0bd6b70fa82ce19931c41f071ea4e)
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.0;
988 
989 	float width;
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.Attach<int32>(lengthArray[i]);
1015 		link.AttachString(stringArray[i]);
1016 	}
1017 
1018 	int32 code;
1019 	if (link.FlushWithReply(code) != B_OK
1020 		|| code != B_OK)
1021 		return;
1022 
1023 	link.Read(widthArray, sizeof(float) * numStrings);
1024 }
1025 
1026 
1027 void
1028 BFont::GetEscapements(const char charArray[], int32 numChars, float escapementArray[]) const
1029 {
1030 	GetEscapements(charArray, numChars, NULL, escapementArray);
1031 }
1032 
1033 
1034 void
1035 BFont::GetEscapements(const char charArray[], int32 numChars, escapement_delta *delta,
1036 	float escapementArray[]) const
1037 {
1038 	if (!charArray || numChars < 1 || !escapementArray)
1039 		return;
1040 
1041 	BPrivate::AppServerLink link;
1042 	link.StartMessage(AS_GET_ESCAPEMENTS_AS_FLOATS);
1043 	link.Attach<uint16>(fFamilyID);
1044 	link.Attach<uint16>(fStyleID);
1045 	link.Attach<float>(fSize);
1046 	link.Attach<float>(fRotation);
1047 	link.Attach<uint32>(fFlags);
1048 
1049 	link.Attach<float>(delta ? delta->nonspace : 0.0);
1050 	link.Attach<float>(delta ? delta->space : 0.0);
1051 
1052 	// TODO: Should we not worry about the port capacity here?!?
1053 	link.Attach<int32>(numChars);
1054 
1055 	uint32 bytesInBuffer = UTF8CountBytes(charArray, numChars);
1056 	link.Attach<int32>(bytesInBuffer);
1057 	link.Attach(charArray, bytesInBuffer);
1058 
1059 	int32 code;
1060 	if (link.FlushWithReply(code) != B_OK
1061 		|| code != B_OK)
1062 		return;
1063 
1064 	link.Read(escapementArray, numChars * sizeof(float));
1065 }
1066 
1067 
1068 void
1069 BFont::GetEscapements(const char charArray[], int32 numChars, escapement_delta *delta,
1070 	BPoint escapementArray[]) const
1071 {
1072 	GetEscapements(charArray, numChars, delta, escapementArray, NULL);
1073 }
1074 
1075 
1076 void
1077 BFont::GetEscapements(const char charArray[], int32 numChars, escapement_delta *delta,
1078 	BPoint escapementArray[], BPoint offsetArray[]) const
1079 {
1080 	if (!charArray || numChars < 1 || !escapementArray)
1081 		return;
1082 
1083 	BPrivate::AppServerLink link;
1084 	link.StartMessage(AS_GET_ESCAPEMENTS);
1085 	link.Attach<uint16>(fFamilyID);
1086 	link.Attach<uint16>(fStyleID);
1087 	link.Attach<float>(fSize);
1088 	link.Attach<float>(fRotation);
1089 	link.Attach<uint32>(fFlags);
1090 
1091 	link.Attach<int32>(numChars);
1092 
1093 	// TODO: Support UTF8 characters
1094 	if (offsetArray) {
1095 		for (int32 i = 0; i < numChars; i++) {
1096 			link.Attach<char>(charArray[i]);
1097 			link.Attach<BPoint>(offsetArray[i]);
1098 		}
1099 	} else {
1100 		BPoint dummypt(0, 0);
1101 
1102 		for (int32 i = 0; i < numChars; i++) {
1103 			link.Attach<char>(charArray[i]);
1104 			link.Attach<BPoint>(dummypt);
1105 		}
1106 	}
1107 
1108 	int32 code;
1109 	if (link.FlushWithReply(code) != B_OK
1110 		|| code != B_OK)
1111 		return;
1112 
1113 	link.Read(escapementArray, 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 
1289 	link.Attach<int32>(numChars);
1290 	link.Attach(charArray, numChars);
1291 
1292 	if (link.FlushWithReply(code) != B_OK
1293 		|| code != B_OK)
1294 		return;
1295 
1296 	for (int32 i = 0; i < numChars; i++)
1297 		link.ReadShape(glyphShapeArray[i]);
1298 }
1299 
1300 
1301 void
1302 BFont::GetHasGlyphs(const char charArray[], int32 numChars, bool hasArray[]) const
1303 {
1304 	if (!charArray || numChars < 1 || !hasArray)
1305 		return;
1306 
1307 	int32 code;
1308 	BPrivate::AppServerLink link;
1309 
1310 	link.StartMessage(AS_GET_HAS_GLYPHS);
1311 	link.Attach<uint16>(fFamilyID);
1312 	link.Attach<uint16>(fStyleID);
1313 	link.Attach<int32>(numChars);
1314 
1315 	uint32 bytesInBuffer = UTF8CountBytes(charArray, numChars);
1316 	link.Attach<int32>(bytesInBuffer);
1317 	link.Attach(charArray, bytesInBuffer);
1318 
1319 	if (link.FlushWithReply(code) != B_OK
1320 		|| code != B_OK)
1321 		return;
1322 
1323 	link.Read(hasArray, sizeof(bool) * numChars);
1324 }
1325 
1326 
1327 BFont
1328 &BFont::operator=(const BFont &font)
1329 {
1330 	fFamilyID = font.fFamilyID;
1331 	fStyleID = font.fStyleID;
1332 	fSize = font.fSize;
1333 	fShear = font.fShear;
1334 	fRotation = font.fRotation;
1335 	fSpacing = font.fSpacing;
1336 	fEncoding = font.fEncoding;
1337 	fFace = font.fFace;
1338 	fHeight = font.fHeight;
1339 	fFlags = font.fFlags;
1340 	return *this;
1341 }
1342 
1343 
1344 bool
1345 BFont::operator==(const BFont &font) const
1346 {
1347 	return fFamilyID == font.fFamilyID
1348 		&& fStyleID == font.fStyleID
1349 		&& fSize == font.fSize
1350 		&& fShear == font.fShear
1351 		&& fRotation == font.fRotation
1352 		&& fSpacing == font.fSpacing
1353 		&& fEncoding == font.fEncoding
1354 		&& fFace == font.fFace;
1355 }
1356 
1357 
1358 bool
1359 BFont::operator!=(const BFont &font) const
1360 {
1361 	return fFamilyID != font.fFamilyID
1362 		|| fStyleID != font.fStyleID
1363 		|| fSize != font.fSize
1364 		|| fShear != font.fShear
1365 		|| fRotation != font.fRotation
1366 		|| fSpacing != font.fSpacing
1367 		|| fEncoding != font.fEncoding
1368 		|| fFace != font.fFace;
1369 }
1370 
1371 
1372 void
1373 BFont::PrintToStream() const
1374 {
1375 	font_family family;
1376 	font_style style;
1377 	GetFamilyAndStyle(&family, &style);
1378 
1379 	printf("BFont { %s (%d), %s (%d) 0x%x %f/%f %fpt (%f %f %f), %d }\n", family,
1380 		fFamilyID, style, fStyleID, fFace, fShear, fRotation, fSize,
1381 		fHeight.ascent, fHeight.descent, fHeight.leading, fEncoding);
1382 }
1383 
1384 
1385 void
1386 BFont::_GetExtraFlags() const
1387 {
1388 	// TODO: this has to be const in order to allow other font getters to stay const as well
1389 	if (fExtraFlags != kUninitializedExtraFlags)
1390 		return;
1391 
1392 	BPrivate::AppServerLink link;
1393 	link.StartMessage(AS_GET_EXTRA_FONT_FLAGS);
1394 	link.Attach<uint16>(fFamilyID);
1395 	link.Attach<uint16>(fStyleID);
1396 
1397 	status_t status = B_ERROR;
1398 	if (link.FlushWithReply(status) != B_OK
1399 		|| status != B_OK) {
1400 		// use defaut values for the flags
1401 		fExtraFlags = (uint32)B_FONT_LEFT_TO_RIGHT << B_PRIVATE_FONT_DIRECTION_SHIFT;
1402 		return;
1403 	}
1404 
1405 	link.Read<uint32>(&fExtraFlags);
1406 }
1407