diff options
author | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
commit | e0c6872cf40896c7be36b11dcc744620f10adf1d (patch) | |
tree | 60335e10d2f4354b0674ec22d7b53f0f8abee672 /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.c | 1157 |
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 */ |