home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Borland Programmer's Resource
/
Borland_Programmers_Resource_CD_1995.iso
/
utils
/
rtfprsr
/
trf_flus.c
< prev
next >
Wrap
Text File
|
1995-05-18
|
27KB
|
1,158 lines
/*
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 */