X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/halibut/blobdiff_plain/c885c2ffbb3aea5c255d3b2e767d4450b2bebcc6..05e9c3c2e7be6c54e31121132f43db83597381e8:/in_pf.c diff --git a/in_pf.c b/in_pf.c index f708ac1..2814445 100644 --- a/in_pf.c +++ b/in_pf.c @@ -1,24 +1,183 @@ +/* + * PostScript Type 1 font file support for Halibut + */ +/* + * Type 1 font file formats are specified by Adobe Technical Note + * #5040: "Supporting Downloadable PostScript Language Fonts". + * Halibut supports hexadecimal format (section 3.1) and IBM PC format + * (section 3.3), commonly called PFA and PFB respectively. + */ + +#include +#include #include +#include #include "halibut.h" #include "paper.h" -static char *pf_read_token(FILE *fp); +#define PFB_ASCII 1 +#define PFB_BINARY 2 +#define PFB_EOF 3 + +typedef struct t1_font_Tag t1_font; +typedef struct t1_data_Tag t1_data; + +struct t1_font_Tag { + t1_data *data; + size_t length1; + size_t length2; + filepos pos; +}; + +struct t1_data_Tag { + char type; + size_t length; + unsigned char *data; + t1_data *next; +}; + +typedef struct pfstate_Tag { + t1_data *data; + t1_data *curblock; + size_t offset; +} pfstate; + +static void pf_identify(t1_font *tf); + +static t1_data *load_pfb_file(FILE *fp, filepos *pos) { + t1_data *head = NULL, *tail = NULL; + int c, i; + char type; + + pos->line = 0; + for (;;) { + if (fgetc(fp) != 128) abort(); + type = fgetc(fp); + if (type == PFB_EOF) return head; + if (tail) { + tail->next = snew(t1_data); + tail = tail->next; + } else { + head = snew(t1_data); + tail = head; + } + tail->type = type; + tail->length = 0; + for (i = 0; i < 4; i++) { + c = fgetc(fp); + if (c == EOF) abort(); + tail->length |= c << (8 * i); + } + tail->data = snewn(tail->length, unsigned char); + if (fread(tail->data, 1, tail->length, fp) != tail->length) abort(); + } + tail->next = NULL; +} + +static t1_data *load_pfa_file(FILE *fp, filepos *pos) { + t1_data *ret = snew(t1_data); + size_t off = 0, len, got; + + pos->line = 0; + ret->type = PFB_ASCII; + len = 32768; + ret->data = snewn(len, unsigned char); + for (;;) { + got = fread(ret->data + off, 1, len - off, fp); + off += got; + if (off != len) break; + len *= 2; + ret->data = sresize(ret->data, len, unsigned char); + } + ret->data = sresize(ret->data, off, unsigned char); + ret->length = off; + ret->next = NULL; + return ret; +} void read_pfa_file(input *in) { + t1_font *tf = snew(t1_font); + + tf->data = load_pfa_file(in->currfp, &in->pos); + tf->pos = in->pos; + tf->length1 = tf->length2 = 0; + fclose(in->currfp); + pf_identify(tf); +} + +void read_pfb_file(input *in) { + t1_font *tf = snew(t1_font); + + tf->data = load_pfb_file(in->currfp, &in->pos); + tf->pos = in->pos; + tf->length1 = tf->length2 = 0; + fclose(in->currfp); + pf_identify(tf); +} +static char *pf_read_token(pfstate *); + +/* + * Read a character from the initial plaintext part of a Type 1 font + */ +static int pf_getc(pfstate *pf) { + if (pf->offset == pf->curblock->length) { + if (pf->curblock->next == NULL) return EOF; + pf->curblock = pf->curblock->next; + pf->offset = 0; + } + if (pf->curblock->type != PFB_ASCII) return EOF; + return pf->curblock->data[pf->offset++]; +} + +static void pf_ungetc(int c, pfstate *pf) { + assert(pf->offset > 0); + pf->offset--; + assert(c == pf->curblock->data[pf->offset]); +} + +static void pf_rewind(pfstate *pf) { + pf->curblock = pf->data; + pf->offset = 0; +} + +static void pf_seek(pfstate *pf, size_t off) { + t1_data *td = pf->data; + + while (td->length < off) { + off -= td->length; + td = td->next; + } + pf->curblock = td; + pf->offset = off; +} + +static size_t pf_tell(pfstate *pf) { + t1_data *td = pf->data; + size_t o = 0; + + while (td != pf->curblock) { + o += td->length; + td = td->next; + } + return o + pf->offset; +} + +static void pf_identify(t1_font *tf) { rdstringc rsc = { 0, 0, NULL }; char *p; size_t len; char *fontname; font_info *fi; - FILE *fp = in->currfp; int c; + pfstate pfs, *pf = &pfs; - in->pos.line = 0; + pf->data = tf->data; + pf_rewind(pf); do { - c = fgetc(fp); + c = pf_getc(pf); if (c == EOF) { sfree(rsc.text); - error(err_pfeof, &in->pos); + err_pfeof(&tf->pos); return; } rdaddc(&rsc, c); @@ -26,7 +185,7 @@ void read_pfa_file(input *in) { p = rsc.text; if ((p = strchr(p, ':')) == NULL) { sfree(rsc.text); - error(err_pfhead, &in->pos); + err_pfhead(&tf->pos); return; } p++; @@ -39,15 +198,13 @@ void read_pfa_file(input *in) { for (fi = all_fonts; fi; fi = fi->next) { if (strcmp(fi->name, fontname) == 0) { - fi->fp = in->currfp; - fi->pos = in->pos; - fi->length1 = fi->length2 = 0; + fi->fontfile = tf; + fi->filetype = TYPE1; sfree(fontname); return; } } - error(err_pfnoafm, &in->pos, fontname); - fclose(in->currfp); + err_pfnoafm(&tf->pos, fontname); sfree(fontname); } @@ -67,40 +224,94 @@ static int pf_isspecial(int c) { c == ']' || c == '{' || c == '}' || c == '/' || c == '%'; } -static long pf_length1(font_info *fi) { - FILE *fp = fi->fp; +static size_t pf_findtoken(t1_font *tf, size_t off, char const *needle) { char *tok; + pfstate pfs, *pf = &pfs; - rewind(fp); - tok = pf_read_token(fp); - while (tok && strcmp(tok, "eexec") != 0) { - sfree(tok); - tok = pf_read_token(fp); + pf->data = tf->data; + pf_seek(pf, off); + for (;;) { + tok = pf_read_token(pf); + if (tok == NULL) { + if (pf->offset == 0 && pf->curblock->type == PFB_BINARY) + pf->curblock = pf->curblock->next; + else + return (size_t)-1; + } else { + if (strcmp(tok, needle) == 0) { + sfree(tok); + return pf_tell(pf); + } + sfree(tok); + } } - if (tok == NULL) { - error(err_pfeof, &fi->pos); +} + +static size_t pf_length1(t1_font *tf) { + size_t ret; + + ret = pf_findtoken(tf, 0, "eexec"); + if (ret == (size_t)-1) { + err_pfeof(&tf->pos); return 0; } - return ftell(fp); + return ret; } -/* - * Return the initial, unencrypted, part of a font. - */ -void pf_part1(font_info *fi, char **bufp, size_t *lenp) { - FILE *fp = fi->fp; - - if (fi->length1 == 0) - fi->length1 = pf_length1(fi); - rewind(fp); - *bufp = snewn(fi->length1, char); - *lenp = fi->length1; - if (fread(*bufp, 1, fi->length1, fp) != (size_t)fi->length1) { - error(err_pfeof, &fi->pos); - *lenp = 0; - sfree(*bufp); - *bufp = NULL; +static size_t pf_length2(t1_font *tf) { + size_t ret; + + if (tf->length1 == 0) + tf->length1 = pf_length1(tf); + ret = pf_findtoken(tf, tf->length1, "cleartomark"); + if (ret == (size_t)-1) { + err_pfeof(&tf->pos); + return 0; + } + return ret - 12 - tf->length1; /* backspace over "cleartomark\n" */ +} + +static void pf_getascii(t1_font *tf, size_t off, size_t len, + char **bufp, size_t *lenp) { + t1_data *td = tf->data; + size_t blk, i; + char *p; + + while (td && off >= td->length) { + off -= td->length; + td = td->next; } + *bufp = NULL; + *lenp = 0; + while (td && len) { + blk = len < td->length ? len : td->length; + if (td->type == PFB_ASCII) { + *bufp = sresize(*bufp, *lenp + blk, char); + memcpy(*bufp + *lenp, td->data + off, blk); + *lenp += blk; + } else { + *bufp = sresize(*bufp, *lenp + blk * 2 + blk / 39 + 3, char); + p = *bufp + *lenp; + for (i = 0; i < blk; i++) { + if (i % 39 == 0) p += sprintf(p, "\n"); + p += sprintf(p, "%02x", td->data[off + i]); + } + p += sprintf(p, "\n"); + *lenp = p - *bufp; + } + len -= blk; + td = td->next; + off = 0; + } +} + +void pf_writeps(font_info const *fi, FILE *ofp) { + char *buf; + size_t len; + + pf_getascii(fi->fontfile, 0, INT_MAX, &buf, &len); + fwrite(buf, 1, len, ofp); + sfree(buf); } static int hexval(char c) { @@ -110,56 +321,77 @@ static int hexval(char c) { return 0; } +static void pf_getbinary(t1_font *tf, size_t off, size_t len, + char **bufp, size_t *lenp) { + t1_data *td = tf->data; + size_t blk, i; + int havenybble = 0; + char *p, nybble; + + while (td && off >= td->length) { + off -= td->length; + td = td->next; + } + *bufp = NULL; + *lenp = 0; + while (td && len) { + blk = len < td->length ? len : td->length; + if (td->type == PFB_BINARY) { + *bufp = sresize(*bufp, *lenp + blk, char); + memcpy(*bufp + *lenp, td->data + off, blk); + *lenp += blk; + } else { + *bufp = sresize(*bufp, *lenp + blk / 2 + 1, char); + p = *bufp + *lenp; + for (i = 0; i < blk; i++) { + if (pf_isspace(td->data[off + i])) continue; + if (!havenybble) + nybble = hexval(td->data[off+i]); + else + *p++ = (nybble << 4) | hexval(td->data[off+i]); + havenybble = !havenybble; + } + *lenp = p - *bufp; + } + len -= blk; + td = td->next; + off = 0; + } +} + + +/* + * Return the initial, unencrypted, part of a font. + */ +void pf_part1(font_info *fi, char **bufp, size_t *lenp) { + t1_font *tf = fi->fontfile; + + if (tf->length1 == 0) + tf->length1 = pf_length1(tf); + pf_getascii(tf, 0, tf->length1, bufp, lenp); +} + /* * Return the middle, encrypted, part of a font. */ void pf_part2(font_info *fi, char **bufp, size_t *lenp) { - FILE *fp = fi->fp; - rdstringc rsc = { 0, 0, NULL }; - char *tok, *p; - unsigned char nybble; - int havenybble = 0; + t1_font *tf = fi->fontfile; - if (fi->length1 == 0) - fi->length1 = pf_length1(fi); - fseek(fp, fi->length1, SEEK_SET); - tok = pf_read_token(fp); - while (tok && strcmp(tok, "cleartomark") != 0) { - for (p = tok; *p; p++) { - if (pf_isspace(*p)) continue; - if (!havenybble) - nybble = hexval(*p); - else - rdaddc(&rsc, (nybble << 4) | hexval(*p)); - havenybble = !havenybble; - } - sfree(tok); - tok = pf_read_token(fp); - } - if (tok == NULL) { - error(err_pfeof, &fi->pos); - *bufp = NULL; - *lenp = 0; - } - *bufp = rsc.text; - /* Trim off the trailing zeroes */ - if (rsc.pos >= 256) - *lenp = rsc.pos - 256; - else { - error(err_pfbad, &fi->pos); - *bufp = NULL; - *lenp = 0; - } + if (tf->length2 == 0) + tf->length2 = pf_length2(tf); + pf_getbinary(tf, tf->length1, tf->length2, bufp, lenp); + if (*lenp >= 256) + *lenp -= 256; } -static char *pf_read_litstring(FILE *fp) { +static char *pf_read_litstring(pfstate *pf) { rdstringc rsc = { 0, 0, NULL }; int depth = 1; int c; rdaddc(&rsc, '('); do { - c = fgetc(fp); + c = pf_getc(pf); switch (c) { case '(': depth++; break; @@ -167,7 +399,7 @@ static char *pf_read_litstring(FILE *fp) { depth--; break; case '\\': rdaddc(&rsc, '\\'); - c = fgetc(fp); + c = pf_getc(pf); break; } if (c != EOF) rdaddc(&rsc, c); @@ -175,47 +407,47 @@ static char *pf_read_litstring(FILE *fp) { return rsc.text; } -static char *pf_read_hexstring(FILE *fp) { +static char *pf_read_hexstring(pfstate *pf) { rdstringc rsc = { 0, 0, NULL }; int c; rdaddc(&rsc, '<'); do { - c = fgetc(fp); + c = pf_getc(pf); if (c != EOF) rdaddc(&rsc, c); } while (c != '>' && c != EOF); return rsc.text; } -static char *pf_read_word(FILE *fp, int c) { +static char *pf_read_word(pfstate *pf, int c) { rdstringc rsc = { 0, 0, NULL }; rdaddc(&rsc, c); if (c == '{' || c == '}' || c == '[' || c == ']') return rsc.text; for (;;) { - c = fgetc(fp); + c = pf_getc(pf); if (pf_isspecial(c) || pf_isspace(c) || c == EOF) break; rdaddc(&rsc, c); } - if (pf_isspecial(c)) ungetc(c, fp); + if (pf_isspecial(c)) pf_ungetc(c, pf); return rsc.text; } -static char *pf_read_token(FILE *fp) { +static char *pf_read_token(pfstate *pf) { int c; do { - c = fgetc(fp); + c = pf_getc(pf); } while (pf_isspace(c)); if (c == EOF) return NULL; if (c == '%') { do { - c = fgetc(fp); + c = pf_getc(pf); } while (c != 012 && c != 015); - return pf_read_token(fp); + return pf_read_token(pf); } - if (c == '(') return pf_read_litstring(fp); - if (c == '<') return pf_read_hexstring(fp); - return pf_read_word(fp, c); + if (c == '(') return pf_read_litstring(pf); + if (c == '<') return pf_read_hexstring(pf); + return pf_read_word(pf, c); }