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