fcmatch.c revision 18bd4a06
1/* 2 * fontconfig/src/fcmatch.c 3 * 4 * Copyright © 2000 Keith Packard 5 * 6 * Permission to use, copy, modify, distribute, and sell this software and its 7 * documentation for any purpose is hereby granted without fee, provided that 8 * the above copyright notice appear in all copies and that both that 9 * copyright notice and this permission notice appear in supporting 10 * documentation, and that the name of the author(s) not be used in 11 * advertising or publicity pertaining to distribution of the software without 12 * specific, written prior permission. The authors make no 13 * representations about the suitability of this software for any purpose. It 14 * is provided "as is" without express or implied warranty. 15 * 16 * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 17 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 18 * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR 19 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 20 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 21 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 22 * PERFORMANCE OF THIS SOFTWARE. 23 */ 24 25#include "fcint.h" 26#include <float.h> 27 28 29static double 30FcCompareNumber (FcValue *value1, FcValue *value2) 31{ 32 double v1, v2, v; 33 34 switch ((int) value1->type) { 35 case FcTypeInteger: 36 v1 = (double) value1->u.i; 37 break; 38 case FcTypeDouble: 39 v1 = value1->u.d; 40 break; 41 default: 42 return -1.0; 43 } 44 switch ((int) value2->type) { 45 case FcTypeInteger: 46 v2 = (double) value2->u.i; 47 break; 48 case FcTypeDouble: 49 v2 = value2->u.d; 50 break; 51 default: 52 return -1.0; 53 } 54 v = v2 - v1; 55 if (v < 0) 56 v = -v; 57 return v; 58} 59 60static double 61FcCompareString (FcValue *v1, FcValue *v2) 62{ 63 return (double) FcStrCmpIgnoreCase (FcValueString(v1), FcValueString(v2)) != 0; 64} 65 66static double 67FcCompareFamily (FcValue *v1, FcValue *v2) 68{ 69 /* rely on the guarantee in FcPatternObjectAddWithBinding that 70 * families are always FcTypeString. */ 71 const FcChar8* v1_string = FcValueString(v1); 72 const FcChar8* v2_string = FcValueString(v2); 73 74 if (FcToLower(*v1_string) != FcToLower(*v2_string) && 75 *v1_string != ' ' && *v2_string != ' ') 76 return 1.0; 77 78 return (double) FcStrCmpIgnoreBlanksAndCase (v1_string, v2_string) != 0; 79} 80 81static double 82FcComparePostScript (FcValue *v1, FcValue *v2) 83{ 84 const FcChar8 *v1_string = FcValueString (v1); 85 const FcChar8 *v2_string = FcValueString (v2); 86 int n; 87 size_t len; 88 89 if (FcToLower (*v1_string) != FcToLower (*v2_string) && 90 *v1_string != ' ' && *v2_string != ' ') 91 return 1.0; 92 93 n = FcStrMatchIgnoreCaseAndDelims (v1_string, v2_string, (const FcChar8 *)" -"); 94 len = strlen ((const char *)v1_string); 95 96 return (double)(len - n) / (double)len; 97} 98 99static double 100FcCompareLang (FcValue *v1, FcValue *v2) 101{ 102 FcLangResult result; 103 FcValue value1 = FcValueCanonicalize(v1), value2 = FcValueCanonicalize(v2); 104 105 switch ((int) value1.type) { 106 case FcTypeLangSet: 107 switch ((int) value2.type) { 108 case FcTypeLangSet: 109 result = FcLangSetCompare (value1.u.l, value2.u.l); 110 break; 111 case FcTypeString: 112 result = FcLangSetHasLang (value1.u.l, 113 value2.u.s); 114 break; 115 default: 116 return -1.0; 117 } 118 break; 119 case FcTypeString: 120 switch ((int) value2.type) { 121 case FcTypeLangSet: 122 result = FcLangSetHasLang (value2.u.l, value1.u.s); 123 break; 124 case FcTypeString: 125 result = FcLangCompare (value1.u.s, 126 value2.u.s); 127 break; 128 default: 129 return -1.0; 130 } 131 break; 132 default: 133 return -1.0; 134 } 135 switch (result) { 136 case FcLangEqual: 137 return 0; 138 case FcLangDifferentCountry: 139 return 1; 140 case FcLangDifferentLang: 141 default: 142 return 2; 143 } 144} 145 146static double 147FcCompareBool (FcValue *v1, FcValue *v2) 148{ 149 if (v2->type != FcTypeBool || v1->type != FcTypeBool) 150 return -1.0; 151 return (double) v2->u.b != v1->u.b; 152} 153 154static double 155FcCompareCharSet (FcValue *v1, FcValue *v2) 156{ 157 return (double) FcCharSetSubtractCount (FcValueCharSet(v1), FcValueCharSet(v2)); 158} 159 160static double 161FcCompareSize (FcValue *value1, FcValue *value2) 162{ 163 double v1, v2, v; 164 165 switch ((int) value1->type) { 166 case FcTypeInteger: 167 v1 = value1->u.i; 168 break; 169 case FcTypeDouble: 170 v1 = value1->u.d; 171 break; 172 default: 173 return -1; 174 } 175 switch ((int) value2->type) { 176 case FcTypeInteger: 177 v2 = value2->u.i; 178 break; 179 case FcTypeDouble: 180 v2 = value2->u.d; 181 break; 182 default: 183 return -1; 184 } 185 if (v2 == 0) 186 return 0; 187 v = v2 - v1; 188 if (v < 0) 189 v = -v; 190 return v; 191} 192 193static double 194FcCompareSizeRange (FcValue *v1, FcValue *v2) 195{ 196 FcValue value1 = FcValueCanonicalize (v1); 197 FcValue value2 = FcValueCanonicalize (v2); 198 FcRange *r1 = NULL, *r2 = NULL; 199 double ret = -1.0; 200 201 switch ((int) value1.type) { 202 case FcTypeDouble: 203 r1 = FcRangeCreateDouble (value1.u.d, value1.u.d); 204 break; 205 case FcTypeRange: 206 r1 = FcRangeCopy (value1.u.r); 207 break; 208 default: 209 goto bail; 210 } 211 switch ((int) value2.type) { 212 case FcTypeDouble: 213 r2 = FcRangeCreateDouble (value2.u.d, value2.u.d); 214 break; 215 case FcTypeRange: 216 r2 = FcRangeCopy (value2.u.r); 217 break; 218 default: 219 goto bail; 220 } 221 222 if (FcRangeIsInRange (r1, r2)) 223 ret = 0.0; 224 else 225 ret = FC_MIN (fabs (r1->end - r2->begin), fabs (r1->begin - r2->end)); 226 227bail: 228 if (r1) 229 FcRangeDestroy (r1); 230 if (r2) 231 FcRangeDestroy (r2); 232 233 return ret; 234} 235 236static double 237FcCompareFilename (FcValue *v1, FcValue *v2) 238{ 239 const FcChar8 *s1 = FcValueString (v1), *s2 = FcValueString (v2); 240 if (FcStrCmp (s1, s2) == 0) 241 return 0.0; 242 else if (FcStrCmpIgnoreCase (s1, s2) == 0) 243 return 1.0; 244 else if (FcStrGlobMatch (s1, s2)) 245 return 2.0; 246 else 247 return 3.0; 248} 249 250 251/* Define priorities to -1 for objects that don't have a compare function. */ 252 253#define PRI_NULL(n) \ 254 PRI_ ## n ## _STRONG = -1, \ 255 PRI_ ## n ## _WEAK = -1, 256#define PRI1(n) 257#define PRI_FcCompareFamily(n) PRI1(n) 258#define PRI_FcCompareString(n) PRI1(n) 259#define PRI_FcCompareNumber(n) PRI1(n) 260#define PRI_FcCompareSize(n) PRI1(n) 261#define PRI_FcCompareBool(n) PRI1(n) 262#define PRI_FcCompareFilename(n) PRI1(n) 263#define PRI_FcCompareCharSet(n) PRI1(n) 264#define PRI_FcCompareLang(n) PRI1(n) 265#define PRI_FcComparePostScript(n) PRI1(n) 266#define PRI_FcCompareSizeRange(n) PRI1(n) 267 268#define FC_OBJECT(NAME, Type, Cmp) PRI_##Cmp(NAME) 269 270typedef enum _FcMatcherPriorityDummy { 271#include "fcobjs.h" 272} FcMatcherPriorityDummy; 273 274#undef FC_OBJECT 275 276 277/* Canonical match priority order. */ 278 279#undef PRI1 280#define PRI1(n) \ 281 PRI_ ## n, \ 282 PRI_ ## n ## _STRONG = PRI_ ## n, \ 283 PRI_ ## n ## _WEAK = PRI_ ## n 284 285typedef enum _FcMatcherPriority { 286 PRI1(FILE), 287 PRI1(FONTFORMAT), 288 PRI1(SCALABLE), 289 PRI1(COLOR), 290 PRI1(FOUNDRY), 291 PRI1(CHARSET), 292 PRI_FAMILY_STRONG, 293 PRI_POSTSCRIPT_NAME_STRONG, 294 PRI1(LANG), 295 PRI_FAMILY_WEAK, 296 PRI_POSTSCRIPT_NAME_WEAK, 297 PRI1(SYMBOL), 298 PRI1(SPACING), 299 PRI1(SIZE), 300 PRI1(PIXEL_SIZE), 301 PRI1(STYLE), 302 PRI1(SLANT), 303 PRI1(WEIGHT), 304 PRI1(WIDTH), 305 PRI1(DECORATIVE), 306 PRI1(ANTIALIAS), 307 PRI1(RASTERIZER), 308 PRI1(OUTLINE), 309 PRI1(FONTVERSION), 310 PRI_END 311} FcMatcherPriority; 312 313#undef PRI1 314 315typedef struct _FcMatcher { 316 FcObject object; 317 double (*compare) (FcValue *value1, FcValue *value2); 318 int strong, weak; 319} FcMatcher; 320 321/* 322 * Order is significant, it defines the precedence of 323 * each value, earlier values are more significant than 324 * later values 325 */ 326#define FC_OBJECT(NAME, Type, Cmp) { FC_##NAME##_OBJECT, Cmp, PRI_##NAME##_STRONG, PRI_##NAME##_WEAK }, 327static const FcMatcher _FcMatchers [] = { 328 { FC_INVALID_OBJECT, NULL, -1, -1 }, 329#include "fcobjs.h" 330}; 331#undef FC_OBJECT 332 333static const FcMatcher* 334FcObjectToMatcher (FcObject object, 335 FcBool include_lang) 336{ 337 if (include_lang) 338 { 339 switch (object) { 340 case FC_FAMILYLANG_OBJECT: 341 case FC_STYLELANG_OBJECT: 342 case FC_FULLNAMELANG_OBJECT: 343 object = FC_LANG_OBJECT; 344 break; 345 } 346 } 347 if (object > FC_MAX_BASE_OBJECT || 348 !_FcMatchers[object].compare || 349 _FcMatchers[object].strong == -1 || 350 _FcMatchers[object].weak == -1) 351 return NULL; 352 353 return _FcMatchers + object; 354} 355 356static FcBool 357FcCompareValueList (FcObject object, 358 const FcMatcher *match, 359 FcValueListPtr v1orig, /* pattern */ 360 FcValueListPtr v2orig, /* target */ 361 FcValue *bestValue, 362 double *value, 363 int *n, 364 FcResult *result) 365{ 366 FcValueListPtr v1, v2; 367 double v, best, bestStrong, bestWeak; 368 int j, k, pos = 0; 369 370 if (!match) 371 { 372 if (bestValue) 373 *bestValue = FcValueCanonicalize(&v2orig->value); 374 if (n) 375 *n = 0; 376 return FcTrue; 377 } 378 379#if 0 380 best = DBL_MAX; 381 bestStrong = DBL_MAX; 382 bestWeak = DBL_MAX; 383 j = 1; 384#else 385 best = 1e99; 386 bestStrong = 1e99; 387 bestWeak = 1e99; 388 j = 0; 389#endif 390 for (v1 = v1orig; v1; v1 = FcValueListNext(v1)) 391 { 392 for (v2 = v2orig, k = 0; v2; v2 = FcValueListNext(v2), k++) 393 { 394 v = (match->compare) (&v1->value, &v2->value); 395 if (v < 0) 396 { 397 *result = FcResultTypeMismatch; 398 return FcFalse; 399 } 400 v = v * 1000 + j; 401 if (v < best) 402 { 403 if (bestValue) 404 *bestValue = FcValueCanonicalize(&v2->value); 405 best = v; 406 pos = k; 407 } 408 if (v1->binding == FcValueBindingStrong) 409 { 410 if (v < bestStrong) 411 bestStrong = v; 412 } 413 else 414 { 415 if (v < bestWeak) 416 bestWeak = v; 417 } 418 } 419 j++; 420 } 421 if (FcDebug () & FC_DBG_MATCHV) 422 { 423 printf (" %s: %g ", FcObjectName (object), best); 424 FcValueListPrint (v1orig); 425 printf (", "); 426 FcValueListPrint (v2orig); 427 printf ("\n"); 428 } 429 if (value) 430 { 431 int weak = match->weak; 432 int strong = match->strong; 433 if (weak == strong) 434 value[strong] += best; 435 else 436 { 437 value[weak] += bestWeak; 438 value[strong] += bestStrong; 439 } 440 } 441 if (n) 442 *n = pos; 443 444 return FcTrue; 445} 446 447/* 448 * Return a value indicating the distance between the two lists of 449 * values 450 */ 451 452static FcBool 453FcCompare (FcPattern *pat, 454 FcPattern *fnt, 455 double *value, 456 FcResult *result) 457{ 458 int i, i1, i2; 459 460 for (i = 0; i < PRI_END; i++) 461 value[i] = 0.0; 462 463 i1 = 0; 464 i2 = 0; 465 while (i1 < pat->num && i2 < fnt->num) 466 { 467 FcPatternElt *elt_i1 = &FcPatternElts(pat)[i1]; 468 FcPatternElt *elt_i2 = &FcPatternElts(fnt)[i2]; 469 470 i = FcObjectCompare(elt_i1->object, elt_i2->object); 471 if (i > 0) 472 i2++; 473 else if (i < 0) 474 i1++; 475 else 476 { 477 const FcMatcher *match = FcObjectToMatcher (elt_i1->object, FcFalse); 478 if (!FcCompareValueList (elt_i1->object, match, 479 FcPatternEltValues(elt_i1), 480 FcPatternEltValues(elt_i2), 481 NULL, value, NULL, result)) 482 return FcFalse; 483 i1++; 484 i2++; 485 } 486 } 487 return FcTrue; 488} 489 490FcPattern * 491FcFontRenderPrepare (FcConfig *config, 492 FcPattern *pat, 493 FcPattern *font) 494{ 495 FcPattern *new; 496 int i; 497 FcPatternElt *fe, *pe; 498 FcValue v; 499 FcResult result; 500 501 assert (pat != NULL); 502 assert (font != NULL); 503 504 new = FcPatternCreate (); 505 if (!new) 506 return NULL; 507 for (i = 0; i < font->num; i++) 508 { 509 fe = &FcPatternElts(font)[i]; 510 if (fe->object == FC_FAMILYLANG_OBJECT || 511 fe->object == FC_STYLELANG_OBJECT || 512 fe->object == FC_FULLNAMELANG_OBJECT) 513 { 514 /* ignore those objects. we need to deal with them 515 * another way */ 516 continue; 517 } 518 if (fe->object == FC_FAMILY_OBJECT || 519 fe->object == FC_STYLE_OBJECT || 520 fe->object == FC_FULLNAME_OBJECT) 521 { 522 FcPatternElt *fel, *pel; 523 524 FC_ASSERT_STATIC ((FC_FAMILY_OBJECT + 1) == FC_FAMILYLANG_OBJECT); 525 FC_ASSERT_STATIC ((FC_STYLE_OBJECT + 1) == FC_STYLELANG_OBJECT); 526 FC_ASSERT_STATIC ((FC_FULLNAME_OBJECT + 1) == FC_FULLNAMELANG_OBJECT); 527 528 fel = FcPatternObjectFindElt (font, fe->object + 1); 529 pel = FcPatternObjectFindElt (pat, fe->object + 1); 530 531 if (fel && pel) 532 { 533 /* The font has name languages, and pattern asks for specific language(s). 534 * Match on language and and prefer that result. 535 * Note: Currently the code only give priority to first matching language. 536 */ 537 int n = 1, j; 538 FcValueListPtr l1, l2, ln = NULL, ll = NULL; 539 const FcMatcher *match = FcObjectToMatcher (pel->object, FcTrue); 540 541 if (!FcCompareValueList (pel->object, match, 542 FcPatternEltValues (pel), 543 FcPatternEltValues (fel), NULL, NULL, &n, &result)) 544 { 545 FcPatternDestroy (new); 546 return NULL; 547 } 548 549 for (j = 0, l1 = FcPatternEltValues (fe), l2 = FcPatternEltValues (fel); 550 l1 != NULL || l2 != NULL; 551 j++, l1 = l1 ? FcValueListNext (l1) : NULL, l2 = l2 ? FcValueListNext (l2) : NULL) 552 { 553 if (j == n) 554 { 555 if (l1) 556 ln = FcValueListPrepend (ln, 557 FcValueCanonicalize (&l1->value), 558 FcValueBindingStrong); 559 if (l2) 560 ll = FcValueListPrepend (ll, 561 FcValueCanonicalize (&l2->value), 562 FcValueBindingStrong); 563 } 564 else 565 { 566 if (l1) 567 ln = FcValueListAppend (ln, 568 FcValueCanonicalize (&l1->value), 569 FcValueBindingStrong); 570 if (l2) 571 ll = FcValueListAppend (ll, 572 FcValueCanonicalize (&l2->value), 573 FcValueBindingStrong); 574 } 575 } 576 FcPatternObjectListAdd (new, fe->object, ln, FcFalse); 577 FcPatternObjectListAdd (new, fel->object, ll, FcFalse); 578 579 continue; 580 } 581 else if (fel) 582 { 583 /* Pattern doesn't ask for specific language. Copy all for name and 584 * lang. */ 585 FcValueListPtr l1, l2; 586 587 l1 = FcValueListDuplicate (FcPatternEltValues (fe)); 588 l2 = FcValueListDuplicate (FcPatternEltValues (fel)); 589 FcPatternObjectListAdd (new, fe->object, l1, FcFalse); 590 FcPatternObjectListAdd (new, fel->object, l2, FcFalse); 591 592 continue; 593 } 594 } 595 596 pe = FcPatternObjectFindElt (pat, fe->object); 597 if (pe) 598 { 599 const FcMatcher *match = FcObjectToMatcher (pe->object, FcFalse); 600 if (!FcCompareValueList (pe->object, match, 601 FcPatternEltValues(pe), 602 FcPatternEltValues(fe), &v, NULL, NULL, &result)) 603 { 604 FcPatternDestroy (new); 605 return NULL; 606 } 607 FcPatternObjectAdd (new, fe->object, v, FcFalse); 608 } 609 else 610 { 611 FcPatternObjectListAdd (new, fe->object, 612 FcValueListDuplicate (FcPatternEltValues (fe)), 613 FcTrue); 614 } 615 } 616 for (i = 0; i < pat->num; i++) 617 { 618 pe = &FcPatternElts(pat)[i]; 619 fe = FcPatternObjectFindElt (font, pe->object); 620 if (!fe && 621 pe->object != FC_FAMILYLANG_OBJECT && 622 pe->object != FC_STYLELANG_OBJECT && 623 pe->object != FC_FULLNAMELANG_OBJECT) 624 { 625 FcPatternObjectListAdd (new, pe->object, 626 FcValueListDuplicate (FcPatternEltValues(pe)), 627 FcFalse); 628 } 629 } 630 631 FcConfigSubstituteWithPat (config, new, pat, FcMatchFont); 632 return new; 633} 634 635static FcPattern * 636FcFontSetMatchInternal (FcFontSet **sets, 637 int nsets, 638 FcPattern *p, 639 FcResult *result) 640{ 641 double score[PRI_END], bestscore[PRI_END]; 642 int f; 643 FcFontSet *s; 644 FcPattern *best; 645 int i; 646 int set; 647 648 for (i = 0; i < PRI_END; i++) 649 bestscore[i] = 0; 650 best = 0; 651 if (FcDebug () & FC_DBG_MATCH) 652 { 653 printf ("Match "); 654 FcPatternPrint (p); 655 } 656 for (set = 0; set < nsets; set++) 657 { 658 s = sets[set]; 659 if (!s) 660 continue; 661 for (f = 0; f < s->nfont; f++) 662 { 663 if (FcDebug () & FC_DBG_MATCHV) 664 { 665 printf ("Font %d ", f); 666 FcPatternPrint (s->fonts[f]); 667 } 668 if (!FcCompare (p, s->fonts[f], score, result)) 669 return 0; 670 if (FcDebug () & FC_DBG_MATCHV) 671 { 672 printf ("Score"); 673 for (i = 0; i < PRI_END; i++) 674 { 675 printf (" %g", score[i]); 676 } 677 printf ("\n"); 678 } 679 for (i = 0; i < PRI_END; i++) 680 { 681 if (best && bestscore[i] < score[i]) 682 break; 683 if (!best || score[i] < bestscore[i]) 684 { 685 for (i = 0; i < PRI_END; i++) 686 bestscore[i] = score[i]; 687 best = s->fonts[f]; 688 break; 689 } 690 } 691 } 692 } 693 if (FcDebug () & FC_DBG_MATCH) 694 { 695 printf ("Best score"); 696 for (i = 0; i < PRI_END; i++) 697 printf (" %g", bestscore[i]); 698 printf ("\n"); 699 FcPatternPrint (best); 700 } 701 if (FcDebug () & FC_DBG_MATCH2) 702 { 703 char *env = getenv ("FC_DBG_MATCH_FILTER"); 704 FcObjectSet *os = NULL; 705 706 if (env) 707 { 708 char *ss, *s; 709 char *p; 710 FcBool f = FcTrue; 711 712 ss = s = strdup (env); 713 os = FcObjectSetCreate (); 714 while (f) 715 { 716 size_t len; 717 char *x; 718 719 if (!(p = strchr (s, ','))) 720 { 721 f = FcFalse; 722 len = strlen (s) + 1; 723 } 724 else 725 { 726 len = (p - s) + 1; 727 } 728 x = malloc (sizeof (char) * len); 729 strncpy (x, s, len - 1); 730 x[len - 1] = 0; 731 if (FcObjectFromName (x) > 0) 732 FcObjectSetAdd (os, x); 733 s = p + 1; 734 free (x); 735 } 736 free (ss); 737 } 738 FcPatternPrint2 (p, best, os); 739 if (os) 740 FcObjectSetDestroy (os); 741 } 742 /* assuming that 'result' is initialized with FcResultNoMatch 743 * outside this function */ 744 if (best) 745 *result = FcResultMatch; 746 747 return best; 748} 749 750FcPattern * 751FcFontSetMatch (FcConfig *config, 752 FcFontSet **sets, 753 int nsets, 754 FcPattern *p, 755 FcResult *result) 756{ 757 FcPattern *best; 758 759 assert (sets != NULL); 760 assert (p != NULL); 761 assert (result != NULL); 762 763 *result = FcResultNoMatch; 764 765 if (!config) 766 { 767 config = FcConfigGetCurrent (); 768 if (!config) 769 return 0; 770 } 771 best = FcFontSetMatchInternal (sets, nsets, p, result); 772 if (best) 773 return FcFontRenderPrepare (config, p, best); 774 else 775 return NULL; 776} 777 778FcPattern * 779FcFontMatch (FcConfig *config, 780 FcPattern *p, 781 FcResult *result) 782{ 783 FcFontSet *sets[2]; 784 int nsets; 785 FcPattern *best; 786 787 assert (p != NULL); 788 assert (result != NULL); 789 790 *result = FcResultNoMatch; 791 792 if (!config) 793 { 794 config = FcConfigGetCurrent (); 795 if (!config) 796 return 0; 797 } 798 nsets = 0; 799 if (config->fonts[FcSetSystem]) 800 sets[nsets++] = config->fonts[FcSetSystem]; 801 if (config->fonts[FcSetApplication]) 802 sets[nsets++] = config->fonts[FcSetApplication]; 803 804 best = FcFontSetMatchInternal (sets, nsets, p, result); 805 if (best) 806 return FcFontRenderPrepare (config, p, best); 807 else 808 return NULL; 809} 810 811typedef struct _FcSortNode { 812 FcPattern *pattern; 813 double score[PRI_END]; 814} FcSortNode; 815 816static int 817FcSortCompare (const void *aa, const void *ab) 818{ 819 FcSortNode *a = *(FcSortNode **) aa; 820 FcSortNode *b = *(FcSortNode **) ab; 821 double *as = &a->score[0]; 822 double *bs = &b->score[0]; 823 double ad = 0, bd = 0; 824 int i; 825 826 i = PRI_END; 827 while (i-- && (ad = *as++) == (bd = *bs++)) 828 ; 829 return ad < bd ? -1 : ad > bd ? 1 : 0; 830} 831 832static FcBool 833FcSortWalk (FcSortNode **n, int nnode, FcFontSet *fs, FcCharSet **csp, FcBool trim) 834{ 835 FcBool ret = FcFalse; 836 FcCharSet *cs; 837 int i; 838 839 cs = 0; 840 if (trim || csp) 841 { 842 cs = FcCharSetCreate (); 843 if (cs == NULL) 844 goto bail; 845 } 846 847 for (i = 0; i < nnode; i++) 848 { 849 FcSortNode *node = *n++; 850 FcBool adds_chars = FcFalse; 851 852 /* 853 * Only fetch node charset if we'd need it 854 */ 855 if (cs) 856 { 857 FcCharSet *ncs; 858 859 if (FcPatternGetCharSet (node->pattern, FC_CHARSET, 0, &ncs) != 860 FcResultMatch) 861 continue; 862 863 if (!FcCharSetMerge (cs, ncs, &adds_chars)) 864 goto bail; 865 } 866 867 /* 868 * If this font isn't a subset of the previous fonts, 869 * add it to the list 870 */ 871 if (!i || !trim || adds_chars) 872 { 873 FcPatternReference (node->pattern); 874 if (FcDebug () & FC_DBG_MATCHV) 875 { 876 printf ("Add "); 877 FcPatternPrint (node->pattern); 878 } 879 if (!FcFontSetAdd (fs, node->pattern)) 880 { 881 FcPatternDestroy (node->pattern); 882 goto bail; 883 } 884 } 885 } 886 if (csp) 887 { 888 *csp = cs; 889 cs = 0; 890 } 891 892 ret = FcTrue; 893 894bail: 895 if (cs) 896 FcCharSetDestroy (cs); 897 898 return ret; 899} 900 901void 902FcFontSetSortDestroy (FcFontSet *fs) 903{ 904 FcFontSetDestroy (fs); 905} 906 907FcFontSet * 908FcFontSetSort (FcConfig *config FC_UNUSED, 909 FcFontSet **sets, 910 int nsets, 911 FcPattern *p, 912 FcBool trim, 913 FcCharSet **csp, 914 FcResult *result) 915{ 916 FcFontSet *ret; 917 FcFontSet *s; 918 FcSortNode *nodes; 919 FcSortNode **nodeps, **nodep; 920 int nnodes; 921 FcSortNode *new; 922 int set; 923 int f; 924 int i; 925 int nPatternLang; 926 FcBool *patternLangSat; 927 FcValue patternLang; 928 929 assert (sets != NULL); 930 assert (p != NULL); 931 assert (result != NULL); 932 933 /* There are some implementation that relying on the result of 934 * "result" to check if the return value of FcFontSetSort 935 * is valid or not. 936 * So we should initialize it to the conservative way since 937 * this function doesn't return NULL anymore. 938 */ 939 if (result) 940 *result = FcResultNoMatch; 941 942 if (FcDebug () & FC_DBG_MATCH) 943 { 944 printf ("Sort "); 945 FcPatternPrint (p); 946 } 947 nnodes = 0; 948 for (set = 0; set < nsets; set++) 949 { 950 s = sets[set]; 951 if (!s) 952 continue; 953 nnodes += s->nfont; 954 } 955 if (!nnodes) 956 return FcFontSetCreate (); 957 958 for (nPatternLang = 0; 959 FcPatternGet (p, FC_LANG, nPatternLang, &patternLang) == FcResultMatch; 960 nPatternLang++) 961 ; 962 963 /* freed below */ 964 nodes = malloc (nnodes * sizeof (FcSortNode) + 965 nnodes * sizeof (FcSortNode *) + 966 nPatternLang * sizeof (FcBool)); 967 if (!nodes) 968 goto bail0; 969 nodeps = (FcSortNode **) (nodes + nnodes); 970 patternLangSat = (FcBool *) (nodeps + nnodes); 971 972 new = nodes; 973 nodep = nodeps; 974 for (set = 0; set < nsets; set++) 975 { 976 s = sets[set]; 977 if (!s) 978 continue; 979 for (f = 0; f < s->nfont; f++) 980 { 981 if (FcDebug () & FC_DBG_MATCHV) 982 { 983 printf ("Font %d ", f); 984 FcPatternPrint (s->fonts[f]); 985 } 986 new->pattern = s->fonts[f]; 987 if (!FcCompare (p, new->pattern, new->score, result)) 988 goto bail1; 989 if (FcDebug () & FC_DBG_MATCHV) 990 { 991 printf ("Score"); 992 for (i = 0; i < PRI_END; i++) 993 { 994 printf (" %g", new->score[i]); 995 } 996 printf ("\n"); 997 } 998 *nodep = new; 999 new++; 1000 nodep++; 1001 } 1002 } 1003 1004 nnodes = new - nodes; 1005 1006 qsort (nodeps, nnodes, sizeof (FcSortNode *), 1007 FcSortCompare); 1008 1009 for (i = 0; i < nPatternLang; i++) 1010 patternLangSat[i] = FcFalse; 1011 1012 for (f = 0; f < nnodes; f++) 1013 { 1014 FcBool satisfies = FcFalse; 1015 /* 1016 * If this node matches any language, go check 1017 * which ones and satisfy those entries 1018 */ 1019 if (nodeps[f]->score[PRI_LANG] < 2000) 1020 { 1021 for (i = 0; i < nPatternLang; i++) 1022 { 1023 FcValue nodeLang; 1024 1025 if (!patternLangSat[i] && 1026 FcPatternGet (p, FC_LANG, i, &patternLang) == FcResultMatch && 1027 FcPatternGet (nodeps[f]->pattern, FC_LANG, 0, &nodeLang) == FcResultMatch) 1028 { 1029 double compare = FcCompareLang (&patternLang, &nodeLang); 1030 if (compare >= 0 && compare < 2) 1031 { 1032 if (FcDebug () & FC_DBG_MATCHV) 1033 { 1034 FcChar8 *family; 1035 FcChar8 *style; 1036 1037 if (FcPatternGetString (nodeps[f]->pattern, FC_FAMILY, 0, &family) == FcResultMatch && 1038 FcPatternGetString (nodeps[f]->pattern, FC_STYLE, 0, &style) == FcResultMatch) 1039 printf ("Font %s:%s matches language %d\n", family, style, i); 1040 } 1041 patternLangSat[i] = FcTrue; 1042 satisfies = FcTrue; 1043 break; 1044 } 1045 } 1046 } 1047 } 1048 if (!satisfies) 1049 { 1050 nodeps[f]->score[PRI_LANG] = 10000.0; 1051 } 1052 } 1053 1054 /* 1055 * Re-sort once the language issues have been settled 1056 */ 1057 qsort (nodeps, nnodes, sizeof (FcSortNode *), 1058 FcSortCompare); 1059 1060 ret = FcFontSetCreate (); 1061 if (!ret) 1062 goto bail1; 1063 1064 if (!FcSortWalk (nodeps, nnodes, ret, csp, trim)) 1065 goto bail2; 1066 1067 free (nodes); 1068 1069 if (FcDebug() & FC_DBG_MATCH) 1070 { 1071 printf ("First font "); 1072 FcPatternPrint (ret->fonts[0]); 1073 } 1074 if (ret->nfont > 0) 1075 *result = FcResultMatch; 1076 1077 return ret; 1078 1079bail2: 1080 FcFontSetDestroy (ret); 1081bail1: 1082 free (nodes); 1083bail0: 1084 return 0; 1085} 1086 1087FcFontSet * 1088FcFontSort (FcConfig *config, 1089 FcPattern *p, 1090 FcBool trim, 1091 FcCharSet **csp, 1092 FcResult *result) 1093{ 1094 FcFontSet *sets[2]; 1095 int nsets; 1096 1097 assert (p != NULL); 1098 assert (result != NULL); 1099 1100 *result = FcResultNoMatch; 1101 1102 if (!config) 1103 { 1104 config = FcConfigGetCurrent (); 1105 if (!config) 1106 return 0; 1107 } 1108 nsets = 0; 1109 if (config->fonts[FcSetSystem]) 1110 sets[nsets++] = config->fonts[FcSetSystem]; 1111 if (config->fonts[FcSetApplication]) 1112 sets[nsets++] = config->fonts[FcSetApplication]; 1113 return FcFontSetSort (config, sets, nsets, p, trim, csp, result); 1114} 1115#define __fcmatch__ 1116#include "fcaliastail.h" 1117#undef __fcmatch__ 1118