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