=================================================================== RCS file: /cvs/mandoc/term_ps.c,v retrieving revision 1.11 retrieving revision 1.25 diff -u -p -r1.11 -r1.25 --- mandoc/term_ps.c 2010/06/25 18:53:14 1.11 +++ mandoc/term_ps.c 2010/07/01 14:23:45 1.25 @@ -1,4 +1,4 @@ -/* $Id: term_ps.c,v 1.11 2010/06/25 18:53:14 kristaps Exp $ */ +/* $Id: term_ps.c,v 1.25 2010/07/01 14:23:45 kristaps Exp $ */ /* * Copyright (c) 2008, 2009 Kristaps Dzonsons * @@ -22,22 +22,335 @@ #include #include +#include #include #include #include +#include #include "out.h" #include "main.h" #include "term.h" -#define PS_CHAR_WIDTH 6 -#define PS_CHAR_HEIGHT 12 -#define PS_CHAR_TOPMARG (792 - 24) -#define PS_CHAR_TOP (PS_CHAR_TOPMARG - 36) -#define PS_CHAR_LEFT 36 -#define PS_CHAR_BOTMARG 24 -#define PS_CHAR_BOT (PS_CHAR_BOTMARG + 36) +/* Convert PostScript point "x" to an AFM unit. */ +#define PNT2AFM(p, x) /* LINTED */ \ + (size_t)((double)(x) * (1000.0 / (double)(p)->engine.ps.scale)) +/* Convert an AFM unit "x" to a PostScript points */ +#define AFM2PNT(p, x) /* LINTED */ \ + (size_t)((double)(x) / (1000.0 / (double)(p)->engine.ps.scale)) + +struct glyph { + size_t wx; /* WX in AFM */ +}; + +struct font { + const char *name; /* FontName in AFM */ +#define MAXCHAR 95 /* total characters we can handle */ + struct glyph gly[MAXCHAR]; /* glyph metrics */ +}; + +/* + * We define, for the time being, three fonts: bold, oblique/italic, and + * normal (roman). The following table hard-codes the font metrics for + * ASCII, i.e., 32--127. + */ + +static const struct font fonts[TERMFONT__MAX] = { + { "Times-Roman", { + { 250 }, + { 333 }, + { 408 }, + { 500 }, + { 500 }, + { 833 }, + { 778 }, + { 333 }, + { 333 }, + { 333 }, + { 500 }, + { 564 }, + { 250 }, + { 333 }, + { 250 }, + { 278 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 278 }, + { 278 }, + { 564 }, + { 564 }, + { 564 }, + { 444 }, + { 921 }, + { 722 }, + { 667 }, + { 667 }, + { 722 }, + { 611 }, + { 556 }, + { 722 }, + { 722 }, + { 333 }, + { 389 }, + { 722 }, + { 611 }, + { 889 }, + { 722 }, + { 722 }, + { 556 }, + { 722 }, + { 667 }, + { 556 }, + { 611 }, + { 722 }, + { 722 }, + { 944 }, + { 722 }, + { 722 }, + { 611 }, + { 333 }, + { 278 }, + { 333 }, + { 469 }, + { 500 }, + { 333 }, + { 444 }, + { 500 }, + { 444 }, + { 500}, + { 444}, + { 333}, + { 500}, + { 500}, + { 278}, + { 278}, + { 500}, + { 278}, + { 778}, + { 500}, + { 500}, + { 500}, + { 500}, + { 333}, + { 389}, + { 278}, + { 500}, + { 500}, + { 722}, + { 500}, + { 500}, + { 444}, + { 480}, + { 200}, + { 480}, + { 541}, + } }, + { "Times-Bold", { + { 250 }, + { 333 }, + { 555 }, + { 500 }, + { 500 }, + { 1000 }, + { 833 }, + { 333 }, + { 333 }, + { 333 }, + { 500 }, + { 570 }, + { 250 }, + { 333 }, + { 250 }, + { 278 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 333 }, + { 333 }, + { 570 }, + { 570 }, + { 570 }, + { 500 }, + { 930 }, + { 722 }, + { 667 }, + { 722 }, + { 722 }, + { 667 }, + { 611 }, + { 778 }, + { 778 }, + { 389 }, + { 500 }, + { 778 }, + { 667 }, + { 944 }, + { 722 }, + { 778 }, + { 611 }, + { 778 }, + { 722 }, + { 556 }, + { 667 }, + { 722 }, + { 722 }, + { 1000 }, + { 722 }, + { 722 }, + { 667 }, + { 333 }, + { 278 }, + { 333 }, + { 581 }, + { 500 }, + { 333 }, + { 500 }, + { 556 }, + { 444 }, + { 556 }, + { 444 }, + { 333 }, + { 500 }, + { 556 }, + { 278 }, + { 333 }, + { 556 }, + { 278 }, + { 833 }, + { 556 }, + { 500 }, + { 556 }, + { 556 }, + { 444 }, + { 389 }, + { 333 }, + { 556 }, + { 500 }, + { 722 }, + { 500 }, + { 500 }, + { 444 }, + { 394 }, + { 220 }, + { 394 }, + { 520 }, + } }, + { "Times-Italic", { + { 250 }, + { 333 }, + { 420 }, + { 500 }, + { 500 }, + { 833 }, + { 778 }, + { 333 }, + { 333 }, + { 333 }, + { 500 }, + { 675 }, + { 250 }, + { 333 }, + { 250 }, + { 278 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 333 }, + { 333 }, + { 675 }, + { 675 }, + { 675 }, + { 500 }, + { 920 }, + { 611 }, + { 611 }, + { 667 }, + { 722 }, + { 611 }, + { 611 }, + { 722 }, + { 722 }, + { 333 }, + { 444 }, + { 667 }, + { 556 }, + { 833 }, + { 667 }, + { 722 }, + { 611 }, + { 722 }, + { 611 }, + { 500 }, + { 556 }, + { 722 }, + { 611 }, + { 833 }, + { 611 }, + { 556 }, + { 556 }, + { 389 }, + { 278 }, + { 389 }, + { 422 }, + { 500 }, + { 333 }, + { 500 }, + { 500 }, + { 444 }, + { 500 }, + { 444 }, + { 278 }, + { 500 }, + { 500 }, + { 278 }, + { 278 }, + { 444 }, + { 278 }, + { 722 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 389 }, + { 389 }, + { 278 }, + { 500 }, + { 444 }, + { 667 }, + { 444 }, + { 444 }, + { 389 }, + { 400 }, + { 275 }, + { 400 }, + { 541 }, + } }, +}; + +/* These work the buffer used by the header and footer. */ #define PS_BUFSLOP 128 #define PS_GROWBUF(p, sz) \ do if ((p)->engine.ps.psmargcur + (sz) > \ @@ -54,38 +367,109 @@ } while (/* CONSTCOND */ 0) -static void ps_letter(struct termp *, char); +static double ps_hspan(const struct termp *, + const struct roffsu *); +static size_t ps_width(const struct termp *, char); +static void ps_advance(struct termp *, size_t); static void ps_begin(struct termp *); static void ps_end(struct termp *); -static void ps_advance(struct termp *, size_t); static void ps_endline(struct termp *); static void ps_fclose(struct termp *); -static size_t ps_width(const struct termp *, char); +static void ps_letter(struct termp *, char); static void ps_pclose(struct termp *); -static void ps_pletter(struct termp *, char); +static void ps_pletter(struct termp *, int); static void ps_printf(struct termp *, const char *, ...); static void ps_putchar(struct termp *, char); static void ps_setfont(struct termp *, enum termfont); void * -ps_alloc(void) +ps_alloc(char *outopts) { struct termp *p; + size_t pagex, pagey, margin, lineheight; + const char *toks[2]; + const char *pp; + char *v; if (NULL == (p = term_alloc(TERMENC_ASCII))) return(NULL); - p->defrmargin = 78; - p->tabwidth = 5; - - p->type = TERMTYPE_PS; - p->letter = ps_letter; + p->advance = ps_advance; p->begin = ps_begin; p->end = ps_end; - p->advance = ps_advance; p->endline = ps_endline; + p->hspan = ps_hspan; + p->letter = ps_letter; + p->type = TERMTYPE_PS; p->width = ps_width; + + p->engine.ps.scale = 11; + + toks[0] = "paper"; + toks[1] = NULL; + + pp = NULL; + + while (outopts && *outopts) + switch (getsubopt(&outopts, UNCONST(toks), &v)) { + case (0): + pp = v; + break; + default: + break; + } + + margin = PNT2AFM(p, 72); + lineheight = PNT2AFM(p, 12); + + /* Default to US letter (millimetres). */ + + pagex = 216; + pagey = 279; + + /* + * The ISO-269 paper sizes can be calculated automatically, but + * it would require bringing in -lm for pow() and I'd rather not + * do that. So just do it the easy way for now. Since this + * only happens once, I'm not terribly concerned. + */ + + if (pp && strcasecmp(pp, "letter")) { + if (0 == strcasecmp(pp, "a3")) { + pagex = 297; + pagey = 420; + } else if (0 == strcasecmp(pp, "a4")) { + pagex = 210; + pagey = 297; + } else if (0 == strcasecmp(pp, "a5")) { + pagex = 148; + pagey = 210; + } else if (0 == strcasecmp(pp, "legal")) { + pagex = 216; + pagey = 356; + } else if (2 != sscanf(pp, "%zux%zu", &pagex, &pagey)) + fprintf(stderr, "%s: Unknown paper\n", pp); + } + + /* Remember millimetres -> AFM units. */ + + pagex = PNT2AFM(p, ((double)pagex * 2.834)); + pagey = PNT2AFM(p, ((double)pagey * 2.834)); + + assert(margin * 2 < pagex); + assert(margin * 2 < pagey); + + p->engine.ps.width = pagex; + p->engine.ps.height = pagey; + p->engine.ps.header = pagey - (margin / 2); + p->engine.ps.top = pagey - margin; + p->engine.ps.footer = (margin / 2); + p->engine.ps.bottom = margin; + p->engine.ps.left = margin; + p->engine.ps.lineheight = lineheight; + + p->defrmargin = pagex - (margin * 2); return(p); } @@ -175,14 +559,20 @@ ps_end(struct termp *p) assert('\0' == p->engine.ps.last); assert(p->engine.ps.psmarg && p->engine.ps.psmarg[0]); printf("%s", p->engine.ps.psmarg); + p->engine.ps.pages++; printf("showpage\n"); - printf("%s\n", "%%EOF"); + + printf("%%%%Trailer\n"); + printf("%%%%Pages: %zu\n", p->engine.ps.pages); + printf("%%%%EOF\n"); } static void ps_begin(struct termp *p) { + time_t t; + int i; /* * Print margins into margin buffer. Nothing gets output to the @@ -196,16 +586,16 @@ ps_begin(struct termp *p) p->engine.ps.psmargcur = 0; p->engine.ps.psstate = PS_MARGINS; - p->engine.ps.pscol = PS_CHAR_LEFT; - p->engine.ps.psrow = PS_CHAR_TOPMARG; + p->engine.ps.pscol = p->engine.ps.left; + p->engine.ps.psrow = p->engine.ps.header; ps_setfont(p, TERMFONT_NONE); (*p->headf)(p, p->argf); (*p->endline)(p); - p->engine.ps.pscol = PS_CHAR_LEFT; - p->engine.ps.psrow = PS_CHAR_BOTMARG; + p->engine.ps.pscol = p->engine.ps.left; + p->engine.ps.psrow = p->engine.ps.footer; (*p->footf)(p, p->argf); (*p->endline)(p); @@ -221,16 +611,37 @@ ps_begin(struct termp *p) * stuff gets printed to the screen, so make sure we're sane. */ - printf("%s\n", "%!PS"); + t = time(NULL); + + printf("%%!PS-Adobe-3.0\n"); + printf("%%%%Creator: mandoc-%s\n", VERSION); + printf("%%%%CreationDate: %s", ctime(&t)); + printf("%%%%DocumentData: Clean7Bit\n"); + printf("%%%%Orientation: Portrait\n"); + printf("%%%%Pages: (atend)\n"); + printf("%%%%PageOrder: Ascend\n"); + printf("%%%%DocumentMedia: Default %zu %zu 0 () ()\n", + AFM2PNT(p, p->engine.ps.width), + AFM2PNT(p, p->engine.ps.height)); + printf("%%%%DocumentNeededResources: font"); + for (i = 0; i < (int)TERMFONT__MAX; i++) + printf(" %s", fonts[i].name); + printf("\n%%%%EndComments\n"); + + printf("%%%%Page: %zu %zu\n", + p->engine.ps.pages + 1, + p->engine.ps.pages + 1); + ps_setfont(p, TERMFONT_NONE); - p->engine.ps.pscol = PS_CHAR_LEFT; - p->engine.ps.psrow = PS_CHAR_TOP; + p->engine.ps.pscol = p->engine.ps.left; + p->engine.ps.psrow = p->engine.ps.top; } static void -ps_pletter(struct termp *p, char c) +ps_pletter(struct termp *p, int c) { + int f; /* * If we're not in a PostScript "word" context, then open one @@ -239,8 +650,8 @@ ps_pletter(struct termp *p, char c) if ( ! (PS_INLINE & p->engine.ps.psstate)) { ps_printf(p, "%zu %zu moveto\n(", - p->engine.ps.pscol, - p->engine.ps.psrow); + AFM2PNT(p, p->engine.ps.pscol), + AFM2PNT(p, p->engine.ps.psrow)); p->engine.ps.psstate |= PS_INLINE; } @@ -265,8 +676,17 @@ ps_pletter(struct termp *p, char c) /* Write the character and adjust where we are on the page. */ - ps_putchar(p, c); - p->engine.ps.pscol += PS_CHAR_WIDTH; + f = (int)p->engine.ps.lastf; + + if (c <= 32 || (c - 32 > MAXCHAR)) { + ps_putchar(p, ' '); + p->engine.ps.pscol += fonts[f].gly[0].wx; + return; + } + + ps_putchar(p, (char)c); + c -= 32; + p->engine.ps.pscol += fonts[f].gly[c].wx; } @@ -376,7 +796,7 @@ ps_advance(struct termp *p, size_t len) */ ps_fclose(p); - p->engine.ps.pscol += len ? len * PS_CHAR_WIDTH : 0; + p->engine.ps.pscol += len; } @@ -402,16 +822,21 @@ ps_endline(struct termp *p) * showpage and restart our row. */ - p->engine.ps.pscol = PS_CHAR_LEFT; - if (p->engine.ps.psrow >= PS_CHAR_HEIGHT + PS_CHAR_BOT) { - p->engine.ps.psrow -= PS_CHAR_HEIGHT; + p->engine.ps.pscol = p->engine.ps.left; + if (p->engine.ps.psrow >= p->engine.ps.lineheight + + p->engine.ps.bottom) { + p->engine.ps.psrow -= p->engine.ps.lineheight; return; } assert(p->engine.ps.psmarg && p->engine.ps.psmarg[0]); printf("%s", p->engine.ps.psmarg); printf("showpage\n"); - p->engine.ps.psrow = PS_CHAR_TOP; + p->engine.ps.pages++; + printf("%%%%Page: %zu %zu\n", + p->engine.ps.pages + 1, + p->engine.ps.pages + 1); + p->engine.ps.psrow = p->engine.ps.top; } @@ -419,21 +844,68 @@ static void ps_setfont(struct termp *p, enum termfont f) { - if (TERMFONT_BOLD == f) - ps_printf(p, "/Courier-Bold\n"); - else if (TERMFONT_UNDER == f) - ps_printf(p, "/Courier-Oblique\n"); - else - ps_printf(p, "/Courier\n"); - - ps_printf(p, "10 selectfont\n"); + assert(f < TERMFONT__MAX); + ps_printf(p, "/%s %zu selectfont\n", + fonts[(int)f].name, p->engine.ps.scale); p->engine.ps.lastf = f; } +/* ARGSUSED */ static size_t ps_width(const struct termp *p, char c) { - return(1); + if (c <= 32 || c - 32 >= MAXCHAR) + return(fonts[(int)TERMFONT_NONE].gly[0].wx); + + c -= 32; + return(fonts[(int)TERMFONT_NONE].gly[(int)c].wx); } + + +static double +ps_hspan(const struct termp *p, const struct roffsu *su) +{ + double r; + + /* + * All of these measurements are derived by converting from the + * native measurement to AFM units. + */ + + switch (su->unit) { + case (SCALE_CM): + r = PNT2AFM(p, su->scale * 28.34); + break; + case (SCALE_IN): + r = PNT2AFM(p, su->scale * 72); + break; + case (SCALE_PC): + r = PNT2AFM(p, su->scale * 12); + break; + case (SCALE_PT): + r = PNT2AFM(p, su->scale * 100); + break; + case (SCALE_EM): + r = su->scale * + fonts[(int)TERMFONT_NONE].gly[109 - 32].wx; + break; + case (SCALE_MM): + r = PNT2AFM(p, su->scale * 2.834); + break; + case (SCALE_EN): + r = su->scale * + fonts[(int)TERMFONT_NONE].gly[110 - 32].wx; + break; + case (SCALE_VS): + r = su->scale * p->engine.ps.lineheight; + break; + default: + r = su->scale; + break; + } + + return(r); +} +