1 /* 2 * Copyright 2001-2016, 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 #ifdef FONTCONFIG_ENABLED 26 27 #include <fontconfig.h> 28 #include <fcfreetype.h> 29 30 #endif // FONTCONFIG_ENABLED 31 32 #include <Shape.h> 33 #include <String.h> 34 #include <UnicodeBlockObjects.h> 35 #include <UTF8.h> 36 37 #include <agg_bounding_rect.h> 38 39 #include <stdio.h> 40 #include <string.h> 41 42 43 // functions needed to convert a freetype vector graphics to a BShape 44 inline BPoint 45 VectorToPoint(const FT_Vector *vector) 46 { 47 BPoint result; 48 result.x = float(vector->x) / 64; 49 result.y = -float(vector->y) / 64; 50 return result; 51 } 52 53 54 int 55 MoveToFunc(const FT_Vector *to, void *user) 56 { 57 ((BShape *)user)->MoveTo(VectorToPoint(to)); 58 return 0; 59 } 60 61 62 int 63 LineToFunc(const FT_Vector *to, void *user) 64 { 65 ((BShape *)user)->LineTo(VectorToPoint(to)); 66 return 0; 67 } 68 69 70 int 71 ConicToFunc(const FT_Vector *control, const FT_Vector *to, void *user) 72 { 73 BPoint controls[3]; 74 75 controls[0] = VectorToPoint(control); 76 controls[1] = VectorToPoint(to); 77 controls[2] = controls[1]; 78 79 ((BShape *)user)->BezierTo(controls); 80 return 0; 81 } 82 83 84 int 85 CubicToFunc(const FT_Vector *control1, const FT_Vector *control2, const FT_Vector *to, void *user) 86 { 87 BPoint controls[3]; 88 89 controls[0] = VectorToPoint(control1); 90 controls[1] = VectorToPoint(control2); 91 controls[2] = VectorToPoint(to); 92 93 ((BShape *)user)->BezierTo(controls); 94 return 0; 95 } 96 97 98 inline bool 99 is_white_space(uint32 charCode) 100 { 101 switch (charCode) { 102 case 0x0009: /* tab */ 103 case 0x000b: /* vertical tab */ 104 case 0x000c: /* form feed */ 105 case 0x0020: /* space */ 106 case 0x00a0: /* non breaking space */ 107 case 0x000a: /* line feed */ 108 case 0x000d: /* carriage return */ 109 case 0x2028: /* line separator */ 110 case 0x2029: /* paragraph separator */ 111 return true; 112 } 113 114 return false; 115 } 116 117 118 // #pragma mark - 119 120 121 /*! 122 \brief Constructor 123 \param style Style object to which the ServerFont belongs 124 \param size Character size in points 125 \param rotation Rotation in degrees 126 \param shear Shear (slant) in degrees. 45 <= shear <= 135 127 \param flags Style flags as defined in <Font.h> 128 \param spacing String spacing flag as defined in <Font.h> 129 */ 130 ServerFont::ServerFont(FontStyle& style, float size, float rotation, 131 float shear, float falseBoldWidth, uint16 flags, uint8 spacing) 132 : 133 fStyle(&style), 134 fSize(size), 135 fRotation(rotation), 136 fShear(shear), 137 fFalseBoldWidth(falseBoldWidth), 138 fBounds(0, 0, 0, 0), 139 fFlags(flags), 140 fSpacing(spacing), 141 fDirection(style.Direction()), 142 fFace(style.Face()), 143 fEncoding(B_UNICODE_UTF8) 144 { 145 fStyle->Acquire(); 146 } 147 148 149 ServerFont::ServerFont() 150 : 151 fStyle(NULL) 152 { 153 *this = *gFontManager->DefaultPlainFont(); 154 } 155 156 157 /*! 158 \brief Copy Constructor 159 \param font ServerFont to copy 160 */ 161 ServerFont::ServerFont(const ServerFont &font) 162 : 163 fStyle(NULL) 164 { 165 *this = font; 166 } 167 168 169 /*! 170 \brief Removes itself as a dependency of its owning style. 171 */ 172 ServerFont::~ServerFont() 173 { 174 fStyle->Release(); 175 } 176 177 178 /*! 179 \brief Returns a copy of the specified font 180 \param The font to copy from. 181 \return A copy of the specified font 182 */ 183 ServerFont& 184 ServerFont::operator=(const ServerFont& font) 185 { 186 fSize = font.fSize; 187 fRotation = font.fRotation; 188 fShear = font.fShear; 189 fFalseBoldWidth = font.fFalseBoldWidth; 190 fFlags = font.fFlags; 191 fSpacing = font.fSpacing; 192 fEncoding = font.fEncoding; 193 fBounds = font.fBounds; 194 195 SetStyle(font.fStyle); 196 197 return *this; 198 } 199 200 201 bool 202 ServerFont::operator==(const ServerFont& other) const 203 { 204 if (GetFamilyAndStyle() != other.GetFamilyAndStyle()) 205 return false; 206 207 return fSize == other.fSize && fRotation == other.fRotation 208 && fShear == other.fShear && fFalseBoldWidth == other.fFalseBoldWidth 209 && fFlags == other.fFlags && fSpacing == other.fSpacing 210 && fEncoding == other.fEncoding && fBounds == other.fBounds 211 && fDirection == other.fDirection && fFace == other.fFace; 212 } 213 214 215 /*! 216 \brief Returns the number of strikes in the font 217 \return The number of strikes in the font 218 */ 219 int32 220 ServerFont::CountTuned() 221 { 222 return fStyle->TunedCount(); 223 } 224 225 226 /*! 227 \brief Returns the file format of the font. 228 \return Mostly B_TRUETYPE_WINDOWS :) 229 */ 230 font_file_format 231 ServerFont::FileFormat() 232 { 233 return fStyle->FileFormat(); 234 } 235 236 237 const char* 238 ServerFont::Style() const 239 { 240 return fStyle->Name(); 241 } 242 243 244 const char* 245 ServerFont::Family() const 246 { 247 return fStyle->Family()->Name(); 248 } 249 250 251 void 252 ServerFont::SetStyle(FontStyle* style) 253 { 254 if (style && style != fStyle) { 255 // detach from old style 256 if (fStyle != NULL) 257 fStyle->Release(); 258 259 // attach to new style 260 fStyle = style; 261 262 fStyle->Acquire(); 263 264 fFace = fStyle->Face(); 265 fDirection = fStyle->Direction(); 266 } 267 } 268 269 270 /*! 271 \brief Sets the ServerFont instance to whatever font is specified 272 This method will lock the font manager. 273 274 \param familyID ID number of the family to set 275 \param styleID ID number of the style to set 276 \return B_OK if successful, B_ERROR if not 277 */ 278 status_t 279 ServerFont::SetFamilyAndStyle(uint16 familyID, uint16 styleID) 280 { 281 FontStyle* style = NULL; 282 283 if (gFontManager->Lock()) { 284 style = gFontManager->GetStyle(familyID, styleID); 285 if (style != NULL) 286 style->Acquire(); 287 288 gFontManager->Unlock(); 289 } 290 291 if (style == NULL) 292 return B_ERROR; 293 294 SetStyle(style); 295 style->Release(); 296 297 return B_OK; 298 } 299 300 301 /*! 302 \brief Sets the ServerFont instance to whatever font is specified 303 \param fontID the combination of family and style ID numbers 304 \return B_OK if successful, B_ERROR if not 305 */ 306 status_t 307 ServerFont::SetFamilyAndStyle(uint32 fontID) 308 { 309 uint16 style = fontID & 0xFFFF; 310 uint16 family = (fontID & 0xFFFF0000) >> 16; 311 312 return SetFamilyAndStyle(family, style); 313 } 314 315 316 status_t 317 ServerFont::SetFace(uint16 face) 318 { 319 // TODO: This needs further investigation. The face variable is actually 320 // flags, but some of them are not enforcable at the same time. Also don't 321 // confuse the Be API "face" with the Freetype face, which is just an 322 // index in case a single font file exports multiple font faces. The 323 // FontStyle class takes care of mapping the font style name to the Be 324 // API face flags in FontStyle::_TranslateStyleToFace(). 325 326 FontStyle* style = NULL; 327 uint16 familyID = FamilyID(); 328 if (gFontManager->Lock()) { 329 int32 count = gFontManager->CountStyles(familyID); 330 for (int32 i = 0; i < count; i++) { 331 style = gFontManager->GetStyleByIndex(familyID, i); 332 if (style == NULL) 333 break; 334 if (style->Face() == face) { 335 style->Acquire(); 336 break; 337 } else 338 style = NULL; 339 } 340 341 gFontManager->Unlock(); 342 } 343 344 if (!style) 345 return B_ERROR; 346 347 SetStyle(style); 348 style->Release(); 349 350 return B_OK; 351 } 352 353 354 /*! 355 \brief Gets the ID values for the ServerFont instance in one shot 356 \return the combination of family and style ID numbers 357 */ 358 uint32 359 ServerFont::GetFamilyAndStyle() const 360 { 361 return (FamilyID() << 16) | StyleID(); 362 } 363 364 365 FT_Face 366 ServerFont::GetTransformedFace(bool rotate, bool shear) const 367 { 368 fStyle->Lock(); 369 FT_Face face = fStyle->FreeTypeFace(); 370 if (!face) { 371 fStyle->Unlock(); 372 return NULL; 373 } 374 375 FT_Set_Char_Size(face, 0, int32(fSize * 64), 72, 72); 376 377 if ((rotate && fRotation != 0) || (shear && fShear != 90)) { 378 FT_Matrix rmatrix, smatrix; 379 380 Angle rotationAngle(fRotation); 381 rmatrix.xx = (FT_Fixed)( rotationAngle.Cosine() * 0x10000); 382 rmatrix.xy = (FT_Fixed)(-rotationAngle.Sine() * 0x10000); 383 rmatrix.yx = (FT_Fixed)( rotationAngle.Sine() * 0x10000); 384 rmatrix.yy = (FT_Fixed)( rotationAngle.Cosine() * 0x10000); 385 386 Angle shearAngle(fShear); 387 smatrix.xx = (FT_Fixed)(0x10000); 388 smatrix.xy = (FT_Fixed)(-shearAngle.Cosine() * 0x10000); 389 smatrix.yx = (FT_Fixed)(0); 390 smatrix.yy = (FT_Fixed)(0x10000); 391 392 // Multiply togheter and apply transform 393 FT_Matrix_Multiply(&rmatrix, &smatrix); 394 FT_Set_Transform(face, &smatrix, NULL); 395 } 396 397 // fStyle will be unlocked in PutTransformedFace() 398 return face; 399 } 400 401 402 void 403 ServerFont::PutTransformedFace(FT_Face face) const 404 { 405 // Reset transformation 406 FT_Set_Transform(face, NULL, NULL); 407 fStyle->Unlock(); 408 } 409 410 411 status_t 412 ServerFont::GetGlyphShapes(const char charArray[], int32 numChars, 413 BShape* shapeArray[]) const 414 { 415 if (!charArray || numChars <= 0 || !shapeArray) 416 return B_BAD_DATA; 417 418 FT_Face face = GetTransformedFace(true, true); 419 if (!face) 420 return B_ERROR; 421 422 FT_Outline_Funcs funcs; 423 funcs.move_to = MoveToFunc; 424 funcs.line_to = LineToFunc; 425 funcs.conic_to = ConicToFunc; 426 funcs.cubic_to = CubicToFunc; 427 funcs.shift = 0; 428 funcs.delta = 0; 429 430 const char* string = charArray; 431 for (int i = 0; i < numChars; i++) { 432 shapeArray[i] = new (std::nothrow) BShape(); 433 if (shapeArray[i] == NULL) { 434 PutTransformedFace(face); 435 return B_NO_MEMORY; 436 } 437 FT_Load_Char(face, UTF8ToCharCode(&string), FT_LOAD_NO_BITMAP); 438 FT_Outline outline = face->glyph->outline; 439 FT_Outline_Decompose(&outline, &funcs, shapeArray[i]); 440 shapeArray[i]->Close(); 441 } 442 443 PutTransformedFace(face); 444 return B_OK; 445 } 446 447 448 #ifdef FONTCONFIG_ENABLED 449 450 /*! 451 \brief For a given codepoint, do a binary search of the defined unicode 452 blocks to figure out which one contains the codepoint. 453 \param codePoint is the point to find 454 \param startGuess is the starting point for the binary search (default 0) 455 */ 456 static 457 int32 458 FindBlockForCodepoint(uint32 codePoint, uint32 startGuess) 459 { 460 uint32 min = 0; 461 uint32 max = kNumUnicodeBlockRanges; 462 uint32 guess = (max + min) / 2; 463 464 if (startGuess > 0) 465 guess = startGuess; 466 467 if (codePoint > kUnicodeBlockMap[max-1].end) 468 return -1; 469 470 while ((max >= min) && (guess < kNumUnicodeBlockRanges)) { 471 uint32 start = kUnicodeBlockMap[guess].start; 472 uint32 end = kUnicodeBlockMap[guess].end; 473 474 if (start <= codePoint && end >= codePoint) 475 return guess; 476 477 if (end < codePoint) { 478 min = guess + 1; 479 } else { 480 max = guess - 1; 481 } 482 483 guess = (max + min) / 2; 484 } 485 486 return -1; 487 } 488 489 /*! 490 \brief parses charmap from fontconfig. See fontconfig docs for FcCharSetFirstPage 491 and FcCharSetNextPage for details on format. 492 \param charMap is a fontconfig character map 493 \param baseCodePoint is the base codepoint returned by fontconfig 494 \param blocksForMap is a unicode_block to store the bitmap of contained blocks 495 */ 496 static 497 void 498 ParseFcMap(FcChar32 charMap[], FcChar32 baseCodePoint, unicode_block& blocksForMap) 499 { 500 for (int i = 0; i < FC_CHARSET_MAP_SIZE; ++i) { 501 FcChar32 curMapBlock = charMap[i]; 502 uint32 rangeStart = 0; 503 uint32 block = 0; 504 505 for (int bit = 0; bit < 32; ++bit) { 506 uint32 startPoint = 0; 507 int32 foundStartBlock = -1; 508 int32 foundEndBlock = -1; 509 510 if ((curMapBlock & 0x8) != 0) { 511 if (rangeStart == 0) { 512 rangeStart = bit; 513 startPoint = baseCodePoint + block + (rangeStart); 514 foundStartBlock = FindBlockForCodepoint(startPoint, 0); 515 if (foundStartBlock >= 0) { 516 blocksForMap = blocksForMap 517 | kUnicodeBlockMap[foundStartBlock].block; 518 } 519 } 520 } else if (rangeStart > 0 && foundStartBlock > 0) { 521 // when we find an empty bit, that's the end of the range 522 uint32 endPoint = baseCodePoint + block + (bit - 1); 523 524 foundEndBlock = FindBlockForCodepoint(endPoint, 525 foundStartBlock); 526 // start the binary search at the block where we found the 527 // start codepoint to ideally find the end in the same 528 // block. 529 ++foundStartBlock; 530 531 while (foundStartBlock <= foundEndBlock) { 532 // if the starting codepoint is found in a different block 533 // than the ending codepoint, we should add all the blocks 534 // inbetween. 535 blocksForMap = blocksForMap 536 | kUnicodeBlockMap[foundStartBlock].block; 537 ++foundStartBlock; 538 } 539 540 foundStartBlock = -1; 541 foundEndBlock = -1; 542 rangeStart = 0; 543 } else { 544 foundStartBlock = -1; 545 rangeStart = 0; 546 } 547 548 curMapBlock >>= 1; 549 } 550 551 block += 32; 552 } 553 } 554 555 #endif // FONTCONFIG_ENABLED 556 557 558 /*! 559 \brief Gets a bitmap that indicates which Unicode blocks are in the font. 560 \param unicode_block to store bitmap in 561 \return B_OK; bitmap will be empty if something went wrong 562 */ 563 status_t 564 ServerFont::GetUnicodeBlocks(unicode_block& blocksForFont) 565 { 566 blocksForFont = unicode_block(); 567 568 #ifdef FONTCONFIG_ENABLED 569 FT_Face face = GetTransformedFace(true, true); 570 if (face == NULL) 571 return B_ERROR; 572 573 FcCharSet *charSet = FcFreeTypeCharSet(face, NULL); 574 if (charSet == NULL) 575 return B_ERROR; 576 577 FcChar32 charMap[FC_CHARSET_MAP_SIZE]; 578 FcChar32 next = 0; 579 FcChar32 baseCodePoint = FcCharSetFirstPage(charSet, charMap, &next); 580 581 while ((baseCodePoint != FC_CHARSET_DONE) && (next != FC_CHARSET_DONE)) { 582 ParseFcMap(charMap, baseCodePoint, blocksForFont); 583 baseCodePoint = FcCharSetNextPage(charSet, charMap, &next); 584 } 585 586 #endif // FONTCONFIG_ENABLED 587 588 return B_OK; 589 } 590 591 /*! 592 \brief Checks if a unicode block specified by a start and end point is defined 593 in the current font 594 \param start of unicode block 595 \param end of unicode block 596 \param hasBlock boolean to store whether the font contains the specified block 597 \return B_OK; hasBlock will be false if something goes wrong 598 */ 599 status_t 600 ServerFont::IncludesUnicodeBlock(uint32 start, uint32 end, bool& hasBlock) 601 { 602 hasBlock = false; 603 604 #ifdef FONTCONFIG_ENABLED 605 FT_Face face = GetTransformedFace(true, true); 606 if (face == NULL) 607 return B_ERROR; 608 609 FcCharSet *charSet = FcFreeTypeCharSet(face, NULL); 610 if (charSet == NULL) 611 return B_ERROR; 612 613 uint32 curCodePoint = start; 614 615 while (curCodePoint <= end && hasBlock == false) { 616 // loop through range; if any character in the range is in the charset 617 // then the block is represented. 618 if (FcCharSetHasChar(charSet, (FcChar32)curCodePoint) == FcTrue) { 619 hasBlock = true; 620 break; 621 } 622 623 ++curCodePoint; 624 } 625 626 #endif // FONTCONFIG_ENABLED 627 628 return B_OK; 629 } 630 631 632 class HasGlyphsConsumer { 633 public: 634 HasGlyphsConsumer(bool* hasArray) 635 : fHasArray(hasArray) 636 { 637 } 638 bool NeedsVector() { return false; } 639 void Start() {} 640 void Finish(double x, double y) {} 641 void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) 642 { 643 fHasArray[index] = false; 644 } 645 bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph, 646 FontCacheEntry* entry, double x, double y, double advanceX, 647 double advanceY) 648 { 649 fHasArray[index] = glyph->glyph_index != 0; 650 return true; 651 } 652 653 private: 654 bool* fHasArray; 655 }; 656 657 658 status_t 659 ServerFont::GetHasGlyphs(const char* string, int32 numBytes, 660 bool* hasArray) const 661 { 662 if (!string || numBytes <= 0 || !hasArray) 663 return B_BAD_DATA; 664 665 HasGlyphsConsumer consumer(hasArray); 666 if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes, 667 NULL, fSpacing)) 668 return B_OK; 669 670 return B_ERROR; 671 } 672 673 674 class EdgesConsumer { 675 public: 676 EdgesConsumer(edge_info* edges, float size) 677 : fEdges(edges) 678 , fSize(size) 679 { 680 } 681 bool NeedsVector() { return false; } 682 void Start() {} 683 void Finish(double x, double y) {} 684 void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) 685 { 686 fEdges[index].left = 0.0; 687 fEdges[index].right = 0.0; 688 } 689 bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph, 690 FontCacheEntry* entry, double x, double y, double advanceX, 691 double advanceY) 692 { 693 fEdges[index].left = glyph->inset_left / fSize; 694 fEdges[index].right = glyph->inset_right / fSize; 695 return true; 696 } 697 698 private: 699 edge_info* fEdges; 700 float fSize; 701 }; 702 703 704 status_t 705 ServerFont::GetEdges(const char* string, int32 numBytes, 706 edge_info* edges) const 707 { 708 if (!string || numBytes <= 0 || !edges) 709 return B_BAD_DATA; 710 711 EdgesConsumer consumer(edges, fSize); 712 if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes, 713 NULL, fSpacing)) { 714 return B_OK; 715 } 716 717 return B_ERROR; 718 719 // FT_Face face = GetTransformedFace(false, false); 720 // if (!face) 721 // return B_ERROR; 722 // 723 // const char *string = charArray; 724 // for (int i = 0; i < numChars; i++) { 725 // FT_Load_Char(face, UTF8ToCharCode(&string), FT_LOAD_NO_BITMAP); 726 // edgeArray[i].left = float(face->glyph->metrics.horiBearingX) 727 // / 64 / fSize; 728 // edgeArray[i].right = float(face->glyph->metrics.horiBearingX 729 // + face->glyph->metrics.width - face->glyph->metrics.horiAdvance) 730 // / 64 / fSize; 731 // } 732 // 733 // PutTransformedFace(face); 734 // return B_OK; 735 } 736 737 738 class BPointEscapementConsumer { 739 public: 740 BPointEscapementConsumer(BPoint* escapements, BPoint* offsets, 741 int32 numChars, float size) 742 : 743 fEscapements(escapements), 744 fOffsets(offsets), 745 fNumChars(numChars), 746 fSize(size) 747 { 748 } 749 750 bool NeedsVector() { return false; } 751 void Start() {} 752 void Finish(double x, double y) {} 753 void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) 754 { 755 _Set(index, 0, 0); 756 } 757 758 bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph, 759 FontCacheEntry* entry, double x, double y, double advanceX, 760 double advanceY) 761 { 762 return _Set(index, advanceX, advanceY); 763 } 764 765 private: 766 inline bool _Set(int32 index, double x, double y) 767 { 768 if (index >= fNumChars) 769 return false; 770 771 fEscapements[index].x = x / fSize; 772 fEscapements[index].y = y / fSize; 773 if (fOffsets) { 774 // ToDo: According to the BeBook: "The offsetArray is applied by 775 // the dynamic spacing in order to improve the relative position 776 // of the character's width with relation to another character, 777 // without altering the width." So this will probably depend on 778 // the spacing mode. 779 fOffsets[index].x = 0; 780 fOffsets[index].y = 0; 781 } 782 return true; 783 } 784 785 BPoint* fEscapements; 786 BPoint* fOffsets; 787 int32 fNumChars; 788 float fSize; 789 }; 790 791 792 status_t 793 ServerFont::GetEscapements(const char* string, int32 numBytes, int32 numChars, 794 escapement_delta delta, BPoint escapementArray[], 795 BPoint offsetArray[]) const 796 { 797 if (!string || numBytes <= 0 || !escapementArray) 798 return B_BAD_DATA; 799 800 BPointEscapementConsumer consumer(escapementArray, offsetArray, numChars, 801 fSize); 802 if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes, 803 &delta, fSpacing)) { 804 return B_OK; 805 } 806 807 return B_ERROR; 808 } 809 810 811 class WidthEscapementConsumer { 812 public: 813 WidthEscapementConsumer(float* widths, int32 numChars, float size) 814 : 815 fWidths(widths), 816 fNumChars(numChars), 817 fSize(size) 818 { 819 } 820 821 bool NeedsVector() { return false; } 822 void Start() {} 823 void Finish(double x, double y) {} 824 void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) 825 { 826 fWidths[index] = 0.0; 827 } 828 829 bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph, 830 FontCacheEntry* entry, double x, double y, double advanceX, 831 double advanceY) 832 { 833 if (index >= fNumChars) 834 return false; 835 836 fWidths[index] = advanceX / fSize; 837 return true; 838 } 839 840 private: 841 float* fWidths; 842 int32 fNumChars; 843 float fSize; 844 }; 845 846 847 848 status_t 849 ServerFont::GetEscapements(const char* string, int32 numBytes, int32 numChars, 850 escapement_delta delta, float widthArray[]) const 851 { 852 if (!string || numBytes <= 0 || !widthArray) 853 return B_BAD_DATA; 854 855 WidthEscapementConsumer consumer(widthArray, numChars, fSize); 856 if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes, 857 &delta, fSpacing)) { 858 return B_OK; 859 } 860 return B_ERROR; 861 } 862 863 864 class BoundingBoxConsumer { 865 public: 866 BoundingBoxConsumer(Transformable& transform, BRect* rectArray, 867 bool asString) 868 : rectArray(rectArray) 869 , stringBoundingBox(INT32_MAX, INT32_MAX, INT32_MIN, INT32_MIN) 870 , fAsString(asString) 871 , fCurves(fPathAdaptor) 872 , fContour(fCurves) 873 , fTransformedOutline(fCurves, transform) 874 , fTransformedContourOutline(fContour, transform) 875 , fTransform(transform) 876 { 877 } 878 879 bool NeedsVector() { return false; } 880 void Start() {} 881 void Finish(double x, double y) {} 882 void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) {} 883 bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph, 884 FontCacheEntry* entry, double x, double y, double advanceX, 885 double advanceY) 886 { 887 if (glyph->data_type != glyph_data_outline) { 888 const agg::rect_i& r = glyph->bounds; 889 if (fAsString) { 890 if (rectArray) { 891 rectArray[index].left = r.x1 + x; 892 rectArray[index].top = r.y1 + y; 893 rectArray[index].right = r.x2 + x + 1; 894 rectArray[index].bottom = r.y2 + y + 1; 895 } else { 896 stringBoundingBox = stringBoundingBox 897 | BRect(r.x1 + x, r.y1 + y, 898 r.x2 + x + 1, r.y2 + y + 1); 899 } 900 } else { 901 rectArray[index].left = r.x1; 902 rectArray[index].top = r.y1; 903 rectArray[index].right = r.x2 + 1; 904 rectArray[index].bottom = r.y2 + 1; 905 } 906 } else { 907 if (fAsString) { 908 entry->InitAdaptors(glyph, x, y, 909 fMonoAdaptor, fGray8Adaptor, fPathAdaptor); 910 } else { 911 entry->InitAdaptors(glyph, 0, 0, 912 fMonoAdaptor, fGray8Adaptor, fPathAdaptor); 913 } 914 double left = 0.0; 915 double top = 0.0; 916 double right = -1.0; 917 double bottom = -1.0; 918 uint32 pathID[1]; 919 pathID[0] = 0; 920 // TODO: use fContour if falseboldwidth is > 0 921 agg::bounding_rect(fTransformedOutline, pathID, 0, 1, 922 &left, &top, &right, &bottom); 923 924 if (rectArray) { 925 rectArray[index] = BRect(left, top, right, bottom); 926 } else { 927 stringBoundingBox = stringBoundingBox 928 | BRect(left, top, right, bottom); 929 } 930 } 931 return true; 932 } 933 934 BRect* rectArray; 935 BRect stringBoundingBox; 936 937 private: 938 bool fAsString; 939 FontCacheEntry::GlyphPathAdapter fPathAdaptor; 940 FontCacheEntry::GlyphGray8Adapter fGray8Adaptor; 941 FontCacheEntry::GlyphMonoAdapter fMonoAdaptor; 942 943 FontCacheEntry::CurveConverter fCurves; 944 FontCacheEntry::ContourConverter fContour; 945 946 FontCacheEntry::TransformedOutline fTransformedOutline; 947 FontCacheEntry::TransformedContourOutline fTransformedContourOutline; 948 949 Transformable& fTransform; 950 }; 951 952 953 status_t 954 ServerFont::GetBoundingBoxes(const char* string, int32 numBytes, 955 BRect rectArray[], bool stringEscapement, font_metric_mode mode, 956 escapement_delta delta, bool asString) 957 { 958 // TODO: The font_metric_mode is not used 959 if (!string || numBytes <= 0 || !rectArray) 960 return B_BAD_DATA; 961 962 Transformable transform(EmbeddedTransformation()); 963 964 BoundingBoxConsumer consumer(transform, rectArray, asString); 965 if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes, 966 stringEscapement ? &delta : NULL, fSpacing)) { 967 return B_OK; 968 } 969 return B_ERROR; 970 } 971 972 973 status_t 974 ServerFont::GetBoundingBoxesForStrings(char *charArray[], int32 lengthArray[], 975 int32 numStrings, BRect rectArray[], font_metric_mode mode, 976 escapement_delta deltaArray[]) 977 { 978 // TODO: The font_metric_mode is never used 979 if (!charArray || !lengthArray|| numStrings <= 0 || !rectArray || !deltaArray) 980 return B_BAD_DATA; 981 982 Transformable transform(EmbeddedTransformation()); 983 984 for (int32 i = 0; i < numStrings; i++) { 985 int32 numBytes = lengthArray[i]; 986 const char* string = charArray[i]; 987 escapement_delta delta = deltaArray[i]; 988 989 BoundingBoxConsumer consumer(transform, NULL, true); 990 if (!GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes, 991 &delta, fSpacing)) { 992 return B_ERROR; 993 } 994 995 rectArray[i] = consumer.stringBoundingBox; 996 } 997 998 return B_OK; 999 } 1000 1001 1002 class StringWidthConsumer { 1003 public: 1004 StringWidthConsumer() : width(0.0) {} 1005 bool NeedsVector() { return false; } 1006 void Start() {} 1007 void Finish(double x, double y) { width = x; } 1008 void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) {} 1009 bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph, 1010 FontCacheEntry* entry, double x, double y, double advanceX, 1011 double advanceY) 1012 { return true; } 1013 1014 float width; 1015 }; 1016 1017 1018 float 1019 ServerFont::StringWidth(const char *string, int32 numBytes, 1020 const escapement_delta* deltaArray) const 1021 { 1022 if (!string || numBytes <= 0) 1023 return 0.0; 1024 1025 StringWidthConsumer consumer; 1026 if (!GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes, 1027 deltaArray, fSpacing)) { 1028 return 0.0; 1029 } 1030 1031 return consumer.width; 1032 } 1033 1034 1035 /*! 1036 \brief Returns a BRect which encloses the entire font 1037 \return A BRect which encloses the entire font 1038 */ 1039 BRect 1040 ServerFont::BoundingBox() 1041 { 1042 // TODO: fBounds is nowhere calculated! 1043 return fBounds; 1044 } 1045 1046 1047 /*! 1048 \brief Obtains the height values for characters in the font in its current state 1049 \param fh pointer to a font_height object to receive the values for the font 1050 */ 1051 void 1052 ServerFont::GetHeight(font_height& height) const 1053 { 1054 fStyle->GetHeight(fSize, height); 1055 } 1056 1057 1058 void 1059 ServerFont::TruncateString(BString* inOut, uint32 mode, float width) const 1060 { 1061 if (!inOut) 1062 return; 1063 1064 // the width of the "…" glyph 1065 float ellipsisWidth = StringWidth(B_UTF8_ELLIPSIS, strlen(B_UTF8_ELLIPSIS)); 1066 1067 // count the individual glyphs 1068 int32 numChars = inOut->CountChars(); 1069 1070 // get the escapement of each glyph in font units 1071 float* escapementArray = new (std::nothrow) float[numChars]; 1072 if (escapementArray == NULL) 1073 return; 1074 1075 static escapement_delta delta = (escapement_delta){ 0.0, 0.0 }; 1076 if (GetEscapements(inOut->String(), inOut->Length(), numChars, delta, 1077 escapementArray) == B_OK) { 1078 truncate_string(*inOut, mode, width, escapementArray, fSize, 1079 ellipsisWidth, numChars); 1080 } 1081 1082 delete[] escapementArray; 1083 } 1084 1085 1086 Transformable 1087 ServerFont::EmbeddedTransformation() const 1088 { 1089 // TODO: cache this? 1090 Transformable transform; 1091 1092 transform.ShearBy(B_ORIGIN, (90.0 - fShear) * M_PI / 180.0, 0.0); 1093 transform.RotateBy(B_ORIGIN, -fRotation * M_PI / 180.0); 1094 1095 return transform; 1096 } 1097 1098