Imported Upstream version 2.14.2
[platform/upstream/fontconfig.git] / src / fcmatch.c
index 62f8e58..cf0876c 100644 (file)
@@ -86,7 +86,7 @@ FcComparePostScript (const FcValue *v1, const FcValue *v2, FcValue *bestValue)
     const FcChar8 *v1_string = FcValueString (v1);
     const FcChar8 *v2_string = FcValueString (v2);
     int n;
-    size_t len;
+    size_t len1, len2, mlen;
 
     *bestValue = FcValueCanonicalize (v2);
 
@@ -95,39 +95,38 @@ FcComparePostScript (const FcValue *v1, const FcValue *v2, FcValue *bestValue)
        return 1.0;
 
     n = FcStrMatchIgnoreCaseAndDelims (v1_string, v2_string, (const FcChar8 *)" -");
-    len = strlen ((const char *)v1_string);
+    len1 = strlen ((const char *)v1_string);
+    len2 = strlen ((const char *)v2_string);
+    mlen = FC_MAX (len1, len2);
 
-    return (double)(len - n) / (double)len;
+    return (double)(mlen - n) / (double)mlen;
 }
 
 static double
 FcCompareLang (const FcValue *v1, const FcValue *v2, FcValue *bestValue)
 {
     FcLangResult    result;
-    FcValue value1 = FcValueCanonicalize(v1), value2 = FcValueCanonicalize(v2);
 
-    switch ((int) value1.type) {
+    switch ((int) v1->type) {
     case FcTypeLangSet:
-       switch ((int) value2.type) {
+       switch ((int) v2->type) {
        case FcTypeLangSet:
-           result = FcLangSetCompare (value1.u.l, value2.u.l);
+           result = FcLangSetCompare (FcValueLangSet (v1), FcValueLangSet (v2));
            break;
        case FcTypeString:
-           result = FcLangSetHasLang (value1.u.l,
-                                      value2.u.s);
+           result = FcLangSetHasLang (FcValueLangSet (v1), FcValueString (v2));
            break;
        default:
            return -1.0;
        }
        break;
     case FcTypeString:
-       switch ((int) value2.type) {
+       switch ((int) v2->type) {
        case FcTypeLangSet:
-           result = FcLangSetHasLang (value2.u.l, value1.u.s);
+           result = FcLangSetHasLang (FcValueLangSet (v2), FcValueString (v1));
            break;
        case FcTypeString:
-           result = FcLangCompare (value1.u.s,
-                                   value2.u.s);
+           result = FcLangCompare (FcValueString (v1), FcValueString (v2));
            break;
        default:
            return -1.0;
@@ -154,10 +153,11 @@ FcCompareBool (const FcValue *v1, const FcValue *v2, FcValue *bestValue)
     if (v2->type != FcTypeBool || v1->type != FcTypeBool)
        return -1.0;
 
+    bestValue->type = FcTypeBool;
     if (v2->u.b != FcDontCare)
-       *bestValue = FcValueCanonicalize (v2);
+       bestValue->u.b = v2->u.b;
     else
-       *bestValue = FcValueCanonicalize (v1);
+       bestValue->u.b = v1->u.b;
 
     return (double) ((v2->u.b ^ v1->u.b) == 1);
 }
@@ -237,7 +237,6 @@ FcCompareSize (const FcValue *v1, const FcValue *v2, FcValue *bestValue)
         b1 = e1 = value1.u.d;
        break;
     case FcTypeRange:
-       abort();
        b1 = value1.u.r->begin;
        e1 = value1.u.r->end;
        break;
@@ -342,10 +341,12 @@ typedef enum _FcMatcherPriority {
     PRI1(SLANT),
     PRI1(WEIGHT),
     PRI1(WIDTH),
+    PRI1(FONT_HAS_HINT),
     PRI1(DECORATIVE),
     PRI1(ANTIALIAS),
     PRI1(RASTERIZER),
     PRI1(OUTLINE),
+    PRI1(ORDER),
     PRI1(FONTVERSION),
     PRI_END
 } FcMatcherPriority;
@@ -406,6 +407,7 @@ FcCompareValueList (FcObject             object,
     FcValueListPtr  v1, v2;
     double         v, best, bestStrong, bestWeak;
     int                    j, k, pos = 0;
+    int weak, strong;
 
     if (!match)
     {
@@ -416,11 +418,13 @@ FcCompareValueList (FcObject           object,
        return FcTrue;
     }
 
+    weak    = match->weak;
+    strong  = match->strong;
+
     best = 1e99;
     bestStrong = 1e99;
     bestWeak = 1e99;
-    j = 0;
-    for (v1 = v1orig; v1; v1 = FcValueListNext(v1))
+    for (v1 = v1orig, j = 0; v1; v1 = FcValueListNext(v1), j++)
     {
        for (v2 = v2orig, k = 0; v2; v2 = FcValueListNext(v2), k++)
        {
@@ -431,7 +435,7 @@ FcCompareValueList (FcObject             object,
                *result = FcResultTypeMismatch;
                return FcFalse;
            }
-           v = v * 1000 + j;
+           v = v * 1000 + j * 100 + k * (v2->value.type == FcTypeString ? 1 : 0);
            if (v < best)
            {
                if (bestValue)
@@ -439,7 +443,13 @@ FcCompareValueList (FcObject            object,
                best = v;
                pos = k;
            }
-           if (v1->binding == FcValueBindingStrong)
+            if (weak == strong)
+            {
+                /* found the best possible match */
+                if (best < 1000)
+                    goto done;
+            }
+            else if (v1->binding == FcValueBindingStrong)
            {
                if (v < bestStrong)
                    bestStrong = v;
@@ -450,8 +460,8 @@ FcCompareValueList (FcObject             object,
                    bestWeak = v;
            }
        }
-       j++;
     }
+done:
     if (FcDebug () & FC_DBG_MATCHV)
     {
        printf (" %s: %g ", FcObjectName (object), best);
@@ -462,8 +472,6 @@ FcCompareValueList (FcObject             object,
     }
     if (value)
     {
-       int weak    = match->weak;
-       int strong  = match->strong;
        if (weak == strong)
            value[strong] += best;
        else
@@ -478,6 +486,120 @@ FcCompareValueList (FcObject           object,
     return FcTrue;
 }
 
+/* The bulk of the time in FcFontMatch and FcFontSort goes to
+ * walking long lists of family names. We speed this up with a
+ * hash table.
+ */
+typedef struct
+{
+    double strong_value;
+    double weak_value;
+} FamilyEntry;
+
+typedef struct
+{
+    FcHashTable *family_hash;
+} FcCompareData;
+
+static void
+FcCompareDataClear (FcCompareData *data)
+{
+    FcHashTableDestroy (data->family_hash);
+}
+
+static void
+FcCompareDataInit (FcPattern     *pat,
+                   FcCompareData *data)
+{
+    FcHashTable *table;
+    FcPatternElt *elt;
+    FcValueListPtr l;
+    int i;
+    const void *key;
+    FamilyEntry *e;
+
+    table = FcHashTableCreate ((FcHashFunc)FcStrHashIgnoreBlanksAndCase,
+                               (FcCompareFunc)FcStrCmpIgnoreBlanksAndCase,
+                               NULL,
+                               NULL,
+                               NULL,
+                               free);
+
+    elt = FcPatternObjectFindElt (pat, FC_FAMILY_OBJECT);
+    if (elt)
+    {
+        for (l = FcPatternEltValues(elt), i = 0; l; l = FcValueListNext(l), i++)
+        {
+            key = FcValueString (&l->value);
+            if (!FcHashTableFind (table, key, (void **)&e))
+            {
+                e = malloc (sizeof (FamilyEntry));
+                e->strong_value = 1e99;
+                e->weak_value = 1e99;
+                FcHashTableAdd (table, (void *)key, e);
+            }
+            if (l->binding == FcValueBindingWeak)
+            {
+                if (i < e->weak_value)
+                    e->weak_value = i;
+            }
+            else
+            {
+                if (i < e->strong_value)
+                    e->strong_value = i;
+            }
+        }
+    }
+
+    data->family_hash = table;
+}
+
+static FcBool
+FcCompareFamilies (FcPattern       *pat,
+                   FcValueListPtr   v1orig,
+                   FcPattern       *fnt,
+                   FcValueListPtr   v2orig,
+                   double          *value,
+                   FcResult        *result,
+                   FcHashTable     *table)
+{
+    FcValueListPtr v2;
+    double strong_value;
+    double weak_value;
+    const void *key;
+    FamilyEntry *e;
+
+    assert (table != NULL);
+
+    strong_value = 1e99;
+    weak_value = 1e99;
+
+    for (v2 = v2orig; v2; v2 = FcValueListNext(v2))
+    {
+        key = FcValueString (&v2->value);
+        if (FcHashTableFind (table, key, (void **)&e))
+        {
+            if (e->strong_value < strong_value)
+                strong_value = e->strong_value;
+            if (e->weak_value < weak_value)
+                weak_value = e->weak_value;
+        }
+    }
+    if (FcDebug () & FC_DBG_MATCHV)
+    {
+       printf ("%s: %g ", FcObjectName (FC_FAMILY_OBJECT), strong_value);
+       FcValueListPrint (v1orig);
+       printf (", ");
+       FcValueListPrint (v2orig);
+       printf ("\n");
+    }
+
+    value[PRI_FAMILY_STRONG] = strong_value;
+    value[PRI_FAMILY_WEAK] = weak_value;
+
+    return FcTrue;
+}
+
 /*
  * Return a value indicating the distance between the two lists of
  * values
@@ -487,7 +609,8 @@ static FcBool
 FcCompare (FcPattern   *pat,
           FcPattern    *fnt,
           double       *value,
-          FcResult     *result)
+          FcResult     *result,
+           FcCompareData *data)
 {
     int                    i, i1, i2;
 
@@ -506,8 +629,18 @@ FcCompare (FcPattern       *pat,
            i2++;
        else if (i < 0)
            i1++;
-       else
-       {
+       else if (elt_i1->object == FC_FAMILY_OBJECT && data->family_hash)
+        {
+            if (!FcCompareFamilies (pat, FcPatternEltValues(elt_i1),
+                                    fnt, FcPatternEltValues(elt_i2),
+                                    value, result,
+                                    data->family_hash))
+                return FcFalse;
+           i1++;
+           i2++;
+        }
+        else
+        {
            const FcMatcher *match = FcObjectToMatcher (elt_i1->object, FcFalse);
            if (!FcCompareValueList (elt_i1->object, match,
                                     FcPatternEltValues(elt_i1),
@@ -591,27 +724,29 @@ FcFontRenderPrepare (FcConfig         *config,
                     l1 != NULL || l2 != NULL;
                     j++, l1 = l1 ? FcValueListNext (l1) : NULL, l2 = l2 ? FcValueListNext (l2) : NULL)
                {
+                   FcValueListPtr (* func) (FcValueListPtr, FcValue, FcValueBinding);
+                   FcValueBinding binding = FcValueBindingEnd;
+
                    if (j == n)
                    {
-                       if (l1)
-                           ln = FcValueListPrepend (ln,
-                                                    FcValueCanonicalize (&l1->value),
-                                                    FcValueBindingStrong);
-                       if (l2)
-                           ll = FcValueListPrepend (ll,
-                                                    FcValueCanonicalize (&l2->value),
-                                                    FcValueBindingStrong);
+                       binding = FcValueBindingStrong;
+                       func = FcValueListPrepend;
                    }
                    else
+                       func = FcValueListAppend;
+                   if (l1)
                    {
-                       if (l1)
-                           ln = FcValueListAppend (ln,
-                                                   FcValueCanonicalize (&l1->value),
-                                                   FcValueBindingStrong);
-                       if (l2)
-                           ll = FcValueListAppend (ll,
-                                                   FcValueCanonicalize (&l2->value),
-                                                   FcValueBindingStrong);
+                       ln = func (ln,
+                                  FcValueCanonicalize (&l1->value),
+                                  l1->binding);
+                   }
+                   if (l2)
+                   {
+                       if (binding == FcValueBindingEnd)
+                           binding = l2->binding;
+                       ll = func (ll,
+                                  FcValueCanonicalize (&l2->value),
+                                  binding);
                    }
                }
                FcPatternObjectListAdd (new, fe->object, ln, FcFalse);
@@ -682,43 +817,9 @@ FcFontRenderPrepare (FcConfig          *config,
        }
        else
        {
-           if (FcRefIsConst (&font->ref) && fe->object == FC_FILE_OBJECT)
-           {
-               FcValueListPtr l = FcPatternEltValues (fe);
-               FcChar8 *dir, *alias;
-
-               while (l->value.type != FcTypeString)
-                   l = FcValueListNext (l);
-               if (!l)
-                   goto bail0;
-               dir = FcStrDirname (FcValueString (&l->value));
-               if (!config)
-                   config = FcConfigGetCurrent ();
-               if (config && FcHashTableFind (config->alias_table, dir, (void **) &alias))
-               {
-                   FcChar8 *base = FcStrBasename (FcValueString (&l->value));
-                   FcChar8 *s = FcStrBuildFilename (alias, base, NULL);
-                   FcValue v;
-
-                   FcStrFree (alias);
-                   FcStrFree (base);
-                   v.type = FcTypeString;
-                   v.u.s = s;
-                   FcPatternObjectAddWithBinding (new, fe->object,
-                                                  FcValueCanonicalize (&v),
-                                                  l->binding,
-                                                  FcTrue);
-                   FcStrFree (s);
-                   FcStrFree (dir);
-                   goto bail0;
-               }
-               else
-                   FcStrFree (dir);
-           }
            FcPatternObjectListAdd (new, fe->object,
                                    FcValueListDuplicate (FcPatternEltValues (fe)),
                                    FcTrue);
-         bail0:;
        }
     }
     for (i = 0; i < pat->num; i++)
@@ -763,9 +864,11 @@ FcFontSetMatchInternal (FcFontSet   **sets,
     double         score[PRI_END], bestscore[PRI_END];
     int                    f;
     FcFontSet      *s;
-    FcPattern      *best;
+    FcPattern      *best, *pat = NULL;
     int                    i;
     int                    set;
+    FcCompareData   data;
+    const FcPatternElt *elt;
 
     for (i = 0; i < PRI_END; i++)
        bestscore[i] = 0;
@@ -775,6 +878,9 @@ FcFontSetMatchInternal (FcFontSet   **sets,
        printf ("Match ");
        FcPatternPrint (p);
     }
+
+    FcCompareDataInit (p, &data);
+
     for (set = 0; set < nsets; set++)
     {
        s = sets[set];
@@ -787,8 +893,11 @@ FcFontSetMatchInternal (FcFontSet   **sets,
                printf ("Font %d ", f);
                FcPatternPrint (s->fonts[f]);
            }
-           if (!FcCompare (p, s->fonts[f], score, result))
+           if (!FcCompare (p, s->fonts[f], score, result, &data))
+            {
+                FcCompareDataClear (&data);
                return 0;
+            }
            if (FcDebug () & FC_DBG_MATCHV)
            {
                printf ("Score");
@@ -812,13 +921,59 @@ FcFontSetMatchInternal (FcFontSet   **sets,
            }
        }
     }
+
+    FcCompareDataClear (&data);
+
+    /* Update the binding according to the score to indicate how exactly values matches on. */
+    if (best)
+    {
+       pat = FcPatternCreate ();
+       elt = FcPatternElts (best);
+       for (i = 0; i < FcPatternObjectCount (best); i++)
+       {
+           const FcMatcher *match = FcObjectToMatcher (elt[i].object, FcFalse);
+           FcValueListPtr l = FcPatternEltValues (&elt[i]);
+
+           if (!match)
+               FcPatternObjectListAdd(pat, elt[i].object,
+                                      FcValueListDuplicate(l), FcTrue);
+           else
+           {
+               FcValueBinding binding = FcValueBindingWeak;
+               FcValueListPtr new = NULL, ll, t = NULL;
+               FcValue v;
+
+               /* If the value was matched exactly, update the binding to Strong. */
+               if (bestscore[match->strong] < 1000)
+                   binding = FcValueBindingStrong;
+
+               for (ll = l; ll != NULL; ll = FcValueListNext (ll))
+               {
+                   if (!new)
+                   {
+                       t = new = FcValueListCreate ();
+                   }
+                   else
+                   {
+                       t->next = FcValueListCreate ();
+                       t = FcValueListNext (t);
+                   }
+                   v = FcValueCanonicalize (&ll->value);
+                   t->value = FcValueSave (v);
+                   t->binding = binding;
+                   t->next = NULL;
+               }
+               FcPatternObjectListAdd (pat, elt[i].object, new, FcTrue);
+           }
+       }
+    }
     if (FcDebug () & FC_DBG_MATCH)
     {
        printf ("Best score");
        for (i = 0; i < PRI_END; i++)
            printf (" %g", bestscore[i]);
        printf ("\n");
-       FcPatternPrint (best);
+       FcPatternPrint (pat);
     }
     if (FcDebug () & FC_DBG_MATCH2)
     {
@@ -841,32 +996,34 @@ FcFontSetMatchInternal (FcFontSet   **sets,
                if (!(p = strchr (s, ',')))
                {
                    f = FcFalse;
-                   len = strlen (s) + 1;
+                   len = strlen (s);
                }
                else
                {
-                   len = (p - s) + 1;
+                   len = (p - s);
+               }
+               x = malloc (sizeof (char) * (len + 1));
+               if (x)
+               {
+                   strcpy (x, s);
+                   if (FcObjectFromName (x) > 0)
+                       FcObjectSetAdd (os, x);
+                   s = p + 1;
+                   free (x);
                }
-               x = malloc (sizeof (char) * len);
-               strncpy (x, s, len - 1);
-               x[len - 1] = 0;
-               if (FcObjectFromName (x) > 0)
-                   FcObjectSetAdd (os, x);
-               s = p + 1;
-               free (x);
            }
            free (ss);
        }
-       FcPatternPrint2 (p, best, os);
+       FcPatternPrint2 (p, pat, os);
        if (os)
            FcObjectSetDestroy (os);
     }
     /* assuming that 'result' is initialized with FcResultNoMatch
      * outside this function */
-    if (best)
+    if (pat)
        *result = FcResultMatch;
 
-    return best;
+    return pat;
 }
 
 FcPattern *
@@ -876,7 +1033,7 @@ FcFontSetMatch (FcConfig    *config,
                FcPattern   *p,
                FcResult    *result)
 {
-    FcPattern      *best;
+    FcPattern      *best, *ret = NULL;
 
     assert (sets != NULL);
     assert (p != NULL);
@@ -884,17 +1041,19 @@ FcFontSetMatch (FcConfig    *config,
 
     *result = FcResultNoMatch;
 
+    config = FcConfigReference (config);
     if (!config)
-    {
-       config = FcConfigGetCurrent ();
-       if (!config)
-           return 0;
-    }
+           return NULL;
     best = FcFontSetMatchInternal (sets, nsets, p, result);
     if (best)
-       return FcFontRenderPrepare (config, p, best);
-    else
-       return NULL;
+    {
+       ret = FcFontRenderPrepare (config, p, best);
+       FcPatternDestroy (best);
+    }
+
+    FcConfigDestroy (config);
+
+    return ret;
 }
 
 FcPattern *
@@ -904,19 +1063,16 @@ FcFontMatch (FcConfig    *config,
 {
     FcFontSet  *sets[2];
     int                nsets;
-    FcPattern   *best;
+    FcPattern   *best, *ret = NULL;
 
     assert (p != NULL);
     assert (result != NULL);
 
     *result = FcResultNoMatch;
 
+    config = FcConfigReference (config);
     if (!config)
-    {
-       config = FcConfigGetCurrent ();
-       if (!config)
-           return 0;
-    }
+       return NULL;
     nsets = 0;
     if (config->fonts[FcSetSystem])
        sets[nsets++] = config->fonts[FcSetSystem];
@@ -925,9 +1081,14 @@ FcFontMatch (FcConfig     *config,
 
     best = FcFontSetMatchInternal (sets, nsets, p, result);
     if (best)
-       return FcFontRenderPrepare (config, p, best);
-    else
-       return NULL;
+    {
+       ret = FcFontRenderPrepare (config, p, best);
+       FcPatternDestroy (best);
+    }
+
+    FcConfigDestroy (config);
+
+    return ret;
 }
 
 typedef struct _FcSortNode {
@@ -1047,6 +1208,7 @@ FcFontSetSort (FcConfig       *config FC_UNUSED,
     int                    nPatternLang;
     FcBool         *patternLangSat;
     FcValue        patternLang;
+    FcCompareData   data;
 
     assert (sets != NULL);
     assert (p != NULL);
@@ -1091,6 +1253,8 @@ FcFontSetSort (FcConfig       *config FC_UNUSED,
     nodeps = (FcSortNode **) (nodes + nnodes);
     patternLangSat = (FcBool *) (nodeps + nnodes);
 
+    FcCompareDataInit (p, &data);
+
     new = nodes;
     nodep = nodeps;
     for (set = 0; set < nsets; set++)
@@ -1106,7 +1270,7 @@ FcFontSetSort (FcConfig       *config FC_UNUSED,
                FcPatternPrint (s->fonts[f]);
            }
            new->pattern = s->fonts[f];
-           if (!FcCompare (p, new->pattern, new->score, result))
+           if (!FcCompare (p, new->pattern, new->score, result, &data))
                goto bail1;
            if (FcDebug () & FC_DBG_MATCHV)
            {
@@ -1123,6 +1287,8 @@ FcFontSetSort (FcConfig       *config FC_UNUSED,
        }
     }
 
+    FcCompareDataClear (&data);
+
     nnodes = new - nodes;
 
     qsort (nodeps, nnodes, sizeof (FcSortNode *),
@@ -1214,7 +1380,7 @@ FcFontSort (FcConfig      *config,
            FcCharSet   **csp,
            FcResult    *result)
 {
-    FcFontSet  *sets[2];
+    FcFontSet  *sets[2], *ret;
     int                nsets;
 
     assert (p != NULL);
@@ -1222,18 +1388,18 @@ FcFontSort (FcConfig    *config,
 
     *result = FcResultNoMatch;
 
+    config = FcConfigReference (config);
     if (!config)
-    {
-       config = FcConfigGetCurrent ();
-       if (!config)
-           return 0;
-    }
+       return NULL;
     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);
+    ret = FcFontSetSort (config, sets, nsets, p, trim, csp, result);
+    FcConfigDestroy (config);
+
+    return ret;
 }
 #define __fcmatch__
 #include "fcaliastail.h"