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