]> git.wh0rd.org - fontconfig.git/blobdiff - src/fclang.c
Bug 44826 - <alias> must contain only a single <family>
[fontconfig.git] / src / fclang.c
index 9795ec8c45e7e391f602e80f4ae8515c0caff291..be42b58c4694f54747c395ab28011a490a7a4dbc 100644 (file)
@@ -1,21 +1,21 @@
 /*
- * $XFree86: xc/lib/fontconfig/fc-lang/fclang.tmpl.c,v 1.1 2002/07/06 23:21:36 keithp Exp $
+ * fontconfig/src/fclang.c
  *
- * Copyright © 2002 Keith Packard, member of The XFree86 Project, Inc.
+ * Copyright © 2002 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
  */
 
 #include "fcint.h"
+#include "fcftint.h"
 
 typedef struct {
-    FcChar8    *lang;
-    FcCharSet  charset;
+    const FcChar8      lang[8];
+    const FcCharSet    charset;
 } FcLangCharSet;
 
+typedef struct {
+    int begin;
+    int end;
+} FcLangCharSetRange;
+
 #include "../fc-lang/fclang.h"
 
-#define NUM_LANG_CHAR_SET      (sizeof (fcLangCharSets) / sizeof (fcLangCharSets[0]))
-                                                
-FcBool
-FcFreeTypeSetLang (FcPattern       *pattern, 
-                  FcCharSet        *charset, 
+struct _FcLangSet {
+    FcStrSet   *extra;
+    FcChar32    map_size;
+    FcChar32   map[NUM_LANG_SET_MAP];
+};
+
+static void
+FcLangSetBitSet (FcLangSet    *ls,
+                unsigned int  id)
+{
+  int bucket;
+
+  id = fcLangCharSetIndices[id];
+  bucket = id >> 5;
+  if (bucket >= ls->map_size)
+    return; /* shouldn't happen really */
+
+  ls->map[bucket] |= ((FcChar32) 1 << (id & 0x1f));
+}
+
+static FcBool
+FcLangSetBitGet (const FcLangSet *ls,
+                unsigned int     id)
+{
+  int bucket;
+
+  id = fcLangCharSetIndices[id];
+  bucket = id >> 5;
+  if (bucket >= ls->map_size)
+    return FcFalse;
+
+  return ((ls->map[bucket] >> (id & 0x1f)) & 1) ? FcTrue : FcFalse;
+}
+
+static void
+FcLangSetBitReset (FcLangSet    *ls,
+                  unsigned int  id)
+{
+  int bucket;
+
+  id = fcLangCharSetIndices[id];
+  bucket = id >> 5;
+  if (bucket >= ls->map_size)
+    return; /* shouldn't happen really */
+
+  ls->map[bucket] &= ~((FcChar32) 1 << (id & 0x1f));
+}
+
+FcLangSet *
+FcFreeTypeLangSet (const FcCharSet  *charset,
                   const FcChar8    *exclusiveLang)
 {
-    int                    i;
+    int                    i, j;
     FcChar32       missing;
-    FcBool         hasLang = FcFalse;
     const FcCharSet *exclusiveCharset = 0;
+    FcLangSet      *ls;
 
     if (exclusiveLang)
-       exclusiveCharset = FcCharSetForLang (exclusiveLang);
+       exclusiveCharset = FcLangGetCharSet (exclusiveLang);
+    ls = FcLangSetCreate ();
+    if (!ls)
+       return 0;
+    if (FcDebug() & FC_DBG_LANGSET)
+    {
+       printf ("font charset");
+       FcCharSetPrint (charset);
+       printf ("\n");
+    }
     for (i = 0; i < NUM_LANG_CHAR_SET; i++)
     {
+       if (FcDebug() & FC_DBG_LANGSET)
+       {
+           printf ("%s charset", fcLangCharSets[i].lang);
+           FcCharSetPrint (&fcLangCharSets[i].charset);
+           printf ("\n");
+       }
+       
        /*
         * Check for Han charsets to make fonts
         * which advertise support for a single language
         * not support other Han languages
         */
        if (exclusiveCharset &&
-           FcFreeTypeIsExclusiveLang (fcLangCharSets[i].lang) &&
-           fcLangCharSets[i].charset.leaves != exclusiveCharset->leaves)
+           FcFreeTypeIsExclusiveLang (fcLangCharSets[i].lang))
        {
-           continue;
+           if (fcLangCharSets[i].charset.num != exclusiveCharset->num)
+               continue;
+
+           for (j = 0; j < fcLangCharSets[i].charset.num; j++)
+               if (FcCharSetLeaf(&fcLangCharSets[i].charset, j) !=
+                   FcCharSetLeaf(exclusiveCharset, j))
+                   continue;
        }
        missing = FcCharSetSubtractCount (&fcLangCharSets[i].charset, charset);
         if (FcDebug() & FC_DBG_SCANV)
-           printf ("%s(%d) ", fcLangCharSets[i].lang, missing);
-       if (!missing)
        {
-           if (!FcPatternAddString (pattern, FC_LANG, fcLangCharSets[i].lang))
-               return FcFalse;
-           hasLang = FcTrue;
+           if (missing && missing < 10)
+           {
+               FcCharSet   *missed = FcCharSetSubtract (&fcLangCharSets[i].charset,
+                                                        charset);
+               FcChar32    ucs4;
+               FcChar32    map[FC_CHARSET_MAP_SIZE];
+               FcChar32    next;
+
+               printf ("\n%s(%u) ", fcLangCharSets[i].lang, missing);
+               printf ("{");
+               for (ucs4 = FcCharSetFirstPage (missed, map, &next);
+                    ucs4 != FC_CHARSET_DONE;
+                    ucs4 = FcCharSetNextPage (missed, map, &next))
+               {
+                   int     i, j;
+                   for (i = 0; i < FC_CHARSET_MAP_SIZE; i++)
+                       if (map[i])
+                       {
+                           for (j = 0; j < 32; j++)
+                               if (map[i] & (1 << j))
+                                   printf (" %04x", ucs4 + i * 32 + j);
+                       }
+               }
+               printf (" }\n\t");
+               FcCharSetDestroy (missed);
+           }
+           else
+               printf ("%s(%u) ", fcLangCharSets[i].lang, missing);
        }
+       if (!missing)
+           FcLangSetBitSet (ls, i);
     }
-    /*
-     * Make sure it has a lang entry
-     */
-    if (!hasLang)
-       if (!FcPatternAddString (pattern, FC_LANG, (FcChar8 *) "x-unknown"))
-           return FcFalse;
 
     if (FcDebug() & FC_DBG_SCANV)
        printf ("\n");
-    return FcTrue;
+
+
+    return ls;
 }
 
+#define FcLangEnd(c)   ((c) == '-' || (c) == '\0')
 
 FcLangResult
 FcLangCompare (const FcChar8 *s1, const FcChar8 *s2)
 {
-    const FcChar8   *orig_s1 = s1;
     FcChar8        c1, c2;
-    FcLangResult    result;
-    /*
-     * Compare ISO 639 language codes
-     */
+    FcLangResult    result = FcLangDifferentLang;
+
     for (;;)
     {
        c1 = *s1++;
        c2 = *s2++;
-       if (c1 == '\0' || c1 == '-')
-           break;
-       if (c2 == '\0' || c2 == '-')
-           break;
+       
        c1 = FcToLower (c1);
        c2 = FcToLower (c2);
        if (c1 != c2)
-           return FcLangDifferentLang;     /* mismatching lang code */
+       {
+           if (FcLangEnd (c1) && FcLangEnd (c2))
+               result = FcLangDifferentTerritory;
+           return result;
+       }
+       else if (!c1)
+           return FcLangEqual;
+       else if (c1 == '-')
+           result = FcLangDifferentTerritory;
     }
-    if (!c1 && !c2)
-       return FcLangEqual;
-    /*
-     * Make x-* mismatch as if the lang part didn't match
-     */
-    result = FcLangDifferentCountry;
-    if (orig_s1[0] == 'x' && (orig_s1[1] == '\0' || orig_s1[1] == '-'))
-       result = FcLangDifferentLang;
-    
-    if (c1 == '\0' || c2 == '\0')
-       return result;
-    /*
-     * Compare ISO 3166 country codes
-     */
+}
+
+/*
+ * Return FcTrue when super contains sub.
+ *
+ * super contains sub if super and sub have the same
+ * language and either the same country or one
+ * is missing the country
+ */
+
+static FcBool
+FcLangContains (const FcChar8 *super, const FcChar8 *sub)
+{
+    FcChar8        c1, c2;
+
     for (;;)
     {
-       c1 = *s1++;
-       c2 = *s2++;
-       if (!c1 || !c2)
-           break;
+       c1 = *super++;
+       c2 = *sub++;
+       
        c1 = FcToLower (c1);
        c2 = FcToLower (c2);
        if (c1 != c2)
-           break;
+       {
+           /* see if super has a country while sub is mising one */
+           if (c1 == '-' && c2 == '\0')
+               return FcTrue;
+           /* see if sub has a country while super is mising one */
+           if (c1 == '\0' && c2 == '-')
+               return FcTrue;
+           return FcFalse;
+       }
+       else if (!c1)
+           return FcTrue;
     }
-    if (c1 == c2)
-       return FcLangEqual;
-    else
-       return result;
 }
 
 const FcCharSet *
-FcCharSetForLang (const FcChar8 *lang)
+FcLangGetCharSet (const FcChar8 *lang)
 {
     int                i;
     int                country = -1;
+
     for (i = 0; i < NUM_LANG_CHAR_SET; i++)
     {
        switch (FcLangCompare (lang, fcLangCharSets[i].lang)) {
        case FcLangEqual:
            return &fcLangCharSets[i].charset;
-       case FcLangDifferentCountry:
+       case FcLangDifferentTerritory:
            if (country == -1)
                country = i;
+       case FcLangDifferentLang:
        default:
            break;
        }
     }
     if (country == -1)
        return 0;
-    return &fcLangCharSets[i].charset;
+    return &fcLangCharSets[country].charset;
+}
+
+FcStrSet *
+FcGetLangs (void)
+{
+    FcStrSet *langs;
+    int        i;
+
+    langs = FcStrSetCreate();
+    if (!langs)
+       return 0;
+
+    for (i = 0; i < NUM_LANG_CHAR_SET; i++)
+       FcStrSetAdd (langs, fcLangCharSets[i].lang);
+
+    return langs;
+}
+
+FcLangSet *
+FcLangSetCreate (void)
+{
+    FcLangSet  *ls;
+
+    ls = malloc (sizeof (FcLangSet));
+    if (!ls)
+       return 0;
+    FcMemAlloc (FC_MEM_LANGSET, sizeof (FcLangSet));
+    memset (ls->map, '\0', sizeof (ls->map));
+    ls->map_size = NUM_LANG_SET_MAP;
+    ls->extra = 0;
+    return ls;
+}
+
+void
+FcLangSetDestroy (FcLangSet *ls)
+{
+    if (ls->extra)
+       FcStrSetDestroy (ls->extra);
+    FcMemFree (FC_MEM_LANGSET, sizeof (FcLangSet));
+    free (ls);
+}
+
+FcLangSet *
+FcLangSetCopy (const FcLangSet *ls)
+{
+    FcLangSet  *new;
+
+    new = FcLangSetCreate ();
+    if (!new)
+       goto bail0;
+    memset (new->map, '\0', sizeof (new->map));
+    memcpy (new->map, ls->map, FC_MIN (sizeof (new->map), ls->map_size * sizeof (ls->map[0])));
+    if (ls->extra)
+    {
+       FcStrList       *list;
+       FcChar8         *extra;
+       
+       new->extra = FcStrSetCreate ();
+       if (!new->extra)
+           goto bail1;
+
+       list = FcStrListCreate (ls->extra);     
+       if (!list)
+           goto bail1;
+       
+       while ((extra = FcStrListNext (list)))
+           if (!FcStrSetAdd (new->extra, extra))
+           {
+               FcStrListDone (list);
+               goto bail1;
+           }
+       FcStrListDone (list);
+    }
+    return new;
+bail1:
+    FcLangSetDestroy (new);
+bail0:
+    return 0;
+}
+
+static int
+FcLangSetIndex (const FcChar8 *lang)
+{
+    int            low, high, mid = 0;
+    int            cmp = 0;
+    FcChar8 firstChar = FcToLower(lang[0]);
+    FcChar8 secondChar = firstChar ? FcToLower(lang[1]) : '\0';
+
+    if (firstChar < 'a')
+    {
+       low = 0;
+       high = fcLangCharSetRanges[0].begin;
+    }
+    else if(firstChar > 'z')
+    {
+       low = fcLangCharSetRanges[25].begin;
+       high = NUM_LANG_CHAR_SET - 1;
+    }
+    else
+    {
+       low = fcLangCharSetRanges[firstChar - 'a'].begin;
+       high = fcLangCharSetRanges[firstChar - 'a'].end;
+       /* no matches */
+       if (low > high)
+           return -low; /* next entry after where it would be */
+    }
+
+    while (low <= high)
+    {
+       mid = (high + low) >> 1;
+       if(fcLangCharSets[mid].lang[0] != firstChar)
+           cmp = FcStrCmpIgnoreCase(fcLangCharSets[mid].lang, lang);
+       else
+       {   /* fast path for resolving 2-letter languages (by far the most common) after
+            * finding the first char (probably already true because of the hash table) */
+           cmp = fcLangCharSets[mid].lang[1] - secondChar;
+           if (cmp == 0 &&
+               (fcLangCharSets[mid].lang[2] != '\0' ||
+                lang[2] != '\0'))
+           {
+               cmp = FcStrCmpIgnoreCase(fcLangCharSets[mid].lang+2,
+                                        lang+2);
+           }
+       }
+       if (cmp == 0)
+           return mid;
+       if (cmp < 0)
+           low = mid + 1;
+       else
+           high = mid - 1;
+    }
+    if (cmp < 0)
+       mid++;
+    return -(mid + 1);
+}
+
+FcBool
+FcLangSetAdd (FcLangSet *ls, const FcChar8 *lang)
+{
+    int            id;
+
+    id = FcLangSetIndex (lang);
+    if (id >= 0)
+    {
+       FcLangSetBitSet (ls, id);
+       return FcTrue;
+    }
+    if (!ls->extra)
+    {
+       ls->extra = FcStrSetCreate ();
+       if (!ls->extra)
+           return FcFalse;
+    }
+    return FcStrSetAdd (ls->extra, lang);
+}
+
+FcBool
+FcLangSetDel (FcLangSet *ls, const FcChar8 *lang)
+{
+    int        id;
+
+    id = FcLangSetIndex (lang);
+    if (id >= 0)
+    {
+       FcLangSetBitReset (ls, id);
+    }
+    else if (ls->extra)
+    {
+       FcStrSetDel (ls->extra, lang);
+    }
+    return FcTrue;
+}
+
+FcLangResult
+FcLangSetHasLang (const FcLangSet *ls, const FcChar8 *lang)
+{
+    int                    id;
+    FcLangResult    best, r;
+    int                    i;
+
+    id = FcLangSetIndex (lang);
+    if (id < 0)
+       id = -id - 1;
+    else if (FcLangSetBitGet (ls, id))
+       return FcLangEqual;
+    best = FcLangDifferentLang;
+    for (i = id - 1; i >= 0; i--)
+    {
+       r = FcLangCompare (lang, fcLangCharSets[i].lang);
+       if (r == FcLangDifferentLang)
+           break;
+       if (FcLangSetBitGet (ls, i) && r < best)
+           best = r;
+    }
+    for (i = id; i < NUM_LANG_CHAR_SET; i++)
+    {
+       r = FcLangCompare (lang, fcLangCharSets[i].lang);
+       if (r == FcLangDifferentLang)
+           break;
+       if (FcLangSetBitGet (ls, i) && r < best)
+           best = r;
+    }
+    if (ls->extra)
+    {
+       FcStrList       *list = FcStrListCreate (ls->extra);
+       FcChar8         *extra;
+       
+       if (list)
+       {
+           while (best > FcLangEqual && (extra = FcStrListNext (list)))
+           {
+               r = FcLangCompare (lang, extra);
+               if (r < best)
+                   best = r;
+           }
+           FcStrListDone (list);
+       }
+    }
+    return best;
+}
+
+static FcLangResult
+FcLangSetCompareStrSet (const FcLangSet *ls, FcStrSet *set)
+{
+    FcStrList      *list = FcStrListCreate (set);
+    FcLangResult    r, best = FcLangDifferentLang;
+    FcChar8        *extra;
+
+    if (list)
+    {
+       while (best > FcLangEqual && (extra = FcStrListNext (list)))
+       {
+           r = FcLangSetHasLang (ls, extra);
+           if (r < best)
+               best = r;
+       }
+       FcStrListDone (list);
+    }
+    return best;
+}
+
+FcLangResult
+FcLangSetCompare (const FcLangSet *lsa, const FcLangSet *lsb)
+{
+    int                    i, j, count;
+    FcLangResult    best, r;
+
+    count = FC_MIN (lsa->map_size, lsb->map_size);
+    count = FC_MIN (NUM_LANG_SET_MAP, count);
+    for (i = 0; i < count; i++)
+       if (lsa->map[i] & lsb->map[i])
+           return FcLangEqual;
+    best = FcLangDifferentLang;
+    for (j = 0; j < NUM_COUNTRY_SET; j++)
+       for (i = 0; i < count; i++)
+           if ((lsa->map[i] & fcLangCountrySets[j][i]) &&
+               (lsb->map[i] & fcLangCountrySets[j][i]))
+           {
+               best = FcLangDifferentTerritory;
+               break;
+           }
+    if (lsa->extra)
+    {
+       r = FcLangSetCompareStrSet (lsb, lsa->extra);
+       if (r < best)
+           best = r;
+    }
+    if (best > FcLangEqual && lsb->extra)
+    {
+       r = FcLangSetCompareStrSet (lsa, lsb->extra);
+       if (r < best)
+           best = r;
+    }
+    return best;
+}
+
+/*
+ * Used in computing values -- mustn't allocate any storage
+ * XXX Not thread-safe
+ */
+FcLangSet *
+FcLangSetPromote (const FcChar8 *lang)
+{
+    static FcLangSet   ls;
+    static FcStrSet    strs;
+    static FcChar8     *str;
+    int                        id;
+
+    memset (ls.map, '\0', sizeof (ls.map));
+    ls.map_size = NUM_LANG_SET_MAP;
+    ls.extra = 0;
+    id = FcLangSetIndex (lang);
+    if (id > 0)
+    {
+       FcLangSetBitSet (&ls, id);
+    }
+    else
+    {
+       ls.extra = &strs;
+       strs.num = 1;
+       strs.size = 1;
+       strs.strs = &str;
+       strs.ref = 1;
+       str = (FcChar8 *) lang;
+    }
+    return &ls;
+}
+
+FcChar32
+FcLangSetHash (const FcLangSet *ls)
+{
+    FcChar32   h = 0;
+    int                i, count;
+
+    count = FC_MIN (ls->map_size, NUM_LANG_SET_MAP);
+    for (i = 0; i < count; i++)
+       h ^= ls->map[i];
+    if (ls->extra)
+       h ^= ls->extra->num;
+    return h;
+}
+
+FcLangSet *
+FcNameParseLangSet (const FcChar8 *string)
+{
+    FcChar8        lang[32], c = 0;
+    int i;
+    FcLangSet      *ls;
+
+    ls = FcLangSetCreate ();
+    if (!ls)
+       goto bail0;
+
+    for(;;)
+    {
+       for(i = 0; i < 31;i++)
+       {
+           c = *string++;
+           if(c == '\0' || c == '|')
+               break; /* end of this code */
+           lang[i] = c;
+       }
+       lang[i] = '\0';
+       if (!FcLangSetAdd (ls, lang))
+           goto bail1;
+       if(c == '\0')
+           break;
+    }
+    return ls;
+bail1:
+    FcLangSetDestroy (ls);
+bail0:
+    return 0;
+}
+
+FcBool
+FcNameUnparseLangSet (FcStrBuf *buf, const FcLangSet *ls)
+{
+    int                i, bit, count;
+    FcChar32   bits;
+    FcBool     first = FcTrue;
+
+    count = FC_MIN (ls->map_size, NUM_LANG_SET_MAP);
+    for (i = 0; i < count; i++)
+    {
+       if ((bits = ls->map[i]))
+       {
+           for (bit = 0; bit <= 31; bit++)
+               if (bits & (1 << bit))
+               {
+                   int id = (i << 5) | bit;
+                   if (!first)
+                       if (!FcStrBufChar (buf, '|'))
+                           return FcFalse;
+                   if (!FcStrBufString (buf, fcLangCharSets[fcLangCharSetIndicesInv[id]].lang))
+                       return FcFalse;
+                   first = FcFalse;
+               }
+       }
+    }
+    if (ls->extra)
+    {
+       FcStrList   *list = FcStrListCreate (ls->extra);
+       FcChar8     *extra;
+
+       if (!list)
+           return FcFalse;
+       while ((extra = FcStrListNext (list)))
+       {
+           if (!first)
+               if (!FcStrBufChar (buf, '|'))
+                {
+                    FcStrListDone (list);
+                   return FcFalse;
+                }
+           if (!FcStrBufString (buf, extra))
+                {
+                    FcStrListDone (list);
+                    return FcFalse;
+                }
+           first = FcFalse;
+       }
+        FcStrListDone (list);
+    }
+    return FcTrue;
+}
+
+FcBool
+FcLangSetEqual (const FcLangSet *lsa, const FcLangSet *lsb)
+{
+    int            i, count;
+
+    count = FC_MIN (lsa->map_size, lsb->map_size);
+    count = FC_MIN (NUM_LANG_SET_MAP, count);
+    for (i = 0; i < count; i++)
+    {
+       if (lsa->map[i] != lsb->map[i])
+           return FcFalse;
+    }
+    if (!lsa->extra && !lsb->extra)
+       return FcTrue;
+    if (lsa->extra && lsb->extra)
+       return FcStrSetEqual (lsa->extra, lsb->extra);
+    return FcFalse;
+}
+
+static FcBool
+FcLangSetContainsLang (const FcLangSet *ls, const FcChar8 *lang)
+{
+    int                    id;
+    int                    i;
+
+    id = FcLangSetIndex (lang);
+    if (id < 0)
+       id = -id - 1;
+    else if (FcLangSetBitGet (ls, id))
+       return FcTrue;
+    /*
+     * search up and down among equal languages for a match
+     */
+    for (i = id - 1; i >= 0; i--)
+    {
+       if (FcLangCompare (fcLangCharSets[i].lang, lang) == FcLangDifferentLang)
+           break;
+       if (FcLangSetBitGet (ls, i) &&
+           FcLangContains (fcLangCharSets[i].lang, lang))
+           return FcTrue;
+    }
+    for (i = id; i < NUM_LANG_CHAR_SET; i++)
+    {
+       if (FcLangCompare (fcLangCharSets[i].lang, lang) == FcLangDifferentLang)
+           break;
+       if (FcLangSetBitGet (ls, i) &&
+           FcLangContains (fcLangCharSets[i].lang, lang))
+           return FcTrue;
+    }
+    if (ls->extra)
+    {
+       FcStrList       *list = FcStrListCreate (ls->extra);
+       FcChar8         *extra;
+       
+       if (list)
+       {
+           while ((extra = FcStrListNext (list)))
+           {
+               if (FcLangContains (extra, lang))
+                   break;
+           }
+           FcStrListDone (list);
+           if (extra)
+               return FcTrue;
+       }
+    }
+    return FcFalse;
+}
+
+/*
+ * return FcTrue if lsa contains every language in lsb
+ */
+FcBool
+FcLangSetContains (const FcLangSet *lsa, const FcLangSet *lsb)
+{
+    int                    i, j, count;
+    FcChar32       missing;
+
+    if (FcDebug() & FC_DBG_MATCHV)
+    {
+       printf ("FcLangSet "); FcLangSetPrint (lsa);
+       printf (" contains "); FcLangSetPrint (lsb);
+       printf ("\n");
+    }
+    /*
+     * check bitmaps for missing language support
+     */
+    count = FC_MIN (lsa->map_size, lsb->map_size);
+    count = FC_MIN (NUM_LANG_SET_MAP, count);
+    for (i = 0; i < count; i++)
+    {
+       missing = lsb->map[i] & ~lsa->map[i];
+       if (missing)
+       {
+           for (j = 0; j < 32; j++)
+               if (missing & (1 << j))
+               {
+                   if (!FcLangSetContainsLang (lsa,
+                                               fcLangCharSets[fcLangCharSetIndicesInv[i*32 + j]].lang))
+                   {
+                       if (FcDebug() & FC_DBG_MATCHV)
+                           printf ("\tMissing bitmap %s\n", fcLangCharSets[fcLangCharSetIndicesInv[i*32+j]].lang);
+                       return FcFalse;
+                   }
+               }
+       }
+    }
+    if (lsb->extra)
+    {
+       FcStrList   *list = FcStrListCreate (lsb->extra);
+       FcChar8     *extra;
+
+       if (list)
+       {
+           while ((extra = FcStrListNext (list)))
+           {
+               if (!FcLangSetContainsLang (lsa, extra))
+               {
+                   if (FcDebug() & FC_DBG_MATCHV)
+                       printf ("\tMissing string %s\n", extra);
+                   break;
+               }
+           }
+           FcStrListDone (list);
+           if (extra)
+               return FcFalse;
+       }
+    }
+    return FcTrue;
+}
+
+FcBool
+FcLangSetSerializeAlloc (FcSerialize *serialize, const FcLangSet *l)
+{
+    if (!FcSerializeAlloc (serialize, l, sizeof (FcLangSet)))
+       return FcFalse;
+    return FcTrue;
+}
+
+FcLangSet *
+FcLangSetSerialize(FcSerialize *serialize, const FcLangSet *l)
+{
+    FcLangSet  *l_serialize = FcSerializePtr (serialize, l);
+
+    if (!l_serialize)
+       return NULL;
+    memset (l_serialize->map, '\0', sizeof (l_serialize->map));
+    memcpy (l_serialize->map, l->map, FC_MIN (sizeof (l_serialize->map), l->map_size * sizeof (l->map[0])));
+    l_serialize->map_size = NUM_LANG_SET_MAP;
+    l_serialize->extra = NULL; /* We don't serialize ls->extra */
+    return l_serialize;
+}
+
+FcStrSet *
+FcLangSetGetLangs (const FcLangSet *ls)
+{
+    FcStrSet *langs;
+    int              i;
+
+    langs = FcStrSetCreate();
+    if (!langs)
+       return 0;
+
+    for (i = 0; i < NUM_LANG_CHAR_SET; i++)
+       if (FcLangSetBitGet (ls, i))
+           FcStrSetAdd (langs, fcLangCharSets[i].lang);
+
+    if (ls->extra)
+    {
+       FcStrList       *list = FcStrListCreate (ls->extra);
+       FcChar8         *extra;
+
+       if (list)
+       {
+           while ((extra = FcStrListNext (list)))
+               FcStrSetAdd (langs, extra);
+
+           FcStrListDone (list);
+       }
+    }
+
+    return langs;
+}
+
+static FcLangSet *
+FcLangSetOperate(const FcLangSet       *a,
+                const FcLangSet        *b,
+                FcBool                 (*func) (FcLangSet      *ls,
+                                                const FcChar8  *s))
+{
+    FcLangSet  *langset = FcLangSetCopy (a);
+    FcStrList  *sl = FcStrListCreate (FcLangSetGetLangs (b));
+    FcChar8    *str;
+
+    while ((str = FcStrListNext (sl)))
+    {
+       func (langset, str);
+    }
+    FcStrListDone (sl);
+
+    return langset;
+}
+
+FcLangSet *
+FcLangSetUnion (const FcLangSet *a, const FcLangSet *b)
+{
+    return FcLangSetOperate(a, b, FcLangSetAdd);
 }
+
+FcLangSet *
+FcLangSetSubtract (const FcLangSet *a, const FcLangSet *b)
+{
+    return FcLangSetOperate(a, b, FcLangSetDel);
+}
+
+#define __fclang__
+#include "fcaliastail.h"
+#include "fcftaliastail.h"
+#undef __fclang__