/* 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 # include # ifdef VARARGS # include # 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 */