Annotation of mandoc/term_ps.c, Revision 1.5
1.5 ! kristaps 1: /* $Id: term_ps.c,v 1.4 2010/06/09 08:07:13 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.4 kristaps 61: static void ps_printf(struct termp *, const char *, ...);
62: static void ps_putchar(struct termp *, char);
1.2 kristaps 63:
64:
1.1 kristaps 65: void *
66: ps_alloc(void)
67: {
68: struct termp *p;
69:
70: if (NULL == (p = term_alloc(TERMENC_ASCII)))
71: return(NULL);
72:
73: p->type = TERMTYPE_PS;
1.2 kristaps 74: p->letter = ps_letter;
75: p->begin = ps_begin;
76: p->end = ps_end;
77: p->advance = ps_advance;
78: p->endline = ps_endline;
1.1 kristaps 79: return(p);
80: }
81:
82:
83: void
84: ps_free(void *arg)
85: {
1.4 kristaps 86: struct termp *p;
87:
88: p = (struct termp *)arg;
89:
90: if (p->engine.ps.psmarg)
91: free(p->engine.ps.psmarg);
92:
93: term_free(p);
94: }
95:
96:
97: static void
98: ps_printf(struct termp *p, const char *fmt, ...)
99: {
100: va_list ap;
101: int pos;
102:
103: va_start(ap, fmt);
104:
105: /*
106: * If we're running in regular mode, then pipe directly into
107: * vprintf(). If we're processing margins, then push the data
108: * into our growable margin buffer.
109: */
1.1 kristaps 110:
1.4 kristaps 111: if ( ! (PS_MARGINS & p->engine.ps.psstate)) {
112: vprintf(fmt, ap);
113: va_end(ap);
114: return;
115: }
116:
117: /*
118: * XXX: I assume that the in-margin print won't exceed
119: * PS_BUFSLOP (128 bytes), which is reasonable but still an
120: * assumption that will cause pukeage if it's not the case.
121: */
122:
123: PS_GROWBUF(p, PS_BUFSLOP);
124:
125: pos = (int)p->engine.ps.psmargcur;
126: vsnprintf(&p->engine.ps.psmarg[pos], PS_BUFSLOP, fmt, ap);
127: p->engine.ps.psmargcur = strlen(p->engine.ps.psmarg);
1.5 ! kristaps 128:
! 129: va_end(ap);
1.4 kristaps 130: }
131:
132:
133: static void
134: ps_putchar(struct termp *p, char c)
135: {
136: int pos;
137:
138: /* See ps_printf(). */
139:
140: if ( ! (PS_MARGINS & p->engine.ps.psstate)) {
141: putchar(c);
142: return;
143: }
144:
145: PS_GROWBUF(p, 2);
146:
147: pos = (int)p->engine.ps.psmargcur++;
1.5 ! kristaps 148: p->engine.ps.psmarg[pos++] = c;
1.4 kristaps 149: p->engine.ps.psmarg[pos] = '\0';
1.2 kristaps 150: }
151:
152:
1.3 kristaps 153: /* ARGSUSED */
1.2 kristaps 154: static void
155: ps_end(struct termp *p)
156: {
157:
1.4 kristaps 158: /*
159: * At the end of the file, do one last showpage. This is the
160: * same behaviour as groff(1) and works for multiple pages as
161: * well as just one.
162: */
163:
164: assert(p->engine.ps.psmarg && p->engine.ps.psmarg[0]);
165: printf("%s", p->engine.ps.psmarg);
166: printf("showpage\n");
1.2 kristaps 167: printf("%s\n", "%%EOF");
168: }
169:
170:
171: static void
172: ps_begin(struct termp *p)
173: {
174:
175: /*
176: * Emit the standard PostScript prologue, set our initial page
177: * position, then run pageopen() on the initial page.
178: */
179:
180: printf("%s\n", "%!PS");
181: printf("%s\n", "/Courier");
182: printf("%s\n", "10 selectfont");
183:
184: p->engine.ps.psstate = 0;
1.4 kristaps 185:
186: if (p->engine.ps.psmarg) {
187: assert(p->engine.ps.psmargsz);
188: p->engine.ps.psmarg[0] = '\0';
189: }
190:
191: p->engine.ps.psmargcur = 0;
192:
193: /*
194: * Now dump the margins into our margin buffer. If we don't do
195: * this, we'd break any current state to run the header and
196: * footer with each and evern new page.
197: */
198:
199: p->engine.ps.pscol = PS_CHAR_LEFT;
200: p->engine.ps.psrow = PS_CHAR_TOPMARG;
201:
202: p->engine.ps.psstate |= PS_MARGINS;
203:
204: (*p->headf)(p, p->argf);
205: (*p->endline)(p);
206:
207: p->engine.ps.psstate &= ~PS_MARGINS;
208: assert(0 == p->engine.ps.psstate);
209:
210: p->engine.ps.pscol = PS_CHAR_LEFT;
211: p->engine.ps.psrow = PS_CHAR_BOTMARG;
212: p->engine.ps.psstate |= PS_MARGINS;
213:
214: (*p->footf)(p, p->argf);
215: (*p->endline)(p);
216:
217: p->engine.ps.psstate &= ~PS_MARGINS;
218: assert(0 == p->engine.ps.psstate);
219:
220: p->engine.ps.pscol = PS_CHAR_LEFT;
221: p->engine.ps.psrow = PS_CHAR_TOP;
222:
223: assert(p->engine.ps.psmarg);
224: assert('\0' != p->engine.ps.psmarg[0]);
1.2 kristaps 225: }
226:
227:
228: static void
229: ps_letter(struct termp *p, char c)
230: {
231:
232: if ( ! (PS_INLINE & p->engine.ps.psstate)) {
233: /*
234: * If we're not in a PostScript "word" context, then
235: * open one now at the current cursor.
236: */
1.4 kristaps 237: ps_printf(p, "%zu %zu moveto\n(",
1.2 kristaps 238: p->engine.ps.pscol,
239: p->engine.ps.psrow);
240: p->engine.ps.psstate |= PS_INLINE;
241: }
242:
243: /*
244: * We need to escape these characters as per the PostScript
245: * specification. We would also escape non-graphable characters
246: * (like tabs), but none of them would get to this point and
247: * it's superfluous to abort() on them.
248: */
249:
250: switch (c) {
251: case ('('):
252: /* FALLTHROUGH */
253: case (')'):
254: /* FALLTHROUGH */
255: case ('\\'):
1.4 kristaps 256: ps_putchar(p, '\\');
1.2 kristaps 257: break;
258: default:
259: break;
260: }
261:
262: /* Write the character and adjust where we are on the page. */
1.4 kristaps 263: ps_putchar(p, c);
1.2 kristaps 264: p->engine.ps.pscol += PS_CHAR_WIDTH;
265: }
266:
267:
268: static void
269: ps_advance(struct termp *p, size_t len)
270: {
271:
272: if (PS_INLINE & p->engine.ps.psstate) {
273: /* Dump out any existing line scope. */
1.4 kristaps 274: ps_printf(p, ") show\n");
1.2 kristaps 275: p->engine.ps.psstate &= ~PS_INLINE;
276: }
277:
278: p->engine.ps.pscol += len ? len * PS_CHAR_WIDTH : 0;
279: }
280:
281:
282: static void
283: ps_endline(struct termp *p)
284: {
285:
286: if (PS_INLINE & p->engine.ps.psstate) {
1.4 kristaps 287: ps_printf(p, ") show\n");
1.2 kristaps 288: p->engine.ps.psstate &= ~PS_INLINE;
289: }
290:
291: if (PS_MARGINS & p->engine.ps.psstate)
292: return;
293:
294: p->engine.ps.pscol = PS_CHAR_LEFT;
295: if (p->engine.ps.psrow >= PS_CHAR_HEIGHT + PS_CHAR_BOT) {
296: p->engine.ps.psrow -= PS_CHAR_HEIGHT;
297: return;
298: }
299:
1.4 kristaps 300: assert(p->engine.ps.psmarg && p->engine.ps.psmarg[0]);
301: printf("%s", p->engine.ps.psmarg);
1.2 kristaps 302: printf("showpage\n");
303: p->engine.ps.psrow = PS_CHAR_TOP;
1.1 kristaps 304: }
CVSweb