Reimplement FC_LANG as FcTypeLang, freeze patterns, other cleanup
[platform/upstream/fontconfig.git] / src / fcmatch.c
1 /*
2  * $XFree86: xc/lib/fontconfig/src/fcmatch.c,v 1.18 2002/08/19 19:32:05 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
201 #define NUM_MATCH_VALUES    13
202
203 static FcBool
204 FcCompareValueList (const char  *object,
205                     FcValueList *v1orig,        /* pattern */
206                     FcValueList *v2orig,        /* target */
207                     FcValue     *bestValue,
208                     double      *value,
209                     FcResult    *result)
210 {
211     FcValueList    *v1, *v2;
212     double          v, best, bestStrong, bestWeak;
213     int             i;
214     int             j;
215     
216     /*
217      * Locate the possible matching entry by examining the
218      * first few characters in object
219      */
220     i = -1;
221     switch (FcToLower (object[0])) {
222     case 'f':
223         switch (FcToLower (object[1])) {
224         case 'o':
225             i = MATCH_FOUNDRY; break;
226         case 'a':
227             i = MATCH_FAMILY; break;
228         }
229         break;
230     case 'c':
231         i = MATCH_CHARSET; break;
232     case 'a':
233         i = MATCH_ANTIALIAS; break;
234     case 'l':
235         i = MATCH_LANG; break;
236     case 's':
237         switch (FcToLower (object[1])) {
238         case 'p':
239             i = MATCH_SPACING; break;
240         case 't':
241             i = MATCH_STYLE; break;
242         case 'l':
243             i = MATCH_SLANT; break;
244         }
245         break;
246     case 'p':
247         i = MATCH_PIXEL_SIZE; break;
248     case 'w':
249         i = MATCH_WEIGHT; break;
250     case 'r':
251         i = MATCH_RASTERIZER; break;
252     case 'o':
253         i = MATCH_OUTLINE; break;
254     }
255     if (i == -1 || 
256         FcStrCmpIgnoreCase ((FcChar8 *) _FcMatchers[i].object,
257                             (FcChar8 *) object) != 0)
258     {
259         if (bestValue)
260             *bestValue = v2orig->value;
261         return FcTrue;
262     }
263 #if 0
264     for (i = 0; i < NUM_MATCHER; i++)
265     {
266         if (!FcStrCmpIgnoreCase ((FcChar8 *) _FcMatchers[i].object,
267                                  (FcChar8 *) object))
268             break;
269     }
270     if (i == NUM_MATCHER)
271     {
272         if (bestValue)
273             *bestValue = v2orig->value;
274         return FcTrue;
275     }
276 #endif
277     best = 1e99;
278     bestStrong = 1e99;
279     bestWeak = 1e99;
280     j = 0;
281     for (v1 = v1orig; v1; v1 = v1->next)
282     {
283         for (v2 = v2orig; v2; v2 = v2->next)
284         {
285             v = (*_FcMatchers[i].compare) (_FcMatchers[i].object,
286                                             v1->value,
287                                             v2->value);
288             if (v < 0)
289             {
290                 *result = FcResultTypeMismatch;
291                 return FcFalse;
292             }
293             if (FcDebug () & FC_DBG_MATCHV)
294                 printf (" v %g j %d ", v, j);
295             v = v * 100 + j;
296             if (v < best)
297             {
298                 if (bestValue)
299                     *bestValue = v2->value;
300                 best = v;
301             }
302             if (v1->binding == FcValueBindingStrong)
303             {
304                 if (v < bestStrong)
305                     bestStrong = v;
306             }
307             else
308             {
309                 if (v < bestWeak)
310                     bestWeak = v;
311             }
312         }
313         j++;
314     }
315     if (FcDebug () & FC_DBG_MATCHV)
316     {
317         printf (" %s: %g ", object, best);
318         FcValueListPrint (v1orig);
319         printf (", ");
320         FcValueListPrint (v2orig);
321         printf ("\n");
322     }
323     if (value)
324     {
325         int weak    = _FcMatchers[i].weak;
326         int strong  = _FcMatchers[i].strong;
327         if (weak == strong)
328             value[strong] += best;
329         else
330         {
331             value[weak] += bestWeak;
332             value[strong] += bestStrong;
333         }
334     }
335     return FcTrue;
336 }
337
338 /*
339  * Return a value indicating the distance between the two lists of
340  * values
341  */
342
343 static FcBool
344 FcCompare (FcPattern    *pat,
345            FcPattern    *fnt,
346            double       *value,
347            FcResult     *result)
348 {
349     int             i, i1, i2;
350     
351     for (i = 0; i < NUM_MATCH_VALUES; i++)
352         value[i] = 0.0;
353     
354     i1 = 0;
355     i2 = 0;
356     while (i1 < pat->num && i2 < fnt->num)
357     {
358         i = strcmp (pat->elts[i1].object, fnt->elts[i2].object);
359         if (i > 0)
360             i2++;
361         else if (i < 0)
362             i1++;
363         else
364         {
365             if (!FcCompareValueList (pat->elts[i1].object,
366                                      pat->elts[i1].values,
367                                      fnt->elts[i2].values,
368                                      0,
369                                      value,
370                                      result))
371                 return FcFalse;
372             i1++;
373             i2++;
374         }
375     }
376     return FcTrue;
377 #if 0
378     for (i1 = 0; i1 < pat->num; i1++)
379     {
380         for (i2 = 0; i2 < fnt->num; i2++)
381         {
382             if (!strcmp (pat->elts[i1].object, fnt->elts[i2].object))
383             {
384                 break;
385             }
386         }
387     }
388     return FcTrue;
389 #endif
390 }
391
392 FcPattern *
393 FcFontRenderPrepare (FcConfig       *config,
394                      FcPattern      *pat,
395                      FcPattern      *font)
396 {
397     FcPattern       *new;
398     int             i;
399     FcPatternElt    *fe, *pe;
400     FcValue         v;
401     FcResult        result;
402     
403     new = FcPatternCreate ();
404     if (!new)
405         return 0;
406     for (i = 0; i < font->num; i++)
407     {
408         fe = &font->elts[i];
409         pe = FcPatternFindElt (pat, fe->object);
410         if (pe)
411         {
412             if (!FcCompareValueList (pe->object, pe->values, 
413                                      fe->values, &v, 0, &result))
414             {
415                 FcPatternDestroy (new);
416                 return 0;
417             }
418         }
419         else
420             v = fe->values->value;
421         FcPatternAdd (new, fe->object, v, FcFalse);
422     }
423     for (i = 0; i < pat->num; i++)
424     {
425         pe = &pat->elts[i];
426         fe = FcPatternFindElt (font, pe->object);
427         if (!fe)
428             FcPatternAdd (new, pe->object, pe->values->value, FcTrue);
429     }
430     FcConfigSubstituteWithPat (config, new, pat, FcMatchFont);
431     return new;
432 }
433
434 FcPattern *
435 FcFontSetMatch (FcConfig    *config,
436                 FcFontSet   **sets,
437                 int         nsets,
438                 FcPattern   *p,
439                 FcResult    *result)
440 {
441     double          score[NUM_MATCH_VALUES], bestscore[NUM_MATCH_VALUES];
442     int             f;
443     FcFontSet       *s;
444     FcPattern       *best;
445     int             i;
446     int             set;
447
448     for (i = 0; i < NUM_MATCH_VALUES; i++)
449         bestscore[i] = 0;
450     best = 0;
451     if (FcDebug () & FC_DBG_MATCH)
452     {
453         printf ("Match ");
454         FcPatternPrint (p);
455     }
456     if (!config)
457     {
458         config = FcConfigGetCurrent ();
459         if (!config)
460             return 0;
461     }
462     for (set = 0; set < nsets; set++)
463     {
464         s = sets[set];
465         if (!s)
466             continue;
467         for (f = 0; f < s->nfont; f++)
468         {
469             if (FcDebug () & FC_DBG_MATCHV)
470             {
471                 printf ("Font %d ", f);
472                 FcPatternPrint (s->fonts[f]);
473             }
474             if (!FcCompare (p, s->fonts[f], score, result))
475                 return 0;
476             if (FcDebug () & FC_DBG_MATCHV)
477             {
478                 printf ("Score");
479                 for (i = 0; i < NUM_MATCH_VALUES; i++)
480                 {
481                     printf (" %g", score[i]);
482                 }
483                 printf ("\n");
484             }
485             for (i = 0; i < NUM_MATCH_VALUES; i++)
486             {
487                 if (best && bestscore[i] < score[i])
488                     break;
489                 if (!best || score[i] < bestscore[i])
490                 {
491                     for (i = 0; i < NUM_MATCH_VALUES; i++)
492                         bestscore[i] = score[i];
493                     best = s->fonts[f];
494                     break;
495                 }
496             }
497         }
498     }
499     if (FcDebug () & FC_DBG_MATCH)
500     {
501         printf ("Best score");
502         for (i = 0; i < NUM_MATCH_VALUES; i++)
503             printf (" %g", bestscore[i]);
504         FcPatternPrint (best);
505     }
506     if (!best)
507     {
508         *result = FcResultNoMatch;
509         return 0;
510     }
511     return FcFontRenderPrepare (config, p, best);
512 }
513
514 FcPattern *
515 FcFontMatch (FcConfig   *config,
516              FcPattern  *p, 
517              FcResult   *result)
518 {
519     FcFontSet   *sets[2];
520     int         nsets;
521
522     if (!config)
523     {
524         config = FcConfigGetCurrent ();
525         if (!config)
526             return 0;
527     }
528     nsets = 0;
529     if (config->fonts[FcSetSystem])
530         sets[nsets++] = config->fonts[FcSetSystem];
531     if (config->fonts[FcSetApplication])
532         sets[nsets++] = config->fonts[FcSetApplication];
533     return FcFontSetMatch (config, sets, nsets, p, result);
534 }
535
536 typedef struct _FcSortNode {
537     FcPattern   *pattern;
538     double      score[NUM_MATCH_VALUES];
539 } FcSortNode;
540
541 static int
542 FcSortCompare (const void *aa, const void *ab)
543 {
544     FcSortNode  *a = *(FcSortNode **) aa;
545     FcSortNode  *b = *(FcSortNode **) ab;
546     double      *as = &a->score[0];
547     double      *bs = &b->score[0];
548     double      ad = 0, bd = 0;
549     int         i;
550
551     i = NUM_MATCH_VALUES;
552     while (i-- && (ad = *as++) == (bd = *bs++))
553         ;
554     return ad < bd ? -1 : ad > bd ? 1 : 0;
555 }
556
557 static FcBool
558 FcSortWalk (FcSortNode **n, int nnode, FcFontSet *fs, FcCharSet **cs, FcBool trim)
559 {
560     FcCharSet   *ncs;
561     FcSortNode  *node;
562
563     while (nnode--)
564     {
565         node = *n++;
566         if (FcPatternGetCharSet (node->pattern, FC_CHARSET, 0, &ncs) == 
567             FcResultMatch)
568         {
569             /*
570              * If this font isn't a subset of the previous fonts,
571              * add it to the list
572              */
573             if (!trim || !*cs || !FcCharSetIsSubset (ncs, *cs))
574             {
575                 if (*cs)
576                 {
577                     ncs = FcCharSetUnion (ncs, *cs);
578                     if (!ncs)
579                         return FcFalse;
580                     FcCharSetDestroy (*cs);
581                 }
582                 else
583                     ncs = FcCharSetCopy (ncs);
584                 *cs = ncs;
585                 FcPatternReference (node->pattern);
586                 if (FcDebug () & FC_DBG_MATCH)
587                 {
588                     printf ("Add ");
589                     FcPatternPrint (node->pattern);
590                 }
591                 if (!FcFontSetAdd (fs, node->pattern))
592                 {
593                     FcPatternDestroy (node->pattern);
594                     return FcFalse;
595                 }
596             }
597         }
598     }
599     return FcTrue;
600 }
601
602 void
603 FcFontSetSortDestroy (FcFontSet *fs)
604 {
605     FcFontSetDestroy (fs);
606 }
607
608 FcFontSet *
609 FcFontSetSort (FcConfig     *config,
610                FcFontSet    **sets,
611                int          nsets,
612                FcPattern    *p,
613                FcBool       trim,
614                FcCharSet    **csp,
615                FcResult     *result)
616 {
617     FcFontSet       *ret;
618     FcFontSet       *s;
619     FcSortNode      *nodes;
620     FcSortNode      **nodeps, **nodep;
621     int             nnodes;
622     FcSortNode      *new;
623     FcCharSet       *cs;
624     int             set;
625     int             f;
626     int             i;
627
628     if (FcDebug () & FC_DBG_MATCH)
629     {
630         printf ("Sort ");
631         FcPatternPrint (p);
632     }
633     nnodes = 0;
634     for (set = 0; set < nsets; set++)
635     {
636         s = sets[set];
637         if (!s)
638             continue;
639         nnodes += s->nfont;
640     }
641     if (!nnodes)
642         goto bail0;
643     nodes = malloc (nnodes * sizeof (FcSortNode) + nnodes * sizeof (FcSortNode *));
644     if (!nodes)
645         goto bail0;
646     nodeps = (FcSortNode **) (nodes + nnodes);
647     
648     new = nodes;
649     nodep = nodeps;
650     for (set = 0; set < nsets; set++)
651     {
652         s = sets[set];
653         if (!s)
654             continue;
655         for (f = 0; f < s->nfont; f++)
656         {
657             if (FcDebug () & FC_DBG_MATCHV)
658             {
659                 printf ("Font %d ", f);
660                 FcPatternPrint (s->fonts[f]);
661             }
662             new->pattern = s->fonts[f];
663             if (!FcCompare (p, new->pattern, new->score, result))
664                 goto bail1;
665             if (FcDebug () & FC_DBG_MATCHV)
666             {
667                 printf ("Score");
668                 for (i = 0; i < NUM_MATCH_VALUES; i++)
669                 {
670                     printf (" %g", new->score[i]);
671                 }
672                 printf ("\n");
673             }
674             *nodep = new;
675             new++;
676             nodep++;
677         }
678     }
679
680     nnodes = new - nodes;
681     
682     qsort (nodeps, nnodes, sizeof (FcSortNode *),
683            FcSortCompare);
684
685     ret = FcFontSetCreate ();
686     if (!ret)
687         goto bail1;
688
689     cs = 0;
690
691     if (!FcSortWalk (nodeps, nnodes, ret, &cs, trim))
692         goto bail2;
693
694     if (csp)
695         *csp = cs;
696     else
697         FcCharSetDestroy (cs);
698
699     free (nodes);
700
701     return ret;
702
703 bail2:
704     if (cs)
705         FcCharSetDestroy (cs);
706     FcFontSetDestroy (ret);
707 bail1:
708     free (nodes);
709 bail0:
710     return 0;
711 }
712
713 FcFontSet *
714 FcFontSort (FcConfig    *config,
715             FcPattern   *p, 
716             FcBool      trim,
717             FcCharSet   **csp,
718             FcResult    *result)
719 {
720     FcFontSet   *sets[2];
721     int         nsets;
722
723     if (!config)
724     {
725         config = FcConfigGetCurrent ();
726         if (!config)
727             return 0;
728     }
729     nsets = 0;
730     if (config->fonts[FcSetSystem])
731         sets[nsets++] = config->fonts[FcSetSystem];
732     if (config->fonts[FcSetApplication])
733         sets[nsets++] = config->fonts[FcSetApplication];
734     return FcFontSetSort (config, sets, nsets, p, trim, csp, result);
735 }