2 * fontconfig/src/fclang.c
4 * Copyright © 2002 Keith Packard
6 * Permission to use, copy, modify, distribute, and sell this software and its
7 * documentation for any purpose is hereby granted without fee, provided that
8 * the above copyright notice appear in all copies and that both that
9 * copyright notice and this permission notice appear in supporting
10 * documentation, and that the name of the author(s) not be used in
11 * advertising or publicity pertaining to distribution of the software without
12 * specific, written prior permission. The authors make no
13 * representations about the suitability of this software for any purpose. It
14 * is provided "as is" without express or implied warranty.
16 * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18 * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR
19 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
20 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
21 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22 * PERFORMANCE OF THIS SOFTWARE.
28 /* Objects MT-safe for readonly access. */
31 const FcChar8 lang[8];
32 const FcCharSet charset;
40 #include "../fc-lang/fclang.h"
45 FcChar32 map[NUM_LANG_SET_MAP];
48 static int FcLangSetIndex (const FcChar8 *lang);
52 FcLangSetBitSet (FcLangSet *ls,
57 id = fcLangCharSetIndices[id];
59 if (bucket >= ls->map_size)
60 return; /* shouldn't happen really */
62 ls->map[bucket] |= ((FcChar32) 1 << (id & 0x1f));
66 FcLangSetBitGet (const FcLangSet *ls,
71 id = fcLangCharSetIndices[id];
73 if (bucket >= ls->map_size)
76 return ((ls->map[bucket] >> (id & 0x1f)) & 1) ? FcTrue : FcFalse;
80 FcLangSetBitReset (FcLangSet *ls,
85 id = fcLangCharSetIndices[id];
87 if (bucket >= ls->map_size)
88 return; /* shouldn't happen really */
90 ls->map[bucket] &= ~((FcChar32) 1 << (id & 0x1f));
94 FcFreeTypeLangSet (const FcCharSet *charset,
95 const FcChar8 *exclusiveLang)
99 const FcCharSet *exclusiveCharset = 0;
103 exclusiveCharset = FcLangGetCharSet (exclusiveLang);
104 ls = FcLangSetCreate ();
107 if (FcDebug() & FC_DBG_LANGSET)
109 printf ("font charset");
110 FcCharSetPrint (charset);
113 for (i = 0; i < NUM_LANG_CHAR_SET; i++)
115 if (FcDebug() & FC_DBG_LANGSET)
117 printf ("%s charset", fcLangCharSets[i].lang);
118 FcCharSetPrint (&fcLangCharSets[i].charset);
123 * Check for Han charsets to make fonts
124 * which advertise support for a single language
125 * not support other Han languages
127 if (exclusiveCharset &&
128 FcFreeTypeIsExclusiveLang (fcLangCharSets[i].lang))
130 if (fcLangCharSets[i].charset.num != exclusiveCharset->num)
133 for (j = 0; j < fcLangCharSets[i].charset.num; j++)
134 if (FcCharSetLeaf(&fcLangCharSets[i].charset, j) !=
135 FcCharSetLeaf(exclusiveCharset, j))
138 missing = FcCharSetSubtractCount (&fcLangCharSets[i].charset, charset);
139 if (FcDebug() & FC_DBG_SCANV)
141 if (missing && missing < 10)
143 FcCharSet *missed = FcCharSetSubtract (&fcLangCharSets[i].charset,
146 FcChar32 map[FC_CHARSET_MAP_SIZE];
149 printf ("\n%s(%u) ", fcLangCharSets[i].lang, missing);
151 for (ucs4 = FcCharSetFirstPage (missed, map, &next);
152 ucs4 != FC_CHARSET_DONE;
153 ucs4 = FcCharSetNextPage (missed, map, &next))
156 for (i = 0; i < FC_CHARSET_MAP_SIZE; i++)
159 for (j = 0; j < 32; j++)
160 if (map[i] & (1 << j))
161 printf (" %04x", ucs4 + i * 32 + j);
165 FcCharSetDestroy (missed);
168 printf ("%s(%u) ", fcLangCharSets[i].lang, missing);
171 FcLangSetBitSet (ls, i);
174 if (FcDebug() & FC_DBG_SCANV)
182 FcLangNormalize (const FcChar8 *lang)
184 FcChar8 *result = NULL, *s, *orig;
185 char *territory, *encoding, *modifier;
186 size_t llen, tlen = 0, mlen = 0;
191 if (FcStrCmpIgnoreCase (lang, (const FcChar8 *)"C") == 0 ||
192 FcStrCmpIgnoreCase (lang, (const FcChar8 *)"POSIX") == 0)
194 result = FcStrCopy ((const FcChar8 *)"en");
198 s = FcStrCopy (lang);
202 /* from the comments in glibc:
204 * LOCALE can consist of up to four recognized parts for the XPG syntax:
206 * language[_territory[.codeset]][@modifier]
208 * Beside the first all of them are allowed to be missing. If the
209 * full specified locale is not found, the less specific one are
210 * looked for. The various part will be stripped off according to
211 * the following order:
213 * (2) normalized codeset
217 * So since we don't take care of the codeset part here, what patterns
218 * we need to deal with is:
220 * 1. language_territory@modifier
221 * 2. language@modifier
224 * then. and maybe no need to try language_territory here.
226 modifier = strchr ((const char *) s, '@');
231 mlen = strlen (modifier);
233 encoding = strchr ((const char *) s, '.');
240 memmove (encoding, modifier, mlen + 1);
244 territory = strchr ((const char *) s, '_');
246 territory = strchr ((const char *) s, '-');
251 tlen = strlen (territory);
253 llen = strlen ((const char *) s);
254 if (llen < 2 || llen > 3)
256 fprintf (stderr, "Fontconfig warning: ignoring %s: not a valid language tag\n",
260 if (territory && (tlen < 2 || tlen > 3))
262 fprintf (stderr, "Fontconfig warning: ignoring %s: not a valid region tag\n",
270 orig = FcStrDowncase (s);
275 if (FcDebug () & FC_DBG_LANGSET)
276 printf("Checking the existence of %s.orth\n", s);
277 if (FcLangSetIndex (s) < 0)
279 memmove (territory - 1, territory + tlen, (mlen > 0 ? mlen + 1 : 0) + 1);
281 modifier = territory;
286 /* we'll miss the opportunity to reduce the correct size
287 * of the allocated memory for the string after that.
295 if (FcDebug () & FC_DBG_LANGSET)
296 printf("Checking the existence of %s.orth\n", s);
297 if (FcLangSetIndex (s) < 0)
302 /* we'll miss the opportunity to reduce the correct size
303 * of the allocated memory for the string after that.
309 if (FcDebug () & FC_DBG_LANGSET)
310 printf("Checking the existence of %s.orth\n", s);
311 if (FcLangSetIndex (s) < 0)
313 /* there seems no languages matched in orth.
314 * add the language as is for fallback.
322 /* we'll miss the opportunity to reduce the correct size
323 * of the allocated memory for the string after that.
334 if (FcDebug () & FC_DBG_LANGSET)
337 printf ("normalized: %s -> %s\n", lang, result);
339 printf ("Unable to normalize %s\n", lang);
345 #define FcLangEnd(c) ((c) == '-' || (c) == '\0')
348 FcLangCompare (const FcChar8 *s1, const FcChar8 *s2)
351 FcLangResult result = FcLangDifferentLang;
362 if (FcLangEnd (c1) && FcLangEnd (c2))
363 result = FcLangDifferentTerritory;
369 result = FcLangDifferentTerritory;
374 * Return FcTrue when super contains sub.
376 * super contains sub if super and sub have the same
377 * language and either the same country or one
378 * is missing the country
382 FcLangContains (const FcChar8 *super, const FcChar8 *sub)
395 /* see if super has a country while sub is mising one */
396 if (c1 == '-' && c2 == '\0')
398 /* see if sub has a country while super is mising one */
399 if (c1 == '\0' && c2 == '-')
409 FcLangGetCharSet (const FcChar8 *lang)
414 for (i = 0; i < NUM_LANG_CHAR_SET; i++)
416 switch (FcLangCompare (lang, fcLangCharSets[i].lang)) {
418 return &fcLangCharSets[i].charset;
419 case FcLangDifferentTerritory:
422 case FcLangDifferentLang:
429 return &fcLangCharSets[country].charset;
438 langs = FcStrSetCreate();
442 for (i = 0; i < NUM_LANG_CHAR_SET; i++)
443 FcStrSetAdd (langs, fcLangCharSets[i].lang);
449 FcLangSetCreate (void)
453 ls = malloc (sizeof (FcLangSet));
456 memset (ls->map, '\0', sizeof (ls->map));
457 ls->map_size = NUM_LANG_SET_MAP;
463 FcLangSetDestroy (FcLangSet *ls)
466 FcStrSetDestroy (ls->extra);
471 FcLangSetCopy (const FcLangSet *ls)
475 new = FcLangSetCreate ();
478 memset (new->map, '\0', sizeof (new->map));
479 memcpy (new->map, ls->map, FC_MIN (sizeof (new->map), ls->map_size * sizeof (ls->map[0])));
485 new->extra = FcStrSetCreate ();
489 list = FcStrListCreate (ls->extra);
493 while ((extra = FcStrListNext (list)))
494 if (!FcStrSetAdd (new->extra, extra))
496 FcStrListDone (list);
499 FcStrListDone (list);
503 FcLangSetDestroy (new);
509 FcLangSetIndex (const FcChar8 *lang)
511 int low, high, mid = 0;
513 FcChar8 firstChar = FcToLower(lang[0]);
514 FcChar8 secondChar = firstChar ? FcToLower(lang[1]) : '\0';
519 high = fcLangCharSetRanges[0].begin;
521 else if(firstChar > 'z')
523 low = fcLangCharSetRanges[25].begin;
524 high = NUM_LANG_CHAR_SET - 1;
528 low = fcLangCharSetRanges[firstChar - 'a'].begin;
529 high = fcLangCharSetRanges[firstChar - 'a'].end;
532 return -low; /* next entry after where it would be */
537 mid = (high + low) >> 1;
538 if(fcLangCharSets[mid].lang[0] != firstChar)
539 cmp = FcStrCmpIgnoreCase(fcLangCharSets[mid].lang, lang);
541 { /* fast path for resolving 2-letter languages (by far the most common) after
542 * finding the first char (probably already true because of the hash table) */
543 cmp = fcLangCharSets[mid].lang[1] - secondChar;
545 (fcLangCharSets[mid].lang[2] != '\0' ||
548 cmp = FcStrCmpIgnoreCase(fcLangCharSets[mid].lang+2,
565 FcLangSetAdd (FcLangSet *ls, const FcChar8 *lang)
569 id = FcLangSetIndex (lang);
572 FcLangSetBitSet (ls, id);
577 ls->extra = FcStrSetCreate ();
581 return FcStrSetAdd (ls->extra, lang);
585 FcLangSetDel (FcLangSet *ls, const FcChar8 *lang)
589 id = FcLangSetIndex (lang);
592 FcLangSetBitReset (ls, id);
596 FcStrSetDel (ls->extra, lang);
602 FcLangSetHasLang (const FcLangSet *ls, const FcChar8 *lang)
605 FcLangResult best, r;
608 id = FcLangSetIndex (lang);
611 else if (FcLangSetBitGet (ls, id))
613 best = FcLangDifferentLang;
614 for (i = id - 1; i >= 0; i--)
616 r = FcLangCompare (lang, fcLangCharSets[i].lang);
617 if (r == FcLangDifferentLang)
619 if (FcLangSetBitGet (ls, i) && r < best)
622 for (i = id; i < NUM_LANG_CHAR_SET; i++)
624 r = FcLangCompare (lang, fcLangCharSets[i].lang);
625 if (r == FcLangDifferentLang)
627 if (FcLangSetBitGet (ls, i) && r < best)
632 FcStrList *list = FcStrListCreate (ls->extra);
637 while (best > FcLangEqual && (extra = FcStrListNext (list)))
639 r = FcLangCompare (lang, extra);
643 FcStrListDone (list);
650 FcLangSetCompareStrSet (const FcLangSet *ls, FcStrSet *set)
652 FcStrList *list = FcStrListCreate (set);
653 FcLangResult r, best = FcLangDifferentLang;
658 while (best > FcLangEqual && (extra = FcStrListNext (list)))
660 r = FcLangSetHasLang (ls, extra);
664 FcStrListDone (list);
670 FcLangSetCompare (const FcLangSet *lsa, const FcLangSet *lsb)
673 FcLangResult best, r;
675 count = FC_MIN (lsa->map_size, lsb->map_size);
676 count = FC_MIN (NUM_LANG_SET_MAP, count);
677 for (i = 0; i < count; i++)
678 if (lsa->map[i] & lsb->map[i])
680 best = FcLangDifferentLang;
681 for (j = 0; j < NUM_COUNTRY_SET; j++)
682 for (i = 0; i < count; i++)
683 if ((lsa->map[i] & fcLangCountrySets[j][i]) &&
684 (lsb->map[i] & fcLangCountrySets[j][i]))
686 best = FcLangDifferentTerritory;
691 r = FcLangSetCompareStrSet (lsb, lsa->extra);
695 if (best > FcLangEqual && lsb->extra)
697 r = FcLangSetCompareStrSet (lsa, lsb->extra);
705 * Used in computing values -- mustn't allocate any storage
708 FcLangSetPromote (const FcChar8 *lang, FcValuePromotionBuffer *vbuf)
715 } FcLangSetPromotionBuffer;
716 FcLangSetPromotionBuffer *buf = (FcLangSetPromotionBuffer *) vbuf;
718 FC_ASSERT_STATIC (sizeof (FcLangSetPromotionBuffer) <= sizeof (FcValuePromotionBuffer));
720 memset (buf->ls.map, '\0', sizeof (buf->ls.map));
721 buf->ls.map_size = NUM_LANG_SET_MAP;
723 id = FcLangSetIndex (lang);
726 FcLangSetBitSet (&buf->ls, id);
730 buf->ls.extra = &buf->strs;
733 buf->strs.strs = &buf->str;
734 FcRefInit (&buf->strs.ref, 1);
735 buf->str = (FcChar8 *) lang;
741 FcLangSetHash (const FcLangSet *ls)
746 count = FC_MIN (ls->map_size, NUM_LANG_SET_MAP);
747 for (i = 0; i < count; i++)
755 FcNameParseLangSet (const FcChar8 *string)
757 FcChar8 lang[32], c = 0;
761 ls = FcLangSetCreate ();
767 for(i = 0; i < 31;i++)
770 if(c == '\0' || c == '|')
771 break; /* end of this code */
775 if (!FcLangSetAdd (ls, lang))
782 FcLangSetDestroy (ls);
788 FcNameUnparseLangSet (FcStrBuf *buf, const FcLangSet *ls)
792 FcBool first = FcTrue;
794 count = FC_MIN (ls->map_size, NUM_LANG_SET_MAP);
795 for (i = 0; i < count; i++)
797 if ((bits = ls->map[i]))
799 for (bit = 0; bit <= 31; bit++)
800 if (bits & (1 << bit))
802 int id = (i << 5) | bit;
804 if (!FcStrBufChar (buf, '|'))
806 if (!FcStrBufString (buf, fcLangCharSets[fcLangCharSetIndicesInv[id]].lang))
814 FcStrList *list = FcStrListCreate (ls->extra);
819 while ((extra = FcStrListNext (list)))
822 if (!FcStrBufChar (buf, '|'))
824 FcStrListDone (list);
827 if (!FcStrBufString (buf, extra))
829 FcStrListDone (list);
834 FcStrListDone (list);
840 FcLangSetEqual (const FcLangSet *lsa, const FcLangSet *lsb)
844 count = FC_MIN (lsa->map_size, lsb->map_size);
845 count = FC_MIN (NUM_LANG_SET_MAP, count);
846 for (i = 0; i < count; i++)
848 if (lsa->map[i] != lsb->map[i])
851 if (!lsa->extra && !lsb->extra)
853 if (lsa->extra && lsb->extra)
854 return FcStrSetEqual (lsa->extra, lsb->extra);
859 FcLangSetContainsLang (const FcLangSet *ls, const FcChar8 *lang)
864 id = FcLangSetIndex (lang);
867 else if (FcLangSetBitGet (ls, id))
870 * search up and down among equal languages for a match
872 for (i = id - 1; i >= 0; i--)
874 if (FcLangCompare (fcLangCharSets[i].lang, lang) == FcLangDifferentLang)
876 if (FcLangSetBitGet (ls, i) &&
877 FcLangContains (fcLangCharSets[i].lang, lang))
880 for (i = id; i < NUM_LANG_CHAR_SET; i++)
882 if (FcLangCompare (fcLangCharSets[i].lang, lang) == FcLangDifferentLang)
884 if (FcLangSetBitGet (ls, i) &&
885 FcLangContains (fcLangCharSets[i].lang, lang))
890 FcStrList *list = FcStrListCreate (ls->extra);
895 while ((extra = FcStrListNext (list)))
897 if (FcLangContains (extra, lang))
900 FcStrListDone (list);
909 * return FcTrue if lsa contains every language in lsb
912 FcLangSetContains (const FcLangSet *lsa, const FcLangSet *lsb)
917 if (FcDebug() & FC_DBG_MATCHV)
919 printf ("FcLangSet "); FcLangSetPrint (lsa);
920 printf (" contains "); FcLangSetPrint (lsb);
924 * check bitmaps for missing language support
926 count = FC_MIN (lsa->map_size, lsb->map_size);
927 count = FC_MIN (NUM_LANG_SET_MAP, count);
928 for (i = 0; i < count; i++)
930 missing = lsb->map[i] & ~lsa->map[i];
933 for (j = 0; j < 32; j++)
934 if (missing & (1 << j))
936 if (!FcLangSetContainsLang (lsa,
937 fcLangCharSets[fcLangCharSetIndicesInv[i*32 + j]].lang))
939 if (FcDebug() & FC_DBG_MATCHV)
940 printf ("\tMissing bitmap %s\n", fcLangCharSets[fcLangCharSetIndicesInv[i*32+j]].lang);
948 FcStrList *list = FcStrListCreate (lsb->extra);
953 while ((extra = FcStrListNext (list)))
955 if (!FcLangSetContainsLang (lsa, extra))
957 if (FcDebug() & FC_DBG_MATCHV)
958 printf ("\tMissing string %s\n", extra);
962 FcStrListDone (list);
971 FcLangSetSerializeAlloc (FcSerialize *serialize, const FcLangSet *l)
973 if (!FcSerializeAlloc (serialize, l, sizeof (FcLangSet)))
979 FcLangSetSerialize(FcSerialize *serialize, const FcLangSet *l)
981 FcLangSet *l_serialize = FcSerializePtr (serialize, l);
985 memset (l_serialize->map, '\0', sizeof (l_serialize->map));
986 memcpy (l_serialize->map, l->map, FC_MIN (sizeof (l_serialize->map), l->map_size * sizeof (l->map[0])));
987 l_serialize->map_size = NUM_LANG_SET_MAP;
988 l_serialize->extra = NULL; /* We don't serialize ls->extra */
993 FcLangSetGetLangs (const FcLangSet *ls)
998 langs = FcStrSetCreate();
1002 for (i = 0; i < NUM_LANG_CHAR_SET; i++)
1003 if (FcLangSetBitGet (ls, i))
1004 FcStrSetAdd (langs, fcLangCharSets[i].lang);
1008 FcStrList *list = FcStrListCreate (ls->extra);
1013 while ((extra = FcStrListNext (list)))
1014 FcStrSetAdd (langs, extra);
1016 FcStrListDone (list);
1024 FcLangSetOperate(const FcLangSet *a,
1026 FcBool (*func) (FcLangSet *ls,
1029 FcLangSet *langset = FcLangSetCopy (a);
1030 FcStrList *sl = FcStrListCreate (FcLangSetGetLangs (b));
1033 while ((str = FcStrListNext (sl)))
1035 func (langset, str);
1043 FcLangSetUnion (const FcLangSet *a, const FcLangSet *b)
1045 return FcLangSetOperate(a, b, FcLangSetAdd);
1049 FcLangSetSubtract (const FcLangSet *a, const FcLangSet *b)
1051 return FcLangSetOperate(a, b, FcLangSetDel);
1055 #include "fcaliastail.h"
1056 #include "fcftaliastail.h"