1/* 2 * Copyright © 2022 Thomas E. Dickey 3 * Copyright © 2000 Keith Packard 4 * 5 * Permission to use, copy, modify, distribute, and sell this software and its 6 * documentation for any purpose is hereby granted without fee, provided that 7 * the above copyright notice appear in all copies and that both that copyright 8 * notice and this permission notice appear in supporting documentation, and 9 * that the name of the above copyright holders not be used in advertising or 10 * publicity pertaining to distribution of the software without specific, 11 * written prior permission. The above copyright holders make no 12 * representations about the suitability of this software for any purpose. It 13 * is provided "as is" without express or implied warranty. 14 * 15 * THE ABOVE LISTED COPYRIGHT HOLDER(S) DISCLAIM ALL WARRANTIES WITH REGARD TO 16 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 17 * FITNESS, IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE 18 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER 19 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF 20 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 21 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 22 */ 23 24#include "xftint.h" 25 26_X_HIDDEN FT_Library _XftFTlibrary; 27 28#define FT_Matrix_Equal(a,b) ((a)->xx == (b)->xx && \ 29 (a)->yy == (b)->yy && \ 30 (a)->xy == (b)->xy && \ 31 (a)->yx == (b)->yx) 32/* 33 * List of all open files (each face in a file is managed separately) 34 */ 35 36static XftFtFile *_XftFtFiles; 37static int XftMaxFreeTypeFiles = 5; 38 39static XftFtFile * 40_XftGetFile (const FcChar8 *file, int id) 41{ 42 XftFtFile *f; 43 44 if (!XftInitFtLibrary ()) 45 return NULL; 46 47 for (f = _XftFtFiles; f; f = f->next) 48 { 49 if (!strcmp (f->file, (const char *) file) && f->id == id) 50 { 51 ++f->ref; 52 if (XftDebug () & XFT_DBG_REF) 53 printf ("FontFile %s/%d matches existing (%d)\n", 54 file, id, f->ref); 55 return f; 56 } 57 } 58 f = malloc (sizeof (XftFtFile) + strlen ((const char *) file) + 1); 59 if (!f) 60 return NULL; 61 62 XftMemAlloc (XFT_MEM_FILE, sizeof (XftFtFile) + strlen ((const char *) file) + 1); 63 if (XftDebug () & XFT_DBG_REF) 64 printf ("FontFile %s/%d matches new\n", 65 file, id); 66 f->next = _XftFtFiles; 67 _XftFtFiles = f; 68 69 f->ref = 1; 70 71 f->file = (char *) (f+1); 72 strcpy (f->file, (const char *) file); 73 f->id = id; 74 75 f->lock = 0; 76 f->face = NULL; 77 f->xsize = 0; 78 f->ysize = 0; 79 f->matrix.xx = f->matrix.xy = f->matrix.yx = f->matrix.yy = 0; 80 return f; 81} 82 83static XftFtFile * 84_XftGetFaceFile (FT_Face face) 85{ 86 XftFtFile *f; 87 88 f = malloc (sizeof (XftFtFile)); 89 if (!f) 90 return NULL; 91 XftMemAlloc (XFT_MEM_FILE, sizeof (XftFtFile)); 92 f->next = NULL; 93 94 f->ref = 1; 95 96 f->file = NULL; 97 f->id = 0; 98 f->lock = 0; 99 f->face = face; 100 f->xsize = 0; 101 f->ysize = 0; 102 f->matrix.xx = f->matrix.xy = f->matrix.yx = f->matrix.yy = 0; 103 return f; 104} 105 106static int 107_XftNumFiles (void) 108{ 109 XftFtFile *f; 110 int count = 0; 111 for (f = _XftFtFiles; f; f = f->next) 112 if (f->face && !f->lock) 113 ++count; 114 return count; 115} 116 117static XftFtFile * 118_XftNthFile (int n) 119{ 120 XftFtFile *f; 121 int count = 0; 122 for (f = _XftFtFiles; f; f = f->next) 123 if (f->face && !f->lock) 124 if (count++ == n) 125 break; 126 return f; 127} 128 129static void 130_XftUncacheFiles (void) 131{ 132 int n; 133 XftFtFile *f; 134 while ((n = _XftNumFiles ()) > XftMaxFreeTypeFiles) 135 { 136 f = _XftNthFile (n ? (rand () % n) : 0); 137 if (f) 138 { 139 if (XftDebug() & XFT_DBG_REF) 140 printf ("Discard file %s/%d from cache\n", 141 f->file, f->id); 142 FT_Done_Face (f->face); 143 f->face = NULL; 144 } 145 } 146} 147 148static FT_Face 149_XftLockFile (XftFtFile *f) 150{ 151 ++f->lock; 152 if (!f->face) 153 { 154 if (XftDebug() & XFT_DBG_REF) 155 printf ("Loading file %s/%d\n", f->file, f->id); 156 if (FT_New_Face (_XftFTlibrary, f->file, f->id, &f->face)) 157 --f->lock; 158 159 f->xsize = 0; 160 f->ysize = 0; 161 f->matrix.xx = f->matrix.xy = f->matrix.yx = f->matrix.yy = 0; 162 _XftUncacheFiles (); 163 } 164 return f->face; 165} 166 167static void 168_XftLockError (const char *reason) 169{ 170 fprintf (stderr, "Xft: locking error %s\n", reason); 171} 172 173static void 174_XftUnlockFile (XftFtFile *f) 175{ 176 if (--f->lock < 0) 177 _XftLockError ("too many file unlocks"); 178} 179 180#define X_SIZE(face,i) ((face)->available_sizes[i].x_ppem) 181#define Y_SIZE(face,i) ((face)->available_sizes[i].y_ppem) 182 183_X_HIDDEN FcBool 184_XftSetFace (XftFtFile *f, FT_F26Dot6 xsize, FT_F26Dot6 ysize, FT_Matrix *matrix) 185{ 186 FT_Face face = f->face; 187 188 if (f->xsize != xsize || f->ysize != ysize) 189 { 190 if (XftDebug() & XFT_DBG_GLYPH) 191 printf ("Set face size to %dx%d (%dx%d)\n", 192 (int) (xsize >> 6), (int) (ysize >> 6), (int) xsize, (int) ysize); 193 /* 194 * Bitmap only faces must match exactly, so find the closest 195 * one (height dominant search) 196 */ 197 if (!(face->face_flags & FT_FACE_FLAG_SCALABLE)) 198 { 199 int i, best = 0; 200 201#define xft_abs(a) ((a) < 0 ? -(a) : (a)) 202#define dist(a,b) (xft_abs((a)-(b))) 203 204 for (i = 1; i < face->num_fixed_sizes; i++) 205 { 206 if (dist (ysize, Y_SIZE(face,i)) < 207 dist (ysize, Y_SIZE(face, best)) || 208 (dist (ysize, Y_SIZE(face, i)) == 209 dist (ysize, Y_SIZE(face, best)) && 210 dist (xsize, X_SIZE(face, i)) < 211 dist (xsize, X_SIZE(face, best)))) 212 { 213 best = i; 214 } 215 } 216 /* 217 * Freetype 2.1.7 and earlier used width/height 218 * for matching sizes in the BDF and PCF loaders. 219 * This has been fixed for 2.1.8. Because BDF and PCF 220 * files have but a single strike per file, we can 221 * simply try both sizes. 222 */ 223 if (FT_Set_Char_Size (face, face->available_sizes[best].x_ppem, 224 face->available_sizes[best].y_ppem, 0, 0) != 0 225 && 226 FT_Set_Char_Size (face, face->available_sizes[best].width << 6, 227 face->available_sizes[best].height << 6, 228 0, 0) != 0) 229 { 230 return False; 231 } 232 } 233 else 234 { 235 if (FT_Set_Char_Size (face, xsize, ysize, 0, 0)) 236 { 237 return False; 238 } 239 } 240 f->xsize = xsize; 241 f->ysize = ysize; 242 } 243 if (!FT_Matrix_Equal (&f->matrix, matrix)) 244 { 245 if (XftDebug() & XFT_DBG_GLYPH) 246 printf ("Set face matrix to (%g,%g,%g,%g)\n", 247 (double) matrix->xx / 0x10000, 248 (double) matrix->xy / 0x10000, 249 (double) matrix->yx / 0x10000, 250 (double) matrix->yy / 0x10000); 251 FT_Set_Transform (face, matrix, NULL); 252 f->matrix = *matrix; 253 } 254 return True; 255} 256 257static void 258_XftReleaseFile (XftFtFile *f) 259{ 260 XftFtFile **prev; 261 262 if (--f->ref != 0) 263 return; 264 if (f->lock) 265 _XftLockError ("Attempt to close locked file"); 266 if (f->file) 267 { 268 for (prev = &_XftFtFiles; *prev; prev = &(*prev)->next) 269 { 270 if (*prev == f) 271 { 272 *prev = f->next; 273 break; 274 } 275 } 276 if (f->face) 277 FT_Done_Face (f->face); 278 } 279 XftMemFree (XFT_MEM_FILE, 280 (sizeof (XftFtFile) + (f->file ? strlen (f->file) + 1 : 0))); 281 free (f); 282} 283 284/* 285 * Find a prime larger than the minimum reasonable hash size 286 */ 287 288static FcChar32 289_XftSqrt (FcChar32 a) 290{ 291 FcChar32 l, h, m; 292 293 l = 2; 294 h = a/2; 295 while ((h-l) > 1) 296 { 297 m = (h+l) >> 1; 298 if (m * m < a) 299 l = m; 300 else 301 h = m; 302 } 303 return h; 304} 305 306static FcBool 307_XftIsPrime (FcChar32 i) 308{ 309 FcChar32 l, t; 310 311 if (i < 2) 312 return FcFalse; 313 if ((i & 1) == 0) 314 { 315 if (i == 2) 316 return FcTrue; 317 return FcFalse; 318 } 319 l = _XftSqrt (i) + 1; 320 for (t = 3; t <= l; t += 2) 321 if (i % t == 0) 322 return FcFalse; 323 return FcTrue; 324} 325 326static FcChar32 327_XftHashSize (FcChar32 num_unicode) 328{ 329 /* at least 31.25 % extra space */ 330 FcChar32 hash = num_unicode + (num_unicode >> 2) + (num_unicode >> 4); 331 332 if ((hash & 1) == 0) 333 hash++; 334 while (!_XftIsPrime (hash)) 335 hash += 2; 336 return hash; 337} 338 339_X_EXPORT FT_Face 340XftLockFace (XftFont *public) 341{ 342 XftFontInt *font = (XftFontInt *) public; 343 XftFontInfo *fi = &font->info; 344 FT_Face face; 345 346 face = _XftLockFile (fi->file); 347 /* 348 * Make sure the face is usable at the requested size 349 */ 350 if (face && !_XftSetFace (fi->file, fi->xsize, fi->ysize, &fi->matrix)) 351 { 352 _XftUnlockFile (fi->file); 353 face = NULL; 354 } 355 return face; 356} 357 358_X_EXPORT void 359XftUnlockFace (XftFont *public) 360{ 361 XftFontInt *font = (XftFontInt *) public; 362 _XftUnlockFile (font->info.file); 363} 364 365static FcBool 366XftFontInfoFill (Display *dpy, _Xconst FcPattern *pattern, XftFontInfo *fi) 367{ 368 XftDisplayInfo *info = _XftDisplayInfoGet (dpy, True); 369 FcChar8 *filename; 370 int id, mid; 371 double dsize; 372 double aspect; 373 FcMatrix *font_matrix, fm1; 374 FcBool hinting, vertical_layout, autohint, global_advance; 375 int hint_style; 376 FcChar32 hash, *hashp; 377 FT_Face face; 378 int nhash; 379 FcBool bitmap; 380 381 if (!info) 382 return FcFalse; 383 384 /* 385 * Initialize the whole XftFontInfo so that padding doesn't interfere with 386 * hash or XftFontInfoEqual(). 387 */ 388 389 memset (fi, '\0', sizeof (*fi)); 390 391 /* 392 * Find the associated file 393 */ 394 switch (FcPatternGetString (pattern, FC_FILE, 0, &filename)) { 395 case FcResultNoMatch: 396 filename = NULL; 397 break; 398 case FcResultMatch: 399 break; 400 default: 401 goto bail0; 402 } 403 404 switch (FcPatternGetInteger (pattern, FC_INDEX, 0, &id)) { 405 case FcResultNoMatch: 406 id = 0; 407 break; 408 case FcResultMatch: 409 break; 410 default: 411 goto bail0; 412 } 413 414 if (filename) 415 fi->file = _XftGetFile (filename, id); 416 else if (FcPatternGetFTFace (pattern, FC_FT_FACE, 0, &face) == FcResultMatch 417 && face) 418 fi->file = _XftGetFaceFile (face); 419 if (!fi->file) 420 goto bail0; 421 422 /* 423 * Compute pixel size 424 */ 425 if (FcPatternGetDouble (pattern, FC_PIXEL_SIZE, 0, &dsize) != FcResultMatch) 426 goto bail1; 427 428 if (FcPatternGetDouble (pattern, FC_ASPECT, 0, &aspect) != FcResultMatch) 429 aspect = 1.0; 430 431 fi->ysize = (FT_F26Dot6) (dsize * 64.0); 432 fi->xsize = (FT_F26Dot6) (dsize * aspect * 64.0); 433 434 if (XftDebug() & XFT_DBG_OPEN) 435 printf ("XftFontInfoFill: %s: %d (%g pixels)\n", 436 (filename ? filename : (const FcChar8 *) "<none>"), id, dsize); 437 /* 438 * Get antialias value 439 */ 440 switch (FcPatternGetBool (pattern, FC_ANTIALIAS, 0, &fi->antialias)) { 441 case FcResultNoMatch: 442 fi->antialias = True; 443 break; 444 case FcResultMatch: 445 break; 446 default: 447 goto bail1; 448 } 449 450 /* 451 * Get rgba value 452 */ 453 switch (FcPatternGetInteger (pattern, FC_RGBA, 0, &fi->rgba)) { 454 case FcResultNoMatch: 455 fi->rgba = FC_RGBA_UNKNOWN; 456 break; 457 case FcResultMatch: 458 break; 459 default: 460 goto bail1; 461 } 462 463 /* 464 * Get lcd_filter value 465 */ 466 switch (FcPatternGetInteger (pattern, FC_LCD_FILTER, 0, &fi->lcd_filter)) { 467 case FcResultNoMatch: 468 fi->lcd_filter = FC_LCD_DEFAULT; 469 break; 470 case FcResultMatch: 471 break; 472 default: 473 goto bail1; 474 } 475 476 /* 477 * Get matrix and transform values 478 */ 479 switch (FcPatternGetMatrix (pattern, FC_MATRIX, 0, &font_matrix)) { 480 case FcResultNoMatch: 481 fi->matrix.xx = fi->matrix.yy = 0x10000; 482 fi->matrix.xy = fi->matrix.yx = 0; 483 break; 484 case FcResultMatch: 485 fi->matrix.xx = (FT_Fixed)(0x10000L * font_matrix->xx); 486 fi->matrix.yy = (FT_Fixed)(0x10000L * font_matrix->yy); 487 fi->matrix.xy = (FT_Fixed)(0x10000L * font_matrix->xy); 488 fi->matrix.yx = (FT_Fixed)(0x10000L * font_matrix->yx); 489 break; 490 default: 491 goto bail1; 492 } 493 494 mid = 1; 495 while (FcPatternGetMatrix (pattern, FC_MATRIX, mid, &font_matrix) == FcResultMatch) { 496 FcMatrixInit(&fm1); 497#define PreScale(value) ((double) (value) / (double) 0x10000L) 498 fm1.xx = PreScale(fi->matrix.xx); 499 fm1.yy = PreScale(fi->matrix.yy); 500 fm1.xy = PreScale(fi->matrix.xy); 501 fm1.yx = PreScale(fi->matrix.yx); 502 FcMatrixMultiply(&fm1, font_matrix, &fm1); 503 fi->matrix.xx = (FT_Fixed)(0x10000L * fm1.xx); 504 fi->matrix.yy = (FT_Fixed)(0x10000L * fm1.yy); 505 fi->matrix.xy = (FT_Fixed)(0x10000L * fm1.xy); 506 fi->matrix.yx = (FT_Fixed)(0x10000L * fm1.yx); 507 mid++; 508 } 509 510 fi->transform = (fi->matrix.xx != 0x10000 || fi->matrix.xy != 0 || 511 fi->matrix.yx != 0 || fi->matrix.yy != 0x10000); 512 513 /* 514 * Get render value, set to false if no Render extension present 515 */ 516 if (info->hasRender) 517 { 518 switch (FcPatternGetBool (pattern, XFT_RENDER, 0, &fi->render)) { 519 case FcResultTypeMismatch: 520 /* 521 * Fontconfig no longer supports xft's custom values in 522 * text patterns, so any name specifying render:true or 523 * render:false will have an invalid type in the resulting 524 * pattern. Just ignore that case so that the app doesn't 525 * just fail 526 */ 527 /* fall through ... */ 528 case FcResultNoMatch: 529 fi->render = info->hasRender; 530 break; 531 case FcResultMatch: 532 break; 533 default: 534 goto bail1; 535 } 536 } 537 else 538 fi->render = FcFalse; 539 540 /* 541 * Compute glyph load flags 542 */ 543 fi->load_flags = FT_LOAD_DEFAULT | FT_LOAD_COLOR; 544 545#ifndef XFT_EMBEDDED_BITMAP 546#define XFT_EMBEDDED_BITMAP "embeddedbitmap" 547#endif 548 549 switch (FcPatternGetBool (pattern, XFT_EMBEDDED_BITMAP, 0, &bitmap)) { 550 case FcResultNoMatch: 551 bitmap = FcFalse; 552 break; 553 case FcResultMatch: 554 break; 555 default: 556 goto bail1; 557 } 558 559 /* disable bitmaps when anti-aliasing or transforming glyphs */ 560 if ((!bitmap && fi->antialias) || fi->transform) 561 fi->load_flags |= FT_LOAD_NO_BITMAP; 562 563 /* disable hinting if requested */ 564 switch (FcPatternGetBool (pattern, FC_HINTING, 0, &hinting)) { 565 case FcResultNoMatch: 566 hinting = FcTrue; 567 break; 568 case FcResultMatch: 569 break; 570 default: 571 goto bail1; 572 } 573 574 switch (FcPatternGetBool (pattern, FC_EMBOLDEN, 0, &fi->embolden)) { 575 case FcResultNoMatch: 576 fi->embolden = FcFalse; 577 break; 578 case FcResultMatch: 579 break; 580 default: 581 goto bail1; 582 } 583 584 switch (FcPatternGetInteger (pattern, FC_HINT_STYLE, 0, &hint_style)) { 585 case FcResultNoMatch: 586 hint_style = FC_HINT_FULL; 587 break; 588 case FcResultMatch: 589 break; 590 default: 591 goto bail1; 592 } 593 594 if (!hinting 595 || hint_style == FC_HINT_NONE 596 ) 597 { 598 fi->load_flags |= FT_LOAD_NO_HINTING; 599 } 600 601 /* Figure out the load target, which modifies the hinting 602 * behavior of FreeType based on the intended use of the glyphs. 603 */ 604 if (fi->antialias) 605 { 606 if (FC_HINT_NONE < hint_style && hint_style < FC_HINT_FULL) 607 { 608 fi->load_flags |= FT_LOAD_TARGET_LIGHT; 609 } 610 else 611 { 612 /* autohinter will snap stems to integer widths, when 613 * the LCD targets are used. 614 */ 615 switch (fi->rgba) { 616 case FC_RGBA_RGB: 617 case FC_RGBA_BGR: 618 fi->load_flags |= FT_LOAD_TARGET_LCD; 619 break; 620 case FC_RGBA_VRGB: 621 case FC_RGBA_VBGR: 622 fi->load_flags |= FT_LOAD_TARGET_LCD_V; 623 break; 624 } 625 } 626 } 627 else 628 fi->load_flags |= FT_LOAD_TARGET_MONO; 629 630 /* set vertical layout if requested */ 631 switch (FcPatternGetBool (pattern, FC_VERTICAL_LAYOUT, 0, &vertical_layout)) { 632 case FcResultNoMatch: 633 vertical_layout = FcFalse; 634 break; 635 case FcResultMatch: 636 break; 637 default: 638 goto bail1; 639 } 640 641 if (vertical_layout) 642 fi->load_flags |= FT_LOAD_VERTICAL_LAYOUT; 643 644 /* force autohinting if requested */ 645 switch (FcPatternGetBool (pattern, FC_AUTOHINT, 0, &autohint)) { 646 case FcResultNoMatch: 647 autohint = FcFalse; 648 break; 649 case FcResultMatch: 650 break; 651 default: 652 goto bail1; 653 } 654 655 if (autohint) 656 fi->load_flags |= FT_LOAD_FORCE_AUTOHINT; 657 658 /* disable global advance width (for broken DynaLab TT CJK fonts) */ 659 switch (FcPatternGetBool (pattern, FC_GLOBAL_ADVANCE, 0, &global_advance)) { 660 case FcResultNoMatch: 661 global_advance = FcTrue; 662 break; 663 case FcResultMatch: 664 break; 665 default: 666 goto bail1; 667 } 668 669 if (!global_advance) 670 fi->load_flags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH; 671 672 /* 673 * Get requested spacing value 674 */ 675 switch (FcPatternGetInteger (pattern, FC_SPACING, 0, &fi->spacing)) { 676 case FcResultNoMatch: 677 fi->spacing = FC_PROPORTIONAL; 678 break; 679 case FcResultMatch: 680 break; 681 default: 682 goto bail1; 683 } 684 685 /* 686 * Check for minspace 687 */ 688 689 switch (FcPatternGetBool (pattern, FC_MINSPACE, 0, &fi->minspace)) { 690 case FcResultNoMatch: 691 fi->minspace = FcFalse; 692 break; 693 case FcResultMatch: 694 break; 695 default: 696 goto bail1; 697 } 698 /* 699 * Check for fixed pixel spacing 700 */ 701 switch (FcPatternGetInteger (pattern, FC_CHAR_WIDTH, 0, &fi->char_width)) { 702 case FcResultNoMatch: 703 fi->char_width = 0; 704 break; 705 case FcResultMatch: 706 if (fi->char_width) 707 fi->spacing = FC_MONO; 708 break; 709 default: 710 goto bail1; 711 } 712 713 /* 714 * Step over hash value in the structure 715 */ 716 hash = 0; 717 hashp = (FcChar32 *) fi + 1; 718 nhash = (sizeof (XftFontInfo) / sizeof (FcChar32)) - 1; 719 720 while (nhash--) 721 hash += *hashp++; 722 fi->hash = hash; 723 724 /* 725 * All done 726 */ 727 return FcTrue; 728 729bail1: 730 _XftReleaseFile (fi->file); 731 fi->file = NULL; 732bail0: 733 return FcFalse; 734} 735 736static void 737XftFontInfoEmpty (Display *dpy _X_UNUSED, XftFontInfo *fi) 738{ 739 if (fi->file) 740 _XftReleaseFile (fi->file); 741} 742 743XftFontInfo * 744XftFontInfoCreate (Display *dpy, _Xconst FcPattern *pattern) 745{ 746 XftFontInfo *fi = malloc (sizeof (XftFontInfo)); 747 748 if (!fi) 749 return NULL; 750 751 if (!XftFontInfoFill (dpy, pattern, fi)) 752 { 753 free (fi); 754 fi = NULL; 755 } 756 XftMemAlloc (XFT_MEM_FONT, sizeof (XftFontInfo)); 757 return fi; 758} 759 760_X_EXPORT void 761XftFontInfoDestroy (Display *dpy, XftFontInfo *fi) 762{ 763 XftFontInfoEmpty (dpy, fi); 764 XftMemFree (XFT_MEM_FONT, sizeof (XftFontInfo)); 765 free (fi); 766} 767 768_X_EXPORT FcChar32 769XftFontInfoHash (_Xconst XftFontInfo *fi) 770{ 771 return fi->hash; 772} 773 774_X_EXPORT FcBool 775XftFontInfoEqual (_Xconst XftFontInfo *a, _Xconst XftFontInfo *b) 776{ 777 return memcmp ((const void *) a, (const void *) b, sizeof (XftFontInfo)) == 0; 778} 779 780_X_EXPORT XftFont * 781XftFontOpenInfo (Display *dpy, 782 FcPattern *pattern, 783 XftFontInfo *fi) 784{ 785 XftDisplayInfo *info = _XftDisplayInfoGet (dpy, True); 786 FT_Face face; 787 XftFont **bucket; 788 XftFontInt *font; 789 XRenderPictFormat *format; 790 FcCharSet *charset; 791 FcChar32 num_unicode; 792 FcChar32 hash_value; 793 FcChar32 rehash_value; 794 FcBool antialias; 795 FcBool color; 796 int max_glyph_memory; 797 size_t alloc_size; 798 int ascent, descent, height; 799 int i; 800 FT_UInt num_glyphs; 801 802 if (!info) 803 return NULL; 804 /* 805 * Find a matching previously opened font 806 */ 807 bucket = &info->fontHash[fi->hash % XFT_NUM_FONT_HASH]; 808 for (font = (XftFontInt *) *bucket; font; font = (XftFontInt *) font->hash_next) 809 if (XftFontInfoEqual (&font->info, fi)) 810 { 811 if (!font->ref++) 812 --info->num_unref_fonts; 813 FcPatternDestroy (pattern); 814 return &font->public; 815 } 816 817 /* 818 * No existing font, create another. 819 */ 820 821 if (XftDebug () & XFT_DBG_CACHE) 822 printf ("New font %s/%d size %dx%d\n", 823 fi->file->file, fi->file->id, 824 (int) fi->xsize >> 6, (int) fi->ysize >> 6); 825 826 if (FcPatternGetInteger (pattern, XFT_MAX_GLYPH_MEMORY, 0, 827 &max_glyph_memory) != FcResultMatch) 828 max_glyph_memory = XFT_FONT_MAX_GLYPH_MEMORY; 829 830 face = _XftLockFile (fi->file); 831 if (!face) 832 goto bail0; 833 834 if (!_XftSetFace (fi->file, fi->xsize, fi->ysize, &fi->matrix)) 835 goto bail1; 836 837 /* 838 * Get the set of Unicode codepoints covered by the font. 839 * If the incoming pattern doesn't provide this data, go 840 * off and compute it. Yes, this is expensive, but it's 841 * required to map Unicode to glyph indices. 842 */ 843 if (FcPatternGetCharSet (pattern, FC_CHARSET, 0, &charset) == FcResultMatch) 844 charset = FcCharSetCopy (charset); 845 else 846 charset = FcFreeTypeCharSet (face, FcConfigGetBlanks (NULL)); 847 848 antialias = fi->antialias; 849 if (!(face->face_flags & FT_FACE_FLAG_SCALABLE)) 850 antialias = FcFalse; 851 852#ifdef FT_HAS_SVG 853 color = (FT_HAS_COLOR(face) && !FT_HAS_SVG(face)) ? FcTrue : FcFalse; 854#else 855 color = FT_HAS_COLOR(face) ? FcTrue : FcFalse; 856#endif 857 858 /* 859 * Find the appropriate picture format 860 */ 861 if (fi->render) 862 { 863 if (color) 864 { 865 format = XRenderFindStandardFormat (dpy, PictStandardARGB32); 866 } 867 else if (antialias) 868 { 869 switch (fi->rgba) { 870 case FC_RGBA_RGB: 871 case FC_RGBA_BGR: 872 case FC_RGBA_VRGB: 873 case FC_RGBA_VBGR: 874 format = XRenderFindStandardFormat (dpy, PictStandardARGB32); 875 break; 876 default: 877 format = XRenderFindStandardFormat (dpy, PictStandardA8); 878 break; 879 } 880 } 881 else 882 { 883 format = XRenderFindStandardFormat (dpy, PictStandardA1); 884 } 885 886 if (!format) 887 goto bail2; 888 } 889 else 890 format = NULL; 891 892 if (charset) 893 { 894 num_unicode = FcCharSetCount (charset); 895 hash_value = _XftHashSize (num_unicode); 896 rehash_value = hash_value - 2; 897 } 898 else 899 { 900 hash_value = 0; 901 rehash_value = 0; 902 } 903 904 /* 905 * Sometimes the glyphs are numbered 1..n, other times 0..n-1, 906 * accept either numbering scheme by making room in the table 907 */ 908 num_glyphs = (FT_UInt)face->num_glyphs + 1; 909 alloc_size = (sizeof (XftFontInt) + 910 (size_t)num_glyphs * sizeof (XftGlyph *) + 911 hash_value * sizeof (XftUcsHash)); 912 font = malloc (alloc_size); 913 914 if (!font) 915 goto bail2; 916 917 XftMemAlloc (XFT_MEM_FONT, alloc_size); 918 919 /* 920 * Public fields 921 */ 922 if (fi->transform) 923 { 924 FT_Vector vector; 925 926 vector.x = 0; 927 vector.y = face->size->metrics.descender; 928 FT_Vector_Transform (&vector, &fi->matrix); 929 descent = (int)(-(vector.y >> 6)); 930 931 vector.x = 0; 932 vector.y = face->size->metrics.ascender; 933 FT_Vector_Transform (&vector, &fi->matrix); 934 ascent = (int)(vector.y >> 6); 935 936 if (fi->minspace) 937 height = ascent + descent; 938 else 939 { 940 vector.x = 0; 941 vector.y = face->size->metrics.height; 942 FT_Vector_Transform (&vector, &fi->matrix); 943 height = (int)(vector.y >> 6); 944 } 945 } 946 else 947 { 948 descent = -(int)(face->size->metrics.descender >> 6); 949 ascent = (int)(face->size->metrics.ascender >> 6); 950 if (fi->minspace) 951 height = ascent + descent; 952 else 953 height = (int)(face->size->metrics.height >> 6); 954 } 955 font->public.ascent = ascent; 956 font->public.descent = descent; 957 font->public.height = height; 958 959 if (fi->char_width) 960 font->public.max_advance_width = fi->char_width; 961 else 962 { 963 if (fi->transform) 964 { 965 FT_Vector vector; 966 vector.x = face->size->metrics.max_advance; 967 vector.y = 0; 968 FT_Vector_Transform (&vector, &fi->matrix); 969 font->public.max_advance_width = (int)(vector.x >> 6); 970 } 971 else 972 font->public.max_advance_width = (int)(face->size->metrics.max_advance >> 6); 973 } 974 font->public.charset = charset; 975 font->public.pattern = pattern; 976 977 /* 978 * Management fields 979 */ 980 font->ref = 1; 981 982 font->next = info->fonts; 983 info->fonts = &font->public; 984 985 font->hash_next = *bucket; 986 *bucket = &font->public; 987 988 /* 989 * Copy the info over 990 */ 991 font->info = *fi; 992 /* 993 * reset the antialias field. It can't 994 * be set correctly until the font is opened, 995 * which doesn't happen in XftFontInfoFill 996 */ 997 font->info.antialias = antialias; 998 999 /* 1000 * Set color value, which is only known once the 1001 * font was loaded 1002 */ 1003 font->info.color = color; 1004 1005 /* 1006 * bump XftFile reference count 1007 */ 1008 font->info.file->ref++; 1009 1010 /* 1011 * Per glyph information 1012 */ 1013 font->glyphs = (XftGlyph **) (font + 1); 1014 memset (font->glyphs, '\0', (size_t)num_glyphs * sizeof (XftGlyph *)); 1015 font->num_glyphs = num_glyphs; 1016 /* 1017 * Memory-usage tracking 1018 */ 1019 font->newest = FT_UINT_MAX; 1020 font->total_inuse = 0; 1021 /* 1022 * Unicode hash table information 1023 */ 1024 font->hash_table = (XftUcsHash *) (font->glyphs + font->num_glyphs); 1025 for (i = 0; (FcChar32) i < hash_value; i++) 1026 { 1027 font->hash_table[i].ucs4 = ((FcChar32) ~0); 1028 font->hash_table[i].glyph = 0; 1029 } 1030 font->hash_value = (int)hash_value; 1031 font->rehash_value = (int)rehash_value; 1032 /* 1033 * X specific fields 1034 */ 1035 font->glyphset = 0; 1036 font->format = format; 1037 1038 /* 1039 * Glyph memory management fields 1040 */ 1041 font->glyph_memory = 0; 1042 font->max_glyph_memory = (unsigned long)max_glyph_memory; 1043 font->track_mem_usage = info->track_mem_usage; 1044 font->use_free_glyphs = info->use_free_glyphs; 1045 font->sizeof_glyph = (font->track_mem_usage 1046 ? sizeof(XftGlyphUsage) 1047 : sizeof(XftGlyph)); 1048 1049 _XftUnlockFile (fi->file); 1050 1051 return &font->public; 1052 1053bail2: 1054 FcCharSetDestroy (charset); 1055bail1: 1056 _XftUnlockFile (fi->file); 1057bail0: 1058 return NULL; 1059} 1060 1061_X_EXPORT XftFont * 1062XftFontOpenPattern (Display *dpy, FcPattern *pattern) 1063{ 1064 XftFontInfo info; 1065 XftFont *font; 1066 1067 if (!XftFontInfoFill (dpy, pattern, &info)) 1068 return NULL; 1069 1070 font = XftFontOpenInfo (dpy, pattern, &info); 1071 XftFontInfoEmpty (dpy, &info); 1072 return font; 1073} 1074 1075_X_EXPORT XftFont * 1076XftFontCopy (Display *dpy _X_UNUSED, XftFont *public) 1077{ 1078 XftFontInt *font = (XftFontInt *) public; 1079 1080 font->ref++; 1081 return public; 1082} 1083 1084static void 1085XftFontDestroy (Display *dpy, XftFont *public) 1086{ 1087 XftDisplayInfo *info = _XftDisplayInfoGet (dpy, False); 1088 XftFontInt *font = (XftFontInt *) public; 1089 FT_UInt i; 1090 1091 /* note reduction in memory use */ 1092 if (info) 1093 info->glyph_memory -= font->glyph_memory; 1094 /* Clean up the info */ 1095 XftFontInfoEmpty (dpy, &font->info); 1096 /* Free the glyphset */ 1097 if (font->glyphset) 1098 XRenderFreeGlyphSet (dpy, font->glyphset); 1099 /* Free the glyphs */ 1100 for (i = 0; i < font->num_glyphs; i++) 1101 { 1102 XftGlyph *xftg = font->glyphs[i]; 1103 if (xftg) 1104 { 1105 if (xftg->bitmap) 1106 free (xftg->bitmap); 1107 free (xftg); 1108 } 1109 } 1110 1111 /* Free the pattern and the charset */ 1112 FcPatternDestroy (font->public.pattern); 1113 FcCharSetDestroy (font->public.charset); 1114 1115 /* Finally, free the font structure */ 1116 XftMemFree (XFT_MEM_FONT, (sizeof (XftFontInt) + 1117 (size_t)font->num_glyphs * sizeof (XftGlyph *) + 1118 (size_t)font->hash_value * sizeof (XftUcsHash))); 1119 free (font); 1120} 1121 1122static XftFont * 1123XftFontFindNthUnref (XftDisplayInfo *info, int n) 1124{ 1125 XftFont *public; 1126 XftFontInt *font; 1127 1128 for (public = info->fonts; public; public = font->next) 1129 { 1130 font = (XftFontInt*) public; 1131 if (!font->ref && !n--) 1132 break; 1133 } 1134 return public; 1135} 1136 1137_X_HIDDEN void 1138XftFontManageMemory (Display *dpy) 1139{ 1140 XftDisplayInfo *info = _XftDisplayInfoGet (dpy, False); 1141 XftFont **prev; 1142 XftFont *public; 1143 XftFontInt *font; 1144 1145 if (!info) 1146 return; 1147 while (info->num_unref_fonts > info->max_unref_fonts) 1148 { 1149 public = XftFontFindNthUnref (info, rand() % info->num_unref_fonts); 1150 font = (XftFontInt *) public; 1151 1152 if (XftDebug () & XFT_DBG_CACHE) 1153 printf ("freeing unreferenced font %s/%d size %dx%d\n", 1154 font->info.file->file, font->info.file->id, 1155 (int) font->info.xsize >> 6, (int) font->info.ysize >> 6); 1156 1157 /* Unhook from display list */ 1158 for (prev = &info->fonts; *prev; prev = &(*(XftFontInt **) prev)->next) 1159 { 1160 if (*prev == public) 1161 { 1162 *prev = font->next; 1163 break; 1164 } 1165 } 1166 /* Unhook from hash list */ 1167 for (prev = &info->fontHash[font->info.hash % XFT_NUM_FONT_HASH]; 1168 *prev; 1169 prev = &(*(XftFontInt **) prev)->hash_next) 1170 { 1171 if (*prev == public) 1172 { 1173 *prev = font->hash_next; 1174 break; 1175 } 1176 } 1177 /* Destroy the font */ 1178 XftFontDestroy (dpy, public); 1179 --info->num_unref_fonts; 1180 } 1181} 1182 1183_X_EXPORT void 1184XftFontClose (Display *dpy, XftFont *public) 1185{ 1186 XftDisplayInfo *info = _XftDisplayInfoGet (dpy, False); 1187 XftFontInt *font = (XftFontInt *) public; 1188 1189 if (--font->ref != 0) 1190 return; 1191 1192 if (info) 1193 { 1194 ++info->num_unref_fonts; 1195 XftFontManageMemory (dpy); 1196 } 1197 else 1198 { 1199 XftFontDestroy (dpy, public); 1200 } 1201} 1202 1203_X_EXPORT FcBool 1204XftInitFtLibrary (void) 1205{ 1206 if (_XftFTlibrary) 1207 return FcTrue; 1208 if (FT_Init_FreeType (&_XftFTlibrary)) 1209 return FcFalse; 1210 return FcTrue; 1211} 1212