Add support for PFB files. This seems to have caused me to completely
authorben <ben@cda61777-01e9-0310-a592-d414129be87e>
Sat, 27 Jan 2007 20:47:41 +0000 (20:47 +0000)
committerben <ben@cda61777-01e9-0310-a592-d414129be87e>
Sat, 27 Jan 2007 20:47:41 +0000 (20:47 +0000)
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

bk_pdf.c
bk_ps.c
doc/output.but
halibut.h
in_afm.c
in_pf.c
input.c
paper.h
psdata.c

index 3a0ec75..99eb933 100644 (file)
--- 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 (file)
--- 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",
index df4580e..001a03a 100644 (file)
@@ -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).
 
index 11e478f..51c6dd4 100644 (file)
--- 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
index d0b2088..b2aacae 100644 (file)
--- 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 (file)
--- a/in_pf.c
+++ b/in_pf.c
+/*
+ * 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 <assert.h>
+#include <limits.h>
 #include <stdio.h>
+#include <stdlib.h>
 #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 (file)
--- 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 (file)
--- 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
index af46099..4412055 100644 (file)
--- 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++)