Bug 72380 - Never drop first font when trimming
[platform/upstream/fontconfig.git] / src / fcmatch.c
index e4e2c53..93e013f 100644 (file)
 /*
- * $XFree86: xc/lib/fontconfig/src/fcmatch.c,v 1.7 2002/05/29 22:07:33 keithp Exp $
+ * fontconfig/src/fcmatch.c
  *
- * Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc.
+ * Copyright © 2000 Keith Packard
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
  * documentation for any purpose is hereby granted without fee, provided that
  * the above copyright notice appear in all copies and that both that
  * copyright notice and this permission notice appear in supporting
- * documentation, and that the name of Keith Packard not be used in
+ * documentation, and that the name of the author(s) not be used in
  * advertising or publicity pertaining to distribution of the software without
- * specific, written prior permission.  Keith Packard makes no
+ * specific, written prior permission.  The authors make no
  * representations about the suitability of this software for any purpose.  It
  * is provided "as is" without express or implied warranty.
  *
- * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
- * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR
  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <string.h>
-#include <ctype.h>
 #include "fcint.h"
-#include <stdio.h>
 
 static double
-FcCompareInteger (char *object, FcValue value1, FcValue value2)
+FcCompareNumber (FcValue *value1, FcValue *value2)
 {
-    int        v;
-    
-    if (value2.type != FcTypeInteger || value1.type != FcTypeInteger)
+    double  v1, v2, v;
+
+    switch ((int) value1->type) {
+    case FcTypeInteger:
+       v1 = (double) value1->u.i;
+       break;
+    case FcTypeDouble:
+       v1 = value1->u.d;
+       break;
+    default:
+       return -1.0;
+    }
+    switch ((int) value2->type) {
+    case FcTypeInteger:
+       v2 = (double) value2->u.i;
+       break;
+    case FcTypeDouble:
+       v2 = value2->u.d;
+       break;
+    default:
        return -1.0;
-    v = value2.u.i - value1.u.i;
+    }
+    v = v2 - v1;
     if (v < 0)
        v = -v;
-    return (double) v;
+    return v;
 }
 
 static double
-FcCompareString (char *object, FcValue value1, FcValue value2)
+FcCompareString (FcValue *v1, FcValue *v2)
 {
-    if (value2.type != FcTypeString || value1.type != FcTypeString)
-       return -1.0;
-    return (double) FcStrCmpIgnoreCase (value1.u.s, value2.u.s) != 0;
+    return (double) FcStrCmpIgnoreCase (FcValueString(v1), FcValueString(v2)) != 0;
+}
+
+static double
+FcCompareFamily (FcValue *v1, FcValue *v2)
+{
+    /* rely on the guarantee in FcPatternObjectAddWithBinding that
+     * families are always FcTypeString. */
+    const FcChar8* v1_string = FcValueString(v1);
+    const FcChar8* v2_string = FcValueString(v2);
+
+    if (FcToLower(*v1_string) != FcToLower(*v2_string) &&
+       *v1_string != ' ' && *v2_string != ' ')
+       return 1.0;
+
+    return (double) FcStrCmpIgnoreBlanksAndCase (v1_string, v2_string) != 0;
+}
+
+static double
+FcComparePostScript (FcValue *v1, FcValue *v2)
+{
+    const FcChar8 *v1_string = FcValueString (v1);
+    const FcChar8 *v2_string = FcValueString (v2);
+    int n;
+    size_t len;
+
+    if (FcToLower (*v1_string) != FcToLower (*v2_string) &&
+       *v1_string != ' ' && *v2_string != ' ')
+       return 1.0;
+
+    n = FcStrMatchIgnoreCaseAndDelims (v1_string, v2_string, (const FcChar8 *)" -");
+    len = strlen ((const char *)v1_string);
+
+    return (double)(len - n) / (double)len;
 }
 
 static double
-FcCompareBool (char *object, FcValue value1, FcValue value2)
+FcCompareLang (FcValue *v1, FcValue *v2)
 {
-    if (value2.type != FcTypeBool || value1.type != FcTypeBool)
+    FcLangResult    result;
+    FcValue value1 = FcValueCanonicalize(v1), value2 = FcValueCanonicalize(v2);
+
+    switch ((int) value1.type) {
+    case FcTypeLangSet:
+       switch ((int) value2.type) {
+       case FcTypeLangSet:
+           result = FcLangSetCompare (value1.u.l, value2.u.l);
+           break;
+       case FcTypeString:
+           result = FcLangSetHasLang (value1.u.l,
+                                      value2.u.s);
+           break;
+       default:
+           return -1.0;
+       }
+       break;
+    case FcTypeString:
+       switch ((int) value2.type) {
+       case FcTypeLangSet:
+           result = FcLangSetHasLang (value2.u.l, value1.u.s);
+           break;
+       case FcTypeString:
+           result = FcLangCompare (value1.u.s,
+                                   value2.u.s);
+           break;
+       default:
+           return -1.0;
+       }
+       break;
+    default:
        return -1.0;
-    return (double) value2.u.b != value1.u.b;
+    }
+    switch (result) {
+    case FcLangEqual:
+       return 0;
+    case FcLangDifferentCountry:
+       return 1;
+    case FcLangDifferentLang:
+    default:
+       return 2;
+    }
 }
 
 static double
-FcCompareCharSet (char *object, FcValue value1, FcValue value2)
+FcCompareBool (FcValue *v1, FcValue *v2)
 {
-    if (value2.type != FcTypeCharSet || value1.type != FcTypeCharSet)
+    if (v2->type != FcTypeBool || v1->type != FcTypeBool)
        return -1.0;
-    return (double) FcCharSetSubtractCount (value1.u.c, value2.u.c);
+    return (double) v2->u.b != v1->u.b;
+}
+
+static double
+FcCompareCharSet (FcValue *v1, FcValue *v2)
+{
+    return (double) FcCharSetSubtractCount (FcValueCharSet(v1), FcValueCharSet(v2));
 }
 
 static double
-FcCompareSize (char *object, FcValue value1, FcValue value2)
+FcCompareSize (FcValue *value1, FcValue *value2)
 {
     double  v1, v2, v;
 
-    switch (value1.type) {
+    switch ((int) value1->type) {
     case FcTypeInteger:
-       v1 = value1.u.i;
+       v1 = value1->u.i;
        break;
     case FcTypeDouble:
-       v1 = value1.u.d;
+       v1 = value1->u.d;
        break;
     default:
        return -1;
     }
-    switch (value2.type) {
+    switch ((int) value2->type) {
     case FcTypeInteger:
-       v2 = value2.u.i;
+       v2 = value2->u.i;
        break;
     case FcTypeDouble:
-       v2 = value2.u.d;
+       v2 = value2->u.d;
        break;
     default:
        return -1;
@@ -97,160 +188,210 @@ FcCompareSize (char *object, FcValue value1, FcValue value2)
     return v;
 }
 
+static double
+FcCompareFilename (FcValue *v1, FcValue *v2)
+{
+    const FcChar8 *s1 = FcValueString (v1), *s2 = FcValueString (v2);
+    if (FcStrCmp (s1, s2) == 0)
+       return 0.0;
+    else if (FcStrCmpIgnoreCase (s1, s2) == 0)
+       return 1.0;
+    else if (FcStrGlobMatch (s1, s2))
+       return 2.0;
+    else
+       return 3.0;
+}
+
+static double
+FcCompareHash (FcValue *v1, FcValue *v2)
+{
+    const FcChar8 *s1 = FcValueString (v1), *s2 = FcValueString (v2);
+
+    /* Do not match an empty string */
+    if (!s1 || !s2 || !s1[0] || !s2[0])
+       return 1.0;
+    return FcCompareString (v1, v2);
+}
+
+#define PRI_NULL(n)                            \
+    PRI_ ## n ## _STRONG = -1,                 \
+    PRI_ ## n ## _WEAK = -1,
+#define PRI1(n)
+#define PRI_FcCompareFamily(n)         PRI1(n)
+#define PRI_FcCompareString(n)         PRI1(n)
+#define PRI_FcCompareNumber(n)         PRI1(n)
+#define PRI_FcCompareSize(n)           PRI1(n)
+#define PRI_FcCompareBool(n)           PRI1(n)
+#define PRI_FcCompareFilename(n)       PRI1(n)
+#define PRI_FcCompareCharSet(n)                PRI1(n)
+#define PRI_FcCompareLang(n)           PRI1(n)
+#define PRI_FcComparePostScript(n)     PRI1(n)
+#define PRI_FcCompareHash(n)           PRI1(n)
+
+#define FC_OBJECT(NAME, Type, Cmp)     PRI_##Cmp(NAME)
+
+typedef enum _FcMatcherPriorityDummy {
+#include "fcobjs.h"
+} FcMatcherPriorityDummy;
+
+#undef FC_OBJECT
+
+#undef PRI1
+#define PRI1(n)                                        \
+    PRI_ ## n,                                 \
+    PRI_ ## n ## _STRONG = PRI_ ## n,          \
+    PRI_ ## n ## _WEAK = PRI_ ## n
+
+typedef enum _FcMatcherPriority {
+    PRI1(HASH),
+    PRI1(FILE),
+    PRI1(FONTFORMAT),
+    PRI1(SCALABLE),
+    PRI1(FOUNDRY),
+    PRI1(CHARSET),
+    PRI_FAMILY_STRONG,
+    PRI_POSTSCRIPT_NAME_STRONG,
+    PRI1(LANG),
+    PRI_FAMILY_WEAK,
+    PRI_POSTSCRIPT_NAME_WEAK,
+    PRI1(SPACING),
+    PRI1(PIXEL_SIZE),
+    PRI1(STYLE),
+    PRI1(SLANT),
+    PRI1(WEIGHT),
+    PRI1(WIDTH),
+    PRI1(DECORATIVE),
+    PRI1(ANTIALIAS),
+    PRI1(RASTERIZER),
+    PRI1(OUTLINE),
+    PRI1(FONTVERSION),
+    PRI_END
+} FcMatcherPriority;
+
+#undef PRI1
+
+typedef struct _FcMatcher {
+    FcObject object;
+    double   (*compare) (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, },
-#define MATCH_FOUNDRY  0
-    
-    { FC_CHARSET,      FcCompareCharSet },
-#define MATCH_CHARSET  1
-    
-    { FC_ANTIALIAS,    FcCompareBool, },
-#define MATCH_ANTIALIAS        2
-    
-    { FC_LANG,         FcCompareString },
-#define MATCH_LANG     3
-    
-    { FC_FAMILY,       FcCompareString, },
-#define MATCH_FAMILY   4
-    
-    { FC_SPACING,      FcCompareInteger, },
-#define MATCH_SPACING  5
-    
-    { FC_PIXEL_SIZE,   FcCompareSize, },
-#define MATCH_PIXEL_SIZE       6
-    
-    { FC_STYLE,                FcCompareString, },
-#define MATCH_STYLE    7
-    
-    { FC_SLANT,                FcCompareInteger, },
-#define MATCH_SLANT    8
-    
-    { FC_WEIGHT,       FcCompareInteger, },
-#define MATCH_WEIGHT   9
-    
-    { FC_RASTERIZER,   FcCompareString, },
-#define MATCH_RASTERIZER       10
-    
-    { FC_OUTLINE,      FcCompareBool, },
-#define MATCH_OUTLINE  11
+#define FC_OBJECT(NAME, Type, Cmp)     { FC_##NAME##_OBJECT,   Cmp,    PRI_##NAME##_STRONG,    PRI_##NAME##_WEAK },
+static const FcMatcher _FcMatchers [] = {
+    { FC_INVALID_OBJECT, NULL, -1, -1 },
+#include "fcobjs.h"
 };
+#undef FC_OBJECT
 
-#define NUM_MATCHER (sizeof _FcMatchers / sizeof _FcMatchers[0])
-
-static FcBool
-FcCompareValueList (const char  *object,
-                   FcValueList *v1orig,        /* pattern */
-                   FcValueList *v2orig,        /* target */
-                   FcValue     *bestValue,
-                   double      *value,
-                   FcResult    *result)
+static const FcMatcher*
+FcObjectToMatcher (FcObject object,
+                  FcBool   include_lang)
 {
-    FcValueList    *v1, *v2;
-    double         v, best;
-    int                    j;
-    int                    i;
-    
-    /*
-     * 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 (include_lang)
     {
-       if (bestValue)
-           *bestValue = v2orig->value;
-       return FcTrue;
-    }
-#if 0
-    for (i = 0; i < NUM_MATCHER; i++)
-    {
-       if (!FcStrCmpIgnoreCase ((FcChar8 *) _FcMatchers[i].object,
-                                (FcChar8 *) object))
+       switch (object) {
+       case FC_FAMILYLANG_OBJECT:
+       case FC_STYLELANG_OBJECT:
+       case FC_FULLNAMELANG_OBJECT:
+           object = FC_LANG_OBJECT;
            break;
+       }
     }
-    if (i == NUM_MATCHER)
+    if (object > FC_MAX_BASE_OBJECT ||
+       !_FcMatchers[object].compare ||
+       _FcMatchers[object].strong == -1 ||
+       _FcMatchers[object].weak == -1)
+       return NULL;
+
+    return _FcMatchers + object;
+}
+
+static FcBool
+FcCompareValueList (FcObject        object,
+                   const FcMatcher *match,
+                   FcValueListPtr   v1orig,    /* pattern */
+                   FcValueListPtr   v2orig,    /* target */
+                   FcValue         *bestValue,
+                   double          *value,
+                   int             *n,
+                   FcResult        *result)
+{
+    FcValueListPtr  v1, v2;
+    double         v, best, bestStrong, bestWeak;
+    int                    j, k, pos = 0;
+
+    if (!match)
     {
        if (bestValue)
-           *bestValue = v2orig->value;
+           *bestValue = FcValueCanonicalize(&v2orig->value);
+       if (n)
+           *n = 0;
        return FcTrue;
     }
-#endif
+
     best = 1e99;
-    j = 0;
-    for (v1 = v1orig; v1; v1 = v1->next)
+    bestStrong = 1e99;
+    bestWeak = 1e99;
+    j = 1;
+    for (v1 = v1orig; v1; v1 = FcValueListNext(v1))
     {
-       for (v2 = v2orig; v2; v2 = v2->next)
+       for (v2 = v2orig, k = 0; v2; v2 = FcValueListNext(v2), k++)
        {
-           v = (*_FcMatchers[i].compare) (_FcMatchers[i].object,
-                                           v1->value,
-                                           v2->value);
+           v = (match->compare) (&v1->value, &v2->value);
            if (v < 0)
            {
                *result = FcResultTypeMismatch;
                return FcFalse;
            }
-           if (FcDebug () & FC_DBG_MATCHV)
-               printf (" v %g j %d ", v, j);
-           v = v * 100 + j;
+           v = v * 1000 + j;
            if (v < best)
            {
                if (bestValue)
-                   *bestValue = v2->value;
+                   *bestValue = FcValueCanonicalize(&v2->value);
                best = v;
+               pos = k;
+           }
+           if (v1->binding == FcValueBindingStrong)
+           {
+               if (v < bestStrong)
+                   bestStrong = v;
+           }
+           else
+           {
+               if (v < bestWeak)
+                   bestWeak = v;
            }
        }
        j++;
     }
     if (FcDebug () & FC_DBG_MATCHV)
     {
-       printf (" %s: %g ", object, best);
+       printf (" %s: %g ", FcObjectName (object), best);
        FcValueListPrint (v1orig);
        printf (", ");
        FcValueListPrint (v2orig);
        printf ("\n");
     }
-    value[i] += best;
+    if (value)
+    {
+       int weak    = match->weak;
+       int strong  = match->strong;
+       if (weak == strong)
+           value[strong] += best;
+       else
+       {
+           value[weak] += bestWeak;
+           value[strong] += bestStrong;
+       }
+    }
+    if (n)
+       *n = pos;
+
     return FcTrue;
 }
 
@@ -266,46 +407,35 @@ FcCompare (FcPattern      *pat,
           FcResult     *result)
 {
     int                    i, i1, i2;
-    
-    for (i = 0; i < NUM_MATCHER; i++)
+
+    for (i = 0; i < PRI_END; 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);
+       FcPatternElt *elt_i1 = &FcPatternElts(pat)[i1];
+       FcPatternElt *elt_i2 = &FcPatternElts(fnt)[i2];
+
+       i = FcObjectCompare(elt_i1->object, elt_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))
+           const FcMatcher *match = FcObjectToMatcher (elt_i1->object, FcFalse);
+           if (!FcCompareValueList (elt_i1->object, match,
+                                    FcPatternEltValues(elt_i1),
+                                    FcPatternEltValues(elt_i2),
+                                    NULL, value, NULL, result))
                return FcFalse;
            i1++;
            i2++;
        }
     }
     return FcTrue;
-#if 0
-    for (i1 = 0; i1 < pat->num; i1++)
-    {
-       for (i2 = 0; i2 < fnt->num; i2++)
-       {
-           if (!strcmp (pat->elts[i1].object, fnt->elts[i2].object))
-           {
-               break;
-           }
-       }
-    }
-    return FcTrue;
-#endif
 }
 
 FcPattern *
@@ -315,57 +445,157 @@ FcFontRenderPrepare (FcConfig        *config,
 {
     FcPattern      *new;
     int                    i;
-    FcPatternElt    *fe, *pe;
+    FcPatternElt    *fe, *pe, *fel, *pel;
     FcValue        v;
-    double         score[NUM_MATCHER];
     FcResult       result;
-    
+
+    assert (pat != NULL);
+    assert (font != NULL);
+
     new = FcPatternCreate ();
     if (!new)
-       return 0;
+       return NULL;
     for (i = 0; i < font->num; i++)
     {
-       fe = &font->elts[i];
-       pe = FcPatternFind (pat, fe->object, FcFalse);
+       fe = &FcPatternElts(font)[i];
+       if (fe->object == FC_FAMILYLANG_OBJECT ||
+           fe->object == FC_STYLELANG_OBJECT ||
+           fe->object == FC_FULLNAMELANG_OBJECT)
+       {
+           /* ignore those objects. we need to deal with them
+            * another way */
+           continue;
+       }
+       if (fe->object == FC_FAMILY_OBJECT ||
+           fe->object == FC_STYLE_OBJECT ||
+           fe->object == FC_FULLNAME_OBJECT)
+       {
+           FC_ASSERT_STATIC ((FC_FAMILY_OBJECT + 1) == FC_FAMILYLANG_OBJECT);
+           FC_ASSERT_STATIC ((FC_STYLE_OBJECT + 1) == FC_STYLELANG_OBJECT);
+           FC_ASSERT_STATIC ((FC_FULLNAME_OBJECT + 1) == FC_FULLNAMELANG_OBJECT);
+
+           fel = FcPatternObjectFindElt (font, fe->object + 1);
+           pel = FcPatternObjectFindElt (pat, fe->object + 1);
+       }
+       else
+       {
+           fel = NULL;
+           pel = NULL;
+       }
+       pe = FcPatternObjectFindElt (pat, fe->object);
        if (pe)
        {
-           if (!FcCompareValueList (pe->object, pe->values, 
-                                    fe->values, &v, score, &result))
+           const FcMatcher *match = FcObjectToMatcher (pe->object, FcFalse);
+
+           if (!FcCompareValueList (pe->object, match,
+                                    FcPatternEltValues(pe),
+                                    FcPatternEltValues(fe), &v, NULL, NULL, &result))
            {
                FcPatternDestroy (new);
-               return 0;
+               return NULL;
+           }
+           if (fel && pel)
+           {
+               int n = 1, j;
+               FcValueListPtr l1, l2, ln = NULL, ll = NULL;
+
+               match = FcObjectToMatcher (pel->object, FcTrue);
+               if (!FcCompareValueList (pel->object, match,
+                                        FcPatternEltValues (pel),
+                                        FcPatternEltValues (fel), NULL, NULL, &n, &result))
+               {
+                   FcPatternDestroy (new);
+                   return NULL;
+               }
+
+               for (j = 0, l1 = FcPatternEltValues (fe), l2 = FcPatternEltValues (fel);
+                    l1 != NULL || l2 != NULL;
+                    j++, l1 = l1 ? FcValueListNext (l1) : NULL, l2 = l2 ? FcValueListNext (l2) : NULL)
+               {
+                   if (j == n)
+                   {
+                       if (l1)
+                           ln = FcValueListPrepend (ln,
+                                                    FcValueCanonicalize (&l1->value),
+                                                    FcValueBindingStrong);
+                       if (l2)
+                           ll = FcValueListPrepend (ll,
+                                                    FcValueCanonicalize (&l2->value),
+                                                    FcValueBindingStrong);
+                   }
+                   else
+                   {
+                       if (l1)
+                           ln = FcValueListAppend (ln,
+                                                   FcValueCanonicalize (&l1->value),
+                                                   FcValueBindingStrong);
+                       if (l2)
+                           ll = FcValueListAppend (ll,
+                                                   FcValueCanonicalize (&l2->value),
+                                                   FcValueBindingStrong);
+                   }
+               }
+               FcPatternObjectListAdd (new, fe->object, ln, FcFalse);
+               FcPatternObjectListAdd (new, fel->object, ll, FcFalse);
+
+               continue;
+           }
+           else if (fel)
+           {
+               FcValueListPtr l1, l2;
+
+           copy_lang:
+               l1 = FcValueListDuplicate (FcPatternEltValues (fe));
+               l2 = FcValueListDuplicate (FcPatternEltValues (fel));
+               FcPatternObjectListAdd (new, fe->object, l1, FcFalse);
+               FcPatternObjectListAdd (new, fel->object, l2, FcFalse);
+
+               continue;
            }
+           FcPatternObjectAdd (new, fe->object, v, FcFalse);
        }
        else
-           v = fe->values->value;
-       FcPatternAdd (new, fe->object, v, FcTrue);
+       {
+           if (fel)
+               goto copy_lang;
+           FcPatternObjectListAdd (new, fe->object,
+                                   FcValueListDuplicate (FcPatternEltValues (fe)),
+                                   FcTrue);
+       }
     }
     for (i = 0; i < pat->num; i++)
     {
-       pe = &pat->elts[i];
-       fe = FcPatternFind (font, pe->object, FcFalse);
-       if (!fe)
-           FcPatternAdd (new, pe->object, pe->values->value, FcTrue);
+       pe = &FcPatternElts(pat)[i];
+       fe = FcPatternObjectFindElt (font, pe->object);
+       if (!fe &&
+           pe->object != FC_FAMILYLANG_OBJECT &&
+           pe->object != FC_STYLELANG_OBJECT &&
+           pe->object != FC_FULLNAMELANG_OBJECT)
+       {
+           FcPatternObjectListAdd (new, pe->object,
+                                   FcValueListDuplicate (FcPatternEltValues(pe)),
+                                   FcFalse);
+       }
     }
-    FcConfigSubstitute (config, new, FcMatchFont);
+
+    FcConfigSubstituteWithPat (config, new, pat, FcMatchFont);
     return new;
 }
 
-FcPattern *
-FcFontSetMatch (FcConfig    *config,
-               FcFontSet   **sets,
-               int         nsets,
-               FcPattern   *p,
-               FcResult    *result)
+static FcPattern *
+FcFontSetMatchInternal (FcFontSet   **sets,
+                       int         nsets,
+                       FcPattern   *p,
+                       FcResult    *result)
 {
-    double         score[NUM_MATCHER], bestscore[NUM_MATCHER];
+    double         score[PRI_END], bestscore[PRI_END];
     int                    f;
     FcFontSet      *s;
     FcPattern      *best;
     int                    i;
     int                    set;
 
-    for (i = 0; i < NUM_MATCHER; i++)
+    for (i = 0; i < PRI_END; i++)
        bestscore[i] = 0;
     best = 0;
     if (FcDebug () & FC_DBG_MATCH)
@@ -373,12 +603,6 @@ FcFontSetMatch (FcConfig    *config,
        printf ("Match ");
        FcPatternPrint (p);
     }
-    if (!config)
-    {
-       config = FcConfigGetCurrent ();
-       if (!config)
-           return 0;
-    }
     for (set = 0; set < nsets; set++)
     {
        s = sets[set];
@@ -396,19 +620,19 @@ FcFontSetMatch (FcConfig    *config,
            if (FcDebug () & FC_DBG_MATCHV)
            {
                printf ("Score");
-               for (i = 0; i < NUM_MATCHER; i++)
+               for (i = 0; i < PRI_END; i++)
                {
                    printf (" %g", score[i]);
                }
                printf ("\n");
            }
-           for (i = 0; i < NUM_MATCHER; i++)
+           for (i = 0; i < PRI_END; 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 < PRI_END; i++)
                        bestscore[i] = score[i];
                    best = s->fonts[f];
                    break;
@@ -419,25 +643,60 @@ FcFontSetMatch (FcConfig    *config,
     if (FcDebug () & FC_DBG_MATCH)
     {
        printf ("Best score");
-       for (i = 0; i < NUM_MATCHER; i++)
+       for (i = 0; i < PRI_END; i++)
            printf (" %g", bestscore[i]);
+       printf ("\n");
        FcPatternPrint (best);
     }
-    if (!best)
+    /* assuming that 'result' is initialized with FcResultNoMatch
+     * outside this function */
+    if (best)
+       *result = FcResultMatch;
+
+    return best;
+}
+
+FcPattern *
+FcFontSetMatch (FcConfig    *config,
+               FcFontSet   **sets,
+               int         nsets,
+               FcPattern   *p,
+               FcResult    *result)
+{
+    FcPattern      *best;
+
+    assert (sets != NULL);
+    assert (p != NULL);
+    assert (result != NULL);
+
+    *result = FcResultNoMatch;
+
+    if (!config)
     {
-       *result = FcResultNoMatch;
-       return 0;
+       config = FcConfigGetCurrent ();
+       if (!config)
+           return 0;
     }
-    return FcFontRenderPrepare (config, p, best);
+    best = FcFontSetMatchInternal (sets, nsets, p, result);
+    if (best)
+       return FcFontRenderPrepare (config, p, best);
+    else
+       return NULL;
 }
 
 FcPattern *
 FcFontMatch (FcConfig  *config,
-            FcPattern  *p, 
+            FcPattern  *p,
             FcResult   *result)
 {
     FcFontSet  *sets[2];
     int                nsets;
+    FcPattern   *best;
+
+    assert (p != NULL);
+    assert (result != NULL);
+
+    *result = FcResultNoMatch;
 
     if (!config)
     {
@@ -450,12 +709,17 @@ FcFontMatch (FcConfig     *config,
        sets[nsets++] = config->fonts[FcSetSystem];
     if (config->fonts[FcSetApplication])
        sets[nsets++] = config->fonts[FcSetApplication];
-    return FcFontSetMatch (config, sets, nsets, p, result);
+
+    best = FcFontSetMatchInternal (sets, nsets, p, result);
+    if (best)
+       return FcFontRenderPrepare (config, p, best);
+    else
+       return NULL;
 }
 
 typedef struct _FcSortNode {
     FcPattern  *pattern;
-    double     score[NUM_MATCHER];
+    double     score[PRI_END];
 } FcSortNode;
 
 static int
@@ -465,60 +729,92 @@ FcSortCompare (const void *aa, const void *ab)
     FcSortNode  *b = *(FcSortNode **) ab;
     double     *as = &a->score[0];
     double     *bs = &b->score[0];
-    double     ad, bd;
+    double     ad = 0, bd = 0;
     int         i;
 
-    i = NUM_MATCHER;
+    i = PRI_END;
     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)
+FcSortWalk (FcSortNode **n, int nnode, FcFontSet *fs, FcCharSet **csp, FcBool trim)
 {
-    FcCharSet  *ncs;
-    FcSortNode *node;
+    FcBool ret = FcFalse;
+    FcCharSet *cs;
+    int i;
 
-    while (nnode--)
+    cs = 0;
+    if (trim || csp)
     {
-       node = *n++;
-       if (FcPatternGetCharSet (node->pattern, FC_CHARSET, 0, &ncs) == 
-           FcResultMatch)
+       cs = FcCharSetCreate ();
+       if (cs == NULL)
+           goto bail;
+    }
+
+    for (i = 0; i < nnode; i++)
+    {
+       FcSortNode      *node = *n++;
+       FcBool          adds_chars = FcFalse;
+
+       /*
+        * Only fetch node charset if we'd need it
+        */
+       if (cs)
        {
-           /*
-            * If this font isn't a subset of the previous fonts,
-            * add it to the list
-            */
-           if (!trim || !*cs || !FcCharSetIsSubset (ncs, *cs))
+           FcCharSet   *ncs;
+
+           if (FcPatternGetCharSet (node->pattern, FC_CHARSET, 0, &ncs) !=
+               FcResultMatch)
+               continue;
+
+           if (!FcCharSetMerge (cs, ncs, &adds_chars))
+               goto bail;
+       }
+
+       /*
+        * If this font isn't a subset of the previous fonts,
+        * add it to the list
+        */
+       if (!i || !trim || adds_chars)
+       {
+           FcPatternReference (node->pattern);
+           if (FcDebug () & FC_DBG_MATCHV)
            {
-               if (*cs)
-               {
-                   ncs = FcCharSetUnion (ncs, *cs);
-                   if (!ncs)
-                       return FcFalse;
-                   FcCharSetDestroy (*cs);
-               }
-               else
-                   ncs = FcCharSetCopy (ncs);
-               *cs = ncs;
-               if (!FcFontSetAdd (fs, node->pattern))
-                   return FcFalse;
+               printf ("Add ");
+               FcPatternPrint (node->pattern);
+           }
+           if (!FcFontSetAdd (fs, node->pattern))
+           {
+               FcPatternDestroy (node->pattern);
+               goto bail;
            }
        }
     }
-    return FcTrue;
+    if (csp)
+    {
+       *csp = cs;
+       cs = 0;
+    }
+
+    ret = FcTrue;
+
+bail:
+    if (cs)
+       FcCharSetDestroy (cs);
+
+    return ret;
 }
 
 void
 FcFontSetSortDestroy (FcFontSet *fs)
 {
-    fs->nfont = 0;
     FcFontSetDestroy (fs);
 }
 
 FcFontSet *
-FcFontSetSort (FcConfig            *config,
+FcFontSetSort (FcConfig            *config FC_UNUSED,
               FcFontSet    **sets,
               int          nsets,
               FcPattern    *p,
@@ -532,11 +828,31 @@ FcFontSetSort (FcConfig       *config,
     FcSortNode     **nodeps, **nodep;
     int                    nnodes;
     FcSortNode     *new;
-    FcCharSet      *cs;
     int                    set;
     int                    f;
     int                    i;
+    int                    nPatternLang;
+    FcBool         *patternLangSat;
+    FcValue        patternLang;
+
+    assert (sets != NULL);
+    assert (p != NULL);
+    assert (result != NULL);
+
+    /* There are some implementation that relying on the result of
+     * "result" to check if the return value of FcFontSetSort
+     * is valid or not.
+     * So we should initialize it to the conservative way since
+     * this function doesn't return NULL anymore.
+     */
+    if (result)
+       *result = FcResultNoMatch;
 
+    if (FcDebug () & FC_DBG_MATCH)
+    {
+       printf ("Sort ");
+       FcPatternPrint (p);
+    }
     nnodes = 0;
     for (set = 0; set < nsets; set++)
     {
@@ -546,12 +862,22 @@ FcFontSetSort (FcConfig       *config,
        nnodes += s->nfont;
     }
     if (!nnodes)
-       goto bail0;
-    nodes = malloc (nnodes * sizeof (FcSortNode) + nnodes * sizeof (FcSortNode *));
+       return FcFontSetCreate ();
+
+    for (nPatternLang = 0;
+        FcPatternGet (p, FC_LANG, nPatternLang, &patternLang) == FcResultMatch;
+        nPatternLang++)
+       ;
+       
+    /* freed below */
+    nodes = malloc (nnodes * sizeof (FcSortNode) +
+                   nnodes * sizeof (FcSortNode *) +
+                   nPatternLang * sizeof (FcBool));
     if (!nodes)
        goto bail0;
     nodeps = (FcSortNode **) (nodes + nnodes);
-    
+    patternLangSat = (FcBool *) (nodeps + nnodes);
+
     new = nodes;
     nodep = nodeps;
     for (set = 0; set < nsets; set++)
@@ -572,7 +898,7 @@ FcFontSetSort (FcConfig         *config,
            if (FcDebug () & FC_DBG_MATCHV)
            {
                printf ("Score");
-               for (i = 0; i < NUM_MATCHER; i++)
+               for (i = 0; i < PRI_END; i++)
                {
                    printf (" %g", new->score[i]);
                }
@@ -585,7 +911,58 @@ FcFontSetSort (FcConfig        *config,
     }
 
     nnodes = new - nodes;
-    
+
+    qsort (nodeps, nnodes, sizeof (FcSortNode *),
+          FcSortCompare);
+
+    for (i = 0; i < nPatternLang; i++)
+       patternLangSat[i] = FcFalse;
+
+    for (f = 0; f < nnodes; f++)
+    {
+       FcBool  satisfies = FcFalse;
+       /*
+        * If this node matches any language, go check
+        * which ones and satisfy those entries
+        */
+       if (nodeps[f]->score[PRI_LANG] < 2000)
+       {
+           for (i = 0; i < nPatternLang; i++)
+           {
+               FcValue     nodeLang;
+               
+               if (!patternLangSat[i] &&
+                   FcPatternGet (p, FC_LANG, i, &patternLang) == FcResultMatch &&
+                   FcPatternGet (nodeps[f]->pattern, FC_LANG, 0, &nodeLang) == FcResultMatch)
+               {
+                   double  compare = FcCompareLang (&patternLang, &nodeLang);
+                   if (compare >= 0 && compare < 2)
+                   {
+                       if (FcDebug () & FC_DBG_MATCHV)
+                       {
+                           FcChar8 *family;
+                           FcChar8 *style;
+
+                           if (FcPatternGetString (nodeps[f]->pattern, FC_FAMILY, 0, &family) == FcResultMatch &&
+                               FcPatternGetString (nodeps[f]->pattern, FC_STYLE, 0, &style) == FcResultMatch)
+                               printf ("Font %s:%s matches language %d\n", family, style, i);
+                       }
+                       patternLangSat[i] = FcTrue;
+                       satisfies = FcTrue;
+                       break;
+                   }
+               }
+           }
+       }
+       if (!satisfies)
+       {
+           nodeps[f]->score[PRI_LANG] = 10000.0;
+       }
+    }
+
+    /*
+     * Re-sort once the language issues have been settled
+     */
     qsort (nodeps, nnodes, sizeof (FcSortNode *),
           FcSortCompare);
 
@@ -593,20 +970,22 @@ FcFontSetSort (FcConfig       *config,
     if (!ret)
        goto bail1;
 
-    cs = 0;
-
-    if (!FcSortWalk (nodeps, nnodes, ret, &cs, trim))
+    if (!FcSortWalk (nodeps, nnodes, ret, csp, trim))
        goto bail2;
 
-    *csp = cs;
-
     free (nodes);
 
+    if (FcDebug() & FC_DBG_MATCH)
+    {
+       printf ("First font ");
+       FcPatternPrint (ret->fonts[0]);
+    }
+    if (ret->nfont > 0)
+       *result = FcResultMatch;
+
     return ret;
 
 bail2:
-    if (cs)
-       FcCharSetDestroy (cs);
     FcFontSetDestroy (ret);
 bail1:
     free (nodes);
@@ -616,7 +995,7 @@ bail0:
 
 FcFontSet *
 FcFontSort (FcConfig   *config,
-           FcPattern   *p, 
+           FcPattern   *p,
            FcBool      trim,
            FcCharSet   **csp,
            FcResult    *result)
@@ -624,6 +1003,11 @@ FcFontSort (FcConfig      *config,
     FcFontSet  *sets[2];
     int                nsets;
 
+    assert (p != NULL);
+    assert (result != NULL);
+
+    *result = FcResultNoMatch;
+
     if (!config)
     {
        config = FcConfigGetCurrent ();
@@ -637,3 +1021,6 @@ FcFontSort (FcConfig       *config,
        sets[nsets++] = config->fonts[FcSetApplication];
     return FcFontSetSort (config, sets, nsets, p, trim, csp, result);
 }
+#define __fcmatch__
+#include "fcaliastail.h"
+#undef __fcmatch__