Replace 'KEITH PACKARD' with 'THE AUTHOR(S)' in license text in all files
[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  * 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
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 **csp, FcBool trim)
610 {
611     FcBool ret = FcFalse;
612     FcCharSet *cs;
613
614     cs = 0;
615     if (trim || csp)
616     {
617         cs = FcCharSetCreate ();
618         if (cs == NULL)
619             goto bail;
620     }
621
622     while (nnode--)
623     {
624         FcSortNode      *node = *n++;
625         FcBool          adds_chars = FcFalse;
626
627         /*
628          * Only fetch node charset if we'd need it
629          */
630         if (cs)
631         {
632             FcCharSet   *ncs;
633
634             if (FcPatternGetCharSet (node->pattern, FC_CHARSET, 0, &ncs) !=
635                 FcResultMatch)
636                 continue;
637
638             if (!FcCharSetMerge (cs, ncs, &adds_chars))
639                 goto bail;
640         }
641
642         /*
643          * If this font isn't a subset of the previous fonts,
644          * add it to the list
645          */
646         if (!trim || adds_chars)
647         {
648             FcPatternReference (node->pattern);
649             if (FcDebug () & FC_DBG_MATCHV)
650             {
651                 printf ("Add ");
652                 FcPatternPrint (node->pattern);
653             }
654             if (!FcFontSetAdd (fs, node->pattern))
655             {
656                 FcPatternDestroy (node->pattern);
657                 goto bail;
658             }
659         }
660     }
661     if (csp)
662     {
663         *csp = cs;
664         cs = 0;
665     }
666
667     ret = FcTrue;
668
669 bail:
670     if (cs)
671         FcCharSetDestroy (cs);
672
673     return ret;
674 }
675
676 void
677 FcFontSetSortDestroy (FcFontSet *fs)
678 {
679     FcFontSetDestroy (fs);
680 }
681
682 FcFontSet *
683 FcFontSetSort (FcConfig     *config,
684                FcFontSet    **sets,
685                int          nsets,
686                FcPattern    *p,
687                FcBool       trim,
688                FcCharSet    **csp,
689                FcResult     *result)
690 {
691     FcFontSet       *ret;
692     FcFontSet       *s;
693     FcSortNode      *nodes;
694     FcSortNode      **nodeps, **nodep;
695     int             nnodes;
696     FcSortNode      *new;
697     int             set;
698     int             f;
699     int             i;
700     int             nPatternLang;
701     FcBool          *patternLangSat;
702     FcValue         patternLang;
703
704     if (FcDebug () & FC_DBG_MATCH)
705     {
706         printf ("Sort ");
707         FcPatternPrint (p);
708     }
709     nnodes = 0;
710     for (set = 0; set < nsets; set++)
711     {
712         s = sets[set];
713         if (!s)
714             continue;
715         nnodes += s->nfont;
716     }
717     if (!nnodes)
718         goto bail0;
719     
720     for (nPatternLang = 0;
721          FcPatternGet (p, FC_LANG, nPatternLang, &patternLang) == FcResultMatch;
722          nPatternLang++)
723         ;
724         
725     /* freed below */
726     nodes = malloc (nnodes * sizeof (FcSortNode) + 
727                     nnodes * sizeof (FcSortNode *) +
728                     nPatternLang * sizeof (FcBool));
729     if (!nodes)
730         goto bail0;
731     nodeps = (FcSortNode **) (nodes + nnodes);
732     patternLangSat = (FcBool *) (nodeps + nnodes);
733     
734     new = nodes;
735     nodep = nodeps;
736     for (set = 0; set < nsets; set++)
737     {
738         s = sets[set];
739         if (!s)
740             continue;
741         for (f = 0; f < s->nfont; f++)
742         {
743             if (FcDebug () & FC_DBG_MATCHV)
744             {
745                 printf ("Font %d ", f);
746                 FcPatternPrint (s->fonts[f]);
747             }
748             new->pattern = s->fonts[f];
749             if (!FcCompare (p, new->pattern, new->score, result))
750                 goto bail1;
751             if (FcDebug () & FC_DBG_MATCHV)
752             {
753                 printf ("Score");
754                 for (i = 0; i < NUM_MATCH_VALUES; i++)
755                 {
756                     printf (" %g", new->score[i]);
757                 }
758                 printf ("\n");
759             }
760             *nodep = new;
761             new++;
762             nodep++;
763         }
764     }
765
766     nnodes = new - nodes;
767     
768     qsort (nodeps, nnodes, sizeof (FcSortNode *),
769            FcSortCompare);
770     
771     for (i = 0; i < nPatternLang; i++)
772         patternLangSat[i] = FcFalse;
773     
774     for (f = 0; f < nnodes; f++)
775     {
776         FcBool  satisfies = FcFalse;
777         /*
778          * If this node matches any language, go check
779          * which ones and satisfy those entries
780          */
781         if (nodeps[f]->score[MATCH_LANG_INDEX] < 200)
782         {
783             for (i = 0; i < nPatternLang; i++)
784             {
785                 FcValue     nodeLang;
786                 
787                 if (!patternLangSat[i] &&
788                     FcPatternGet (p, FC_LANG, i, &patternLang) == FcResultMatch &&
789                     FcPatternGet (nodeps[f]->pattern, FC_LANG, 0, &nodeLang) == FcResultMatch)
790                 {
791                     double  compare = FcCompareLang (&patternLang, &nodeLang);
792                     if (compare >= 0 && compare < 2)
793                     {
794                         if (FcDebug () & FC_DBG_MATCHV)
795                         {
796                             FcChar8 *family;
797                             FcChar8 *style;
798
799                             if (FcPatternGetString (nodeps[f]->pattern, FC_FAMILY, 0, &family) == FcResultMatch &&
800                                 FcPatternGetString (nodeps[f]->pattern, FC_STYLE, 0, &style) == FcResultMatch)
801                                 printf ("Font %s:%s matches language %d\n", family, style, i);
802                         }
803                         patternLangSat[i] = FcTrue;
804                         satisfies = FcTrue;
805                         break;
806                     }
807                 }
808             }
809         }
810         if (!satisfies)
811             nodeps[f]->score[MATCH_LANG_INDEX] = 10000.0;
812     }
813
814     /*
815      * Re-sort once the language issues have been settled
816      */
817     qsort (nodeps, nnodes, sizeof (FcSortNode *),
818            FcSortCompare);
819
820     ret = FcFontSetCreate ();
821     if (!ret)
822         goto bail1;
823
824     if (!FcSortWalk (nodeps, nnodes, ret, csp, trim))
825         goto bail2;
826
827     free (nodes);
828
829     if (FcDebug() & FC_DBG_MATCH)
830     {
831         printf ("First font ");
832         FcPatternPrint (ret->fonts[0]);
833     }
834     return ret;
835
836 bail2:
837     FcFontSetDestroy (ret);
838 bail1:
839     free (nodes);
840 bail0:
841     return 0;
842 }
843
844 FcFontSet *
845 FcFontSort (FcConfig    *config,
846             FcPattern   *p, 
847             FcBool      trim,
848             FcCharSet   **csp,
849             FcResult    *result)
850 {
851     FcFontSet   *sets[2];
852     int         nsets;
853
854     if (!config)
855     {
856         config = FcConfigGetCurrent ();
857         if (!config)
858             return 0;
859     }
860     nsets = 0;
861     if (config->fonts[FcSetSystem])
862         sets[nsets++] = config->fonts[FcSetSystem];
863     if (config->fonts[FcSetApplication])
864         sets[nsets++] = config->fonts[FcSetApplication];
865     return FcFontSetSort (config, sets, nsets, p, trim, csp, result);
866 }
867 #define __fcmatch__
868 #include "fcaliastail.h"
869 #undef __fcmatch__