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