Annotation of mandoc/term_ps.c, Revision 1.4
1.4 ! kristaps 1: /* $Id: term_ps.c,v 1.3 2010/06/08 15:06:01 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);
! 128: }
! 129:
! 130:
! 131: static void
! 132: ps_putchar(struct termp *p, char c)
! 133: {
! 134: int pos;
! 135:
! 136: /* See ps_printf(). */
! 137:
! 138: if ( ! (PS_MARGINS & p->engine.ps.psstate)) {
! 139: putchar(c);
! 140: return;
! 141: }
! 142:
! 143: PS_GROWBUF(p, 2);
! 144:
! 145: pos = (int)p->engine.ps.psmargcur++;
! 146: p->engine.ps.psmarg[pos] = c;
! 147: p->engine.ps.psmarg[pos] = '\0';
1.2 kristaps 148: }
149:
150:
1.3 kristaps 151: /* ARGSUSED */
1.2 kristaps 152: static void
153: ps_end(struct termp *p)
154: {
155:
1.4 ! kristaps 156: /*
! 157: * At the end of the file, do one last showpage. This is the
! 158: * same behaviour as groff(1) and works for multiple pages as
! 159: * well as just one.
! 160: */
! 161:
! 162: assert(p->engine.ps.psmarg && p->engine.ps.psmarg[0]);
! 163: printf("%s", p->engine.ps.psmarg);
! 164: printf("showpage\n");
1.2 kristaps 165: printf("%s\n", "%%EOF");
166: }
167:
168:
169: static void
170: ps_begin(struct termp *p)
171: {
172:
173: /*
174: * Emit the standard PostScript prologue, set our initial page
175: * position, then run pageopen() on the initial page.
176: */
177:
178: printf("%s\n", "%!PS");
179: printf("%s\n", "/Courier");
180: printf("%s\n", "10 selectfont");
181:
182: p->engine.ps.psstate = 0;
1.4 ! kristaps 183:
! 184: if (p->engine.ps.psmarg) {
! 185: assert(p->engine.ps.psmargsz);
! 186: p->engine.ps.psmarg[0] = '\0';
! 187: }
! 188:
! 189: p->engine.ps.psmargcur = 0;
! 190:
! 191: /*
! 192: * Now dump the margins into our margin buffer. If we don't do
! 193: * this, we'd break any current state to run the header and
! 194: * footer with each and evern new page.
! 195: */
! 196:
! 197: p->engine.ps.pscol = PS_CHAR_LEFT;
! 198: p->engine.ps.psrow = PS_CHAR_TOPMARG;
! 199:
! 200: p->engine.ps.psstate |= PS_MARGINS;
! 201:
! 202: (*p->headf)(p, p->argf);
! 203: (*p->endline)(p);
! 204:
! 205: p->engine.ps.psstate &= ~PS_MARGINS;
! 206: assert(0 == p->engine.ps.psstate);
! 207:
! 208: p->engine.ps.pscol = PS_CHAR_LEFT;
! 209: p->engine.ps.psrow = PS_CHAR_BOTMARG;
! 210: p->engine.ps.psstate |= PS_MARGINS;
! 211:
! 212: (*p->footf)(p, p->argf);
! 213: (*p->endline)(p);
! 214:
! 215: p->engine.ps.psstate &= ~PS_MARGINS;
! 216: assert(0 == p->engine.ps.psstate);
! 217:
! 218: p->engine.ps.pscol = PS_CHAR_LEFT;
! 219: p->engine.ps.psrow = PS_CHAR_TOP;
! 220:
! 221: assert(p->engine.ps.psmarg);
! 222: assert('\0' != p->engine.ps.psmarg[0]);
1.2 kristaps 223: }
224:
225:
226: static void
227: ps_letter(struct termp *p, char c)
228: {
229:
230: if ( ! (PS_INLINE & p->engine.ps.psstate)) {
231: /*
232: * If we're not in a PostScript "word" context, then
233: * open one now at the current cursor.
234: */
1.4 ! kristaps 235: ps_printf(p, "%zu %zu moveto\n(",
1.2 kristaps 236: p->engine.ps.pscol,
237: p->engine.ps.psrow);
238: p->engine.ps.psstate |= PS_INLINE;
239: }
240:
241: /*
242: * We need to escape these characters as per the PostScript
243: * specification. We would also escape non-graphable characters
244: * (like tabs), but none of them would get to this point and
245: * it's superfluous to abort() on them.
246: */
247:
248: switch (c) {
249: case ('('):
250: /* FALLTHROUGH */
251: case (')'):
252: /* FALLTHROUGH */
253: case ('\\'):
1.4 ! kristaps 254: ps_putchar(p, '\\');
1.2 kristaps 255: break;
256: default:
257: break;
258: }
259:
260: /* Write the character and adjust where we are on the page. */
1.4 ! kristaps 261: ps_putchar(p, c);
1.2 kristaps 262: p->engine.ps.pscol += PS_CHAR_WIDTH;
263: }
264:
265:
266: static void
267: ps_advance(struct termp *p, size_t len)
268: {
269:
270: if (PS_INLINE & p->engine.ps.psstate) {
271: /* Dump out any existing line scope. */
1.4 ! kristaps 272: ps_printf(p, ") show\n");
1.2 kristaps 273: p->engine.ps.psstate &= ~PS_INLINE;
274: }
275:
276: p->engine.ps.pscol += len ? len * PS_CHAR_WIDTH : 0;
277: }
278:
279:
280: static void
281: ps_endline(struct termp *p)
282: {
283:
284: if (PS_INLINE & p->engine.ps.psstate) {
1.4 ! kristaps 285: ps_printf(p, ") show\n");
1.2 kristaps 286: p->engine.ps.psstate &= ~PS_INLINE;
287: }
288:
289: if (PS_MARGINS & p->engine.ps.psstate)
290: return;
291:
292: p->engine.ps.pscol = PS_CHAR_LEFT;
293: if (p->engine.ps.psrow >= PS_CHAR_HEIGHT + PS_CHAR_BOT) {
294: p->engine.ps.psrow -= PS_CHAR_HEIGHT;
295: return;
296: }
297:
1.4 ! kristaps 298: assert(p->engine.ps.psmarg && p->engine.ps.psmarg[0]);
! 299: printf("%s", p->engine.ps.psmarg);
1.2 kristaps 300: printf("showpage\n");
301: p->engine.ps.psrow = PS_CHAR_TOP;
1.1 kristaps 302: }
CVSweb