2 * Copyright (c) 2003 The NetBSD Foundation, Inc.
5 * This code is derived from software contributed to The NetBSD Foundation
6 * by Ben Collver <collver1@attbi.com>.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the NetBSD
19 * Foundation, Inc. and its contributors.
20 * 4. Neither the name of The NetBSD Foundation nor the names of its
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
25 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
26 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
27 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
28 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 * POSSIBILITY OF SUCH DAMAGE.
38 * This utility allows you to generate from an ISO10646-1 encoded
39 * BDF font other BDF fonts in any possible encoding. This way, you can
40 * derive from a single ISO10646-1 master font a whole set of 8-bit
41 * fonts in all ISO 8859 and various other encodings. (Hopefully
42 * a future XFree86 release will have a similar facility built into
43 * the server, which can reencode ISO10646-1 on the fly, because
44 * storing the same fonts in many different encodings is clearly
45 * a waste of storage capacity).
58 /* global variable for argv[0] */
59 const char *my_name = NULL;
61 static char *u_basename(char *pathname)
65 ptr = strrchr(pathname, '/');
66 return ((ptr == NULL) ? pathname : &ptr[1]);
69 /* "CLASS" "z" string and memory manipulation */
71 void *zmalloc(size_t size)
83 void *zrealloc(void *ptr, size_t size)
86 temp = realloc(ptr, size);
94 char *zstrdup(const char *str)
99 fprintf(stderr, "%s: zstrdup(NULL)\n", my_name);
102 retval = strdup(str);
103 if (retval == NULL) {
110 void zstrcpy(char **dest, const char *source)
114 *dest = zstrdup(source);
117 void zquotedcpy(char **dest, const char *source)
119 const char *start, *end;
127 end = strrchr(start, '"');
129 *dest = zmalloc(end-start+1);
130 strncpy(*dest, start, end-start);
131 (*dest)[end-start] = '\0';
133 *dest = zstrdup(source);
137 void zstrcat(char **dest, const char *source)
143 dest_size = strlen(*dest) + 1;
144 source_size = strlen(source);
145 *dest = zrealloc(*dest, dest_size + source_size);
146 strcpy(*dest + dest_size - 1, source);
149 void zstrtoupper(char *s)
153 for (t = s; *t != '\000'; t++)
157 char *zitoa(int value)
159 static char zitoa_buf[23];
179 #define zs_true(x) (x != NULL && strcmp(x, "0") != 0)
180 #define zi_true(x) (x == 1)
182 /* "CLASS" "dynamic array" */
192 da_t *da_new(char *name)
196 da = zmalloc(sizeof(da_t));
202 zstrcpy(&(da->name), name);
206 void *da_fetch(da_t *da, int key)
210 if (key >= 0 && key < da->size && da->values[key] != NULL)
213 if (key == -1 && da->nv != NULL)
219 int da_fetch_int(da_t *da, int key)
223 t = da_fetch(da, key);
229 #define da_fetch_str(a,k) \
230 (char *)da_fetch(a,k)
232 void da_add(da_t *da, int key, void *value)
236 if (key >= da->size) {
238 da->values = zrealloc(da->values,
239 da->size * sizeof(void *));
240 for (; i < da->size; i++)
241 da->values[i] = NULL;
243 if (da->values[key] != NULL) {
244 free(da->values[key]);
253 da->values[key] = value;
254 } else if (key == -1) {
261 void da_add_str(da_t *da, int key, char *value)
263 da_add(da, key, value?zstrdup(value):NULL);
266 void da_add_int(da_t *da, int key, int value)
270 v = zmalloc(sizeof(int));
275 #define da_count(da) (da->count)
276 #define da_size(da) (da->size)
278 void da_clear(da_t *da)
282 for (i = da->size; i; i--)
284 if (da->values != NULL)
291 /* "CLASS" file input */
293 #define TYPICAL_LINE_SIZE (80)
295 /* read a line and strip trailing whitespace */
296 int read_line(FILE *fp, char **buffer)
298 int buffer_size = TYPICAL_LINE_SIZE;
303 *buffer = zmalloc(TYPICAL_LINE_SIZE);
306 if ((c = getc(fp)) == EOF)
309 while (c != '\n' && !eof) {
310 if (position + 1 >= buffer_size) {
311 buffer_size = buffer_size * 2 + 1;
312 *buffer = zrealloc(*buffer, buffer_size);
314 (*buffer)[position++] = c;
315 (*buffer)[position] = '\0';
327 while (position > 1) {
329 if (!isspace((*buffer)[position]))
331 (*buffer)[position] = '\0';
340 DEC VT100 graphics characters in the range 1-31 (as expected by
341 some old xterm versions and a few other applications)
343 #define decmap_size 31
344 int decmap[decmap_size] = {
345 0x25C6, /* BLACK DIAMOND */
346 0x2592, /* MEDIUM SHADE */
347 0x2409, /* SYMBOL FOR HORIZONTAL TABULATION */
348 0x240C, /* SYMBOL FOR FORM FEED */
349 0x240D, /* SYMBOL FOR CARRIAGE RETURN */
350 0x240A, /* SYMBOL FOR LINE FEED */
351 0x00B0, /* DEGREE SIGN */
352 0x00B1, /* PLUS-MINUS SIGN */
353 0x2424, /* SYMBOL FOR NEWLINE */
354 0x240B, /* SYMBOL FOR VERTICAL TABULATION */
355 0x2518, /* BOX DRAWINGS LIGHT UP AND LEFT */
356 0x2510, /* BOX DRAWINGS LIGHT DOWN AND LEFT */
357 0x250C, /* BOX DRAWINGS LIGHT DOWN AND RIGHT */
358 0x2514, /* BOX DRAWINGS LIGHT UP AND RIGHT */
359 0x253C, /* BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL */
360 0x23BA, /* HORIZONTAL SCAN LINE-1 (Unicode 3.2 draft) */
361 0x23BB, /* HORIZONTAL SCAN LINE-3 (Unicode 3.2 draft) */
362 0x2500, /* BOX DRAWINGS LIGHT HORIZONTAL */
363 0x23BC, /* HORIZONTAL SCAN LINE-7 (Unicode 3.2 draft) */
364 0x23BD, /* HORIZONTAL SCAN LINE-9 (Unicode 3.2 draft) */
365 0x251C, /* BOX DRAWINGS LIGHT VERTICAL AND RIGHT */
366 0x2524, /* BOX DRAWINGS LIGHT VERTICAL AND LEFT */
367 0x2534, /* BOX DRAWINGS LIGHT UP AND HORIZONTAL */
368 0x252C, /* BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */
369 0x2502, /* BOX DRAWINGS LIGHT VERTICAL */
370 0x2264, /* LESS-THAN OR EQUAL TO */
371 0x2265, /* GREATER-THAN OR EQUAL TO */
372 0x03C0, /* GREEK SMALL LETTER PI */
373 0x2260, /* NOT EQUAL TO */
374 0x00A3, /* POUND SIGN */
375 0x00B7 /* MIDDLE DOT */
378 int is_control(int ucs)
380 return ((ucs >= 0x00 && ucs <= 0x1f) ||
381 (ucs >= 0x7f && ucs <= 0x9f));
384 int is_blockgraphics(int ucs)
386 return ucs >= 0x2500 && ucs <= 0x25FF;
389 /* calculate the bounding box that covers both provided bounding boxes */
397 bbx_t *combine_bbx(int awidth, int aheight, int axoff, int ayoff,
398 int cwidth, int cheight, int cxoff, int cyoff, bbx_t *r)
401 r->cheight = cheight;
405 if (axoff < r->cxoff) {
406 r->cwidth += r->cxoff - axoff;
409 if (ayoff < r->cyoff) {
410 r->cheight += r->cyoff - ayoff;
413 if (awidth + axoff > r->cwidth + r->cxoff) {
414 r->cwidth = awidth + axoff - r->cxoff;
416 if (aheight + ayoff > r->cheight + r->cyoff) {
417 r->cheight = aheight + ayoff - r->cyoff;
425 "Usage: ucs2any [+d|-d] <source-name> { <mapping-file> <registry-encoding> }\n"
429 " +d put DEC VT100 graphics characters in the C0 range\n"
430 " (default for upright charcell fonts)\n"
432 " -d do not put DEC VT100 graphics characters in the\n"
433 " C0 range (default for all other font types)\n"
435 " <source-name> is the name of an ISO10646-1 encoded BDF file\n"
437 " <mapping-file> is the name of a character set table like those on\n"
438 " <ftp://ftp.unicode.org/Public/MAPPINGS/>\n"
440 " <registry-encoding> are the CHARSET_REGISTRY and CHARSET_ENCODING\n"
441 " field values for the font name (XLFD) of the\n"
442 " target font, separated by a hyphen\n"
446 " ucs2any 6x13.bdf 8859-1.TXT iso8859-1 8859-2.TXT iso8859-2\n"
448 "will generate the files 6x13-iso8859-1.bdf and 6x13-iso8859-2.bdf\n"
452 int chars_compare(const void *aa, const void *bb)
461 * Return != 0 if "string" starts with "pattern" followed by whitespace.
462 * If it does, return a pointer to the first non space char.
464 static const char * startswith(const char *string, const char *pattern)
466 int l = strlen(pattern);
468 if (strlen(string) <= l) return NULL;
469 if (strncmp(string, pattern, l) != 0) return NULL;
471 if (!isspace(*string)) return NULL;
472 while (isspace(*string))
477 int main(int argc, char *argv[])
481 char *fsource = NULL;
487 const char *nextc = NULL;
488 char *startfont = NULL;
490 char *spacing = NULL;
496 char *registry = NULL;
497 char *encoding = NULL;
498 char *fontname = NULL;
503 int default_char_index = -1;
504 int startproperties_index = -1;
505 int fontname_index = -1;
506 int charset_registry_index = -1;
507 int slant_index = -1;
508 int spacing_index = -1;
509 int charset_encoding_index = -1;
510 int fontboundingbox_index = -1;
520 char *registry_encoding = NULL;
524 startchar = da_new("startchar");
525 my_char = da_new("my_char");
527 headers = da_new("headers");
535 if (strcmp(argv[ai], "+d") == 0) {
538 } else if (strcmp(argv[ai], "-d") == 0) {
547 /* open and read source file */
549 fsource_fp = fopen(fsource, "r");
550 if (fsource_fp == NULL) {
551 fprintf(stderr, "%s: Can't read file '%s': %s!\n", my_name,
552 fsource, strerror(errno));
559 while (read_line(fsource_fp, &l)) {
560 if (startswith(l, "CHARS"))
562 if (startswith(l, "STARTFONT")) {
563 zstrcpy(&startfont, l);
564 } else if (startswith(l, "_XMBDFED_INFO") ||
565 startswith(l, "XFREE86_GLYPH_RANGES"))
568 } else if ((nextc = startswith(l, "DEFAULT_CHAR")) != NULL)
570 default_char = atoi(nextc);
571 default_char_index = ++nextheader;
572 da_add_str(headers, default_char_index, NULL);
574 if ((nextc = startswith(l, "STARTPROPERTIES")) != NULL)
576 properties = atoi(nextc);
577 startproperties_index = ++nextheader;
578 da_add_str(headers, startproperties_index, NULL);
579 } else if ((nextc = startswith(l, "FONT")) != NULL)
582 /* slightly simplistic check ... */
583 zquotedcpy(&fontname, nextc);
584 if ((term = strstr(fontname, "-ISO10646-1")) == NULL) {
586 "%s: FONT name in '%s' is '%s' and not '*-ISO10646-1'!\n",
587 my_name, fsource, fontname);
591 fontname_index = ++nextheader;
592 da_add_str(headers, fontname_index, NULL);
593 } else if ((nextc = startswith(l, "CHARSET_REGISTRY")) != NULL)
595 if (strcmp(nextc, "\"ISO10646\"") != 0) {
597 "%s: CHARSET_REGISTRY in '%s' is '%s' and not 'ISO10646'!\n",
598 my_name, fsource, nextc);
601 charset_registry_index = ++nextheader;
602 da_add_str(headers, charset_registry_index, NULL);
603 } else if ((nextc = startswith(l, "CHARSET_ENCODING")) != NULL)
605 if (strcmp(nextc, "\"1\"") != 0) {
607 "%s: CHARSET_ENCODING in '%s' is '%s' and not '1'!\n",
608 my_name, fsource, nextc);
611 charset_encoding_index = ++nextheader;
612 da_add_str(headers, charset_encoding_index, NULL);
613 } else if (startswith(l, "FONTBOUNDINGBOX")) {
614 fontboundingbox_index = ++nextheader;
615 da_add_str(headers, fontboundingbox_index, NULL);
616 } else if ((nextc = startswith(l, "SLANT")) != NULL)
618 zquotedcpy(&slant, nextc);
619 slant_index = ++nextheader;
620 da_add_str(headers, slant_index, NULL);
621 } else if ((nextc = startswith(l, "SPACING")) != NULL)
623 zquotedcpy(&spacing, nextc);
624 zstrtoupper(spacing);
625 spacing_index = ++nextheader;
626 da_add_str(headers, spacing_index, NULL);
627 } else if ((nextc = startswith(l, "COMMENT")) != NULL) {
628 if (strncmp(nextc, "$Id: ", 5)==0) {
630 char *id = NULL, *end = NULL;
631 id = zstrdup(nextc + 5);
632 end = strrchr(id, '$');
633 if (end) *end = '\0';
634 zstrcpy(&header, "COMMENT Derived from ");
635 zstrcat(&header, id);
636 zstrcat(&header, "\n");
638 da_add_str(headers, ++nextheader, header);
641 da_add_str(headers, ++nextheader, l);
644 da_add_str(headers, ++nextheader, l);
650 if (startfont == NULL) {
651 fprintf(stderr, "%s: No STARTFONT line found in '%s'!\n",
656 /* read characters */
657 while (read_line(fsource_fp, &l)) {
658 if (startswith(l, "STARTCHAR")) {
662 } else if ((nextc = startswith(l, "ENCODING")) != NULL) {
664 da_add_str(startchar, code, sc);
665 da_add_str(my_char, code, "");
666 } else if (strcmp(l, "ENDFONT")==0) {
668 zstrcpy(&sc, "STARTCHAR ???\n");
670 zstrcpy(&t, da_fetch_str(my_char, code));
673 da_add_str(my_char, code, t);
674 if (strcmp(l, "ENDCHAR")==0) {
676 zstrcpy(&sc, "STARTCHAR ???\n");
686 zstrcpy(&fmap, argv[ai]);
690 char * hyphen = strchr(argv[i], '-');
691 if (!hyphen || strchr(hyphen+1, '-') != NULL) {
693 "%s: Argument registry-encoding '%s' not in expected format!\n",
694 my_name, i < argc ? fmap : "");
697 temp = zstrdup(argv[i]);
698 hyphen = strchr(temp, '-');
699 if (hyphen) *hyphen = 0;
700 zstrcpy(®istry, temp);
701 zstrcpy(&encoding, hyphen+1);
704 fprintf(stderr, "map file argument \"%s\" needs a "
705 "coresponding registry-encoding argument\n", fmap);
712 /* open and read source file */
713 fmap_fp = fopen(fmap, "r");
714 if (fmap_fp == NULL) {
716 "%s: Can't read mapping file '%s': %s!\n",
717 my_name, fmap, strerror(errno));
723 for (;read_line(fmap_fp, &l); free(l)) {
726 for (p = l; isspace(p[0]); p++)
728 if (p[0] == '\0' || p[0] == '#')
730 if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
731 target = strtol(p+2, &endp, 16);
732 if (*endp == '\0') goto bad;
736 for (; isspace(p[0]); p++)
738 if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
739 ucs = strtol(p+2, &endp, 16);
740 if (*endp == '\0') goto bad;
745 if (!is_control(ucs)) {
746 if (zs_true(da_fetch_str(startchar, ucs)))
748 da_add_int(map, target, ucs);
750 if (!((is_blockgraphics(ucs) &&
751 strcmp(slant, "R") != 0) ||
755 "No glyph for character U+%04X (0x%02x) available.\n",
762 fprintf(stderr, "Unrecognized line in '%s':\n%s\n", fmap, l);
766 /* add default character */
767 if (!zi_true(da_fetch_int(map, 0))) {
768 if (zs_true(da_fetch_str(startchar, default_char))) {
769 da_add_int(map, 0, default_char);
770 da_add_str(startchar, default_char,
771 "STARTCHAR defaultchar\n");
773 fprintf(stderr, "%s",
774 "No default character defined.\n");
778 if (dec_chars == 1 ||
779 (dec_chars == -1 && strcmp(slant, "R") == 0 &&
780 strcmp(spacing, "C") == 0))
782 /* add DEC VT100 graphics characters in the range 1-31
783 (as expected by some old xterm versions) */
784 for (i = 0; i < decmap_size; i++) {
785 if (zs_true(da_fetch_str(startchar, decmap[i])))
787 da_add_int(map, i + 1, decmap[i]);
792 /* list of characters that will be written out */
796 "No characters found for %s-%s.\n",
802 chars = zmalloc(j * sizeof(int));
803 memset(chars, 0, j * sizeof(int));
804 for (k = 0, i = 0; k < da_count(map) && i < da_size(map); i++) {
805 if (da_fetch(map, i) != NULL)
808 qsort(chars, j, sizeof(int), chars_compare);
810 /* find overall font bounding box */
812 for (i = 0; i < j; i++) {
813 ucs = da_fetch_int(map, chars[i]);
814 zstrcpy(&t, da_fetch_str(my_char, ucs));
815 if ((nextc = startswith(t, "BBX")) != NULL
816 || (nextc = strstr(t, "\nBBX")) != NULL)
821 if (*nextc == '\n') {
823 while (isspace(*nextc))
826 for (;isspace(*nextc);)
828 w = strtol(nextc, &endp, 10);
830 if (*nextc == '\0') goto bbxbad;
831 for (;isspace(*nextc);)
833 h = strtol(nextc, &endp, 10);
835 if (*nextc == '\0') goto bbxbad;
836 for (;isspace(*nextc);)
838 x = strtol(nextc, &endp, 10);
840 if (*nextc == '\0') goto bbxbad;
841 for (;isspace(*nextc);)
843 y = strtol(nextc, &endp, 10);
844 if (bbx.cwidth == -1) {
850 combine_bbx(bbx.cwidth, bbx.cheight,
851 bbx.cxoff, bbx.cyoff,
856 fprintf(stderr, "Unparsable BBX found for U+%04x!\n", ucs);
859 "Warning: No BBX found for U+%04X!\n",
864 if (!registry) registry = zstrdup("");
865 if (!encoding) encoding = zstrdup("");
867 /* generate output file name */
868 zstrcpy(®istry_encoding, "-");
869 zstrcat(®istry_encoding, registry);
870 zstrcat(®istry_encoding, "-");
871 zstrcat(®istry_encoding, encoding);
874 char * p = strstr(fsource, ".bdf");
876 zstrcpy(&fout, fsource);
877 p = strstr(fout, ".bdf");
879 zstrcat(&fout, registry_encoding);
880 zstrcat(&fout, ".bdf");
882 zstrcpy(&fout, fsource);
883 zstrcat(&fout, registry_encoding);
887 /* remove path prefix */
888 zstrcpy(&t, u_basename(fout));
891 /* write new BDF file */
892 fprintf(stderr, "Writing %d characters into file '%s'.\n",
894 fout_fp = fopen(fout, "w");
895 if (fout_fp == NULL) {
896 fprintf(stderr, "%s: Can't write file '%s': %s!\n",
897 my_name, fout, strerror(errno));
901 fprintf(fout_fp, "%s\n", startfont);
902 fprintf(fout_fp, "%s",
903 "COMMENT AUTOMATICALLY GENERATED FILE. DO NOT EDIT!\n");
905 "COMMENT Generated with 'ucs2any %s %s %s-%s'\n",
906 fsource, fmap, registry, encoding);
907 fprintf(fout_fp, "%s",
908 "COMMENT from an ISO10646-1 encoded source BDF font.\n");
909 fprintf(fout_fp, "%s",
910 "COMMENT ucs2any by Ben Collver <collver1@attbi.com>, 2003.\n");
911 fprintf(fout_fp, "%s",
912 "COMMENT based on ucs2any.pl by Markus Kuhn <mkuhn@acm.org>, 2000.\n");
914 for (i = 0; i <= nextheader; i++) {
915 if (i == default_char_index)
916 fprintf(fout_fp, "DEFAULT_CHAR %d\n", default_char);
917 else if (i == startproperties_index)
918 fprintf(fout_fp, "STARTPROPERTIES %d\n", properties);
919 else if (i == fontname_index) {
920 fprintf(fout_fp, "FONT %s%s\n", fontname, registry_encoding);
922 else if (i == charset_registry_index)
923 fprintf(fout_fp, "CHARSET_REGISTRY \"%s\"\n", registry);
924 else if (i == slant_index)
925 fprintf(fout_fp, "SLANT \"%s\"\n", slant);
926 else if (i == charset_encoding_index)
927 fprintf(fout_fp, "CHARSET_ENCODING \"%s\"\n", encoding);
928 else if (i == fontboundingbox_index)
929 fprintf(fout_fp, "FONTBOUNDINGBOX %d %d %d %d\n", bbx.cwidth, bbx.cheight, bbx.cxoff, bbx.cyoff);
930 else if (i == spacing_index)
931 fprintf(fout_fp, "SPACING \"%s\"\n", spacing);
933 fprintf(fout_fp, "%s\n", da_fetch_str(headers, i));
936 fprintf(fout_fp, "CHARS %d\n", j);
938 /* Write characters */
939 for (i = 0; i < j; i++) {
940 ucs = da_fetch_int(map, chars[i]);
941 fprintf(fout_fp, "%s", da_fetch_str(startchar,
943 fprintf(fout_fp, "ENCODING %d\n", chars[i]);
944 fprintf(fout_fp, "%s", da_fetch_str(my_char,
947 fprintf(fout_fp, "%s", "ENDFONT\n");