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