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