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