source@mandoc.bsd.lv
 help / color / mirror / Atom feed
* mdocml: Initial PDF shim over PS.
@ 2010-07-25 11:44 kristaps
  0 siblings, 0 replies; only message in thread
From: kristaps @ 2010-07-25 11:44 UTC (permalink / raw)
  To: source

Log Message:
-----------
Initial PDF shim over PS.  This produces working PDF output with -Tpdf.
It's currently missing the xref table, so you'll get a warning in most
PDF viewers).  It also produces lots of redundant output, which will go
away once I get a better handle on the PDF spec.  The code doesn't
really touch any existing functionality; it's a bunch of conditionals
atop the -Tps (term_ps.c) implementation.  I'm checking it in now to
have it exist and be auditable.  It needs clean-up, polish, and general
care (and xref!).

Modified Files:
--------------
    mdocml:
        main.c
        main.h
        mandoc.1
        term.h
        term_ps.c

Revision Data
-------------
Index: term.h
===================================================================
RCS file: /usr/vhosts/mdocml.bsd.lv/cvs/mdocml/term.h,v
retrieving revision 1.74
retrieving revision 1.75
diff -Lterm.h -Lterm.h -u -p -r1.74 -r1.75
--- term.h
+++ term.h
@@ -27,7 +27,8 @@ enum	termenc {
 
 enum	termtype {
 	TERMTYPE_CHAR,
-	TERMTYPE_PS
+	TERMTYPE_PS,
+	TERMTYPE_PDF
 };
 
 enum	termfont {
@@ -63,6 +64,9 @@ struct	termp_ps {
 	size_t		  left;		/* body left (AFM units) */
 	size_t		  header;	/* header pos (AFM units) */
 	size_t		  footer;	/* footer pos (AFM units) */
+	size_t		  pdfbytes;
+	size_t		  pdflastpg;
+	size_t		  pdfbody;
 };
 
 struct	termp {
Index: mandoc.1
===================================================================
RCS file: /usr/vhosts/mdocml.bsd.lv/cvs/mdocml/mandoc.1,v
retrieving revision 1.72
retrieving revision 1.73
diff -Lmandoc.1 -Lmandoc.1 -u -p -r1.72 -r1.73
--- mandoc.1
+++ mandoc.1
@@ -192,6 +192,10 @@ Implies
 .Fl W Ns Cm all
 and
 .Fl f Ns Cm strict .
+.It Fl T Ns Cm pdf
+Produce PDF output.
+See
+.Sx PDF Output .
 .It Fl T Ns Cm ps
 Produce PostScript output.
 See
@@ -331,6 +335,14 @@ If an unknown value is encountered,
 .Ar letter
 is used.
 .El
+.Ss PDF Output
+PDF-1.1 output may be generated by
+.Fl T Ns Cm pdf .
+See
+.Sx PostScript Output
+for
+.Fl O
+arguments and defaults.
 .Ss XHTML Output
 Output produced by
 .Fl T Ns Cm xhtml
Index: term_ps.c
===================================================================
RCS file: /usr/vhosts/mdocml.bsd.lv/cvs/mdocml/term_ps.c,v
retrieving revision 1.36
retrieving revision 1.37
diff -Lterm_ps.c -Lterm_ps.c -u -p -r1.36 -r1.37
--- term_ps.c
+++ term_ps.c
@@ -373,6 +373,7 @@ static	double		  ps_hspan(const struct t
 static	size_t		  ps_width(const struct termp *, char);
 static	void		  ps_advance(struct termp *, size_t);
 static	void		  ps_begin(struct termp *);
+static	void		  ps_closepage(struct termp *);
 static	void		  ps_end(struct termp *);
 static	void		  ps_endline(struct termp *);
 static	void		  ps_fclose(struct termp *);
@@ -382,12 +383,39 @@ static	void		  ps_pletter(struct termp *
 static	void		  ps_printf(struct termp *, const char *, ...);
 static	void		  ps_putchar(struct termp *, char);
 static	void		  ps_setfont(struct termp *, enum termfont);
+static	struct termp	 *pspdf_alloc(char *);
+
+
+void *
+pdf_alloc(char *outopts)
+{
+	struct termp	*p;
+
+	if (NULL == (p = pspdf_alloc(outopts)))
+		return(p);
+
+	p->type = TERMTYPE_PDF;
+	return(p);
+}
 
 
 void *
 ps_alloc(char *outopts)
 {
 	struct termp	*p;
+
+	if (NULL == (p = pspdf_alloc(outopts)))
+		return(p);
+
+	p->type = TERMTYPE_PS;
+	return(p);
+}
+
+
+static struct termp *
+pspdf_alloc(char *outopts)
+{
+	struct termp	*p;
 	size_t		 pagex, pagey, marginx, marginy, lineheight;
 	const char	*toks[2];
 	const char	*pp;
@@ -402,7 +430,6 @@ ps_alloc(char *outopts)
 	p->endline = ps_endline;
 	p->hspan = ps_hspan;
 	p->letter = ps_letter;
-	p->type = TERMTYPE_PS;
 	p->width = ps_width;
 	
 	toks[0] = "paper";
@@ -487,7 +514,7 @@ ps_alloc(char *outopts)
 
 
 void
-ps_free(void *arg)
+pspdf_free(void *arg)
 {
 	struct termp	*p;
 
@@ -504,7 +531,7 @@ static void
 ps_printf(struct termp *p, const char *fmt, ...)
 {
 	va_list		 ap;
-	int		 pos;
+	int		 pos, len;
 
 	va_start(ap, fmt);
 
@@ -515,8 +542,10 @@ ps_printf(struct termp *p, const char *f
 	 */
 
 	if ( ! (PS_MARGINS & p->engine.ps.flags)) {
-		vprintf(fmt, ap);
+		len = vprintf(fmt, ap);
 		va_end(ap);
+		p->engine.ps.pdfbytes += /* LINTED */
+			len < 0 ? 0 : (size_t)len;
 		return;
 	}
 
@@ -529,10 +558,13 @@ ps_printf(struct termp *p, const char *f
 	PS_GROWBUF(p, PS_BUFSLOP);
 
 	pos = (int)p->engine.ps.psmargcur;
-	vsnprintf(&p->engine.ps.psmarg[pos], PS_BUFSLOP, fmt, ap);
-	p->engine.ps.psmargcur = strlen(p->engine.ps.psmarg);
+	len = vsnprintf(&p->engine.ps.psmarg[pos], PS_BUFSLOP, fmt, ap);
 
 	va_end(ap);
+
+	p->engine.ps.psmargcur = strlen(p->engine.ps.psmarg);
+	p->engine.ps.pdfbytes += /* LINTED */
+		len < 0 ? 0 : (size_t)len;
 }
 
 
@@ -545,6 +577,7 @@ ps_putchar(struct termp *p, char c)
 
 	if ( ! (PS_MARGINS & p->engine.ps.flags)) {
 		putchar(c);
+		p->engine.ps.pdfbytes++;
 		return;
 	}
 
@@ -556,10 +589,65 @@ ps_putchar(struct termp *p, char c)
 }
 
 
+static void
+ps_closepage(struct termp *p)
+{
+	int		 i;
+	size_t		 len;
+
+	assert(p->engine.ps.psmarg && p->engine.ps.psmarg[0]);
+	ps_printf(p, "%s", p->engine.ps.psmarg);
+
+	if (TERMTYPE_PS == p->type) {
+		ps_printf(p, "showpage\n");
+	} else {
+		ps_printf(p, "ET\n");
+		len = p->engine.ps.pdfbytes - p->engine.ps.pdflastpg;
+		ps_printf(p, "endstream\n");
+		ps_printf(p, "endobj\n");
+		ps_printf(p, "%zu 0 obj\n", 
+				p->engine.ps.pdfbody +
+				(p->engine.ps.pages + 1) * 4 + 1);
+		ps_printf(p, "%zu\n", len);
+		ps_printf(p, "endobj\n");
+		ps_printf(p, "%zu 0 obj\n", 
+				p->engine.ps.pdfbody +
+				(p->engine.ps.pages + 1) * 4 + 2);
+		ps_printf(p, "<<\n");
+		ps_printf(p, "/ProcSet [/PDF /Text]\n");
+		ps_printf(p, "/Font <<\n");
+		for (i = 0; i < (int)TERMFONT__MAX; i++) 
+			ps_printf(p, "/F%d %d 0 R\n", i, 3 + i);
+		ps_printf(p, ">>\n");
+		ps_printf(p, ">>\n");
+		ps_printf(p, "%zu 0 obj\n", 
+				p->engine.ps.pdfbody +
+				(p->engine.ps.pages + 1) * 4 + 3);
+		ps_printf(p, "<<\n");
+		ps_printf(p, "/Type /Page\n");
+		ps_printf(p, "/Parent 2 0 R\n");
+		ps_printf(p, "/Resources %zu 0 R\n",
+				p->engine.ps.pdfbody +
+				(p->engine.ps.pages + 1) * 4 + 2);
+		ps_printf(p, "/Contents %zu 0 R\n",
+				p->engine.ps.pdfbody +
+				(p->engine.ps.pages + 1) * 4);
+		ps_printf(p, ">>\n");
+		ps_printf(p, "endobj\n");
+	}
+
+	p->engine.ps.pages++;
+	p->engine.ps.psrow = p->engine.ps.top;
+	assert( ! (PS_NEWPAGE & p->engine.ps.flags));
+	p->engine.ps.flags |= PS_NEWPAGE;
+}
+
+
 /* ARGSUSED */
 static void
 ps_end(struct termp *p)
 {
+	size_t		 i, xref;
 
 	/*
 	 * At the end of the file, do one last showpage.  This is the
@@ -570,15 +658,59 @@ ps_end(struct termp *p)
 	if ( ! (PS_NEWPAGE & p->engine.ps.flags)) {
 		assert(0 == p->engine.ps.flags);
 		assert('\0' == p->engine.ps.last);
-		assert(p->engine.ps.psmarg && p->engine.ps.psmarg[0]);
-		printf("%s", p->engine.ps.psmarg);
-		p->engine.ps.pages++;
-		printf("showpage\n");
+		ps_closepage(p);
 	}
 
-	printf("%%%%Trailer\n");
-	printf("%%%%Pages: %zu\n", p->engine.ps.pages);
-	printf("%%%%EOF\n");
+	if (TERMTYPE_PS == p->type) {
+		ps_printf(p, "%%%%Trailer\n");
+		ps_printf(p, "%%%%Pages: %zu\n", p->engine.ps.pages);
+		ps_printf(p, "%%%%EOF\n");
+		return;
+	} 
+
+	ps_printf(p, "2 0 obj\n");
+	ps_printf(p, "<<\n");
+	ps_printf(p, "/Type /Pages\n");
+	ps_printf(p, "/MediaBox [0 0 %zu %zu]\n",
+			(size_t)AFM2PNT(p, p->engine.ps.width),
+			(size_t)AFM2PNT(p, p->engine.ps.height));
+
+	ps_printf(p, "/Count %zu\n", p->engine.ps.pages);
+	ps_printf(p, "/Kids [");
+
+	for (i = 0; i < p->engine.ps.pages; i++)
+		ps_printf(p, " %zu 0 R", 
+				p->engine.ps.pdfbody +
+				(i + 1) * 4 + 3);
+	ps_printf(p, "]\n");
+	ps_printf(p, ">>\n");
+	ps_printf(p, "endobj\n");
+	ps_printf(p, "%zu 0 obj\n",
+			p->engine.ps.pdfbody +
+			(p->engine.ps.pages * 4) + 4);
+	ps_printf(p, "<<\n");
+	ps_printf(p, "/Type /Catalog\n");
+	ps_printf(p, "/Pages 2 0 R\n");
+	ps_printf(p, ">>\n");
+	xref = p->engine.ps.pdfbytes;
+	ps_printf(p, "xref\n");
+	ps_printf(p, "0 %zu\n",
+			p->engine.ps.pdfbody +
+			(p->engine.ps.pages * 4) + 5);
+	ps_printf(p, "0000000000 65535 f\n");
+	ps_printf(p, "trailer\n");
+	ps_printf(p, "<<\n");
+	ps_printf(p, "/Size %zu\n", 
+			p->engine.ps.pdfbody +
+			(p->engine.ps.pages * 4) + 5);
+	ps_printf(p, "/Root %zu 0 R\n", 
+			p->engine.ps.pdfbody +
+			(p->engine.ps.pages * 4) + 4);
+	ps_printf(p, "/Info 1 0 R\n");
+	ps_printf(p, ">>\n");
+	ps_printf(p, "startxref\n");
+	ps_printf(p, "%zu\n", xref);
+	ps_printf(p, "%%%%EOF\n");
 }
 
 
@@ -598,6 +730,7 @@ ps_begin(struct termp *p)
 		p->engine.ps.psmarg[0] = '\0';
 	}
 
+	p->engine.ps.pdfbytes = 0;
 	p->engine.ps.psmargcur = 0;
 	p->engine.ps.flags = PS_MARGINS;
 	p->engine.ps.pscol = p->engine.ps.left;
@@ -627,21 +760,44 @@ ps_begin(struct termp *p)
 
 	t = time(NULL);
 
-	printf("%%!PS-Adobe-3.0\n");
-	printf("%%%%Creator: mandoc-%s\n", VERSION);
-	printf("%%%%CreationDate: %s", ctime(&t));
-	printf("%%%%DocumentData: Clean7Bit\n");
-	printf("%%%%Orientation: Portrait\n");
-	printf("%%%%Pages: (atend)\n");
-	printf("%%%%PageOrder: Ascend\n");
-	printf("%%%%DocumentMedia: Default %zu %zu 0 () ()\n",
-			(size_t)AFM2PNT(p, p->engine.ps.width),
-			(size_t)AFM2PNT(p, p->engine.ps.height));
-	printf("%%%%DocumentNeededResources: font");
-	for (i = 0; i < (int)TERMFONT__MAX; i++)
-		printf(" %s", fonts[i].name);
-	printf("\n%%%%EndComments\n");
+	if (TERMTYPE_PS == p->type) {
+		ps_printf(p, "%%!PS-Adobe-3.0\n");
+		ps_printf(p, "%%%%Creator: mandoc-%s\n", VERSION);
+		ps_printf(p, "%%%%CreationDate: %s", ctime(&t));
+		ps_printf(p, "%%%%DocumentData: Clean7Bit\n");
+		ps_printf(p, "%%%%Orientation: Portrait\n");
+		ps_printf(p, "%%%%Pages: (atend)\n");
+		ps_printf(p, "%%%%PageOrder: Ascend\n");
+		ps_printf(p, "%%%%DocumentMedia: "
+				"Default %zu %zu 0 () ()\n",
+				(size_t)AFM2PNT(p, p->engine.ps.width),
+				(size_t)AFM2PNT(p, p->engine.ps.height));
+		ps_printf(p, "%%%%DocumentNeededResources: font");
+
+		for (i = 0; i < (int)TERMFONT__MAX; i++)
+			ps_printf(p, " %s", fonts[i].name);
 
+		ps_printf(p, "\n%%%%EndComments\n");
+	} else {
+		ps_printf(p, "%%PDF-1.1\n");
+		ps_printf(p, "1 0 obj\n");
+		ps_printf(p, "<<\n");
+		ps_printf(p, "/Creator mandoc-%s\n", VERSION);
+		ps_printf(p, ">>\n");
+		ps_printf(p, "endobj\n");
+
+		for (i = 0; i < (int)TERMFONT__MAX; i++) {
+			ps_printf(p, "%d 0 obj\n", i + 3);
+			ps_printf(p, "<<\n");
+			ps_printf(p, "/Type /Font\n");
+			ps_printf(p, "/Subtype /Type1\n");
+			ps_printf(p, "/Name /F%zu\n", i);
+			ps_printf(p, "/BaseFont /%s\n", fonts[i].name);
+			ps_printf(p, ">>\n");
+		}
+	}
+
+	p->engine.ps.pdfbody = (size_t)TERMFONT__MAX + 3;
 	p->engine.ps.pscol = p->engine.ps.left;
 	p->engine.ps.psrow = p->engine.ps.top;
 	p->engine.ps.flags |= PS_NEWPAGE;
@@ -660,12 +816,24 @@ ps_pletter(struct termp *p, int c)
 	 */
 
 	if (PS_NEWPAGE & p->engine.ps.flags) {
-		printf("%%%%Page: %zu %zu\n", 
-				p->engine.ps.pages + 1, 
-				p->engine.ps.pages + 1);
-		ps_printf(p, "/%s %zu selectfont\n", 
-				fonts[(int)p->engine.ps.lastf].name, 
-				p->engine.ps.scale);
+		if (TERMTYPE_PS == p->type) {
+			ps_printf(p, "%%%%Page: %zu %zu\n", 
+					p->engine.ps.pages + 1, 
+					p->engine.ps.pages + 1);
+			ps_printf(p, "/%s %zu selectfont\n", 
+					fonts[(int)p->engine.ps.lastf].name, 
+					p->engine.ps.scale);
+		} else {
+			ps_printf(p, "%zu 0 obj\n", 
+					p->engine.ps.pdfbody +
+					(p->engine.ps.pages + 1) * 4);
+			ps_printf(p, "<<\n");
+			ps_printf(p, "/Length %zu 0 R\n", 
+					p->engine.ps.pdfbody +
+					(p->engine.ps.pages + 1) * 4 + 1);
+			ps_printf(p, ">>\nstream\n");
+		}
+		p->engine.ps.pdflastpg = p->engine.ps.pdfbytes;
 		p->engine.ps.flags &= ~PS_NEWPAGE;
 	}
 	
@@ -675,9 +843,17 @@ ps_pletter(struct termp *p, int c)
 	 */
 
 	if ( ! (PS_INLINE & p->engine.ps.flags)) {
-		ps_printf(p, "%.3f %.3f moveto\n(", 
-				AFM2PNT(p, p->engine.ps.pscol),
-				AFM2PNT(p, p->engine.ps.psrow));
+		if (TERMTYPE_PS != p->type) {
+			ps_printf(p, "BT\n/F%d %zu Tf\n", 
+					(int)p->engine.ps.lastf,
+					p->engine.ps.scale);
+			ps_printf(p, "%.3f %.3f Td\n(",
+					AFM2PNT(p, p->engine.ps.pscol),
+					AFM2PNT(p, p->engine.ps.psrow));
+		} else
+			ps_printf(p, "%.3f %.3f moveto\n(", 
+					AFM2PNT(p, p->engine.ps.pscol),
+					AFM2PNT(p, p->engine.ps.psrow));
 		p->engine.ps.flags |= PS_INLINE;
 	}
 
@@ -731,7 +907,11 @@ ps_pclose(struct termp *p)
 	if ( ! (PS_INLINE & p->engine.ps.flags))
 		return;
 	
-	ps_printf(p, ") show\n");
+	if (TERMTYPE_PS != p->type) {
+		ps_printf(p, ") Tj\nET\n");
+	} else
+		ps_printf(p, ") show\n");
+
 	p->engine.ps.flags &= ~PS_INLINE;
 }
 
@@ -865,13 +1045,7 @@ ps_endline(struct termp *p)
 		return;
 	}
 
-	assert(p->engine.ps.psmarg && p->engine.ps.psmarg[0]);
-	printf("%s", p->engine.ps.psmarg);
-	printf("showpage\n");
-	p->engine.ps.pages++;
-	p->engine.ps.psrow = p->engine.ps.top;
-	assert( ! (PS_NEWPAGE & p->engine.ps.flags));
-	p->engine.ps.flags |= PS_NEWPAGE;
+	ps_closepage(p);
 }
 
 
@@ -890,8 +1064,14 @@ ps_setfont(struct termp *p, enum termfon
 	if (PS_NEWPAGE & p->engine.ps.flags)
 		return;
 
-	ps_printf(p, "/%s %zu selectfont\n", 
-			fonts[(int)f].name, p->engine.ps.scale);
+	if (TERMTYPE_PS == p->type)
+		ps_printf(p, "/%s %zu selectfont\n", 
+				fonts[(int)f].name, 
+				p->engine.ps.scale);
+	else
+		ps_printf(p, "/F%d %zu Tf\n", 
+				(int)f, 
+				p->engine.ps.scale);
 }
 
 
Index: main.h
===================================================================
RCS file: /usr/vhosts/mdocml.bsd.lv/cvs/mdocml/main.h,v
retrieving revision 1.8
retrieving revision 1.9
diff -Lmain.h -Lmain.h -u -p -r1.8 -r1.9
--- main.h
+++ main.h
@@ -44,8 +44,9 @@ void		  tree_man(void *, const struct ma
 void		 *ascii_alloc(char *);
 void		  ascii_free(void *);
 
+void		 *pdf_alloc(char *);
 void		 *ps_alloc(char *);
-void		  ps_free(void *);
+void		  pspdf_free(void *);
 
 void		  terminal_mdoc(void *, const struct mdoc *);
 void		  terminal_man(void *, const struct man *);
Index: main.c
===================================================================
RCS file: /usr/vhosts/mdocml.bsd.lv/cvs/mdocml/main.c,v
retrieving revision 1.99
retrieving revision 1.100
diff -Lmain.c -Lmain.c -u -p -r1.99 -r1.100
--- main.c
+++ main.c
@@ -68,7 +68,8 @@ enum	outt {
 	OUTT_HTML,
 	OUTT_XHTML,
 	OUTT_LINT,
-	OUTT_PS
+	OUTT_PS,
+	OUTT_PDF
 };
 
 struct	curparse {
@@ -630,9 +631,13 @@ fdesc(struct curparse *curp)
 			curp->outdata = ascii_alloc(curp->outopts);
 			curp->outfree = ascii_free;
 			break;
+		case (OUTT_PDF):
+			curp->outdata = pdf_alloc(curp->outopts);
+			curp->outfree = pspdf_free;
+			break;
 		case (OUTT_PS):
 			curp->outdata = ps_alloc(curp->outopts);
-			curp->outfree = ps_free;
+			curp->outfree = pspdf_free;
 			break;
 		default:
 			break;
@@ -650,6 +655,8 @@ fdesc(struct curparse *curp)
 			curp->outman = tree_man;
 			curp->outmdoc = tree_mdoc;
 			break;
+		case (OUTT_PDF):
+			/* FALLTHROUGH */
 		case (OUTT_ASCII):
 			/* FALLTHROUGH */
 		case (OUTT_PS):
@@ -784,6 +791,8 @@ toptions(struct curparse *curp, char *ar
 		curp->outtype = OUTT_XHTML;
 	else if (0 == strcmp(arg, "ps"))
 		curp->outtype = OUTT_PS;
+	else if (0 == strcmp(arg, "pdf"))
+		curp->outtype = OUTT_PDF;
 	else {
 		fprintf(stderr, "%s: Bad argument\n", arg);
 		return(0);
--
 To unsubscribe send an email to source+unsubscribe@mdocml.bsd.lv

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2010-07-25 11:44 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-07-25 11:44 mdocml: Initial PDF shim over PS kristaps

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).