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, false), 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 } 146 147 148 ServerFont::ServerFont() 149 : 150 fStyle(NULL) 151 { 152 *this = *gFontManager->DefaultPlainFont(); 153 } 154 155 156 /*! 157 \brief Copy Constructor 158 \param font ServerFont to copy 159 */ 160 ServerFont::ServerFont(const ServerFont &font) 161 : 162 fStyle(NULL) 163 { 164 *this = font; 165 } 166 167 168 /*! 169 \brief Removes itself as a dependency of its owning style. 170 */ 171 ServerFont::~ServerFont() 172 { 173 } 174 175 176 /*! 177 \brief Returns a copy of the specified font 178 \param The font to copy from. 179 \return A copy of the specified font 180 */ 181 ServerFont& 182 ServerFont::operator=(const ServerFont& font) 183 { 184 fSize = font.fSize; 185 fRotation = font.fRotation; 186 fShear = font.fShear; 187 fFalseBoldWidth = font.fFalseBoldWidth; 188 fFlags = font.fFlags; 189 fSpacing = font.fSpacing; 190 fEncoding = font.fEncoding; 191 fBounds = font.fBounds; 192 193 SetStyle(font.fStyle); 194 195 fFace = font.fFace; 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 fStyle.SetTo(style, false); 256 257 fFace = fStyle->PreservedFace(fFace); 258 fDirection = fStyle->Direction(); 259 } 260 } 261 262 263 /*! 264 \brief Sets the ServerFont instance to whatever font is specified 265 This method will lock the font manager. 266 267 \param familyID ID number of the family to set 268 \param styleID ID number of the style to set 269 \return B_OK if successful, B_ERROR if not 270 */ 271 status_t 272 ServerFont::SetFamilyAndStyle(uint16 familyID, uint16 styleID) 273 { 274 BReference<FontStyle> style; 275 276 if (gFontManager->Lock()) { 277 style.SetTo(gFontManager->GetStyle(familyID, styleID), false); 278 279 gFontManager->Unlock(); 280 } 281 282 if (style == NULL) 283 return B_ERROR; 284 285 SetStyle(style); 286 287 return B_OK; 288 } 289 290 291 /*! 292 \brief Sets the ServerFont instance to whatever font is specified 293 \param fontID the combination of family and style ID numbers 294 \return B_OK if successful, B_ERROR if not 295 */ 296 status_t 297 ServerFont::SetFamilyAndStyle(uint32 fontID) 298 { 299 uint16 style = fontID & 0xFFFF; 300 uint16 family = (fontID & 0xFFFF0000) >> 16; 301 302 return SetFamilyAndStyle(family, style); 303 } 304 305 306 status_t 307 ServerFont::SetFace(uint16 face) 308 { 309 // Don't confuse the Be API "face" with the Freetype face, which is just 310 // an index in case a single font file exports multiple font faces. The 311 // FontStyle class takes care of mapping the font style name to the Be 312 // API face flags in FontStyle::_TranslateStyleToFace(). 313 314 BReference <FontStyle> style; 315 uint16 familyID = FamilyID(); 316 if (gFontManager->Lock()) { 317 int32 count = gFontManager->CountStyles(familyID); 318 for (int32 i = 0; i < count; i++) { 319 style.SetTo(gFontManager->GetStyleByIndex(familyID, i), false); 320 if (style == NULL) 321 break; 322 if (style->PreservedFace(face) == face) 323 break; 324 else 325 style = NULL; 326 } 327 328 gFontManager->Unlock(); 329 } 330 331 if (!style) 332 return B_ERROR; 333 334 fFace = face; 335 SetStyle(style); 336 337 return B_OK; 338 } 339 340 341 /*! 342 \brief Gets the ID values for the ServerFont instance in one shot 343 \return the combination of family and style ID numbers 344 */ 345 uint32 346 ServerFont::GetFamilyAndStyle() const 347 { 348 return (FamilyID() << 16) | StyleID(); 349 } 350 351 352 FT_Face 353 ServerFont::GetTransformedFace(bool rotate, bool shear) const 354 { 355 fStyle->Lock(); 356 FT_Face face = fStyle->FreeTypeFace(); 357 if (!face) { 358 fStyle->Unlock(); 359 return NULL; 360 } 361 362 FT_Set_Char_Size(face, 0, int32(fSize * 64), 72, 72); 363 364 if ((rotate && fRotation != 0) || (shear && fShear != 90)) { 365 FT_Matrix rmatrix, smatrix; 366 367 Angle rotationAngle(fRotation); 368 rmatrix.xx = (FT_Fixed)( rotationAngle.Cosine() * 0x10000); 369 rmatrix.xy = (FT_Fixed)(-rotationAngle.Sine() * 0x10000); 370 rmatrix.yx = (FT_Fixed)( rotationAngle.Sine() * 0x10000); 371 rmatrix.yy = (FT_Fixed)( rotationAngle.Cosine() * 0x10000); 372 373 Angle shearAngle(fShear); 374 smatrix.xx = (FT_Fixed)(0x10000); 375 smatrix.xy = (FT_Fixed)(-shearAngle.Cosine() * 0x10000); 376 smatrix.yx = (FT_Fixed)(0); 377 smatrix.yy = (FT_Fixed)(0x10000); 378 379 // Multiply togheter and apply transform 380 FT_Matrix_Multiply(&rmatrix, &smatrix); 381 FT_Set_Transform(face, &smatrix, NULL); 382 } 383 384 // fStyle will be unlocked in PutTransformedFace() 385 return face; 386 } 387 388 389 void 390 ServerFont::PutTransformedFace(FT_Face face) const 391 { 392 // Reset transformation 393 FT_Set_Transform(face, NULL, NULL); 394 fStyle->Unlock(); 395 } 396 397 398 status_t 399 ServerFont::GetGlyphShapes(const char charArray[], int32 numChars, 400 BShape* shapeArray[]) const 401 { 402 if (!charArray || numChars <= 0 || !shapeArray) 403 return B_BAD_DATA; 404 405 FT_Face face = GetTransformedFace(true, true); 406 if (!face) 407 return B_ERROR; 408 409 FT_Outline_Funcs funcs; 410 funcs.move_to = MoveToFunc; 411 funcs.line_to = LineToFunc; 412 funcs.conic_to = ConicToFunc; 413 funcs.cubic_to = CubicToFunc; 414 funcs.shift = 0; 415 funcs.delta = 0; 416 417 const char* string = charArray; 418 for (int i = 0; i < numChars; i++) { 419 shapeArray[i] = new (std::nothrow) BShape(); 420 if (shapeArray[i] == NULL) { 421 PutTransformedFace(face); 422 return B_NO_MEMORY; 423 } 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 #ifdef FONTCONFIG_ENABLED 436 437 /*! 438 \brief For a given codepoint, do a binary search of the defined unicode 439 blocks to figure out which one contains the codepoint. 440 \param codePoint is the point to find 441 \param startGuess is the starting point for the binary search (default 0) 442 */ 443 static 444 int32 445 FindBlockForCodepoint(uint32 codePoint, uint32 startGuess) 446 { 447 uint32 min = 0; 448 uint32 max = kNumUnicodeBlockRanges; 449 uint32 guess = (max + min) / 2; 450 451 if (startGuess > 0) 452 guess = startGuess; 453 454 if (codePoint > kUnicodeBlockMap[max-1].end) 455 return -1; 456 457 while ((max >= min) && (guess < kNumUnicodeBlockRanges)) { 458 uint32 start = kUnicodeBlockMap[guess].start; 459 uint32 end = kUnicodeBlockMap[guess].end; 460 461 if (start <= codePoint && end >= codePoint) 462 return guess; 463 464 if (end < codePoint) { 465 min = guess + 1; 466 } else { 467 max = guess - 1; 468 } 469 470 guess = (max + min) / 2; 471 } 472 473 return -1; 474 } 475 476 /*! 477 \brief parses charmap from fontconfig. See fontconfig docs for FcCharSetFirstPage 478 and FcCharSetNextPage for details on format. 479 \param charMap is a fontconfig character map 480 \param baseCodePoint is the base codepoint returned by fontconfig 481 \param blocksForMap is a unicode_block to store the bitmap of contained blocks 482 */ 483 static 484 void 485 ParseFcMap(FcChar32 charMap[], FcChar32 baseCodePoint, unicode_block& blocksForMap) 486 { 487 uint32 block = 0; 488 const uint8 BITS_PER_BLOCK = 32; 489 uint32 currentCodePoint = 0; 490 491 if (baseCodePoint > kUnicodeBlockMap[kNumUnicodeBlockRanges-1].end) 492 return; 493 494 for (int i = 0; i < FC_CHARSET_MAP_SIZE; ++i) { 495 FcChar32 curMapBlock = charMap[i]; 496 int32 rangeStart = -1; 497 int32 startBlock = -1; 498 int32 endBlock = -1; 499 uint32 startPoint = 0; 500 501 currentCodePoint = baseCodePoint + block; 502 503 for (int bit = 0; bit < BITS_PER_BLOCK; ++bit) { 504 if (curMapBlock == 0 && startBlock < 0) 505 // if no more bits are set then short-circuit the loop 506 break; 507 508 if ((curMapBlock & 0x1) != 0 && rangeStart < 0) { 509 rangeStart = bit; 510 startPoint = currentCodePoint + rangeStart; 511 startBlock = FindBlockForCodepoint(startPoint, 0); 512 if (startBlock >= 0) { 513 blocksForMap = blocksForMap 514 | kUnicodeBlockMap[startBlock].block; 515 } 516 } else if (rangeStart >= 0 && startBlock >= 0) { 517 // when we find an empty bit, that's the end of the range 518 uint32 endPoint = currentCodePoint + (bit - 1); 519 520 endBlock = FindBlockForCodepoint(endPoint, 521 startBlock); 522 // start the binary search at the block where we found the 523 // start codepoint to ideally find the end in the same 524 // block. 525 ++startBlock; 526 527 while (startBlock <= endBlock) { 528 // if the starting codepoint is found in a different block 529 // than the ending codepoint, we should add all the blocks 530 // inbetween. 531 blocksForMap = blocksForMap 532 | kUnicodeBlockMap[startBlock].block; 533 ++startBlock; 534 } 535 536 startBlock = -1; 537 endBlock = -1; 538 rangeStart = -1; 539 } 540 541 curMapBlock >>= 1; 542 } 543 544 if (rangeStart >= 0 && startBlock >= 0) { 545 // if we hit the end of the block and had 546 // found a start of the range then we 547 // should end the range at the end of the block 548 uint32 endPoint = currentCodePoint + BITS_PER_BLOCK - 1; 549 550 endBlock = FindBlockForCodepoint(endPoint, 551 startBlock); 552 // start the binary search at the block where we found the 553 // start codepoint to ideally find the end in the same 554 // block. 555 ++startBlock; 556 557 while (startBlock <= endBlock) { 558 // if the starting codepoint is found in a different block 559 // than the ending codepoint, we should add all the blocks 560 // inbetween. 561 blocksForMap = blocksForMap 562 | kUnicodeBlockMap[startBlock].block; 563 ++startBlock; 564 } 565 } 566 567 block += BITS_PER_BLOCK; 568 } 569 } 570 571 #endif // FONTCONFIG_ENABLED 572 573 574 /*! 575 \brief Gets a bitmap that indicates which Unicode blocks are in the font. 576 \param unicode_block to store bitmap in 577 \return B_OK; bitmap will be empty if something went wrong 578 */ 579 status_t 580 ServerFont::GetUnicodeBlocks(unicode_block& blocksForFont) 581 { 582 blocksForFont = unicode_block(); 583 584 #ifdef FONTCONFIG_ENABLED 585 FT_Face face = GetTransformedFace(true, true); 586 if (face == NULL) 587 return B_ERROR; 588 589 FcCharSet *charSet = FcFreeTypeCharSet(face, NULL); 590 if (charSet == NULL) { 591 PutTransformedFace(face); 592 return B_ERROR; 593 } 594 595 FcChar32 charMap[FC_CHARSET_MAP_SIZE]; 596 FcChar32 next = 0; 597 FcChar32 baseCodePoint = FcCharSetFirstPage(charSet, charMap, &next); 598 599 while ((baseCodePoint != FC_CHARSET_DONE) && (next != FC_CHARSET_DONE)) { 600 ParseFcMap(charMap, baseCodePoint, blocksForFont); 601 baseCodePoint = FcCharSetNextPage(charSet, charMap, &next); 602 } 603 604 FcCharSetDestroy(charSet); 605 PutTransformedFace(face); 606 #endif // FONTCONFIG_ENABLED 607 608 return B_OK; 609 } 610 611 /*! 612 \brief Checks if a unicode block specified by a start and end point is defined 613 in the current font 614 \param start of unicode block 615 \param end of unicode block 616 \param hasBlock boolean to store whether the font contains the specified block 617 \return B_OK; hasBlock will be false if something goes wrong 618 */ 619 status_t 620 ServerFont::IncludesUnicodeBlock(uint32 start, uint32 end, bool& hasBlock) 621 { 622 hasBlock = false; 623 624 #ifdef FONTCONFIG_ENABLED 625 FT_Face face = GetTransformedFace(true, true); 626 if (face == NULL) 627 return B_ERROR; 628 629 FcCharSet *charSet = FcFreeTypeCharSet(face, NULL); 630 if (charSet == NULL) { 631 PutTransformedFace(face); 632 return B_ERROR; 633 } 634 635 uint32 curCodePoint = start; 636 637 while (curCodePoint <= end && hasBlock == false) { 638 // loop through range; if any character in the range is in the charset 639 // then the block is represented. 640 if (FcCharSetHasChar(charSet, (FcChar32)curCodePoint) == FcTrue) { 641 hasBlock = true; 642 break; 643 } 644 645 ++curCodePoint; 646 } 647 648 FcCharSetDestroy(charSet); 649 PutTransformedFace(face); 650 #endif // FONTCONFIG_ENABLED 651 652 return B_OK; 653 } 654 655 656 status_t 657 ServerFont::GetHasGlyphs(const char* string, int32 numBytes, int32 numChars, 658 bool* hasArray) const 659 { 660 if (string == NULL || numBytes <= 0 || numChars <= 0 || hasArray == NULL) 661 return B_BAD_DATA; 662 663 FontCacheEntry* entry = NULL; 664 FontCacheReference cacheReference; 665 BObjectList<FontCacheReference> fallbacks(21, true); 666 int32 fallbacksCount = -1; 667 668 entry = GlyphLayoutEngine::FontCacheEntryFor(*this, false); 669 if (entry == NULL || !cacheReference.SetTo(entry, false)) 670 return B_ERROR; 671 672 uint32 charCode; 673 int32 charIndex = 0; 674 const char* start = string; 675 while (charIndex < numChars && (charCode = UTF8ToCharCode(&string)) != 0) { 676 hasArray[charIndex] = entry->CanCreateGlyph(charCode); 677 678 if (hasArray[charIndex] == false) { 679 if (fallbacksCount < 0) { 680 GlyphLayoutEngine::PopulateAndLockFallbacks( 681 fallbacks, *this, false, false); 682 fallbacksCount = fallbacks.CountItems(); 683 } 684 685 for (int32 index = 0; index < fallbacksCount; index++) { 686 FontCacheEntry* fallbackEntry 687 = fallbacks.ItemAt(index)->Entry(); 688 if (fallbackEntry->CanCreateGlyph(charCode)) { 689 hasArray[charIndex] = true; 690 break; 691 } 692 } 693 } 694 695 charIndex++; 696 if (string - start + 1 > numBytes) 697 break; 698 } 699 700 return B_OK; 701 } 702 703 704 class EdgesConsumer { 705 public: 706 EdgesConsumer(edge_info* edges, float size) 707 : 708 fEdges(edges), 709 fSize(size) 710 { 711 } 712 713 bool NeedsVector() { return false; } 714 void Start() {} 715 void Finish(double x, double y) {} 716 void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) 717 { 718 fEdges[index].left = 0.0; 719 fEdges[index].right = 0.0; 720 } 721 722 bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph, 723 FontCacheEntry* entry, double x, double y, double advanceX, 724 double advanceY) 725 { 726 fEdges[index].left = glyph->inset_left / fSize; 727 fEdges[index].right = glyph->inset_right / fSize; 728 return true; 729 } 730 731 private: 732 edge_info* fEdges; 733 float fSize; 734 }; 735 736 737 status_t 738 ServerFont::GetEdges(const char* string, int32 numBytes, int32 numChars, 739 edge_info* edges) const 740 { 741 if (string == NULL || numBytes <= 0 || numChars <= 0 || edges == NULL) 742 return B_BAD_DATA; 743 744 EdgesConsumer consumer(edges, fSize); 745 if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes, 746 numChars, NULL, fSpacing)) { 747 return B_OK; 748 } 749 750 return B_ERROR; 751 752 // FT_Face face = GetTransformedFace(false, false); 753 // if (!face) 754 // return B_ERROR; 755 // 756 // const char *string = charArray; 757 // for (int i = 0; i < numChars; i++) { 758 // FT_Load_Char(face, UTF8ToCharCode(&string), FT_LOAD_NO_BITMAP); 759 // edgeArray[i].left = float(face->glyph->metrics.horiBearingX) 760 // / 64 / fSize; 761 // edgeArray[i].right = float(face->glyph->metrics.horiBearingX 762 // + face->glyph->metrics.width - face->glyph->metrics.horiAdvance) 763 // / 64 / fSize; 764 // } 765 // 766 // PutTransformedFace(face); 767 // return B_OK; 768 } 769 770 771 class BPointEscapementConsumer { 772 public: 773 BPointEscapementConsumer(BPoint* escapements, BPoint* offsets, float size) 774 : 775 fEscapements(escapements), 776 fOffsets(offsets), 777 fSize(size) 778 { 779 } 780 781 bool NeedsVector() { return false; } 782 void Start() {} 783 void Finish(double x, double y) {} 784 void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) 785 { 786 _Set(index, 0, 0); 787 } 788 789 bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph, 790 FontCacheEntry* entry, double x, double y, double advanceX, 791 double advanceY) 792 { 793 return _Set(index, advanceX, advanceY); 794 } 795 796 private: 797 inline bool _Set(int32 index, double x, double y) 798 { 799 fEscapements[index].x = x / fSize; 800 fEscapements[index].y = y / fSize; 801 if (fOffsets) { 802 // ToDo: According to the BeBook: "The offsetArray is applied by 803 // the dynamic spacing in order to improve the relative position 804 // of the character's width with relation to another character, 805 // without altering the width." So this will probably depend on 806 // the spacing mode. 807 fOffsets[index].x = 0; 808 fOffsets[index].y = 0; 809 } 810 return true; 811 } 812 813 BPoint* fEscapements; 814 BPoint* fOffsets; 815 float fSize; 816 }; 817 818 819 status_t 820 ServerFont::GetEscapements(const char* string, int32 numBytes, int32 numChars, 821 escapement_delta delta, BPoint escapementArray[], 822 BPoint offsetArray[]) const 823 { 824 if (string == NULL || numBytes <= 0 || numChars <= 0 825 || escapementArray == NULL) { 826 return B_BAD_DATA; 827 } 828 829 BPointEscapementConsumer consumer(escapementArray, offsetArray, fSize); 830 if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes, 831 numChars, &delta, fSpacing)) { 832 return B_OK; 833 } 834 835 return B_ERROR; 836 } 837 838 839 class WidthEscapementConsumer { 840 public: 841 WidthEscapementConsumer(float* widths, float size) 842 : 843 fWidths(widths), 844 fSize(size) 845 { 846 } 847 848 bool NeedsVector() { return false; } 849 void Start() {} 850 void Finish(double x, double y) {} 851 void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) 852 { 853 fWidths[index] = 0.0; 854 } 855 856 bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph, 857 FontCacheEntry* entry, double x, double y, double advanceX, 858 double advanceY) 859 { 860 fWidths[index] = advanceX / fSize; 861 return true; 862 } 863 864 private: 865 float* fWidths; 866 float fSize; 867 }; 868 869 870 871 status_t 872 ServerFont::GetEscapements(const char* string, int32 numBytes, int32 numChars, 873 escapement_delta delta, float widthArray[]) const 874 { 875 if (string == NULL || numBytes <= 0 || numChars <= 0 || widthArray == NULL) 876 return B_BAD_DATA; 877 878 WidthEscapementConsumer consumer(widthArray, fSize); 879 if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes, 880 numChars, &delta, fSpacing)) { 881 return B_OK; 882 } 883 884 return B_ERROR; 885 } 886 887 888 class BoundingBoxConsumer { 889 public: 890 BoundingBoxConsumer(Transformable& transform, BRect* rectArray, 891 bool asString) 892 : 893 rectArray(rectArray), 894 stringBoundingBox(INT32_MAX, INT32_MAX, INT32_MIN, INT32_MIN), 895 fAsString(asString), 896 fCurves(fPathAdaptor), 897 fContour(fCurves), 898 fTransformedOutline(fCurves, transform), 899 fTransformedContourOutline(fContour, transform), 900 fTransform(transform) 901 { 902 } 903 904 bool NeedsVector() { return false; } 905 void Start() {} 906 void Finish(double x, double y) {} 907 void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) {} 908 909 bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph, 910 FontCacheEntry* entry, double x, double y, double advanceX, 911 double advanceY) 912 { 913 if (glyph->data_type != glyph_data_outline) { 914 const agg::rect_i& r = glyph->bounds; 915 if (fAsString) { 916 if (rectArray) { 917 rectArray[index].left = r.x1 + x; 918 rectArray[index].top = r.y1 + y; 919 rectArray[index].right = r.x2 + x + 1; 920 rectArray[index].bottom = r.y2 + y + 1; 921 } else { 922 stringBoundingBox = stringBoundingBox 923 | BRect(r.x1 + x, r.y1 + y, 924 r.x2 + x + 1, r.y2 + y + 1); 925 } 926 } else { 927 rectArray[index].left = r.x1; 928 rectArray[index].top = r.y1; 929 rectArray[index].right = r.x2 + 1; 930 rectArray[index].bottom = r.y2 + 1; 931 } 932 } else { 933 if (fAsString) { 934 entry->InitAdaptors(glyph, x, y, 935 fMonoAdaptor, fGray8Adaptor, fPathAdaptor); 936 } else { 937 entry->InitAdaptors(glyph, 0, 0, 938 fMonoAdaptor, fGray8Adaptor, fPathAdaptor); 939 } 940 double left = 0.0; 941 double top = 0.0; 942 double right = -1.0; 943 double bottom = -1.0; 944 uint32 pathID[1]; 945 pathID[0] = 0; 946 // TODO: use fContour if falseboldwidth is > 0 947 agg::bounding_rect(fTransformedOutline, pathID, 0, 1, 948 &left, &top, &right, &bottom); 949 950 if (rectArray) { 951 rectArray[index] = BRect(left, top, right, bottom); 952 } else { 953 stringBoundingBox = stringBoundingBox 954 | BRect(left, top, right, bottom); 955 } 956 } 957 return true; 958 } 959 960 BRect* rectArray; 961 BRect stringBoundingBox; 962 963 private: 964 bool fAsString; 965 FontCacheEntry::GlyphPathAdapter fPathAdaptor; 966 FontCacheEntry::GlyphGray8Adapter fGray8Adaptor; 967 FontCacheEntry::GlyphMonoAdapter fMonoAdaptor; 968 969 FontCacheEntry::CurveConverter fCurves; 970 FontCacheEntry::ContourConverter fContour; 971 972 FontCacheEntry::TransformedOutline fTransformedOutline; 973 FontCacheEntry::TransformedContourOutline fTransformedContourOutline; 974 975 Transformable& fTransform; 976 }; 977 978 979 status_t 980 ServerFont::GetBoundingBoxes(const char* string, int32 numBytes, int32 numChars, 981 BRect rectArray[], bool stringEscapement, font_metric_mode mode, 982 escapement_delta delta, bool asString) 983 { 984 // TODO: The font_metric_mode is not used 985 if (string == NULL || numBytes <= 0 || numChars <= 0 || rectArray == NULL) 986 return B_BAD_DATA; 987 988 Transformable transform(EmbeddedTransformation()); 989 990 BoundingBoxConsumer consumer(transform, rectArray, asString); 991 if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes, 992 numChars, stringEscapement ? &delta : NULL, fSpacing)) { 993 return B_OK; 994 } 995 return B_ERROR; 996 } 997 998 999 status_t 1000 ServerFont::GetBoundingBoxesForStrings(char *charArray[], size_t lengthArray[], 1001 int32 numStrings, BRect rectArray[], font_metric_mode mode, 1002 escapement_delta deltaArray[]) 1003 { 1004 // TODO: The font_metric_mode is never used 1005 if (charArray == NULL || lengthArray == NULL || numStrings <= 0 1006 || rectArray == NULL || deltaArray == NULL) { 1007 return B_BAD_DATA; 1008 } 1009 1010 Transformable transform(EmbeddedTransformation()); 1011 1012 for (int32 i = 0; i < numStrings; i++) { 1013 size_t numBytes = lengthArray[i]; 1014 const char* string = charArray[i]; 1015 escapement_delta delta = deltaArray[i]; 1016 1017 BoundingBoxConsumer consumer(transform, NULL, true); 1018 if (!GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes, 1019 INT32_MAX, &delta, fSpacing)) { 1020 return B_ERROR; 1021 } 1022 1023 rectArray[i] = consumer.stringBoundingBox; 1024 } 1025 1026 return B_OK; 1027 } 1028 1029 1030 class StringWidthConsumer { 1031 public: 1032 StringWidthConsumer() 1033 : 1034 width(0.0) 1035 { 1036 } 1037 1038 bool NeedsVector() { return false; } 1039 void Start() {} 1040 void Finish(double x, double y) { width = x; } 1041 void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) {} 1042 bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph, 1043 FontCacheEntry* entry, double x, double y, double advanceX, 1044 double advanceY) 1045 { 1046 return true; 1047 } 1048 1049 float width; 1050 }; 1051 1052 1053 float 1054 ServerFont::StringWidth(const char *string, int32 numBytes, 1055 const escapement_delta* deltaArray) const 1056 { 1057 if (!string || numBytes <= 0) 1058 return 0.0; 1059 1060 StringWidthConsumer consumer; 1061 if (!GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes, 1062 INT32_MAX, deltaArray, fSpacing)) { 1063 return 0.0; 1064 } 1065 1066 return consumer.width; 1067 } 1068 1069 1070 /*! 1071 \brief Returns a BRect which encloses the entire font 1072 \return A BRect which encloses the entire font 1073 */ 1074 BRect 1075 ServerFont::BoundingBox() 1076 { 1077 // TODO: fBounds is nowhere calculated! 1078 return fBounds; 1079 } 1080 1081 1082 /*! 1083 \brief Obtains the height values for characters in the font in its current state 1084 \param fh pointer to a font_height object to receive the values for the font 1085 */ 1086 void 1087 ServerFont::GetHeight(font_height& height) const 1088 { 1089 fStyle->GetHeight(fSize, height); 1090 } 1091 1092 1093 void 1094 ServerFont::TruncateString(BString* inOut, uint32 mode, float width) const 1095 { 1096 if (!inOut) 1097 return; 1098 1099 // the width of the "…" glyph 1100 float ellipsisWidth = StringWidth(B_UTF8_ELLIPSIS, strlen(B_UTF8_ELLIPSIS)); 1101 1102 // count the individual glyphs 1103 int32 numChars = inOut->CountChars(); 1104 1105 // get the escapement of each glyph in font units 1106 float* escapementArray = new (std::nothrow) float[numChars]; 1107 if (escapementArray == NULL) 1108 return; 1109 1110 static escapement_delta delta = (escapement_delta){ 0.0, 0.0 }; 1111 if (GetEscapements(inOut->String(), inOut->Length(), numChars, delta, 1112 escapementArray) == B_OK) { 1113 truncate_string(*inOut, mode, width, escapementArray, fSize, 1114 ellipsisWidth, numChars); 1115 } 1116 1117 delete[] escapementArray; 1118 } 1119 1120 1121 Transformable 1122 ServerFont::EmbeddedTransformation() const 1123 { 1124 // TODO: cache this? 1125 Transformable transform; 1126 1127 transform.ShearBy(B_ORIGIN, (90.0 - fShear) * M_PI / 180.0, 0.0); 1128 transform.RotateBy(B_ORIGIN, -fRotation * M_PI / 180.0); 1129 1130 return transform; 1131 } 1132 1133