summaryrefslogtreecommitdiffstats
path: root/term_ps.c
diff options
context:
space:
mode:
authorKristaps Dzonsons <kristaps@bsd.lv>2010-06-11 16:58:20 +0000
committerKristaps Dzonsons <kristaps@bsd.lv>2010-06-11 16:58:20 +0000
commit58a26cd399ba32d2d911f924c14c7b25fc297b78 (patch)
treebe76cbd7c6677829504d46b83def49467bc3fec5 /term_ps.c
parent8c998f6fb38c48a3f834b396046a65cb28997c05 (diff)
downloadmandoc-58a26cd399ba32d2d911f924c14c7b25fc297b78.tar.gz
Implement font-switching for PostScript. -Tps now supports
TERMFONT_BOLD (Courier-Bold) and TERMFONT_UNDER (Courier-Oblique). It doesn't look half bad. This accomplished through tricksy juggling of the one-char back-buffer.
Diffstat (limited to 'term_ps.c')
-rw-r--r--term_ps.c194
1 files changed, 133 insertions, 61 deletions
diff --git a/term_ps.c b/term_ps.c
index 0fbc499b..f1e1f7d3 100644
--- a/term_ps.c
+++ b/term_ps.c
@@ -53,14 +53,18 @@
} \
} while (/* CONSTCOND */ 0)
+
static void ps_letter(struct termp *, char);
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 void ps_pclose(struct termp *);
static void ps_pletter(struct termp *, char);
static void ps_printf(struct termp *, const char *, ...);
static void ps_putchar(struct termp *, char);
+static void ps_setfont(struct termp *, enum termfont);
void *
@@ -175,56 +179,47 @@ static void
ps_begin(struct termp *p)
{
- /*
- * Emit the standard PostScript prologue, set our initial page
- * position, then run pageopen() on the initial page.
+ /*
+ * Print margins into margin buffer. Nothing gets output to the
+ * screen yet, so we don't need to initialise the primary state.
*/
- printf("%s\n", "%!PS");
- printf("%s\n", "/Courier");
- printf("%s\n", "10 selectfont");
-
- p->engine.ps.psstate = 0;
-
if (p->engine.ps.psmarg) {
assert(p->engine.ps.psmargsz);
p->engine.ps.psmarg[0] = '\0';
}
p->engine.ps.psmargcur = 0;
-
- /*
- * Now dump the margins into our margin buffer. If we don't do
- * this, we'd break any current state to run the header and
- * footer with each and evern new page.
- */
-
+ p->engine.ps.psstate = PS_MARGINS;
p->engine.ps.pscol = PS_CHAR_LEFT;
p->engine.ps.psrow = PS_CHAR_TOPMARG;
- p->engine.ps.psstate |= PS_MARGINS;
+ ps_setfont(p, TERMFONT_NONE);
(*p->headf)(p, p->argf);
(*p->endline)(p);
- p->engine.ps.psstate &= ~PS_MARGINS;
- assert(0 == p->engine.ps.psstate);
-
p->engine.ps.pscol = PS_CHAR_LEFT;
p->engine.ps.psrow = PS_CHAR_BOTMARG;
- p->engine.ps.psstate |= PS_MARGINS;
(*p->footf)(p, p->argf);
(*p->endline)(p);
p->engine.ps.psstate &= ~PS_MARGINS;
+
assert(0 == p->engine.ps.psstate);
+ assert(p->engine.ps.psmarg);
+ assert('\0' != p->engine.ps.psmarg[0]);
+
+ /*
+ * Print header and initialise page state. Following this,
+ * stuff gets printed to the screen, so make sure we're sane.
+ */
+ printf("%s\n", "%!PS");
+ ps_setfont(p, TERMFONT_NONE);
p->engine.ps.pscol = PS_CHAR_LEFT;
p->engine.ps.psrow = PS_CHAR_TOP;
-
- assert(p->engine.ps.psmarg);
- assert('\0' != p->engine.ps.psmarg[0]);
}
@@ -232,11 +227,12 @@ static void
ps_pletter(struct termp *p, char c)
{
+ /*
+ * If we're not in a PostScript "word" context, then open one
+ * now at the current cursor.
+ */
+
if ( ! (PS_INLINE & p->engine.ps.psstate)) {
- /*
- * If we're not in a PostScript "word" context, then
- * open one now at the current cursor.
- */
ps_printf(p, "%zu %zu moveto\n(",
p->engine.ps.pscol,
p->engine.ps.psrow);
@@ -263,59 +259,118 @@ 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;
}
static void
+ps_pclose(struct termp *p)
+{
+
+ /*
+ * Spit out that we're exiting a word context (this is a
+ * "partial close" because we don't check the last-char buffer
+ * or anything).
+ */
+
+ if ( ! (PS_INLINE & p->engine.ps.psstate))
+ return;
+
+ ps_printf(p, ") show\n");
+ p->engine.ps.psstate &= ~PS_INLINE;
+}
+
+
+static void
+ps_fclose(struct termp *p)
+{
+
+ /*
+ * Strong closure: if we have a last-char, spit it out after
+ * checking that we're in the right font mode. This will of
+ * course open a new scope, if applicable.
+ *
+ * Following this, close out any scope that's open.
+ */
+
+ if ('\0' != p->engine.ps.last) {
+ if (p->engine.ps.lastf != TERMFONT_NONE) {
+ ps_pclose(p);
+ ps_setfont(p, TERMFONT_NONE);
+ }
+ ps_pletter(p, p->engine.ps.last);
+ p->engine.ps.last = '\0';
+ }
+
+ if ( ! (PS_INLINE & p->engine.ps.psstate))
+ return;
+
+ ps_pclose(p);
+}
+
+
+static void
ps_letter(struct termp *p, char c)
{
char cc;
-
+
+ /*
+ * State machine dictates whether to buffer the last character
+ * or not. Basically, encoded words are detected by checking if
+ * we're an "8" and switching on the buffer. Then we put "8" in
+ * our buffer, and on the next charater, flush both character
+ * and buffer. Thus, "regular" words are detected by having a
+ * regular character and a regular buffer character.
+ */
+
if ('\0' == p->engine.ps.last) {
assert(8 != c);
p->engine.ps.last = c;
return;
} else if (8 == p->engine.ps.last) {
assert(8 != c);
- p->engine.ps.last = c;
- return;
+ p->engine.ps.last = '\0';
} else if (8 == c) {
assert(8 != p->engine.ps.last);
+ if ('_' == p->engine.ps.last) {
+ if (p->engine.ps.lastf != TERMFONT_UNDER) {
+ ps_pclose(p);
+ ps_setfont(p, TERMFONT_UNDER);
+ }
+ } else if (p->engine.ps.lastf != TERMFONT_BOLD) {
+ ps_pclose(p);
+ ps_setfont(p, TERMFONT_BOLD);
+ }
p->engine.ps.last = c;
return;
} else {
+ if (p->engine.ps.lastf != TERMFONT_NONE) {
+ ps_pclose(p);
+ ps_setfont(p, TERMFONT_NONE);
+ }
cc = p->engine.ps.last;
p->engine.ps.last = c;
c = cc;
}
- return(ps_pletter(p, c));
+ ps_pletter(p, c);
}
static void
ps_advance(struct termp *p, size_t len)
{
- size_t i;
-
- if ('\0' != p->engine.ps.last) {
- ps_pletter(p, p->engine.ps.last);
- p->engine.ps.last = '\0';
- }
- if (PS_INLINE & p->engine.ps.psstate) {
- assert(8 != p->engine.ps.last);
- if (p->engine.ps.last)
- ps_letter(p, p->engine.ps.last);
- p->engine.ps.last = '\0';
- for (i = 0; i < len; i++)
- ps_letter(p, ' ');
- return;
- }
+ /*
+ * Advance some spaces. This can probably be made smarter,
+ * i.e., to have multiple space-separated words in the same
+ * scope, but this is easier: just close out the current scope
+ * and readjust our column settings.
+ */
- assert('\0' == p->engine.ps.last);
+ ps_fclose(p);
p->engine.ps.pscol += len ? len * PS_CHAR_WIDTH : 0;
}
@@ -324,24 +379,24 @@ static void
ps_endline(struct termp *p)
{
- if ('\0' != p->engine.ps.last) {
- ps_pletter(p, p->engine.ps.last);
- p->engine.ps.last = '\0';
- }
+ /* Close out any scopes we have open: we're at eoln. */
- if (PS_INLINE & p->engine.ps.psstate) {
- assert(8 != p->engine.ps.last);
- if (p->engine.ps.last)
- ps_letter(p, p->engine.ps.last);
- p->engine.ps.last = '\0';
- ps_printf(p, ") show\n");
- p->engine.ps.psstate &= ~PS_INLINE;
- } else
- assert('\0' == p->engine.ps.last);
+ ps_fclose(p);
+
+ /*
+ * If we're in the margin, don't try to recalculate our current
+ * row. XXX: if the column tries to be fancy with multiple
+ * lines, we'll do nasty stuff.
+ */
if (PS_MARGINS & p->engine.ps.psstate)
return;
+ /*
+ * Put us down a line. If we're at the page bottom, spit out a
+ * 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;
@@ -353,3 +408,20 @@ ps_endline(struct termp *p)
printf("showpage\n");
p->engine.ps.psrow = PS_CHAR_TOP;
}
+
+
+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");
+ p->engine.ps.lastf = f;
+}
+