xref: /dragonfly/contrib/mdocml/term_ps.c (revision 99db7d0e)
1*99db7d0eSSascha Wildner /* $Id: term_ps.c,v 1.92 2020/09/06 14:45:22 schwarze Exp $ */
280387638SSascha Wildner /*
336342e81SSascha Wildner  * Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4*99db7d0eSSascha Wildner  * Copyright (c) 2014,2015,2016,2017,2020 Ingo Schwarze <schwarze@openbsd.org>
554ba9607SSascha Wildner  * Copyright (c) 2017 Marc Espie <espie@openbsd.org>
680387638SSascha Wildner  *
780387638SSascha Wildner  * Permission to use, copy, modify, and distribute this software for any
880387638SSascha Wildner  * purpose with or without fee is hereby granted, provided that the above
980387638SSascha Wildner  * copyright notice and this permission notice appear in all copies.
1080387638SSascha Wildner  *
1154ba9607SSascha Wildner  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1280387638SSascha Wildner  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1354ba9607SSascha Wildner  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1480387638SSascha Wildner  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1580387638SSascha Wildner  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1680387638SSascha Wildner  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1780387638SSascha Wildner  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1880387638SSascha Wildner  */
1980387638SSascha Wildner #include "config.h"
2080387638SSascha Wildner 
2180387638SSascha Wildner #include <sys/types.h>
2280387638SSascha Wildner 
2380387638SSascha Wildner #include <assert.h>
2454ba9607SSascha Wildner #if HAVE_ERR
2554ba9607SSascha Wildner #include <err.h>
2654ba9607SSascha Wildner #endif
2780387638SSascha Wildner #include <stdarg.h>
2880387638SSascha Wildner #include <stdint.h>
2980387638SSascha Wildner #include <stdio.h>
3080387638SSascha Wildner #include <stdlib.h>
3180387638SSascha Wildner #include <string.h>
3280387638SSascha Wildner #include <unistd.h>
3380387638SSascha Wildner 
34070c62a6SFranco Fichtner #include "mandoc_aux.h"
3580387638SSascha Wildner #include "out.h"
3680387638SSascha Wildner #include "term.h"
3754ba9607SSascha Wildner #include "manconf.h"
3854ba9607SSascha Wildner #include "main.h"
3980387638SSascha Wildner 
40a4c7eb57SSascha Wildner /* These work the buffer used by the header and footer. */
41a4c7eb57SSascha Wildner #define	PS_BUFSLOP	  128
42a4c7eb57SSascha Wildner 
4380387638SSascha Wildner /* Convert PostScript point "x" to an AFM unit. */
44070c62a6SFranco Fichtner #define	PNT2AFM(p, x) \
45a4c7eb57SSascha Wildner 	(size_t)((double)(x) * (1000.0 / (double)(p)->ps->scale))
4680387638SSascha Wildner 
4780387638SSascha Wildner /* Convert an AFM unit "x" to a PostScript points */
48070c62a6SFranco Fichtner #define	AFM2PNT(p, x) \
49a4c7eb57SSascha Wildner 	((double)(x) / (1000.0 / (double)(p)->ps->scale))
5080387638SSascha Wildner 
5180387638SSascha Wildner struct	glyph {
5280387638SSascha Wildner 	unsigned short	  wx; /* WX in AFM */
5380387638SSascha Wildner };
5480387638SSascha Wildner 
5580387638SSascha Wildner struct	font {
5680387638SSascha Wildner 	const char	 *name; /* FontName in AFM */
5780387638SSascha Wildner #define	MAXCHAR		  95 /* total characters we can handle */
5880387638SSascha Wildner 	struct glyph	  gly[MAXCHAR]; /* glyph metrics */
5980387638SSascha Wildner };
6080387638SSascha Wildner 
61a4c7eb57SSascha Wildner struct	termp_ps {
62a4c7eb57SSascha Wildner 	int		  flags;
63a4c7eb57SSascha Wildner #define	PS_INLINE	 (1 << 0)	/* we're in a word */
64a4c7eb57SSascha Wildner #define	PS_MARGINS	 (1 << 1)	/* we're in the margins */
65a4c7eb57SSascha Wildner #define	PS_NEWPAGE	 (1 << 2)	/* new page, no words yet */
6654ba9607SSascha Wildner #define	PS_BACKSP	 (1 << 3)	/* last character was backspace */
67a4c7eb57SSascha Wildner 	size_t		  pscol;	/* visible column (AFM units) */
6854ba9607SSascha Wildner 	size_t		  pscolnext;	/* used for overstrike */
69a4c7eb57SSascha Wildner 	size_t		  psrow;	/* visible row (AFM units) */
7054ba9607SSascha Wildner 	size_t		  lastrow;	/* psrow of the previous word */
71a4c7eb57SSascha Wildner 	char		 *psmarg;	/* margin buf */
72a4c7eb57SSascha Wildner 	size_t		  psmargsz;	/* margin buf size */
73a4c7eb57SSascha Wildner 	size_t		  psmargcur;	/* cur index in margin buf */
7454ba9607SSascha Wildner 	char		  last;		/* last non-backspace seen */
75a4c7eb57SSascha Wildner 	enum termfont	  lastf;	/* last set font */
7654ba9607SSascha Wildner 	enum termfont	  nextf;	/* building next font here */
77a4c7eb57SSascha Wildner 	size_t		  scale;	/* font scaling factor */
78a4c7eb57SSascha Wildner 	size_t		  pages;	/* number of pages shown */
79a4c7eb57SSascha Wildner 	size_t		  lineheight;	/* line height (AFM units) */
80a4c7eb57SSascha Wildner 	size_t		  top;		/* body top (AFM units) */
81a4c7eb57SSascha Wildner 	size_t		  bottom;	/* body bottom (AFM units) */
8254ba9607SSascha Wildner 	const char	 *medianame;	/* for DocumentMedia and PageSize */
83a4c7eb57SSascha Wildner 	size_t		  height;	/* page height (AFM units */
84a4c7eb57SSascha Wildner 	size_t		  width;	/* page width (AFM units) */
85070c62a6SFranco Fichtner 	size_t		  lastwidth;	/* page width before last ll */
86a4c7eb57SSascha Wildner 	size_t		  left;		/* body left (AFM units) */
87a4c7eb57SSascha Wildner 	size_t		  header;	/* header pos (AFM units) */
88a4c7eb57SSascha Wildner 	size_t		  footer;	/* footer pos (AFM units) */
89a4c7eb57SSascha Wildner 	size_t		  pdfbytes;	/* current output byte */
90a4c7eb57SSascha Wildner 	size_t		  pdflastpg;	/* byte of last page mark */
91a4c7eb57SSascha Wildner 	size_t		  pdfbody;	/* start of body object */
92a4c7eb57SSascha Wildner 	size_t		 *pdfobjs;	/* table of object offsets */
93a4c7eb57SSascha Wildner 	size_t		  pdfobjsz;	/* size of pdfobjs */
94a4c7eb57SSascha Wildner };
95a4c7eb57SSascha Wildner 
9654ba9607SSascha Wildner static	int		  ps_hspan(const struct termp *,
97a4c7eb57SSascha Wildner 				const struct roffsu *);
98a4c7eb57SSascha Wildner static	size_t		  ps_width(const struct termp *, int);
99a4c7eb57SSascha Wildner static	void		  ps_advance(struct termp *, size_t);
100a4c7eb57SSascha Wildner static	void		  ps_begin(struct termp *);
101a4c7eb57SSascha Wildner static	void		  ps_closepage(struct termp *);
102a4c7eb57SSascha Wildner static	void		  ps_end(struct termp *);
103a4c7eb57SSascha Wildner static	void		  ps_endline(struct termp *);
104a4c7eb57SSascha Wildner static	void		  ps_growbuf(struct termp *, size_t);
105a4c7eb57SSascha Wildner static	void		  ps_letter(struct termp *, int);
106a4c7eb57SSascha Wildner static	void		  ps_pclose(struct termp *);
10754ba9607SSascha Wildner static	void		  ps_plast(struct termp *);
108a4c7eb57SSascha Wildner static	void		  ps_pletter(struct termp *, int);
10954ba9607SSascha Wildner static	void		  ps_printf(struct termp *, const char *, ...)
11054ba9607SSascha Wildner 				__attribute__((__format__ (__printf__, 2, 3)));
111a4c7eb57SSascha Wildner static	void		  ps_putchar(struct termp *, char);
112a4c7eb57SSascha Wildner static	void		  ps_setfont(struct termp *, enum termfont);
11354ba9607SSascha Wildner static	void		  ps_setwidth(struct termp *, int, int);
11454ba9607SSascha Wildner static	struct termp	 *pspdf_alloc(const struct manoutput *, enum termtype);
115a4c7eb57SSascha Wildner static	void		  pdf_obj(struct termp *, size_t);
116a4c7eb57SSascha Wildner 
11780387638SSascha Wildner /*
11880387638SSascha Wildner  * We define, for the time being, three fonts: bold, oblique/italic, and
11980387638SSascha Wildner  * normal (roman).  The following table hard-codes the font metrics for
12080387638SSascha Wildner  * ASCII, i.e., 32--127.
12180387638SSascha Wildner  */
12280387638SSascha Wildner 
12380387638SSascha Wildner static	const struct font fonts[TERMFONT__MAX] = {
12480387638SSascha Wildner 	{ "Times-Roman", {
12580387638SSascha Wildner 		{ 250 },
12680387638SSascha Wildner 		{ 333 },
12780387638SSascha Wildner 		{ 408 },
12880387638SSascha Wildner 		{ 500 },
12980387638SSascha Wildner 		{ 500 },
13080387638SSascha Wildner 		{ 833 },
13180387638SSascha Wildner 		{ 778 },
13280387638SSascha Wildner 		{ 333 },
13380387638SSascha Wildner 		{ 333 },
13480387638SSascha Wildner 		{ 333 },
13580387638SSascha Wildner 		{ 500 },
13680387638SSascha Wildner 		{ 564 },
13780387638SSascha Wildner 		{ 250 },
13880387638SSascha Wildner 		{ 333 },
13980387638SSascha Wildner 		{ 250 },
14080387638SSascha Wildner 		{ 278 },
14180387638SSascha Wildner 		{ 500 },
14280387638SSascha Wildner 		{ 500 },
14380387638SSascha Wildner 		{ 500 },
14480387638SSascha Wildner 		{ 500 },
14580387638SSascha Wildner 		{ 500 },
14680387638SSascha Wildner 		{ 500 },
14780387638SSascha Wildner 		{ 500 },
14880387638SSascha Wildner 		{ 500 },
14980387638SSascha Wildner 		{ 500 },
15080387638SSascha Wildner 		{ 500 },
15180387638SSascha Wildner 		{ 278 },
15280387638SSascha Wildner 		{ 278 },
15380387638SSascha Wildner 		{ 564 },
15480387638SSascha Wildner 		{ 564 },
15580387638SSascha Wildner 		{ 564 },
15680387638SSascha Wildner 		{ 444 },
15780387638SSascha Wildner 		{ 921 },
15880387638SSascha Wildner 		{ 722 },
15980387638SSascha Wildner 		{ 667 },
16080387638SSascha Wildner 		{ 667 },
16180387638SSascha Wildner 		{ 722 },
16280387638SSascha Wildner 		{ 611 },
16380387638SSascha Wildner 		{ 556 },
16480387638SSascha Wildner 		{ 722 },
16580387638SSascha Wildner 		{ 722 },
16680387638SSascha Wildner 		{ 333 },
16780387638SSascha Wildner 		{ 389 },
16880387638SSascha Wildner 		{ 722 },
16980387638SSascha Wildner 		{ 611 },
17080387638SSascha Wildner 		{ 889 },
17180387638SSascha Wildner 		{ 722 },
17280387638SSascha Wildner 		{ 722 },
17380387638SSascha Wildner 		{ 556 },
17480387638SSascha Wildner 		{ 722 },
17580387638SSascha Wildner 		{ 667 },
17680387638SSascha Wildner 		{ 556 },
17780387638SSascha Wildner 		{ 611 },
17880387638SSascha Wildner 		{ 722 },
17980387638SSascha Wildner 		{ 722 },
18080387638SSascha Wildner 		{ 944 },
18180387638SSascha Wildner 		{ 722 },
18280387638SSascha Wildner 		{ 722 },
18380387638SSascha Wildner 		{ 611 },
18480387638SSascha Wildner 		{ 333 },
18580387638SSascha Wildner 		{ 278 },
18680387638SSascha Wildner 		{ 333 },
18780387638SSascha Wildner 		{ 469 },
18880387638SSascha Wildner 		{ 500 },
18980387638SSascha Wildner 		{ 333 },
19080387638SSascha Wildner 		{ 444 },
19180387638SSascha Wildner 		{ 500 },
19280387638SSascha Wildner 		{ 444 },
19380387638SSascha Wildner 		{  500},
19480387638SSascha Wildner 		{  444},
19580387638SSascha Wildner 		{  333},
19680387638SSascha Wildner 		{  500},
19780387638SSascha Wildner 		{  500},
19880387638SSascha Wildner 		{  278},
19980387638SSascha Wildner 		{  278},
20080387638SSascha Wildner 		{  500},
20180387638SSascha Wildner 		{  278},
20280387638SSascha Wildner 		{  778},
20380387638SSascha Wildner 		{  500},
20480387638SSascha Wildner 		{  500},
20580387638SSascha Wildner 		{  500},
20680387638SSascha Wildner 		{  500},
20780387638SSascha Wildner 		{  333},
20880387638SSascha Wildner 		{  389},
20980387638SSascha Wildner 		{  278},
21080387638SSascha Wildner 		{  500},
21180387638SSascha Wildner 		{  500},
21280387638SSascha Wildner 		{  722},
21380387638SSascha Wildner 		{  500},
21480387638SSascha Wildner 		{  500},
21580387638SSascha Wildner 		{  444},
21680387638SSascha Wildner 		{  480},
21780387638SSascha Wildner 		{  200},
21880387638SSascha Wildner 		{  480},
21980387638SSascha Wildner 		{  541},
22080387638SSascha Wildner 	} },
22180387638SSascha Wildner 	{ "Times-Bold", {
22280387638SSascha Wildner 		{ 250  },
22380387638SSascha Wildner 		{ 333  },
22480387638SSascha Wildner 		{ 555  },
22580387638SSascha Wildner 		{ 500  },
22680387638SSascha Wildner 		{ 500  },
22780387638SSascha Wildner 		{ 1000 },
22880387638SSascha Wildner 		{ 833  },
22980387638SSascha Wildner 		{ 333  },
23080387638SSascha Wildner 		{ 333  },
23180387638SSascha Wildner 		{ 333  },
23280387638SSascha Wildner 		{ 500  },
23380387638SSascha Wildner 		{ 570  },
23480387638SSascha Wildner 		{ 250  },
23580387638SSascha Wildner 		{ 333  },
23680387638SSascha Wildner 		{ 250  },
23780387638SSascha Wildner 		{ 278  },
23880387638SSascha Wildner 		{ 500  },
23980387638SSascha Wildner 		{ 500  },
24080387638SSascha Wildner 		{ 500  },
24180387638SSascha Wildner 		{ 500  },
24280387638SSascha Wildner 		{ 500  },
24380387638SSascha Wildner 		{ 500  },
24480387638SSascha Wildner 		{ 500  },
24580387638SSascha Wildner 		{ 500  },
24680387638SSascha Wildner 		{ 500  },
24780387638SSascha Wildner 		{ 500  },
24880387638SSascha Wildner 		{ 333  },
24980387638SSascha Wildner 		{ 333  },
25080387638SSascha Wildner 		{ 570  },
25180387638SSascha Wildner 		{ 570  },
25280387638SSascha Wildner 		{ 570  },
25380387638SSascha Wildner 		{ 500  },
25480387638SSascha Wildner 		{ 930  },
25580387638SSascha Wildner 		{ 722  },
25680387638SSascha Wildner 		{ 667  },
25780387638SSascha Wildner 		{ 722  },
25880387638SSascha Wildner 		{ 722  },
25980387638SSascha Wildner 		{ 667  },
26080387638SSascha Wildner 		{ 611  },
26180387638SSascha Wildner 		{ 778  },
26280387638SSascha Wildner 		{ 778  },
26380387638SSascha Wildner 		{ 389  },
26480387638SSascha Wildner 		{ 500  },
26580387638SSascha Wildner 		{ 778  },
26680387638SSascha Wildner 		{ 667  },
26780387638SSascha Wildner 		{ 944  },
26880387638SSascha Wildner 		{ 722  },
26980387638SSascha Wildner 		{ 778  },
27080387638SSascha Wildner 		{ 611  },
27180387638SSascha Wildner 		{ 778  },
27280387638SSascha Wildner 		{ 722  },
27380387638SSascha Wildner 		{ 556  },
27480387638SSascha Wildner 		{ 667  },
27580387638SSascha Wildner 		{ 722  },
27680387638SSascha Wildner 		{ 722  },
27780387638SSascha Wildner 		{ 1000 },
27880387638SSascha Wildner 		{ 722  },
27980387638SSascha Wildner 		{ 722  },
28080387638SSascha Wildner 		{ 667  },
28180387638SSascha Wildner 		{ 333  },
28280387638SSascha Wildner 		{ 278  },
28380387638SSascha Wildner 		{ 333  },
28480387638SSascha Wildner 		{ 581  },
28580387638SSascha Wildner 		{ 500  },
28680387638SSascha Wildner 		{ 333  },
28780387638SSascha Wildner 		{ 500  },
28880387638SSascha Wildner 		{ 556  },
28980387638SSascha Wildner 		{ 444  },
29080387638SSascha Wildner 		{  556 },
29180387638SSascha Wildner 		{  444 },
29280387638SSascha Wildner 		{  333 },
29380387638SSascha Wildner 		{  500 },
29480387638SSascha Wildner 		{  556 },
29580387638SSascha Wildner 		{  278 },
29680387638SSascha Wildner 		{  333 },
29780387638SSascha Wildner 		{  556 },
29880387638SSascha Wildner 		{  278 },
29980387638SSascha Wildner 		{  833 },
30080387638SSascha Wildner 		{  556 },
30180387638SSascha Wildner 		{  500 },
30280387638SSascha Wildner 		{  556 },
30380387638SSascha Wildner 		{  556 },
30480387638SSascha Wildner 		{  444 },
30580387638SSascha Wildner 		{  389 },
30680387638SSascha Wildner 		{  333 },
30780387638SSascha Wildner 		{  556 },
30880387638SSascha Wildner 		{  500 },
30980387638SSascha Wildner 		{  722 },
31080387638SSascha Wildner 		{  500 },
31180387638SSascha Wildner 		{  500 },
31280387638SSascha Wildner 		{  444 },
31380387638SSascha Wildner 		{  394 },
31480387638SSascha Wildner 		{  220 },
31580387638SSascha Wildner 		{  394 },
31680387638SSascha Wildner 		{  520 },
31780387638SSascha Wildner 	} },
31880387638SSascha Wildner 	{ "Times-Italic", {
31980387638SSascha Wildner 		{ 250  },
32080387638SSascha Wildner 		{ 333  },
32180387638SSascha Wildner 		{ 420  },
32280387638SSascha Wildner 		{ 500  },
32380387638SSascha Wildner 		{ 500  },
32480387638SSascha Wildner 		{ 833  },
32580387638SSascha Wildner 		{ 778  },
32680387638SSascha Wildner 		{ 333  },
32780387638SSascha Wildner 		{ 333  },
32880387638SSascha Wildner 		{ 333  },
32980387638SSascha Wildner 		{ 500  },
33080387638SSascha Wildner 		{ 675  },
33180387638SSascha Wildner 		{ 250  },
33280387638SSascha Wildner 		{ 333  },
33380387638SSascha Wildner 		{ 250  },
33480387638SSascha Wildner 		{ 278  },
33580387638SSascha Wildner 		{ 500  },
33680387638SSascha Wildner 		{ 500  },
33780387638SSascha Wildner 		{ 500  },
33880387638SSascha Wildner 		{ 500  },
33980387638SSascha Wildner 		{ 500  },
34080387638SSascha Wildner 		{ 500  },
34180387638SSascha Wildner 		{ 500  },
34280387638SSascha Wildner 		{ 500  },
34380387638SSascha Wildner 		{ 500  },
34480387638SSascha Wildner 		{ 500  },
34580387638SSascha Wildner 		{ 333  },
34680387638SSascha Wildner 		{ 333  },
34780387638SSascha Wildner 		{ 675  },
34880387638SSascha Wildner 		{ 675  },
34980387638SSascha Wildner 		{ 675  },
35080387638SSascha Wildner 		{ 500  },
35180387638SSascha Wildner 		{ 920  },
35280387638SSascha Wildner 		{ 611  },
35380387638SSascha Wildner 		{ 611  },
35480387638SSascha Wildner 		{ 667  },
35580387638SSascha Wildner 		{ 722  },
35680387638SSascha Wildner 		{ 611  },
35780387638SSascha Wildner 		{ 611  },
35880387638SSascha Wildner 		{ 722  },
35980387638SSascha Wildner 		{ 722  },
36080387638SSascha Wildner 		{ 333  },
36180387638SSascha Wildner 		{ 444  },
36280387638SSascha Wildner 		{ 667  },
36380387638SSascha Wildner 		{ 556  },
36480387638SSascha Wildner 		{ 833  },
36580387638SSascha Wildner 		{ 667  },
36680387638SSascha Wildner 		{ 722  },
36780387638SSascha Wildner 		{ 611  },
36880387638SSascha Wildner 		{ 722  },
36980387638SSascha Wildner 		{ 611  },
37080387638SSascha Wildner 		{ 500  },
37180387638SSascha Wildner 		{ 556  },
37280387638SSascha Wildner 		{ 722  },
37380387638SSascha Wildner 		{ 611  },
37480387638SSascha Wildner 		{ 833  },
37580387638SSascha Wildner 		{ 611  },
37680387638SSascha Wildner 		{ 556  },
37780387638SSascha Wildner 		{ 556  },
37880387638SSascha Wildner 		{ 389  },
37980387638SSascha Wildner 		{ 278  },
38080387638SSascha Wildner 		{ 389  },
38180387638SSascha Wildner 		{ 422  },
38280387638SSascha Wildner 		{ 500  },
38380387638SSascha Wildner 		{ 333  },
38480387638SSascha Wildner 		{ 500  },
38580387638SSascha Wildner 		{ 500  },
38680387638SSascha Wildner 		{ 444  },
38780387638SSascha Wildner 		{  500 },
38880387638SSascha Wildner 		{  444 },
38980387638SSascha Wildner 		{  278 },
39080387638SSascha Wildner 		{  500 },
39180387638SSascha Wildner 		{  500 },
39280387638SSascha Wildner 		{  278 },
39380387638SSascha Wildner 		{  278 },
39480387638SSascha Wildner 		{  444 },
39580387638SSascha Wildner 		{  278 },
39680387638SSascha Wildner 		{  722 },
39780387638SSascha Wildner 		{  500 },
39880387638SSascha Wildner 		{  500 },
39980387638SSascha Wildner 		{  500 },
40080387638SSascha Wildner 		{  500 },
40180387638SSascha Wildner 		{  389 },
40280387638SSascha Wildner 		{  389 },
40380387638SSascha Wildner 		{  278 },
40480387638SSascha Wildner 		{  500 },
40580387638SSascha Wildner 		{  444 },
40680387638SSascha Wildner 		{  667 },
40780387638SSascha Wildner 		{  444 },
40880387638SSascha Wildner 		{  444 },
40980387638SSascha Wildner 		{  389 },
41080387638SSascha Wildner 		{  400 },
41180387638SSascha Wildner 		{  275 },
41280387638SSascha Wildner 		{  400 },
41380387638SSascha Wildner 		{  541 },
41480387638SSascha Wildner 	} },
41554ba9607SSascha Wildner 	{ "Times-BoldItalic", {
41654ba9607SSascha Wildner 		{  250 },
41754ba9607SSascha Wildner 		{  389 },
41854ba9607SSascha Wildner 		{  555 },
41954ba9607SSascha Wildner 		{  500 },
42054ba9607SSascha Wildner 		{  500 },
42154ba9607SSascha Wildner 		{  833 },
42254ba9607SSascha Wildner 		{  778 },
42354ba9607SSascha Wildner 		{  333 },
42454ba9607SSascha Wildner 		{  333 },
42554ba9607SSascha Wildner 		{  333 },
42654ba9607SSascha Wildner 		{  500 },
42754ba9607SSascha Wildner 		{  570 },
42854ba9607SSascha Wildner 		{  250 },
42954ba9607SSascha Wildner 		{  333 },
43054ba9607SSascha Wildner 		{  250 },
43154ba9607SSascha Wildner 		{  278 },
43254ba9607SSascha Wildner 		{  500 },
43354ba9607SSascha Wildner 		{  500 },
43454ba9607SSascha Wildner 		{  500 },
43554ba9607SSascha Wildner 		{  500 },
43654ba9607SSascha Wildner 		{  500 },
43754ba9607SSascha Wildner 		{  500 },
43854ba9607SSascha Wildner 		{  500 },
43954ba9607SSascha Wildner 		{  500 },
44054ba9607SSascha Wildner 		{  500 },
44154ba9607SSascha Wildner 		{  500 },
44254ba9607SSascha Wildner 		{  333 },
44354ba9607SSascha Wildner 		{  333 },
44454ba9607SSascha Wildner 		{  570 },
44554ba9607SSascha Wildner 		{  570 },
44654ba9607SSascha Wildner 		{  570 },
44754ba9607SSascha Wildner 		{  500 },
44854ba9607SSascha Wildner 		{  832 },
44954ba9607SSascha Wildner 		{  667 },
45054ba9607SSascha Wildner 		{  667 },
45154ba9607SSascha Wildner 		{  667 },
45254ba9607SSascha Wildner 		{  722 },
45354ba9607SSascha Wildner 		{  667 },
45454ba9607SSascha Wildner 		{  667 },
45554ba9607SSascha Wildner 		{  722 },
45654ba9607SSascha Wildner 		{  778 },
45754ba9607SSascha Wildner 		{  389 },
45854ba9607SSascha Wildner 		{  500 },
45954ba9607SSascha Wildner 		{  667 },
46054ba9607SSascha Wildner 		{  611 },
46154ba9607SSascha Wildner 		{  889 },
46254ba9607SSascha Wildner 		{  722 },
46354ba9607SSascha Wildner 		{  722 },
46454ba9607SSascha Wildner 		{  611 },
46554ba9607SSascha Wildner 		{  722 },
46654ba9607SSascha Wildner 		{  667 },
46754ba9607SSascha Wildner 		{  556 },
46854ba9607SSascha Wildner 		{  611 },
46954ba9607SSascha Wildner 		{  722 },
47054ba9607SSascha Wildner 		{  667 },
47154ba9607SSascha Wildner 		{  889 },
47254ba9607SSascha Wildner 		{  667 },
47354ba9607SSascha Wildner 		{  611 },
47454ba9607SSascha Wildner 		{  611 },
47554ba9607SSascha Wildner 		{  333 },
47654ba9607SSascha Wildner 		{  278 },
47754ba9607SSascha Wildner 		{  333 },
47854ba9607SSascha Wildner 		{  570 },
47954ba9607SSascha Wildner 		{  500 },
48054ba9607SSascha Wildner 		{  333 },
48154ba9607SSascha Wildner 		{  500 },
48254ba9607SSascha Wildner 		{  500 },
48354ba9607SSascha Wildner 		{  444 },
48454ba9607SSascha Wildner 		{  500 },
48554ba9607SSascha Wildner 		{  444 },
48654ba9607SSascha Wildner 		{  333 },
48754ba9607SSascha Wildner 		{  500 },
48854ba9607SSascha Wildner 		{  556 },
48954ba9607SSascha Wildner 		{  278 },
49054ba9607SSascha Wildner 		{  278 },
49154ba9607SSascha Wildner 		{  500 },
49254ba9607SSascha Wildner 		{  278 },
49354ba9607SSascha Wildner 		{  778 },
49454ba9607SSascha Wildner 		{  556 },
49554ba9607SSascha Wildner 		{  500 },
49654ba9607SSascha Wildner 		{  500 },
49754ba9607SSascha Wildner 		{  500 },
49854ba9607SSascha Wildner 		{  389 },
49954ba9607SSascha Wildner 		{  389 },
50054ba9607SSascha Wildner 		{  278 },
50154ba9607SSascha Wildner 		{  556 },
50254ba9607SSascha Wildner 		{  444 },
50354ba9607SSascha Wildner 		{  667 },
50454ba9607SSascha Wildner 		{  500 },
50554ba9607SSascha Wildner 		{  444 },
50654ba9607SSascha Wildner 		{  389 },
50754ba9607SSascha Wildner 		{  348 },
50854ba9607SSascha Wildner 		{  220 },
50954ba9607SSascha Wildner 		{  348 },
51054ba9607SSascha Wildner 		{  570 },
51154ba9607SSascha Wildner 	} },
51280387638SSascha Wildner };
51380387638SSascha Wildner 
51480387638SSascha Wildner void *
pdf_alloc(const struct manoutput * outopts)51554ba9607SSascha Wildner pdf_alloc(const struct manoutput *outopts)
51680387638SSascha Wildner {
51754ba9607SSascha Wildner 	return pspdf_alloc(outopts, TERMTYPE_PDF);
51880387638SSascha Wildner }
51980387638SSascha Wildner 
52080387638SSascha Wildner void *
ps_alloc(const struct manoutput * outopts)52154ba9607SSascha Wildner ps_alloc(const struct manoutput *outopts)
52280387638SSascha Wildner {
52354ba9607SSascha Wildner 	return pspdf_alloc(outopts, TERMTYPE_PS);
52480387638SSascha Wildner }
52580387638SSascha Wildner 
52680387638SSascha Wildner static struct termp *
pspdf_alloc(const struct manoutput * outopts,enum termtype type)52754ba9607SSascha Wildner pspdf_alloc(const struct manoutput *outopts, enum termtype type)
52880387638SSascha Wildner {
52980387638SSascha Wildner 	struct termp	*p;
53036342e81SSascha Wildner 	unsigned int	 pagex, pagey;
53136342e81SSascha Wildner 	size_t		 marginx, marginy, lineheight;
53280387638SSascha Wildner 	const char	*pp;
53380387638SSascha Wildner 
53454ba9607SSascha Wildner 	p = mandoc_calloc(1, sizeof(*p));
53554ba9607SSascha Wildner 	p->tcol = p->tcols = mandoc_calloc(1, sizeof(*p->tcol));
53654ba9607SSascha Wildner 	p->maxtcol = 1;
53754ba9607SSascha Wildner 	p->type = type;
53854ba9607SSascha Wildner 
539a4c7eb57SSascha Wildner 	p->enc = TERMENC_ASCII;
54054ba9607SSascha Wildner 	p->fontq = mandoc_reallocarray(NULL,
54154ba9607SSascha Wildner 	    (p->fontsz = 8), sizeof(*p->fontq));
54254ba9607SSascha Wildner 	p->fontq[0] = p->fontl = TERMFONT_NONE;
54354ba9607SSascha Wildner 	p->ps = mandoc_calloc(1, sizeof(*p->ps));
54480387638SSascha Wildner 
54580387638SSascha Wildner 	p->advance = ps_advance;
54680387638SSascha Wildner 	p->begin = ps_begin;
54780387638SSascha Wildner 	p->end = ps_end;
54880387638SSascha Wildner 	p->endline = ps_endline;
54980387638SSascha Wildner 	p->hspan = ps_hspan;
55080387638SSascha Wildner 	p->letter = ps_letter;
551070c62a6SFranco Fichtner 	p->setwidth = ps_setwidth;
55280387638SSascha Wildner 	p->width = ps_width;
55380387638SSascha Wildner 
55480387638SSascha Wildner 	/* Default to US letter (millimetres). */
55580387638SSascha Wildner 
55654ba9607SSascha Wildner 	p->ps->medianame = "Letter";
55780387638SSascha Wildner 	pagex = 216;
55880387638SSascha Wildner 	pagey = 279;
55980387638SSascha Wildner 
56080387638SSascha Wildner 	/*
56180387638SSascha Wildner 	 * The ISO-269 paper sizes can be calculated automatically, but
56280387638SSascha Wildner 	 * it would require bringing in -lm for pow() and I'd rather not
56380387638SSascha Wildner 	 * do that.  So just do it the easy way for now.  Since this
56480387638SSascha Wildner 	 * only happens once, I'm not terribly concerned.
56580387638SSascha Wildner 	 */
56680387638SSascha Wildner 
56754ba9607SSascha Wildner 	pp = outopts->paper;
56854ba9607SSascha Wildner 	if (pp != NULL && strcasecmp(pp, "letter") != 0) {
56954ba9607SSascha Wildner 		if (strcasecmp(pp, "a3") == 0) {
57054ba9607SSascha Wildner 			p->ps->medianame = "A3";
57180387638SSascha Wildner 			pagex = 297;
57280387638SSascha Wildner 			pagey = 420;
57354ba9607SSascha Wildner 		} else if (strcasecmp(pp, "a4") == 0) {
57454ba9607SSascha Wildner 			p->ps->medianame = "A4";
57580387638SSascha Wildner 			pagex = 210;
57680387638SSascha Wildner 			pagey = 297;
57754ba9607SSascha Wildner 		} else if (strcasecmp(pp, "a5") == 0) {
57854ba9607SSascha Wildner 			p->ps->medianame = "A5";
57980387638SSascha Wildner 			pagex = 148;
58080387638SSascha Wildner 			pagey = 210;
58154ba9607SSascha Wildner 		} else if (strcasecmp(pp, "legal") == 0) {
58254ba9607SSascha Wildner 			p->ps->medianame = "Legal";
58380387638SSascha Wildner 			pagex = 216;
58480387638SSascha Wildner 			pagey = 356;
58554ba9607SSascha Wildner 		} else if (sscanf(pp, "%ux%u", &pagex, &pagey) == 2)
58654ba9607SSascha Wildner 			p->ps->medianame = "CustomSize";
58754ba9607SSascha Wildner 		else
58854ba9607SSascha Wildner 			warnx("%s: Unknown paper", pp);
58936342e81SSascha Wildner 	}
59080387638SSascha Wildner 
59180387638SSascha Wildner 	/*
59280387638SSascha Wildner 	 * This MUST be defined before any PNT2AFM or AFM2PNT
59380387638SSascha Wildner 	 * calculations occur.
59480387638SSascha Wildner 	 */
59580387638SSascha Wildner 
596a4c7eb57SSascha Wildner 	p->ps->scale = 11;
59780387638SSascha Wildner 
59880387638SSascha Wildner 	/* Remember millimetres -> AFM units. */
59980387638SSascha Wildner 
60054ba9607SSascha Wildner 	pagex = PNT2AFM(p, ((double)pagex * 72.0 / 25.4));
60154ba9607SSascha Wildner 	pagey = PNT2AFM(p, ((double)pagey * 72.0 / 25.4));
60280387638SSascha Wildner 
60380387638SSascha Wildner 	/* Margins are 1/9 the page x and y. */
60480387638SSascha Wildner 
605070c62a6SFranco Fichtner 	marginx = (size_t)((double)pagex / 9.0);
606070c62a6SFranco Fichtner 	marginy = (size_t)((double)pagey / 9.0);
60780387638SSascha Wildner 
60880387638SSascha Wildner 	/* Line-height is 1.4em. */
60980387638SSascha Wildner 
610a4c7eb57SSascha Wildner 	lineheight = PNT2AFM(p, ((double)p->ps->scale * 1.4));
61180387638SSascha Wildner 
612070c62a6SFranco Fichtner 	p->ps->width = p->ps->lastwidth = (size_t)pagex;
61336342e81SSascha Wildner 	p->ps->height = (size_t)pagey;
614a4c7eb57SSascha Wildner 	p->ps->header = pagey - (marginy / 2) - (lineheight / 2);
615a4c7eb57SSascha Wildner 	p->ps->top = pagey - marginy;
616a4c7eb57SSascha Wildner 	p->ps->footer = (marginy / 2) - (lineheight / 2);
617a4c7eb57SSascha Wildner 	p->ps->bottom = marginy;
618a4c7eb57SSascha Wildner 	p->ps->left = marginx;
619a4c7eb57SSascha Wildner 	p->ps->lineheight = lineheight;
62080387638SSascha Wildner 
62180387638SSascha Wildner 	p->defrmargin = pagex - (marginx * 2);
62254ba9607SSascha Wildner 	return p;
62380387638SSascha Wildner }
62480387638SSascha Wildner 
625070c62a6SFranco Fichtner static void
ps_setwidth(struct termp * p,int iop,int width)62654ba9607SSascha Wildner ps_setwidth(struct termp *p, int iop, int width)
627070c62a6SFranco Fichtner {
628070c62a6SFranco Fichtner 	size_t	 lastwidth;
629070c62a6SFranco Fichtner 
630070c62a6SFranco Fichtner 	lastwidth = p->ps->width;
63154ba9607SSascha Wildner 	if (iop > 0)
632070c62a6SFranco Fichtner 		p->ps->width += width;
63354ba9607SSascha Wildner 	else if (iop == 0)
63454ba9607SSascha Wildner 		p->ps->width = width ? (size_t)width : p->ps->lastwidth;
63554ba9607SSascha Wildner 	else if (p->ps->width > (size_t)width)
636070c62a6SFranco Fichtner 		p->ps->width -= width;
637070c62a6SFranco Fichtner 	else
63854ba9607SSascha Wildner 		p->ps->width = 0;
639070c62a6SFranco Fichtner 	p->ps->lastwidth = lastwidth;
640070c62a6SFranco Fichtner }
64180387638SSascha Wildner 
64280387638SSascha Wildner void
pspdf_free(void * arg)64380387638SSascha Wildner pspdf_free(void *arg)
64480387638SSascha Wildner {
64580387638SSascha Wildner 	struct termp	*p;
64680387638SSascha Wildner 
64780387638SSascha Wildner 	p = (struct termp *)arg;
64880387638SSascha Wildner 
649a4c7eb57SSascha Wildner 	free(p->ps->psmarg);
650a4c7eb57SSascha Wildner 	free(p->ps->pdfobjs);
65180387638SSascha Wildner 
652a4c7eb57SSascha Wildner 	free(p->ps);
65380387638SSascha Wildner 	term_free(p);
65480387638SSascha Wildner }
65580387638SSascha Wildner 
65680387638SSascha Wildner static void
ps_printf(struct termp * p,const char * fmt,...)65780387638SSascha Wildner ps_printf(struct termp *p, const char *fmt, ...)
65880387638SSascha Wildner {
65980387638SSascha Wildner 	va_list		 ap;
66080387638SSascha Wildner 	int		 pos, len;
66180387638SSascha Wildner 
66280387638SSascha Wildner 	va_start(ap, fmt);
66380387638SSascha Wildner 
66480387638SSascha Wildner 	/*
66580387638SSascha Wildner 	 * If we're running in regular mode, then pipe directly into
66680387638SSascha Wildner 	 * vprintf().  If we're processing margins, then push the data
66780387638SSascha Wildner 	 * into our growable margin buffer.
66880387638SSascha Wildner 	 */
66980387638SSascha Wildner 
670a4c7eb57SSascha Wildner 	if ( ! (PS_MARGINS & p->ps->flags)) {
67180387638SSascha Wildner 		len = vprintf(fmt, ap);
67280387638SSascha Wildner 		va_end(ap);
673070c62a6SFranco Fichtner 		p->ps->pdfbytes += len < 0 ? 0 : (size_t)len;
67480387638SSascha Wildner 		return;
67580387638SSascha Wildner 	}
67680387638SSascha Wildner 
67780387638SSascha Wildner 	/*
67880387638SSascha Wildner 	 * XXX: I assume that the in-margin print won't exceed
67980387638SSascha Wildner 	 * PS_BUFSLOP (128 bytes), which is reasonable but still an
68080387638SSascha Wildner 	 * assumption that will cause pukeage if it's not the case.
68180387638SSascha Wildner 	 */
68280387638SSascha Wildner 
68380387638SSascha Wildner 	ps_growbuf(p, PS_BUFSLOP);
68480387638SSascha Wildner 
685a4c7eb57SSascha Wildner 	pos = (int)p->ps->psmargcur;
68636342e81SSascha Wildner 	vsnprintf(&p->ps->psmarg[pos], PS_BUFSLOP, fmt, ap);
68780387638SSascha Wildner 
68880387638SSascha Wildner 	va_end(ap);
68980387638SSascha Wildner 
690a4c7eb57SSascha Wildner 	p->ps->psmargcur = strlen(p->ps->psmarg);
69180387638SSascha Wildner }
69280387638SSascha Wildner 
69380387638SSascha Wildner static void
ps_putchar(struct termp * p,char c)69480387638SSascha Wildner ps_putchar(struct termp *p, char c)
69580387638SSascha Wildner {
69680387638SSascha Wildner 	int		 pos;
69780387638SSascha Wildner 
69880387638SSascha Wildner 	/* See ps_printf(). */
69980387638SSascha Wildner 
700a4c7eb57SSascha Wildner 	if ( ! (PS_MARGINS & p->ps->flags)) {
70180387638SSascha Wildner 		putchar(c);
702a4c7eb57SSascha Wildner 		p->ps->pdfbytes++;
70380387638SSascha Wildner 		return;
70480387638SSascha Wildner 	}
70580387638SSascha Wildner 
70680387638SSascha Wildner 	ps_growbuf(p, 2);
70780387638SSascha Wildner 
708a4c7eb57SSascha Wildner 	pos = (int)p->ps->psmargcur++;
709a4c7eb57SSascha Wildner 	p->ps->psmarg[pos++] = c;
710a4c7eb57SSascha Wildner 	p->ps->psmarg[pos] = '\0';
71180387638SSascha Wildner }
71280387638SSascha Wildner 
71380387638SSascha Wildner static void
pdf_obj(struct termp * p,size_t obj)71480387638SSascha Wildner pdf_obj(struct termp *p, size_t obj)
71580387638SSascha Wildner {
71680387638SSascha Wildner 
71780387638SSascha Wildner 	assert(obj > 0);
71880387638SSascha Wildner 
719a4c7eb57SSascha Wildner 	if ((obj - 1) >= p->ps->pdfobjsz) {
720a4c7eb57SSascha Wildner 		p->ps->pdfobjsz = obj + 128;
721070c62a6SFranco Fichtner 		p->ps->pdfobjs = mandoc_reallocarray(p->ps->pdfobjs,
722070c62a6SFranco Fichtner 		    p->ps->pdfobjsz, sizeof(size_t));
72380387638SSascha Wildner 	}
72480387638SSascha Wildner 
725a4c7eb57SSascha Wildner 	p->ps->pdfobjs[(int)obj - 1] = p->ps->pdfbytes;
72680387638SSascha Wildner 	ps_printf(p, "%zu 0 obj\n", obj);
72780387638SSascha Wildner }
72880387638SSascha Wildner 
72980387638SSascha Wildner static void
ps_closepage(struct termp * p)73080387638SSascha Wildner ps_closepage(struct termp *p)
73180387638SSascha Wildner {
73280387638SSascha Wildner 	int		 i;
73380387638SSascha Wildner 	size_t		 len, base;
73480387638SSascha Wildner 
73580387638SSascha Wildner 	/*
73680387638SSascha Wildner 	 * Close out a page that we've already flushed to output.  In
73754ba9607SSascha Wildner 	 * PostScript, we simply note that the page must be shown.  In
73880387638SSascha Wildner 	 * PDF, we must now create the Length, Resource, and Page node
73980387638SSascha Wildner 	 * for the page contents.
74080387638SSascha Wildner 	 */
74180387638SSascha Wildner 
742a4c7eb57SSascha Wildner 	assert(p->ps->psmarg && p->ps->psmarg[0]);
743a4c7eb57SSascha Wildner 	ps_printf(p, "%s", p->ps->psmarg);
74480387638SSascha Wildner 
74580387638SSascha Wildner 	if (TERMTYPE_PS != p->type) {
746a4c7eb57SSascha Wildner 		len = p->ps->pdfbytes - p->ps->pdflastpg;
747a4c7eb57SSascha Wildner 		base = p->ps->pages * 4 + p->ps->pdfbody;
74880387638SSascha Wildner 
74980387638SSascha Wildner 		ps_printf(p, "endstream\nendobj\n");
75080387638SSascha Wildner 
75180387638SSascha Wildner 		/* Length of content. */
75280387638SSascha Wildner 		pdf_obj(p, base + 1);
75380387638SSascha Wildner 		ps_printf(p, "%zu\nendobj\n", len);
75480387638SSascha Wildner 
75580387638SSascha Wildner 		/* Resource for content. */
75680387638SSascha Wildner 		pdf_obj(p, base + 2);
75780387638SSascha Wildner 		ps_printf(p, "<<\n/ProcSet [/PDF /Text]\n");
75880387638SSascha Wildner 		ps_printf(p, "/Font <<\n");
75980387638SSascha Wildner 		for (i = 0; i < (int)TERMFONT__MAX; i++)
76080387638SSascha Wildner 			ps_printf(p, "/F%d %d 0 R\n", i, 3 + i);
76154ba9607SSascha Wildner 		ps_printf(p, ">>\n>>\nendobj\n");
76280387638SSascha Wildner 
76380387638SSascha Wildner 		/* Page node. */
76480387638SSascha Wildner 		pdf_obj(p, base + 3);
76580387638SSascha Wildner 		ps_printf(p, "<<\n");
76680387638SSascha Wildner 		ps_printf(p, "/Type /Page\n");
76780387638SSascha Wildner 		ps_printf(p, "/Parent 2 0 R\n");
76880387638SSascha Wildner 		ps_printf(p, "/Resources %zu 0 R\n", base + 2);
76980387638SSascha Wildner 		ps_printf(p, "/Contents %zu 0 R\n", base);
77080387638SSascha Wildner 		ps_printf(p, ">>\nendobj\n");
77180387638SSascha Wildner 	} else
77280387638SSascha Wildner 		ps_printf(p, "showpage\n");
77380387638SSascha Wildner 
774a4c7eb57SSascha Wildner 	p->ps->pages++;
775a4c7eb57SSascha Wildner 	p->ps->psrow = p->ps->top;
776a4c7eb57SSascha Wildner 	assert( ! (PS_NEWPAGE & p->ps->flags));
777a4c7eb57SSascha Wildner 	p->ps->flags |= PS_NEWPAGE;
77880387638SSascha Wildner }
77980387638SSascha Wildner 
78080387638SSascha Wildner static void
ps_end(struct termp * p)78180387638SSascha Wildner ps_end(struct termp *p)
78280387638SSascha Wildner {
78380387638SSascha Wildner 	size_t		 i, xref, base;
78480387638SSascha Wildner 
78554ba9607SSascha Wildner 	ps_plast(p);
78654ba9607SSascha Wildner 	ps_pclose(p);
78754ba9607SSascha Wildner 
78880387638SSascha Wildner 	/*
78980387638SSascha Wildner 	 * At the end of the file, do one last showpage.  This is the
79080387638SSascha Wildner 	 * same behaviour as groff(1) and works for multiple pages as
79180387638SSascha Wildner 	 * well as just one.
79280387638SSascha Wildner 	 */
79380387638SSascha Wildner 
794a4c7eb57SSascha Wildner 	if ( ! (PS_NEWPAGE & p->ps->flags)) {
795a4c7eb57SSascha Wildner 		assert(0 == p->ps->flags);
796a4c7eb57SSascha Wildner 		assert('\0' == p->ps->last);
79780387638SSascha Wildner 		ps_closepage(p);
79880387638SSascha Wildner 	}
79980387638SSascha Wildner 
80080387638SSascha Wildner 	if (TERMTYPE_PS == p->type) {
80180387638SSascha Wildner 		ps_printf(p, "%%%%Trailer\n");
802a4c7eb57SSascha Wildner 		ps_printf(p, "%%%%Pages: %zu\n", p->ps->pages);
80380387638SSascha Wildner 		ps_printf(p, "%%%%EOF\n");
80480387638SSascha Wildner 		return;
80580387638SSascha Wildner 	}
80680387638SSascha Wildner 
80780387638SSascha Wildner 	pdf_obj(p, 2);
80880387638SSascha Wildner 	ps_printf(p, "<<\n/Type /Pages\n");
80980387638SSascha Wildner 	ps_printf(p, "/MediaBox [0 0 %zu %zu]\n",
810a4c7eb57SSascha Wildner 			(size_t)AFM2PNT(p, p->ps->width),
811a4c7eb57SSascha Wildner 			(size_t)AFM2PNT(p, p->ps->height));
81280387638SSascha Wildner 
813a4c7eb57SSascha Wildner 	ps_printf(p, "/Count %zu\n", p->ps->pages);
81480387638SSascha Wildner 	ps_printf(p, "/Kids [");
81580387638SSascha Wildner 
816a4c7eb57SSascha Wildner 	for (i = 0; i < p->ps->pages; i++)
817070c62a6SFranco Fichtner 		ps_printf(p, " %zu 0 R", i * 4 + p->ps->pdfbody + 3);
81880387638SSascha Wildner 
819070c62a6SFranco Fichtner 	base = (p->ps->pages - 1) * 4 + p->ps->pdfbody + 4;
82080387638SSascha Wildner 
82180387638SSascha Wildner 	ps_printf(p, "]\n>>\nendobj\n");
82280387638SSascha Wildner 	pdf_obj(p, base);
82380387638SSascha Wildner 	ps_printf(p, "<<\n");
82480387638SSascha Wildner 	ps_printf(p, "/Type /Catalog\n");
82580387638SSascha Wildner 	ps_printf(p, "/Pages 2 0 R\n");
82654ba9607SSascha Wildner 	ps_printf(p, ">>\nendobj\n");
827a4c7eb57SSascha Wildner 	xref = p->ps->pdfbytes;
82880387638SSascha Wildner 	ps_printf(p, "xref\n");
82980387638SSascha Wildner 	ps_printf(p, "0 %zu\n", base + 1);
83080387638SSascha Wildner 	ps_printf(p, "0000000000 65535 f \n");
83180387638SSascha Wildner 
83280387638SSascha Wildner 	for (i = 0; i < base; i++)
83380387638SSascha Wildner 		ps_printf(p, "%.10zu 00000 n \n",
834a4c7eb57SSascha Wildner 		    p->ps->pdfobjs[(int)i]);
83580387638SSascha Wildner 
83680387638SSascha Wildner 	ps_printf(p, "trailer\n");
83780387638SSascha Wildner 	ps_printf(p, "<<\n");
83880387638SSascha Wildner 	ps_printf(p, "/Size %zu\n", base + 1);
83980387638SSascha Wildner 	ps_printf(p, "/Root %zu 0 R\n", base);
84080387638SSascha Wildner 	ps_printf(p, "/Info 1 0 R\n");
84180387638SSascha Wildner 	ps_printf(p, ">>\n");
84280387638SSascha Wildner 	ps_printf(p, "startxref\n");
84380387638SSascha Wildner 	ps_printf(p, "%zu\n", xref);
84480387638SSascha Wildner 	ps_printf(p, "%%%%EOF\n");
84580387638SSascha Wildner }
84680387638SSascha Wildner 
84780387638SSascha Wildner static void
ps_begin(struct termp * p)84880387638SSascha Wildner ps_begin(struct termp *p)
84980387638SSascha Wildner {
85054ba9607SSascha Wildner 	size_t		 width, height;
85180387638SSascha Wildner 	int		 i;
85280387638SSascha Wildner 
85380387638SSascha Wildner 	/*
85480387638SSascha Wildner 	 * Print margins into margin buffer.  Nothing gets output to the
85580387638SSascha Wildner 	 * screen yet, so we don't need to initialise the primary state.
85680387638SSascha Wildner 	 */
85780387638SSascha Wildner 
858a4c7eb57SSascha Wildner 	if (p->ps->psmarg) {
859a4c7eb57SSascha Wildner 		assert(p->ps->psmargsz);
860a4c7eb57SSascha Wildner 		p->ps->psmarg[0] = '\0';
86180387638SSascha Wildner 	}
86280387638SSascha Wildner 
863a4c7eb57SSascha Wildner 	/*p->ps->pdfbytes = 0;*/
864a4c7eb57SSascha Wildner 	p->ps->psmargcur = 0;
865a4c7eb57SSascha Wildner 	p->ps->flags = PS_MARGINS;
866a4c7eb57SSascha Wildner 	p->ps->pscol = p->ps->left;
867a4c7eb57SSascha Wildner 	p->ps->psrow = p->ps->header;
86854ba9607SSascha Wildner 	p->ps->lastrow = 0; /* impossible row */
86980387638SSascha Wildner 
87080387638SSascha Wildner 	ps_setfont(p, TERMFONT_NONE);
87180387638SSascha Wildner 
87280387638SSascha Wildner 	(*p->headf)(p, p->argf);
87380387638SSascha Wildner 	(*p->endline)(p);
87480387638SSascha Wildner 
875a4c7eb57SSascha Wildner 	p->ps->pscol = p->ps->left;
876a4c7eb57SSascha Wildner 	p->ps->psrow = p->ps->footer;
87780387638SSascha Wildner 
87880387638SSascha Wildner 	(*p->footf)(p, p->argf);
87980387638SSascha Wildner 	(*p->endline)(p);
88080387638SSascha Wildner 
881a4c7eb57SSascha Wildner 	p->ps->flags &= ~PS_MARGINS;
88280387638SSascha Wildner 
883a4c7eb57SSascha Wildner 	assert(0 == p->ps->flags);
884a4c7eb57SSascha Wildner 	assert(p->ps->psmarg);
885a4c7eb57SSascha Wildner 	assert('\0' != p->ps->psmarg[0]);
88680387638SSascha Wildner 
88780387638SSascha Wildner 	/*
88880387638SSascha Wildner 	 * Print header and initialise page state.  Following this,
88980387638SSascha Wildner 	 * stuff gets printed to the screen, so make sure we're sane.
89080387638SSascha Wildner 	 */
89180387638SSascha Wildner 
89280387638SSascha Wildner 	if (TERMTYPE_PS == p->type) {
89354ba9607SSascha Wildner 		width = AFM2PNT(p, p->ps->width);
89454ba9607SSascha Wildner 		height = AFM2PNT(p, p->ps->height);
89554ba9607SSascha Wildner 
89680387638SSascha Wildner 		ps_printf(p, "%%!PS-Adobe-3.0\n");
89780387638SSascha Wildner 		ps_printf(p, "%%%%DocumentData: Clean7Bit\n");
89880387638SSascha Wildner 		ps_printf(p, "%%%%Orientation: Portrait\n");
89980387638SSascha Wildner 		ps_printf(p, "%%%%Pages: (atend)\n");
90080387638SSascha Wildner 		ps_printf(p, "%%%%PageOrder: Ascend\n");
90154ba9607SSascha Wildner 		ps_printf(p, "%%%%DocumentMedia: man-%s %zu %zu 0 () ()\n",
90254ba9607SSascha Wildner 		    p->ps->medianame, width, height);
90380387638SSascha Wildner 		ps_printf(p, "%%%%DocumentNeededResources: font");
90480387638SSascha Wildner 
90580387638SSascha Wildner 		for (i = 0; i < (int)TERMFONT__MAX; i++)
90680387638SSascha Wildner 			ps_printf(p, " %s", fonts[i].name);
90780387638SSascha Wildner 
90854ba9607SSascha Wildner 		ps_printf(p, "\n%%%%DocumentSuppliedResources: "
90954ba9607SSascha Wildner 		    "procset MandocProcs 1.0 0\n");
91054ba9607SSascha Wildner 		ps_printf(p, "%%%%EndComments\n");
91154ba9607SSascha Wildner 		ps_printf(p, "%%%%BeginProlog\n");
91254ba9607SSascha Wildner 		ps_printf(p, "%%%%BeginResource: procset MandocProcs "
91354ba9607SSascha Wildner 		    "10170 10170\n");
91454ba9607SSascha Wildner 		/* The font size is effectively hard-coded for now. */
91554ba9607SSascha Wildner 		ps_printf(p, "/fs %zu def\n", p->ps->scale);
91654ba9607SSascha Wildner 		for (i = 0; i < (int)TERMFONT__MAX; i++)
91754ba9607SSascha Wildner 			ps_printf(p, "/f%d { /%s fs selectfont } def\n",
91854ba9607SSascha Wildner 			    i, fonts[i].name);
91954ba9607SSascha Wildner 		ps_printf(p, "/s { 3 1 roll moveto show } bind def\n");
92054ba9607SSascha Wildner 		ps_printf(p, "/c { exch currentpoint exch pop "
92154ba9607SSascha Wildner 		    "moveto show } bind def\n");
92254ba9607SSascha Wildner 		ps_printf(p, "%%%%EndResource\n");
92354ba9607SSascha Wildner 		ps_printf(p, "%%%%EndProlog\n");
92454ba9607SSascha Wildner 		ps_printf(p, "%%%%BeginSetup\n");
92554ba9607SSascha Wildner 		ps_printf(p, "%%%%BeginFeature: *PageSize %s\n",
92654ba9607SSascha Wildner 		    p->ps->medianame);
92754ba9607SSascha Wildner 		ps_printf(p, "<</PageSize [%zu %zu]>>setpagedevice\n",
92854ba9607SSascha Wildner 		    width, height);
92954ba9607SSascha Wildner 		ps_printf(p, "%%%%EndFeature\n");
93054ba9607SSascha Wildner 		ps_printf(p, "%%%%EndSetup\n");
93180387638SSascha Wildner 	} else {
93280387638SSascha Wildner 		ps_printf(p, "%%PDF-1.1\n");
93380387638SSascha Wildner 		pdf_obj(p, 1);
93480387638SSascha Wildner 		ps_printf(p, "<<\n");
93580387638SSascha Wildner 		ps_printf(p, ">>\n");
93680387638SSascha Wildner 		ps_printf(p, "endobj\n");
93780387638SSascha Wildner 
93880387638SSascha Wildner 		for (i = 0; i < (int)TERMFONT__MAX; i++) {
93980387638SSascha Wildner 			pdf_obj(p, (size_t)i + 3);
94080387638SSascha Wildner 			ps_printf(p, "<<\n");
94180387638SSascha Wildner 			ps_printf(p, "/Type /Font\n");
94280387638SSascha Wildner 			ps_printf(p, "/Subtype /Type1\n");
943070c62a6SFranco Fichtner 			ps_printf(p, "/Name /F%d\n", i);
94480387638SSascha Wildner 			ps_printf(p, "/BaseFont /%s\n", fonts[i].name);
94554ba9607SSascha Wildner 			ps_printf(p, ">>\nendobj\n");
94680387638SSascha Wildner 		}
94780387638SSascha Wildner 	}
94880387638SSascha Wildner 
949a4c7eb57SSascha Wildner 	p->ps->pdfbody = (size_t)TERMFONT__MAX + 3;
950a4c7eb57SSascha Wildner 	p->ps->pscol = p->ps->left;
951a4c7eb57SSascha Wildner 	p->ps->psrow = p->ps->top;
952a4c7eb57SSascha Wildner 	p->ps->flags |= PS_NEWPAGE;
95380387638SSascha Wildner 	ps_setfont(p, TERMFONT_NONE);
95480387638SSascha Wildner }
95580387638SSascha Wildner 
95680387638SSascha Wildner static void
ps_pletter(struct termp * p,int c)95780387638SSascha Wildner ps_pletter(struct termp *p, int c)
95880387638SSascha Wildner {
95980387638SSascha Wildner 	int		 f;
96080387638SSascha Wildner 
96180387638SSascha Wildner 	/*
96280387638SSascha Wildner 	 * If we haven't opened a page context, then output that we're
96380387638SSascha Wildner 	 * in a new page and make sure the font is correctly set.
96480387638SSascha Wildner 	 */
96580387638SSascha Wildner 
966a4c7eb57SSascha Wildner 	if (PS_NEWPAGE & p->ps->flags) {
96780387638SSascha Wildner 		if (TERMTYPE_PS == p->type) {
96880387638SSascha Wildner 			ps_printf(p, "%%%%Page: %zu %zu\n",
969070c62a6SFranco Fichtner 			    p->ps->pages + 1, p->ps->pages + 1);
97054ba9607SSascha Wildner 			ps_printf(p, "f%d\n", (int)p->ps->lastf);
97180387638SSascha Wildner 		} else {
972a4c7eb57SSascha Wildner 			pdf_obj(p, p->ps->pdfbody +
973a4c7eb57SSascha Wildner 			    p->ps->pages * 4);
97480387638SSascha Wildner 			ps_printf(p, "<<\n");
97580387638SSascha Wildner 			ps_printf(p, "/Length %zu 0 R\n",
976070c62a6SFranco Fichtner 			    p->ps->pdfbody + 1 + p->ps->pages * 4);
97780387638SSascha Wildner 			ps_printf(p, ">>\nstream\n");
97880387638SSascha Wildner 		}
979a4c7eb57SSascha Wildner 		p->ps->pdflastpg = p->ps->pdfbytes;
980a4c7eb57SSascha Wildner 		p->ps->flags &= ~PS_NEWPAGE;
98180387638SSascha Wildner 	}
98280387638SSascha Wildner 
98380387638SSascha Wildner 	/*
98480387638SSascha Wildner 	 * If we're not in a PostScript "word" context, then open one
98580387638SSascha Wildner 	 * now at the current cursor.
98680387638SSascha Wildner 	 */
98780387638SSascha Wildner 
988a4c7eb57SSascha Wildner 	if ( ! (PS_INLINE & p->ps->flags)) {
98980387638SSascha Wildner 		if (TERMTYPE_PS != p->type) {
99080387638SSascha Wildner 			ps_printf(p, "BT\n/F%d %zu Tf\n",
991070c62a6SFranco Fichtner 			    (int)p->ps->lastf, p->ps->scale);
99280387638SSascha Wildner 			ps_printf(p, "%.3f %.3f Td\n(",
993a4c7eb57SSascha Wildner 			    AFM2PNT(p, p->ps->pscol),
994a4c7eb57SSascha Wildner 			    AFM2PNT(p, p->ps->psrow));
99554ba9607SSascha Wildner 		} else {
99654ba9607SSascha Wildner 			ps_printf(p, "%.3f", AFM2PNT(p, p->ps->pscol));
99754ba9607SSascha Wildner 			if (p->ps->psrow != p->ps->lastrow)
99854ba9607SSascha Wildner 				ps_printf(p, " %.3f",
999a4c7eb57SSascha Wildner 				    AFM2PNT(p, p->ps->psrow));
100054ba9607SSascha Wildner 			ps_printf(p, "(");
100154ba9607SSascha Wildner 		}
1002a4c7eb57SSascha Wildner 		p->ps->flags |= PS_INLINE;
100380387638SSascha Wildner 	}
100480387638SSascha Wildner 
1005a4c7eb57SSascha Wildner 	assert( ! (PS_NEWPAGE & p->ps->flags));
100680387638SSascha Wildner 
100780387638SSascha Wildner 	/*
100880387638SSascha Wildner 	 * We need to escape these characters as per the PostScript
100980387638SSascha Wildner 	 * specification.  We would also escape non-graphable characters
101080387638SSascha Wildner 	 * (like tabs), but none of them would get to this point and
101180387638SSascha Wildner 	 * it's superfluous to abort() on them.
101280387638SSascha Wildner 	 */
101380387638SSascha Wildner 
101480387638SSascha Wildner 	switch (c) {
1015070c62a6SFranco Fichtner 	case '(':
1016070c62a6SFranco Fichtner 	case ')':
1017070c62a6SFranco Fichtner 	case '\\':
101880387638SSascha Wildner 		ps_putchar(p, '\\');
101980387638SSascha Wildner 		break;
102080387638SSascha Wildner 	default:
102180387638SSascha Wildner 		break;
102280387638SSascha Wildner 	}
102380387638SSascha Wildner 
102480387638SSascha Wildner 	/* Write the character and adjust where we are on the page. */
102580387638SSascha Wildner 
1026a4c7eb57SSascha Wildner 	f = (int)p->ps->lastf;
102780387638SSascha Wildner 
1028070c62a6SFranco Fichtner 	if (c <= 32 || c - 32 >= MAXCHAR)
1029070c62a6SFranco Fichtner 		c = 32;
103080387638SSascha Wildner 
103180387638SSascha Wildner 	ps_putchar(p, (char)c);
103280387638SSascha Wildner 	c -= 32;
1033a4c7eb57SSascha Wildner 	p->ps->pscol += (size_t)fonts[f].gly[c].wx;
103480387638SSascha Wildner }
103580387638SSascha Wildner 
103680387638SSascha Wildner static void
ps_pclose(struct termp * p)103780387638SSascha Wildner ps_pclose(struct termp *p)
103880387638SSascha Wildner {
103980387638SSascha Wildner 
104080387638SSascha Wildner 	/*
104180387638SSascha Wildner 	 * Spit out that we're exiting a word context (this is a
104280387638SSascha Wildner 	 * "partial close" because we don't check the last-char buffer
104380387638SSascha Wildner 	 * or anything).
104480387638SSascha Wildner 	 */
104580387638SSascha Wildner 
1046a4c7eb57SSascha Wildner 	if ( ! (PS_INLINE & p->ps->flags))
104780387638SSascha Wildner 		return;
104880387638SSascha Wildner 
104954ba9607SSascha Wildner 	if (TERMTYPE_PS != p->type)
105080387638SSascha Wildner 		ps_printf(p, ") Tj\nET\n");
105154ba9607SSascha Wildner 	else if (p->ps->psrow == p->ps->lastrow)
105254ba9607SSascha Wildner 		ps_printf(p, ")c\n");
105354ba9607SSascha Wildner 	else {
105454ba9607SSascha Wildner 		ps_printf(p, ")s\n");
105554ba9607SSascha Wildner 		p->ps->lastrow = p->ps->psrow;
105654ba9607SSascha Wildner 	}
105780387638SSascha Wildner 
1058a4c7eb57SSascha Wildner 	p->ps->flags &= ~PS_INLINE;
105980387638SSascha Wildner }
106080387638SSascha Wildner 
106154ba9607SSascha Wildner /* If we have a `last' char that wasn't printed yet, print it now. */
106280387638SSascha Wildner static void
ps_plast(struct termp * p)106354ba9607SSascha Wildner ps_plast(struct termp *p)
106480387638SSascha Wildner {
106554ba9607SSascha Wildner 	size_t	 wx;
106680387638SSascha Wildner 
106754ba9607SSascha Wildner 	if (p->ps->last == '\0')
106880387638SSascha Wildner 		return;
106980387638SSascha Wildner 
107054ba9607SSascha Wildner 	/* Check the font mode; open a new scope if it doesn't match. */
107154ba9607SSascha Wildner 
107254ba9607SSascha Wildner 	if (p->ps->nextf != p->ps->lastf) {
107380387638SSascha Wildner 		ps_pclose(p);
107454ba9607SSascha Wildner 		ps_setfont(p, p->ps->nextf);
107554ba9607SSascha Wildner 	}
107654ba9607SSascha Wildner 	p->ps->nextf = TERMFONT_NONE;
107754ba9607SSascha Wildner 
107854ba9607SSascha Wildner 	/*
107954ba9607SSascha Wildner 	 * For an overstrike, if a previous character
108054ba9607SSascha Wildner 	 * was wider, advance to center the new one.
108154ba9607SSascha Wildner 	 */
108254ba9607SSascha Wildner 
108354ba9607SSascha Wildner 	if (p->ps->pscolnext) {
108454ba9607SSascha Wildner 		wx = fonts[p->ps->lastf].gly[(int)p->ps->last-32].wx;
108554ba9607SSascha Wildner 		if (p->ps->pscol + wx < p->ps->pscolnext)
108654ba9607SSascha Wildner 			p->ps->pscol = (p->ps->pscol +
108754ba9607SSascha Wildner 			    p->ps->pscolnext - wx) / 2;
108854ba9607SSascha Wildner 	}
108954ba9607SSascha Wildner 
109054ba9607SSascha Wildner 	ps_pletter(p, p->ps->last);
109154ba9607SSascha Wildner 	p->ps->last = '\0';
109254ba9607SSascha Wildner 
109354ba9607SSascha Wildner 	/*
109454ba9607SSascha Wildner 	 * For an overstrike, if a previous character
109554ba9607SSascha Wildner 	 * was wider, advance to the end of the old one.
109654ba9607SSascha Wildner 	 */
109754ba9607SSascha Wildner 
109854ba9607SSascha Wildner 	if (p->ps->pscol < p->ps->pscolnext) {
109954ba9607SSascha Wildner 		ps_pclose(p);
110054ba9607SSascha Wildner 		p->ps->pscol = p->ps->pscolnext;
110154ba9607SSascha Wildner 	}
110280387638SSascha Wildner }
110380387638SSascha Wildner 
110480387638SSascha Wildner static void
ps_letter(struct termp * p,int arg)1105a4c7eb57SSascha Wildner ps_letter(struct termp *p, int arg)
110680387638SSascha Wildner {
110754ba9607SSascha Wildner 	size_t		savecol;
110854ba9607SSascha Wildner 	char		c;
1109a4c7eb57SSascha Wildner 
1110a4c7eb57SSascha Wildner 	c = arg >= 128 || arg <= 0 ? '?' : arg;
111180387638SSascha Wildner 
111280387638SSascha Wildner 	/*
111354ba9607SSascha Wildner 	 * When receiving a backspace, merely flag it.
111454ba9607SSascha Wildner 	 * We don't know yet whether it is
111554ba9607SSascha Wildner 	 * a font instruction or an overstrike.
111680387638SSascha Wildner 	 */
111780387638SSascha Wildner 
111854ba9607SSascha Wildner 	if (c == '\b') {
111954ba9607SSascha Wildner 		assert(p->ps->last != '\0');
112054ba9607SSascha Wildner 		assert( ! (p->ps->flags & PS_BACKSP));
112154ba9607SSascha Wildner 		p->ps->flags |= PS_BACKSP;
112280387638SSascha Wildner 		return;
112380387638SSascha Wildner 	}
112480387638SSascha Wildner 
112554ba9607SSascha Wildner 	/*
112654ba9607SSascha Wildner 	 * Decode font instructions.
112754ba9607SSascha Wildner 	 */
112854ba9607SSascha Wildner 
112954ba9607SSascha Wildner 	if (p->ps->flags & PS_BACKSP) {
113054ba9607SSascha Wildner 		if (p->ps->last == '_') {
113154ba9607SSascha Wildner 			switch (p->ps->nextf) {
113254ba9607SSascha Wildner 			case TERMFONT_BI:
113354ba9607SSascha Wildner 				break;
113454ba9607SSascha Wildner 			case TERMFONT_BOLD:
113554ba9607SSascha Wildner 				p->ps->nextf = TERMFONT_BI;
113654ba9607SSascha Wildner 				break;
113754ba9607SSascha Wildner 			default:
113854ba9607SSascha Wildner 				p->ps->nextf = TERMFONT_UNDER;
113954ba9607SSascha Wildner 			}
114054ba9607SSascha Wildner 			p->ps->last = c;
114154ba9607SSascha Wildner 			p->ps->flags &= ~PS_BACKSP;
114254ba9607SSascha Wildner 			return;
114354ba9607SSascha Wildner 		}
114454ba9607SSascha Wildner 		if (p->ps->last == c) {
114554ba9607SSascha Wildner 			switch (p->ps->nextf) {
114654ba9607SSascha Wildner 			case TERMFONT_BI:
114754ba9607SSascha Wildner 				break;
114854ba9607SSascha Wildner 			case TERMFONT_UNDER:
114954ba9607SSascha Wildner 				p->ps->nextf = TERMFONT_BI;
115054ba9607SSascha Wildner 				break;
115154ba9607SSascha Wildner 			default:
115254ba9607SSascha Wildner 				p->ps->nextf = TERMFONT_BOLD;
115354ba9607SSascha Wildner 			}
115454ba9607SSascha Wildner 			p->ps->flags &= ~PS_BACKSP;
115554ba9607SSascha Wildner 			return;
115654ba9607SSascha Wildner 		}
115754ba9607SSascha Wildner 
115854ba9607SSascha Wildner 		/*
115954ba9607SSascha Wildner 		 * This is not a font instruction, but rather
116054ba9607SSascha Wildner 		 * the next character.  Prepare for overstrike.
116154ba9607SSascha Wildner 		 */
116254ba9607SSascha Wildner 
116354ba9607SSascha Wildner 		savecol = p->ps->pscol;
116454ba9607SSascha Wildner 	} else
116554ba9607SSascha Wildner 		savecol = SIZE_MAX;
116654ba9607SSascha Wildner 
116754ba9607SSascha Wildner 	/*
116854ba9607SSascha Wildner 	 * We found the next character, so the font instructions
116954ba9607SSascha Wildner 	 * for the previous one are complete.
117054ba9607SSascha Wildner 	 * Use them and print it.
117154ba9607SSascha Wildner 	 */
117254ba9607SSascha Wildner 
117354ba9607SSascha Wildner 	ps_plast(p);
117454ba9607SSascha Wildner 
117554ba9607SSascha Wildner 	/*
117654ba9607SSascha Wildner 	 * Do not print the current character yet because font
117754ba9607SSascha Wildner 	 * instructions might follow; only remember the character.
117854ba9607SSascha Wildner 	 * It will get printed later from ps_plast().
117954ba9607SSascha Wildner 	 */
118054ba9607SSascha Wildner 
118154ba9607SSascha Wildner 	p->ps->last = c;
118254ba9607SSascha Wildner 
118354ba9607SSascha Wildner 	/*
118454ba9607SSascha Wildner 	 * For an overstrike, back up to the previous position.
118554ba9607SSascha Wildner 	 * If the previous character is wider than any it overstrikes,
118654ba9607SSascha Wildner 	 * remember the current position, because it might also be
118754ba9607SSascha Wildner 	 * wider than all that will overstrike it.
118854ba9607SSascha Wildner 	 */
118954ba9607SSascha Wildner 
119054ba9607SSascha Wildner 	if (savecol != SIZE_MAX) {
119154ba9607SSascha Wildner 		if (p->ps->pscolnext < p->ps->pscol)
119254ba9607SSascha Wildner 			p->ps->pscolnext = p->ps->pscol;
119354ba9607SSascha Wildner 		ps_pclose(p);
119454ba9607SSascha Wildner 		p->ps->pscol = savecol;
119554ba9607SSascha Wildner 		p->ps->flags &= ~PS_BACKSP;
119654ba9607SSascha Wildner 	} else
119754ba9607SSascha Wildner 		p->ps->pscolnext = 0;
119880387638SSascha Wildner }
119980387638SSascha Wildner 
120080387638SSascha Wildner static void
ps_advance(struct termp * p,size_t len)120180387638SSascha Wildner ps_advance(struct termp *p, size_t len)
120280387638SSascha Wildner {
120380387638SSascha Wildner 
120480387638SSascha Wildner 	/*
120580387638SSascha Wildner 	 * Advance some spaces.  This can probably be made smarter,
120680387638SSascha Wildner 	 * i.e., to have multiple space-separated words in the same
120780387638SSascha Wildner 	 * scope, but this is easier:  just close out the current scope
120880387638SSascha Wildner 	 * and readjust our column settings.
120980387638SSascha Wildner 	 */
121080387638SSascha Wildner 
121154ba9607SSascha Wildner 	ps_plast(p);
121254ba9607SSascha Wildner 	ps_pclose(p);
1213a4c7eb57SSascha Wildner 	p->ps->pscol += len;
121480387638SSascha Wildner }
121580387638SSascha Wildner 
121680387638SSascha Wildner static void
ps_endline(struct termp * p)121780387638SSascha Wildner ps_endline(struct termp *p)
121880387638SSascha Wildner {
121980387638SSascha Wildner 
122080387638SSascha Wildner 	/* Close out any scopes we have open: we're at eoln. */
122180387638SSascha Wildner 
122254ba9607SSascha Wildner 	ps_plast(p);
122354ba9607SSascha Wildner 	ps_pclose(p);
122480387638SSascha Wildner 
122580387638SSascha Wildner 	/*
122680387638SSascha Wildner 	 * If we're in the margin, don't try to recalculate our current
122780387638SSascha Wildner 	 * row.  XXX: if the column tries to be fancy with multiple
122880387638SSascha Wildner 	 * lines, we'll do nasty stuff.
122980387638SSascha Wildner 	 */
123080387638SSascha Wildner 
1231a4c7eb57SSascha Wildner 	if (PS_MARGINS & p->ps->flags)
123280387638SSascha Wildner 		return;
123380387638SSascha Wildner 
123480387638SSascha Wildner 	/* Left-justify. */
123580387638SSascha Wildner 
1236a4c7eb57SSascha Wildner 	p->ps->pscol = p->ps->left;
123780387638SSascha Wildner 
123880387638SSascha Wildner 	/* If we haven't printed anything, return. */
123980387638SSascha Wildner 
1240a4c7eb57SSascha Wildner 	if (PS_NEWPAGE & p->ps->flags)
124180387638SSascha Wildner 		return;
124280387638SSascha Wildner 
124380387638SSascha Wildner 	/*
124480387638SSascha Wildner 	 * Put us down a line.  If we're at the page bottom, spit out a
124580387638SSascha Wildner 	 * showpage and restart our row.
124680387638SSascha Wildner 	 */
124780387638SSascha Wildner 
1248070c62a6SFranco Fichtner 	if (p->ps->psrow >= p->ps->lineheight + p->ps->bottom) {
1249a4c7eb57SSascha Wildner 		p->ps->psrow -= p->ps->lineheight;
125080387638SSascha Wildner 		return;
125180387638SSascha Wildner 	}
125280387638SSascha Wildner 
125380387638SSascha Wildner 	ps_closepage(p);
125454ba9607SSascha Wildner 
1255*99db7d0eSSascha Wildner 	if ((int)p->tcol->offset > p->ti)
125654ba9607SSascha Wildner 		p->tcol->offset -= p->ti;
1257*99db7d0eSSascha Wildner 	else
1258*99db7d0eSSascha Wildner 		p->tcol->offset = 0;
125954ba9607SSascha Wildner 	p->ti = 0;
126080387638SSascha Wildner }
126180387638SSascha Wildner 
126280387638SSascha Wildner static void
ps_setfont(struct termp * p,enum termfont f)126380387638SSascha Wildner ps_setfont(struct termp *p, enum termfont f)
126480387638SSascha Wildner {
126580387638SSascha Wildner 
126680387638SSascha Wildner 	assert(f < TERMFONT__MAX);
1267a4c7eb57SSascha Wildner 	p->ps->lastf = f;
126880387638SSascha Wildner 
126980387638SSascha Wildner 	/*
127080387638SSascha Wildner 	 * If we're still at the top of the page, let the font-setting
127180387638SSascha Wildner 	 * be delayed until we actually have stuff to print.
127280387638SSascha Wildner 	 */
127380387638SSascha Wildner 
1274a4c7eb57SSascha Wildner 	if (PS_NEWPAGE & p->ps->flags)
127580387638SSascha Wildner 		return;
127680387638SSascha Wildner 
127780387638SSascha Wildner 	if (TERMTYPE_PS == p->type)
127854ba9607SSascha Wildner 		ps_printf(p, "f%d\n", (int)f);
127980387638SSascha Wildner 	else
128080387638SSascha Wildner 		ps_printf(p, "/F%d %zu Tf\n",
1281070c62a6SFranco Fichtner 		    (int)f, p->ps->scale);
128280387638SSascha Wildner }
128380387638SSascha Wildner 
128480387638SSascha Wildner static size_t
ps_width(const struct termp * p,int c)1285a4c7eb57SSascha Wildner ps_width(const struct termp *p, int c)
128680387638SSascha Wildner {
128780387638SSascha Wildner 
128880387638SSascha Wildner 	if (c <= 32 || c - 32 >= MAXCHAR)
1289070c62a6SFranco Fichtner 		c = 0;
1290070c62a6SFranco Fichtner 	else
129180387638SSascha Wildner 		c -= 32;
1292070c62a6SFranco Fichtner 
129354ba9607SSascha Wildner 	return (size_t)fonts[(int)TERMFONT_NONE].gly[c].wx;
129480387638SSascha Wildner }
129580387638SSascha Wildner 
129654ba9607SSascha Wildner static int
ps_hspan(const struct termp * p,const struct roffsu * su)129780387638SSascha Wildner ps_hspan(const struct termp *p, const struct roffsu *su)
129880387638SSascha Wildner {
129980387638SSascha Wildner 	double		 r;
130080387638SSascha Wildner 
130180387638SSascha Wildner 	/*
130280387638SSascha Wildner 	 * All of these measurements are derived by converting from the
130380387638SSascha Wildner 	 * native measurement to AFM units.
130480387638SSascha Wildner 	 */
130580387638SSascha Wildner 	switch (su->unit) {
130654ba9607SSascha Wildner 	case SCALE_BU:
130754ba9607SSascha Wildner 		/*
130854ba9607SSascha Wildner 		 * Traditionally, the default unit is fixed to the
130954ba9607SSascha Wildner 		 * output media.  So this would refer to the point.  In
131054ba9607SSascha Wildner 		 * mandoc(1), however, we stick to the default terminal
131154ba9607SSascha Wildner 		 * scaling unit so that output is the same regardless
131254ba9607SSascha Wildner 		 * the media.
131354ba9607SSascha Wildner 		 */
131454ba9607SSascha Wildner 		r = PNT2AFM(p, su->scale * 72.0 / 240.0);
131554ba9607SSascha Wildner 		break;
1316070c62a6SFranco Fichtner 	case SCALE_CM:
131754ba9607SSascha Wildner 		r = PNT2AFM(p, su->scale * 72.0 / 2.54);
131880387638SSascha Wildner 		break;
1319070c62a6SFranco Fichtner 	case SCALE_EM:
132080387638SSascha Wildner 		r = su->scale *
132180387638SSascha Wildner 		    fonts[(int)TERMFONT_NONE].gly[109 - 32].wx;
132280387638SSascha Wildner 		break;
1323070c62a6SFranco Fichtner 	case SCALE_EN:
132480387638SSascha Wildner 		r = su->scale *
132580387638SSascha Wildner 		    fonts[(int)TERMFONT_NONE].gly[110 - 32].wx;
132680387638SSascha Wildner 		break;
132754ba9607SSascha Wildner 	case SCALE_IN:
132854ba9607SSascha Wildner 		r = PNT2AFM(p, su->scale * 72.0);
132954ba9607SSascha Wildner 		break;
133054ba9607SSascha Wildner 	case SCALE_MM:
133154ba9607SSascha Wildner 		r = su->scale *
133254ba9607SSascha Wildner 		    fonts[(int)TERMFONT_NONE].gly[109 - 32].wx / 100.0;
133354ba9607SSascha Wildner 		break;
133454ba9607SSascha Wildner 	case SCALE_PC:
133554ba9607SSascha Wildner 		r = PNT2AFM(p, su->scale * 12.0);
133654ba9607SSascha Wildner 		break;
133754ba9607SSascha Wildner 	case SCALE_PT:
133854ba9607SSascha Wildner 		r = PNT2AFM(p, su->scale * 1.0);
133954ba9607SSascha Wildner 		break;
1340070c62a6SFranco Fichtner 	case SCALE_VS:
1341a4c7eb57SSascha Wildner 		r = su->scale * p->ps->lineheight;
134280387638SSascha Wildner 		break;
134380387638SSascha Wildner 	default:
134480387638SSascha Wildner 		r = su->scale;
134580387638SSascha Wildner 		break;
134680387638SSascha Wildner 	}
134780387638SSascha Wildner 
134854ba9607SSascha Wildner 	return r * 24.0;
134980387638SSascha Wildner }
135080387638SSascha Wildner 
1351a4c7eb57SSascha Wildner static void
ps_growbuf(struct termp * p,size_t sz)1352a4c7eb57SSascha Wildner ps_growbuf(struct termp *p, size_t sz)
1353a4c7eb57SSascha Wildner {
1354a4c7eb57SSascha Wildner 	if (p->ps->psmargcur + sz <= p->ps->psmargsz)
1355a4c7eb57SSascha Wildner 		return;
1356a4c7eb57SSascha Wildner 
1357a4c7eb57SSascha Wildner 	if (sz < PS_BUFSLOP)
1358a4c7eb57SSascha Wildner 		sz = PS_BUFSLOP;
1359a4c7eb57SSascha Wildner 
1360a4c7eb57SSascha Wildner 	p->ps->psmargsz += sz;
1361070c62a6SFranco Fichtner 	p->ps->psmarg = mandoc_realloc(p->ps->psmarg, p->ps->psmargsz);
1362a4c7eb57SSascha Wildner }
1363