371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov/* $Id: term_ps.c,v 1.80 2015/12/23 20:50:13 schwarze Exp $ */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * Copyright (c) 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * Permission to use, copy, modify, and distribute this software for any
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * purpose with or without fee is hereby granted, provided that the above
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * copyright notice and this permission notice appear in all copies.
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore/* These work the buffer used by the header and footer. */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore/* Convert PostScript point "x" to an AFM unit. */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore (size_t)((double)(x) * (1000.0 / (double)(p)->ps->scale))
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore/* Convert an AFM unit "x" to a PostScript points */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore ((double)(x) / (1000.0 / (double)(p)->ps->scale))
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore#define MAXCHAR 95 /* total characters we can handle */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore struct glyph gly[MAXCHAR]; /* glyph metrics */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore#define PS_INLINE (1 << 0) /* we're in a word */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore#define PS_MARGINS (1 << 1) /* we're in the margins */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore#define PS_NEWPAGE (1 << 2) /* new page, no words yet */
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov#define PS_BACKSP (1 << 3) /* last character was backspace */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore size_t pscol; /* visible column (AFM units) */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore size_t psmargcur; /* cur index in margin buf */
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov enum termfont nextf; /* building next font here */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore size_t lineheight; /* line height (AFM units) */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore size_t bottom; /* body bottom (AFM units) */
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov size_t lastwidth; /* page width before last ll */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore size_t pdflastpg; /* byte of last page mark */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore size_t *pdfobjs; /* table of object offsets */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore const struct roffsu *);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amorestatic size_t ps_width(const struct termp *, int);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amorestatic void ps_advance(struct termp *, size_t);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amorestatic void ps_growbuf(struct termp *, size_t);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amorestatic void ps_printf(struct termp *, const char *, ...);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amorestatic void ps_putchar(struct termp *, char);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amorestatic void ps_setfont(struct termp *, enum termfont);
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankovstatic void ps_setwidth(struct termp *, int, int);
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankovstatic struct termp *pspdf_alloc(const struct manoutput *);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * We define, for the time being, three fonts: bold, oblique/italic, and
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * normal (roman). The following table hard-codes the font metrics for
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * ASCII, i.e., 32--127.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amorestatic const struct font fonts[TERMFONT__MAX] = {
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore { "Times-Roman", {
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore { "Times-Bold", {
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore { "Times-Italic", {
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov { "Times-BoldItalic", {
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amorestatic struct termp *
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore p->ps = mandoc_calloc(1, sizeof(struct termp_ps));
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* Default to US letter (millimetres). */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * The ISO-269 paper sizes can be calculated automatically, but
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * it would require bringing in -lm for pow() and I'd rather not
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * do that. So just do it the easy way for now. Since this
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * only happens once, I'm not terribly concerned.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore } else if (2 != sscanf(pp, "%ux%u", &pagex, &pagey))
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * This MUST be defined before any PNT2AFM or AFM2PNT
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * calculations occur.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* Remember millimetres -> AFM units. */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore pagex = PNT2AFM(p, ((double)pagex * 2.834));
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore pagey = PNT2AFM(p, ((double)pagey * 2.834));
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* Margins are 1/9 the page x and y. */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* Line-height is 1.4em. */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore lineheight = PNT2AFM(p, ((double)p->ps->scale * 1.4));
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov p->ps->width = p->ps->lastwidth = (size_t)pagex;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore p->ps->header = pagey - (marginy / 2) - (lineheight / 2);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore p->ps->footer = (marginy / 2) - (lineheight / 2);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov else if (iop == 0)
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov p->ps->width = width ? (size_t)width : p->ps->lastwidth;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amoreps_printf(struct termp *p, const char *fmt, ...)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * If we're running in regular mode, then pipe directly into
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * vprintf(). If we're processing margins, then push the data
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * into our growable margin buffer.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * XXX: I assume that the in-margin print won't exceed
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * PS_BUFSLOP (128 bytes), which is reasonable but still an
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * assumption that will cause pukeage if it's not the case.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore vsnprintf(&p->ps->psmarg[pos], PS_BUFSLOP, fmt, ap);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* See ps_printf(). */
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov p->ps->pdfobjs = mandoc_reallocarray(p->ps->pdfobjs,
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore p->ps->pdfobjs[(int)obj - 1] = p->ps->pdfbytes;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * Close out a page that we've already flushed to output. In
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * PostScript, we simply note that the page must be showed. In
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * PDF, we must now create the Length, Resource, and Page node
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * for the page contents.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* Length of content. */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* Resource for content. */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore ps_printf(p, "<<\n/ProcSet [/PDF /Text]\n");
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov for (i = 0; i < (int)TERMFONT__MAX; i++)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* Page node. */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore ps_printf(p, "/Resources %zu 0 R\n", base + 2);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * At the end of the file, do one last showpage. This is the
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * same behaviour as groff(1) and works for multiple pages as
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * well as just one.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore ps_printf(p, "%%%%Pages: %zu\n", p->ps->pages);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov ps_printf(p, " %zu 0 R", i * 4 + p->ps->pdfbody + 3);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov base = (p->ps->pages - 1) * 4 + p->ps->pdfbody + 4;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore for (i = 0; i < base; i++)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * Print margins into margin buffer. Nothing gets output to the
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * screen yet, so we don't need to initialise the primary state.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /*p->ps->pdfbytes = 0;*/
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * Print header and initialise page state. Following this,
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * stuff gets printed to the screen, so make sure we're sane.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore ps_printf(p, "%%%%DocumentData: Clean7Bit\n");
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore ps_printf(p, "%%%%Orientation: Portrait\n");
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov "Default %zu %zu 0 () ()\n",
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore ps_printf(p, "%%%%DocumentNeededResources: font");
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore for (i = 0; i < (int)TERMFONT__MAX; i++)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore for (i = 0; i < (int)TERMFONT__MAX; i++) {
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore ps_printf(p, "/BaseFont /%s\n", fonts[i].name);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * If we haven't opened a page context, then output that we're
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * in a new page and make sure the font is correctly set.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * If we're not in a PostScript "word" context, then open one
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * now at the current cursor.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * We need to escape these characters as per the PostScript
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * specification. We would also escape non-graphable characters
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * (like tabs), but none of them would get to this point and
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * it's superfluous to abort() on them.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* Write the character and adjust where we are on the page. */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * Spit out that we're exiting a word context (this is a
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * "partial close" because we don't check the last-char buffer
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * or anything).
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * Strong closure: if we have a last-char, spit it out after
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * checking that we're in the right font mode. This will of
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * course open a new scope, if applicable.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * Following this, close out any scope that's open.
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * When receiving a backspace, merely flag it.
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * We don't know yet whether it is
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * a font instruction or an overstrike.
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if (c == '\b') {
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * Decode font instructions.
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * This is not a font instruction, but rather
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * the next character. Prepare for overstrike.
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * We found the next character, so the font instructions
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * for the previous one are complete.
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * Use them and print it.
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * For an overstrike, if a previous character
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * was wider, advance to center the new one.
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov wx = fonts[p->ps->lastf].gly[(int)p->ps->last-32].wx;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * For an overstrike, if a previous character
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * was wider, advance to the end of the old one.
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * Do not print the current character yet because font
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * instructions might follow; only remember it.
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * For the first character, nothing else is done.
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * The final character will get printed from ps_fclose().
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * For an overstrike, back up to the previous position.
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * If the previous character is wider than any it overstrikes,
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * remember the current position, because it might also be
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * wider than all that will overstrike it.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * Advance some spaces. This can probably be made smarter,
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * i.e., to have multiple space-separated words in the same
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * scope, but this is easier: just close out the current scope
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * and readjust our column settings.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* Close out any scopes we have open: we're at eoln. */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * If we're in the margin, don't try to recalculate our current
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * row. XXX: if the column tries to be fancy with multiple
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * lines, we'll do nasty stuff.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* Left-justify. */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* If we haven't printed anything, return. */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * Put us down a line. If we're at the page bottom, spit out a
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * showpage and restart our row.
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if (p->ps->psrow >= p->ps->lineheight + p->ps->bottom) {
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * If we're still at the top of the page, let the font-setting
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * be delayed until we actually have stuff to print.
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov return (size_t)fonts[(int)TERMFONT_NONE].gly[c].wx;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amoreps_hspan(const struct termp *p, const struct roffsu *su)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * All of these measurements are derived by converting from the
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * native measurement to AFM units.
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * Traditionally, the default unit is fixed to the
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * output media. So this would refer to the point. In
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * mandoc(1), however, we stick to the default terminal
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * scaling unit so that output is the same regardless
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * the media.
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov fonts[(int)TERMFONT_NONE].gly[109 - 32].wx / 100.0;
371584c2eae4cf827fd406ba26c14f021adaaa70Yuri Pankov return r * 24.0;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if (p->ps->psmargcur + sz <= p->ps->psmargsz)
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov p->ps->psmarg = mandoc_realloc(p->ps->psmarg, p->ps->psmargsz);