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

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