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