xref: /openbsd-src/usr.bin/mandoc/term_ps.c (revision 30c2fcb2c4ce3e21f6a50e35322d8b2befee8dfa)
1*30c2fcb2Sschwarze /* $OpenBSD: term_ps.c,v 1.56 2020/09/06 14:44:19 schwarze Exp $ */
2f95d589eSschwarze /*
3a5e11edeSschwarze  * Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4*30c2fcb2Sschwarze  * Copyright (c) 2014,2015,2016,2017,2020 Ingo Schwarze <schwarze@openbsd.org>
5e66ff9cbSespie  * Copyright (c) 2017 Marc Espie <espie@openbsd.org>
6f95d589eSschwarze  *
7f95d589eSschwarze  * Permission to use, copy, modify, and distribute this software for any
8f95d589eSschwarze  * purpose with or without fee is hereby granted, provided that the above
9f95d589eSschwarze  * copyright notice and this permission notice appear in all copies.
10f95d589eSschwarze  *
112ccd0917Sschwarze  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
12f95d589eSschwarze  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
132ccd0917Sschwarze  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
14f95d589eSschwarze  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15f95d589eSschwarze  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16f95d589eSschwarze  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17f95d589eSschwarze  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18f95d589eSschwarze  */
1955e8b2edSderaadt #include <sys/types.h>
20f95d589eSschwarze 
21f95d589eSschwarze #include <assert.h>
22eba1598bSschwarze #include <err.h>
23f95d589eSschwarze #include <stdarg.h>
243e2de047Sschwarze #include <stdint.h>
25f95d589eSschwarze #include <stdio.h>
26f95d589eSschwarze #include <stdlib.h>
27f95d589eSschwarze #include <string.h>
28769ee804Sschwarze #include <unistd.h>
29f95d589eSschwarze 
304f4f7972Sschwarze #include "mandoc_aux.h"
31f95d589eSschwarze #include "out.h"
32f95d589eSschwarze #include "term.h"
332ccd0917Sschwarze #include "manconf.h"
34dd617d76Sschwarze #include "main.h"
35f95d589eSschwarze 
36a5e11edeSschwarze /* These work the buffer used by the header and footer. */
37a5e11edeSschwarze #define	PS_BUFSLOP	  128
38a5e11edeSschwarze 
39769ee804Sschwarze /* Convert PostScript point "x" to an AFM unit. */
4049aff9f8Sschwarze #define	PNT2AFM(p, x) \
41a5e11edeSschwarze 	(size_t)((double)(x) * (1000.0 / (double)(p)->ps->scale))
42769ee804Sschwarze 
43769ee804Sschwarze /* Convert an AFM unit "x" to a PostScript points */
4449aff9f8Sschwarze #define	AFM2PNT(p, x) \
45a5e11edeSschwarze 	((double)(x) / (1000.0 / (double)(p)->ps->scale))
46769ee804Sschwarze 
4706f7b709Sschwarze struct	glyph {
48ddce0b0cSschwarze 	unsigned short	  wx; /* WX in AFM */
4906f7b709Sschwarze };
50f95d589eSschwarze 
5106f7b709Sschwarze struct	font {
5206f7b709Sschwarze 	const char	 *name; /* FontName in AFM */
5306f7b709Sschwarze #define	MAXCHAR		  95 /* total characters we can handle */
5406f7b709Sschwarze 	struct glyph	  gly[MAXCHAR]; /* glyph metrics */
5506f7b709Sschwarze };
5606f7b709Sschwarze 
57a5e11edeSschwarze struct	termp_ps {
58a5e11edeSschwarze 	int		  flags;
59a5e11edeSschwarze #define	PS_INLINE	 (1 << 0)	/* we're in a word */
60a5e11edeSschwarze #define	PS_MARGINS	 (1 << 1)	/* we're in the margins */
61a5e11edeSschwarze #define	PS_NEWPAGE	 (1 << 2)	/* new page, no words yet */
62148007bcSschwarze #define	PS_BACKSP	 (1 << 3)	/* last character was backspace */
63a5e11edeSschwarze 	size_t		  pscol;	/* visible column (AFM units) */
642dfa6404Sschwarze 	size_t		  pscolnext;	/* used for overstrike */
65a5e11edeSschwarze 	size_t		  psrow;	/* visible row (AFM units) */
66e66ff9cbSespie 	size_t		  lastrow;	/* psrow of the previous word */
67a5e11edeSschwarze 	char		 *psmarg;	/* margin buf */
68a5e11edeSschwarze 	size_t		  psmargsz;	/* margin buf size */
69a5e11edeSschwarze 	size_t		  psmargcur;	/* cur index in margin buf */
70148007bcSschwarze 	char		  last;		/* last non-backspace seen */
71a5e11edeSschwarze 	enum termfont	  lastf;	/* last set font */
72e1e5254aSschwarze 	enum termfont	  nextf;	/* building next font here */
73a5e11edeSschwarze 	size_t		  scale;	/* font scaling factor */
74a5e11edeSschwarze 	size_t		  pages;	/* number of pages shown */
75a5e11edeSschwarze 	size_t		  lineheight;	/* line height (AFM units) */
76a5e11edeSschwarze 	size_t		  top;		/* body top (AFM units) */
77a5e11edeSschwarze 	size_t		  bottom;	/* body bottom (AFM units) */
785cab5f2bSschwarze 	const char	 *medianame;	/* for DocumentMedia and PageSize */
79a5e11edeSschwarze 	size_t		  height;	/* page height (AFM units */
80a5e11edeSschwarze 	size_t		  width;	/* page width (AFM units) */
815281506aSschwarze 	size_t		  lastwidth;	/* page width before last ll */
82a5e11edeSschwarze 	size_t		  left;		/* body left (AFM units) */
83a5e11edeSschwarze 	size_t		  header;	/* header pos (AFM units) */
84a5e11edeSschwarze 	size_t		  footer;	/* footer pos (AFM units) */
85a5e11edeSschwarze 	size_t		  pdfbytes;	/* current output byte */
86a5e11edeSschwarze 	size_t		  pdflastpg;	/* byte of last page mark */
87a5e11edeSschwarze 	size_t		  pdfbody;	/* start of body object */
88a5e11edeSschwarze 	size_t		 *pdfobjs;	/* table of object offsets */
89a5e11edeSschwarze 	size_t		  pdfobjsz;	/* size of pdfobjs */
90a5e11edeSschwarze };
91a5e11edeSschwarze 
9213a35416Sschwarze static	int		  ps_hspan(const struct termp *,
93a5e11edeSschwarze 				const struct roffsu *);
94a5e11edeSschwarze static	size_t		  ps_width(const struct termp *, int);
95a5e11edeSschwarze static	void		  ps_advance(struct termp *, size_t);
96a5e11edeSschwarze static	void		  ps_begin(struct termp *);
97a5e11edeSschwarze static	void		  ps_closepage(struct termp *);
98a5e11edeSschwarze static	void		  ps_end(struct termp *);
99a5e11edeSschwarze static	void		  ps_endline(struct termp *);
100a5e11edeSschwarze static	void		  ps_growbuf(struct termp *, size_t);
101a5e11edeSschwarze static	void		  ps_letter(struct termp *, int);
102a5e11edeSschwarze static	void		  ps_pclose(struct termp *);
1036470d541Sschwarze static	void		  ps_plast(struct termp *);
104a5e11edeSschwarze static	void		  ps_pletter(struct termp *, int);
10557c6a104Sschwarze static	void		  ps_printf(struct termp *, const char *, ...)
106e6187497Sschwarze 				__attribute__((__format__ (__printf__, 2, 3)));
107a5e11edeSschwarze static	void		  ps_putchar(struct termp *, char);
108a5e11edeSschwarze static	void		  ps_setfont(struct termp *, enum termfont);
10913a35416Sschwarze static	void		  ps_setwidth(struct termp *, int, int);
11020457567Sespie static	struct termp	 *pspdf_alloc(const struct manoutput *, enum termtype);
111a5e11edeSschwarze static	void		  pdf_obj(struct termp *, size_t);
112a5e11edeSschwarze 
11306f7b709Sschwarze /*
11406f7b709Sschwarze  * We define, for the time being, three fonts: bold, oblique/italic, and
11506f7b709Sschwarze  * normal (roman).  The following table hard-codes the font metrics for
11606f7b709Sschwarze  * ASCII, i.e., 32--127.
11706f7b709Sschwarze  */
11806f7b709Sschwarze 
11906f7b709Sschwarze static	const struct font fonts[TERMFONT__MAX] = {
120769ee804Sschwarze 	{ "Times-Roman", {
121769ee804Sschwarze 		{ 250 },
122769ee804Sschwarze 		{ 333 },
123769ee804Sschwarze 		{ 408 },
124769ee804Sschwarze 		{ 500 },
125769ee804Sschwarze 		{ 500 },
126769ee804Sschwarze 		{ 833 },
127769ee804Sschwarze 		{ 778 },
128769ee804Sschwarze 		{ 333 },
129769ee804Sschwarze 		{ 333 },
130769ee804Sschwarze 		{ 333 },
131769ee804Sschwarze 		{ 500 },
132769ee804Sschwarze 		{ 564 },
133769ee804Sschwarze 		{ 250 },
134769ee804Sschwarze 		{ 333 },
135769ee804Sschwarze 		{ 250 },
136769ee804Sschwarze 		{ 278 },
137769ee804Sschwarze 		{ 500 },
138769ee804Sschwarze 		{ 500 },
139769ee804Sschwarze 		{ 500 },
140769ee804Sschwarze 		{ 500 },
141769ee804Sschwarze 		{ 500 },
142769ee804Sschwarze 		{ 500 },
143769ee804Sschwarze 		{ 500 },
144769ee804Sschwarze 		{ 500 },
145769ee804Sschwarze 		{ 500 },
146769ee804Sschwarze 		{ 500 },
147769ee804Sschwarze 		{ 278 },
148769ee804Sschwarze 		{ 278 },
149769ee804Sschwarze 		{ 564 },
150769ee804Sschwarze 		{ 564 },
151769ee804Sschwarze 		{ 564 },
152769ee804Sschwarze 		{ 444 },
153769ee804Sschwarze 		{ 921 },
154769ee804Sschwarze 		{ 722 },
155769ee804Sschwarze 		{ 667 },
156769ee804Sschwarze 		{ 667 },
157769ee804Sschwarze 		{ 722 },
158769ee804Sschwarze 		{ 611 },
159769ee804Sschwarze 		{ 556 },
160769ee804Sschwarze 		{ 722 },
161769ee804Sschwarze 		{ 722 },
162769ee804Sschwarze 		{ 333 },
163769ee804Sschwarze 		{ 389 },
164769ee804Sschwarze 		{ 722 },
165769ee804Sschwarze 		{ 611 },
166769ee804Sschwarze 		{ 889 },
167769ee804Sschwarze 		{ 722 },
168769ee804Sschwarze 		{ 722 },
169769ee804Sschwarze 		{ 556 },
170769ee804Sschwarze 		{ 722 },
171769ee804Sschwarze 		{ 667 },
172769ee804Sschwarze 		{ 556 },
173769ee804Sschwarze 		{ 611 },
174769ee804Sschwarze 		{ 722 },
175769ee804Sschwarze 		{ 722 },
176769ee804Sschwarze 		{ 944 },
177769ee804Sschwarze 		{ 722 },
178769ee804Sschwarze 		{ 722 },
179769ee804Sschwarze 		{ 611 },
180769ee804Sschwarze 		{ 333 },
181769ee804Sschwarze 		{ 278 },
182769ee804Sschwarze 		{ 333 },
183769ee804Sschwarze 		{ 469 },
184769ee804Sschwarze 		{ 500 },
185769ee804Sschwarze 		{ 333 },
186769ee804Sschwarze 		{ 444 },
187769ee804Sschwarze 		{ 500 },
188769ee804Sschwarze 		{ 444 },
189769ee804Sschwarze 		{  500},
190769ee804Sschwarze 		{  444},
191769ee804Sschwarze 		{  333},
192769ee804Sschwarze 		{  500},
193769ee804Sschwarze 		{  500},
194769ee804Sschwarze 		{  278},
195769ee804Sschwarze 		{  278},
196769ee804Sschwarze 		{  500},
197769ee804Sschwarze 		{  278},
198769ee804Sschwarze 		{  778},
199769ee804Sschwarze 		{  500},
200769ee804Sschwarze 		{  500},
201769ee804Sschwarze 		{  500},
202769ee804Sschwarze 		{  500},
203769ee804Sschwarze 		{  333},
204769ee804Sschwarze 		{  389},
205769ee804Sschwarze 		{  278},
206769ee804Sschwarze 		{  500},
207769ee804Sschwarze 		{  500},
208769ee804Sschwarze 		{  722},
209769ee804Sschwarze 		{  500},
210769ee804Sschwarze 		{  500},
211769ee804Sschwarze 		{  444},
212769ee804Sschwarze 		{  480},
213769ee804Sschwarze 		{  200},
214769ee804Sschwarze 		{  480},
215769ee804Sschwarze 		{  541},
21606f7b709Sschwarze 	} },
217769ee804Sschwarze 	{ "Times-Bold", {
218769ee804Sschwarze 		{ 250  },
219769ee804Sschwarze 		{ 333  },
220769ee804Sschwarze 		{ 555  },
221769ee804Sschwarze 		{ 500  },
222769ee804Sschwarze 		{ 500  },
223769ee804Sschwarze 		{ 1000 },
224769ee804Sschwarze 		{ 833  },
225769ee804Sschwarze 		{ 333  },
226769ee804Sschwarze 		{ 333  },
227769ee804Sschwarze 		{ 333  },
228769ee804Sschwarze 		{ 500  },
229769ee804Sschwarze 		{ 570  },
230769ee804Sschwarze 		{ 250  },
231769ee804Sschwarze 		{ 333  },
232769ee804Sschwarze 		{ 250  },
233769ee804Sschwarze 		{ 278  },
234769ee804Sschwarze 		{ 500  },
235769ee804Sschwarze 		{ 500  },
236769ee804Sschwarze 		{ 500  },
237769ee804Sschwarze 		{ 500  },
238769ee804Sschwarze 		{ 500  },
239769ee804Sschwarze 		{ 500  },
240769ee804Sschwarze 		{ 500  },
241769ee804Sschwarze 		{ 500  },
242769ee804Sschwarze 		{ 500  },
243769ee804Sschwarze 		{ 500  },
244769ee804Sschwarze 		{ 333  },
245769ee804Sschwarze 		{ 333  },
246769ee804Sschwarze 		{ 570  },
247769ee804Sschwarze 		{ 570  },
248769ee804Sschwarze 		{ 570  },
249769ee804Sschwarze 		{ 500  },
250769ee804Sschwarze 		{ 930  },
251769ee804Sschwarze 		{ 722  },
252769ee804Sschwarze 		{ 667  },
253769ee804Sschwarze 		{ 722  },
254769ee804Sschwarze 		{ 722  },
255769ee804Sschwarze 		{ 667  },
256769ee804Sschwarze 		{ 611  },
257769ee804Sschwarze 		{ 778  },
258769ee804Sschwarze 		{ 778  },
259769ee804Sschwarze 		{ 389  },
260769ee804Sschwarze 		{ 500  },
261769ee804Sschwarze 		{ 778  },
262769ee804Sschwarze 		{ 667  },
263769ee804Sschwarze 		{ 944  },
264769ee804Sschwarze 		{ 722  },
265769ee804Sschwarze 		{ 778  },
266769ee804Sschwarze 		{ 611  },
267769ee804Sschwarze 		{ 778  },
268769ee804Sschwarze 		{ 722  },
269769ee804Sschwarze 		{ 556  },
270769ee804Sschwarze 		{ 667  },
271769ee804Sschwarze 		{ 722  },
272769ee804Sschwarze 		{ 722  },
273769ee804Sschwarze 		{ 1000 },
274769ee804Sschwarze 		{ 722  },
275769ee804Sschwarze 		{ 722  },
276769ee804Sschwarze 		{ 667  },
277769ee804Sschwarze 		{ 333  },
278769ee804Sschwarze 		{ 278  },
279769ee804Sschwarze 		{ 333  },
280769ee804Sschwarze 		{ 581  },
281769ee804Sschwarze 		{ 500  },
282769ee804Sschwarze 		{ 333  },
283769ee804Sschwarze 		{ 500  },
284769ee804Sschwarze 		{ 556  },
285769ee804Sschwarze 		{ 444  },
286769ee804Sschwarze 		{  556 },
287769ee804Sschwarze 		{  444 },
288769ee804Sschwarze 		{  333 },
289769ee804Sschwarze 		{  500 },
290769ee804Sschwarze 		{  556 },
291769ee804Sschwarze 		{  278 },
292769ee804Sschwarze 		{  333 },
293769ee804Sschwarze 		{  556 },
294769ee804Sschwarze 		{  278 },
295769ee804Sschwarze 		{  833 },
296769ee804Sschwarze 		{  556 },
297769ee804Sschwarze 		{  500 },
298769ee804Sschwarze 		{  556 },
299769ee804Sschwarze 		{  556 },
300769ee804Sschwarze 		{  444 },
301769ee804Sschwarze 		{  389 },
302769ee804Sschwarze 		{  333 },
303769ee804Sschwarze 		{  556 },
304769ee804Sschwarze 		{  500 },
305769ee804Sschwarze 		{  722 },
306769ee804Sschwarze 		{  500 },
307769ee804Sschwarze 		{  500 },
308769ee804Sschwarze 		{  444 },
309769ee804Sschwarze 		{  394 },
310769ee804Sschwarze 		{  220 },
311769ee804Sschwarze 		{  394 },
312769ee804Sschwarze 		{  520 },
31306f7b709Sschwarze 	} },
314769ee804Sschwarze 	{ "Times-Italic", {
315769ee804Sschwarze 		{ 250  },
316769ee804Sschwarze 		{ 333  },
317769ee804Sschwarze 		{ 420  },
318769ee804Sschwarze 		{ 500  },
319769ee804Sschwarze 		{ 500  },
320769ee804Sschwarze 		{ 833  },
321769ee804Sschwarze 		{ 778  },
322769ee804Sschwarze 		{ 333  },
323769ee804Sschwarze 		{ 333  },
324769ee804Sschwarze 		{ 333  },
325769ee804Sschwarze 		{ 500  },
326769ee804Sschwarze 		{ 675  },
327769ee804Sschwarze 		{ 250  },
328769ee804Sschwarze 		{ 333  },
329769ee804Sschwarze 		{ 250  },
330769ee804Sschwarze 		{ 278  },
331769ee804Sschwarze 		{ 500  },
332769ee804Sschwarze 		{ 500  },
333769ee804Sschwarze 		{ 500  },
334769ee804Sschwarze 		{ 500  },
335769ee804Sschwarze 		{ 500  },
336769ee804Sschwarze 		{ 500  },
337769ee804Sschwarze 		{ 500  },
338769ee804Sschwarze 		{ 500  },
339769ee804Sschwarze 		{ 500  },
340769ee804Sschwarze 		{ 500  },
341769ee804Sschwarze 		{ 333  },
342769ee804Sschwarze 		{ 333  },
343769ee804Sschwarze 		{ 675  },
344769ee804Sschwarze 		{ 675  },
345769ee804Sschwarze 		{ 675  },
346769ee804Sschwarze 		{ 500  },
347769ee804Sschwarze 		{ 920  },
348769ee804Sschwarze 		{ 611  },
349769ee804Sschwarze 		{ 611  },
350769ee804Sschwarze 		{ 667  },
351769ee804Sschwarze 		{ 722  },
352769ee804Sschwarze 		{ 611  },
353769ee804Sschwarze 		{ 611  },
354769ee804Sschwarze 		{ 722  },
355769ee804Sschwarze 		{ 722  },
356769ee804Sschwarze 		{ 333  },
357769ee804Sschwarze 		{ 444  },
358769ee804Sschwarze 		{ 667  },
359769ee804Sschwarze 		{ 556  },
360769ee804Sschwarze 		{ 833  },
361769ee804Sschwarze 		{ 667  },
362769ee804Sschwarze 		{ 722  },
363769ee804Sschwarze 		{ 611  },
364769ee804Sschwarze 		{ 722  },
365769ee804Sschwarze 		{ 611  },
366769ee804Sschwarze 		{ 500  },
367769ee804Sschwarze 		{ 556  },
368769ee804Sschwarze 		{ 722  },
369769ee804Sschwarze 		{ 611  },
370769ee804Sschwarze 		{ 833  },
371769ee804Sschwarze 		{ 611  },
372769ee804Sschwarze 		{ 556  },
373769ee804Sschwarze 		{ 556  },
374769ee804Sschwarze 		{ 389  },
375769ee804Sschwarze 		{ 278  },
376769ee804Sschwarze 		{ 389  },
377769ee804Sschwarze 		{ 422  },
378769ee804Sschwarze 		{ 500  },
379769ee804Sschwarze 		{ 333  },
380769ee804Sschwarze 		{ 500  },
381769ee804Sschwarze 		{ 500  },
382769ee804Sschwarze 		{ 444  },
383769ee804Sschwarze 		{  500 },
384769ee804Sschwarze 		{  444 },
385769ee804Sschwarze 		{  278 },
386769ee804Sschwarze 		{  500 },
387769ee804Sschwarze 		{  500 },
388769ee804Sschwarze 		{  278 },
389769ee804Sschwarze 		{  278 },
390769ee804Sschwarze 		{  444 },
391769ee804Sschwarze 		{  278 },
392769ee804Sschwarze 		{  722 },
393769ee804Sschwarze 		{  500 },
394769ee804Sschwarze 		{  500 },
395769ee804Sschwarze 		{  500 },
396769ee804Sschwarze 		{  500 },
397769ee804Sschwarze 		{  389 },
398769ee804Sschwarze 		{  389 },
399769ee804Sschwarze 		{  278 },
400769ee804Sschwarze 		{  500 },
401769ee804Sschwarze 		{  444 },
402769ee804Sschwarze 		{  667 },
403769ee804Sschwarze 		{  444 },
404769ee804Sschwarze 		{  444 },
405769ee804Sschwarze 		{  389 },
406769ee804Sschwarze 		{  400 },
407769ee804Sschwarze 		{  275 },
408769ee804Sschwarze 		{  400 },
409769ee804Sschwarze 		{  541 },
41006f7b709Sschwarze 	} },
411e1e5254aSschwarze 	{ "Times-BoldItalic", {
412e1e5254aSschwarze 		{  250 },
413e1e5254aSschwarze 		{  389 },
414e1e5254aSschwarze 		{  555 },
415e1e5254aSschwarze 		{  500 },
416e1e5254aSschwarze 		{  500 },
417e1e5254aSschwarze 		{  833 },
418e1e5254aSschwarze 		{  778 },
419e1e5254aSschwarze 		{  333 },
420e1e5254aSschwarze 		{  333 },
421e1e5254aSschwarze 		{  333 },
422e1e5254aSschwarze 		{  500 },
423e1e5254aSschwarze 		{  570 },
424e1e5254aSschwarze 		{  250 },
425e1e5254aSschwarze 		{  333 },
426e1e5254aSschwarze 		{  250 },
427e1e5254aSschwarze 		{  278 },
428e1e5254aSschwarze 		{  500 },
429e1e5254aSschwarze 		{  500 },
430e1e5254aSschwarze 		{  500 },
431e1e5254aSschwarze 		{  500 },
432e1e5254aSschwarze 		{  500 },
433e1e5254aSschwarze 		{  500 },
434e1e5254aSschwarze 		{  500 },
435e1e5254aSschwarze 		{  500 },
436e1e5254aSschwarze 		{  500 },
437e1e5254aSschwarze 		{  500 },
438e1e5254aSschwarze 		{  333 },
439e1e5254aSschwarze 		{  333 },
440e1e5254aSschwarze 		{  570 },
441e1e5254aSschwarze 		{  570 },
442e1e5254aSschwarze 		{  570 },
443e1e5254aSschwarze 		{  500 },
444e1e5254aSschwarze 		{  832 },
445e1e5254aSschwarze 		{  667 },
446e1e5254aSschwarze 		{  667 },
447e1e5254aSschwarze 		{  667 },
448e1e5254aSschwarze 		{  722 },
449e1e5254aSschwarze 		{  667 },
450e1e5254aSschwarze 		{  667 },
451e1e5254aSschwarze 		{  722 },
452e1e5254aSschwarze 		{  778 },
453e1e5254aSschwarze 		{  389 },
454e1e5254aSschwarze 		{  500 },
455e1e5254aSschwarze 		{  667 },
456e1e5254aSschwarze 		{  611 },
457e1e5254aSschwarze 		{  889 },
458e1e5254aSschwarze 		{  722 },
459e1e5254aSschwarze 		{  722 },
460e1e5254aSschwarze 		{  611 },
461e1e5254aSschwarze 		{  722 },
462e1e5254aSschwarze 		{  667 },
463e1e5254aSschwarze 		{  556 },
464e1e5254aSschwarze 		{  611 },
465e1e5254aSschwarze 		{  722 },
466e1e5254aSschwarze 		{  667 },
467e1e5254aSschwarze 		{  889 },
468e1e5254aSschwarze 		{  667 },
469e1e5254aSschwarze 		{  611 },
470e1e5254aSschwarze 		{  611 },
471e1e5254aSschwarze 		{  333 },
472e1e5254aSschwarze 		{  278 },
473e1e5254aSschwarze 		{  333 },
474e1e5254aSschwarze 		{  570 },
475e1e5254aSschwarze 		{  500 },
476e1e5254aSschwarze 		{  333 },
477e1e5254aSschwarze 		{  500 },
478e1e5254aSschwarze 		{  500 },
479e1e5254aSschwarze 		{  444 },
480e1e5254aSschwarze 		{  500 },
481e1e5254aSschwarze 		{  444 },
482e1e5254aSschwarze 		{  333 },
483e1e5254aSschwarze 		{  500 },
484e1e5254aSschwarze 		{  556 },
485e1e5254aSschwarze 		{  278 },
486e1e5254aSschwarze 		{  278 },
487e1e5254aSschwarze 		{  500 },
488e1e5254aSschwarze 		{  278 },
489e1e5254aSschwarze 		{  778 },
490e1e5254aSschwarze 		{  556 },
491e1e5254aSschwarze 		{  500 },
492e1e5254aSschwarze 		{  500 },
493e1e5254aSschwarze 		{  500 },
494e1e5254aSschwarze 		{  389 },
495e1e5254aSschwarze 		{  389 },
496e1e5254aSschwarze 		{  278 },
497e1e5254aSschwarze 		{  556 },
498e1e5254aSschwarze 		{  444 },
499e1e5254aSschwarze 		{  667 },
500e1e5254aSschwarze 		{  500 },
501e1e5254aSschwarze 		{  444 },
502e1e5254aSschwarze 		{  389 },
503e1e5254aSschwarze 		{  348 },
504e1e5254aSschwarze 		{  220 },
505e1e5254aSschwarze 		{  348 },
506e1e5254aSschwarze 		{  570 },
507e1e5254aSschwarze 	} },
50806f7b709Sschwarze };
50906f7b709Sschwarze 
510ddce0b0cSschwarze void *
pdf_alloc(const struct manoutput * outopts)51116536faaSschwarze pdf_alloc(const struct manoutput *outopts)
512ddce0b0cSschwarze {
51320457567Sespie 	return pspdf_alloc(outopts, TERMTYPE_PDF);
514ddce0b0cSschwarze }
515f95d589eSschwarze 
516f95d589eSschwarze void *
ps_alloc(const struct manoutput * outopts)51716536faaSschwarze ps_alloc(const struct manoutput *outopts)
518f95d589eSschwarze {
51920457567Sespie 	return pspdf_alloc(outopts, TERMTYPE_PS);
520ddce0b0cSschwarze }
521ddce0b0cSschwarze 
522ddce0b0cSschwarze static struct termp *
pspdf_alloc(const struct manoutput * outopts,enum termtype type)52320457567Sespie pspdf_alloc(const struct manoutput *outopts, enum termtype type)
524ddce0b0cSschwarze {
525ddce0b0cSschwarze 	struct termp	*p;
52604e980cbSschwarze 	unsigned int	 pagex, pagey;
52704e980cbSschwarze 	size_t		 marginx, marginy, lineheight;
528769ee804Sschwarze 	const char	*pp;
529f95d589eSschwarze 
530e93ea447Sschwarze 	p = mandoc_calloc(1, sizeof(*p));
531e93ea447Sschwarze 	p->tcol = p->tcols = mandoc_calloc(1, sizeof(*p->tcol));
532e93ea447Sschwarze 	p->maxtcol = 1;
53320457567Sespie 	p->type = type;
534e93ea447Sschwarze 
535a5e11edeSschwarze 	p->enc = TERMENC_ASCII;
536c48a0735Sschwarze 	p->fontq = mandoc_reallocarray(NULL,
537e93ea447Sschwarze 	    (p->fontsz = 8), sizeof(*p->fontq));
538c48a0735Sschwarze 	p->fontq[0] = p->fontl = TERMFONT_NONE;
539e93ea447Sschwarze 	p->ps = mandoc_calloc(1, sizeof(*p->ps));
540f95d589eSschwarze 
541769ee804Sschwarze 	p->advance = ps_advance;
542f95d589eSschwarze 	p->begin = ps_begin;
543f95d589eSschwarze 	p->end = ps_end;
544f95d589eSschwarze 	p->endline = ps_endline;
545769ee804Sschwarze 	p->hspan = ps_hspan;
546769ee804Sschwarze 	p->letter = ps_letter;
5475281506aSschwarze 	p->setwidth = ps_setwidth;
5483ebeb861Sschwarze 	p->width = ps_width;
54906f7b709Sschwarze 
550769ee804Sschwarze 	/* Default to US letter (millimetres). */
551769ee804Sschwarze 
5525cab5f2bSschwarze 	p->ps->medianame = "Letter";
553769ee804Sschwarze 	pagex = 216;
554769ee804Sschwarze 	pagey = 279;
555769ee804Sschwarze 
556769ee804Sschwarze 	/*
557769ee804Sschwarze 	 * The ISO-269 paper sizes can be calculated automatically, but
558769ee804Sschwarze 	 * it would require bringing in -lm for pow() and I'd rather not
559769ee804Sschwarze 	 * do that.  So just do it the easy way for now.  Since this
560769ee804Sschwarze 	 * only happens once, I'm not terribly concerned.
561769ee804Sschwarze 	 */
562769ee804Sschwarze 
5632ccd0917Sschwarze 	pp = outopts->paper;
5645cab5f2bSschwarze 	if (pp != NULL && strcasecmp(pp, "letter") != 0) {
5655cab5f2bSschwarze 		if (strcasecmp(pp, "a3") == 0) {
5665cab5f2bSschwarze 			p->ps->medianame = "A3";
567769ee804Sschwarze 			pagex = 297;
568769ee804Sschwarze 			pagey = 420;
5695cab5f2bSschwarze 		} else if (strcasecmp(pp, "a4") == 0) {
5705cab5f2bSschwarze 			p->ps->medianame = "A4";
571769ee804Sschwarze 			pagex = 210;
572769ee804Sschwarze 			pagey = 297;
5735cab5f2bSschwarze 		} else if (strcasecmp(pp, "a5") == 0) {
5745cab5f2bSschwarze 			p->ps->medianame = "A5";
575769ee804Sschwarze 			pagex = 148;
576769ee804Sschwarze 			pagey = 210;
5775cab5f2bSschwarze 		} else if (strcasecmp(pp, "legal") == 0) {
5785cab5f2bSschwarze 			p->ps->medianame = "Legal";
579769ee804Sschwarze 			pagex = 216;
580769ee804Sschwarze 			pagey = 356;
5815cab5f2bSschwarze 		} else if (sscanf(pp, "%ux%u", &pagex, &pagey) == 2)
5825cab5f2bSschwarze 			p->ps->medianame = "CustomSize";
5835cab5f2bSschwarze 		else
584eba1598bSschwarze 			warnx("%s: Unknown paper", pp);
5851cdbf331Sschwarze 	}
586769ee804Sschwarze 
587769ee804Sschwarze 	/*
588769ee804Sschwarze 	 * This MUST be defined before any PNT2AFM or AFM2PNT
589769ee804Sschwarze 	 * calculations occur.
590769ee804Sschwarze 	 */
591769ee804Sschwarze 
592a5e11edeSschwarze 	p->ps->scale = 11;
593769ee804Sschwarze 
594769ee804Sschwarze 	/* Remember millimetres -> AFM units. */
595769ee804Sschwarze 
596353761a3Sschwarze 	pagex = PNT2AFM(p, ((double)pagex * 72.0 / 25.4));
597353761a3Sschwarze 	pagey = PNT2AFM(p, ((double)pagey * 72.0 / 25.4));
598769ee804Sschwarze 
599769ee804Sschwarze 	/* Margins are 1/9 the page x and y. */
600769ee804Sschwarze 
60149aff9f8Sschwarze 	marginx = (size_t)((double)pagex / 9.0);
60249aff9f8Sschwarze 	marginy = (size_t)((double)pagey / 9.0);
603769ee804Sschwarze 
604769ee804Sschwarze 	/* Line-height is 1.4em. */
605769ee804Sschwarze 
606a5e11edeSschwarze 	lineheight = PNT2AFM(p, ((double)p->ps->scale * 1.4));
60706f7b709Sschwarze 
6085281506aSschwarze 	p->ps->width = p->ps->lastwidth = (size_t)pagex;
60904e980cbSschwarze 	p->ps->height = (size_t)pagey;
610a5e11edeSschwarze 	p->ps->header = pagey - (marginy / 2) - (lineheight / 2);
611a5e11edeSschwarze 	p->ps->top = pagey - marginy;
612a5e11edeSschwarze 	p->ps->footer = (marginy / 2) - (lineheight / 2);
613a5e11edeSschwarze 	p->ps->bottom = marginy;
614a5e11edeSschwarze 	p->ps->left = marginx;
615a5e11edeSschwarze 	p->ps->lineheight = lineheight;
61606f7b709Sschwarze 
617769ee804Sschwarze 	p->defrmargin = pagex - (marginx * 2);
618526e306bSschwarze 	return p;
619f95d589eSschwarze }
620f95d589eSschwarze 
6215281506aSschwarze static void
ps_setwidth(struct termp * p,int iop,int width)62213a35416Sschwarze ps_setwidth(struct termp *p, int iop, int width)
6235281506aSschwarze {
6245281506aSschwarze 	size_t	 lastwidth;
6255281506aSschwarze 
6265281506aSschwarze 	lastwidth = p->ps->width;
62799b0a561Sschwarze 	if (iop > 0)
62810111bf6Sschwarze 		p->ps->width += width;
62999b0a561Sschwarze 	else if (iop == 0)
63013a35416Sschwarze 		p->ps->width = width ? (size_t)width : p->ps->lastwidth;
63113a35416Sschwarze 	else if (p->ps->width > (size_t)width)
63210111bf6Sschwarze 		p->ps->width -= width;
63310111bf6Sschwarze 	else
63499b0a561Sschwarze 		p->ps->width = 0;
6355281506aSschwarze 	p->ps->lastwidth = lastwidth;
6365281506aSschwarze }
6375281506aSschwarze 
638f95d589eSschwarze void
pspdf_free(void * arg)639ddce0b0cSschwarze pspdf_free(void *arg)
640f95d589eSschwarze {
641f95d589eSschwarze 	struct termp	*p;
642f95d589eSschwarze 
643f95d589eSschwarze 	p = (struct termp *)arg;
644f95d589eSschwarze 
645a5e11edeSschwarze 	free(p->ps->psmarg);
646a5e11edeSschwarze 	free(p->ps->pdfobjs);
647f95d589eSschwarze 
648a5e11edeSschwarze 	free(p->ps);
649f95d589eSschwarze 	term_free(p);
650f95d589eSschwarze }
651f95d589eSschwarze 
652f95d589eSschwarze static void
ps_printf(struct termp * p,const char * fmt,...)653f95d589eSschwarze ps_printf(struct termp *p, const char *fmt, ...)
654f95d589eSschwarze {
655f95d589eSschwarze 	va_list		 ap;
656ddce0b0cSschwarze 	int		 pos, len;
657f95d589eSschwarze 
658f95d589eSschwarze 	va_start(ap, fmt);
659f95d589eSschwarze 
660f95d589eSschwarze 	/*
661f95d589eSschwarze 	 * If we're running in regular mode, then pipe directly into
662f95d589eSschwarze 	 * vprintf().  If we're processing margins, then push the data
663f95d589eSschwarze 	 * into our growable margin buffer.
664f95d589eSschwarze 	 */
665f95d589eSschwarze 
666a5e11edeSschwarze 	if ( ! (PS_MARGINS & p->ps->flags)) {
667ddce0b0cSschwarze 		len = vprintf(fmt, ap);
668f95d589eSschwarze 		va_end(ap);
66949aff9f8Sschwarze 		p->ps->pdfbytes += len < 0 ? 0 : (size_t)len;
670f95d589eSschwarze 		return;
671f95d589eSschwarze 	}
672f95d589eSschwarze 
673f95d589eSschwarze 	/*
674f95d589eSschwarze 	 * XXX: I assume that the in-margin print won't exceed
675f95d589eSschwarze 	 * PS_BUFSLOP (128 bytes), which is reasonable but still an
676f95d589eSschwarze 	 * assumption that will cause pukeage if it's not the case.
677f95d589eSschwarze 	 */
678f95d589eSschwarze 
679e5a40dc6Sschwarze 	ps_growbuf(p, PS_BUFSLOP);
680f95d589eSschwarze 
681a5e11edeSschwarze 	pos = (int)p->ps->psmargcur;
6821cdbf331Sschwarze 	vsnprintf(&p->ps->psmarg[pos], PS_BUFSLOP, fmt, ap);
683f95d589eSschwarze 
684f95d589eSschwarze 	va_end(ap);
685ddce0b0cSschwarze 
686a5e11edeSschwarze 	p->ps->psmargcur = strlen(p->ps->psmarg);
687f95d589eSschwarze }
688f95d589eSschwarze 
689f95d589eSschwarze static void
ps_putchar(struct termp * p,char c)690f95d589eSschwarze ps_putchar(struct termp *p, char c)
691f95d589eSschwarze {
692f95d589eSschwarze 	int		 pos;
693f95d589eSschwarze 
694f95d589eSschwarze 	/* See ps_printf(). */
695f95d589eSschwarze 
696a5e11edeSschwarze 	if ( ! (PS_MARGINS & p->ps->flags)) {
697f95d589eSschwarze 		putchar(c);
698a5e11edeSschwarze 		p->ps->pdfbytes++;
699f95d589eSschwarze 		return;
700f95d589eSschwarze 	}
701f95d589eSschwarze 
702e5a40dc6Sschwarze 	ps_growbuf(p, 2);
703f95d589eSschwarze 
704a5e11edeSschwarze 	pos = (int)p->ps->psmargcur++;
705a5e11edeSschwarze 	p->ps->psmarg[pos++] = c;
706a5e11edeSschwarze 	p->ps->psmarg[pos] = '\0';
707f95d589eSschwarze }
708f95d589eSschwarze 
709ddce0b0cSschwarze static void
pdf_obj(struct termp * p,size_t obj)7108cd724fbSschwarze pdf_obj(struct termp *p, size_t obj)
7118cd724fbSschwarze {
7128cd724fbSschwarze 
7138cd724fbSschwarze 	assert(obj > 0);
7148cd724fbSschwarze 
715a5e11edeSschwarze 	if ((obj - 1) >= p->ps->pdfobjsz) {
716a5e11edeSschwarze 		p->ps->pdfobjsz = obj + 128;
7178286bf36Sschwarze 		p->ps->pdfobjs = mandoc_reallocarray(p->ps->pdfobjs,
7188286bf36Sschwarze 		    p->ps->pdfobjsz, sizeof(size_t));
7198cd724fbSschwarze 	}
7208cd724fbSschwarze 
721a5e11edeSschwarze 	p->ps->pdfobjs[(int)obj - 1] = p->ps->pdfbytes;
7228cd724fbSschwarze 	ps_printf(p, "%zu 0 obj\n", obj);
7238cd724fbSschwarze }
7248cd724fbSschwarze 
7258cd724fbSschwarze static void
ps_closepage(struct termp * p)726ddce0b0cSschwarze ps_closepage(struct termp *p)
727ddce0b0cSschwarze {
728ddce0b0cSschwarze 	int		 i;
7298cd724fbSschwarze 	size_t		 len, base;
7308cd724fbSschwarze 
7318cd724fbSschwarze 	/*
7328cd724fbSschwarze 	 * Close out a page that we've already flushed to output.  In
733e66ff9cbSespie 	 * PostScript, we simply note that the page must be shown.  In
7348cd724fbSschwarze 	 * PDF, we must now create the Length, Resource, and Page node
7358cd724fbSschwarze 	 * for the page contents.
7368cd724fbSschwarze 	 */
737ddce0b0cSschwarze 
738a5e11edeSschwarze 	assert(p->ps->psmarg && p->ps->psmarg[0]);
739a5e11edeSschwarze 	ps_printf(p, "%s", p->ps->psmarg);
740ddce0b0cSschwarze 
7418cd724fbSschwarze 	if (TERMTYPE_PS != p->type) {
742a5e11edeSschwarze 		len = p->ps->pdfbytes - p->ps->pdflastpg;
743a5e11edeSschwarze 		base = p->ps->pages * 4 + p->ps->pdfbody;
7448cd724fbSschwarze 
7458cd724fbSschwarze 		ps_printf(p, "endstream\nendobj\n");
7468cd724fbSschwarze 
7478cd724fbSschwarze 		/* Length of content. */
7488cd724fbSschwarze 		pdf_obj(p, base + 1);
7498cd724fbSschwarze 		ps_printf(p, "%zu\nendobj\n", len);
7508cd724fbSschwarze 
7518cd724fbSschwarze 		/* Resource for content. */
7528cd724fbSschwarze 		pdf_obj(p, base + 2);
7538cd724fbSschwarze 		ps_printf(p, "<<\n/ProcSet [/PDF /Text]\n");
754ddce0b0cSschwarze 		ps_printf(p, "/Font <<\n");
755ddce0b0cSschwarze 		for (i = 0; i < (int)TERMFONT__MAX; i++)
756ddce0b0cSschwarze 			ps_printf(p, "/F%d %d 0 R\n", i, 3 + i);
757893b2dc2Sschwarze 		ps_printf(p, ">>\n>>\nendobj\n");
7588cd724fbSschwarze 
7598cd724fbSschwarze 		/* Page node. */
7608cd724fbSschwarze 		pdf_obj(p, base + 3);
761ddce0b0cSschwarze 		ps_printf(p, "<<\n");
762ddce0b0cSschwarze 		ps_printf(p, "/Type /Page\n");
763ddce0b0cSschwarze 		ps_printf(p, "/Parent 2 0 R\n");
7648cd724fbSschwarze 		ps_printf(p, "/Resources %zu 0 R\n", base + 2);
7658cd724fbSschwarze 		ps_printf(p, "/Contents %zu 0 R\n", base);
7668cd724fbSschwarze 		ps_printf(p, ">>\nendobj\n");
7678cd724fbSschwarze 	} else
7688cd724fbSschwarze 		ps_printf(p, "showpage\n");
769ddce0b0cSschwarze 
770a5e11edeSschwarze 	p->ps->pages++;
771a5e11edeSschwarze 	p->ps->psrow = p->ps->top;
772a5e11edeSschwarze 	assert( ! (PS_NEWPAGE & p->ps->flags));
773a5e11edeSschwarze 	p->ps->flags |= PS_NEWPAGE;
774ddce0b0cSschwarze }
775ddce0b0cSschwarze 
776f95d589eSschwarze static void
ps_end(struct termp * p)777f95d589eSschwarze ps_end(struct termp *p)
778f95d589eSschwarze {
7798cd724fbSschwarze 	size_t		 i, xref, base;
780f95d589eSschwarze 
7816470d541Sschwarze 	ps_plast(p);
7826470d541Sschwarze 	ps_pclose(p);
7836470d541Sschwarze 
784f95d589eSschwarze 	/*
785f95d589eSschwarze 	 * At the end of the file, do one last showpage.  This is the
786f95d589eSschwarze 	 * same behaviour as groff(1) and works for multiple pages as
787f95d589eSschwarze 	 * well as just one.
788f95d589eSschwarze 	 */
789f95d589eSschwarze 
790a5e11edeSschwarze 	if ( ! (PS_NEWPAGE & p->ps->flags)) {
791a5e11edeSschwarze 		assert(0 == p->ps->flags);
792a5e11edeSschwarze 		assert('\0' == p->ps->last);
793ddce0b0cSschwarze 		ps_closepage(p);
794769ee804Sschwarze 	}
79506f7b709Sschwarze 
796ddce0b0cSschwarze 	if (TERMTYPE_PS == p->type) {
797ddce0b0cSschwarze 		ps_printf(p, "%%%%Trailer\n");
798a5e11edeSschwarze 		ps_printf(p, "%%%%Pages: %zu\n", p->ps->pages);
799ddce0b0cSschwarze 		ps_printf(p, "%%%%EOF\n");
800ddce0b0cSschwarze 		return;
801ddce0b0cSschwarze 	}
802ddce0b0cSschwarze 
8038cd724fbSschwarze 	pdf_obj(p, 2);
8048cd724fbSschwarze 	ps_printf(p, "<<\n/Type /Pages\n");
805ddce0b0cSschwarze 	ps_printf(p, "/MediaBox [0 0 %zu %zu]\n",
806a5e11edeSschwarze 			(size_t)AFM2PNT(p, p->ps->width),
807a5e11edeSschwarze 			(size_t)AFM2PNT(p, p->ps->height));
808ddce0b0cSschwarze 
809a5e11edeSschwarze 	ps_printf(p, "/Count %zu\n", p->ps->pages);
810ddce0b0cSschwarze 	ps_printf(p, "/Kids [");
811ddce0b0cSschwarze 
812a5e11edeSschwarze 	for (i = 0; i < p->ps->pages; i++)
81349aff9f8Sschwarze 		ps_printf(p, " %zu 0 R", i * 4 + p->ps->pdfbody + 3);
8148cd724fbSschwarze 
81549aff9f8Sschwarze 	base = (p->ps->pages - 1) * 4 + p->ps->pdfbody + 4;
8168cd724fbSschwarze 
8178cd724fbSschwarze 	ps_printf(p, "]\n>>\nendobj\n");
8188cd724fbSschwarze 	pdf_obj(p, base);
819ddce0b0cSschwarze 	ps_printf(p, "<<\n");
820ddce0b0cSschwarze 	ps_printf(p, "/Type /Catalog\n");
821ddce0b0cSschwarze 	ps_printf(p, "/Pages 2 0 R\n");
822893b2dc2Sschwarze 	ps_printf(p, ">>\nendobj\n");
823a5e11edeSschwarze 	xref = p->ps->pdfbytes;
824ddce0b0cSschwarze 	ps_printf(p, "xref\n");
8258cd724fbSschwarze 	ps_printf(p, "0 %zu\n", base + 1);
826ddce0b0cSschwarze 	ps_printf(p, "0000000000 65535 f \n");
8278cd724fbSschwarze 
8288cd724fbSschwarze 	for (i = 0; i < base; i++)
8298cd724fbSschwarze 		ps_printf(p, "%.10zu 00000 n \n",
830a5e11edeSschwarze 		    p->ps->pdfobjs[(int)i]);
8318cd724fbSschwarze 
832ddce0b0cSschwarze 	ps_printf(p, "trailer\n");
833ddce0b0cSschwarze 	ps_printf(p, "<<\n");
8348cd724fbSschwarze 	ps_printf(p, "/Size %zu\n", base + 1);
8358cd724fbSschwarze 	ps_printf(p, "/Root %zu 0 R\n", base);
836ddce0b0cSschwarze 	ps_printf(p, "/Info 1 0 R\n");
837ddce0b0cSschwarze 	ps_printf(p, ">>\n");
838ddce0b0cSschwarze 	ps_printf(p, "startxref\n");
839ddce0b0cSschwarze 	ps_printf(p, "%zu\n", xref);
840ddce0b0cSschwarze 	ps_printf(p, "%%%%EOF\n");
841f95d589eSschwarze }
842f95d589eSschwarze 
843f95d589eSschwarze static void
ps_begin(struct termp * p)844f95d589eSschwarze ps_begin(struct termp *p)
845f95d589eSschwarze {
8465cab5f2bSschwarze 	size_t		 width, height;
84706f7b709Sschwarze 	int		 i;
848f95d589eSschwarze 
849f95d589eSschwarze 	/*
85031e23753Sschwarze 	 * Print margins into margin buffer.  Nothing gets output to the
85131e23753Sschwarze 	 * screen yet, so we don't need to initialise the primary state.
852f95d589eSschwarze 	 */
853f95d589eSschwarze 
854a5e11edeSschwarze 	if (p->ps->psmarg) {
855a5e11edeSschwarze 		assert(p->ps->psmargsz);
856a5e11edeSschwarze 		p->ps->psmarg[0] = '\0';
857f95d589eSschwarze 	}
858f95d589eSschwarze 
859a5e11edeSschwarze 	/*p->ps->pdfbytes = 0;*/
860a5e11edeSschwarze 	p->ps->psmargcur = 0;
861a5e11edeSschwarze 	p->ps->flags = PS_MARGINS;
862a5e11edeSschwarze 	p->ps->pscol = p->ps->left;
863a5e11edeSschwarze 	p->ps->psrow = p->ps->header;
864e66ff9cbSespie 	p->ps->lastrow = 0; /* impossible row */
865f95d589eSschwarze 
86631e23753Sschwarze 	ps_setfont(p, TERMFONT_NONE);
867f95d589eSschwarze 
868f95d589eSschwarze 	(*p->headf)(p, p->argf);
869f95d589eSschwarze 	(*p->endline)(p);
870f95d589eSschwarze 
871a5e11edeSschwarze 	p->ps->pscol = p->ps->left;
872a5e11edeSschwarze 	p->ps->psrow = p->ps->footer;
873f95d589eSschwarze 
874f95d589eSschwarze 	(*p->footf)(p, p->argf);
875f95d589eSschwarze 	(*p->endline)(p);
876f95d589eSschwarze 
877a5e11edeSschwarze 	p->ps->flags &= ~PS_MARGINS;
87831e23753Sschwarze 
879a5e11edeSschwarze 	assert(0 == p->ps->flags);
880a5e11edeSschwarze 	assert(p->ps->psmarg);
881a5e11edeSschwarze 	assert('\0' != p->ps->psmarg[0]);
88231e23753Sschwarze 
88331e23753Sschwarze 	/*
88431e23753Sschwarze 	 * Print header and initialise page state.  Following this,
88531e23753Sschwarze 	 * stuff gets printed to the screen, so make sure we're sane.
88631e23753Sschwarze 	 */
88731e23753Sschwarze 
888ddce0b0cSschwarze 	if (TERMTYPE_PS == p->type) {
8895cab5f2bSschwarze 		width = AFM2PNT(p, p->ps->width);
8905cab5f2bSschwarze 		height = AFM2PNT(p, p->ps->height);
8915cab5f2bSschwarze 
892ddce0b0cSschwarze 		ps_printf(p, "%%!PS-Adobe-3.0\n");
893ddce0b0cSschwarze 		ps_printf(p, "%%%%DocumentData: Clean7Bit\n");
894ddce0b0cSschwarze 		ps_printf(p, "%%%%Orientation: Portrait\n");
895ddce0b0cSschwarze 		ps_printf(p, "%%%%Pages: (atend)\n");
896ddce0b0cSschwarze 		ps_printf(p, "%%%%PageOrder: Ascend\n");
8975cab5f2bSschwarze 		ps_printf(p, "%%%%DocumentMedia: man-%s %zu %zu 0 () ()\n",
8985cab5f2bSschwarze 		    p->ps->medianame, width, height);
899ddce0b0cSschwarze 		ps_printf(p, "%%%%DocumentNeededResources: font");
90006f7b709Sschwarze 
901ddce0b0cSschwarze 		for (i = 0; i < (int)TERMFONT__MAX; i++)
902ddce0b0cSschwarze 			ps_printf(p, " %s", fonts[i].name);
903ddce0b0cSschwarze 
904e66ff9cbSespie 		ps_printf(p, "\n%%%%DocumentSuppliedResources: "
905e66ff9cbSespie 		    "procset MandocProcs 1.0 0\n");
906e66ff9cbSespie 		ps_printf(p, "%%%%EndComments\n");
907e66ff9cbSespie 		ps_printf(p, "%%%%BeginProlog\n");
908e66ff9cbSespie 		ps_printf(p, "%%%%BeginResource: procset MandocProcs "
909e66ff9cbSespie 		    "10170 10170\n");
910e66ff9cbSespie 		/* The font size is effectively hard-coded for now. */
911e66ff9cbSespie 		ps_printf(p, "/fs %zu def\n", p->ps->scale);
912e66ff9cbSespie 		for (i = 0; i < (int)TERMFONT__MAX; i++)
913e66ff9cbSespie 			ps_printf(p, "/f%d { /%s fs selectfont } def\n",
914e66ff9cbSespie 			    i, fonts[i].name);
915e66ff9cbSespie 		ps_printf(p, "/s { 3 1 roll moveto show } bind def\n");
916e66ff9cbSespie 		ps_printf(p, "/c { exch currentpoint exch pop "
917e66ff9cbSespie 		    "moveto show } bind def\n");
918e66ff9cbSespie 		ps_printf(p, "%%%%EndResource\n");
919e66ff9cbSespie 		ps_printf(p, "%%%%EndProlog\n");
9205cab5f2bSschwarze 		ps_printf(p, "%%%%BeginSetup\n");
9215cab5f2bSschwarze 		ps_printf(p, "%%%%BeginFeature: *PageSize %s\n",
9225cab5f2bSschwarze 		    p->ps->medianame);
9235cab5f2bSschwarze 		ps_printf(p, "<</PageSize [%zu %zu]>>setpagedevice\n",
9245cab5f2bSschwarze 		    width, height);
9255cab5f2bSschwarze 		ps_printf(p, "%%%%EndFeature\n");
9265cab5f2bSschwarze 		ps_printf(p, "%%%%EndSetup\n");
927ddce0b0cSschwarze 	} else {
928ddce0b0cSschwarze 		ps_printf(p, "%%PDF-1.1\n");
9298cd724fbSschwarze 		pdf_obj(p, 1);
930ddce0b0cSschwarze 		ps_printf(p, "<<\n");
931ddce0b0cSschwarze 		ps_printf(p, ">>\n");
932ddce0b0cSschwarze 		ps_printf(p, "endobj\n");
933ddce0b0cSschwarze 
934ddce0b0cSschwarze 		for (i = 0; i < (int)TERMFONT__MAX; i++) {
9358cd724fbSschwarze 			pdf_obj(p, (size_t)i + 3);
936ddce0b0cSschwarze 			ps_printf(p, "<<\n");
937ddce0b0cSschwarze 			ps_printf(p, "/Type /Font\n");
938ddce0b0cSschwarze 			ps_printf(p, "/Subtype /Type1\n");
939597db8fdSschwarze 			ps_printf(p, "/Name /F%d\n", i);
940ddce0b0cSschwarze 			ps_printf(p, "/BaseFont /%s\n", fonts[i].name);
941893b2dc2Sschwarze 			ps_printf(p, ">>\nendobj\n");
942ddce0b0cSschwarze 		}
943ddce0b0cSschwarze 	}
944ddce0b0cSschwarze 
945a5e11edeSschwarze 	p->ps->pdfbody = (size_t)TERMFONT__MAX + 3;
946a5e11edeSschwarze 	p->ps->pscol = p->ps->left;
947a5e11edeSschwarze 	p->ps->psrow = p->ps->top;
948a5e11edeSschwarze 	p->ps->flags |= PS_NEWPAGE;
949769ee804Sschwarze 	ps_setfont(p, TERMFONT_NONE);
950f95d589eSschwarze }
951f95d589eSschwarze 
952f95d589eSschwarze static void
ps_pletter(struct termp * p,int c)95306f7b709Sschwarze ps_pletter(struct termp *p, int c)
954f95d589eSschwarze {
95506f7b709Sschwarze 	int		 f;
956f95d589eSschwarze 
957f95d589eSschwarze 	/*
958769ee804Sschwarze 	 * If we haven't opened a page context, then output that we're
959769ee804Sschwarze 	 * in a new page and make sure the font is correctly set.
960769ee804Sschwarze 	 */
961769ee804Sschwarze 
962a5e11edeSschwarze 	if (PS_NEWPAGE & p->ps->flags) {
963ddce0b0cSschwarze 		if (TERMTYPE_PS == p->type) {
964ddce0b0cSschwarze 			ps_printf(p, "%%%%Page: %zu %zu\n",
96549aff9f8Sschwarze 			    p->ps->pages + 1, p->ps->pages + 1);
966e66ff9cbSespie 			ps_printf(p, "f%d\n", (int)p->ps->lastf);
967ddce0b0cSschwarze 		} else {
968a5e11edeSschwarze 			pdf_obj(p, p->ps->pdfbody +
969a5e11edeSschwarze 			    p->ps->pages * 4);
970ddce0b0cSschwarze 			ps_printf(p, "<<\n");
971ddce0b0cSschwarze 			ps_printf(p, "/Length %zu 0 R\n",
97249aff9f8Sschwarze 			    p->ps->pdfbody + 1 + p->ps->pages * 4);
973ddce0b0cSschwarze 			ps_printf(p, ">>\nstream\n");
974ddce0b0cSschwarze 		}
975a5e11edeSschwarze 		p->ps->pdflastpg = p->ps->pdfbytes;
976a5e11edeSschwarze 		p->ps->flags &= ~PS_NEWPAGE;
977769ee804Sschwarze 	}
978769ee804Sschwarze 
979769ee804Sschwarze 	/*
98031e23753Sschwarze 	 * If we're not in a PostScript "word" context, then open one
98131e23753Sschwarze 	 * now at the current cursor.
982f95d589eSschwarze 	 */
98331e23753Sschwarze 
984a5e11edeSschwarze 	if ( ! (PS_INLINE & p->ps->flags)) {
985ddce0b0cSschwarze 		if (TERMTYPE_PS != p->type) {
986ddce0b0cSschwarze 			ps_printf(p, "BT\n/F%d %zu Tf\n",
98749aff9f8Sschwarze 			    (int)p->ps->lastf, p->ps->scale);
988ddce0b0cSschwarze 			ps_printf(p, "%.3f %.3f Td\n(",
989a5e11edeSschwarze 			    AFM2PNT(p, p->ps->pscol),
990a5e11edeSschwarze 			    AFM2PNT(p, p->ps->psrow));
991e66ff9cbSespie 		} else {
992e66ff9cbSespie 			ps_printf(p, "%.3f", AFM2PNT(p, p->ps->pscol));
993e66ff9cbSespie 			if (p->ps->psrow != p->ps->lastrow)
994e66ff9cbSespie 				ps_printf(p, " %.3f",
995a5e11edeSschwarze 				    AFM2PNT(p, p->ps->psrow));
996e66ff9cbSespie 			ps_printf(p, "(");
997e66ff9cbSespie 		}
998a5e11edeSschwarze 		p->ps->flags |= PS_INLINE;
999f95d589eSschwarze 	}
1000f95d589eSschwarze 
1001a5e11edeSschwarze 	assert( ! (PS_NEWPAGE & p->ps->flags));
1002769ee804Sschwarze 
1003f95d589eSschwarze 	/*
1004f95d589eSschwarze 	 * We need to escape these characters as per the PostScript
1005f95d589eSschwarze 	 * specification.  We would also escape non-graphable characters
1006f95d589eSschwarze 	 * (like tabs), but none of them would get to this point and
1007f95d589eSschwarze 	 * it's superfluous to abort() on them.
1008f95d589eSschwarze 	 */
1009f95d589eSschwarze 
1010f95d589eSschwarze 	switch (c) {
101149aff9f8Sschwarze 	case '(':
101249aff9f8Sschwarze 	case ')':
101349aff9f8Sschwarze 	case '\\':
1014f95d589eSschwarze 		ps_putchar(p, '\\');
1015f95d589eSschwarze 		break;
1016f95d589eSschwarze 	default:
1017f95d589eSschwarze 		break;
1018f95d589eSschwarze 	}
1019f95d589eSschwarze 
1020f95d589eSschwarze 	/* Write the character and adjust where we are on the page. */
102131e23753Sschwarze 
1022a5e11edeSschwarze 	f = (int)p->ps->lastf;
102306f7b709Sschwarze 
102439b58d4bSschwarze 	if (c <= 32 || c - 32 >= MAXCHAR)
102539b58d4bSschwarze 		c = 32;
102606f7b709Sschwarze 
1027769ee804Sschwarze 	ps_putchar(p, (char)c);
102806f7b709Sschwarze 	c -= 32;
1029a5e11edeSschwarze 	p->ps->pscol += (size_t)fonts[f].gly[c].wx;
1030f95d589eSschwarze }
1031f95d589eSschwarze 
1032f95d589eSschwarze static void
ps_pclose(struct termp * p)103331e23753Sschwarze ps_pclose(struct termp *p)
103431e23753Sschwarze {
103531e23753Sschwarze 
103631e23753Sschwarze 	/*
103731e23753Sschwarze 	 * Spit out that we're exiting a word context (this is a
103831e23753Sschwarze 	 * "partial close" because we don't check the last-char buffer
103931e23753Sschwarze 	 * or anything).
104031e23753Sschwarze 	 */
104131e23753Sschwarze 
1042a5e11edeSschwarze 	if ( ! (PS_INLINE & p->ps->flags))
104331e23753Sschwarze 		return;
104431e23753Sschwarze 
1045e66ff9cbSespie 	if (TERMTYPE_PS != p->type)
1046ddce0b0cSschwarze 		ps_printf(p, ") Tj\nET\n");
1047e66ff9cbSespie 	else if (p->ps->psrow == p->ps->lastrow)
1048e66ff9cbSespie 		ps_printf(p, ")c\n");
1049e66ff9cbSespie 	else {
1050e66ff9cbSespie 		ps_printf(p, ")s\n");
1051e66ff9cbSespie 		p->ps->lastrow = p->ps->psrow;
1052e66ff9cbSespie 	}
1053ddce0b0cSschwarze 
1054a5e11edeSschwarze 	p->ps->flags &= ~PS_INLINE;
105531e23753Sschwarze }
105631e23753Sschwarze 
10576470d541Sschwarze /* If we have a `last' char that wasn't printed yet, print it now. */
105831e23753Sschwarze static void
ps_plast(struct termp * p)10596470d541Sschwarze ps_plast(struct termp *p)
106031e23753Sschwarze {
10616470d541Sschwarze 	size_t	 wx;
106231e23753Sschwarze 
10636470d541Sschwarze 	if (p->ps->last == '\0')
10646470d541Sschwarze 		return;
106531e23753Sschwarze 
10666470d541Sschwarze 	/* Check the font mode; open a new scope if it doesn't match. */
10676470d541Sschwarze 
1068e1e5254aSschwarze 	if (p->ps->nextf != p->ps->lastf) {
106931e23753Sschwarze 		ps_pclose(p);
1070e1e5254aSschwarze 		ps_setfont(p, p->ps->nextf);
107131e23753Sschwarze 	}
1072e1e5254aSschwarze 	p->ps->nextf = TERMFONT_NONE;
10736470d541Sschwarze 
10746470d541Sschwarze 	/*
10756470d541Sschwarze 	 * For an overstrike, if a previous character
10766470d541Sschwarze 	 * was wider, advance to center the new one.
10776470d541Sschwarze 	 */
10786470d541Sschwarze 
10796470d541Sschwarze 	if (p->ps->pscolnext) {
10806470d541Sschwarze 		wx = fonts[p->ps->lastf].gly[(int)p->ps->last-32].wx;
10816470d541Sschwarze 		if (p->ps->pscol + wx < p->ps->pscolnext)
10826470d541Sschwarze 			p->ps->pscol = (p->ps->pscol +
10836470d541Sschwarze 			    p->ps->pscolnext - wx) / 2;
108431e23753Sschwarze 	}
108531e23753Sschwarze 
10866470d541Sschwarze 	ps_pletter(p, p->ps->last);
10876470d541Sschwarze 	p->ps->last = '\0';
108831e23753Sschwarze 
10896470d541Sschwarze 	/*
10906470d541Sschwarze 	 * For an overstrike, if a previous character
10916470d541Sschwarze 	 * was wider, advance to the end of the old one.
10926470d541Sschwarze 	 */
10936470d541Sschwarze 
10946470d541Sschwarze 	if (p->ps->pscol < p->ps->pscolnext) {
109531e23753Sschwarze 		ps_pclose(p);
10966470d541Sschwarze 		p->ps->pscol = p->ps->pscolnext;
10976470d541Sschwarze 	}
109831e23753Sschwarze }
109931e23753Sschwarze 
110031e23753Sschwarze static void
ps_letter(struct termp * p,int arg)1101a5e11edeSschwarze ps_letter(struct termp *p, int arg)
110231e23753Sschwarze {
11036470d541Sschwarze 	size_t		savecol;
1104e72bdc81Sschwarze 	char		c;
1105a5e11edeSschwarze 
1106a5e11edeSschwarze 	c = arg >= 128 || arg <= 0 ? '?' : arg;
110731e23753Sschwarze 
110831e23753Sschwarze 	/*
1109148007bcSschwarze 	 * When receiving a backspace, merely flag it.
1110148007bcSschwarze 	 * We don't know yet whether it is
1111148007bcSschwarze 	 * a font instruction or an overstrike.
111231e23753Sschwarze 	 */
111331e23753Sschwarze 
1114148007bcSschwarze 	if (c == '\b') {
1115e1e5254aSschwarze 		assert(p->ps->last != '\0');
1116148007bcSschwarze 		assert( ! (p->ps->flags & PS_BACKSP));
1117148007bcSschwarze 		p->ps->flags |= PS_BACKSP;
1118148007bcSschwarze 		return;
1119148007bcSschwarze 	}
1120148007bcSschwarze 
1121148007bcSschwarze 	/*
1122148007bcSschwarze 	 * Decode font instructions.
1123148007bcSschwarze 	 */
1124148007bcSschwarze 
1125148007bcSschwarze 	if (p->ps->flags & PS_BACKSP) {
1126148007bcSschwarze 		if (p->ps->last == '_') {
1127e1e5254aSschwarze 			switch (p->ps->nextf) {
1128e1e5254aSschwarze 			case TERMFONT_BI:
1129e1e5254aSschwarze 				break;
1130e1e5254aSschwarze 			case TERMFONT_BOLD:
1131e1e5254aSschwarze 				p->ps->nextf = TERMFONT_BI;
1132e1e5254aSschwarze 				break;
1133e1e5254aSschwarze 			default:
1134e1e5254aSschwarze 				p->ps->nextf = TERMFONT_UNDER;
113531e23753Sschwarze 			}
1136148007bcSschwarze 			p->ps->last = c;
1137148007bcSschwarze 			p->ps->flags &= ~PS_BACKSP;
1138148007bcSschwarze 			return;
1139148007bcSschwarze 		}
1140148007bcSschwarze 		if (p->ps->last == c) {
1141e1e5254aSschwarze 			switch (p->ps->nextf) {
1142e1e5254aSschwarze 			case TERMFONT_BI:
1143e1e5254aSschwarze 				break;
1144e1e5254aSschwarze 			case TERMFONT_UNDER:
1145e1e5254aSschwarze 				p->ps->nextf = TERMFONT_BI;
1146e1e5254aSschwarze 				break;
1147e1e5254aSschwarze 			default:
1148e1e5254aSschwarze 				p->ps->nextf = TERMFONT_BOLD;
1149e1e5254aSschwarze 			}
1150148007bcSschwarze 			p->ps->flags &= ~PS_BACKSP;
1151148007bcSschwarze 			return;
1152e1e5254aSschwarze 		}
1153148007bcSschwarze 
1154148007bcSschwarze 		/*
1155148007bcSschwarze 		 * This is not a font instruction, but rather
1156148007bcSschwarze 		 * the next character.  Prepare for overstrike.
1157148007bcSschwarze 		 */
1158148007bcSschwarze 
1159148007bcSschwarze 		savecol = p->ps->pscol;
1160148007bcSschwarze 	} else
1161148007bcSschwarze 		savecol = SIZE_MAX;
1162148007bcSschwarze 
1163148007bcSschwarze 	/*
1164148007bcSschwarze 	 * We found the next character, so the font instructions
1165148007bcSschwarze 	 * for the previous one are complete.
1166148007bcSschwarze 	 * Use them and print it.
1167148007bcSschwarze 	 */
1168148007bcSschwarze 
11696470d541Sschwarze 	ps_plast(p);
1170148007bcSschwarze 
1171148007bcSschwarze 	/*
1172148007bcSschwarze 	 * Do not print the current character yet because font
11736470d541Sschwarze 	 * instructions might follow; only remember the character.
11746470d541Sschwarze 	 * It will get printed later from ps_plast().
1175148007bcSschwarze 	 */
1176148007bcSschwarze 
1177a5e11edeSschwarze 	p->ps->last = c;
1178148007bcSschwarze 
1179148007bcSschwarze 	/*
1180148007bcSschwarze 	 * For an overstrike, back up to the previous position.
11812dfa6404Sschwarze 	 * If the previous character is wider than any it overstrikes,
11822dfa6404Sschwarze 	 * remember the current position, because it might also be
11832dfa6404Sschwarze 	 * wider than all that will overstrike it.
1184148007bcSschwarze 	 */
1185148007bcSschwarze 
1186148007bcSschwarze 	if (savecol != SIZE_MAX) {
11872dfa6404Sschwarze 		if (p->ps->pscolnext < p->ps->pscol)
11882dfa6404Sschwarze 			p->ps->pscolnext = p->ps->pscol;
1189148007bcSschwarze 		ps_pclose(p);
1190148007bcSschwarze 		p->ps->pscol = savecol;
1191148007bcSschwarze 		p->ps->flags &= ~PS_BACKSP;
11922dfa6404Sschwarze 	} else
11932dfa6404Sschwarze 		p->ps->pscolnext = 0;
119431e23753Sschwarze }
119531e23753Sschwarze 
119631e23753Sschwarze static void
ps_advance(struct termp * p,size_t len)1197f95d589eSschwarze ps_advance(struct termp *p, size_t len)
1198f95d589eSschwarze {
1199f95d589eSschwarze 
120031e23753Sschwarze 	/*
120131e23753Sschwarze 	 * Advance some spaces.  This can probably be made smarter,
120231e23753Sschwarze 	 * i.e., to have multiple space-separated words in the same
120331e23753Sschwarze 	 * scope, but this is easier:  just close out the current scope
120431e23753Sschwarze 	 * and readjust our column settings.
120531e23753Sschwarze 	 */
1206f95d589eSschwarze 
12076470d541Sschwarze 	ps_plast(p);
12086470d541Sschwarze 	ps_pclose(p);
1209a5e11edeSschwarze 	p->ps->pscol += len;
1210f95d589eSschwarze }
1211f95d589eSschwarze 
1212f95d589eSschwarze static void
ps_endline(struct termp * p)1213f95d589eSschwarze ps_endline(struct termp *p)
1214f95d589eSschwarze {
1215f95d589eSschwarze 
121631e23753Sschwarze 	/* Close out any scopes we have open: we're at eoln. */
121731e23753Sschwarze 
12186470d541Sschwarze 	ps_plast(p);
12196470d541Sschwarze 	ps_pclose(p);
122031e23753Sschwarze 
122131e23753Sschwarze 	/*
122231e23753Sschwarze 	 * If we're in the margin, don't try to recalculate our current
122331e23753Sschwarze 	 * row.  XXX: if the column tries to be fancy with multiple
122431e23753Sschwarze 	 * lines, we'll do nasty stuff.
122531e23753Sschwarze 	 */
1226f95d589eSschwarze 
1227a5e11edeSschwarze 	if (PS_MARGINS & p->ps->flags)
1228769ee804Sschwarze 		return;
1229769ee804Sschwarze 
1230769ee804Sschwarze 	/* Left-justify. */
1231769ee804Sschwarze 
1232a5e11edeSschwarze 	p->ps->pscol = p->ps->left;
1233769ee804Sschwarze 
1234769ee804Sschwarze 	/* If we haven't printed anything, return. */
1235769ee804Sschwarze 
1236a5e11edeSschwarze 	if (PS_NEWPAGE & p->ps->flags)
1237f95d589eSschwarze 		return;
1238f95d589eSschwarze 
123931e23753Sschwarze 	/*
124031e23753Sschwarze 	 * Put us down a line.  If we're at the page bottom, spit out a
124131e23753Sschwarze 	 * showpage and restart our row.
124231e23753Sschwarze 	 */
124331e23753Sschwarze 
124449aff9f8Sschwarze 	if (p->ps->psrow >= p->ps->lineheight + p->ps->bottom) {
1245a5e11edeSschwarze 		p->ps->psrow -= p->ps->lineheight;
1246f95d589eSschwarze 		return;
1247f95d589eSschwarze 	}
1248f95d589eSschwarze 
1249ddce0b0cSschwarze 	ps_closepage(p);
125011d70615Sschwarze 
1251*30c2fcb2Sschwarze 	if ((int)p->tcol->offset > p->ti)
1252e93ea447Sschwarze 		p->tcol->offset -= p->ti;
1253*30c2fcb2Sschwarze 	else
1254*30c2fcb2Sschwarze 		p->tcol->offset = 0;
125511d70615Sschwarze 	p->ti = 0;
1256f95d589eSschwarze }
125731e23753Sschwarze 
125831e23753Sschwarze static void
ps_setfont(struct termp * p,enum termfont f)125931e23753Sschwarze ps_setfont(struct termp *p, enum termfont f)
126031e23753Sschwarze {
126131e23753Sschwarze 
126206f7b709Sschwarze 	assert(f < TERMFONT__MAX);
1263a5e11edeSschwarze 	p->ps->lastf = f;
1264769ee804Sschwarze 
1265769ee804Sschwarze 	/*
1266769ee804Sschwarze 	 * If we're still at the top of the page, let the font-setting
1267769ee804Sschwarze 	 * be delayed until we actually have stuff to print.
1268769ee804Sschwarze 	 */
1269769ee804Sschwarze 
1270a5e11edeSschwarze 	if (PS_NEWPAGE & p->ps->flags)
1271769ee804Sschwarze 		return;
1272769ee804Sschwarze 
1273ddce0b0cSschwarze 	if (TERMTYPE_PS == p->type)
1274e66ff9cbSespie 		ps_printf(p, "f%d\n", (int)f);
1275ddce0b0cSschwarze 	else
1276ddce0b0cSschwarze 		ps_printf(p, "/F%d %zu Tf\n",
127749aff9f8Sschwarze 		    (int)f, p->ps->scale);
127831e23753Sschwarze }
127931e23753Sschwarze 
12803ebeb861Sschwarze static size_t
ps_width(const struct termp * p,int c)1281a5e11edeSschwarze ps_width(const struct termp *p, int c)
12823ebeb861Sschwarze {
12833ebeb861Sschwarze 
128406f7b709Sschwarze 	if (c <= 32 || c - 32 >= MAXCHAR)
128539b58d4bSschwarze 		c = 0;
128639b58d4bSschwarze 	else
128706f7b709Sschwarze 		c -= 32;
128839b58d4bSschwarze 
1289526e306bSschwarze 	return (size_t)fonts[(int)TERMFONT_NONE].gly[c].wx;
12903ebeb861Sschwarze }
1291769ee804Sschwarze 
129213a35416Sschwarze static int
ps_hspan(const struct termp * p,const struct roffsu * su)1293769ee804Sschwarze ps_hspan(const struct termp *p, const struct roffsu *su)
1294769ee804Sschwarze {
1295769ee804Sschwarze 	double		 r;
1296769ee804Sschwarze 
1297769ee804Sschwarze 	/*
1298769ee804Sschwarze 	 * All of these measurements are derived by converting from the
1299769ee804Sschwarze 	 * native measurement to AFM units.
1300769ee804Sschwarze 	 */
1301769ee804Sschwarze 	switch (su->unit) {
1302d5dc6f2eSschwarze 	case SCALE_BU:
1303d5dc6f2eSschwarze 		/*
1304d5dc6f2eSschwarze 		 * Traditionally, the default unit is fixed to the
1305d5dc6f2eSschwarze 		 * output media.  So this would refer to the point.  In
1306d5dc6f2eSschwarze 		 * mandoc(1), however, we stick to the default terminal
1307d5dc6f2eSschwarze 		 * scaling unit so that output is the same regardless
1308d5dc6f2eSschwarze 		 * the media.
1309d5dc6f2eSschwarze 		 */
1310d5dc6f2eSschwarze 		r = PNT2AFM(p, su->scale * 72.0 / 240.0);
1311d5dc6f2eSschwarze 		break;
131249aff9f8Sschwarze 	case SCALE_CM:
1313d5dc6f2eSschwarze 		r = PNT2AFM(p, su->scale * 72.0 / 2.54);
1314769ee804Sschwarze 		break;
131549aff9f8Sschwarze 	case SCALE_EM:
1316769ee804Sschwarze 		r = su->scale *
1317769ee804Sschwarze 		    fonts[(int)TERMFONT_NONE].gly[109 - 32].wx;
1318769ee804Sschwarze 		break;
131949aff9f8Sschwarze 	case SCALE_EN:
1320769ee804Sschwarze 		r = su->scale *
1321769ee804Sschwarze 		    fonts[(int)TERMFONT_NONE].gly[110 - 32].wx;
1322769ee804Sschwarze 		break;
1323d5dc6f2eSschwarze 	case SCALE_IN:
1324d5dc6f2eSschwarze 		r = PNT2AFM(p, su->scale * 72.0);
1325d5dc6f2eSschwarze 		break;
1326d5dc6f2eSschwarze 	case SCALE_MM:
1327d5dc6f2eSschwarze 		r = su->scale *
1328d5dc6f2eSschwarze 		    fonts[(int)TERMFONT_NONE].gly[109 - 32].wx / 100.0;
1329d5dc6f2eSschwarze 		break;
1330d5dc6f2eSschwarze 	case SCALE_PC:
1331d5dc6f2eSschwarze 		r = PNT2AFM(p, su->scale * 12.0);
1332d5dc6f2eSschwarze 		break;
1333d5dc6f2eSschwarze 	case SCALE_PT:
1334d5dc6f2eSschwarze 		r = PNT2AFM(p, su->scale * 1.0);
1335d5dc6f2eSschwarze 		break;
133649aff9f8Sschwarze 	case SCALE_VS:
1337a5e11edeSschwarze 		r = su->scale * p->ps->lineheight;
1338769ee804Sschwarze 		break;
1339769ee804Sschwarze 	default:
1340769ee804Sschwarze 		r = su->scale;
1341769ee804Sschwarze 		break;
1342769ee804Sschwarze 	}
1343769ee804Sschwarze 
1344526e306bSschwarze 	return r * 24.0;
1345769ee804Sschwarze }
1346769ee804Sschwarze 
1347a5e11edeSschwarze static void
ps_growbuf(struct termp * p,size_t sz)1348a5e11edeSschwarze ps_growbuf(struct termp *p, size_t sz)
1349a5e11edeSschwarze {
1350a5e11edeSschwarze 	if (p->ps->psmargcur + sz <= p->ps->psmargsz)
1351a5e11edeSschwarze 		return;
1352a5e11edeSschwarze 
1353a5e11edeSschwarze 	if (sz < PS_BUFSLOP)
1354a5e11edeSschwarze 		sz = PS_BUFSLOP;
1355a5e11edeSschwarze 
1356a5e11edeSschwarze 	p->ps->psmargsz += sz;
13578286bf36Sschwarze 	p->ps->psmarg = mandoc_realloc(p->ps->psmarg, p->ps->psmargsz);
1358a5e11edeSschwarze }
1359