]> git.lyx.org Git - lyx.git/blob - src/tex2lyx/Preamble.cpp
01587cc02dfbbe0995cde64168ed7cca9e472129
[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[0]           = "default";
477         h_font_roman[1]           = "default";
478         h_font_sans[0]            = "default";
479         h_font_sans[1]            = "default";
480         h_font_typewriter[0]      = "default";
481         h_font_typewriter[1]      = "default";
482         h_font_math[0]            = "auto";
483         h_font_math[1]            = "auto";
484         h_font_default_family     = "default";
485         h_use_non_tex_fonts       = false;
486         h_font_sc                 = "false";
487         h_font_osf                = "false";
488         h_font_sf_scale[0]        = "100";
489         h_font_sf_scale[1]        = "100";
490         h_font_tt_scale[0]        = "100";
491         h_font_tt_scale[1]        = "100";
492         //h_font_cjk
493         h_graphics                = "default";
494         h_default_output_format   = "default";
495         h_html_be_strict          = "false";
496         h_html_css_as_file        = "0";
497         h_html_math_output        = "0";
498         h_index[0]                = "Index";
499         h_index_command           = "default";
500         h_inputencoding           = "auto";
501         h_justification           = "true";
502         h_language                = "english";
503         h_language_package        = "none";
504         //h_listings_params;
505         h_maintain_unincluded_children = "false";
506         //h_margins;
507         //h_notefontcolor;
508         //h_options;
509         h_output_changes          = "false";
510         h_output_sync             = "0";
511         //h_output_sync_macro
512         h_papercolumns            = "1";
513         h_paperfontsize           = "default";
514         h_paperorientation        = "portrait";
515         h_paperpagestyle          = "default";
516         //h_papersides;
517         h_papersize               = "default";
518         h_paragraph_indentation   = "default";
519         h_paragraph_separation    = "indent";
520         //h_pdf_title;
521         //h_pdf_author;
522         //h_pdf_subject;
523         //h_pdf_keywords;
524         h_pdf_bookmarks           = "0";
525         h_pdf_bookmarksnumbered   = "0";
526         h_pdf_bookmarksopen       = "0";
527         h_pdf_bookmarksopenlevel  = "1";
528         h_pdf_breaklinks          = "0";
529         h_pdf_pdfborder           = "0";
530         h_pdf_colorlinks          = "0";
531         h_pdf_backref             = "section";
532         h_pdf_pdfusetitle         = "0";
533         //h_pdf_pagemode;
534         //h_pdf_quoted_options;
535         h_quotes_language         = "english";
536         h_secnumdepth             = "3";
537         h_shortcut[0]             = "idx";
538         h_spacing                 = "single";
539         h_suppress_date           = "false";
540         h_textclass               = "article";
541         h_tocdepth                = "3";
542         h_tracking_changes        = "false";
543         h_use_bibtopic            = "false";
544         h_use_indices             = "false";
545         h_use_geometry            = "false";
546         h_use_default_options     = "false";
547         h_use_hyperref            = "false";
548         h_use_refstyle            = false;
549         h_use_packages["amsmath"]    = "1";
550         h_use_packages["amssymb"]    = "0";
551         h_use_packages["cancel"]     = "0";
552         h_use_packages["esint"]      = "1";
553         h_use_packages["mhchem"]     = "0";
554         h_use_packages["mathdots"]   = "0";
555         h_use_packages["mathtools"]  = "0";
556         h_use_packages["stackrel"]   = "0";
557         h_use_packages["stmaryrd"]   = "0";
558         h_use_packages["undertilde"] = "0";
559 }
560
561
562 void Preamble::handle_hyperref(vector<string> & options)
563 {
564         // FIXME swallow inputencoding changes that might surround the
565         //       hyperref setup if it was written by LyX
566         h_use_hyperref = "true";
567         // swallow "unicode=true", since LyX does always write that
568         vector<string>::iterator it =
569                 find(options.begin(), options.end(), "unicode=true");
570         if (it != options.end())
571                 options.erase(it);
572         it = find(options.begin(), options.end(), "pdfusetitle");
573         if (it != options.end()) {
574                 h_pdf_pdfusetitle = "1";
575                 options.erase(it);
576         }
577         string bookmarks = process_keyval_opt(options, "bookmarks");
578         if (bookmarks == "true")
579                 h_pdf_bookmarks = "1";
580         else if (bookmarks == "false")
581                 h_pdf_bookmarks = "0";
582         if (h_pdf_bookmarks == "1") {
583                 string bookmarksnumbered =
584                         process_keyval_opt(options, "bookmarksnumbered");
585                 if (bookmarksnumbered == "true")
586                         h_pdf_bookmarksnumbered = "1";
587                 else if (bookmarksnumbered == "false")
588                         h_pdf_bookmarksnumbered = "0";
589                 string bookmarksopen =
590                         process_keyval_opt(options, "bookmarksopen");
591                 if (bookmarksopen == "true")
592                         h_pdf_bookmarksopen = "1";
593                 else if (bookmarksopen == "false")
594                         h_pdf_bookmarksopen = "0";
595                 if (h_pdf_bookmarksopen == "1") {
596                         string bookmarksopenlevel =
597                                 process_keyval_opt(options, "bookmarksopenlevel");
598                         if (!bookmarksopenlevel.empty())
599                                 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
600                 }
601         }
602         string breaklinks = process_keyval_opt(options, "breaklinks");
603         if (breaklinks == "true")
604                 h_pdf_breaklinks = "1";
605         else if (breaklinks == "false")
606                 h_pdf_breaklinks = "0";
607         string pdfborder = process_keyval_opt(options, "pdfborder");
608         if (pdfborder == "{0 0 0}")
609                 h_pdf_pdfborder = "1";
610         else if (pdfborder == "{0 0 1}")
611                 h_pdf_pdfborder = "0";
612         string backref = process_keyval_opt(options, "backref");
613         if (!backref.empty())
614                 h_pdf_backref = backref;
615         string colorlinks = process_keyval_opt(options, "colorlinks");
616         if (colorlinks == "true")
617                 h_pdf_colorlinks = "1";
618         else if (colorlinks == "false")
619                 h_pdf_colorlinks = "0";
620         string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
621         if (!pdfpagemode.empty())
622                 h_pdf_pagemode = pdfpagemode;
623         string pdftitle = process_keyval_opt(options, "pdftitle");
624         if (!pdftitle.empty()) {
625                 h_pdf_title = remove_braces(pdftitle);
626         }
627         string pdfauthor = process_keyval_opt(options, "pdfauthor");
628         if (!pdfauthor.empty()) {
629                 h_pdf_author = remove_braces(pdfauthor);
630         }
631         string pdfsubject = process_keyval_opt(options, "pdfsubject");
632         if (!pdfsubject.empty())
633                 h_pdf_subject = remove_braces(pdfsubject);
634         string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
635         if (!pdfkeywords.empty())
636                 h_pdf_keywords = remove_braces(pdfkeywords);
637         if (!options.empty()) {
638                 if (!h_pdf_quoted_options.empty())
639                         h_pdf_quoted_options += ',';
640                 h_pdf_quoted_options += join(options, ",");
641                 options.clear();
642         }
643 }
644
645
646 void Preamble::handle_geometry(vector<string> & options)
647 {
648         h_use_geometry = "true";
649         vector<string>::iterator it;
650         // paper orientation
651         if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
652                 h_paperorientation = "landscape";
653                 options.erase(it);
654         }
655         // paper size
656         // keyval version: "paper=letter"
657         string paper = process_keyval_opt(options, "paper");
658         if (!paper.empty())
659                 h_papersize = paper + "paper";
660         // alternative version: "letterpaper"
661         handle_opt(options, known_paper_sizes, h_papersize);
662         delete_opt(options, known_paper_sizes);
663         // page margins
664         char const * const * margin = known_paper_margins;
665         for (; *margin; ++margin) {
666                 string value = process_keyval_opt(options, *margin);
667                 if (!value.empty()) {
668                         int k = margin - known_paper_margins;
669                         string name = known_coded_paper_margins[k];
670                         h_margins += '\\' + name + ' ' + value + '\n';
671                 }
672         }
673 }
674
675
676 void Preamble::handle_package(Parser &p, string const & name,
677                               string const & opts, bool in_lyx_preamble)
678 {
679         vector<string> options = split_options(opts);
680         add_package(name, options);
681         char const * const * where = 0;
682
683         if (is_known(name, known_xetex_packages)) {
684                 xetex = true;
685                 h_use_non_tex_fonts = true;
686                 registerAutomaticallyLoadedPackage("fontspec");
687                 if (h_inputencoding == "auto")
688                         p.setEncoding("UTF-8");
689         }
690
691         // roman fonts
692         if (is_known(name, known_roman_fonts))
693                 h_font_roman[0] = name;
694
695         if (name == "fourier") {
696                 h_font_roman[0] = "utopia";
697                 // when font uses real small capitals
698                 if (opts == "expert")
699                         h_font_sc = "true";
700         }
701
702         if (name == "garamondx") {
703                 h_font_roman[0] = "garamondx";
704                 if (opts == "osfI")
705                         h_font_osf = "true";
706         }
707
708         if (name == "libertine") {
709                 h_font_roman[0] = "libertine";
710                 // this automatically invokes biolinum
711                 h_font_sans[0] = "biolinum";
712                 if (opts == "osf")
713                         h_font_osf = "true";
714                 else if (opts == "lining")
715                         h_font_osf = "false";
716         }
717
718         if (name == "libertine-type1") {
719                 h_font_roman[0] = "libertine";
720                 // NOTE: contrary to libertine.sty, libertine-type1
721                 // does not automatically invoke biolinum
722                 if (opts == "lining")
723                         h_font_osf = "false";
724                 else if (opts == "osf")
725                         h_font_osf = "true";
726         }
727
728         if (name == "mathdesign") {
729                 if (opts.find("charter") != string::npos)
730                         h_font_roman[0] = "md-charter";
731                 if (opts.find("garamond") != string::npos)
732                         h_font_roman[0] = "md-garamond";
733                 if (opts.find("utopia") != string::npos)
734                         h_font_roman[0] = "md-utopia";
735                 if (opts.find("expert") != string::npos) {
736                         h_font_sc = "true";
737                         h_font_osf = "true";
738                 }
739         }
740
741         else if (name == "mathpazo")
742                 h_font_roman[0] = "palatino";
743
744         else if (name == "mathptmx")
745                 h_font_roman[0] = "times";
746
747         // sansserif fonts
748         if (is_known(name, known_sans_fonts)) {
749                 h_font_sans[0] = name;
750                 if (options.size() >= 1) {
751                         if (scale_as_percentage(opts, h_font_sf_scale[0]))
752                                 options.clear();
753                 }
754         }
755
756         if (name == "biolinum-type1") {
757                 h_font_sans[0] = "biolinum";
758                 // biolinum can have several options, e.g. [osf,scaled=0.97]
759                 string::size_type pos = opts.find("osf");
760                 if (pos != string::npos)
761                         h_font_osf = "true";
762         }
763
764         // typewriter fonts
765         if (is_known(name, known_typewriter_fonts)) {
766                 // fourier can be set as roman font _only_
767                 // fourier as typewriter is handled in handling of \ttdefault
768                 if (name != "fourier") {
769                         h_font_typewriter[0] = name;
770                         if (options.size() >= 1) {
771                                 if (scale_as_percentage(opts, h_font_tt_scale[0]))
772                                         options.clear();
773                         }
774                 }
775         }
776
777         if (name == "libertineMono-type1") {
778                 h_font_typewriter[0] = "libertine-mono";
779         }
780
781         // font uses old-style figure
782         if (name == "eco")
783                 h_font_osf = "true";
784
785         // math fonts
786         if (is_known(name, known_math_fonts))
787                 h_font_math[0] = name;
788
789         if (name == "newtxmath") {
790                 if (opts.empty())
791                         h_font_math[0] = "newtxmath";
792                 else if (opts == "garamondx")
793                         h_font_math[0] = "garamondx-ntxm";
794                 else if (opts == "libertine")
795                         h_font_math[0] = "libertine-ntxm";
796                 else if (opts == "minion")
797                         h_font_math[0] = "minion-ntxm";
798         }
799
800         if (name == "iwona")
801                 if (opts == "math")
802                         h_font_math[0] = "iwona-math";
803
804         if (name == "kurier")
805                 if (opts == "math")
806                         h_font_math[0] = "kurier-math";
807
808         // after the detection and handling of special cases, we can remove the
809         // fonts, otherwise they would appear in the preamble, see bug #7856
810         if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
811                 ||      is_known(name, known_typewriter_fonts) || is_known(name, known_math_fonts))
812                 ;
813
814         else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
815                  name == "esint" || name == "mhchem" || name == "mathdots" ||
816                  name == "mathtools" || name == "stackrel" ||
817                  name == "stmaryrd" || name == "undertilde")
818                 h_use_packages[name] = "2";
819
820         else if (name == "babel") {
821                 h_language_package = "default";
822                 // One might think we would have to do nothing if babel is loaded
823                 // without any options to prevent pollution of the preamble with this
824                 // babel call in every roundtrip.
825                 // But the user could have defined babel-specific things afterwards. So
826                 // we need to keep it in the preamble to prevent cases like bug #7861.
827                 if (!opts.empty()) {
828                         // check if more than one option was used - used later for inputenc
829                         if (options.begin() != options.end() - 1)
830                                 one_language = false;
831                         // babel takes the last language of the option of its \usepackage
832                         // call as document language. If there is no such language option, the
833                         // last language in the documentclass options is used.
834                         handle_opt(options, known_languages, h_language);
835                         // translate the babel name to a LyX name
836                         h_language = babel2lyx(h_language);
837                         if (h_language == "japanese") {
838                                 // For Japanese, the encoding isn't indicated in the source
839                                 // file, and there's really not much we can do. We could
840                                 // 1) offer a list of possible encodings to choose from, or
841                                 // 2) determine the encoding of the file by inspecting it.
842                                 // For the time being, we leave the encoding alone so that
843                                 // we don't get iconv errors when making a wrong guess, and
844                                 // we will output a note at the top of the document
845                                 // explaining what to do.
846                                 Encoding const * const enc = encodings.fromIconvName(
847                                         p.getEncoding(), Encoding::japanese, false);
848                                 if (enc)
849                                         h_inputencoding = enc->name();
850                                 is_nonCJKJapanese = true;
851                                 // in this case babel can be removed from the preamble
852                                 registerAutomaticallyLoadedPackage("babel");
853                         } else {
854                                 // If babel is called with options, LyX puts them by default into the
855                                 // document class options. This works for most languages, except
856                                 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
857                                 // perhaps in future others.
858                                 // Therefore keep the babel call as it is as the user might have
859                                 // reasons for it.
860                                 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
861                         }
862                         delete_opt(options, known_languages);
863                 } else {
864                         h_preamble << "\\usepackage{babel}\n";
865                         explicit_babel = true;
866                 }
867         }
868
869         else if (name == "polyglossia") {
870                 h_language_package = "default";
871                 h_default_output_format = "pdf4";
872                 h_use_non_tex_fonts = true;
873                 xetex = true;
874                 registerAutomaticallyLoadedPackage("xunicode");
875                 if (h_inputencoding == "auto")
876                         p.setEncoding("UTF-8");
877         }
878
879         else if (name == "CJK") {
880                 // set the encoding to "auto" because it might be set to "default" by the babel handling
881                 // and this would not be correct for CJK
882                 if (h_inputencoding == "default")
883                         h_inputencoding = "auto";
884                 registerAutomaticallyLoadedPackage("CJK");
885         }
886
887         else if (name == "CJKutf8") {
888                 h_inputencoding = "utf8-cjk";
889                 p.setEncoding("UTF-8");
890                 registerAutomaticallyLoadedPackage("CJKutf8");
891         }
892
893         else if (name == "fontenc") {
894                 h_fontencoding = getStringFromVector(options, ",");
895                 /* We could do the following for better round trip support,
896                  * but this makes the document less portable, so I skip it:
897                 if (h_fontencoding == lyxrc.fontenc)
898                         h_fontencoding = "global";
899                  */
900                 options.clear();
901         }
902
903         else if (name == "inputenc" || name == "luainputenc") {
904                 // h_inputencoding is only set when there is not more than one
905                 // inputenc option because otherwise h_inputencoding must be
906                 // set to "auto" (the default encoding of the document language)
907                 // Therefore check that exactly one option is passed to inputenc.
908                 // It is also only set when there is not more than one babel
909                 // language option.
910                 if (!options.empty()) {
911                         string const encoding = options.back();
912                         Encoding const * const enc = encodings.fromLaTeXName(
913                                 encoding, Encoding::inputenc, true);
914                         if (!enc)
915                                 cerr << "Unknown encoding " << encoding << ". Ignoring." << std::endl;
916                         else {
917                                 if (!enc->unsafe() && options.size() == 1 && one_language == true)
918                                         h_inputencoding = enc->name();
919                                 p.setEncoding(enc->iconvName());
920                         }
921                         options.clear();
922                 }
923         }
924
925         else if (name == "srcltx") {
926                 h_output_sync = "1";
927                 if (!opts.empty()) {
928                         h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
929                         options.clear();
930                 } else
931                         h_output_sync_macro = "\\usepackage{srcltx}";
932         }
933
934         else if (is_known(name, known_old_language_packages)) {
935                 // known language packages from the times before babel
936                 // if they are found and not also babel, they will be used as
937                 // custom language package
938                 h_language_package = "\\usepackage{" + name + "}";
939         }
940
941         else if (name == "lyxskak") {
942                 // ignore this and its options
943                 const char * const o[] = {"ps", "mover", 0};
944                 delete_opt(options, o);
945         }
946
947         else if (is_known(name, known_lyx_packages) && options.empty()) {
948                 if (name == "splitidx")
949                         h_use_indices = "true";
950                 if (name == "refstyle")
951                         h_use_refstyle = true;
952                 else if (name == "prettyref")
953                         h_use_refstyle = false;
954                 if (!in_lyx_preamble) {
955                         h_preamble << package_beg_sep << name
956                                    << package_mid_sep << "\\usepackage{"
957                                    << name << '}';
958                         if (p.next_token().cat() == catNewline ||
959                             (p.next_token().cat() == catSpace &&
960                              p.next_next_token().cat() == catNewline))
961                                 h_preamble << '\n';
962                         h_preamble << package_end_sep;
963                 }
964         }
965
966         else if (name == "geometry")
967                 handle_geometry(options);
968
969         else if (name == "subfig")
970                 ; // ignore this FIXME: Use the package separator mechanism instead
971
972         else if ((where = is_known(name, known_languages)))
973                 h_language = known_coded_languages[where - known_languages];
974
975         else if (name == "natbib") {
976                 h_biblio_style = "plainnat";
977                 h_cite_engine = "natbib";
978                 h_cite_engine_type = "authoryear";
979                 vector<string>::iterator it =
980                         find(options.begin(), options.end(), "authoryear");
981                 if (it != options.end())
982                         options.erase(it);
983                 else {
984                         it = find(options.begin(), options.end(), "numbers");
985                         if (it != options.end()) {
986                                 h_cite_engine_type = "numerical";
987                                 options.erase(it);
988                         }
989                 }
990         }
991
992         else if (name == "jurabib") {
993                 h_biblio_style = "jurabib";
994                 h_cite_engine = "jurabib";
995                 h_cite_engine_type = "authoryear";
996         }
997
998         else if (name == "bibtopic")
999                 h_use_bibtopic = "true";
1000
1001         else if (name == "hyperref")
1002                 handle_hyperref(options);
1003
1004         else if (name == "algorithm2e") {
1005                 // Load "algorithm2e" module
1006                 addModule("algorithm2e");
1007                 // Add the package options to the global document options
1008                 if (!options.empty()) {
1009                         if (h_options.empty())
1010                                 h_options = join(options, ",");
1011                         else
1012                                 h_options += ',' + join(options, ",");
1013                 }
1014         }
1015
1016         else if (!in_lyx_preamble) {
1017                 if (options.empty())
1018                         h_preamble << "\\usepackage{" << name << '}';
1019                 else {
1020                         h_preamble << "\\usepackage[" << opts << "]{"
1021                                    << name << '}';
1022                         options.clear();
1023                 }
1024                 if (p.next_token().cat() == catNewline ||
1025                     (p.next_token().cat() == catSpace &&
1026                      p.next_next_token().cat() == catNewline))
1027                         h_preamble << '\n';
1028         }
1029
1030         // We need to do something with the options...
1031         if (!options.empty())
1032                 cerr << "Ignoring options '" << join(options, ",")
1033                      << "' of package " << name << '.' << endl;
1034
1035         // remove the whitespace
1036         p.skip_spaces();
1037 }
1038
1039
1040 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1041 {
1042         while (p.good()) {
1043                 Token t = p.get_token();
1044                 if (t.cat() == catEscape &&
1045                     is_known(t.cs(), known_if_commands))
1046                         handle_if(p, in_lyx_preamble);
1047                 else {
1048                         if (!in_lyx_preamble)
1049                                 h_preamble << t.asInput();
1050                         if (t.cat() == catEscape && t.cs() == "fi")
1051                                 return;
1052                 }
1053         }
1054 }
1055
1056
1057 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1058 {
1059         // set the quote language
1060         // LyX only knows the following quotes languages:
1061         // english, swedish, german, polish, french and danish
1062         // (quotes for "japanese" and "chinese-traditional" are missing because
1063         //  they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
1064         // conversion list taken from
1065         // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
1066         // (quotes for kazakh and interlingua are unknown)
1067         // danish
1068         if (is_known(h_language, known_danish_quotes_languages))
1069                 h_quotes_language = "danish";
1070         // french
1071         else if (is_known(h_language, known_french_quotes_languages))
1072                 h_quotes_language = "french";
1073         // german
1074         else if (is_known(h_language, known_german_quotes_languages))
1075                 h_quotes_language = "german";
1076         // polish
1077         else if (is_known(h_language, known_polish_quotes_languages))
1078                 h_quotes_language = "polish";
1079         // swedish
1080         else if (is_known(h_language, known_swedish_quotes_languages))
1081                 h_quotes_language = "swedish";
1082         //english
1083         else if (is_known(h_language, known_english_quotes_languages))
1084                 h_quotes_language = "english";
1085
1086         if (contains(h_float_placement, "H"))
1087                 registerAutomaticallyLoadedPackage("float");
1088         if (h_spacing != "single" && h_spacing != "default")
1089                 registerAutomaticallyLoadedPackage("setspace");
1090         if (h_use_packages["amsmath"] == "2") {
1091                 // amsbsy and amstext are already provided by amsmath
1092                 registerAutomaticallyLoadedPackage("amsbsy");
1093                 registerAutomaticallyLoadedPackage("amstext");
1094         }
1095
1096         // output the LyX file settings
1097         // Important: Keep the version formatting in sync with LyX and
1098         //            lyx2lyx (bug 7951)
1099         string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1100         os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1101            << lyx_version_minor << '\n'
1102            << "\\lyxformat " << LYX_FORMAT << '\n'
1103            << "\\begin_document\n"
1104            << "\\begin_header\n"
1105            << "\\origin " << origin << "\n"
1106            << "\\textclass " << h_textclass << "\n";
1107         string const raw = subdoc ? empty_string() : h_preamble.str();
1108         if (!raw.empty()) {
1109                 os << "\\begin_preamble\n";
1110                 for (string::size_type i = 0; i < raw.size(); ++i) {
1111                         if (raw[i] == package_beg_sep) {
1112                                 // Here follows some package loading code that
1113                                 // must be skipped if the package is loaded
1114                                 // automatically.
1115                                 string::size_type j = raw.find(package_mid_sep, i);
1116                                 if (j == string::npos)
1117                                         return false;
1118                                 string::size_type k = raw.find(package_end_sep, j);
1119                                 if (k == string::npos)
1120                                         return false;
1121                                 string const package = raw.substr(i + 1, j - i - 1);
1122                                 string const replacement = raw.substr(j + 1, k - j - 1);
1123                                 if (auto_packages.find(package) == auto_packages.end())
1124                                         os << replacement;
1125                                 i = k;
1126                         } else
1127                                 os.put(raw[i]);
1128                 }
1129                 os << "\n\\end_preamble\n";
1130         }
1131         if (!h_options.empty())
1132                 os << "\\options " << h_options << "\n";
1133         os << "\\use_default_options " << h_use_default_options << "\n";
1134         if (!used_modules.empty()) {
1135                 os << "\\begin_modules\n";
1136                 vector<string>::const_iterator const end = used_modules.end();
1137                 vector<string>::const_iterator it = used_modules.begin();
1138                 for (; it != end; ++it)
1139                         os << *it << '\n';
1140                 os << "\\end_modules\n";
1141         }
1142         os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1143            << "\\language " << h_language << "\n"
1144            << "\\language_package " << h_language_package << "\n"
1145            << "\\inputencoding " << h_inputencoding << "\n"
1146            << "\\fontencoding " << h_fontencoding << "\n"
1147            << "\\font_roman \"" << h_font_roman[0]
1148            << "\" \"" << h_font_roman[1] << "\"\n"
1149            << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1150            << "\\font_typewriter \"" << h_font_typewriter[0]
1151            << "\" \"" << h_font_typewriter[1] << "\"\n"
1152            << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1153            << "\\font_default_family " << h_font_default_family << "\n"
1154            << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1155            << "\\font_sc " << h_font_sc << "\n"
1156            << "\\font_osf " << h_font_osf << "\n"
1157            << "\\font_sf_scale " << h_font_sf_scale[0]
1158            << ' ' << h_font_sf_scale[1] << '\n'
1159            << "\\font_tt_scale " << h_font_tt_scale[0]
1160            << ' ' << h_font_tt_scale[1] << '\n';
1161         if (!h_font_cjk.empty())
1162                 os << "\\font_cjk " << h_font_cjk << '\n';
1163         os << "\\graphics " << h_graphics << '\n'
1164            << "\\default_output_format " << h_default_output_format << "\n"
1165            << "\\output_sync " << h_output_sync << "\n";
1166         if (h_output_sync == "1")
1167                 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1168         os << "\\bibtex_command " << h_bibtex_command << "\n"
1169            << "\\index_command " << h_index_command << "\n";
1170         if (!h_float_placement.empty())
1171                 os << "\\float_placement " << h_float_placement << "\n";
1172         os << "\\paperfontsize " << h_paperfontsize << "\n"
1173            << "\\spacing " << h_spacing << "\n"
1174            << "\\use_hyperref " << h_use_hyperref << '\n';
1175         if (h_use_hyperref == "true") {
1176                 if (!h_pdf_title.empty())
1177                         os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1178                 if (!h_pdf_author.empty())
1179                         os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1180                 if (!h_pdf_subject.empty())
1181                         os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1182                 if (!h_pdf_keywords.empty())
1183                         os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1184                 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1185                       "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1186                       "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1187                       "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1188                       "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1189                       "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1190                       "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1191                       "\\pdf_backref " << h_pdf_backref << "\n"
1192                       "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1193                 if (!h_pdf_pagemode.empty())
1194                         os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1195                 if (!h_pdf_quoted_options.empty())
1196                         os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1197         }
1198         os << "\\papersize " << h_papersize << "\n"
1199            << "\\use_geometry " << h_use_geometry << '\n';
1200         for (map<string, string>::const_iterator it = h_use_packages.begin();
1201              it != h_use_packages.end(); ++it)
1202                 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1203         os << "\\cite_engine " << h_cite_engine << '\n'
1204            << "\\cite_engine_type " << h_cite_engine_type << '\n'
1205            << "\\biblio_style " << h_biblio_style << "\n"
1206            << "\\use_bibtopic " << h_use_bibtopic << "\n"
1207            << "\\use_indices " << h_use_indices << "\n"
1208            << "\\paperorientation " << h_paperorientation << '\n'
1209            << "\\suppress_date " << h_suppress_date << '\n'
1210            << "\\justification " << h_justification << '\n'
1211            << "\\use_refstyle " << h_use_refstyle << '\n';
1212         if (!h_fontcolor.empty())
1213                 os << "\\fontcolor " << h_fontcolor << '\n';
1214         if (!h_notefontcolor.empty())
1215                 os << "\\notefontcolor " << h_notefontcolor << '\n';
1216         if (!h_backgroundcolor.empty())
1217                 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1218         if (!h_boxbgcolor.empty())
1219                 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1220         if (index_number != 0)
1221                 for (int i = 0; i < index_number; i++) {
1222                         os << "\\index " << h_index[i] << '\n'
1223                            << "\\shortcut " << h_shortcut[i] << '\n'
1224                            << "\\color " << h_color << '\n'
1225                            << "\\end_index\n";
1226                 }
1227         else {
1228                 os << "\\index " << h_index[0] << '\n'
1229                    << "\\shortcut " << h_shortcut[0] << '\n'
1230                    << "\\color " << h_color << '\n'
1231                    << "\\end_index\n";
1232         }
1233         os << h_margins
1234            << "\\secnumdepth " << h_secnumdepth << "\n"
1235            << "\\tocdepth " << h_tocdepth << "\n"
1236            << "\\paragraph_separation " << h_paragraph_separation << "\n";
1237         if (h_paragraph_separation == "skip")
1238                 os << "\\defskip " << h_defskip << "\n";
1239         else
1240                 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1241         os << "\\quotes_language " << h_quotes_language << "\n"
1242            << "\\papercolumns " << h_papercolumns << "\n"
1243            << "\\papersides " << h_papersides << "\n"
1244            << "\\paperpagestyle " << h_paperpagestyle << "\n";
1245         if (!h_listings_params.empty())
1246                 os << "\\listings_params " << h_listings_params << "\n";
1247         os << "\\tracking_changes " << h_tracking_changes << "\n"
1248            << "\\output_changes " << h_output_changes << "\n"
1249            << "\\html_math_output " << h_html_math_output << "\n"
1250            << "\\html_css_as_file " << h_html_css_as_file << "\n"
1251            << "\\html_be_strict " << h_html_be_strict << "\n"
1252            << authors_
1253            << "\\end_header\n\n"
1254            << "\\begin_body\n";
1255         return true;
1256 }
1257
1258
1259 void Preamble::parse(Parser & p, string const & forceclass,
1260                      TeX2LyXDocClass & tc)
1261 {
1262         // initialize fixed types
1263         special_columns_['D'] = 3;
1264         bool is_full_document = false;
1265         bool is_lyx_file = false;
1266         bool in_lyx_preamble = false;
1267
1268         // determine whether this is a full document or a fragment for inclusion
1269         while (p.good()) {
1270                 Token const & t = p.get_token();
1271
1272                 if (t.cat() == catEscape && t.cs() == "documentclass") {
1273                         is_full_document = true;
1274                         break;
1275                 }
1276         }
1277         p.reset();
1278
1279         while (is_full_document && p.good()) {
1280                 Token const & t = p.get_token();
1281
1282 #ifdef FILEDEBUG
1283                 cerr << "t: " << t << "\n";
1284 #endif
1285
1286                 //
1287                 // cat codes
1288                 //
1289                 if (!in_lyx_preamble &&
1290                     (t.cat() == catLetter ||
1291                      t.cat() == catSuper ||
1292                      t.cat() == catSub ||
1293                      t.cat() == catOther ||
1294                      t.cat() == catMath ||
1295                      t.cat() == catActive ||
1296                      t.cat() == catBegin ||
1297                      t.cat() == catEnd ||
1298                      t.cat() == catAlign ||
1299                      t.cat() == catParameter))
1300                         h_preamble << t.cs();
1301
1302                 else if (!in_lyx_preamble &&
1303                          (t.cat() == catSpace || t.cat() == catNewline))
1304                         h_preamble << t.asInput();
1305
1306                 else if (t.cat() == catComment) {
1307                         static regex const islyxfile("%% LyX .* created this file");
1308                         static regex const usercommands("User specified LaTeX commands");
1309
1310                         string const comment = t.asInput();
1311
1312                         // magically switch encoding default if it looks like XeLaTeX
1313                         static string const magicXeLaTeX =
1314                                 "% This document must be compiled with XeLaTeX ";
1315                         if (comment.size() > magicXeLaTeX.size()
1316                                   && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1317                                   && h_inputencoding == "auto") {
1318                                 cerr << "XeLaTeX comment found, switching to UTF8\n";
1319                                 h_inputencoding = "utf8";
1320                         }
1321                         smatch sub;
1322                         if (regex_search(comment, sub, islyxfile)) {
1323                                 is_lyx_file = true;
1324                                 in_lyx_preamble = true;
1325                         } else if (is_lyx_file
1326                                    && regex_search(comment, sub, usercommands))
1327                                 in_lyx_preamble = false;
1328                         else if (!in_lyx_preamble)
1329                                 h_preamble << t.asInput();
1330                 }
1331
1332                 else if (t.cs() == "pagestyle")
1333                         h_paperpagestyle = p.verbatim_item();
1334
1335                 else if (t.cs() == "setdefaultlanguage") {
1336                         xetex = true;
1337                         // We don't yet care about non-language variant options
1338                         // because LyX doesn't support this yet, see bug #8214
1339                         if (p.hasOpt()) {
1340                                 string langopts = p.getOpt();
1341                                 // check if the option contains a variant, if yes, extract it
1342                                 string::size_type pos_var = langopts.find("variant");
1343                                 string::size_type i = langopts.find(',', pos_var);
1344                                 string::size_type k = langopts.find('=', pos_var);
1345                                 if (pos_var != string::npos){
1346                                         string variant;
1347                                         if (i == string::npos)
1348                                                 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1349                                         else
1350                                                 variant = langopts.substr(k + 1, i - k - 1);
1351                                         h_language = variant;
1352                                 }
1353                                 p.verbatim_item();
1354                         } else
1355                                 h_language = p.verbatim_item();
1356                         //finally translate the poyglossia name to a LyX name
1357                         h_language = polyglossia2lyx(h_language);
1358                 }
1359
1360                 else if (t.cs() == "setotherlanguage") {
1361                         // We don't yet care about the option because LyX doesn't
1362                         // support this yet, see bug #8214
1363                         p.hasOpt() ? p.getOpt() : string();
1364                         p.verbatim_item();
1365                 }
1366
1367                 else if (t.cs() == "setmainfont") {
1368                         // we don't care about the option
1369                         p.hasOpt() ? p.getOpt() : string();
1370                         h_font_roman[1] = p.getArg('{', '}');
1371                 }
1372
1373                 else if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1374                         // LyX currently only supports the scale option
1375                         string scale;
1376                         if (p.hasOpt()) {
1377                                 string fontopts = p.getArg('[', ']');
1378                                 // check if the option contains a scaling, if yes, extract it
1379                                 string::size_type pos = fontopts.find("Scale");
1380                                 if (pos != string::npos) {
1381                                         string::size_type i = fontopts.find(',', pos);
1382                                         if (i == string::npos)
1383                                                 scale_as_percentage(fontopts.substr(pos + 1), scale);
1384                                         else
1385                                                 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1386                                 }
1387                         }
1388                         if (t.cs() == "setsansfont") {
1389                                 if (!scale.empty())
1390                                         h_font_sf_scale[1] = scale;
1391                                 h_font_sans[1] = p.getArg('{', '}');
1392                         } else {
1393                                 if (!scale.empty())
1394                                         h_font_tt_scale[1] = scale;
1395                                 h_font_typewriter[1] = p.getArg('{', '}');
1396                         }
1397                 }
1398
1399                 else if (t.cs() == "date") {
1400                         string argument = p.getArg('{', '}');
1401                         if (argument.empty())
1402                                 h_suppress_date = "true";
1403                         else
1404                                 h_preamble << t.asInput() << '{' << argument << '}';
1405                 }
1406
1407                 else if (t.cs() == "color") {
1408                         string const space =
1409                                 (p.hasOpt() ? p.getOpt() : string());
1410                         string argument = p.getArg('{', '}');
1411                         // check the case that a standard color is used
1412                         if (space.empty() && is_known(argument, known_basic_colors)) {
1413                                 h_fontcolor = rgbcolor2code(argument);
1414                                 preamble.registerAutomaticallyLoadedPackage("color");
1415                         } else if (space.empty() && argument == "document_fontcolor")
1416                                 preamble.registerAutomaticallyLoadedPackage("color");
1417                         // check the case that LyX's document_fontcolor is defined
1418                         // but not used for \color
1419                         else {
1420                                 h_preamble << t.asInput();
1421                                 if (!space.empty())
1422                                         h_preamble << space;
1423                                 h_preamble << '{' << argument << '}';
1424                                 // the color might already be set because \definecolor
1425                                 // is parsed before this
1426                                 h_fontcolor = "";
1427                         }
1428                 }
1429
1430                 else if (t.cs() == "pagecolor") {
1431                         string argument = p.getArg('{', '}');
1432                         // check the case that a standard color is used
1433                         if (is_known(argument, known_basic_colors)) {
1434                                 h_backgroundcolor = rgbcolor2code(argument);
1435                         } else if (argument == "page_backgroundcolor")
1436                                 preamble.registerAutomaticallyLoadedPackage("color");
1437                         // check the case that LyX's page_backgroundcolor is defined
1438                         // but not used for \pagecolor
1439                         else {
1440                                 h_preamble << t.asInput() << '{' << argument << '}';
1441                                 // the color might already be set because \definecolor
1442                                 // is parsed before this
1443                                 h_backgroundcolor = "";
1444                         }
1445                 }
1446
1447                 else if (t.cs() == "makeatletter") {
1448                         // LyX takes care of this
1449                         p.setCatcode('@', catLetter);
1450                 }
1451
1452                 else if (t.cs() == "makeatother") {
1453                         // LyX takes care of this
1454                         p.setCatcode('@', catOther);
1455                 }
1456
1457                 else if (t.cs() == "makeindex") {
1458                         // LyX will re-add this if a print index command is found
1459                         p.skip_spaces();
1460                 }
1461
1462                 else if (t.cs() == "newindex") {
1463                         string const indexname = p.getArg('[', ']');
1464                         string const shortcut = p.verbatim_item();
1465                         if (!indexname.empty())
1466                                 h_index[index_number] = indexname;
1467                         else
1468                                 h_index[index_number] = shortcut;
1469                         h_shortcut[index_number] = shortcut;
1470                         index_number += 1;
1471                         p.skip_spaces();
1472                 }
1473
1474                 else if (t.cs() == "RS@ifundefined") {
1475                         string const name = p.verbatim_item();
1476                         string const body1 = p.verbatim_item();
1477                         string const body2 = p.verbatim_item();
1478                         // only non-lyxspecific stuff
1479                         if (in_lyx_preamble &&
1480                             (name == "subsecref" || name == "thmref" || name == "lemref"))
1481                                 p.skip_spaces();
1482                         else {
1483                                 ostringstream ss;
1484                                 ss << '\\' << t.cs();
1485                                 ss << '{' << name << '}'
1486                                    << '{' << body1 << '}'
1487                                    << '{' << body2 << '}';
1488                                 h_preamble << ss.str();
1489                         }
1490                 }
1491
1492                 else if (t.cs() == "AtBeginDocument") {
1493                         string const name = p.verbatim_item();
1494                         // only non-lyxspecific stuff
1495                         if (in_lyx_preamble &&
1496                             (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
1497                                 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
1498                                 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
1499                                 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
1500                                 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
1501                                 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
1502                                 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
1503                                 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
1504                                 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
1505                                 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
1506                                 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
1507                                 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
1508                                 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
1509                                 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
1510                                 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
1511                                 p.skip_spaces();
1512                         else {
1513                                 ostringstream ss;
1514                                 ss << '\\' << t.cs();
1515                                 ss << '{' << name << '}';
1516                                 h_preamble << ss.str();
1517                         }
1518                 }
1519
1520                 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1521                       || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1522                       || t.cs() == "providecommand" || t.cs() == "providecommandx"
1523                                 || t.cs() == "DeclareRobustCommand"
1524                       || t.cs() == "DeclareRobustCommandx"
1525                                 || t.cs() == "ProvideTextCommandDefault"
1526                                 || t.cs() == "DeclareMathAccent") {
1527                         bool star = false;
1528                         if (p.next_token().character() == '*') {
1529                                 p.get_token();
1530                                 star = true;
1531                         }
1532                         string const name = p.verbatim_item();
1533                         string const opt1 = p.getFullOpt();
1534                         string const opt2 = p.getFullOpt();
1535                         string const body = p.verbatim_item();
1536                         // store the in_lyx_preamble setting
1537                         bool const was_in_lyx_preamble = in_lyx_preamble;
1538                         // font settings
1539                         if (name == "\\rmdefault")
1540                                 if (is_known(body, known_roman_fonts)) {
1541                                         h_font_roman[0] = body;
1542                                         p.skip_spaces();
1543                                         in_lyx_preamble = true;
1544                                 }
1545                         if (name == "\\sfdefault")
1546                                 if (is_known(body, known_sans_fonts)) {
1547                                         h_font_sans[0] = body;
1548                                         p.skip_spaces();
1549                                         in_lyx_preamble = true;
1550                                 }
1551                         if (name == "\\ttdefault")
1552                                 if (is_known(body, known_typewriter_fonts)) {
1553                                         h_font_typewriter[0] = body;
1554                                         p.skip_spaces();
1555                                         in_lyx_preamble = true;
1556                                 }
1557                         if (name == "\\familydefault") {
1558                                 string family = body;
1559                                 // remove leading "\"
1560                                 h_font_default_family = family.erase(0,1);
1561                                 p.skip_spaces();
1562                                 in_lyx_preamble = true;
1563                         }
1564
1565                         // remove LyX-specific definitions that are re-added by LyX
1566                         // if necessary
1567                         // \lyxline is an ancient command that is converted by tex2lyx into
1568                         // a \rule therefore remove its preamble code
1569                         if (name == "\\lyxdot" || name == "\\lyxarrow"
1570                             || name == "\\lyxline" || name == "\\LyX") {
1571                                 p.skip_spaces();
1572                                 in_lyx_preamble = true;
1573                         }
1574
1575                         // Add the command to the known commands
1576                         add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1577
1578                         // only non-lyxspecific stuff
1579                         if (!in_lyx_preamble) {
1580                                 ostringstream ss;
1581                                 ss << '\\' << t.cs();
1582                                 if (star)
1583                                         ss << '*';
1584                                 ss << '{' << name << '}' << opt1 << opt2
1585                                    << '{' << body << "}";
1586                                 h_preamble << ss.str();
1587 /*
1588                                 ostream & out = in_preamble ? h_preamble : os;
1589                                 out << "\\" << t.cs() << "{" << name << "}"
1590                                     << opts << "{" << body << "}";
1591 */
1592                         }
1593                         // restore the in_lyx_preamble setting
1594                         in_lyx_preamble = was_in_lyx_preamble;
1595                 }
1596
1597                 else if (t.cs() == "documentclass") {
1598                         vector<string>::iterator it;
1599                         vector<string> opts = split_options(p.getArg('[', ']'));
1600                         handle_opt(opts, known_fontsizes, h_paperfontsize);
1601                         delete_opt(opts, known_fontsizes);
1602                         // delete "pt" at the end
1603                         string::size_type i = h_paperfontsize.find("pt");
1604                         if (i != string::npos)
1605                                 h_paperfontsize.erase(i);
1606                         // The documentclass options are always parsed before the options
1607                         // of the babel call so that a language cannot overwrite the babel
1608                         // options.
1609                         handle_opt(opts, known_languages, h_language);
1610                         delete_opt(opts, known_languages);
1611
1612                         // paper orientation
1613                         if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1614                                 h_paperorientation = "landscape";
1615                                 opts.erase(it);
1616                         }
1617                         // paper sides
1618                         if ((it = find(opts.begin(), opts.end(), "oneside"))
1619                                  != opts.end()) {
1620                                 h_papersides = "1";
1621                                 opts.erase(it);
1622                         }
1623                         if ((it = find(opts.begin(), opts.end(), "twoside"))
1624                                  != opts.end()) {
1625                                 h_papersides = "2";
1626                                 opts.erase(it);
1627                         }
1628                         // paper columns
1629                         if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1630                                  != opts.end()) {
1631                                 h_papercolumns = "1";
1632                                 opts.erase(it);
1633                         }
1634                         if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1635                                  != opts.end()) {
1636                                 h_papercolumns = "2";
1637                                 opts.erase(it);
1638                         }
1639                         // paper sizes
1640                         // some size options are known to any document classes, other sizes
1641                         // are handled by the \geometry command of the geometry package
1642                         handle_opt(opts, known_class_paper_sizes, h_papersize);
1643                         delete_opt(opts, known_class_paper_sizes);
1644                         // the remaining options
1645                         h_options = join(opts, ",");
1646                         // FIXME This does not work for classes that have a
1647                         //       different name in LyX than in LaTeX
1648                         h_textclass = p.getArg('{', '}');
1649                         p.skip_spaces();
1650                 }
1651
1652                 else if (t.cs() == "usepackage") {
1653                         string const options = p.getArg('[', ']');
1654                         string const name = p.getArg('{', '}');
1655                         vector<string> vecnames;
1656                         split(name, vecnames, ',');
1657                         vector<string>::const_iterator it  = vecnames.begin();
1658                         vector<string>::const_iterator end = vecnames.end();
1659                         for (; it != end; ++it)
1660                                 handle_package(p, trimSpaceAndEol(*it), options,
1661                                                in_lyx_preamble);
1662                 }
1663
1664                 else if (t.cs() == "inputencoding") {
1665                         string const encoding = p.getArg('{','}');
1666                         Encoding const * const enc = encodings.fromLaTeXName(
1667                                 encoding, Encoding::inputenc, true);
1668                         if (!enc)
1669                                 cerr << "Unknown encoding " << encoding << ". Ignoring." << std::endl;
1670                         else {
1671                                 if (!enc->unsafe())
1672                                         h_inputencoding = enc->name();
1673                                 p.setEncoding(enc->iconvName());
1674                         }
1675                 }
1676
1677                 else if (t.cs() == "newenvironment") {
1678                         string const name = p.getArg('{', '}');
1679                         string const opt1 = p.getFullOpt();
1680                         string const opt2 = p.getFullOpt();
1681                         string const beg = p.verbatim_item();
1682                         string const end = p.verbatim_item();
1683                         if (!in_lyx_preamble) {
1684                                 h_preamble << "\\newenvironment{" << name
1685                                            << '}' << opt1 << opt2 << '{'
1686                                            << beg << "}{" << end << '}';
1687                         }
1688                         add_known_environment(name, opt1, !opt2.empty(),
1689                                               from_utf8(beg), from_utf8(end));
1690
1691                 }
1692
1693                 else if (t.cs() == "newtheorem") {
1694                         string const name = p.getArg('{', '}');
1695                         string const opt1 = p.getFullOpt();
1696                         string const opt2 = p.getFullOpt();
1697                         string const body = p.verbatim_item();
1698                         string const opt3 = p.getFullOpt();
1699
1700                         string const complete = "\\newtheorem{" + name + '}' +
1701                                           opt1 + opt2 + '{' + body + '}' + opt3;
1702
1703                         add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
1704
1705                         if (!in_lyx_preamble)
1706                                 h_preamble << complete;
1707                 }
1708
1709                 else if (t.cs() == "def") {
1710                         string name = p.get_token().cs();
1711                         // In fact, name may be more than the name:
1712                         // In the test case of bug 8116
1713                         // name == "csname SF@gobble@opt \endcsname".
1714                         // Therefore, we need to use asInput() instead of cs().
1715                         while (p.next_token().cat() != catBegin)
1716                                 name += p.get_token().asInput();
1717                         if (!in_lyx_preamble)
1718                                 h_preamble << "\\def\\" << name << '{'
1719                                            << p.verbatim_item() << "}";
1720                 }
1721
1722                 else if (t.cs() == "newcolumntype") {
1723                         string const name = p.getArg('{', '}');
1724                         trimSpaceAndEol(name);
1725                         int nargs = 0;
1726                         string opts = p.getOpt();
1727                         if (!opts.empty()) {
1728                                 istringstream is(string(opts, 1));
1729                                 is >> nargs;
1730                         }
1731                         special_columns_[name[0]] = nargs;
1732                         h_preamble << "\\newcolumntype{" << name << "}";
1733                         if (nargs)
1734                                 h_preamble << "[" << nargs << "]";
1735                         h_preamble << "{" << p.verbatim_item() << "}";
1736                 }
1737
1738                 else if (t.cs() == "setcounter") {
1739                         string const name = p.getArg('{', '}');
1740                         string const content = p.getArg('{', '}');
1741                         if (name == "secnumdepth")
1742                                 h_secnumdepth = content;
1743                         else if (name == "tocdepth")
1744                                 h_tocdepth = content;
1745                         else
1746                                 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1747                 }
1748
1749                 else if (t.cs() == "setlength") {
1750                         string const name = p.verbatim_item();
1751                         string const content = p.verbatim_item();
1752                         // the paragraphs are only not indented when \parindent is set to zero
1753                         if (name == "\\parindent" && content != "") {
1754                                 if (content[0] == '0')
1755                                         h_paragraph_separation = "skip";
1756                                 else
1757                                         h_paragraph_indentation = translate_len(content);
1758                         } else if (name == "\\parskip") {
1759                                 if (content == "\\smallskipamount")
1760                                         h_defskip = "smallskip";
1761                                 else if (content == "\\medskipamount")
1762                                         h_defskip = "medskip";
1763                                 else if (content == "\\bigskipamount")
1764                                         h_defskip = "bigskip";
1765                                 else
1766                                         h_defskip = translate_len(content);
1767                         } else
1768                                 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1769                 }
1770
1771                 else if (t.cs() == "onehalfspacing")
1772                         h_spacing = "onehalf";
1773
1774                 else if (t.cs() == "doublespacing")
1775                         h_spacing = "double";
1776
1777                 else if (t.cs() == "setstretch")
1778                         h_spacing = "other " + p.verbatim_item();
1779
1780                 else if (t.cs() == "synctex") {
1781                         // the scheme is \synctex=value
1782                         // where value can only be "1" or "-1"
1783                         h_output_sync = "1";
1784                         // there can be any character behind the value (e.g. a linebreak or a '\'
1785                         // therefore we extract it char by char
1786                         p.get_token();
1787                         string value = p.get_token().asInput();
1788                         if (value == "-")
1789                                 value += p.get_token().asInput();
1790                         h_output_sync_macro = "\\synctex=" + value;
1791                 }
1792
1793                 else if (t.cs() == "begin") {
1794                         string const name = p.getArg('{', '}');
1795                         if (name == "document")
1796                                 break;
1797                         h_preamble << "\\begin{" << name << "}";
1798                 }
1799
1800                 else if (t.cs() == "geometry") {
1801                         vector<string> opts = split_options(p.getArg('{', '}'));
1802                         handle_geometry(opts);
1803                 }
1804
1805                 else if (t.cs() == "definecolor") {
1806                         string const color = p.getArg('{', '}');
1807                         string const space = p.getArg('{', '}');
1808                         string const value = p.getArg('{', '}');
1809                         if (color == "document_fontcolor" && space == "rgb") {
1810                                 RGBColor c(RGBColorFromLaTeX(value));
1811                                 h_fontcolor = X11hexname(c);
1812                         } else if (color == "note_fontcolor" && space == "rgb") {
1813                                 RGBColor c(RGBColorFromLaTeX(value));
1814                                 h_notefontcolor = X11hexname(c);
1815                         } else if (color == "page_backgroundcolor" && space == "rgb") {
1816                                 RGBColor c(RGBColorFromLaTeX(value));
1817                                 h_backgroundcolor = X11hexname(c);
1818                         } else if (color == "shadecolor" && space == "rgb") {
1819                                 RGBColor c(RGBColorFromLaTeX(value));
1820                                 h_boxbgcolor = X11hexname(c);
1821                         } else {
1822                                 h_preamble << "\\definecolor{" << color
1823                                            << "}{" << space << "}{" << value
1824                                            << '}';
1825                         }
1826                 }
1827
1828                 else if (t.cs() == "bibliographystyle")
1829                         h_biblio_style = p.verbatim_item();
1830
1831                 else if (t.cs() == "jurabibsetup") {
1832                         // FIXME p.getArg('{', '}') is most probably wrong (it
1833                         //       does not handle nested braces).
1834                         //       Use p.verbatim_item() instead.
1835                         vector<string> jurabibsetup =
1836                                 split_options(p.getArg('{', '}'));
1837                         // add jurabibsetup to the jurabib package options
1838                         add_package("jurabib", jurabibsetup);
1839                         if (!jurabibsetup.empty()) {
1840                                 h_preamble << "\\jurabibsetup{"
1841                                            << join(jurabibsetup, ",") << '}';
1842                         }
1843                 }
1844
1845                 else if (t.cs() == "hypersetup") {
1846                         vector<string> hypersetup =
1847                                 split_options(p.verbatim_item());
1848                         // add hypersetup to the hyperref package options
1849                         handle_hyperref(hypersetup);
1850                         if (!hypersetup.empty()) {
1851                                 h_preamble << "\\hypersetup{"
1852                                            << join(hypersetup, ",") << '}';
1853                         }
1854                 }
1855
1856                 else if (is_known(t.cs(), known_if_3arg_commands)) {
1857                         // prevent misparsing of \usepackage if it is used
1858                         // as an argument (see e.g. our own output of
1859                         // \@ifundefined above)
1860                         string const arg1 = p.verbatim_item();
1861                         string const arg2 = p.verbatim_item();
1862                         string const arg3 = p.verbatim_item();
1863                         // test case \@ifundefined{date}{}{\date{}}
1864                         if (t.cs() == "@ifundefined" && arg1 == "date" &&
1865                             arg2.empty() && arg3 == "\\date{}") {
1866                                 h_suppress_date = "true";
1867                         // older tex2lyx versions did output
1868                         // \@ifundefined{definecolor}{\usepackage{color}}{}
1869                         } else if (t.cs() == "@ifundefined" &&
1870                                    arg1 == "definecolor" &&
1871                                    arg2 == "\\usepackage{color}" &&
1872                                    arg3.empty()) {
1873                                 if (!in_lyx_preamble)
1874                                         h_preamble << package_beg_sep
1875                                                    << "color"
1876                                                    << package_mid_sep
1877                                                    << "\\@ifundefined{definecolor}{color}{}"
1878                                                    << package_end_sep;
1879                         // test for case
1880                         //\@ifundefined{showcaptionsetup}{}{%
1881                         // \PassOptionsToPackage{caption=false}{subfig}}
1882                         // that LyX uses for subfloats
1883                         } else if (t.cs() == "@ifundefined" &&
1884                                    arg1 == "showcaptionsetup" && arg2.empty()
1885                                 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1886                                 ; // do nothing
1887                         } else if (!in_lyx_preamble) {
1888                                 h_preamble << t.asInput()
1889                                            << '{' << arg1 << '}'
1890                                            << '{' << arg2 << '}'
1891                                            << '{' << arg3 << '}';
1892                         }
1893                 }
1894
1895                 else if (is_known(t.cs(), known_if_commands)) {
1896                         // must not parse anything in conditional code, since
1897                         // LyX would output the parsed contents unconditionally
1898                         if (!in_lyx_preamble)
1899                                 h_preamble << t.asInput();
1900                         handle_if(p, in_lyx_preamble);
1901                 }
1902
1903                 else if (!t.cs().empty() && !in_lyx_preamble)
1904                         h_preamble << '\\' << t.cs();
1905         }
1906
1907         // remove the whitespace
1908         p.skip_spaces();
1909
1910         // Force textclass if the user wanted it
1911         if (!forceclass.empty())
1912                 h_textclass = forceclass;
1913         tc.setName(h_textclass);
1914         if (!tc.load()) {
1915                 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1916                 exit(EXIT_FAILURE);
1917         }
1918         if (h_papersides.empty()) {
1919                 ostringstream ss;
1920                 ss << tc.sides();
1921                 h_papersides = ss.str();
1922         }
1923
1924         // If the CJK package is used we cannot set the document language from
1925         // the babel options. Instead, we guess which language is used most
1926         // and set this one.
1927         default_language = h_language;
1928         if (is_full_document &&
1929             (auto_packages.find("CJK") != auto_packages.end() ||
1930              auto_packages.find("CJKutf8") != auto_packages.end())) {
1931                 p.pushPosition();
1932                 h_language = guessLanguage(p, default_language);
1933                 p.popPosition();
1934                 if (explicit_babel && h_language != default_language) {
1935                         // We set the document language to a CJK language,
1936                         // but babel is explicitly called in the user preamble
1937                         // without options. LyX will not add the default
1938                         // language to the document options if it is either
1939                         // english, or no text is set as default language.
1940                         // Therefore we need to add a language option explicitly.
1941                         // FIXME: It would be better to remove all babel calls
1942                         //        from the user preamble, but this is difficult
1943                         //        without re-introducing bug 7861.
1944                         if (h_options.empty())
1945                                 h_options = lyx2babel(default_language);
1946                         else
1947                                 h_options += ',' + lyx2babel(default_language);
1948                 }
1949         }
1950 }
1951
1952
1953 string babel2lyx(string const & language)
1954 {
1955         char const * const * where = is_known(language, known_languages);
1956         if (where)
1957                 return known_coded_languages[where - known_languages];
1958         return language;
1959 }
1960
1961
1962 string lyx2babel(string const & language)
1963 {
1964         char const * const * where = is_known(language, known_coded_languages);
1965         if (where)
1966                 return known_languages[where - known_coded_languages];
1967         return language;
1968 }
1969
1970
1971 string Preamble::polyglossia2lyx(string const & language)
1972 {
1973         char const * const * where = is_known(language, polyglossia_languages);
1974         if (where)
1975                 return coded_polyglossia_languages[where - polyglossia_languages];
1976         return language;
1977 }
1978
1979
1980 string rgbcolor2code(string const & name)
1981 {
1982         char const * const * where = is_known(name, known_basic_colors);
1983         if (where) {
1984                 // "red", "green" etc
1985                 return known_basic_color_codes[where - known_basic_colors];
1986         }
1987         // "255,0,0", "0,255,0" etc
1988         RGBColor c(RGBColorFromLaTeX(name));
1989         return X11hexname(c);
1990 }
1991
1992 // }])
1993
1994
1995 } // namespace lyx