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