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