]> git.wh0rd.org - fontconfig.git/blob - src/fclang.c
633. Perform country-independent matching for Chinese languages in
[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 }