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