/* $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 #ifdef HAVE_STRING_H #include #endif /* HAVE_STRING_H */ #ifdef HAVE_STRINGS_H #include #endif /* HAVE_STRINGS_H */ #ifdef HAVE_CTYPE_H #include #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 (inpadd(" \\\\\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) iadd(*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 (scnadd("\\text{"); if (flg) // If flag set then add everything up to scn { while (inpadd(*inp); inp++; } } flg=0; // Re-use flg while (inpadd(' '); // 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 (++openlast_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) idata.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); }