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