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<uint8>(fSpacing); 1051 link.Attach<float>(fRotation); 1052 link.Attach<uint32>(fFlags); 1053 1054 link.Attach<float>(delta ? delta->nonspace : 0.0f); 1055 link.Attach<float>(delta ? delta->space : 0.0f); 1056 link.Attach<int32>(numChars); 1057 1058 // TODO: Should we not worry about the port capacity here?!? 1059 uint32 bytesInBuffer = UTF8CountBytes(charArray, numChars); 1060 link.Attach<int32>(bytesInBuffer); 1061 link.Attach(charArray, bytesInBuffer); 1062 1063 int32 code; 1064 if (link.FlushWithReply(code) != B_OK 1065 || code != B_OK) 1066 return; 1067 1068 link.Read(escapementArray, numChars * sizeof(float)); 1069 } 1070 1071 1072 void 1073 BFont::GetEscapements(const char charArray[], int32 numChars, escapement_delta *delta, 1074 BPoint escapementArray[]) const 1075 { 1076 GetEscapements(charArray, numChars, delta, escapementArray, NULL); 1077 } 1078 1079 1080 void 1081 BFont::GetEscapements(const char charArray[], int32 numChars, escapement_delta *delta, 1082 BPoint escapementArray[], BPoint offsetArray[]) const 1083 { 1084 if (!charArray || numChars < 1 || !escapementArray) 1085 return; 1086 1087 BPrivate::AppServerLink link; 1088 link.StartMessage(AS_GET_ESCAPEMENTS); 1089 link.Attach<uint16>(fFamilyID); 1090 link.Attach<uint16>(fStyleID); 1091 link.Attach<float>(fSize); 1092 link.Attach<uint8>(fSpacing); 1093 link.Attach<float>(fRotation); 1094 link.Attach<uint32>(fFlags); 1095 1096 link.Attach<float>(delta ? delta->nonspace : 0.0); 1097 link.Attach<float>(delta ? delta->space : 0.0); 1098 link.Attach<bool>(offsetArray != NULL); 1099 link.Attach<int32>(numChars); 1100 1101 // TODO: Should we not worry about the port capacity here?!? 1102 uint32 bytesInBuffer = UTF8CountBytes(charArray, numChars); 1103 link.Attach<int32>(bytesInBuffer); 1104 link.Attach(charArray, bytesInBuffer); 1105 1106 int32 code; 1107 if (link.FlushWithReply(code) != B_OK 1108 || code != B_OK) 1109 return; 1110 1111 link.Read(escapementArray, sizeof(BPoint) * numChars); 1112 if (offsetArray) 1113 link.Read(offsetArray, sizeof(BPoint) * numChars); 1114 } 1115 1116 1117 void 1118 BFont::GetEdges(const char charArray[], int32 numChars, edge_info edgeArray[]) const 1119 { 1120 if (!charArray || numChars < 1 || !edgeArray) 1121 return; 1122 1123 int32 code; 1124 BPrivate::AppServerLink link; 1125 1126 link.StartMessage(AS_GET_EDGES); 1127 link.Attach<int32>(numChars); 1128 1129 uint32 bytesInBuffer = UTF8CountBytes(charArray, numChars); 1130 link.Attach<int32>(bytesInBuffer); 1131 link.Attach(charArray, bytesInBuffer); 1132 1133 if (link.FlushWithReply(code) != B_OK 1134 || code != B_OK) 1135 return; 1136 1137 link.Read(edgeArray, sizeof(edge_info) * numChars); 1138 } 1139 1140 1141 void 1142 BFont::GetHeight(font_height *_height) const 1143 { 1144 if (_height == NULL) 1145 return; 1146 1147 if (fHeight.ascent == kUninitializedAscent) { 1148 // we don't have the font height cached yet 1149 BPrivate::AppServerLink link; 1150 1151 link.StartMessage(AS_GET_FONT_HEIGHT); 1152 link.Attach<uint16>(fFamilyID); 1153 link.Attach<uint16>(fStyleID); 1154 link.Attach<float>(fSize); 1155 1156 int32 code; 1157 if (link.FlushWithReply(code) != B_OK 1158 || code != B_OK) 1159 return; 1160 1161 // Who put that "const" to this method? :-) 1162 // We made fHeight mutable for this, but we should drop the "const" when we can 1163 link.Read<font_height>(&fHeight); 1164 } 1165 1166 *_height = fHeight; 1167 } 1168 1169 1170 void 1171 BFont::GetBoundingBoxesAsGlyphs(const char charArray[], int32 numChars, font_metric_mode mode, 1172 BRect boundingBoxArray[]) const 1173 { 1174 _GetBoundingBoxes(charArray, numChars, mode, false, NULL, boundingBoxArray); 1175 } 1176 1177 1178 void 1179 BFont::GetBoundingBoxesAsString(const char charArray[], int32 numChars, font_metric_mode mode, 1180 escapement_delta *delta, BRect boundingBoxArray[]) const 1181 { 1182 _GetBoundingBoxes(charArray, numChars, mode, true, delta, boundingBoxArray); 1183 } 1184 1185 1186 void 1187 BFont::_GetBoundingBoxes(const char charArray[], int32 numChars, font_metric_mode mode, 1188 bool string_escapement, escapement_delta *delta, BRect boundingBoxArray[]) const 1189 { 1190 if (!charArray || numChars < 1 || !boundingBoxArray) 1191 return; 1192 1193 int32 code; 1194 BPrivate::AppServerLink link; 1195 1196 link.StartMessage(AS_GET_BOUNDINGBOXES_CHARS); 1197 link.Attach<uint16>(fFamilyID); 1198 link.Attach<uint16>(fStyleID); 1199 link.Attach<float>(fSize); 1200 link.Attach<float>(fRotation); 1201 link.Attach<float>(fShear); 1202 link.Attach<uint8>(fSpacing); 1203 1204 link.Attach<uint32>(fFlags); 1205 link.Attach<font_metric_mode>(mode); 1206 link.Attach<bool>(string_escapement); 1207 1208 if (delta) { 1209 link.Attach<escapement_delta>(*delta); 1210 } else { 1211 escapement_delta emptyDelta = {0, 0}; 1212 link.Attach<escapement_delta>(emptyDelta); 1213 } 1214 1215 link.Attach<int32>(numChars); 1216 uint32 bytesInBuffer = UTF8CountBytes(charArray, numChars); 1217 link.Attach<int32>(bytesInBuffer); 1218 link.Attach(charArray, bytesInBuffer); 1219 1220 if (link.FlushWithReply(code) != B_OK 1221 || code != B_OK) 1222 return; 1223 1224 link.Read(boundingBoxArray, sizeof(BRect) * numChars); 1225 } 1226 1227 1228 void 1229 BFont::GetBoundingBoxesForStrings(const char *stringArray[], int32 numStrings, 1230 font_metric_mode mode, escapement_delta deltas[], BRect boundingBoxArray[]) const 1231 { 1232 if (!stringArray || numStrings < 1 || !boundingBoxArray) 1233 return; 1234 1235 int32 code; 1236 BPrivate::AppServerLink link; 1237 1238 link.StartMessage(AS_GET_BOUNDINGBOXES_STRINGS); 1239 link.Attach<uint16>(fFamilyID); 1240 link.Attach<uint16>(fStyleID); 1241 link.Attach<float>(fSize); 1242 link.Attach<float>(fRotation); 1243 link.Attach<float>(fShear); 1244 link.Attach<uint8>(fSpacing); 1245 link.Attach<uint32>(fFlags); 1246 link.Attach<font_metric_mode>(mode); 1247 link.Attach<int32>(numStrings); 1248 1249 if (deltas) { 1250 for (int32 i = 0; i < numStrings; i++) { 1251 link.AttachString(stringArray[i]); 1252 link.Attach<escapement_delta>(deltas[i]); 1253 } 1254 } else { 1255 escapement_delta emptyDelta = {0, 0}; 1256 1257 for (int32 i = 0; i < numStrings; i++) { 1258 link.AttachString(stringArray[i]); 1259 link.Attach<escapement_delta>(emptyDelta); 1260 } 1261 } 1262 1263 if (link.FlushWithReply(code) != B_OK 1264 || code != B_OK) 1265 return; 1266 1267 link.Read(boundingBoxArray, sizeof(BRect) * numStrings); 1268 } 1269 1270 1271 void 1272 BFont::GetGlyphShapes(const char charArray[], int32 numChars, BShape *glyphShapeArray[]) const 1273 { 1274 // TODO: implement code specifically for passing BShapes to and from the server 1275 if (!charArray || numChars < 1 || !glyphShapeArray) 1276 return; 1277 1278 int32 code; 1279 BPrivate::AppServerLink link; 1280 1281 link.StartMessage(AS_GET_GLYPH_SHAPES); 1282 link.Attach<uint16>(fFamilyID); 1283 link.Attach<uint16>(fStyleID); 1284 link.Attach<float>(fSize); 1285 link.Attach<float>(fShear); 1286 link.Attach<float>(fRotation); 1287 link.Attach<uint32>(fFlags); 1288 link.Attach<int32>(numChars); 1289 1290 uint32 bytesInBuffer = UTF8CountBytes(charArray, numChars); 1291 link.Attach<int32>(bytesInBuffer); 1292 link.Attach(charArray, bytesInBuffer); 1293 1294 if (link.FlushWithReply(code) != B_OK 1295 || code != B_OK) 1296 return; 1297 1298 for (int32 i = 0; i < numChars; i++) 1299 link.ReadShape(glyphShapeArray[i]); 1300 } 1301 1302 1303 void 1304 BFont::GetHasGlyphs(const char charArray[], int32 numChars, bool hasArray[]) const 1305 { 1306 if (!charArray || numChars < 1 || !hasArray) 1307 return; 1308 1309 int32 code; 1310 BPrivate::AppServerLink link; 1311 1312 link.StartMessage(AS_GET_HAS_GLYPHS); 1313 link.Attach<uint16>(fFamilyID); 1314 link.Attach<uint16>(fStyleID); 1315 link.Attach<int32>(numChars); 1316 1317 uint32 bytesInBuffer = UTF8CountBytes(charArray, numChars); 1318 link.Attach<int32>(bytesInBuffer); 1319 link.Attach(charArray, bytesInBuffer); 1320 1321 if (link.FlushWithReply(code) != B_OK 1322 || code != B_OK) 1323 return; 1324 1325 link.Read(hasArray, sizeof(bool) * numChars); 1326 } 1327 1328 1329 BFont & 1330 BFont::operator=(const BFont &font) 1331 { 1332 fFamilyID = font.fFamilyID; 1333 fStyleID = font.fStyleID; 1334 fSize = font.fSize; 1335 fShear = font.fShear; 1336 fRotation = font.fRotation; 1337 fSpacing = font.fSpacing; 1338 fEncoding = font.fEncoding; 1339 fFace = font.fFace; 1340 fHeight = font.fHeight; 1341 fFlags = font.fFlags; 1342 return *this; 1343 } 1344 1345 1346 bool 1347 BFont::operator==(const BFont &font) const 1348 { 1349 return fFamilyID == font.fFamilyID 1350 && fStyleID == font.fStyleID 1351 && fSize == font.fSize 1352 && fShear == font.fShear 1353 && fRotation == font.fRotation 1354 && fSpacing == font.fSpacing 1355 && fEncoding == font.fEncoding 1356 && fFace == font.fFace; 1357 } 1358 1359 1360 bool 1361 BFont::operator!=(const BFont &font) const 1362 { 1363 return fFamilyID != font.fFamilyID 1364 || fStyleID != font.fStyleID 1365 || fSize != font.fSize 1366 || fShear != font.fShear 1367 || fRotation != font.fRotation 1368 || fSpacing != font.fSpacing 1369 || fEncoding != font.fEncoding 1370 || fFace != font.fFace; 1371 } 1372 1373 1374 void 1375 BFont::PrintToStream() const 1376 { 1377 font_family family; 1378 font_style style; 1379 GetFamilyAndStyle(&family, &style); 1380 1381 printf("BFont { %s (%d), %s (%d) 0x%x %f/%f %fpt (%f %f %f), %d }\n", family, 1382 fFamilyID, style, fStyleID, fFace, fShear, fRotation, fSize, 1383 fHeight.ascent, fHeight.descent, fHeight.leading, fEncoding); 1384 } 1385 1386 1387 void 1388 BFont::_GetExtraFlags() const 1389 { 1390 // TODO: this has to be const in order to allow other font getters to stay const as well 1391 if (fExtraFlags != kUninitializedExtraFlags) 1392 return; 1393 1394 BPrivate::AppServerLink link; 1395 link.StartMessage(AS_GET_EXTRA_FONT_FLAGS); 1396 link.Attach<uint16>(fFamilyID); 1397 link.Attach<uint16>(fStyleID); 1398 1399 status_t status = B_ERROR; 1400 if (link.FlushWithReply(status) != B_OK 1401 || status != B_OK) { 1402 // use defaut values for the flags 1403 fExtraFlags = (uint32)B_FONT_LEFT_TO_RIGHT << B_PRIVATE_FONT_DIRECTION_SHIFT; 1404 return; 1405 } 1406 1407 link.Read<uint32>(&fExtraFlags); 1408 } 1409