Annotation of mandoc/term_ps.c, Revision 1.8
1.8 ! kristaps 1: /* $Id: term_ps.c,v 1.7 2010/06/11 07:23:04 kristaps Exp $ */
1.1 kristaps 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:
1.4 kristaps 21: #include <sys/param.h>
22:
1.2 kristaps 23: #include <assert.h>
1.4 kristaps 24: #include <stdarg.h>
1.2 kristaps 25: #include <stdio.h>
1.1 kristaps 26: #include <stdlib.h>
1.4 kristaps 27: #include <string.h>
1.1 kristaps 28:
29: #include "out.h"
30: #include "main.h"
31: #include "term.h"
32:
1.2 kristaps 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:
1.4 kristaps 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:
1.2 kristaps 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 *);
1.8 ! kristaps 61: static void ps_pletter(struct termp *, char);
1.4 kristaps 62: static void ps_printf(struct termp *, const char *, ...);
63: static void ps_putchar(struct termp *, char);
1.2 kristaps 64:
65:
1.1 kristaps 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;
1.2 kristaps 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;
1.1 kristaps 80: return(p);
81: }
82:
83:
84: void
85: ps_free(void *arg)
86: {
1.4 kristaps 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: */
1.1 kristaps 111:
1.4 kristaps 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);
1.5 kristaps 129:
130: va_end(ap);
1.4 kristaps 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++;
1.5 kristaps 149: p->engine.ps.psmarg[pos++] = c;
1.4 kristaps 150: p->engine.ps.psmarg[pos] = '\0';
1.2 kristaps 151: }
152:
153:
1.3 kristaps 154: /* ARGSUSED */
1.2 kristaps 155: static void
156: ps_end(struct termp *p)
157: {
158:
1.4 kristaps 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:
1.8 ! kristaps 165: assert(0 == p->engine.ps.psstate);
! 166: assert('\0' == p->engine.ps.last);
1.4 kristaps 167: assert(p->engine.ps.psmarg && p->engine.ps.psmarg[0]);
168: printf("%s", p->engine.ps.psmarg);
169: printf("showpage\n");
1.2 kristaps 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;
1.4 kristaps 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]);
1.2 kristaps 228: }
229:
230:
231: static void
1.8 ! kristaps 232: ps_pletter(struct termp *p, char c)
1.2 kristaps 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: */
1.4 kristaps 240: ps_printf(p, "%zu %zu moveto\n(",
1.2 kristaps 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 ('\\'):
1.4 kristaps 259: ps_putchar(p, '\\');
1.2 kristaps 260: break;
261: default:
262: break;
263: }
264:
265: /* Write the character and adjust where we are on the page. */
1.4 kristaps 266: ps_putchar(p, c);
1.2 kristaps 267: p->engine.ps.pscol += PS_CHAR_WIDTH;
268: }
269:
270:
271: static void
1.8 ! kristaps 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
1.2 kristaps 299: ps_advance(struct termp *p, size_t len)
300: {
1.6 kristaps 301: size_t i;
1.2 kristaps 302:
1.8 ! kristaps 303: if ('\0' != p->engine.ps.last) {
! 304: ps_pletter(p, p->engine.ps.last);
! 305: p->engine.ps.last = '\0';
! 306: }
! 307:
1.2 kristaps 308: if (PS_INLINE & p->engine.ps.psstate) {
1.7 kristaps 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';
1.6 kristaps 313: for (i = 0; i < len; i++)
314: ps_letter(p, ' ');
315: return;
1.2 kristaps 316: }
317:
1.7 kristaps 318: assert('\0' == p->engine.ps.last);
1.2 kristaps 319: p->engine.ps.pscol += len ? len * PS_CHAR_WIDTH : 0;
320: }
321:
322:
323: static void
324: ps_endline(struct termp *p)
325: {
1.8 ! kristaps 326:
! 327: if ('\0' != p->engine.ps.last) {
! 328: ps_pletter(p, p->engine.ps.last);
! 329: p->engine.ps.last = '\0';
! 330: }
1.2 kristaps 331:
332: if (PS_INLINE & p->engine.ps.psstate) {
1.7 kristaps 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';
1.4 kristaps 337: ps_printf(p, ") show\n");
1.2 kristaps 338: p->engine.ps.psstate &= ~PS_INLINE;
1.7 kristaps 339: } else
340: assert('\0' == p->engine.ps.last);
1.2 kristaps 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:
1.4 kristaps 351: assert(p->engine.ps.psmarg && p->engine.ps.psmarg[0]);
352: printf("%s", p->engine.ps.psmarg);
1.2 kristaps 353: printf("showpage\n");
354: p->engine.ps.psrow = PS_CHAR_TOP;
1.1 kristaps 355: }
CVSweb