Correct embedding of Type 1 fonts in PDF. Error cases (e.g. invalid Type 1
authorben <ben@cda61777-01e9-0310-a592-d414129be87e>
Sat, 9 Dec 2006 14:44:47 +0000 (14:44 +0000)
committerben <ben@cda61777-01e9-0310-a592-d414129be87e>
Sat, 9 Dec 2006 14:44:47 +0000 (14:44 +0000)
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
error.c
halibut.h
in_pf.c
paper.h

index ba19372..319e8fb 100644 (file)
--- 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 (file)
--- 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);
index 5898a8e..3cb1ed7 100644 (file)
--- 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 (file)
--- a/in_pf.c
+++ b/in_pf.c
 #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 (file)
--- 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