]> git.lyx.org Git - lyx.git/blob - src/tex2lyx/Preamble.cpp
tex2lyx/Preamble.cpp: remove a FIXME, the problem was only in trunk (bug #8211) and...
[lyx.git] / src / tex2lyx / Preamble.cpp
1 /**
2  * \file Preamble.cpp
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 Uwe Stöhr
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 // {[(
13
14 #include <config.h>
15
16 #include "Preamble.h"
17 #include "tex2lyx.h"
18
19 #include "LayoutFile.h"
20 #include "Layout.h"
21 #include "Lexer.h"
22 #include "TextClass.h"
23
24 #include "support/convert.h"
25 #include "support/FileName.h"
26 #include "support/filetools.h"
27 #include "support/lstrings.h"
28
29 #include "support/regex.h"
30
31 #include <algorithm>
32 #include <iostream>
33
34 using namespace std;
35 using namespace lyx::support;
36
37
38 namespace lyx {
39
40 // special columntypes
41 extern map<char, int> special_columns;
42
43 Preamble preamble;
44
45 namespace {
46
47 //add this to known_languages when updating to lyxformat 266:
48 // "armenian" (needs special handling since not supported by standard babel)
49 //add these to known_languages when updating to lyxformat 268:
50 //"chinese-simplified", "chinese-traditional", "japanese", "korean"
51 // Both changes require first that support for non-babel languages (CJK,
52 // armtex) is added.
53 /**
54  * known babel language names (including synonyms)
55  * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
56  * not yet supported by LyX: kurmanji
57  * please keep this in sync with known_coded_languages line by line!
58  */
59 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
60 "american", "arabic", "arabtex", "australian", "austrian", "bahasa", "bahasai",
61 "bahasam", "basque", "belarusian", "brazil", "brazilian", "breton", "british",
62 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
63 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "francais",
64 "french", "frenchb", "frenchle", "frenchpro", "galician", "german", "germanb",
65 "greek", "hebrew", "hungarian", "icelandic", "indon", "indonesian", "interlingua",
66 "irish", "italian", "kazakh", "latin", "latvian", "lithuanian", "lowersorbian",
67 "lsorbian", "magyar", "malay", "meyalu", "mongolian", "naustrian", "newzealand",
68 "ngerman", "ngermanb", "norsk", "nynorsk", "polutonikogreek", "polish",
69 "portuges", "portuguese", "romanian", "russian", "russianb", "samin",
70 "scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish",
71 "swedish", "thai", "turkish", "turkmen", "ukraineb", "ukrainian",
72 "uppersorbian", "UKenglish", "USenglish", "usorbian", "vietnam", "welsh",
73 0};
74
75 /**
76  * the same as known_languages with .lyx names
77  * please keep this in sync with known_languages line by line!
78  */
79 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
80 "american", "arabic_arabi", "arabic_arabtex", "australian", "austrian", "bahasa", "bahasa",
81 "bahasam", "basque", "belarusian", "brazilian", "brazilian", "breton", "british",
82 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
83 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "french",
84 "french", "french", "french", "french", "galician", "german", "german",
85 "greek", "hebrew", "magyar", "icelandic", "bahasa", "bahasa", "interlingua",
86 "irish", "italian", "kazakh", "latin", "latvian", "lithuanian", "lowersorbian",
87 "lowersorbian", "magyar", "bahasam", "bahasam", "mongolian", "naustrian", "newzealand",
88 "ngerman", "ngerman", "norsk", "nynorsk", "polutonikogreek", "polish",
89 "portuguese", "portuguese", "romanian", "russian", "russian", "samin",
90 "scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish",
91 "swedish", "thai", "turkish", "turkmen", "ukrainian", "ukrainian",
92 "uppersorbian", "uppersorbian", "english", "english", "vietnamese", "welsh",
93 0};
94
95 /**
96  * known polyglossia language names (including variants)
97  */
98 const char * const polyglossia_languages[] = {
99 "albanian", "croatian", "hebrew", "norsk", "swedish", "amharic", "czech", "hindi",
100 "nynorsk", "syriac", "arabic", "danish", "icelandic", "occitan", "tamil",
101 "armenian", "divehi", "interlingua", "polish", "telugu", "asturian", "dutch",
102 "irish", "portuges", "thai", "bahasai", "english", "italian", "romanian", "turkish",
103 "bahasam", "esperanto", "lao", "russian", "turkmen", "basque", "estonian", "latin",
104 "samin", "ukrainian", "bengali", "farsi", "latvian", "sanskrit", "urdu", "brazil",
105 "brazilian", "finnish", "lithuanian", "scottish", "usorbian", "breton", "french",
106 "lsorbian", "serbian", "vietnamese", "bulgarian", "galician", "magyar", "slovak",
107 "welsh", "catalan", "german", "malayalam", "slovenian", "coptic", "greek",
108 "marathi", "spanish",
109 "american", "ancient", "australian", "british", "monotonic", "newzealand",
110 "polytonic", 0};
111
112 /**
113  * the same as polyglossia_languages with .lyx names
114  * please keep this in sync with polyglossia_languages line by line!
115  */
116 const char * const coded_polyglossia_languages[] = {
117 "albanian", "croatian", "hebrew", "norsk", "swedish", "amharic", "czech", "hindi",
118 "nynorsk", "syriac", "arabic_arabi", "danish", "icelandic", "occitan", "tamil",
119 "armenian", "divehi", "interlingua", "polish", "telugu", "asturian", "dutch",
120 "irish", "portuges", "thai", "bahasa", "english", "italian", "romanian", "turkish",
121 "bahasam", "esperanto", "lao", "russian", "turkmen", "basque", "estonian", "latin",
122 "samin", "ukrainian", "bengali", "farsi", "latvian", "sanskrit", "urdu", "brazilian",
123 "brazilian", "finnish", "lithuanian", "scottish", "uppersorbian", "breton", "french",
124 "lowersorbian", "serbian", "vietnamese", "bulgarian", "galician", "magyar", "slovak",
125 "welsh", "catalan", "ngerman", "malayalam", "slovene", "coptic", "greek",
126 "marathi", "spanish",
127 "american", "ancientgreek", "australian", "british", "greek", "newzealand",
128 "polutonikogreek", 0};
129
130 /// languages with english quotes (.lyx names)
131 const char * const known_english_quotes_languages[] = {"american", "australian",
132 "bahasa", "bahasam", "brazilian", "canadian", "chinese-simplified", "english",
133 "esperanto", "hebrew", "irish", "korean", "newzealand", "portuguese", "scottish",
134 "thai", 0};
135
136 /// languages with french quotes (.lyx names)
137 const char * const known_french_quotes_languages[] = {"albanian",
138 "arabic_arabi", "arabic_arabtex", "basque", "canadien", "catalan", "french",
139 "galician", "greek", "italian", "norsk", "nynorsk", "polutonikogreek",
140 "russian", "spanish", "spanish-mexico", "turkish", "turkmen", "ukrainian",
141 "vietnamese", 0};
142
143 /// languages with german quotes (.lyx names)
144 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
145 "czech", "german", "icelandic", "lithuanian", "lowersorbian", "naustrian",
146 "ngerman", "serbian", "serbian-latin", "slovak", "slovene", "uppersorbian", 0};
147
148 /// languages with polish quotes (.lyx names)
149 const char * const known_polish_quotes_languages[] = {"afrikaans", "croatian",
150 "dutch", "estonian", "magyar", "polish", "romanian", 0};
151
152 /// languages with swedish quotes (.lyx names)
153 const char * const known_swedish_quotes_languages[] = {"finnish",
154 "swedish", 0};
155
156 /// known language packages from the times before babel
157 const char * const known_old_language_packages[] = {"french", "frenchle",
158 "frenchpro", "german", "ngerman", "pmfrench", 0};
159
160 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
161
162 const char * const known_roman_fonts[] = { "ae", "beraserif", "bookman",
163 "ccfonts", "chancery", "charter", "cmr", "fourier", "lmodern", "mathpazo",
164 "mathptmx", "newcent", "utopia", 0};
165
166 const char * const known_sans_fonts[] = { "avant", "berasans", "cmbr", "cmss",
167 "helvet", "lmss", 0};
168
169 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
170 "courier", "lmtt", "luximono", "fourier", "lmodern", "mathpazo", "mathptmx",
171 "newcent", 0};
172
173 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
174 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
175 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
176 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
177 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
178
179 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
180 "executivepaper", "legalpaper", "letterpaper", 0};
181
182 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
183 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
184
185 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
186 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
187 "columnsep", 0};
188
189 /// commands that can start an \if...\else...\endif sequence
190 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
191 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
192 "ifsidecap", "ifupgreek", 0};
193
194 const char * const known_basic_colors[] = {"blue", "black", "cyan", "green",
195 "magenta", "red", "white", "yellow", 0};
196
197 const char * const known_basic_color_codes[] = {"#0000ff", "#000000", "#00ffff", "#00ff00",
198 "#ff00ff", "#ff0000", "#ffffff", "#ffff00", 0};
199
200 /// conditional commands with three arguments like \@ifundefined{}{}{}
201 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
202 0};
203
204 /// packages that work only in xetex
205 /// polyglossia is handled separately
206 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
207 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
208 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
209
210 /// packages that are automatically skipped if loaded by LyX
211 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
212 "amstext", "amsthm", "array", "booktabs", "calc", "color", "float", "fontspec",
213 "graphicx", "hhline", "ifthen", "longtable", "makeidx", "multirow",
214 "nomencl", "pdfpages", "rotating", "rotfloat", "splitidx", "setspace",
215 "subscript", "textcomp", "ulem", "url", "varioref", "verbatim", "wrapfig",
216 "xunicode", 0};
217
218 // codes used to remove packages that are loaded automatically by LyX.
219 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
220 const char package_beg_sep = '\001';
221 const char package_mid_sep = '\002';
222 const char package_end_sep = '\003';
223
224
225 // returns true if at least one of the options in what has been found
226 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
227 {
228         if (opts.empty())
229                 return false;
230
231         bool found = false;
232         // the last language option is the document language (for babel and LyX)
233         // the last size option is the document font size
234         vector<string>::iterator it;
235         vector<string>::iterator position = opts.begin();
236         for (; *what; ++what) {
237                 it = find(opts.begin(), opts.end(), *what);
238                 if (it != opts.end()) {
239                         if (it >= position) {
240                                 found = true;
241                                 target = *what;
242                                 position = it;
243                         }
244                 }
245         }
246         return found;
247 }
248
249
250 void delete_opt(vector<string> & opts, char const * const * what)
251 {
252         if (opts.empty())
253                 return;
254
255         // remove found options from the list
256         // do this after handle_opt to avoid potential memory leaks
257         vector<string>::iterator it;
258         for (; *what; ++what) {
259                 it = find(opts.begin(), opts.end(), *what);
260                 if (it != opts.end())
261                         opts.erase(it);
262         }
263 }
264
265
266 /*!
267  * Split a package options string (keyval format) into a vector.
268  * Example input:
269  *   authorformat=smallcaps,
270  *   commabeforerest,
271  *   titleformat=colonsep,
272  *   bibformat={tabular,ibidem,numbered}
273  */
274 vector<string> split_options(string const & input)
275 {
276         vector<string> options;
277         string option;
278         Parser p(input);
279         while (p.good()) {
280                 Token const & t = p.get_token();
281                 if (t.asInput() == ",") {
282                         options.push_back(trimSpaceAndEol(option));
283                         option.erase();
284                 } else if (t.asInput() == "=") {
285                         option += '=';
286                         p.skip_spaces(true);
287                         if (p.next_token().asInput() == "{")
288                                 option += '{' + p.getArg('{', '}') + '}';
289                 } else if (t.cat() != catSpace)
290                         option += t.asInput();
291         }
292
293         if (!option.empty())
294                 options.push_back(trimSpaceAndEol(option));
295
296         return options;
297 }
298
299
300 /*!
301  * Retrieve a keyval option "name={value with=sign}" named \p name from
302  * \p options and return the value.
303  * The found option is also removed from \p options.
304  */
305 string process_keyval_opt(vector<string> & options, string name)
306 {
307         for (size_t i = 0; i < options.size(); ++i) {
308                 vector<string> option;
309                 split(options[i], option, '=');
310                 if (option.size() < 2)
311                         continue;
312                 if (option[0] == name) {
313                         options.erase(options.begin() + i);
314                         option.erase(option.begin());
315                         return join(option, "=");
316                 }
317         }
318         return "";
319 }
320
321 } // anonymous namespace
322
323
324 bool Preamble::indentParagraphs() const
325 {
326         return h_paragraph_separation == "indent";
327 }
328
329
330 bool Preamble::isPackageUsed(string const & package) const
331 {
332         return used_packages.find(package) != used_packages.end();
333 }
334
335
336 vector<string> Preamble::getPackageOptions(string const & package) const
337 {
338         map<string, vector<string> >::const_iterator it = used_packages.find(package);
339         if (it != used_packages.end())
340                 return it->second;
341         return vector<string>();
342 }
343
344
345 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
346 {
347         auto_packages.insert(package);
348 }
349
350
351 void Preamble::addModule(string const & module)
352 {
353         used_modules.push_back(module);
354 }
355
356
357 void Preamble::suppressDate(bool suppress)
358 {
359         if (suppress)
360                 h_suppress_date = "true";
361         else
362                 h_suppress_date = "false";
363 }
364
365
366 void Preamble::registerAuthor(std::string const & name)
367 {
368         Author author(from_utf8(name), empty_docstring());
369         author.setUsed(true);
370         authors_.record(author);
371         h_tracking_changes = "true";
372         h_output_changes = "true";
373 }
374
375
376 Author const & Preamble::getAuthor(std::string const & name) const
377 {
378         Author author(from_utf8(name), empty_docstring());
379         for (AuthorList::Authors::const_iterator it = authors_.begin();
380              it != authors_.end(); ++it)
381                 if (*it == author)
382                         return *it;
383         static Author const dummy;
384         return dummy;
385 }
386
387
388 void Preamble::add_package(string const & name, vector<string> & options)
389 {
390         // every package inherits the global options
391         if (used_packages.find(name) == used_packages.end())
392                 used_packages[name] = split_options(h_options);
393
394         vector<string> & v = used_packages[name];
395         v.insert(v.end(), options.begin(), options.end());
396         if (name == "jurabib") {
397                 // Don't output the order argument (see the cite command
398                 // handling code in text.cpp).
399                 vector<string>::iterator end =
400                         remove(options.begin(), options.end(), "natbiborder");
401                 end = remove(options.begin(), end, "jurabiborder");
402                 options.erase(end, options.end());
403         }
404 }
405
406
407 namespace {
408
409 // Given is a string like "scaled=0.9", return 0.9 * 100
410 string const scale_as_percentage(string const & scale)
411 {
412         string::size_type pos = scale.find('=');
413         if (pos != string::npos) {
414                 string value = scale.substr(pos + 1);
415                 if (isStrDbl(value))
416                         return convert<string>(100 * convert<double>(value));
417         }
418         // If the input string didn't match our expectations.
419         // return the default value "100"
420         return "100";
421 }
422
423
424 string remove_braces(string const & value)
425 {
426         if (value.empty())
427                 return value;
428         if (value[0] == '{' && value[value.length()-1] == '}')
429                 return value.substr(1, value.length()-2);
430         return value;
431 }
432
433 } // anonymous namespace
434
435
436 Preamble::Preamble() : one_language(true), title_layout_found(false)
437 {
438         //h_backgroundcolor;
439         //h_boxbgcolor;
440         h_biblio_style            = "plain";
441         h_cite_engine             = "basic";
442         h_cite_engine_type        = "numerical";
443         h_defskip                 = "medskip";
444         //h_float_placement;
445         //h_fontcolor;
446         h_fontencoding            = "default";
447         h_font_roman              = "default";
448         h_font_sans               = "default";
449         h_font_typewriter         = "default";
450         h_font_default_family     = "default";
451         h_use_non_tex_fonts       = "false";
452         h_font_sc                 = "false";
453         h_font_osf                = "false";
454         h_font_sf_scale           = "100";
455         h_font_tt_scale           = "100";
456         h_graphics                = "default";
457         h_default_output_format   = "default";
458         h_html_be_strict          = "false";
459         h_html_css_as_file        = "0";
460         h_html_math_output        = "0";
461         h_inputencoding           = "auto";
462         h_justification           = "true";
463         h_language                = "english";
464         h_language_package        = "none";
465         //h_listings_params;
466         //h_margins;
467         //h_notefontcolor;
468         //h_options;
469         h_output_changes          = "false";
470         h_papercolumns            = "1";
471         h_paperfontsize           = "default";
472         h_paperorientation        = "portrait";
473         h_paperpagestyle          = "default";
474         //h_papersides;
475         h_papersize               = "default";
476         h_paragraph_indentation   = "default";
477         h_paragraph_separation    = "indent";
478         //h_pdf_title;
479         //h_pdf_author;
480         //h_pdf_subject;
481         //h_pdf_keywords;
482         h_pdf_bookmarks           = "1";
483         h_pdf_bookmarksnumbered   = "0";
484         h_pdf_bookmarksopen       = "0";
485         h_pdf_bookmarksopenlevel  = "1";
486         h_pdf_breaklinks          = "0";
487         h_pdf_pdfborder           = "0";
488         h_pdf_colorlinks          = "0";
489         h_pdf_backref             = "section";
490         h_pdf_pdfusetitle         = "1";
491         //h_pdf_pagemode;
492         //h_pdf_quoted_options;
493         h_quotes_language         = "english";
494         h_secnumdepth             = "3";
495         h_spacing                 = "single";
496         h_suppress_date           = "false";
497         h_textclass               = "article";
498         h_tocdepth                = "3";
499         h_tracking_changes        = "false";
500         h_use_bibtopic            = "false";
501         h_use_indices             = "false";
502         h_use_geometry            = "false";
503         h_use_default_options     = "false";
504         h_use_hyperref            = "0";
505         h_use_refstyle            = "0";
506         h_use_packages["amsmath"]    = "1";
507         h_use_packages["amssymb"]    = "0";
508         h_use_packages["esint"]      = "1";
509         h_use_packages["mhchem"]     = "0";
510         h_use_packages["mathdots"]   = "0";
511         h_use_packages["mathtools"]  = "0";
512         h_use_packages["undertilde"] = "0";
513 }
514
515
516 void Preamble::handle_hyperref(vector<string> & options)
517 {
518         // FIXME swallow inputencoding changes that might surround the
519         //       hyperref setup if it was written by LyX
520         h_use_hyperref = "1";
521         // swallow "unicode=true", since LyX does always write that
522         vector<string>::iterator it =
523                 find(options.begin(), options.end(), "unicode=true");
524         if (it != options.end())
525                 options.erase(it);
526         it = find(options.begin(), options.end(), "pdfusetitle");
527         if (it != options.end()) {
528                 h_pdf_pdfusetitle = "1";
529                 options.erase(it);
530         }
531         string bookmarks = process_keyval_opt(options, "bookmarks");
532         if (bookmarks == "true")
533                 h_pdf_bookmarks = "1";
534         else if (bookmarks == "false")
535                 h_pdf_bookmarks = "0";
536         if (h_pdf_bookmarks == "1") {
537                 string bookmarksnumbered =
538                         process_keyval_opt(options, "bookmarksnumbered");
539                 if (bookmarksnumbered == "true")
540                         h_pdf_bookmarksnumbered = "1";
541                 else if (bookmarksnumbered == "false")
542                         h_pdf_bookmarksnumbered = "0";
543                 string bookmarksopen =
544                         process_keyval_opt(options, "bookmarksopen");
545                 if (bookmarksopen == "true")
546                         h_pdf_bookmarksopen = "1";
547                 else if (bookmarksopen == "false")
548                         h_pdf_bookmarksopen = "0";
549                 if (h_pdf_bookmarksopen == "1") {
550                         string bookmarksopenlevel =
551                                 process_keyval_opt(options, "bookmarksopenlevel");
552                         if (!bookmarksopenlevel.empty())
553                                 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
554                 }
555         }
556         string breaklinks = process_keyval_opt(options, "breaklinks");
557         if (breaklinks == "true")
558                 h_pdf_breaklinks = "1";
559         else if (breaklinks == "false")
560                 h_pdf_breaklinks = "0";
561         string pdfborder = process_keyval_opt(options, "pdfborder");
562         if (pdfborder == "{0 0 0}")
563                 h_pdf_pdfborder = "1";
564         else if (pdfborder == "{0 0 1}")
565                 h_pdf_pdfborder = "0";
566         string backref = process_keyval_opt(options, "backref");
567         if (!backref.empty())
568                 h_pdf_backref = backref;
569         string colorlinks = process_keyval_opt(options, "colorlinks");
570         if (colorlinks == "true")
571                 h_pdf_colorlinks = "1";
572         else if (colorlinks == "false")
573                 h_pdf_colorlinks = "0";
574         string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
575         if (!pdfpagemode.empty())
576                 h_pdf_pagemode = pdfpagemode;
577         string pdftitle = process_keyval_opt(options, "pdftitle");
578         if (!pdftitle.empty()) {
579                 h_pdf_title = remove_braces(pdftitle);
580         }
581         string pdfauthor = process_keyval_opt(options, "pdfauthor");
582         if (!pdfauthor.empty()) {
583                 h_pdf_author = remove_braces(pdfauthor);
584         }
585         string pdfsubject = process_keyval_opt(options, "pdfsubject");
586         if (!pdfsubject.empty())
587                 h_pdf_subject = remove_braces(pdfsubject);
588         string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
589         if (!pdfkeywords.empty())
590                 h_pdf_keywords = remove_braces(pdfkeywords);
591         if (!options.empty()) {
592                 if (!h_pdf_quoted_options.empty())
593                         h_pdf_quoted_options += ',';
594                 h_pdf_quoted_options += join(options, ",");
595                 options.clear();
596         }
597 }
598
599
600 void Preamble::handle_geometry(vector<string> & options)
601 {
602         h_use_geometry = "true";
603         vector<string>::iterator it;
604         // paper orientation
605         if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
606                 h_paperorientation = "landscape";
607                 options.erase(it);
608         }
609         // paper size
610         // keyval version: "paper=letter"
611         string paper = process_keyval_opt(options, "paper");
612         if (!paper.empty())
613                 h_papersize = paper + "paper";
614         // alternative version: "letterpaper"
615         handle_opt(options, known_paper_sizes, h_papersize);
616         delete_opt(options, known_paper_sizes);
617         // page margins
618         char const * const * margin = known_paper_margins;
619         for (; *margin; ++margin) {
620                 string value = process_keyval_opt(options, *margin);
621                 if (!value.empty()) {
622                         int k = margin - known_paper_margins;
623                         string name = known_coded_paper_margins[k];
624                         h_margins += '\\' + name + ' ' + value + '\n';
625                 }
626         }
627 }
628
629
630 void Preamble::handle_package(Parser &p, string const & name,
631                               string const & opts, bool in_lyx_preamble)
632 {
633         vector<string> options = split_options(opts);
634         add_package(name, options);
635         string scale;
636
637         if (is_known(name, known_xetex_packages)) {
638                 xetex = true;
639                 h_use_non_tex_fonts = "true";
640                 registerAutomaticallyLoadedPackage("fontspec");
641                 if (h_inputencoding == "auto")
642                         p.setEncoding("utf8");
643         }
644
645         // roman fonts
646         if (is_known(name, known_roman_fonts)) {
647                 h_font_roman = name;
648                 p.skip_spaces();
649         }
650
651         if (name == "fourier") {
652                 h_font_roman = "utopia";
653                 // when font uses real small capitals
654                 if (opts == "expert")
655                         h_font_sc = "true";
656         }
657
658         else if (name == "mathpazo")
659                 h_font_roman = "palatino";
660
661         else if (name == "mathptmx")
662                 h_font_roman = "times";
663
664         // sansserif fonts
665         if (is_known(name, known_sans_fonts)) {
666                 h_font_sans = name;
667                 if (!opts.empty()) {
668                         scale = opts;
669                         h_font_sf_scale = scale_as_percentage(scale);
670                 }
671         }
672
673         // typewriter fonts
674         if (is_known(name, known_typewriter_fonts)) {
675                 // fourier can be set as roman font _only_
676                 // fourier as typewriter is handled in handling of \ttdefault
677                 if (name != "fourier") {
678                         h_font_typewriter = name;
679                         if (!opts.empty()) {
680                                 scale = opts;
681                                 h_font_tt_scale = scale_as_percentage(scale);
682                         }
683                 }
684         }
685
686         // font uses old-style figure
687         if (name == "eco")
688                 h_font_osf = "true";
689
690         // after the detection and handling of special cases, we can remove the
691         // fonts, otherwise they would appear in the preamble, see bug #7856
692         if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
693                 ||      is_known(name, known_typewriter_fonts))
694                 ;
695
696         else if (name == "amsmath" || name == "amssymb" ||
697                  name == "esint" || name == "mhchem" || name == "mathdots" ||
698                  name == "mathtools" || name == "undertilde")
699                 h_use_packages[name] = "2";
700
701         else if (name == "babel") {
702                 h_language_package = "default";
703                 // One might think we would have to do nothing if babel is loaded
704                 // without any options to prevent pollution of the preamble with this
705                 // babel call in every roundtrip.
706                 // But the user could have defined babel-specific things afterwards. So
707                 // we need to keep it in the preamble to prevent cases like bug #7861.
708                 if (!opts.empty()) {
709                         // check if more than one option was used - used later for inputenc
710                         if (options.begin() != options.end() - 1)
711                                 one_language = false;
712                         // babel takes the last language of the option of its \usepackage
713                         // call as document language. If there is no such language option, the
714                         // last language in the documentclass options is used.
715                         handle_opt(options, known_languages, h_language);
716                         // If babel is called with options, LyX puts them by default into the
717                         // document class options. This works for most languages, except
718                         // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
719                         // perhaps in future others.
720                         // Therefore keep the babel call as it is as the user might have
721                         // reasons for it.
722                         h_preamble << "\\usepackage[" << opts << "]{babel}\n";
723                         delete_opt(options, known_languages);
724                         // finally translate the babel name to a LyX name
725                         h_language = babel2lyx(h_language);
726                 }
727                 else
728                         h_preamble << "\\usepackage{babel}\n";
729         }
730
731         else if (name == "polyglossia") {
732                 h_language_package = "default";
733                 h_default_output_format = "pdf4";
734                 h_use_non_tex_fonts = "true";
735                 xetex = true;
736                 registerAutomaticallyLoadedPackage("xunicode");
737                 if (h_inputencoding == "auto")
738                         p.setEncoding("utf8");
739         }
740
741         else if (name == "fontenc") {
742                 h_fontencoding = getStringFromVector(options, ",");
743                 /* We could do the following for better round trip support,
744                  * but this makes the document less portable, so I skip it:
745                 if (h_fontencoding == lyxrc.fontenc)
746                         h_fontencoding = "global";
747                  */
748                 options.clear();
749         }
750
751         else if (name == "inputenc" || name == "luainputenc") {
752                 // h_inputencoding is only set when there is not more than one
753                 // inputenc option because otherwise h_inputencoding must be
754                 // set to "auto" (the default encoding of the document language)
755                 // Therefore check for the "," character.
756                 // It is also only set when there is not more than one babel
757                 // language option.
758                 if (opts.find(",") == string::npos && one_language == true)
759                         h_inputencoding = opts;
760                 if (!options.empty())
761                         p.setEncoding(options.back());
762                 options.clear();
763         }
764
765         else if (is_known(name, known_old_language_packages)) {
766                 // known language packages from the times before babel
767                 // if they are found and not also babel, they will be used as
768                 // custom language package
769                 h_language_package = "\\usepackage{" + name + "}";
770         }
771
772         else if (name == "prettyref")
773                 ; // ignore this FIXME: Use the package separator mechanism instead
774
775         else if (name == "lyxskak") {
776                 // ignore this and its options
777                 const char * const o[] = {"ps", "mover", 0};
778                 delete_opt(options, o);
779         }
780
781         else if (is_known(name, known_lyx_packages) && options.empty()) {
782                 if (name == "splitidx")
783                         h_use_indices = "true";
784                 if (!in_lyx_preamble)
785                         h_preamble << package_beg_sep << name
786                                    << package_mid_sep << "\\usepackage{"
787                                    << name << "}\n" << package_end_sep;
788         }
789
790         else if (name == "geometry")
791                 handle_geometry(options);
792
793         else if (name == "subfig")
794                 ; // ignore this FIXME: Use the package separator mechanism instead
795
796         else if (is_known(name, known_languages))
797                 h_language = name;
798
799         else if (name == "natbib") {
800                 h_biblio_style = "plainnat";
801                 h_cite_engine = "natbib";
802                 h_cite_engine_type = "authoryear";
803                 vector<string>::iterator it =
804                         find(options.begin(), options.end(), "authoryear");
805                 if (it != options.end())
806                         options.erase(it);
807                 else {
808                         it = find(options.begin(), options.end(), "numbers");
809                         if (it != options.end()) {
810                                 h_cite_engine_type = "numerical";
811                                 options.erase(it);
812                         }
813                 }
814         }
815
816         else if (name == "jurabib") {
817                 h_biblio_style = "jurabib";
818                 h_cite_engine = "jurabib";
819                 h_cite_engine_type = "authoryear";
820         }
821
822         else if (name == "hyperref")
823                 handle_hyperref(options);
824
825         else if (!in_lyx_preamble) {
826                 if (options.empty())
827                         h_preamble << "\\usepackage{" << name << "}\n";
828                 else {
829                         h_preamble << "\\usepackage[" << opts << "]{"
830                                    << name << "}\n";
831                         options.clear();
832                 }
833         }
834
835         // We need to do something with the options...
836         if (!options.empty())
837                 cerr << "Ignoring options '" << join(options, ",")
838                      << "' of package " << name << '.' << endl;
839
840         // remove the whitespace
841         p.skip_spaces();
842 }
843
844
845 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
846 {
847         while (p.good()) {
848                 Token t = p.get_token();
849                 if (t.cat() == catEscape &&
850                     is_known(t.cs(), known_if_commands))
851                         handle_if(p, in_lyx_preamble);
852                 else {
853                         if (!in_lyx_preamble)
854                                 h_preamble << t.asInput();
855                         if (t.cat() == catEscape && t.cs() == "fi")
856                                 return;
857                 }
858         }
859 }
860
861
862 bool Preamble::writeLyXHeader(ostream & os, bool subdoc)
863 {
864         // set the quote language
865         // LyX only knows the following quotes languages:
866         // english, swedish, german, polish, french and danish
867         // (quotes for "japanese" and "chinese-traditional" are missing because
868         //  they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
869         // conversion list taken from
870         // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
871         // (quotes for kazakh and interlingua are unknown)
872         // danish
873         if (h_language == "danish")
874                 h_quotes_language = "danish";
875         // french
876         else if (is_known(h_language, known_french_quotes_languages))
877                 h_quotes_language = "french";
878         // german
879         else if (is_known(h_language, known_german_quotes_languages))
880                 h_quotes_language = "german";
881         // polish
882         else if (is_known(h_language, known_polish_quotes_languages))
883                 h_quotes_language = "polish";
884         // swedish
885         else if (is_known(h_language, known_swedish_quotes_languages))
886                 h_quotes_language = "swedish";
887         //english
888         else if (is_known(h_language, known_english_quotes_languages))
889                 h_quotes_language = "english";
890
891         if (contains(h_float_placement, "H"))
892                 registerAutomaticallyLoadedPackage("float");
893         if (h_spacing != "single" && h_spacing != "default")
894                 registerAutomaticallyLoadedPackage("setspace");
895         if (h_use_packages["amsmath"] == "2") {
896                 // amsbsy and amstext are already provided by amsmath
897                 registerAutomaticallyLoadedPackage("amsbsy");
898                 registerAutomaticallyLoadedPackage("amstext");
899         }
900
901         // output the LyX file settings
902         os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
903            << "\\lyxformat " << LYX_FORMAT << '\n'
904            << "\\begin_document\n"
905            << "\\begin_header\n"
906            << "\\textclass " << h_textclass << "\n";
907         string const raw = subdoc ? empty_string() : h_preamble.str();
908         if (!raw.empty()) {
909                 os << "\\begin_preamble\n";
910                 for (string::size_type i = 0; i < raw.size(); ++i) {
911                         if (raw[i] == package_beg_sep) {
912                                 // Here follows some package loading code that
913                                 // must be skipped if the package is loaded
914                                 // automatically.
915                                 string::size_type j = raw.find(package_mid_sep, i);
916                                 if (j == string::npos)
917                                         return false;
918                                 string::size_type k = raw.find(package_end_sep, j);
919                                 if (k == string::npos)
920                                         return false;
921                                 string const package = raw.substr(i + 1, j - i - 1);
922                                 string const replacement = raw.substr(j + 1, k - j - 1);
923                                 if (auto_packages.find(package) == auto_packages.end())
924                                         os << replacement;
925                                 i = k;
926                         } else
927                                 os.put(raw[i]);
928                 }
929                 os << "\n\\end_preamble\n";
930         }
931         if (!h_options.empty())
932                 os << "\\options " << h_options << "\n";
933         os << "\\use_default_options " << h_use_default_options << "\n";
934         if (!used_modules.empty()) {
935                 os << "\\begin_modules\n";
936                 vector<string>::const_iterator const end = used_modules.end();
937                 vector<string>::const_iterator it = used_modules.begin();
938                 for (; it != end; ++it)
939                         os << *it << '\n';
940                 os << "\\end_modules\n";
941         }
942         os << "\\language " << h_language << "\n"
943            << "\\language_package " << h_language_package << "\n"
944            << "\\inputencoding " << h_inputencoding << "\n"
945            << "\\fontencoding " << h_fontencoding << "\n"
946            << "\\font_roman " << h_font_roman << "\n"
947            << "\\font_sans " << h_font_sans << "\n"
948            << "\\font_typewriter " << h_font_typewriter << "\n"
949            << "\\font_default_family " << h_font_default_family << "\n"
950            << "\\use_non_tex_fonts " << h_use_non_tex_fonts << "\n"
951            << "\\font_sc " << h_font_sc << "\n"
952            << "\\font_osf " << h_font_osf << "\n"
953            << "\\font_sf_scale " << h_font_sf_scale << "\n"
954            << "\\font_tt_scale " << h_font_tt_scale << "\n"
955            << "\\graphics " << h_graphics << "\n"
956            << "\\default_output_format " << h_default_output_format << "\n";
957         if (!h_float_placement.empty())
958                 os << "\\float_placement " << h_float_placement << "\n";
959         os << "\\paperfontsize " << h_paperfontsize << "\n"
960            << "\\spacing " << h_spacing << "\n"
961            << "\\use_hyperref " << h_use_hyperref << '\n';
962         if (h_use_hyperref == "1") {
963                 if (!h_pdf_title.empty())
964                         os << "\\pdf_title \"" << h_pdf_title << "\"\n";
965                 if (!h_pdf_author.empty())
966                         os << "\\pdf_author \"" << h_pdf_author << "\"\n";
967                 if (!h_pdf_subject.empty())
968                         os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
969                 if (!h_pdf_keywords.empty())
970                         os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
971                 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
972                       "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
973                       "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
974                       "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
975                       "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
976                       "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
977                       "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
978                       "\\pdf_backref " << h_pdf_backref << "\n"
979                       "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
980                 if (!h_pdf_pagemode.empty())
981                         os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
982                 if (!h_pdf_quoted_options.empty())
983                         os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
984         }
985         os << "\\papersize " << h_papersize << "\n"
986            << "\\use_geometry " << h_use_geometry << '\n';
987         for (map<string, string>::const_iterator it = h_use_packages.begin();
988              it != h_use_packages.end(); ++it)
989                 os << "\\use_package " << it->first << ' ' << it->second << '\n';
990         os << "\\cite_engine " << h_cite_engine << '\n'
991            << "\\cite_engine_type " << h_cite_engine_type << '\n'
992            << "\\biblio_style " << h_biblio_style << "\n"
993            << "\\use_bibtopic " << h_use_bibtopic << "\n"
994            << "\\use_indices " << h_use_indices << "\n"
995            << "\\paperorientation " << h_paperorientation << '\n'
996            << "\\suppress_date " << h_suppress_date << '\n'
997            << "\\justification " << h_justification << '\n'
998            << "\\use_refstyle " << h_use_refstyle << '\n';
999         if (!h_fontcolor.empty())
1000                 os << "\\fontcolor " << h_fontcolor << '\n';
1001         if (!h_notefontcolor.empty())
1002                 os << "\\notefontcolor " << h_notefontcolor << '\n';
1003         if (!h_backgroundcolor.empty())
1004                 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1005         if (!h_boxbgcolor.empty())
1006                 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1007         os << h_margins
1008            << "\\secnumdepth " << h_secnumdepth << "\n"
1009            << "\\tocdepth " << h_tocdepth << "\n"
1010            << "\\paragraph_separation " << h_paragraph_separation << "\n";
1011         if (h_paragraph_separation == "skip")
1012                 os << "\\defskip " << h_defskip << "\n";
1013         else
1014                 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1015         os << "\\quotes_language " << h_quotes_language << "\n"
1016            << "\\papercolumns " << h_papercolumns << "\n"
1017            << "\\papersides " << h_papersides << "\n"
1018            << "\\paperpagestyle " << h_paperpagestyle << "\n";
1019         if (!h_listings_params.empty())
1020                 os << "\\listings_params " << h_listings_params << "\n";
1021         os << "\\tracking_changes " << h_tracking_changes << "\n"
1022            << "\\output_changes " << h_output_changes << "\n"
1023            << "\\html_math_output " << h_html_math_output << "\n"
1024            << "\\html_css_as_file " << h_html_css_as_file << "\n"
1025            << "\\html_be_strict " << h_html_be_strict << "\n"
1026            << authors_
1027            << "\\end_header\n\n"
1028            << "\\begin_body\n";
1029         return true;
1030 }
1031
1032
1033 void Preamble::parse(Parser & p, string const & forceclass,
1034                      TeX2LyXDocClass & tc)
1035 {
1036         // initialize fixed types
1037         special_columns['D'] = 3;
1038         bool is_full_document = false;
1039         bool is_lyx_file = false;
1040         bool in_lyx_preamble = false;
1041
1042         // determine whether this is a full document or a fragment for inclusion
1043         while (p.good()) {
1044                 Token const & t = p.get_token();
1045
1046                 if (t.cat() == catEscape && t.cs() == "documentclass") {
1047                         is_full_document = true;
1048                         break;
1049                 }
1050         }
1051         p.reset();
1052
1053         while (is_full_document && p.good()) {
1054                 Token const & t = p.get_token();
1055
1056 #ifdef FILEDEBUG
1057                 cerr << "t: " << t << "\n";
1058 #endif
1059
1060                 //
1061                 // cat codes
1062                 //
1063                 if (!in_lyx_preamble &&
1064                     (t.cat() == catLetter ||
1065                      t.cat() == catSuper ||
1066                      t.cat() == catSub ||
1067                      t.cat() == catOther ||
1068                      t.cat() == catMath ||
1069                      t.cat() == catActive ||
1070                      t.cat() == catBegin ||
1071                      t.cat() == catEnd ||
1072                      t.cat() == catAlign ||
1073                      t.cat() == catParameter))
1074                         h_preamble << t.cs();
1075
1076                 else if (!in_lyx_preamble &&
1077                          (t.cat() == catSpace || t.cat() == catNewline))
1078                         h_preamble << t.asInput();
1079
1080                 else if (t.cat() == catComment) {
1081                         static regex const islyxfile("%% LyX .* created this file");
1082                         static regex const usercommands("User specified LaTeX commands");
1083
1084                         string const comment = t.asInput();
1085
1086                         // magically switch encoding default if it looks like XeLaTeX
1087                         static string const magicXeLaTeX =
1088                                 "% This document must be compiled with XeLaTeX ";
1089                         if (comment.size() > magicXeLaTeX.size()
1090                                   && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1091                                   && h_inputencoding == "auto") {
1092                                 cerr << "XeLaTeX comment found, switching to UTF8\n";
1093                                 h_inputencoding = "utf8";
1094                         }
1095                         smatch sub;
1096                         if (regex_search(comment, sub, islyxfile)) {
1097                                 is_lyx_file = true;
1098                                 in_lyx_preamble = true;
1099                         } else if (is_lyx_file
1100                                    && regex_search(comment, sub, usercommands))
1101                                 in_lyx_preamble = false;
1102                         else if (!in_lyx_preamble)
1103                                 h_preamble << t.asInput();
1104                 }
1105
1106                 else if (t.cs() == "pagestyle")
1107                         h_paperpagestyle = p.verbatim_item();
1108
1109                 else if (t.cs() == "setdefaultlanguage") {
1110                         xetex = true;
1111                         // We don't yet care about non-language variant options
1112                         // because LyX doesn't support this yet, see bug #8214
1113                         if (p.hasOpt()) {
1114                                 string langopts = p.getOpt();
1115                                 // check if the option contains a variant, if yes, extract it
1116                                 string::size_type pos_var = langopts.find("variant");
1117                                 string::size_type i = langopts.find(',', pos_var);
1118                                 if (pos_var != string::npos){
1119                                         string variant;
1120                                         if (i == string::npos)
1121                                                 variant = langopts.substr(pos_var + 8, langopts.length() - pos_var - 9);
1122                                         else
1123                                                 variant = langopts.substr(pos_var + 8, i - pos_var - 8);
1124                                         h_language = variant;
1125                                 }
1126                                 p.verbatim_item();
1127                         } else
1128                                 h_language = p.verbatim_item();
1129                         //finally translate the poyglossia name to a LyX name
1130                         h_language = polyglossia2lyx(h_language);
1131                 }
1132
1133                 else if (t.cs() == "setotherlanguage") {
1134                         // We don't yet care about the option because LyX doesn't
1135                         // support this yet, see bug #8214
1136                         p.hasOpt() ? p.getOpt() : string();
1137                         p.verbatim_item();
1138                 }
1139
1140                 else if (t.cs() == "setmainfont") {
1141                         // we don't care about the option
1142                         p.hasOpt() ? p.getOpt() : string();
1143                         h_font_roman = p.getArg('{', '}');
1144                 }
1145
1146                 else if (t.cs() == "setsansfont") {
1147                         // we don't care about the option
1148                         p.hasOpt() ? p.getOpt() : string();
1149                         h_font_sans = p.getArg('{', '}');
1150                 }
1151
1152                 else if (t.cs() == "setmonofont") {
1153                         // we don't care about the option
1154                         p.hasOpt() ? p.getOpt() : string();
1155                         h_font_typewriter = p.getArg('{', '}');
1156                 }
1157
1158                 else if (t.cs() == "date") {
1159                         string argument = p.getArg('{', '}');
1160                         if (argument.empty())
1161                                 h_suppress_date = "true";
1162                         else
1163                                 h_preamble << t.asInput() << '{' << argument << '}';
1164                 }
1165
1166                 else if (t.cs() == "color") {
1167                         string const space =
1168                                 (p.hasOpt() ? p.getOpt() : string());
1169                         string argument = p.getArg('{', '}');
1170                         // check the case that a standard color is used
1171                         if (space.empty() && is_known(argument, known_basic_colors)) {
1172                                 h_fontcolor = rgbcolor2code(argument);
1173                                 preamble.registerAutomaticallyLoadedPackage("color");
1174                         } else if (space.empty() && argument == "document_fontcolor")
1175                                 preamble.registerAutomaticallyLoadedPackage("color");
1176                         // check the case that LyX's document_fontcolor is defined
1177                         // but not used for \color
1178                         else {
1179                                 h_preamble << t.asInput();
1180                                 if (!space.empty())
1181                                         h_preamble << space;
1182                                 h_preamble << '{' << argument << '}';
1183                                 // the color might already be set because \definecolor
1184                                 // is parsed before this
1185                                 h_fontcolor = "";
1186                         }
1187                 }
1188
1189                 else if (t.cs() == "pagecolor") {
1190                         string argument = p.getArg('{', '}');
1191                         // check the case that a standard color is used
1192                         if (is_known(argument, known_basic_colors)) {
1193                                 h_backgroundcolor = rgbcolor2code(argument);
1194                         } else if (argument == "page_backgroundcolor")
1195                                 preamble.registerAutomaticallyLoadedPackage("color");
1196                         // check the case that LyX's page_backgroundcolor is defined
1197                         // but not used for \pagecolor
1198                         else {
1199                                 h_preamble << t.asInput() << '{' << argument << '}';
1200                                 // the color might already be set because \definecolor
1201                                 // is parsed before this
1202                                 h_backgroundcolor = "";
1203                         }
1204                 }
1205
1206                 else if (t.cs() == "makeatletter") {
1207                         // LyX takes care of this
1208                         p.setCatCode('@', catLetter);
1209                 }
1210
1211                 else if (t.cs() == "makeatother") {
1212                         // LyX takes care of this
1213                         p.setCatCode('@', catOther);
1214                 }
1215
1216                 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1217                       || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1218                       || t.cs() == "providecommand" || t.cs() == "providecommandx"
1219                                 || t.cs() == "DeclareRobustCommand"
1220                       || t.cs() == "DeclareRobustCommandx"
1221                                 || t.cs() == "ProvideTextCommandDefault"
1222                                 || t.cs() == "DeclareMathAccent") {
1223                         bool star = false;
1224                         if (p.next_token().character() == '*') {
1225                                 p.get_token();
1226                                 star = true;
1227                         }
1228                         string const name = p.verbatim_item();
1229                         string const opt1 = p.getFullOpt();
1230                         string const opt2 = p.getFullOpt();
1231                         string const body = p.verbatim_item();
1232                         // font settings
1233                         if (name == "\\rmdefault")
1234                                 if (is_known(body, known_roman_fonts))
1235                                         h_font_roman = body;
1236                         if (name == "\\sfdefault")
1237                                 if (is_known(body, known_sans_fonts))
1238                                         h_font_sans = body;
1239                         if (name == "\\ttdefault")
1240                                 if (is_known(body, known_typewriter_fonts))
1241                                         h_font_typewriter = body;
1242                         if (name == "\\familydefault") {
1243                                 string family = body;
1244                                 // remove leading "\"
1245                                 h_font_default_family = family.erase(0,1);
1246                         }
1247
1248                         // remove the lyxdot definition that is re-added by LyX
1249                         // if necessary
1250                         if (name == "\\lyxdot")
1251                                 in_lyx_preamble = true;
1252
1253                         // Add the command to the known commands
1254                         add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1255
1256                         // only non-lyxspecific stuff
1257                         if (!in_lyx_preamble) {
1258                                 ostringstream ss;
1259                                 ss << '\\' << t.cs();
1260                                 if (star)
1261                                         ss << '*';
1262                                 ss << '{' << name << '}' << opt1 << opt2
1263                                    << '{' << body << "}";
1264                                 h_preamble << ss.str();
1265 /*
1266                                 ostream & out = in_preamble ? h_preamble : os;
1267                                 out << "\\" << t.cs() << "{" << name << "}"
1268                                     << opts << "{" << body << "}";
1269 */
1270                         }
1271                 }
1272
1273                 else if (t.cs() == "documentclass") {
1274                         vector<string>::iterator it;
1275                         vector<string> opts = split_options(p.getArg('[', ']'));
1276                         handle_opt(opts, known_fontsizes, h_paperfontsize);
1277                         delete_opt(opts, known_fontsizes);
1278                         // delete "pt" at the end
1279                         string::size_type i = h_paperfontsize.find("pt");
1280                         if (i != string::npos)
1281                                 h_paperfontsize.erase(i);
1282                         // The documentclass options are always parsed before the options
1283                         // of the babel call so that a language cannot overwrite the babel
1284                         // options.
1285                         handle_opt(opts, known_languages, h_language);
1286                         delete_opt(opts, known_languages);
1287
1288                         // paper orientation
1289                         if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1290                                 h_paperorientation = "landscape";
1291                                 opts.erase(it);
1292                         }
1293                         // paper sides
1294                         if ((it = find(opts.begin(), opts.end(), "oneside"))
1295                                  != opts.end()) {
1296                                 h_papersides = "1";
1297                                 opts.erase(it);
1298                         }
1299                         if ((it = find(opts.begin(), opts.end(), "twoside"))
1300                                  != opts.end()) {
1301                                 h_papersides = "2";
1302                                 opts.erase(it);
1303                         }
1304                         // paper columns
1305                         if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1306                                  != opts.end()) {
1307                                 h_papercolumns = "1";
1308                                 opts.erase(it);
1309                         }
1310                         if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1311                                  != opts.end()) {
1312                                 h_papercolumns = "2";
1313                                 opts.erase(it);
1314                         }
1315                         // paper sizes
1316                         // some size options are known to any document classes, other sizes
1317                         // are handled by the \geometry command of the geometry package
1318                         handle_opt(opts, known_class_paper_sizes, h_papersize);
1319                         delete_opt(opts, known_class_paper_sizes);
1320                         // the remaining options
1321                         h_options = join(opts, ",");
1322                         // FIXME This does not work for classes that have a
1323                         //       different name in LyX than in LaTeX
1324                         h_textclass = p.getArg('{', '}');
1325                 }
1326
1327                 else if (t.cs() == "usepackage") {
1328                         string const options = p.getArg('[', ']');
1329                         string const name = p.getArg('{', '}');
1330                         vector<string> vecnames;
1331                         split(name, vecnames, ',');
1332                         vector<string>::const_iterator it  = vecnames.begin();
1333                         vector<string>::const_iterator end = vecnames.end();
1334                         for (; it != end; ++it)
1335                                 handle_package(p, trimSpaceAndEol(*it), options,
1336                                                in_lyx_preamble);
1337                 }
1338
1339                 else if (t.cs() == "inputencoding") {
1340                         string const encoding = p.getArg('{','}');
1341                         h_inputencoding = encoding;
1342                         p.setEncoding(encoding);
1343                 }
1344
1345                 else if (t.cs() == "newenvironment") {
1346                         string const name = p.getArg('{', '}');
1347                         string const opt1 = p.getFullOpt();
1348                         string const opt2 = p.getFullOpt();
1349                         string const beg = p.verbatim_item();
1350                         string const end = p.verbatim_item();
1351                         if (!in_lyx_preamble) {
1352                                 h_preamble << "\\newenvironment{" << name
1353                                            << '}' << opt1 << opt2 << '{'
1354                                            << beg << "}{" << end << '}';
1355                         }
1356                         add_known_environment(name, opt1, !opt2.empty(),
1357                                               from_utf8(beg), from_utf8(end));
1358
1359                 }
1360
1361                 else if (t.cs() == "def") {
1362                         string name = p.get_token().cs();
1363                         // In fact, name may be more than the name:
1364                         // In the test case of bug 8116
1365                         // name == "csname SF@gobble@opt \endcsname".
1366                         // Therefore, we need to use asInput() instead of cs().
1367                         while (p.next_token().cat() != catBegin)
1368                                 name += p.get_token().asInput();
1369                         if (!in_lyx_preamble)
1370                                 h_preamble << "\\def\\" << name << '{'
1371                                            << p.verbatim_item() << "}";
1372                 }
1373
1374                 else if (t.cs() == "newcolumntype") {
1375                         string const name = p.getArg('{', '}');
1376                         trimSpaceAndEol(name);
1377                         int nargs = 0;
1378                         string opts = p.getOpt();
1379                         if (!opts.empty()) {
1380                                 istringstream is(string(opts, 1));
1381                                 is >> nargs;
1382                         }
1383                         special_columns[name[0]] = nargs;
1384                         h_preamble << "\\newcolumntype{" << name << "}";
1385                         if (nargs)
1386                                 h_preamble << "[" << nargs << "]";
1387                         h_preamble << "{" << p.verbatim_item() << "}";
1388                 }
1389
1390                 else if (t.cs() == "setcounter") {
1391                         string const name = p.getArg('{', '}');
1392                         string const content = p.getArg('{', '}');
1393                         if (name == "secnumdepth")
1394                                 h_secnumdepth = content;
1395                         else if (name == "tocdepth")
1396                                 h_tocdepth = content;
1397                         else
1398                                 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1399                 }
1400
1401                 else if (t.cs() == "setlength") {
1402                         string const name = p.verbatim_item();
1403                         string const content = p.verbatim_item();
1404                         // the paragraphs are only not indented when \parindent is set to zero
1405                         if (name == "\\parindent" && content != "") {
1406                                 if (content[0] == '0')
1407                                         h_paragraph_separation = "skip";
1408                                 else
1409                                         h_paragraph_indentation = translate_len(content);
1410                         } else if (name == "\\parskip") {
1411                                 if (content == "\\smallskipamount")
1412                                         h_defskip = "smallskip";
1413                                 else if (content == "\\medskipamount")
1414                                         h_defskip = "medskip";
1415                                 else if (content == "\\bigskipamount")
1416                                         h_defskip = "bigskip";
1417                                 else
1418                                         h_defskip = content;
1419                         } else
1420                                 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1421                 }
1422
1423                 else if (t.cs() == "onehalfspacing")
1424                         h_spacing = "onehalf";
1425
1426                 else if (t.cs() == "doublespacing")
1427                         h_spacing = "double";
1428
1429                 else if (t.cs() == "setstretch")
1430                         h_spacing = "other " + p.verbatim_item();
1431
1432                 else if (t.cs() == "begin") {
1433                         string const name = p.getArg('{', '}');
1434                         if (name == "document")
1435                                 break;
1436                         h_preamble << "\\begin{" << name << "}";
1437                 }
1438
1439                 else if (t.cs() == "geometry") {
1440                         vector<string> opts = split_options(p.getArg('{', '}'));
1441                         handle_geometry(opts);
1442                 }
1443
1444                 else if (t.cs() == "definecolor") {
1445                         string const color = p.getArg('{', '}');
1446                         string const space = p.getArg('{', '}');
1447                         string const value = p.getArg('{', '}');
1448                         if (color == "document_fontcolor" && space == "rgb") {
1449                                 RGBColor c(RGBColorFromLaTeX(value));
1450                                 h_fontcolor = X11hexname(c);
1451                         } else if (color == "note_fontcolor" && space == "rgb") {
1452                                 RGBColor c(RGBColorFromLaTeX(value));
1453                                 h_notefontcolor = X11hexname(c);
1454                         } else if (color == "page_backgroundcolor" && space == "rgb") {
1455                                 RGBColor c(RGBColorFromLaTeX(value));
1456                                 h_backgroundcolor = X11hexname(c);
1457                         } else if (color == "shadecolor" && space == "rgb") {
1458                                 RGBColor c(RGBColorFromLaTeX(value));
1459                                 h_boxbgcolor = X11hexname(c);
1460                         } else {
1461                                 h_preamble << "\\definecolor{" << color
1462                                            << "}{" << space << "}{" << value
1463                                            << '}';
1464                         }
1465                 }
1466
1467                 else if (t.cs() == "bibliographystyle")
1468                         h_biblio_style = p.verbatim_item();
1469
1470                 else if (t.cs() == "jurabibsetup") {
1471                         // FIXME p.getArg('{', '}') is most probably wrong (it
1472                         //       does not handle nested braces).
1473                         //       Use p.verbatim_item() instead.
1474                         vector<string> jurabibsetup =
1475                                 split_options(p.getArg('{', '}'));
1476                         // add jurabibsetup to the jurabib package options
1477                         add_package("jurabib", jurabibsetup);
1478                         if (!jurabibsetup.empty()) {
1479                                 h_preamble << "\\jurabibsetup{"
1480                                            << join(jurabibsetup, ",") << '}';
1481                         }
1482                 }
1483
1484                 else if (t.cs() == "hypersetup") {
1485                         vector<string> hypersetup =
1486                                 split_options(p.verbatim_item());
1487                         // add hypersetup to the hyperref package options
1488                         handle_hyperref(hypersetup);
1489                         if (!hypersetup.empty()) {
1490                                 h_preamble << "\\hypersetup{"
1491                                            << join(hypersetup, ",") << '}';
1492                         }
1493                 }
1494
1495                 else if (is_known(t.cs(), known_if_3arg_commands)) {
1496                         // prevent misparsing of \usepackage if it is used
1497                         // as an argument (see e.g. our own output of
1498                         // \@ifundefined above)
1499                         string const arg1 = p.verbatim_item();
1500                         string const arg2 = p.verbatim_item();
1501                         string const arg3 = p.verbatim_item();
1502                         // test case \@ifundefined{date}{}{\date{}}
1503                         if (t.cs() == "@ifundefined" && arg1 == "date" &&
1504                             arg2.empty() && arg3 == "\\date{}") {
1505                                 h_suppress_date = "true";
1506                         // older tex2lyx versions did output
1507                         // \@ifundefined{definecolor}{\usepackage{color}}{}
1508                         } else if (t.cs() == "@ifundefined" &&
1509                                    arg1 == "definecolor" &&
1510                                    arg2 == "\\usepackage{color}" &&
1511                                    arg3.empty()) {
1512                                 if (!in_lyx_preamble)
1513                                         h_preamble << package_beg_sep
1514                                                    << "color"
1515                                                    << package_mid_sep
1516                                                    << "\\@ifundefined{definecolor}{color}{}"
1517                                                    << package_end_sep;
1518                         // test for case
1519                         //\@ifundefined{showcaptionsetup}{}{%
1520                         // \PassOptionsToPackage{caption=false}{subfig}}
1521                         // that LyX uses for subfloats
1522                         } else if (t.cs() == "@ifundefined" &&
1523                                    arg1 == "showcaptionsetup" && arg2.empty()
1524                                 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1525                                 ; // do nothing
1526                         } else if (!in_lyx_preamble) {
1527                                 h_preamble << t.asInput()
1528                                            << '{' << arg1 << '}'
1529                                            << '{' << arg2 << '}'
1530                                            << '{' << arg3 << '}';
1531                         }
1532                 }
1533
1534                 else if (is_known(t.cs(), known_if_commands)) {
1535                         // must not parse anything in conditional code, since
1536                         // LyX would output the parsed contents unconditionally
1537                         if (!in_lyx_preamble)
1538                                 h_preamble << t.asInput();
1539                         handle_if(p, in_lyx_preamble);
1540                 }
1541
1542                 else if (!t.cs().empty() && !in_lyx_preamble)
1543                         h_preamble << '\\' << t.cs();
1544         }
1545
1546         // remove the whitespace
1547         p.skip_spaces();
1548
1549         // Force textclass if the user wanted it
1550         if (!forceclass.empty())
1551                 h_textclass = forceclass;
1552         if (noweb_mode && !prefixIs(h_textclass, "literate-"))
1553                 h_textclass.insert(0, "literate-");
1554         tc.setName(h_textclass);
1555         if (!tc.load()) {
1556                 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1557                 exit(EXIT_FAILURE);
1558         }
1559         if (h_papersides.empty()) {
1560                 ostringstream ss;
1561                 ss << tc.sides();
1562                 h_papersides = ss.str();
1563         }
1564 }
1565
1566
1567 string babel2lyx(string const & language)
1568 {
1569         char const * const * where = is_known(language, known_languages);
1570         if (where)
1571                 return known_coded_languages[where - known_languages];
1572         return language;
1573 }
1574
1575
1576 string polyglossia2lyx(string const & language)
1577 {
1578         char const * const * where = is_known(language, polyglossia_languages);
1579         if (where)
1580                 return coded_polyglossia_languages[where - polyglossia_languages];
1581         return language;
1582 }
1583
1584
1585 string rgbcolor2code(string const & name)
1586 {
1587         char const * const * where = is_known(name, known_basic_colors);
1588         if (where) {
1589                 // "red", "green" etc
1590                 return known_basic_color_codes[where - known_basic_colors];
1591         }
1592         // "255,0,0", "0,255,0" etc
1593         RGBColor c(RGBColorFromLaTeX(name));
1594         return X11hexname(c);
1595 }
1596
1597 // }])
1598
1599
1600 } // namespace lyx