1 /* 2 * Copyright 2001-2009, Haiku. 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 * Michael Lotz <mmlr@mlotz.ch> 9 * Stephan Aßmus <superstippi@gmx.de> 10 */ 11 12 13 #include "ServerFont.h" 14 15 #include "Angle.h" 16 #include "GlyphLayoutEngine.h" 17 #include "FontManager.h" 18 #include "truncate_string.h" 19 #include "utf8_functions.h" 20 21 #include FT_FREETYPE_H 22 #include FT_GLYPH_H 23 #include FT_OUTLINE_H 24 25 #include <Shape.h> 26 #include <String.h> 27 #include <UTF8.h> 28 29 #include <agg_bounding_rect.h> 30 31 #include <stdio.h> 32 #include <string.h> 33 34 // functions needed to convert a freetype vector graphics to a BShape 35 inline BPoint 36 VectorToPoint(const FT_Vector *vector) 37 { 38 BPoint result; 39 result.x = float(vector->x) / 64; 40 result.y = -float(vector->y) / 64; 41 return result; 42 } 43 44 45 int 46 MoveToFunc(const FT_Vector *to, void *user) 47 { 48 ((BShape *)user)->MoveTo(VectorToPoint(to)); 49 return 0; 50 } 51 52 53 int 54 LineToFunc(const FT_Vector *to, void *user) 55 { 56 ((BShape *)user)->LineTo(VectorToPoint(to)); 57 return 0; 58 } 59 60 61 int 62 ConicToFunc(const FT_Vector *control, const FT_Vector *to, void *user) 63 { 64 BPoint controls[3]; 65 66 controls[0] = VectorToPoint(control); 67 controls[1] = VectorToPoint(to); 68 controls[2] = controls[1]; 69 70 ((BShape *)user)->BezierTo(controls); 71 return 0; 72 } 73 74 75 int 76 CubicToFunc(const FT_Vector *control1, const FT_Vector *control2, const FT_Vector *to, void *user) 77 { 78 BPoint controls[3]; 79 80 controls[0] = VectorToPoint(control1); 81 controls[1] = VectorToPoint(control2); 82 controls[2] = VectorToPoint(to); 83 84 ((BShape *)user)->BezierTo(controls); 85 return 0; 86 } 87 88 89 inline bool 90 is_white_space(uint32 charCode) 91 { 92 switch (charCode) { 93 case 0x0009: /* tab */ 94 case 0x000b: /* vertical tab */ 95 case 0x000c: /* form feed */ 96 case 0x0020: /* space */ 97 case 0x00a0: /* non breaking space */ 98 case 0x000a: /* line feed */ 99 case 0x000d: /* carriage return */ 100 case 0x2028: /* line separator */ 101 case 0x2029: /* paragraph separator */ 102 return true; 103 } 104 105 return false; 106 } 107 108 109 // #pragma mark - 110 111 112 /*! 113 \brief Constructor 114 \param style Style object to which the ServerFont belongs 115 \param size Character size in points 116 \param rotation Rotation in degrees 117 \param shear Shear (slant) in degrees. 45 <= shear <= 135 118 \param flags Style flags as defined in <Font.h> 119 \param spacing String spacing flag as defined in <Font.h> 120 */ 121 ServerFont::ServerFont(FontStyle& style, float size, 122 float rotation, float shear, float falseBoldWidth, 123 uint16 flags, uint8 spacing) 124 : fStyle(&style), 125 fSize(size), 126 fRotation(rotation), 127 fShear(shear), 128 fFalseBoldWidth(falseBoldWidth), 129 fBounds(0, 0, 0, 0), 130 fFlags(flags), 131 fSpacing(spacing), 132 fDirection(style.Direction()), 133 fFace(style.Face()), 134 fEncoding(B_UNICODE_UTF8) 135 { 136 fStyle->Acquire(); 137 } 138 139 140 ServerFont::ServerFont() 141 : 142 fStyle(NULL) 143 { 144 *this = *gFontManager->DefaultPlainFont(); 145 } 146 147 148 /*! 149 \brief Copy Constructor 150 \param font ServerFont to copy 151 */ 152 ServerFont::ServerFont(const ServerFont &font) 153 : 154 fStyle(NULL) 155 { 156 *this = font; 157 } 158 159 160 /*! 161 \brief Removes itself as a dependency of its owning style. 162 */ 163 ServerFont::~ServerFont() 164 { 165 fStyle->Release(); 166 } 167 168 169 /*! 170 \brief Returns a copy of the specified font 171 \param The font to copy from. 172 \return A copy of the specified font 173 */ 174 ServerFont& 175 ServerFont::operator=(const ServerFont& font) 176 { 177 if (font.fStyle) { 178 fSize = font.fSize; 179 fRotation = font.fRotation; 180 fShear = font.fShear; 181 fFalseBoldWidth = font.fFalseBoldWidth; 182 fFlags = font.fFlags; 183 fSpacing = font.fSpacing; 184 fEncoding = font.fEncoding; 185 fBounds = font.fBounds; 186 187 SetStyle(font.fStyle); 188 } 189 190 return *this; 191 } 192 193 194 /*! 195 \brief Returns the number of strikes in the font 196 \return The number of strikes in the font 197 */ 198 int32 199 ServerFont::CountTuned() 200 { 201 return fStyle->TunedCount(); 202 } 203 204 205 /*! 206 \brief Returns the file format of the font. 207 \return Mostly B_TRUETYPE_WINDOWS :) 208 */ 209 font_file_format 210 ServerFont::FileFormat() 211 { 212 return fStyle->FileFormat(); 213 } 214 215 216 const char* 217 ServerFont::Style() const 218 { 219 return fStyle->Name(); 220 } 221 222 223 const char* 224 ServerFont::Family() const 225 { 226 return fStyle->Family()->Name(); 227 } 228 229 230 void 231 ServerFont::SetStyle(FontStyle* style) 232 { 233 if (style && style != fStyle) { 234 // detach from old style 235 if (fStyle) 236 fStyle->Release(); 237 238 // attach to new style 239 fStyle = style; 240 241 fStyle->Acquire(); 242 243 fFace = fStyle->Face(); 244 fDirection = fStyle->Direction(); 245 } 246 } 247 248 249 /*! 250 \brief Sets the ServerFont instance to whatever font is specified 251 This method will lock the font manager. 252 253 \param familyID ID number of the family to set 254 \param styleID ID number of the style to set 255 \return B_OK if successful, B_ERROR if not 256 */ 257 status_t 258 ServerFont::SetFamilyAndStyle(uint16 familyID, uint16 styleID) 259 { 260 FontStyle* style = NULL; 261 262 if (gFontManager->Lock()) { 263 style = gFontManager->GetStyle(familyID, styleID); 264 if (style != NULL) 265 style->Acquire(); 266 267 gFontManager->Unlock(); 268 } 269 270 if (!style) 271 return B_ERROR; 272 273 SetStyle(style); 274 style->Release(); 275 276 return B_OK; 277 } 278 279 280 /*! 281 \brief Sets the ServerFont instance to whatever font is specified 282 \param fontID the combination of family and style ID numbers 283 \return B_OK if successful, B_ERROR if not 284 */ 285 status_t 286 ServerFont::SetFamilyAndStyle(uint32 fontID) 287 { 288 uint16 style = fontID & 0xFFFF; 289 uint16 family = (fontID & 0xFFFF0000) >> 16; 290 291 return SetFamilyAndStyle(family, style); 292 } 293 294 295 status_t 296 ServerFont::SetFace(uint16 face) 297 { 298 // TODO: This needs further investigation. The face variable is actually 299 // flags, but some of them are not enforcable at the same time. Also don't 300 // confuse the Be API "face" with the Freetype face, which is just an 301 // index in case a single font file exports multiple font faces. The 302 // FontStyle class takes care of mapping the font style name to the Be 303 // API face flags in FontStyle::_TranslateStyleToFace(). 304 305 FontStyle* style = NULL; 306 uint16 familyID = FamilyID(); 307 if (gFontManager->Lock()) { 308 int32 count = gFontManager->CountStyles(familyID); 309 for (int32 i = 0; i < count; i++) { 310 style = gFontManager->GetStyleByIndex(familyID, i); 311 if (style == NULL) 312 break; 313 if (style->Face() == face) { 314 style->Acquire(); 315 break; 316 } else 317 style = NULL; 318 } 319 320 gFontManager->Unlock(); 321 } 322 323 if (!style) 324 return B_ERROR; 325 326 SetStyle(style); 327 style->Release(); 328 329 return B_OK; 330 } 331 332 333 /*! 334 \brief Gets the ID values for the ServerFont instance in one shot 335 \return the combination of family and style ID numbers 336 */ 337 uint32 338 ServerFont::GetFamilyAndStyle() const 339 { 340 return (FamilyID() << 16) | StyleID(); 341 } 342 343 344 FT_Face 345 ServerFont::GetTransformedFace(bool rotate, bool shear) const 346 { 347 fStyle->Lock(); 348 FT_Face face = fStyle->FreeTypeFace(); 349 if (!face) { 350 fStyle->Unlock(); 351 return NULL; 352 } 353 354 FT_Set_Char_Size(face, 0, int32(fSize * 64), 72, 72); 355 356 if ((rotate && fRotation != 0) || (shear && fShear != 90)) { 357 FT_Matrix rmatrix, smatrix; 358 359 Angle rotationAngle(fRotation); 360 rmatrix.xx = (FT_Fixed)( rotationAngle.Cosine() * 0x10000); 361 rmatrix.xy = (FT_Fixed)(-rotationAngle.Sine() * 0x10000); 362 rmatrix.yx = (FT_Fixed)( rotationAngle.Sine() * 0x10000); 363 rmatrix.yy = (FT_Fixed)( rotationAngle.Cosine() * 0x10000); 364 365 Angle shearAngle(fShear); 366 smatrix.xx = (FT_Fixed)(0x10000); 367 smatrix.xy = (FT_Fixed)(-shearAngle.Cosine() * 0x10000); 368 smatrix.yx = (FT_Fixed)(0); 369 smatrix.yy = (FT_Fixed)(0x10000); 370 371 // Multiply togheter and apply transform 372 FT_Matrix_Multiply(&rmatrix, &smatrix); 373 FT_Set_Transform(face, &smatrix, NULL); 374 } 375 376 // fStyle will be unlocked in PutTransformedFace() 377 return face; 378 } 379 380 381 void 382 ServerFont::PutTransformedFace(FT_Face face) const 383 { 384 // Reset transformation 385 FT_Set_Transform(face, NULL, NULL); 386 fStyle->Unlock(); 387 } 388 389 390 status_t 391 ServerFont::GetGlyphShapes(const char charArray[], int32 numChars, 392 BShape *shapeArray[]) const 393 { 394 if (!charArray || numChars <= 0 || !shapeArray) 395 return B_BAD_DATA; 396 397 FT_Face face = GetTransformedFace(true, true); 398 if (!face) 399 return B_ERROR; 400 401 FT_Outline_Funcs funcs; 402 funcs.move_to = MoveToFunc; 403 funcs.line_to = LineToFunc; 404 funcs.conic_to = ConicToFunc; 405 funcs.cubic_to = CubicToFunc; 406 funcs.shift = 0; 407 funcs.delta = 0; 408 409 const char *string = charArray; 410 for (int i = 0; i < numChars; i++) { 411 shapeArray[i] = new BShape(); 412 FT_Load_Char(face, UTF8ToCharCode(&string), FT_LOAD_NO_BITMAP); 413 FT_Outline outline = face->glyph->outline; 414 FT_Outline_Decompose(&outline, &funcs, shapeArray[i]); 415 shapeArray[i]->Close(); 416 } 417 418 PutTransformedFace(face); 419 return B_OK; 420 } 421 422 423 class HasGlyphsConsumer { 424 public: 425 HasGlyphsConsumer(bool* hasArray) 426 : fHasArray(hasArray) 427 { 428 } 429 void Start() {} 430 void Finish(double x, double y) {} 431 void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) 432 { 433 fHasArray[index] = false; 434 } 435 bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph, 436 FontCacheEntry* entry, double x, double y) 437 { 438 fHasArray[index] = glyph->glyph_index >= 0; 439 return true; 440 } 441 442 private: 443 bool* fHasArray; 444 }; 445 446 447 status_t 448 ServerFont::GetHasGlyphs(const char* string, int32 numBytes, 449 bool* hasArray) const 450 { 451 if (!string || numBytes <= 0 || !hasArray) 452 return B_BAD_DATA; 453 454 bool kerning = true; // TODO make this a property? 455 456 HasGlyphsConsumer consumer(hasArray); 457 if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes, 458 NULL, kerning, fSpacing)) 459 return B_OK; 460 461 return B_ERROR; 462 } 463 464 465 class EdgesConsumer { 466 public: 467 EdgesConsumer(edge_info* edges, float size) 468 : fEdges(edges) 469 , fSize(size) 470 { 471 } 472 void Start() {} 473 void Finish(double x, double y) {} 474 void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) 475 { 476 fEdges[index].left = 0.0; 477 fEdges[index].right = 0.0; 478 } 479 bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph, 480 FontCacheEntry* entry, double x, double y) 481 { 482 fEdges[index].left = glyph->inset_left / fSize; 483 fEdges[index].right = glyph->inset_right / fSize; 484 return true; 485 } 486 487 private: 488 edge_info* fEdges; 489 float fSize; 490 }; 491 492 493 status_t 494 ServerFont::GetEdges(const char* string, int32 numBytes, 495 edge_info* edges) const 496 { 497 if (!string || numBytes <= 0 || !edges) 498 return B_BAD_DATA; 499 500 bool kerning = true; // TODO make this a property? 501 502 EdgesConsumer consumer(edges, fSize); 503 if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes, 504 NULL, kerning, fSpacing)) 505 return B_OK; 506 507 return B_ERROR; 508 509 // FT_Face face = GetTransformedFace(false, false); 510 // if (!face) 511 // return B_ERROR; 512 // 513 // const char *string = charArray; 514 // for (int i = 0; i < numChars; i++) { 515 // FT_Load_Char(face, UTF8ToCharCode(&string), FT_LOAD_NO_BITMAP); 516 // edgeArray[i].left = float(face->glyph->metrics.horiBearingX) 517 // / 64 / fSize; 518 // edgeArray[i].right = float(face->glyph->metrics.horiBearingX 519 // + face->glyph->metrics.width - face->glyph->metrics.horiAdvance) 520 // / 64 / fSize; 521 // } 522 // 523 // PutTransformedFace(face); 524 // return B_OK; 525 } 526 527 528 class BPointEscapementConsumer { 529 public: 530 BPointEscapementConsumer(BPoint* escapements, BPoint* offsets, 531 int32 numChars, float size) 532 : 533 fEscapements(escapements), 534 fOffsets(offsets), 535 fNumChars(numChars), 536 fSize(size) 537 { 538 } 539 540 void Start() {} 541 void Finish(double x, double y) {} 542 void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) 543 { 544 _Set(index, 0, 0); 545 } 546 547 bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph, 548 FontCacheEntry* entry, double x, double y) 549 { 550 return _Set(index, glyph->advance_x, glyph->advance_y); 551 } 552 553 private: 554 inline bool _Set(int32 index, double x, double y) 555 { 556 if (index >= fNumChars) 557 return false; 558 559 fEscapements[index].x = x / fSize; 560 fEscapements[index].y = y / fSize; 561 if (fOffsets) { 562 // ToDo: According to the BeBook: "The offsetArray is applied by 563 // the dynamic spacing in order to improve the relative position 564 // of the character's width with relation to another character, 565 // without altering the width." So this will probably depend on 566 // the spacing mode. 567 fOffsets[index].x = 0; 568 fOffsets[index].y = 0; 569 } 570 return true; 571 } 572 573 BPoint* fEscapements; 574 BPoint* fOffsets; 575 int32 fNumChars; 576 float fSize; 577 }; 578 579 580 status_t 581 ServerFont::GetEscapements(const char* string, int32 numBytes, int32 numChars, 582 escapement_delta delta, BPoint escapementArray[], 583 BPoint offsetArray[]) const 584 { 585 if (!string || numBytes <= 0 || !escapementArray) 586 return B_BAD_DATA; 587 588 bool kerning = true; // TODO make this a property? 589 590 BPointEscapementConsumer consumer(escapementArray, offsetArray, numChars, 591 fSize); 592 if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes, 593 &delta, kerning, fSpacing)) 594 return B_OK; 595 596 return B_ERROR; 597 } 598 599 600 class WidthEscapementConsumer { 601 public: 602 WidthEscapementConsumer(float* widths, int32 numChars, float size) 603 : 604 fWidths(widths), 605 fNumChars(numChars), 606 fSize(size) 607 { 608 } 609 610 void Start() {} 611 void Finish(double x, double y) {} 612 void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) 613 { 614 fWidths[index] = 0.0; 615 } 616 617 bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph, 618 FontCacheEntry* entry, double x, double y) 619 { 620 if (index >= fNumChars) 621 return false; 622 623 fWidths[index] = glyph->advance_x / fSize; 624 return true; 625 } 626 627 private: 628 float* fWidths; 629 int32 fNumChars; 630 float fSize; 631 }; 632 633 634 635 status_t 636 ServerFont::GetEscapements(const char* string, int32 numBytes, int32 numChars, 637 escapement_delta delta, float widthArray[]) const 638 { 639 if (!string || numBytes <= 0 || !widthArray) 640 return B_BAD_DATA; 641 642 bool kerning = true; // TODO make this a property? 643 644 WidthEscapementConsumer consumer(widthArray, numChars, fSize); 645 if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes, 646 &delta, kerning, fSpacing)) 647 return B_OK; 648 return B_ERROR; 649 } 650 651 652 class BoundingBoxConsumer { 653 public: 654 BoundingBoxConsumer(Transformable& transform, BRect* rectArray, 655 bool asString) 656 : rectArray(rectArray) 657 , stringBoundingBox(LONG_MAX, LONG_MAX, LONG_MIN, LONG_MIN) 658 , fAsString(asString) 659 , fCurves(fPathAdaptor) 660 , fContour(fCurves) 661 , fTransformedOutline(fCurves, transform) 662 , fTransformedContourOutline(fContour, transform) 663 , fTransform(transform) 664 { 665 } 666 667 void Start() {} 668 void Finish(double x, double y) {} 669 void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) {} 670 bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph, 671 FontCacheEntry* entry, double x, double y) 672 { 673 if (glyph->data_type != glyph_data_outline) { 674 const agg::rect_i& r = glyph->bounds; 675 if (fAsString) { 676 if (rectArray) { 677 rectArray[index].left = r.x1 + x; 678 rectArray[index].top = r.y1 + y; 679 rectArray[index].right = r.x2 + x + 1; 680 rectArray[index].bottom = r.y2 + y + 1; 681 } else { 682 stringBoundingBox = stringBoundingBox 683 | BRect(r.x1 + x, r.y1 + y, 684 r.x2 + x + 1, r.y2 + y + 1); 685 } 686 } else { 687 rectArray[index].left = r.x1; 688 rectArray[index].top = r.y1; 689 rectArray[index].right = r.x2 + 1; 690 rectArray[index].bottom = r.y2 + 1; 691 } 692 } else { 693 if (fAsString) { 694 entry->InitAdaptors(glyph, x, y, 695 fMonoAdaptor, fGray8Adaptor, fPathAdaptor); 696 } else { 697 entry->InitAdaptors(glyph, 0, 0, 698 fMonoAdaptor, fGray8Adaptor, fPathAdaptor); 699 } 700 double left = 0.0; 701 double top = 0.0; 702 double right = -1.0; 703 double bottom = -1.0; 704 uint32 pathID[1]; 705 pathID[0] = 0; 706 // TODO: use fContour if falseboldwidth is > 0 707 agg::bounding_rect(fTransformedOutline, pathID, 0, 1, 708 &left, &top, &right, &bottom); 709 710 if (rectArray) { 711 rectArray[index] = BRect(left, top, right, bottom); 712 } else { 713 stringBoundingBox = stringBoundingBox 714 | BRect(left, top, right, bottom); 715 } 716 } 717 return true; 718 } 719 720 BRect* rectArray; 721 BRect stringBoundingBox; 722 723 private: 724 bool fAsString; 725 FontCacheEntry::GlyphPathAdapter fPathAdaptor; 726 FontCacheEntry::GlyphGray8Adapter fGray8Adaptor; 727 FontCacheEntry::GlyphMonoAdapter fMonoAdaptor; 728 729 FontCacheEntry::CurveConverter fCurves; 730 FontCacheEntry::ContourConverter fContour; 731 732 FontCacheEntry::TransformedOutline fTransformedOutline; 733 FontCacheEntry::TransformedContourOutline fTransformedContourOutline; 734 735 Transformable& fTransform; 736 }; 737 738 739 status_t 740 ServerFont::GetBoundingBoxes(const char* string, int32 numBytes, 741 BRect rectArray[], bool stringEscapement, font_metric_mode mode, 742 escapement_delta delta, bool asString) 743 { 744 // TODO: The font_metric_mode is not used 745 if (!string || numBytes <= 0 || !rectArray) 746 return B_BAD_DATA; 747 748 bool kerning = true; // TODO make this a property? 749 750 Transformable transform(EmbeddedTransformation()); 751 752 BoundingBoxConsumer consumer(transform, rectArray, asString); 753 if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes, 754 stringEscapement ? &delta : NULL, kerning, fSpacing)) 755 return B_OK; 756 return B_ERROR; 757 } 758 759 760 status_t 761 ServerFont::GetBoundingBoxesForStrings(char *charArray[], int32 lengthArray[], 762 int32 numStrings, BRect rectArray[], font_metric_mode mode, 763 escapement_delta deltaArray[]) 764 { 765 // TODO: The font_metric_mode is never used 766 if (!charArray || !lengthArray|| numStrings <= 0 || !rectArray || !deltaArray) 767 return B_BAD_DATA; 768 769 bool kerning = true; // TODO make this a property? 770 771 Transformable transform(EmbeddedTransformation()); 772 773 for (int32 i = 0; i < numStrings; i++) { 774 int32 numBytes = lengthArray[i]; 775 const char* string = charArray[i]; 776 escapement_delta delta = deltaArray[i]; 777 778 BoundingBoxConsumer consumer(transform, NULL, true); 779 if (!GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes, 780 &delta, kerning, fSpacing)) 781 return B_ERROR; 782 783 rectArray[i] = consumer.stringBoundingBox; 784 } 785 786 return B_OK; 787 } 788 789 790 class StringWidthConsumer { 791 public: 792 StringWidthConsumer() : width(0.0) {} 793 void Start() {} 794 void Finish(double x, double y) { width = x; } 795 void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) {} 796 bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph, 797 FontCacheEntry* entry, double x, double y) 798 { return true; } 799 800 float width; 801 }; 802 803 804 float 805 ServerFont::StringWidth(const char *string, int32 numBytes, 806 const escapement_delta* deltaArray) const 807 { 808 if (!string || numBytes <= 0) 809 return 0.0; 810 811 bool kerning = true; // TODO make this a property? 812 813 StringWidthConsumer consumer; 814 if (!GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes, 815 deltaArray, kerning, fSpacing)) 816 return 0.0; 817 818 return consumer.width; 819 } 820 821 822 /*! 823 \brief Returns a BRect which encloses the entire font 824 \return A BRect which encloses the entire font 825 */ 826 BRect 827 ServerFont::BoundingBox() 828 { 829 // TODO: fBounds is nowhere calculated! 830 return fBounds; 831 } 832 833 834 /*! 835 \brief Obtains the height values for characters in the font in its current state 836 \param fh pointer to a font_height object to receive the values for the font 837 */ 838 void 839 ServerFont::GetHeight(font_height& height) const 840 { 841 fStyle->GetHeight(fSize, height); 842 } 843 844 845 void 846 ServerFont::TruncateString(BString* inOut, uint32 mode, float width) const 847 { 848 if (!inOut) 849 return; 850 851 // the width of the "…" glyph 852 float ellipsisWidth = StringWidth(B_UTF8_ELLIPSIS, strlen(B_UTF8_ELLIPSIS)); 853 const char* string = inOut->String(); 854 int32 length = inOut->Length(); 855 856 // temporary array to hold result 857 char* result = new char[length + 3]; 858 859 // count the individual glyphs 860 int32 numChars = UTF8CountChars(string, -1); 861 862 // get the escapement of each glyph in font units 863 float* escapementArray = new float[numChars]; 864 static escapement_delta delta = (escapement_delta){ 0.0, 0.0 }; 865 if (GetEscapements(string, length, numChars, delta, escapementArray) 866 == B_OK) { 867 truncate_string(string, mode, width, result, escapementArray, fSize, 868 ellipsisWidth, length, numChars); 869 870 inOut->SetTo(result); 871 } 872 873 delete[] escapementArray; 874 delete[] result; 875 } 876 877 878 Transformable 879 ServerFont::EmbeddedTransformation() const 880 { 881 // TODO: cache this? 882 Transformable transform; 883 884 transform.ShearBy(B_ORIGIN, (90.0 - fShear) * M_PI / 180.0, 0.0); 885 transform.RotateBy(B_ORIGIN, -fRotation * M_PI / 180.0); 886 887 return transform; 888 } 889 890