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