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