]> git.wh0rd.org - fontconfig.git/blobdiff - src/fcmatch.c
Fix weird first/not-first lameness in font matches, replacing with target
[fontconfig.git] / src / fcmatch.c
index 069f69ff087cf5156704cf49607e882c7c3b04e4..99e9fd1b7c094b414865c542a733e1de9567cf30 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $XFree86: $
+ * $XFree86: xc/lib/fontconfig/src/fcmatch.c,v 1.16 2002/07/06 23:47:44 keithp Exp $
  *
  * Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc.
  *
@@ -48,6 +48,33 @@ FcCompareString (char *object, FcValue value1, FcValue value2)
     return (double) FcStrCmpIgnoreCase (value1.u.s, value2.u.s) != 0;
 }
 
+static double
+FcCompareFamily (char *object, FcValue value1, FcValue value2)
+{
+    if (value2.type != FcTypeString || value1.type != FcTypeString)
+       return -1.0;
+    return (double) FcStrCmpIgnoreBlanksAndCase (value1.u.s, value2.u.s) != 0;
+}
+
+static double
+FcCompareLang (char *object, FcValue value1, FcValue value2)
+{
+    FcLangResult    result;
+    
+    if (value2.type != FcTypeString || value1.type != FcTypeString)
+       return -1.0;
+    result = FcLangCompare (value1.u.s, value2.u.s);
+    switch (result) {
+    case FcLangEqual:
+       return 0;
+    case FcLangDifferentCountry:
+       return 1;
+    case FcLangDifferentLang:
+    default:
+       return 2;
+    }
+}
+
 static double
 FcCompareBool (char *object, FcValue value1, FcValue value2)
 {
@@ -97,27 +124,56 @@ FcCompareSize (char *object, FcValue value1, FcValue value2)
     return v;
 }
 
+typedef struct _FcMatcher {
+    char           *object;
+    double         (*compare) (char *object, FcValue value1, FcValue value2);
+    int                    strong, weak;
+} FcMatcher;
+
 /*
  * Order is significant, it defines the precedence of
  * each value, earlier values are more significant than
  * later values
  */
 static FcMatcher _FcMatchers [] = {
-    { FC_FOUNDRY,      FcCompareString, },
-    { FC_CHARSET,      FcCompareCharSet },
-    { FC_ANTIALIAS,    FcCompareBool, },
-    { FC_LANG,         FcCompareString },
-    { FC_FAMILY,       FcCompareString, },
-    { FC_SPACING,      FcCompareInteger, },
-    { FC_PIXEL_SIZE,   FcCompareSize, },
-    { FC_STYLE,                FcCompareString, },
-    { FC_SLANT,                FcCompareInteger, },
-    { FC_WEIGHT,       FcCompareInteger, },
-    { FC_RASTERIZER,   FcCompareString, },
-    { FC_OUTLINE,      FcCompareBool, },
+    { FC_FOUNDRY,      FcCompareString,        0, 0 },
+#define MATCH_FOUNDRY      0
+    
+    { FC_CHARSET,      FcCompareCharSet,       1, 1 },
+#define MATCH_CHARSET      1
+    
+    { FC_FAMILY,       FcCompareFamily,        2, 4 },
+#define MATCH_FAMILY       2
+    
+    { FC_LANG,         FcCompareLang,          3, 3 },
+#define MATCH_LANG         3
+    
+    { FC_SPACING,      FcCompareInteger,       5, 5 },
+#define MATCH_SPACING      4
+    
+    { FC_PIXEL_SIZE,   FcCompareSize,          6, 6 },
+#define MATCH_PIXEL_SIZE    5
+    
+    { FC_STYLE,                FcCompareString,        7, 7 },
+#define MATCH_STYLE        6
+    
+    { FC_SLANT,                FcCompareInteger,       8, 8 },
+#define MATCH_SLANT        7
+    
+    { FC_WEIGHT,       FcCompareInteger,       9, 9 },
+#define MATCH_WEIGHT       8
+    
+    { FC_ANTIALIAS,    FcCompareBool,          10, 10 },
+#define MATCH_ANTIALIAS            9
+    
+    { FC_RASTERIZER,   FcCompareString,        11, 11 },
+#define MATCH_RASTERIZER    10
+    
+    { FC_OUTLINE,      FcCompareBool,          12, 12 },
+#define MATCH_OUTLINE      11
 };
 
-#define NUM_MATCHER (sizeof _FcMatchers / sizeof _FcMatchers[0])
+#define NUM_MATCH_VALUES    13
 
 static FcBool
 FcCompareValueList (const char  *object,
@@ -128,13 +184,62 @@ FcCompareValueList (const char  *object,
                    FcResult    *result)
 {
     FcValueList    *v1, *v2;
-    double         v, best;
-    int                    j;
+    double         v, best, bestStrong, bestWeak;
     int                    i;
+    int                    j;
     
+    /*
+     * Locate the possible matching entry by examining the
+     * first few characters in object
+     */
+    i = -1;
+    switch (FcToLower (object[0])) {
+    case 'f':
+       switch (FcToLower (object[1])) {
+       case 'o':
+           i = MATCH_FOUNDRY; break;
+       case 'a':
+           i = MATCH_FAMILY; break;
+       }
+       break;
+    case 'c':
+       i = MATCH_CHARSET; break;
+    case 'a':
+       i = MATCH_ANTIALIAS; break;
+    case 'l':
+       i = MATCH_LANG; break;
+    case 's':
+       switch (FcToLower (object[1])) {
+       case 'p':
+           i = MATCH_SPACING; break;
+       case 't':
+           i = MATCH_STYLE; break;
+       case 'l':
+           i = MATCH_SLANT; break;
+       }
+       break;
+    case 'p':
+       i = MATCH_PIXEL_SIZE; break;
+    case 'w':
+       i = MATCH_WEIGHT; break;
+    case 'r':
+       i = MATCH_RASTERIZER; break;
+    case 'o':
+       i = MATCH_OUTLINE; break;
+    }
+    if (i == -1 || 
+       FcStrCmpIgnoreCase ((FcChar8 *) _FcMatchers[i].object,
+                           (FcChar8 *) object) != 0)
+    {
+       if (bestValue)
+           *bestValue = v2orig->value;
+       return FcTrue;
+    }
+#if 0
     for (i = 0; i < NUM_MATCHER; i++)
     {
-       if (!FcStrCmpIgnoreCase (_FcMatchers[i].object, object))
+       if (!FcStrCmpIgnoreCase ((FcChar8 *) _FcMatchers[i].object,
+                                (FcChar8 *) object))
            break;
     }
     if (i == NUM_MATCHER)
@@ -143,8 +248,10 @@ FcCompareValueList (const char  *object,
            *bestValue = v2orig->value;
        return FcTrue;
     }
-    
+#endif
     best = 1e99;
+    bestStrong = 1e99;
+    bestWeak = 1e99;
     j = 0;
     for (v1 = v1orig; v1; v1 = v1->next)
     {
@@ -167,6 +274,16 @@ FcCompareValueList (const char  *object,
                    *bestValue = v2->value;
                best = v;
            }
+           if (v1->binding == FcValueBindingStrong)
+           {
+               if (v < bestStrong)
+                   bestStrong = v;
+           }
+           else
+           {
+               if (v < bestWeak)
+                   bestWeak = v;
+           }
        }
        j++;
     }
@@ -178,7 +295,18 @@ FcCompareValueList (const char  *object,
        FcValueListPrint (v2orig);
        printf ("\n");
     }
-    value[i] += best;
+    if (value)
+    {
+       int weak    = _FcMatchers[i].weak;
+       int strong  = _FcMatchers[i].strong;
+       if (weak == strong)
+           value[strong] += best;
+       else
+       {
+           value[weak] += bestWeak;
+           value[strong] += bestStrong;
+       }
+    }
     return FcTrue;
 }
 
@@ -195,64 +323,105 @@ FcCompare (FcPattern     *pat,
 {
     int                    i, i1, i2;
     
-    for (i = 0; i < NUM_MATCHER; i++)
+    for (i = 0; i < NUM_MATCH_VALUES; i++)
        value[i] = 0.0;
     
+    i1 = 0;
+    i2 = 0;
+    while (i1 < pat->num && i2 < fnt->num)
+    {
+       i = strcmp (pat->elts[i1].object, fnt->elts[i2].object);
+       if (i > 0)
+           i2++;
+       else if (i < 0)
+           i1++;
+       else
+       {
+           if (!FcCompareValueList (pat->elts[i1].object,
+                                    pat->elts[i1].values,
+                                    fnt->elts[i2].values,
+                                    0,
+                                    value,
+                                    result))
+               return FcFalse;
+           i1++;
+           i2++;
+       }
+    }
+    return FcTrue;
+#if 0
     for (i1 = 0; i1 < pat->num; i1++)
     {
        for (i2 = 0; i2 < fnt->num; i2++)
        {
-           if (!FcStrCmpIgnoreCase (pat->elts[i1].object,
-                                    fnt->elts[i2].object))
+           if (!strcmp (pat->elts[i1].object, fnt->elts[i2].object))
            {
-               if (!FcCompareValueList (pat->elts[i1].object,
-                                        pat->elts[i1].values,
-                                        fnt->elts[i2].values,
-                                        0,
-                                        value,
-                                        result))
-                   return FcFalse;
                break;
            }
        }
-#if 0
-       /*
-        * Overspecified patterns are slightly penalized in
-        * case some other font includes the requested field
-        */
-       if (i2 == fnt->num)
+    }
+    return FcTrue;
+#endif
+}
+
+FcPattern *
+FcFontRenderPrepare (FcConfig      *config,
+                    FcPattern      *pat,
+                    FcPattern      *font)
+{
+    FcPattern      *new;
+    int                    i;
+    FcPatternElt    *fe, *pe;
+    FcValue        v;
+    FcResult       result;
+    
+    new = FcPatternCreate ();
+    if (!new)
+       return 0;
+    for (i = 0; i < font->num; i++)
+    {
+       fe = &font->elts[i];
+       pe = FcPatternFindElt (pat, fe->object);
+       if (pe)
        {
-           for (i2 = 0; i2 < NUM_MATCHER; i2++)
+           if (!FcCompareValueList (pe->object, pe->values, 
+                                    fe->values, &v, 0, &result))
            {
-               if (!FcStrCmpIgnoreCase (_FcMatchers[i2].object,
-                                        pat->elts[i1].object))
-               {
-                   value[i2] = 1.0;
-                   break;
-               }
+               FcPatternDestroy (new);
+               return 0;
            }
        }
-#endif
+       else
+           v = fe->values->value;
+       FcPatternAdd (new, fe->object, v, FcFalse);
     }
-    return FcTrue;
+    for (i = 0; i < pat->num; i++)
+    {
+       pe = &pat->elts[i];
+       fe = FcPatternFindElt (font, pe->object);
+       if (!fe)
+           FcPatternAdd (new, pe->object, pe->values->value, FcTrue);
+    }
+    FcPatternAddPattern (new, FC_PATTERN, pat);
+    FcConfigSubstitute (config, new, FcMatchFont);
+    return new;
 }
 
 FcPattern *
-FcFontMatch (FcConfig  *config,
-            FcPattern  *p, 
-            FcResult   *result)
+FcFontSetMatch (FcConfig    *config,
+               FcFontSet   **sets,
+               int         nsets,
+               FcPattern   *p,
+               FcResult    *result)
 {
-    double         score[NUM_MATCHER], bestscore[NUM_MATCHER];
+    double         score[NUM_MATCH_VALUES], bestscore[NUM_MATCH_VALUES];
     int                    f;
     FcFontSet      *s;
     FcPattern      *best;
-    FcPattern      *new;
-    FcPatternElt   *fe, *pe;
-    FcValue        v;
     int                    i;
-    FcSetName      set;
+    int                    set;
 
-    for (i = 0; i < NUM_MATCHER; i++)
+    for (i = 0; i < NUM_MATCH_VALUES; i++)
        bestscore[i] = 0;
     best = 0;
     if (FcDebug () & FC_DBG_MATCH)
@@ -266,9 +435,9 @@ FcFontMatch (FcConfig       *config,
        if (!config)
            return 0;
     }
-    for (set = FcSetSystem; set <= FcSetApplication; set++)
+    for (set = 0; set < nsets; set++)
     {
-       s = config->fonts[set];
+       s = sets[set];
        if (!s)
            continue;
        for (f = 0; f < s->nfont; f++)
@@ -283,19 +452,19 @@ FcFontMatch (FcConfig     *config,
            if (FcDebug () & FC_DBG_MATCHV)
            {
                printf ("Score");
-               for (i = 0; i < NUM_MATCHER; i++)
+               for (i = 0; i < NUM_MATCH_VALUES; i++)
                {
                    printf (" %g", score[i]);
                }
                printf ("\n");
            }
-           for (i = 0; i < NUM_MATCHER; i++)
+           for (i = 0; i < NUM_MATCH_VALUES; i++)
            {
                if (best && bestscore[i] < score[i])
                    break;
                if (!best || score[i] < bestscore[i])
                {
-                   for (i = 0; i < NUM_MATCHER; i++)
+                   for (i = 0; i < NUM_MATCH_VALUES; i++)
                        bestscore[i] = score[i];
                    best = s->fonts[f];
                    break;
@@ -306,7 +475,7 @@ FcFontMatch (FcConfig       *config,
     if (FcDebug () & FC_DBG_MATCH)
     {
        printf ("Best score");
-       for (i = 0; i < NUM_MATCHER; i++)
+       for (i = 0; i < NUM_MATCH_VALUES; i++)
            printf (" %g", bestscore[i]);
        FcPatternPrint (best);
     }
@@ -315,33 +484,228 @@ FcFontMatch (FcConfig    *config,
        *result = FcResultNoMatch;
        return 0;
     }
-    new = FcPatternCreate ();
-    if (!new)
-       return 0;
-    for (i = 0; i < best->num; i++)
+    return FcFontRenderPrepare (config, p, best);
+}
+
+FcPattern *
+FcFontMatch (FcConfig  *config,
+            FcPattern  *p, 
+            FcResult   *result)
+{
+    FcFontSet  *sets[2];
+    int                nsets;
+
+    if (!config)
     {
-       fe = &best->elts[i];
-       pe = FcPatternFind (p, fe->object, FcFalse);
-       if (pe)
+       config = FcConfigGetCurrent ();
+       if (!config)
+           return 0;
+    }
+    nsets = 0;
+    if (config->fonts[FcSetSystem])
+       sets[nsets++] = config->fonts[FcSetSystem];
+    if (config->fonts[FcSetApplication])
+       sets[nsets++] = config->fonts[FcSetApplication];
+    return FcFontSetMatch (config, sets, nsets, p, result);
+}
+
+typedef struct _FcSortNode {
+    FcPattern  *pattern;
+    double     score[NUM_MATCH_VALUES];
+} FcSortNode;
+
+static int
+FcSortCompare (const void *aa, const void *ab)
+{
+    FcSortNode  *a = *(FcSortNode **) aa;
+    FcSortNode  *b = *(FcSortNode **) ab;
+    double     *as = &a->score[0];
+    double     *bs = &b->score[0];
+    double     ad = 0, bd = 0;
+    int         i;
+
+    i = NUM_MATCH_VALUES;
+    while (i-- && (ad = *as++) == (bd = *bs++))
+       ;
+    return ad < bd ? -1 : ad > bd ? 1 : 0;
+}
+
+static FcBool
+FcSortWalk (FcSortNode **n, int nnode, FcFontSet *fs, FcCharSet **cs, FcBool trim)
+{
+    FcCharSet  *ncs;
+    FcSortNode *node;
+
+    while (nnode--)
+    {
+       node = *n++;
+       if (FcPatternGetCharSet (node->pattern, FC_CHARSET, 0, &ncs) == 
+           FcResultMatch)
        {
-           if (!FcCompareValueList (pe->object, pe->values, 
-                                    fe->values, &v, score, result))
+           /*
+            * If this font isn't a subset of the previous fonts,
+            * add it to the list
+            */
+           if (!trim || !*cs || !FcCharSetIsSubset (ncs, *cs))
            {
-               FcPatternDestroy (new);
-               return 0;
+               if (*cs)
+               {
+                   ncs = FcCharSetUnion (ncs, *cs);
+                   if (!ncs)
+                       return FcFalse;
+                   FcCharSetDestroy (*cs);
+               }
+               else
+                   ncs = FcCharSetCopy (ncs);
+               *cs = ncs;
+               FcPatternReference (node->pattern);
+               if (FcDebug () & FC_DBG_MATCH)
+               {
+                   printf ("Add ");
+                   FcPatternPrint (node->pattern);
+               }
+               if (!FcFontSetAdd (fs, node->pattern))
+               {
+                   FcPatternDestroy (node->pattern);
+                   return FcFalse;
+               }
            }
        }
-       else
-           v = fe->values->value;
-       FcPatternAdd (new, fe->object, v, FcTrue);
     }
-    for (i = 0; i < p->num; i++)
+    return FcTrue;
+}
+
+void
+FcFontSetSortDestroy (FcFontSet *fs)
+{
+    FcFontSetDestroy (fs);
+}
+
+FcFontSet *
+FcFontSetSort (FcConfig            *config,
+              FcFontSet    **sets,
+              int          nsets,
+              FcPattern    *p,
+              FcBool       trim,
+              FcCharSet    **csp,
+              FcResult     *result)
+{
+    FcFontSet      *ret;
+    FcFontSet      *s;
+    FcSortNode     *nodes;
+    FcSortNode     **nodeps, **nodep;
+    int                    nnodes;
+    FcSortNode     *new;
+    FcCharSet      *cs;
+    int                    set;
+    int                    f;
+    int                    i;
+
+    if (FcDebug () & FC_DBG_MATCH)
+    {
+       printf ("Sort ");
+       FcPatternPrint (p);
+    }
+    nnodes = 0;
+    for (set = 0; set < nsets; set++)
     {
-       pe = &p->elts[i];
-       fe = FcPatternFind (best, pe->object, FcFalse);
-       if (!fe)
-           FcPatternAdd (new, pe->object, pe->values->value, FcTrue);
+       s = sets[set];
+       if (!s)
+           continue;
+       nnodes += s->nfont;
     }
-    FcConfigSubstitute (config, new, FcMatchFont);
-    return new;
+    if (!nnodes)
+       goto bail0;
+    nodes = malloc (nnodes * sizeof (FcSortNode) + nnodes * sizeof (FcSortNode *));
+    if (!nodes)
+       goto bail0;
+    nodeps = (FcSortNode **) (nodes + nnodes);
+    
+    new = nodes;
+    nodep = nodeps;
+    for (set = 0; set < nsets; set++)
+    {
+       s = sets[set];
+       if (!s)
+           continue;
+       for (f = 0; f < s->nfont; f++)
+       {
+           if (FcDebug () & FC_DBG_MATCHV)
+           {
+               printf ("Font %d ", f);
+               FcPatternPrint (s->fonts[f]);
+           }
+           new->pattern = s->fonts[f];
+           if (!FcCompare (p, new->pattern, new->score, result))
+               goto bail1;
+           if (FcDebug () & FC_DBG_MATCHV)
+           {
+               printf ("Score");
+               for (i = 0; i < NUM_MATCH_VALUES; i++)
+               {
+                   printf (" %g", new->score[i]);
+               }
+               printf ("\n");
+           }
+           *nodep = new;
+           new++;
+           nodep++;
+       }
+    }
+
+    nnodes = new - nodes;
+    
+    qsort (nodeps, nnodes, sizeof (FcSortNode *),
+          FcSortCompare);
+
+    ret = FcFontSetCreate ();
+    if (!ret)
+       goto bail1;
+
+    cs = 0;
+
+    if (!FcSortWalk (nodeps, nnodes, ret, &cs, trim))
+       goto bail2;
+
+    if (csp)
+       *csp = cs;
+    else
+       FcCharSetDestroy (cs);
+
+    free (nodes);
+
+    return ret;
+
+bail2:
+    if (cs)
+       FcCharSetDestroy (cs);
+    FcFontSetDestroy (ret);
+bail1:
+    free (nodes);
+bail0:
+    return 0;
+}
+
+FcFontSet *
+FcFontSort (FcConfig   *config,
+           FcPattern   *p, 
+           FcBool      trim,
+           FcCharSet   **csp,
+           FcResult    *result)
+{
+    FcFontSet  *sets[2];
+    int                nsets;
+
+    if (!config)
+    {
+       config = FcConfigGetCurrent ();
+       if (!config)
+           return 0;
+    }
+    nsets = 0;
+    if (config->fonts[FcSetSystem])
+       sets[nsets++] = config->fonts[FcSetSystem];
+    if (config->fonts[FcSetApplication])
+       sets[nsets++] = config->fonts[FcSetApplication];
+    return FcFontSetSort (config, sets, nsets, p, trim, csp, result);
 }