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