summaryrefslogtreecommitdiff
path: root/support/word2x/latex-embed.cc
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /support/word2x/latex-embed.cc
Initial commit
Diffstat (limited to 'support/word2x/latex-embed.cc')
-rw-r--r--support/word2x/latex-embed.cc555
1 files changed, 555 insertions, 0 deletions
diff --git a/support/word2x/latex-embed.cc b/support/word2x/latex-embed.cc
new file mode 100644
index 0000000000..810e3a17ad
--- /dev/null
+++ b/support/word2x/latex-embed.cc
@@ -0,0 +1,555 @@
+/* $Id: latex-embed.cc,v 1.12 1997/04/17 20:03:19 dps Exp $ */
+/* Embed handling for *TeX output */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+#include <stdio.h>
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif /* HAVE_STRING_H */
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif /* HAVE_STRINGS_H */
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif /* HAVE_CTYPE_H */
+#include "interface.h"
+#include "tblock.h"
+#include "fmt-latex.h"
+
+
+/* For detial of this see The TeXbook p. 141) */
+typedef enum {Disp=0, DispP, Text, TextP,
+ Script, ScriptP,
+ SScript, SScriptP } style;
+
+enum TypeIndex { Op_Sup=0, Op_Sub, Op_FTop, Op_FBot, Op_Cramp };
+/* Style navigation map */
+static const style style_map[][5]=
+{
+ { Script, ScriptP, Text, TextP, DispP }, // style D
+ { ScriptP, ScriptP, TextP, TextP, DispP }, // style D'
+ { Script, ScriptP, Script, ScriptP, TextP }, // Style T
+ { ScriptP, ScriptP, ScriptP, ScriptP, TextP }, // Style T'
+ { SScript, SScriptP, SScript, SScriptP, ScriptP }, // Style S
+ { SScriptP, SScriptP, SScriptP, SScriptP, ScriptP }, // Style S'
+ { SScript, SScriptP, SScript, SScriptP, SScriptP }, // Style SS
+ { SScriptP, SScriptP, SScriptP, SScriptP, SScriptP } // Style SS'
+};
+
+static tblock *cvt_eqn(const char *, const char *, const int, const style);
+
+
+/* Skip a term */
+const char *skip_to_next(const char *ptr)
+{
+ int blevel;
+ int ign_nxt;
+
+ ign_nxt=0;
+ blevel=0;
+ while(1)
+ {
+ switch(*ptr)
+ {
+ case '\\':
+ ign_nxt=1;
+ break;
+
+ case '(':
+ if (!ign_nxt)
+ blevel++;
+ break;
+
+ case ')':
+ if (!ign_nxt)
+ blevel--;
+ if (blevel<0)
+ return ptr;
+ break;
+
+ case ',':
+ if (!ign_nxt && blevel==0)
+ return ptr;
+ break;
+
+ case '\0':
+ return ptr;
+
+ default:
+ break;
+ }
+ ptr++;
+ }
+}
+
+
+/* Equation sub-bits */
+
+/* \b bracket */
+static const char *eqn_backet(tblock *res, const char *inp,
+ const int mline, const style sty)
+{
+ const char *end;
+ tblock *r1;
+
+ end=skip_to_next(inp);
+ if (*inp!=')')
+ return NULL;
+ {
+ r1=cvt_eqn(inp, end, mline, sty);
+ res->add("\\left(");
+ res->add(*r1);
+ res->add("\\right)");
+ delete(r1);
+ }
+ return end;
+}
+
+/* \l group */
+static const char *eqn_list(tblock *res, const char *inp,
+ const int mline, const style sty)
+{
+ tblock *r1;
+ const char *end;
+
+ end=skip_to_next(inp);
+ if (*inp!=')')
+ return NULL;
+ {
+ r1=cvt_eqn(inp, end, mline, sty);
+ res->add("{");
+ res->add(*r1);
+ res->add("}");
+ delete(r1);
+ }
+ return end;
+}
+
+/* \o overlay */
+static const char *eqn_overlay(tblock *res, const char *inp,
+ const int mline, const style sty)
+{
+ const char *end;
+ tblock *r1;
+
+ end=skip_to_next(inp);
+ if (*inp!=')')
+ return NULL;
+ {
+ r1=cvt_eqn(inp, end, mline, sty);
+ res->add("\\smash{");
+ res->add(*r1);
+ res->add("}");
+ delete(r1);
+ }
+ return end;
+}
+
+/* \r root */
+static const char *eqn_root(tblock *res, const char *inp,
+ const int mline, const style sty)
+
+{
+ tblock *r1, *r2;
+ const char *mid, *end;
+
+ mid=skip_to_next(inp);
+ switch (*mid)
+ {
+ case ',':
+ end=skip_to_next(mid+1);
+ if (*end!=')')
+ return NULL;
+
+ r1=cvt_eqn(inp, mid, mline, style_map[sty][Op_Cramp]);
+ r2=cvt_eqn(mid+1, end, mline, style_map[sty][Op_Cramp]);
+ res->add("\\sqrt["); res->add(*r1);
+ res->add("]{"); // TeX syntax
+ res->add(*r2);
+ res->add('}');
+ delete(r1);
+ delete(r2);
+ break;
+
+ case ')':
+ r1=cvt_eqn(inp, mid, mline, style_map[sty][Op_Cramp]);
+ res->add("\\sqrt{");
+ res->add(*r1);
+ res->add(*r1);
+ res->add('}');
+ delete(r1);
+ break;
+
+ default:
+ break;
+ }
+ return NULL;
+}
+
+/* \f fraction */
+static const char *eqn_fract(tblock *res, const char *inp,
+ const int mline, const style sty)
+{
+ tblock *r1, *r2;
+ const char *mid, *end;
+
+ mid=skip_to_next(inp);
+ if (*mid!=',')
+ return NULL;
+
+ end=skip_to_next(mid+1);
+ if (*end!=')')
+ return NULL;
+
+ r1=cvt_eqn(inp, mid, mline, style_map[sty][Op_FTop]);
+ r2=cvt_eqn(mid+1, end, mline, style_map[sty][Op_FBot]);
+ res->add('{');
+ res->add(*r1);
+ res->add(" \\over "); // TeX syntax
+ res->add(*r2);
+ res->add('}');
+ delete(r1);
+ delete(r2);
+
+ return end;
+}
+
+
+/* Anything I do not understand */
+const char *eqn_default(tblock *res, const char *inp, const char *end,
+ const int mline, const style sty)
+{
+ tblock *r1;
+
+ res->add("{\\text{\tt \\backslash ");
+ res->add(inp, end-inp+1);
+ inp=end+1;
+ do
+ {
+ end=skip_to_next(inp);
+ r1=cvt_eqn(inp, end, mline, sty);
+ res->add(*r1);
+ if (*end!='\0')
+ res->add(*end);
+ else
+ res->add("{\\it EOF}");
+ delete(r1);
+ inp=end+1;
+ } while (*end!='\0' && *end!=')');
+ res->add("}} ");
+
+ return end;
+}
+
+/* The actual work is in this recursive procedure */
+static tblock *cvt_eqn(const char *inp, const char *stop,
+ const int mline, const style sty)
+{
+ tblock *res;
+ const char *mid, *end;
+ int l, i;
+
+ typedef const char *(*eqn_proc)(tblock *, const char *, const int,
+ const style);
+ static const struct { const char *name; eqn_proc func; } eqp[]=
+ {
+ { "b", eqn_backet },
+ { "l", eqn_list },
+ { "o", eqn_overlay },
+ { "r", eqn_root },
+ { "f", eqn_fract },
+ { NULL, NULL },
+ };
+
+ res=new(tblock);
+ while (inp<stop)
+ {
+ if (isspace(*inp) && *inp!='\n')
+ {
+ inp++;
+ continue;
+ }
+
+ switch (*inp)
+ {
+ case '\0':
+ return res;
+
+ case '\n':
+ if (mline)
+ res->add(" \\\\\n");
+ break;
+
+ case '<':
+ case '>':
+ case '=':
+ if (mline)
+ {
+ res->add(" & ");
+ res->add(*inp);
+ res->add(" & ");
+ }
+ else
+ res->add(*inp);
+ break;
+
+ case '\\':
+ inp++;
+ switch(*inp)
+ {
+ case '\0':
+ cerr<<"cvt_eqn saw \\\\0\n";
+ return res; // Safety.
+
+ case '{':
+ res->add(" \\{"); // More guesswork
+ break;
+
+ case '}':
+ res->add(" \\}"); // More guesswork
+ break;
+
+ default:
+ mid=inp;
+ for (mid=inp; !isspace(*mid) && *mid!='('; mid++) ;
+ for (end=mid; *end!='('; end++)
+ {
+ if (*end=='\0')
+ break;
+ }
+ for (i=0; (unsigned) i<N(eqp); i++)
+ {
+ if (eqp[i].name==NULL)
+ {
+ inp=eqn_default(res, inp, end, mline, sty);
+ break;
+ }
+ l=strlen(eqp[i].name);
+ if (l!=mid-inp)
+ continue;
+ if (strncasecmp(eqp[i].name, inp, l)==0)
+ {
+ if ((inp=(*(eqp[i].func))(res, end+1,
+ mline, sty))!=NULL)
+ break;
+ }
+ }
+ break;
+ }
+ break;
+ case '+':
+ case '-':
+ res->add(*inp);
+ break;
+ case '*':
+ res->add("\\times ");
+ break;
+
+ case ' ':
+ res->add("\\ ");
+ break;
+
+ case '_':
+ res->add("\\hbox{\\_}");
+ break;
+
+ default:
+ int flg=0;
+ const char *scn;
+
+ /*
+ * This section is meant to catch 72 dpi and render it as
+ * \hbox{72 dpi} but not catch 12 * 18 (which should become
+ * 12\times 18).
+ */
+ if (isdigit(*inp) || *inp=='-' || *inp=='+')
+ {
+
+ /* Scan forward to see what comes text */
+ scn=inp;
+ if (*scn=='-' || *scn=='+')
+ scn++; // Skip sign
+ while (scn<stop && isdigit(*scn))
+ scn++; // Skip number
+ if (*scn=='.')
+ {
+ scn++;
+ while (scn<stop && isdigit(*scn))
+ scn++; // Hanlde decimals number
+ }
+
+ /* Now start looking at what comes next */
+ while (scn<stop)
+ {
+ if (isspace(*scn))
+ {
+ scn++;
+ continue;
+ }
+ if (isupper(*scn) || islower(*scn))
+ flg=1;
+ else
+ flg=0;
+ break;
+ }
+ }
+
+ /*
+ * This section is meant to catch strings and render them nicely
+ * in a mbox.
+ */
+ if (islower(*inp) || isupper(*inp) || flg)
+ {
+ res->add("\\text{");
+ if (flg) // If flag set then add everything up to scn
+ {
+ while (inp<scn)
+ {
+ res->add(*inp);
+ inp++;
+ }
+ }
+
+ flg=0; // Re-use flg
+ while (inp<stop && (islower(*inp) || isupper(*inp)
+ || isspace(*inp)
+ || *inp=='_'
+ || *inp=='^'))
+ {
+ if (isspace(*inp))
+ {
+ flg=1;
+ inp++;
+ continue; // If space, just set the flag
+ }
+ if (flg)
+ res->add(' '); // If skiped a space, add one
+ flg=0; // Clear flag
+ if (*inp=='_' || *inp=='^')
+ res->add('\\');
+ res->add(*inp);
+ inp++;
+ }
+ res->add("} ");
+ inp--;
+ break;
+ }
+ res->add(*inp);
+ break;
+ }
+ inp++;
+ }
+ return res;
+}
+
+/* Equations --- need more examples here */
+static void equation(const char *txt, const docfmt *fmt, FILE *out,
+ void *d)
+{
+
+ static const cmap comment_map[]={ { '\n', "\n% (contd) % " } };
+ struct latex_data *dp;
+ tblock *cvt, eqn, *op;
+ const char *s;
+ int mline;
+
+ dp=(struct latex_data *) d;
+ cvt=map_string(txt, comment_map);
+ fprintf(out, "%%\n%% EMBED %s\n", (const char *) (*cvt));
+ delete(cvt);
+
+ for (mline=0, s=txt; *s!='\0'; s++)
+ {
+ if (*s=='\n')
+ {
+ mline=1;
+ break;
+ }
+ }
+ if (!mline)
+ eqn.add((dp->par_flg) ? "$" : "$$");
+ else
+ eqn.add("\\begin{eqnarray*}\n");
+ cvt=cvt_eqn(txt+3, txt+strlen(txt), mline, (dp->par_flg) ? Disp : Text);
+ eqn.add(*cvt);
+ delete(cvt);
+
+ if (!mline)
+ eqn.add((dp->par_flg) ? "$" : "$$%");
+ else
+ eqn.add("\n\\end{eqnarray*}\n");
+
+ op=word_wrap(eqn, "\n", "\n", fmt->maxline);
+ fputs((const char *) (*op), out);
+ fputc('\n', out);
+ delete(op);
+}
+
+
+
+/* Table of contents entries, used as a cue for stuff like sections */
+/* This code jus stashes it away for the paragraph code */
+static void add_contents(const char *txt, const docfmt *fmt, FILE *out,
+ void *d)
+{
+ const char *open, *close;
+ tblock entry;
+ struct latex_data *dp;
+
+ fmt=fmt;
+ out=out;
+ dp=(struct latex_data *) d;
+ for (open=txt; *open!='"'; open++)
+ {
+ if (*open=='\0')
+ {
+ cerr<<"Found tc entry but no openning quote\n";
+ return;
+ }
+ }
+
+ for (close=open+1; *close!='"'; close++)
+ {
+ if (*close=='\0')
+ {
+ cerr<<"Found tc entry but no closing quote\n";
+ return;
+ }
+ }
+
+ if (close-open==1)
+ {
+ cerr<<"Ignoring empty table of contents entry\n";
+ return;
+ }
+
+ while (++open<close)
+ entry.add(*open);
+ if (dp->last_tc!=NULL)
+ free((void *) dp->last_tc);
+ dp->last_tc=strdup(entry);
+
+}
+
+static struct embed emb[]=
+{
+ { "tc ", 3, add_contents }, // Table of contents line
+ { "eq ", 3, equation }, // Equations
+};
+
+void ltx_embed(const tok_seq::tok *t, const docfmt *fmt, FILE *out,
+ void *d)
+{
+ int i;
+
+ for (i=0; (unsigned) i<N(emb); i++)
+ {
+ if (strncmp(t->data.d, emb[i].key, emb[i].key_len)==0)
+ {
+ (emb[i].handle)(t->data.d, fmt, out, d);
+ return;
+ }
+ }
+ fprintf(out, "%%\n%% %s\n", t->data.d);
+}