| /* | |
| * psfxtable.c | |
| * | |
| * Manipulate headers and Unicode tables for psf fonts | |
| * | |
| * Copyright (C) 1999 Andries E. Brouwer | |
| * derived from sources that were | |
| * Copyright (C) 1994 H. Peter Anvin | |
| * | |
| * This program may be freely copied under the terms of the GNU | |
| * General Public License (GPL), version 2, or at your option | |
| * any later version. | |
| */ | |
| #include "config.h" | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <ctype.h> | |
| #include <sysexits.h> | |
| #include <kfont.h> | |
| #include "libcommon.h" | |
| /* | |
| * call: psfxtable -i infont -o outfont -it intable -ot outtable | |
| * psfaddtable infont intable [outfont] | |
| * psfgettable infont [outtable] | |
| * psfstriptable infont [outfont] | |
| * | |
| * When outfont is requested it will get psf1 header when | |
| * infont had psf1 header and intable does not have sequences | |
| * and psf2 header otherwise. | |
| */ | |
| /* | |
| * Parse humanly readable unicode table. | |
| * Format: lines | |
| * <fontposition><tab><uc_defs> | |
| * or | |
| * <fontrange><tab><uc_defs> | |
| * or | |
| * <fontrange><tab><uc_range> | |
| * where | |
| * <uc_defs> :: <empty> | <uc_def><space><uc_defs> | |
| * <uc_def> :: <uc> | <uc>,<uc_def> | |
| * <uc> :: U+<h><h><h><h> | |
| * <h> :: <hexadecimal digit> | |
| * <range> :: <value>-<value> | |
| * Blank lines and lines starting with # are ignored. | |
| */ | |
| struct unicode_list *uclistheads; | |
| static void | |
| addpair(long fontpos, unicode uc) | |
| { | |
| struct unicode_list *ul; | |
| struct unicode_seq *us; | |
| ul = xmalloc(sizeof(struct unicode_list)); | |
| us = xmalloc(sizeof(struct unicode_seq)); | |
| us->uc = uc; | |
| us->prev = us; | |
| us->next = NULL; | |
| ul->seq = us; | |
| ul->prev = uclistheads[fontpos].prev; | |
| ul->prev->next = ul; | |
| ul->next = NULL; | |
| uclistheads[fontpos].prev = ul; | |
| } | |
| static void | |
| addseq(long fontpos, unicode uc) | |
| { | |
| struct unicode_list *ul; | |
| struct unicode_seq *us; | |
| ul = uclistheads[fontpos].prev; | |
| us = xmalloc(sizeof(struct unicode_seq)); | |
| us->uc = uc; | |
| us->prev = ul->seq->prev; | |
| us->prev->next = us; | |
| us->next = NULL; | |
| ul->seq->prev = us; | |
| } | |
| static long | |
| getunicode(char **p0) | |
| { | |
| char *p = *p0; | |
| while (*p == ' ' || *p == '\t') | |
| p++; | |
| if (*p != 'U' || p[1] != '+' || | |
| !isxdigit(p[2]) || !isxdigit(p[3]) || !isxdigit(p[4]) || | |
| !isxdigit(p[5]) || isxdigit(p[6])) | |
| return -1; | |
| *p0 = p + 6; | |
| return strtol(p + 2, 0, 16); | |
| } | |
| static void | |
| parse_itab_line(char *buf, size_t fontlen) | |
| { | |
| char *p, *p1; | |
| long i; | |
| long fp0, fp1, un0, un1; | |
| if ((p = strchr(buf, '\n')) != NULL) | |
| *p = 0; | |
| else { | |
| char *u = _("%s: Warning: line too long\n"); | |
| fprintf(stderr, u, get_progname()); | |
| exit(EX_DATAERR); | |
| } | |
| p = buf; | |
| while (*p == ' ' || *p == '\t') | |
| p++; | |
| if (!*p || *p == '#') | |
| return; | |
| fp0 = strtol(p, &p1, 0); | |
| if (p1 == p) { | |
| char *u = _("%s: Bad input line: %s\n"); | |
| fprintf(stderr, u, get_progname(), buf); | |
| exit(EX_DATAERR); | |
| } | |
| p = p1; | |
| if (*p == '-') { | |
| p++; | |
| fp1 = strtol(p, &p1, 0); | |
| if (p1 == p) { | |
| char *u = _("%s: Bad input line: %s\n"); | |
| fprintf(stderr, u, get_progname(), buf); | |
| exit(EX_DATAERR); | |
| } | |
| p = p1; | |
| } else | |
| fp1 = 0; | |
| if (fp0 < 0 || fp0 >= fontlen) { | |
| char *u = _("%s: Glyph number (0x%lx) past end of font\n"); | |
| fprintf(stderr, u, get_progname(), fp0); | |
| exit(EX_DATAERR); | |
| } | |
| if (fp1 && (fp1 < fp0 || fp1 >= fontlen)) { | |
| char *u = _("%s: Bad end of range (0x%lx)\n"); | |
| fprintf(stderr, u, get_progname(), fp1); | |
| exit(EX_DATAERR); | |
| } | |
| if (fp1) { | |
| /* we have a range; expect the word "idem" | |
| or a Unicode range of the same length */ | |
| while (*p == ' ' || *p == '\t') | |
| p++; | |
| if (!strncmp(p, "idem", 4)) { | |
| for (i = fp0; i <= fp1; i++) | |
| addpair(i, i); | |
| p += 4; | |
| } else { | |
| un0 = getunicode(&p); | |
| while (*p == ' ' || *p == '\t') | |
| p++; | |
| if (*p != '-') { | |
| char *u = _("%s: Corresponding to a range of " | |
| "font positions, there should be " | |
| "a Unicode range\n"); | |
| fprintf(stderr, u, get_progname()); | |
| exit(EX_DATAERR); | |
| } | |
| p++; | |
| un1 = getunicode(&p); | |
| if (un0 < 0 || un1 < 0) { | |
| char *u = _("%s: Bad Unicode range " | |
| "corresponding to font position " | |
| "range 0x%x-0x%x\n"); | |
| fprintf(stderr, u, get_progname(), fp0, fp1); | |
| exit(EX_DATAERR); | |
| } | |
| if (un1 - un0 != fp1 - fp0) { | |
| char *u = _("%s: Unicode range U+%x-U+%x not " | |
| "of the same length as font " | |
| "position range 0x%x-0x%x\n"); | |
| fprintf(stderr, u, get_progname(), | |
| un0, un1, fp0, fp1); | |
| exit(EX_DATAERR); | |
| } | |
| for (i = fp0; i <= fp1; i++) | |
| addpair(i, un0 - fp0 + i); | |
| } /* not idem */ | |
| } else { /* no range */ | |
| while ((un0 = getunicode(&p)) >= 0) { | |
| addpair(fp0, un0); | |
| while (*p++ == ',' && (un1 = getunicode(&p)) >= 0) { | |
| addseq(fp0, un1); | |
| } | |
| p--; | |
| } | |
| while (*p == ' ' || *p == '\t') | |
| p++; | |
| if (*p && *p != '#') { | |
| char *u = _("%s: trailing junk (%s) ignored\n"); | |
| fprintf(stderr, u, get_progname(), p); | |
| } | |
| } | |
| } | |
| static void | |
| read_itable(FILE *itab, size_t fontlen, struct unicode_list **uclistheadsp) | |
| { | |
| char buf[65536]; | |
| size_t i; | |
| if (uclistheadsp) { | |
| *uclistheadsp = xrealloc(*uclistheadsp, fontlen * sizeof(struct unicode_list)); | |
| for (i = 0; i < fontlen; i++) { | |
| struct unicode_list *up = &((*uclistheadsp)[i]); | |
| up->next = NULL; | |
| up->seq = NULL; | |
| up->prev = up; | |
| } | |
| while (fgets(buf, sizeof(buf), itab) != NULL) | |
| parse_itab_line(buf, fontlen); | |
| } | |
| } | |
| int debug = 0; | |
| int main(int argc, char **argv) | |
| { | |
| const char *ifname, *ofname, *itname, *otname; | |
| FILE *ifil, *ofil, *itab, *otab; | |
| int psftype, hastable, notable, rc; | |
| size_t charsize, fontlen; | |
| size_t i; | |
| size_t width = 8, bytewidth, height; | |
| char *inbuf, *fontbuf; | |
| size_t inbuflth, fontbuflth; | |
| struct psffont *font = NULL; | |
| set_progname(argv[0]); | |
| setlocale(LC_ALL, ""); | |
| bindtextdomain(PACKAGE_NAME, LOCALEDIR); | |
| textdomain(PACKAGE_NAME); | |
| if (argc == 2 && !strcmp(argv[1], "-V")) | |
| print_version_and_exit(); | |
| ifil = ofil = itab = otab = NULL; | |
| ifname = ofname = itname = otname = NULL; | |
| fontbuf = NULL; | |
| notable = 0; | |
| if (!strcmp(get_progname(), "psfaddtable")) { | |
| /* Do not send binary data to stdout without explicit "-" */ | |
| if (argc != 4) { | |
| char *u = _("Usage:\n\t%s infont intable outfont\n"); | |
| fprintf(stderr, u, get_progname()); | |
| exit(EX_USAGE); | |
| } | |
| ifname = argv[1]; | |
| itname = argv[2]; | |
| ofname = argv[3]; | |
| } else if (!strcmp(get_progname(), "psfgettable")) { | |
| if (argc < 2 || argc > 3) { | |
| char *u = _("Usage:\n\t%s infont [outtable]\n"); | |
| fprintf(stderr, u, get_progname()); | |
| exit(EX_USAGE); | |
| } | |
| ifname = argv[1]; | |
| otname = (argc == 3) ? argv[2] : "-"; | |
| } else if (!strcmp(get_progname(), "psfstriptable")) { | |
| /* Do not send binary data to stdout without explicit "-" */ | |
| if (argc != 3) { | |
| char *u = _("Usage:\n\t%s infont outfont\n"); | |
| fprintf(stderr, u, get_progname()); | |
| exit(EX_USAGE); | |
| } | |
| ifname = argv[1]; | |
| ofname = argv[2]; | |
| notable = 1; | |
| } else { | |
| int j; | |
| for (j = 1; j < argc; j++) { | |
| if ((!strcmp(argv[j], "-i") || !strcmp(argv[j], "-if")) && j < argc - 1) | |
| ifname = argv[++j]; | |
| else if ((!strcmp(argv[j], "-o") || !strcmp(argv[j], "-of")) && j < argc - 1) | |
| ofname = argv[++j]; | |
| else if (!strcmp(argv[j], "-it") && j < argc - 1) | |
| itname = argv[++j]; | |
| else if (!strcmp(argv[j], "-ot") && j < argc - 1) | |
| otname = argv[++j]; | |
| else if (!strcmp(argv[j], "-nt")) | |
| notable = 1; | |
| else | |
| break; | |
| } | |
| if (j < argc || argc <= 1) { | |
| char *u = _("Usage:\n\t%s [-i infont] [-o outfont] " | |
| "[-it intable] [-ot outtable] [-nt]\n"); | |
| fprintf(stderr, u, get_progname()); | |
| exit(EX_USAGE); | |
| } | |
| } | |
| struct kfont_ctx *ctx = kfont_context_new(); | |
| if (ctx == NULL) { | |
| nomem(); | |
| } | |
| if (!ifname) | |
| ifname = "-"; | |
| if (!strcmp(ifname, "-")) | |
| ifil = stdin; | |
| else { | |
| ifil = fopen(ifname, "r"); | |
| if (!ifil) { | |
| perror(ifname); | |
| exit(EX_NOINPUT); | |
| } | |
| } | |
| if (!itname) | |
| /* nothing */; | |
| else if (!strcmp(itname, "-")) | |
| itab = stdin; | |
| else { | |
| itab = fopen(itname, "r"); | |
| if (!itab) { | |
| perror(itname); | |
| exit(EX_NOINPUT); | |
| } | |
| } | |
| /* Refuse ifil == itab == stdin ? Perhaps not. */ | |
| if (!ofname) | |
| /* nothing */; | |
| else if (!strcmp(ofname, "-")) | |
| ofil = stdout; | |
| else { | |
| ofil = fopen(ofname, "w"); | |
| if (!ofil) { | |
| perror(ofname); | |
| exit(EX_CANTCREAT); | |
| } | |
| } | |
| if (!otname) | |
| /* nothing */; | |
| else if (!strcmp(otname, "-")) | |
| otab = stdout; | |
| else { | |
| otab = fopen(otname, "w"); | |
| if (!otab) { | |
| perror(otname); | |
| exit(EX_CANTCREAT); | |
| } | |
| } | |
| if ((rc = kfont_read_file(ctx, ifil, &inbuf, &inbuflth)) < 0) | |
| exit(-rc); | |
| if (kfont_psffont_read(ctx, inbuf, inbuflth, (itab ? KFONT_FLAG_SKIP_LOAD_UNICODE_MAP : 0), &font) < 0) { | |
| char *u = _("%s: Bad magic number on %s\n"); | |
| fprintf(stderr, u, get_progname(), ifname); | |
| exit(EX_DATAERR); | |
| } | |
| fclose(ifil); | |
| uclistheads = kfont_psffont_unicode_list(ctx, font); | |
| fontlen = kfont_psffont_length(ctx, font); | |
| hastable = (uclistheads != NULL); | |
| /* | |
| charsize = fontbuflth / fontlen; | |
| bytewidth = (width + 7) / 8; | |
| if (!bytewidth) | |
| bytewidth = 1; | |
| height = charsize / bytewidth; | |
| hastable = (uclistheads != NULL); | |
| if (PSF1_MAGIC_OK((unsigned char *)inbuf)) { | |
| psftype = 1; | |
| } else if (PSF2_MAGIC_OK((unsigned char *)inbuf)) { | |
| psftype = 2; | |
| } else { | |
| char *u = _("%s: psf file with unknown magic\n"); | |
| fprintf(stderr, u, get_progname()); | |
| exit(EX_DATAERR); | |
| } | |
| */ | |
| if (itab) { | |
| read_itable(itab, fontlen, &uclistheads); | |
| fclose(itab); | |
| } | |
| if (otab) { | |
| struct unicode_list *ul; | |
| struct unicode_seq *us; | |
| const char *sep; | |
| if (!hastable) { | |
| char *u = _("%s: input font does not have an index\n"); | |
| fprintf(stderr, u, get_progname()); | |
| exit(EX_DATAERR); | |
| } | |
| fprintf(otab, | |
| "#\n# Character table extracted from font %s\n#\n", | |
| ifname); | |
| for (i = 0; i < fontlen; i++) { | |
| fprintf(otab, "0x%03lx\t", i); | |
| sep = ""; | |
| ul = uclistheads[i].next; | |
| while (ul) { | |
| us = ul->seq; | |
| while (us) { | |
| fprintf(otab, "%sU+%04x", sep, us->uc); | |
| us = us->next; | |
| sep = ", "; | |
| } | |
| ul = ul->next; | |
| sep = " "; | |
| } | |
| fprintf(otab, "\n"); | |
| } | |
| fclose(otab); | |
| } | |
| if (ofil) { | |
| int rc; | |
| rc = kfont_write_psffont(ctx, ofil, fontbuf, width, height, fontlen, psftype, notable ? NULL : uclistheads); | |
| if (rc < 0) | |
| exit(-rc); | |
| fclose(ofil); | |
| } | |
| return EX_OK; | |
| } |