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