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