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