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