static void make_pages_node(object *node, object *parent, page_data *first,
page_data *last, object *resources);
+static int make_outline(object *parent, outline_element *start, int n,
+ int open);
void pdf_backend(paragraph *sourceform, keywordlist *keywords,
indexdata *idx, void *vdoc) {
filename = dupstr("output.pdf");
for (p = sourceform; p; p = p->next) {
- p->private_data = NULL;
if (p->type == para_Config && p->parent) {
if (!ustricmp(p->keyword, L"pdf-filename")) {
sfree(filename);
resources = new_object(&olist);
/*
- * We currently don't support outlines, so here's a null
- * outlines dictionary.
- */
- objtext(outlines, "<<\n/Type Outlines\n/Count 0\n>>\n");
-
- /*
* The catalogue just contains references to the outlines and
* pages objects.
*/
objref(cat, outlines);
objtext(cat, "\n/Pages ");
objref(cat, pages);
- objtext(cat, "\n>>\n");
+ objtext(cat, "\n/PageMode /UseOutlines\n>>\n");
/*
* Set up the resources dictionary, which mostly means
pageno = 0;
for (page = doc->pages; page; page = page->next) {
object *opage, *cstr;
+ rect *r;
text_fragment *frag;
char buf[256];
objref(opage, cstr);
objtext(opage, "\n");
+ /*
+ * Render any rectangles on the page.
+ */
+ 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);
+ objstream(cstr, buf);
+ }
+
objstream(cstr, "BT\n");
for (frag = page->first_text; frag; frag = frag->next) {
char *c;
}
objstream(cstr, "ET");
+ /*
+ * Also, we want an annotation dictionary containing the
+ * cross-references from this page.
+ */
+ if (page->first_xref) {
+ xref *xr;
+ objtext(opage, "/Annots [\n");
+
+ for (xr = page->first_xref; xr; xr = xr->next) {
+ object *annot;
+ char buf[256];
+
+ annot = new_object(&olist);
+ objref(opage, annot);
+ objtext(opage, "\n");
+
+ 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);
+ objtext(annot, buf);
+ objtext(annot, "]\n/Border [0 0 0]\n");
+
+ if (xr->dest.type == PAGE) {
+ objtext(annot, "/Dest [");
+ objref(annot, (object *)xr->dest.page->spare);
+ objtext(annot, " /XYZ null null null]\n");
+ } else {
+ char *p;
+
+ objtext(annot, "/A <<\n/Type /Action\n/S /URI\n/URI (");
+ for (p = xr->dest.url; *p; p++) {
+ char c[2];
+ c[0] = *p;
+ c[1] = '\0';
+ if (*p == '(' || *p == ')' || *p == '\\')
+ objtext(annot, "\\");
+ objtext(annot, c);
+ }
+ objtext(annot, ")\n>>\n");
+ }
+
+ objtext(annot, ">>\n");
+ }
+
+ objtext(opage, "]\n");
+ }
+
objtext(opage, ">>\n");
}
/*
+ * Set up the outlines dictionary.
+ */
+ {
+ int topcount;
+ char buf[80];
+
+ objtext(outlines, "<<\n/Type /Outlines\n");
+ topcount = make_outline(outlines, doc->outline_elements,
+ doc->n_outline_elements, TRUE);
+ sprintf(buf, "/Count %d\n>>\n", topcount);
+ objtext(outlines, buf);
+ }
+
+ /*
* Assemble the final linear form of every object.
*/
for (o = olist.head; o; o = o->next) {
objtext(node, ">>\n");
}
+
+/*
+ * In text on the page, PDF uses the PostScript font model, which
+ * means that glyphs are identified by PS strings and hence font
+ * encoding can be managed independently of the supplied encoding
+ * of the font. However, in the document outline, the PDF spec
+ * simply asks for ordinary text strings without mentioning what
+ * character set they are supposed to be interpreted in.
+ *
+ * Therefore, for the moment, I'm going to assume they're US-ASCII
+ * only. If anyone knows better, they should let me know :-/
+ */
+static int pdf_convert(wchar_t *s, char **result) {
+ int doing = (result != 0);
+ int ok = TRUE;
+ char *p = NULL;
+ int plen = 0, psize = 0;
+
+ for (; *s; s++) {
+ wchar_t c = *s;
+ char outc;
+
+ if (c >= 32 && c <= 126) {
+ /* Char is OK. */
+ outc = (char)c;
+ } else {
+ /* Char is not OK. */
+ ok = FALSE;
+ outc = 0xBF; /* approximate the good old DEC `uh?' */
+ }
+ if (doing) {
+ if (plen >= psize) {
+ psize = plen + 256;
+ p = resize(p, psize);
+ }
+ p[plen++] = outc;
+ }
+ }
+ if (doing) {
+ p = resize(p, plen+1);
+ p[plen] = '\0';
+ *result = p;
+ }
+ return ok;
+}
+
+static int make_outline(object *parent, outline_element *items, int n,
+ int open)
+{
+ int level, totalcount = 0;
+ outline_element *itemp;
+ object *curr, *prev = NULL, *first = NULL, *last = NULL;
+
+ assert(n > 0);
+
+ level = items->level;
+
+ while (n > 0) {
+ char *title, *p;
+
+ /*
+ * Here we expect to be sitting on an item at the given
+ * level. So we start by constructing an outline entry for
+ * that item.
+ */
+ assert(items->level == level);
+
+ pdf_convert(items->pdata->outline_title, &title);
+
+ totalcount++;
+ curr = new_object(parent->list);
+ if (!first) first = curr;
+ last = curr;
+ objtext(curr, "<<\n/Title (");
+ for (p = title; *p; p++) {
+ char c[2];
+ if (*p == '\\' || *p == '(' || *p == ')')
+ objtext(curr, "\\");
+ c[0] = *p;
+ c[1] = '\0';
+ objtext(curr, c);
+ }
+ objtext(curr, ")\n/Parent ");
+ objref(curr, parent);
+ objtext(curr, "\n/Dest [");
+ objref(curr, (object *)items->pdata->first->page->spare);
+ objtext(curr, " /XYZ null null null]\n");
+ if (prev) {
+ objtext(curr, "/Prev ");
+ objref(curr, prev);
+ objtext(curr, "\n");
+
+ objtext(prev, "/Next ");
+ objref(prev, curr);
+ objtext(prev, "\n>>\n");
+ }
+ prev = curr;
+
+ items++, n--;
+ for (itemp = items; itemp < items+n && itemp->level > level;
+ itemp++);
+
+ if (itemp > items) {
+ char buf[80];
+ int count = make_outline(curr, items, itemp - items, FALSE);
+ if (!open)
+ count = -count;
+ else
+ totalcount += count;
+ sprintf(buf, "/Count %d\n", count);
+ objtext(curr, buf);
+ }
+
+ n -= itemp - items;
+ items = itemp;
+ }
+ objtext(prev, ">>\n");
+
+ assert(first && last);
+ objtext(parent, "/First ");
+ objref(parent, first);
+ objtext(parent, "\n/Last ");
+ objref(parent, last);
+ objtext(parent, "\n");
+
+ return totalcount;
+}