1 /* 2 * Copyright 2001-2005, 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 <new> 13 14 #include <stdio.h> 15 #include <stdlib.h> 16 17 #include <Autolock.h> 18 #include <Font.h> 19 #include <Locker.h> 20 #include <Message.h> 21 #include <PortLink.h> 22 #include <Rect.h> 23 #include <ServerProtocol.h> 24 #include <Shape.h> 25 #include <String.h> 26 27 #include <AppServerLink.h> 28 #include <moreUTF8.h> 29 #include <truncate_string.h> 30 #include <FontPrivate.h> 31 #include <ObjectList.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 // TODO: the following functions are private Be API functions - if no problems 347 // arise by not exporting these symbols, we can just remove them 348 #if 0 349 void _font_control_(BFont *font, int32 cmd, void *data) {} 350 status_t get_font_cache_info(uint32 id, void *set) { return B_ERROR; } 351 status_t set_font_cache_info(uint32 id, void *set) { return B_ERROR; } 352 #endif 353 354 355 /*! 356 \brief Private function used to replace the R5 hack which sets a system font 357 \param which string denoting which font to set 358 \param family the new family for the system font 359 \param style the new style for the system font 360 \param size the size for the system font to have 361 362 R5 used a global area offset table to set the system fonts in the Font 363 preferences panel. Bleah. 364 */ 365 void 366 _set_system_font_(const char *which, font_family family, font_style style, 367 float size) 368 { 369 BPrivate::AppServerLink link; 370 371 link.StartMessage(AS_SET_SYSTEM_FONT); 372 link.AttachString(which, B_OS_NAME_LENGTH); 373 link.AttachString(family, sizeof(font_family)); 374 link.AttachString(style, sizeof(font_style)); 375 link.Attach<float>(size); 376 link.Flush(); 377 } 378 379 380 status_t 381 _get_system_default_font_(const char* which, font_family family, 382 font_style style, float* _size) 383 { 384 BPrivate::AppServerLink link; 385 386 link.StartMessage(AS_GET_SYSTEM_DEFAULT_FONT); 387 link.AttachString(which, B_OS_NAME_LENGTH); 388 389 int32 status = B_ERROR; 390 if (link.FlushWithReply(status) != B_OK 391 || status < B_OK) 392 return status; 393 394 link.ReadString(family, sizeof(font_family)); 395 link.ReadString(style, sizeof(font_style)); 396 link.Read<float>(_size); 397 return B_OK; 398 } 399 400 401 /*! 402 \brief Returns the number of installed font families 403 \return The number of installed font families 404 */ 405 406 int32 407 count_font_families() 408 { 409 return sFontList.CountFamilies(); 410 } 411 412 413 /*! 414 \brief Returns the number of styles available for a font family 415 \return The number of styles available for a font family 416 */ 417 418 int32 419 count_font_styles(font_family family) 420 { 421 return sFontList.CountStyles(family); 422 } 423 424 425 /*! 426 \brief Retrieves the family name at the specified index 427 \param index Unique font identifier code. 428 \param name font_family string to receive the name of the family 429 \param flags if non-NULL, the values of the flags IS_FIXED and B_HAS_TUNED_FONT are returned 430 \return B_ERROR if the index does not correspond to a font family 431 */ 432 433 status_t 434 get_font_family(int32 index, font_family *_name, uint32 *_flags) 435 { 436 if (_name == NULL) 437 return B_BAD_VALUE; 438 439 return sFontList.FamilyAt(index, _name, _flags); 440 } 441 442 443 /*! 444 \brief Retrieves the family name at the specified index 445 \param index Unique font identifier code. 446 \param name font_family string to receive the name of the family 447 \param flags if non-NULL, the values of the flags IS_FIXED and B_HAS_TUNED_FONT are returned 448 \return B_ERROR if the index does not correspond to a font style 449 */ 450 451 status_t 452 get_font_style(font_family family, int32 index, font_style *_name, 453 uint32 *_flags) 454 { 455 return get_font_style(family, index, _name, NULL, _flags); 456 } 457 458 459 /*! 460 \brief Retrieves the family name at the specified index 461 \param index Unique font identifier code. 462 \param name font_family string to receive the name of the family 463 \param face recipient of font face value, such as B_REGULAR_FACE 464 \param flags if non-NULL, the values of the flags IS_FIXED and B_HAS_TUNED_FONT are returned 465 \return B_ERROR if the index does not correspond to a font style 466 467 The face value returned by this function is not very reliable. At the same time, the value 468 returned should be fairly reliable, returning the proper flag for 90%-99% of font names. 469 */ 470 471 status_t 472 get_font_style(font_family family, int32 index, font_style *_name, 473 uint16 *_face, uint32 *_flags) 474 { 475 if (_name == NULL) 476 return B_BAD_VALUE; 477 478 return sFontList.StyleAt(family, index, _name, _face, _flags); 479 } 480 481 482 /*! 483 \brief Updates the font family list 484 \param checkOnly is ignored 485 \return true if the font list has changed, false if not. 486 */ 487 488 bool 489 update_font_families(bool /*checkOnly*/) 490 { 491 return sFontList.UpdatedOnServer(); 492 } 493 494 495 // #pragma mark - 496 497 498 BFont::BFont() 499 : 500 // initialise for be_plain_font (avoid circular definition) 501 fFamilyID(0), 502 fStyleID(0), 503 fSize(10.0), 504 fShear(90.0), 505 fRotation(0.0), 506 fSpacing(0), 507 fEncoding(0), 508 fFace(0), 509 fFlags(0), 510 fExtraFlags(kUninitializedExtraFlags) 511 { 512 if (be_plain_font != NULL && this != &sPlainFont) 513 *this = *be_plain_font; 514 else { 515 fHeight.ascent = 7.0; 516 fHeight.descent = 2.0; 517 fHeight.leading = 13.0; 518 } 519 } 520 521 522 BFont::BFont(const BFont &font) 523 { 524 *this = font; 525 } 526 527 528 BFont::BFont(const BFont *font) 529 { 530 if (font) 531 *this = *font; 532 else 533 *this = *be_plain_font; 534 } 535 536 537 /*! 538 \brief Sets the font's family and style all at once 539 \param family Font family to set 540 \param style Font style to set 541 \return B_NAME_NOT_FOUND if family or style do not exist. 542 */ 543 544 status_t 545 BFont::SetFamilyAndStyle(const font_family family, const font_style style) 546 { 547 if (family == NULL && style == NULL) 548 return B_BAD_VALUE; 549 550 BPrivate::AppServerLink link; 551 552 link.StartMessage(AS_GET_FAMILY_AND_STYLE_IDS); 553 link.AttachString(family, sizeof(font_family)); 554 link.AttachString(style, sizeof(font_style)); 555 link.Attach<uint16>(fFamilyID); 556 link.Attach<uint16>(0xffff); 557 link.Attach<uint16>(fFace); 558 559 int32 status = B_ERROR; 560 if (link.FlushWithReply(status) != B_OK 561 || status != B_OK) 562 return status; 563 564 link.Read<uint16>(&fFamilyID); 565 link.Read<uint16>(&fStyleID); 566 link.Read<uint16>(&fFace); 567 fHeight.ascent = kUninitializedAscent; 568 569 return B_OK; 570 } 571 572 573 /*! 574 \brief Sets the font's family and style all at once 575 \param code Unique font identifier obtained from the server. 576 */ 577 578 void 579 BFont::SetFamilyAndStyle(uint32 fontcode) 580 { 581 // R5 has a bug here: the face is not updated even though the IDs are set. This 582 // is a problem because the face flag includes Regular/Bold/Italic information in 583 // addition to stuff like underlining and strikethrough. As a result, this will 584 // need a trip to the server and, thus, be slower than R5's in order to be correct 585 586 uint16 family, style; 587 style = fontcode & 0xFFFF; 588 family = (fontcode & 0xFFFF0000) >> 16; 589 590 BPrivate::AppServerLink link; 591 link.StartMessage(AS_GET_FAMILY_AND_STYLE_IDS); 592 link.AttachString(NULL); // no family and style name 593 link.AttachString(NULL); 594 link.Attach<uint16>(family); 595 link.Attach<uint16>(style); 596 link.Attach<uint16>(fFace); 597 598 int32 code; 599 if (link.FlushWithReply(code) != B_OK 600 || code != B_OK) 601 return; 602 603 link.Read<uint16>(&fFamilyID); 604 link.Read<uint16>(&fStyleID); 605 link.Read<uint16>(&fFace); 606 fHeight.ascent = kUninitializedAscent; 607 } 608 609 610 /*! 611 \brief Sets the font's family and face all at once 612 \param family Font family to set 613 \param face Font face to set. 614 \return B_ERROR if family does not exists or face is an invalid value. 615 616 To comply with the BeBook, this function will only set valid values - i.e. passing a 617 nonexistent family will cause only the face to be set. Additionally, if a particular 618 face does not exist in a family, the closest match will be chosen. 619 */ 620 621 status_t 622 BFont::SetFamilyAndFace(const font_family family, uint16 face) 623 { 624 BPrivate::AppServerLink link; 625 link.StartMessage(AS_GET_FAMILY_AND_STYLE_IDS); 626 link.AttachString(family, sizeof(font_family)); 627 link.AttachString(NULL); // no style given 628 link.Attach<uint16>(fFamilyID); 629 link.Attach<uint16>(0xffff); 630 link.Attach<uint16>(face); 631 632 int32 status = B_ERROR; 633 if (link.FlushWithReply(status) != B_OK 634 || status != B_OK) 635 return status; 636 637 link.Read<uint16>(&fFamilyID); 638 link.Read<uint16>(&fStyleID); 639 link.Read<uint16>(&fFace); 640 fHeight.ascent = kUninitializedAscent; 641 642 return B_OK; 643 } 644 645 646 void 647 BFont::SetSize(float size) 648 { 649 fSize = size; 650 fHeight.ascent = kUninitializedAscent; 651 } 652 653 654 void 655 BFont::SetShear(float shear) 656 { 657 fShear = shear; 658 fHeight.ascent = kUninitializedAscent; 659 } 660 661 662 void 663 BFont::SetRotation(float rotation) 664 { 665 fRotation = rotation; 666 fHeight.ascent = kUninitializedAscent; 667 } 668 669 670 void 671 BFont::SetSpacing(uint8 spacing) 672 { 673 fSpacing = spacing; 674 } 675 676 677 void 678 BFont::SetEncoding(uint8 encoding) 679 { 680 fEncoding = encoding; 681 } 682 683 684 void 685 BFont::SetFace(uint16 face) 686 { 687 if (face == fFace) 688 return; 689 690 SetFamilyAndFace(NULL, face); 691 } 692 693 694 void 695 BFont::SetFlags(uint32 flags) 696 { 697 fFlags = flags; 698 } 699 700 701 void 702 BFont::GetFamilyAndStyle(font_family *family, font_style *style) const 703 { 704 if (family == NULL && style == NULL) 705 return; 706 707 // it's okay to call this function with either family or style set to NULL 708 709 font_family familyBuffer; 710 font_style styleBuffer; 711 712 if (family == NULL) 713 family = &familyBuffer; 714 if (style == NULL) 715 style = &styleBuffer; 716 717 BPrivate::AppServerLink link; 718 link.StartMessage(AS_GET_FAMILY_AND_STYLE); 719 link.Attach<uint16>(fFamilyID); 720 link.Attach<uint16>(fStyleID); 721 722 int32 code; 723 if (link.FlushWithReply(code) != B_OK 724 || code != B_OK) { 725 // the least we can do is to clear the buffers 726 memset(*family, 0, sizeof(font_family)); 727 memset(*style, 0, sizeof(font_style)); 728 return; 729 } 730 731 link.ReadString(*family, sizeof(font_family)); 732 link.ReadString(*style, sizeof(font_style)); 733 } 734 735 736 uint32 737 BFont::FamilyAndStyle() const 738 { 739 return (fFamilyID << 16UL) | fStyleID; 740 } 741 742 743 float 744 BFont::Size() const 745 { 746 return fSize; 747 } 748 749 750 float 751 BFont::Shear() const 752 { 753 return fShear; 754 } 755 756 757 float 758 BFont::Rotation() const 759 { 760 return fRotation; 761 } 762 763 764 uint8 765 BFont::Spacing() const 766 { 767 return fSpacing; 768 } 769 770 771 uint8 772 BFont::Encoding() const 773 { 774 return fEncoding; 775 } 776 777 778 uint16 779 BFont::Face() const 780 { 781 return fFace; 782 } 783 784 785 uint32 786 BFont::Flags() const 787 { 788 return fFlags; 789 } 790 791 792 font_direction 793 BFont::Direction() const 794 { 795 _GetExtraFlags(); 796 return (font_direction)(fExtraFlags >> B_PRIVATE_FONT_DIRECTION_SHIFT); 797 } 798 799 800 bool 801 BFont::IsFixed() const 802 { 803 _GetExtraFlags(); 804 return (fExtraFlags & B_IS_FIXED) != 0; 805 } 806 807 808 /*! 809 \brief Returns true if the font is fixed-width and contains both full and half-width characters 810 811 This was left unimplemented as of R5. It was a way to work with both Kanji and Roman 812 characters in the same fixed-width font. 813 */ 814 815 bool 816 BFont::IsFullAndHalfFixed() const 817 { 818 _GetExtraFlags(); 819 return (fExtraFlags & B_PRIVATE_FONT_IS_FULL_AND_HALF_FIXED) != 0; 820 } 821 822 823 BRect 824 BFont::BoundingBox() const 825 { 826 BPrivate::AppServerLink link; 827 link.StartMessage(AS_GET_FONT_BOUNDING_BOX); 828 link.Attach<uint16>(fFamilyID); 829 link.Attach<uint16>(fStyleID); 830 831 int32 code; 832 if (link.FlushWithReply(code) != B_OK 833 || code != B_OK) 834 return BRect(0, 0, 0 ,0); 835 836 BRect box; 837 link.Read<BRect>(&box); 838 return box; 839 } 840 841 842 unicode_block 843 BFont::Blocks() const 844 { 845 // TODO: Add Block support 846 return unicode_block(); 847 } 848 849 850 font_file_format 851 BFont::FileFormat() const 852 { 853 BPrivate::AppServerLink link; 854 link.StartMessage(AS_GET_FONT_FILE_FORMAT); 855 link.Attach<uint16>(fFamilyID); 856 link.Attach<uint16>(fStyleID); 857 858 int32 status; 859 if (link.FlushWithReply(status) != B_OK 860 || status != B_OK) { 861 // just take a safe bet... 862 return B_TRUETYPE_WINDOWS; 863 } 864 865 uint16 format; 866 link.Read<uint16>(&format); 867 868 return (font_file_format)format; 869 } 870 871 872 int32 873 BFont::CountTuned() const 874 { 875 BPrivate::AppServerLink link; 876 link.StartMessage(AS_GET_TUNED_COUNT); 877 link.Attach<uint16>(fFamilyID); 878 link.Attach<uint16>(fStyleID); 879 880 int32 code; 881 if (link.FlushWithReply(code) != B_OK 882 || code != B_OK) 883 return -1; 884 885 int32 count; 886 link.Read<int32>(&count); 887 return count; 888 } 889 890 891 void 892 BFont::GetTunedInfo(int32 index, tuned_font_info *info) const 893 { 894 if (!info) 895 return; 896 897 BPrivate::AppServerLink link; 898 link.StartMessage(AS_GET_TUNED_INFO); 899 link.Attach<uint16>(fFamilyID); 900 link.Attach<uint16>(fStyleID); 901 link.Attach<uint32>(index); 902 903 int32 code; 904 if (link.FlushWithReply(code) != B_OK 905 || code != B_OK) 906 return; 907 908 link.Read<tuned_font_info>(info); 909 } 910 911 912 void 913 BFont::TruncateString(BString *inOut, uint32 mode, float width) const 914 { 915 // NOTE: Careful, we cannot directly use "inOut->String()" as result 916 // array, because the string length increases by 3 bytes in the worst case scenario. 917 const char *string = inOut->String(); 918 GetTruncatedStrings(&string, 1, mode, width, inOut); 919 } 920 921 922 void 923 BFont::GetTruncatedStrings(const char *stringArray[], int32 numStrings, 924 uint32 mode, float width, BString resultArray[]) const 925 { 926 if (stringArray && resultArray && numStrings > 0) { 927 // allocate storage, see BeBook for "+ 3" (make space for ellipsis) 928 char** truncatedStrings = new char*[numStrings]; 929 for (int32 i = 0; i < numStrings; i++) { 930 truncatedStrings[i] = new char[strlen(stringArray[i]) + 3]; 931 } 932 933 GetTruncatedStrings(stringArray, numStrings, mode, width, truncatedStrings); 934 935 // copy the strings into the BString array and free each one 936 for (int32 i = 0; i < numStrings; i++) { 937 resultArray[i].SetTo(truncatedStrings[i]); 938 delete[] truncatedStrings[i]; 939 } 940 delete[] truncatedStrings; 941 } 942 } 943 944 945 void 946 BFont::GetTruncatedStrings(const char *stringArray[], int32 numStrings, 947 uint32 mode, float width, char *resultArray[]) const 948 { 949 if (stringArray && numStrings > 0) { 950 // the width of the "…" glyph 951 float ellipsisWidth = StringWidth(B_UTF8_ELLIPSIS); 952 for (int32 i = 0; i < numStrings; i++) { 953 int32 length = strlen(stringArray[i]); 954 // count the individual glyphs 955 int32 numChars = UTF8CountChars(stringArray[i], length); 956 // get the escapement of each glyph in font units 957 float* escapementArray = new float[numChars]; 958 GetEscapements(stringArray[i], numChars, NULL, escapementArray); 959 960 truncate_string(stringArray[i], mode, width, resultArray[i], 961 escapementArray, fSize, ellipsisWidth, length, numChars); 962 963 delete[] escapementArray; 964 } 965 } 966 } 967 968 969 float 970 BFont::StringWidth(const char *string) const 971 { 972 if (!string) 973 return 0.0; 974 975 int32 length = strlen(string); 976 float width; 977 GetStringWidths(&string, &length, 1, &width); 978 979 return width; 980 } 981 982 983 float 984 BFont::StringWidth(const char *string, int32 length) const 985 { 986 if (!string || length < 1) 987 return 0.0; 988 989 float width; 990 GetStringWidths(&string, &length, 1, &width); 991 992 return width; 993 } 994 995 996 void 997 BFont::GetStringWidths(const char *stringArray[], const int32 lengthArray[], 998 int32 numStrings, float widthArray[]) const 999 { 1000 if (!stringArray || !lengthArray || numStrings < 1 || !widthArray) 1001 return; 1002 1003 BPrivate::AppServerLink link; 1004 link.StartMessage(AS_GET_STRING_WIDTHS); 1005 link.Attach<uint16>(fFamilyID); 1006 link.Attach<uint16>(fStyleID); 1007 link.Attach<float>(fSize); 1008 link.Attach<uint8>(fSpacing); 1009 link.Attach<int32>(numStrings); 1010 1011 // TODO: all strings into a single array??? 1012 // we do have a maximum message length, and it could be easily touched here... 1013 for (int32 i = 0; i < numStrings; i++) { 1014 link.Attach<int32>(lengthArray[i]); 1015 link.AttachString(stringArray[i]); 1016 } 1017 1018 int32 code; 1019 if (link.FlushWithReply(code) != B_OK 1020 || code != B_OK) 1021 return; 1022 1023 link.Read(widthArray, sizeof(float) * numStrings); 1024 } 1025 1026 1027 void 1028 BFont::GetEscapements(const char charArray[], int32 numChars, float escapementArray[]) const 1029 { 1030 GetEscapements(charArray, numChars, NULL, escapementArray); 1031 } 1032 1033 1034 void 1035 BFont::GetEscapements(const char charArray[], int32 numChars, escapement_delta *delta, 1036 float escapementArray[]) const 1037 { 1038 if (!charArray || numChars < 1 || !escapementArray) 1039 return; 1040 1041 BPrivate::AppServerLink link; 1042 link.StartMessage(AS_GET_ESCAPEMENTS_AS_FLOATS); 1043 link.Attach<uint16>(fFamilyID); 1044 link.Attach<uint16>(fStyleID); 1045 link.Attach<float>(fSize); 1046 link.Attach<float>(fRotation); 1047 link.Attach<uint32>(fFlags); 1048 1049 link.Attach<float>(delta ? delta->nonspace : 0.0); 1050 link.Attach<float>(delta ? delta->space : 0.0); 1051 1052 // TODO: Should we not worry about the port capacity here?!? 1053 link.Attach<int32>(numChars); 1054 1055 uint32 bytesInBuffer = UTF8CountBytes(charArray, numChars); 1056 link.Attach<int32>(bytesInBuffer); 1057 link.Attach(charArray, bytesInBuffer); 1058 1059 int32 code; 1060 if (link.FlushWithReply(code) != B_OK 1061 || code != B_OK) 1062 return; 1063 1064 link.Read(escapementArray, numChars * sizeof(float)); 1065 } 1066 1067 1068 void 1069 BFont::GetEscapements(const char charArray[], int32 numChars, escapement_delta *delta, 1070 BPoint escapementArray[]) const 1071 { 1072 GetEscapements(charArray, numChars, delta, escapementArray, NULL); 1073 } 1074 1075 1076 void 1077 BFont::GetEscapements(const char charArray[], int32 numChars, escapement_delta *delta, 1078 BPoint escapementArray[], BPoint offsetArray[]) const 1079 { 1080 if (!charArray || numChars < 1 || !escapementArray) 1081 return; 1082 1083 BPrivate::AppServerLink link; 1084 link.StartMessage(AS_GET_ESCAPEMENTS); 1085 link.Attach<uint16>(fFamilyID); 1086 link.Attach<uint16>(fStyleID); 1087 link.Attach<float>(fSize); 1088 link.Attach<float>(fRotation); 1089 link.Attach<uint32>(fFlags); 1090 1091 link.Attach<int32>(numChars); 1092 1093 // TODO: Support UTF8 characters 1094 if (offsetArray) { 1095 for (int32 i = 0; i < numChars; i++) { 1096 link.Attach<char>(charArray[i]); 1097 link.Attach<BPoint>(offsetArray[i]); 1098 } 1099 } else { 1100 BPoint dummypt(0, 0); 1101 1102 for (int32 i = 0; i < numChars; i++) { 1103 link.Attach<char>(charArray[i]); 1104 link.Attach<BPoint>(dummypt); 1105 } 1106 } 1107 1108 int32 code; 1109 if (link.FlushWithReply(code) != B_OK 1110 || code != B_OK) 1111 return; 1112 1113 link.Read(escapementArray, 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 1289 link.Attach<int32>(numChars); 1290 link.Attach(charArray, numChars); 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