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