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