aboutsummaryrefslogtreecommitdiff
path: root/fontconfig/src/fclang.c
diff options
context:
space:
mode:
Diffstat (limited to 'fontconfig/src/fclang.c')
-rw-r--r--fontconfig/src/fclang.c1708
1 files changed, 886 insertions, 822 deletions
diff --git a/fontconfig/src/fclang.c b/fontconfig/src/fclang.c
index 1d62c4e3f..6a9cf01d6 100644
--- a/fontconfig/src/fclang.c
+++ b/fontconfig/src/fclang.c
@@ -1,822 +1,886 @@
-/*
- * fontconfig/src/fclang.c
- *
- * 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
- * advertising or publicity pertaining to distribution of the software without
- * specific, written prior permission. Keith Packard makes no
- * representations about the suitability of this software for any purpose. It
- * is provided "as is" without express or implied warranty.
- *
- * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
- * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
- * 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 "fcint.h"
-#include "fcftint.h"
-
-typedef struct {
- const FcChar8 lang[8];
- const FcCharSet charset;
-} FcLangCharSet;
-
-typedef struct {
- int begin;
- int end;
-} FcLangCharSetRange;
-
-#include "../fc-lang/fclang.h"
-
-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;
-}
-
-FcLangSet *
-FcFreeTypeLangSet (const FcCharSet *charset,
- const FcChar8 *exclusiveLang)
-{
- int i, j;
- FcChar32 missing;
- const FcCharSet *exclusiveCharset = 0;
- FcLangSet *ls;
-
- if (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))
- {
- 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)
- {
- 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);
- }
-
- if (FcDebug() & FC_DBG_SCANV)
- printf ("\n");
-
-
- return ls;
-}
-
-#define FcLangEnd(c) ((c) == '-' || (c) == '\0')
-
-FcLangResult
-FcLangCompare (const FcChar8 *s1, const FcChar8 *s2)
-{
- FcChar8 c1, c2;
- FcLangResult result = FcLangDifferentLang;
-
- for (;;)
- {
- c1 = *s1++;
- c2 = *s2++;
-
- c1 = FcToLower (c1);
- c2 = FcToLower (c2);
- if (c1 != c2)
- {
- if (FcLangEnd (c1) && FcLangEnd (c2))
- result = FcLangDifferentTerritory;
- return result;
- }
- else if (!c1)
- return FcLangEqual;
- else if (c1 == '-')
- result = FcLangDifferentTerritory;
- }
-}
-
-/*
- * 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 = *super++;
- c2 = *sub++;
-
- c1 = FcToLower (c1);
- c2 = FcToLower (c2);
- if (c1 != c2)
- {
- /* 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;
- }
-}
-
-const FcCharSet *
-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 FcLangDifferentTerritory:
- if (country == -1)
- country = i;
- case FcLangDifferentLang:
- default:
- break;
- }
- }
- if (country == -1)
- return 0;
- 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);
-}
-
-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
- */
-FcLangSet *
-FcLangSetPromote (const FcChar8 *lang)
-{
- static FcLangSet ls;
- static FcStrSet strs;
- static FcChar8 *str;
- int id;
-
- memset (ls.map, '\0', sizeof (ls.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;
-}
-
-#define __fclang__
-#include "fcaliastail.h"
-#include "fcftaliastail.h"
-#undef __fclang__
+/*
+ * fontconfig/src/fclang.c
+ *
+ * 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 the author(s) not be used in
+ * advertising or publicity pertaining to distribution of the software without
+ * 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.
+ *
+ * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * 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 "fcint.h"
+#include "fcftint.h"
+
+typedef struct {
+ const FcChar8 lang[8];
+ const FcCharSet charset;
+} FcLangCharSet;
+
+typedef struct {
+ int begin;
+ int end;
+} FcLangCharSetRange;
+
+#include "../fc-lang/fclang.h"
+
+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, j;
+ FcChar32 missing;
+ const FcCharSet *exclusiveCharset = 0;
+ FcLangSet *ls;
+
+ if (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))
+ {
+ 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)
+ {
+ 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);
+ }
+
+ if (FcDebug() & FC_DBG_SCANV)
+ printf ("\n");
+
+
+ return ls;
+}
+
+#define FcLangEnd(c) ((c) == '-' || (c) == '\0')
+
+FcLangResult
+FcLangCompare (const FcChar8 *s1, const FcChar8 *s2)
+{
+ FcChar8 c1, c2;
+ FcLangResult result = FcLangDifferentLang;
+
+ for (;;)
+ {
+ c1 = *s1++;
+ c2 = *s2++;
+
+ c1 = FcToLower (c1);
+ c2 = FcToLower (c2);
+ if (c1 != c2)
+ {
+ if (FcLangEnd (c1) && FcLangEnd (c2))
+ result = FcLangDifferentTerritory;
+ return result;
+ }
+ else if (!c1)
+ return FcLangEqual;
+ else if (c1 == '-')
+ result = FcLangDifferentTerritory;
+ }
+}
+
+/*
+ * 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 = *super++;
+ c2 = *sub++;
+
+ c1 = FcToLower (c1);
+ c2 = FcToLower (c2);
+ if (c1 != c2)
+ {
+ /* 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;
+ }
+}
+
+const FcCharSet *
+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 FcLangDifferentTerritory:
+ if (country == -1)
+ country = i;
+ case FcLangDifferentLang:
+ default:
+ break;
+ }
+ }
+ if (country == -1)
+ return 0;
+ 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__