summaryrefslogtreecommitdiff
path: root/support/RTF-1_06a1/trf-flush.c
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /support/RTF-1_06a1/trf-flush.c
Initial commit
Diffstat (limited to 'support/RTF-1_06a1/trf-flush.c')
-rw-r--r--support/RTF-1_06a1/trf-flush.c1157
1 files changed, 1157 insertions, 0 deletions
diff --git a/support/RTF-1_06a1/trf-flush.c b/support/RTF-1_06a1/trf-flush.c
new file mode 100644
index 0000000000..238f58689b
--- /dev/null
+++ b/support/RTF-1_06a1/trf-flush.c
@@ -0,0 +1,1157 @@
+/*
+ trf-flush.c - state and content text flushing
+
+ Eventually, all output will end up being flushed through one
+ or two low-level routines, which will allow better trapping
+ on conditions such as initial state needing to be dumped,
+ diversions needing to be started, trap positions needing to
+ be flushed, etc. Maybe in 1.06a2.
+*/
+
+# include <stdio.h>
+# include <sys/types.h>
+# ifdef VARARGS
+# include <varargs.h>
+# endif /* VARARGS */
+# include "rtf.h"
+# include "rtf2troff.h"
+
+
+static int initialStateFlushed = 0;
+
+static int useInLine = 0; /* non-zero for inline char state changes */
+static char inLineChgs[rtfBufSiz] = "";
+
+/*
+ Whether any content text chars have been written to current
+ paragraph.
+*/
+
+static int inPara = 0;
+static int oLen = 0;
+static int breakOK = 0;
+
+
+static void FlushDocState ();
+static void FlushParState ();
+static void FlushCharState ();
+static void FlushSACharState ();
+static void Continuation ();
+static void CalcInLineChanges ();
+static void _PutS ();
+static char *ApplyIndirection ();
+
+static void DrawLine ();
+static char *TabTypeStr ();
+static char *JustTypeStr ();
+static void CheckVMargins ();
+static double LineLen ();
+
+
+/*
+ Flush any discrepancies between state as written and current
+ internal state to bring the former in sync with the latter.
+
+ Virtually all formatting text is written by this operation.
+
+ It's assumed here, perhaps unfortunately, that things needing
+ a Flush() first won't occur in the middle of output line
+ collection.
+*/
+
+void FlushState ()
+{
+ /* flush */
+ FlushInitialState ();
+ if (docStateChanged)
+ FlushDocState ();
+ /* header/footer depend on some doc properties */
+ if (docStateChanged || sectStateChanged)
+ FlushSectState ();
+ /* para line length depends on some doc properties; ditto tabs */
+ if (docStateChanged || parStateChanged)
+ FlushParState ();
+ if (charStateChanged)
+ FlushCharState ();
+
+ /* sync */
+ if (docStateChanged)
+ bcopy ((char *) ids, (char *) wds, (int) sizeof (DocState));
+ if (sectStateChanged)
+ bcopy ((char *) iss, (char *) wss, (int) sizeof (SectState));
+ if (parStateChanged)
+ bcopy ((char *) ips, (char *) wps, (int) sizeof (ParState));
+ if (charStateChanged)
+ bcopy ((char *) ics, (char *) wcs, (int) sizeof (CharState));
+
+ docStateChanged = 0;
+ sectStateChanged = 0;
+ parStateChanged = 0;
+ charStateChanged = 0;
+}
+
+
+/*
+ This is called at the beginning of output to write out absolute
+ initial values for some important state stuff. The other
+ state-writers usually write values relative to the last written
+ values, so this is needed to write absolute values that the
+ relative values can be relative *to*.
+
+ Problem: it's important to avoid tripping the first pseudo-page
+ transition, or the header for the first page will be lost. This
+ occurs when non-diverted text processing occurs or when a number
+ of different requests (e.g., .in) occur. Header/footer text
+ processingn occurs in diversions, so that's not a problem. To
+ avoid tripping the trap with requests, use things like 'in instead
+ of .in. Losing a break isn't a problem since there's no content
+ text to write yet.
+
+ The page length is written early and header/footer traps are
+ planted. These traps stay intact. At most, the footer trap
+ position might be moved.
+
+ Tp is non-zero if a section's title page is special.
+
+ Macros Ha, Hf, Hl and Hr are defined if/when all-page, first-page,
+ left-page and right-page headers are given, and the number registers
+ of the same name, which initially have value zero, are set to 1.
+ Similarly for footers. The register Tm defines the top margins.
+ The registers Hp and Fp define the header and footer positions.
+
+ HE, FO need are written to exit if there are trap loops and
+ to not space too much if vertical margins are weird.
+*/
+
+void FlushInitialState ()
+{
+ if (initialStateFlushed)
+ return;
+
+ Comment ("begin initial layout setup, change as desired");
+
+ /* check whether it appears landscape *should have* been selected */
+ if (ids->pageHeight < ids->pageWidth && !ids->landscape)
+ {
+ fprintf (stderr, "Turning landscape on\n");
+ ids->landscape = 1;
+ }
+
+ if (ids->landscape)
+ {
+ if (tvers == XROFF)
+ fprintf (f, ".dc landscape\n");
+ /* reverse page height and width? */
+ }
+ fprintf (f, ".pl %gi\n", ids->pageHeight);
+ if (tvers == XROFF)
+ {
+ double pLen;
+
+ /* have to tell printer the page length in 300dpi units */
+ /* if not default 11in (this is orientation dependent) */
+ if (ids->landscape)
+ pLen = ids->pageWidth;
+ else
+ pLen = ids->pageHeight;
+ if (pLen != 11.0)
+ fprintf (f, ".dc length %d\n", (int) (pLen * 300));
+ }
+
+ /* abandon hope, all ye who who enter here to try to read this... */
+
+ fprintf (f, ".ad %s\n", JustTypeStr (ips->justification));
+ fprintf (f, ".po %gi\n", ids->leftMargin);
+ fprintf (f, "'in %gi\n", ips->leftIndent); /* ' to avoid break */
+ fprintf (f, ".ll %gi\n", LineLen (ids, ips));
+ fprintf (f, ".ps %d\n", ics->fontSize);
+ Comment ("%gi = %gp", ips->spaceBetween, ips->spaceBetween * 72);
+ fprintf (f, ".vs %gi\n", ips->spaceBetween);
+ fprintf (f, ".ft R\n");
+
+ /* plant traps */
+
+ Comment ("plant header trap");
+ fprintf (f, ".nr %s %d\n", rTitlePageSpecial, iss->titleSpecial);
+ fprintf (f, ".nr %s %gi\n", rTopMargin, ids->topMargin);
+ fprintf (f, ".nr %s %gi\n", rHeaderPos, iss->headerPos);
+ fprintf (f, ".nr %s 0\n", rHeaderAll);
+ fprintf (f, ".nr %s 0\n", rHeaderFirst);
+ fprintf (f, ".nr %s 0\n", rHeaderLeft);
+ fprintf (f, ".nr %s 0\n", rHeaderRight);
+ fprintf (f, ".de %s\n", mHeader);
+ fprintf (f, ".if \\\\n(%s>=\\\\n(Bm \\{\\\n",
+ rTopMargin, rBottomMargin);
+ fprintf (f, ".\ttm Trap Loop Death detected...\n");
+ fprintf (f, ".\tex\n");
+ fprintf (f, ".\\}\n");
+ fprintf (f, ".rs\n");
+ fprintf (f, ".if \\\\n(%s<\\\\n(%s 'sp |\\\\n(%su\n",
+ rHeaderPos, rTopMargin, rHeaderPos);
+ fprintf (f, ".ev 1\n");
+ /*fprintf (f, ".nf\n"); /* correct? */
+ /* ugly stuff to select correct header text macro */
+ fprintf (f, ".ie (\\\\n%%=1&\\\\n(%s>0&\\\\n(%s>0) .%s\n",
+ rHeaderFirst, rTitlePageSpecial, mHeaderFirst);
+ fprintf (f, ".el \\{\\\n");
+ fprintf (f, ". ie \\\\n(%s>0 \\{\\\n", rHeaderLeft);
+ fprintf (f, ". ie o .%s\n", mHeaderRight);
+ fprintf (f, ". el .%s\n", mHeaderLeft);
+ fprintf (f, ". \\}\n");
+ fprintf (f, ". el .if \\\\n(%s>0 .%s\n", rHeaderAll, mHeaderAll);
+ fprintf (f, ".\\}\n");
+ /* end ugly stuff */
+ fprintf (f, ".ev\n");
+ fprintf (f, "'sp |\\\\n(%su\n", rTopMargin);
+ fprintf (f, ".ns\n");
+ fprintf (f, "..\n");
+ fprintf (f, ".wh 0i %s\n", mHeader);
+
+ Comment ("plant footer trap");
+ fprintf (f, ".nr %s %gi\n",
+ rBottomMargin, ids->pageHeight - ids->bottomMargin);
+ fprintf (f, ".nr %s %gi\n",
+ rFooterPos, ids->pageHeight - iss->footerPos);
+ fprintf (f, ".nr %s 0\n", rFooterAll);
+ fprintf (f, ".nr %s 0\n", rFooterFirst);
+ fprintf (f, ".nr %s 0\n", rFooterLeft);
+ fprintf (f, ".nr %s 0\n", rFooterRight);
+ fprintf (f, ".de %s\n", mFooter);
+ fprintf (f, ".if \\\\n(%s>\\\\n(%s 'sp |\\\\n(%su\n",
+ rFooterPos, rBottomMargin, rFooterPos);
+ fprintf (f, ".ev 1\n");
+ /*fprintf (f, ".nf\n"); /* correct? */
+ /* ugly stuff to select correct footer text macro */
+ fprintf (f, ".ie (\\\\n%%=1&\\\\n(%s>0&\\\\n(%s>0) .%s\n",
+ rFooterFirst, rTitlePageSpecial, mFooterFirst);
+ fprintf (f, ".el \\{\\\n");
+ fprintf (f, ". ie \\\\n(%s>0 \\{\\\n", rFooterLeft);
+ fprintf (f, ". ie o .%s\n", mFooterRight);
+ fprintf (f, ". el .%s\n", mFooterLeft);
+ fprintf (f, ". \\}\n");
+ fprintf (f, ". el .if \\\\n(%s>0 .%s\n", rFooterAll, mFooterAll);
+ fprintf (f, ".\\}\n");
+ /* end ugly stuff */
+ fprintf (f, ".ev\n");
+ fprintf (f, "'bp\n");
+ fprintf (f, "..\n");
+ fprintf (f, ".wh %gi %s\n", -ids->bottomMargin, mFooter);
+
+ Comment ("end initial layout setup");
+
+ /* manually sync everything that was just flushed */
+ wds->bottomMargin = ids->bottomMargin;
+ wds->bottomMargin = ids->bottomMargin;
+ wds->landscape = ids->landscape;
+ wds->leftMargin = ids->leftMargin;
+ wds->leftMargin = ids->leftMargin;
+ wds->pageHeight = ids->pageHeight;
+ wds->pageWidth = ids->pageWidth;
+ wds->rightMargin = ids->rightMargin;
+ wds->topMargin = ids->topMargin;
+ wss->footerPos = iss->footerPos;
+ wss->headerPos = iss->headerPos;
+ wss->titleSpecial = iss->titleSpecial;
+ wps->justification = ips->justification;
+ wps->leftIndent = ips->leftIndent;
+ wps->rightIndent = ips->rightIndent;
+ wps->spaceBetween = ips->spaceBetween;
+ wcs->fontSize = ics->fontSize;
+
+ ++initialStateFlushed;
+}
+
+
+/*
+ Note that right margin is document property in RTF, but has the
+ effect of changing line length, which is handled under paragraph
+ property changes. Ditto for change of default tab width.
+*/
+
+static void FlushDocState ()
+{
+ CheckVMargins ();
+ if (ids->landscape != wds->landscape)
+ {
+ /* note: once on, can't turn off */
+ if (ids->landscape) /* it's now on */
+ {
+ Flush ();
+ if (tvers == XROFF)
+ fprintf (f, ".dc landscape\n");
+ }
+ }
+ if (ids->pageHeight != wds->pageHeight)
+ {
+ Flush ();
+ fprintf (f, ".pl %gi\n", ids->pageHeight);
+ if (tvers == XROFF)
+ {
+ double pLen;
+
+ /* have to tell printer the page length in 300dpi units */
+ /* if not default 11in (this is orientation dependent) */
+
+ if (ids->landscape)
+ pLen = ids->pageWidth;
+ else
+ pLen = ids->pageHeight;
+ if (pLen != 11.0)
+ fprintf (f, ".dc length %d\n",
+ (int) (pLen * 300));
+ }
+ }
+ if (ids->leftMargin != wds->leftMargin)
+ {
+ Flush ();
+ fprintf (f, ".po %gi\n", ids->leftMargin);
+ }
+}
+
+
+/*
+ If the top margin or the header or footer positions have
+ changed, redefine the registers giving their sizes. If the
+ bottom margin has changed, move the trap to the right spot.
+ (Document and section state interact here.)
+
+ This is also called when a macro is about to be diverted, so that
+ the trap position isn't set within a different environment.
+ (Is that necessary?)
+
+ The really ugly thing here is to try and catch cases where the
+ header position is set below the top margin, and especially where the
+ footer position *above* the bottom margin. The latter can result
+ in loops where the footer trap is invoked in a loop.
+*/
+
+void FlushSectState ()
+{
+ if (iss->titleSpecial != wss->titleSpecial)
+ {
+ Flush ();
+ fprintf (f, ".nr %s %d\n",
+ rTitlePageSpecial, iss->titleSpecial);
+ }
+ if (ids->topMargin != wds->topMargin)
+ {
+ Flush ();
+ fprintf (f, ".nr %s %gi\n", rTopMargin, ids->topMargin);
+ }
+ if (iss->headerPos != wss->headerPos)
+ {
+ Flush ();
+ fprintf (f, ".nr %s %gi\n", rHeaderPos, iss->headerPos);
+ }
+ if (ids->bottomMargin != wds->bottomMargin)
+ {
+ Flush ();
+ fprintf (f, ".ch %s %gi\n", mHeader, -ids->bottomMargin);
+ }
+ if (iss->footerPos != wss->footerPos)
+ {
+ Flush ();
+ fprintf (f, ".nr %s %gi\n",
+ rFooterPos, ids->pageHeight - iss->footerPos);
+ }
+}
+
+
+static void FlushParState ()
+{
+int tabdiff;
+int i;
+
+ if (ips->justification != wps->justification)
+ {
+ Flush ();
+ fprintf (f, ".ad %s\n", JustTypeStr (ips->justification));
+ }
+ if (ips->leftIndent != wps->leftIndent)
+ {
+ Flush ();
+ fprintf (f, ".in %+gi\n", ips->leftIndent - wps->leftIndent);
+ }
+ /*
+ troff doesn't set right indent, rather it sets
+ line length (function of page width - po - rm - ri)
+ */
+ if (ids->pageWidth != wds->pageWidth
+ || ids->leftMargin != wds->leftMargin
+ || ids->rightMargin != wds->rightMargin
+ || ips->rightIndent != wps->rightIndent)
+ {
+ Flush ();
+ fprintf (f, ".ll %gi\n", LineLen (ids, ips));
+ }
+ if (ips->spaceBetween != wps->spaceBetween)
+ {
+ Flush ();
+ fprintf (f, ".vs %gi\n", ips->spaceBetween);
+ }
+
+ /*
+ Determine if tabs have changed, which they will if there
+ are a different number of tab stops than previously, or any
+ of the current ones are different than those last written
+ out. Change of default width is a change, too.
+ */
+
+ tabdiff = 0;
+ if (ids->tabWidth != wds->tabWidth)
+ tabdiff = 1;
+ else if (ips->nTabs != wps->nTabs)
+ tabdiff = 1;
+ else
+ {
+ for (i = 0; i < ips->nTabs; i++)
+ {
+ if (ips->tab[i] != wps->tab[i]
+ || ips->tabType[i] != wps->tabType[i])
+ {
+ tabdiff = 1;
+ break;
+ }
+ }
+ }
+ if (tabdiff)
+ {
+ Flush ();
+ if (ips->nTabs == 0) /* use defaults */
+ {
+ fprintf (f, ".ta %gi", ids->tabWidth);
+ for (i = 1; i < maxTab; i++)
+ fprintf (f, " +%gi", ids->tabWidth);
+ }
+ else
+ {
+ fprintf (f, ".ta %gi%s", ips->tab[0],
+ TabTypeStr (ips->tabType[0]));
+ for (i = 1; i < ips->nTabs; i++)
+ {
+ fprintf (f, " +%gi%s",
+ ips->tab[i] - ips->tab[i-1],
+ TabTypeStr (ips->tabType[i]));
+ }
+ }
+ fprintf (f, "\n");
+ }
+ if (ips->tabChar != wps->tabChar)
+ {
+ Flush ();
+ switch (ips->tabChar)
+ {
+ case rtfLeaderMotion:
+ fprintf (f, ".tc\n");
+ break;
+ case rtfLeaderDot:
+ fprintf (f, ".tc .\n");
+ break;
+ case rtfLeaderHyphen:
+ fprintf (f, ".tc -\n");
+ break;
+ case rtfLeaderUnder:
+ case rtfLeaderThick:
+ fprintf (f, ".tc _\n");
+ break;
+ }
+ }
+}
+
+
+/*
+ Flush character state. Actually, if useInLine is true, this
+ just calculates the string of inline commands that should be
+ generated, and those are later flushed in PutString ().
+*/
+
+static void FlushCharState ()
+{
+ if (useInLine)
+ CalcInLineChanges ();
+ else
+ FlushSACharState ();
+}
+
+
+/*
+ Flush character state, using standalone requests.
+ If in a paragraph, generates a \c to cause stuff on current line
+ to be joined to next so extraneous space won't end up in the
+ output.
+*/
+static void FlushSACharState ()
+{
+u_long csFontBits, wsFontBits;
+int idiff;
+double ddiff;
+
+ if (ics->fontSize != wcs->fontSize) /* write font size */
+ {
+ Continuation ();
+ idiff = ics->fontSize - wcs->fontSize;
+ fprintf (f, ".ps %+d\n", idiff);
+ }
+ /*
+ Note: super/subscripts don't always have intended effect
+ in non-inline mode. Output may need hand fixing.
+ */
+ if (ics->superScript != wcs->superScript)
+ {
+ Continuation ();
+ ddiff = wcs->superScript - ics->superScript;
+ fprintf (f, "'sp %gp\n", ddiff);
+ }
+ if (ics->subScript != wcs->subScript)
+ {
+ Continuation ();
+ ddiff = ics->subScript - wcs->subScript;
+ fprintf (f, "'sp %gp\n", ddiff);
+ }
+ if (ics->charStyle != wcs->charStyle) /* write R, I, B */
+ {
+ /*
+ Since troff implements plain, bold and italic by
+ changing fonts, figure out whether the font needs
+ to be changed. This doesn't understand simultaneous
+ bold+italic (boo-hoo), and treats it as italic.
+ */
+ csFontBits = StyleFontBits (ics->charStyle);
+ wsFontBits = StyleFontBits (wcs->charStyle);
+ if (csFontBits != wsFontBits)
+ {
+ Continuation ();
+ if (csFontBits == 0) /* neither bold or italic */
+ fprintf (f, ".ft R\n");
+ else if (csFontBits & styleItalic)
+ fprintf (f, ".ft I\n");
+ else if (csFontBits & styleBold)
+ fprintf (f, ".ft B\n");
+ }
+
+ /* if smallcaps now on and wasn't before, turn on */
+ if ((ics->charStyle & styleSmallCaps)
+ && !(wcs->charStyle & styleSmallCaps))
+ {
+ Continuation ();
+ fprintf (f, ".ps -1\n");
+ }
+ /* if smallcaps now off and wasn't before, turn off */
+ if (!(ics->charStyle & styleSmallCaps)
+ && (wcs->charStyle & styleSmallCaps))
+ {
+ Continuation ();
+ fprintf (f, ".ps +1\n");
+ }
+ }
+}
+
+
+static void Continuation ()
+{
+ if (oLen > 0)
+ {
+ if (breakOK)
+ fprintf (f, "\n");
+ else
+ fprintf (f, "\\c\n"); /* need ApplyIndirection() ? */
+ ResetParLine ();
+ }
+}
+
+
+/*
+ Generate a string of inline-changes, which need to be flushed with
+ indirection applied.
+*/
+
+static void CalcInLineChanges ()
+{
+char *picp = inLineChgs;
+int csFontBits, wsFontBits;
+int idiff;
+double ddiff;
+char c;
+
+ *picp = '\0';
+ if (ics->fontSize != wcs->fontSize) /* write font size */
+ {
+ idiff = ics->fontSize - wcs->fontSize;
+ c = '+';
+ if (idiff < 0)
+ {
+ c = '-';
+ idiff *= -1;
+ }
+ while (idiff > 9)
+ {
+ sprintf (picp, "\\s%c9", c);
+ picp += strlen (picp);
+ idiff -= 9;
+ }
+ sprintf (picp, "\\s%c%d", c, idiff);
+ picp += strlen (picp);
+ }
+ if (ics->superScript != wcs->superScript)
+ {
+ ddiff = wcs->superScript - ics->superScript;
+ sprintf (picp, "\\v'%gp'", ddiff);
+ picp += strlen (picp);
+ }
+ if (ics->subScript != wcs->subScript)
+ {
+ ddiff = ics->subScript - wcs->subScript;
+ sprintf (picp, "\\v'%gp'", ddiff);
+ picp += strlen (picp);
+ }
+ if (ics->charStyle != wcs->charStyle) /* write R, I, B */
+ {
+ /*
+ Since troff implements plain, bold and italic by
+ changing fonts, figure out whether the font needs
+ to be changed. This doesn't understand simultaneous
+ bold+italic (boo-hoo), and treats it as italic.
+ */
+ csFontBits = ics->charStyle & (styleBold | styleItalic);
+ wsFontBits = wcs->charStyle & (styleBold | styleItalic);
+ if (csFontBits != wsFontBits)
+ {
+ if (csFontBits == 0) /* neither bold or italic */
+ sprintf (picp, "\\fR");
+ else if (csFontBits & styleItalic)
+ sprintf (picp, "\\fI");
+ else if (csFontBits & styleBold)
+ sprintf (picp, "\\fB");
+ /* this is a NOP if no "if" was triggered above */
+ picp += strlen (picp);
+ }
+
+ /* if smallcaps now on and wasn't before, turn on */
+ if ((ics->charStyle & styleSmallCaps)
+ && !(wcs->charStyle & styleSmallCaps))
+ {
+ sprintf (picp, "\\s-1");
+ picp += strlen (picp);
+ }
+ /* if smallcaps now off and wasn't before, turn off */
+ if (!(ics->charStyle & styleSmallCaps)
+ && (wcs->charStyle & styleSmallCaps))
+ {
+ sprintf (picp, "\\s+1");
+ picp += strlen (picp);
+ }
+ }
+}
+
+
+/*
+ Save font, point size and vertical spacing. Called at beginning
+ of table to get an idea of the values for the parameters that tbl
+ will use at the beginning of each cell. FlushTblFPV() is called
+ after each cell is begin, to undo this if the previous cell ends
+ with some different values, so those values will carry through.
+*/
+
+
+static double vs;
+static int ps;
+static u_long font;
+
+
+void SaveTblFPV ()
+{
+ FlushState (); /* make sure internal state same as written */
+ vs = ips->spaceBetween;
+ ps = ics->fontSize;
+ font = StyleFontBits (ics->charStyle);
+}
+
+
+void FlushTblFPV ()
+{
+u_long curFont;
+
+ if (1 || ips->spaceBetween != vs) /* tbl will have set it to vs, */
+ { /* so set it back */
+ fprintf (f, ".vs %gi\n", ips->spaceBetween);
+ wps->spaceBetween = ips->spaceBetween;
+ }
+ if (1 || ics->fontSize != ps) /* tbl will have... */
+ {
+ fprintf (f, ".ps %d\n", ics->fontSize);
+ wcs->fontSize = ics->fontSize;
+ }
+ curFont = StyleFontBits (ics->charStyle);
+ if (1 || curFont != font) /* tbl will have... */
+ {
+ if (curFont == 0)
+ fprintf (f, ".ft R\n");
+ else if (curFont & styleItalic)
+ fprintf (f, ".ft I\n");
+ else if (curFont & styleBold)
+ fprintf (f, ".ft B\n");
+ /* now the hard part */
+ wcs->charStyle &= ~StyleFontBits (wcs->charStyle);
+ wcs->charStyle |= curFont;
+ }
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+void ResetPar ()
+{
+ inPara = 0;
+ ResetParLine ();
+}
+
+
+void ResetParLine ()
+{
+ oLen = 0;
+ breakOK = 0;
+}
+
+
+/*
+ Unconditional flush -- force output line and prevent next line
+ from being joined to it. Also handle any bottom border and
+ "extra space after paragraph" if any is needed.
+*/
+
+void Par ()
+{
+ FlushInitialState ();
+ if (inPara)
+ fprintf (f, "\n.br\n");
+ else
+ fprintf (f, ".sp\n");
+ ResetPar ();
+ if (ips->borderType != rtfNoBorderType
+ && (ips->borderFlags & borderBottom) != 0)
+ {
+ /* draw bottom border */
+ DrawLine (ips->borderType);
+ }
+
+ if (ips->spaceAfter != 0.0)
+ fprintf (f, ".sp %gi\n", ips->spaceAfter);
+
+}
+
+
+void Sect ()
+{
+char *p = NULL;
+char buf[20];
+
+ Par (); /* finish current paragraph */
+ switch (iss->breakType)
+ {
+ case rtfNoBreak:
+ break; /* nothing to do */
+ case rtfColBreak:
+ /* this is untested! */
+ sprintf (buf, ".sp |\\n(%s\n", rBottomMargin);
+ p = buf;
+ break;
+ case rtfPageBreak:
+ p = ".bp";
+ break;
+ case rtfEvenBreak:
+ p = ".if e .bp";
+ break;
+ case rtfOddBreak:
+ p = ".if o .bp";
+ break;
+ }
+ if (p != NULL)
+ {
+ FlushInitialState ();
+ fprintf (f, "%s\n", p);
+ }
+}
+
+/*
+ Document content text writing routines. These should not be
+ used to write out formatting text.
+
+ Flush() force out any collected content text, if any
+ PutString() write out a string of characters
+ PutFunnyChar() map char > 127 onto troff equivalent
+*/
+
+
+void Flush ()
+{
+ if (inPara)
+ {
+ _PutS ("\n");
+ ResetPar ();
+ }
+}
+
+
+/*
+ Dump out a piece of content text. Argument should be a string just
+ as you would write it normally, assuming no levels of indirection.
+
+ Does state flushing, beginning-of-paragraph processing, flushes
+ pending inline changes, and writes out the string (account for levels
+ of indirection).
+
+ Handles underlining if continuous underlining on, or word underlining
+ is on and string isn't " " or "\ ".
+
+ Does *not* do:
+ special char mapping (do before calling)
+ to-caps mapping (ditto)
+
+*/
+
+void PutString (s)
+char *s;
+{
+int doUnderlining = 0;
+int doStrikeThru = 0;
+char *p;
+
+ if (ics->charStyle & styleInvisible)
+ return;
+
+ if (stateChanged)
+ {
+ useInLine = 1;
+ FlushState (); /* clears stateChanged */
+ useInLine = 0;
+ }
+
+ /*
+ It's OK to hang onto inline changes until after this if-block
+ since only paragraph properties are used here; inlines only
+ affect character properties.
+ */
+
+ if (inPara == 0) /* just beginning a paragraph */
+ {
+ if (ips->spaceBefore != 0.0)
+ fprintf (f, ".sp %gi\n", ips->spaceBefore);
+ if (ips->borderType != rtfNoBorderType
+ && (ips->borderFlags & borderTop) != 0)
+ {
+ /* draw top border */
+ DrawLine (ips->borderType);
+ }
+ if (ips->firstIndent != 0.0)
+ fprintf (f, ".ti %gi\n", ips->firstIndent);
+ }
+
+ if (inLineChgs[0] != '\0')
+ {
+ _PutS (ApplyIndirection (inLineChgs));
+ inLineChgs[0] = '\0';
+ }
+
+ /* Break up long output lines. */
+ if (oLen > lineBreakLen && breakOK && s[0] != ' ')
+ _PutS ("\n"); /* (<-- turns breakOK off) */
+
+ /*
+ See if this is a natural breakpoint (single space not
+ at beginning of line). If so, remember it for following
+ characters, so long lines can be broken. If this is
+ a breakpoint, but the previous character was too, then
+ we're seeing multiple whitespace characters, and it's really
+ not a breakpoint, since breaking the line would then result
+ in loss of whitespace when troff joins lines back together
+ (it tosses trailing whitespace; this is only safe when that
+ consists of a single space).
+ */
+
+ if (oLen > 0 && s[0] == ' ' && s[1] == '\0')
+ {
+ if (breakOK)
+ breakOK = 0; /* multiple whitespace; not OK */
+ else
+ breakOK = 1;
+ }
+
+ if (ics->charStyle & styleUnderline)
+ ++doUnderlining;
+ if (ics->charStyle & styleStrikeThru)
+ ++doStrikeThru;
+ else if (ics->charStyle & styleWUnderline)
+ {
+ if (strcmp (s, " ") != 0 && strcmp (s, "\\ ") != 0)
+ ++doUnderlining;
+ }
+ if (doUnderlining || doStrikeThru)
+ {
+ if (oLen > 0) /* force onto own line if necessary */
+ {
+ p = ApplyIndirection ("\\c\n");
+ _PutS (p);
+ }
+ /* mark horizontal position */
+ p = ApplyIndirection ("\\kx");
+ _PutS (p);
+ }
+ p = ApplyIndirection (s);
+ _PutS (p);
+ if (doUnderlining)
+ {
+ /* return to marked position, draw underline */
+ p = ApplyIndirection ("\\l'|\\nxu\\(ul'");
+ _PutS (p);
+ }
+ if (doStrikeThru)
+ {
+ /* return to marked position, draw strikethrough */
+ p = ApplyIndirection ("\\v'-.2v'\\l'|\\nxu-'\\v'.2v'");
+ _PutS (p);
+ }
+
+ inPara = 1;
+}
+
+
+/*
+ Write something to current paragraph, keeping track of last char
+ and number of characters written to current line. Need oLen and
+ breakOK to know when to break output line for readability.
+
+ When a newline is written, oLen is reset.
+
+ When a non-space is written, breakOK is turned off, which handles
+ cases where PutString() saw a single space and thought a natural
+ break was in order, but that space ends up coming out in the middle
+ of control language, such as for underlining.
+*/
+
+static void _PutS (s)
+char *s;
+{
+char c;
+
+ while ((c = *s++) != '\0')
+ {
+ fputc (c, f);
+ if (c == '\n')
+ ResetParLine ();
+ else
+ ++oLen;
+ if (c != ' ')
+ breakOK = 0;
+ }
+}
+
+
+/*
+ Process a string to apply indirection.
+ Level Action
+ 0 \ -> \
+ 1 \ -> \\
+ 2 \ -> \\\\
+
+ Note: returns pointer into static buffer.
+*/
+
+static char *ApplyIndirection (s)
+char *s;
+{
+static char buf[100];
+static char *p, c;
+static int slashCount, i;
+
+ slashCount = 1; /* figure out how many \'s */
+ for (i = 0; i < indirectionLevel; i++) /* one \ maps to */
+ slashCount += slashCount;
+ p = buf;
+ while ((c = *s++) != '\0')
+ {
+ if (c != '\\')
+ *p++ = c;
+ else for (i = 0; i < slashCount; i++)
+ *p++ = '\\';
+ }
+ *p = '\0';
+ return (buf);
+}
+
+
+/*
+ Draw horizontal line. Sets vertical size not to space down very
+ much, then restores. Sets point size big for thick lines, then
+ restores.
+
+ Probably should take current boldface setting into account.
+*/
+
+static void DrawLine (type)
+int type;
+{
+int ps;
+double vs;
+char buf[100], c;
+
+ switch (type)
+ {
+ default:
+ case rtfBorderHair:
+ case rtfBorderSingle:
+ ps = 10;
+ vs = .1;
+ c = '_';
+ break;
+ case rtfBorderThick:
+ case rtfBorderShadow:
+ ps = 36;
+ vs = .3;
+ c = '_';
+ break;
+ case rtfBorderDouble:
+ ps = 5;
+ vs = .3;
+ c = '=';
+ break;
+ case rtfBorderDot:
+ ps = 10;
+ vs = .1;
+ c = '.';
+ break;
+ }
+ Flush ();
+ if (ps != wcs->fontSize) /* change point size if necessary */
+ fprintf (f, ".ps %d\n", ps);
+ fprintf (f, ".vs %gi\n", vs);
+ sprintf (buf, "\\l'%gi\\&%c'", LineLen (ids, ips), c);
+ fprintf (f, "%s\n", ApplyIndirection (buf));
+ fprintf (f, ".br\n");
+ fprintf (f, ".vs\n"); /* restore */
+ if (ps != wcs->fontSize) /* restore if was changed */
+ fprintf (f, ".ps\n");
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ Miscellaneous stuff
+*/
+
+
+static char *TabTypeStr (type)
+int type;
+{
+char *p = ""; /* assume left justified (default) */
+
+ switch (type)
+ {
+ case rtfTabDecimal: /* <- act like right tab, oh, well... */
+ case rtfTabRight: p = "R"; break;
+ case rtfTabCenter: p = "C"; break;
+ }
+ return (p);
+}
+
+
+static char *JustTypeStr (type)
+int type;
+{
+char *p = "l"; /* default if unrecognized */
+
+ switch (type)
+ {
+ default: /* <- if unrecognized */
+ case rtfQuadLeft:
+ p = "l";
+ break;
+ case rtfQuadRight:
+ p = "r";
+ break;
+ case rtfQuadCenter:
+ p = "c";
+ break;
+ case rtfQuadJust:
+ p = "b";
+ break;
+ }
+ return (p);
+}
+
+
+/*
+ Check vertical margins. Constraints:
+
+ Top margin should not extend to or below bottom margin
+ Top margin should be below header margin
+ Bottom margin MUST be above top margin (or Trap Loop Death will occur)
+*/
+
+static void CheckVMargins ()
+{
+ if (ids->topMargin + ids->bottomMargin >= ids->pageHeight)
+ {
+ fprintf (stderr, "Top margin is below bottom margin. Yow!\n");
+ exit (1);
+ }
+}
+
+
+static double LineLen (docState, parState)
+DocState *docState;
+ParState *parState;
+{
+ return (docState->pageWidth
+ - (docState->leftMargin + docState->rightMargin)
+ - parState->rightIndent);
+}
+
+
+/*
+ Comment - dump a comment to the output. The .\" and \n at
+ beginning and end are supplied automatically.
+*/
+
+
+# ifdef VARARGS
+
+/*
+ This version is for systems that have varargs.
+*/
+
+void
+Comment (va_alist)
+va_dcl
+{
+va_list args;
+char *fmt;
+
+ Flush ();
+ fprintf (f, ".\\\" ");
+ va_start (args);
+ fmt = va_arg (args, char *);
+ vfprintf (f, fmt, args);
+ va_end (args);
+ fprintf (f, "\n");
+}
+
+# else /* !VARARGS */
+
+/*
+ This version is for systems that don't have varargs.
+*/
+
+void
+Comment (fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9)
+char *fmt;
+char *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8, *a9;
+{
+ Flush ();
+ fprintf (f, ".\\\" ");
+ fprintf (f, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+ fprintf (f, "\n");
+}
+
+# endif /* VARARGS */