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