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