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 uint32 block = 0; 501 const uint8 BITS_PER_BLOCK = 32; 502 uint32 currentCodePoint = 0; 503 504 if (baseCodePoint > kUnicodeBlockMap[kNumUnicodeBlockRanges-1].end) 505 return; 506 507 for (int i = 0; i < FC_CHARSET_MAP_SIZE; ++i) { 508 FcChar32 curMapBlock = charMap[i]; 509 int32 rangeStart = -1; 510 int32 startBlock = -1; 511 int32 endBlock = -1; 512 uint32 startPoint = 0; 513 514 currentCodePoint = baseCodePoint + block; 515 516 for (int bit = 0; bit < BITS_PER_BLOCK; ++bit) { 517 if (curMapBlock == 0 && startBlock < 0) 518 // if no more bits are set then short-circuit the loop 519 break; 520 521 if ((curMapBlock & 0x1) != 0 && rangeStart < 0) { 522 rangeStart = bit; 523 startPoint = currentCodePoint + rangeStart; 524 startBlock = FindBlockForCodepoint(startPoint, 0); 525 if (startBlock >= 0) { 526 blocksForMap = blocksForMap 527 | kUnicodeBlockMap[startBlock].block; 528 } 529 } else if (rangeStart >= 0 && startBlock >= 0) { 530 // when we find an empty bit, that's the end of the range 531 uint32 endPoint = currentCodePoint + (bit - 1); 532 533 endBlock = FindBlockForCodepoint(endPoint, 534 startBlock); 535 // start the binary search at the block where we found the 536 // start codepoint to ideally find the end in the same 537 // block. 538 ++startBlock; 539 540 while (startBlock <= endBlock) { 541 // if the starting codepoint is found in a different block 542 // than the ending codepoint, we should add all the blocks 543 // inbetween. 544 blocksForMap = blocksForMap 545 | kUnicodeBlockMap[startBlock].block; 546 ++startBlock; 547 } 548 549 startBlock = -1; 550 endBlock = -1; 551 rangeStart = -1; 552 } 553 554 curMapBlock >>= 1; 555 } 556 557 if (rangeStart >= 0 && startBlock >= 0) { 558 // if we hit the end of the block and had 559 // found a start of the range then we 560 // should end the range at the end of the block 561 uint32 endPoint = currentCodePoint + BITS_PER_BLOCK - 1; 562 563 endBlock = FindBlockForCodepoint(endPoint, 564 startBlock); 565 // start the binary search at the block where we found the 566 // start codepoint to ideally find the end in the same 567 // block. 568 ++startBlock; 569 570 while (startBlock <= endBlock) { 571 // if the starting codepoint is found in a different block 572 // than the ending codepoint, we should add all the blocks 573 // inbetween. 574 blocksForMap = blocksForMap 575 | kUnicodeBlockMap[startBlock].block; 576 ++startBlock; 577 } 578 } 579 580 block += BITS_PER_BLOCK; 581 } 582 } 583 584 #endif // FONTCONFIG_ENABLED 585 586 587 /*! 588 \brief Gets a bitmap that indicates which Unicode blocks are in the font. 589 \param unicode_block to store bitmap in 590 \return B_OK; bitmap will be empty if something went wrong 591 */ 592 status_t 593 ServerFont::GetUnicodeBlocks(unicode_block& blocksForFont) 594 { 595 blocksForFont = unicode_block(); 596 597 #ifdef FONTCONFIG_ENABLED 598 FT_Face face = GetTransformedFace(true, true); 599 if (face == NULL) 600 return B_ERROR; 601 602 FcCharSet *charSet = FcFreeTypeCharSet(face, NULL); 603 if (charSet == NULL) { 604 PutTransformedFace(face); 605 return B_ERROR; 606 } 607 608 FcChar32 charMap[FC_CHARSET_MAP_SIZE]; 609 FcChar32 next = 0; 610 FcChar32 baseCodePoint = FcCharSetFirstPage(charSet, charMap, &next); 611 612 while ((baseCodePoint != FC_CHARSET_DONE) && (next != FC_CHARSET_DONE)) { 613 ParseFcMap(charMap, baseCodePoint, blocksForFont); 614 baseCodePoint = FcCharSetNextPage(charSet, charMap, &next); 615 } 616 617 FcCharSetDestroy(charSet); 618 PutTransformedFace(face); 619 #endif // FONTCONFIG_ENABLED 620 621 return B_OK; 622 } 623 624 /*! 625 \brief Checks if a unicode block specified by a start and end point is defined 626 in the current font 627 \param start of unicode block 628 \param end of unicode block 629 \param hasBlock boolean to store whether the font contains the specified block 630 \return B_OK; hasBlock will be false if something goes wrong 631 */ 632 status_t 633 ServerFont::IncludesUnicodeBlock(uint32 start, uint32 end, bool& hasBlock) 634 { 635 hasBlock = false; 636 637 #ifdef FONTCONFIG_ENABLED 638 FT_Face face = GetTransformedFace(true, true); 639 if (face == NULL) 640 return B_ERROR; 641 642 FcCharSet *charSet = FcFreeTypeCharSet(face, NULL); 643 if (charSet == NULL) { 644 PutTransformedFace(face); 645 return B_ERROR; 646 } 647 648 uint32 curCodePoint = start; 649 650 while (curCodePoint <= end && hasBlock == false) { 651 // loop through range; if any character in the range is in the charset 652 // then the block is represented. 653 if (FcCharSetHasChar(charSet, (FcChar32)curCodePoint) == FcTrue) { 654 hasBlock = true; 655 break; 656 } 657 658 ++curCodePoint; 659 } 660 661 FcCharSetDestroy(charSet); 662 PutTransformedFace(face); 663 #endif // FONTCONFIG_ENABLED 664 665 return B_OK; 666 } 667 668 669 class HasGlyphsConsumer { 670 public: 671 HasGlyphsConsumer(bool* hasArray) 672 : fHasArray(hasArray) 673 { 674 } 675 bool NeedsVector() { return false; } 676 void Start() {} 677 void Finish(double x, double y) {} 678 void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) 679 { 680 fHasArray[index] = false; 681 } 682 bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph, 683 FontCacheEntry* entry, double x, double y, double advanceX, 684 double advanceY) 685 { 686 fHasArray[index] = glyph->glyph_index != 0; 687 return true; 688 } 689 690 private: 691 bool* fHasArray; 692 }; 693 694 695 status_t 696 ServerFont::GetHasGlyphs(const char* string, int32 numBytes, 697 bool* hasArray) const 698 { 699 if (!string || numBytes <= 0 || !hasArray) 700 return B_BAD_DATA; 701 702 HasGlyphsConsumer consumer(hasArray); 703 if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes, 704 NULL, fSpacing)) 705 return B_OK; 706 707 return B_ERROR; 708 } 709 710 711 class EdgesConsumer { 712 public: 713 EdgesConsumer(edge_info* edges, float size) 714 : fEdges(edges) 715 , fSize(size) 716 { 717 } 718 bool NeedsVector() { return false; } 719 void Start() {} 720 void Finish(double x, double y) {} 721 void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) 722 { 723 fEdges[index].left = 0.0; 724 fEdges[index].right = 0.0; 725 } 726 bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph, 727 FontCacheEntry* entry, double x, double y, double advanceX, 728 double advanceY) 729 { 730 fEdges[index].left = glyph->inset_left / fSize; 731 fEdges[index].right = glyph->inset_right / fSize; 732 return true; 733 } 734 735 private: 736 edge_info* fEdges; 737 float fSize; 738 }; 739 740 741 status_t 742 ServerFont::GetEdges(const char* string, int32 numBytes, 743 edge_info* edges) const 744 { 745 if (!string || numBytes <= 0 || !edges) 746 return B_BAD_DATA; 747 748 EdgesConsumer consumer(edges, fSize); 749 if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes, 750 NULL, fSpacing)) { 751 return B_OK; 752 } 753 754 return B_ERROR; 755 756 // FT_Face face = GetTransformedFace(false, false); 757 // if (!face) 758 // return B_ERROR; 759 // 760 // const char *string = charArray; 761 // for (int i = 0; i < numChars; i++) { 762 // FT_Load_Char(face, UTF8ToCharCode(&string), FT_LOAD_NO_BITMAP); 763 // edgeArray[i].left = float(face->glyph->metrics.horiBearingX) 764 // / 64 / fSize; 765 // edgeArray[i].right = float(face->glyph->metrics.horiBearingX 766 // + face->glyph->metrics.width - face->glyph->metrics.horiAdvance) 767 // / 64 / fSize; 768 // } 769 // 770 // PutTransformedFace(face); 771 // return B_OK; 772 } 773 774 775 class BPointEscapementConsumer { 776 public: 777 BPointEscapementConsumer(BPoint* escapements, BPoint* offsets, 778 int32 numChars, float size) 779 : 780 fEscapements(escapements), 781 fOffsets(offsets), 782 fNumChars(numChars), 783 fSize(size) 784 { 785 } 786 787 bool NeedsVector() { return false; } 788 void Start() {} 789 void Finish(double x, double y) {} 790 void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) 791 { 792 _Set(index, 0, 0); 793 } 794 795 bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph, 796 FontCacheEntry* entry, double x, double y, double advanceX, 797 double advanceY) 798 { 799 return _Set(index, advanceX, advanceY); 800 } 801 802 private: 803 inline bool _Set(int32 index, double x, double y) 804 { 805 if (index >= fNumChars) 806 return false; 807 808 fEscapements[index].x = x / fSize; 809 fEscapements[index].y = y / fSize; 810 if (fOffsets) { 811 // ToDo: According to the BeBook: "The offsetArray is applied by 812 // the dynamic spacing in order to improve the relative position 813 // of the character's width with relation to another character, 814 // without altering the width." So this will probably depend on 815 // the spacing mode. 816 fOffsets[index].x = 0; 817 fOffsets[index].y = 0; 818 } 819 return true; 820 } 821 822 BPoint* fEscapements; 823 BPoint* fOffsets; 824 int32 fNumChars; 825 float fSize; 826 }; 827 828 829 status_t 830 ServerFont::GetEscapements(const char* string, int32 numBytes, int32 numChars, 831 escapement_delta delta, BPoint escapementArray[], 832 BPoint offsetArray[]) const 833 { 834 if (!string || numBytes <= 0 || !escapementArray) 835 return B_BAD_DATA; 836 837 BPointEscapementConsumer consumer(escapementArray, offsetArray, numChars, 838 fSize); 839 if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes, 840 &delta, fSpacing)) { 841 return B_OK; 842 } 843 844 return B_ERROR; 845 } 846 847 848 class WidthEscapementConsumer { 849 public: 850 WidthEscapementConsumer(float* widths, int32 numChars, float size) 851 : 852 fWidths(widths), 853 fNumChars(numChars), 854 fSize(size) 855 { 856 } 857 858 bool NeedsVector() { return false; } 859 void Start() {} 860 void Finish(double x, double y) {} 861 void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) 862 { 863 fWidths[index] = 0.0; 864 } 865 866 bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph, 867 FontCacheEntry* entry, double x, double y, double advanceX, 868 double advanceY) 869 { 870 if (index >= fNumChars) 871 return false; 872 873 fWidths[index] = advanceX / fSize; 874 return true; 875 } 876 877 private: 878 float* fWidths; 879 int32 fNumChars; 880 float fSize; 881 }; 882 883 884 885 status_t 886 ServerFont::GetEscapements(const char* string, int32 numBytes, int32 numChars, 887 escapement_delta delta, float widthArray[]) const 888 { 889 if (!string || numBytes <= 0 || !widthArray) 890 return B_BAD_DATA; 891 892 WidthEscapementConsumer consumer(widthArray, numChars, fSize); 893 if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes, 894 &delta, fSpacing)) { 895 return B_OK; 896 } 897 return B_ERROR; 898 } 899 900 901 class BoundingBoxConsumer { 902 public: 903 BoundingBoxConsumer(Transformable& transform, BRect* rectArray, 904 bool asString) 905 : rectArray(rectArray) 906 , stringBoundingBox(INT32_MAX, INT32_MAX, INT32_MIN, INT32_MIN) 907 , fAsString(asString) 908 , fCurves(fPathAdaptor) 909 , fContour(fCurves) 910 , fTransformedOutline(fCurves, transform) 911 , fTransformedContourOutline(fContour, transform) 912 , fTransform(transform) 913 { 914 } 915 916 bool NeedsVector() { return false; } 917 void Start() {} 918 void Finish(double x, double y) {} 919 void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) {} 920 bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph, 921 FontCacheEntry* entry, double x, double y, double advanceX, 922 double advanceY) 923 { 924 if (glyph->data_type != glyph_data_outline) { 925 const agg::rect_i& r = glyph->bounds; 926 if (fAsString) { 927 if (rectArray) { 928 rectArray[index].left = r.x1 + x; 929 rectArray[index].top = r.y1 + y; 930 rectArray[index].right = r.x2 + x + 1; 931 rectArray[index].bottom = r.y2 + y + 1; 932 } else { 933 stringBoundingBox = stringBoundingBox 934 | BRect(r.x1 + x, r.y1 + y, 935 r.x2 + x + 1, r.y2 + y + 1); 936 } 937 } else { 938 rectArray[index].left = r.x1; 939 rectArray[index].top = r.y1; 940 rectArray[index].right = r.x2 + 1; 941 rectArray[index].bottom = r.y2 + 1; 942 } 943 } else { 944 if (fAsString) { 945 entry->InitAdaptors(glyph, x, y, 946 fMonoAdaptor, fGray8Adaptor, fPathAdaptor); 947 } else { 948 entry->InitAdaptors(glyph, 0, 0, 949 fMonoAdaptor, fGray8Adaptor, fPathAdaptor); 950 } 951 double left = 0.0; 952 double top = 0.0; 953 double right = -1.0; 954 double bottom = -1.0; 955 uint32 pathID[1]; 956 pathID[0] = 0; 957 // TODO: use fContour if falseboldwidth is > 0 958 agg::bounding_rect(fTransformedOutline, pathID, 0, 1, 959 &left, &top, &right, &bottom); 960 961 if (rectArray) { 962 rectArray[index] = BRect(left, top, right, bottom); 963 } else { 964 stringBoundingBox = stringBoundingBox 965 | BRect(left, top, right, bottom); 966 } 967 } 968 return true; 969 } 970 971 BRect* rectArray; 972 BRect stringBoundingBox; 973 974 private: 975 bool fAsString; 976 FontCacheEntry::GlyphPathAdapter fPathAdaptor; 977 FontCacheEntry::GlyphGray8Adapter fGray8Adaptor; 978 FontCacheEntry::GlyphMonoAdapter fMonoAdaptor; 979 980 FontCacheEntry::CurveConverter fCurves; 981 FontCacheEntry::ContourConverter fContour; 982 983 FontCacheEntry::TransformedOutline fTransformedOutline; 984 FontCacheEntry::TransformedContourOutline fTransformedContourOutline; 985 986 Transformable& fTransform; 987 }; 988 989 990 status_t 991 ServerFont::GetBoundingBoxes(const char* string, int32 numBytes, 992 BRect rectArray[], bool stringEscapement, font_metric_mode mode, 993 escapement_delta delta, bool asString) 994 { 995 // TODO: The font_metric_mode is not used 996 if (!string || numBytes <= 0 || !rectArray) 997 return B_BAD_DATA; 998 999 Transformable transform(EmbeddedTransformation()); 1000 1001 BoundingBoxConsumer consumer(transform, rectArray, asString); 1002 if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes, 1003 stringEscapement ? &delta : NULL, fSpacing)) { 1004 return B_OK; 1005 } 1006 return B_ERROR; 1007 } 1008 1009 1010 status_t 1011 ServerFont::GetBoundingBoxesForStrings(char *charArray[], int32 lengthArray[], 1012 int32 numStrings, BRect rectArray[], font_metric_mode mode, 1013 escapement_delta deltaArray[]) 1014 { 1015 // TODO: The font_metric_mode is never used 1016 if (!charArray || !lengthArray|| numStrings <= 0 || !rectArray || !deltaArray) 1017 return B_BAD_DATA; 1018 1019 Transformable transform(EmbeddedTransformation()); 1020 1021 for (int32 i = 0; i < numStrings; i++) { 1022 int32 numBytes = lengthArray[i]; 1023 const char* string = charArray[i]; 1024 escapement_delta delta = deltaArray[i]; 1025 1026 BoundingBoxConsumer consumer(transform, NULL, true); 1027 if (!GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes, 1028 &delta, fSpacing)) { 1029 return B_ERROR; 1030 } 1031 1032 rectArray[i] = consumer.stringBoundingBox; 1033 } 1034 1035 return B_OK; 1036 } 1037 1038 1039 class StringWidthConsumer { 1040 public: 1041 StringWidthConsumer() : width(0.0) {} 1042 bool NeedsVector() { return false; } 1043 void Start() {} 1044 void Finish(double x, double y) { width = x; } 1045 void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) {} 1046 bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph, 1047 FontCacheEntry* entry, double x, double y, double advanceX, 1048 double advanceY) 1049 { return true; } 1050 1051 float width; 1052 }; 1053 1054 1055 float 1056 ServerFont::StringWidth(const char *string, int32 numBytes, 1057 const escapement_delta* deltaArray) const 1058 { 1059 if (!string || numBytes <= 0) 1060 return 0.0; 1061 1062 StringWidthConsumer consumer; 1063 if (!GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes, 1064 deltaArray, fSpacing)) { 1065 return 0.0; 1066 } 1067 1068 return consumer.width; 1069 } 1070 1071 1072 /*! 1073 \brief Returns a BRect which encloses the entire font 1074 \return A BRect which encloses the entire font 1075 */ 1076 BRect 1077 ServerFont::BoundingBox() 1078 { 1079 // TODO: fBounds is nowhere calculated! 1080 return fBounds; 1081 } 1082 1083 1084 /*! 1085 \brief Obtains the height values for characters in the font in its current state 1086 \param fh pointer to a font_height object to receive the values for the font 1087 */ 1088 void 1089 ServerFont::GetHeight(font_height& height) const 1090 { 1091 fStyle->GetHeight(fSize, height); 1092 } 1093 1094 1095 void 1096 ServerFont::TruncateString(BString* inOut, uint32 mode, float width) const 1097 { 1098 if (!inOut) 1099 return; 1100 1101 // the width of the "…" glyph 1102 float ellipsisWidth = StringWidth(B_UTF8_ELLIPSIS, strlen(B_UTF8_ELLIPSIS)); 1103 1104 // count the individual glyphs 1105 int32 numChars = inOut->CountChars(); 1106 1107 // get the escapement of each glyph in font units 1108 float* escapementArray = new (std::nothrow) float[numChars]; 1109 if (escapementArray == NULL) 1110 return; 1111 1112 static escapement_delta delta = (escapement_delta){ 0.0, 0.0 }; 1113 if (GetEscapements(inOut->String(), inOut->Length(), numChars, delta, 1114 escapementArray) == B_OK) { 1115 truncate_string(*inOut, mode, width, escapementArray, fSize, 1116 ellipsisWidth, numChars); 1117 } 1118 1119 delete[] escapementArray; 1120 } 1121 1122 1123 Transformable 1124 ServerFont::EmbeddedTransformation() const 1125 { 1126 // TODO: cache this? 1127 Transformable transform; 1128 1129 transform.ShearBy(B_ORIGIN, (90.0 - fShear) * M_PI / 180.0, 0.0); 1130 transform.RotateBy(B_ORIGIN, -fRotation * M_PI / 180.0); 1131 1132 return transform; 1133 } 1134 1135