fclang.c revision 953daeba
1/*
2 * fontconfig/src/fclang.c
3 *
4 * Copyright © 2002 Keith Packard
5 *
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.
15 *
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.
23 */
24
25#include "fcint.h"
26#include "fcftint.h"
27
28/* Objects MT-safe for readonly access. */
29
30typedef struct {
31    const FcChar8    	lang[8];
32    const FcCharSet	charset;
33} FcLangCharSet;
34
35typedef struct {
36    int begin;
37    int end;
38} FcLangCharSetRange;
39
40#include "../fc-lang/fclang.h"
41
42struct _FcLangSet {
43    FcStrSet	*extra;
44    FcChar32    map_size;
45    FcChar32	map[NUM_LANG_SET_MAP];
46};
47
48static int FcLangSetIndex (const FcChar8 *lang);
49
50
51static void
52FcLangSetBitSet (FcLangSet    *ls,
53		 unsigned int  id)
54{
55  unsigned int bucket;
56
57  id = fcLangCharSetIndices[id];
58  bucket = id >> 5;
59  if (bucket >= ls->map_size)
60    return; /* shouldn't happen really */
61
62  ls->map[bucket] |= ((FcChar32) 1 << (id & 0x1f));
63}
64
65static FcBool
66FcLangSetBitGet (const FcLangSet *ls,
67		 unsigned int     id)
68{
69  unsigned int bucket;
70
71  id = fcLangCharSetIndices[id];
72  bucket = id >> 5;
73  if (bucket >= ls->map_size)
74    return FcFalse;
75
76  return ((ls->map[bucket] >> (id & 0x1f)) & 1) ? FcTrue : FcFalse;
77}
78
79static void
80FcLangSetBitReset (FcLangSet    *ls,
81		   unsigned int  id)
82{
83  unsigned int bucket;
84
85  id = fcLangCharSetIndices[id];
86  bucket = id >> 5;
87  if (bucket >= ls->map_size)
88    return; /* shouldn't happen really */
89
90  ls->map[bucket] &= ~((FcChar32) 1 << (id & 0x1f));
91}
92
93FcLangSet *
94FcFreeTypeLangSet (const FcCharSet  *charset,
95		   const FcChar8    *exclusiveLang)
96{
97    int		    i, j;
98    FcChar32	    missing;
99    const FcCharSet *exclusiveCharset = 0;
100    FcLangSet	    *ls;
101
102    if (exclusiveLang)
103	exclusiveCharset = FcLangGetCharSet (exclusiveLang);
104    ls = FcLangSetCreate ();
105    if (!ls)
106	return 0;
107    if (FcDebug() & FC_DBG_LANGSET)
108    {
109	printf ("font charset");
110	FcCharSetPrint (charset);
111	printf ("\n");
112    }
113    for (i = 0; i < NUM_LANG_CHAR_SET; i++)
114    {
115	if (FcDebug() & FC_DBG_LANGSET)
116	{
117	    printf ("%s charset", fcLangCharSets[i].lang);
118	    FcCharSetPrint (&fcLangCharSets[i].charset);
119	    printf ("\n");
120	}
121
122	/*
123	 * Check for Han charsets to make fonts
124	 * which advertise support for a single language
125	 * not support other Han languages
126	 */
127	if (exclusiveCharset &&
128	    FcFreeTypeIsExclusiveLang (fcLangCharSets[i].lang))
129	{
130	    if (fcLangCharSets[i].charset.num != exclusiveCharset->num)
131		continue;
132
133	    for (j = 0; j < fcLangCharSets[i].charset.num; j++)
134		if (FcCharSetLeaf(&fcLangCharSets[i].charset, j) !=
135		    FcCharSetLeaf(exclusiveCharset, j))
136		    continue;
137	}
138	missing = FcCharSetSubtractCount (&fcLangCharSets[i].charset, charset);
139        if (FcDebug() & FC_DBG_SCANV)
140	{
141	    if (missing && missing < 10)
142	    {
143		FcCharSet   *missed = FcCharSetSubtract (&fcLangCharSets[i].charset,
144							 charset);
145		FcChar32    ucs4;
146		FcChar32    map[FC_CHARSET_MAP_SIZE];
147		FcChar32    next;
148
149		printf ("\n%s(%u) ", fcLangCharSets[i].lang, missing);
150		printf ("{");
151		for (ucs4 = FcCharSetFirstPage (missed, map, &next);
152		     ucs4 != FC_CHARSET_DONE;
153		     ucs4 = FcCharSetNextPage (missed, map, &next))
154		{
155		    int	    i, j;
156		    for (i = 0; i < FC_CHARSET_MAP_SIZE; i++)
157			if (map[i])
158			{
159			    for (j = 0; j < 32; j++)
160				if (map[i] & (1 << j))
161				    printf (" %04x", ucs4 + i * 32 + j);
162			}
163		}
164		printf (" }\n\t");
165		FcCharSetDestroy (missed);
166	    }
167	    else
168		printf ("%s(%u) ", fcLangCharSets[i].lang, missing);
169	}
170	if (!missing)
171	    FcLangSetBitSet (ls, i);
172    }
173
174    if (FcDebug() & FC_DBG_SCANV)
175	printf ("\n");
176
177
178    return ls;
179}
180
181FcChar8 *
182FcLangNormalize (const FcChar8 *lang)
183{
184    FcChar8 *result = NULL, *s, *orig;
185    char *territory, *encoding, *modifier;
186    size_t llen, tlen = 0, mlen = 0;
187
188    if (!lang || !*lang)
189	return NULL;
190
191    if (FcStrCmpIgnoreCase (lang, (const FcChar8 *)"C") == 0 ||
192	FcStrCmpIgnoreCase (lang, (const FcChar8 *)"C.UTF-8") == 0 ||
193	FcStrCmpIgnoreCase (lang, (const FcChar8 *)"C.utf8") == 0 ||
194	FcStrCmpIgnoreCase (lang, (const FcChar8 *)"POSIX") == 0)
195    {
196	result = FcStrCopy ((const FcChar8 *)"en");
197	goto bail;
198    }
199
200    s = FcStrCopy (lang);
201    if (!s)
202	goto bail;
203
204    /* from the comments in glibc:
205     *
206     * LOCALE can consist of up to four recognized parts for the XPG syntax:
207     *
208     *            language[_territory[.codeset]][@modifier]
209     *
210     * Beside the first all of them are allowed to be missing.  If the
211     * full specified locale is not found, the less specific one are
212     * looked for.  The various part will be stripped off according to
213     * the following order:
214     *            (1) codeset
215     *            (2) normalized codeset
216     *            (3) territory
217     *            (4) modifier
218     *
219     * So since we don't take care of the codeset part here, what patterns
220     * we need to deal with is:
221     *
222     *   1. language_territory@modifier
223     *   2. language@modifier
224     *   3. language
225     *
226     * then. and maybe no need to try language_territory here.
227     */
228    modifier = strchr ((const char *) s, '@');
229    if (modifier)
230    {
231	*modifier = 0;
232	modifier++;
233	mlen = strlen (modifier);
234    }
235    encoding = strchr ((const char *) s, '.');
236    if (encoding)
237    {
238	*encoding = 0;
239	encoding++;
240	if (modifier)
241	{
242	    memmove (encoding, modifier, mlen + 1);
243	    modifier = encoding;
244	}
245    }
246    territory = strchr ((const char *) s, '_');
247    if (!territory)
248	territory = strchr ((const char *) s, '-');
249    if (territory)
250    {
251	*territory = 0;
252	territory++;
253	tlen = strlen (territory);
254    }
255    llen = strlen ((const char *) s);
256    if (llen < 2 || llen > 3)
257    {
258	fprintf (stderr, "Fontconfig warning: ignoring %s: not a valid language tag\n",
259		 lang);
260	goto bail0;
261    }
262    if (territory && (tlen < 2 || tlen > 3))
263    {
264	fprintf (stderr, "Fontconfig warning: ignoring %s: not a valid region tag\n",
265		 lang);
266	goto bail0;
267    }
268    if (territory)
269	territory[-1] = '-';
270    if (modifier)
271	modifier[-1] = '@';
272    orig = FcStrDowncase (s);
273    if (!orig)
274	goto bail0;
275    if (territory)
276    {
277	if (FcDebug () & FC_DBG_LANGSET)
278	    printf("Checking the existence of %s.orth\n", s);
279	if (FcLangSetIndex (s) < 0)
280	{
281	    memmove (territory - 1, territory + tlen, (mlen > 0 ? mlen + 1 : 0) + 1);
282	    if (modifier)
283		modifier = territory;
284	}
285	else
286	{
287	    result = s;
288	    /* we'll miss the opportunity to reduce the correct size
289	     * of the allocated memory for the string after that.
290	     */
291	    s = NULL;
292	    goto bail1;
293	}
294    }
295    if (modifier)
296    {
297	if (FcDebug () & FC_DBG_LANGSET)
298	    printf("Checking the existence of %s.orth\n", s);
299	if (FcLangSetIndex (s) < 0)
300	    modifier[-1] = 0;
301	else
302	{
303	    result = s;
304	    /* we'll miss the opportunity to reduce the correct size
305	     * of the allocated memory for the string after that.
306	     */
307	    s = NULL;
308	    goto bail1;
309	}
310    }
311    if (FcDebug () & FC_DBG_LANGSET)
312	printf("Checking the existence of %s.orth\n", s);
313    if (FcLangSetIndex (s) < 0)
314    {
315	/* there seems no languages matched in orth.
316	 * add the language as is for fallback.
317	 */
318	result = orig;
319	orig = NULL;
320    }
321    else
322    {
323	result = s;
324	/* we'll miss the opportunity to reduce the correct size
325	 * of the allocated memory for the string after that.
326	 */
327	s = NULL;
328    }
329  bail1:
330    if (orig)
331	FcStrFree (orig);
332  bail0:
333    if (s)
334	free (s);
335  bail:
336    if (FcDebug () & FC_DBG_LANGSET)
337    {
338	if (result)
339	    printf ("normalized: %s -> %s\n", lang, result);
340	else
341	    printf ("Unable to normalize %s\n", lang);
342    }
343
344    return result;
345}
346
347#define FcLangEnd(c)	((c) == '-' || (c) == '\0')
348
349FcLangResult
350FcLangCompare (const FcChar8 *s1, const FcChar8 *s2)
351{
352    FcChar8	    c1, c2;
353    FcLangResult    result = FcLangDifferentLang;
354
355    for (;;)
356    {
357	c1 = *s1++;
358	c2 = *s2++;
359
360	c1 = FcToLower (c1);
361	c2 = FcToLower (c2);
362	if (c1 != c2)
363	{
364	    if (FcLangEnd (c1) && FcLangEnd (c2))
365		result = FcLangDifferentTerritory;
366	    return result;
367	}
368	else if (!c1)
369	    return FcLangEqual;
370	else if (c1 == '-')
371	    result = FcLangDifferentTerritory;
372    }
373}
374
375/*
376 * Return FcTrue when super contains sub.
377 *
378 * super contains sub if super and sub have the same
379 * language and either the same country or one
380 * is missing the country
381 */
382
383static FcBool
384FcLangContains (const FcChar8 *super, const FcChar8 *sub)
385{
386    FcChar8	    c1, c2;
387
388    for (;;)
389    {
390	c1 = *super++;
391	c2 = *sub++;
392
393	c1 = FcToLower (c1);
394	c2 = FcToLower (c2);
395	if (c1 != c2)
396	{
397	    /* see if super has a country while sub is mising one */
398	    if (c1 == '-' && c2 == '\0')
399		return FcTrue;
400	    /* see if sub has a country while super is mising one */
401	    if (c1 == '\0' && c2 == '-')
402		return FcTrue;
403	    return FcFalse;
404	}
405	else if (!c1)
406	    return FcTrue;
407    }
408}
409
410const FcCharSet *
411FcLangGetCharSet (const FcChar8 *lang)
412{
413    int		i;
414    int		country = -1;
415
416    for (i = 0; i < NUM_LANG_CHAR_SET; i++)
417    {
418	switch (FcLangCompare (lang, fcLangCharSets[i].lang)) {
419	case FcLangEqual:
420	    return &fcLangCharSets[i].charset;
421	case FcLangDifferentTerritory:
422	    if (country == -1)
423		country = i;
424	case FcLangDifferentLang:
425	default:
426	    break;
427	}
428    }
429    if (country == -1)
430	return 0;
431    return &fcLangCharSets[country].charset;
432}
433
434FcStrSet *
435FcGetLangs (void)
436{
437    FcStrSet *langs;
438    int	i;
439
440    langs = FcStrSetCreate();
441    if (!langs)
442	return 0;
443
444    for (i = 0; i < NUM_LANG_CHAR_SET; i++)
445	FcStrSetAdd (langs, fcLangCharSets[i].lang);
446
447    return langs;
448}
449
450FcLangSet *
451FcLangSetCreate (void)
452{
453    FcLangSet	*ls;
454
455    ls = malloc (sizeof (FcLangSet));
456    if (!ls)
457	return 0;
458    memset (ls->map, '\0', sizeof (ls->map));
459    ls->map_size = NUM_LANG_SET_MAP;
460    ls->extra = 0;
461    return ls;
462}
463
464void
465FcLangSetDestroy (FcLangSet *ls)
466{
467    if (ls->extra)
468	FcStrSetDestroy (ls->extra);
469    free (ls);
470}
471
472FcLangSet *
473FcLangSetCopy (const FcLangSet *ls)
474{
475    FcLangSet	*new;
476
477    new = FcLangSetCreate ();
478    if (!new)
479	goto bail0;
480    memset (new->map, '\0', sizeof (new->map));
481    memcpy (new->map, ls->map, FC_MIN (sizeof (new->map), ls->map_size * sizeof (ls->map[0])));
482    if (ls->extra)
483    {
484	FcStrList	*list;
485	FcChar8		*extra;
486
487	new->extra = FcStrSetCreate ();
488	if (!new->extra)
489	    goto bail1;
490
491	list = FcStrListCreate (ls->extra);
492	if (!list)
493	    goto bail1;
494
495	while ((extra = FcStrListNext (list)))
496	    if (!FcStrSetAdd (new->extra, extra))
497	    {
498		FcStrListDone (list);
499		goto bail1;
500	    }
501	FcStrListDone (list);
502    }
503    return new;
504bail1:
505    FcLangSetDestroy (new);
506bail0:
507    return 0;
508}
509
510/* When the language isn't found, the return value r is such that:
511 *  1) r < 0
512 *  2) -r -1 is the index of the first language in fcLangCharSets that comes
513 *     after the 'lang' argument in lexicographic order.
514 *
515 *  The -1 is necessary to avoid problems with language id 0 (otherwise, we
516 *  wouldn't be able to distinguish between “language found, id is 0” and
517 *  “language not found, sorts right before the language with id 0”).
518 */
519static int
520FcLangSetIndex (const FcChar8 *lang)
521{
522    int	    low, high, mid = 0;
523    int	    cmp = 0;
524    FcChar8 firstChar = FcToLower(lang[0]);
525    FcChar8 secondChar = firstChar ? FcToLower(lang[1]) : '\0';
526
527    if (firstChar < 'a')
528    {
529	low = 0;
530	high = fcLangCharSetRanges[0].begin;
531    }
532    else if(firstChar > 'z')
533    {
534	low = fcLangCharSetRanges[25].begin;
535	high = NUM_LANG_CHAR_SET - 1;
536    }
537    else
538    {
539	low = fcLangCharSetRanges[firstChar - 'a'].begin;
540	high = fcLangCharSetRanges[firstChar - 'a'].end;
541	/* no matches */
542	if (low > high)
543	    return -(low+1); /* one past next entry after where it would be */
544    }
545
546    while (low <= high)
547    {
548	mid = (high + low) >> 1;
549	if(fcLangCharSets[mid].lang[0] != firstChar)
550	    cmp = FcStrCmpIgnoreCase(fcLangCharSets[mid].lang, lang);
551	else
552	{   /* fast path for resolving 2-letter languages (by far the most common) after
553	     * finding the first char (probably already true because of the hash table) */
554	    cmp = fcLangCharSets[mid].lang[1] - secondChar;
555	    if (cmp == 0 &&
556		(fcLangCharSets[mid].lang[2] != '\0' ||
557		 lang[2] != '\0'))
558	    {
559		cmp = FcStrCmpIgnoreCase(fcLangCharSets[mid].lang+2,
560					 lang+2);
561	    }
562	}
563	if (cmp == 0)
564	    return mid;
565	if (cmp < 0)
566	    low = mid + 1;
567	else
568	    high = mid - 1;
569    }
570    if (cmp < 0)
571	mid++;
572    return -(mid + 1);
573}
574
575FcBool
576FcLangSetAdd (FcLangSet *ls, const FcChar8 *lang)
577{
578    int	    id;
579
580    id = FcLangSetIndex (lang);
581    if (id >= 0)
582    {
583	FcLangSetBitSet (ls, id);
584	return FcTrue;
585    }
586    if (!ls->extra)
587    {
588	ls->extra = FcStrSetCreate ();
589	if (!ls->extra)
590	    return FcFalse;
591    }
592    return FcStrSetAdd (ls->extra, lang);
593}
594
595FcBool
596FcLangSetDel (FcLangSet *ls, const FcChar8 *lang)
597{
598    int	id;
599
600    id = FcLangSetIndex (lang);
601    if (id >= 0)
602    {
603	FcLangSetBitReset (ls, id);
604    }
605    else if (ls->extra)
606    {
607	FcStrSetDel (ls->extra, lang);
608    }
609    return FcTrue;
610}
611
612FcLangResult
613FcLangSetHasLang (const FcLangSet *ls, const FcChar8 *lang)
614{
615    int		    id;
616    FcLangResult    best, r;
617    int		    i;
618
619    id = FcLangSetIndex (lang);
620    if (id < 0)
621	id = -id - 1;
622    else if (FcLangSetBitGet (ls, id))
623	return FcLangEqual;
624    best = FcLangDifferentLang;
625    for (i = id - 1; i >= 0; i--)
626    {
627	r = FcLangCompare (lang, fcLangCharSets[i].lang);
628	if (r == FcLangDifferentLang)
629	    break;
630	if (FcLangSetBitGet (ls, i) && r < best)
631	    best = r;
632    }
633    for (i = id; i < NUM_LANG_CHAR_SET; i++)
634    {
635	r = FcLangCompare (lang, fcLangCharSets[i].lang);
636	if (r == FcLangDifferentLang)
637	    break;
638	if (FcLangSetBitGet (ls, i) && r < best)
639	    best = r;
640    }
641    if (ls->extra)
642    {
643	FcStrList	*list = FcStrListCreate (ls->extra);
644	FcChar8		*extra;
645
646	if (list)
647	{
648	    while (best > FcLangEqual && (extra = FcStrListNext (list)))
649	    {
650		r = FcLangCompare (lang, extra);
651		if (r < best)
652		    best = r;
653	    }
654	    FcStrListDone (list);
655	}
656    }
657    return best;
658}
659
660static FcLangResult
661FcLangSetCompareStrSet (const FcLangSet *ls, FcStrSet *set)
662{
663    FcStrList	    *list = FcStrListCreate (set);
664    FcLangResult    r, best = FcLangDifferentLang;
665    FcChar8	    *extra;
666
667    if (list)
668    {
669	while (best > FcLangEqual && (extra = FcStrListNext (list)))
670	{
671	    r = FcLangSetHasLang (ls, extra);
672	    if (r < best)
673		best = r;
674	}
675	FcStrListDone (list);
676    }
677    return best;
678}
679
680FcLangResult
681FcLangSetCompare (const FcLangSet *lsa, const FcLangSet *lsb)
682{
683    int		    i, j, count;
684    FcLangResult    best, r;
685    FcChar32 aInCountrySet, bInCountrySet;
686
687    count = FC_MIN (lsa->map_size, lsb->map_size);
688    count = FC_MIN (NUM_LANG_SET_MAP, count);
689    for (i = 0; i < count; i++)
690	if (lsa->map[i] & lsb->map[i])
691	    return FcLangEqual;
692    best = FcLangDifferentLang;
693    for (j = 0; j < NUM_COUNTRY_SET; j++)
694    {
695	aInCountrySet = 0;
696	bInCountrySet = 0;
697
698	for (i = 0; i < count; i++)
699	{
700	    aInCountrySet |= lsa->map[i] & fcLangCountrySets[j][i];
701	    bInCountrySet |= lsb->map[i] & fcLangCountrySets[j][i];
702
703	    if (aInCountrySet && bInCountrySet)
704	    {
705		best = FcLangDifferentTerritory;
706		break;
707	    }
708	}
709    }
710    if (lsa->extra)
711    {
712	r = FcLangSetCompareStrSet (lsb, lsa->extra);
713	if (r < best)
714	    best = r;
715    }
716    if (best > FcLangEqual && lsb->extra)
717    {
718	r = FcLangSetCompareStrSet (lsa, lsb->extra);
719	if (r < best)
720	    best = r;
721    }
722    return best;
723}
724
725/*
726 * Used in computing values -- mustn't allocate any storage
727 */
728FcLangSet *
729FcLangSetPromote (const FcChar8 *lang, FcValuePromotionBuffer *vbuf)
730{
731    int		id;
732    typedef struct {
733	FcLangSet  ls;
734	FcStrSet   strs;
735	FcChar8   *str;
736    } FcLangSetPromotionBuffer;
737    FcLangSetPromotionBuffer *buf = (FcLangSetPromotionBuffer *) vbuf;
738
739    FC_ASSERT_STATIC (sizeof (FcLangSetPromotionBuffer) <= sizeof (FcValuePromotionBuffer));
740
741    memset (buf->ls.map, '\0', sizeof (buf->ls.map));
742    buf->ls.map_size = NUM_LANG_SET_MAP;
743    buf->ls.extra = 0;
744    if (lang)
745    {
746	id = FcLangSetIndex (lang);
747	if (id >= 0)
748	{
749	    FcLangSetBitSet (&buf->ls, id);
750	}
751	else
752	{
753	    buf->ls.extra = &buf->strs;
754	    buf->strs.num = 1;
755	    buf->strs.size = 1;
756	    buf->strs.strs = &buf->str;
757	    FcRefInit (&buf->strs.ref, 1);
758	    buf->str = (FcChar8 *) lang;
759	}
760    }
761    return &buf->ls;
762}
763
764FcChar32
765FcLangSetHash (const FcLangSet *ls)
766{
767    FcChar32	h = 0;
768    int		i, count;
769
770    count = FC_MIN (ls->map_size, NUM_LANG_SET_MAP);
771    for (i = 0; i < count; i++)
772	h ^= ls->map[i];
773    if (ls->extra)
774	h ^= ls->extra->num;
775    return h;
776}
777
778FcLangSet *
779FcNameParseLangSet (const FcChar8 *string)
780{
781    FcChar8	    lang[32], c = 0;
782    int i;
783    FcLangSet	    *ls;
784
785    ls = FcLangSetCreate ();
786    if (!ls)
787	goto bail0;
788
789    for(;;)
790    {
791	for(i = 0; i < 31;i++)
792	{
793	    c = *string++;
794	    if(c == '\0' || c == '|')
795		break; /* end of this code */
796	    lang[i] = c;
797	}
798	lang[i] = '\0';
799	if (!FcLangSetAdd (ls, lang))
800	    goto bail1;
801	if(c == '\0')
802	    break;
803    }
804    return ls;
805bail1:
806    FcLangSetDestroy (ls);
807bail0:
808    return 0;
809}
810
811FcBool
812FcNameUnparseLangSet (FcStrBuf *buf, const FcLangSet *ls)
813{
814    int		i, bit, count;
815    FcChar32	bits;
816    FcBool	first = FcTrue;
817
818    count = FC_MIN (ls->map_size, NUM_LANG_SET_MAP);
819    for (i = 0; i < count; i++)
820    {
821	if ((bits = ls->map[i]))
822	{
823	    for (bit = 0; bit <= 31; bit++)
824		if (bits & (1 << bit))
825		{
826		    int id = (i << 5) | bit;
827		    if (!first)
828			if (!FcStrBufChar (buf, '|'))
829			    return FcFalse;
830		    if (!FcStrBufString (buf, fcLangCharSets[fcLangCharSetIndicesInv[id]].lang))
831			return FcFalse;
832		    first = FcFalse;
833		}
834	}
835    }
836    if (ls->extra)
837    {
838	FcStrList   *list = FcStrListCreate (ls->extra);
839	FcChar8	    *extra;
840
841	if (!list)
842	    return FcFalse;
843	while ((extra = FcStrListNext (list)))
844	{
845	    if (!first)
846		if (!FcStrBufChar (buf, '|'))
847                {
848                    FcStrListDone (list);
849		    return FcFalse;
850                }
851	    if (!FcStrBufString (buf, extra))
852                {
853                    FcStrListDone (list);
854                    return FcFalse;
855                }
856	    first = FcFalse;
857	}
858        FcStrListDone (list);
859    }
860    return FcTrue;
861}
862
863FcBool
864FcLangSetEqual (const FcLangSet *lsa, const FcLangSet *lsb)
865{
866    int	    i, count;
867
868    count = FC_MIN (lsa->map_size, lsb->map_size);
869    count = FC_MIN (NUM_LANG_SET_MAP, count);
870    for (i = 0; i < count; i++)
871    {
872	if (lsa->map[i] != lsb->map[i])
873	    return FcFalse;
874    }
875    if (!lsa->extra && !lsb->extra)
876	return FcTrue;
877    if (lsa->extra && lsb->extra)
878	return FcStrSetEqual (lsa->extra, lsb->extra);
879    return FcFalse;
880}
881
882static FcBool
883FcLangSetContainsLang (const FcLangSet *ls, const FcChar8 *lang)
884{
885    int		    id;
886    int		    i;
887
888    id = FcLangSetIndex (lang);
889    if (id < 0)
890	id = -id - 1;
891    else if (FcLangSetBitGet (ls, id))
892	return FcTrue;
893    /*
894     * search up and down among equal languages for a match
895     */
896    for (i = id - 1; i >= 0; i--)
897    {
898	if (FcLangCompare (fcLangCharSets[i].lang, lang) == FcLangDifferentLang)
899	    break;
900	if (FcLangSetBitGet (ls, i) &&
901	    FcLangContains (fcLangCharSets[i].lang, lang))
902	    return FcTrue;
903    }
904    for (i = id; i < NUM_LANG_CHAR_SET; i++)
905    {
906	if (FcLangCompare (fcLangCharSets[i].lang, lang) == FcLangDifferentLang)
907	    break;
908	if (FcLangSetBitGet (ls, i) &&
909	    FcLangContains (fcLangCharSets[i].lang, lang))
910	    return FcTrue;
911    }
912    if (ls->extra)
913    {
914	FcStrList	*list = FcStrListCreate (ls->extra);
915	FcChar8		*extra;
916
917	if (list)
918	{
919	    while ((extra = FcStrListNext (list)))
920	    {
921		if (FcLangContains (extra, lang))
922		    break;
923	    }
924	    FcStrListDone (list);
925    	    if (extra)
926		return FcTrue;
927	}
928    }
929    return FcFalse;
930}
931
932/*
933 * return FcTrue if lsa contains every language in lsb
934 */
935FcBool
936FcLangSetContains (const FcLangSet *lsa, const FcLangSet *lsb)
937{
938    int		    i, j, count;
939    FcChar32	    missing;
940
941    if (FcDebug() & FC_DBG_MATCHV)
942    {
943	printf ("FcLangSet "); FcLangSetPrint (lsa);
944	printf (" contains "); FcLangSetPrint (lsb);
945	printf ("\n");
946    }
947    /*
948     * check bitmaps for missing language support
949     */
950    count = FC_MIN (lsa->map_size, lsb->map_size);
951    count = FC_MIN (NUM_LANG_SET_MAP, count);
952    for (i = 0; i < count; i++)
953    {
954	missing = lsb->map[i] & ~lsa->map[i];
955	if (missing)
956	{
957	    for (j = 0; j < 32; j++)
958		if (missing & (1 << j))
959		{
960		    if (!FcLangSetContainsLang (lsa,
961						fcLangCharSets[fcLangCharSetIndicesInv[i*32 + j]].lang))
962		    {
963			if (FcDebug() & FC_DBG_MATCHV)
964			    printf ("\tMissing bitmap %s\n", fcLangCharSets[fcLangCharSetIndicesInv[i*32+j]].lang);
965			return FcFalse;
966		    }
967		}
968	}
969    }
970    if (lsb->extra)
971    {
972	FcStrList   *list = FcStrListCreate (lsb->extra);
973	FcChar8	    *extra;
974
975	if (list)
976	{
977	    while ((extra = FcStrListNext (list)))
978	    {
979		if (!FcLangSetContainsLang (lsa, extra))
980		{
981		    if (FcDebug() & FC_DBG_MATCHV)
982			printf ("\tMissing string %s\n", extra);
983		    break;
984		}
985	    }
986	    FcStrListDone (list);
987	    if (extra)
988		return FcFalse;
989	}
990    }
991    return FcTrue;
992}
993
994FcBool
995FcLangSetSerializeAlloc (FcSerialize *serialize, const FcLangSet *l)
996{
997    if (!FcSerializeAlloc (serialize, l, sizeof (FcLangSet)))
998	return FcFalse;
999    return FcTrue;
1000}
1001
1002FcLangSet *
1003FcLangSetSerialize(FcSerialize *serialize, const FcLangSet *l)
1004{
1005    FcLangSet	*l_serialize = FcSerializePtr (serialize, l);
1006
1007    if (!l_serialize)
1008	return NULL;
1009    memset (l_serialize->map, '\0', sizeof (l_serialize->map));
1010    memcpy (l_serialize->map, l->map, FC_MIN (sizeof (l_serialize->map), l->map_size * sizeof (l->map[0])));
1011    l_serialize->map_size = NUM_LANG_SET_MAP;
1012    l_serialize->extra = NULL; /* We don't serialize ls->extra */
1013    return l_serialize;
1014}
1015
1016FcStrSet *
1017FcLangSetGetLangs (const FcLangSet *ls)
1018{
1019    FcStrSet *langs;
1020    int	      i;
1021
1022    langs = FcStrSetCreate();
1023    if (!langs)
1024	return 0;
1025
1026    for (i = 0; i < NUM_LANG_CHAR_SET; i++)
1027	if (FcLangSetBitGet (ls, i))
1028	    FcStrSetAdd (langs, fcLangCharSets[i].lang);
1029
1030    if (ls->extra)
1031    {
1032	FcStrList	*list = FcStrListCreate (ls->extra);
1033	FcChar8		*extra;
1034
1035	if (list)
1036	{
1037	    while ((extra = FcStrListNext (list)))
1038		FcStrSetAdd (langs, extra);
1039
1040	    FcStrListDone (list);
1041	}
1042    }
1043
1044    return langs;
1045}
1046
1047static FcLangSet *
1048FcLangSetOperate(const FcLangSet	*a,
1049		 const FcLangSet	*b,
1050		 FcBool			(*func) (FcLangSet 	*ls,
1051						 const FcChar8	*s))
1052{
1053    FcLangSet	*langset = FcLangSetCopy (a);
1054    FcStrSet	*set = FcLangSetGetLangs (b);
1055    FcStrList	*sl = FcStrListCreate (set);
1056    FcChar8	*str;
1057
1058    FcStrSetDestroy (set);
1059    while ((str = FcStrListNext (sl)))
1060    {
1061	func (langset, str);
1062    }
1063    FcStrListDone (sl);
1064
1065    return langset;
1066}
1067
1068FcLangSet *
1069FcLangSetUnion (const FcLangSet *a, const FcLangSet *b)
1070{
1071    return FcLangSetOperate(a, b, FcLangSetAdd);
1072}
1073
1074FcLangSet *
1075FcLangSetSubtract (const FcLangSet *a, const FcLangSet *b)
1076{
1077    return FcLangSetOperate(a, b, FcLangSetDel);
1078}
1079
1080#define __fclang__
1081#include "fcaliastail.h"
1082#include "fcftaliastail.h"
1083#undef __fclang__
1084