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