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