fcmatch.c revision d91dd368
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 the author(s) not be used in
11 * advertising or publicity pertaining to distribution of the software without
12 * specific, written prior permission.  The authors make 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 <float.h>
27
28
29static double
30FcCompareNumber (FcValue *value1, FcValue *value2)
31{
32    double  v1, v2, v;
33
34    switch ((int) value1->type) {
35    case FcTypeInteger:
36	v1 = (double) value1->u.i;
37	break;
38    case FcTypeDouble:
39	v1 = value1->u.d;
40	break;
41    default:
42	return -1.0;
43    }
44    switch ((int) value2->type) {
45    case FcTypeInteger:
46	v2 = (double) value2->u.i;
47	break;
48    case FcTypeDouble:
49	v2 = value2->u.d;
50	break;
51    default:
52	return -1.0;
53    }
54    v = v2 - v1;
55    if (v < 0)
56	v = -v;
57    return v;
58}
59
60static double
61FcCompareString (FcValue *v1, FcValue *v2)
62{
63    return (double) FcStrCmpIgnoreCase (FcValueString(v1), FcValueString(v2)) != 0;
64}
65
66static double
67FcCompareFamily (FcValue *v1, FcValue *v2)
68{
69    /* rely on the guarantee in FcPatternObjectAddWithBinding that
70     * families are always FcTypeString. */
71    const FcChar8* v1_string = FcValueString(v1);
72    const FcChar8* v2_string = FcValueString(v2);
73
74    if (FcToLower(*v1_string) != FcToLower(*v2_string) &&
75	*v1_string != ' ' && *v2_string != ' ')
76       return 1.0;
77
78    return (double) FcStrCmpIgnoreBlanksAndCase (v1_string, v2_string) != 0;
79}
80
81static double
82FcComparePostScript (FcValue *v1, FcValue *v2)
83{
84    const FcChar8 *v1_string = FcValueString (v1);
85    const FcChar8 *v2_string = FcValueString (v2);
86    int n;
87    size_t len;
88
89    if (FcToLower (*v1_string) != FcToLower (*v2_string) &&
90	*v1_string != ' ' && *v2_string != ' ')
91	return 1.0;
92
93    n = FcStrMatchIgnoreCaseAndDelims (v1_string, v2_string, (const FcChar8 *)" -");
94    len = strlen ((const char *)v1_string);
95
96    return (double)(len - n) / (double)len;
97}
98
99static double
100FcCompareLang (FcValue *v1, FcValue *v2)
101{
102    FcLangResult    result;
103    FcValue value1 = FcValueCanonicalize(v1), value2 = FcValueCanonicalize(v2);
104
105    switch ((int) value1.type) {
106    case FcTypeLangSet:
107	switch ((int) value2.type) {
108	case FcTypeLangSet:
109	    result = FcLangSetCompare (value1.u.l, value2.u.l);
110	    break;
111	case FcTypeString:
112	    result = FcLangSetHasLang (value1.u.l,
113				       value2.u.s);
114	    break;
115	default:
116	    return -1.0;
117	}
118	break;
119    case FcTypeString:
120	switch ((int) value2.type) {
121	case FcTypeLangSet:
122	    result = FcLangSetHasLang (value2.u.l, value1.u.s);
123	    break;
124	case FcTypeString:
125	    result = FcLangCompare (value1.u.s,
126				    value2.u.s);
127	    break;
128	default:
129	    return -1.0;
130	}
131	break;
132    default:
133	return -1.0;
134    }
135    switch (result) {
136    case FcLangEqual:
137	return 0;
138    case FcLangDifferentCountry:
139	return 1;
140    case FcLangDifferentLang:
141    default:
142	return 2;
143    }
144}
145
146static double
147FcCompareBool (FcValue *v1, FcValue *v2)
148{
149    if (v2->type != FcTypeBool || v1->type != FcTypeBool)
150	return -1.0;
151    return (double) v2->u.b != v1->u.b;
152}
153
154static double
155FcCompareCharSet (FcValue *v1, FcValue *v2)
156{
157    return (double) FcCharSetSubtractCount (FcValueCharSet(v1), FcValueCharSet(v2));
158}
159
160static double
161FcCompareSize (FcValue *value1, FcValue *value2)
162{
163    double  v1, v2, v;
164
165    switch ((int) value1->type) {
166    case FcTypeInteger:
167	v1 = value1->u.i;
168	break;
169    case FcTypeDouble:
170	v1 = value1->u.d;
171	break;
172    default:
173	return -1;
174    }
175    switch ((int) value2->type) {
176    case FcTypeInteger:
177	v2 = value2->u.i;
178	break;
179    case FcTypeDouble:
180	v2 = value2->u.d;
181	break;
182    default:
183	return -1;
184    }
185    if (v2 == 0)
186	return 0;
187    v = v2 - v1;
188    if (v < 0)
189	v = -v;
190    return v;
191}
192
193static double
194FcCompareFilename (FcValue *v1, FcValue *v2)
195{
196    const FcChar8 *s1 = FcValueString (v1), *s2 = FcValueString (v2);
197    if (FcStrCmp (s1, s2) == 0)
198	return 0.0;
199    else if (FcStrCmpIgnoreCase (s1, s2) == 0)
200	return 1.0;
201    else if (FcStrGlobMatch (s1, s2))
202	return 2.0;
203    else
204	return 3.0;
205}
206
207static double
208FcCompareHash (FcValue *v1, FcValue *v2)
209{
210    const FcChar8 *s1 = FcValueString (v1), *s2 = FcValueString (v2);
211
212    /* Do not match an empty string */
213    if (!s1 || !s2 || !s1[0] || !s2[0])
214	return 1.0;
215    return FcCompareString (v1, v2);
216}
217
218#define PRI_NULL(n)				\
219    PRI_ ## n ## _STRONG = -1,			\
220    PRI_ ## n ## _WEAK = -1,
221#define PRI1(n)
222#define PRI_FcCompareFamily(n)		PRI1(n)
223#define PRI_FcCompareString(n)		PRI1(n)
224#define PRI_FcCompareNumber(n)		PRI1(n)
225#define PRI_FcCompareSize(n)		PRI1(n)
226#define PRI_FcCompareBool(n)		PRI1(n)
227#define PRI_FcCompareFilename(n)	PRI1(n)
228#define PRI_FcCompareCharSet(n)		PRI1(n)
229#define PRI_FcCompareLang(n)		PRI1(n)
230#define PRI_FcComparePostScript(n)	PRI1(n)
231#define PRI_FcCompareHash(n)		PRI1(n)
232
233#define FC_OBJECT(NAME, Type, Cmp)	PRI_##Cmp(NAME)
234
235typedef enum _FcMatcherPriorityDummy {
236#include "fcobjs.h"
237} FcMatcherPriorityDummy;
238
239#undef FC_OBJECT
240
241#undef PRI1
242#define PRI1(n)					\
243    PRI_ ## n,					\
244    PRI_ ## n ## _STRONG = PRI_ ## n,		\
245    PRI_ ## n ## _WEAK = PRI_ ## n
246
247typedef enum _FcMatcherPriority {
248    PRI1(HASH),
249    PRI1(FILE),
250    PRI1(FONTFORMAT),
251    PRI1(SCALABLE),
252    PRI1(FOUNDRY),
253    PRI1(CHARSET),
254    PRI_FAMILY_STRONG,
255    PRI_POSTSCRIPT_NAME_STRONG,
256    PRI1(LANG),
257    PRI_FAMILY_WEAK,
258    PRI_POSTSCRIPT_NAME_WEAK,
259    PRI1(SPACING),
260    PRI1(PIXEL_SIZE),
261    PRI1(STYLE),
262    PRI1(SLANT),
263    PRI1(WEIGHT),
264    PRI1(WIDTH),
265    PRI1(DECORATIVE),
266    PRI1(ANTIALIAS),
267    PRI1(RASTERIZER),
268    PRI1(OUTLINE),
269    PRI1(FONTVERSION),
270    PRI_END
271} FcMatcherPriority;
272
273#undef PRI1
274
275typedef struct _FcMatcher {
276    FcObject object;
277    double   (*compare) (FcValue *value1, FcValue *value2);
278    int      strong, weak;
279} FcMatcher;
280
281/*
282 * Order is significant, it defines the precedence of
283 * each value, earlier values are more significant than
284 * later values
285 */
286#define FC_OBJECT(NAME, Type, Cmp)	{ FC_##NAME##_OBJECT,	Cmp,	PRI_##NAME##_STRONG,	PRI_##NAME##_WEAK },
287static const FcMatcher _FcMatchers [] = {
288    { FC_INVALID_OBJECT, NULL, -1, -1 },
289#include "fcobjs.h"
290};
291#undef FC_OBJECT
292
293static const FcMatcher*
294FcObjectToMatcher (FcObject object,
295		   FcBool   include_lang)
296{
297    if (include_lang)
298    {
299	switch (object) {
300	case FC_FAMILYLANG_OBJECT:
301	case FC_STYLELANG_OBJECT:
302	case FC_FULLNAMELANG_OBJECT:
303	    object = FC_LANG_OBJECT;
304	    break;
305	}
306    }
307    if (object > FC_MAX_BASE_OBJECT ||
308	!_FcMatchers[object].compare ||
309	_FcMatchers[object].strong == -1 ||
310	_FcMatchers[object].weak == -1)
311	return NULL;
312
313    return _FcMatchers + object;
314}
315
316static FcBool
317FcCompareValueList (FcObject	     object,
318		    const FcMatcher *match,
319		    FcValueListPtr   v1orig,	/* pattern */
320		    FcValueListPtr   v2orig,	/* target */
321		    FcValue         *bestValue,
322		    double          *value,
323		    int             *n,
324		    FcResult        *result)
325{
326    FcValueListPtr  v1, v2;
327    double    	    v, best, bestStrong, bestWeak;
328    int		    j, k, pos = 0;
329
330    if (!match)
331    {
332	if (bestValue)
333	    *bestValue = FcValueCanonicalize(&v2orig->value);
334	if (n)
335	    *n = 0;
336	return FcTrue;
337    }
338
339    best = DBL_MAX;
340    bestStrong = DBL_MAX;
341    bestWeak = DBL_MAX;
342    j = 1;
343    for (v1 = v1orig; v1; v1 = FcValueListNext(v1))
344    {
345	for (v2 = v2orig, k = 0; v2; v2 = FcValueListNext(v2), k++)
346	{
347	    v = (match->compare) (&v1->value, &v2->value);
348	    if (v < 0)
349	    {
350		*result = FcResultTypeMismatch;
351		return FcFalse;
352	    }
353	    v = v * 1000 + j;
354	    if (v < best)
355	    {
356		if (bestValue)
357		    *bestValue = FcValueCanonicalize(&v2->value);
358		best = v;
359		pos = k;
360	    }
361	    if (v1->binding == FcValueBindingStrong)
362	    {
363		if (v < bestStrong)
364		    bestStrong = v;
365	    }
366	    else
367	    {
368		if (v < bestWeak)
369		    bestWeak = v;
370	    }
371	}
372	j++;
373    }
374    if (FcDebug () & FC_DBG_MATCHV)
375    {
376	printf (" %s: %g ", FcObjectName (object), best);
377	FcValueListPrint (v1orig);
378	printf (", ");
379	FcValueListPrint (v2orig);
380	printf ("\n");
381    }
382    if (value)
383    {
384	int weak    = match->weak;
385	int strong  = match->strong;
386	if (weak == strong)
387	    value[strong] += best;
388	else
389	{
390	    value[weak] += bestWeak;
391	    value[strong] += bestStrong;
392	}
393    }
394    if (n)
395	*n = pos;
396
397    return FcTrue;
398}
399
400/*
401 * Return a value indicating the distance between the two lists of
402 * values
403 */
404
405static FcBool
406FcCompare (FcPattern	*pat,
407	   FcPattern	*fnt,
408	   double	*value,
409	   FcResult	*result)
410{
411    int		    i, i1, i2;
412
413    for (i = 0; i < PRI_END; i++)
414	value[i] = 0.0;
415
416    i1 = 0;
417    i2 = 0;
418    while (i1 < pat->num && i2 < fnt->num)
419    {
420	FcPatternElt *elt_i1 = &FcPatternElts(pat)[i1];
421	FcPatternElt *elt_i2 = &FcPatternElts(fnt)[i2];
422
423	i = FcObjectCompare(elt_i1->object, elt_i2->object);
424	if (i > 0)
425	    i2++;
426	else if (i < 0)
427	    i1++;
428	else
429	{
430	    const FcMatcher *match = FcObjectToMatcher (elt_i1->object, FcFalse);
431	    if (!FcCompareValueList (elt_i1->object, match,
432				     FcPatternEltValues(elt_i1),
433				     FcPatternEltValues(elt_i2),
434				     NULL, value, NULL, result))
435		return FcFalse;
436	    i1++;
437	    i2++;
438	}
439    }
440    return FcTrue;
441}
442
443FcPattern *
444FcFontRenderPrepare (FcConfig	    *config,
445		     FcPattern	    *pat,
446		     FcPattern	    *font)
447{
448    FcPattern	    *new;
449    int		    i;
450    FcPatternElt    *fe, *pe, *fel, *pel;
451    FcValue	    v;
452    FcResult	    result;
453
454    assert (pat != NULL);
455    assert (font != NULL);
456
457    new = FcPatternCreate ();
458    if (!new)
459	return NULL;
460    for (i = 0; i < font->num; i++)
461    {
462	fe = &FcPatternElts(font)[i];
463	if (fe->object == FC_FAMILYLANG_OBJECT ||
464	    fe->object == FC_STYLELANG_OBJECT ||
465	    fe->object == FC_FULLNAMELANG_OBJECT)
466	{
467	    /* ignore those objects. we need to deal with them
468	     * another way */
469	    continue;
470	}
471	if (fe->object == FC_FAMILY_OBJECT ||
472	    fe->object == FC_STYLE_OBJECT ||
473	    fe->object == FC_FULLNAME_OBJECT)
474	{
475	    FC_ASSERT_STATIC ((FC_FAMILY_OBJECT + 1) == FC_FAMILYLANG_OBJECT);
476	    FC_ASSERT_STATIC ((FC_STYLE_OBJECT + 1) == FC_STYLELANG_OBJECT);
477	    FC_ASSERT_STATIC ((FC_FULLNAME_OBJECT + 1) == FC_FULLNAMELANG_OBJECT);
478
479	    fel = FcPatternObjectFindElt (font, fe->object + 1);
480	    pel = FcPatternObjectFindElt (pat, fe->object + 1);
481	}
482	else
483	{
484	    fel = NULL;
485	    pel = NULL;
486	}
487	pe = FcPatternObjectFindElt (pat, fe->object);
488	if (pe)
489	{
490	    const FcMatcher *match = FcObjectToMatcher (pe->object, FcFalse);
491
492	    if (!FcCompareValueList (pe->object, match,
493				     FcPatternEltValues(pe),
494				     FcPatternEltValues(fe), &v, NULL, NULL, &result))
495	    {
496		FcPatternDestroy (new);
497		return NULL;
498	    }
499	    if (fel && pel)
500	    {
501		int n = 1, j;
502		FcValueListPtr l1, l2, ln = NULL, ll = NULL;
503
504		match = FcObjectToMatcher (pel->object, FcTrue);
505		if (!FcCompareValueList (pel->object, match,
506					 FcPatternEltValues (pel),
507					 FcPatternEltValues (fel), NULL, NULL, &n, &result))
508		{
509		    FcPatternDestroy (new);
510		    return NULL;
511		}
512
513		for (j = 0, l1 = FcPatternEltValues (fe), l2 = FcPatternEltValues (fel);
514		     l1 != NULL || l2 != NULL;
515		     j++, l1 = l1 ? FcValueListNext (l1) : NULL, l2 = l2 ? FcValueListNext (l2) : NULL)
516		{
517		    if (j == n)
518		    {
519			if (l1)
520			    ln = FcValueListPrepend (ln,
521						     FcValueCanonicalize (&l1->value),
522						     FcValueBindingStrong);
523			if (l2)
524			    ll = FcValueListPrepend (ll,
525						     FcValueCanonicalize (&l2->value),
526						     FcValueBindingStrong);
527		    }
528		    else
529		    {
530			if (l1)
531			    ln = FcValueListAppend (ln,
532						    FcValueCanonicalize (&l1->value),
533						    FcValueBindingStrong);
534			if (l2)
535			    ll = FcValueListAppend (ll,
536						    FcValueCanonicalize (&l2->value),
537						    FcValueBindingStrong);
538		    }
539		}
540		FcPatternObjectListAdd (new, fe->object, ln, FcFalse);
541		FcPatternObjectListAdd (new, fel->object, ll, FcFalse);
542
543		continue;
544	    }
545	    else if (fel)
546	    {
547		FcValueListPtr l1, l2;
548
549	    copy_lang:
550		l1 = FcValueListDuplicate (FcPatternEltValues (fe));
551		l2 = FcValueListDuplicate (FcPatternEltValues (fel));
552		FcPatternObjectListAdd (new, fe->object, l1, FcFalse);
553		FcPatternObjectListAdd (new, fel->object, l2, FcFalse);
554
555		continue;
556	    }
557	    FcPatternObjectAdd (new, fe->object, v, FcFalse);
558	}
559	else
560	{
561	    if (fel)
562		goto copy_lang;
563	    FcPatternObjectListAdd (new, fe->object,
564				    FcValueListDuplicate (FcPatternEltValues (fe)),
565				    FcTrue);
566	}
567    }
568    for (i = 0; i < pat->num; i++)
569    {
570	pe = &FcPatternElts(pat)[i];
571	fe = FcPatternObjectFindElt (font, pe->object);
572	if (!fe &&
573	    pe->object != FC_FAMILYLANG_OBJECT &&
574	    pe->object != FC_STYLELANG_OBJECT &&
575	    pe->object != FC_FULLNAMELANG_OBJECT)
576	{
577	    FcPatternObjectListAdd (new, pe->object,
578				    FcValueListDuplicate (FcPatternEltValues(pe)),
579				    FcFalse);
580	}
581    }
582
583    FcConfigSubstituteWithPat (config, new, pat, FcMatchFont);
584    return new;
585}
586
587static FcPattern *
588FcFontSetMatchInternal (FcFontSet   **sets,
589			int	    nsets,
590			FcPattern   *p,
591			FcResult    *result)
592{
593    double    	    score[PRI_END], bestscore[PRI_END];
594    int		    f;
595    FcFontSet	    *s;
596    FcPattern	    *best;
597    int		    i;
598    int		    set;
599
600    for (i = 0; i < PRI_END; i++)
601	bestscore[i] = 0;
602    best = 0;
603    if (FcDebug () & FC_DBG_MATCH)
604    {
605	printf ("Match ");
606	FcPatternPrint (p);
607    }
608    for (set = 0; set < nsets; set++)
609    {
610	s = sets[set];
611	if (!s)
612	    continue;
613	for (f = 0; f < s->nfont; f++)
614	{
615	    if (FcDebug () & FC_DBG_MATCHV)
616	    {
617		printf ("Font %d ", f);
618		FcPatternPrint (s->fonts[f]);
619	    }
620	    if (!FcCompare (p, s->fonts[f], score, result))
621		return 0;
622	    if (FcDebug () & FC_DBG_MATCHV)
623	    {
624		printf ("Score");
625		for (i = 0; i < PRI_END; i++)
626		{
627		    printf (" %g", score[i]);
628		}
629		printf ("\n");
630	    }
631	    for (i = 0; i < PRI_END; i++)
632	    {
633		if (best && bestscore[i] < score[i])
634		    break;
635		if (!best || score[i] < bestscore[i])
636		{
637		    for (i = 0; i < PRI_END; i++)
638			bestscore[i] = score[i];
639		    best = s->fonts[f];
640		    break;
641		}
642	    }
643	}
644    }
645    if (FcDebug () & FC_DBG_MATCH)
646    {
647	printf ("Best score");
648	for (i = 0; i < PRI_END; i++)
649	    printf (" %g", bestscore[i]);
650	printf ("\n");
651	FcPatternPrint (best);
652    }
653    /* assuming that 'result' is initialized with FcResultNoMatch
654     * outside this function */
655    if (best)
656	*result = FcResultMatch;
657
658    return best;
659}
660
661FcPattern *
662FcFontSetMatch (FcConfig    *config,
663		FcFontSet   **sets,
664		int	    nsets,
665		FcPattern   *p,
666		FcResult    *result)
667{
668    FcPattern	    *best;
669
670    assert (sets != NULL);
671    assert (p != NULL);
672    assert (result != NULL);
673
674    *result = FcResultNoMatch;
675
676    if (!config)
677    {
678	config = FcConfigGetCurrent ();
679	if (!config)
680	    return 0;
681    }
682    best = FcFontSetMatchInternal (sets, nsets, p, result);
683    if (best)
684	return FcFontRenderPrepare (config, p, best);
685    else
686	return NULL;
687}
688
689FcPattern *
690FcFontMatch (FcConfig	*config,
691	     FcPattern	*p,
692	     FcResult	*result)
693{
694    FcFontSet	*sets[2];
695    int		nsets;
696    FcPattern   *best;
697
698    assert (p != NULL);
699    assert (result != NULL);
700
701    *result = FcResultNoMatch;
702
703    if (!config)
704    {
705	config = FcConfigGetCurrent ();
706	if (!config)
707	    return 0;
708    }
709    nsets = 0;
710    if (config->fonts[FcSetSystem])
711	sets[nsets++] = config->fonts[FcSetSystem];
712    if (config->fonts[FcSetApplication])
713	sets[nsets++] = config->fonts[FcSetApplication];
714
715    best = FcFontSetMatchInternal (sets, nsets, p, result);
716    if (best)
717	return FcFontRenderPrepare (config, p, best);
718    else
719	return NULL;
720}
721
722typedef struct _FcSortNode {
723    FcPattern	*pattern;
724    double	score[PRI_END];
725} FcSortNode;
726
727static int
728FcSortCompare (const void *aa, const void *ab)
729{
730    FcSortNode  *a = *(FcSortNode **) aa;
731    FcSortNode  *b = *(FcSortNode **) ab;
732    double	*as = &a->score[0];
733    double	*bs = &b->score[0];
734    double	ad = 0, bd = 0;
735    int         i;
736
737    i = PRI_END;
738    while (i-- && (ad = *as++) == (bd = *bs++))
739	;
740    return ad < bd ? -1 : ad > bd ? 1 : 0;
741}
742
743static FcBool
744FcSortWalk (FcSortNode **n, int nnode, FcFontSet *fs, FcCharSet **csp, FcBool trim)
745{
746    FcBool ret = FcFalse;
747    FcCharSet *cs;
748
749    cs = 0;
750    if (trim || csp)
751    {
752	cs = FcCharSetCreate ();
753	if (cs == NULL)
754	    goto bail;
755    }
756
757    while (nnode--)
758    {
759	FcSortNode	*node = *n++;
760	FcBool		adds_chars = FcFalse;
761
762	/*
763	 * Only fetch node charset if we'd need it
764	 */
765	if (cs)
766	{
767	    FcCharSet	*ncs;
768
769	    if (FcPatternGetCharSet (node->pattern, FC_CHARSET, 0, &ncs) !=
770		FcResultMatch)
771	        continue;
772
773	    if (!FcCharSetMerge (cs, ncs, &adds_chars))
774		goto bail;
775	}
776
777	/*
778	 * If this font isn't a subset of the previous fonts,
779	 * add it to the list
780	 */
781	if (!trim || adds_chars)
782	{
783	    FcPatternReference (node->pattern);
784	    if (FcDebug () & FC_DBG_MATCHV)
785	    {
786		printf ("Add ");
787		FcPatternPrint (node->pattern);
788	    }
789	    if (!FcFontSetAdd (fs, node->pattern))
790	    {
791		FcPatternDestroy (node->pattern);
792		goto bail;
793	    }
794	}
795    }
796    if (csp)
797    {
798	*csp = cs;
799	cs = 0;
800    }
801
802    ret = FcTrue;
803
804bail:
805    if (cs)
806	FcCharSetDestroy (cs);
807
808    return ret;
809}
810
811void
812FcFontSetSortDestroy (FcFontSet *fs)
813{
814    FcFontSetDestroy (fs);
815}
816
817FcFontSet *
818FcFontSetSort (FcConfig	    *config FC_UNUSED,
819	       FcFontSet    **sets,
820	       int	    nsets,
821	       FcPattern    *p,
822	       FcBool	    trim,
823	       FcCharSet    **csp,
824	       FcResult	    *result)
825{
826    FcFontSet	    *ret;
827    FcFontSet	    *s;
828    FcSortNode	    *nodes;
829    FcSortNode	    **nodeps, **nodep;
830    int		    nnodes;
831    FcSortNode	    *new;
832    int		    set;
833    int		    f;
834    int		    i;
835    int		    nPatternLang;
836    FcBool    	    *patternLangSat;
837    FcValue	    patternLang;
838
839    assert (sets != NULL);
840    assert (p != NULL);
841    assert (result != NULL);
842
843    /* There are some implementation that relying on the result of
844     * "result" to check if the return value of FcFontSetSort
845     * is valid or not.
846     * So we should initialize it to the conservative way since
847     * this function doesn't return NULL anymore.
848     */
849    if (result)
850	*result = FcResultNoMatch;
851
852    if (FcDebug () & FC_DBG_MATCH)
853    {
854	printf ("Sort ");
855	FcPatternPrint (p);
856    }
857    nnodes = 0;
858    for (set = 0; set < nsets; set++)
859    {
860	s = sets[set];
861	if (!s)
862	    continue;
863	nnodes += s->nfont;
864    }
865    if (!nnodes)
866	return FcFontSetCreate ();
867
868    for (nPatternLang = 0;
869	 FcPatternGet (p, FC_LANG, nPatternLang, &patternLang) == FcResultMatch;
870	 nPatternLang++)
871	;
872
873    /* freed below */
874    nodes = malloc (nnodes * sizeof (FcSortNode) +
875		    nnodes * sizeof (FcSortNode *) +
876		    nPatternLang * sizeof (FcBool));
877    if (!nodes)
878	goto bail0;
879    nodeps = (FcSortNode **) (nodes + nnodes);
880    patternLangSat = (FcBool *) (nodeps + nnodes);
881
882    new = nodes;
883    nodep = nodeps;
884    for (set = 0; set < nsets; set++)
885    {
886	s = sets[set];
887	if (!s)
888	    continue;
889	for (f = 0; f < s->nfont; f++)
890	{
891	    if (FcDebug () & FC_DBG_MATCHV)
892	    {
893		printf ("Font %d ", f);
894		FcPatternPrint (s->fonts[f]);
895	    }
896	    new->pattern = s->fonts[f];
897	    if (!FcCompare (p, new->pattern, new->score, result))
898		goto bail1;
899	    if (FcDebug () & FC_DBG_MATCHV)
900	    {
901		printf ("Score");
902		for (i = 0; i < PRI_END; i++)
903		{
904		    printf (" %g", new->score[i]);
905		}
906		printf ("\n");
907	    }
908	    *nodep = new;
909	    new++;
910	    nodep++;
911	}
912    }
913
914    nnodes = new - nodes;
915
916    qsort (nodeps, nnodes, sizeof (FcSortNode *),
917	   FcSortCompare);
918
919    for (i = 0; i < nPatternLang; i++)
920	patternLangSat[i] = FcFalse;
921
922    for (f = 0; f < nnodes; f++)
923    {
924	FcBool	satisfies = FcFalse;
925	/*
926	 * If this node matches any language, go check
927	 * which ones and satisfy those entries
928	 */
929	if (nodeps[f]->score[PRI_LANG] < 2000)
930	{
931	    for (i = 0; i < nPatternLang; i++)
932	    {
933		FcValue	    nodeLang;
934
935		if (!patternLangSat[i] &&
936		    FcPatternGet (p, FC_LANG, i, &patternLang) == FcResultMatch &&
937		    FcPatternGet (nodeps[f]->pattern, FC_LANG, 0, &nodeLang) == FcResultMatch)
938		{
939		    double  compare = FcCompareLang (&patternLang, &nodeLang);
940		    if (compare >= 0 && compare < 2)
941		    {
942			if (FcDebug () & FC_DBG_MATCHV)
943			{
944			    FcChar8 *family;
945			    FcChar8 *style;
946
947			    if (FcPatternGetString (nodeps[f]->pattern, FC_FAMILY, 0, &family) == FcResultMatch &&
948				FcPatternGetString (nodeps[f]->pattern, FC_STYLE, 0, &style) == FcResultMatch)
949				printf ("Font %s:%s matches language %d\n", family, style, i);
950			}
951			patternLangSat[i] = FcTrue;
952			satisfies = FcTrue;
953			break;
954		    }
955		}
956	    }
957	}
958	if (!satisfies)
959	{
960	    nodeps[f]->score[PRI_LANG] = 10000.0;
961	}
962    }
963
964    /*
965     * Re-sort once the language issues have been settled
966     */
967    qsort (nodeps, nnodes, sizeof (FcSortNode *),
968	   FcSortCompare);
969
970    ret = FcFontSetCreate ();
971    if (!ret)
972	goto bail1;
973
974    if (!FcSortWalk (nodeps, nnodes, ret, csp, trim))
975	goto bail2;
976
977    free (nodes);
978
979    if (FcDebug() & FC_DBG_MATCH)
980    {
981	printf ("First font ");
982	FcPatternPrint (ret->fonts[0]);
983    }
984    if (ret->nfont > 0)
985	*result = FcResultMatch;
986
987    return ret;
988
989bail2:
990    FcFontSetDestroy (ret);
991bail1:
992    free (nodes);
993bail0:
994    return 0;
995}
996
997FcFontSet *
998FcFontSort (FcConfig	*config,
999	    FcPattern	*p,
1000	    FcBool	trim,
1001	    FcCharSet	**csp,
1002	    FcResult	*result)
1003{
1004    FcFontSet	*sets[2];
1005    int		nsets;
1006
1007    assert (p != NULL);
1008    assert (result != NULL);
1009
1010    *result = FcResultNoMatch;
1011
1012    if (!config)
1013    {
1014	config = FcConfigGetCurrent ();
1015	if (!config)
1016	    return 0;
1017    }
1018    nsets = 0;
1019    if (config->fonts[FcSetSystem])
1020	sets[nsets++] = config->fonts[FcSetSystem];
1021    if (config->fonts[FcSetApplication])
1022	sets[nsets++] = config->fonts[FcSetApplication];
1023    return FcFontSetSort (config, sets, nsets, p, trim, csp, result);
1024}
1025#define __fcmatch__
1026#include "fcaliastail.h"
1027#undef __fcmatch__
1028