[BACK]Return to term_ps.c CVS log [TXT][DIR] Up to [cvsweb.bsd.lv] / mandoc

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