blob: 0eda1bdfc5cfdc377783c9a022aecfe8390f4520 [file] [log] [blame]
/*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sysexits.h>
#include "nls.h"
#include "version.h"
#include "psf.h"
#include "xmalloc.h"
#include "psffontop.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(int fontpos, unsigned int 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(int fontpos, unsigned int 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 int
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, int fontlen){
char *p, *p1;
int 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, 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, 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, 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, progname, fp0);
exit(EX_DATAERR);
}
if (fp1 && (fp1 < fp0 || fp1 >= fontlen)) {
char *u = _("%s: Bad end of range (0x%lx)\n");
fprintf(stderr, u, 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, 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, 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, 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, progname, p);
}
}
}
static void
read_itable(FILE *itab, int fontlen, struct unicode_list **uclistheadsp) {
char buf[65536];
int 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) {
char *ifname, *ofname, *itname, *otname;
FILE *ifil, *ofil, *itab, *otab;
int psftype, fontlen, charsize, hastable, notable;
int i;
int width = 8, bytewidth, height;
char *inbuf, *fontbuf;
int inbuflth, fontbuflth;
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(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, progname);
exit(EX_USAGE);
}
ifname = argv[1];
itname = argv[2];
ofname = argv[3];
} else if (!strcmp(progname, "psfgettable")) {
if (argc < 2 || argc > 3) {
char *u = _("Usage:\n\t%s infont [outtable]\n");
fprintf(stderr, u, progname);
exit(EX_USAGE);
}
ifname = argv[1];
otname = (argc == 3) ? argv[2] : "-";
} else if (!strcmp(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, progname);
exit(EX_USAGE);
}
ifname = argv[1];
ofname = argv[2];
notable = 1;
} else {
for (i = 1; i < argc; i ++) {
if ((!strcmp(argv[i], "-i") || !strcmp(argv[i], "-if"))
&& i < argc-1)
ifname = argv[++i];
else if((!strcmp(argv[i],"-o")||!strcmp(argv[i],"-of"))
&& i < argc-1)
ofname = argv[++i];
else if(!strcmp(argv[i], "-it") && i < argc-1)
itname = argv[++i];
else if(!strcmp(argv[i], "-ot") && i < argc-1)
otname = argv[++i];
else if(!strcmp(argv[i], "-nt"))
notable = 1;
else
break;
}
if (i < argc || argc <= 1) {
char *u = _("Usage:\n\t%s [-i infont] [-o outfont] "
"[-it intable] [-ot outtable] [-nt]\n");
fprintf(stderr, u, progname);
exit(EX_USAGE);
}
}
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 (readpsffont(ifil, &inbuf, &inbuflth, &fontbuf, &fontbuflth,
&width, &fontlen, 0,
itab ? NULL : &uclistheads) == -1) {
char *u = _("%s: Bad magic number on %s\n");
fprintf(stderr, u, progname, ifname);
exit(EX_DATAERR);
}
fclose(ifil);
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, progname);
exit(EX_DATAERR);
}
if (itab) {
read_itable(itab, fontlen, &uclistheads);
fclose(itab);
}
if (otab) {
struct unicode_list *ul;
struct unicode_seq *us;
char *sep;
if (!hastable) {
char *u = _("%s: input font does not have an index\n");
fprintf(stderr, u, 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%03x\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) {
writepsffont(ofil, fontbuf, width, height, fontlen, psftype,
notable ? NULL : uclistheads);
fclose(ofil);
}
return EX_OK;
}