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 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 void Start() {} 442 void Finish(double x, double y) {} 443 void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) 444 { 445 fHasArray[index] = false; 446 } 447 bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph, 448 FontCacheEntry* entry, double x, double y) 449 { 450 fHasArray[index] = glyph->glyph_index != 0; 451 return true; 452 } 453 454 private: 455 bool* fHasArray; 456 }; 457 458 459 status_t 460 ServerFont::GetHasGlyphs(const char* string, int32 numBytes, 461 bool* hasArray) const 462 { 463 if (!string || numBytes <= 0 || !hasArray) 464 return B_BAD_DATA; 465 466 bool kerning = true; // TODO make this a property? 467 468 HasGlyphsConsumer consumer(hasArray); 469 if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes, 470 NULL, kerning, 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 void Start() {} 485 void Finish(double x, double y) {} 486 void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) 487 { 488 fEdges[index].left = 0.0; 489 fEdges[index].right = 0.0; 490 } 491 bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph, 492 FontCacheEntry* entry, double x, double y) 493 { 494 fEdges[index].left = glyph->inset_left / fSize; 495 fEdges[index].right = glyph->inset_right / fSize; 496 return true; 497 } 498 499 private: 500 edge_info* fEdges; 501 float fSize; 502 }; 503 504 505 status_t 506 ServerFont::GetEdges(const char* string, int32 numBytes, 507 edge_info* edges) const 508 { 509 if (!string || numBytes <= 0 || !edges) 510 return B_BAD_DATA; 511 512 bool kerning = true; // TODO make this a property? 513 514 EdgesConsumer consumer(edges, fSize); 515 if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes, 516 NULL, kerning, fSpacing)) 517 return B_OK; 518 519 return B_ERROR; 520 521 // FT_Face face = GetTransformedFace(false, false); 522 // if (!face) 523 // return B_ERROR; 524 // 525 // const char *string = charArray; 526 // for (int i = 0; i < numChars; i++) { 527 // FT_Load_Char(face, UTF8ToCharCode(&string), FT_LOAD_NO_BITMAP); 528 // edgeArray[i].left = float(face->glyph->metrics.horiBearingX) 529 // / 64 / fSize; 530 // edgeArray[i].right = float(face->glyph->metrics.horiBearingX 531 // + face->glyph->metrics.width - face->glyph->metrics.horiAdvance) 532 // / 64 / fSize; 533 // } 534 // 535 // PutTransformedFace(face); 536 // return B_OK; 537 } 538 539 540 class BPointEscapementConsumer { 541 public: 542 BPointEscapementConsumer(BPoint* escapements, BPoint* offsets, 543 int32 numChars, float size) 544 : 545 fEscapements(escapements), 546 fOffsets(offsets), 547 fNumChars(numChars), 548 fSize(size) 549 { 550 } 551 552 void Start() {} 553 void Finish(double x, double y) {} 554 void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) 555 { 556 _Set(index, 0, 0); 557 } 558 559 bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph, 560 FontCacheEntry* entry, double x, double y) 561 { 562 return _Set(index, glyph->advance_x, glyph->advance_y); 563 } 564 565 private: 566 inline bool _Set(int32 index, double x, double y) 567 { 568 if (index >= fNumChars) 569 return false; 570 571 fEscapements[index].x = x / fSize; 572 fEscapements[index].y = y / fSize; 573 if (fOffsets) { 574 // ToDo: According to the BeBook: "The offsetArray is applied by 575 // the dynamic spacing in order to improve the relative position 576 // of the character's width with relation to another character, 577 // without altering the width." So this will probably depend on 578 // the spacing mode. 579 fOffsets[index].x = 0; 580 fOffsets[index].y = 0; 581 } 582 return true; 583 } 584 585 BPoint* fEscapements; 586 BPoint* fOffsets; 587 int32 fNumChars; 588 float fSize; 589 }; 590 591 592 status_t 593 ServerFont::GetEscapements(const char* string, int32 numBytes, int32 numChars, 594 escapement_delta delta, BPoint escapementArray[], 595 BPoint offsetArray[]) const 596 { 597 if (!string || numBytes <= 0 || !escapementArray) 598 return B_BAD_DATA; 599 600 bool kerning = true; // TODO make this a property? 601 602 BPointEscapementConsumer consumer(escapementArray, offsetArray, numChars, 603 fSize); 604 if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes, 605 &delta, kerning, fSpacing)) 606 return B_OK; 607 608 return B_ERROR; 609 } 610 611 612 class WidthEscapementConsumer { 613 public: 614 WidthEscapementConsumer(float* widths, int32 numChars, float size) 615 : 616 fWidths(widths), 617 fNumChars(numChars), 618 fSize(size) 619 { 620 } 621 622 void Start() {} 623 void Finish(double x, double y) {} 624 void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) 625 { 626 fWidths[index] = 0.0; 627 } 628 629 bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph, 630 FontCacheEntry* entry, double x, double y) 631 { 632 if (index >= fNumChars) 633 return false; 634 635 fWidths[index] = glyph->advance_x / fSize; 636 return true; 637 } 638 639 private: 640 float* fWidths; 641 int32 fNumChars; 642 float fSize; 643 }; 644 645 646 647 status_t 648 ServerFont::GetEscapements(const char* string, int32 numBytes, int32 numChars, 649 escapement_delta delta, float widthArray[]) const 650 { 651 if (!string || numBytes <= 0 || !widthArray) 652 return B_BAD_DATA; 653 654 bool kerning = true; // TODO make this a property? 655 656 WidthEscapementConsumer consumer(widthArray, numChars, fSize); 657 if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes, 658 &delta, kerning, fSpacing)) 659 return B_OK; 660 return B_ERROR; 661 } 662 663 664 class BoundingBoxConsumer { 665 public: 666 BoundingBoxConsumer(Transformable& transform, BRect* rectArray, 667 bool asString) 668 : rectArray(rectArray) 669 , stringBoundingBox(LONG_MAX, LONG_MAX, LONG_MIN, LONG_MIN) 670 , fAsString(asString) 671 , fCurves(fPathAdaptor) 672 , fContour(fCurves) 673 , fTransformedOutline(fCurves, transform) 674 , fTransformedContourOutline(fContour, transform) 675 , fTransform(transform) 676 { 677 } 678 679 void Start() {} 680 void Finish(double x, double y) {} 681 void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) {} 682 bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph, 683 FontCacheEntry* entry, double x, double y) 684 { 685 if (glyph->data_type != glyph_data_outline) { 686 const agg::rect_i& r = glyph->bounds; 687 if (fAsString) { 688 if (rectArray) { 689 rectArray[index].left = r.x1 + x; 690 rectArray[index].top = r.y1 + y; 691 rectArray[index].right = r.x2 + x + 1; 692 rectArray[index].bottom = r.y2 + y + 1; 693 } else { 694 stringBoundingBox = stringBoundingBox 695 | BRect(r.x1 + x, r.y1 + y, 696 r.x2 + x + 1, r.y2 + y + 1); 697 } 698 } else { 699 rectArray[index].left = r.x1; 700 rectArray[index].top = r.y1; 701 rectArray[index].right = r.x2 + 1; 702 rectArray[index].bottom = r.y2 + 1; 703 } 704 } else { 705 if (fAsString) { 706 entry->InitAdaptors(glyph, x, y, 707 fMonoAdaptor, fGray8Adaptor, fPathAdaptor); 708 } else { 709 entry->InitAdaptors(glyph, 0, 0, 710 fMonoAdaptor, fGray8Adaptor, fPathAdaptor); 711 } 712 double left = 0.0; 713 double top = 0.0; 714 double right = -1.0; 715 double bottom = -1.0; 716 uint32 pathID[1]; 717 pathID[0] = 0; 718 // TODO: use fContour if falseboldwidth is > 0 719 agg::bounding_rect(fTransformedOutline, pathID, 0, 1, 720 &left, &top, &right, &bottom); 721 722 if (rectArray) { 723 rectArray[index] = BRect(left, top, right, bottom); 724 } else { 725 stringBoundingBox = stringBoundingBox 726 | BRect(left, top, right, bottom); 727 } 728 } 729 return true; 730 } 731 732 BRect* rectArray; 733 BRect stringBoundingBox; 734 735 private: 736 bool fAsString; 737 FontCacheEntry::GlyphPathAdapter fPathAdaptor; 738 FontCacheEntry::GlyphGray8Adapter fGray8Adaptor; 739 FontCacheEntry::GlyphMonoAdapter fMonoAdaptor; 740 741 FontCacheEntry::CurveConverter fCurves; 742 FontCacheEntry::ContourConverter fContour; 743 744 FontCacheEntry::TransformedOutline fTransformedOutline; 745 FontCacheEntry::TransformedContourOutline fTransformedContourOutline; 746 747 Transformable& fTransform; 748 }; 749 750 751 status_t 752 ServerFont::GetBoundingBoxes(const char* string, int32 numBytes, 753 BRect rectArray[], bool stringEscapement, font_metric_mode mode, 754 escapement_delta delta, bool asString) 755 { 756 // TODO: The font_metric_mode is not used 757 if (!string || numBytes <= 0 || !rectArray) 758 return B_BAD_DATA; 759 760 bool kerning = true; // TODO make this a property? 761 762 Transformable transform(EmbeddedTransformation()); 763 764 BoundingBoxConsumer consumer(transform, rectArray, asString); 765 if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes, 766 stringEscapement ? &delta : NULL, kerning, fSpacing)) 767 return B_OK; 768 return B_ERROR; 769 } 770 771 772 status_t 773 ServerFont::GetBoundingBoxesForStrings(char *charArray[], int32 lengthArray[], 774 int32 numStrings, BRect rectArray[], font_metric_mode mode, 775 escapement_delta deltaArray[]) 776 { 777 // TODO: The font_metric_mode is never used 778 if (!charArray || !lengthArray|| numStrings <= 0 || !rectArray || !deltaArray) 779 return B_BAD_DATA; 780 781 bool kerning = true; // TODO make this a property? 782 783 Transformable transform(EmbeddedTransformation()); 784 785 for (int32 i = 0; i < numStrings; i++) { 786 int32 numBytes = lengthArray[i]; 787 const char* string = charArray[i]; 788 escapement_delta delta = deltaArray[i]; 789 790 BoundingBoxConsumer consumer(transform, NULL, true); 791 if (!GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes, 792 &delta, kerning, fSpacing)) 793 return B_ERROR; 794 795 rectArray[i] = consumer.stringBoundingBox; 796 } 797 798 return B_OK; 799 } 800 801 802 class StringWidthConsumer { 803 public: 804 StringWidthConsumer() : width(0.0) {} 805 void Start() {} 806 void Finish(double x, double y) { width = x; } 807 void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) {} 808 bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph, 809 FontCacheEntry* entry, double x, double y) 810 { return true; } 811 812 float width; 813 }; 814 815 816 float 817 ServerFont::StringWidth(const char *string, int32 numBytes, 818 const escapement_delta* deltaArray) const 819 { 820 if (!string || numBytes <= 0) 821 return 0.0; 822 823 bool kerning = true; // TODO make this a property? 824 825 StringWidthConsumer consumer; 826 if (!GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes, 827 deltaArray, kerning, fSpacing)) 828 return 0.0; 829 830 return consumer.width; 831 } 832 833 834 /*! 835 \brief Returns a BRect which encloses the entire font 836 \return A BRect which encloses the entire font 837 */ 838 BRect 839 ServerFont::BoundingBox() 840 { 841 // TODO: fBounds is nowhere calculated! 842 return fBounds; 843 } 844 845 846 /*! 847 \brief Obtains the height values for characters in the font in its current state 848 \param fh pointer to a font_height object to receive the values for the font 849 */ 850 void 851 ServerFont::GetHeight(font_height& height) const 852 { 853 fStyle->GetHeight(fSize, height); 854 } 855 856 857 void 858 ServerFont::TruncateString(BString* inOut, uint32 mode, float width) const 859 { 860 if (!inOut) 861 return; 862 863 // the width of the "…" glyph 864 float ellipsisWidth = StringWidth(B_UTF8_ELLIPSIS, strlen(B_UTF8_ELLIPSIS)); 865 866 // count the individual glyphs 867 int32 numChars = inOut->CountChars(); 868 869 // get the escapement of each glyph in font units 870 float* escapementArray = new float[numChars]; 871 static escapement_delta delta = (escapement_delta){ 0.0, 0.0 }; 872 if (GetEscapements(inOut->String(), inOut->Length(), numChars, delta, 873 escapementArray) == B_OK) { 874 truncate_string(*inOut, mode, width, escapementArray, fSize, 875 ellipsisWidth, numChars); 876 } 877 878 delete[] escapementArray; 879 } 880 881 882 Transformable 883 ServerFont::EmbeddedTransformation() const 884 { 885 // TODO: cache this? 886 Transformable transform; 887 888 transform.ShearBy(B_ORIGIN, (90.0 - fShear) * M_PI / 180.0, 0.0); 889 transform.RotateBy(B_ORIGIN, -fRotation * M_PI / 180.0); 890 891 return transform; 892 } 893 894