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