633. Perform country-independent matching for Chinese languages in
[platform/upstream/fontconfig.git] / src / fclang.c
1 /*
2  * $XFree86: xc/lib/fontconfig/src/fclang.c,v 1.7 2002/08/26 23:34:31 keithp Exp $
3  *
4  * Copyright © 2002 Keith Packard, member of The XFree86 Project, Inc.
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 Keith Packard not be used in
11  * advertising or publicity pertaining to distribution of the software without
12  * specific, written prior permission.  Keith Packard makes 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  * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18  * EVENT SHALL KEITH PACKARD 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
27 typedef struct {
28     FcChar8     *lang;
29     FcCharSet   charset;
30 } FcLangCharSet;
31
32 #include "../fc-lang/fclang.h"
33
34 struct _FcLangSet {
35     FcChar32    map[NUM_LANG_SET_MAP];
36     FcStrSet    *extra;
37 };
38
39 #define FcLangSetBitSet(ls, id) ((ls)->map[(id)>>5] |= ((FcChar32) 1 << ((id) & 0x1f)))
40 #define FcLangSetBitGet(ls, id) (((ls)->map[(id)>>5] >> ((id) & 0x1f)) & 1)
41
42 FcLangSet *
43 FcFreeTypeLangSet (const FcCharSet  *charset, 
44                    const FcChar8    *exclusiveLang)
45 {
46     int             i;
47     FcChar32        missing;
48     const FcCharSet *exclusiveCharset = 0;
49     FcLangSet       *ls;
50     
51
52     if (exclusiveLang)
53         exclusiveCharset = FcCharSetForLang (exclusiveLang);
54     ls = FcLangSetCreate ();
55     if (!ls)
56         return 0;
57     for (i = 0; i < NUM_LANG_CHAR_SET; i++)
58     {
59         /*
60          * Check for Han charsets to make fonts
61          * which advertise support for a single language
62          * not support other Han languages
63          */
64         if (exclusiveCharset &&
65             FcFreeTypeIsExclusiveLang (fcLangCharSets[i].lang) &&
66             fcLangCharSets[i].charset.leaves != exclusiveCharset->leaves)
67         {
68             continue;
69         }
70         missing = FcCharSetSubtractCount (&fcLangCharSets[i].charset, charset);
71         if (FcDebug() & FC_DBG_SCANV)
72         {
73             if (missing && missing < 10)
74             {
75                 FcCharSet   *missed = FcCharSetSubtract (&fcLangCharSets[i].charset, 
76                                                          charset);
77                 FcChar32    ucs4;
78                 FcChar32    map[FC_CHARSET_MAP_SIZE];
79                 FcChar32    next;
80
81                 printf ("\n%s(%d) ", fcLangCharSets[i].lang, missing);
82                 printf ("{");
83                 for (ucs4 = FcCharSetFirstPage (missed, map, &next);
84                      ucs4 != FC_CHARSET_DONE;
85                      ucs4 = FcCharSetNextPage (missed, map, &next))
86                 {
87                     int     i, j;
88                     for (i = 0; i < FC_CHARSET_MAP_SIZE; i++)
89                         if (map[i])
90                         {
91                             for (j = 0; j < 32; j++)
92                                 if (map[i] & (1 << j))
93                                     printf (" %04x", ucs4 + i * 32 + j);
94                         }
95                 }
96                 printf (" }\n\t");
97                 FcCharSetDestroy (missed);
98             }
99             else
100                 printf ("%s(%d) ", fcLangCharSets[i].lang, missing);
101         }
102         if (!missing)
103             FcLangSetBitSet (ls, i);
104     }
105
106     if (FcDebug() & FC_DBG_SCANV)
107         printf ("\n");
108     
109     
110     return ls;
111 }
112
113 #define FcLangEnd(c)    ((c) == '-' || (c) == '\0')
114
115 FcLangResult
116 FcLangCompare (const FcChar8 *s1, const FcChar8 *s2)
117 {
118     FcChar8         c1, c2;
119     FcLangResult    result = FcLangDifferentLang;
120
121     for (;;)
122     {
123         c1 = *s1++;
124         c2 = *s2++;
125         
126         c1 = FcToLower (c1);
127         c2 = FcToLower (c2);
128         if (c1 != c2)
129         {
130             if (FcLangEnd (c1) && FcLangEnd (c2))
131                 result = FcLangDifferentCountry;
132             return result;
133         }
134         else if (!c1)
135             return FcLangEqual;
136         else if (c1 == '-')
137             result = FcLangDifferentCountry;
138     }
139 }
140
141 const FcCharSet *
142 FcCharSetForLang (const FcChar8 *lang)
143 {
144     int         i;
145     int         country = -1;
146     for (i = 0; i < NUM_LANG_CHAR_SET; i++)
147     {
148         switch (FcLangCompare (lang, fcLangCharSets[i].lang)) {
149         case FcLangEqual:
150             return &fcLangCharSets[i].charset;
151         case FcLangDifferentCountry:
152             if (country == -1)
153                 country = i;
154         default:
155             break;
156         }
157     }
158     if (country == -1)
159         return 0;
160     return &fcLangCharSets[i].charset;
161 }
162
163 FcLangSet *
164 FcLangSetCreate (void)
165 {
166     FcLangSet   *ls;
167
168     ls = malloc (sizeof (FcLangSet));
169     if (!ls)
170         return 0;
171     FcMemAlloc (FC_MEM_LANGSET, sizeof (FcLangSet));
172     memset (ls->map, '\0', sizeof (ls->map));
173     ls->extra = 0;
174     return ls;
175 }
176
177 void
178 FcLangSetDestroy (FcLangSet *ls)
179 {
180     if (ls->extra)
181         FcStrSetDestroy (ls->extra);
182     FcMemFree (FC_MEM_LANGSET, sizeof (FcLangSet));
183     free (ls);
184 }
185
186 FcLangSet *
187 FcLangSetCopy (const FcLangSet *ls)
188 {
189     FcLangSet   *new;
190
191     new = FcLangSetCreate ();
192     if (!new)
193         goto bail0;
194     memcpy (new->map, ls->map, sizeof (new->map));
195     if (ls->extra)
196     {
197         FcStrList       *list;
198         FcChar8         *extra;
199         
200         new->extra = FcStrSetCreate ();
201         if (!new->extra)
202             goto bail1;
203
204         list = FcStrListCreate (ls->extra);     
205         if (!list)
206             goto bail1;
207         
208         while ((extra = FcStrListNext (list)))
209             if (!FcStrSetAdd (new->extra, extra))
210             {
211                 FcStrListDone (list);
212                 goto bail1;
213             }
214         FcStrListDone (list);
215     }
216     return new;
217 bail1:
218     FcLangSetDestroy (new);
219 bail0:
220     return 0;
221 }
222
223 static int
224 FcLangSetIndex (const FcChar8 *lang)
225 {
226     int     low, high, mid;
227     int     cmp;
228
229     low = 0;
230     high = NUM_LANG_CHAR_SET - 1;
231     while (low <= high)
232     {
233         mid = (high + low) >> 1;
234         cmp = FcStrCmpIgnoreCase (fcLangCharSets[mid].lang, lang);
235         if (cmp == 0) 
236             return mid;
237         if (cmp < 0)
238             low = mid + 1;
239         else
240             high = mid - 1;
241     }
242     if (cmp < 0)
243         mid++;
244     return -(mid + 1);
245 }
246
247 FcBool
248 FcLangSetAdd (FcLangSet *ls, const FcChar8 *lang)
249 {
250     int     id;
251
252     id = FcLangSetIndex (lang);
253     if (id >= 0)
254     {
255         FcLangSetBitSet (ls, id);
256         return FcTrue;
257     }
258     if (!ls->extra)
259     {
260         ls->extra = FcStrSetCreate ();
261         if (!ls->extra)
262             return FcFalse;
263     }
264     return FcStrSetAdd (ls->extra, lang);
265 }
266
267 FcLangResult
268 FcLangSetHasLang (const FcLangSet *ls, const FcChar8 *lang)
269 {
270     int             id;
271     FcLangResult    best, r;
272     int             i;
273
274     id = FcLangSetIndex (lang);
275     if (id < 0)
276         id = -id - 1;
277     else if (FcLangSetBitGet (ls, id))
278         return FcLangEqual;
279     best = FcLangDifferentLang;
280     for (i = id - 1; i >= 0; i--)
281     {
282         r = FcLangCompare (lang, fcLangCharSets[i].lang);
283         if (r == FcLangDifferentLang)
284             break;
285         if (FcLangSetBitGet (ls, i) && r < best)
286             best = r;
287     }
288     for (i = id; i < NUM_LANG_CHAR_SET; i++)
289     {
290         r = FcLangCompare (lang, fcLangCharSets[i].lang);
291         if (r == FcLangDifferentLang)
292             break;
293         if (FcLangSetBitGet (ls, i) && r < best)
294             best = r;
295     }
296     if (ls->extra)
297     {
298         FcStrList       *list = FcStrListCreate (ls->extra);
299         FcChar8         *extra;
300         FcLangResult    r;
301         
302         if (list)
303         {
304             while (best > FcLangEqual && (extra = FcStrListNext (list)))
305             {
306                 r = FcLangCompare (lang, extra);
307                 if (r < best)
308                     best = r;
309             }
310             FcStrListDone (list);
311         }
312     }
313     return best;
314 }
315
316 static FcLangResult
317 FcLangSetCompareStrSet (const FcLangSet *ls, FcStrSet *set)
318 {
319     FcStrList       *list = FcStrListCreate (set);
320     FcLangResult    r, best = FcLangDifferentLang;
321     FcChar8         *extra;
322
323     if (list)
324     {
325         while (best > FcLangEqual && (extra = FcStrListNext (list)))
326         {
327             r = FcLangSetHasLang (ls, extra);
328             if (r < best)
329                 best = r;
330         }
331         FcStrListDone (list);
332     }
333     return best;
334 }
335
336 FcLangResult
337 FcLangSetCompare (const FcLangSet *lsa, const FcLangSet *lsb)
338 {
339     int             i, j;
340     FcLangResult    best, r;
341
342     for (i = 0; i < NUM_LANG_SET_MAP; i++)
343         if (lsa->map[i] & lsb->map[i])
344             return FcLangEqual;
345     best = FcLangDifferentLang;
346     for (j = 0; j < NUM_COUNTRY_SET; j++)
347         for (i = 0; i < NUM_LANG_SET_MAP; i++)
348             if ((lsa->map[i] & fcLangCountrySets[j][i]) &&
349                 (lsb->map[i] & fcLangCountrySets[j][i]))
350             {
351                 best = FcLangDifferentCountry;
352                 break;
353             }
354     if (lsa->extra)
355     {
356         r = FcLangSetCompareStrSet (lsb, lsa->extra);
357         if (r < best)
358             best = r;
359     }
360     if (best > FcLangEqual && lsb->extra)
361     {
362         r = FcLangSetCompareStrSet (lsa, lsb->extra);
363         if (r < best)
364             best = r;
365     }
366     return best;
367 }
368
369 /*
370  * Used in computing values -- mustn't allocate any storage
371  */
372 FcLangSet *
373 FcLangSetPromote (const FcChar8 *lang)
374 {
375     static FcLangSet    ls;
376     static FcStrSet     strs;
377     static FcChar8      *str;
378     int                 id;
379
380     memset (ls.map, '\0', sizeof (ls.map));
381     ls.extra = 0;
382     id = FcLangSetIndex (lang);
383     if (id > 0)
384     {
385         FcLangSetBitSet (&ls, id);
386     }
387     else
388     {
389         ls.extra = &strs;
390         strs.num = 1;
391         strs.size = 1;
392         strs.strs = &str;
393         strs.ref = 1;
394         str = (FcChar8 *) lang;
395     }
396     return &ls;
397 }
398
399 FcChar32
400 FcLangSetHash (const FcLangSet *ls)
401 {
402     FcChar32    h = 0;
403     int         i;
404
405     for (i = 0; i < NUM_LANG_SET_MAP; i++)
406         h ^= ls->map[i];
407     if (ls->extra)
408         h ^= ls->extra->num;
409     return h;
410 }
411
412 FcLangSet *
413 FcNameParseLangSet (const FcChar8 *string)
414 {
415     FcChar8         lang[32];
416     const FcChar8   *end, *next;
417     FcLangSet       *ls;
418
419     ls = FcLangSetCreate ();
420     if (!ls)
421         goto bail0;
422
423     while (string && *string) 
424     {
425         end = (FcChar8 *) strchr ((char *) string, '|');
426         if (!end)
427         {
428             end = string + strlen ((char *) string);
429             next = end;
430         }
431         else
432             next = end + 1;
433         if (end - string < sizeof (lang) - 1)
434         {
435             strncpy ((char *) lang, (char *) string, end - string);
436             lang[end-string] = '\0';
437             if (!FcLangSetAdd (ls, lang))
438                 goto bail1;
439         }
440         string = next;
441     }
442     return ls;
443 bail1:
444     FcLangSetDestroy (ls);
445 bail0:
446     return 0;
447 }
448
449 FcBool
450 FcNameUnparseLangSet (FcStrBuf *buf, const FcLangSet *ls)
451 {
452     int         i, bit;
453     FcChar32    bits;
454     FcBool      first = FcTrue;
455
456     for (i = 0; i < NUM_LANG_SET_MAP; i++)
457     {
458         if ((bits = ls->map[i]))
459         {
460             for (bit = 0; bit <= 31; bit++)
461                 if (bits & (1 << bit))
462                 {
463                     int id = (i << 5) | bit;
464                     if (!first)
465                         if (!FcStrBufChar (buf, '|'))
466                             return FcFalse;
467                     if (!FcStrBufString (buf, fcLangCharSets[id].lang))
468                         return FcFalse;
469                     first = FcFalse;
470                 }
471         }
472     }
473     if (ls->extra)
474     {
475         FcStrList   *list = FcStrListCreate (ls->extra);
476         FcChar8     *extra;
477
478         if (!list)
479             return FcFalse;
480         while ((extra = FcStrListNext (list)))
481         {
482             if (!first)
483                 if (!FcStrBufChar (buf, '|'))
484                     return FcFalse;
485             if (!FcStrBufString (buf, extra));
486                 return FcFalse;
487             first = FcFalse;
488         }
489     }
490     return FcTrue;
491 }
492
493 FcBool
494 FcLangSetEqual (const FcLangSet *lsa, const FcLangSet *lsb)
495 {
496     int     i;
497
498     for (i = 0; i < NUM_LANG_SET_MAP; i++)
499     {
500         if (lsa->map[i] != lsb->map[i])
501             return FcFalse;
502     }
503     if (!lsa->extra && !lsb->extra)
504         return FcTrue;
505     if (lsa->extra && lsb->extra)
506         return FcStrSetEqual (lsa->extra, lsb->extra);
507     return FcFalse;
508 }