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