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

Annotation of mandoc/term_ps.c, Revision 1.11

1.11    ! kristaps    1: /*     $Id: term_ps.c,v 1.10 2010/06/19 20:46:28 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:
        !           434: static size_t
        !           435: ps_width(const struct termp *p, char c)
        !           436: {
        !           437:
        !           438:        return(1);
        !           439: }

CVSweb