1 /* 2 * Copyright 2001-2005, 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 */ 9 10 11 #include <ByteOrder.h> 12 #include <Shape.h> 13 #include <String.h> 14 #include <UTF8.h> 15 16 #include "Angle.h" 17 #include "FontManager.h" 18 #include "moreUTF8.h" 19 #include "truncate_string.h" 20 21 #include FT_FREETYPE_H 22 #include FT_GLYPH_H 23 #include FT_OUTLINE_H 24 25 #include "ServerFont.h" 26 27 28 // functions needed to convert a freetype vector graphics to a BShape 29 inline BPoint 30 VectorToPoint(FT_Vector *vector) 31 { 32 BPoint result; 33 result.x = float(int32(vector->x)) / 2097152; 34 result.y = -float(int32(vector->y)) / 2097152; 35 return result; 36 } 37 38 int 39 MoveToFunc(FT_Vector *to, void *user) 40 { 41 ((BShape *)user)->MoveTo(VectorToPoint(to)); 42 return 0; 43 } 44 45 int 46 LineToFunc(FT_Vector *to, void *user) 47 { 48 ((BShape *)user)->LineTo(VectorToPoint(to)); 49 return 0; 50 } 51 52 int 53 ConicToFunc(FT_Vector *control, FT_Vector *to, void *user) 54 { 55 BPoint controls[3]; 56 57 controls[0] = VectorToPoint(control); 58 controls[1] = VectorToPoint(to); 59 controls[2] = controls[1]; 60 61 ((BShape *)user)->BezierTo(controls); 62 return 0; 63 } 64 65 int 66 CubicToFunc(FT_Vector *control1, FT_Vector *control2, FT_Vector *to, void *user) 67 { 68 BPoint controls[3]; 69 70 controls[0] = VectorToPoint(control1); 71 controls[1] = VectorToPoint(control2); 72 controls[2] = VectorToPoint(to); 73 74 ((BShape *)user)->BezierTo(controls); 75 return 0; 76 } 77 78 79 // is_white_space 80 inline bool 81 is_white_space(uint16 glyph) 82 { 83 // TODO: handle them all! 84 if (glyph == ' ' || glyph == B_TAB) 85 return true; 86 return false; 87 } 88 89 90 // #pragma mark - 91 92 93 class FaceGetter { 94 public: 95 FaceGetter(FontStyle *style) 96 : 97 fStyle(style) 98 { 99 style->Lock(); 100 } 101 102 ~FaceGetter() 103 { 104 fStyle->Unlock(); 105 } 106 107 FT_Face Face() 108 { 109 return fStyle->FreeTypeFace(); 110 } 111 112 private: 113 FontStyle *fStyle; 114 }; 115 116 117 // #pragma mark - 118 119 120 /*! 121 \brief Constructor 122 \param style Style object to which the ServerFont belongs 123 \param size Character size in points 124 \param rotation Rotation in degrees 125 \param shear Shear (slant) in degrees. 45 <= shear <= 135 126 \param flags Style flags as defined in <Font.h> 127 \param spacing String spacing flag as defined in <Font.h> 128 */ 129 ServerFont::ServerFont(FontStyle& style, float size, 130 float rotation, float shear, 131 uint16 flags, uint8 spacing) 132 : fStyle(&style), 133 fSize(size), 134 fRotation(rotation), 135 fShear(shear), 136 fBounds(0, 0, 0, 0), 137 fFlags(flags), 138 fSpacing(spacing), 139 fDirection(style.Direction()), 140 fFace(style.Face()), 141 fEncoding(B_UNICODE_UTF8) 142 { 143 fStyle->Acquire(); 144 } 145 146 147 ServerFont::ServerFont() 148 : 149 fStyle(NULL) 150 { 151 *this = *gFontManager->DefaultPlainFont(); 152 } 153 154 155 /*! 156 \brief Copy Constructor 157 \param font ServerFont to copy 158 */ 159 ServerFont::ServerFont(const ServerFont &font) 160 : 161 fStyle(NULL) 162 { 163 *this = font; 164 } 165 166 167 /*! 168 \brief Removes itself as a dependency of its owning style. 169 */ 170 ServerFont::~ServerFont() 171 { 172 fStyle->Release(); 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 fFlags = font.fFlags; 188 fSpacing = font.fSpacing; 189 fDirection = font.fDirection; 190 fFace = font.fFace; 191 fEncoding = font.fEncoding; 192 fBounds = font.fBounds; 193 194 SetStyle(*font.fStyle); 195 return *this; 196 } 197 198 199 /*! 200 \brief Returns the number of strikes in the font 201 \return The number of strikes in the font 202 */ 203 int32 204 ServerFont::CountTuned() 205 { 206 return fStyle->TunedCount(); 207 } 208 209 210 /*! 211 \brief Returns the file format of the font. 212 \return Mostly B_TRUETYPE_WINDOWS :) 213 */ 214 font_file_format 215 ServerFont::FileFormat() 216 { 217 return fStyle->FileFormat(); 218 } 219 220 221 const char* 222 ServerFont::Style() const 223 { 224 return fStyle->Name(); 225 } 226 227 228 const char* 229 ServerFont::Family() const 230 { 231 return fStyle->Family()->Name(); 232 } 233 234 235 void 236 ServerFont::SetStyle(FontStyle& style) 237 { 238 if (&style != fStyle) { 239 // detach from old style 240 if (fStyle) 241 fStyle->Release(); 242 243 // attach to new style 244 fStyle = &style; 245 fFace = style.Face(); 246 fDirection = style.Direction(); 247 248 fStyle->Acquire(); 249 } 250 } 251 252 253 /*! 254 \brief Sets the ServerFont instance to whatever font is specified 255 \param familyID ID number of the family to set 256 \param styleID ID number of the style to set 257 \return B_OK if successful, B_ERROR if not 258 */ 259 status_t 260 ServerFont::SetFamilyAndStyle(uint16 familyID, uint16 styleID) 261 { 262 FontStyle* style = NULL; 263 264 if (gFontManager->Lock()) { 265 style = gFontManager->GetStyle(familyID, styleID); 266 if (style != NULL) 267 style->Acquire(); 268 269 gFontManager->Unlock(); 270 } 271 272 if (!style) 273 return B_ERROR; 274 275 SetStyle(*style); 276 style->Release(); 277 278 return B_OK; 279 } 280 281 282 /*! 283 \brief Sets the ServerFont instance to whatever font is specified 284 \param fontID the combination of family and style ID numbers 285 \return B_OK if successful, B_ERROR if not 286 */ 287 status_t 288 ServerFont::SetFamilyAndStyle(uint32 fontID) 289 { 290 uint16 style = fontID & 0xFFFF; 291 uint16 family = (fontID & 0xFFFF0000) >> 16; 292 293 return SetFamilyAndStyle(family, style); 294 } 295 296 297 void 298 ServerFont::SetFace(uint32 face) 299 { 300 // TODO: change font style as requested! 301 fFace = face; 302 } 303 304 305 /*! 306 \brief Gets the ID values for the ServerFont instance in one shot 307 \return the combination of family and style ID numbers 308 */ 309 uint32 310 ServerFont::GetFamilyAndStyle(void) const 311 { 312 return (FamilyID() << 16) | StyleID(); 313 } 314 315 316 BShape ** 317 ServerFont::GetGlyphShapes(const char charArray[], int32 numChars) const 318 { 319 if (!charArray || numChars <= 0) 320 return NULL; 321 322 FT_Outline_Funcs funcs; 323 funcs.move_to = MoveToFunc; 324 funcs.line_to = LineToFunc; 325 funcs.conic_to = ConicToFunc; 326 funcs.cubic_to = CubicToFunc; 327 328 FaceGetter getter(fStyle); 329 FT_Face face = getter.Face(); 330 if (!face) 331 return NULL; 332 333 FT_Set_Char_Size(face, 0, int32(fSize * 64), 72, 72); 334 335 Angle rotation(fRotation); 336 Angle shear(fShear); 337 338 // First, rotate 339 FT_Matrix rmatrix; 340 rmatrix.xx = (FT_Fixed)( rotation.Cosine()*0x10000); 341 rmatrix.xy = (FT_Fixed)(-rotation.Sine()*0x10000); 342 rmatrix.yx = (FT_Fixed)( rotation.Sine()*0x10000); 343 rmatrix.yy = (FT_Fixed)( rotation.Cosine()*0x10000); 344 345 // Next, shear 346 FT_Matrix smatrix; 347 smatrix.xx = (FT_Fixed)(0x10000); 348 smatrix.xy = (FT_Fixed)(-shear.Cosine()*0x10000); 349 smatrix.yx = (FT_Fixed)(0); 350 smatrix.yy = (FT_Fixed)(0x10000); 351 352 // Multiply togheter 353 FT_Matrix_Multiply(&rmatrix, &smatrix); 354 355 //FT_Vector pen; 356 //FT_Set_Transform(face, &smatrix, &pen); 357 358 BShape **shapes = (BShape **)malloc(sizeof(BShape *) * numChars); 359 for (int i = 0; i < numChars; i++) { 360 shapes[i] = new BShape(); 361 shapes[i]->Clear(); 362 // TODO : this is wrong (the nth char isn't charArray[i]) 363 FT_Load_Char(face, charArray[i], FT_LOAD_NO_BITMAP); 364 FT_Outline outline = face->glyph->outline; 365 FT_Outline_Decompose(&outline, &funcs, shapes[i]); 366 shapes[i]->Close(); 367 } 368 369 return shapes; 370 } 371 372 373 void 374 ServerFont::GetHasGlyphs(const char charArray[], int32 numChars, bool hasArray[]) const 375 { 376 if (!charArray || numChars <= 0 || !hasArray) 377 return; 378 379 FaceGetter getter(fStyle); 380 FT_Face face = getter.Face(); 381 if (!face) 382 return; 383 384 FT_Set_Char_Size(face, 0, int32(fSize * 64), 72, 72); 385 386 // UTF8 handling...this can probably be smarter 387 // Here is what I do in the AGGTextRenderer to handle UTF8... 388 // It is probably highly inefficient, so it should be reviewed. 389 int32 numBytes = UTF8CountBytes(charArray, numChars); 390 int32 convertedLength = numBytes * 2; 391 char* convertedBuffer = new char[convertedLength]; 392 393 int32 state = 0; 394 status_t ret; 395 if ((ret = convert_from_utf8(B_UNICODE_CONVERSION, 396 charArray, &numBytes, 397 convertedBuffer, &convertedLength, 398 &state, B_SUBSTITUTE)) >= B_OK 399 && (ret = swap_data(B_INT16_TYPE, convertedBuffer, convertedLength, 400 B_SWAP_BENDIAN_TO_HOST)) >= B_OK) { 401 402 uint16* glyphIndex = (uint16*)convertedBuffer; 403 // just to be sure 404 numChars = min_c((uint32)numChars, convertedLength / sizeof(uint16)); 405 406 for (int i = 0; i < numChars; i++) { 407 hasArray[i] = FT_Get_Char_Index(face, glyphIndex[i]) > 0; 408 } 409 } 410 delete[] convertedBuffer; 411 } 412 413 414 void 415 ServerFont::GetEdges(const char charArray[], int32 numChars, edge_info edgeArray[]) const 416 { 417 if (!charArray || numChars <= 0 || !edgeArray) 418 return; 419 420 FaceGetter getter(fStyle); 421 FT_Face face = getter.Face(); 422 if (!face) 423 return; 424 425 FT_Set_Char_Size(face, 0, int32(fSize * 64), 72, 72); 426 427 // UTF8 handling...this can probably be smarter 428 // Here is what I do in the AGGTextRenderer to handle UTF8... 429 // It is probably highly inefficient, so it should be reviewed. 430 int32 numBytes = UTF8CountBytes(charArray, numChars); 431 int32 convertedLength = numBytes * 2; 432 char* convertedBuffer = new char[convertedLength]; 433 434 int32 state = 0; 435 status_t ret; 436 if ((ret = convert_from_utf8(B_UNICODE_CONVERSION, 437 charArray, &numBytes, 438 convertedBuffer, &convertedLength, 439 &state, B_SUBSTITUTE)) >= B_OK 440 && (ret = swap_data(B_INT16_TYPE, convertedBuffer, convertedLength, 441 B_SWAP_BENDIAN_TO_HOST)) >= B_OK) { 442 443 uint16* glyphIndex = (uint16*)convertedBuffer; 444 // just to be sure 445 numChars = min_c((uint32)numChars, convertedLength / sizeof(uint16)); 446 447 for (int i = 0; i < numChars; i++) { 448 FT_Load_Char(face, glyphIndex[i], FT_LOAD_NO_BITMAP); 449 if (face->glyph) { 450 edgeArray[i].left = float(face->glyph->metrics.horiBearingX /64) / fSize; 451 edgeArray[i].right = float((face->glyph->metrics.horiBearingX 452 + face->glyph->metrics.width - face->glyph->metrics.horiAdvance)/64) /fSize; 453 } 454 } 455 } 456 delete[] convertedBuffer; 457 } 458 459 460 BPoint* 461 ServerFont::GetEscapements(const char charArray[], int32 numChars, 462 BPoint offsetArray[]) const 463 { 464 if (!charArray || numChars <= 0 || !offsetArray) 465 return NULL; 466 467 FaceGetter getter(fStyle); 468 FT_Face face = getter.Face(); 469 if (!face) 470 return NULL; 471 472 FT_Set_Char_Size(face, 0, int32(fSize * 64), 72, 72); 473 474 Angle rotation(fRotation); 475 Angle shear(fShear); 476 477 // First, rotate 478 FT_Matrix rmatrix; 479 rmatrix.xx = (FT_Fixed)( rotation.Cosine()*0x10000); 480 rmatrix.xy = (FT_Fixed)(-rotation.Sine()*0x10000); 481 rmatrix.yx = (FT_Fixed)( rotation.Sine()*0x10000); 482 rmatrix.yy = (FT_Fixed)( rotation.Cosine()*0x10000); 483 484 // Next, shear 485 FT_Matrix smatrix; 486 smatrix.xx = (FT_Fixed)(0x10000); 487 smatrix.xy = (FT_Fixed)(-shear.Cosine()*0x10000); 488 smatrix.yx = (FT_Fixed)(0); 489 smatrix.yy = (FT_Fixed)(0x10000); 490 491 // Multiply togheter 492 FT_Matrix_Multiply(&rmatrix, &smatrix); 493 494 //FT_Vector pen; 495 //FT_Set_Transform(face, &smatrix, &pen); 496 497 // TODO: I'm not sure if this the correct interpretation 498 // of the BeBook. Have actual tests been done here? 499 500 // TODO: handle UTF8... see below!! 501 BPoint *escapements = (BPoint *)malloc(sizeof(BPoint) * numChars); 502 for (int i = 0; i < numChars; i++) { 503 // TODO : this is wrong (the nth char isn't charArray[i]) 504 FT_Load_Char(face, charArray[i], FT_LOAD_NO_BITMAP); 505 // escapements[i].x = float(face->glyph->metrics.width / 64) / fSize; 506 // escapements[i].y = 0; 507 escapements[i].x = float(face->glyph->metrics.horiAdvance / 64) / fSize; 508 escapements[i].y = float(face->glyph->metrics.vertAdvance / 64) / fSize; 509 escapements[i] += offsetArray[i]; 510 } 511 512 return escapements; 513 } 514 515 516 bool 517 ServerFont::GetEscapements(const char charArray[], int32 numChars, int32 numBytes, 518 float widthArray[], escapement_delta delta) const 519 { 520 if (!charArray || numChars <= 0) 521 return false; 522 523 FaceGetter getter(fStyle); 524 FT_Face face = getter.Face(); 525 if (!face) 526 return false; 527 528 FT_Set_Char_Size(face, 0, int32(fSize * 64), 72, 72); 529 530 // UTF8 handling...this can probably be smarter 531 // Here is what I do in the AGGTextRenderer to handle UTF8... 532 // It is probably highly inefficient, so it should be reviewed. 533 int32 convertedLength = numBytes * 2; 534 char* convertedBuffer = new char[convertedLength]; 535 536 int32 state = 0; 537 status_t ret; 538 if ((ret = convert_from_utf8(B_UNICODE_CONVERSION, 539 charArray, &numBytes, 540 convertedBuffer, &convertedLength, 541 &state, B_SUBSTITUTE)) >= B_OK 542 && (ret = swap_data(B_INT16_TYPE, convertedBuffer, convertedLength, 543 B_SWAP_BENDIAN_TO_HOST)) >= B_OK) { 544 545 uint16* glyphIndex = (uint16*)convertedBuffer; 546 // just to be sure 547 numChars = min_c((uint32)numChars, convertedLength / sizeof(uint16)); 548 549 for (int i = 0; i < numChars; i++) { 550 FT_Load_Char(face, glyphIndex[i], FT_LOAD_NO_BITMAP); 551 if (face->glyph) { 552 widthArray[i] = ((float)face->glyph->metrics.horiAdvance / 64.0) / fSize; 553 widthArray[i] += is_white_space(glyphIndex[i]) ? delta.space : delta.nonspace; 554 } 555 } 556 } 557 delete[] convertedBuffer; 558 559 return ret >= B_OK; 560 } 561 562 563 bool 564 ServerFont::GetBoundingBoxesAsString(const char charArray[], int32 numChars, 565 BRect rectArray[], bool string_escapement, font_metric_mode mode, 566 escapement_delta delta) 567 { 568 if (!charArray || numChars <= 0 || !rectArray) 569 return false; 570 571 FaceGetter getter(fStyle); 572 FT_Face face = getter.Face(); 573 if (!face) 574 return false; 575 576 FT_Set_Char_Size(face, 0, int32(fSize * 64), 72, 72); 577 578 // UTF8 handling...this can probably be smarter 579 // Here is what I do in the AGGTextRenderer to handle UTF8... 580 // It is probably highly inefficient, so it should be reviewed. 581 int32 numBytes = UTF8CountBytes(charArray, numChars); 582 int32 convertedLength = numBytes * 2; 583 char* convertedBuffer = new char[convertedLength]; 584 585 int32 state = 0; 586 status_t ret; 587 if ((ret = convert_from_utf8(B_UNICODE_CONVERSION, 588 charArray, &numBytes, 589 convertedBuffer, &convertedLength, 590 &state, B_SUBSTITUTE)) >= B_OK 591 && (ret = swap_data(B_INT16_TYPE, convertedBuffer, convertedLength, 592 B_SWAP_BENDIAN_TO_HOST)) >= B_OK) { 593 594 uint16* glyphIndex = (uint16*)convertedBuffer; 595 // just to be sure 596 numChars = min_c((uint32)numChars, convertedLength / sizeof(uint16)); 597 598 for (int i = 0; i < numChars; i++) { 599 if (string_escapement) { 600 if (i>0) 601 rectArray[i].OffsetBy(is_white_space(glyphIndex[i-1]) ? delta.space/2.0 : delta.nonspace/2.0, 0.0); 602 rectArray[i].OffsetBy(is_white_space(glyphIndex[i]) ? delta.space/2.0 : delta.nonspace/2.0, 0.0); 603 } 604 FT_Load_Char(face, glyphIndex[i], FT_LOAD_NO_BITMAP); 605 if (face->glyph) { 606 if (i<numChars-1) 607 rectArray[i+1].left = rectArray[i+1].right = rectArray[i].left + 608 face->glyph->metrics.horiAdvance / 64.0; 609 rectArray[i].left += float(face->glyph->metrics.horiBearingX) /64.0; 610 rectArray[i].right += float(face->glyph->metrics.horiBearingX 611 + face->glyph->metrics.width)/64.0; 612 rectArray[i].top = -float(face->glyph->metrics.horiBearingY) /64.0; 613 rectArray[i].bottom = float(face->glyph->metrics.height 614 - face->glyph->metrics.horiBearingY) /64.0; 615 } 616 } 617 } 618 delete[] convertedBuffer; 619 620 return true; 621 } 622 623 624 bool 625 ServerFont::GetBoundingBoxesForStrings(char *charArray[], int32 lengthArray[], 626 int32 numStrings, BRect rectArray[], font_metric_mode mode, escapement_delta deltaArray[]) 627 { 628 if (!charArray || !lengthArray|| numStrings <= 0 || !rectArray || !deltaArray) 629 return false; 630 631 FaceGetter getter(fStyle); 632 FT_Face face = getter.Face(); 633 if (!face) 634 return false; 635 636 FT_Set_Char_Size(face, 0, int32(fSize * 64), 72, 72); 637 638 for (int32 i = 0; i < numStrings; i++) { 639 // TODO: ... 640 } 641 642 return true; 643 } 644 645 646 float 647 ServerFont::StringWidth(const char* string, int32 numBytes) const 648 { 649 if (!string || numBytes <= 0) 650 return 0.0; 651 652 FaceGetter getter(fStyle); 653 FT_Face face = getter.Face(); 654 if (!face) 655 return 0.0; 656 657 FT_Set_Char_Size(face, 0, int32(fSize * 64), 72, 72); 658 659 int32 convertedLength = numBytes * 2; 660 char* convertedBuffer = new char[convertedLength]; 661 float width = 0.0; 662 663 int32 state = 0; 664 status_t ret; 665 if ((ret = convert_from_utf8(B_UNICODE_CONVERSION, 666 string, &numBytes, 667 convertedBuffer, &convertedLength, 668 &state, B_SUBSTITUTE)) >= B_OK 669 && (ret = swap_data(B_INT16_TYPE, convertedBuffer, convertedLength, 670 B_SWAP_BENDIAN_TO_HOST)) >= B_OK) { 671 672 uint16* glyphIndex = (uint16*)convertedBuffer; 673 // just to be sure 674 int numChars = convertedLength / sizeof(uint16); 675 676 for (int i = 0; i < numChars; i++) { 677 FT_Load_Char(face, glyphIndex[i], FT_LOAD_NO_BITMAP); 678 width += face->glyph->advance.x / 64.0; 679 } 680 } 681 delete[] convertedBuffer; 682 683 return width; 684 } 685 686 687 /*! 688 \brief Returns a BRect which encloses the entire font 689 \return A BRect which encloses the entire font 690 */ 691 BRect 692 ServerFont::BoundingBox() 693 { 694 // TODO: fBounds is nowhere calculated! 695 return fBounds; 696 } 697 698 699 /*! 700 \brief Obtains the height values for characters in the font in its current state 701 \param fh pointer to a font_height object to receive the values for the font 702 */ 703 void 704 ServerFont::GetHeight(font_height& height) const 705 { 706 fStyle->GetHeight(fSize, height); 707 } 708 709 710 void 711 ServerFont::TruncateString(BString* inOut, uint32 mode, float width) const 712 { 713 if (inOut) { 714 // the width of the "…" glyph 715 float ellipsisWidth = StringWidth(B_UTF8_ELLIPSIS, 3); 716 const char* string = inOut->String(); 717 int32 length = strlen(string); 718 // temporary array to hold result 719 char* result = new char[length + 3]; 720 // count the individual glyphs 721 int32 numChars = UTF8CountChars(string, length); 722 // get the escapement of each glyph in font units 723 float* escapementArray = new float[numChars]; 724 static escapement_delta delta = (escapement_delta){ 0.0, 0.0 }; 725 GetEscapements(string, numChars, length, escapementArray, delta); 726 727 truncate_string(string, mode, width, result, 728 escapementArray, fSize, ellipsisWidth, length, numChars); 729 730 inOut->SetTo(result); 731 732 delete[] escapementArray; 733 delete[] result; 734 } 735 } 736 737