blob: 9f3e046d7df2398df3b2383070fee6c6d88021c7 [file] [log] [blame]
/*
* 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"
/* Objects MT-safe for readonly access. */
typedef struct {
const FcChar8 lang[16];
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 int FcLangSetIndex (const FcChar8 *lang);
static void
FcLangSetBitSet (FcLangSet *ls,
unsigned int id)
{
unsigned int bucket;
id = fcLangCharSetIndices[id];
bucket = id >> 5;
if (bucket >= ls->map_size)
return; /* shouldn't happen really */
ls->map[bucket] |= ((FcChar32) 1U << (id & 0x1f));
}
static FcBool
FcLangSetBitGet (const FcLangSet *ls,
unsigned int id)
{
unsigned 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)
{
unsigned int bucket;
id = fcLangCharSetIndices[id];
bucket = id >> 5;
if (bucket >= ls->map_size)
return; /* shouldn't happen really */
ls->map[bucket] &= ~((FcChar32) 1U << (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] & (1U << 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;
}
FcChar8 *
FcLangNormalize (const FcChar8 *lang)
{
FcChar8 *result = NULL, *s, *orig;
char *territory, *encoding, *modifier;
size_t llen, tlen = 0, mlen = 0;
if (!lang || !*lang)
return NULL;
/* might be called without initialization */
FcInitDebug ();
if (FcStrCmpIgnoreCase (lang, (const FcChar8 *)"C") == 0 ||
FcStrCmpIgnoreCase (lang, (const FcChar8 *)"C.UTF-8") == 0 ||
FcStrCmpIgnoreCase (lang, (const FcChar8 *)"C.utf8") == 0 ||
FcStrCmpIgnoreCase (lang, (const FcChar8 *)"POSIX") == 0)
{
result = FcStrCopy ((const FcChar8 *)"en");
goto bail;
}
s = FcStrCopy (lang);
if (!s)
goto bail;
/* from the comments in glibc:
*
* LOCALE can consist of up to four recognized parts for the XPG syntax:
*
* language[_territory[.codeset]][@modifier]
*
* Beside the first all of them are allowed to be missing. If the
* full specified locale is not found, the less specific one are
* looked for. The various part will be stripped off according to
* the following order:
* (1) codeset
* (2) normalized codeset
* (3) territory
* (4) modifier
*
* So since we don't take care of the codeset part here, what patterns
* we need to deal with is:
*
* 1. language_territory@modifier
* 2. language@modifier
* 3. language
*
* then. and maybe no need to try language_territory here.
*/
modifier = strchr ((const char *) s, '@');
if (modifier)
{
*modifier = 0;
modifier++;
mlen = strlen (modifier);
}
encoding = strchr ((const char *) s, '.');
if (encoding)
{
*encoding = 0;
encoding++;
if (modifier)
{
memmove (encoding, modifier, mlen + 1);
modifier = encoding;
}
}
territory = strchr ((const char *) s, '_');
if (!territory)
territory = strchr ((const char *) s, '-');
if (territory)
{
*territory = 0;
territory++;
tlen = strlen (territory);
}
llen = strlen ((const char *) s);
if (llen < 2 || llen > 3)
{
fprintf (stderr, "Fontconfig warning: ignoring %s: not a valid language tag\n",
lang);
goto bail0;
}
if (territory && (tlen < 2 || tlen > 3) &&
!(territory[0] == 'z' && tlen < 5))
{
fprintf (stderr, "Fontconfig warning: ignoring %s: not a valid region tag\n",
lang);
goto bail0;
}
if (territory)
territory[-1] = '-';
if (modifier)
modifier[-1] = '@';
orig = FcStrDowncase (s);
if (!orig)
goto bail0;
if (territory)
{
if (FcDebug () & FC_DBG_LANGSET)
printf("Checking the existence of %s.orth\n", s);
if (FcLangSetIndex (s) < 0)
{
memmove (territory - 1, territory + tlen, (mlen > 0 ? mlen + 1 : 0) + 1);
if (modifier)
modifier = territory;
}
else
{
result = s;
/* we'll miss the opportunity to reduce the correct size
* of the allocated memory for the string after that.
*/
s = NULL;
goto bail1;
}
}
if (modifier)
{
if (FcDebug () & FC_DBG_LANGSET)
printf("Checking the existence of %s.orth\n", s);
if (FcLangSetIndex (s) < 0)
modifier[-1] = 0;
else
{
result = s;
/* we'll miss the opportunity to reduce the correct size
* of the allocated memory for the string after that.
*/
s = NULL;
goto bail1;
}
}
if (FcDebug () & FC_DBG_LANGSET)
printf("Checking the existence of %s.orth\n", s);
if (FcLangSetIndex (s) < 0)
{
/* there seems no languages matched in orth.
* add the language as is for fallback.
*/
result = orig;
orig = NULL;
}
else
{
result = s;
/* we'll miss the opportunity to reduce the correct size
* of the allocated memory for the string after that.
*/
s = NULL;
}
bail1:
if (orig)
FcStrFree (orig);
bail0:
if (s)
free (s);
bail:
if (FcDebug () & FC_DBG_LANGSET)
{
if (result)
printf ("normalized: %s -> %s\n", lang, result);
else
printf ("Unable to normalize %s\n", lang);
}
return result;
}
#define FcLangEnd(c) ((c) == '-' || (c) == '\0')
FcLangResult
FcLangCompare (const FcChar8 *s1, const FcChar8 *s2)
{
FcChar8 c1, c2;
FcLangResult result = FcLangDifferentLang;
const FcChar8 *s1_orig = s1;
FcBool is_und;
is_und = FcToLower (s1[0]) == 'u' &&
FcToLower (s1[1]) == 'n' &&
FcToLower (s1[2]) == 'd' &&
FcLangEnd (s1[3]);
for (;;)
{
c1 = *s1++;
c2 = *s2++;
c1 = FcToLower (c1);
c2 = FcToLower (c2);
if (c1 != c2)
{
if (!is_und && FcLangEnd (c1) && FcLangEnd (c2))
result = FcLangDifferentTerritory;
return result;
}
else if (!c1)
{
return is_und ? result : FcLangEqual;
}
else if (c1 == '-')
{
if (!is_und)
result = FcLangDifferentTerritory;
}
/* If we parsed past "und-", then do not consider it undefined anymore,
* as there's *something* specified. */
if (is_und && s1 - s1_orig == 4)
is_und = FcFalse;
}
}
/*
* 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 missing one */
if (c1 == '-' && c2 == '\0')
return FcTrue;
/* see if sub has a country while super is missing 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;
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)
return;
if (ls->extra)
FcStrSetDestroy (ls->extra);
free (ls);
}
FcLangSet *
FcLangSetCopy (const FcLangSet *ls)
{
FcLangSet *new;
if (!ls)
return NULL;
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;
}
/* When the language isn't found, the return value r is such that:
* 1) r < 0
* 2) -r -1 is the index of the first language in fcLangCharSets that comes
* after the 'lang' argument in lexicographic order.
*
* The -1 is necessary to avoid problems with language id 0 (otherwise, we
* wouldn't be able to distinguish between “language found, id is 0” and
* “language not found, sorts right before the language with id 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+1); /* one past 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;
FcChar32 aInCountrySet, bInCountrySet;
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++)
{
aInCountrySet = 0;
bInCountrySet = 0;
for (i = 0; i < count; i++)
{
aInCountrySet |= lsa->map[i] & fcLangCountrySets[j][i];
bInCountrySet |= lsb->map[i] & fcLangCountrySets[j][i];
if (aInCountrySet && bInCountrySet)
{
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, FcValuePromotionBuffer *vbuf)
{
int id;
typedef struct {
FcLangSet ls;
FcStrSet strs;
FcChar8 *str;
} FcLangSetPromotionBuffer;
FcLangSetPromotionBuffer *buf = (FcLangSetPromotionBuffer *) vbuf;
FC_ASSERT_STATIC (sizeof (FcLangSetPromotionBuffer) <= sizeof (FcValuePromotionBuffer));
memset (buf->ls.map, '\0', sizeof (buf->ls.map));
buf->ls.map_size = NUM_LANG_SET_MAP;
buf->ls.extra = 0;
if (lang)
{
id = FcLangSetIndex (lang);
if (id >= 0)
{
FcLangSetBitSet (&buf->ls, id);
}
else
{
buf->ls.extra = &buf->strs;
buf->strs.num = 1;
buf->strs.size = 1;
buf->strs.strs = &buf->str;
FcRefInit (&buf->strs.ref, 1);
buf->str = (FcChar8 *) lang;
}
}
return &buf->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 & (1U << 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 & (1U << 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);
FcStrSet *set = FcLangSetGetLangs (b);
FcStrList *sl = FcStrListCreate (set);
FcChar8 *str;
FcStrSetDestroy (set);
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__