]> git.cameronkatri.com Git - mandoc.git/blob - term_ps.c
Delay opening a word state until a character is ready to be output.
[mandoc.git] / term_ps.c
1 /* $Id: term_ps.c,v 1.8 2010/06/11 15:26:39 kristaps Exp $ */
2 /*
3 * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20
21 #include <sys/param.h>
22
23 #include <assert.h>
24 #include <stdarg.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include "out.h"
30 #include "main.h"
31 #include "term.h"
32
33 #define PS_CHAR_WIDTH 6
34 #define PS_CHAR_HEIGHT 12
35 #define PS_CHAR_TOPMARG (792 - 24)
36 #define PS_CHAR_TOP (PS_CHAR_TOPMARG - 36)
37 #define PS_CHAR_LEFT 36
38 #define PS_CHAR_BOTMARG 24
39 #define PS_CHAR_BOT (PS_CHAR_BOTMARG + 36)
40
41 #define PS_BUFSLOP 128
42 #define PS_GROWBUF(p, sz) \
43 do if ((p)->engine.ps.psmargcur + (sz) > \
44 (p)->engine.ps.psmargsz) { \
45 (p)->engine.ps.psmargsz += /* CONSTCOND */ \
46 MAX(PS_BUFSLOP, (sz)); \
47 (p)->engine.ps.psmarg = realloc \
48 ((p)->engine.ps.psmarg, \
49 (p)->engine.ps.psmargsz); \
50 if (NULL == (p)->engine.ps.psmarg) { \
51 perror(NULL); \
52 exit(EXIT_FAILURE); \
53 } \
54 } while (/* CONSTCOND */ 0)
55
56 static void ps_letter(struct termp *, char);
57 static void ps_begin(struct termp *);
58 static void ps_end(struct termp *);
59 static void ps_advance(struct termp *, size_t);
60 static void ps_endline(struct termp *);
61 static void ps_pletter(struct termp *, char);
62 static void ps_printf(struct termp *, const char *, ...);
63 static void ps_putchar(struct termp *, char);
64
65
66 void *
67 ps_alloc(void)
68 {
69 struct termp *p;
70
71 if (NULL == (p = term_alloc(TERMENC_ASCII)))
72 return(NULL);
73
74 p->type = TERMTYPE_PS;
75 p->letter = ps_letter;
76 p->begin = ps_begin;
77 p->end = ps_end;
78 p->advance = ps_advance;
79 p->endline = ps_endline;
80 return(p);
81 }
82
83
84 void
85 ps_free(void *arg)
86 {
87 struct termp *p;
88
89 p = (struct termp *)arg;
90
91 if (p->engine.ps.psmarg)
92 free(p->engine.ps.psmarg);
93
94 term_free(p);
95 }
96
97
98 static void
99 ps_printf(struct termp *p, const char *fmt, ...)
100 {
101 va_list ap;
102 int pos;
103
104 va_start(ap, fmt);
105
106 /*
107 * If we're running in regular mode, then pipe directly into
108 * vprintf(). If we're processing margins, then push the data
109 * into our growable margin buffer.
110 */
111
112 if ( ! (PS_MARGINS & p->engine.ps.psstate)) {
113 vprintf(fmt, ap);
114 va_end(ap);
115 return;
116 }
117
118 /*
119 * XXX: I assume that the in-margin print won't exceed
120 * PS_BUFSLOP (128 bytes), which is reasonable but still an
121 * assumption that will cause pukeage if it's not the case.
122 */
123
124 PS_GROWBUF(p, PS_BUFSLOP);
125
126 pos = (int)p->engine.ps.psmargcur;
127 vsnprintf(&p->engine.ps.psmarg[pos], PS_BUFSLOP, fmt, ap);
128 p->engine.ps.psmargcur = strlen(p->engine.ps.psmarg);
129
130 va_end(ap);
131 }
132
133
134 static void
135 ps_putchar(struct termp *p, char c)
136 {
137 int pos;
138
139 /* See ps_printf(). */
140
141 if ( ! (PS_MARGINS & p->engine.ps.psstate)) {
142 putchar(c);
143 return;
144 }
145
146 PS_GROWBUF(p, 2);
147
148 pos = (int)p->engine.ps.psmargcur++;
149 p->engine.ps.psmarg[pos++] = c;
150 p->engine.ps.psmarg[pos] = '\0';
151 }
152
153
154 /* ARGSUSED */
155 static void
156 ps_end(struct termp *p)
157 {
158
159 /*
160 * At the end of the file, do one last showpage. This is the
161 * same behaviour as groff(1) and works for multiple pages as
162 * well as just one.
163 */
164
165 assert(0 == p->engine.ps.psstate);
166 assert('\0' == p->engine.ps.last);
167 assert(p->engine.ps.psmarg && p->engine.ps.psmarg[0]);
168 printf("%s", p->engine.ps.psmarg);
169 printf("showpage\n");
170 printf("%s\n", "%%EOF");
171 }
172
173
174 static void
175 ps_begin(struct termp *p)
176 {
177
178 /*
179 * Emit the standard PostScript prologue, set our initial page
180 * position, then run pageopen() on the initial page.
181 */
182
183 printf("%s\n", "%!PS");
184 printf("%s\n", "/Courier");
185 printf("%s\n", "10 selectfont");
186
187 p->engine.ps.psstate = 0;
188
189 if (p->engine.ps.psmarg) {
190 assert(p->engine.ps.psmargsz);
191 p->engine.ps.psmarg[0] = '\0';
192 }
193
194 p->engine.ps.psmargcur = 0;
195
196 /*
197 * Now dump the margins into our margin buffer. If we don't do
198 * this, we'd break any current state to run the header and
199 * footer with each and evern new page.
200 */
201
202 p->engine.ps.pscol = PS_CHAR_LEFT;
203 p->engine.ps.psrow = PS_CHAR_TOPMARG;
204
205 p->engine.ps.psstate |= PS_MARGINS;
206
207 (*p->headf)(p, p->argf);
208 (*p->endline)(p);
209
210 p->engine.ps.psstate &= ~PS_MARGINS;
211 assert(0 == p->engine.ps.psstate);
212
213 p->engine.ps.pscol = PS_CHAR_LEFT;
214 p->engine.ps.psrow = PS_CHAR_BOTMARG;
215 p->engine.ps.psstate |= PS_MARGINS;
216
217 (*p->footf)(p, p->argf);
218 (*p->endline)(p);
219
220 p->engine.ps.psstate &= ~PS_MARGINS;
221 assert(0 == p->engine.ps.psstate);
222
223 p->engine.ps.pscol = PS_CHAR_LEFT;
224 p->engine.ps.psrow = PS_CHAR_TOP;
225
226 assert(p->engine.ps.psmarg);
227 assert('\0' != p->engine.ps.psmarg[0]);
228 }
229
230
231 static void
232 ps_pletter(struct termp *p, char c)
233 {
234
235 if ( ! (PS_INLINE & p->engine.ps.psstate)) {
236 /*
237 * If we're not in a PostScript "word" context, then
238 * open one now at the current cursor.
239 */
240 ps_printf(p, "%zu %zu moveto\n(",
241 p->engine.ps.pscol,
242 p->engine.ps.psrow);
243 p->engine.ps.psstate |= PS_INLINE;
244 }
245
246 /*
247 * We need to escape these characters as per the PostScript
248 * specification. We would also escape non-graphable characters
249 * (like tabs), but none of them would get to this point and
250 * it's superfluous to abort() on them.
251 */
252
253 switch (c) {
254 case ('('):
255 /* FALLTHROUGH */
256 case (')'):
257 /* FALLTHROUGH */
258 case ('\\'):
259 ps_putchar(p, '\\');
260 break;
261 default:
262 break;
263 }
264
265 /* Write the character and adjust where we are on the page. */
266 ps_putchar(p, c);
267 p->engine.ps.pscol += PS_CHAR_WIDTH;
268 }
269
270
271 static void
272 ps_letter(struct termp *p, char c)
273 {
274 char cc;
275
276 if ('\0' == p->engine.ps.last) {
277 assert(8 != c);
278 p->engine.ps.last = c;
279 return;
280 } else if (8 == p->engine.ps.last) {
281 assert(8 != c);
282 p->engine.ps.last = c;
283 return;
284 } else if (8 == c) {
285 assert(8 != p->engine.ps.last);
286 p->engine.ps.last = c;
287 return;
288 } else {
289 cc = p->engine.ps.last;
290 p->engine.ps.last = c;
291 c = cc;
292 }
293
294 return(ps_pletter(p, c));
295 }
296
297
298 static void
299 ps_advance(struct termp *p, size_t len)
300 {
301 size_t i;
302
303 if ('\0' != p->engine.ps.last) {
304 ps_pletter(p, p->engine.ps.last);
305 p->engine.ps.last = '\0';
306 }
307
308 if (PS_INLINE & p->engine.ps.psstate) {
309 assert(8 != p->engine.ps.last);
310 if (p->engine.ps.last)
311 ps_letter(p, p->engine.ps.last);
312 p->engine.ps.last = '\0';
313 for (i = 0; i < len; i++)
314 ps_letter(p, ' ');
315 return;
316 }
317
318 assert('\0' == p->engine.ps.last);
319 p->engine.ps.pscol += len ? len * PS_CHAR_WIDTH : 0;
320 }
321
322
323 static void
324 ps_endline(struct termp *p)
325 {
326
327 if ('\0' != p->engine.ps.last) {
328 ps_pletter(p, p->engine.ps.last);
329 p->engine.ps.last = '\0';
330 }
331
332 if (PS_INLINE & p->engine.ps.psstate) {
333 assert(8 != p->engine.ps.last);
334 if (p->engine.ps.last)
335 ps_letter(p, p->engine.ps.last);
336 p->engine.ps.last = '\0';
337 ps_printf(p, ") show\n");
338 p->engine.ps.psstate &= ~PS_INLINE;
339 } else
340 assert('\0' == p->engine.ps.last);
341
342 if (PS_MARGINS & p->engine.ps.psstate)
343 return;
344
345 p->engine.ps.pscol = PS_CHAR_LEFT;
346 if (p->engine.ps.psrow >= PS_CHAR_HEIGHT + PS_CHAR_BOT) {
347 p->engine.ps.psrow -= PS_CHAR_HEIGHT;
348 return;
349 }
350
351 assert(p->engine.ps.psmarg && p->engine.ps.psmarg[0]);
352 printf("%s", p->engine.ps.psmarg);
353 printf("showpage\n");
354 p->engine.ps.psrow = PS_CHAR_TOP;
355 }