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
563	fHeight.ascent = kUninitializedAscent;
564	fExtraFlags = kUninitializedExtraFlags;
565
566	return B_OK;
567}
568
569
570// Sets the font's family and style all at once
571void
572BFont::SetFamilyAndStyle(uint32 code)
573{
574	// R5 has a bug here: the face is not updated even though the IDs are set.
575	// This is a problem because the face flag includes Regular/Bold/Italic
576	// information in addition to stuff like underlining and strikethrough.
577	// As a result, this will need a trip to the server and, thus, be slower
578	// than R5's in order to be correct
579
580	uint16 family, style;
581	style = code & 0xFFFF;
582	family = (code & 0xFFFF0000) >> 16;
583
584	BPrivate::AppServerLink link;
585	link.StartMessage(AS_GET_FAMILY_AND_STYLE_IDS);
586	link.AttachString(NULL);	// no family and style name
587	link.AttachString(NULL);
588	link.Attach<uint16>(family);
589	link.Attach<uint16>(style);
590	link.Attach<uint16>(fFace);
591
592	int32 fontcode;
593	if (link.FlushWithReply(fontcode) != B_OK || fontcode != B_OK)
594		return;
595
596	link.Read<uint16>(&fFamilyID);
597	link.Read<uint16>(&fStyleID);
598	link.Read<uint16>(&fFace);
599	fHeight.ascent = kUninitializedAscent;
600	fExtraFlags = kUninitializedExtraFlags;
601}
602
603
604// Sets the font's family and face all at once
605status_t
606BFont::SetFamilyAndFace(const font_family family, uint16 face)
607{
608	// To comply with the BeBook, this function will only set valid values
609	// i.e. passing a nonexistent family will cause only the face to be set.
610	// Additionally, if a particular  face does not exist in a family, the
611	// closest match will be chosen.
612
613	BPrivate::AppServerLink link;
614	link.StartMessage(AS_GET_FAMILY_AND_STYLE_IDS);
615	link.AttachString(family, sizeof(font_family));
616	link.AttachString(NULL);	// no style given
617	link.Attach<uint16>(fFamilyID);
618	link.Attach<uint16>(0xffff);
619	link.Attach<uint16>(face);
620
621	int32 status = B_ERROR;
622	if (link.FlushWithReply(status) != B_OK || status != B_OK)
623		return status;
624
625	link.Read<uint16>(&fFamilyID);
626	link.Read<uint16>(&fStyleID);
627	link.Read<uint16>(&fFace);
628	fHeight.ascent = kUninitializedAscent;
629	fExtraFlags = kUninitializedExtraFlags;
630
631	return B_OK;
632}
633
634
635void
636BFont::SetSize(float size)
637{
638	fSize = size;
639	fHeight.ascent = kUninitializedAscent;
640}
641
642
643void
644BFont::SetShear(float shear)
645{
646	fShear = shear;
647	fHeight.ascent = kUninitializedAscent;
648}
649
650
651void
652BFont::SetRotation(float rotation)
653{
654	fRotation = rotation;
655	fHeight.ascent = kUninitializedAscent;
656}
657
658
659void
660BFont::SetFalseBoldWidth(float width)
661{
662	fFalseBoldWidth = width;
663}
664
665
666void
667BFont::SetSpacing(uint8 spacing)
668{
669	fSpacing = spacing;
670}
671
672
673void
674BFont::SetEncoding(uint8 encoding)
675{
676	fEncoding = encoding;
677}
678
679
680void
681BFont::SetFace(uint16 face)
682{
683	if (face == fFace)
684		return;
685
686	SetFamilyAndFace(NULL, face);
687}
688
689
690void
691BFont::SetFlags(uint32 flags)
692{
693	fFlags = flags;
694}
695
696
697void
698BFont::GetFamilyAndStyle(font_family* family, font_style* style) const
699{
700	if (family == NULL && style == NULL)
701		return;
702
703	// it's okay to call this function with either family or style set to NULL
704
705	font_family familyBuffer;
706	font_style styleBuffer;
707
708	if (family == NULL)
709		family = &familyBuffer;
710	if (style == NULL)
711		style = &styleBuffer;
712
713	BPrivate::AppServerLink link;
714	link.StartMessage(AS_GET_FAMILY_AND_STYLE);
715	link.Attach<uint16>(fFamilyID);
716	link.Attach<uint16>(fStyleID);
717
718	int32 code;
719	if (link.FlushWithReply(code) != B_OK || code != B_OK) {
720		// the least we can do is to clear the buffers
721		memset(*family, 0, sizeof(font_family));
722		memset(*style, 0, sizeof(font_style));
723		return;
724	}
725
726	link.ReadString(*family, sizeof(font_family));
727	link.ReadString(*style, sizeof(font_style));
728}
729
730
731uint32
732BFont::FamilyAndStyle() const
733{
734	return (fFamilyID << 16UL) | fStyleID;
735}
736
737
738float
739BFont::Size() const
740{
741	return fSize;
742}
743
744
745float
746BFont::Shear() const
747{
748	return fShear;
749}
750
751
752float
753BFont::Rotation() const
754{
755	return fRotation;
756}
757
758
759float
760BFont::FalseBoldWidth() const
761{
762	return fFalseBoldWidth;
763}
764
765
766uint8
767BFont::Spacing() const
768{
769	return fSpacing;
770}
771
772
773uint8
774BFont::Encoding() const
775{
776	return fEncoding;
777}
778
779
780uint16
781BFont::Face() const
782{
783	return fFace;
784}
785
786
787uint32
788BFont::Flags() const
789{
790	return fFlags;
791}
792
793
794font_direction
795BFont::Direction() const
796{
797	_GetExtraFlags();
798	return (font_direction)(fExtraFlags >> B_PRIVATE_FONT_DIRECTION_SHIFT);
799}
800
801
802bool
803BFont::IsFixed() const
804{
805	_GetExtraFlags();
806	return (fExtraFlags & B_IS_FIXED) != 0;
807}
808
809
810// Returns whether or not the font is fixed-width and contains both
811// full and half-width characters.
812bool
813BFont::IsFullAndHalfFixed() const
814{
815	// This was left unimplemented as of R5. It is a way to work with both
816	// Kanji and Roman characters in the same fixed-width font.
817
818	_GetExtraFlags();
819	return (fExtraFlags & B_PRIVATE_FONT_IS_FULL_AND_HALF_FIXED) != 0;
820}
821
822
823BRect
824BFont::BoundingBox() const
825{
826	BPrivate::AppServerLink link;
827	link.StartMessage(AS_GET_FONT_BOUNDING_BOX);
828	link.Attach<uint16>(fFamilyID);
829	link.Attach<uint16>(fStyleID);
830	link.Attach<float>(fSize);
831
832	int32 code;
833	if (link.FlushWithReply(code) != B_OK
834		|| code != B_OK)
835		return BRect(0, 0, 0 ,0);
836
837	BRect box;
838	link.Read<BRect>(&box);
839	return box;
840}
841
842
843unicode_block
844BFont::Blocks() const
845{
846	BPrivate::AppServerLink link;
847	link.StartMessage(AS_GET_UNICODE_BLOCKS);
848	link.Attach<uint16>(fFamilyID);
849	link.Attach<uint16>(fStyleID);
850
851	int32 status;
852	if (link.FlushWithReply(status) != B_OK
853		|| status != B_OK) {
854		return unicode_block(~0LL, ~0LL);
855	}
856
857	unicode_block blocksForFont;
858	link.Read<unicode_block>(&blocksForFont);
859
860	return blocksForFont;
861}
862
863bool
864BFont::IncludesBlock(uint32 start, uint32 end) const
865{
866	BPrivate::AppServerLink link;
867	link.StartMessage(AS_GET_HAS_UNICODE_BLOCK);
868	link.Attach<uint16>(fFamilyID);
869	link.Attach<uint16>(fStyleID);
870	link.Attach<uint32>(start);
871	link.Attach<uint32>(end);
872
873	int32 status;
874	if (link.FlushWithReply(status) != B_OK
875		|| status != B_OK) {
876		return false;
877	}
878
879	bool hasBlock;
880	link.Read<bool>(&hasBlock);
881
882	return hasBlock;
883}
884
885
886font_file_format
887BFont::FileFormat() const
888{
889	BPrivate::AppServerLink link;
890	link.StartMessage(AS_GET_FONT_FILE_FORMAT);
891	link.Attach<uint16>(fFamilyID);
892	link.Attach<uint16>(fStyleID);
893
894	int32 status;
895	if (link.FlushWithReply(status) != B_OK
896		|| status != B_OK) {
897		// just take a safe bet...
898		return B_TRUETYPE_WINDOWS;
899	}
900
901	uint16 format;
902	link.Read<uint16>(&format);
903
904	return (font_file_format)format;
905}
906
907
908int32
909BFont::CountTuned() const
910{
911	BPrivate::AppServerLink link;
912	link.StartMessage(AS_GET_TUNED_COUNT);
913	link.Attach<uint16>(fFamilyID);
914	link.Attach<uint16>(fStyleID);
915
916	int32 code;
917	if (link.FlushWithReply(code) != B_OK
918		|| code != B_OK)
919		return -1;
920
921	int32 count;
922	link.Read<int32>(&count);
923	return count;
924}
925
926
927void
928BFont::GetTunedInfo(int32 index, tuned_font_info* info) const
929{
930	if (info == NULL)
931		return;
932
933	BPrivate::AppServerLink link;
934	link.StartMessage(AS_GET_TUNED_INFO);
935	link.Attach<uint16>(fFamilyID);
936	link.Attach<uint16>(fStyleID);
937	link.Attach<uint32>(index);
938
939	int32 code;
940	if (link.FlushWithReply(code) != B_OK || code != B_OK)
941		return;
942
943	link.Read<tuned_font_info>(info);
944}
945
946
947// Truncates a string to a given _pixel_ width based on the font and size
948void
949BFont::TruncateString(BString* inOut, uint32 mode, float width) const
950{
951	if (mode == B_NO_TRUNCATION)
952		return;
953
954	// NOTE: Careful, we cannot directly use "inOut->String()" as result
955	// array, because the string length increases by 3 bytes in the worst
956	// case scenario.
957	const char* string = inOut->String();
958	GetTruncatedStrings(&string, 1, mode, width, inOut);
959}
960
961
962void
963BFont::GetTruncatedStrings(const char* stringArray[], int32 numStrings,
964	uint32 mode, float width, BString resultArray[]) const
965{
966	if (stringArray != NULL && numStrings > 0) {
967		// the width of the "���" glyph
968		float ellipsisWidth = StringWidth(B_UTF8_ELLIPSIS);
969
970		for (int32 i = 0; i < numStrings; i++) {
971			resultArray[i] = stringArray[i];
972			int32 numChars = resultArray[i].CountChars();
973
974			// get the escapement of each glyph in font units
975			float* escapementArray = new float[numChars];
976			GetEscapements(stringArray[i], numChars, NULL, escapementArray);
977
978			truncate_string(resultArray[i], mode, width, escapementArray,
979				fSize, ellipsisWidth, numChars);
980
981			delete[] escapementArray;
982		}
983	}
984}
985
986
987void
988BFont::GetTruncatedStrings(const char* stringArray[], int32 numStrings,
989	uint32 mode, float width, char* resultArray[]) const
990{
991	if (stringArray != NULL && numStrings > 0) {
992		for (int32 i = 0; i < numStrings; i++) {
993			BString* strings = new BString[numStrings];
994			GetTruncatedStrings(stringArray, numStrings, mode, width, strings);
995
996			for (int32 i = 0; i < numStrings; i++)
997				strcpy(resultArray[i], strings[i].String());
998
999			delete[] strings;
1000		}
1001	}
1002}
1003
1004
1005float
1006BFont::StringWidth(const char* string) const
1007{
1008	if (string == NULL)
1009		return 0.0;
1010
1011	int32 length = strlen(string);
1012	float width;
1013	GetStringWidths(&string, &length, 1, &width);
1014
1015	return width;
1016}
1017
1018
1019float
1020BFont::StringWidth(const char* string, int32 length) const
1021{
1022	if (!string || length < 1)
1023		return 0.0f;
1024
1025	float width = 0.0f;
1026	GetStringWidths(&string, &length, 1, &width);
1027
1028	return width;
1029}
1030
1031
1032void
1033BFont::GetStringWidths(const char* stringArray[], const int32 lengthArray[],
1034	int32 numStrings, float widthArray[]) const
1035{
1036	if (stringArray == NULL || lengthArray == NULL || numStrings < 1
1037		|| widthArray == NULL) {
1038		return;
1039	}
1040
1041	BPrivate::AppServerLink link;
1042	link.StartMessage(AS_GET_STRING_WIDTHS);
1043	link.Attach<uint16>(fFamilyID);
1044	link.Attach<uint16>(fStyleID);
1045	link.Attach<float>(fSize);
1046	link.Attach<uint8>(fSpacing);
1047	link.Attach<int32>(numStrings);
1048
1049	// TODO: all strings into a single array???
1050	// we do have a maximum message length, and it could be easily touched
1051	// here...
1052	for (int32 i = 0; i < numStrings; i++)
1053		link.AttachString(stringArray[i], lengthArray[i]);
1054
1055	status_t status;
1056	if (link.FlushWithReply(status) != B_OK || status != B_OK)
1057		return;
1058
1059	link.Read(widthArray, sizeof(float) * numStrings);
1060}
1061
1062
1063void
1064BFont::GetEscapements(const char charArray[], int32 numChars,
1065	float escapementArray[]) const
1066{
1067	GetEscapements(charArray, numChars, NULL, escapementArray);
1068}
1069
1070
1071void
1072BFont::GetEscapements(const char charArray[], int32 numChars,
1073	escapement_delta* delta, float escapementArray[]) const
1074{
1075	if (charArray == NULL || numChars < 1 || escapementArray == NULL)
1076		return;
1077
1078	BPrivate::AppServerLink link;
1079	link.StartMessage(AS_GET_ESCAPEMENTS_AS_FLOATS);
1080	link.Attach<uint16>(fFamilyID);
1081	link.Attach<uint16>(fStyleID);
1082	link.Attach<float>(fSize);
1083	link.Attach<uint8>(fSpacing);
1084	link.Attach<float>(fRotation);
1085	link.Attach<uint32>(fFlags);
1086
1087	link.Attach<float>(delta ? delta->nonspace : 0.0f);
1088	link.Attach<float>(delta ? delta->space : 0.0f);
1089	link.Attach<int32>(numChars);
1090
1091	// TODO: Should we not worry about the port capacity here?!?
1092	uint32 bytesInBuffer = UTF8CountBytes(charArray, numChars);
1093	link.Attach<int32>(bytesInBuffer);
1094	link.Attach(charArray, bytesInBuffer);
1095
1096	int32 code;
1097	if (link.FlushWithReply(code) != B_OK || code != B_OK)
1098		return;
1099
1100	link.Read(escapementArray, numChars * sizeof(float));
1101}
1102
1103
1104void
1105BFont::GetEscapements(const char charArray[], int32 numChars,
1106	escapement_delta* delta, BPoint escapementArray[]) const
1107{
1108	GetEscapements(charArray, numChars, delta, escapementArray, NULL);
1109}
1110
1111
1112void
1113BFont::GetEscapements(const char charArray[], int32 numChars,
1114	escapement_delta* delta, BPoint escapementArray[],
1115	BPoint offsetArray[]) const
1116{
1117	if (charArray == NULL || numChars < 1 || escapementArray == NULL)
1118		return;
1119
1120	BPrivate::AppServerLink link;
1121	link.StartMessage(AS_GET_ESCAPEMENTS);
1122	link.Attach<uint16>(fFamilyID);
1123	link.Attach<uint16>(fStyleID);
1124	link.Attach<float>(fSize);
1125	link.Attach<uint8>(fSpacing);
1126	link.Attach<float>(fRotation);
1127	link.Attach<uint32>(fFlags);
1128
1129	link.Attach<float>(delta ? delta->nonspace : 0.0);
1130	link.Attach<float>(delta ? delta->space : 0.0);
1131	link.Attach<bool>(offsetArray != NULL);
1132	link.Attach<int32>(numChars);
1133
1134	// TODO: Should we not worry about the port capacity here?!?
1135	uint32 bytesInBuffer = UTF8CountBytes(charArray, numChars);
1136	link.Attach<int32>(bytesInBuffer);
1137	link.Attach(charArray, bytesInBuffer);
1138
1139	int32 code;
1140	if (link.FlushWithReply(code) != B_OK || code != B_OK)
1141		return;
1142
1143	link.Read(escapementArray, sizeof(BPoint) * numChars);
1144	if (offsetArray)
1145		link.Read(offsetArray, sizeof(BPoint) * numChars);
1146}
1147
1148
1149void
1150BFont::GetEdges(const char charArray[], int32 numChars,
1151	edge_info edgeArray[]) const
1152{
1153	if (!charArray || numChars < 1 || !edgeArray)
1154		return;
1155
1156	int32 code;
1157	BPrivate::AppServerLink link;
1158
1159	link.StartMessage(AS_GET_EDGES);
1160	link.Attach<uint16>(fFamilyID);
1161	link.Attach<uint16>(fStyleID);
1162	link.Attach<int32>(numChars);
1163
1164	uint32 bytesInBuffer = UTF8CountBytes(charArray, numChars);
1165	link.Attach<int32>(bytesInBuffer);
1166	link.Attach(charArray, bytesInBuffer);
1167
1168	if (link.FlushWithReply(code) != B_OK || code != B_OK)
1169		return;
1170
1171	link.Read(edgeArray, sizeof(edge_info) * numChars);
1172}
1173
1174
1175void
1176BFont::GetHeight(font_height* _height) const
1177{
1178	if (_height == NULL)
1179		return;
1180
1181	if (fHeight.ascent == kUninitializedAscent) {
1182		// we don't have the font height cached yet
1183		BPrivate::AppServerLink link;
1184
1185		link.StartMessage(AS_GET_FONT_HEIGHT);
1186		link.Attach<uint16>(fFamilyID);
1187		link.Attach<uint16>(fStyleID);
1188		link.Attach<float>(fSize);
1189
1190		int32 code;
1191		if (link.FlushWithReply(code) != B_OK || code != B_OK)
1192			return;
1193
1194		// Who put that "const" to this method? :-)
1195		// We made fHeight mutable for this, but we should drop the "const"
1196		// when we can
1197		link.Read<font_height>(&fHeight);
1198	}
1199
1200	*_height = fHeight;
1201}
1202
1203
1204void
1205BFont::GetBoundingBoxesAsGlyphs(const char charArray[], int32 numChars,
1206	font_metric_mode mode, BRect boundingBoxArray[]) const
1207{
1208	_GetBoundingBoxes(charArray, numChars, mode, false, NULL,
1209		boundingBoxArray, false);
1210}
1211
1212
1213void
1214BFont::GetBoundingBoxesAsString(const char charArray[], int32 numChars,
1215	font_metric_mode mode, escapement_delta* delta,
1216	BRect boundingBoxArray[]) const
1217{
1218	_GetBoundingBoxes(charArray, numChars, mode, true, delta,
1219		boundingBoxArray, true);
1220}
1221
1222
1223void
1224BFont::_GetBoundingBoxes(const char charArray[], int32 numChars,
1225	font_metric_mode mode, bool string_escapement, escapement_delta* delta,
1226	BRect boundingBoxArray[], bool asString) const
1227{
1228	if (charArray == NULL || numChars < 1 || boundingBoxArray == NULL)
1229		return;
1230
1231	int32 code;
1232	BPrivate::AppServerLink link;
1233
1234	link.StartMessage(asString
1235		? AS_GET_BOUNDINGBOXES_STRING : AS_GET_BOUNDINGBOXES_CHARS);
1236	link.Attach<uint16>(fFamilyID);
1237	link.Attach<uint16>(fStyleID);
1238	link.Attach<float>(fSize);
1239	link.Attach<float>(fRotation);
1240	link.Attach<float>(fShear);
1241	link.Attach<float>(fFalseBoldWidth);
1242	link.Attach<uint8>(fSpacing);
1243
1244	link.Attach<uint32>(fFlags);
1245	link.Attach<font_metric_mode>(mode);
1246	link.Attach<bool>(string_escapement);
1247
1248	if (delta != NULL) {
1249		link.Attach<escapement_delta>(*delta);
1250	} else {
1251		escapement_delta emptyDelta = {0, 0};
1252		link.Attach<escapement_delta>(emptyDelta);
1253	}
1254
1255	link.Attach<int32>(numChars);
1256	uint32 bytesInBuffer = UTF8CountBytes(charArray, numChars);
1257	link.Attach<int32>(bytesInBuffer);
1258	link.Attach(charArray, bytesInBuffer);
1259
1260	if (link.FlushWithReply(code) != B_OK || code != B_OK)
1261		return;
1262
1263	link.Read(boundingBoxArray, sizeof(BRect) * numChars);
1264}
1265
1266
1267void
1268BFont::GetBoundingBoxesForStrings(const char* stringArray[], int32 numStrings,
1269	font_metric_mode mode, escapement_delta deltas[],
1270	BRect boundingBoxArray[]) const
1271{
1272	if (!stringArray || numStrings < 1 || !boundingBoxArray)
1273		return;
1274
1275	int32 code;
1276	BPrivate::AppServerLink link;
1277
1278	link.StartMessage(AS_GET_BOUNDINGBOXES_STRINGS);
1279	link.Attach<uint16>(fFamilyID);
1280	link.Attach<uint16>(fStyleID);
1281	link.Attach<float>(fSize);
1282	link.Attach<float>(fRotation);
1283	link.Attach<float>(fShear);
1284	link.Attach<float>(fFalseBoldWidth);
1285	link.Attach<uint8>(fSpacing);
1286	link.Attach<uint32>(fFlags);
1287	link.Attach<font_metric_mode>(mode);
1288	link.Attach<int32>(numStrings);
1289
1290	if (deltas) {
1291		for (int32 i = 0; i < numStrings; i++) {
1292			link.AttachString(stringArray[i]);
1293			link.Attach<escapement_delta>(deltas[i]);
1294		}
1295	} else {
1296		escapement_delta emptyDelta = {0, 0};
1297
1298		for (int32 i = 0; i < numStrings; i++) {
1299			link.AttachString(stringArray[i]);
1300			link.Attach<escapement_delta>(emptyDelta);
1301		}
1302	}
1303
1304	if (link.FlushWithReply(code) != B_OK || code != B_OK)
1305		return;
1306
1307	link.Read(boundingBoxArray, sizeof(BRect) * numStrings);
1308}
1309
1310
1311void
1312BFont::GetGlyphShapes(const char charArray[], int32 numChars,
1313	BShape* glyphShapeArray[]) const
1314{
1315	// TODO: implement code specifically for passing BShapes to and
1316	// from the server
1317	if (!charArray || numChars < 1 || !glyphShapeArray)
1318		return;
1319
1320	int32 code;
1321	BPrivate::AppServerLink link;
1322
1323	link.StartMessage(AS_GET_GLYPH_SHAPES);
1324	link.Attach<uint16>(fFamilyID);
1325	link.Attach<uint16>(fStyleID);
1326	link.Attach<float>(fSize);
1327	link.Attach<float>(fShear);
1328	link.Attach<float>(fRotation);
1329	link.Attach<float>(fFalseBoldWidth);
1330	link.Attach<uint32>(fFlags);
1331	link.Attach<int32>(numChars);
1332
1333	uint32 bytesInBuffer = UTF8CountBytes(charArray, numChars);
1334	link.Attach<int32>(bytesInBuffer);
1335	link.Attach(charArray, bytesInBuffer);
1336
1337	if (link.FlushWithReply(code) != B_OK || code != B_OK)
1338		return;
1339
1340	for (int32 i = 0; i < numChars; i++)
1341		link.ReadShape(glyphShapeArray[i]);
1342}
1343
1344
1345void
1346BFont::GetHasGlyphs(const char charArray[], int32 numChars,
1347	bool hasArray[]) const
1348{
1349	if (!charArray || numChars < 1 || !hasArray)
1350		return;
1351
1352	int32 code;
1353	BPrivate::AppServerLink link;
1354
1355	link.StartMessage(AS_GET_HAS_GLYPHS);
1356	link.Attach<uint16>(fFamilyID);
1357	link.Attach<uint16>(fStyleID);
1358	link.Attach<int32>(numChars);
1359
1360	uint32 bytesInBuffer = UTF8CountBytes(charArray, numChars);
1361	link.Attach<int32>(bytesInBuffer);
1362	link.Attach(charArray, bytesInBuffer);
1363
1364	if (link.FlushWithReply(code) != B_OK || code != B_OK)
1365		return;
1366
1367	link.Read(hasArray, sizeof(bool) * numChars);
1368}
1369
1370
1371BFont&
1372BFont::operator=(const BFont& font)
1373{
1374	fFamilyID = font.fFamilyID;
1375	fStyleID = font.fStyleID;
1376	fSize = font.fSize;
1377	fShear = font.fShear;
1378	fRotation = font.fRotation;
1379	fFalseBoldWidth = font.fFalseBoldWidth;
1380	fSpacing = font.fSpacing;
1381	fEncoding = font.fEncoding;
1382	fFace = font.fFace;
1383	fHeight = font.fHeight;
1384	fFlags = font.fFlags;
1385	fExtraFlags = font.fExtraFlags;
1386
1387	return *this;
1388}
1389
1390
1391bool
1392BFont::operator==(const BFont& font) const
1393{
1394	return fFamilyID == font.fFamilyID
1395		&& fStyleID == font.fStyleID
1396		&& fSize == font.fSize
1397		&& fShear == font.fShear
1398		&& fRotation == font.fRotation
1399		&& fFalseBoldWidth == font.fFalseBoldWidth
1400		&& fSpacing == font.fSpacing
1401		&& fEncoding == font.fEncoding
1402		&& fFace == font.fFace;
1403}
1404
1405
1406bool
1407BFont::operator!=(const BFont& font) const
1408{
1409	return fFamilyID != font.fFamilyID
1410		|| fStyleID != font.fStyleID
1411		|| fSize != font.fSize
1412		|| fShear != font.fShear
1413		|| fRotation != font.fRotation
1414		|| fFalseBoldWidth != font.fFalseBoldWidth
1415		|| fSpacing != font.fSpacing
1416		|| fEncoding != font.fEncoding
1417		|| fFace != font.fFace;
1418}
1419
1420
1421void
1422BFont::PrintToStream() const
1423{
1424	font_family family;
1425	font_style style;
1426	GetFamilyAndStyle(&family, &style);
1427
1428	printf("BFont { %s (%d), %s (%d) 0x%x %f/%f %fpt (%f %f %f), %d }\n",
1429		family, fFamilyID, style, fStyleID, fFace, fShear, fRotation, fSize,
1430		fHeight.ascent, fHeight.descent, fHeight.leading, fEncoding);
1431}
1432
1433
1434void
1435BFont::_GetExtraFlags() const
1436{
1437	// TODO: this has to be const in order to allow other font getters to
1438	// stay const as well
1439	if (fExtraFlags != kUninitializedExtraFlags)
1440		return;
1441
1442	BPrivate::AppServerLink link;
1443	link.StartMessage(AS_GET_EXTRA_FONT_FLAGS);
1444	link.Attach<uint16>(fFamilyID);
1445	link.Attach<uint16>(fStyleID);
1446
1447	status_t status = B_ERROR;
1448	if (link.FlushWithReply(status) != B_OK || status != B_OK) {
1449		// use defaut values for the flags
1450		fExtraFlags = (uint32)B_FONT_LEFT_TO_RIGHT
1451			<< B_PRIVATE_FONT_DIRECTION_SHIFT;
1452		return;
1453	}
1454
1455	link.Read<uint32>(&fExtraFlags);
1456}
1457
1458
1459status_t
1460BFont::LoadFont(const char* path)
1461{
1462	return LoadFont(path, 0, 0);
1463}
1464
1465
1466status_t
1467BFont::LoadFont(const char* path, uint16 index, uint16 instance)
1468{
1469	BPrivate::AppServerLink link;
1470	link.StartMessage(AS_ADD_FONT_FILE);
1471	link.AttachString(path);
1472	link.Attach<uint16>(index);
1473	link.Attach<uint16>(instance);
1474	status_t status = B_ERROR;
1475	if (link.FlushWithReply(status) != B_OK || status != B_OK) {
1476		return status;
1477	}
1478
1479	link.Read<uint16>(&fFamilyID);
1480	link.Read<uint16>(&fStyleID);
1481	link.Read<uint16>(&fFace);
1482	fHeight.ascent = kUninitializedAscent;
1483	fExtraFlags = kUninitializedExtraFlags;
1484
1485	return B_OK;
1486}
1487
1488
1489status_t
1490BFont::LoadFont(const area_id fontAreaID, size_t size, size_t offset)
1491{
1492	return LoadFont(fontAreaID, size, offset, 0, 0);
1493}
1494
1495
1496status_t
1497BFont::LoadFont(const area_id fontAreaID, size_t size, size_t offset, uint16 index, uint16 instance)
1498{
1499	BPrivate::AppServerLink link;
1500
1501	link.StartMessage(AS_ADD_FONT_MEMORY);
1502
1503	link.Attach<int32>(fontAreaID);
1504	link.Attach<size_t>(size);
1505	link.Attach<size_t>(offset);
1506	link.Attach<uint16>(index);
1507	link.Attach<uint16>(instance);
1508
1509	status_t status = B_ERROR;
1510	if (link.FlushWithReply(status) != B_OK || status != B_OK) {
1511		return status;
1512	}
1513
1514	link.Read<uint16>(&fFamilyID);
1515	link.Read<uint16>(&fStyleID);
1516	link.Read<uint16>(&fFace);
1517	fHeight.ascent = kUninitializedAscent;
1518	fExtraFlags = kUninitializedExtraFlags;
1519
1520	return B_OK;
1521}
1522
1523
1524status_t
1525BFont::UnloadFont()
1526{
1527	BPrivate::AppServerLink link;
1528
1529	link.StartMessage(AS_REMOVE_FONT);
1530
1531	link.Attach<uint16>(fFamilyID);
1532	link.Attach<uint16>(fStyleID);
1533
1534	status_t status = B_ERROR;
1535	if (link.FlushWithReply(status) != B_OK || status != B_OK) {
1536		return status;
1537	}
1538
1539	// reset to plain font
1540	fFamilyID = 0;
1541	fStyleID = 0;
1542	fFace = 0;
1543	fHeight.ascent = kUninitializedAscent;
1544	fExtraFlags = kUninitializedExtraFlags;
1545
1546	return B_OK;
1547}
1548