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 (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 (char *object, FcValue value1, FcValue value2)
64 if (value2.type != FcTypeString || value1.type != FcTypeString)
66 return (double) FcStrCmpIgnoreCase (value1.u.s, value2.u.s) != 0;
70 FcCompareFamily (char *object, FcValue value1, FcValue value2)
72 if (value2.type != FcTypeString || value1.type != FcTypeString)
74 return (double) FcStrCmpIgnoreBlanksAndCase (value1.u.s, value2.u.s) != 0;
78 FcCompareLang (char *object, FcValue value1, FcValue value2)
82 switch (value1.type) {
84 switch (value2.type) {
86 result = FcLangSetCompare (value1.u.l, value2.u.l);
89 result = FcLangSetHasLang (value1.u.l, value2.u.s);
96 switch (value2.type) {
98 result = FcLangSetHasLang (value2.u.l, value1.u.s);
101 result = FcLangCompare (value1.u.s, value2.u.s);
113 case FcLangDifferentCountry:
115 case FcLangDifferentLang:
122 FcCompareBool (char *object, FcValue value1, FcValue value2)
124 if (value2.type != FcTypeBool || value1.type != FcTypeBool)
126 return (double) value2.u.b != value1.u.b;
130 FcCompareCharSet (char *object, FcValue value1, FcValue value2)
132 if (value2.type != FcTypeCharSet || value1.type != FcTypeCharSet)
134 return (double) FcCharSetSubtractCount (value1.u.c, value2.u.c);
138 FcCompareSize (char *object, FcValue value1, FcValue value2)
142 switch (value1.type) {
152 switch (value2.type) {
170 typedef struct _FcMatcher {
172 double (*compare) (char *object, FcValue value1, FcValue value2);
177 * Order is significant, it defines the precedence of
178 * each value, earlier values are more significant than
181 static FcMatcher _FcMatchers [] = {
182 { FC_FOUNDRY, FcCompareString, 0, 0 },
183 #define MATCH_FOUNDRY 0
185 { FC_CHARSET, FcCompareCharSet, 1, 1 },
186 #define MATCH_CHARSET 1
188 { FC_FAMILY, FcCompareFamily, 2, 4 },
189 #define MATCH_FAMILY 2
191 { FC_LANG, FcCompareLang, 3, 3 },
194 { FC_SPACING, FcCompareNumber, 5, 5 },
195 #define MATCH_SPACING 4
197 { FC_PIXEL_SIZE, FcCompareSize, 6, 6 },
198 #define MATCH_PIXEL_SIZE 5
200 { FC_STYLE, FcCompareString, 7, 7 },
201 #define MATCH_STYLE 6
203 { FC_SLANT, FcCompareNumber, 8, 8 },
204 #define MATCH_SLANT 7
206 { FC_WEIGHT, FcCompareNumber, 9, 9 },
207 #define MATCH_WEIGHT 8
209 { FC_WIDTH, FcCompareNumber, 10, 10 },
210 #define MATCH_WIDTH 9
212 { FC_ANTIALIAS, FcCompareBool, 11, 11 },
213 #define MATCH_ANTIALIAS 10
215 { FC_RASTERIZER, FcCompareString, 12, 12 },
216 #define MATCH_RASTERIZER 11
218 { FC_OUTLINE, FcCompareBool, 13, 13 },
219 #define MATCH_OUTLINE 12
221 { FC_FONTVERSION, FcCompareNumber, 14, 14 },
222 #define MATCH_FONTVERSION 13
225 #define NUM_MATCH_VALUES 15
228 FcCompareValueList (const char *object,
229 FcValueList *v1orig, /* pattern */
230 FcValueList *v2orig, /* target */
235 FcValueList *v1, *v2;
236 double v, best, bestStrong, bestWeak;
241 * Locate the possible matching entry by examining the
242 * first few characters in object
245 switch (FcToLower (object[0])) {
247 switch (FcToLower (object[1])) {
249 switch (FcToLower (object[2])) {
251 i = MATCH_FOUNDRY; break;
253 i = MATCH_FONTVERSION; break;
257 i = MATCH_FAMILY; break;
261 i = MATCH_CHARSET; break;
263 i = MATCH_ANTIALIAS; break;
265 i = MATCH_LANG; break;
267 switch (FcToLower (object[1])) {
269 i = MATCH_SPACING; break;
271 i = MATCH_STYLE; break;
273 i = MATCH_SLANT; break;
277 i = MATCH_PIXEL_SIZE; break;
279 switch (FcToLower (object[1])) {
281 i = MATCH_WIDTH; break;
283 i = MATCH_WEIGHT; break;
287 i = MATCH_RASTERIZER; break;
289 i = MATCH_OUTLINE; break;
292 FcStrCmpIgnoreCase ((FcChar8 *) _FcMatchers[i].object,
293 (FcChar8 *) object) != 0)
296 *bestValue = v2orig->value;
300 for (i = 0; i < NUM_MATCHER; i++)
302 if (!FcStrCmpIgnoreCase ((FcChar8 *) _FcMatchers[i].object,
306 if (i == NUM_MATCHER)
309 *bestValue = v2orig->value;
317 for (v1 = v1orig; v1; v1 = v1->next)
319 for (v2 = v2orig; v2; v2 = v2->next)
321 v = (*_FcMatchers[i].compare) (_FcMatchers[i].object,
326 *result = FcResultTypeMismatch;
329 if (FcDebug () & FC_DBG_MATCHV)
330 printf (" v %g j %d ", v, j);
335 *bestValue = v2->value;
338 if (v1->binding == FcValueBindingStrong)
351 if (FcDebug () & FC_DBG_MATCHV)
353 printf (" %s: %g ", object, best);
354 FcValueListPrint (v1orig);
356 FcValueListPrint (v2orig);
361 int weak = _FcMatchers[i].weak;
362 int strong = _FcMatchers[i].strong;
364 value[strong] += best;
367 value[weak] += bestWeak;
368 value[strong] += bestStrong;
375 * Return a value indicating the distance between the two lists of
380 FcCompare (FcPattern *pat,
387 for (i = 0; i < NUM_MATCH_VALUES; i++)
392 while (i1 < pat->num && i2 < fnt->num)
394 i = strcmp (pat->elts[i1].object, fnt->elts[i2].object);
401 if (!FcCompareValueList (pat->elts[i1].object,
402 pat->elts[i1].values,
403 fnt->elts[i2].values,
414 for (i1 = 0; i1 < pat->num; i1++)
416 for (i2 = 0; i2 < fnt->num; i2++)
418 if (!strcmp (pat->elts[i1].object, fnt->elts[i2].object))
429 FcFontRenderPrepare (FcConfig *config,
435 FcPatternElt *fe, *pe;
439 new = FcPatternCreate ();
442 for (i = 0; i < font->num; i++)
445 pe = FcPatternFindElt (pat, fe->object);
448 if (!FcCompareValueList (pe->object, pe->values,
449 fe->values, &v, 0, &result))
451 FcPatternDestroy (new);
456 v = fe->values->value;
457 FcPatternAdd (new, fe->object, v, FcFalse);
459 for (i = 0; i < pat->num; i++)
462 fe = FcPatternFindElt (font, pe->object);
464 FcPatternAdd (new, pe->object, pe->values->value, FcTrue);
466 FcConfigSubstituteWithPat (config, new, pat, FcMatchFont);
471 FcFontSetMatch (FcConfig *config,
477 double score[NUM_MATCH_VALUES], bestscore[NUM_MATCH_VALUES];
484 for (i = 0; i < NUM_MATCH_VALUES; i++)
487 if (FcDebug () & FC_DBG_MATCH)
494 config = FcConfigGetCurrent ();
498 for (set = 0; set < nsets; set++)
503 for (f = 0; f < s->nfont; f++)
505 if (FcDebug () & FC_DBG_MATCHV)
507 printf ("Font %d ", f);
508 FcPatternPrint (s->fonts[f]);
510 if (!FcCompare (p, s->fonts[f], score, result))
512 if (FcDebug () & FC_DBG_MATCHV)
515 for (i = 0; i < NUM_MATCH_VALUES; i++)
517 printf (" %g", score[i]);
521 for (i = 0; i < NUM_MATCH_VALUES; i++)
523 if (best && bestscore[i] < score[i])
525 if (!best || score[i] < bestscore[i])
527 for (i = 0; i < NUM_MATCH_VALUES; i++)
528 bestscore[i] = score[i];
535 if (FcDebug () & FC_DBG_MATCH)
537 printf ("Best score");
538 for (i = 0; i < NUM_MATCH_VALUES; i++)
539 printf (" %g", bestscore[i]);
540 FcPatternPrint (best);
544 *result = FcResultNoMatch;
547 return FcFontRenderPrepare (config, p, best);
551 FcFontMatch (FcConfig *config,
560 config = FcConfigGetCurrent ();
565 if (config->fonts[FcSetSystem])
566 sets[nsets++] = config->fonts[FcSetSystem];
567 if (config->fonts[FcSetApplication])
568 sets[nsets++] = config->fonts[FcSetApplication];
569 return FcFontSetMatch (config, sets, nsets, p, result);
572 typedef struct _FcSortNode {
574 double score[NUM_MATCH_VALUES];
578 FcSortCompare (const void *aa, const void *ab)
580 FcSortNode *a = *(FcSortNode **) aa;
581 FcSortNode *b = *(FcSortNode **) ab;
582 double *as = &a->score[0];
583 double *bs = &b->score[0];
584 double ad = 0, bd = 0;
587 i = NUM_MATCH_VALUES;
588 while (i-- && (ad = *as++) == (bd = *bs++))
590 return ad < bd ? -1 : ad > bd ? 1 : 0;
594 FcSortWalk (FcSortNode **n, int nnode, FcFontSet *fs, FcCharSet **cs, FcBool trim)
602 if (FcPatternGetCharSet (node->pattern, FC_CHARSET, 0, &ncs) ==
606 * If this font isn't a subset of the previous fonts,
609 if (!trim || !*cs || !FcCharSetIsSubset (ncs, *cs))
613 ncs = FcCharSetUnion (ncs, *cs);
616 FcCharSetDestroy (*cs);
619 ncs = FcCharSetCopy (ncs);
621 FcPatternReference (node->pattern);
622 if (FcDebug () & FC_DBG_MATCH)
625 FcPatternPrint (node->pattern);
627 if (!FcFontSetAdd (fs, node->pattern))
629 FcPatternDestroy (node->pattern);
639 FcFontSetSortDestroy (FcFontSet *fs)
641 FcFontSetDestroy (fs);
645 FcFontSetSort (FcConfig *config,
656 FcSortNode **nodeps, **nodep;
664 if (FcDebug () & FC_DBG_MATCH)
670 for (set = 0; set < nsets; set++)
680 nodes = malloc (nnodes * sizeof (FcSortNode) + nnodes * sizeof (FcSortNode *));
683 nodeps = (FcSortNode **) (nodes + nnodes);
687 for (set = 0; set < nsets; set++)
692 for (f = 0; f < s->nfont; f++)
694 if (FcDebug () & FC_DBG_MATCHV)
696 printf ("Font %d ", f);
697 FcPatternPrint (s->fonts[f]);
699 new->pattern = s->fonts[f];
700 if (!FcCompare (p, new->pattern, new->score, result))
702 if (FcDebug () & FC_DBG_MATCHV)
705 for (i = 0; i < NUM_MATCH_VALUES; i++)
707 printf (" %g", new->score[i]);
717 nnodes = new - nodes;
719 qsort (nodeps, nnodes, sizeof (FcSortNode *),
722 ret = FcFontSetCreate ();
728 if (!FcSortWalk (nodeps, nnodes, ret, &cs, trim))
734 FcCharSetDestroy (cs);
742 FcCharSetDestroy (cs);
743 FcFontSetDestroy (ret);
751 FcFontSort (FcConfig *config,
762 config = FcConfigGetCurrent ();
767 if (config->fonts[FcSetSystem])
768 sets[nsets++] = config->fonts[FcSetSystem];
769 if (config->fonts[FcSetApplication])
770 sets[nsets++] = config->fonts[FcSetApplication];
771 return FcFontSetSort (config, sets, nsets, p, trim, csp, result);