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