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