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