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