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