xref: /dragonfly/contrib/mdocml/term_ps.c (revision 36342e81)
1*36342e81SSascha Wildner /*	$Id: term_ps.c,v 1.54 2011/10/16 12:20:34 schwarze Exp $ */
280387638SSascha Wildner /*
3*36342e81SSascha Wildner  * Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
480387638SSascha Wildner  *
580387638SSascha Wildner  * Permission to use, copy, modify, and distribute this software for any
680387638SSascha Wildner  * purpose with or without fee is hereby granted, provided that the above
780387638SSascha Wildner  * copyright notice and this permission notice appear in all copies.
880387638SSascha Wildner  *
980387638SSascha Wildner  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1080387638SSascha Wildner  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1180387638SSascha Wildner  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1280387638SSascha Wildner  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1380387638SSascha Wildner  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1480387638SSascha Wildner  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1580387638SSascha Wildner  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1680387638SSascha Wildner  */
1780387638SSascha Wildner #ifdef HAVE_CONFIG_H
1880387638SSascha Wildner #include "config.h"
1980387638SSascha Wildner #endif
2080387638SSascha Wildner 
2180387638SSascha Wildner #include <sys/types.h>
2280387638SSascha Wildner 
2380387638SSascha Wildner #include <assert.h>
2480387638SSascha Wildner #include <stdarg.h>
2580387638SSascha Wildner #include <stdint.h>
2680387638SSascha Wildner #include <stdio.h>
2780387638SSascha Wildner #include <stdlib.h>
2880387638SSascha Wildner #include <string.h>
2980387638SSascha Wildner #include <time.h>
3080387638SSascha Wildner #include <unistd.h>
3180387638SSascha Wildner 
3280387638SSascha Wildner #include "mandoc.h"
3380387638SSascha Wildner #include "out.h"
3480387638SSascha Wildner #include "main.h"
3580387638SSascha Wildner #include "term.h"
3680387638SSascha Wildner 
37a4c7eb57SSascha Wildner /* These work the buffer used by the header and footer. */
38a4c7eb57SSascha Wildner #define	PS_BUFSLOP	  128
39a4c7eb57SSascha Wildner 
4080387638SSascha Wildner /* Convert PostScript point "x" to an AFM unit. */
4180387638SSascha Wildner #define	PNT2AFM(p, x) /* LINTED */ \
42a4c7eb57SSascha Wildner 	(size_t)((double)(x) * (1000.0 / (double)(p)->ps->scale))
4380387638SSascha Wildner 
4480387638SSascha Wildner /* Convert an AFM unit "x" to a PostScript points */
4580387638SSascha Wildner #define	AFM2PNT(p, x) /* LINTED */ \
46a4c7eb57SSascha Wildner 	((double)(x) / (1000.0 / (double)(p)->ps->scale))
4780387638SSascha Wildner 
4880387638SSascha Wildner struct	glyph {
4980387638SSascha Wildner 	unsigned short	  wx; /* WX in AFM */
5080387638SSascha Wildner };
5180387638SSascha Wildner 
5280387638SSascha Wildner struct	font {
5380387638SSascha Wildner 	const char	 *name; /* FontName in AFM */
5480387638SSascha Wildner #define	MAXCHAR		  95 /* total characters we can handle */
5580387638SSascha Wildner 	struct glyph	  gly[MAXCHAR]; /* glyph metrics */
5680387638SSascha Wildner };
5780387638SSascha Wildner 
58a4c7eb57SSascha Wildner struct	termp_ps {
59a4c7eb57SSascha Wildner 	int		  flags;
60a4c7eb57SSascha Wildner #define	PS_INLINE	 (1 << 0)	/* we're in a word */
61a4c7eb57SSascha Wildner #define	PS_MARGINS	 (1 << 1)	/* we're in the margins */
62a4c7eb57SSascha Wildner #define	PS_NEWPAGE	 (1 << 2)	/* new page, no words yet */
63a4c7eb57SSascha Wildner 	size_t		  pscol;	/* visible column (AFM units) */
64a4c7eb57SSascha Wildner 	size_t		  psrow;	/* visible row (AFM units) */
65a4c7eb57SSascha Wildner 	char		 *psmarg;	/* margin buf */
66a4c7eb57SSascha Wildner 	size_t		  psmargsz;	/* margin buf size */
67a4c7eb57SSascha Wildner 	size_t		  psmargcur;	/* cur index in margin buf */
68a4c7eb57SSascha Wildner 	char		  last;		/* character buffer */
69a4c7eb57SSascha Wildner 	enum termfont	  lastf;	/* last set font */
70a4c7eb57SSascha Wildner 	size_t		  scale;	/* font scaling factor */
71a4c7eb57SSascha Wildner 	size_t		  pages;	/* number of pages shown */
72a4c7eb57SSascha Wildner 	size_t		  lineheight;	/* line height (AFM units) */
73a4c7eb57SSascha Wildner 	size_t		  top;		/* body top (AFM units) */
74a4c7eb57SSascha Wildner 	size_t		  bottom;	/* body bottom (AFM units) */
75a4c7eb57SSascha Wildner 	size_t		  height;	/* page height (AFM units */
76a4c7eb57SSascha Wildner 	size_t		  width;	/* page width (AFM units) */
77a4c7eb57SSascha Wildner 	size_t		  left;		/* body left (AFM units) */
78a4c7eb57SSascha Wildner 	size_t		  header;	/* header pos (AFM units) */
79a4c7eb57SSascha Wildner 	size_t		  footer;	/* footer pos (AFM units) */
80a4c7eb57SSascha Wildner 	size_t		  pdfbytes; 	/* current output byte */
81a4c7eb57SSascha Wildner 	size_t		  pdflastpg;	/* byte of last page mark */
82a4c7eb57SSascha Wildner 	size_t		  pdfbody;	/* start of body object */
83a4c7eb57SSascha Wildner 	size_t		 *pdfobjs;	/* table of object offsets */
84a4c7eb57SSascha Wildner 	size_t		  pdfobjsz;	/* size of pdfobjs */
85a4c7eb57SSascha Wildner };
86a4c7eb57SSascha Wildner 
87a4c7eb57SSascha Wildner static	double		  ps_hspan(const struct termp *,
88a4c7eb57SSascha Wildner 				const struct roffsu *);
89a4c7eb57SSascha Wildner static	size_t		  ps_width(const struct termp *, int);
90a4c7eb57SSascha Wildner static	void		  ps_advance(struct termp *, size_t);
91a4c7eb57SSascha Wildner static	void		  ps_begin(struct termp *);
92a4c7eb57SSascha Wildner static	void		  ps_closepage(struct termp *);
93a4c7eb57SSascha Wildner static	void		  ps_end(struct termp *);
94a4c7eb57SSascha Wildner static	void		  ps_endline(struct termp *);
95a4c7eb57SSascha Wildner static	void		  ps_fclose(struct termp *);
96a4c7eb57SSascha Wildner static	void		  ps_growbuf(struct termp *, size_t);
97a4c7eb57SSascha Wildner static	void		  ps_letter(struct termp *, int);
98a4c7eb57SSascha Wildner static	void		  ps_pclose(struct termp *);
99a4c7eb57SSascha Wildner static	void		  ps_pletter(struct termp *, int);
100a4c7eb57SSascha Wildner static	void		  ps_printf(struct termp *, const char *, ...);
101a4c7eb57SSascha Wildner static	void		  ps_putchar(struct termp *, char);
102a4c7eb57SSascha Wildner static	void		  ps_setfont(struct termp *, enum termfont);
103a4c7eb57SSascha Wildner static	struct termp	 *pspdf_alloc(char *);
104a4c7eb57SSascha Wildner static	void		  pdf_obj(struct termp *, size_t);
105a4c7eb57SSascha Wildner 
10680387638SSascha Wildner /*
10780387638SSascha Wildner  * We define, for the time being, three fonts: bold, oblique/italic, and
10880387638SSascha Wildner  * normal (roman).  The following table hard-codes the font metrics for
10980387638SSascha Wildner  * ASCII, i.e., 32--127.
11080387638SSascha Wildner  */
11180387638SSascha Wildner 
11280387638SSascha Wildner static	const struct font fonts[TERMFONT__MAX] = {
11380387638SSascha Wildner 	{ "Times-Roman", {
11480387638SSascha Wildner 		{ 250 },
11580387638SSascha Wildner 		{ 333 },
11680387638SSascha Wildner 		{ 408 },
11780387638SSascha Wildner 		{ 500 },
11880387638SSascha Wildner 		{ 500 },
11980387638SSascha Wildner 		{ 833 },
12080387638SSascha Wildner 		{ 778 },
12180387638SSascha Wildner 		{ 333 },
12280387638SSascha Wildner 		{ 333 },
12380387638SSascha Wildner 		{ 333 },
12480387638SSascha Wildner 		{ 500 },
12580387638SSascha Wildner 		{ 564 },
12680387638SSascha Wildner 		{ 250 },
12780387638SSascha Wildner 		{ 333 },
12880387638SSascha Wildner 		{ 250 },
12980387638SSascha Wildner 		{ 278 },
13080387638SSascha Wildner 		{ 500 },
13180387638SSascha Wildner 		{ 500 },
13280387638SSascha Wildner 		{ 500 },
13380387638SSascha Wildner 		{ 500 },
13480387638SSascha Wildner 		{ 500 },
13580387638SSascha Wildner 		{ 500 },
13680387638SSascha Wildner 		{ 500 },
13780387638SSascha Wildner 		{ 500 },
13880387638SSascha Wildner 		{ 500 },
13980387638SSascha Wildner 		{ 500 },
14080387638SSascha Wildner 		{ 278 },
14180387638SSascha Wildner 		{ 278 },
14280387638SSascha Wildner 		{ 564 },
14380387638SSascha Wildner 		{ 564 },
14480387638SSascha Wildner 		{ 564 },
14580387638SSascha Wildner 		{ 444 },
14680387638SSascha Wildner 		{ 921 },
14780387638SSascha Wildner 		{ 722 },
14880387638SSascha Wildner 		{ 667 },
14980387638SSascha Wildner 		{ 667 },
15080387638SSascha Wildner 		{ 722 },
15180387638SSascha Wildner 		{ 611 },
15280387638SSascha Wildner 		{ 556 },
15380387638SSascha Wildner 		{ 722 },
15480387638SSascha Wildner 		{ 722 },
15580387638SSascha Wildner 		{ 333 },
15680387638SSascha Wildner 		{ 389 },
15780387638SSascha Wildner 		{ 722 },
15880387638SSascha Wildner 		{ 611 },
15980387638SSascha Wildner 		{ 889 },
16080387638SSascha Wildner 		{ 722 },
16180387638SSascha Wildner 		{ 722 },
16280387638SSascha Wildner 		{ 556 },
16380387638SSascha Wildner 		{ 722 },
16480387638SSascha Wildner 		{ 667 },
16580387638SSascha Wildner 		{ 556 },
16680387638SSascha Wildner 		{ 611 },
16780387638SSascha Wildner 		{ 722 },
16880387638SSascha Wildner 		{ 722 },
16980387638SSascha Wildner 		{ 944 },
17080387638SSascha Wildner 		{ 722 },
17180387638SSascha Wildner 		{ 722 },
17280387638SSascha Wildner 		{ 611 },
17380387638SSascha Wildner 		{ 333 },
17480387638SSascha Wildner 		{ 278 },
17580387638SSascha Wildner 		{ 333 },
17680387638SSascha Wildner 		{ 469 },
17780387638SSascha Wildner 		{ 500 },
17880387638SSascha Wildner 		{ 333 },
17980387638SSascha Wildner 		{ 444 },
18080387638SSascha Wildner 		{ 500 },
18180387638SSascha Wildner 		{ 444 },
18280387638SSascha Wildner 		{  500},
18380387638SSascha Wildner 		{  444},
18480387638SSascha Wildner 		{  333},
18580387638SSascha Wildner 		{  500},
18680387638SSascha Wildner 		{  500},
18780387638SSascha Wildner 		{  278},
18880387638SSascha Wildner 		{  278},
18980387638SSascha Wildner 		{  500},
19080387638SSascha Wildner 		{  278},
19180387638SSascha Wildner 		{  778},
19280387638SSascha Wildner 		{  500},
19380387638SSascha Wildner 		{  500},
19480387638SSascha Wildner 		{  500},
19580387638SSascha Wildner 		{  500},
19680387638SSascha Wildner 		{  333},
19780387638SSascha Wildner 		{  389},
19880387638SSascha Wildner 		{  278},
19980387638SSascha Wildner 		{  500},
20080387638SSascha Wildner 		{  500},
20180387638SSascha Wildner 		{  722},
20280387638SSascha Wildner 		{  500},
20380387638SSascha Wildner 		{  500},
20480387638SSascha Wildner 		{  444},
20580387638SSascha Wildner 		{  480},
20680387638SSascha Wildner 		{  200},
20780387638SSascha Wildner 		{  480},
20880387638SSascha Wildner 		{  541},
20980387638SSascha Wildner 	} },
21080387638SSascha Wildner 	{ "Times-Bold", {
21180387638SSascha Wildner 		{ 250  },
21280387638SSascha Wildner 		{ 333  },
21380387638SSascha Wildner 		{ 555  },
21480387638SSascha Wildner 		{ 500  },
21580387638SSascha Wildner 		{ 500  },
21680387638SSascha Wildner 		{ 1000 },
21780387638SSascha Wildner 		{ 833  },
21880387638SSascha Wildner 		{ 333  },
21980387638SSascha Wildner 		{ 333  },
22080387638SSascha Wildner 		{ 333  },
22180387638SSascha Wildner 		{ 500  },
22280387638SSascha Wildner 		{ 570  },
22380387638SSascha Wildner 		{ 250  },
22480387638SSascha Wildner 		{ 333  },
22580387638SSascha Wildner 		{ 250  },
22680387638SSascha Wildner 		{ 278  },
22780387638SSascha Wildner 		{ 500  },
22880387638SSascha Wildner 		{ 500  },
22980387638SSascha Wildner 		{ 500  },
23080387638SSascha Wildner 		{ 500  },
23180387638SSascha Wildner 		{ 500  },
23280387638SSascha Wildner 		{ 500  },
23380387638SSascha Wildner 		{ 500  },
23480387638SSascha Wildner 		{ 500  },
23580387638SSascha Wildner 		{ 500  },
23680387638SSascha Wildner 		{ 500  },
23780387638SSascha Wildner 		{ 333  },
23880387638SSascha Wildner 		{ 333  },
23980387638SSascha Wildner 		{ 570  },
24080387638SSascha Wildner 		{ 570  },
24180387638SSascha Wildner 		{ 570  },
24280387638SSascha Wildner 		{ 500  },
24380387638SSascha Wildner 		{ 930  },
24480387638SSascha Wildner 		{ 722  },
24580387638SSascha Wildner 		{ 667  },
24680387638SSascha Wildner 		{ 722  },
24780387638SSascha Wildner 		{ 722  },
24880387638SSascha Wildner 		{ 667  },
24980387638SSascha Wildner 		{ 611  },
25080387638SSascha Wildner 		{ 778  },
25180387638SSascha Wildner 		{ 778  },
25280387638SSascha Wildner 		{ 389  },
25380387638SSascha Wildner 		{ 500  },
25480387638SSascha Wildner 		{ 778  },
25580387638SSascha Wildner 		{ 667  },
25680387638SSascha Wildner 		{ 944  },
25780387638SSascha Wildner 		{ 722  },
25880387638SSascha Wildner 		{ 778  },
25980387638SSascha Wildner 		{ 611  },
26080387638SSascha Wildner 		{ 778  },
26180387638SSascha Wildner 		{ 722  },
26280387638SSascha Wildner 		{ 556  },
26380387638SSascha Wildner 		{ 667  },
26480387638SSascha Wildner 		{ 722  },
26580387638SSascha Wildner 		{ 722  },
26680387638SSascha Wildner 		{ 1000 },
26780387638SSascha Wildner 		{ 722  },
26880387638SSascha Wildner 		{ 722  },
26980387638SSascha Wildner 		{ 667  },
27080387638SSascha Wildner 		{ 333  },
27180387638SSascha Wildner 		{ 278  },
27280387638SSascha Wildner 		{ 333  },
27380387638SSascha Wildner 		{ 581  },
27480387638SSascha Wildner 		{ 500  },
27580387638SSascha Wildner 		{ 333  },
27680387638SSascha Wildner 		{ 500  },
27780387638SSascha Wildner 		{ 556  },
27880387638SSascha Wildner 		{ 444  },
27980387638SSascha Wildner 		{  556 },
28080387638SSascha Wildner 		{  444 },
28180387638SSascha Wildner 		{  333 },
28280387638SSascha Wildner 		{  500 },
28380387638SSascha Wildner 		{  556 },
28480387638SSascha Wildner 		{  278 },
28580387638SSascha Wildner 		{  333 },
28680387638SSascha Wildner 		{  556 },
28780387638SSascha Wildner 		{  278 },
28880387638SSascha Wildner 		{  833 },
28980387638SSascha Wildner 		{  556 },
29080387638SSascha Wildner 		{  500 },
29180387638SSascha Wildner 		{  556 },
29280387638SSascha Wildner 		{  556 },
29380387638SSascha Wildner 		{  444 },
29480387638SSascha Wildner 		{  389 },
29580387638SSascha Wildner 		{  333 },
29680387638SSascha Wildner 		{  556 },
29780387638SSascha Wildner 		{  500 },
29880387638SSascha Wildner 		{  722 },
29980387638SSascha Wildner 		{  500 },
30080387638SSascha Wildner 		{  500 },
30180387638SSascha Wildner 		{  444 },
30280387638SSascha Wildner 		{  394 },
30380387638SSascha Wildner 		{  220 },
30480387638SSascha Wildner 		{  394 },
30580387638SSascha Wildner 		{  520 },
30680387638SSascha Wildner 	} },
30780387638SSascha Wildner 	{ "Times-Italic", {
30880387638SSascha Wildner 		{ 250  },
30980387638SSascha Wildner 		{ 333  },
31080387638SSascha Wildner 		{ 420  },
31180387638SSascha Wildner 		{ 500  },
31280387638SSascha Wildner 		{ 500  },
31380387638SSascha Wildner 		{ 833  },
31480387638SSascha Wildner 		{ 778  },
31580387638SSascha Wildner 		{ 333  },
31680387638SSascha Wildner 		{ 333  },
31780387638SSascha Wildner 		{ 333  },
31880387638SSascha Wildner 		{ 500  },
31980387638SSascha Wildner 		{ 675  },
32080387638SSascha Wildner 		{ 250  },
32180387638SSascha Wildner 		{ 333  },
32280387638SSascha Wildner 		{ 250  },
32380387638SSascha Wildner 		{ 278  },
32480387638SSascha Wildner 		{ 500  },
32580387638SSascha Wildner 		{ 500  },
32680387638SSascha Wildner 		{ 500  },
32780387638SSascha Wildner 		{ 500  },
32880387638SSascha Wildner 		{ 500  },
32980387638SSascha Wildner 		{ 500  },
33080387638SSascha Wildner 		{ 500  },
33180387638SSascha Wildner 		{ 500  },
33280387638SSascha Wildner 		{ 500  },
33380387638SSascha Wildner 		{ 500  },
33480387638SSascha Wildner 		{ 333  },
33580387638SSascha Wildner 		{ 333  },
33680387638SSascha Wildner 		{ 675  },
33780387638SSascha Wildner 		{ 675  },
33880387638SSascha Wildner 		{ 675  },
33980387638SSascha Wildner 		{ 500  },
34080387638SSascha Wildner 		{ 920  },
34180387638SSascha Wildner 		{ 611  },
34280387638SSascha Wildner 		{ 611  },
34380387638SSascha Wildner 		{ 667  },
34480387638SSascha Wildner 		{ 722  },
34580387638SSascha Wildner 		{ 611  },
34680387638SSascha Wildner 		{ 611  },
34780387638SSascha Wildner 		{ 722  },
34880387638SSascha Wildner 		{ 722  },
34980387638SSascha Wildner 		{ 333  },
35080387638SSascha Wildner 		{ 444  },
35180387638SSascha Wildner 		{ 667  },
35280387638SSascha Wildner 		{ 556  },
35380387638SSascha Wildner 		{ 833  },
35480387638SSascha Wildner 		{ 667  },
35580387638SSascha Wildner 		{ 722  },
35680387638SSascha Wildner 		{ 611  },
35780387638SSascha Wildner 		{ 722  },
35880387638SSascha Wildner 		{ 611  },
35980387638SSascha Wildner 		{ 500  },
36080387638SSascha Wildner 		{ 556  },
36180387638SSascha Wildner 		{ 722  },
36280387638SSascha Wildner 		{ 611  },
36380387638SSascha Wildner 		{ 833  },
36480387638SSascha Wildner 		{ 611  },
36580387638SSascha Wildner 		{ 556  },
36680387638SSascha Wildner 		{ 556  },
36780387638SSascha Wildner 		{ 389  },
36880387638SSascha Wildner 		{ 278  },
36980387638SSascha Wildner 		{ 389  },
37080387638SSascha Wildner 		{ 422  },
37180387638SSascha Wildner 		{ 500  },
37280387638SSascha Wildner 		{ 333  },
37380387638SSascha Wildner 		{ 500  },
37480387638SSascha Wildner 		{ 500  },
37580387638SSascha Wildner 		{ 444  },
37680387638SSascha Wildner 		{  500 },
37780387638SSascha Wildner 		{  444 },
37880387638SSascha Wildner 		{  278 },
37980387638SSascha Wildner 		{  500 },
38080387638SSascha Wildner 		{  500 },
38180387638SSascha Wildner 		{  278 },
38280387638SSascha Wildner 		{  278 },
38380387638SSascha Wildner 		{  444 },
38480387638SSascha Wildner 		{  278 },
38580387638SSascha Wildner 		{  722 },
38680387638SSascha Wildner 		{  500 },
38780387638SSascha Wildner 		{  500 },
38880387638SSascha Wildner 		{  500 },
38980387638SSascha Wildner 		{  500 },
39080387638SSascha Wildner 		{  389 },
39180387638SSascha Wildner 		{  389 },
39280387638SSascha Wildner 		{  278 },
39380387638SSascha Wildner 		{  500 },
39480387638SSascha Wildner 		{  444 },
39580387638SSascha Wildner 		{  667 },
39680387638SSascha Wildner 		{  444 },
39780387638SSascha Wildner 		{  444 },
39880387638SSascha Wildner 		{  389 },
39980387638SSascha Wildner 		{  400 },
40080387638SSascha Wildner 		{  275 },
40180387638SSascha Wildner 		{  400 },
40280387638SSascha Wildner 		{  541 },
40380387638SSascha Wildner 	} },
40480387638SSascha Wildner };
40580387638SSascha Wildner 
40680387638SSascha Wildner void *
40780387638SSascha Wildner pdf_alloc(char *outopts)
40880387638SSascha Wildner {
40980387638SSascha Wildner 	struct termp	*p;
41080387638SSascha Wildner 
41180387638SSascha Wildner 	if (NULL != (p = pspdf_alloc(outopts)))
41280387638SSascha Wildner 		p->type = TERMTYPE_PDF;
41380387638SSascha Wildner 
41480387638SSascha Wildner 	return(p);
41580387638SSascha Wildner }
41680387638SSascha Wildner 
41780387638SSascha Wildner void *
41880387638SSascha Wildner ps_alloc(char *outopts)
41980387638SSascha Wildner {
42080387638SSascha Wildner 	struct termp	*p;
42180387638SSascha Wildner 
42280387638SSascha Wildner 	if (NULL != (p = pspdf_alloc(outopts)))
42380387638SSascha Wildner 		p->type = TERMTYPE_PS;
42480387638SSascha Wildner 
42580387638SSascha Wildner 	return(p);
42680387638SSascha Wildner }
42780387638SSascha Wildner 
42880387638SSascha Wildner static struct termp *
42980387638SSascha Wildner pspdf_alloc(char *outopts)
43080387638SSascha Wildner {
43180387638SSascha Wildner 	struct termp	*p;
432*36342e81SSascha Wildner 	unsigned int	 pagex, pagey;
433*36342e81SSascha Wildner 	size_t		 marginx, marginy, lineheight;
43480387638SSascha Wildner 	const char	*toks[2];
43580387638SSascha Wildner 	const char	*pp;
43680387638SSascha Wildner 	char		*v;
43780387638SSascha Wildner 
438a4c7eb57SSascha Wildner 	p = mandoc_calloc(1, sizeof(struct termp));
439a4c7eb57SSascha Wildner 	p->enc = TERMENC_ASCII;
440a4c7eb57SSascha Wildner 	p->ps = mandoc_calloc(1, sizeof(struct termp_ps));
44180387638SSascha Wildner 
44280387638SSascha Wildner 	p->advance = ps_advance;
44380387638SSascha Wildner 	p->begin = ps_begin;
44480387638SSascha Wildner 	p->end = ps_end;
44580387638SSascha Wildner 	p->endline = ps_endline;
44680387638SSascha Wildner 	p->hspan = ps_hspan;
44780387638SSascha Wildner 	p->letter = ps_letter;
44880387638SSascha Wildner 	p->width = ps_width;
44980387638SSascha Wildner 
45080387638SSascha Wildner 	toks[0] = "paper";
45180387638SSascha Wildner 	toks[1] = NULL;
45280387638SSascha Wildner 
45380387638SSascha Wildner 	pp = NULL;
45480387638SSascha Wildner 
45580387638SSascha Wildner 	while (outopts && *outopts)
45680387638SSascha Wildner 		switch (getsubopt(&outopts, UNCONST(toks), &v)) {
45780387638SSascha Wildner 		case (0):
45880387638SSascha Wildner 			pp = v;
45980387638SSascha Wildner 			break;
46080387638SSascha Wildner 		default:
46180387638SSascha Wildner 			break;
46280387638SSascha Wildner 		}
46380387638SSascha Wildner 
46480387638SSascha Wildner 	/* Default to US letter (millimetres). */
46580387638SSascha Wildner 
46680387638SSascha Wildner 	pagex = 216;
46780387638SSascha Wildner 	pagey = 279;
46880387638SSascha Wildner 
46980387638SSascha Wildner 	/*
47080387638SSascha Wildner 	 * The ISO-269 paper sizes can be calculated automatically, but
47180387638SSascha Wildner 	 * it would require bringing in -lm for pow() and I'd rather not
47280387638SSascha Wildner 	 * do that.  So just do it the easy way for now.  Since this
47380387638SSascha Wildner 	 * only happens once, I'm not terribly concerned.
47480387638SSascha Wildner 	 */
47580387638SSascha Wildner 
47680387638SSascha Wildner 	if (pp && strcasecmp(pp, "letter")) {
47780387638SSascha Wildner 		if (0 == strcasecmp(pp, "a3")) {
47880387638SSascha Wildner 			pagex = 297;
47980387638SSascha Wildner 			pagey = 420;
48080387638SSascha Wildner 		} else if (0 == strcasecmp(pp, "a4")) {
48180387638SSascha Wildner 			pagex = 210;
48280387638SSascha Wildner 			pagey = 297;
48380387638SSascha Wildner 		} else if (0 == strcasecmp(pp, "a5")) {
48480387638SSascha Wildner 			pagex = 148;
48580387638SSascha Wildner 			pagey = 210;
48680387638SSascha Wildner 		} else if (0 == strcasecmp(pp, "legal")) {
48780387638SSascha Wildner 			pagex = 216;
48880387638SSascha Wildner 			pagey = 356;
489*36342e81SSascha Wildner 		} else if (2 != sscanf(pp, "%ux%u", &pagex, &pagey))
49080387638SSascha Wildner 			fprintf(stderr, "%s: Unknown paper\n", pp);
491*36342e81SSascha Wildner 	}
49280387638SSascha Wildner 
49380387638SSascha Wildner 	/*
49480387638SSascha Wildner 	 * This MUST be defined before any PNT2AFM or AFM2PNT
49580387638SSascha Wildner 	 * calculations occur.
49680387638SSascha Wildner 	 */
49780387638SSascha Wildner 
498a4c7eb57SSascha Wildner 	p->ps->scale = 11;
49980387638SSascha Wildner 
50080387638SSascha Wildner 	/* Remember millimetres -> AFM units. */
50180387638SSascha Wildner 
50280387638SSascha Wildner 	pagex = PNT2AFM(p, ((double)pagex * 2.834));
50380387638SSascha Wildner 	pagey = PNT2AFM(p, ((double)pagey * 2.834));
50480387638SSascha Wildner 
50580387638SSascha Wildner 	/* Margins are 1/9 the page x and y. */
50680387638SSascha Wildner 
50780387638SSascha Wildner 	marginx = /* LINTED */
50880387638SSascha Wildner 		(size_t)((double)pagex / 9.0);
50980387638SSascha Wildner 	marginy = /* LINTED */
51080387638SSascha Wildner 		(size_t)((double)pagey / 9.0);
51180387638SSascha Wildner 
51280387638SSascha Wildner 	/* Line-height is 1.4em. */
51380387638SSascha Wildner 
514a4c7eb57SSascha Wildner 	lineheight = PNT2AFM(p, ((double)p->ps->scale * 1.4));
51580387638SSascha Wildner 
516*36342e81SSascha Wildner 	p->ps->width = (size_t)pagex;
517*36342e81SSascha Wildner 	p->ps->height = (size_t)pagey;
518a4c7eb57SSascha Wildner 	p->ps->header = pagey - (marginy / 2) - (lineheight / 2);
519a4c7eb57SSascha Wildner 	p->ps->top = pagey - marginy;
520a4c7eb57SSascha Wildner 	p->ps->footer = (marginy / 2) - (lineheight / 2);
521a4c7eb57SSascha Wildner 	p->ps->bottom = marginy;
522a4c7eb57SSascha Wildner 	p->ps->left = marginx;
523a4c7eb57SSascha Wildner 	p->ps->lineheight = lineheight;
52480387638SSascha Wildner 
52580387638SSascha Wildner 	p->defrmargin = pagex - (marginx * 2);
52680387638SSascha Wildner 	return(p);
52780387638SSascha Wildner }
52880387638SSascha Wildner 
52980387638SSascha Wildner 
53080387638SSascha Wildner void
53180387638SSascha Wildner pspdf_free(void *arg)
53280387638SSascha Wildner {
53380387638SSascha Wildner 	struct termp	*p;
53480387638SSascha Wildner 
53580387638SSascha Wildner 	p = (struct termp *)arg;
53680387638SSascha Wildner 
537a4c7eb57SSascha Wildner 	if (p->ps->psmarg)
538a4c7eb57SSascha Wildner 		free(p->ps->psmarg);
539a4c7eb57SSascha Wildner 	if (p->ps->pdfobjs)
540a4c7eb57SSascha Wildner 		free(p->ps->pdfobjs);
54180387638SSascha Wildner 
542a4c7eb57SSascha Wildner 	free(p->ps);
54380387638SSascha Wildner 	term_free(p);
54480387638SSascha Wildner }
54580387638SSascha Wildner 
54680387638SSascha Wildner 
54780387638SSascha Wildner static void
54880387638SSascha Wildner ps_printf(struct termp *p, const char *fmt, ...)
54980387638SSascha Wildner {
55080387638SSascha Wildner 	va_list		 ap;
55180387638SSascha Wildner 	int		 pos, len;
55280387638SSascha Wildner 
55380387638SSascha Wildner 	va_start(ap, fmt);
55480387638SSascha Wildner 
55580387638SSascha Wildner 	/*
55680387638SSascha Wildner 	 * If we're running in regular mode, then pipe directly into
55780387638SSascha Wildner 	 * vprintf().  If we're processing margins, then push the data
55880387638SSascha Wildner 	 * into our growable margin buffer.
55980387638SSascha Wildner 	 */
56080387638SSascha Wildner 
561a4c7eb57SSascha Wildner 	if ( ! (PS_MARGINS & p->ps->flags)) {
56280387638SSascha Wildner 		len = vprintf(fmt, ap);
56380387638SSascha Wildner 		va_end(ap);
564a4c7eb57SSascha Wildner 		p->ps->pdfbytes += /* LINTED */
56580387638SSascha Wildner 			len < 0 ? 0 : (size_t)len;
56680387638SSascha Wildner 		return;
56780387638SSascha Wildner 	}
56880387638SSascha Wildner 
56980387638SSascha Wildner 	/*
57080387638SSascha Wildner 	 * XXX: I assume that the in-margin print won't exceed
57180387638SSascha Wildner 	 * PS_BUFSLOP (128 bytes), which is reasonable but still an
57280387638SSascha Wildner 	 * assumption that will cause pukeage if it's not the case.
57380387638SSascha Wildner 	 */
57480387638SSascha Wildner 
57580387638SSascha Wildner 	ps_growbuf(p, PS_BUFSLOP);
57680387638SSascha Wildner 
577a4c7eb57SSascha Wildner 	pos = (int)p->ps->psmargcur;
578*36342e81SSascha Wildner 	vsnprintf(&p->ps->psmarg[pos], PS_BUFSLOP, fmt, ap);
57980387638SSascha Wildner 
58080387638SSascha Wildner 	va_end(ap);
58180387638SSascha Wildner 
582a4c7eb57SSascha Wildner 	p->ps->psmargcur = strlen(p->ps->psmarg);
58380387638SSascha Wildner }
58480387638SSascha Wildner 
58580387638SSascha Wildner 
58680387638SSascha Wildner static void
58780387638SSascha Wildner ps_putchar(struct termp *p, char c)
58880387638SSascha Wildner {
58980387638SSascha Wildner 	int		 pos;
59080387638SSascha Wildner 
59180387638SSascha Wildner 	/* See ps_printf(). */
59280387638SSascha Wildner 
593a4c7eb57SSascha Wildner 	if ( ! (PS_MARGINS & p->ps->flags)) {
59480387638SSascha Wildner 		/* LINTED */
59580387638SSascha Wildner 		putchar(c);
596a4c7eb57SSascha Wildner 		p->ps->pdfbytes++;
59780387638SSascha Wildner 		return;
59880387638SSascha Wildner 	}
59980387638SSascha Wildner 
60080387638SSascha Wildner 	ps_growbuf(p, 2);
60180387638SSascha Wildner 
602a4c7eb57SSascha Wildner 	pos = (int)p->ps->psmargcur++;
603a4c7eb57SSascha Wildner 	p->ps->psmarg[pos++] = c;
604a4c7eb57SSascha Wildner 	p->ps->psmarg[pos] = '\0';
60580387638SSascha Wildner }
60680387638SSascha Wildner 
60780387638SSascha Wildner 
60880387638SSascha Wildner static void
60980387638SSascha Wildner pdf_obj(struct termp *p, size_t obj)
61080387638SSascha Wildner {
61180387638SSascha Wildner 
61280387638SSascha Wildner 	assert(obj > 0);
61380387638SSascha Wildner 
614a4c7eb57SSascha Wildner 	if ((obj - 1) >= p->ps->pdfobjsz) {
615a4c7eb57SSascha Wildner 		p->ps->pdfobjsz = obj + 128;
616a4c7eb57SSascha Wildner 		p->ps->pdfobjs = realloc
617a4c7eb57SSascha Wildner 			(p->ps->pdfobjs,
618a4c7eb57SSascha Wildner 			 p->ps->pdfobjsz * sizeof(size_t));
619a4c7eb57SSascha Wildner 		if (NULL == p->ps->pdfobjs) {
62080387638SSascha Wildner 			perror(NULL);
62180387638SSascha Wildner 			exit((int)MANDOCLEVEL_SYSERR);
62280387638SSascha Wildner 		}
62380387638SSascha Wildner 	}
62480387638SSascha Wildner 
625a4c7eb57SSascha Wildner 	p->ps->pdfobjs[(int)obj - 1] = p->ps->pdfbytes;
62680387638SSascha Wildner 	ps_printf(p, "%zu 0 obj\n", obj);
62780387638SSascha Wildner }
62880387638SSascha Wildner 
62980387638SSascha Wildner 
63080387638SSascha Wildner static void
63180387638SSascha Wildner ps_closepage(struct termp *p)
63280387638SSascha Wildner {
63380387638SSascha Wildner 	int		 i;
63480387638SSascha Wildner 	size_t		 len, base;
63580387638SSascha Wildner 
63680387638SSascha Wildner 	/*
63780387638SSascha Wildner 	 * Close out a page that we've already flushed to output.  In
63880387638SSascha Wildner 	 * PostScript, we simply note that the page must be showed.  In
63980387638SSascha Wildner 	 * PDF, we must now create the Length, Resource, and Page node
64080387638SSascha Wildner 	 * for the page contents.
64180387638SSascha Wildner 	 */
64280387638SSascha Wildner 
643a4c7eb57SSascha Wildner 	assert(p->ps->psmarg && p->ps->psmarg[0]);
644a4c7eb57SSascha Wildner 	ps_printf(p, "%s", p->ps->psmarg);
64580387638SSascha Wildner 
64680387638SSascha Wildner 	if (TERMTYPE_PS != p->type) {
64780387638SSascha Wildner 		ps_printf(p, "ET\n");
64880387638SSascha Wildner 
649a4c7eb57SSascha Wildner 		len = p->ps->pdfbytes - p->ps->pdflastpg;
650a4c7eb57SSascha Wildner 		base = p->ps->pages * 4 + p->ps->pdfbody;
65180387638SSascha Wildner 
65280387638SSascha Wildner 		ps_printf(p, "endstream\nendobj\n");
65380387638SSascha Wildner 
65480387638SSascha Wildner 		/* Length of content. */
65580387638SSascha Wildner 		pdf_obj(p, base + 1);
65680387638SSascha Wildner 		ps_printf(p, "%zu\nendobj\n", len);
65780387638SSascha Wildner 
65880387638SSascha Wildner 		/* Resource for content. */
65980387638SSascha Wildner 		pdf_obj(p, base + 2);
66080387638SSascha Wildner 		ps_printf(p, "<<\n/ProcSet [/PDF /Text]\n");
66180387638SSascha Wildner 		ps_printf(p, "/Font <<\n");
66280387638SSascha Wildner 		for (i = 0; i < (int)TERMFONT__MAX; i++)
66380387638SSascha Wildner 			ps_printf(p, "/F%d %d 0 R\n", i, 3 + i);
66480387638SSascha Wildner 		ps_printf(p, ">>\n>>\n");
66580387638SSascha Wildner 
66680387638SSascha Wildner 		/* Page node. */
66780387638SSascha Wildner 		pdf_obj(p, base + 3);
66880387638SSascha Wildner 		ps_printf(p, "<<\n");
66980387638SSascha Wildner 		ps_printf(p, "/Type /Page\n");
67080387638SSascha Wildner 		ps_printf(p, "/Parent 2 0 R\n");
67180387638SSascha Wildner 		ps_printf(p, "/Resources %zu 0 R\n", base + 2);
67280387638SSascha Wildner 		ps_printf(p, "/Contents %zu 0 R\n", base);
67380387638SSascha Wildner 		ps_printf(p, ">>\nendobj\n");
67480387638SSascha Wildner 	} else
67580387638SSascha Wildner 		ps_printf(p, "showpage\n");
67680387638SSascha Wildner 
677a4c7eb57SSascha Wildner 	p->ps->pages++;
678a4c7eb57SSascha Wildner 	p->ps->psrow = p->ps->top;
679a4c7eb57SSascha Wildner 	assert( ! (PS_NEWPAGE & p->ps->flags));
680a4c7eb57SSascha Wildner 	p->ps->flags |= PS_NEWPAGE;
68180387638SSascha Wildner }
68280387638SSascha Wildner 
68380387638SSascha Wildner 
68480387638SSascha Wildner /* ARGSUSED */
68580387638SSascha Wildner static void
68680387638SSascha Wildner ps_end(struct termp *p)
68780387638SSascha Wildner {
68880387638SSascha Wildner 	size_t		 i, xref, base;
68980387638SSascha Wildner 
69080387638SSascha Wildner 	/*
69180387638SSascha Wildner 	 * At the end of the file, do one last showpage.  This is the
69280387638SSascha Wildner 	 * same behaviour as groff(1) and works for multiple pages as
69380387638SSascha Wildner 	 * well as just one.
69480387638SSascha Wildner 	 */
69580387638SSascha Wildner 
696a4c7eb57SSascha Wildner 	if ( ! (PS_NEWPAGE & p->ps->flags)) {
697a4c7eb57SSascha Wildner 		assert(0 == p->ps->flags);
698a4c7eb57SSascha Wildner 		assert('\0' == p->ps->last);
69980387638SSascha Wildner 		ps_closepage(p);
70080387638SSascha Wildner 	}
70180387638SSascha Wildner 
70280387638SSascha Wildner 	if (TERMTYPE_PS == p->type) {
70380387638SSascha Wildner 		ps_printf(p, "%%%%Trailer\n");
704a4c7eb57SSascha Wildner 		ps_printf(p, "%%%%Pages: %zu\n", p->ps->pages);
70580387638SSascha Wildner 		ps_printf(p, "%%%%EOF\n");
70680387638SSascha Wildner 		return;
70780387638SSascha Wildner 	}
70880387638SSascha Wildner 
70980387638SSascha Wildner 	pdf_obj(p, 2);
71080387638SSascha Wildner 	ps_printf(p, "<<\n/Type /Pages\n");
71180387638SSascha Wildner 	ps_printf(p, "/MediaBox [0 0 %zu %zu]\n",
712a4c7eb57SSascha Wildner 			(size_t)AFM2PNT(p, p->ps->width),
713a4c7eb57SSascha Wildner 			(size_t)AFM2PNT(p, p->ps->height));
71480387638SSascha Wildner 
715a4c7eb57SSascha Wildner 	ps_printf(p, "/Count %zu\n", p->ps->pages);
71680387638SSascha Wildner 	ps_printf(p, "/Kids [");
71780387638SSascha Wildner 
718a4c7eb57SSascha Wildner 	for (i = 0; i < p->ps->pages; i++)
71980387638SSascha Wildner 		ps_printf(p, " %zu 0 R", i * 4 +
720a4c7eb57SSascha Wildner 				p->ps->pdfbody + 3);
72180387638SSascha Wildner 
722a4c7eb57SSascha Wildner 	base = (p->ps->pages - 1) * 4 +
723a4c7eb57SSascha Wildner 		p->ps->pdfbody + 4;
72480387638SSascha Wildner 
72580387638SSascha Wildner 	ps_printf(p, "]\n>>\nendobj\n");
72680387638SSascha Wildner 	pdf_obj(p, base);
72780387638SSascha Wildner 	ps_printf(p, "<<\n");
72880387638SSascha Wildner 	ps_printf(p, "/Type /Catalog\n");
72980387638SSascha Wildner 	ps_printf(p, "/Pages 2 0 R\n");
73080387638SSascha Wildner 	ps_printf(p, ">>\n");
731a4c7eb57SSascha Wildner 	xref = p->ps->pdfbytes;
73280387638SSascha Wildner 	ps_printf(p, "xref\n");
73380387638SSascha Wildner 	ps_printf(p, "0 %zu\n", base + 1);
73480387638SSascha Wildner 	ps_printf(p, "0000000000 65535 f \n");
73580387638SSascha Wildner 
73680387638SSascha Wildner 	for (i = 0; i < base; i++)
73780387638SSascha Wildner 		ps_printf(p, "%.10zu 00000 n \n",
738a4c7eb57SSascha Wildner 				p->ps->pdfobjs[(int)i]);
73980387638SSascha Wildner 
74080387638SSascha Wildner 	ps_printf(p, "trailer\n");
74180387638SSascha Wildner 	ps_printf(p, "<<\n");
74280387638SSascha Wildner 	ps_printf(p, "/Size %zu\n", base + 1);
74380387638SSascha Wildner 	ps_printf(p, "/Root %zu 0 R\n", base);
74480387638SSascha Wildner 	ps_printf(p, "/Info 1 0 R\n");
74580387638SSascha Wildner 	ps_printf(p, ">>\n");
74680387638SSascha Wildner 	ps_printf(p, "startxref\n");
74780387638SSascha Wildner 	ps_printf(p, "%zu\n", xref);
74880387638SSascha Wildner 	ps_printf(p, "%%%%EOF\n");
74980387638SSascha Wildner }
75080387638SSascha Wildner 
75180387638SSascha Wildner 
75280387638SSascha Wildner static void
75380387638SSascha Wildner ps_begin(struct termp *p)
75480387638SSascha Wildner {
75580387638SSascha Wildner 	time_t		 t;
75680387638SSascha Wildner 	int		 i;
75780387638SSascha Wildner 
75880387638SSascha Wildner 	/*
75980387638SSascha Wildner 	 * Print margins into margin buffer.  Nothing gets output to the
76080387638SSascha Wildner 	 * screen yet, so we don't need to initialise the primary state.
76180387638SSascha Wildner 	 */
76280387638SSascha Wildner 
763a4c7eb57SSascha Wildner 	if (p->ps->psmarg) {
764a4c7eb57SSascha Wildner 		assert(p->ps->psmargsz);
765a4c7eb57SSascha Wildner 		p->ps->psmarg[0] = '\0';
76680387638SSascha Wildner 	}
76780387638SSascha Wildner 
768a4c7eb57SSascha Wildner 	/*p->ps->pdfbytes = 0;*/
769a4c7eb57SSascha Wildner 	p->ps->psmargcur = 0;
770a4c7eb57SSascha Wildner 	p->ps->flags = PS_MARGINS;
771a4c7eb57SSascha Wildner 	p->ps->pscol = p->ps->left;
772a4c7eb57SSascha Wildner 	p->ps->psrow = p->ps->header;
77380387638SSascha Wildner 
77480387638SSascha Wildner 	ps_setfont(p, TERMFONT_NONE);
77580387638SSascha Wildner 
77680387638SSascha Wildner 	(*p->headf)(p, p->argf);
77780387638SSascha Wildner 	(*p->endline)(p);
77880387638SSascha Wildner 
779a4c7eb57SSascha Wildner 	p->ps->pscol = p->ps->left;
780a4c7eb57SSascha Wildner 	p->ps->psrow = p->ps->footer;
78180387638SSascha Wildner 
78280387638SSascha Wildner 	(*p->footf)(p, p->argf);
78380387638SSascha Wildner 	(*p->endline)(p);
78480387638SSascha Wildner 
785a4c7eb57SSascha Wildner 	p->ps->flags &= ~PS_MARGINS;
78680387638SSascha Wildner 
787a4c7eb57SSascha Wildner 	assert(0 == p->ps->flags);
788a4c7eb57SSascha Wildner 	assert(p->ps->psmarg);
789a4c7eb57SSascha Wildner 	assert('\0' != p->ps->psmarg[0]);
79080387638SSascha Wildner 
79180387638SSascha Wildner 	/*
79280387638SSascha Wildner 	 * Print header and initialise page state.  Following this,
79380387638SSascha Wildner 	 * stuff gets printed to the screen, so make sure we're sane.
79480387638SSascha Wildner 	 */
79580387638SSascha Wildner 
79680387638SSascha Wildner 	t = time(NULL);
79780387638SSascha Wildner 
79880387638SSascha Wildner 	if (TERMTYPE_PS == p->type) {
79980387638SSascha Wildner 		ps_printf(p, "%%!PS-Adobe-3.0\n");
80080387638SSascha Wildner 		ps_printf(p, "%%%%CreationDate: %s", ctime(&t));
80180387638SSascha Wildner 		ps_printf(p, "%%%%DocumentData: Clean7Bit\n");
80280387638SSascha Wildner 		ps_printf(p, "%%%%Orientation: Portrait\n");
80380387638SSascha Wildner 		ps_printf(p, "%%%%Pages: (atend)\n");
80480387638SSascha Wildner 		ps_printf(p, "%%%%PageOrder: Ascend\n");
80580387638SSascha Wildner 		ps_printf(p, "%%%%DocumentMedia: "
80680387638SSascha Wildner 				"Default %zu %zu 0 () ()\n",
807a4c7eb57SSascha Wildner 				(size_t)AFM2PNT(p, p->ps->width),
808a4c7eb57SSascha Wildner 				(size_t)AFM2PNT(p, p->ps->height));
80980387638SSascha Wildner 		ps_printf(p, "%%%%DocumentNeededResources: font");
81080387638SSascha Wildner 
81180387638SSascha Wildner 		for (i = 0; i < (int)TERMFONT__MAX; i++)
81280387638SSascha Wildner 			ps_printf(p, " %s", fonts[i].name);
81380387638SSascha Wildner 
81480387638SSascha Wildner 		ps_printf(p, "\n%%%%EndComments\n");
81580387638SSascha Wildner 	} else {
81680387638SSascha Wildner 		ps_printf(p, "%%PDF-1.1\n");
81780387638SSascha Wildner 		pdf_obj(p, 1);
81880387638SSascha Wildner 		ps_printf(p, "<<\n");
81980387638SSascha Wildner 		ps_printf(p, ">>\n");
82080387638SSascha Wildner 		ps_printf(p, "endobj\n");
82180387638SSascha Wildner 
82280387638SSascha Wildner 		for (i = 0; i < (int)TERMFONT__MAX; i++) {
82380387638SSascha Wildner 			pdf_obj(p, (size_t)i + 3);
82480387638SSascha Wildner 			ps_printf(p, "<<\n");
82580387638SSascha Wildner 			ps_printf(p, "/Type /Font\n");
82680387638SSascha Wildner 			ps_printf(p, "/Subtype /Type1\n");
82780387638SSascha Wildner 			ps_printf(p, "/Name /F%zu\n", i);
82880387638SSascha Wildner 			ps_printf(p, "/BaseFont /%s\n", fonts[i].name);
82980387638SSascha Wildner 			ps_printf(p, ">>\n");
83080387638SSascha Wildner 		}
83180387638SSascha Wildner 	}
83280387638SSascha Wildner 
833a4c7eb57SSascha Wildner 	p->ps->pdfbody = (size_t)TERMFONT__MAX + 3;
834a4c7eb57SSascha Wildner 	p->ps->pscol = p->ps->left;
835a4c7eb57SSascha Wildner 	p->ps->psrow = p->ps->top;
836a4c7eb57SSascha Wildner 	p->ps->flags |= PS_NEWPAGE;
83780387638SSascha Wildner 	ps_setfont(p, TERMFONT_NONE);
83880387638SSascha Wildner }
83980387638SSascha Wildner 
84080387638SSascha Wildner 
84180387638SSascha Wildner static void
84280387638SSascha Wildner ps_pletter(struct termp *p, int c)
84380387638SSascha Wildner {
84480387638SSascha Wildner 	int		 f;
84580387638SSascha Wildner 
84680387638SSascha Wildner 	/*
84780387638SSascha Wildner 	 * If we haven't opened a page context, then output that we're
84880387638SSascha Wildner 	 * in a new page and make sure the font is correctly set.
84980387638SSascha Wildner 	 */
85080387638SSascha Wildner 
851a4c7eb57SSascha Wildner 	if (PS_NEWPAGE & p->ps->flags) {
85280387638SSascha Wildner 		if (TERMTYPE_PS == p->type) {
85380387638SSascha Wildner 			ps_printf(p, "%%%%Page: %zu %zu\n",
854a4c7eb57SSascha Wildner 					p->ps->pages + 1,
855a4c7eb57SSascha Wildner 					p->ps->pages + 1);
85680387638SSascha Wildner 			ps_printf(p, "/%s %zu selectfont\n",
857a4c7eb57SSascha Wildner 					fonts[(int)p->ps->lastf].name,
858a4c7eb57SSascha Wildner 					p->ps->scale);
85980387638SSascha Wildner 		} else {
860a4c7eb57SSascha Wildner 			pdf_obj(p, p->ps->pdfbody +
861a4c7eb57SSascha Wildner 					p->ps->pages * 4);
86280387638SSascha Wildner 			ps_printf(p, "<<\n");
86380387638SSascha Wildner 			ps_printf(p, "/Length %zu 0 R\n",
864a4c7eb57SSascha Wildner 					p->ps->pdfbody + 1 +
865a4c7eb57SSascha Wildner 					p->ps->pages * 4);
86680387638SSascha Wildner 			ps_printf(p, ">>\nstream\n");
86780387638SSascha Wildner 		}
868a4c7eb57SSascha Wildner 		p->ps->pdflastpg = p->ps->pdfbytes;
869a4c7eb57SSascha Wildner 		p->ps->flags &= ~PS_NEWPAGE;
87080387638SSascha Wildner 	}
87180387638SSascha Wildner 
87280387638SSascha Wildner 	/*
87380387638SSascha Wildner 	 * If we're not in a PostScript "word" context, then open one
87480387638SSascha Wildner 	 * now at the current cursor.
87580387638SSascha Wildner 	 */
87680387638SSascha Wildner 
877a4c7eb57SSascha Wildner 	if ( ! (PS_INLINE & p->ps->flags)) {
87880387638SSascha Wildner 		if (TERMTYPE_PS != p->type) {
87980387638SSascha Wildner 			ps_printf(p, "BT\n/F%d %zu Tf\n",
880a4c7eb57SSascha Wildner 					(int)p->ps->lastf,
881a4c7eb57SSascha Wildner 					p->ps->scale);
88280387638SSascha Wildner 			ps_printf(p, "%.3f %.3f Td\n(",
883a4c7eb57SSascha Wildner 					AFM2PNT(p, p->ps->pscol),
884a4c7eb57SSascha Wildner 					AFM2PNT(p, p->ps->psrow));
88580387638SSascha Wildner 		} else
88680387638SSascha Wildner 			ps_printf(p, "%.3f %.3f moveto\n(",
887a4c7eb57SSascha Wildner 					AFM2PNT(p, p->ps->pscol),
888a4c7eb57SSascha Wildner 					AFM2PNT(p, p->ps->psrow));
889a4c7eb57SSascha Wildner 		p->ps->flags |= PS_INLINE;
89080387638SSascha Wildner 	}
89180387638SSascha Wildner 
892a4c7eb57SSascha Wildner 	assert( ! (PS_NEWPAGE & p->ps->flags));
89380387638SSascha Wildner 
89480387638SSascha Wildner 	/*
89580387638SSascha Wildner 	 * We need to escape these characters as per the PostScript
89680387638SSascha Wildner 	 * specification.  We would also escape non-graphable characters
89780387638SSascha Wildner 	 * (like tabs), but none of them would get to this point and
89880387638SSascha Wildner 	 * it's superfluous to abort() on them.
89980387638SSascha Wildner 	 */
90080387638SSascha Wildner 
90180387638SSascha Wildner 	switch (c) {
90280387638SSascha Wildner 	case ('('):
90380387638SSascha Wildner 		/* FALLTHROUGH */
90480387638SSascha Wildner 	case (')'):
90580387638SSascha Wildner 		/* FALLTHROUGH */
90680387638SSascha Wildner 	case ('\\'):
90780387638SSascha Wildner 		ps_putchar(p, '\\');
90880387638SSascha Wildner 		break;
90980387638SSascha Wildner 	default:
91080387638SSascha Wildner 		break;
91180387638SSascha Wildner 	}
91280387638SSascha Wildner 
91380387638SSascha Wildner 	/* Write the character and adjust where we are on the page. */
91480387638SSascha Wildner 
915a4c7eb57SSascha Wildner 	f = (int)p->ps->lastf;
91680387638SSascha Wildner 
91780387638SSascha Wildner 	if (c <= 32 || (c - 32 >= MAXCHAR)) {
91880387638SSascha Wildner 		ps_putchar(p, ' ');
919a4c7eb57SSascha Wildner 		p->ps->pscol += (size_t)fonts[f].gly[0].wx;
92080387638SSascha Wildner 		return;
92180387638SSascha Wildner 	}
92280387638SSascha Wildner 
92380387638SSascha Wildner 	ps_putchar(p, (char)c);
92480387638SSascha Wildner 	c -= 32;
925a4c7eb57SSascha Wildner 	p->ps->pscol += (size_t)fonts[f].gly[c].wx;
92680387638SSascha Wildner }
92780387638SSascha Wildner 
92880387638SSascha Wildner 
92980387638SSascha Wildner static void
93080387638SSascha Wildner ps_pclose(struct termp *p)
93180387638SSascha Wildner {
93280387638SSascha Wildner 
93380387638SSascha Wildner 	/*
93480387638SSascha Wildner 	 * Spit out that we're exiting a word context (this is a
93580387638SSascha Wildner 	 * "partial close" because we don't check the last-char buffer
93680387638SSascha Wildner 	 * or anything).
93780387638SSascha Wildner 	 */
93880387638SSascha Wildner 
939a4c7eb57SSascha Wildner 	if ( ! (PS_INLINE & p->ps->flags))
94080387638SSascha Wildner 		return;
94180387638SSascha Wildner 
94280387638SSascha Wildner 	if (TERMTYPE_PS != p->type) {
94380387638SSascha Wildner 		ps_printf(p, ") Tj\nET\n");
94480387638SSascha Wildner 	} else
94580387638SSascha Wildner 		ps_printf(p, ") show\n");
94680387638SSascha Wildner 
947a4c7eb57SSascha Wildner 	p->ps->flags &= ~PS_INLINE;
94880387638SSascha Wildner }
94980387638SSascha Wildner 
95080387638SSascha Wildner 
95180387638SSascha Wildner static void
95280387638SSascha Wildner ps_fclose(struct termp *p)
95380387638SSascha Wildner {
95480387638SSascha Wildner 
95580387638SSascha Wildner 	/*
95680387638SSascha Wildner 	 * Strong closure: if we have a last-char, spit it out after
95780387638SSascha Wildner 	 * checking that we're in the right font mode.  This will of
95880387638SSascha Wildner 	 * course open a new scope, if applicable.
95980387638SSascha Wildner 	 *
96080387638SSascha Wildner 	 * Following this, close out any scope that's open.
96180387638SSascha Wildner 	 */
96280387638SSascha Wildner 
963a4c7eb57SSascha Wildner 	if ('\0' != p->ps->last) {
964a4c7eb57SSascha Wildner 		if (p->ps->lastf != TERMFONT_NONE) {
96580387638SSascha Wildner 			ps_pclose(p);
96680387638SSascha Wildner 			ps_setfont(p, TERMFONT_NONE);
96780387638SSascha Wildner 		}
968a4c7eb57SSascha Wildner 		ps_pletter(p, p->ps->last);
969a4c7eb57SSascha Wildner 		p->ps->last = '\0';
97080387638SSascha Wildner 	}
97180387638SSascha Wildner 
972a4c7eb57SSascha Wildner 	if ( ! (PS_INLINE & p->ps->flags))
97380387638SSascha Wildner 		return;
97480387638SSascha Wildner 
97580387638SSascha Wildner 	ps_pclose(p);
97680387638SSascha Wildner }
97780387638SSascha Wildner 
97880387638SSascha Wildner 
97980387638SSascha Wildner static void
980a4c7eb57SSascha Wildner ps_letter(struct termp *p, int arg)
98180387638SSascha Wildner {
982a4c7eb57SSascha Wildner 	char		cc, c;
983a4c7eb57SSascha Wildner 
984a4c7eb57SSascha Wildner 	/* LINTED */
985a4c7eb57SSascha Wildner 	c = arg >= 128 || arg <= 0 ? '?' : arg;
98680387638SSascha Wildner 
98780387638SSascha Wildner 	/*
98880387638SSascha Wildner 	 * State machine dictates whether to buffer the last character
98980387638SSascha Wildner 	 * or not.  Basically, encoded words are detected by checking if
99080387638SSascha Wildner 	 * we're an "8" and switching on the buffer.  Then we put "8" in
99180387638SSascha Wildner 	 * our buffer, and on the next charater, flush both character
99280387638SSascha Wildner 	 * and buffer.  Thus, "regular" words are detected by having a
99380387638SSascha Wildner 	 * regular character and a regular buffer character.
99480387638SSascha Wildner 	 */
99580387638SSascha Wildner 
996a4c7eb57SSascha Wildner 	if ('\0' == p->ps->last) {
99780387638SSascha Wildner 		assert(8 != c);
998a4c7eb57SSascha Wildner 		p->ps->last = c;
99980387638SSascha Wildner 		return;
1000a4c7eb57SSascha Wildner 	} else if (8 == p->ps->last) {
100180387638SSascha Wildner 		assert(8 != c);
1002a4c7eb57SSascha Wildner 		p->ps->last = '\0';
100380387638SSascha Wildner 	} else if (8 == c) {
1004a4c7eb57SSascha Wildner 		assert(8 != p->ps->last);
1005a4c7eb57SSascha Wildner 		if ('_' == p->ps->last) {
1006a4c7eb57SSascha Wildner 			if (p->ps->lastf != TERMFONT_UNDER) {
100780387638SSascha Wildner 				ps_pclose(p);
100880387638SSascha Wildner 				ps_setfont(p, TERMFONT_UNDER);
100980387638SSascha Wildner 			}
1010a4c7eb57SSascha Wildner 		} else if (p->ps->lastf != TERMFONT_BOLD) {
101180387638SSascha Wildner 			ps_pclose(p);
101280387638SSascha Wildner 			ps_setfont(p, TERMFONT_BOLD);
101380387638SSascha Wildner 		}
1014a4c7eb57SSascha Wildner 		p->ps->last = c;
101580387638SSascha Wildner 		return;
101680387638SSascha Wildner 	} else {
1017a4c7eb57SSascha Wildner 		if (p->ps->lastf != TERMFONT_NONE) {
101880387638SSascha Wildner 			ps_pclose(p);
101980387638SSascha Wildner 			ps_setfont(p, TERMFONT_NONE);
102080387638SSascha Wildner 		}
1021a4c7eb57SSascha Wildner 		cc = p->ps->last;
1022a4c7eb57SSascha Wildner 		p->ps->last = c;
102380387638SSascha Wildner 		c = cc;
102480387638SSascha Wildner 	}
102580387638SSascha Wildner 
102680387638SSascha Wildner 	ps_pletter(p, c);
102780387638SSascha Wildner }
102880387638SSascha Wildner 
102980387638SSascha Wildner 
103080387638SSascha Wildner static void
103180387638SSascha Wildner ps_advance(struct termp *p, size_t len)
103280387638SSascha Wildner {
103380387638SSascha Wildner 
103480387638SSascha Wildner 	/*
103580387638SSascha Wildner 	 * Advance some spaces.  This can probably be made smarter,
103680387638SSascha Wildner 	 * i.e., to have multiple space-separated words in the same
103780387638SSascha Wildner 	 * scope, but this is easier:  just close out the current scope
103880387638SSascha Wildner 	 * and readjust our column settings.
103980387638SSascha Wildner 	 */
104080387638SSascha Wildner 
104180387638SSascha Wildner 	ps_fclose(p);
1042a4c7eb57SSascha Wildner 	p->ps->pscol += len;
104380387638SSascha Wildner }
104480387638SSascha Wildner 
104580387638SSascha Wildner 
104680387638SSascha Wildner static void
104780387638SSascha Wildner ps_endline(struct termp *p)
104880387638SSascha Wildner {
104980387638SSascha Wildner 
105080387638SSascha Wildner 	/* Close out any scopes we have open: we're at eoln. */
105180387638SSascha Wildner 
105280387638SSascha Wildner 	ps_fclose(p);
105380387638SSascha Wildner 
105480387638SSascha Wildner 	/*
105580387638SSascha Wildner 	 * If we're in the margin, don't try to recalculate our current
105680387638SSascha Wildner 	 * row.  XXX: if the column tries to be fancy with multiple
105780387638SSascha Wildner 	 * lines, we'll do nasty stuff.
105880387638SSascha Wildner 	 */
105980387638SSascha Wildner 
1060a4c7eb57SSascha Wildner 	if (PS_MARGINS & p->ps->flags)
106180387638SSascha Wildner 		return;
106280387638SSascha Wildner 
106380387638SSascha Wildner 	/* Left-justify. */
106480387638SSascha Wildner 
1065a4c7eb57SSascha Wildner 	p->ps->pscol = p->ps->left;
106680387638SSascha Wildner 
106780387638SSascha Wildner 	/* If we haven't printed anything, return. */
106880387638SSascha Wildner 
1069a4c7eb57SSascha Wildner 	if (PS_NEWPAGE & p->ps->flags)
107080387638SSascha Wildner 		return;
107180387638SSascha Wildner 
107280387638SSascha Wildner 	/*
107380387638SSascha Wildner 	 * Put us down a line.  If we're at the page bottom, spit out a
107480387638SSascha Wildner 	 * showpage and restart our row.
107580387638SSascha Wildner 	 */
107680387638SSascha Wildner 
1077a4c7eb57SSascha Wildner 	if (p->ps->psrow >= p->ps->lineheight +
1078a4c7eb57SSascha Wildner 			p->ps->bottom) {
1079a4c7eb57SSascha Wildner 		p->ps->psrow -= p->ps->lineheight;
108080387638SSascha Wildner 		return;
108180387638SSascha Wildner 	}
108280387638SSascha Wildner 
108380387638SSascha Wildner 	ps_closepage(p);
108480387638SSascha Wildner }
108580387638SSascha Wildner 
108680387638SSascha Wildner 
108780387638SSascha Wildner static void
108880387638SSascha Wildner ps_setfont(struct termp *p, enum termfont f)
108980387638SSascha Wildner {
109080387638SSascha Wildner 
109180387638SSascha Wildner 	assert(f < TERMFONT__MAX);
1092a4c7eb57SSascha Wildner 	p->ps->lastf = f;
109380387638SSascha Wildner 
109480387638SSascha Wildner 	/*
109580387638SSascha Wildner 	 * If we're still at the top of the page, let the font-setting
109680387638SSascha Wildner 	 * be delayed until we actually have stuff to print.
109780387638SSascha Wildner 	 */
109880387638SSascha Wildner 
1099a4c7eb57SSascha Wildner 	if (PS_NEWPAGE & p->ps->flags)
110080387638SSascha Wildner 		return;
110180387638SSascha Wildner 
110280387638SSascha Wildner 	if (TERMTYPE_PS == p->type)
110380387638SSascha Wildner 		ps_printf(p, "/%s %zu selectfont\n",
110480387638SSascha Wildner 				fonts[(int)f].name,
1105a4c7eb57SSascha Wildner 				p->ps->scale);
110680387638SSascha Wildner 	else
110780387638SSascha Wildner 		ps_printf(p, "/F%d %zu Tf\n",
110880387638SSascha Wildner 				(int)f,
1109a4c7eb57SSascha Wildner 				p->ps->scale);
111080387638SSascha Wildner }
111180387638SSascha Wildner 
111280387638SSascha Wildner 
111380387638SSascha Wildner /* ARGSUSED */
111480387638SSascha Wildner static size_t
1115a4c7eb57SSascha Wildner ps_width(const struct termp *p, int c)
111680387638SSascha Wildner {
111780387638SSascha Wildner 
111880387638SSascha Wildner 	if (c <= 32 || c - 32 >= MAXCHAR)
111980387638SSascha Wildner 		return((size_t)fonts[(int)TERMFONT_NONE].gly[0].wx);
112080387638SSascha Wildner 
112180387638SSascha Wildner 	c -= 32;
1122a4c7eb57SSascha Wildner 	return((size_t)fonts[(int)TERMFONT_NONE].gly[c].wx);
112380387638SSascha Wildner }
112480387638SSascha Wildner 
112580387638SSascha Wildner 
112680387638SSascha Wildner static double
112780387638SSascha Wildner ps_hspan(const struct termp *p, const struct roffsu *su)
112880387638SSascha Wildner {
112980387638SSascha Wildner 	double		 r;
113080387638SSascha Wildner 
113180387638SSascha Wildner 	/*
113280387638SSascha Wildner 	 * All of these measurements are derived by converting from the
113380387638SSascha Wildner 	 * native measurement to AFM units.
113480387638SSascha Wildner 	 */
113580387638SSascha Wildner 
113680387638SSascha Wildner 	switch (su->unit) {
113780387638SSascha Wildner 	case (SCALE_CM):
113880387638SSascha Wildner 		r = PNT2AFM(p, su->scale * 28.34);
113980387638SSascha Wildner 		break;
114080387638SSascha Wildner 	case (SCALE_IN):
114180387638SSascha Wildner 		r = PNT2AFM(p, su->scale * 72);
114280387638SSascha Wildner 		break;
114380387638SSascha Wildner 	case (SCALE_PC):
114480387638SSascha Wildner 		r = PNT2AFM(p, su->scale * 12);
114580387638SSascha Wildner 		break;
114680387638SSascha Wildner 	case (SCALE_PT):
114780387638SSascha Wildner 		r = PNT2AFM(p, su->scale * 100);
114880387638SSascha Wildner 		break;
114980387638SSascha Wildner 	case (SCALE_EM):
115080387638SSascha Wildner 		r = su->scale *
115180387638SSascha Wildner 			fonts[(int)TERMFONT_NONE].gly[109 - 32].wx;
115280387638SSascha Wildner 		break;
115380387638SSascha Wildner 	case (SCALE_MM):
115480387638SSascha Wildner 		r = PNT2AFM(p, su->scale * 2.834);
115580387638SSascha Wildner 		break;
115680387638SSascha Wildner 	case (SCALE_EN):
115780387638SSascha Wildner 		r = su->scale *
115880387638SSascha Wildner 			fonts[(int)TERMFONT_NONE].gly[110 - 32].wx;
115980387638SSascha Wildner 		break;
116080387638SSascha Wildner 	case (SCALE_VS):
1161a4c7eb57SSascha Wildner 		r = su->scale * p->ps->lineheight;
116280387638SSascha Wildner 		break;
116380387638SSascha Wildner 	default:
116480387638SSascha Wildner 		r = su->scale;
116580387638SSascha Wildner 		break;
116680387638SSascha Wildner 	}
116780387638SSascha Wildner 
116880387638SSascha Wildner 	return(r);
116980387638SSascha Wildner }
117080387638SSascha Wildner 
1171a4c7eb57SSascha Wildner static void
1172a4c7eb57SSascha Wildner ps_growbuf(struct termp *p, size_t sz)
1173a4c7eb57SSascha Wildner {
1174a4c7eb57SSascha Wildner 	if (p->ps->psmargcur + sz <= p->ps->psmargsz)
1175a4c7eb57SSascha Wildner 		return;
1176a4c7eb57SSascha Wildner 
1177a4c7eb57SSascha Wildner 	if (sz < PS_BUFSLOP)
1178a4c7eb57SSascha Wildner 		sz = PS_BUFSLOP;
1179a4c7eb57SSascha Wildner 
1180a4c7eb57SSascha Wildner 	p->ps->psmargsz += sz;
1181a4c7eb57SSascha Wildner 
1182a4c7eb57SSascha Wildner 	p->ps->psmarg = mandoc_realloc
1183a4c7eb57SSascha Wildner 		(p->ps->psmarg, p->ps->psmargsz);
1184a4c7eb57SSascha Wildner }
1185a4c7eb57SSascha Wildner 
1186