]> git.wh0rd.org - fontconfig.git/blob - src/fcmatch.c
Add fontversion field
[fontconfig.git] / src / fcmatch.c
1 /*
2 * $XFree86: xc/lib/fontconfig/src/fcmatch.c,v 1.20 2002/08/31 22:17:32 keithp Exp $
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 FcCompareFamily (char *object, FcValue value1, FcValue value2)
53 {
54 if (value2.type != FcTypeString || value1.type != FcTypeString)
55 return -1.0;
56 return (double) FcStrCmpIgnoreBlanksAndCase (value1.u.s, value2.u.s) != 0;
57 }
58
59 static double
60 FcCompareLang (char *object, FcValue value1, FcValue value2)
61 {
62 FcLangResult result;
63
64 switch (value1.type) {
65 case FcTypeLangSet:
66 switch (value2.type) {
67 case FcTypeLangSet:
68 result = FcLangSetCompare (value1.u.l, value2.u.l);
69 break;
70 case FcTypeString:
71 result = FcLangSetHasLang (value1.u.l, value2.u.s);
72 break;
73 default:
74 return -1.0;
75 }
76 break;
77 case FcTypeString:
78 switch (value2.type) {
79 case FcTypeLangSet:
80 result = FcLangSetHasLang (value2.u.l, value1.u.s);
81 break;
82 case FcTypeString:
83 result = FcLangCompare (value1.u.s, value2.u.s);
84 break;
85 default:
86 return -1.0;
87 }
88 break;
89 default:
90 return -1.0;
91 }
92 switch (result) {
93 case FcLangEqual:
94 return 0;
95 case FcLangDifferentCountry:
96 return 1;
97 case FcLangDifferentLang:
98 default:
99 return 2;
100 }
101 }
102
103 static double
104 FcCompareBool (char *object, FcValue value1, FcValue value2)
105 {
106 if (value2.type != FcTypeBool || value1.type != FcTypeBool)
107 return -1.0;
108 return (double) value2.u.b != value1.u.b;
109 }
110
111 static double
112 FcCompareCharSet (char *object, FcValue value1, FcValue value2)
113 {
114 if (value2.type != FcTypeCharSet || value1.type != FcTypeCharSet)
115 return -1.0;
116 return (double) FcCharSetSubtractCount (value1.u.c, value2.u.c);
117 }
118
119 static double
120 FcCompareSize (char *object, FcValue value1, FcValue value2)
121 {
122 double v1, v2, v;
123
124 switch (value1.type) {
125 case FcTypeInteger:
126 v1 = value1.u.i;
127 break;
128 case FcTypeDouble:
129 v1 = value1.u.d;
130 break;
131 default:
132 return -1;
133 }
134 switch (value2.type) {
135 case FcTypeInteger:
136 v2 = value2.u.i;
137 break;
138 case FcTypeDouble:
139 v2 = value2.u.d;
140 break;
141 default:
142 return -1;
143 }
144 if (v2 == 0)
145 return 0;
146 v = v2 - v1;
147 if (v < 0)
148 v = -v;
149 return v;
150 }
151
152 typedef struct _FcMatcher {
153 char *object;
154 double (*compare) (char *object, FcValue value1, FcValue value2);
155 int strong, weak;
156 } FcMatcher;
157
158 /*
159 * Order is significant, it defines the precedence of
160 * each value, earlier values are more significant than
161 * later values
162 */
163 static FcMatcher _FcMatchers [] = {
164 { FC_FOUNDRY, FcCompareString, 0, 0 },
165 #define MATCH_FOUNDRY 0
166
167 { FC_CHARSET, FcCompareCharSet, 1, 1 },
168 #define MATCH_CHARSET 1
169
170 { FC_FAMILY, FcCompareFamily, 2, 4 },
171 #define MATCH_FAMILY 2
172
173 { FC_LANG, FcCompareLang, 3, 3 },
174 #define MATCH_LANG 3
175
176 { FC_SPACING, FcCompareInteger, 5, 5 },
177 #define MATCH_SPACING 4
178
179 { FC_PIXEL_SIZE, FcCompareSize, 6, 6 },
180 #define MATCH_PIXEL_SIZE 5
181
182 { FC_STYLE, FcCompareString, 7, 7 },
183 #define MATCH_STYLE 6
184
185 { FC_SLANT, FcCompareInteger, 8, 8 },
186 #define MATCH_SLANT 7
187
188 { FC_WEIGHT, FcCompareInteger, 9, 9 },
189 #define MATCH_WEIGHT 8
190
191 { FC_ANTIALIAS, FcCompareBool, 10, 10 },
192 #define MATCH_ANTIALIAS 9
193
194 { FC_RASTERIZER, FcCompareString, 11, 11 },
195 #define MATCH_RASTERIZER 10
196
197 { FC_OUTLINE, FcCompareBool, 12, 12 },
198 #define MATCH_OUTLINE 11
199
200 { FC_FONTVERSION, FcCompareInteger, 13, 13 },
201 #define MATCH_FONTVERSION 12
202 };
203
204 #define NUM_MATCH_VALUES 14
205
206 static FcBool
207 FcCompareValueList (const char *object,
208 FcValueList *v1orig, /* pattern */
209 FcValueList *v2orig, /* target */
210 FcValue *bestValue,
211 double *value,
212 FcResult *result)
213 {
214 FcValueList *v1, *v2;
215 double v, best, bestStrong, bestWeak;
216 int i;
217 int j;
218
219 /*
220 * Locate the possible matching entry by examining the
221 * first few characters in object
222 */
223 i = -1;
224 switch (FcToLower (object[0])) {
225 case 'f':
226 switch (FcToLower (object[1])) {
227 case 'o':
228 switch (FcToLower (object[2])) {
229 case 'u':
230 i = MATCH_FOUNDRY; break;
231 case 'n':
232 i = MATCH_FONTVERSION; break;
233 }
234 break;
235 case 'a':
236 i = MATCH_FAMILY; break;
237 }
238 break;
239 case 'c':
240 i = MATCH_CHARSET; break;
241 case 'a':
242 i = MATCH_ANTIALIAS; break;
243 case 'l':
244 i = MATCH_LANG; break;
245 case 's':
246 switch (FcToLower (object[1])) {
247 case 'p':
248 i = MATCH_SPACING; break;
249 case 't':
250 i = MATCH_STYLE; break;
251 case 'l':
252 i = MATCH_SLANT; break;
253 }
254 break;
255 case 'p':
256 i = MATCH_PIXEL_SIZE; break;
257 case 'w':
258 i = MATCH_WEIGHT; break;
259 case 'r':
260 i = MATCH_RASTERIZER; break;
261 case 'o':
262 i = MATCH_OUTLINE; break;
263 }
264 if (i == -1 ||
265 FcStrCmpIgnoreCase ((FcChar8 *) _FcMatchers[i].object,
266 (FcChar8 *) object) != 0)
267 {
268 if (bestValue)
269 *bestValue = v2orig->value;
270 return FcTrue;
271 }
272 #if 0
273 for (i = 0; i < NUM_MATCHER; i++)
274 {
275 if (!FcStrCmpIgnoreCase ((FcChar8 *) _FcMatchers[i].object,
276 (FcChar8 *) object))
277 break;
278 }
279 if (i == NUM_MATCHER)
280 {
281 if (bestValue)
282 *bestValue = v2orig->value;
283 return FcTrue;
284 }
285 #endif
286 best = 1e99;
287 bestStrong = 1e99;
288 bestWeak = 1e99;
289 j = 0;
290 for (v1 = v1orig; v1; v1 = v1->next)
291 {
292 for (v2 = v2orig; v2; v2 = v2->next)
293 {
294 v = (*_FcMatchers[i].compare) (_FcMatchers[i].object,
295 v1->value,
296 v2->value);
297 if (v < 0)
298 {
299 *result = FcResultTypeMismatch;
300 return FcFalse;
301 }
302 if (FcDebug () & FC_DBG_MATCHV)
303 printf (" v %g j %d ", v, j);
304 v = v * 100 + j;
305 if (v < best)
306 {
307 if (bestValue)
308 *bestValue = v2->value;
309 best = v;
310 }
311 if (v1->binding == FcValueBindingStrong)
312 {
313 if (v < bestStrong)
314 bestStrong = v;
315 }
316 else
317 {
318 if (v < bestWeak)
319 bestWeak = v;
320 }
321 }
322 j++;
323 }
324 if (FcDebug () & FC_DBG_MATCHV)
325 {
326 printf (" %s: %g ", object, best);
327 FcValueListPrint (v1orig);
328 printf (", ");
329 FcValueListPrint (v2orig);
330 printf ("\n");
331 }
332 if (value)
333 {
334 int weak = _FcMatchers[i].weak;
335 int strong = _FcMatchers[i].strong;
336 if (weak == strong)
337 value[strong] += best;
338 else
339 {
340 value[weak] += bestWeak;
341 value[strong] += bestStrong;
342 }
343 }
344 return FcTrue;
345 }
346
347 /*
348 * Return a value indicating the distance between the two lists of
349 * values
350 */
351
352 static FcBool
353 FcCompare (FcPattern *pat,
354 FcPattern *fnt,
355 double *value,
356 FcResult *result)
357 {
358 int i, i1, i2;
359
360 for (i = 0; i < NUM_MATCH_VALUES; i++)
361 value[i] = 0.0;
362
363 i1 = 0;
364 i2 = 0;
365 while (i1 < pat->num && i2 < fnt->num)
366 {
367 i = strcmp (pat->elts[i1].object, fnt->elts[i2].object);
368 if (i > 0)
369 i2++;
370 else if (i < 0)
371 i1++;
372 else
373 {
374 if (!FcCompareValueList (pat->elts[i1].object,
375 pat->elts[i1].values,
376 fnt->elts[i2].values,
377 0,
378 value,
379 result))
380 return FcFalse;
381 i1++;
382 i2++;
383 }
384 }
385 return FcTrue;
386 #if 0
387 for (i1 = 0; i1 < pat->num; i1++)
388 {
389 for (i2 = 0; i2 < fnt->num; i2++)
390 {
391 if (!strcmp (pat->elts[i1].object, fnt->elts[i2].object))
392 {
393 break;
394 }
395 }
396 }
397 return FcTrue;
398 #endif
399 }
400
401 FcPattern *
402 FcFontRenderPrepare (FcConfig *config,
403 FcPattern *pat,
404 FcPattern *font)
405 {
406 FcPattern *new;
407 int i;
408 FcPatternElt *fe, *pe;
409 FcValue v;
410 FcResult result;
411
412 new = FcPatternCreate ();
413 if (!new)
414 return 0;
415 for (i = 0; i < font->num; i++)
416 {
417 fe = &font->elts[i];
418 pe = FcPatternFindElt (pat, fe->object);
419 if (pe)
420 {
421 if (!FcCompareValueList (pe->object, pe->values,
422 fe->values, &v, 0, &result))
423 {
424 FcPatternDestroy (new);
425 return 0;
426 }
427 }
428 else
429 v = fe->values->value;
430 FcPatternAdd (new, fe->object, v, FcFalse);
431 }
432 for (i = 0; i < pat->num; i++)
433 {
434 pe = &pat->elts[i];
435 fe = FcPatternFindElt (font, pe->object);
436 if (!fe)
437 FcPatternAdd (new, pe->object, pe->values->value, FcTrue);
438 }
439 FcConfigSubstituteWithPat (config, new, pat, FcMatchFont);
440 return new;
441 }
442
443 FcPattern *
444 FcFontSetMatch (FcConfig *config,
445 FcFontSet **sets,
446 int nsets,
447 FcPattern *p,
448 FcResult *result)
449 {
450 double score[NUM_MATCH_VALUES], bestscore[NUM_MATCH_VALUES];
451 int f;
452 FcFontSet *s;
453 FcPattern *best;
454 int i;
455 int set;
456
457 for (i = 0; i < NUM_MATCH_VALUES; i++)
458 bestscore[i] = 0;
459 best = 0;
460 if (FcDebug () & FC_DBG_MATCH)
461 {
462 printf ("Match ");
463 FcPatternPrint (p);
464 }
465 if (!config)
466 {
467 config = FcConfigGetCurrent ();
468 if (!config)
469 return 0;
470 }
471 for (set = 0; set < nsets; set++)
472 {
473 s = sets[set];
474 if (!s)
475 continue;
476 for (f = 0; f < s->nfont; f++)
477 {
478 if (FcDebug () & FC_DBG_MATCHV)
479 {
480 printf ("Font %d ", f);
481 FcPatternPrint (s->fonts[f]);
482 }
483 if (!FcCompare (p, s->fonts[f], score, result))
484 return 0;
485 if (FcDebug () & FC_DBG_MATCHV)
486 {
487 printf ("Score");
488 for (i = 0; i < NUM_MATCH_VALUES; i++)
489 {
490 printf (" %g", score[i]);
491 }
492 printf ("\n");
493 }
494 for (i = 0; i < NUM_MATCH_VALUES; i++)
495 {
496 if (best && bestscore[i] < score[i])
497 break;
498 if (!best || score[i] < bestscore[i])
499 {
500 for (i = 0; i < NUM_MATCH_VALUES; i++)
501 bestscore[i] = score[i];
502 best = s->fonts[f];
503 break;
504 }
505 }
506 }
507 }
508 if (FcDebug () & FC_DBG_MATCH)
509 {
510 printf ("Best score");
511 for (i = 0; i < NUM_MATCH_VALUES; i++)
512 printf (" %g", bestscore[i]);
513 FcPatternPrint (best);
514 }
515 if (!best)
516 {
517 *result = FcResultNoMatch;
518 return 0;
519 }
520 return FcFontRenderPrepare (config, p, best);
521 }
522
523 FcPattern *
524 FcFontMatch (FcConfig *config,
525 FcPattern *p,
526 FcResult *result)
527 {
528 FcFontSet *sets[2];
529 int nsets;
530
531 if (!config)
532 {
533 config = FcConfigGetCurrent ();
534 if (!config)
535 return 0;
536 }
537 nsets = 0;
538 if (config->fonts[FcSetSystem])
539 sets[nsets++] = config->fonts[FcSetSystem];
540 if (config->fonts[FcSetApplication])
541 sets[nsets++] = config->fonts[FcSetApplication];
542 return FcFontSetMatch (config, sets, nsets, p, result);
543 }
544
545 typedef struct _FcSortNode {
546 FcPattern *pattern;
547 double score[NUM_MATCH_VALUES];
548 } FcSortNode;
549
550 static int
551 FcSortCompare (const void *aa, const void *ab)
552 {
553 FcSortNode *a = *(FcSortNode **) aa;
554 FcSortNode *b = *(FcSortNode **) ab;
555 double *as = &a->score[0];
556 double *bs = &b->score[0];
557 double ad = 0, bd = 0;
558 int i;
559
560 i = NUM_MATCH_VALUES;
561 while (i-- && (ad = *as++) == (bd = *bs++))
562 ;
563 return ad < bd ? -1 : ad > bd ? 1 : 0;
564 }
565
566 static FcBool
567 FcSortWalk (FcSortNode **n, int nnode, FcFontSet *fs, FcCharSet **cs, FcBool trim)
568 {
569 FcCharSet *ncs;
570 FcSortNode *node;
571
572 while (nnode--)
573 {
574 node = *n++;
575 if (FcPatternGetCharSet (node->pattern, FC_CHARSET, 0, &ncs) ==
576 FcResultMatch)
577 {
578 /*
579 * If this font isn't a subset of the previous fonts,
580 * add it to the list
581 */
582 if (!trim || !*cs || !FcCharSetIsSubset (ncs, *cs))
583 {
584 if (*cs)
585 {
586 ncs = FcCharSetUnion (ncs, *cs);
587 if (!ncs)
588 return FcFalse;
589 FcCharSetDestroy (*cs);
590 }
591 else
592 ncs = FcCharSetCopy (ncs);
593 *cs = ncs;
594 FcPatternReference (node->pattern);
595 if (FcDebug () & FC_DBG_MATCH)
596 {
597 printf ("Add ");
598 FcPatternPrint (node->pattern);
599 }
600 if (!FcFontSetAdd (fs, node->pattern))
601 {
602 FcPatternDestroy (node->pattern);
603 return FcFalse;
604 }
605 }
606 }
607 }
608 return FcTrue;
609 }
610
611 void
612 FcFontSetSortDestroy (FcFontSet *fs)
613 {
614 FcFontSetDestroy (fs);
615 }
616
617 FcFontSet *
618 FcFontSetSort (FcConfig *config,
619 FcFontSet **sets,
620 int nsets,
621 FcPattern *p,
622 FcBool trim,
623 FcCharSet **csp,
624 FcResult *result)
625 {
626 FcFontSet *ret;
627 FcFontSet *s;
628 FcSortNode *nodes;
629 FcSortNode **nodeps, **nodep;
630 int nnodes;
631 FcSortNode *new;
632 FcCharSet *cs;
633 int set;
634 int f;
635 int i;
636
637 if (FcDebug () & FC_DBG_MATCH)
638 {
639 printf ("Sort ");
640 FcPatternPrint (p);
641 }
642 nnodes = 0;
643 for (set = 0; set < nsets; set++)
644 {
645 s = sets[set];
646 if (!s)
647 continue;
648 nnodes += s->nfont;
649 }
650 if (!nnodes)
651 goto bail0;
652 /* freed below */
653 nodes = malloc (nnodes * sizeof (FcSortNode) + nnodes * sizeof (FcSortNode *));
654 if (!nodes)
655 goto bail0;
656 nodeps = (FcSortNode **) (nodes + nnodes);
657
658 new = nodes;
659 nodep = nodeps;
660 for (set = 0; set < nsets; set++)
661 {
662 s = sets[set];
663 if (!s)
664 continue;
665 for (f = 0; f < s->nfont; f++)
666 {
667 if (FcDebug () & FC_DBG_MATCHV)
668 {
669 printf ("Font %d ", f);
670 FcPatternPrint (s->fonts[f]);
671 }
672 new->pattern = s->fonts[f];
673 if (!FcCompare (p, new->pattern, new->score, result))
674 goto bail1;
675 if (FcDebug () & FC_DBG_MATCHV)
676 {
677 printf ("Score");
678 for (i = 0; i < NUM_MATCH_VALUES; i++)
679 {
680 printf (" %g", new->score[i]);
681 }
682 printf ("\n");
683 }
684 *nodep = new;
685 new++;
686 nodep++;
687 }
688 }
689
690 nnodes = new - nodes;
691
692 qsort (nodeps, nnodes, sizeof (FcSortNode *),
693 FcSortCompare);
694
695 ret = FcFontSetCreate ();
696 if (!ret)
697 goto bail1;
698
699 cs = 0;
700
701 if (!FcSortWalk (nodeps, nnodes, ret, &cs, trim))
702 goto bail2;
703
704 if (csp)
705 *csp = cs;
706 else
707 FcCharSetDestroy (cs);
708
709 free (nodes);
710
711 return ret;
712
713 bail2:
714 if (cs)
715 FcCharSetDestroy (cs);
716 FcFontSetDestroy (ret);
717 bail1:
718 free (nodes);
719 bail0:
720 return 0;
721 }
722
723 FcFontSet *
724 FcFontSort (FcConfig *config,
725 FcPattern *p,
726 FcBool trim,
727 FcCharSet **csp,
728 FcResult *result)
729 {
730 FcFontSet *sets[2];
731 int nsets;
732
733 if (!config)
734 {
735 config = FcConfigGetCurrent ();
736 if (!config)
737 return 0;
738 }
739 nsets = 0;
740 if (config->fonts[FcSetSystem])
741 sets[nsets++] = config->fonts[FcSetSystem];
742 if (config->fonts[FcSetApplication])
743 sets[nsets++] = config->fonts[FcSetApplication];
744 return FcFontSetSort (config, sets, nsets, p, trim, csp, result);
745 }