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