shithub: neatpost

Download patch

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)