fcmatch.c revision 1887081f
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 (const FcValue *value1, const FcValue *value2, FcValue *bestValue)
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    *bestValue = FcValueCanonicalize (value2);
58    return v;
59}
60
61static double
62FcCompareString (const FcValue *v1, const FcValue *v2, FcValue *bestValue)
63{
64    *bestValue = FcValueCanonicalize (v2);
65    return (double) FcStrCmpIgnoreCase (FcValueString(v1), FcValueString(v2)) != 0;
66}
67
68static double
69FcCompareFamily (const FcValue *v1, const FcValue *v2, FcValue *bestValue)
70{
71    /* rely on the guarantee in FcPatternObjectAddWithBinding that
72     * families are always FcTypeString. */
73    const FcChar8* v1_string = FcValueString(v1);
74    const FcChar8* v2_string = FcValueString(v2);
75
76    *bestValue = FcValueCanonicalize (v2);
77
78    if (FcToLower(*v1_string) != FcToLower(*v2_string) &&
79	*v1_string != ' ' && *v2_string != ' ')
80       return 1.0;
81
82    return (double) FcStrCmpIgnoreBlanksAndCase (v1_string, v2_string) != 0;
83}
84
85static double
86FcComparePostScript (const FcValue *v1, const FcValue *v2, FcValue *bestValue)
87{
88    const FcChar8 *v1_string = FcValueString (v1);
89    const FcChar8 *v2_string = FcValueString (v2);
90    int n;
91    size_t len;
92
93    *bestValue = FcValueCanonicalize (v2);
94
95    if (FcToLower (*v1_string) != FcToLower (*v2_string) &&
96	*v1_string != ' ' && *v2_string != ' ')
97	return 1.0;
98
99    n = FcStrMatchIgnoreCaseAndDelims (v1_string, v2_string, (const FcChar8 *)" -");
100    len = strlen ((const char *)v1_string);
101
102    return (double)(len - n) / (double)len;
103}
104
105static double
106FcCompareLang (const FcValue *v1, const FcValue *v2, FcValue *bestValue)
107{
108    FcLangResult    result;
109    FcValue value1 = FcValueCanonicalize(v1), value2 = FcValueCanonicalize(v2);
110
111    switch ((int) value1.type) {
112    case FcTypeLangSet:
113	switch ((int) value2.type) {
114	case FcTypeLangSet:
115	    result = FcLangSetCompare (value1.u.l, value2.u.l);
116	    break;
117	case FcTypeString:
118	    result = FcLangSetHasLang (value1.u.l,
119				       value2.u.s);
120	    break;
121	default:
122	    return -1.0;
123	}
124	break;
125    case FcTypeString:
126	switch ((int) value2.type) {
127	case FcTypeLangSet:
128	    result = FcLangSetHasLang (value2.u.l, value1.u.s);
129	    break;
130	case FcTypeString:
131	    result = FcLangCompare (value1.u.s,
132				    value2.u.s);
133	    break;
134	default:
135	    return -1.0;
136	}
137	break;
138    default:
139	return -1.0;
140    }
141    *bestValue = FcValueCanonicalize (v2);
142    switch (result) {
143    case FcLangEqual:
144	return 0;
145    case FcLangDifferentCountry:
146	return 1;
147    case FcLangDifferentLang:
148    default:
149	return 2;
150    }
151}
152
153static double
154FcCompareBool (const FcValue *v1, const FcValue *v2, FcValue *bestValue)
155{
156    if (v2->type != FcTypeBool || v1->type != FcTypeBool)
157	return -1.0;
158
159    if (v2->u.b != FcDontCare)
160	*bestValue = FcValueCanonicalize (v2);
161    else
162	*bestValue = FcValueCanonicalize (v1);
163
164    return (double) ((v2->u.b ^ v1->u.b) == 1);
165}
166
167static double
168FcCompareCharSet (const FcValue *v1, const FcValue *v2, FcValue *bestValue)
169{
170    *bestValue = FcValueCanonicalize (v2); /* TODO Improve. */
171    return (double) FcCharSetSubtractCount (FcValueCharSet(v1), FcValueCharSet(v2));
172}
173
174static double
175FcCompareRange (const FcValue *v1, const FcValue *v2, FcValue *bestValue)
176{
177    FcValue value1 = FcValueCanonicalize (v1);
178    FcValue value2 = FcValueCanonicalize (v2);
179    double b1, e1, b2, e2, d;
180
181    switch ((int) value1.type) {
182    case FcTypeInteger:
183        b1 = e1 = value1.u.i;
184	break;
185    case FcTypeDouble:
186        b1 = e1 = value1.u.d;
187	break;
188    case FcTypeRange:
189	b1 = value1.u.r->begin;
190	e1 = value1.u.r->end;
191	break;
192    default:
193	return -1;
194    }
195    switch ((int) value2.type) {
196    case FcTypeInteger:
197        b2 = e2 = value2.u.i;
198	break;
199    case FcTypeDouble:
200        b2 = e2 = value2.u.d;
201	break;
202    case FcTypeRange:
203	b2 = value2.u.r->begin;
204	e2 = value2.u.r->end;
205	break;
206    default:
207	return -1;
208    }
209
210    if (e1 < b2)
211      d = b2;
212    else if (e2 < b1)
213      d = e2;
214    else
215      d = (FC_MAX (b1, b2) + FC_MIN (e1, e2)) * .5;
216
217    bestValue->type = FcTypeDouble;
218    bestValue->u.d = d;
219
220    /* If the ranges overlap, it's a match, otherwise return closest distance. */
221    if (e1 < b2 || e2 < b1)
222	return FC_MIN (fabs (b2 - e1), fabs (b1 - e2));
223    else
224	return 0.0;
225}
226
227static double
228FcCompareSize (const FcValue *v1, const FcValue *v2, FcValue *bestValue)
229{
230    FcValue value1 = FcValueCanonicalize (v1);
231    FcValue value2 = FcValueCanonicalize (v2);
232    double b1, e1, b2, e2;
233
234    switch ((int) value1.type) {
235    case FcTypeInteger:
236        b1 = e1 = value1.u.i;
237	break;
238    case FcTypeDouble:
239        b1 = e1 = value1.u.d;
240	break;
241    case FcTypeRange:
242	abort();
243	b1 = value1.u.r->begin;
244	e1 = value1.u.r->end;
245	break;
246    default:
247	return -1;
248    }
249    switch ((int) value2.type) {
250    case FcTypeInteger:
251        b2 = e2 = value2.u.i;
252	break;
253    case FcTypeDouble:
254        b2 = e2 = value2.u.d;
255	break;
256    case FcTypeRange:
257	b2 = value2.u.r->begin;
258	e2 = value2.u.r->end;
259	break;
260    default:
261	return -1;
262    }
263
264    bestValue->type = FcTypeDouble;
265    bestValue->u.d = (b1 + e1) * .5;
266
267    /* If the ranges overlap, it's a match, otherwise return closest distance. */
268    if (e1 < b2 || e2 < b1)
269	return FC_MIN (fabs (b2 - e1), fabs (b1 - e2));
270    if (b2 != e2 && b1 == e2) /* Semi-closed interval. */
271        return 1e-15;
272    else
273	return 0.0;
274}
275
276static double
277FcCompareFilename (const FcValue *v1, const FcValue *v2, FcValue *bestValue)
278{
279    const FcChar8 *s1 = FcValueString (v1), *s2 = FcValueString (v2);
280    *bestValue = FcValueCanonicalize (v2);
281    if (FcStrCmp (s1, s2) == 0)
282	return 0.0;
283    else if (FcStrCmpIgnoreCase (s1, s2) == 0)
284	return 1.0;
285    else if (FcStrGlobMatch (s1, s2))
286	return 2.0;
287    else
288	return 3.0;
289}
290
291
292/* Define priorities to -1 for objects that don't have a compare function. */
293
294#define PRI_NULL(n)				\
295    PRI_ ## n ## _STRONG = -1,			\
296    PRI_ ## n ## _WEAK = -1,
297#define PRI1(n)
298#define PRI_FcCompareFamily(n)		PRI1(n)
299#define PRI_FcCompareString(n)		PRI1(n)
300#define PRI_FcCompareNumber(n)		PRI1(n)
301#define PRI_FcCompareBool(n)		PRI1(n)
302#define PRI_FcCompareFilename(n)	PRI1(n)
303#define PRI_FcCompareCharSet(n)		PRI1(n)
304#define PRI_FcCompareLang(n)		PRI1(n)
305#define PRI_FcComparePostScript(n)	PRI1(n)
306#define PRI_FcCompareRange(n)		PRI1(n)
307#define PRI_FcCompareSize(n)		PRI1(n)
308
309#define FC_OBJECT(NAME, Type, Cmp)	PRI_##Cmp(NAME)
310
311typedef enum _FcMatcherPriorityDummy {
312#include "fcobjs.h"
313} FcMatcherPriorityDummy;
314
315#undef FC_OBJECT
316
317
318/* Canonical match priority order. */
319
320#undef PRI1
321#define PRI1(n)					\
322    PRI_ ## n,					\
323    PRI_ ## n ## _STRONG = PRI_ ## n,		\
324    PRI_ ## n ## _WEAK = PRI_ ## n
325
326typedef enum _FcMatcherPriority {
327    PRI1(FILE),
328    PRI1(FONTFORMAT),
329    PRI1(VARIABLE),
330    PRI1(SCALABLE),
331    PRI1(COLOR),
332    PRI1(FOUNDRY),
333    PRI1(CHARSET),
334    PRI_FAMILY_STRONG,
335    PRI_POSTSCRIPT_NAME_STRONG,
336    PRI1(LANG),
337    PRI_FAMILY_WEAK,
338    PRI_POSTSCRIPT_NAME_WEAK,
339    PRI1(SYMBOL),
340    PRI1(SPACING),
341    PRI1(SIZE),
342    PRI1(PIXEL_SIZE),
343    PRI1(STYLE),
344    PRI1(SLANT),
345    PRI1(WEIGHT),
346    PRI1(WIDTH),
347    PRI1(DECORATIVE),
348    PRI1(ANTIALIAS),
349    PRI1(RASTERIZER),
350    PRI1(OUTLINE),
351    PRI1(FONTVERSION),
352    PRI_END
353} FcMatcherPriority;
354
355#undef PRI1
356
357typedef struct _FcMatcher {
358    FcObject object;
359    double   (*compare) (const FcValue *v1, const FcValue *v2, FcValue *bestValue);
360    int      strong, weak;
361} FcMatcher;
362
363/*
364 * Order is significant, it defines the precedence of
365 * each value, earlier values are more significant than
366 * later values
367 */
368#define FC_OBJECT(NAME, Type, Cmp)	{ FC_##NAME##_OBJECT,	Cmp,	PRI_##NAME##_STRONG,	PRI_##NAME##_WEAK },
369static const FcMatcher _FcMatchers [] = {
370    { FC_INVALID_OBJECT, NULL, -1, -1 },
371#include "fcobjs.h"
372};
373#undef FC_OBJECT
374
375static const FcMatcher*
376FcObjectToMatcher (FcObject object,
377		   FcBool   include_lang)
378{
379    if (include_lang)
380    {
381	switch (object) {
382	case FC_FAMILYLANG_OBJECT:
383	case FC_STYLELANG_OBJECT:
384	case FC_FULLNAMELANG_OBJECT:
385	    object = FC_LANG_OBJECT;
386	    break;
387	}
388    }
389    if (object > FC_MAX_BASE_OBJECT ||
390	!_FcMatchers[object].compare ||
391	_FcMatchers[object].strong == -1 ||
392	_FcMatchers[object].weak == -1)
393	return NULL;
394
395    return _FcMatchers + object;
396}
397
398static FcBool
399FcCompareValueList (FcObject	     object,
400		    const FcMatcher *match,
401		    FcValueListPtr   v1orig,	/* pattern */
402		    FcValueListPtr   v2orig,	/* target */
403		    FcValue         *bestValue,
404		    double          *value,
405		    int             *n,
406		    FcResult        *result)
407{
408    FcValueListPtr  v1, v2;
409    double    	    v, best, bestStrong, bestWeak;
410    int		    j, k, pos = 0;
411
412    if (!match)
413    {
414	if (bestValue)
415	    *bestValue = FcValueCanonicalize(&v2orig->value);
416	if (n)
417	    *n = 0;
418	return FcTrue;
419    }
420
421    best = DBL_MAX;
422    bestStrong = DBL_MAX;
423    bestWeak = DBL_MAX;
424    j = 1;
425    for (v1 = v1orig; v1; v1 = FcValueListNext(v1))
426    {
427	for (v2 = v2orig, k = 0; v2; v2 = FcValueListNext(v2), k++)
428	{
429	    FcValue matchValue;
430	    v = (match->compare) (&v1->value, &v2->value, &matchValue);
431	    if (v < 0)
432	    {
433		*result = FcResultTypeMismatch;
434		return FcFalse;
435	    }
436	    v = v * 1000 + j;
437	    if (v < best)
438	    {
439		if (bestValue)
440		    *bestValue = matchValue;
441		best = v;
442		pos = k;
443	    }
444	    if (v1->binding == FcValueBindingStrong)
445	    {
446		if (v < bestStrong)
447		    bestStrong = v;
448	    }
449	    else
450	    {
451		if (v < bestWeak)
452		    bestWeak = v;
453	    }
454	}
455	j++;
456    }
457    if (FcDebug () & FC_DBG_MATCHV)
458    {
459	printf (" %s: %g ", FcObjectName (object), best);
460	FcValueListPrint (v1orig);
461	printf (", ");
462	FcValueListPrint (v2orig);
463	printf ("\n");
464    }
465    if (value)
466    {
467	int weak    = match->weak;
468	int strong  = match->strong;
469	if (weak == strong)
470	    value[strong] += best;
471	else
472	{
473	    value[weak] += bestWeak;
474	    value[strong] += bestStrong;
475	}
476    }
477    if (n)
478	*n = pos;
479
480    return FcTrue;
481}
482
483/*
484 * Return a value indicating the distance between the two lists of
485 * values
486 */
487
488static FcBool
489FcCompare (FcPattern	*pat,
490	   FcPattern	*fnt,
491	   double	*value,
492	   FcResult	*result)
493{
494    int		    i, i1, i2;
495
496    for (i = 0; i < PRI_END; i++)
497	value[i] = 0.0;
498
499    i1 = 0;
500    i2 = 0;
501    while (i1 < pat->num && i2 < fnt->num)
502    {
503	FcPatternElt *elt_i1 = &FcPatternElts(pat)[i1];
504	FcPatternElt *elt_i2 = &FcPatternElts(fnt)[i2];
505
506	i = FcObjectCompare(elt_i1->object, elt_i2->object);
507	if (i > 0)
508	    i2++;
509	else if (i < 0)
510	    i1++;
511	else
512	{
513	    const FcMatcher *match = FcObjectToMatcher (elt_i1->object, FcFalse);
514	    if (!FcCompareValueList (elt_i1->object, match,
515				     FcPatternEltValues(elt_i1),
516				     FcPatternEltValues(elt_i2),
517				     NULL, value, NULL, result))
518		return FcFalse;
519	    i1++;
520	    i2++;
521	}
522    }
523    return FcTrue;
524}
525
526FcPattern *
527FcFontRenderPrepare (FcConfig	    *config,
528		     FcPattern	    *pat,
529		     FcPattern	    *font)
530{
531    FcPattern	    *new;
532    int		    i;
533    FcPatternElt    *fe, *pe;
534    FcValue	    v;
535    FcResult	    result;
536    FcBool	    variable = FcFalse;
537    FcStrBuf        variations;
538
539    assert (pat != NULL);
540    assert (font != NULL);
541
542    FcPatternObjectGetBool (font, FC_VARIABLE_OBJECT, 0, &variable);
543    assert (variable != FcDontCare);
544    if (variable)
545	FcStrBufInit (&variations, NULL, 0);
546
547    new = FcPatternCreate ();
548    if (!new)
549	return NULL;
550    for (i = 0; i < font->num; i++)
551    {
552	fe = &FcPatternElts(font)[i];
553	if (fe->object == FC_FAMILYLANG_OBJECT ||
554	    fe->object == FC_STYLELANG_OBJECT ||
555	    fe->object == FC_FULLNAMELANG_OBJECT)
556	{
557	    /* ignore those objects. we need to deal with them
558	     * another way */
559	    continue;
560	}
561	if (fe->object == FC_FAMILY_OBJECT ||
562	    fe->object == FC_STYLE_OBJECT ||
563	    fe->object == FC_FULLNAME_OBJECT)
564	{
565	    FcPatternElt    *fel, *pel;
566
567	    FC_ASSERT_STATIC ((FC_FAMILY_OBJECT + 1) == FC_FAMILYLANG_OBJECT);
568	    FC_ASSERT_STATIC ((FC_STYLE_OBJECT + 1) == FC_STYLELANG_OBJECT);
569	    FC_ASSERT_STATIC ((FC_FULLNAME_OBJECT + 1) == FC_FULLNAMELANG_OBJECT);
570
571	    fel = FcPatternObjectFindElt (font, fe->object + 1);
572	    pel = FcPatternObjectFindElt (pat, fe->object + 1);
573
574	    if (fel && pel)
575	    {
576		/* The font has name languages, and pattern asks for specific language(s).
577		 * Match on language and and prefer that result.
578		 * Note:  Currently the code only give priority to first matching language.
579		 */
580		int n = 1, j;
581		FcValueListPtr l1, l2, ln = NULL, ll = NULL;
582		const FcMatcher *match = FcObjectToMatcher (pel->object, FcTrue);
583
584		if (!FcCompareValueList (pel->object, match,
585					 FcPatternEltValues (pel),
586					 FcPatternEltValues (fel), NULL, NULL, &n, &result))
587		{
588		    FcPatternDestroy (new);
589		    return NULL;
590		}
591
592		for (j = 0, l1 = FcPatternEltValues (fe), l2 = FcPatternEltValues (fel);
593		     l1 != NULL || l2 != NULL;
594		     j++, l1 = l1 ? FcValueListNext (l1) : NULL, l2 = l2 ? FcValueListNext (l2) : NULL)
595		{
596		    if (j == n)
597		    {
598			if (l1)
599			    ln = FcValueListPrepend (ln,
600						     FcValueCanonicalize (&l1->value),
601						     FcValueBindingStrong);
602			if (l2)
603			    ll = FcValueListPrepend (ll,
604						     FcValueCanonicalize (&l2->value),
605						     FcValueBindingStrong);
606		    }
607		    else
608		    {
609			if (l1)
610			    ln = FcValueListAppend (ln,
611						    FcValueCanonicalize (&l1->value),
612						    FcValueBindingStrong);
613			if (l2)
614			    ll = FcValueListAppend (ll,
615						    FcValueCanonicalize (&l2->value),
616						    FcValueBindingStrong);
617		    }
618		}
619		FcPatternObjectListAdd (new, fe->object, ln, FcFalse);
620		FcPatternObjectListAdd (new, fel->object, ll, FcFalse);
621
622		continue;
623	    }
624	    else if (fel)
625	    {
626		/* Pattern doesn't ask for specific language.  Copy all for name and
627		 * lang. */
628		FcValueListPtr l1, l2;
629
630		l1 = FcValueListDuplicate (FcPatternEltValues (fe));
631		l2 = FcValueListDuplicate (FcPatternEltValues (fel));
632		FcPatternObjectListAdd (new, fe->object, l1, FcFalse);
633		FcPatternObjectListAdd (new, fel->object, l2, FcFalse);
634
635		continue;
636	    }
637	}
638
639	pe = FcPatternObjectFindElt (pat, fe->object);
640	if (pe)
641	{
642	    const FcMatcher *match = FcObjectToMatcher (pe->object, FcFalse);
643	    if (!FcCompareValueList (pe->object, match,
644				     FcPatternEltValues(pe),
645				     FcPatternEltValues(fe), &v, NULL, NULL, &result))
646	    {
647		FcPatternDestroy (new);
648		return NULL;
649	    }
650	    FcPatternObjectAdd (new, fe->object, v, FcFalse);
651
652	    /* Set font-variations settings for standard axes in variable fonts. */
653	    if (variable &&
654		FcPatternEltValues(fe)->value.type == FcTypeRange &&
655		(fe->object == FC_WEIGHT_OBJECT ||
656		 fe->object == FC_WIDTH_OBJECT ||
657		 fe->object == FC_SIZE_OBJECT))
658	    {
659		double num;
660		FcChar8 temp[128];
661		const char *tag = "    ";
662		assert (v.type == FcTypeDouble);
663		num = v.u.d;
664		if (variations.len)
665		    FcStrBufChar (&variations, ',');
666		switch (fe->object)
667		{
668		    case FC_WEIGHT_OBJECT:
669			tag = "wght";
670			num = FcWeightToOpenType (num);
671			break;
672
673		    case FC_WIDTH_OBJECT:
674			tag = "wdth";
675			break;
676
677		    case FC_SIZE_OBJECT:
678			tag = "opsz";
679			break;
680		}
681		sprintf ((char *) temp, "%4s=%g", tag, num);
682		FcStrBufString (&variations, temp);
683	    }
684	}
685	else
686	{
687	    FcPatternObjectListAdd (new, fe->object,
688				    FcValueListDuplicate (FcPatternEltValues (fe)),
689				    FcTrue);
690	}
691    }
692    for (i = 0; i < pat->num; i++)
693    {
694	pe = &FcPatternElts(pat)[i];
695	fe = FcPatternObjectFindElt (font, pe->object);
696	if (!fe &&
697	    pe->object != FC_FAMILYLANG_OBJECT &&
698	    pe->object != FC_STYLELANG_OBJECT &&
699	    pe->object != FC_FULLNAMELANG_OBJECT)
700	{
701	    FcPatternObjectListAdd (new, pe->object,
702				    FcValueListDuplicate (FcPatternEltValues(pe)),
703				    FcFalse);
704	}
705    }
706
707    if (variable && variations.len)
708    {
709	FcChar8 *vars = NULL;
710	if (FcPatternObjectGetString (new, FC_FONT_VARIATIONS_OBJECT, 0, &vars) == FcResultMatch)
711	{
712	    FcStrBufChar (&variations, ',');
713	    FcStrBufString (&variations, vars);
714	    FcPatternObjectDel (new, FC_FONT_VARIATIONS_OBJECT);
715	}
716
717	FcPatternObjectAddString (new, FC_FONT_VARIATIONS_OBJECT, FcStrBufDoneStatic (&variations));
718	FcStrBufDestroy (&variations);
719    }
720
721    FcConfigSubstituteWithPat (config, new, pat, FcMatchFont);
722    return new;
723}
724
725static FcPattern *
726FcFontSetMatchInternal (FcFontSet   **sets,
727			int	    nsets,
728			FcPattern   *p,
729			FcResult    *result)
730{
731    double    	    score[PRI_END], bestscore[PRI_END];
732    int		    f;
733    FcFontSet	    *s;
734    FcPattern	    *best;
735    int		    i;
736    int		    set;
737
738    for (i = 0; i < PRI_END; i++)
739	bestscore[i] = 0;
740    best = 0;
741    if (FcDebug () & FC_DBG_MATCH)
742    {
743	printf ("Match ");
744	FcPatternPrint (p);
745    }
746    for (set = 0; set < nsets; set++)
747    {
748	s = sets[set];
749	if (!s)
750	    continue;
751	for (f = 0; f < s->nfont; f++)
752	{
753	    if (FcDebug () & FC_DBG_MATCHV)
754	    {
755		printf ("Font %d ", f);
756		FcPatternPrint (s->fonts[f]);
757	    }
758	    if (!FcCompare (p, s->fonts[f], score, result))
759		return 0;
760	    if (FcDebug () & FC_DBG_MATCHV)
761	    {
762		printf ("Score");
763		for (i = 0; i < PRI_END; i++)
764		{
765		    printf (" %g", score[i]);
766		}
767		printf ("\n");
768	    }
769	    for (i = 0; i < PRI_END; i++)
770	    {
771		if (best && bestscore[i] < score[i])
772		    break;
773		if (!best || score[i] < bestscore[i])
774		{
775		    for (i = 0; i < PRI_END; i++)
776			bestscore[i] = score[i];
777		    best = s->fonts[f];
778		    break;
779		}
780	    }
781	}
782    }
783    if (FcDebug () & FC_DBG_MATCH)
784    {
785	printf ("Best score");
786	for (i = 0; i < PRI_END; i++)
787	    printf (" %g", bestscore[i]);
788	printf ("\n");
789	FcPatternPrint (best);
790    }
791    if (FcDebug () & FC_DBG_MATCH2)
792    {
793	char *env = getenv ("FC_DBG_MATCH_FILTER");
794	FcObjectSet *os = NULL;
795
796	if (env)
797	{
798	    char *ss, *s;
799	    char *p;
800	    FcBool f = FcTrue;
801
802	    ss = s = strdup (env);
803	    if (ss == NULL)
804	    {
805		    fprintf (stderr, "Fontconfig Error: %s\n",
806			strerror (errno));
807		    exit (1);
808	    }
809	    os = FcObjectSetCreate ();
810	    while (f)
811	    {
812		size_t len;
813		char *x;
814
815		if (!(p = strchr (s, ',')))
816		{
817		    f = FcFalse;
818		    len = strlen (s);
819		}
820		else
821		{
822		    len = (p - s);
823		}
824		x = malloc (sizeof (char) * (len + 1));
825		if (x)
826		{
827		    strcpy (x, s);
828		    if (FcObjectFromName (x) > 0)
829			FcObjectSetAdd (os, x);
830		    s = p + 1;
831		    free (x);
832		}
833	    }
834	    free (ss);
835	}
836	FcPatternPrint2 (p, best, os);
837	if (os)
838	    FcObjectSetDestroy (os);
839    }
840    /* assuming that 'result' is initialized with FcResultNoMatch
841     * outside this function */
842    if (best)
843	*result = FcResultMatch;
844
845    return best;
846}
847
848FcPattern *
849FcFontSetMatch (FcConfig    *config,
850		FcFontSet   **sets,
851		int	    nsets,
852		FcPattern   *p,
853		FcResult    *result)
854{
855    FcPattern	    *best;
856
857    assert (sets != NULL);
858    assert (p != NULL);
859    assert (result != NULL);
860
861    *result = FcResultNoMatch;
862
863    if (!config)
864    {
865	config = FcConfigGetCurrent ();
866	if (!config)
867	    return 0;
868    }
869    best = FcFontSetMatchInternal (sets, nsets, p, result);
870    if (best)
871	return FcFontRenderPrepare (config, p, best);
872    else
873	return NULL;
874}
875
876FcPattern *
877FcFontMatch (FcConfig	*config,
878	     FcPattern	*p,
879	     FcResult	*result)
880{
881    FcFontSet	*sets[2];
882    int		nsets;
883    FcPattern   *best;
884
885    assert (p != NULL);
886    assert (result != NULL);
887
888    *result = FcResultNoMatch;
889
890    if (!config)
891    {
892	config = FcConfigGetCurrent ();
893	if (!config)
894	    return 0;
895    }
896    nsets = 0;
897    if (config->fonts[FcSetSystem])
898	sets[nsets++] = config->fonts[FcSetSystem];
899    if (config->fonts[FcSetApplication])
900	sets[nsets++] = config->fonts[FcSetApplication];
901
902    best = FcFontSetMatchInternal (sets, nsets, p, result);
903    if (best)
904	return FcFontRenderPrepare (config, p, best);
905    else
906	return NULL;
907}
908
909typedef struct _FcSortNode {
910    FcPattern	*pattern;
911    double	score[PRI_END];
912} FcSortNode;
913
914static int
915FcSortCompare (const void *aa, const void *ab)
916{
917    FcSortNode  *a = *(FcSortNode **) aa;
918    FcSortNode  *b = *(FcSortNode **) ab;
919    double	*as = &a->score[0];
920    double	*bs = &b->score[0];
921    double	ad = 0, bd = 0;
922    int         i;
923
924    i = PRI_END;
925    while (i-- && (ad = *as++) == (bd = *bs++))
926	;
927    return ad < bd ? -1 : ad > bd ? 1 : 0;
928}
929
930static FcBool
931FcSortWalk (FcSortNode **n, int nnode, FcFontSet *fs, FcCharSet **csp, FcBool trim)
932{
933    FcBool ret = FcFalse;
934    FcCharSet *cs;
935    int i;
936
937    cs = 0;
938    if (trim || csp)
939    {
940	cs = FcCharSetCreate ();
941	if (cs == NULL)
942	    goto bail;
943    }
944
945    for (i = 0; i < nnode; i++)
946    {
947	FcSortNode	*node = *n++;
948	FcBool		adds_chars = FcFalse;
949
950	/*
951	 * Only fetch node charset if we'd need it
952	 */
953	if (cs)
954	{
955	    FcCharSet	*ncs;
956
957	    if (FcPatternGetCharSet (node->pattern, FC_CHARSET, 0, &ncs) !=
958		FcResultMatch)
959	        continue;
960
961	    if (!FcCharSetMerge (cs, ncs, &adds_chars))
962		goto bail;
963	}
964
965	/*
966	 * If this font isn't a subset of the previous fonts,
967	 * add it to the list
968	 */
969	if (!i || !trim || adds_chars)
970	{
971	    FcPatternReference (node->pattern);
972	    if (FcDebug () & FC_DBG_MATCHV)
973	    {
974		printf ("Add ");
975		FcPatternPrint (node->pattern);
976	    }
977	    if (!FcFontSetAdd (fs, node->pattern))
978	    {
979		FcPatternDestroy (node->pattern);
980		goto bail;
981	    }
982	}
983    }
984    if (csp)
985    {
986	*csp = cs;
987	cs = 0;
988    }
989
990    ret = FcTrue;
991
992bail:
993    if (cs)
994	FcCharSetDestroy (cs);
995
996    return ret;
997}
998
999void
1000FcFontSetSortDestroy (FcFontSet *fs)
1001{
1002    FcFontSetDestroy (fs);
1003}
1004
1005FcFontSet *
1006FcFontSetSort (FcConfig	    *config FC_UNUSED,
1007	       FcFontSet    **sets,
1008	       int	    nsets,
1009	       FcPattern    *p,
1010	       FcBool	    trim,
1011	       FcCharSet    **csp,
1012	       FcResult	    *result)
1013{
1014    FcFontSet	    *ret;
1015    FcFontSet	    *s;
1016    FcSortNode	    *nodes;
1017    FcSortNode	    **nodeps, **nodep;
1018    int		    nnodes;
1019    FcSortNode	    *new;
1020    int		    set;
1021    int		    f;
1022    int		    i;
1023    int		    nPatternLang;
1024    FcBool    	    *patternLangSat;
1025    FcValue	    patternLang;
1026
1027    assert (sets != NULL);
1028    assert (p != NULL);
1029    assert (result != NULL);
1030
1031    /* There are some implementation that relying on the result of
1032     * "result" to check if the return value of FcFontSetSort
1033     * is valid or not.
1034     * So we should initialize it to the conservative way since
1035     * this function doesn't return NULL anymore.
1036     */
1037    if (result)
1038	*result = FcResultNoMatch;
1039
1040    if (FcDebug () & FC_DBG_MATCH)
1041    {
1042	printf ("Sort ");
1043	FcPatternPrint (p);
1044    }
1045    nnodes = 0;
1046    for (set = 0; set < nsets; set++)
1047    {
1048	s = sets[set];
1049	if (!s)
1050	    continue;
1051	nnodes += s->nfont;
1052    }
1053    if (!nnodes)
1054	return FcFontSetCreate ();
1055
1056    for (nPatternLang = 0;
1057	 FcPatternGet (p, FC_LANG, nPatternLang, &patternLang) == FcResultMatch;
1058	 nPatternLang++)
1059	;
1060
1061    /* freed below */
1062    nodes = malloc (nnodes * sizeof (FcSortNode) +
1063		    nnodes * sizeof (FcSortNode *) +
1064		    nPatternLang * sizeof (FcBool));
1065    if (!nodes)
1066	goto bail0;
1067    nodeps = (FcSortNode **) (nodes + nnodes);
1068    patternLangSat = (FcBool *) (nodeps + nnodes);
1069
1070    new = nodes;
1071    nodep = nodeps;
1072    for (set = 0; set < nsets; set++)
1073    {
1074	s = sets[set];
1075	if (!s)
1076	    continue;
1077	for (f = 0; f < s->nfont; f++)
1078	{
1079	    if (FcDebug () & FC_DBG_MATCHV)
1080	    {
1081		printf ("Font %d ", f);
1082		FcPatternPrint (s->fonts[f]);
1083	    }
1084	    new->pattern = s->fonts[f];
1085	    if (!FcCompare (p, new->pattern, new->score, result))
1086		goto bail1;
1087	    if (FcDebug () & FC_DBG_MATCHV)
1088	    {
1089		printf ("Score");
1090		for (i = 0; i < PRI_END; i++)
1091		{
1092		    printf (" %g", new->score[i]);
1093		}
1094		printf ("\n");
1095	    }
1096	    *nodep = new;
1097	    new++;
1098	    nodep++;
1099	}
1100    }
1101
1102    nnodes = new - nodes;
1103
1104    qsort (nodeps, nnodes, sizeof (FcSortNode *),
1105	   FcSortCompare);
1106
1107    for (i = 0; i < nPatternLang; i++)
1108	patternLangSat[i] = FcFalse;
1109
1110    for (f = 0; f < nnodes; f++)
1111    {
1112	FcBool	satisfies = FcFalse;
1113	/*
1114	 * If this node matches any language, go check
1115	 * which ones and satisfy those entries
1116	 */
1117	if (nodeps[f]->score[PRI_LANG] < 2000)
1118	{
1119	    for (i = 0; i < nPatternLang; i++)
1120	    {
1121		FcValue	    nodeLang;
1122
1123		if (!patternLangSat[i] &&
1124		    FcPatternGet (p, FC_LANG, i, &patternLang) == FcResultMatch &&
1125		    FcPatternGet (nodeps[f]->pattern, FC_LANG, 0, &nodeLang) == FcResultMatch)
1126		{
1127		    FcValue matchValue;
1128		    double  compare = FcCompareLang (&patternLang, &nodeLang, &matchValue);
1129		    if (compare >= 0 && compare < 2)
1130		    {
1131			if (FcDebug () & FC_DBG_MATCHV)
1132			{
1133			    FcChar8 *family;
1134			    FcChar8 *style;
1135
1136			    if (FcPatternGetString (nodeps[f]->pattern, FC_FAMILY, 0, &family) == FcResultMatch &&
1137				FcPatternGetString (nodeps[f]->pattern, FC_STYLE, 0, &style) == FcResultMatch)
1138				printf ("Font %s:%s matches language %d\n", family, style, i);
1139			}
1140			patternLangSat[i] = FcTrue;
1141			satisfies = FcTrue;
1142			break;
1143		    }
1144		}
1145	    }
1146	}
1147	if (!satisfies)
1148	{
1149	    nodeps[f]->score[PRI_LANG] = 10000.0;
1150	}
1151    }
1152
1153    /*
1154     * Re-sort once the language issues have been settled
1155     */
1156    qsort (nodeps, nnodes, sizeof (FcSortNode *),
1157	   FcSortCompare);
1158
1159    ret = FcFontSetCreate ();
1160    if (!ret)
1161	goto bail1;
1162
1163    if (!FcSortWalk (nodeps, nnodes, ret, csp, trim))
1164	goto bail2;
1165
1166    free (nodes);
1167
1168    if (FcDebug() & FC_DBG_MATCH)
1169    {
1170	printf ("First font ");
1171	FcPatternPrint (ret->fonts[0]);
1172    }
1173    if (ret->nfont > 0)
1174	*result = FcResultMatch;
1175
1176    return ret;
1177
1178bail2:
1179    FcFontSetDestroy (ret);
1180bail1:
1181    free (nodes);
1182bail0:
1183    return 0;
1184}
1185
1186FcFontSet *
1187FcFontSort (FcConfig	*config,
1188	    FcPattern	*p,
1189	    FcBool	trim,
1190	    FcCharSet	**csp,
1191	    FcResult	*result)
1192{
1193    FcFontSet	*sets[2];
1194    int		nsets;
1195
1196    assert (p != NULL);
1197    assert (result != NULL);
1198
1199    *result = FcResultNoMatch;
1200
1201    if (!config)
1202    {
1203	config = FcConfigGetCurrent ();
1204	if (!config)
1205	    return 0;
1206    }
1207    nsets = 0;
1208    if (config->fonts[FcSetSystem])
1209	sets[nsets++] = config->fonts[FcSetSystem];
1210    if (config->fonts[FcSetApplication])
1211	sets[nsets++] = config->fonts[FcSetApplication];
1212    return FcFontSetSort (config, sets, nsets, p, trim, csp, result);
1213}
1214#define __fcmatch__
1215#include "fcaliastail.h"
1216#undef __fcmatch__
1217