ref: 91a48e11ca156dd5c8c5cd6a9401763c6aac7635
parent: 825749e792f7641f276439a9da067475ce5f8113
author: Ali Gholami Rudi <ali@rudi.ir>
date: Sun Jan 12 12:18:47 EST 2020
post: bookmark and named destination support for neatpdf
--- a/pdf.c
+++ b/pdf.c
@@ -19,6 +19,8 @@
static int obj_sz, obj_n; /* number of pdf objects */
static int *page_id; /* page object ids */
static int page_sz, page_n; /* number of pages */
+static int pdf_outline; /* pdf outline hierarchiy */
+static int pdf_dests; /* named destinations */
static struct sbuf *pg; /* current page contents */
static int o_f, o_s, o_m; /* font and size */
@@ -728,18 +730,95 @@
pdfout(" /Subtype /Link\n");
pdfout(" /Rect [%s", pdfpos(o_h, o_v));
pdfout(" %s]\n", pdfpos(o_h + hwid, o_v + vwid));
- /* only external links are supported */
- pdfout(" /A << /S /URI /URI (%s) >>\n", lnk);
+ if (lnk[0] == '#') { /* internal links */
+ pdfout(" /A << /S /GoTo /D (%s) >>\n", lnk + 1);
+ } else { /* external links */
+ pdfout(" /A << /S /URI /URI (%s) >>\n", lnk);
+ }
pdfout(">>\n");
obj_end();
}
-void outname(char *name, int page, int off)
+void outname(int n, char (*desc)[64], int *page, int *off)
{
+ int i;
+ o_flush();
+ pdf_dests = obj_beg(0);
+ pdfout("<<\n");
+ for (i = 0; i < n; i++) {
+ if (page[i] > 0 && page[i] - 1 < page_n)
+ pdfout(" /%s [ %d 0 R /XYZ 0 %d 0 ]\n",
+ desc[i], page_id[page[i] - 1],
+ pdf_height - (off[i] * 72 / dev_res));
+ }
+ pdfout(">>\n");
+ obj_end();
}
void outmark(int n, char (*desc)[256], int *page, int *off, int *level)
{
+ int *objs = malloc(n * sizeof(objs[0]));
+ int i, j;
+ int cnt = 0;
+ /* allocating objects */
+ pdf_outline = obj_map();
+ for (i = 0; i < n; i++)
+ objs[i] = obj_map();
+ o_flush();
+ /* root object */
+ obj_beg(pdf_outline);
+ pdfout("<<\n");
+ for (i = 0; i < n; i++)
+ if (level[i] == 0)
+ cnt++;
+ pdfout(" /Count %d\n", cnt);
+ pdfout(" /First %d 0 R\n", objs[0]);
+ for (i = n - 1; i > 0 && level[i] > 0; i--)
+ ;
+ pdfout(" /Last %d 0 R\n", objs[i]);
+ pdfout(">>\n");
+ obj_end();
+ /* other objects */
+ for (i = 0; i < n; i++) {
+ int cnt = 0;
+ for (j = i + 1; j < n && level[j] > level[i]; j++)
+ if (level[j] == level[i] + 1)
+ cnt++;
+ obj_beg(objs[i]);
+ pdfout("<<\n");
+ pdfout(" /Title (%s)\n", desc[i]);
+ /* the parent field */
+ for (j = i - 1; j >= 0 && level[j] >= level[i]; j--)
+ ;
+ pdfout(" /Parent %d 0 R\n", j >= 0 ? objs[j] : pdf_outline);
+ /* the next field */
+ for (j = i + 1; j < n && level[j] > level[i]; j++)
+ ;
+ if (j < n && level[j] == level[i])
+ pdfout(" /Next %d 0 R\n", objs[j]);
+ /* the prev field */
+ for (j = i - 1; j >= 0 && level[j] > level[i]; j--)
+ ;
+ if (j >= 0 && level[j] == level[i])
+ pdfout(" /Prev %d 0 R\n", objs[j]);
+ /* node children */
+ if (cnt) {
+ int last = 0;
+ pdfout(" /Count %d\n", cnt);
+ pdfout(" /First %d 0 R\n", objs[i + 1]);
+ for (j = i + 1; j < n && level[j] > level[i]; j++)
+ if (level[j] == level[i] + 1)
+ last = j;
+ pdfout(" /Last %d 0 R\n", objs[last]);
+ }
+ if (page[i] > 0 && page[i] - 1 < page_n)
+ pdfout(" /Dest [ %d 0 R /XYZ 0 %d 0 ]\n",
+ page_id[page[i] - 1],
+ pdf_height - (off[i] * 72 / dev_res));
+ pdfout(">>\n");
+ obj_end();
+ }
+ free(objs);
}
void outinfo(char *kwd, char *val)
@@ -905,6 +984,10 @@
pdfout("<<\n");
pdfout(" /Type /Catalog\n");
pdfout(" /Pages %d 0 R\n", pdf_pages);
+ if (pdf_dests > 0)
+ pdfout(" /Dests %d 0 R\n", pdf_dests);
+ if (pdf_outline > 0)
+ pdfout(" /Outlines %d 0 R\n", pdf_outline);
pdfout(">>\n");
obj_end();
/* fonts */
--- a/post.c
+++ b/post.c
@@ -1,7 +1,7 @@
/*
* NEATPOST: NEATROFF'S POSTSCRIPT/PDF POSTPROCESSOR
*
- * Copyright (C) 2013-2018 Ali Gholami Rudi <ali at rudi dot ir>
+ * Copyright (C) 2013-2020 Ali Gholami Rudi <ali at rudi dot ir>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -27,7 +27,7 @@
static int ps_linewidth = 40; /* drawing line thickness in thousandths of an em */
static int o_pages; /* output pages */
-/* bookmark management */
+/* bookmarks */
static char (*mark_desc)[256]; /* bookmark description */
static int *mark_page; /* bookmark page */
static int *mark_offset; /* bookmark offset */
@@ -35,6 +35,13 @@
static int mark_n; /* number of bookmarks */
static int mark_sz; /* allocated size of bookmark arrays */
+/* named destinations */
+static char (*name_desc)[64]; /* reference name */
+static int *name_page; /* reference page */
+static int *name_offset; /* reference offset */
+static int name_n; /* number of references */
+static int name_sz; /* allocated size of name arrays */
+
static int next(void)
{
return getc(stdin);
@@ -303,24 +310,22 @@
outpdf(path, hwid, vwid);
}
if (!strcmp("name", cmd)) {
- char name[1 << 10];
- int page = 0, offset = 0;
+ char *spec = arg;
int nspec;
- nspec = sscanf(arg, "%s %d %d", name, &page, &offset);
- if (name[0] && nspec > 1)
- outname(name, page == o_pages ? 0 : page, offset);
+ if (name_n == name_sz) {
+ name_sz = name_sz == 0 ? 128 : name_sz * 2;
+ name_desc = mextend(name_desc, name_n, name_sz, sizeof(name_desc[0]));
+ name_page = mextend(name_page, name_n, name_sz, sizeof(name_page[0]));
+ name_offset = mextend(name_offset, name_n, name_sz, sizeof(name_offset[0]));
+ }
+ spec = strcut(name_desc[name_n], spec);
+ nspec = sscanf(spec, "%d %d", &name_page[name_n], &name_offset[name_n]);
+ if (name_desc[name_n][0] && nspec > 0)
+ name_n++;
}
- if (!strcmp("link", cmd)) {
- char link[1 << 12];
- int hwid, vwid, nspec;
- char *spec = arg;
- spec = strcut(link, spec);
- nspec = sscanf(spec, "%d %d", &hwid, &vwid);
- if (link[0] && nspec == 2)
- outlink(link, hwid, vwid);
- }
if (!strcmp("mark", cmd)) {
char *spec = arg;
+ int nspec;
if (mark_n == mark_sz) {
mark_sz = mark_sz == 0 ? 128 : mark_sz * 2;
mark_desc = mextend(mark_desc, mark_n, mark_sz, sizeof(mark_desc[0]));
@@ -329,11 +334,20 @@
mark_level = mextend(mark_level, mark_n, mark_sz, sizeof(mark_level[0]));
}
spec = strcut(mark_desc[mark_n], spec);
- sscanf(spec, "%d %d %d", &mark_page[mark_n],
+ nspec = sscanf(spec, "%d %d %d", &mark_page[mark_n],
&mark_offset[mark_n], &mark_level[mark_n]);
- if (mark_desc[mark_n][0])
+ if (mark_desc[mark_n][0] && nspec > 0)
mark_n++;
}
+ if (!strcmp("link", cmd)) {
+ char link[1 << 12];
+ int hwid, vwid, nspec;
+ char *spec = arg;
+ spec = strcut(link, spec);
+ nspec = sscanf(spec, "%d %d", &hwid, &vwid);
+ if (link[0] && nspec == 2)
+ outlink(link, hwid, vwid);
+ }
if (!strcmp("info", cmd)) {
char *spec = arg;
char kwd[128];
@@ -470,7 +484,10 @@
postcmd(c);
if (o_pages)
docpageend(o_pages);
- outmark(mark_n, mark_desc, mark_page, mark_offset, mark_level);
+ if (name_n)
+ outname(name_n, name_desc, name_page, name_offset);
+ if (mark_n)
+ outmark(mark_n, mark_desc, mark_page, mark_offset, mark_level);
}
static struct paper {
@@ -566,5 +583,8 @@
free(mark_page);
free(mark_offset);
free(mark_level);
+ free(name_desc);
+ free(name_page);
+ free(name_offset);
return 0;
}
--- a/post.h
+++ b/post.h
@@ -60,8 +60,8 @@
void outeps(char *eps, int hwid, int vwid);
void outpdf(char *pdf, int hwid, int vwid);
void outlink(char *dst, int hwid, int vwid);
-void outname(char *name, int page, int off);
void outmark(int n, char (*desc)[256], int *page, int *off, int *level);
+void outname(int n, char (*desc)[64], int *page, int *off);
void outinfo(char *kwd, char *val);
void outpage(void);
void outmnt(int f);
--- a/ps.c
+++ b/ps.c
@@ -323,16 +323,18 @@
}
}
-void outname(char *name, int page, int off)
+void outname(int n, char (*desc)[64], int *page, int *off)
{
+ int i;
o_flush();
- outf("[ /Dest /%s", name);
- if (page > 0)
- outf(" /Page %d", page);
- if (off > 0)
- outf(" /View [/XYZ null %d null]",
- (ps_height - off) * 72 / dev_res);
- outf(" /DEST pdfmark\n");
+ for (i = 0; i < n; i++) {
+ outf("[ /Dest /%s", desc[i]);
+ outf(" /Page %d", page[i]);
+ if (off[i] > 0)
+ outf(" /View [/XYZ null %d null]",
+ (ps_height - off[i]) * 72 / dev_res);
+ outf(" /DEST pdfmark\n");
+ }
}
void outmark(int n, char (*desc)[256], int *page, int *off, int *level)