From c885c2ffbb3aea5c255d3b2e767d4450b2bebcc6 Mon Sep 17 00:00:00 2001 From: ben Date: Sat, 9 Dec 2006 14:44:47 +0000 Subject: [PATCH] Correct embedding of Type 1 fonts in PDF. Error cases (e.g. invalid Type 1 fonts) may not be well handled, and may emit invalid PDF. git-svn-id: svn://svn.tartarus.org/sgt/halibut@6974 cda61777-01e9-0310-a592-d414129be87e --- bk_pdf.c | 37 +++++++++--- error.c | 21 +++++++ halibut.h | 4 ++ in_pf.c | 205 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- paper.h | 11 ++++ 5 files changed, 260 insertions(+), 18 deletions(-) diff --git a/bk_pdf.c b/bk_pdf.c index ba19372..319e8fb 100644 --- a/bk_pdf.c +++ b/bk_pdf.c @@ -34,6 +34,7 @@ struct objlist_Tag { static object *new_object(objlist *list); static void objtext(object *o, char const *text); static void objstream(object *o, char const *text); +static void objstream_len(object *o, char const *text, size_t len); static void pdf_string(void (*add)(object *, char const *), object *, char const *); static void pdf_string_len(void (*add)(object *, char const *), @@ -237,14 +238,20 @@ void pdf_backend(paragraph *sourceform, keywordlist *keywords, objtext(fontdesc, buf); if (fi->fp) { object *fontfile = new_object(&olist); - char buf[513]; size_t len; - rewind(fi->fp); - do { - len = fread(buf, 1, sizeof(buf)-1, fi->fp); - buf[len] = 0; - objstream(fontfile, buf); - } while (len == sizeof(buf)-1); + char *ffbuf; + + pf_part1((font_info *)fi, &ffbuf, &len); + objstream_len(fontfile, ffbuf, len); + sfree(ffbuf); + sprintf(buf, "<<\n/Length1 %lu\n", (unsigned long)len); + objtext(fontfile, buf); + pf_part2((font_info *)fi, &ffbuf, &len); + objstream_len(fontfile, ffbuf, len); + sfree(ffbuf); + sprintf(buf, "/Length2 %lu\n", (unsigned long)len); + objtext(fontfile, buf); + objtext(fontfile, "/Length3 0\n"); objtext(fontdesc, "/FontFile "); objref(fontdesc, fontfile); } @@ -479,12 +486,19 @@ void pdf_backend(paragraph *sourceform, keywordlist *keywords, sprintf(text, "%d 0 obj\n", o->number); rdaddsc(&rs, text); - if (!o->main.text && o->stream.text) { + if (o->stream.text) { + char buf[100]; + sprintf(buf, "stream%06d", o->number); + fp = fopen(buf, "wb"); + fwrite(o->stream.text, 1, o->stream.pos, fp); + fclose(fp); 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); - sprintf(text, "<<\n/Filter/FlateDecode\n/Length %d\n>>\n", zlen); + if (!o->main.text) + rdaddsc(&o->main, "<<\n"); + sprintf(text, "/Filter/FlateDecode\n/Length %d\n>>\n", zlen); rdaddsc(&o->main, text); } @@ -596,6 +610,11 @@ static void objtext(object *o, char const *text) rdaddsc(&o->main, text); } +static void objstream_len(object *o, char const *text, size_t len) +{ + rdaddsn(&o->stream, text, len); +} + static void objstream(object *o, char const *text) { rdaddsc(&o->stream, text); diff --git a/error.c b/error.c index dfe65e3..2dae220 100644 --- a/error.c +++ b/error.c @@ -315,6 +315,27 @@ static void do_error(int code, va_list ap) { sprintf(error, "AFM key '%.200s' requires %d values", sp, i); flags = FILEPOS; break; + case err_pfeof: + fpos = *va_arg(ap, filepos *); + sprintf(error, "Type 1 font file ended unexpectedly"); + flags = FILEPOS; + break; + case err_pfhead: + fpos = *va_arg(ap, filepos *); + sprintf(error, "Type 1 font file header line invalid"); + flags = FILEPOS; + break; + case err_pfbad: + fpos = *va_arg(ap, filepos *); + sprintf(error, "Type 1 font file invalid"); + flags = FILEPOS; + break; + case err_pfnoafm: + fpos = *va_arg(ap, filepos *); + sp = va_arg(ap, char *); + sprintf(error, "No metrics available for Type 1 font '%.200s'", sp); + flags = FILEPOS; + break; case err_whatever: sp = va_arg(ap, char *); vsprintf(error, sp, ap); diff --git a/halibut.h b/halibut.h index 5898a8e..3cb1ed7 100644 --- a/halibut.h +++ b/halibut.h @@ -248,6 +248,10 @@ enum { err_afmkey, /* missing expected keyword in AFM */ err_afmvers, /* unsupported AFM version */ err_afmval, /* missing value(s) for AFM key */ + err_pfeof, /* eof in Type 1 font file */ + err_pfhead, /* bad Type 1 header line */ + err_pfbad, /* otherwise invalide Type 1 font */ + err_pfnoafm, /* Type 1 font but no AFM */ err_whatever /* random error of another type */ }; diff --git a/in_pf.c b/in_pf.c index 50e663e..f708ac1 100644 --- a/in_pf.c +++ b/in_pf.c @@ -2,33 +2,220 @@ #include "halibut.h" #include "paper.h" +static char *pf_read_token(FILE *fp); + void read_pfa_file(input *in) { - char buf[512], *p; + rdstringc rsc = { 0, 0, NULL }; + char *p; size_t len; char *fontname; font_info *fi; + FILE *fp = in->currfp; + int c; - len = fread(buf, 1, sizeof(buf) - 1, in->currfp); - buf[len] = 0; - if (strncmp(buf, "%!FontType1-", 12) && - strncmp(buf, "%!PS-AdobeFont-", 15)) + in->pos.line = 0; + do { + c = fgetc(fp); + if (c == EOF) { + sfree(rsc.text); + error(err_pfeof, &in->pos); + return; + } + rdaddc(&rsc, c); + } while (c != 012 && c != 015); + p = rsc.text; + if ((p = strchr(p, ':')) == NULL) { + sfree(rsc.text); + error(err_pfhead, &in->pos); return; - p = buf; - p += strcspn(p, ":") + 1; + } + p++; p += strspn(p, " \t"); len = strcspn(p, " \t"); fontname = snewn(len + 1, char); memcpy(fontname, p, len); fontname[len] = 0; + sfree(rsc.text); + 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; sfree(fontname); return; } } + error(err_pfnoafm, &in->pos, fontname); fclose(in->currfp); sfree(fontname); } - - + +/* + * PostScript white space characters; PLRM3 table 3.1 + */ +static int pf_isspace(int c) { + return c == 000 || c == 011 || c == 012 || c == 014 || c == 015 || + c == ' '; +} + +/* + * PostScript special characters; PLRM3 page 27 + */ +static int pf_isspecial(int c) { + return c == '(' || c == ')' || c == '<' || c == '>' || c == '[' || + c == ']' || c == '{' || c == '}' || c == '/' || c == '%'; +} + +static long pf_length1(font_info *fi) { + FILE *fp = fi->fp; + char *tok; + + rewind(fp); + tok = pf_read_token(fp); + while (tok && strcmp(tok, "eexec") != 0) { + sfree(tok); + tok = pf_read_token(fp); + } + if (tok == NULL) { + error(err_pfeof, &fi->pos); + return 0; + } + return ftell(fp); +} + +/* + * 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 int hexval(char c) { + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'A' && c <= 'F') return c - 'A' + 0xA; + if (c >= 'a' && c <= 'f') return c - 'a' + 0xa; + return 0; +} + +/* + * 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; + + 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; + } +} + +static char *pf_read_litstring(FILE *fp) { + rdstringc rsc = { 0, 0, NULL }; + int depth = 1; + int c; + + rdaddc(&rsc, '('); + do { + c = fgetc(fp); + switch (c) { + case '(': + depth++; break; + case ')': + depth--; break; + case '\\': + rdaddc(&rsc, '\\'); + c = fgetc(fp); + break; + } + if (c != EOF) rdaddc(&rsc, c); + } while (depth > 0 && c != EOF); + return rsc.text; +} + +static char *pf_read_hexstring(FILE *fp) { + rdstringc rsc = { 0, 0, NULL }; + int c; + + rdaddc(&rsc, '<'); + do { + c = fgetc(fp); + if (c != EOF) rdaddc(&rsc, c); + } while (c != '>' && c != EOF); + return rsc.text; +} + +static char *pf_read_word(FILE *fp, int c) { + rdstringc rsc = { 0, 0, NULL }; + + rdaddc(&rsc, c); + if (c == '{' || c == '}' || c == '[' || c == ']') + return rsc.text; + for (;;) { + c = fgetc(fp); + if (pf_isspecial(c) || pf_isspace(c) || c == EOF) break; + rdaddc(&rsc, c); + } + if (pf_isspecial(c)) ungetc(c, fp); + return rsc.text; +} + +static char *pf_read_token(FILE *fp) { + int c; + + do { + c = fgetc(fp); + } while (pf_isspace(c)); + if (c == EOF) return NULL; + if (c == '%') { + do { + c = fgetc(fp); + } while (c != 012 && c != 015); + return pf_read_token(fp); + } + if (c == '(') return pf_read_litstring(fp); + if (c == '<') return pf_read_hexstring(fp); + return pf_read_word(fp, c); +} diff --git a/paper.h b/paper.h index 03b0f1b..b0ea032 100644 --- a/paper.h +++ b/paper.h @@ -68,6 +68,11 @@ struct font_info_Tag { * The file containing this font, if any. */ FILE *fp; + filepos pos; + /* + * Lengths of the unencrypted and encrypted portions of the font. + */ + long length1, length2; /* * An array of pointers to the available glyph names, and their * corresponding character widths. These two arrays have @@ -376,4 +381,10 @@ const kern_pair *ps_std_font_kerns(char const *fontname); */ char *pdf_outline_convert(wchar_t *s, int *len); +/* + * Backend functions exported by in_pf.c + */ +void pf_part1(font_info *fi, char **bufp, size_t *lenp); +void pf_part2(font_info *fi, char **bufp, size_t *lenp); + #endif -- 2.11.0