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