]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * fontconfig/fc-lang/fc-lang.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 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 | * 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 "fccharset.c" | |
26 | #include "fcstr.c" | |
27 | #include "fcserialize.c" | |
28 | ||
29 | /* | |
30 | * fc-lang | |
31 | * | |
32 | * Read a set of language orthographies and build C declarations for | |
33 | * charsets which can then be used to identify which languages are | |
34 | * supported by a given font. Note that this uses some utilities | |
35 | * from the fontconfig library, so the necessary file is simply | |
36 | * included in this compilation. A couple of extra utility | |
37 | * functions are also needed in slightly modified form | |
38 | */ | |
39 | ||
40 | void | |
41 | FcMemAlloc (int kind, int size) | |
42 | { | |
43 | } | |
44 | ||
45 | void | |
46 | FcMemFree (int kind, int size) | |
47 | { | |
48 | } | |
49 | ||
50 | FcPrivate void | |
51 | FcCacheObjectReference (void *object) | |
52 | { | |
53 | } | |
54 | ||
55 | FcPrivate void | |
56 | FcCacheObjectDereference (void *object) | |
57 | { | |
58 | } | |
59 | ||
60 | int FcDebugVal; | |
61 | ||
62 | FcChar8 * | |
63 | FcConfigHome (void) | |
64 | { | |
65 | return (FcChar8 *) getenv ("HOME"); | |
66 | } | |
67 | ||
68 | static void | |
69 | fatal (const char *file, int lineno, const char *msg) | |
70 | { | |
71 | if (lineno) | |
72 | fprintf (stderr, "%s:%d: %s\n", file, lineno, msg); | |
73 | else | |
74 | fprintf (stderr, "%s: %s\n", file, msg); | |
75 | exit (1); | |
76 | } | |
77 | ||
78 | static char * | |
79 | get_line (FILE *f, char *line, int *lineno) | |
80 | { | |
81 | char *hash; | |
82 | int end; | |
83 | if (!fgets (line, 1024, f)) | |
84 | return 0; | |
85 | ++(*lineno); | |
86 | hash = strchr (line, '#'); | |
87 | if (hash) | |
88 | *hash = '\0'; | |
89 | ||
90 | end = strlen (line); | |
91 | while (end > 0 && isspace (line[end-1])) | |
92 | line[--end] = '\0'; | |
93 | ||
94 | if (line[0] == '\0' || line[0] == '\n' || line[0] == '\032' || line[0] == '\r') | |
95 | return get_line (f, line, lineno); | |
96 | return line; | |
97 | } | |
98 | ||
99 | static char *dir = 0; | |
100 | ||
101 | static FILE * | |
102 | scanopen (char *file) | |
103 | { | |
104 | FILE *f; | |
105 | ||
106 | f = fopen (file, "r"); | |
107 | if (!f && dir) | |
108 | { | |
109 | char path[1024]; | |
110 | ||
111 | strcpy (path, dir); | |
112 | strcat (path, "/"); | |
113 | strcat (path, file); | |
114 | f = fopen (path, "r"); | |
115 | } | |
116 | return f; | |
117 | } | |
118 | ||
119 | /* | |
120 | * build a single charset from a source file | |
121 | * | |
122 | * The file format is quite simple, either | |
123 | * a single hex value or a pair separated with a dash | |
124 | * | |
125 | * Comments begin with '#' | |
126 | */ | |
127 | ||
128 | static const FcCharSet * | |
129 | scan (FILE *f, char *file, FcCharSetFreezer *freezer) | |
130 | { | |
131 | FcCharSet *c = 0; | |
132 | FcCharSet *n; | |
133 | int start, end, ucs4; | |
134 | char line[1024]; | |
135 | int lineno = 0; | |
136 | ||
137 | while (get_line (f, line, &lineno)) | |
138 | { | |
139 | if (!strncmp (line, "include", 7)) | |
140 | { | |
141 | FILE *included_f; | |
142 | char *included_file; | |
143 | included_file = strchr (line, ' '); | |
144 | if (!included_file) | |
145 | fatal (file, lineno, | |
146 | "invalid syntax, expected: include filename"); | |
147 | while (isspace(*included_file)) | |
148 | included_file++; | |
149 | included_f = scanopen (included_file); | |
150 | if (!included_f) | |
151 | fatal (included_file, 0, "can't open"); | |
152 | n = scan (included_f, included_file, freezer); | |
153 | fclose (included_f); | |
154 | if (!c) | |
155 | c = FcCharSetCreate (); | |
156 | if (!FcCharSetMerge (c, n, NULL)) | |
157 | fatal (file, lineno, "out of memory"); | |
158 | FcCharSetDestroy (n); | |
159 | continue; | |
160 | } | |
161 | if (strchr (line, '-')) | |
162 | { | |
163 | if (sscanf (line, "%x-%x", &start, &end) != 2) | |
164 | fatal (file, lineno, "parse error"); | |
165 | } | |
166 | else | |
167 | { | |
168 | if (sscanf (line, "%x", &start) != 1) | |
169 | fatal (file, lineno, "parse error"); | |
170 | end = start; | |
171 | } | |
172 | if (!c) | |
173 | c = FcCharSetCreate (); | |
174 | for (ucs4 = start; ucs4 <= end; ucs4++) | |
175 | { | |
176 | if (!FcCharSetAddChar (c, ucs4)) | |
177 | fatal (file, lineno, "out of memory"); | |
178 | } | |
179 | } | |
180 | n = FcCharSetFreeze (freezer, c); | |
181 | FcCharSetDestroy (c); | |
182 | return n; | |
183 | } | |
184 | ||
185 | /* | |
186 | * Convert a file name into a name suitable for C declarations | |
187 | */ | |
188 | static char * | |
189 | get_name (char *file) | |
190 | { | |
191 | char *name; | |
192 | char *dot; | |
193 | ||
194 | dot = strchr (file, '.'); | |
195 | if (!dot) | |
196 | dot = file + strlen(file); | |
197 | name = malloc (dot - file + 1); | |
198 | strncpy (name, file, dot - file); | |
199 | name[dot-file] = '\0'; | |
200 | return name; | |
201 | } | |
202 | ||
203 | /* | |
204 | * Convert a C name into a language name | |
205 | */ | |
206 | static char * | |
207 | get_lang (char *name) | |
208 | { | |
209 | char *lang = malloc (strlen (name) + 1); | |
210 | char *l = lang; | |
211 | char c; | |
212 | ||
213 | while ((c = *name++)) | |
214 | { | |
215 | if (isupper ((int) (unsigned char) c)) | |
216 | c = tolower ((int) (unsigned char) c); | |
217 | if (c == '_') | |
218 | c = '-'; | |
219 | if (c == ' ') | |
220 | continue; | |
221 | *l++ = c; | |
222 | } | |
223 | *l++ = '\0'; | |
224 | return lang; | |
225 | } | |
226 | ||
227 | typedef struct _Entry { | |
228 | int id; | |
229 | char *file; | |
230 | } Entry; | |
231 | ||
232 | static int compare (const void *a, const void *b) | |
233 | { | |
234 | const Entry const *as = a, *bs = b; | |
235 | return FcStrCmpIgnoreCase (as->file, bs->file); | |
236 | } | |
237 | ||
238 | #define MAX_LANG 1024 | |
239 | #define MAX_LANG_SET_MAP ((MAX_LANG + 31) / 32) | |
240 | ||
241 | #define BitSet(map, i) ((map)[(entries[i].id)>>5] |= ((FcChar32) 1 << ((entries[i].id) & 0x1f))) | |
242 | #define BitGet(map, i) ((map)[(entries[i].id)>>5] >> ((entries[i].id) & 0x1f)) & 1) | |
243 | ||
244 | int | |
245 | main (int argc, char **argv) | |
246 | { | |
247 | static Entry entries[MAX_LANG]; | |
248 | static const FcCharSet *sets[MAX_LANG]; | |
249 | static int duplicate[MAX_LANG]; | |
250 | static int country[MAX_LANG]; | |
251 | static char *names[MAX_LANG]; | |
252 | static char *langs[MAX_LANG]; | |
253 | static int off[MAX_LANG]; | |
254 | FILE *f; | |
255 | int ncountry = 0; | |
256 | int i = 0; | |
257 | int nsets = 0; | |
258 | int argi; | |
259 | FcCharLeaf **leaves; | |
260 | int total_leaves = 0; | |
261 | int l, sl, tl, tn; | |
262 | static char line[1024]; | |
263 | static FcChar32 map[MAX_LANG_SET_MAP]; | |
264 | int num_lang_set_map; | |
265 | int setRangeStart[26]; | |
266 | int setRangeEnd[26]; | |
267 | FcChar8 setRangeChar; | |
268 | FcCharSetFreezer *freezer; | |
269 | ||
270 | freezer = FcCharSetFreezerCreate (); | |
271 | if (!freezer) | |
272 | fatal (argv[0], 0, "out of memory"); | |
273 | argi = 1; | |
274 | while (argv[argi]) | |
275 | { | |
276 | if (!strcmp (argv[argi], "-d")) | |
277 | { | |
278 | argi++; | |
279 | dir = argv[argi++]; | |
280 | continue; | |
281 | } | |
282 | if (i == MAX_LANG) | |
283 | fatal (argv[0], 0, "Too many languages"); | |
284 | entries[i].id = i; | |
285 | entries[i].file = argv[argi++]; | |
286 | i++; | |
287 | } | |
288 | entries[i].file = 0; | |
289 | qsort (entries, i, sizeof (Entry), compare); | |
290 | i = 0; | |
291 | while (entries[i].file) | |
292 | { | |
293 | f = scanopen (entries[i].file); | |
294 | if (!f) | |
295 | fatal (entries[i].file, 0, strerror (errno)); | |
296 | sets[i] = scan (f, entries[i].file, freezer); | |
297 | names[i] = get_name (entries[i].file); | |
298 | langs[i] = get_lang(names[i]); | |
299 | if (strchr (langs[i], '-')) | |
300 | country[ncountry++] = i; | |
301 | ||
302 | total_leaves += sets[i]->num; | |
303 | i++; | |
304 | fclose (f); | |
305 | } | |
306 | nsets = i; | |
307 | sets[i] = 0; | |
308 | leaves = malloc (total_leaves * sizeof (FcCharLeaf *)); | |
309 | tl = 0; | |
310 | /* | |
311 | * Find unique leaves | |
312 | */ | |
313 | for (i = 0; sets[i]; i++) | |
314 | { | |
315 | for (sl = 0; sl < sets[i]->num; sl++) | |
316 | { | |
317 | for (l = 0; l < tl; l++) | |
318 | if (leaves[l] == FcCharSetLeaf(sets[i], sl)) | |
319 | break; | |
320 | if (l == tl) | |
321 | leaves[tl++] = FcCharSetLeaf(sets[i], sl); | |
322 | } | |
323 | } | |
324 | ||
325 | /* | |
326 | * Scan the input until the marker is found | |
327 | */ | |
328 | ||
329 | while (fgets (line, sizeof (line), stdin)) | |
330 | { | |
331 | if (!strncmp (line, "@@@", 3)) | |
332 | break; | |
333 | fputs (line, stdout); | |
334 | } | |
335 | ||
336 | printf ("/* total size: %d unique leaves: %d */\n\n", | |
337 | total_leaves, tl); | |
338 | ||
339 | /* | |
340 | * Find duplicate charsets | |
341 | */ | |
342 | duplicate[0] = -1; | |
343 | for (i = 1; sets[i]; i++) | |
344 | { | |
345 | int j; | |
346 | ||
347 | duplicate[i] = -1; | |
348 | for (j = 0; j < i; j++) | |
349 | if (sets[j] == sets[i]) | |
350 | { | |
351 | duplicate[i] = j; | |
352 | break; | |
353 | } | |
354 | } | |
355 | ||
356 | tn = 0; | |
357 | for (i = 0; sets[i]; i++) { | |
358 | if (duplicate[i] >= 0) | |
359 | continue; | |
360 | off[i] = tn; | |
361 | tn += sets[i]->num; | |
362 | } | |
363 | ||
364 | printf ("#define LEAF0 (%d * sizeof (FcLangCharSet))\n", nsets); | |
365 | printf ("#define OFF0 (LEAF0 + %d * sizeof (FcCharLeaf))\n", tl); | |
366 | printf ("#define NUM0 (OFF0 + %d * sizeof (intptr_t))\n", tn); | |
367 | printf ("#define SET(n) (n * sizeof (FcLangCharSet) + offsetof (FcLangCharSet, charset))\n"); | |
368 | printf ("#define OFF(s,o) (OFF0 + o * sizeof (intptr_t) - SET(s))\n"); | |
369 | printf ("#define NUM(s,n) (NUM0 + n * sizeof (FcChar16) - SET(s))\n"); | |
370 | printf ("#define LEAF(o,l) (LEAF0 + l * sizeof (FcCharLeaf) - (OFF0 + o * sizeof (intptr_t)))\n"); | |
371 | printf ("#define fcLangCharSets (fcLangData.langCharSets)\n"); | |
372 | printf ("#define fcLangCharSetIndices (fcLangData.langIndices)\n"); | |
373 | printf ("\n"); | |
374 | ||
375 | printf ("static const struct {\n" | |
376 | " FcLangCharSet langCharSets[%d];\n" | |
377 | " FcCharLeaf leaves[%d];\n" | |
378 | " intptr_t leaf_offsets[%d];\n" | |
379 | " FcChar16 numbers[%d];\n" | |
380 | " FcChar%s langIndices[%d];\n" | |
381 | "} fcLangData = {\n", | |
382 | nsets, tl, tn, tn, | |
383 | nsets < 256 ? "8 " : "16", nsets); | |
384 | ||
385 | /* | |
386 | * Dump sets | |
387 | */ | |
388 | ||
389 | printf ("{\n"); | |
390 | for (i = 0; sets[i]; i++) | |
391 | { | |
392 | int j = duplicate[i]; | |
393 | ||
394 | if (j < 0) | |
395 | j = i; | |
396 | ||
397 | printf (" { \"%s\", " | |
398 | " { FC_REF_CONSTANT, %d, OFF(%d,%d), NUM(%d,%d) } }, /* %d */\n", | |
399 | langs[i], | |
400 | sets[j]->num, i, off[j], i, off[j], i); | |
401 | } | |
402 | printf ("},\n"); | |
403 | ||
404 | /* | |
405 | * Dump leaves | |
406 | */ | |
407 | printf ("{\n"); | |
408 | for (l = 0; l < tl; l++) | |
409 | { | |
410 | printf (" { { /* %d */", l); | |
411 | for (i = 0; i < 256/32; i++) | |
412 | { | |
413 | if (i % 4 == 0) | |
414 | printf ("\n "); | |
415 | printf (" 0x%08x,", leaves[l]->map[i]); | |
416 | } | |
417 | printf ("\n } },\n"); | |
418 | } | |
419 | printf ("},\n"); | |
420 | ||
421 | /* | |
422 | * Dump leaves | |
423 | */ | |
424 | printf ("{\n"); | |
425 | for (i = 0; sets[i]; i++) | |
426 | { | |
427 | int n; | |
428 | ||
429 | if (duplicate[i] >= 0) | |
430 | continue; | |
431 | printf (" /* %s */\n", names[i]); | |
432 | for (n = 0; n < sets[i]->num; n++) | |
433 | { | |
434 | if (n % 4 == 0) | |
435 | printf (" "); | |
436 | for (l = 0; l < tl; l++) | |
437 | if (leaves[l] == FcCharSetLeaf(sets[i], n)) | |
438 | break; | |
439 | if (l == tl) | |
440 | fatal (names[i], 0, "can't find leaf"); | |
441 | printf (" LEAF(%3d,%3d),", off[i], l); | |
442 | if (n % 4 == 3) | |
443 | printf ("\n"); | |
444 | } | |
445 | if (n % 4 != 0) | |
446 | printf ("\n"); | |
447 | } | |
448 | printf ("},\n"); | |
449 | ||
450 | ||
451 | printf ("{\n"); | |
452 | for (i = 0; sets[i]; i++) | |
453 | { | |
454 | int n; | |
455 | ||
456 | if (duplicate[i] >= 0) | |
457 | continue; | |
458 | printf (" /* %s */\n", names[i]); | |
459 | for (n = 0; n < sets[i]->num; n++) | |
460 | { | |
461 | if (n % 8 == 0) | |
462 | printf (" "); | |
463 | printf (" 0x%04x,", FcCharSetNumbers (sets[i])[n]); | |
464 | if (n % 8 == 7) | |
465 | printf ("\n"); | |
466 | } | |
467 | if (n % 8 != 0) | |
468 | printf ("\n"); | |
469 | } | |
470 | printf ("},\n"); | |
471 | ||
472 | printf ("{\n"); | |
473 | for (i = 0; sets[i]; i++) | |
474 | { | |
475 | printf (" %d, /* %s */\n", entries[i].id, names[i]); | |
476 | } | |
477 | printf ("}\n"); | |
478 | ||
479 | printf ("};\n\n"); | |
480 | ||
481 | printf ("#define NUM_LANG_CHAR_SET %d\n", i); | |
482 | num_lang_set_map = (i + 31) / 32; | |
483 | printf ("#define NUM_LANG_SET_MAP %d\n", num_lang_set_map); | |
484 | /* | |
485 | * Dump indices with country codes | |
486 | */ | |
487 | if (ncountry) | |
488 | { | |
489 | int c; | |
490 | int ncountry_ent = 0; | |
491 | printf ("\n"); | |
492 | printf ("static const FcChar32 fcLangCountrySets[][NUM_LANG_SET_MAP] = {\n"); | |
493 | for (c = 0; c < ncountry; c++) | |
494 | { | |
495 | i = country[c]; | |
496 | if (i >= 0) | |
497 | { | |
498 | int lang = strchr (langs[i], '-') - langs[i]; | |
499 | int d, k; | |
500 | ||
501 | for (k = 0; k < num_lang_set_map; k++) | |
502 | map[k] = 0; | |
503 | ||
504 | BitSet (map, i); | |
505 | for (d = c + 1; d < ncountry; d++) | |
506 | { | |
507 | int j = country[d]; | |
508 | if (j >= 0 && !strncmp (langs[j], langs[i], lang + 1)) | |
509 | { | |
510 | BitSet(map, j); | |
511 | country[d] = -1; | |
512 | } | |
513 | } | |
514 | printf (" {"); | |
515 | for (k = 0; k < num_lang_set_map; k++) | |
516 | printf (" 0x%08x,", map[k]); | |
517 | printf (" }, /* %*.*s */\n", | |
518 | lang, lang, langs[i]); | |
519 | ++ncountry_ent; | |
520 | } | |
521 | } | |
522 | printf ("};\n\n"); | |
523 | printf ("#define NUM_COUNTRY_SET %d\n", ncountry_ent); | |
524 | } | |
525 | ||
526 | ||
527 | /* | |
528 | * Find ranges for each letter for faster searching | |
529 | */ | |
530 | setRangeChar = 'a'; | |
531 | memset(setRangeStart, '\0', sizeof (setRangeStart)); | |
532 | memset(setRangeEnd, '\0', sizeof (setRangeEnd)); | |
533 | for (i = 0; sets[i]; i++) | |
534 | { | |
535 | char c = names[i][0]; | |
536 | ||
537 | while (setRangeChar <= c && c <= 'z') | |
538 | setRangeStart[setRangeChar++ - 'a'] = i; | |
539 | } | |
540 | for (setRangeChar = 'a'; setRangeChar < 'z'; setRangeChar++) | |
541 | setRangeEnd[setRangeChar - 'a'] = setRangeStart[setRangeChar+1-'a'] - 1; | |
542 | setRangeEnd[setRangeChar - 'a'] = i - 1; | |
543 | ||
544 | /* | |
545 | * Dump sets start/finish for the fastpath | |
546 | */ | |
547 | printf ("\n"); | |
548 | printf ("static const FcLangCharSetRange fcLangCharSetRanges[] = {\n"); | |
549 | printf ("\n"); | |
550 | for (setRangeChar = 'a'; setRangeChar <= 'z' ; setRangeChar++) | |
551 | { | |
552 | printf (" { %d, %d }, /* %c */\n", | |
553 | setRangeStart[setRangeChar - 'a'], | |
554 | setRangeEnd[setRangeChar - 'a'], setRangeChar); | |
555 | } | |
556 | printf ("};\n\n"); | |
557 | ||
558 | while (fgets (line, sizeof (line), stdin)) | |
559 | fputs (line, stdout); | |
560 | ||
561 | fflush (stdout); | |
562 | exit (ferror (stdout)); | |
563 | } |