diff options
Diffstat (limited to 'fontconfig/src/fclang.c')
-rw-r--r-- | fontconfig/src/fclang.c | 822 |
1 files changed, 822 insertions, 0 deletions
diff --git a/fontconfig/src/fclang.c b/fontconfig/src/fclang.c new file mode 100644 index 000000000..1d62c4e3f --- /dev/null +++ b/fontconfig/src/fclang.c @@ -0,0 +1,822 @@ +/* + * 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__ |