home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / word2x0a.zip / source / latex-embed.cc < prev    next >
C/C++ Source or Header  |  1998-04-20  |  10KB  |  546 lines

  1. /* $Id: latex-embed.cc,v 1.12 1997/04/17 20:03:19 dps Exp $ */
  2. /* Embed handling for *TeX output */
  3. #include <stdio.h>
  4. #include <string.h>
  5. #include <ctype.h>
  6. #include "interface.h"
  7. #include "tblock.h"
  8. #include "fmt-latex.h"
  9.  
  10.  
  11. /* For detial of this see The TeXbook p. 141) */
  12. typedef enum {Disp=0, DispP, Text, TextP,
  13.           Script, ScriptP,
  14.           SScript, SScriptP } style;
  15.  
  16. enum TypeIndex { Op_Sup=0, Op_Sub, Op_FTop, Op_FBot, Op_Cramp };
  17. /* Style navigation map */
  18. static const style style_map[][5]=
  19. {
  20.     { Script, ScriptP, Text, TextP, DispP },          // style D
  21.     { ScriptP, ScriptP, TextP, TextP, DispP },          // style D'
  22.     { Script, ScriptP, Script, ScriptP, TextP },      // Style T
  23.     { ScriptP, ScriptP, ScriptP, ScriptP, TextP },      // Style T'
  24.     { SScript, SScriptP, SScript, SScriptP, ScriptP },      // Style S
  25.     { SScriptP, SScriptP, SScriptP, SScriptP, ScriptP },  // Style S'
  26.     { SScript, SScriptP, SScript, SScriptP, SScriptP },      // Style SS
  27.     { SScriptP, SScriptP, SScriptP, SScriptP, SScriptP }  // Style SS'
  28. };
  29.  
  30. static tblock *cvt_eqn(const char *, const char *, const int, const style);
  31.  
  32.  
  33. /* Skip a term */
  34. const char *skip_to_next(const char *ptr)
  35. {
  36.     int blevel;
  37.     int ign_nxt;
  38.  
  39.     ign_nxt=0;
  40.     blevel=0;
  41.     while(1)
  42.     {
  43.     switch(*ptr)
  44.     {
  45.     case '\\':
  46.         ign_nxt=1;
  47.         break;
  48.  
  49.     case '(':
  50.         if (!ign_nxt)
  51.         blevel++;
  52.         break;
  53.  
  54.     case ')':
  55.         if (!ign_nxt)
  56.         blevel--;
  57.         if (blevel<0)
  58.         return ptr;
  59.         break;
  60.  
  61.     case ',':
  62.         if (!ign_nxt && blevel==0)
  63.         return ptr;
  64.         break;
  65.  
  66.     case '\0':
  67.         return ptr;
  68.  
  69.     default:
  70.         break;
  71.     }
  72.     ptr++;
  73.     }
  74. }
  75.  
  76.  
  77. /* Equation sub-bits */
  78.  
  79. /* \b bracket */
  80. static const char *eqn_backet(tblock *res, const char *inp,
  81.                   const int mline, const style sty)
  82. {
  83.     const char *end;
  84.     tblock *r1;
  85.  
  86.     end=skip_to_next(inp);
  87.     if (*inp!=')')
  88.     return NULL;
  89.     {
  90.     r1=cvt_eqn(inp, end, mline, sty);
  91.     res->add("\\left(");
  92.     res->add(*r1);
  93.     res->add("\\right)");
  94.     delete(r1);
  95.     }
  96.     return end;
  97. }
  98.  
  99. /* \l group */
  100. static const char *eqn_list(tblock *res, const char *inp,
  101.                 const int mline, const style sty)
  102. {
  103.     tblock *r1;
  104.     const char *end;
  105.  
  106.     end=skip_to_next(inp);
  107.     if (*inp!=')')
  108.     return NULL;
  109.     {
  110.     r1=cvt_eqn(inp, end, mline, sty);
  111.     res->add("{");
  112.     res->add(*r1);
  113.     res->add("}");
  114.     delete(r1);
  115.     }
  116.     return end;
  117. }
  118.  
  119. /* \o overlay */
  120. static const char *eqn_overlay(tblock *res, const char *inp,
  121.                    const int mline, const style sty)
  122. {
  123.     const char *end;
  124.     tblock *r1;
  125.  
  126.     end=skip_to_next(inp);
  127.     if (*inp!=')')
  128.     return NULL;
  129.     {
  130.     r1=cvt_eqn(inp, end, mline, sty);
  131.     res->add("\\smash{");
  132.     res->add(*r1);
  133.     res->add("}");
  134.     delete(r1);
  135.     }
  136.     return end;
  137. }
  138.  
  139. /* \r root */
  140. static const char *eqn_root(tblock *res, const char *inp,
  141.             const int mline, const style sty)
  142.  
  143. {
  144.     tblock *r1, *r2;
  145.     const char *mid, *end;
  146.  
  147.     mid=skip_to_next(inp);
  148.     switch (*mid)
  149.     {
  150.     case ',':
  151.     end=skip_to_next(mid+1);
  152.     if (*end!=')')
  153.         return NULL;
  154.  
  155.     r1=cvt_eqn(inp, mid, mline, style_map[sty][Op_Cramp]);
  156.     r2=cvt_eqn(mid+1, end, mline, style_map[sty][Op_Cramp]);
  157.     res->add("\\sqrt["); res->add(*r1);
  158.     res->add("]{"); // TeX syntax
  159.     res->add(*r2);
  160.     res->add('}');
  161.     delete(r1);
  162.     delete(r2);
  163.     break;
  164.  
  165.     case ')':
  166.     r1=cvt_eqn(inp, mid, mline, style_map[sty][Op_Cramp]);
  167.     res->add("\\sqrt{");
  168.     res->add(*r1);
  169.     res->add(*r1);
  170.     res->add('}');
  171.     delete(r1);
  172.     break;
  173.  
  174.     default:
  175.     break;
  176.     }
  177.     return NULL;
  178. }
  179.  
  180. /* \f fraction */
  181. static const char *eqn_fract(tblock *res, const char *inp,
  182.             const int mline, const style sty)
  183. {
  184.     tblock *r1, *r2;
  185.     const char *mid, *end;
  186.  
  187.     mid=skip_to_next(inp);
  188.     if (*mid!=',')
  189.     return NULL;
  190.  
  191.     end=skip_to_next(mid+1);
  192.     if (*end!=')')
  193.     return NULL;
  194.  
  195.     r1=cvt_eqn(inp, mid, mline, style_map[sty][Op_FTop]);
  196.     r2=cvt_eqn(mid+1, end, mline, style_map[sty][Op_FBot]);
  197.     res->add('{');
  198.     res->add(*r1);
  199.     res->add(" \\over "); // TeX syntax
  200.     res->add(*r2);
  201.     res->add('}');
  202.     delete(r1);
  203.     delete(r2);
  204.  
  205.     return end;
  206. }
  207.  
  208.  
  209. /* Anything I do not understand */
  210. const char *eqn_default(tblock *res, const char *inp, const char *end,
  211.             const int mline, const style sty)
  212. {
  213.     tblock *r1;
  214.  
  215.     res->add("{\\text{\tt \\backslash ");
  216.     res->add(inp, end-inp+1);
  217.     inp=end+1;
  218.     do
  219.     {
  220.     end=skip_to_next(inp);
  221.     r1=cvt_eqn(inp, end, mline, sty);
  222.     res->add(*r1);
  223.     if (*end!='\0')
  224.         res->add(*end);
  225.     else
  226.         res->add("{\\it EOF}");
  227.     delete(r1);
  228.     inp=end+1;
  229.     } while (*end!='\0' && *end!=')');
  230.     res->add("}} ");
  231.  
  232.     return end;
  233. }
  234.  
  235. /* The actual work is in this recursive procedure */
  236. static tblock *cvt_eqn(const char *inp, const char *stop,
  237.                const int mline, const style sty)
  238. {
  239.     tblock *res;
  240.     const char *mid, *end;
  241.     int l, i;
  242.  
  243.     typedef const char *(*eqn_proc)(tblock *, const char *, const int,
  244.                     const style);
  245.     static const struct { const char *name; eqn_proc func; } eqp[]=
  246.     {
  247.     { "b", eqn_backet },
  248.     { "l", eqn_list },
  249.     { "o", eqn_overlay },
  250.     { "r", eqn_root },
  251.     { "f", eqn_fract },
  252.     { NULL, NULL },
  253.     };
  254.     
  255.     res=new(tblock);
  256.     while (inp<stop)
  257.     {
  258.     if (isspace(*inp) && *inp!='\n')
  259.     {
  260.         inp++;
  261.         continue;
  262.     }
  263.  
  264.     switch (*inp)
  265.     {
  266.     case '\0':
  267.         return res;
  268.     
  269.     case '\n':
  270.         if (mline)
  271.         res->add(" \\\\\n");
  272.         break;
  273.  
  274.     case '<':
  275.     case '>':
  276.     case '=':
  277.         if (mline)
  278.         {
  279.         res->add(" & ");
  280.         res->add(*inp);
  281.         res->add(" & ");
  282.         }
  283.         else
  284.         res->add(*inp);
  285.         break;
  286.  
  287.     case '\\':
  288.         inp++;
  289.         switch(*inp)
  290.         {
  291.         case '\0':
  292.         cerr<<"cvt_eqn saw \\\\0\n";
  293.         return res;    // Safety.
  294.  
  295.         case '{':
  296.         res->add(" \\{"); // More guesswork
  297.         break;
  298.  
  299.         case '}':
  300.         res->add(" \\}"); // More guesswork
  301.         break;
  302.  
  303.         default:
  304.         mid=inp;
  305.         for (mid=inp; !isspace(*mid) && *mid!='('; mid++) ;
  306.             for (end=mid; *end!='('; end++)
  307.         {
  308.             if (*end=='\0')
  309.             break;
  310.         }
  311.         for (i=0; (unsigned) i<N(eqp); i++)
  312.         {
  313.             if (eqp[i].name==NULL)
  314.             {
  315.             inp=eqn_default(res, inp, end, mline, sty);
  316.             break;
  317.             }
  318.             l=strlen(eqp[i].name);
  319.             if (l!=mid-inp)
  320.             continue;
  321.             if (strncasecmp(eqp[i].name, inp, l)==0)
  322.             {
  323.             if ((inp=(*(eqp[i].func))(res, end+1,
  324.                           mline, sty))!=NULL)
  325.                 break;
  326.             }
  327.         }
  328.         break;
  329.         }
  330.         break;
  331.     case '+':
  332.     case '-':
  333.         res->add(*inp);
  334.         break;
  335.     case '*':
  336.         res->add("\\times ");
  337.         break;
  338.  
  339.     case ' ':
  340.         res->add("\\ ");
  341.         break;
  342.  
  343.     case '_':
  344.         res->add("\\hbox{\\_}");
  345.         break;
  346.  
  347.     default:
  348.         int flg=0;
  349.         const char *scn;
  350.  
  351.         /*
  352.          * This section is meant to catch 72 dpi and render it as
  353.          * \hbox{72 dpi} but not catch 12 * 18 (which should become
  354.          * 12\times 18).
  355.          */
  356.         if (isdigit(*inp) || *inp=='-' || *inp=='+')
  357.         {
  358.  
  359.         /* Scan forward to see what comes text */
  360.         scn=inp;        
  361.         if (*scn=='-' || *scn=='+')
  362.             scn++;    // Skip sign    
  363.         while (scn<stop && isdigit(*scn))
  364.             scn++;    // Skip number
  365.         if (*scn=='.')
  366.         {
  367.             scn++;
  368.             while (scn<stop && isdigit(*scn))
  369.             scn++;    //  Hanlde decimals number
  370.         }
  371.  
  372.         /* Now start looking at what comes next */
  373.         while (scn<stop)
  374.         {
  375.             if (isspace(*scn))
  376.             {
  377.             scn++;
  378.             continue;
  379.             }
  380.             if (isupper(*scn) || islower(*scn))
  381.             flg=1;
  382.             else
  383.             flg=0;
  384.             break;
  385.         }
  386.         }
  387.  
  388.         /*
  389.          * This section is meant to catch strings and render them nicely
  390.          * in a mbox.
  391.          */
  392.         if (islower(*inp) || isupper(*inp) || flg)
  393.         {
  394.         res->add("\\text{");         
  395.         if (flg)    // If flag set then add everything up to scn
  396.         {
  397.             while (inp<scn)
  398.             {
  399.             res->add(*inp);
  400.             inp++;
  401.             }
  402.         }
  403.  
  404.         flg=0;        // Re-use flg
  405.         while (inp<stop && (islower(*inp) || isupper(*inp)
  406.                     || isspace(*inp)
  407.                     || *inp=='_'
  408.                     || *inp=='^'))
  409.         {
  410.             if (isspace(*inp))
  411.             {
  412.             flg=1;
  413.             inp++;
  414.             continue;      // If space, just set the flag
  415.             }
  416.             if (flg)
  417.             res->add(' ');      // If skiped a space, add one
  418.             flg=0;          // Clear flag
  419.             if (*inp=='_' || *inp=='^')
  420.             res->add('\\');
  421.             res->add(*inp);
  422.             inp++;
  423.         }
  424.         res->add("} ");
  425.         inp--;
  426.         break;
  427.         }
  428.         res->add(*inp);
  429.         break;
  430.     }
  431.     inp++;
  432.     }
  433.     return res;
  434. }
  435.  
  436. /* Equations --- need more examples here */
  437. static void equation(const char *txt, const docfmt *fmt, FILE *out,
  438.              void *d)
  439. {
  440.  
  441.     static const cmap comment_map[]={ { '\n', "\n% (contd) % " } };
  442.     struct latex_data *dp;
  443.     tblock *cvt, eqn, *op;
  444.     const char *s;
  445.     int mline;
  446.     
  447.     dp=(struct latex_data *) d;
  448.     cvt=map_string(txt, comment_map);
  449.     fprintf(out, "%%\n%% EMBED %s\n", (const char *) (*cvt));
  450.     delete(cvt);
  451.  
  452.     for (mline=0, s=txt; *s!='\0'; s++)
  453.     {
  454.     if (*s=='\n')
  455.     {
  456.         mline=1;
  457.         break;
  458.     }
  459.     }
  460.     if (!mline)
  461.     eqn.add((dp->par_flg) ? "$" : "$$");
  462.     else
  463.     eqn.add("\\begin{eqnarray*}\n");
  464.     cvt=cvt_eqn(txt+3, txt+strlen(txt), mline, (dp->par_flg) ? Disp : Text);
  465.     eqn.add(*cvt);
  466.     delete(cvt);
  467.  
  468.     if (!mline)
  469.     eqn.add((dp->par_flg) ? "$" : "$$%");
  470.     else
  471.     eqn.add("\n\\end{eqnarray*}\n");
  472.  
  473.     op=word_wrap(eqn, "\n", "\n", fmt->maxline);
  474.     fputs((const char *) (*op), out);
  475.     fputc('\n', out);
  476.     delete(op);
  477. }
  478.     
  479.  
  480.  
  481. /* Table of contents entries, used as a cue for stuff like sections */
  482. /* This code jus stashes it away for the paragraph code */
  483. static void add_contents(const char *txt, const docfmt *fmt, FILE *out,
  484.              void *d)
  485. {
  486.     const char *open, *close;
  487.     tblock entry;
  488.     struct latex_data *dp;
  489.  
  490.     fmt=fmt;
  491.     out=out;
  492.     dp=(struct latex_data *) d;
  493.     for (open=txt; *open!='"'; open++)
  494.     {
  495.     if (*open=='\0')
  496.     {
  497.         cerr<<"Found tc entry but no openning quote\n";
  498.         return;
  499.     }
  500.     }
  501.     
  502.     for (close=open+1; *close!='"'; close++)
  503.     {
  504.     if (*close=='\0')
  505.     {
  506.         cerr<<"Found tc entry but no closing quote\n";
  507.         return;
  508.     }
  509.     }
  510.  
  511.     if (close-open==1)
  512.     {
  513.     cerr<<"Ignoring empty table of contents entry\n";
  514.     return;
  515.     }
  516.  
  517.     while (++open<close)
  518.     entry.add(*open);
  519.     if (dp->last_tc!=NULL)
  520.     free((void *) dp->last_tc);
  521.     dp->last_tc=strdup(entry);
  522.  
  523. }
  524.  
  525. static struct embed emb[]=
  526. {
  527.     { "tc ", 3, add_contents },    // Table of contents line
  528.     { "eq ", 3, equation },    // Equations
  529. };
  530.  
  531. void ltx_embed(const tok_seq::tok *t, const docfmt *fmt, FILE *out,
  532.            void *d)
  533.     int i;
  534.  
  535.     for (i=0; (unsigned) i<N(emb); i++)
  536.     {
  537.     if (strncmp(t->data.d, emb[i].key, emb[i].key_len)==0)
  538.     {
  539.         (emb[i].handle)(t->data.d, fmt, out, d);
  540.         return;
  541.     }
  542.     }
  543.     fprintf(out, "%%\n%% %s\n", t->data.d);
  544. }
  545.