Annotation of mandoc/term_ps.c, Revision 1.9
1.9 ! kristaps 1: /* $Id: term_ps.c,v 1.8 2010/06/11 15:26:39 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.9 ! kristaps 56:
1.2 kristaps 57: static void ps_letter(struct termp *, char);
58: static void ps_begin(struct termp *);
59: static void ps_end(struct termp *);
60: static void ps_advance(struct termp *, size_t);
61: static void ps_endline(struct termp *);
1.9 ! kristaps 62: static void ps_fclose(struct termp *);
! 63: static void ps_pclose(struct termp *);
1.8 kristaps 64: static void ps_pletter(struct termp *, char);
1.4 kristaps 65: static void ps_printf(struct termp *, const char *, ...);
66: static void ps_putchar(struct termp *, char);
1.9 ! kristaps 67: static void ps_setfont(struct termp *, enum termfont);
1.2 kristaps 68:
69:
1.1 kristaps 70: void *
71: ps_alloc(void)
72: {
73: struct termp *p;
74:
75: if (NULL == (p = term_alloc(TERMENC_ASCII)))
76: return(NULL);
77:
78: p->type = TERMTYPE_PS;
1.2 kristaps 79: p->letter = ps_letter;
80: p->begin = ps_begin;
81: p->end = ps_end;
82: p->advance = ps_advance;
83: p->endline = ps_endline;
1.1 kristaps 84: return(p);
85: }
86:
87:
88: void
89: ps_free(void *arg)
90: {
1.4 kristaps 91: struct termp *p;
92:
93: p = (struct termp *)arg;
94:
95: if (p->engine.ps.psmarg)
96: free(p->engine.ps.psmarg);
97:
98: term_free(p);
99: }
100:
101:
102: static void
103: ps_printf(struct termp *p, const char *fmt, ...)
104: {
105: va_list ap;
106: int pos;
107:
108: va_start(ap, fmt);
109:
110: /*
111: * If we're running in regular mode, then pipe directly into
112: * vprintf(). If we're processing margins, then push the data
113: * into our growable margin buffer.
114: */
1.1 kristaps 115:
1.4 kristaps 116: if ( ! (PS_MARGINS & p->engine.ps.psstate)) {
117: vprintf(fmt, ap);
118: va_end(ap);
119: return;
120: }
121:
122: /*
123: * XXX: I assume that the in-margin print won't exceed
124: * PS_BUFSLOP (128 bytes), which is reasonable but still an
125: * assumption that will cause pukeage if it's not the case.
126: */
127:
128: PS_GROWBUF(p, PS_BUFSLOP);
129:
130: pos = (int)p->engine.ps.psmargcur;
131: vsnprintf(&p->engine.ps.psmarg[pos], PS_BUFSLOP, fmt, ap);
132: p->engine.ps.psmargcur = strlen(p->engine.ps.psmarg);
1.5 kristaps 133:
134: va_end(ap);
1.4 kristaps 135: }
136:
137:
138: static void
139: ps_putchar(struct termp *p, char c)
140: {
141: int pos;
142:
143: /* See ps_printf(). */
144:
145: if ( ! (PS_MARGINS & p->engine.ps.psstate)) {
146: putchar(c);
147: return;
148: }
149:
150: PS_GROWBUF(p, 2);
151:
152: pos = (int)p->engine.ps.psmargcur++;
1.5 kristaps 153: p->engine.ps.psmarg[pos++] = c;
1.4 kristaps 154: p->engine.ps.psmarg[pos] = '\0';
1.2 kristaps 155: }
156:
157:
1.3 kristaps 158: /* ARGSUSED */
1.2 kristaps 159: static void
160: ps_end(struct termp *p)
161: {
162:
1.4 kristaps 163: /*
164: * At the end of the file, do one last showpage. This is the
165: * same behaviour as groff(1) and works for multiple pages as
166: * well as just one.
167: */
168:
1.8 kristaps 169: assert(0 == p->engine.ps.psstate);
170: assert('\0' == p->engine.ps.last);
1.4 kristaps 171: assert(p->engine.ps.psmarg && p->engine.ps.psmarg[0]);
172: printf("%s", p->engine.ps.psmarg);
173: printf("showpage\n");
1.2 kristaps 174: printf("%s\n", "%%EOF");
175: }
176:
177:
178: static void
179: ps_begin(struct termp *p)
180: {
181:
1.9 ! kristaps 182: /*
! 183: * Print margins into margin buffer. Nothing gets output to the
! 184: * screen yet, so we don't need to initialise the primary state.
1.2 kristaps 185: */
186:
1.4 kristaps 187: if (p->engine.ps.psmarg) {
188: assert(p->engine.ps.psmargsz);
189: p->engine.ps.psmarg[0] = '\0';
190: }
191:
192: p->engine.ps.psmargcur = 0;
1.9 ! kristaps 193: p->engine.ps.psstate = PS_MARGINS;
1.4 kristaps 194: p->engine.ps.pscol = PS_CHAR_LEFT;
195: p->engine.ps.psrow = PS_CHAR_TOPMARG;
196:
1.9 ! kristaps 197: ps_setfont(p, TERMFONT_NONE);
1.4 kristaps 198:
199: (*p->headf)(p, p->argf);
200: (*p->endline)(p);
201:
202: p->engine.ps.pscol = PS_CHAR_LEFT;
203: p->engine.ps.psrow = PS_CHAR_BOTMARG;
204:
205: (*p->footf)(p, p->argf);
206: (*p->endline)(p);
207:
208: p->engine.ps.psstate &= ~PS_MARGINS;
1.9 ! kristaps 209:
1.4 kristaps 210: assert(0 == p->engine.ps.psstate);
1.9 ! kristaps 211: assert(p->engine.ps.psmarg);
! 212: assert('\0' != p->engine.ps.psmarg[0]);
! 213:
! 214: /*
! 215: * Print header and initialise page state. Following this,
! 216: * stuff gets printed to the screen, so make sure we're sane.
! 217: */
1.4 kristaps 218:
1.9 ! kristaps 219: printf("%s\n", "%!PS");
! 220: ps_setfont(p, TERMFONT_NONE);
1.4 kristaps 221: p->engine.ps.pscol = PS_CHAR_LEFT;
222: p->engine.ps.psrow = PS_CHAR_TOP;
1.2 kristaps 223: }
224:
225:
226: static void
1.8 kristaps 227: ps_pletter(struct termp *p, char c)
1.2 kristaps 228: {
229:
1.9 ! kristaps 230: /*
! 231: * If we're not in a PostScript "word" context, then open one
! 232: * now at the current cursor.
! 233: */
! 234:
1.2 kristaps 235: if ( ! (PS_INLINE & p->engine.ps.psstate)) {
1.4 kristaps 236: ps_printf(p, "%zu %zu moveto\n(",
1.2 kristaps 237: p->engine.ps.pscol,
238: p->engine.ps.psrow);
239: p->engine.ps.psstate |= PS_INLINE;
240: }
241:
242: /*
243: * We need to escape these characters as per the PostScript
244: * specification. We would also escape non-graphable characters
245: * (like tabs), but none of them would get to this point and
246: * it's superfluous to abort() on them.
247: */
248:
249: switch (c) {
250: case ('('):
251: /* FALLTHROUGH */
252: case (')'):
253: /* FALLTHROUGH */
254: case ('\\'):
1.4 kristaps 255: ps_putchar(p, '\\');
1.2 kristaps 256: break;
257: default:
258: break;
259: }
260:
261: /* Write the character and adjust where we are on the page. */
1.9 ! kristaps 262:
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
1.9 ! kristaps 269: ps_pclose(struct termp *p)
! 270: {
! 271:
! 272: /*
! 273: * Spit out that we're exiting a word context (this is a
! 274: * "partial close" because we don't check the last-char buffer
! 275: * or anything).
! 276: */
! 277:
! 278: if ( ! (PS_INLINE & p->engine.ps.psstate))
! 279: return;
! 280:
! 281: ps_printf(p, ") show\n");
! 282: p->engine.ps.psstate &= ~PS_INLINE;
! 283: }
! 284:
! 285:
! 286: static void
! 287: ps_fclose(struct termp *p)
! 288: {
! 289:
! 290: /*
! 291: * Strong closure: if we have a last-char, spit it out after
! 292: * checking that we're in the right font mode. This will of
! 293: * course open a new scope, if applicable.
! 294: *
! 295: * Following this, close out any scope that's open.
! 296: */
! 297:
! 298: if ('\0' != p->engine.ps.last) {
! 299: if (p->engine.ps.lastf != TERMFONT_NONE) {
! 300: ps_pclose(p);
! 301: ps_setfont(p, TERMFONT_NONE);
! 302: }
! 303: ps_pletter(p, p->engine.ps.last);
! 304: p->engine.ps.last = '\0';
! 305: }
! 306:
! 307: if ( ! (PS_INLINE & p->engine.ps.psstate))
! 308: return;
! 309:
! 310: ps_pclose(p);
! 311: }
! 312:
! 313:
! 314: static void
1.8 kristaps 315: ps_letter(struct termp *p, char c)
316: {
317: char cc;
1.9 ! kristaps 318:
! 319: /*
! 320: * State machine dictates whether to buffer the last character
! 321: * or not. Basically, encoded words are detected by checking if
! 322: * we're an "8" and switching on the buffer. Then we put "8" in
! 323: * our buffer, and on the next charater, flush both character
! 324: * and buffer. Thus, "regular" words are detected by having a
! 325: * regular character and a regular buffer character.
! 326: */
! 327:
1.8 kristaps 328: if ('\0' == p->engine.ps.last) {
329: assert(8 != c);
330: p->engine.ps.last = c;
331: return;
332: } else if (8 == p->engine.ps.last) {
333: assert(8 != c);
1.9 ! kristaps 334: p->engine.ps.last = '\0';
1.8 kristaps 335: } else if (8 == c) {
336: assert(8 != p->engine.ps.last);
1.9 ! kristaps 337: if ('_' == p->engine.ps.last) {
! 338: if (p->engine.ps.lastf != TERMFONT_UNDER) {
! 339: ps_pclose(p);
! 340: ps_setfont(p, TERMFONT_UNDER);
! 341: }
! 342: } else if (p->engine.ps.lastf != TERMFONT_BOLD) {
! 343: ps_pclose(p);
! 344: ps_setfont(p, TERMFONT_BOLD);
! 345: }
1.8 kristaps 346: p->engine.ps.last = c;
347: return;
348: } else {
1.9 ! kristaps 349: if (p->engine.ps.lastf != TERMFONT_NONE) {
! 350: ps_pclose(p);
! 351: ps_setfont(p, TERMFONT_NONE);
! 352: }
1.8 kristaps 353: cc = p->engine.ps.last;
354: p->engine.ps.last = c;
355: c = cc;
356: }
357:
1.9 ! kristaps 358: ps_pletter(p, c);
1.8 kristaps 359: }
360:
361:
362: static void
1.2 kristaps 363: ps_advance(struct termp *p, size_t len)
364: {
365:
1.9 ! kristaps 366: /*
! 367: * Advance some spaces. This can probably be made smarter,
! 368: * i.e., to have multiple space-separated words in the same
! 369: * scope, but this is easier: just close out the current scope
! 370: * and readjust our column settings.
! 371: */
1.8 kristaps 372:
1.9 ! kristaps 373: ps_fclose(p);
1.2 kristaps 374: p->engine.ps.pscol += len ? len * PS_CHAR_WIDTH : 0;
375: }
376:
377:
378: static void
379: ps_endline(struct termp *p)
380: {
1.8 kristaps 381:
1.9 ! kristaps 382: /* Close out any scopes we have open: we're at eoln. */
! 383:
! 384: ps_fclose(p);
1.2 kristaps 385:
1.9 ! kristaps 386: /*
! 387: * If we're in the margin, don't try to recalculate our current
! 388: * row. XXX: if the column tries to be fancy with multiple
! 389: * lines, we'll do nasty stuff.
! 390: */
1.2 kristaps 391:
392: if (PS_MARGINS & p->engine.ps.psstate)
393: return;
394:
1.9 ! kristaps 395: /*
! 396: * Put us down a line. If we're at the page bottom, spit out a
! 397: * showpage and restart our row.
! 398: */
! 399:
1.2 kristaps 400: p->engine.ps.pscol = PS_CHAR_LEFT;
401: if (p->engine.ps.psrow >= PS_CHAR_HEIGHT + PS_CHAR_BOT) {
402: p->engine.ps.psrow -= PS_CHAR_HEIGHT;
403: return;
404: }
405:
1.4 kristaps 406: assert(p->engine.ps.psmarg && p->engine.ps.psmarg[0]);
407: printf("%s", p->engine.ps.psmarg);
1.2 kristaps 408: printf("showpage\n");
409: p->engine.ps.psrow = PS_CHAR_TOP;
1.1 kristaps 410: }
1.9 ! kristaps 411:
! 412:
! 413: static void
! 414: ps_setfont(struct termp *p, enum termfont f)
! 415: {
! 416:
! 417: if (TERMFONT_BOLD == f)
! 418: ps_printf(p, "/Courier-Bold\n");
! 419: else if (TERMFONT_UNDER == f)
! 420: ps_printf(p, "/Courier-Oblique\n");
! 421: else
! 422: ps_printf(p, "/Courier\n");
! 423:
! 424: ps_printf(p, "10 selectfont\n");
! 425: p->engine.ps.lastf = f;
! 426: }
! 427:
CVSweb