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

  1. /*
  2.     trf-flush.c - state and content text flushing
  3.  
  4.     Eventually, all output will end up being flushed through one
  5.     or two low-level routines, which will allow better trapping
  6.     on conditions such as initial state needing to be dumped,
  7.     diversions needing to be started, trap positions needing to
  8.     be flushed, etc.  Maybe in 1.06a2.
  9. */
  10.  
  11. # include    <stdio.h>
  12. # include    <sys/types.h>
  13. # ifdef    VARARGS
  14. # include    <varargs.h>
  15. # endif    /* VARARGS */
  16. # include    "rtf.h"
  17. # include    "rtf2troff.h"
  18.  
  19.  
  20. static int    initialStateFlushed = 0;
  21.  
  22. static int    useInLine = 0;    /* non-zero for inline char state changes */
  23. static char    inLineChgs[rtfBufSiz] = "";
  24.  
  25. /*
  26.     Whether any content text chars have been written to current
  27.     paragraph.
  28. */
  29.  
  30. static int    inPara = 0;
  31. static int    oLen = 0;
  32. static int    breakOK = 0;
  33.  
  34.  
  35. static void    FlushDocState ();
  36. static void    FlushParState ();
  37. static void    FlushCharState ();
  38. static void    FlushSACharState ();
  39. static void    Continuation ();
  40. static void    CalcInLineChanges ();
  41. static void    _PutS ();
  42. static char    *ApplyIndirection ();
  43.  
  44. static void    DrawLine ();
  45. static char    *TabTypeStr ();
  46. static char    *JustTypeStr ();
  47. static void    CheckVMargins ();
  48. static double    LineLen ();
  49.  
  50.  
  51. /*
  52.     Flush any discrepancies between state as written and current
  53.     internal state to bring the former in sync with the latter.
  54.  
  55.     Virtually all formatting text is written by this operation.
  56.  
  57.     It's assumed here, perhaps unfortunately, that things needing
  58.     a Flush() first won't occur in the middle of output line
  59.     collection.
  60. */
  61.  
  62. void FlushState ()
  63. {
  64.     /* flush */
  65.     FlushInitialState ();
  66.     if (docStateChanged)
  67.         FlushDocState ();
  68.     /* header/footer depend on some doc properties */
  69.     if (docStateChanged || sectStateChanged)
  70.         FlushSectState ();
  71.     /* para line length depends on some doc properties; ditto tabs */
  72.     if (docStateChanged || parStateChanged)
  73.         FlushParState ();
  74.     if (charStateChanged)
  75.         FlushCharState ();
  76.  
  77.     /* sync */
  78.     if (docStateChanged)
  79.         bcopy ((char *) ids, (char *) wds, (int) sizeof (DocState));
  80.     if (sectStateChanged)
  81.         bcopy ((char *) iss, (char *) wss, (int) sizeof (SectState));
  82.     if (parStateChanged)
  83.         bcopy ((char *) ips, (char *) wps, (int) sizeof (ParState));
  84.     if (charStateChanged)
  85.         bcopy ((char *) ics, (char *) wcs, (int) sizeof (CharState));
  86.  
  87.     docStateChanged = 0;
  88.     sectStateChanged = 0;
  89.     parStateChanged = 0;
  90.     charStateChanged = 0;
  91. }
  92.  
  93.  
  94. /*
  95.     This is called at the beginning of output to write out absolute
  96.     initial values for some important state stuff.  The other
  97.     state-writers usually write values relative to the last written
  98.     values, so this is needed to write absolute values that the
  99.     relative values can be relative *to*.
  100.  
  101.     Problem: it's important to avoid tripping the first pseudo-page
  102.     transition, or the header for the first page will be lost.  This
  103.     occurs when non-diverted text processing occurs or when a number
  104.     of different requests (e.g., .in) occur.  Header/footer text
  105.     processingn occurs in diversions, so that's not a problem.  To
  106.     avoid tripping the trap with requests, use things like 'in instead
  107.     of .in.  Losing a break isn't a problem since there's no content
  108.     text to write yet.
  109.  
  110.     The page length is written early and header/footer traps are
  111.     planted.  These traps stay intact.  At most, the footer trap
  112.     position might be moved.
  113.  
  114.     Tp is non-zero if a section's title page is special.
  115.  
  116.     Macros Ha, Hf, Hl and Hr are defined if/when all-page, first-page,
  117.     left-page and right-page headers are given, and the number registers
  118.     of the same name, which initially have value zero, are set to 1.
  119.     Similarly for footers.  The register Tm defines the top margins.
  120.     The registers Hp and Fp define the header and footer positions.
  121.  
  122.     HE, FO need are written to exit if there are trap loops and
  123.     to not space too much if vertical margins are weird.
  124. */
  125.  
  126. void FlushInitialState ()
  127. {
  128.     if (initialStateFlushed)
  129.         return;
  130.  
  131.     Comment ("begin initial layout setup, change as desired");
  132.  
  133.     /* check whether it appears landscape *should have* been selected */
  134.     if (ids->pageHeight < ids->pageWidth && !ids->landscape)
  135.     {
  136.         fprintf (stderr, "Turning landscape on\n");
  137.         ids->landscape = 1;
  138.     }
  139.  
  140.     if (ids->landscape)
  141.     {
  142.         if (tvers == XROFF)
  143.             fprintf (f, ".dc landscape\n");
  144.         /* reverse page height and width? */
  145.     }
  146.     fprintf (f, ".pl %gi\n", ids->pageHeight);
  147.     if (tvers == XROFF)
  148.     {
  149.     double    pLen;
  150.  
  151.         /* have to tell printer the page length in 300dpi units */
  152.         /* if not default 11in (this is orientation dependent) */
  153.         if (ids->landscape)
  154.             pLen = ids->pageWidth;
  155.         else
  156.             pLen = ids->pageHeight;
  157.         if (pLen != 11.0)
  158.             fprintf (f, ".dc length %d\n", (int) (pLen * 300));
  159.     }
  160.  
  161.     /* abandon hope, all ye who who enter here to try to read this... */
  162.  
  163.     fprintf (f, ".ad %s\n", JustTypeStr (ips->justification));
  164.     fprintf (f, ".po %gi\n", ids->leftMargin);
  165.     fprintf (f, "'in %gi\n", ips->leftIndent);    /* ' to avoid break */
  166.     fprintf (f, ".ll %gi\n", LineLen (ids, ips));
  167.     fprintf (f, ".ps %d\n", ics->fontSize);
  168.     Comment ("%gi = %gp", ips->spaceBetween, ips->spaceBetween * 72);
  169.     fprintf (f, ".vs %gi\n", ips->spaceBetween);
  170.     fprintf (f, ".ft R\n");
  171.  
  172.     /* plant traps */
  173.  
  174.     Comment ("plant header trap");
  175.     fprintf (f, ".nr %s %d\n", rTitlePageSpecial, iss->titleSpecial);
  176.     fprintf (f, ".nr %s %gi\n", rTopMargin, ids->topMargin);
  177.     fprintf (f, ".nr %s %gi\n", rHeaderPos, iss->headerPos);
  178.     fprintf (f, ".nr %s 0\n", rHeaderAll);
  179.     fprintf (f, ".nr %s 0\n", rHeaderFirst);
  180.     fprintf (f, ".nr %s 0\n", rHeaderLeft);
  181.     fprintf (f, ".nr %s 0\n", rHeaderRight);
  182.     fprintf (f, ".de %s\n", mHeader);
  183.     fprintf (f, ".if \\\\n(%s>=\\\\n(Bm \\{\\\n",
  184.                     rTopMargin, rBottomMargin);
  185.     fprintf (f, ".\ttm Trap Loop Death detected...\n");
  186.     fprintf (f, ".\tex\n");
  187.     fprintf (f, ".\\}\n");
  188.     fprintf (f, ".rs\n");
  189.     fprintf (f, ".if \\\\n(%s<\\\\n(%s 'sp |\\\\n(%su\n",
  190.             rHeaderPos, rTopMargin, rHeaderPos);
  191.     fprintf (f, ".ev 1\n");
  192.     /*fprintf (f, ".nf\n");                    /* correct? */
  193.     /* ugly stuff to select correct header text macro */
  194.     fprintf (f, ".ie (\\\\n%%=1&\\\\n(%s>0&\\\\n(%s>0) .%s\n",
  195.             rHeaderFirst, rTitlePageSpecial, mHeaderFirst);
  196.     fprintf (f, ".el \\{\\\n");
  197.     fprintf (f, ".    ie \\\\n(%s>0 \\{\\\n", rHeaderLeft);
  198.     fprintf (f, ".        ie o .%s\n", mHeaderRight);
  199.     fprintf (f, ".        el .%s\n", mHeaderLeft);
  200.     fprintf (f, ".    \\}\n");
  201.     fprintf (f, ".    el .if \\\\n(%s>0 .%s\n", rHeaderAll, mHeaderAll);
  202.     fprintf (f, ".\\}\n");
  203.     /* end ugly stuff */
  204.     fprintf (f, ".ev\n");
  205.     fprintf (f, "'sp |\\\\n(%su\n", rTopMargin);
  206.     fprintf (f, ".ns\n");
  207.     fprintf (f, "..\n");
  208.     fprintf (f, ".wh 0i %s\n", mHeader);
  209.  
  210.     Comment ("plant footer trap");
  211.     fprintf (f, ".nr %s %gi\n",
  212.             rBottomMargin, ids->pageHeight - ids->bottomMargin);
  213.     fprintf (f, ".nr %s %gi\n",
  214.             rFooterPos, ids->pageHeight - iss->footerPos);
  215.     fprintf (f, ".nr %s 0\n", rFooterAll);
  216.     fprintf (f, ".nr %s 0\n", rFooterFirst);
  217.     fprintf (f, ".nr %s 0\n", rFooterLeft);
  218.     fprintf (f, ".nr %s 0\n", rFooterRight);
  219.     fprintf (f, ".de %s\n", mFooter);
  220.     fprintf (f, ".if \\\\n(%s>\\\\n(%s 'sp |\\\\n(%su\n",
  221.             rFooterPos, rBottomMargin, rFooterPos);
  222.     fprintf (f, ".ev 1\n");
  223.     /*fprintf (f, ".nf\n");                    /* correct? */
  224.     /* ugly stuff to select correct footer text macro */
  225.     fprintf (f, ".ie (\\\\n%%=1&\\\\n(%s>0&\\\\n(%s>0) .%s\n",
  226.             rFooterFirst, rTitlePageSpecial, mFooterFirst);
  227.     fprintf (f, ".el \\{\\\n");
  228.     fprintf (f, ".    ie \\\\n(%s>0 \\{\\\n", rFooterLeft);
  229.     fprintf (f, ".        ie o .%s\n", mFooterRight);
  230.     fprintf (f, ".        el .%s\n", mFooterLeft);
  231.     fprintf (f, ".    \\}\n");
  232.     fprintf (f, ".    el .if \\\\n(%s>0 .%s\n", rFooterAll, mFooterAll);
  233.     fprintf (f, ".\\}\n");
  234.     /* end ugly stuff */
  235.     fprintf (f, ".ev\n");
  236.     fprintf (f, "'bp\n");
  237.     fprintf (f, "..\n");
  238.     fprintf (f, ".wh %gi %s\n", -ids->bottomMargin, mFooter);
  239.  
  240.     Comment ("end initial layout setup");
  241.  
  242.     /* manually sync everything that was just flushed */
  243.     wds->bottomMargin = ids->bottomMargin;
  244.     wds->bottomMargin = ids->bottomMargin;
  245.     wds->landscape = ids->landscape;
  246.     wds->leftMargin = ids->leftMargin;
  247.     wds->leftMargin = ids->leftMargin;
  248.     wds->pageHeight = ids->pageHeight;
  249.     wds->pageWidth = ids->pageWidth;
  250.     wds->rightMargin = ids->rightMargin;
  251.     wds->topMargin = ids->topMargin;
  252.     wss->footerPos = iss->footerPos;
  253.     wss->headerPos = iss->headerPos;
  254.     wss->titleSpecial = iss->titleSpecial;
  255.     wps->justification = ips->justification;
  256.     wps->leftIndent = ips->leftIndent;
  257.     wps->rightIndent = ips->rightIndent;
  258.     wps->spaceBetween = ips->spaceBetween;
  259.     wcs->fontSize = ics->fontSize;
  260.  
  261.     ++initialStateFlushed;
  262. }
  263.  
  264.  
  265. /*
  266.     Note that right margin is document property in RTF, but has the
  267.     effect of changing line length, which is handled under paragraph
  268.     property changes.  Ditto for change of default tab width.
  269. */
  270.  
  271. static void FlushDocState ()
  272. {
  273.     CheckVMargins ();
  274.     if (ids->landscape != wds->landscape)
  275.     {
  276.         /* note: once on, can't turn off */
  277.         if (ids->landscape)    /* it's now on */
  278.         {
  279.             Flush ();
  280.             if (tvers == XROFF)
  281.                 fprintf (f, ".dc landscape\n");
  282.         }
  283.     }
  284.     if (ids->pageHeight != wds->pageHeight)
  285.     {
  286.         Flush ();
  287.         fprintf (f, ".pl %gi\n", ids->pageHeight);
  288.         if (tvers == XROFF)
  289.         {
  290.         double    pLen;
  291.  
  292.         /* have to tell printer the page length in 300dpi units */
  293.         /* if not default 11in (this is orientation dependent) */
  294.  
  295.             if (ids->landscape)
  296.                 pLen = ids->pageWidth;
  297.             else
  298.                 pLen = ids->pageHeight;
  299.             if (pLen != 11.0)
  300.                 fprintf (f, ".dc length %d\n",
  301.                             (int) (pLen * 300));
  302.         }
  303.     }
  304.     if (ids->leftMargin != wds->leftMargin)
  305.     {
  306.         Flush ();
  307.         fprintf (f, ".po %gi\n", ids->leftMargin);
  308.     }
  309. }
  310.  
  311.  
  312. /*
  313.     If the top margin or the header or footer positions have
  314.     changed, redefine the registers giving their sizes.  If the
  315.     bottom margin has changed, move the trap to the right spot.
  316.     (Document and section state interact here.)
  317.  
  318.     This is also called when a macro is about to be diverted, so that
  319.     the trap position isn't set within a different environment.
  320.     (Is that necessary?)
  321.  
  322.     The really ugly thing here is to try and catch cases where the
  323.     header position is set below the top margin, and especially where the
  324.     footer position *above* the bottom margin.  The latter can result
  325.     in loops where the footer trap is invoked in a loop.
  326. */
  327.  
  328. void FlushSectState ()
  329. {
  330.     if (iss->titleSpecial != wss->titleSpecial)
  331.     {
  332.         Flush ();
  333.         fprintf (f, ".nr %s %d\n",
  334.                 rTitlePageSpecial, iss->titleSpecial);
  335.     }
  336.     if (ids->topMargin != wds->topMargin)
  337.     {
  338.         Flush ();
  339.         fprintf (f, ".nr %s %gi\n", rTopMargin, ids->topMargin);
  340.     }
  341.     if (iss->headerPos != wss->headerPos)
  342.     {
  343.         Flush ();
  344.         fprintf (f, ".nr %s %gi\n", rHeaderPos, iss->headerPos);
  345.     }
  346.     if (ids->bottomMargin != wds->bottomMargin)
  347.     {
  348.         Flush ();
  349.         fprintf (f, ".ch %s %gi\n", mHeader, -ids->bottomMargin);
  350.     }
  351.     if (iss->footerPos != wss->footerPos)
  352.     {
  353.         Flush ();
  354.         fprintf (f, ".nr %s %gi\n",
  355.                 rFooterPos, ids->pageHeight - iss->footerPos);
  356.     }
  357. }
  358.  
  359.  
  360. static void FlushParState ()
  361. {
  362. int    tabdiff;
  363. int    i;
  364.  
  365.     if (ips->justification != wps->justification)
  366.     {
  367.         Flush ();
  368.         fprintf (f, ".ad %s\n", JustTypeStr (ips->justification));
  369.     }
  370.     if (ips->leftIndent != wps->leftIndent)
  371.     {
  372.         Flush ();
  373.         fprintf (f, ".in %+gi\n", ips->leftIndent - wps->leftIndent);
  374.     }
  375.     /*
  376.         troff doesn't set right indent, rather it sets
  377.         line length (function of page width - po - rm - ri)
  378.     */
  379.     if (ids->pageWidth != wds->pageWidth
  380.         || ids->leftMargin != wds->leftMargin
  381.         || ids->rightMargin != wds->rightMargin
  382.         || ips->rightIndent != wps->rightIndent)
  383.     {
  384.         Flush ();
  385.         fprintf (f, ".ll %gi\n", LineLen (ids, ips));
  386.     }
  387.     if (ips->spaceBetween != wps->spaceBetween)
  388.     {
  389.         Flush ();
  390.         fprintf (f, ".vs %gi\n", ips->spaceBetween);
  391.     }
  392.  
  393.     /*
  394.         Determine if tabs have changed, which they will if there
  395.         are a different number of tab stops than previously, or any
  396.         of the current ones are different than those last written
  397.         out.  Change of default width is a change, too.
  398.     */
  399.  
  400.     tabdiff = 0;
  401.     if (ids->tabWidth != wds->tabWidth)
  402.         tabdiff = 1;
  403.     else if (ips->nTabs != wps->nTabs)
  404.         tabdiff = 1;
  405.     else
  406.     {
  407.         for (i = 0; i < ips->nTabs; i++)
  408.         {
  409.             if (ips->tab[i] != wps->tab[i]
  410.                 || ips->tabType[i] != wps->tabType[i])
  411.             {
  412.                 tabdiff = 1;
  413.                 break;
  414.             }
  415.         }
  416.     }
  417.     if (tabdiff)
  418.     {
  419.         Flush ();
  420.         if (ips->nTabs == 0)        /* use defaults */
  421.         {
  422.             fprintf (f, ".ta %gi", ids->tabWidth);
  423.             for (i = 1; i < maxTab; i++)
  424.                 fprintf (f, " +%gi", ids->tabWidth);
  425.         }
  426.         else
  427.         {
  428.             fprintf (f, ".ta %gi%s", ips->tab[0],
  429.                         TabTypeStr (ips->tabType[0]));
  430.             for (i = 1; i < ips->nTabs; i++)
  431.             {
  432.                 fprintf (f, " +%gi%s",
  433.                         ips->tab[i] - ips->tab[i-1],
  434.                         TabTypeStr (ips->tabType[i]));
  435.             }
  436.         }
  437.         fprintf (f, "\n");
  438.     }
  439.     if (ips->tabChar != wps->tabChar)
  440.     {
  441.         Flush ();
  442.         switch (ips->tabChar)
  443.         {
  444.         case rtfLeaderMotion:
  445.             fprintf (f, ".tc\n");
  446.             break;
  447.         case rtfLeaderDot:
  448.             fprintf (f, ".tc .\n");
  449.             break;
  450.         case rtfLeaderHyphen:
  451.             fprintf (f, ".tc -\n");
  452.             break;
  453.         case rtfLeaderUnder:
  454.         case rtfLeaderThick:
  455.             fprintf (f, ".tc _\n");
  456.             break;
  457.         }
  458.     }
  459. }
  460.  
  461.  
  462. /*
  463.     Flush character state.  Actually, if useInLine is true, this
  464.     just calculates the string of inline commands that should be
  465.     generated, and those are later flushed in PutString ().
  466. */
  467.  
  468. static void FlushCharState ()
  469. {
  470.     if (useInLine)
  471.         CalcInLineChanges ();
  472.     else
  473.         FlushSACharState ();
  474. }
  475.  
  476.  
  477. /*
  478.     Flush character state, using standalone requests.
  479.     If in a paragraph, generates a \c to cause stuff on current line
  480.     to be joined to next so extraneous space won't end up in the
  481.     output.
  482. */
  483. static void FlushSACharState ()
  484. {
  485. u_long    csFontBits, wsFontBits;
  486. int    idiff;
  487. double    ddiff;
  488.  
  489.     if (ics->fontSize != wcs->fontSize)    /* write font size */
  490.     {
  491.         Continuation ();
  492.         idiff = ics->fontSize - wcs->fontSize;
  493.         fprintf (f, ".ps %+d\n", idiff);
  494.     }
  495.     /*
  496.         Note: super/subscripts don't always have intended effect
  497.         in non-inline mode.  Output may need hand fixing.
  498.     */
  499.     if (ics->superScript != wcs->superScript)
  500.     {
  501.         Continuation ();
  502.         ddiff = wcs->superScript - ics->superScript;
  503.         fprintf (f, "'sp %gp\n", ddiff);
  504.     }
  505.     if (ics->subScript != wcs->subScript)
  506.     {
  507.         Continuation ();
  508.         ddiff = ics->subScript - wcs->subScript;
  509.         fprintf (f, "'sp %gp\n", ddiff);
  510.     }
  511.     if (ics->charStyle != wcs->charStyle)    /* write R, I, B */
  512.     {
  513.         /*
  514.             Since troff implements plain, bold and italic by
  515.             changing fonts, figure out whether the font needs
  516.             to be changed.  This doesn't understand simultaneous
  517.             bold+italic (boo-hoo), and treats it as italic.
  518.         */
  519.         csFontBits = StyleFontBits (ics->charStyle);
  520.         wsFontBits = StyleFontBits (wcs->charStyle);
  521.         if (csFontBits != wsFontBits)
  522.         {
  523.             Continuation ();
  524.             if (csFontBits == 0)    /* neither bold or italic */
  525.                 fprintf (f, ".ft R\n");
  526.             else if (csFontBits & styleItalic)
  527.                 fprintf (f, ".ft I\n");
  528.             else if (csFontBits & styleBold)
  529.                 fprintf (f, ".ft B\n");
  530.         }
  531.  
  532.         /* if smallcaps now on and wasn't before, turn on */
  533.         if ((ics->charStyle & styleSmallCaps)
  534.             && !(wcs->charStyle & styleSmallCaps))
  535.         {
  536.             Continuation ();
  537.             fprintf (f, ".ps -1\n");
  538.         }
  539.         /* if smallcaps now off and wasn't before, turn off */
  540.         if (!(ics->charStyle & styleSmallCaps)
  541.             && (wcs->charStyle & styleSmallCaps))
  542.         {
  543.             Continuation ();
  544.             fprintf (f, ".ps +1\n");
  545.         }
  546.     }
  547. }
  548.  
  549.  
  550. static void Continuation ()
  551. {
  552.     if (oLen > 0)
  553.     {
  554.         if (breakOK)
  555.             fprintf (f, "\n");
  556.         else
  557.             fprintf (f, "\\c\n");    /* need ApplyIndirection() ? */
  558.         ResetParLine ();
  559.     }
  560. }
  561.  
  562.  
  563. /*
  564.     Generate a string of inline-changes, which need to be flushed with
  565.     indirection applied.
  566. */
  567.  
  568. static void CalcInLineChanges ()
  569. {
  570. char    *picp = inLineChgs;
  571. int    csFontBits, wsFontBits;
  572. int    idiff;
  573. double    ddiff;
  574. char    c;
  575.  
  576.     *picp = '\0';
  577.     if (ics->fontSize != wcs->fontSize)    /* write font size */
  578.     {
  579.         idiff = ics->fontSize - wcs->fontSize;
  580.         c = '+';
  581.         if (idiff < 0)
  582.         {
  583.             c = '-';
  584.             idiff *= -1;
  585.         }
  586.         while (idiff > 9)
  587.         {
  588.             sprintf (picp, "\\s%c9", c);
  589.             picp += strlen (picp);
  590.             idiff -= 9;
  591.         }
  592.         sprintf (picp, "\\s%c%d", c, idiff);
  593.         picp += strlen (picp);
  594.     }
  595.     if (ics->superScript != wcs->superScript)
  596.     {
  597.         ddiff = wcs->superScript - ics->superScript;
  598.         sprintf (picp, "\\v'%gp'", ddiff);
  599.         picp += strlen (picp);
  600.     }
  601.     if (ics->subScript != wcs->subScript)
  602.     {
  603.         ddiff = ics->subScript - wcs->subScript;
  604.         sprintf (picp, "\\v'%gp'", ddiff);
  605.         picp += strlen (picp);
  606.     }
  607.     if (ics->charStyle != wcs->charStyle)    /* write R, I, B */
  608.     {
  609.         /*
  610.             Since troff implements plain, bold and italic by
  611.             changing fonts, figure out whether the font needs
  612.             to be changed.  This doesn't understand simultaneous
  613.             bold+italic (boo-hoo), and treats it as italic.
  614.         */
  615.         csFontBits = ics->charStyle & (styleBold | styleItalic);
  616.         wsFontBits = wcs->charStyle & (styleBold | styleItalic);
  617.         if (csFontBits != wsFontBits)
  618.         {
  619.             if (csFontBits == 0)    /* neither bold or italic */
  620.                 sprintf (picp, "\\fR");
  621.             else if (csFontBits & styleItalic)
  622.                 sprintf (picp, "\\fI");
  623.             else if (csFontBits & styleBold)
  624.                 sprintf (picp, "\\fB");
  625.             /* this is a NOP if no "if" was triggered above */
  626.             picp += strlen (picp);
  627.         }
  628.  
  629.         /* if smallcaps now on and wasn't before, turn on */
  630.         if ((ics->charStyle & styleSmallCaps)
  631.             && !(wcs->charStyle & styleSmallCaps))
  632.         {
  633.             sprintf (picp, "\\s-1");
  634.             picp += strlen (picp);
  635.         }
  636.         /* if smallcaps now off and wasn't before, turn off */
  637.         if (!(ics->charStyle & styleSmallCaps)
  638.             && (wcs->charStyle & styleSmallCaps))
  639.         {
  640.                 sprintf (picp, "\\s+1");
  641.                 picp += strlen (picp);
  642.         }
  643.     }
  644. }
  645.  
  646.  
  647. /*
  648.     Save font, point size and vertical spacing.  Called at beginning
  649.     of table to get an idea of the values for the parameters that tbl
  650.     will use at the beginning of each cell.  FlushTblFPV() is called
  651.     after each cell is begin, to undo this if the previous cell ends
  652.     with some different values, so those values will carry through.
  653. */
  654.  
  655.  
  656. static double    vs;
  657. static int    ps;
  658. static u_long    font;
  659.  
  660.  
  661. void SaveTblFPV ()
  662. {
  663.     FlushState ();        /* make sure internal state same as written */
  664.     vs = ips->spaceBetween;
  665.     ps = ics->fontSize;
  666.     font = StyleFontBits (ics->charStyle);
  667. }
  668.  
  669.  
  670. void FlushTblFPV ()
  671. {
  672. u_long    curFont;
  673.  
  674.     if (1 || ips->spaceBetween != vs)    /* tbl will have set it to vs, */
  675.     {                /* so set it back */
  676.         fprintf (f, ".vs %gi\n", ips->spaceBetween);
  677.         wps->spaceBetween = ips->spaceBetween;
  678.     }
  679.     if (1 || ics->fontSize != ps)    /* tbl will have... */
  680.     {
  681.         fprintf (f, ".ps %d\n", ics->fontSize);
  682.         wcs->fontSize = ics->fontSize;
  683.     }
  684.     curFont = StyleFontBits (ics->charStyle);
  685.     if (1 || curFont != font)        /* tbl will have... */
  686.     {
  687.         if (curFont == 0)
  688.             fprintf (f, ".ft R\n");
  689.         else if (curFont & styleItalic)
  690.             fprintf (f, ".ft I\n");
  691.         else if (curFont & styleBold)
  692.             fprintf (f, ".ft B\n");
  693.         /* now the hard part */
  694.         wcs->charStyle &= ~StyleFontBits (wcs->charStyle);
  695.         wcs->charStyle |= curFont;
  696.     }
  697. }
  698.  
  699.  
  700. /* ---------------------------------------------------------------------- */
  701.  
  702.  
  703. void ResetPar ()
  704. {
  705.     inPara = 0;
  706.     ResetParLine ();
  707. }
  708.  
  709.  
  710. void ResetParLine ()
  711. {
  712.     oLen = 0;
  713.     breakOK = 0;
  714. }
  715.  
  716.  
  717. /*
  718.     Unconditional flush -- force output line and prevent next line
  719.     from being joined to it.  Also handle any bottom border and
  720.     "extra space after paragraph" if any is needed.
  721. */
  722.  
  723. void Par ()
  724. {
  725.     FlushInitialState ();
  726.     if (inPara)
  727.         fprintf (f, "\n.br\n");
  728.     else
  729.         fprintf (f, ".sp\n");
  730.     ResetPar ();
  731.     if (ips->borderType != rtfNoBorderType
  732.         && (ips->borderFlags & borderBottom) != 0)
  733.     {
  734.         /* draw bottom border */
  735.         DrawLine (ips->borderType);
  736.     }
  737.  
  738.     if (ips->spaceAfter != 0.0)
  739.         fprintf (f, ".sp %gi\n", ips->spaceAfter);
  740.  
  741. }
  742.  
  743.  
  744. void Sect ()
  745. {
  746. char    *p = NULL;
  747. char    buf[20];
  748.  
  749.     Par ();        /* finish current paragraph */
  750.     switch (iss->breakType)
  751.     {
  752.     case rtfNoBreak:
  753.         break;        /* nothing to do */
  754.     case rtfColBreak:
  755.         /* this is untested! */
  756.         sprintf (buf, ".sp |\\n(%s\n", rBottomMargin);
  757.         p = buf;
  758.         break;
  759.     case rtfPageBreak:
  760.         p = ".bp";
  761.         break;
  762.     case rtfEvenBreak:
  763.         p = ".if e .bp";
  764.         break;
  765.     case rtfOddBreak:
  766.         p = ".if o .bp";
  767.         break;
  768.     }
  769.     if (p != NULL)
  770.     {
  771.         FlushInitialState ();
  772.         fprintf (f, "%s\n", p);
  773.     }
  774. }
  775.  
  776. /*
  777.     Document content text writing routines.  These should not be
  778.     used to write out formatting text.
  779.  
  780.     Flush()        force out any collected content text, if any
  781.     PutString()    write out a string of characters
  782.     PutFunnyChar()    map char > 127 onto troff equivalent
  783. */
  784.  
  785.  
  786. void Flush ()
  787. {
  788.     if (inPara)
  789.     {
  790.         _PutS ("\n");
  791.         ResetPar ();
  792.     }
  793. }
  794.  
  795.  
  796. /*
  797.     Dump out a piece of content text.  Argument should be a string just
  798.     as you would write it normally, assuming no levels of indirection.
  799.  
  800.     Does state flushing, beginning-of-paragraph processing, flushes
  801.     pending inline changes, and writes out the string (account for levels
  802.     of indirection).
  803.  
  804.     Handles underlining if continuous underlining on, or word underlining
  805.     is on and string isn't " " or "\ ".
  806.  
  807.     Does *not* do:
  808.         special char mapping    (do before calling)
  809.         to-caps mapping        (ditto)
  810.  
  811. */
  812.  
  813. void PutString (s)
  814. char    *s;
  815. {
  816. int    doUnderlining = 0;
  817. int    doStrikeThru = 0;
  818. char    *p;
  819.  
  820.     if (ics->charStyle & styleInvisible)
  821.         return;
  822.  
  823.     if (stateChanged)
  824.     {
  825.         useInLine = 1;
  826.         FlushState ();        /* clears stateChanged */
  827.         useInLine = 0;
  828.     }
  829.  
  830.     /*
  831.         It's OK to hang onto inline changes until after this if-block
  832.         since only paragraph properties are used here; inlines only
  833.         affect character properties.
  834.     */
  835.  
  836.     if (inPara == 0)        /* just beginning a paragraph */
  837.     {
  838.         if (ips->spaceBefore != 0.0)
  839.             fprintf (f, ".sp %gi\n", ips->spaceBefore);
  840.         if (ips->borderType != rtfNoBorderType
  841.             && (ips->borderFlags & borderTop) != 0)
  842.         {
  843.             /* draw top border */
  844.             DrawLine (ips->borderType);
  845.         }
  846.         if (ips->firstIndent != 0.0)
  847.             fprintf (f, ".ti %gi\n", ips->firstIndent);
  848.     }
  849.  
  850.     if (inLineChgs[0] != '\0')
  851.     {
  852.         _PutS (ApplyIndirection (inLineChgs));
  853.         inLineChgs[0] = '\0';
  854.     }
  855.  
  856.     /* Break up long output lines.  */
  857.     if (oLen > lineBreakLen && breakOK && s[0] != ' ')
  858.         _PutS ("\n");    /* (<-- turns breakOK off) */
  859.  
  860.     /*
  861.         See if this is a natural breakpoint (single space not
  862.         at beginning of line).  If so, remember it for following
  863.         characters, so long lines can be broken.  If this is
  864.         a breakpoint, but the previous character was too, then
  865.         we're seeing multiple whitespace characters, and it's really
  866.         not a breakpoint, since breaking the line would then result
  867.         in loss of whitespace when troff joins lines back together
  868.         (it tosses trailing whitespace; this is only safe when that
  869.         consists of a single space).
  870.     */
  871.  
  872.     if (oLen > 0 && s[0] == ' ' && s[1] == '\0')
  873.     {
  874.         if (breakOK)
  875.             breakOK = 0;    /* multiple whitespace; not OK */
  876.         else
  877.             breakOK = 1;
  878.     }
  879.  
  880.     if (ics->charStyle & styleUnderline)
  881.         ++doUnderlining;
  882.     if (ics->charStyle & styleStrikeThru)
  883.         ++doStrikeThru;
  884.     else if (ics->charStyle & styleWUnderline)
  885.     {
  886.         if (strcmp (s, " ") != 0 && strcmp (s, "\\ ") != 0)
  887.             ++doUnderlining;
  888.     }
  889.     if (doUnderlining || doStrikeThru)
  890.     {
  891.         if (oLen > 0)    /* force onto own line if necessary */
  892.         {
  893.             p = ApplyIndirection ("\\c\n");
  894.             _PutS (p);
  895.         }
  896.         /* mark horizontal position */
  897.         p = ApplyIndirection ("\\kx");
  898.         _PutS (p);
  899.     }
  900.     p = ApplyIndirection (s);
  901.     _PutS (p);
  902.     if (doUnderlining)
  903.     {
  904.         /* return to marked position, draw underline */
  905.         p = ApplyIndirection ("\\l'|\\nxu\\(ul'");
  906.         _PutS (p);
  907.     }
  908.     if (doStrikeThru)
  909.     {
  910.         /* return to marked position, draw strikethrough */
  911.         p = ApplyIndirection ("\\v'-.2v'\\l'|\\nxu-'\\v'.2v'");
  912.         _PutS (p);
  913.     }
  914.  
  915.     inPara = 1;
  916. }
  917.  
  918.  
  919. /*
  920.     Write something to current paragraph, keeping track of last char
  921.     and number of characters written to current line.  Need oLen and
  922.     breakOK to know when to break output line for readability.
  923.  
  924.     When a newline is written, oLen is reset.
  925.  
  926.     When a non-space is written, breakOK is turned off, which handles
  927.     cases where PutString() saw a single space and thought a natural
  928.     break was in order, but that space ends up coming out in the middle
  929.     of control language, such as for underlining.
  930. */
  931.  
  932. static void _PutS (s)
  933. char    *s;
  934. {
  935. char    c;
  936.  
  937.     while ((c = *s++) != '\0')
  938.     {
  939.         fputc (c, f);
  940.         if (c == '\n')
  941.             ResetParLine ();
  942.         else
  943.             ++oLen;
  944.         if (c != ' ')
  945.             breakOK = 0;
  946.     }
  947. }
  948.  
  949.  
  950. /*
  951.     Process a string to apply indirection.
  952.     Level    Action
  953.     0    \ -> \
  954.     1    \ -> \\
  955.     2    \ -> \\\\
  956.  
  957.     Note: returns pointer into static buffer.
  958. */
  959.  
  960. static char *ApplyIndirection (s)
  961. char    *s;
  962. {
  963. static char    buf[100];
  964. static char    *p, c;
  965. static int    slashCount, i;
  966.  
  967.     slashCount = 1;                /* figure out how many \'s */
  968.     for (i = 0; i < indirectionLevel; i++)    /* one \ maps to */
  969.         slashCount += slashCount;
  970.     p = buf;
  971.     while ((c = *s++) != '\0')
  972.     {
  973.         if (c != '\\')
  974.             *p++ = c;
  975.         else for (i = 0; i < slashCount; i++)
  976.             *p++ = '\\';
  977.     }
  978.     *p = '\0';
  979.     return (buf);
  980. }
  981.  
  982.  
  983. /*
  984.     Draw horizontal line.  Sets vertical size not to space down very
  985.     much, then restores.  Sets point size big for thick lines, then
  986.     restores.
  987.  
  988.     Probably should take current boldface setting into account.
  989. */
  990.  
  991. static void DrawLine (type)
  992. int    type;
  993. {
  994. int    ps;
  995. double    vs;
  996. char    buf[100], c;
  997.  
  998.     switch (type)
  999.     {
  1000.     default:
  1001.     case rtfBorderHair:
  1002.     case rtfBorderSingle:
  1003.         ps = 10;
  1004.         vs = .1;
  1005.         c = '_';
  1006.         break;
  1007.     case rtfBorderThick:
  1008.     case rtfBorderShadow:
  1009.         ps = 36;
  1010.         vs = .3;
  1011.         c = '_';
  1012.         break;
  1013.     case rtfBorderDouble:
  1014.         ps = 5;
  1015.         vs = .3;
  1016.         c = '=';
  1017.         break;
  1018.     case rtfBorderDot:
  1019.         ps = 10;
  1020.         vs = .1;
  1021.         c = '.';
  1022.         break;
  1023.     }
  1024.     Flush ();
  1025.     if (ps != wcs->fontSize)    /* change point size if necessary */
  1026.         fprintf (f, ".ps %d\n", ps);
  1027.     fprintf (f, ".vs %gi\n", vs);
  1028.     sprintf (buf, "\\l'%gi\\&%c'", LineLen (ids, ips), c);
  1029.     fprintf (f, "%s\n", ApplyIndirection (buf));
  1030.     fprintf (f, ".br\n");
  1031.     fprintf (f, ".vs\n");        /* restore */
  1032.     if (ps != wcs->fontSize)    /* restore if was changed */
  1033.         fprintf (f, ".ps\n");
  1034. }
  1035.  
  1036.  
  1037. /* ---------------------------------------------------------------------- */
  1038.  
  1039. /*
  1040.     Miscellaneous stuff
  1041. */
  1042.  
  1043.  
  1044. static char *TabTypeStr (type)
  1045. int    type;
  1046. {
  1047. char    *p = "";    /* assume left justified (default) */
  1048.  
  1049.     switch (type)
  1050.     {
  1051.     case rtfTabDecimal:    /* <- act like right tab, oh, well... */
  1052.     case rtfTabRight:    p = "R"; break;
  1053.     case rtfTabCenter:    p = "C"; break;
  1054.     }
  1055.     return (p);
  1056. }
  1057.  
  1058.  
  1059. static char *JustTypeStr (type)
  1060. int    type;
  1061. {
  1062. char    *p = "l";    /* default if unrecognized */
  1063.  
  1064.     switch (type)
  1065.     {
  1066.     default:        /* <- if unrecognized */
  1067.     case rtfQuadLeft:
  1068.         p = "l";
  1069.         break;
  1070.     case rtfQuadRight:
  1071.         p = "r";
  1072.         break;
  1073.     case rtfQuadCenter:
  1074.         p = "c";
  1075.         break;
  1076.     case rtfQuadJust:
  1077.         p = "b";
  1078.         break;
  1079.     }
  1080.     return (p);
  1081. }
  1082.  
  1083.  
  1084. /*
  1085.     Check vertical margins.  Constraints:
  1086.     
  1087.     Top margin should not extend to or below bottom margin
  1088.     Top margin should be below header margin
  1089.     Bottom margin MUST be above top margin (or Trap Loop Death will occur)
  1090. */
  1091.  
  1092. static void CheckVMargins ()
  1093. {
  1094.     if (ids->topMargin + ids->bottomMargin >= ids->pageHeight)
  1095.     {
  1096.         fprintf (stderr, "Top margin is below bottom margin. Yow!\n");
  1097.         exit (1);
  1098.     }
  1099. }
  1100.  
  1101.  
  1102. static double LineLen (docState, parState)
  1103. DocState    *docState;
  1104. ParState    *parState;
  1105. {
  1106.     return (docState->pageWidth
  1107.             - (docState->leftMargin + docState->rightMargin)
  1108.             - parState->rightIndent);
  1109. }
  1110.  
  1111.  
  1112. /*
  1113.     Comment - dump a comment to the output.  The .\" and \n at
  1114.     beginning and end are supplied automatically.
  1115. */
  1116.  
  1117.  
  1118. # ifdef    VARARGS
  1119.  
  1120. /*
  1121.     This version is for systems that have varargs.
  1122. */
  1123.  
  1124. void
  1125. Comment (va_alist)
  1126. va_dcl
  1127. {
  1128. va_list    args;
  1129. char    *fmt;
  1130.  
  1131.     Flush ();
  1132.     fprintf (f, ".\\\" ");
  1133.     va_start (args);
  1134.     fmt = va_arg (args, char *);
  1135.     vfprintf (f, fmt, args);
  1136.     va_end (args);
  1137.     fprintf (f, "\n");
  1138. }
  1139.  
  1140. # else    /* !VARARGS */
  1141.  
  1142. /*
  1143.     This version is for systems that don't have varargs.
  1144. */
  1145.  
  1146. void
  1147. Comment (fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9)
  1148. char    *fmt;
  1149. char    *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8, *a9;
  1150. {
  1151.     Flush ();
  1152.     fprintf (f, ".\\\" ");
  1153.     fprintf (f, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9);
  1154.     fprintf (f, "\n");
  1155. }
  1156.  
  1157. # endif    /* VARARGS */
  1158.