2 * $RCSId: xc/lib/fontconfig/src/fcmatch.c,v 1.20 2002/08/31 22:17:32 keithp Exp $
4 * Copyright © 2000 Keith Packard
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 Keith Packard not be used in
11 * advertising or publicity pertaining to distribution of the software without
12 * specific, written prior permission. Keith Packard makes no
13 * representations about the suitability of this software for any purpose. It
14 * is provided "as is" without express or implied warranty.
16 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18 * EVENT SHALL KEITH PACKARD 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.
31 FcCompareNumber (const char *object, FcValue *value1, FcValue *value2)
35 switch (value1->type) {
37 v1 = (double) value1->u.i;
45 switch (value2->type) {
47 v2 = (double) value2->u.i;
62 FcCompareString (const char *object, FcValue *v1, FcValue *v2)
64 FcValue value1 = FcValueCanonicalize(v1), value2 = FcValueCanonicalize(v2);
65 if (value2.type != FcTypeString || value1.type != FcTypeString)
67 return (double) FcStrCmpIgnoreCase (value1.u.s, value2.u.s) != 0;
71 FcCompareFamily (const char *object, FcValue *v1, FcValue *v2)
73 FcValue value1 = FcValueCanonicalize(v1), value2 = FcValueCanonicalize(v2);
74 if (value2.type != FcTypeString || value1.type != FcTypeString)
76 return (double) FcStrCmpIgnoreBlanksAndCase (value1.u.s, value2.u.s) != 0;
80 FcCompareLang (const char *object, FcValue *v1, FcValue *v2)
83 FcValue value1 = FcValueCanonicalize(v1), value2 = FcValueCanonicalize(v2);
85 switch (value1.type) {
87 switch (value2.type) {
89 result = FcLangSetCompare (value1.u.l, value2.u.l);
92 result = FcLangSetHasLang (value1.u.l,
100 switch (value2.type) {
102 result = FcLangSetHasLang (value2.u.l, value1.u.s);
105 result = FcLangCompare (value1.u.s,
118 case FcLangDifferentCountry:
120 case FcLangDifferentLang:
127 FcCompareBool (const char *object, FcValue *value1, FcValue *value2)
129 if (value2->type != FcTypeBool || value1->type != FcTypeBool)
131 return (double) value2->u.b != value1->u.b;
135 FcCompareCharSet (const char *object, FcValue *v1, FcValue *v2)
137 FcValue value1 = FcValueCanonicalize(v1), value2 = FcValueCanonicalize(v2);
139 if (value2.type != FcTypeCharSet || value1.type != FcTypeCharSet)
141 return (double) FcCharSetSubtractCount (value1.u.c, value2.u.c);
145 FcCompareSize (const char *object, FcValue *value1, FcValue *value2)
149 switch (value1->type) {
159 switch (value2->type) {
177 typedef struct _FcMatcher {
179 double (*compare) (const char *object, FcValue *value1, FcValue *value2);
184 * Order is significant, it defines the precedence of
185 * each value, earlier values are more significant than
188 static FcMatcher _FcMatchers [] = {
189 { FC_FOUNDRY, FcCompareString, 0, 0 },
190 #define MATCH_FOUNDRY 0
191 #define MATCH_FOUNDRY_INDEX 0
193 { FC_CHARSET, FcCompareCharSet, 1, 1 },
194 #define MATCH_CHARSET 1
195 #define MATCH_CHARSET_INDEX 1
197 { FC_FAMILY, FcCompareFamily, 2, 4 },
198 #define MATCH_FAMILY 2
199 #define MATCH_FAMILY_STRONG_INDEX 2
200 #define MATCH_FAMILY_WEAK_INDEX 4
202 { FC_LANG, FcCompareLang, 3, 3 },
204 #define MATCH_LANG_INDEX 3
206 { FC_SPACING, FcCompareNumber, 5, 5 },
207 #define MATCH_SPACING 4
208 #define MATCH_SPACING_INDEX 5
210 { FC_PIXEL_SIZE, FcCompareSize, 6, 6 },
211 #define MATCH_PIXEL_SIZE 5
212 #define MATCH_PIXEL_SIZE_INDEX 6
214 { FC_STYLE, FcCompareString, 7, 7 },
215 #define MATCH_STYLE 6
216 #define MATCH_STYLE_INDEX 7
218 { FC_SLANT, FcCompareNumber, 8, 8 },
219 #define MATCH_SLANT 7
220 #define MATCH_SLANT_INDEX 8
222 { FC_WEIGHT, FcCompareNumber, 9, 9 },
223 #define MATCH_WEIGHT 8
224 #define MATCH_WEIGHT_INDEX 9
226 { FC_WIDTH, FcCompareNumber, 10, 10 },
227 #define MATCH_WIDTH 9
228 #define MATCH_WIDTH_INDEX 10
230 { FC_ANTIALIAS, FcCompareBool, 11, 11 },
231 #define MATCH_ANTIALIAS 10
232 #define MATCH_ANTIALIAS_INDEX 11
234 { FC_RASTERIZER, FcCompareString, 12, 12 },
235 #define MATCH_RASTERIZER 11
236 #define MATCH_RASTERIZER_INDEX 12
238 { FC_OUTLINE, FcCompareBool, 13, 13 },
239 #define MATCH_OUTLINE 12
240 #define MATCH_OUTLINE_INDEX 13
242 { FC_FONTVERSION, FcCompareNumber, 14, 14 },
243 #define MATCH_FONTVERSION 13
244 #define MATCH_FONTVERSION_INDEX 14
247 #define NUM_MATCH_VALUES 15
250 FcCompareValueList (const char *object,
251 FcValueListPtr v1orig, /* pattern */
252 FcValueListPtr v2orig, /* target */
257 FcValueListPtr v1, v2;
258 double v, best, bestStrong, bestWeak;
263 * Locate the possible matching entry by examining the
264 * first few characters in object
267 switch (FcToLower (object[0])) {
269 switch (FcToLower (object[1])) {
271 switch (FcToLower (object[2])) {
273 i = MATCH_FOUNDRY; break;
275 i = MATCH_FONTVERSION; break;
279 i = MATCH_FAMILY; break;
283 i = MATCH_CHARSET; break;
285 i = MATCH_ANTIALIAS; break;
287 i = MATCH_LANG; break;
289 switch (FcToLower (object[1])) {
291 i = MATCH_SPACING; break;
293 i = MATCH_STYLE; break;
295 i = MATCH_SLANT; break;
299 i = MATCH_PIXEL_SIZE; break;
301 switch (FcToLower (object[1])) {
303 i = MATCH_WIDTH; break;
305 i = MATCH_WEIGHT; break;
309 i = MATCH_RASTERIZER; break;
311 i = MATCH_OUTLINE; break;
314 FcStrCmpIgnoreCase ((FcChar8 *) _FcMatchers[i].object,
315 (FcChar8 *) object) != 0)
318 *bestValue = FcValueCanonicalize(&FcValueListPtrU(v2orig)->value);
322 for (i = 0; i < NUM_MATCHER; i++)
324 if (!FcStrCmpIgnoreCase ((FcChar8 *) _FcMatchers[i].object,
328 if (i == NUM_MATCHER)
331 *bestValue = v2orig->value;
339 for (v1 = v1orig; FcValueListPtrU(v1);
340 v1 = FcValueListPtrU(v1)->next)
342 for (v2 = v2orig; FcValueListPtrU(v2);
343 v2 = FcValueListPtrU(v2)->next)
345 v = (*_FcMatchers[i].compare) (_FcMatchers[i].object,
346 &FcValueListPtrU(v1)->value,
347 &FcValueListPtrU(v2)->value);
350 *result = FcResultTypeMismatch;
353 if (FcDebug () & FC_DBG_MATCHV)
354 printf (" v %g j %d ", v, j);
359 *bestValue = FcValueCanonicalize(&FcValueListPtrU(v2)->value);
362 if (FcValueListPtrU(v1)->binding == FcValueBindingStrong)
375 if (FcDebug () & FC_DBG_MATCHV)
377 printf (" %s: %g ", object, best);
378 FcValueListPrint (v1orig);
380 FcValueListPrint (v2orig);
385 int weak = _FcMatchers[i].weak;
386 int strong = _FcMatchers[i].strong;
388 value[strong] += best;
391 value[weak] += bestWeak;
392 value[strong] += bestStrong;
399 * Return a value indicating the distance between the two lists of
404 FcCompare (FcPattern *pat,
411 for (i = 0; i < NUM_MATCH_VALUES; i++)
416 while (i1 < pat->num && i2 < fnt->num)
418 i = FcObjectPtrCompare((FcPatternEltU(pat->elts)+i1)->object,
419 (FcPatternEltU(fnt->elts)+i2)->object);
426 if (!FcCompareValueList (FcObjectPtrU((FcPatternEltU(pat->elts)+i1)->object),
427 (FcPatternEltU(pat->elts)+i1)->values,
428 (FcPatternEltU(fnt->elts)+i2)->values,
439 for (i1 = 0; i1 < pat->num; i1++)
441 for (i2 = 0; i2 < fnt->num; i2++)
443 if (!strcmp (pat->elts[i1].object, fnt->elts[i2].object))
454 FcFontRenderPrepare (FcConfig *config,
460 FcPatternElt *fe, *pe;
464 new = FcPatternCreate ();
467 for (i = 0; i < font->num; i++)
469 fe = FcPatternEltU(font->elts)+i;
470 pe = FcPatternFindElt (pat, FcObjectPtrU(fe->object));
473 if (!FcCompareValueList (FcObjectPtrU(pe->object), pe->values,
474 fe->values, &v, 0, &result))
476 FcPatternDestroy (new);
481 v = FcValueCanonicalize(&FcValueListPtrU(fe->values)->value);
482 FcPatternAdd (new, FcObjectPtrU(fe->object), v, FcFalse);
484 for (i = 0; i < pat->num; i++)
486 pe = FcPatternEltU(pat->elts)+i;
487 fe = FcPatternFindElt (font, FcObjectPtrU(pe->object));
489 FcPatternAdd (new, FcObjectPtrU(pe->object),
490 FcValueCanonicalize(&FcValueListPtrU(pe->values)->value), FcTrue);
493 if (FcPatternFindElt (font, FC_FILE))
494 FcPatternTransferFullFname (new, font);
496 FcConfigSubstituteWithPat (config, new, pat, FcMatchFont);
501 FcFontSetMatch (FcConfig *config,
507 double score[NUM_MATCH_VALUES], bestscore[NUM_MATCH_VALUES];
514 for (i = 0; i < NUM_MATCH_VALUES; i++)
517 if (FcDebug () & FC_DBG_MATCH)
524 config = FcConfigGetCurrent ();
527 *result = FcResultOutOfMemory;
531 for (set = 0; set < nsets; set++)
536 for (f = 0; f < s->nfont; f++)
538 if (FcDebug () & FC_DBG_MATCHV)
540 printf ("Font %d ", f);
541 FcPatternPrint (s->fonts[f]);
543 if (!FcCompare (p, s->fonts[f], score, result))
545 if (FcDebug () & FC_DBG_MATCHV)
548 for (i = 0; i < NUM_MATCH_VALUES; i++)
550 printf (" %g", score[i]);
554 for (i = 0; i < NUM_MATCH_VALUES; i++)
556 if (best && bestscore[i] < score[i])
558 if (!best || score[i] < bestscore[i])
560 for (i = 0; i < NUM_MATCH_VALUES; i++)
561 bestscore[i] = score[i];
568 if (FcDebug () & FC_DBG_MATCH)
570 printf ("Best score");
571 for (i = 0; i < NUM_MATCH_VALUES; i++)
572 printf (" %g", bestscore[i]);
573 FcPatternPrint (best);
577 *result = FcResultNoMatch;
580 return FcFontRenderPrepare (config, p, best);
584 FcFontMatch (FcConfig *config,
593 config = FcConfigGetCurrent ();
598 if (config->fonts[FcSetSystem])
599 sets[nsets++] = config->fonts[FcSetSystem];
600 if (config->fonts[FcSetApplication])
601 sets[nsets++] = config->fonts[FcSetApplication];
602 return FcFontSetMatch (config, sets, nsets, p, result);
605 typedef struct _FcSortNode {
607 double score[NUM_MATCH_VALUES];
611 FcSortCompare (const void *aa, const void *ab)
613 FcSortNode *a = *(FcSortNode **) aa;
614 FcSortNode *b = *(FcSortNode **) ab;
615 double *as = &a->score[0];
616 double *bs = &b->score[0];
617 double ad = 0, bd = 0;
620 i = NUM_MATCH_VALUES;
621 while (i-- && (ad = *as++) == (bd = *bs++))
623 return ad < bd ? -1 : ad > bd ? 1 : 0;
627 FcSortWalk (FcSortNode **n, int nnode, FcFontSet *fs, FcCharSet **cs, FcBool trim)
635 if (FcPatternGetCharSet (node->pattern, FC_CHARSET, 0, &ncs) ==
639 * If this font isn't a subset of the previous fonts,
642 if (!trim || !*cs || !FcCharSetIsSubset (ncs, *cs))
646 ncs = FcCharSetUnion (ncs, *cs);
649 FcCharSetDestroy (*cs);
652 ncs = FcCharSetCopy (ncs);
654 FcPatternReference (node->pattern);
655 if (FcDebug () & FC_DBG_MATCH)
658 FcPatternPrint (node->pattern);
660 if (!FcFontSetAdd (fs, node->pattern))
662 FcPatternDestroy (node->pattern);
672 FcFontSetSortDestroy (FcFontSet *fs)
674 FcFontSetDestroy (fs);
678 FcFontSetSort (FcConfig *config,
689 FcSortNode **nodeps, **nodep;
697 FcBool *patternLangSat;
700 if (FcDebug () & FC_DBG_MATCH)
706 for (set = 0; set < nsets; set++)
716 for (nPatternLang = 0;
717 FcPatternGet (p, FC_LANG, nPatternLang, &patternLang) == FcResultMatch;
722 nodes = malloc (nnodes * sizeof (FcSortNode) +
723 nnodes * sizeof (FcSortNode *) +
724 nPatternLang * sizeof (FcBool));
727 nodeps = (FcSortNode **) (nodes + nnodes);
728 patternLangSat = (FcBool *) (nodeps + nnodes);
732 for (set = 0; set < nsets; set++)
737 for (f = 0; f < s->nfont; f++)
739 if (FcDebug () & FC_DBG_MATCHV)
741 printf ("Font %d ", f);
742 FcPatternPrint (s->fonts[f]);
744 new->pattern = s->fonts[f];
745 if (!FcCompare (p, new->pattern, new->score, result))
747 if (FcDebug () & FC_DBG_MATCHV)
750 for (i = 0; i < NUM_MATCH_VALUES; i++)
752 printf (" %g", new->score[i]);
762 nnodes = new - nodes;
764 qsort (nodeps, nnodes, sizeof (FcSortNode *),
767 for (i = 0; i < nPatternLang; i++)
768 patternLangSat[i] = FcFalse;
770 for (f = 0; f < nnodes; f++)
772 FcBool satisfies = FcFalse;
774 * If this node matches any language, go check
775 * which ones and satisfy those entries
777 if (nodeps[f]->score[MATCH_LANG_INDEX] < nPatternLang)
779 for (i = 0; i < nPatternLang; i++)
783 if (!patternLangSat[i] &&
784 FcPatternGet (p, FC_LANG, i, &patternLang) == FcResultMatch &&
785 FcPatternGet (nodeps[f]->pattern, FC_LANG, 0, &nodeLang) == FcResultMatch)
787 double compare = FcCompareLang (FC_LANG, &patternLang,
789 if (compare >= 0 && compare < 2)
791 if (FcDebug () & FC_DBG_MATCHV)
796 if (FcPatternGetString (nodeps[f]->pattern, FC_FAMILY, 0, &family) == FcResultMatch &&
797 FcPatternGetString (nodeps[f]->pattern, FC_STYLE, 0, &style) == FcResultMatch)
798 printf ("Font %s:%s matches language %d\n", family, style, i);
800 patternLangSat[i] = FcTrue;
808 nodeps[f]->score[MATCH_LANG_INDEX] = 1000.0;
812 * Re-sort once the language issues have been settled
814 qsort (nodeps, nnodes, sizeof (FcSortNode *),
817 ret = FcFontSetCreate ();
823 if (!FcSortWalk (nodeps, nnodes, ret, &cs, trim))
829 FcCharSetDestroy (cs);
837 FcCharSetDestroy (cs);
838 FcFontSetDestroy (ret);
846 FcFontSort (FcConfig *config,
857 config = FcConfigGetCurrent ();
862 if (config->fonts[FcSetSystem])
863 sets[nsets++] = config->fonts[FcSetSystem];
864 if (config->fonts[FcSetApplication])
865 sets[nsets++] = config->fonts[FcSetApplication];
866 return FcFontSetSort (config, sets, nsets, p, trim, csp, result);