fcmatch.c revision 70712537
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 Keith Packard not be used in 11 * advertising or publicity pertaining to distribution of the software without 12 * specific, written prior permission. Keith Packard makes 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 <string.h> 27#include <ctype.h> 28#include <stdio.h> 29#include <float.h> 30 31static double 32FcCompareNumber (FcValue *value1, FcValue *value2) 33{ 34 double v1, v2, v; 35 36 switch (value1->type) { 37 case FcTypeInteger: 38 v1 = (double) value1->u.i; 39 break; 40 case FcTypeDouble: 41 v1 = value1->u.d; 42 break; 43 default: 44 return -1.0; 45 } 46 switch (value2->type) { 47 case FcTypeInteger: 48 v2 = (double) value2->u.i; 49 break; 50 case FcTypeDouble: 51 v2 = value2->u.d; 52 break; 53 default: 54 return -1.0; 55 } 56 v = v2 - v1; 57 if (v < 0) 58 v = -v; 59 return v; 60} 61 62static double 63FcCompareString (FcValue *v1, FcValue *v2) 64{ 65 return (double) FcStrCmpIgnoreCase (FcValueString(v1), FcValueString(v2)) != 0; 66} 67 68static double 69FcCompareFamily (FcValue *v1, FcValue *v2) 70{ 71 /* rely on the guarantee in FcPatternAddWithBinding that 72 * families are always FcTypeString. */ 73 const FcChar8* v1_string = FcValueString(v1); 74 const FcChar8* v2_string = FcValueString(v2); 75 76 if (FcToLower(*v1_string) != FcToLower(*v2_string) && 77 *v1_string != ' ' && *v2_string != ' ') 78 return 1.0; 79 80 return (double) FcStrCmpIgnoreBlanksAndCase (v1_string, v2_string) != 0; 81} 82 83static double 84FcCompareLang (FcValue *v1, FcValue *v2) 85{ 86 FcLangResult result; 87 FcValue value1 = FcValueCanonicalize(v1), value2 = FcValueCanonicalize(v2); 88 89 switch (value1.type) { 90 case FcTypeLangSet: 91 switch (value2.type) { 92 case FcTypeLangSet: 93 result = FcLangSetCompare (value1.u.l, value2.u.l); 94 break; 95 case FcTypeString: 96 result = FcLangSetHasLang (value1.u.l, 97 value2.u.s); 98 break; 99 default: 100 return -1.0; 101 } 102 break; 103 case FcTypeString: 104 switch (value2.type) { 105 case FcTypeLangSet: 106 result = FcLangSetHasLang (value2.u.l, value1.u.s); 107 break; 108 case FcTypeString: 109 result = FcLangCompare (value1.u.s, 110 value2.u.s); 111 break; 112 default: 113 return -1.0; 114 } 115 break; 116 default: 117 return -1.0; 118 } 119 switch (result) { 120 case FcLangEqual: 121 return 0; 122 case FcLangDifferentCountry: 123 return 1; 124 case FcLangDifferentLang: 125 default: 126 return 2; 127 } 128} 129 130static double 131FcCompareBool (FcValue *v1, FcValue *v2) 132{ 133 if (v2->type != FcTypeBool || v1->type != FcTypeBool) 134 return -1.0; 135 return (double) v2->u.b != v1->u.b; 136} 137 138static double 139FcCompareCharSet (FcValue *v1, FcValue *v2) 140{ 141 return (double) FcCharSetSubtractCount (FcValueCharSet(v1), FcValueCharSet(v2)); 142} 143 144static double 145FcCompareSize (FcValue *value1, FcValue *value2) 146{ 147 double v1, v2, v; 148 149 switch (value1->type) { 150 case FcTypeInteger: 151 v1 = value1->u.i; 152 break; 153 case FcTypeDouble: 154 v1 = value1->u.d; 155 break; 156 default: 157 return -1; 158 } 159 switch (value2->type) { 160 case FcTypeInteger: 161 v2 = value2->u.i; 162 break; 163 case FcTypeDouble: 164 v2 = value2->u.d; 165 break; 166 default: 167 return -1; 168 } 169 if (v2 == 0) 170 return 0; 171 v = v2 - v1; 172 if (v < 0) 173 v = -v; 174 return v; 175} 176 177typedef struct _FcMatcher { 178 FcObject object; 179 double (*compare) (FcValue *value1, FcValue *value2); 180 int strong, weak; 181} FcMatcher; 182 183/* 184 * Order is significant, it defines the precedence of 185 * each value, earlier values are more significant than 186 * later values 187 */ 188static const FcMatcher _FcMatchers [] = { 189 { FC_FOUNDRY_OBJECT, FcCompareString, 0, 0 }, 190#define MATCH_FOUNDRY 0 191 { FC_CHARSET_OBJECT, FcCompareCharSet, 1, 1 }, 192#define MATCH_CHARSET 1 193 { FC_FAMILY_OBJECT, FcCompareFamily, 2, 4 }, 194#define MATCH_FAMILY 2 195 { FC_LANG_OBJECT, FcCompareLang, 3, 3 }, 196#define MATCH_LANG 3 197#define MATCH_LANG_INDEX 3 198 { FC_SPACING_OBJECT, FcCompareNumber, 5, 5 }, 199#define MATCH_SPACING 4 200 { FC_PIXEL_SIZE_OBJECT, FcCompareSize, 6, 6 }, 201#define MATCH_PIXEL_SIZE 5 202 { FC_STYLE_OBJECT, FcCompareString, 7, 7 }, 203#define MATCH_STYLE 6 204 { FC_SLANT_OBJECT, FcCompareNumber, 8, 8 }, 205#define MATCH_SLANT 7 206 { FC_WEIGHT_OBJECT, FcCompareNumber, 9, 9 }, 207#define MATCH_WEIGHT 8 208 { FC_WIDTH_OBJECT, FcCompareNumber, 10, 10 }, 209#define MATCH_WIDTH 9 210 { FC_DECORATIVE_OBJECT, FcCompareBool, 11, 11 }, 211#define MATCH_DECORATIVE 10 212 { FC_ANTIALIAS_OBJECT, FcCompareBool, 12, 12 }, 213#define MATCH_ANTIALIAS 11 214 { FC_RASTERIZER_OBJECT, FcCompareString, 13, 13 }, 215#define MATCH_RASTERIZER 12 216 { FC_OUTLINE_OBJECT, FcCompareBool, 14, 14 }, 217#define MATCH_OUTLINE 13 218 { FC_FONTVERSION_OBJECT, FcCompareNumber, 15, 15 }, 219#define MATCH_FONTVERSION 14 220}; 221 222#define NUM_MATCH_VALUES 16 223 224static const FcMatcher* 225FcObjectToMatcher (FcObject object) 226{ 227 int i; 228 229 i = -1; 230 switch (object) { 231 case FC_FOUNDRY_OBJECT: 232 i = MATCH_FOUNDRY; break; 233 case FC_FONTVERSION_OBJECT: 234 i = MATCH_FONTVERSION; break; 235 case FC_FAMILY_OBJECT: 236 i = MATCH_FAMILY; break; 237 case FC_CHARSET_OBJECT: 238 i = MATCH_CHARSET; break; 239 case FC_ANTIALIAS_OBJECT: 240 i = MATCH_ANTIALIAS; break; 241 case FC_LANG_OBJECT: 242 i = MATCH_LANG; break; 243 case FC_SPACING_OBJECT: 244 i = MATCH_SPACING; break; 245 case FC_STYLE_OBJECT: 246 i = MATCH_STYLE; break; 247 case FC_SLANT_OBJECT: 248 i = MATCH_SLANT; break; 249 case FC_PIXEL_SIZE_OBJECT: 250 i = MATCH_PIXEL_SIZE; break; 251 case FC_WIDTH_OBJECT: 252 i = MATCH_WIDTH; break; 253 case FC_WEIGHT_OBJECT: 254 i = MATCH_WEIGHT; break; 255 case FC_RASTERIZER_OBJECT: 256 i = MATCH_RASTERIZER; break; 257 case FC_OUTLINE_OBJECT: 258 i = MATCH_OUTLINE; break; 259 case FC_DECORATIVE_OBJECT: 260 i = MATCH_DECORATIVE; break; 261 } 262 263 if (i < 0) 264 return NULL; 265 266 return _FcMatchers+i; 267} 268 269static FcBool 270FcCompareValueList (FcObject object, 271 FcValueListPtr v1orig, /* pattern */ 272 FcValueListPtr v2orig, /* target */ 273 FcValue *bestValue, 274 double *value, 275 FcResult *result) 276{ 277 FcValueListPtr v1, v2; 278 double v, best, bestStrong, bestWeak; 279 int j; 280 const FcMatcher *match = FcObjectToMatcher(object); 281 282 if (!match) 283 { 284 if (bestValue) 285 *bestValue = FcValueCanonicalize(&v2orig->value); 286 return FcTrue; 287 } 288 289 best = DBL_MAX; 290 bestStrong = DBL_MAX; 291 bestWeak = DBL_MAX; 292 j = 1; 293 for (v1 = v1orig; v1; v1 = FcValueListNext(v1)) 294 { 295 for (v2 = v2orig; v2; v2 = FcValueListNext(v2)) 296 { 297 v = (match->compare) (&v1->value, &v2->value); 298 if (v < 0) 299 { 300 *result = FcResultTypeMismatch; 301 return FcFalse; 302 } 303 v = v * 1000 + j; 304 if (v < best) 305 { 306 if (bestValue) 307 *bestValue = FcValueCanonicalize(&v2->value); 308 best = v; 309 } 310 if (v1->binding == FcValueBindingStrong) 311 { 312 if (v < bestStrong) 313 bestStrong = v; 314 } 315 else 316 { 317 if (v < bestWeak) 318 bestWeak = v; 319 } 320 } 321 j++; 322 } 323 if (FcDebug () & FC_DBG_MATCHV) 324 { 325 printf (" %s: %g ", FcObjectName (object), best); 326 FcValueListPrint (v1orig); 327 printf (", "); 328 FcValueListPrint (v2orig); 329 printf ("\n"); 330 } 331 if (value) 332 { 333 int weak = match->weak; 334 int strong = match->strong; 335 if (weak == strong) 336 value[strong] += best; 337 else 338 { 339 value[weak] += bestWeak; 340 value[strong] += bestStrong; 341 } 342 } 343 return FcTrue; 344} 345 346/* 347 * Return a value indicating the distance between the two lists of 348 * values 349 */ 350 351static FcBool 352FcCompare (FcPattern *pat, 353 FcPattern *fnt, 354 double *value, 355 FcResult *result) 356{ 357 int i, i1, i2; 358 359 for (i = 0; i < NUM_MATCH_VALUES; i++) 360 value[i] = 0.0; 361 362 i1 = 0; 363 i2 = 0; 364 while (i1 < pat->num && i2 < fnt->num) 365 { 366 FcPatternElt *elt_i1 = &FcPatternElts(pat)[i1]; 367 FcPatternElt *elt_i2 = &FcPatternElts(fnt)[i2]; 368 369 i = FcObjectCompare(elt_i1->object, elt_i2->object); 370 if (i > 0) 371 i2++; 372 else if (i < 0) 373 i1++; 374 else 375 { 376 if (!FcCompareValueList (elt_i1->object, 377 FcPatternEltValues(elt_i1), 378 FcPatternEltValues(elt_i2), 379 0, value, result)) 380 return FcFalse; 381 i1++; 382 i2++; 383 } 384 } 385 return FcTrue; 386} 387 388FcPattern * 389FcFontRenderPrepare (FcConfig *config, 390 FcPattern *pat, 391 FcPattern *font) 392{ 393 FcPattern *new; 394 int i; 395 FcPatternElt *fe, *pe; 396 FcValue v; 397 FcResult result; 398 399 new = FcPatternCreate (); 400 if (!new) 401 return 0; 402 for (i = 0; i < font->num; i++) 403 { 404 fe = &FcPatternElts(font)[i]; 405 pe = FcPatternObjectFindElt (pat, fe->object); 406 if (pe) 407 { 408 if (!FcCompareValueList (pe->object, FcPatternEltValues(pe), 409 FcPatternEltValues(fe), &v, 0, &result)) 410 { 411 FcPatternDestroy (new); 412 return 0; 413 } 414 } 415 else 416 v = FcValueCanonicalize(&FcPatternEltValues (fe)->value); 417 FcPatternObjectAdd (new, fe->object, v, FcFalse); 418 } 419 for (i = 0; i < pat->num; i++) 420 { 421 pe = &FcPatternElts(pat)[i]; 422 fe = FcPatternObjectFindElt (font, pe->object); 423 if (!fe) 424 { 425 v = FcValueCanonicalize(&FcPatternEltValues(pe)->value); 426 FcPatternObjectAdd (new, pe->object, v, FcTrue); 427 } 428 } 429 430 FcConfigSubstituteWithPat (config, new, pat, FcMatchFont); 431 return new; 432} 433 434static FcPattern * 435FcFontSetMatchInternal (FcConfig *config, 436 FcFontSet **sets, 437 int nsets, 438 FcPattern *p, 439 FcResult *result) 440{ 441 double score[NUM_MATCH_VALUES], bestscore[NUM_MATCH_VALUES]; 442 int f; 443 FcFontSet *s; 444 FcPattern *best; 445 int i; 446 int set; 447 448 for (i = 0; i < NUM_MATCH_VALUES; i++) 449 bestscore[i] = 0; 450 best = 0; 451 if (FcDebug () & FC_DBG_MATCH) 452 { 453 printf ("Match "); 454 FcPatternPrint (p); 455 } 456 for (set = 0; set < nsets; set++) 457 { 458 s = sets[set]; 459 if (!s) 460 continue; 461 for (f = 0; f < s->nfont; f++) 462 { 463 if (FcDebug () & FC_DBG_MATCHV) 464 { 465 printf ("Font %d ", f); 466 FcPatternPrint (s->fonts[f]); 467 } 468 if (!FcCompare (p, s->fonts[f], score, result)) 469 return 0; 470 if (FcDebug () & FC_DBG_MATCHV) 471 { 472 printf ("Score"); 473 for (i = 0; i < NUM_MATCH_VALUES; i++) 474 { 475 printf (" %g", score[i]); 476 } 477 printf ("\n"); 478 } 479 for (i = 0; i < NUM_MATCH_VALUES; i++) 480 { 481 if (best && bestscore[i] < score[i]) 482 break; 483 if (!best || score[i] < bestscore[i]) 484 { 485 for (i = 0; i < NUM_MATCH_VALUES; i++) 486 bestscore[i] = score[i]; 487 best = s->fonts[f]; 488 break; 489 } 490 } 491 } 492 } 493 if (FcDebug () & FC_DBG_MATCH) 494 { 495 printf ("Best score"); 496 for (i = 0; i < NUM_MATCH_VALUES; i++) 497 printf (" %g", bestscore[i]); 498 printf ("\n"); 499 FcPatternPrint (best); 500 } 501 if (!best) 502 { 503 *result = FcResultNoMatch; 504 return 0; 505 } 506 return best; 507} 508 509FcPattern * 510FcFontSetMatch (FcConfig *config, 511 FcFontSet **sets, 512 int nsets, 513 FcPattern *p, 514 FcResult *result) 515{ 516 FcPattern *best; 517 518 if (!config) 519 { 520 config = FcConfigGetCurrent (); 521 if (!config) 522 return 0; 523 } 524 best = FcFontSetMatchInternal (config, sets, nsets, p, result); 525 if (best) 526 return FcFontRenderPrepare (config, p, best); 527 else 528 return NULL; 529} 530 531FcPattern * 532FcFontMatch (FcConfig *config, 533 FcPattern *p, 534 FcResult *result) 535{ 536 FcFontSet *sets[2]; 537 int nsets; 538 FcPattern *best; 539 540 if (!config) 541 { 542 config = FcConfigGetCurrent (); 543 if (!config) 544 return 0; 545 } 546 nsets = 0; 547 if (config->fonts[FcSetSystem]) 548 sets[nsets++] = config->fonts[FcSetSystem]; 549 if (config->fonts[FcSetApplication]) 550 sets[nsets++] = config->fonts[FcSetApplication]; 551 552 best = FcFontSetMatchInternal (config, sets, nsets, p, result); 553 if (best) 554 return FcFontRenderPrepare (config, p, best); 555 else 556 return NULL; 557} 558 559typedef struct _FcSortNode { 560 FcPattern *pattern; 561 double score[NUM_MATCH_VALUES]; 562} FcSortNode; 563 564static int 565FcSortCompare (const void *aa, const void *ab) 566{ 567 FcSortNode *a = *(FcSortNode **) aa; 568 FcSortNode *b = *(FcSortNode **) ab; 569 double *as = &a->score[0]; 570 double *bs = &b->score[0]; 571 double ad = 0, bd = 0; 572 int i; 573 574 i = NUM_MATCH_VALUES; 575 while (i-- && (ad = *as++) == (bd = *bs++)) 576 ; 577 return ad < bd ? -1 : ad > bd ? 1 : 0; 578} 579 580static FcBool 581FcSortWalk (FcSortNode **n, int nnode, FcFontSet *fs, FcCharSet **csp, FcBool trim) 582{ 583 FcBool ret = FcFalse; 584 FcCharSet *cs; 585 586 cs = 0; 587 if (trim || csp) 588 { 589 cs = FcCharSetCreate (); 590 if (cs == NULL) 591 goto bail; 592 } 593 594 while (nnode--) 595 { 596 FcSortNode *node = *n++; 597 FcBool adds_chars = FcFalse; 598 599 /* 600 * Only fetch node charset if we'd need it 601 */ 602 if (cs) 603 { 604 FcCharSet *ncs; 605 606 if (FcPatternGetCharSet (node->pattern, FC_CHARSET, 0, &ncs) != 607 FcResultMatch) 608 continue; 609 610 if (!FcCharSetMerge (cs, ncs, &adds_chars)) 611 goto bail; 612 } 613 614 /* 615 * If this font isn't a subset of the previous fonts, 616 * add it to the list 617 */ 618 if (!trim || adds_chars) 619 { 620 FcPatternReference (node->pattern); 621 if (FcDebug () & FC_DBG_MATCHV) 622 { 623 printf ("Add "); 624 FcPatternPrint (node->pattern); 625 } 626 if (!FcFontSetAdd (fs, node->pattern)) 627 { 628 FcPatternDestroy (node->pattern); 629 goto bail; 630 } 631 } 632 } 633 if (csp) 634 { 635 *csp = cs; 636 cs = 0; 637 } 638 639 ret = FcTrue; 640 641bail: 642 if (cs) 643 FcCharSetDestroy (cs); 644 645 return ret; 646} 647 648void 649FcFontSetSortDestroy (FcFontSet *fs) 650{ 651 FcFontSetDestroy (fs); 652} 653 654FcFontSet * 655FcFontSetSort (FcConfig *config, 656 FcFontSet **sets, 657 int nsets, 658 FcPattern *p, 659 FcBool trim, 660 FcCharSet **csp, 661 FcResult *result) 662{ 663 FcFontSet *ret; 664 FcFontSet *s; 665 FcSortNode *nodes; 666 FcSortNode **nodeps, **nodep; 667 int nnodes; 668 FcSortNode *new; 669 int set; 670 int f; 671 int i; 672 int nPatternLang; 673 FcBool *patternLangSat; 674 FcValue patternLang; 675 676 if (FcDebug () & FC_DBG_MATCH) 677 { 678 printf ("Sort "); 679 FcPatternPrint (p); 680 } 681 nnodes = 0; 682 for (set = 0; set < nsets; set++) 683 { 684 s = sets[set]; 685 if (!s) 686 continue; 687 nnodes += s->nfont; 688 } 689 if (!nnodes) 690 goto bail0; 691 692 for (nPatternLang = 0; 693 FcPatternGet (p, FC_LANG, nPatternLang, &patternLang) == FcResultMatch; 694 nPatternLang++) 695 ; 696 697 /* freed below */ 698 nodes = malloc (nnodes * sizeof (FcSortNode) + 699 nnodes * sizeof (FcSortNode *) + 700 nPatternLang * sizeof (FcBool)); 701 if (!nodes) 702 goto bail0; 703 nodeps = (FcSortNode **) (nodes + nnodes); 704 patternLangSat = (FcBool *) (nodeps + nnodes); 705 706 new = nodes; 707 nodep = nodeps; 708 for (set = 0; set < nsets; set++) 709 { 710 s = sets[set]; 711 if (!s) 712 continue; 713 for (f = 0; f < s->nfont; f++) 714 { 715 if (FcDebug () & FC_DBG_MATCHV) 716 { 717 printf ("Font %d ", f); 718 FcPatternPrint (s->fonts[f]); 719 } 720 new->pattern = s->fonts[f]; 721 if (!FcCompare (p, new->pattern, new->score, result)) 722 goto bail1; 723 if (FcDebug () & FC_DBG_MATCHV) 724 { 725 printf ("Score"); 726 for (i = 0; i < NUM_MATCH_VALUES; i++) 727 { 728 printf (" %g", new->score[i]); 729 } 730 printf ("\n"); 731 } 732 *nodep = new; 733 new++; 734 nodep++; 735 } 736 } 737 738 nnodes = new - nodes; 739 740 qsort (nodeps, nnodes, sizeof (FcSortNode *), 741 FcSortCompare); 742 743 for (i = 0; i < nPatternLang; i++) 744 patternLangSat[i] = FcFalse; 745 746 for (f = 0; f < nnodes; f++) 747 { 748 FcBool satisfies = FcFalse; 749 /* 750 * If this node matches any language, go check 751 * which ones and satisfy those entries 752 */ 753 if (nodeps[f]->score[MATCH_LANG_INDEX] < 200) 754 { 755 for (i = 0; i < nPatternLang; i++) 756 { 757 FcValue nodeLang; 758 759 if (!patternLangSat[i] && 760 FcPatternGet (p, FC_LANG, i, &patternLang) == FcResultMatch && 761 FcPatternGet (nodeps[f]->pattern, FC_LANG, 0, &nodeLang) == FcResultMatch) 762 { 763 double compare = FcCompareLang (&patternLang, &nodeLang); 764 if (compare >= 0 && compare < 2) 765 { 766 if (FcDebug () & FC_DBG_MATCHV) 767 { 768 FcChar8 *family; 769 FcChar8 *style; 770 771 if (FcPatternGetString (nodeps[f]->pattern, FC_FAMILY, 0, &family) == FcResultMatch && 772 FcPatternGetString (nodeps[f]->pattern, FC_STYLE, 0, &style) == FcResultMatch) 773 printf ("Font %s:%s matches language %d\n", family, style, i); 774 } 775 patternLangSat[i] = FcTrue; 776 satisfies = FcTrue; 777 break; 778 } 779 } 780 } 781 } 782 if (!satisfies) 783 nodeps[f]->score[MATCH_LANG_INDEX] = 10000.0; 784 } 785 786 /* 787 * Re-sort once the language issues have been settled 788 */ 789 qsort (nodeps, nnodes, sizeof (FcSortNode *), 790 FcSortCompare); 791 792 ret = FcFontSetCreate (); 793 if (!ret) 794 goto bail1; 795 796 if (!FcSortWalk (nodeps, nnodes, ret, csp, trim)) 797 goto bail2; 798 799 free (nodes); 800 801 if (FcDebug() & FC_DBG_MATCH) 802 { 803 printf ("First font "); 804 FcPatternPrint (ret->fonts[0]); 805 } 806 return ret; 807 808bail2: 809 FcFontSetDestroy (ret); 810bail1: 811 free (nodes); 812bail0: 813 return 0; 814} 815 816FcFontSet * 817FcFontSort (FcConfig *config, 818 FcPattern *p, 819 FcBool trim, 820 FcCharSet **csp, 821 FcResult *result) 822{ 823 FcFontSet *sets[2]; 824 int nsets; 825 826 if (!config) 827 { 828 config = FcConfigGetCurrent (); 829 if (!config) 830 return 0; 831 } 832 nsets = 0; 833 if (config->fonts[FcSetSystem]) 834 sets[nsets++] = config->fonts[FcSetSystem]; 835 if (config->fonts[FcSetApplication]) 836 sets[nsets++] = config->fonts[FcSetApplication]; 837 return FcFontSetSort (config, sets, nsets, p, trim, csp, result); 838} 839#define __fcmatch__ 840#include "fcaliastail.h" 841#undef __fcmatch__ 842