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