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