blob: 34cde0de8e9c0ab0250e65990e77db88827ca0de [file]
/*
* 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;
}