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