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
34using namespace std;
35
36const float kUninitializedAscent = INFINITY;
37const uint32 kUninitializedExtraFlags = 0xffffffff;
38
39// The actual objects which the globals point to
40static BFont sPlainFont;
41static BFont sBoldFont;
42static BFont sFixedFont;
43
44const BFont *be_plain_font = &sPlainFont;
45const BFont *be_bold_font = &sBoldFont;
46const BFont *be_fixed_font = &sFixedFont;
47
48
49struct style {
50	BString	name;
51	uint16	face;
52	uint32	flags;
53};
54
55struct family {
56	BString	name;
57	uint32	flags;
58	BObjectList<style> styles;
59};
60
61namespace {
62
63class 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
95pthread_once_t FontList::sDefaultInitOnce = PTHREAD_ONCE_INIT;
96FontList* FontList::sDefaultInstance = NULL;
97
98}	// unnamed namespace
99
100
101//	#pragma mark -
102
103
104static int
105compare_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
112namespace {
113
114FontList::FontList()
115	: BLocker("font list"),
116	fLastFamily(NULL),
117	fLastUpdate(0),
118	fRevision(0)
119{
120}
121
122
123FontList::~FontList()
124{
125}
126
127
128/*static*/ FontList*
129FontList::Default()
130{
131	if (sDefaultInstance == NULL)
132		pthread_once(&sDefaultInitOnce, &_InitSingleton);
133
134	return sDefaultInstance;
135}
136
137
138bool
139FontList::UpdatedOnServer()
140{
141	return _RevisionOnServer() != fRevision;
142}
143
144
145int32
146FontList::_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
162status_t
163FontList::_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
225status_t
226FontList::_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
236family*
237FontList::_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
250status_t
251FontList::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
270status_t
271FontList::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
297int32
298FontList::CountFamilies()
299{
300	BAutolock locker(this);
301
302	_UpdateIfNecessary();
303	return fFamilies.CountItems();
304}
305
306
307int32
308FontList::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
323FontList::_InitSingleton()
324{
325	sDefaultInstance = new FontList;
326}
327
328}	// unnamed namespace
329
330
331//	#pragma mark -
332
333
334void
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
372void _font_control_(BFont *font, int32 cmd, void *data)
373{
374}
375
376status_t get_font_cache_info(uint32 id, void *set)
377{
378	return B_ERROR;
379}
380
381status_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
388void
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
405status_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
427int32
428count_font_families()
429{
430	return FontList::Default()->CountFamilies();
431}
432
433
434// Returns the number of styles available for a font family
435int32
436count_font_styles(font_family family)
437{
438	return FontList::Default()->CountStyles(family);
439}
440
441
442// Retrieves the family name at the specified index
443status_t
444get_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
454status_t
455get_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
463status_t
464get_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
479bool
480update_font_families(bool /*checkOnly*/)
481{
482	return FontList::Default()->UpdatedOnServer();
483}
484
485
486//	#pragma mark -
487
488
489BFont::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
514BFont::BFont(const BFont &font)
515{
516	*this = font;
517}
518
519
520BFont::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
530status_t
531BFont::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
560void
561BFont::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
594status_t
595BFont::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
624void
625BFont::SetSize(float size)
626{
627	fSize = size;
628	fHeight.ascent = kUninitializedAscent;
629}
630
631
632void
633BFont::SetShear(float shear)
634{
635	fShear = shear;
636	fHeight.ascent = kUninitializedAscent;
637}
638
639
640void
641BFont::SetRotation(float rotation)
642{
643	fRotation = rotation;
644	fHeight.ascent = kUninitializedAscent;
645}
646
647
648void
649BFont::SetFalseBoldWidth(float width)
650{
651	fFalseBoldWidth = width;
652}
653
654
655void
656BFont::SetSpacing(uint8 spacing)
657{
658	fSpacing = spacing;
659}
660
661
662void
663BFont::SetEncoding(uint8 encoding)
664{
665	fEncoding = encoding;
666}
667
668
669void
670BFont::SetFace(uint16 face)
671{
672	if (face == fFace)
673		return;
674
675	SetFamilyAndFace(NULL, face);
676}
677
678
679void
680BFont::SetFlags(uint32 flags)
681{
682	fFlags = flags;
683}
684
685
686void
687BFont::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
720uint32
721BFont::FamilyAndStyle() const
722{
723	return (fFamilyID << 16UL) | fStyleID;
724}
725
726
727float
728BFont::Size() const
729{
730	return fSize;
731}
732
733
734float
735BFont::Shear() const
736{
737	return fShear;
738}
739
740
741float
742BFont::Rotation() const
743{
744	return fRotation;
745}
746
747
748float
749BFont::FalseBoldWidth() const
750{
751	return fFalseBoldWidth;
752}
753
754
755uint8
756BFont::Spacing() const
757{
758	return fSpacing;
759}
760
761
762uint8
763BFont::Encoding() const
764{
765	return fEncoding;
766}
767
768
769uint16
770BFont::Face() const
771{
772	return fFace;
773}
774
775
776uint32
777BFont::Flags() const
778{
779	return fFlags;
780}
781
782
783font_direction
784BFont::Direction() const
785{
786	_GetExtraFlags();
787	return (font_direction)(fExtraFlags >> B_PRIVATE_FONT_DIRECTION_SHIFT);
788}
789
790
791bool
792BFont::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.
801bool
802BFont::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
812BRect
813BFont::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
831unicode_block
832BFont::Blocks() const
833{
834	// TODO: Add Block support
835	return unicode_block(~0LL, ~0LL);
836}
837
838
839font_file_format
840BFont::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
861int32
862BFont::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
880void
881BFont::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
900void
901BFont::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
911void
912BFont::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
936void
937BFont::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
954float
955BFont::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
968float
969BFont::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
981void
982BFont::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
1012void
1013BFont::GetEscapements(const char charArray[], int32 numChars,
1014	float escapementArray[]) const
1015{
1016	GetEscapements(charArray, numChars, NULL, escapementArray);
1017}
1018
1019
1020void
1021BFont::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
1053void
1054BFont::GetEscapements(const char charArray[], int32 numChars,
1055	escapement_delta *delta, BPoint escapementArray[]) const
1056{
1057	GetEscapements(charArray, numChars, delta, escapementArray, NULL);
1058}
1059
1060
1061void
1062BFont::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
1098void
1099BFont::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
1124void
1125BFont::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
1153void
1154BFont::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
1162void
1163BFont::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
1172void
1173BFont::_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
1216void
1217BFont::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
1260void
1261BFont::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
1294void
1295BFont::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
1320BFont &
1321BFont::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
1340bool
1341BFont::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
1355bool
1356BFont::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
1370void
1371BFont::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
1383void
1384BFont::_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