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