There's no need to reset the font every line if it hasn't changed. This saves
[sgt/halibut] / bk_pdf.c
index ce71c5e..0a36ad9 100644 (file)
--- a/bk_pdf.c
+++ b/bk_pdf.c
@@ -38,9 +38,13 @@ static void pdf_string(void (*add)(object *, char const *),
 static void pdf_string_len(void (*add)(object *, char const *),
                           object *, char const *, int);
 static void objref(object *o, object *dest);
+static char *pdf_outline_convert(wchar_t *s, int *len);
+
+static int is_std_font(char const *name);
 
 static void make_pages_node(object *node, object *parent, page_data *first,
-                           page_data *last, object *resources);
+                           page_data *last, object *resources,
+                           object *mediabox);
 static int make_outline(object *parent, outline_element *start, int n,
                        int open);
 static int pdf_versionid(FILE *fp, word *words);
@@ -56,7 +60,7 @@ void pdf_backend(paragraph *sourceform, keywordlist *keywords,
     char *filename;
     paragraph *p;
     objlist olist;
-    object *o, *cat, *outlines, *pages, *resources;
+    object *o, *info, *cat, *outlines, *pages, *resources, *mediabox;
     int fileoff;
 
     IGNORE(keywords);
@@ -64,7 +68,7 @@ void pdf_backend(paragraph *sourceform, keywordlist *keywords,
 
     filename = dupstr("output.pdf");
     for (p = sourceform; p; p = p->next) {
-       if (p->type == para_Config && p->parent) {
+       if (p->type == para_Config) {
            if (!ustricmp(p->keyword, L"pdf-filename")) {
                sfree(filename);
                filename = dupstr(adv(p->origkeyword));
@@ -75,6 +79,29 @@ void pdf_backend(paragraph *sourceform, keywordlist *keywords,
     olist.head = olist.tail = NULL;
     olist.number = 1;
 
+    {
+       char buf[256];
+
+       info = new_object(&olist);
+       objtext(info, "<<\n");
+       if (doc->n_outline_elements > 0) {
+           char *title;
+           int titlelen;
+
+           title =
+              pdf_outline_convert(doc->outline_elements->pdata->outline_title,
+                                       &titlelen);
+           objtext(info, "/Title ");
+           pdf_string_len(objtext, info, title, titlelen);
+           sfree(title);
+           objtext(info, "\n");
+       }
+       objtext(info, "/Producer ");
+       sprintf(buf, "Halibut, %s", version);
+       pdf_string(objtext, info, buf);
+       objtext(info, "\n>>\n");
+    }
+
     cat = new_object(&olist);
     if (doc->n_outline_elements > 0)
        outlines = new_object(&olist);
@@ -123,7 +150,7 @@ void pdf_backend(paragraph *sourceform, keywordlist *keywords,
        objtext(font, "<<\n/Type /Font\n/Subtype /Type1\n/Name /");
        objtext(font, fe->name);
        objtext(font, "\n/BaseFont /");
-       objtext(font, fe->font->name);
+       objtext(font, fe->font->info->name);
        objtext(font, "\n/Encoding <<\n/Type /Encoding\n/Differences [");
 
        for (i = 0; i < 256; i++) {
@@ -137,29 +164,99 @@ void pdf_backend(paragraph *sourceform, keywordlist *keywords,
 
        objtext(font, "\n]\n>>\n");
 
-       {
+#define FF_FIXEDPITCH  0x00000001
+#define FF_SERIF       0x00000002
+#define FF_SYMBOLIC    0x00000004
+#define FF_SCRIPT      0x00000008
+#define FF_NONSYMBOLIC 0x00000020
+#define FF_ITALIC      0x00000040
+#define FF_ALLCAP      0x00010000
+#define FF_SMALLCAP    0x00020000
+#define FF_FORCEBOLD   0x00040000
+
+       if (!is_std_font(fe->font->info->name)){
            object *widths = new_object(&olist);
-           objtext(font, "/FirstChar 0\n/LastChar 255\n/Widths ");
+           object *fontdesc = new_object(&olist);
+           int firstchar = -1, lastchar = -1;
+           char buf[80];
+           font_info const *fi = fe->font->info;
+           int flags;
+           for (i = 0; i < 256; i++)
+               if (fe->indices[i] >= 0) {
+                   if (firstchar < 0) firstchar = i;
+                   lastchar = i;
+               }
+           sprintf(buf, "/FirstChar %d\n/LastChar %d\n/Widths ",
+                   firstchar, lastchar);
+           objtext(font, buf);
            objref(font, widths);
            objtext(font, "\n");
            objtext(widths, "[\n");
-           for (i = 0; i < 256; i++) {
-               char buf[80];
+           for (i = firstchar; i <= lastchar; i++) {
                double width;
                if (fe->indices[i] < 0)
                    width = 0.0;
                else
-                   width = fe->font->widths[fe->indices[i]];
-               sprintf(buf, "%g\n", 1000.0 * width / 4096.0);
+                   width = fi->widths[fe->indices[i]];
+               sprintf(buf, "%g\n", 1000.0 * width / FUNITS_PER_PT);
                objtext(widths, buf);
            }
            objtext(widths, "]\n");
+           objtext(font, "/FontDescriptor ");
+           objref(font, fontdesc);
+           objtext(fontdesc, "<<\n/Type /FontDescriptor\n/Name /");
+           objtext(fontdesc, fi->name);
+           flags = 0;
+           if (fi->italicangle) flags |= FF_ITALIC;
+           flags |= FF_NONSYMBOLIC;
+           sprintf(buf, "\n/Flags %d\n", flags);
+           objtext(fontdesc, buf);
+           sprintf(buf, "/FontBBox [%g %g %g %g]\n", fi->fontbbox[0],
+                   fi->fontbbox[1], fi->fontbbox[2], fi->fontbbox[3]);
+           objtext(fontdesc, buf);
+           sprintf(buf, "/ItalicAngle %g\n", fi->italicangle);
+           objtext(fontdesc, buf);
+           sprintf(buf, "/Ascent %g\n", fi->ascent);
+           objtext(fontdesc, buf);
+           sprintf(buf, "/Descent %g\n", fi->descent);
+           objtext(fontdesc, buf);
+           sprintf(buf, "/CapHeight %g\n", fi->capheight);
+           objtext(fontdesc, buf);
+           sprintf(buf, "/XHeight %g\n", fi->xheight);
+           objtext(fontdesc, buf);
+           sprintf(buf, "/StemH %g\n", fi->stemh);
+           objtext(fontdesc, buf);
+           sprintf(buf, "/StemV %g\n", fi->stemv);
+           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);
+               objtext(fontdesc, "/FontFile ");
+               objref(fontdesc, fontfile);
+           }
+           objtext(fontdesc, "\n>>\n");
        }
 
-       objtext(font, ">>\n");
+       objtext(font, "\n>>\n");
     }
     objtext(resources, ">>\n>>\n");
 
+    {
+       char buf[255];
+       mediabox = new_object(&olist);
+       sprintf(buf, "[0 0 %g %g]\n",
+               doc->paper_width / FUNITS_PER_PT,
+               doc->paper_height / FUNITS_PER_PT);
+       objtext(mediabox, buf);
+    }
+
     /*
      * Define the page objects for each page, and get each one
      * ready to have a `Parent' specification added to it.
@@ -175,7 +272,7 @@ void pdf_backend(paragraph *sourceform, keywordlist *keywords,
     /*
      * Recursively build the page tree.
      */
-    make_pages_node(pages, NULL, doc->pages, NULL, resources);
+    make_pages_node(pages, NULL, doc->pages, NULL, resources, mediabox);
 
     /*
      * Create and render the individual pages.
@@ -200,11 +297,9 @@ void pdf_backend(paragraph *sourceform, keywordlist *keywords,
         * that it's inheritable and may be omitted if it's present
         * in a Pages node. In our case it is: it's present in the
         * topmost /Pages node because we carefully put it there.
-        * So we don't need a /Resources entry here.
+        * So we don't need a /Resources entry here.  The same applies
+        * to /MediaBox.
         */
-       sprintf(buf, "/MediaBox [0 0 %g %g]\n",
-               doc->paper_width / 4096.0, doc->paper_height / 4096.0);
-       objtext(opage, buf);
 
        /*
         * Now we're ready to define a content stream containing
@@ -220,8 +315,9 @@ void pdf_backend(paragraph *sourceform, keywordlist *keywords,
         */
        for (r = page->first_rect; r; r = r->next) {
            char buf[512];
-           sprintf(buf, "%g %g %g %g re f\n", r->x / 4096.0,
-                   r->y / 4096.0, r->w / 4096.0, r->h / 4096.0);
+           sprintf(buf, "%g %g %g %g re f\n",
+                   r->x / FUNITS_PER_PT, r->y / FUNITS_PER_PT,
+                   r->w / FUNITS_PER_PT, r->h / FUNITS_PER_PT);
            objstream(cstr, buf);
        }
 
@@ -265,10 +361,11 @@ void pdf_backend(paragraph *sourceform, keywordlist *keywords,
                 */
                if (lx < 0) {
                    sprintf(buf, "1 0 0 1 %g %g Tm ",
-                           frag->x/4096.0, frag->y/4096.0);
+                           frag->x/FUNITS_PER_PT, frag->y/FUNITS_PER_PT);
                } else {
                    sprintf(buf, "%g %g Td ",
-                           (frag->x - lx)/4096.0, (frag->y - ly)/4096.0);
+                           (frag->x - lx)/FUNITS_PER_PT,
+                           (frag->y - ly)/FUNITS_PER_PT);
                }
                objstream(cstr, buf);
                lx = x = frag->x;
@@ -291,7 +388,7 @@ void pdf_backend(paragraph *sourceform, keywordlist *keywords,
                        if (frag->x != x) {
                            sprintf(buf, "%g",
                                    (x - frag->x) * 1000.0 /
-                                   (4096.0 * frag->fontsize));
+                                   (FUNITS_PER_PT * frag->fontsize));
                            objstream(cstr, buf);
                        }
                        pdf_string(objstream, cstr, frag->text);
@@ -330,8 +427,8 @@ void pdf_backend(paragraph *sourceform, keywordlist *keywords,
 
                objtext(annot, "<<\n/Type /Annot\n/Subtype /Link\n/Rect [");
                sprintf(buf, "%g %g %g %g",
-                       xr->lx / 4096.0, xr->by / 4096.0,
-                       xr->rx / 4096.0, xr->ty / 4096.0);
+                       xr->lx / FUNITS_PER_PT, xr->by / FUNITS_PER_PT,
+                       xr->rx / FUNITS_PER_PT, xr->ty / FUNITS_PER_PT);
                objtext(annot, buf);
                objtext(annot, "]\n/Border [0 0 0]\n");
 
@@ -453,8 +550,8 @@ void pdf_backend(paragraph *sourceform, keywordlist *keywords,
     /*
      * Trailer
      */
-    fprintf(fp, "trailer\n<<\n/Size %d\n/Root %d 0 R\n>>\n",
-           olist.tail->number + 1, cat->number);
+    fprintf(fp, "trailer\n<<\n/Size %d\n/Root %d 0 R\n/Info %d 0 R\n>>\n",
+           olist.tail->number + 1, cat->number, info->number);
     fprintf(fp, "startxref\n%d\n%%%%EOF\n", fileoff);
 
     fclose(fp);
@@ -464,7 +561,7 @@ void pdf_backend(paragraph *sourceform, keywordlist *keywords,
 
 static object *new_object(objlist *list)
 {
-    object *obj = mknew(object);
+    object *obj = snew(object);
 
     obj->list = list;
 
@@ -505,8 +602,24 @@ static void objref(object *o, object *dest)
     rdaddsc(&o->main, buf);
 }
 
+static char const * const stdfonts[] = {
+    "Times-Roman", "Times-Bold", "Times-Italic", "Times-BoldItalic",
+    "Helvetica", "Helvetica-Bold", "Helvetica-Oblique","Helvetica-BoldOblique",
+    "Courier", "Courier-Bold", "Courier-Oblique", "Courier-BoldOblique",
+    "Symbol", "ZapfDingbats"
+};
+
+static int is_std_font(char const *name) {
+    unsigned i;
+    for (i = 0; i < lenof(stdfonts); i++)
+       if (strcmp(name, stdfonts[i]) == 0)
+           return TRUE;
+    return FALSE;
+}
+
 static void make_pages_node(object *node, object *parent, page_data *first,
-                           page_data *last, object *resources)
+                           page_data *last, object *resources,
+                           object *mediabox)
 {
     int count;
     page_data *page;
@@ -554,7 +667,8 @@ static void make_pages_node(object *node, object *parent, page_data *first,
                objtext((object *)thisfirst->spare, "\n");
            } else {
                object *newnode = new_object(node->list);
-               make_pages_node(newnode, node, thisfirst, thislast, NULL);
+               make_pages_node(newnode, node, thisfirst, thislast,
+                               NULL, NULL);
                objref(node, newnode);
            }
            objtext(node, "\n");
@@ -581,6 +695,11 @@ static void make_pages_node(object *node, object *parent, page_data *first,
        objref(node, resources);
        objtext(node, "\n");
     }
+    if (mediabox) {
+       objtext(node, "/MediaBox ");
+       objref(node, mediabox);
+       objtext(node, "\n");
+    }
 
     objtext(node, ">>\n");
 }
@@ -648,6 +767,7 @@ static int make_outline(object *parent, outline_element *items, int n,
        last = curr;
        objtext(curr, "<<\n/Title ");
        pdf_string_len(objtext, curr, title, titlelen);
+       sfree(title);
        objtext(curr, "\n/Parent ");
        objref(curr, parent);
        objtext(curr, "\n/Dest [");