From: ben Date: Sat, 27 Jan 2007 20:47:41 +0000 (+0000) Subject: Add support for PFB files. This seems to have caused me to completely X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/halibut/commitdiff_plain/3e2dd889175865f670a2309f757eee3d6a7f4723 Add support for PFB files. This seems to have caused me to completely rewrite the Type 1 font support, and I'm sure the result is more complex than it needs to be, but it seems to work correctly, so I shouldn't complain. git-svn-id: svn://svn.tartarus.org/sgt/halibut@7175 cda61777-01e9-0310-a592-d414129be87e --- diff --git a/bk_pdf.c b/bk_pdf.c index 3a0ec75..99eb933 100644 --- a/bk_pdf.c +++ b/bk_pdf.c @@ -236,7 +236,7 @@ void pdf_backend(paragraph *sourceform, keywordlist *keywords, objtext(fontdesc, buf); sprintf(buf, "/StemV %g\n", fi->stemv); objtext(fontdesc, buf); - if (fi->fp) { + if (fi->fontfile) { object *fontfile = new_object(&olist); size_t len; char *ffbuf; @@ -487,13 +487,20 @@ void pdf_backend(paragraph *sourceform, keywordlist *keywords, rdaddsc(&rs, text); if (o->stream.text) { + if (!o->main.text) + rdaddsc(&o->main, "<<\n"); +#ifdef PDF_NOCOMPRESS + zlen = o->stream.pos; + zbuf = snewn(zlen, char); + memcpy(zbuf, o->stream.text, zlen); + sprintf(text, "/Length %d\n>>\n", zlen); +#else zcontext = deflate_compress_new(DEFLATE_TYPE_ZLIB); deflate_compress_data(zcontext, o->stream.text, o->stream.pos, DEFLATE_END_OF_DATA, &zbuf, &zlen); deflate_compress_free(zcontext); - if (!o->main.text) - rdaddsc(&o->main, "<<\n"); sprintf(text, "/Filter/FlateDecode\n/Length %d\n>>\n", zlen); +#endif rdaddsc(&o->main, text); } diff --git a/bk_ps.c b/bk_ps.c index 5d1a951..7afde34 100644 --- a/bk_ps.c +++ b/bk_ps.c @@ -68,12 +68,12 @@ void ps_backend(paragraph *sourceform, keywordlist *keywords, fprintf(fp, "%%%%DocumentNeededResources:\n"); for (fe = doc->fonts->head; fe; fe = fe->next) /* XXX This may request the same font multiple times. */ - if (!fe->font->info->fp) + if (!fe->font->info->fontfile) fprintf(fp, "%%%%+ font %s\n", fe->font->info->name); fprintf(fp, "%%%%DocumentSuppliedResources: procset Halibut 0 3\n"); for (fe = doc->fonts->head; fe; fe = fe->next) /* XXX This may request the same font multiple times. */ - if (fe->font->info->fp) + if (fe->font->info->fontfile) fprintf(fp, "%%%%+ font %s\n", fe->font->info->name); fprintf(fp, "%%%%EndComments\n"); @@ -198,15 +198,9 @@ void ps_backend(paragraph *sourceform, keywordlist *keywords, for (fe = doc->fonts->head; fe; fe = fe->next) { /* XXX This may request the same font multiple times. */ - if (fe->font->info->fp) { - char buf[512]; - size_t len; + if (fe->font->info->fontfile) { fprintf(fp, "%%%%BeginResource: font %s\n", fe->font->info->name); - rewind(fe->font->info->fp); - do { - len = fread(buf, 1, sizeof(buf), fe->font->info->fp); - fwrite(buf, 1, len, fp); - } while (len == sizeof(buf)); + pf_writeps(fe->font->info, fp); fprintf(fp, "%%%%EndResource\n"); } else { fprintf(fp, "%%%%IncludeResource: font %s\n", diff --git a/doc/output.but b/doc/output.but index df4580e..001a03a 100644 --- a/doc/output.but +++ b/doc/output.but @@ -1673,14 +1673,15 @@ built into all PDF and most PostScript implementations. These are: \b \cw{Courier-BoldOblique} -These fonts can be used without further formality. To use any other font, -Halibut needs at least to know its measurements, which are provided in an -\i{Adobe Font Metrics} (\I{AFM files}AFM) file. Halibut can also -\I{embedding fonts}embed -\i{Type 1 fonts} in its PDF and PostScript output if provided with an -ASCII-format (\I{PFA files}PFA) font file. To provide an AFM or PFA file -to Halibut, simply name it on Halibut's command line. If both are named, -the AFM file must come first. +These fonts can be used without further formality. To use any other +font, Halibut needs at least to know its measurements, which are +provided in an \i{Adobe Font Metrics} (\I{AFM files}AFM) file. +Halibut can also \I{embedding fonts}embed \i{Type 1 fonts} in its PDF +and PostScript output if provided with font file in either hexadecimal (\I{PFA +files}PFA) or IBM PC (\I{PFB files}PFB) format. To provide +an AFM, PFA, or PFB file to Halibut, simply name it on Halibut's command +line. If a PFA or PFB file is specified, the corresponding AFM file +must come first. \ii{Font sizes} are specified in PostScript \i{points} (72 to the inch). diff --git a/halibut.h b/halibut.h index 11e478f..51c6dd4 100644 --- a/halibut.h +++ b/halibut.h @@ -401,6 +401,7 @@ void read_afm_file(input *in); * in_pf.c */ void read_pfa_file(input *in); +void read_pfb_file(input *in); /* * keywords.c diff --git a/in_afm.c b/in_afm.c index d0b2088..b2aacae 100644 --- a/in_afm.c +++ b/in_afm.c @@ -56,7 +56,7 @@ void read_afm_file(input *in) { fi = snew(font_info); fi->name = NULL; fi->widths = newtree234(width_cmp); - fi->fp = NULL; + fi->fontfile = NULL; fi->kerns = newtree234(kern_cmp); fi->ligs = newtree234(lig_cmp); fi->fontbbox[0] = fi->fontbbox[1] = fi->fontbbox[2] = fi->fontbbox[3] = 0; diff --git a/in_pf.c b/in_pf.c index f708ac1..3df6d37 100644 --- a/in_pf.c +++ b/in_pf.c @@ -1,24 +1,181 @@ +/* + * 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(); + } +} + +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; + 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); + error(err_pfeof, &tf->pos); return; } rdaddc(&rsc, c); @@ -26,7 +183,7 @@ void read_pfa_file(input *in) { p = rsc.text; if ((p = strchr(p, ':')) == NULL) { sfree(rsc.text); - error(err_pfhead, &in->pos); + error(err_pfhead, &tf->pos); return; } p++; @@ -39,15 +196,12 @@ 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; sfree(fontname); return; } } - error(err_pfnoafm, &in->pos, fontname); - fclose(in->currfp); + error(err_pfnoafm, &tf->pos, fontname); sfree(fontname); } @@ -67,40 +221,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) { + error(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) { + error(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 +318,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 +396,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 +404,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); } diff --git a/input.c b/input.c index a677129..66f4afa 100644 --- a/input.c +++ b/input.c @@ -1582,6 +1582,7 @@ struct { } magics[] = { { "%!FontType1-", 12, &read_pfa_file }, { "%!PS-AdobeFont-", 15, &read_pfa_file }, + { "\x80\x01", 2, &read_pfb_file }, { "StartFontMetrics", 16, &read_afm_file }, }; diff --git a/paper.h b/paper.h index a15cbc7..29d6407 100644 --- a/paper.h +++ b/paper.h @@ -87,14 +87,9 @@ struct font_info_Tag { */ const char *name; /* - * The file containing this font, if any. + * Pointer to data about the file containing the font, if any. */ - FILE *fp; - filepos pos; - /* - * Lengths of the unencrypted and encrypted portions of the font. - */ - long length1, length2; + void *fontfile; /* A tree of glyph_widths */ tree234 *widths; /* A tree of kern_pairs */ @@ -405,5 +400,6 @@ char *pdf_outline_convert(wchar_t *s, int *len); */ void pf_part1(font_info *fi, char **bufp, size_t *lenp); void pf_part2(font_info *fi, char **bufp, size_t *lenp); +void pf_writeps(font_info const *fi, FILE *ofp); #endif diff --git a/psdata.c b/psdata.c index af46099..4412055 100644 --- a/psdata.c +++ b/psdata.c @@ -4473,7 +4473,7 @@ void init_std_fonts(void) { if (done) return; for (i = 0; i < (int)lenof(ps_std_fonts); i++) { font_info *fi = snew(font_info); - fi->fp = NULL; + fi->fontfile = NULL; fi->name = ps_std_fonts[i].name; fi->widths = newtree234(width_cmp); for (j = 0; j < (int)lenof(fi->bmp); j++)