]> git.lyx.org Git - lyx.git/blob - src/tex2lyx/text.C
28e53c184c3134043f0d4dd17686343299748e88
[lyx.git] / src / tex2lyx / text.C
1 /** The .tex to .lyx converter
2     \author André Pönitz (2003)
3  */
4
5 // {[(
6
7 #include <config.h>
8
9 #include "tex2lyx.h"
10 #include "context.h"
11 #include "FloatList.h"
12 #include "support/lstrings.h"
13 #include "support/tostr.h"
14
15 #include <iostream>
16 #include <map>
17 #include <sstream>
18 #include <vector>
19
20 using std::cerr;
21 using std::endl;
22 using std::map;
23 using std::ostream;
24 using std::ostringstream;
25 using std::string;
26 using std::vector;
27
28 using lyx::support::rtrim;
29 using lyx::support::suffixIs;
30
31
32 // thin wrapper around parse_text using a string
33 string parse_text(Parser & p, unsigned flags, const bool outer,
34                   Context & context)
35 {
36         ostringstream os;
37         parse_text(p, os, flags, outer, context);
38         return os.str();
39 }
40
41 // parses a subdocument, usually useful in insets (whence the name)
42 void parse_text_in_inset(Parser & p, ostream & os, unsigned flags, bool outer,
43                 Context & context)
44 {
45         Context newcontext(true, context.textclass);
46         parse_text(p, os, flags, outer, newcontext);
47         newcontext.check_end_layout(os);
48 }
49
50
51 // parses a paragraph snippet, useful for example for \emph{...}
52 void parse_text_snippet(Parser & p, ostream & os, unsigned flags, bool outer,
53                 Context & context)
54 {
55         Context newcontext(false, context.textclass);
56         parse_text(p, os, flags, outer, newcontext);
57         // should not be needed
58         newcontext.check_end_layout(os);
59 }
60
61
62 namespace {
63
64 char const * known_latex_commands[] = { "ref", "cite", "label", "index",
65 "printindex", "pageref", "url", 0 };
66
67 // LaTeX names for quotes
68 char const * known_quotes[] = { "glqq", "grqq", "quotedblbase",
69 "textquotedblleft", "quotesinglbase", "guilsinglleft", "guilsinglright", 0};
70
71 // the same as known_quotes with .lyx names
72 char const * known_coded_quotes[] = { "gld", "grd", "gld",
73 "grd", "gls", "fls", "frd", 0};
74
75 char const * known_sizes[] = { "tiny", "scriptsize", "footnotesize",
76 "small", "normalsize", "large", "Large", "LARGE", "huge", "Huge", 0};
77
78 char const * known_coded_sizes[] = { "tiny", "scriptsize", "footnotesize",
79 "small", "normal", "large", "larger", "largest",  "huge", "giant", 0};
80
81 // splits "x=z, y=b" into a map
82 map<string, string> split_map(string const & s)
83 {
84         map<string, string> res;
85         vector<string> v;
86         split(s, v);
87         for (size_t i = 0; i < v.size(); ++i) {
88                 size_t const pos   = v[i].find('=');
89                 string const index = v[i].substr(0, pos);
90                 string const value = v[i].substr(pos + 1, string::npos);
91                 res[trim(index)] = trim(value);
92         }
93         return res;
94 }
95
96
97 void begin_inset(ostream & os, string const & name)
98 {
99         os << "\n\\begin_inset " << name;
100 }
101
102
103 void end_inset(ostream & os)
104 {
105         os << "\n\\end_inset \n\n";
106 }
107
108
109 void skip_braces(Parser & p)
110 {
111         if (p.next_token().cat() != catBegin)
112                 return;
113         p.get_token();
114         if (p.next_token().cat() == catEnd) {
115                 p.get_token();
116                 return;
117         }
118         p.putback();
119 }
120
121
122 void handle_ert(ostream & os, string const & s, Context const & context)
123 {
124         Context newcontext(true, context.textclass);
125         begin_inset(os, "ERT");
126         os << "\nstatus Collapsed\n";
127         newcontext.check_layout(os);
128         for (string::const_iterator it = s.begin(), et = s.end(); it != et; ++it) {
129                 if (*it == '\\')
130                         os << "\n\\backslash \n";
131                 else
132                         os << *it;
133         }
134         newcontext.check_end_layout(os);
135         end_inset(os);
136 }
137
138
139 struct isLayout {
140         isLayout(string const name) : name_(name) {}
141         bool operator()(LyXLayout_ptr const & ptr) {
142                 return ptr.get() && ptr->latexname() == name_;
143         }
144 private:
145         string const name_;
146 };
147
148
149 LyXLayout_ptr findLayout(LyXTextClass const & textclass,
150                          string const & name) 
151 {
152         LyXTextClass::const_iterator it  = textclass.begin();
153         LyXTextClass::const_iterator end = textclass.end();
154         it = std::find_if(it, end, isLayout(name));
155         return (it == end) ? LyXLayout_ptr() : *it;
156 }
157
158
159 void output_command_layout(ostream & os, Parser & p, bool outer,
160                            Context & parent_context,
161                            LyXLayout_ptr newlayout)
162 {
163 //      parent_context.dump(os, "#parent_context before output_command_layout");
164         parent_context.check_end_layout(os);
165         Context context(true, parent_context.textclass, newlayout,
166                         parent_context.layout);
167         context.check_layout(os);
168         if (context.layout->optionalargs > 0) {
169                 string s; 
170                 if (p.next_token().character() == '[') {
171                         p.get_token(); // eat '['
172                         begin_inset(os, "OptArg\n");
173                         os << "collapsed true\n";
174                         parse_text_in_inset(p, os, FLAG_BRACK_LAST, outer, context);
175                         end_inset(os);
176                 }
177         }
178         parse_text_snippet(p, os, FLAG_ITEM, outer, context);
179         context.check_end_layout(os);   
180 //      context.dump(os, "#context after output_command_layout");
181 //      parent_context.dump(os, "#parent_context after output_command_layout");
182 }
183
184
185 void parse_environment(Parser & p, ostream & os, bool outer,
186                        Context & parent_context)
187 {
188 //      parent_context.dump(os, "#parent_context before parse_environment");
189         LyXLayout_ptr newlayout;
190         string const name = p.getArg('{', '}');
191         const bool is_starred = suffixIs(name, '*');
192         string const unstarred_name = rtrim(name, "*");
193         active_environments.push_back(name);
194         if (is_math_env(name)) {
195                 parent_context.check_layout(os);
196                 begin_inset(os, "Formula ");
197                 os << "\\begin{" << name << "}";
198                 parse_math(p, os, FLAG_END, MATH_MODE);
199                 os << "\\end{" << name << "}";
200                 end_inset(os);
201         } else if (name == "tabular") {
202                 parent_context.check_layout(os);
203                 begin_inset(os, "Tabular ");
204                 handle_tabular(p, os, parent_context);
205                 end_inset(os);
206         } else if (parent_context.textclass.floats().typeExist(unstarred_name)) {
207                 parent_context.check_layout(os);
208                 begin_inset(os, "Float " + unstarred_name + "\n");
209                 if (p.next_token().asInput() == "[") {
210                         os << "placement " << p.getArg('[', ']') << '\n';
211                 }
212                 os << "wide " << tostr(is_starred)
213                    << "\ncollapsed false\n";
214                 parse_text_in_inset(p, os, FLAG_END, outer, parent_context);
215                         end_inset(os);
216         } else if (name == "center") {
217                 parse_text(p, os, FLAG_END, outer, parent_context);
218                 // The single '=' is meant here.
219         } else if ((newlayout = findLayout(parent_context.textclass, name)).get() &&
220                    newlayout->isEnvironment()) {
221                 Context context(true, parent_context.textclass, newlayout,
222                                 parent_context.layout);
223                 parent_context.check_end_layout(os);
224 //              context.dump(os, "#context in parse_environment");
225                 switch (context.layout->latextype) {
226                 case  LATEX_LIST_ENVIRONMENT:
227                         context.extra_stuff = "\\labelwidthstring "
228                                 + p.verbatim_item() + '\n';
229                         break;
230                 case  LATEX_BIB_ENVIRONMENT:
231                         p.verbatim_item(); // swallow next arg
232                         break;
233                 default:
234                         break;
235                 }
236                 //context.check_layout(os);
237                 parse_text(p, os, FLAG_END, outer, context);
238 //              context.dump(os, "#context after parse_environment");
239                 context.check_end_layout(os);
240         } else {
241                 parent_context.check_layout(os);
242                 handle_ert(os, "\\begin{" + name + "}", parent_context);
243                 parse_text_snippet(p, os, FLAG_END, outer, parent_context);
244                 handle_ert(os, "\\end{" + name + "}", parent_context);
245         }
246 }
247
248 } // anonymous namespace
249
250
251
252
253 void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
254                 Context & context)
255 {
256         LyXLayout_ptr newlayout;
257         while (p.good()) {
258                 Token const & t = p.get_token();
259
260 #ifdef FILEDEBUG
261                 cerr << "t: " << t << " flags: " << flags << "\n";
262 #endif
263
264                 if (flags & FLAG_ITEM) {
265                         if (t.cat() == catSpace)
266                                 continue;
267
268                         flags &= ~FLAG_ITEM;
269                         if (t.cat() == catBegin) {
270                                 // skip the brace and collect everything to the next matching
271                                 // closing brace
272                                 flags |= FLAG_BRACE_LAST;
273                                 continue;
274                         }
275
276                         // handle only this single token, leave the loop if done
277                         flags |= FLAG_LEAVE;
278                 }
279
280                 if (t.character() == ']' && (flags & FLAG_BRACK_LAST)) 
281                         return;
282
283                 //
284                 // cat codes
285                 //
286                 if (t.cat() == catMath) {
287                         // we are inside some text mode thingy, so opening new math is allowed
288                         context.check_layout(os);
289                         begin_inset(os, "Formula ");
290                         Token const & n = p.get_token();
291                         if (n.cat() == catMath && outer) {
292                                 // TeX's $$...$$ syntax for displayed math
293                                 os << "\\[";
294                                 parse_math(p, os, FLAG_SIMPLE, MATH_MODE);
295                                 os << "\\]";
296                                 p.get_token(); // skip the second '$' token
297                         } else {
298                                 // simple $...$  stuff
299                                 p.putback();
300                                 os << '$';
301                                 parse_math(p, os, FLAG_SIMPLE, MATH_MODE);
302                                 os << '$';
303                         }
304                         end_inset(os);
305                 }
306
307                 else if (t.cat() == catSuper || t.cat() == catSub)
308                         cerr << "catcode " << t << " illegal in text mode\n";
309
310                 // Basic support for english quotes. This should be
311                 // extended to other quotes, but is not so easy (a
312                 // left english quote is the same as a right german
313                 // quote...)
314                 else if (t.asInput() == "`" 
315                          && p.next_token().asInput() == "`") {
316                         context.check_layout(os);
317                         begin_inset(os, "Quotes ");
318                         os << "eld";
319                         end_inset(os);
320                         p.get_token();
321                         skip_braces(p);
322                 }       
323                 else if (t.asInput() == "'" 
324                          && p.next_token().asInput() == "'") {
325                         context.check_layout(os);
326                         begin_inset(os, "Quotes ");
327                         os << "erd";
328                         end_inset(os);
329                         p.get_token();
330                         skip_braces(p);
331                 }       
332
333
334                 else if (t.cat() == catLetter ||
335                                t.cat() == catSpace ||
336                                t.cat() == catOther ||
337                                t.cat() == catAlign ||
338                                t.cat() == catParameter) {
339                         context.check_layout(os);
340                         os << t.character();
341                 }
342
343                 else if (t.cat() == catNewline) {
344                         if (p.next_token().cat() == catNewline) {
345                                 p.get_token();
346                                 context.need_layout = true;
347                                 // this should be done by the parser already
348                                 cerr << "what are we doing here?" << endl;
349                         } else {
350                                 os << " "; // note the space
351                         }
352                 }
353
354                 else if (t.cat() == catActive) {
355                         context.check_layout(os);
356                         if (t.character() == '~') {
357                                 if (context.layout->free_spacing)
358                                         os << ' ';
359                                 else 
360                                         os << "\\InsetSpace ~\n";
361                         } else
362                                 os << t.character();
363                 }
364
365                 else if (t.cat() == catBegin) {
366 // FIXME??? 
367                         // special handling of size changes
368                         context.check_layout(os);
369                         bool const is_size = is_known(p.next_token().cs(), known_sizes);
370                         Context newcontext(false, context.textclass);
371 //                      need_end_layout = false;
372                         string const s = parse_text(p, FLAG_BRACE_LAST, outer, newcontext);
373 //                      need_end_layout = true;
374                         if (s.empty() && p.next_token().character() == '`')
375                                 ; // ignore it in  {}``
376                         else if (is_size || s == "[" || s == "]" || s == "*")
377                                 os << s;
378                         else {
379                                 handle_ert(os, "{", context);
380                                 os << s;
381                                 handle_ert(os, "}", context);
382                         }
383                 }
384
385                 else if (t.cat() == catEnd) {
386                         if (flags & FLAG_BRACE_LAST) {
387                                 context.check_end_layout(os);
388                                 return;
389                         }
390                         cerr << "stray '}' in text\n";
391                         handle_ert(os, "}", context);
392                 }
393
394                 else if (t.cat() == catComment)
395                         handle_comment(p);
396
397                 //
398                 // control sequences
399                 //
400
401                 else if (t.cs() == "(") {
402                         context.check_layout(os);
403                         begin_inset(os, "Formula");
404                         os << " \\(";
405                         parse_math(p, os, FLAG_SIMPLE2, MATH_MODE);
406                         os << "\\)";
407                         end_inset(os);
408                 }
409
410                 else if (t.cs() == "[") {
411                         context.check_layout(os);
412                         begin_inset(os, "Formula");
413                         os << " \\[";
414                         parse_math(p, os, FLAG_EQUATION, MATH_MODE);
415                         os << "\\]";
416                         end_inset(os);
417                 }
418
419                 else if (t.cs() == "begin")
420                         parse_environment(p, os, outer, context);
421
422                 else if (t.cs() == "end") {
423                         if (flags & FLAG_END) {
424                                 // eat environment name
425                                 string const name = p.getArg('{', '}');
426                                 if (name != active_environment())
427                                         cerr << "\\end{" + name + "} does not match \\begin{"
428                                                 + active_environment() + "}\n";
429                                 active_environments.pop_back();
430                                 context.check_end_layout(os);
431                                 return;
432                         }
433                         p.error("found 'end' unexpectedly");
434                 }
435
436                 else if (t.cs() == "item") {
437                         // should be done automatically by Parser::tokenize
438                         //p.skip_spaces();
439                         string s; 
440                         if (p.next_token().character() == '[') {
441                                 p.get_token(); // eat '['
442                                 Context newcontext(false, context.textclass);
443                                 s = parse_text(p, FLAG_BRACK_LAST, outer, newcontext);
444                         }
445                         context.need_layout = true;
446                         context.check_layout(os);
447                         if (s.size())
448                                 os << s << ' ';
449                 }
450
451                 else if (t.cs() == "def") {
452                         string name = p.get_token().cs();
453                         while (p.next_token().cat() != catBegin)
454                                 name += p.get_token().asString();
455                         handle_ert(os, "\\def\\" + name + '{' + p.verbatim_item() + '}', context);
456                 }
457
458                 else if (t.cs() == "par") {
459                         p.skip_spaces();
460                         context.check_end_layout(os);
461                         context.need_layout = true;
462 //                      if (p.next_token().cs() != "\\begin")
463 //                              handle_par(os);
464                         //cerr << "next token: '" << p.next_token().cs() << "'\n";
465                 }
466
467                 // Must attempt to parse "Section*" before "Section".
468                 else if ((p.next_token().asInput() == "*") &&
469                          // The single '=' is meant here.
470                          (newlayout = findLayout(context.textclass,
471                                                  t.cs() + '*')).get() &&
472                          newlayout->isCommand()) {
473                         p.get_token();
474                         output_command_layout(os, p, outer, context, newlayout);
475                 }
476
477                 // The single '=' is meant here.
478                 else if ((newlayout = findLayout(context.textclass, t.cs())).get() &&
479                          newlayout->isCommand()) {
480                         output_command_layout(os, p, outer, context, newlayout);
481                 }
482
483                 else if (t.cs() == "includegraphics") {
484                         map<string, string> opts = split_map(p.getArg('[', ']'));
485                         string name = p.verbatim_item();
486                         
487                         context.check_layout(os);
488                         begin_inset(os, "Graphics ");
489                         os << "\n\tfilename " << name << '\n';
490                         if (opts.find("width") != opts.end())
491                                 os << "\twidth " << opts["width"] << '\n';
492                         if (opts.find("height") != opts.end())
493                                 os << "\theight " << opts["height"] << '\n';
494                         end_inset(os);
495                 }
496                 
497                 else if (t.cs() == "footnote") {
498                         context.check_layout(os);
499                         begin_inset(os, "Foot\n");
500                         os << "collapsed true\n";
501                         parse_text_in_inset(p, os, FLAG_ITEM, false, context);
502                         end_inset(os);
503                 }
504
505                 else if (t.cs() == "marginpar") {
506                         context.check_layout(os);
507                         begin_inset(os, "Marginal\n");
508                         os << "collapsed true\n";
509                         parse_text_in_inset(p, os, FLAG_ITEM, false, context);
510                         end_inset(os);
511                 }
512
513                 else if (t.cs() == "ensuremath") {
514                         context.check_layout(os);
515                         Context newcontext(false, context.textclass);
516                         string s = parse_text(p, FLAG_ITEM, false, newcontext);
517                         if (s == "±" || s == "³" || s == "²" || s == "µ")
518                                 os << s;
519                         else
520                                 handle_ert(os, "\\ensuremath{" + s + "}",
521                                            context);
522                 }
523
524                 else if (t.cs() == "hfill") {
525                         context.check_layout(os);
526                         os << "\n\\hfill\n";
527                         skip_braces(p);
528                 }
529
530                 else if (t.cs() == "makeindex" || t.cs() == "maketitle")
531                         skip_braces(p); // swallow this
532
533                 else if (t.cs() == "tableofcontents") {
534                         context.check_layout(os);
535                         begin_inset(os, "LatexCommand ");
536                         os << '\\' << t.cs() << "{}\n";
537                         end_inset(os);
538                         skip_braces(p); // swallow this
539                 }
540
541
542                 else if (t.cs() == "textrm") {
543                         context.check_layout(os);
544                         os << "\n\\family roman \n";
545                         parse_text_snippet(p, os, FLAG_ITEM, outer, context);
546                         os << "\n\\family default \n";
547                 }
548
549                 else if (t.cs() == "textsf") {
550                         context.check_layout(os);
551                         os << "\n\\family sans \n";
552                         parse_text_snippet(p, os, FLAG_ITEM, outer, context);
553                         os << "\n\\family default \n";
554                 }
555
556                 else if (t.cs() == "texttt") {
557                         context.check_layout(os);
558                         os << "\n\\family typewriter \n";
559                         parse_text_snippet(p, os, FLAG_ITEM, outer, context);
560                         os << "\n\\family default \n";
561                 }
562
563                 else if (t.cs() == "textit") {
564                         context.check_layout(os);
565                         os << "\n\\shape italic \n";
566                         parse_text_snippet(p, os, FLAG_ITEM, outer, context);
567                         os << "\n\\shape default \n";
568                 }
569
570                 else if (t.cs() == "textsc") {
571                         context.check_layout(os);
572                         os << "\n\\noun on \n";
573                         parse_text_snippet(p, os, FLAG_ITEM, outer, context);
574                         os << "\n\\noun default \n";
575                 }
576
577                 else if (t.cs() == "textbf") {
578                         context.check_layout(os);
579                         os << "\n\\series bold \n";
580                         parse_text_snippet(p, os, FLAG_ITEM, outer, context);
581                         os << "\n\\series default \n";
582                 }
583
584                 else if (t.cs() == "underbar") {
585                         context.check_layout(os);
586                         os << "\n\\bar under \n";
587                         parse_text_snippet(p, os, FLAG_ITEM, outer, context);
588                         os << "\n\\bar default \n";
589                 }
590
591                 else if (t.cs() == "emph" || t.cs() == "noun") {
592                         context.check_layout(os);
593                         os << "\n\\" << t.cs() << " on \n";
594                         parse_text_snippet(p, os, FLAG_ITEM, outer, context);
595                         os << "\n\\" << t.cs() << " default \n";
596                 }
597
598                 else if (t.cs() == "bibitem") {
599                         context.check_layout(os);
600                         os << "\\bibitem ";
601                         os << p.getOpt();
602                         os << '{' << p.verbatim_item() << '}' << "\n";
603                 }
604
605                 else if (is_known(t.cs(), known_latex_commands)) {
606                         context.check_layout(os);
607                         begin_inset(os, "LatexCommand ");
608                         os << '\\' << t.cs();
609                         os << p.getOpt();
610                         os << p.getOpt();
611                         os << '{' << p.verbatim_item() << "}\n";
612                         end_inset(os);
613                 }
614
615                 else if (is_known(t.cs(), known_quotes)) {
616                   char const ** where = is_known(t.cs(), known_quotes);
617                         begin_inset(os, "Quotes ");
618                         os << known_coded_quotes[where - known_quotes];
619                         end_inset(os);
620                         skip_braces(p);
621                 }
622
623                 else if (is_known(t.cs(), known_sizes)) {
624                         char const ** where = is_known(t.cs(), known_sizes);
625                         context.check_layout(os);
626                         os << "\n\\size " << known_coded_sizes[where - known_sizes] << "\n";
627                 }
628
629                 else if (t.cs() == "LyX" || t.cs() == "TeX" 
630                          || t.cs() == "LaTeX") {
631                         context.check_layout(os);
632                         os << t.cs();
633                         skip_braces(p); // eat {}
634                 }
635
636                 else if (t.cs() == "LaTeXe") {
637                         context.check_layout(os);
638                         os << "LaTeX2e";
639                         skip_braces(p); // eat {}
640                 }
641
642                 else if (t.cs() == "ldots") {
643                         context.check_layout(os);
644                         skip_braces(p);
645                         os << "\\SpecialChar \\ldots{}\n";
646                 }
647
648                 else if (t.cs() == "lyxarrow") {
649                         context.check_layout(os);
650                         os << "\\SpecialChar \\menuseparator\n";
651                         skip_braces(p);
652                 }
653
654                 else if (t.cs() == "ldots") {
655                         context.check_layout(os);
656                         os << "\\SpecialChar \\ldots{}\n";
657                         skip_braces(p);
658                 }
659
660                 else if (t.cs() == "@" && p.next_token().asInput() == ".") {
661                         context.check_layout(os);
662                         os << "\\SpecialChar \\@.\n";
663                         p.get_token();
664                 }
665
666                 else if (t.cs() == "-") {
667                         context.check_layout(os);
668                         os << "\\SpecialChar \\-\n";
669                 }
670
671                 else if (t.cs() == "textasciitilde") {
672                         context.check_layout(os);
673                         os << '~';
674                         skip_braces(p);
675                 }
676
677                 else if (t.cs() == "textasciicircum") {
678                         context.check_layout(os);
679                         os << '^';
680                         skip_braces(p);
681                 }
682
683                 else if (t.cs() == "textbackslash") {
684                         context.check_layout(os);
685                         os << "\n\\backslash \n";
686                         skip_braces(p);
687                 }
688
689                 else if (t.cs() == "_" || t.cs() == "&" || t.cs() == "#" 
690                             || t.cs() == "$" || t.cs() == "{" || t.cs() == "}" 
691                             || t.cs() == "%") {
692                         context.check_layout(os);
693                         os << t.cs();
694                 }
695
696                 else if (t.cs() == "char") {
697                         context.check_layout(os);
698                         if (p.next_token().character() == '`') {
699                                 p.get_token();
700                                 if (p.next_token().cs() == "\"") {
701                                         p.get_token();
702                                         os << '"';
703                                         skip_braces(p);
704                                 } else {
705                                         handle_ert(os, "\\char`", context);
706                                 }
707                         } else {
708                                 handle_ert(os, "\\char", context);
709                         }
710                 }
711
712                 else if (t.cs() == "\"") {
713                         context.check_layout(os);
714                         string const name = p.verbatim_item();
715                              if (name == "a") os << 'ä';
716                         else if (name == "o") os << 'ö';
717                         else if (name == "u") os << 'ü';
718                         else if (name == "A") os << 'Ä';
719                         else if (name == "O") os << 'Ö';
720                         else if (name == "U") os << 'Ü';
721                         else handle_ert(os, "\"{" + name + "}", context);
722                 }
723
724                 else if (t.cs() == "=" || t.cs() == "H" || t.cs() == "c"
725                       || t.cs() == "^" || t.cs() == "'" || t.cs() == "~") {
726                         // we need the trim as the LyX parser chokes on such spaces
727                         context.check_layout(os);
728                         os << "\n\\i \\" << t.cs() << "{"
729                            << trim(parse_text(p, FLAG_ITEM, outer, context), " ") << "}\n";
730                 }
731
732                 else if (t.cs() == "ss") {
733                         context.check_layout(os);
734                         os << "ß";
735                 }
736
737                 else if (t.cs() == "i" || t.cs() == "j") {
738                         context.check_layout(os);
739                         os << "\\" << t.cs() << ' ';
740                 }
741
742                 else if (t.cs() == "\\") {
743                         context.check_layout(os);
744                         os << "\n\\newline \n";
745                 }
746         
747                 else if (t.cs() == "input") {
748                         context.check_layout(os);
749                         handle_ert(os, "\\input{" + p.verbatim_item() + "}\n",
750                                    context);
751                 }
752                 else if (t.cs() == "fancyhead") {
753                         context.check_layout(os);
754                         ostringstream ss;
755                         ss << "\\fancyhead";
756                         ss << p.getOpt();
757                         ss << '{' << p.verbatim_item() << "}\n";
758                         handle_ert(os, ss.str(), context);
759                 }
760
761                 else {
762                         //cerr << "#: " << t << " mode: " << mode << endl;
763                         // heuristic: read up to next non-nested space
764                         /*
765                         string s = t.asInput();
766                         string z = p.verbatim_item();
767                         while (p.good() && z != " " && z.size()) {
768                                 //cerr << "read: " << z << endl;
769                                 s += z;
770                                 z = p.verbatim_item();
771                         }
772                         cerr << "found ERT: " << s << endl;
773                         handle_ert(os, s + ' ', context);
774                         */
775                         context.check_layout(os);
776                         handle_ert(os, t.asInput() + ' ', context);
777                 }
778
779                 if (flags & FLAG_LEAVE) {
780                         flags &= ~FLAG_LEAVE;
781                         break;
782                 }
783         }
784 }
785
786
787 // }])