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