]> git.wh0rd.org - fontconfig.git/blob - src/fcmatch.c
Initial revision
[fontconfig.git] / src / fcmatch.c
1 /*
2 * $XFree86: $
3 *
4 * Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc.
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 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.
15 *
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.
23 */
24
25 #include <string.h>
26 #include <ctype.h>
27 #include "fcint.h"
28 #include <stdio.h>
29
30 static double
31 FcCompareInteger (char *object, FcValue value1, FcValue value2)
32 {
33 int v;
34
35 if (value2.type != FcTypeInteger || value1.type != FcTypeInteger)
36 return -1.0;
37 v = value2.u.i - value1.u.i;
38 if (v < 0)
39 v = -v;
40 return (double) v;
41 }
42
43 static double
44 FcCompareString (char *object, FcValue value1, FcValue value2)
45 {
46 if (value2.type != FcTypeString || value1.type != FcTypeString)
47 return -1.0;
48 return (double) FcStrCmpIgnoreCase (value1.u.s, value2.u.s) != 0;
49 }
50
51 static double
52 FcCompareBool (char *object, FcValue value1, FcValue value2)
53 {
54 if (value2.type != FcTypeBool || value1.type != FcTypeBool)
55 return -1.0;
56 return (double) value2.u.b != value1.u.b;
57 }
58
59 static double
60 FcCompareCharSet (char *object, FcValue value1, FcValue value2)
61 {
62 if (value2.type != FcTypeCharSet || value1.type != FcTypeCharSet)
63 return -1.0;
64 return (double) FcCharSetSubtractCount (value1.u.c, value2.u.c);
65 }
66
67 static double
68 FcCompareSize (char *object, FcValue value1, FcValue value2)
69 {
70 double v1, v2, v;
71
72 switch (value1.type) {
73 case FcTypeInteger:
74 v1 = value1.u.i;
75 break;
76 case FcTypeDouble:
77 v1 = value1.u.d;
78 break;
79 default:
80 return -1;
81 }
82 switch (value2.type) {
83 case FcTypeInteger:
84 v2 = value2.u.i;
85 break;
86 case FcTypeDouble:
87 v2 = value2.u.d;
88 break;
89 default:
90 return -1;
91 }
92 if (v2 == 0)
93 return 0;
94 v = v2 - v1;
95 if (v < 0)
96 v = -v;
97 return v;
98 }
99
100 /*
101 * Order is significant, it defines the precedence of
102 * each value, earlier values are more significant than
103 * later values
104 */
105 static FcMatcher _FcMatchers [] = {
106 { FC_FOUNDRY, FcCompareString, },
107 { FC_CHARSET, FcCompareCharSet },
108 { FC_ANTIALIAS, FcCompareBool, },
109 { FC_LANG, FcCompareString },
110 { FC_FAMILY, FcCompareString, },
111 { FC_SPACING, FcCompareInteger, },
112 { FC_PIXEL_SIZE, FcCompareSize, },
113 { FC_STYLE, FcCompareString, },
114 { FC_SLANT, FcCompareInteger, },
115 { FC_WEIGHT, FcCompareInteger, },
116 { FC_RASTERIZER, FcCompareString, },
117 { FC_OUTLINE, FcCompareBool, },
118 };
119
120 #define NUM_MATCHER (sizeof _FcMatchers / sizeof _FcMatchers[0])
121
122 static FcBool
123 FcCompareValueList (const char *object,
124 FcValueList *v1orig, /* pattern */
125 FcValueList *v2orig, /* target */
126 FcValue *bestValue,
127 double *value,
128 FcResult *result)
129 {
130 FcValueList *v1, *v2;
131 double v, best;
132 int j;
133 int i;
134
135 for (i = 0; i < NUM_MATCHER; i++)
136 {
137 if (!FcStrCmpIgnoreCase (_FcMatchers[i].object, object))
138 break;
139 }
140 if (i == NUM_MATCHER)
141 {
142 if (bestValue)
143 *bestValue = v2orig->value;
144 return FcTrue;
145 }
146
147 best = 1e99;
148 j = 0;
149 for (v1 = v1orig; v1; v1 = v1->next)
150 {
151 for (v2 = v2orig; v2; v2 = v2->next)
152 {
153 v = (*_FcMatchers[i].compare) (_FcMatchers[i].object,
154 v1->value,
155 v2->value);
156 if (v < 0)
157 {
158 *result = FcResultTypeMismatch;
159 return FcFalse;
160 }
161 if (FcDebug () & FC_DBG_MATCHV)
162 printf (" v %g j %d ", v, j);
163 v = v * 100 + j;
164 if (v < best)
165 {
166 if (bestValue)
167 *bestValue = v2->value;
168 best = v;
169 }
170 }
171 j++;
172 }
173 if (FcDebug () & FC_DBG_MATCHV)
174 {
175 printf (" %s: %g ", object, best);
176 FcValueListPrint (v1orig);
177 printf (", ");
178 FcValueListPrint (v2orig);
179 printf ("\n");
180 }
181 value[i] += best;
182 return FcTrue;
183 }
184
185 /*
186 * Return a value indicating the distance between the two lists of
187 * values
188 */
189
190 static FcBool
191 FcCompare (FcPattern *pat,
192 FcPattern *fnt,
193 double *value,
194 FcResult *result)
195 {
196 int i, i1, i2;
197
198 for (i = 0; i < NUM_MATCHER; i++)
199 value[i] = 0.0;
200
201 for (i1 = 0; i1 < pat->num; i1++)
202 {
203 for (i2 = 0; i2 < fnt->num; i2++)
204 {
205 if (!FcStrCmpIgnoreCase (pat->elts[i1].object,
206 fnt->elts[i2].object))
207 {
208 if (!FcCompareValueList (pat->elts[i1].object,
209 pat->elts[i1].values,
210 fnt->elts[i2].values,
211 0,
212 value,
213 result))
214 return FcFalse;
215 break;
216 }
217 }
218 #if 0
219 /*
220 * Overspecified patterns are slightly penalized in
221 * case some other font includes the requested field
222 */
223 if (i2 == fnt->num)
224 {
225 for (i2 = 0; i2 < NUM_MATCHER; i2++)
226 {
227 if (!FcStrCmpIgnoreCase (_FcMatchers[i2].object,
228 pat->elts[i1].object))
229 {
230 value[i2] = 1.0;
231 break;
232 }
233 }
234 }
235 #endif
236 }
237 return FcTrue;
238 }
239
240 FcPattern *
241 FcFontMatch (FcConfig *config,
242 FcPattern *p,
243 FcResult *result)
244 {
245 double score[NUM_MATCHER], bestscore[NUM_MATCHER];
246 int f;
247 FcFontSet *s;
248 FcPattern *best;
249 FcPattern *new;
250 FcPatternElt *fe, *pe;
251 FcValue v;
252 int i;
253 FcSetName set;
254
255 for (i = 0; i < NUM_MATCHER; i++)
256 bestscore[i] = 0;
257 best = 0;
258 if (FcDebug () & FC_DBG_MATCH)
259 {
260 printf ("Match ");
261 FcPatternPrint (p);
262 }
263 if (!config)
264 {
265 config = FcConfigGetCurrent ();
266 if (!config)
267 return 0;
268 }
269 for (set = FcSetSystem; set <= FcSetApplication; set++)
270 {
271 s = config->fonts[set];
272 if (!s)
273 continue;
274 for (f = 0; f < s->nfont; f++)
275 {
276 if (FcDebug () & FC_DBG_MATCHV)
277 {
278 printf ("Font %d ", f);
279 FcPatternPrint (s->fonts[f]);
280 }
281 if (!FcCompare (p, s->fonts[f], score, result))
282 return 0;
283 if (FcDebug () & FC_DBG_MATCHV)
284 {
285 printf ("Score");
286 for (i = 0; i < NUM_MATCHER; i++)
287 {
288 printf (" %g", score[i]);
289 }
290 printf ("\n");
291 }
292 for (i = 0; i < NUM_MATCHER; i++)
293 {
294 if (best && bestscore[i] < score[i])
295 break;
296 if (!best || score[i] < bestscore[i])
297 {
298 for (i = 0; i < NUM_MATCHER; i++)
299 bestscore[i] = score[i];
300 best = s->fonts[f];
301 break;
302 }
303 }
304 }
305 }
306 if (FcDebug () & FC_DBG_MATCH)
307 {
308 printf ("Best score");
309 for (i = 0; i < NUM_MATCHER; i++)
310 printf (" %g", bestscore[i]);
311 FcPatternPrint (best);
312 }
313 if (!best)
314 {
315 *result = FcResultNoMatch;
316 return 0;
317 }
318 new = FcPatternCreate ();
319 if (!new)
320 return 0;
321 for (i = 0; i < best->num; i++)
322 {
323 fe = &best->elts[i];
324 pe = FcPatternFind (p, fe->object, FcFalse);
325 if (pe)
326 {
327 if (!FcCompareValueList (pe->object, pe->values,
328 fe->values, &v, score, result))
329 {
330 FcPatternDestroy (new);
331 return 0;
332 }
333 }
334 else
335 v = fe->values->value;
336 FcPatternAdd (new, fe->object, v, FcTrue);
337 }
338 for (i = 0; i < p->num; i++)
339 {
340 pe = &p->elts[i];
341 fe = FcPatternFind (best, pe->object, FcFalse);
342 if (!fe)
343 FcPatternAdd (new, pe->object, pe->values->value, FcTrue);
344 }
345 FcConfigSubstitute (config, new, FcMatchFont);
346 return new;
347 }