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