fcmatch.c revision f79b9446
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 best = DBL_MAX; 380 bestStrong = DBL_MAX; 381 bestWeak = DBL_MAX; 382 j = 1; 383 for (v1 = v1orig; v1; v1 = FcValueListNext(v1)) 384 { 385 for (v2 = v2orig, k = 0; v2; v2 = FcValueListNext(v2), k++) 386 { 387 v = (match->compare) (&v1->value, &v2->value); 388 if (v < 0) 389 { 390 *result = FcResultTypeMismatch; 391 return FcFalse; 392 } 393 v = v * 1000 + j; 394 if (v < best) 395 { 396 if (bestValue) 397 *bestValue = FcValueCanonicalize(&v2->value); 398 best = v; 399 pos = k; 400 } 401 if (v1->binding == FcValueBindingStrong) 402 { 403 if (v < bestStrong) 404 bestStrong = v; 405 } 406 else 407 { 408 if (v < bestWeak) 409 bestWeak = v; 410 } 411 } 412 j++; 413 } 414 if (FcDebug () & FC_DBG_MATCHV) 415 { 416 printf (" %s: %g ", FcObjectName (object), best); 417 FcValueListPrint (v1orig); 418 printf (", "); 419 FcValueListPrint (v2orig); 420 printf ("\n"); 421 } 422 if (value) 423 { 424 int weak = match->weak; 425 int strong = match->strong; 426 if (weak == strong) 427 value[strong] += best; 428 else 429 { 430 value[weak] += bestWeak; 431 value[strong] += bestStrong; 432 } 433 } 434 if (n) 435 *n = pos; 436 437 return FcTrue; 438} 439 440/* 441 * Return a value indicating the distance between the two lists of 442 * values 443 */ 444 445static FcBool 446FcCompare (FcPattern *pat, 447 FcPattern *fnt, 448 double *value, 449 FcResult *result) 450{ 451 int i, i1, i2; 452 453 for (i = 0; i < PRI_END; i++) 454 value[i] = 0.0; 455 456 i1 = 0; 457 i2 = 0; 458 while (i1 < pat->num && i2 < fnt->num) 459 { 460 FcPatternElt *elt_i1 = &FcPatternElts(pat)[i1]; 461 FcPatternElt *elt_i2 = &FcPatternElts(fnt)[i2]; 462 463 i = FcObjectCompare(elt_i1->object, elt_i2->object); 464 if (i > 0) 465 i2++; 466 else if (i < 0) 467 i1++; 468 else 469 { 470 const FcMatcher *match = FcObjectToMatcher (elt_i1->object, FcFalse); 471 if (!FcCompareValueList (elt_i1->object, match, 472 FcPatternEltValues(elt_i1), 473 FcPatternEltValues(elt_i2), 474 NULL, value, NULL, result)) 475 return FcFalse; 476 i1++; 477 i2++; 478 } 479 } 480 return FcTrue; 481} 482 483FcPattern * 484FcFontRenderPrepare (FcConfig *config, 485 FcPattern *pat, 486 FcPattern *font) 487{ 488 FcPattern *new; 489 int i; 490 FcPatternElt *fe, *pe; 491 FcValue v; 492 FcResult result; 493 494 assert (pat != NULL); 495 assert (font != NULL); 496 497 new = FcPatternCreate (); 498 if (!new) 499 return NULL; 500 for (i = 0; i < font->num; i++) 501 { 502 fe = &FcPatternElts(font)[i]; 503 if (fe->object == FC_FAMILYLANG_OBJECT || 504 fe->object == FC_STYLELANG_OBJECT || 505 fe->object == FC_FULLNAMELANG_OBJECT) 506 { 507 /* ignore those objects. we need to deal with them 508 * another way */ 509 continue; 510 } 511 if (fe->object == FC_FAMILY_OBJECT || 512 fe->object == FC_STYLE_OBJECT || 513 fe->object == FC_FULLNAME_OBJECT) 514 { 515 FcPatternElt *fel, *pel; 516 517 FC_ASSERT_STATIC ((FC_FAMILY_OBJECT + 1) == FC_FAMILYLANG_OBJECT); 518 FC_ASSERT_STATIC ((FC_STYLE_OBJECT + 1) == FC_STYLELANG_OBJECT); 519 FC_ASSERT_STATIC ((FC_FULLNAME_OBJECT + 1) == FC_FULLNAMELANG_OBJECT); 520 521 fel = FcPatternObjectFindElt (font, fe->object + 1); 522 pel = FcPatternObjectFindElt (pat, fe->object + 1); 523 524 if (fel && pel) 525 { 526 /* The font has name languages, and pattern asks for specific language(s). 527 * Match on language and and prefer that result. 528 * Note: Currently the code only give priority to first matching language. 529 */ 530 int n = 1, j; 531 FcValueListPtr l1, l2, ln = NULL, ll = NULL; 532 const FcMatcher *match = FcObjectToMatcher (pel->object, FcTrue); 533 534 if (!FcCompareValueList (pel->object, match, 535 FcPatternEltValues (pel), 536 FcPatternEltValues (fel), NULL, NULL, &n, &result)) 537 { 538 FcPatternDestroy (new); 539 return NULL; 540 } 541 542 for (j = 0, l1 = FcPatternEltValues (fe), l2 = FcPatternEltValues (fel); 543 l1 != NULL || l2 != NULL; 544 j++, l1 = l1 ? FcValueListNext (l1) : NULL, l2 = l2 ? FcValueListNext (l2) : NULL) 545 { 546 if (j == n) 547 { 548 if (l1) 549 ln = FcValueListPrepend (ln, 550 FcValueCanonicalize (&l1->value), 551 FcValueBindingStrong); 552 if (l2) 553 ll = FcValueListPrepend (ll, 554 FcValueCanonicalize (&l2->value), 555 FcValueBindingStrong); 556 } 557 else 558 { 559 if (l1) 560 ln = FcValueListAppend (ln, 561 FcValueCanonicalize (&l1->value), 562 FcValueBindingStrong); 563 if (l2) 564 ll = FcValueListAppend (ll, 565 FcValueCanonicalize (&l2->value), 566 FcValueBindingStrong); 567 } 568 } 569 FcPatternObjectListAdd (new, fe->object, ln, FcFalse); 570 FcPatternObjectListAdd (new, fel->object, ll, FcFalse); 571 572 continue; 573 } 574 else if (fel) 575 { 576 /* Pattern doesn't ask for specific language. Copy all for name and 577 * lang. */ 578 FcValueListPtr l1, l2; 579 580 l1 = FcValueListDuplicate (FcPatternEltValues (fe)); 581 l2 = FcValueListDuplicate (FcPatternEltValues (fel)); 582 FcPatternObjectListAdd (new, fe->object, l1, FcFalse); 583 FcPatternObjectListAdd (new, fel->object, l2, FcFalse); 584 585 continue; 586 } 587 } 588 589 pe = FcPatternObjectFindElt (pat, fe->object); 590 if (pe) 591 { 592 const FcMatcher *match = FcObjectToMatcher (pe->object, FcFalse); 593 if (!FcCompareValueList (pe->object, match, 594 FcPatternEltValues(pe), 595 FcPatternEltValues(fe), &v, NULL, NULL, &result)) 596 { 597 FcPatternDestroy (new); 598 return NULL; 599 } 600 FcPatternObjectAdd (new, fe->object, v, FcFalse); 601 } 602 else 603 { 604 FcPatternObjectListAdd (new, fe->object, 605 FcValueListDuplicate (FcPatternEltValues (fe)), 606 FcTrue); 607 } 608 } 609 for (i = 0; i < pat->num; i++) 610 { 611 pe = &FcPatternElts(pat)[i]; 612 fe = FcPatternObjectFindElt (font, pe->object); 613 if (!fe && 614 pe->object != FC_FAMILYLANG_OBJECT && 615 pe->object != FC_STYLELANG_OBJECT && 616 pe->object != FC_FULLNAMELANG_OBJECT) 617 { 618 FcPatternObjectListAdd (new, pe->object, 619 FcValueListDuplicate (FcPatternEltValues(pe)), 620 FcFalse); 621 } 622 } 623 624 FcConfigSubstituteWithPat (config, new, pat, FcMatchFont); 625 return new; 626} 627 628static FcPattern * 629FcFontSetMatchInternal (FcFontSet **sets, 630 int nsets, 631 FcPattern *p, 632 FcResult *result) 633{ 634 double score[PRI_END], bestscore[PRI_END]; 635 int f; 636 FcFontSet *s; 637 FcPattern *best; 638 int i; 639 int set; 640 641 for (i = 0; i < PRI_END; i++) 642 bestscore[i] = 0; 643 best = 0; 644 if (FcDebug () & FC_DBG_MATCH) 645 { 646 printf ("Match "); 647 FcPatternPrint (p); 648 } 649 for (set = 0; set < nsets; set++) 650 { 651 s = sets[set]; 652 if (!s) 653 continue; 654 for (f = 0; f < s->nfont; f++) 655 { 656 if (FcDebug () & FC_DBG_MATCHV) 657 { 658 printf ("Font %d ", f); 659 FcPatternPrint (s->fonts[f]); 660 } 661 if (!FcCompare (p, s->fonts[f], score, result)) 662 return 0; 663 if (FcDebug () & FC_DBG_MATCHV) 664 { 665 printf ("Score"); 666 for (i = 0; i < PRI_END; i++) 667 { 668 printf (" %g", score[i]); 669 } 670 printf ("\n"); 671 } 672 for (i = 0; i < PRI_END; i++) 673 { 674 if (best && bestscore[i] < score[i]) 675 break; 676 if (!best || score[i] < bestscore[i]) 677 { 678 for (i = 0; i < PRI_END; i++) 679 bestscore[i] = score[i]; 680 best = s->fonts[f]; 681 break; 682 } 683 } 684 } 685 } 686 if (FcDebug () & FC_DBG_MATCH) 687 { 688 printf ("Best score"); 689 for (i = 0; i < PRI_END; i++) 690 printf (" %g", bestscore[i]); 691 printf ("\n"); 692 FcPatternPrint (best); 693 } 694 if (FcDebug () & FC_DBG_MATCH2) 695 { 696 char *env = getenv ("FC_DBG_MATCH_FILTER"); 697 FcObjectSet *os = NULL; 698 699 if (env) 700 { 701 char *ss, *s; 702 char *p; 703 FcBool f = FcTrue; 704 705 ss = s = strdup (env); 706 os = FcObjectSetCreate (); 707 while (f) 708 { 709 size_t len; 710 char *x; 711 712 if (!(p = strchr (s, ','))) 713 { 714 f = FcFalse; 715 len = strlen (s) + 1; 716 } 717 else 718 { 719 len = (p - s) + 1; 720 } 721 x = malloc (sizeof (char) * len); 722 strncpy (x, s, len - 1); 723 x[len - 1] = 0; 724 if (FcObjectFromName (x) > 0) 725 FcObjectSetAdd (os, x); 726 s = p + 1; 727 free (x); 728 } 729 free (ss); 730 } 731 FcPatternPrint2 (p, best, os); 732 if (os) 733 FcObjectSetDestroy (os); 734 } 735 /* assuming that 'result' is initialized with FcResultNoMatch 736 * outside this function */ 737 if (best) 738 *result = FcResultMatch; 739 740 return best; 741} 742 743FcPattern * 744FcFontSetMatch (FcConfig *config, 745 FcFontSet **sets, 746 int nsets, 747 FcPattern *p, 748 FcResult *result) 749{ 750 FcPattern *best; 751 752 assert (sets != NULL); 753 assert (p != NULL); 754 assert (result != NULL); 755 756 *result = FcResultNoMatch; 757 758 if (!config) 759 { 760 config = FcConfigGetCurrent (); 761 if (!config) 762 return 0; 763 } 764 best = FcFontSetMatchInternal (sets, nsets, p, result); 765 if (best) 766 return FcFontRenderPrepare (config, p, best); 767 else 768 return NULL; 769} 770 771FcPattern * 772FcFontMatch (FcConfig *config, 773 FcPattern *p, 774 FcResult *result) 775{ 776 FcFontSet *sets[2]; 777 int nsets; 778 FcPattern *best; 779 780 assert (p != NULL); 781 assert (result != NULL); 782 783 *result = FcResultNoMatch; 784 785 if (!config) 786 { 787 config = FcConfigGetCurrent (); 788 if (!config) 789 return 0; 790 } 791 nsets = 0; 792 if (config->fonts[FcSetSystem]) 793 sets[nsets++] = config->fonts[FcSetSystem]; 794 if (config->fonts[FcSetApplication]) 795 sets[nsets++] = config->fonts[FcSetApplication]; 796 797 best = FcFontSetMatchInternal (sets, nsets, p, result); 798 if (best) 799 return FcFontRenderPrepare (config, p, best); 800 else 801 return NULL; 802} 803 804typedef struct _FcSortNode { 805 FcPattern *pattern; 806 double score[PRI_END]; 807} FcSortNode; 808 809static int 810FcSortCompare (const void *aa, const void *ab) 811{ 812 FcSortNode *a = *(FcSortNode **) aa; 813 FcSortNode *b = *(FcSortNode **) ab; 814 double *as = &a->score[0]; 815 double *bs = &b->score[0]; 816 double ad = 0, bd = 0; 817 int i; 818 819 i = PRI_END; 820 while (i-- && (ad = *as++) == (bd = *bs++)) 821 ; 822 return ad < bd ? -1 : ad > bd ? 1 : 0; 823} 824 825static FcBool 826FcSortWalk (FcSortNode **n, int nnode, FcFontSet *fs, FcCharSet **csp, FcBool trim) 827{ 828 FcBool ret = FcFalse; 829 FcCharSet *cs; 830 int i; 831 832 cs = 0; 833 if (trim || csp) 834 { 835 cs = FcCharSetCreate (); 836 if (cs == NULL) 837 goto bail; 838 } 839 840 for (i = 0; i < nnode; i++) 841 { 842 FcSortNode *node = *n++; 843 FcBool adds_chars = FcFalse; 844 845 /* 846 * Only fetch node charset if we'd need it 847 */ 848 if (cs) 849 { 850 FcCharSet *ncs; 851 852 if (FcPatternGetCharSet (node->pattern, FC_CHARSET, 0, &ncs) != 853 FcResultMatch) 854 continue; 855 856 if (!FcCharSetMerge (cs, ncs, &adds_chars)) 857 goto bail; 858 } 859 860 /* 861 * If this font isn't a subset of the previous fonts, 862 * add it to the list 863 */ 864 if (!i || !trim || adds_chars) 865 { 866 FcPatternReference (node->pattern); 867 if (FcDebug () & FC_DBG_MATCHV) 868 { 869 printf ("Add "); 870 FcPatternPrint (node->pattern); 871 } 872 if (!FcFontSetAdd (fs, node->pattern)) 873 { 874 FcPatternDestroy (node->pattern); 875 goto bail; 876 } 877 } 878 } 879 if (csp) 880 { 881 *csp = cs; 882 cs = 0; 883 } 884 885 ret = FcTrue; 886 887bail: 888 if (cs) 889 FcCharSetDestroy (cs); 890 891 return ret; 892} 893 894void 895FcFontSetSortDestroy (FcFontSet *fs) 896{ 897 FcFontSetDestroy (fs); 898} 899 900FcFontSet * 901FcFontSetSort (FcConfig *config FC_UNUSED, 902 FcFontSet **sets, 903 int nsets, 904 FcPattern *p, 905 FcBool trim, 906 FcCharSet **csp, 907 FcResult *result) 908{ 909 FcFontSet *ret; 910 FcFontSet *s; 911 FcSortNode *nodes; 912 FcSortNode **nodeps, **nodep; 913 int nnodes; 914 FcSortNode *new; 915 int set; 916 int f; 917 int i; 918 int nPatternLang; 919 FcBool *patternLangSat; 920 FcValue patternLang; 921 922 assert (sets != NULL); 923 assert (p != NULL); 924 assert (result != NULL); 925 926 /* There are some implementation that relying on the result of 927 * "result" to check if the return value of FcFontSetSort 928 * is valid or not. 929 * So we should initialize it to the conservative way since 930 * this function doesn't return NULL anymore. 931 */ 932 if (result) 933 *result = FcResultNoMatch; 934 935 if (FcDebug () & FC_DBG_MATCH) 936 { 937 printf ("Sort "); 938 FcPatternPrint (p); 939 } 940 nnodes = 0; 941 for (set = 0; set < nsets; set++) 942 { 943 s = sets[set]; 944 if (!s) 945 continue; 946 nnodes += s->nfont; 947 } 948 if (!nnodes) 949 return FcFontSetCreate (); 950 951 for (nPatternLang = 0; 952 FcPatternGet (p, FC_LANG, nPatternLang, &patternLang) == FcResultMatch; 953 nPatternLang++) 954 ; 955 956 /* freed below */ 957 nodes = malloc (nnodes * sizeof (FcSortNode) + 958 nnodes * sizeof (FcSortNode *) + 959 nPatternLang * sizeof (FcBool)); 960 if (!nodes) 961 goto bail0; 962 nodeps = (FcSortNode **) (nodes + nnodes); 963 patternLangSat = (FcBool *) (nodeps + nnodes); 964 965 new = nodes; 966 nodep = nodeps; 967 for (set = 0; set < nsets; set++) 968 { 969 s = sets[set]; 970 if (!s) 971 continue; 972 for (f = 0; f < s->nfont; f++) 973 { 974 if (FcDebug () & FC_DBG_MATCHV) 975 { 976 printf ("Font %d ", f); 977 FcPatternPrint (s->fonts[f]); 978 } 979 new->pattern = s->fonts[f]; 980 if (!FcCompare (p, new->pattern, new->score, result)) 981 goto bail1; 982 if (FcDebug () & FC_DBG_MATCHV) 983 { 984 printf ("Score"); 985 for (i = 0; i < PRI_END; i++) 986 { 987 printf (" %g", new->score[i]); 988 } 989 printf ("\n"); 990 } 991 *nodep = new; 992 new++; 993 nodep++; 994 } 995 } 996 997 nnodes = new - nodes; 998 999 qsort (nodeps, nnodes, sizeof (FcSortNode *), 1000 FcSortCompare); 1001 1002 for (i = 0; i < nPatternLang; i++) 1003 patternLangSat[i] = FcFalse; 1004 1005 for (f = 0; f < nnodes; f++) 1006 { 1007 FcBool satisfies = FcFalse; 1008 /* 1009 * If this node matches any language, go check 1010 * which ones and satisfy those entries 1011 */ 1012 if (nodeps[f]->score[PRI_LANG] < 2000) 1013 { 1014 for (i = 0; i < nPatternLang; i++) 1015 { 1016 FcValue nodeLang; 1017 1018 if (!patternLangSat[i] && 1019 FcPatternGet (p, FC_LANG, i, &patternLang) == FcResultMatch && 1020 FcPatternGet (nodeps[f]->pattern, FC_LANG, 0, &nodeLang) == FcResultMatch) 1021 { 1022 double compare = FcCompareLang (&patternLang, &nodeLang); 1023 if (compare >= 0 && compare < 2) 1024 { 1025 if (FcDebug () & FC_DBG_MATCHV) 1026 { 1027 FcChar8 *family; 1028 FcChar8 *style; 1029 1030 if (FcPatternGetString (nodeps[f]->pattern, FC_FAMILY, 0, &family) == FcResultMatch && 1031 FcPatternGetString (nodeps[f]->pattern, FC_STYLE, 0, &style) == FcResultMatch) 1032 printf ("Font %s:%s matches language %d\n", family, style, i); 1033 } 1034 patternLangSat[i] = FcTrue; 1035 satisfies = FcTrue; 1036 break; 1037 } 1038 } 1039 } 1040 } 1041 if (!satisfies) 1042 { 1043 nodeps[f]->score[PRI_LANG] = 10000.0; 1044 } 1045 } 1046 1047 /* 1048 * Re-sort once the language issues have been settled 1049 */ 1050 qsort (nodeps, nnodes, sizeof (FcSortNode *), 1051 FcSortCompare); 1052 1053 ret = FcFontSetCreate (); 1054 if (!ret) 1055 goto bail1; 1056 1057 if (!FcSortWalk (nodeps, nnodes, ret, csp, trim)) 1058 goto bail2; 1059 1060 free (nodes); 1061 1062 if (FcDebug() & FC_DBG_MATCH) 1063 { 1064 printf ("First font "); 1065 FcPatternPrint (ret->fonts[0]); 1066 } 1067 if (ret->nfont > 0) 1068 *result = FcResultMatch; 1069 1070 return ret; 1071 1072bail2: 1073 FcFontSetDestroy (ret); 1074bail1: 1075 free (nodes); 1076bail0: 1077 return 0; 1078} 1079 1080FcFontSet * 1081FcFontSort (FcConfig *config, 1082 FcPattern *p, 1083 FcBool trim, 1084 FcCharSet **csp, 1085 FcResult *result) 1086{ 1087 FcFontSet *sets[2]; 1088 int nsets; 1089 1090 assert (p != NULL); 1091 assert (result != NULL); 1092 1093 *result = FcResultNoMatch; 1094 1095 if (!config) 1096 { 1097 config = FcConfigGetCurrent (); 1098 if (!config) 1099 return 0; 1100 } 1101 nsets = 0; 1102 if (config->fonts[FcSetSystem]) 1103 sets[nsets++] = config->fonts[FcSetSystem]; 1104 if (config->fonts[FcSetApplication]) 1105 sets[nsets++] = config->fonts[FcSetApplication]; 1106 return FcFontSetSort (config, sets, nsets, p, trim, csp, result); 1107} 1108#define __fcmatch__ 1109#include "fcaliastail.h" 1110#undef __fcmatch__ 1111