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