3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
9 * Full author contact details are available in file CREDITS.
20 #include "LayoutFile.h"
23 #include "TextClass.h"
26 #include "support/convert.h"
27 #include "support/FileName.h"
28 #include "support/filetools.h"
29 #include "support/lstrings.h"
31 #include "support/regex.h"
37 using namespace lyx::support;
46 // CJK languages are handled in text.cpp, polyglossia languages are listed
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!
53 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
54 "american", "arabic", "arabtex", "australian", "austrian", "azerbaijani", "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",
71 * the same as known_languages with .lyx names
72 * please keep this in sync with known_languages line by line!
74 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
75 "american", "arabic_arabi", "arabic_arabtex", "australian", "austrian", "azerbaijani", "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",
91 /// languages with british quotes (.lyx names)
92 const char * const known_british_quotes_languages[] = {"british", "welsh", 0};
94 /// languages with cjk quotes (.lyx names)
95 const char * const known_cjk_quotes_languages[] = {"chinese-traditional",
96 "japanese", "japanese-cjk", 0};
98 /// languages with cjk-angle quotes (.lyx names)
99 const char * const known_cjkangle_quotes_languages[] = {"korean", 0};
101 /// languages with danish quotes (.lyx names)
102 const char * const known_danish_quotes_languages[] = {"danish", 0};
104 /// languages with english quotes (.lyx names)
105 const char * const known_english_quotes_languages[] = {"american", "australian",
106 "bahasa", "bahasam", "bengali", "brazilian", "canadian", "chinese-simplified", "english",
107 "esperanto", "farsi", "interlingua", "irish", "newzealand", "scottish",
108 "thai", "turkish", "vietnamese", 0};
110 /// languages with french quotes (.lyx names)
111 const char * const known_french_quotes_languages[] = {"ancientgreek",
112 "arabic_arabi", "arabic_arabtex", "asturian", "belarusian", "breton",
113 "canadien", "catalan", "french", "friulan", "galician", "italian", "occitan",
114 "piedmontese", "portuguese", "spanish", "spanish-mexico", 0};
116 /// languages with german quotes (.lyx names)
117 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
118 "czech", "estonian", "georgian", "german", "icelandic", "latvian", "lithuanian",
119 "lowersorbian", "macedonian", "naustrian", "ngerman", "romansh", "slovak", "slovene",
122 /// languages with polish quotes (.lyx names)
123 const char * const known_polish_quotes_languages[] = {"afrikaans", "bosnian", "croatian",
124 "dutch", "magyar", "polish", "romanian", "serbian", "serbian-latin", 0};
126 /// languages with russian quotes (.lyx names)
127 const char * const known_russian_quotes_languages[] = {"azerbaijani", "oldrussian",
128 "russian", "ukrainian", 0};
130 /// languages with swedish quotes (.lyx names)
131 const char * const known_swedish_quotes_languages[] = {"finnish", "swedish", 0};
133 /// languages with swiss quotes (.lyx names)
134 const char * const known_swiss_quotes_languages[] = {"albanian",
135 "armenian", "basque", "churchslavonic", "german-ch", "german-ch-old",
136 "norsk", "nynorsk", "turkmen", "ukrainian", "vietnamese", 0};
138 /// known language packages from the times before babel
139 const char * const known_old_language_packages[] = {"french", "frenchle",
140 "frenchpro", "german", "ngerman", "pmfrench", 0};
142 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
144 const char * const known_roman_font_packages[] = { "ae", "beraserif", "bookman",
145 "ccfonts", "chancery", "charter", "cmr", "cochineal", "crimson", "DejaVuSerif", "DejaVuSerifCondensed", "fourier",
146 "garamondx", "libertine", "libertineRoman", "libertine-type1", "lmodern", "mathdesign", "mathpazo",
147 "mathptmx", "MinionPro", "newcent", "noto", "noto-serif", "PTSerif", "tgbonum", "tgchorus",
148 "tgpagella", "tgschola", "tgtermes", "utopia", "xcharter", 0 };
150 const char * const known_sans_font_packages[] = { "avant", "berasans", "biolinum",
151 "biolinum-type1", "cantarell", "cmbr", "cmss", "DejaVuSans", "DejaVuSansCondensed", "helvet", "iwona", "iwonac", "iwonal", "iwonalc",
152 "kurier", "kurierc", "kurierl", "kurierlc", "lmss", "noto", "noto-sans", "PTSans",
153 "tgadventor", "tgheros", "uop", 0 };
155 const char * const known_typewriter_font_packages[] = { "beramono", "cmtl", "cmtt",
156 "courier", "DejaVuSansMono", "lmtt", "luximono", "fourier", "libertineMono", "libertineMono-type1", "lmodern",
157 "mathpazo", "mathptmx", "newcent", "noto", "noto-mono", "PTMono", "tgcursor", "txtt", 0 };
159 const char * const known_math_font_packages[] = { "eulervm", "newtxmath", 0};
161 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
162 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
163 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
164 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
165 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
167 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
168 "executivepaper", "legalpaper", "letterpaper", 0};
170 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
171 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
173 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
174 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
177 /// commands that can start an \if...\else...\endif sequence
178 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
179 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
180 "ifsidecap", "ifupgreek", 0};
182 const char * const known_basic_colors[] = {"black", "blue", "brown", "cyan",
183 "darkgray", "gray", "green", "lightgray", "lime", "magenta", "orange", "olive",
184 "pink", "purple", "red", "teal", "violet", "white", "yellow", 0};
186 const char * const known_basic_color_codes[] = {"#000000", "#0000ff", "#964B00", "#00ffff",
187 "#a9a9a9", "#808080", "#00ff00", "#d3d3d3", "#bfff00", "#ff00ff", "#ff7f00", "#808000",
188 "#ffc0cb", "#800080", "#ff0000", "#008080", "#8f00ff", "#ffffff", "#ffff00", 0};
190 /// conditional commands with three arguments like \@ifundefined{}{}{}
191 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
195 * Known file extensions for TeX files as used by \\includeonly
197 char const * const known_tex_extensions[] = {"tex", 0};
199 /// packages that work only in xetex
200 /// polyglossia is handled separately
201 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
202 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
203 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
205 /// packages that are automatically skipped if loaded by LyX
206 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
207 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color",
208 "float", "fontspec", "framed", "graphicx", "hhline", "ifthen", "longtable",
209 "makeidx", "minted", "multirow", "nomencl", "pdfpages", "prettyref", "refstyle",
210 "rotating", "rotfloat", "splitidx", "setspace", "subscript", "tabularx","textcomp", "tipa",
211 "tipx", "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xcolor", "xltabular",
214 // codes used to remove packages that are loaded automatically by LyX.
215 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
216 const char package_beg_sep = '\001';
217 const char package_mid_sep = '\002';
218 const char package_end_sep = '\003';
221 // returns true if at least one of the options in what has been found
222 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
228 // the last language option is the document language (for babel and LyX)
229 // the last size option is the document font size
230 vector<string>::iterator it;
231 vector<string>::iterator position = opts.begin();
232 for (; *what; ++what) {
233 it = find(opts.begin(), opts.end(), *what);
234 if (it != opts.end()) {
235 if (it >= position) {
246 void delete_opt(vector<string> & opts, char const * const * what)
251 // remove found options from the list
252 // do this after handle_opt to avoid potential memory leaks
253 vector<string>::iterator it;
254 for (; *what; ++what) {
255 it = find(opts.begin(), opts.end(), *what);
256 if (it != opts.end())
263 * Split a package options string (keyval format) into a vector.
265 * authorformat=smallcaps,
267 * titleformat=colonsep,
268 * bibformat={tabular,ibidem,numbered}
270 vector<string> split_options(string const & input)
272 vector<string> options;
276 Token const & t = p.get_token();
277 if (t.asInput() == ",") {
278 options.push_back(trimSpaceAndEol(option));
280 } else if (t.asInput() == "=") {
283 if (p.next_token().asInput() == "{")
284 option += '{' + p.getArg('{', '}') + '}';
285 } else if (t.cat() != catSpace && t.cat() != catComment)
286 option += t.asInput();
290 options.push_back(trimSpaceAndEol(option));
297 * Retrieve a keyval option "name={value with=sign}" named \p name from
298 * \p options and return the value.
299 * The found option is also removed from \p options.
301 string process_keyval_opt(vector<string> & options, string name)
303 for (size_t i = 0; i < options.size(); ++i) {
304 vector<string> option;
305 split(options[i], option, '=');
306 if (option.size() < 2)
308 if (option[0] == name) {
309 options.erase(options.begin() + i);
310 option.erase(option.begin());
311 return join(option, "=");
317 } // anonymous namespace
321 * known polyglossia language names (including variants)
322 * FIXME: support spelling=old for german variants (german vs. ngerman LyX names etc)
324 const char * const Preamble::polyglossia_languages[] = {
325 "albanian", "american", "amharic", "ancient", "arabic", "armenian", "asturian", "australian",
326 "bahasai", "bahasam", "basque", "bengali", "brazil", "brazilian", "breton", "british", "bulgarian",
327 "catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
328 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
329 "galician", "greek", "monotonic", "hebrew", "hindi",
330 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean",
331 "lao", "latin", "latvian", "lithuanian", "lsorbian", "magyar", "malayalam", "marathi",
332 "austrian", "newzealand", "german", "norsk", "nynorsk", "occitan", "oldrussian",
333 "piedmontese", "polish", "polytonic", "portuges", "romanian", "romansh", "russian",
334 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovenian", "spanish", "swedish", "syriac",
335 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
336 "ukrainian", "urdu", "usorbian", "vietnamese", "welsh", 0};
337 // not yet supported by LyX: "korean", "nko"
340 * the same as polyglossia_languages with .lyx names
341 * please keep this in sync with polyglossia_languages line by line!
343 const char * const Preamble::coded_polyglossia_languages[] = {
344 "albanian", "american", "amharic", "ancientgreek", "arabic_arabi", "armenian", "asturian", "australian",
345 "bahasa", "bahasam", "basque", "bengali", "brazilian", "brazilian", "breton", "british", "bulgarian",
346 "catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
347 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
348 "galician", "greek", "greek", "hebrew", "hindi",
349 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean",
350 "lao", "latin", "latvian", "lithuanian", "lowersorbian", "magyar", "malayalam", "marathi",
351 "naustrian","newzealand", "ngerman", "norsk", "nynorsk", "occitan", "oldrussian",
352 "piedmontese", "polish", "polutonikogreek", "portuges", "romanian", "romansh", "russian",
353 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovene", "spanish", "swedish", "syriac",
354 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
355 "ukrainian", "urdu", "uppersorbian", "vietnamese", "welsh", 0};
356 // not yet supported by LyX: "korean-polyglossia", "nko"
359 bool Preamble::usePolyglossia() const
361 return h_use_non_tex_fonts && h_language_package == "default";
365 bool Preamble::indentParagraphs() const
367 return h_paragraph_separation == "indent";
371 bool Preamble::isPackageUsed(string const & package) const
373 return used_packages.find(package) != used_packages.end();
377 bool Preamble::isPackageAutoLoaded(string const & package) const
379 return auto_packages.find(package) != auto_packages.end();
383 vector<string> Preamble::getPackageOptions(string const & package) const
385 map<string, vector<string> >::const_iterator it = used_packages.find(package);
386 if (it != used_packages.end())
388 return vector<string>();
392 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
394 auto_packages.insert(package);
398 void Preamble::addModule(string const & module)
400 for (auto const & m : used_modules) {
404 used_modules.push_back(module);
408 void Preamble::suppressDate(bool suppress)
411 h_suppress_date = "true";
413 h_suppress_date = "false";
417 void Preamble::registerAuthor(std::string const & name)
419 Author author(from_utf8(name), empty_docstring());
420 author.setUsed(true);
421 authors_.record(author);
422 h_tracking_changes = "true";
423 h_output_changes = "true";
427 Author const & Preamble::getAuthor(std::string const & name) const
429 Author author(from_utf8(name), empty_docstring());
430 for (AuthorList::Authors::const_iterator it = authors_.begin();
431 it != authors_.end(); ++it)
434 static Author const dummy;
439 int Preamble::getSpecialTableColumnArguments(char c) const
441 map<char, int>::const_iterator it = special_columns_.find(c);
442 if (it == special_columns_.end())
448 void Preamble::add_package(string const & name, vector<string> & options)
450 // every package inherits the global options
451 if (used_packages.find(name) == used_packages.end())
452 used_packages[name] = split_options(h_options);
454 // Insert options passed via PassOptionsToPackage
455 for (auto const & p : extra_package_options_) {
456 if (p.first == name) {
457 vector<string> eo = getVectorFromString(p.second);
458 for (auto const & eoi : eo)
459 options.push_back(eoi);
463 vector<string> & v = used_packages[name];
464 v.insert(v.end(), options.begin(), options.end());
465 if (name == "jurabib") {
466 // Don't output the order argument (see the cite command
467 // handling code in text.cpp).
468 vector<string>::iterator end =
469 remove(options.begin(), options.end(), "natbiborder");
470 end = remove(options.begin(), end, "jurabiborder");
471 options.erase(end, options.end());
478 // Given is a string like "scaled=0.9" or "Scale=0.9", return 0.9 * 100
479 bool scale_as_percentage(string const & scale, string & percentage)
481 string::size_type pos = scale.find('=');
482 if (pos != string::npos) {
483 string value = scale.substr(pos + 1);
484 if (isStrDbl(value)) {
485 percentage = convert<string>(
486 static_cast<int>(100 * convert<double>(value)));
494 string remove_braces(string const & value)
498 if (value[0] == '{' && value[value.length()-1] == '}')
499 return value.substr(1, value.length()-2);
503 } // anonymous namespace
506 Preamble::Preamble() : one_language(true), explicit_babel(false),
507 title_layout_found(false), index_number(0), h_font_cjk_set(false)
511 h_biblio_style = "plain";
512 h_bibtex_command = "default";
513 h_cite_engine = "basic";
514 h_cite_engine_type = "default";
516 h_defskip = "medskip";
517 h_dynamic_quotes = false;
520 h_fontencoding = "default";
521 h_font_roman[0] = "default";
522 h_font_roman[1] = "default";
523 h_font_sans[0] = "default";
524 h_font_sans[1] = "default";
525 h_font_typewriter[0] = "default";
526 h_font_typewriter[1] = "default";
527 h_font_math[0] = "auto";
528 h_font_math[1] = "auto";
529 h_font_default_family = "default";
530 h_use_non_tex_fonts = false;
532 h_font_roman_osf = "false";
533 h_font_sans_osf = "false";
534 h_font_typewriter_osf = "false";
535 h_font_sf_scale[0] = "100";
536 h_font_sf_scale[1] = "100";
537 h_font_tt_scale[0] = "100";
538 h_font_tt_scale[1] = "100";
539 // h_font_roman_opts;
541 // h_font_typewriter_opts;
543 h_is_mathindent = "0";
544 h_math_numbering_side = "default";
545 h_graphics = "default";
546 h_default_output_format = "default";
547 h_html_be_strict = "false";
548 h_html_css_as_file = "0";
549 h_html_math_output = "0";
550 h_index[0] = "Index";
551 h_index_command = "default";
552 h_inputencoding = "auto-legacy";
553 h_justification = "true";
554 h_language = "english";
555 h_language_package = "none";
557 h_maintain_unincluded_children = "false";
561 h_output_changes = "false";
563 //h_output_sync_macro
564 h_papercolumns = "1";
565 h_paperfontsize = "default";
566 h_paperorientation = "portrait";
567 h_paperpagestyle = "default";
569 h_papersize = "default";
570 h_paragraph_indentation = "default";
571 h_paragraph_separation = "indent";
576 h_pdf_bookmarks = "0";
577 h_pdf_bookmarksnumbered = "0";
578 h_pdf_bookmarksopen = "0";
579 h_pdf_bookmarksopenlevel = "1";
580 h_pdf_breaklinks = "0";
581 h_pdf_pdfborder = "0";
582 h_pdf_colorlinks = "0";
583 h_pdf_backref = "section";
584 h_pdf_pdfusetitle = "0";
586 //h_pdf_quoted_options;
587 h_quotes_style = "english";
589 h_shortcut[0] = "idx";
590 h_spacing = "single";
591 h_save_transient_properties = "true";
592 h_suppress_date = "false";
593 h_textclass = "article";
595 h_tracking_changes = "false";
596 h_use_bibtopic = "false";
597 h_use_dash_ligatures = "true";
598 h_use_indices = "false";
599 h_use_geometry = "false";
600 h_use_default_options = "false";
601 h_use_hyperref = "false";
602 h_use_microtype = "false";
603 h_use_lineno = "false";
604 h_use_refstyle = false;
605 h_use_minted = false;
606 h_use_packages["amsmath"] = "1";
607 h_use_packages["amssymb"] = "0";
608 h_use_packages["cancel"] = "0";
609 h_use_packages["esint"] = "1";
610 h_use_packages["mhchem"] = "0";
611 h_use_packages["mathdots"] = "0";
612 h_use_packages["mathtools"] = "0";
613 h_use_packages["stackrel"] = "0";
614 h_use_packages["stmaryrd"] = "0";
615 h_use_packages["undertilde"] = "0";
619 void Preamble::handle_hyperref(vector<string> & options)
621 // FIXME swallow inputencoding changes that might surround the
622 // hyperref setup if it was written by LyX
623 h_use_hyperref = "true";
624 // swallow "unicode=true", since LyX does always write that
625 vector<string>::iterator it =
626 find(options.begin(), options.end(), "unicode=true");
627 if (it != options.end())
629 it = find(options.begin(), options.end(), "pdfusetitle");
630 if (it != options.end()) {
631 h_pdf_pdfusetitle = "1";
634 string bookmarks = process_keyval_opt(options, "bookmarks");
635 if (bookmarks == "true")
636 h_pdf_bookmarks = "1";
637 else if (bookmarks == "false")
638 h_pdf_bookmarks = "0";
639 if (h_pdf_bookmarks == "1") {
640 string bookmarksnumbered =
641 process_keyval_opt(options, "bookmarksnumbered");
642 if (bookmarksnumbered == "true")
643 h_pdf_bookmarksnumbered = "1";
644 else if (bookmarksnumbered == "false")
645 h_pdf_bookmarksnumbered = "0";
646 string bookmarksopen =
647 process_keyval_opt(options, "bookmarksopen");
648 if (bookmarksopen == "true")
649 h_pdf_bookmarksopen = "1";
650 else if (bookmarksopen == "false")
651 h_pdf_bookmarksopen = "0";
652 if (h_pdf_bookmarksopen == "1") {
653 string bookmarksopenlevel =
654 process_keyval_opt(options, "bookmarksopenlevel");
655 if (!bookmarksopenlevel.empty())
656 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
659 string breaklinks = process_keyval_opt(options, "breaklinks");
660 if (breaklinks == "true")
661 h_pdf_breaklinks = "1";
662 else if (breaklinks == "false")
663 h_pdf_breaklinks = "0";
664 string pdfborder = process_keyval_opt(options, "pdfborder");
665 if (pdfborder == "{0 0 0}")
666 h_pdf_pdfborder = "1";
667 else if (pdfborder == "{0 0 1}")
668 h_pdf_pdfborder = "0";
669 string backref = process_keyval_opt(options, "backref");
670 if (!backref.empty())
671 h_pdf_backref = backref;
672 string colorlinks = process_keyval_opt(options, "colorlinks");
673 if (colorlinks == "true")
674 h_pdf_colorlinks = "1";
675 else if (colorlinks == "false")
676 h_pdf_colorlinks = "0";
677 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
678 if (!pdfpagemode.empty())
679 h_pdf_pagemode = pdfpagemode;
680 string pdftitle = process_keyval_opt(options, "pdftitle");
681 if (!pdftitle.empty()) {
682 h_pdf_title = remove_braces(pdftitle);
684 string pdfauthor = process_keyval_opt(options, "pdfauthor");
685 if (!pdfauthor.empty()) {
686 h_pdf_author = remove_braces(pdfauthor);
688 string pdfsubject = process_keyval_opt(options, "pdfsubject");
689 if (!pdfsubject.empty())
690 h_pdf_subject = remove_braces(pdfsubject);
691 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
692 if (!pdfkeywords.empty())
693 h_pdf_keywords = remove_braces(pdfkeywords);
694 if (!options.empty()) {
695 if (!h_pdf_quoted_options.empty())
696 h_pdf_quoted_options += ',';
697 h_pdf_quoted_options += join(options, ",");
703 void Preamble::handle_geometry(vector<string> & options)
705 h_use_geometry = "true";
706 vector<string>::iterator it;
708 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
709 h_paperorientation = "landscape";
713 // keyval version: "paper=letter"
714 string paper = process_keyval_opt(options, "paper");
716 h_papersize = paper + "paper";
717 // alternative version: "letterpaper"
718 handle_opt(options, known_paper_sizes, h_papersize);
719 delete_opt(options, known_paper_sizes);
721 char const * const * margin = known_paper_margins;
722 for (; *margin; ++margin) {
723 string value = process_keyval_opt(options, *margin);
724 if (!value.empty()) {
725 int k = margin - known_paper_margins;
726 string name = known_coded_paper_margins[k];
727 h_margins += '\\' + name + ' ' + value + '\n';
733 void Preamble::handle_package(Parser &p, string const & name,
734 string const & opts, bool in_lyx_preamble,
737 vector<string> options = split_options(opts);
738 add_package(name, options);
740 if (is_known(name, known_xetex_packages)) {
742 h_use_non_tex_fonts = true;
743 registerAutomaticallyLoadedPackage("fontspec");
744 if (h_inputencoding == "auto-legacy")
745 p.setEncoding("UTF-8");
749 if (is_known(name, known_roman_font_packages))
750 h_font_roman[0] = name;
752 vector<string> allopts = getVectorFromString(opts);
755 if (name == "ccfonts") {
756 for (auto const & opt : allopts) {
762 h_font_roman_opts = xopts;
766 if (name == "lmodern") {
767 for (auto const & opt : allopts) {
773 h_font_roman_opts = xopts;
777 if (name == "fourier") {
778 h_font_roman[0] = "utopia";
779 // when font uses real small capitals
780 for (auto const & opt : allopts) {
782 h_font_roman_osf = "true";
785 if (opt == "expert") {
794 h_font_roman_opts = xopts;
798 if (name == "garamondx") {
799 h_font_roman[0] = "garamondx";
800 for (auto const & opt : allopts) {
802 h_font_roman_osf = "true";
810 h_font_roman_opts = xopts;
814 if (name == "libertine") {
815 h_font_roman[0] = "libertine";
816 // this automatically invokes biolinum
817 h_font_sans[0] = "biolinum";
818 // as well as libertineMono
819 h_font_typewriter[0] = "libertine-mono";
820 for (auto const & opt : allopts) {
822 h_font_roman_osf = "true";
825 if (opt == "lining") {
826 h_font_roman_osf = "false";
834 h_font_roman_opts = xopts;
838 if (name == "libertineRoman" || name == "libertine-type1") {
839 h_font_roman[0] = "libertine";
840 // NOTE: contrary to libertine.sty, libertineRoman
841 // and libertine-type1 do not automatically invoke
842 // biolinum and libertineMono
843 if (opts == "lining")
844 h_font_roman_osf = "false";
845 else if (opts == "osf")
846 h_font_roman_osf = "true";
849 if (name == "MinionPro") {
850 h_font_roman[0] = "minionpro";
851 h_font_roman_osf = "true";
852 h_font_math[0] = "auto";
853 for (auto const & opt : allopts) {
855 h_font_roman_osf = "false";
858 if (opt == "onlytext") {
859 h_font_math[0] = "default";
867 h_font_roman_opts = xopts;
871 if (name == "mathdesign") {
872 if (opts.find("charter") != string::npos)
873 h_font_roman[0] = "md-charter";
874 if (opts.find("garamond") != string::npos)
875 h_font_roman[0] = "md-garamond";
876 if (opts.find("utopia") != string::npos)
877 h_font_roman[0] = "md-utopia";
878 if (opts.find("expert") != string::npos) {
880 h_font_roman_osf = "true";
884 else if (name == "mathpazo") {
885 h_font_roman[0] = "palatino";
886 for (auto const & opt : allopts) {
888 h_font_roman_osf = "true";
900 h_font_roman_opts = xopts;
904 else if (name == "mathptmx") {
905 h_font_roman[0] = "times";
906 for (auto const & opt : allopts) {
912 h_font_roman_opts = xopts;
916 if (name == "crimson")
917 h_font_roman[0] = "cochineal";
919 if (name == "cochineal") {
920 h_font_roman[0] = "cochineal";
921 // cochineal can have several options, e.g. [proportional,osf]
922 for (auto const & opt : allopts) {
924 h_font_roman_osf = "true";
927 if (opt == "proportional")
934 h_font_roman_opts = xopts;
938 if (name == "noto") {
939 // noto can have several options
941 h_font_roman[0] = "NotoSerif-TLF";
942 string::size_type pos = opts.find("rm");
943 if (pos != string::npos)
944 h_font_roman[0] = "NotoSerif-TLF";
945 pos = opts.find("sf");
946 if (pos != string::npos)
947 h_font_sans[0] = "NotoSans-TLF";
948 pos = opts.find("nott");
949 if (pos != string::npos) {
950 h_font_roman[0] = "NotoSerif-TLF";
951 h_font_sans[0] = "NotoSans-TLF";
953 // noto as typewriter is handled in handling of \ttdefault
954 // special cases are handled in handling of \rmdefault and \sfdefault
955 for (auto const & opt : allopts) {
963 h_font_roman_osf = "true";
972 if (name == "paratype") {
973 // in this case all fonts are ParaType
974 h_font_roman[0] = "PTSerif-TLF";
975 h_font_sans[0] = "default";
976 h_font_typewriter[0] = "default";
979 if (name == "PTSerif")
980 h_font_roman[0] = "PTSerif-TLF";
982 if (name == "XCharter") {
983 h_font_roman[0] = "xcharter";
984 for (auto const & opt : allopts) {
986 h_font_roman_osf = "true";
994 h_font_roman_opts = xopts;
998 if (name == "plex-serif") {
1000 h_font_roman[0] = "IBMPlexSerif";
1001 else if (opts.find("thin") != string::npos)
1002 h_font_roman[0] = "IBMPlexSerifThin";
1003 else if (opts.find("extralight") != string::npos)
1004 h_font_roman[0] = "IBMPlexSerifExtraLight";
1005 else if (opts.find("light") != string::npos)
1006 h_font_roman[0] = "IBMPlexSerifLight";
1007 else if (opts.find("semibold") != string::npos)
1008 h_font_roman[0] = "IBMPlexSerifSemibold";
1009 for (auto const & opt : allopts) {
1012 if (opt == "extralight")
1016 if (opt == "semibold")
1023 h_font_roman_opts = xopts;
1026 if (name == "noto-serif") {
1027 h_font_roman[0] = "NotoSerifRegular";
1028 if (!opts.empty()) {
1029 if (opts.find("thin") != string::npos)
1030 h_font_roman[0] = "NotoSerifThin";
1031 else if (opts.find("medium") != string::npos)
1032 h_font_roman[0] = "NotoSerifMedium";
1033 else if (opts.find("extralight") != string::npos)
1034 h_font_roman[0] = "NotoSerifExtralight";
1035 else if (opts.find("light") != string::npos)
1036 h_font_roman[0] = "NotoSerifLight";
1038 for (auto const & opt : allopts) {
1039 if (opt == "regular")
1043 if (opt == "extralight")
1047 if (opt == "semibold")
1054 h_font_roman_opts = xopts;
1058 if (name == "sourceserifpro") {
1059 h_font_roman[0] = "ADOBESourceSerifPro";
1060 for (auto const & opt : allopts) {
1062 h_font_roman_osf = "true";
1070 h_font_roman_opts = xopts;
1075 if (is_known(name, known_sans_font_packages)) {
1076 h_font_sans[0] = name;
1077 if (options.size() >= 1) {
1078 if (scale_as_percentage(opts, h_font_sf_scale[0]))
1083 if (name == "biolinum" || name == "biolinum-type1") {
1084 h_font_sans[0] = "biolinum";
1085 // biolinum can have several options, e.g. [osf,scaled=0.97]
1086 string::size_type pos = opts.find("osf");
1087 if (pos != string::npos)
1088 h_font_sans_osf = "true";
1091 if (name == "cantarell") {
1092 for (auto const & opt : allopts) {
1093 if (opt == "defaultsans")
1095 if (prefixIs(opt, "oldstyle")) {
1096 h_font_sans_osf = "true";
1099 if (prefixIs(opt, "scale=")) {
1100 scale_as_percentage(opt, h_font_sf_scale[0]);
1108 h_font_sans_opts = xopts;
1112 if (name == "PTSans") {
1113 h_font_sans[0] = "PTSans-TLF";
1114 if (options.size() >= 1) {
1115 if (scale_as_percentage(opts, h_font_sf_scale[0]))
1120 if (name == "plex-sans") {
1121 if (opts.find("condensed") != string::npos)
1122 h_font_sans[0] = "IBMPlexSansCondensed";
1123 else if (opts.find("thin") != string::npos)
1124 h_font_sans[0] = "IBMPlexSansThin";
1125 else if (opts.find("extralight") != string::npos)
1126 h_font_sans[0] = "IBMPlexSansExtraLight";
1127 else if (opts.find("light") != string::npos)
1128 h_font_sans[0] = "IBMPlexSansLight";
1129 else if (opts.find("semibold") != string::npos)
1130 h_font_sans[0] = "IBMPlexSansSemibold";
1132 h_font_sans[0] = "IBMPlexSans";
1133 for (auto const & opt : allopts) {
1136 if (opt == "extralight")
1140 if (opt == "semibold")
1142 if (prefixIs(opt, "scale=")) {
1143 scale_as_percentage(opt, h_font_sf_scale[0]);
1151 h_font_sans_opts = xopts;
1154 if (name == "noto-sans") {
1155 h_font_sans[0] = "NotoSansRegular";
1156 if (!opts.empty()) {
1157 if (opts.find("medium") != string::npos)
1158 h_font_sans[0] = "NotoSansMedium";
1159 else if (opts.find("thin") != string::npos)
1160 h_font_sans[0] = "NotoSansThin";
1161 else if (opts.find("extralight") != string::npos)
1162 h_font_sans[0] = "NotoSansExtralight";
1163 else if (opts.find("light") != string::npos)
1164 h_font_sans[0] = "NotoSansLight";
1166 for (auto const & opt : allopts) {
1167 if (opt == "regular")
1171 if (opt == "extralight")
1175 if (opt == "semibold")
1178 h_font_sans_osf = "true";
1186 h_font_sans_opts = xopts;
1190 if (name == "sourcesanspro") {
1191 h_font_sans[0] = "ADOBESourceSansPro";
1192 for (auto const & opt : allopts) {
1193 if (prefixIs(opt, "scaled=")) {
1194 scale_as_percentage(opt, h_font_sf_scale[0]);
1198 h_font_sans_osf = "true";
1206 h_font_sans_opts = xopts;
1211 if (is_known(name, known_typewriter_font_packages)) {
1212 // fourier can be set as roman font _only_
1213 // fourier as typewriter is handled in handling of \ttdefault
1214 if (name != "fourier") {
1215 h_font_typewriter[0] = name;
1216 if (options.size() >= 1) {
1217 if (scale_as_percentage(opts, h_font_tt_scale[0]))
1223 if (name == "libertineMono" || name == "libertineMono-type1")
1224 h_font_typewriter[0] = "libertine-mono";
1226 if (name == "PTMono") {
1227 h_font_typewriter[0] = "PTMono-TLF";
1228 if (options.size() >= 1) {
1229 if (scale_as_percentage(opts, h_font_tt_scale[0]))
1234 if (name == "plex-mono") {
1235 if (opts.find("thin") != string::npos)
1236 h_font_typewriter[0] = "IBMPlexMonoThin";
1237 else if (opts.find("extralight") != string::npos)
1238 h_font_typewriter[0] = "IBMPlexMonoExtraLight";
1239 else if (opts.find("light") != string::npos)
1240 h_font_typewriter[0] = "IBMPlexMonoLight";
1241 else if (opts.find("semibold") != string::npos)
1242 h_font_typewriter[0] = "IBMPlexMonoSemibold";
1244 h_font_typewriter[0] = "IBMPlexMono";
1245 for (auto const & opt : allopts) {
1248 if (opt == "extralight")
1252 if (opt == "semibold")
1254 if (prefixIs(opt, "scale=")) {
1255 scale_as_percentage(opt, h_font_tt_scale[0]);
1263 h_font_typewriter_opts = xopts;
1267 if (name == "noto-mono") {
1268 h_font_typewriter[0] = "NotoMonoRegular";
1269 for (auto const & opt : allopts) {
1270 if (opt == "regular")
1277 h_font_typewriter_opts = xopts;
1281 if (name == "sourcecodepro") {
1282 h_font_typewriter[0] = "ADOBESourceCodePro";
1283 for (auto const & opt : allopts) {
1284 if (prefixIs(opt, "scaled=")) {
1285 scale_as_percentage(opt, h_font_tt_scale[0]);
1289 h_font_typewriter_osf = "true";
1297 h_font_typewriter_opts = xopts;
1301 // font uses old-style figure
1303 h_font_roman_osf = "true";
1306 if (is_known(name, known_math_font_packages))
1307 h_font_math[0] = name;
1309 if (name == "newtxmath") {
1311 h_font_math[0] = "newtxmath";
1312 else if (opts == "garamondx")
1313 h_font_math[0] = "garamondx-ntxm";
1314 else if (opts == "libertine")
1315 h_font_math[0] = "libertine-ntxm";
1316 else if (opts == "minion")
1317 h_font_math[0] = "minion-ntxm";
1318 else if (opts == "cochineal")
1319 h_font_math[0] = "cochineal-ntxm";
1322 if (name == "iwona")
1324 h_font_math[0] = "iwona-math";
1326 if (name == "kurier")
1328 h_font_math[0] = "kurier-math";
1330 // after the detection and handling of special cases, we can remove the
1331 // fonts, otherwise they would appear in the preamble, see bug #7856
1332 if (is_known(name, known_roman_font_packages) || is_known(name, known_sans_font_packages)
1333 || is_known(name, known_typewriter_font_packages) || is_known(name, known_math_font_packages))
1335 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
1336 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
1337 name == "esint" || name == "mhchem" || name == "mathdots" ||
1338 name == "mathtools" || name == "stackrel" ||
1339 name == "stmaryrd" || name == "undertilde") {
1340 h_use_packages[name] = "2";
1341 registerAutomaticallyLoadedPackage(name);
1344 else if (name == "babel") {
1345 h_language_package = "default";
1346 // One might think we would have to do nothing if babel is loaded
1347 // without any options to prevent pollution of the preamble with this
1348 // babel call in every roundtrip.
1349 // But the user could have defined babel-specific things afterwards. So
1350 // we need to keep it in the preamble to prevent cases like bug #7861.
1351 if (!opts.empty()) {
1352 // check if more than one option was used - used later for inputenc
1353 if (options.begin() != options.end() - 1)
1354 one_language = false;
1355 // babel takes the last language of the option of its \usepackage
1356 // call as document language. If there is no such language option, the
1357 // last language in the documentclass options is used.
1358 handle_opt(options, known_languages, h_language);
1359 // translate the babel name to a LyX name
1360 h_language = babel2lyx(h_language);
1361 if (h_language == "japanese") {
1362 // For Japanese, the encoding isn't indicated in the source
1363 // file, and there's really not much we can do. We could
1364 // 1) offer a list of possible encodings to choose from, or
1365 // 2) determine the encoding of the file by inspecting it.
1366 // For the time being, we leave the encoding alone so that
1367 // we don't get iconv errors when making a wrong guess, and
1368 // we will output a note at the top of the document
1369 // explaining what to do.
1370 Encoding const * const enc = encodings.fromIconvName(
1371 p.getEncoding(), Encoding::japanese, false);
1373 h_inputencoding = enc->name();
1374 is_nonCJKJapanese = true;
1375 // in this case babel can be removed from the preamble
1376 registerAutomaticallyLoadedPackage("babel");
1378 // If babel is called with options, LyX puts them by default into the
1379 // document class options. This works for most languages, except
1380 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
1381 // perhaps in future others.
1382 // Therefore keep the babel call as it is as the user might have
1384 string const babelcall = "\\usepackage[" + opts + "]{babel}\n";
1385 if (!contains(h_preamble.str(), babelcall))
1386 h_preamble << babelcall;
1388 delete_opt(options, known_languages);
1390 if (!contains(h_preamble.str(), "\\usepackage{babel}\n"))
1391 h_preamble << "\\usepackage{babel}\n";
1392 explicit_babel = true;
1396 else if (name == "polyglossia") {
1397 h_language_package = "default";
1398 h_default_output_format = "pdf4";
1399 h_use_non_tex_fonts = true;
1401 registerAutomaticallyLoadedPackage("xunicode");
1402 if (h_inputencoding == "auto-legacy")
1403 p.setEncoding("UTF-8");
1406 else if (name == "CJK") {
1407 // set the encoding to "auto-legacy" because it might be set to "auto-legacy-plain" by the babel handling
1408 // and this would not be correct for CJK
1409 if (h_inputencoding == "auto-legacy-plain")
1410 h_inputencoding = "auto-legacy";
1411 registerAutomaticallyLoadedPackage("CJK");
1414 else if (name == "CJKutf8") {
1415 h_inputencoding = "utf8-cjk";
1416 p.setEncoding("UTF-8");
1417 registerAutomaticallyLoadedPackage("CJKutf8");
1420 else if (name == "fontenc") {
1421 h_fontencoding = getStringFromVector(options, ",");
1425 else if (name == "inputenc" || name == "luainputenc") {
1426 // h_inputencoding is only set when there is not more than one
1427 // inputenc option because otherwise h_inputencoding must be
1428 // set to "auto-legacy" (the default encodings of the document's languages)
1429 // Therefore check that exactly one option is passed to inputenc.
1430 // It is also only set when there is not more than one babel
1432 if (!options.empty()) {
1433 string const encoding = options.back();
1434 Encoding const * const enc = encodings.fromLaTeXName(
1435 encoding, Encoding::inputenc, true);
1437 if (!detectEncoding)
1438 cerr << "Unknown encoding " << encoding
1439 << ". Ignoring." << std::endl;
1441 if (!enc->unsafe() && options.size() == 1 && one_language == true)
1442 h_inputencoding = enc->name();
1443 p.setEncoding(enc->iconvName());
1449 else if (name == "srcltx") {
1450 h_output_sync = "1";
1451 if (!opts.empty()) {
1452 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
1455 h_output_sync_macro = "\\usepackage{srcltx}";
1458 else if (is_known(name, known_old_language_packages)) {
1459 // known language packages from the times before babel
1460 // if they are found and not also babel, they will be used as
1461 // custom language package
1462 h_language_package = "\\usepackage{" + name + "}";
1465 else if (name == "lyxskak") {
1466 // ignore this and its options
1467 const char * const o[] = {"ps", "mover", 0};
1468 delete_opt(options, o);
1471 else if (is_known(name, known_lyx_packages) && options.empty()) {
1472 if (name == "splitidx")
1473 h_use_indices = "true";
1474 else if (name == "minted")
1475 h_use_minted = true;
1476 else if (name == "refstyle")
1477 h_use_refstyle = true;
1478 else if (name == "prettyref")
1479 h_use_refstyle = false;
1480 if (!in_lyx_preamble) {
1481 h_preamble << package_beg_sep << name
1482 << package_mid_sep << "\\usepackage{"
1484 if (p.next_token().cat() == catNewline ||
1485 (p.next_token().cat() == catSpace &&
1486 p.next_next_token().cat() == catNewline))
1488 h_preamble << package_end_sep;
1492 else if (name == "geometry")
1493 handle_geometry(options);
1495 else if (name == "subfig")
1496 ; // ignore this FIXME: Use the package separator mechanism instead
1498 else if (char const * const * where = is_known(name, known_languages))
1499 h_language = known_coded_languages[where - known_languages];
1501 else if (name == "natbib") {
1502 h_biblio_style = "plainnat";
1503 h_cite_engine = "natbib";
1504 h_cite_engine_type = "authoryear";
1505 vector<string>::iterator it =
1506 find(options.begin(), options.end(), "authoryear");
1507 if (it != options.end())
1510 it = find(options.begin(), options.end(), "numbers");
1511 if (it != options.end()) {
1512 h_cite_engine_type = "numerical";
1516 if (!options.empty())
1517 h_biblio_options = join(options, ",");
1520 else if (name == "biblatex") {
1521 h_biblio_style = "plainnat";
1522 h_cite_engine = "biblatex";
1523 h_cite_engine_type = "authoryear";
1525 vector<string>::iterator it =
1526 find(options.begin(), options.end(), "natbib");
1527 if (it != options.end()) {
1529 h_cite_engine = "biblatex-natbib";
1531 opt = process_keyval_opt(options, "natbib");
1533 h_cite_engine = "biblatex-natbib";
1535 opt = process_keyval_opt(options, "style");
1537 h_biblatex_citestyle = opt;
1538 h_biblatex_bibstyle = opt;
1540 opt = process_keyval_opt(options, "citestyle");
1542 h_biblatex_citestyle = opt;
1543 opt = process_keyval_opt(options, "bibstyle");
1545 h_biblatex_bibstyle = opt;
1547 opt = process_keyval_opt(options, "refsection");
1549 if (opt == "none" || opt == "part"
1550 || opt == "chapter" || opt == "section"
1551 || opt == "subsection")
1554 cerr << "Ignoring unkown refesection value '"
1557 opt = process_keyval_opt(options, "bibencoding");
1560 if (!options.empty()) {
1561 h_biblio_options = join(options, ",");
1566 else if (name == "jurabib") {
1567 h_biblio_style = "jurabib";
1568 h_cite_engine = "jurabib";
1569 h_cite_engine_type = "authoryear";
1570 if (!options.empty())
1571 h_biblio_options = join(options, ",");
1574 else if (name == "bibtopic")
1575 h_use_bibtopic = "true";
1577 else if (name == "chapterbib")
1578 h_multibib = "child";
1580 else if (name == "hyperref")
1581 handle_hyperref(options);
1583 else if (name == "algorithm2e") {
1584 // Load "algorithm2e" module
1585 addModule("algorithm2e");
1586 // Add the package options to the global document options
1587 if (!options.empty()) {
1588 if (h_options.empty())
1589 h_options = join(options, ",");
1591 h_options += ',' + join(options, ",");
1594 else if (name == "microtype") {
1595 //we internally support only microtype without params
1596 if (options.empty())
1597 h_use_microtype = "true";
1599 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1602 else if (name == "lineno") {
1603 h_use_lineno = "true";
1604 if (!options.empty()) {
1605 h_lineno_options = join(options, ",");
1610 else if (!in_lyx_preamble) {
1611 if (options.empty())
1612 h_preamble << "\\usepackage{" << name << '}';
1614 h_preamble << "\\usepackage[" << opts << "]{"
1618 if (p.next_token().cat() == catNewline ||
1619 (p.next_token().cat() == catSpace &&
1620 p.next_next_token().cat() == catNewline))
1624 // We need to do something with the options...
1625 if (!options.empty() && !detectEncoding)
1626 cerr << "Ignoring options '" << join(options, ",")
1627 << "' of package " << name << '.' << endl;
1629 // remove the whitespace
1634 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1637 Token t = p.get_token();
1638 if (t.cat() == catEscape &&
1639 is_known(t.cs(), known_if_commands))
1640 handle_if(p, in_lyx_preamble);
1642 if (!in_lyx_preamble)
1643 h_preamble << t.asInput();
1644 if (t.cat() == catEscape && t.cs() == "fi")
1651 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1653 if (contains(h_float_placement, "H"))
1654 registerAutomaticallyLoadedPackage("float");
1655 if (h_spacing != "single" && h_spacing != "default")
1656 registerAutomaticallyLoadedPackage("setspace");
1657 if (h_use_packages["amsmath"] == "2") {
1658 // amsbsy and amstext are already provided by amsmath
1659 registerAutomaticallyLoadedPackage("amsbsy");
1660 registerAutomaticallyLoadedPackage("amstext");
1663 // output the LyX file settings
1664 // Important: Keep the version formatting in sync with LyX and
1665 // lyx2lyx (bug 7951)
1666 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1667 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1668 << lyx_version_minor << '\n'
1669 << "\\lyxformat " << LYX_FORMAT << '\n'
1670 << "\\begin_document\n"
1671 << "\\begin_header\n"
1672 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1673 << "\\origin " << origin << "\n"
1674 << "\\textclass " << h_textclass << "\n";
1675 string const raw = subdoc ? empty_string() : h_preamble.str();
1677 os << "\\begin_preamble\n";
1678 for (string::size_type i = 0; i < raw.size(); ++i) {
1679 if (raw[i] == package_beg_sep) {
1680 // Here follows some package loading code that
1681 // must be skipped if the package is loaded
1683 string::size_type j = raw.find(package_mid_sep, i);
1684 if (j == string::npos)
1686 string::size_type k = raw.find(package_end_sep, j);
1687 if (k == string::npos)
1689 string const package = raw.substr(i + 1, j - i - 1);
1690 string const replacement = raw.substr(j + 1, k - j - 1);
1691 if (auto_packages.find(package) == auto_packages.end())
1697 os << "\n\\end_preamble\n";
1699 if (!h_options.empty())
1700 os << "\\options " << h_options << "\n";
1701 os << "\\use_default_options " << h_use_default_options << "\n";
1702 if (!used_modules.empty()) {
1703 os << "\\begin_modules\n";
1704 vector<string>::const_iterator const end = used_modules.end();
1705 vector<string>::const_iterator it = used_modules.begin();
1706 for (; it != end; ++it)
1708 os << "\\end_modules\n";
1710 if (!h_includeonlys.empty()) {
1711 os << "\\begin_includeonly\n";
1712 for (auto const & iofile : h_includeonlys)
1713 os << iofile << '\n';
1714 os << "\\end_includeonly\n";
1716 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1717 << "\\language " << h_language << "\n"
1718 << "\\language_package " << h_language_package << "\n"
1719 << "\\inputencoding " << h_inputencoding << "\n"
1720 << "\\fontencoding " << h_fontencoding << "\n"
1721 << "\\font_roman \"" << h_font_roman[0]
1722 << "\" \"" << h_font_roman[1] << "\"\n"
1723 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1724 << "\\font_typewriter \"" << h_font_typewriter[0]
1725 << "\" \"" << h_font_typewriter[1] << "\"\n"
1726 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1727 << "\\font_default_family " << h_font_default_family << "\n"
1728 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1729 << "\\font_sc " << h_font_sc << "\n"
1730 << "\\font_roman_osf " << h_font_roman_osf << "\n"
1731 << "\\font_sans_osf " << h_font_sans_osf << "\n"
1732 << "\\font_typewriter_osf " << h_font_typewriter_osf << "\n";
1733 if (!h_font_roman_opts.empty())
1734 os << "\\font_roman_opts \"" << h_font_roman_opts << "\"" << '\n';
1735 os << "\\font_sf_scale " << h_font_sf_scale[0]
1736 << ' ' << h_font_sf_scale[1] << '\n';
1737 if (!h_font_sans_opts.empty())
1738 os << "\\font_sans_opts \"" << h_font_sans_opts << "\"" << '\n';
1739 os << "\\font_tt_scale " << h_font_tt_scale[0]
1740 << ' ' << h_font_tt_scale[1] << '\n';
1741 if (!h_font_cjk.empty())
1742 os << "\\font_cjk " << h_font_cjk << '\n';
1743 if (!h_font_typewriter_opts.empty())
1744 os << "\\font_typewriter_opts \"" << h_font_typewriter_opts << "\"" << '\n';
1745 os << "\\use_microtype " << h_use_microtype << '\n'
1746 << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
1747 << "\\graphics " << h_graphics << '\n'
1748 << "\\default_output_format " << h_default_output_format << "\n"
1749 << "\\output_sync " << h_output_sync << "\n";
1750 if (h_output_sync == "1")
1751 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1752 os << "\\bibtex_command " << h_bibtex_command << "\n"
1753 << "\\index_command " << h_index_command << "\n";
1754 if (!h_float_placement.empty())
1755 os << "\\float_placement " << h_float_placement << "\n";
1756 os << "\\paperfontsize " << h_paperfontsize << "\n"
1757 << "\\spacing " << h_spacing << "\n"
1758 << "\\use_hyperref " << h_use_hyperref << '\n';
1759 if (h_use_hyperref == "true") {
1760 if (!h_pdf_title.empty())
1761 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1762 if (!h_pdf_author.empty())
1763 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1764 if (!h_pdf_subject.empty())
1765 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1766 if (!h_pdf_keywords.empty())
1767 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1768 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1769 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1770 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1771 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1772 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1773 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1774 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1775 "\\pdf_backref " << h_pdf_backref << "\n"
1776 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1777 if (!h_pdf_pagemode.empty())
1778 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1779 if (!h_pdf_quoted_options.empty())
1780 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1782 os << "\\papersize " << h_papersize << "\n"
1783 << "\\use_geometry " << h_use_geometry << '\n';
1784 for (map<string, string>::const_iterator it = h_use_packages.begin();
1785 it != h_use_packages.end(); ++it)
1786 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1787 os << "\\cite_engine " << h_cite_engine << '\n'
1788 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1789 << "\\biblio_style " << h_biblio_style << "\n"
1790 << "\\use_bibtopic " << h_use_bibtopic << "\n";
1791 if (!h_biblio_options.empty())
1792 os << "\\biblio_options " << h_biblio_options << "\n";
1793 if (!h_biblatex_bibstyle.empty())
1794 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
1795 if (!h_biblatex_citestyle.empty())
1796 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
1797 if (!h_multibib.empty())
1798 os << "\\multibib " << h_multibib << "\n";
1799 os << "\\use_indices " << h_use_indices << "\n"
1800 << "\\paperorientation " << h_paperorientation << '\n'
1801 << "\\suppress_date " << h_suppress_date << '\n'
1802 << "\\justification " << h_justification << '\n'
1803 << "\\use_refstyle " << h_use_refstyle << '\n'
1804 << "\\use_minted " << h_use_minted << '\n'
1805 << "\\use_lineno " << h_use_lineno << '\n';
1806 if (!h_lineno_options.empty())
1807 os << "\\lineno_options " << h_lineno_options << '\n';
1808 if (!h_fontcolor.empty())
1809 os << "\\fontcolor " << h_fontcolor << '\n';
1810 if (!h_notefontcolor.empty())
1811 os << "\\notefontcolor " << h_notefontcolor << '\n';
1812 if (!h_backgroundcolor.empty())
1813 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1814 if (!h_boxbgcolor.empty())
1815 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1816 if (index_number != 0)
1817 for (int i = 0; i < index_number; i++) {
1818 os << "\\index " << h_index[i] << '\n'
1819 << "\\shortcut " << h_shortcut[i] << '\n'
1820 << "\\color " << h_color << '\n'
1824 os << "\\index " << h_index[0] << '\n'
1825 << "\\shortcut " << h_shortcut[0] << '\n'
1826 << "\\color " << h_color << '\n'
1830 << "\\secnumdepth " << h_secnumdepth << "\n"
1831 << "\\tocdepth " << h_tocdepth << "\n"
1832 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1833 if (h_paragraph_separation == "skip")
1834 os << "\\defskip " << h_defskip << "\n";
1836 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1837 os << "\\is_math_indent " << h_is_mathindent << "\n";
1838 if (!h_mathindentation.empty())
1839 os << "\\math_indentation " << h_mathindentation << "\n";
1840 os << "\\math_numbering_side " << h_math_numbering_side << "\n";
1841 os << "\\quotes_style " << h_quotes_style << "\n"
1842 << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
1843 << "\\papercolumns " << h_papercolumns << "\n"
1844 << "\\papersides " << h_papersides << "\n"
1845 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1846 if (!h_listings_params.empty())
1847 os << "\\listings_params " << h_listings_params << "\n";
1848 os << "\\tracking_changes " << h_tracking_changes << "\n"
1849 << "\\output_changes " << h_output_changes << "\n"
1850 << "\\html_math_output " << h_html_math_output << "\n"
1851 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1852 << "\\html_be_strict " << h_html_be_strict << "\n"
1854 << "\\end_header\n\n"
1855 << "\\begin_body\n";
1860 void Preamble::parse(Parser & p, string const & forceclass,
1861 TeX2LyXDocClass & tc)
1863 // initialize fixed types
1864 special_columns_['D'] = 3;
1865 parse(p, forceclass, false, tc);
1869 void Preamble::parse(Parser & p, string const & forceclass,
1870 bool detectEncoding, TeX2LyXDocClass & tc)
1872 bool is_full_document = false;
1873 bool is_lyx_file = false;
1874 bool in_lyx_preamble = false;
1876 // determine whether this is a full document or a fragment for inclusion
1878 Token const & t = p.get_token();
1880 if (t.cat() == catEscape && t.cs() == "documentclass") {
1881 is_full_document = true;
1887 if (detectEncoding && !is_full_document)
1890 while (is_full_document && p.good()) {
1891 if (detectEncoding && h_inputencoding != "auto-legacy" &&
1892 h_inputencoding != "auto-legacy-plain")
1895 Token const & t = p.get_token();
1898 if (!detectEncoding)
1899 cerr << "t: " << t << '\n';
1905 if (!in_lyx_preamble &&
1906 (t.cat() == catLetter ||
1907 t.cat() == catSuper ||
1908 t.cat() == catSub ||
1909 t.cat() == catOther ||
1910 t.cat() == catMath ||
1911 t.cat() == catActive ||
1912 t.cat() == catBegin ||
1913 t.cat() == catEnd ||
1914 t.cat() == catAlign ||
1915 t.cat() == catParameter)) {
1916 h_preamble << t.cs();
1920 if (!in_lyx_preamble &&
1921 (t.cat() == catSpace || t.cat() == catNewline)) {
1922 h_preamble << t.asInput();
1926 if (t.cat() == catComment) {
1927 static regex const islyxfile("%% LyX .* created this file");
1928 static regex const usercommands("User specified LaTeX commands");
1930 string const comment = t.asInput();
1932 // magically switch encoding default if it looks like XeLaTeX
1933 static string const magicXeLaTeX =
1934 "% This document must be compiled with XeLaTeX ";
1935 if (comment.size() > magicXeLaTeX.size()
1936 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1937 && h_inputencoding == "auto-legacy") {
1938 if (!detectEncoding)
1939 cerr << "XeLaTeX comment found, switching to UTF8\n";
1940 h_inputencoding = "utf8";
1943 if (regex_search(comment, sub, islyxfile)) {
1945 in_lyx_preamble = true;
1946 } else if (is_lyx_file
1947 && regex_search(comment, sub, usercommands))
1948 in_lyx_preamble = false;
1949 else if (!in_lyx_preamble)
1950 h_preamble << t.asInput();
1954 if (t.cs() == "PassOptionsToPackage") {
1955 string const poptions = p.getArg('{', '}');
1956 string const package = p.verbatim_item();
1957 extra_package_options_.insert(make_pair(package, poptions));
1961 if (t.cs() == "pagestyle") {
1962 h_paperpagestyle = p.verbatim_item();
1966 if (t.cs() == "setdefaultlanguage") {
1968 // We don't yet care about non-language variant options
1969 // because LyX doesn't support this yet, see bug #8214
1971 string langopts = p.getOpt();
1972 // check if the option contains a variant, if yes, extract it
1973 string::size_type pos_var = langopts.find("variant");
1974 string::size_type i = langopts.find(',', pos_var);
1975 string::size_type k = langopts.find('=', pos_var);
1976 if (pos_var != string::npos){
1978 if (i == string::npos)
1979 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1981 variant = langopts.substr(k + 1, i - k - 1);
1982 h_language = variant;
1986 h_language = p.verbatim_item();
1987 //finally translate the poyglossia name to a LyX name
1988 h_language = polyglossia2lyx(h_language);
1992 if (t.cs() == "setotherlanguage") {
1993 // We don't yet care about the option because LyX doesn't
1994 // support this yet, see bug #8214
1995 p.hasOpt() ? p.getOpt() : string();
2000 if (t.cs() == "setmainfont") {
2001 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2002 h_font_roman[1] = p.getArg('{', '}');
2003 if (!fontopts.empty()) {
2004 vector<string> opts = getVectorFromString(fontopts);
2006 for (auto const & opt : opts) {
2007 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2010 if (!fontopts.empty())
2014 h_font_roman_opts = fontopts;
2019 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
2020 // LyX currently only supports the scale option
2021 string scale, fontopts;
2023 fontopts = p.getArg('[', ']');
2024 if (!fontopts.empty()) {
2025 vector<string> opts = getVectorFromString(fontopts);
2027 for (auto const & opt : opts) {
2028 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2031 if (prefixIs(opt, "Scale=")) {
2032 scale_as_percentage(opt, scale);
2035 if (!fontopts.empty())
2041 if (t.cs() == "setsansfont") {
2043 h_font_sf_scale[1] = scale;
2044 h_font_sans[1] = p.getArg('{', '}');
2045 if (!fontopts.empty())
2046 h_font_sans_opts = fontopts;
2049 h_font_tt_scale[1] = scale;
2050 h_font_typewriter[1] = p.getArg('{', '}');
2051 if (!fontopts.empty())
2052 h_font_typewriter_opts = fontopts;
2057 if (t.cs() == "babelfont") {
2059 h_use_non_tex_fonts = true;
2060 h_language_package = "babel";
2061 if (h_inputencoding == "auto-legacy")
2062 p.setEncoding("UTF-8");
2063 // we don't care about the lang option
2064 string const lang = p.hasOpt() ? p.getArg('[', ']') : string();
2065 string const family = p.getArg('{', '}');
2066 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2067 string const fontname = p.getArg('{', '}');
2068 if (lang.empty() && family == "rm") {
2069 h_font_roman[1] = fontname;
2070 if (!fontopts.empty()) {
2071 vector<string> opts = getVectorFromString(fontopts);
2073 for (auto const & opt : opts) {
2074 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2077 if (!fontopts.empty())
2081 h_font_roman_opts = fontopts;
2084 } else if (lang.empty() && (family == "sf" || family == "tt")) {
2085 // LyX currently only supports the scale option
2087 if (!fontopts.empty()) {
2088 vector<string> opts = getVectorFromString(fontopts);
2090 for (auto const & opt : opts) {
2091 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2094 if (prefixIs(opt, "Scale=")) {
2095 scale_as_percentage(opt, scale);
2098 if (!fontopts.empty())
2103 if (family == "sf") {
2105 h_font_sf_scale[1] = scale;
2106 h_font_sans[1] = fontname;
2107 if (!fontopts.empty())
2108 h_font_sans_opts = fontopts;
2111 h_font_tt_scale[1] = scale;
2112 h_font_typewriter[1] = fontname;
2113 if (!fontopts.empty())
2114 h_font_typewriter_opts = fontopts;
2118 // not rm, sf or tt or lang specific
2119 h_preamble << '\\' << t.cs();
2121 h_preamble << '[' << lang << ']';
2122 h_preamble << '{' << family << '}';
2123 if (!fontopts.empty())
2124 h_preamble << '[' << fontopts << ']';
2125 h_preamble << '{' << fontname << '}' << '\n';
2130 if (t.cs() == "date") {
2131 string argument = p.getArg('{', '}');
2132 if (argument.empty())
2133 h_suppress_date = "true";
2135 h_preamble << t.asInput() << '{' << argument << '}';
2139 if (t.cs() == "color") {
2140 string const space =
2141 (p.hasOpt() ? p.getOpt() : string());
2142 string argument = p.getArg('{', '}');
2143 // check the case that a standard color is used
2144 if (space.empty() && is_known(argument, known_basic_colors)) {
2145 h_fontcolor = rgbcolor2code(argument);
2146 registerAutomaticallyLoadedPackage("color");
2147 } else if (space.empty() && argument == "document_fontcolor")
2148 registerAutomaticallyLoadedPackage("color");
2149 // check the case that LyX's document_fontcolor is defined
2150 // but not used for \color
2152 h_preamble << t.asInput();
2154 h_preamble << space;
2155 h_preamble << '{' << argument << '}';
2156 // the color might already be set because \definecolor
2157 // is parsed before this
2163 if (t.cs() == "pagecolor") {
2164 string argument = p.getArg('{', '}');
2165 // check the case that a standard color is used
2166 if (is_known(argument, known_basic_colors)) {
2167 h_backgroundcolor = rgbcolor2code(argument);
2168 } else if (argument == "page_backgroundcolor")
2169 registerAutomaticallyLoadedPackage("color");
2170 // check the case that LyX's page_backgroundcolor is defined
2171 // but not used for \pagecolor
2173 h_preamble << t.asInput() << '{' << argument << '}';
2174 // the color might already be set because \definecolor
2175 // is parsed before this
2176 h_backgroundcolor = "";
2181 if (t.cs() == "makeatletter") {
2182 // LyX takes care of this
2183 p.setCatcode('@', catLetter);
2187 if (t.cs() == "makeatother") {
2188 // LyX takes care of this
2189 p.setCatcode('@', catOther);
2193 if (t.cs() == "makeindex") {
2194 // LyX will re-add this if a print index command is found
2199 if (t.cs() == "newindex") {
2200 string const indexname = p.getArg('[', ']');
2201 string const shortcut = p.verbatim_item();
2202 if (!indexname.empty())
2203 h_index[index_number] = indexname;
2205 h_index[index_number] = shortcut;
2206 h_shortcut[index_number] = shortcut;
2212 if (t.cs() == "addbibresource") {
2213 string const options = p.getArg('[', ']');
2214 string const arg = removeExtension(p.getArg('{', '}'));
2215 if (!options.empty()) {
2216 // check if the option contains a bibencoding, if yes, extract it
2217 string::size_type pos = options.find("bibencoding=");
2219 if (pos != string::npos) {
2220 string::size_type i = options.find(',', pos);
2221 if (i == string::npos)
2222 encoding = options.substr(pos + 1);
2224 encoding = options.substr(pos, i - pos);
2225 pos = encoding.find('=');
2226 if (pos == string::npos)
2229 encoding = encoding.substr(pos + 1);
2231 if (!encoding.empty())
2232 biblatex_encodings.push_back(normalize_filename(arg) + ' ' + encoding);
2234 biblatex_bibliographies.push_back(arg);
2238 if (t.cs() == "bibliography") {
2239 vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
2240 biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
2244 if (t.cs() == "RS@ifundefined") {
2245 string const name = p.verbatim_item();
2246 string const body1 = p.verbatim_item();
2247 string const body2 = p.verbatim_item();
2248 // only non-lyxspecific stuff
2249 if (in_lyx_preamble &&
2250 (name == "subsecref" || name == "thmref" || name == "lemref"))
2254 ss << '\\' << t.cs();
2255 ss << '{' << name << '}'
2256 << '{' << body1 << '}'
2257 << '{' << body2 << '}';
2258 h_preamble << ss.str();
2263 if (t.cs() == "AtBeginDocument") {
2264 string const name = p.verbatim_item();
2265 // only non-lyxspecific stuff
2266 if (in_lyx_preamble &&
2267 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
2268 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
2269 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
2270 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
2271 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
2272 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
2273 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
2274 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
2275 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
2276 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
2277 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
2278 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
2279 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
2280 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
2281 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
2285 ss << '\\' << t.cs();
2286 ss << '{' << name << '}';
2287 h_preamble << ss.str();
2292 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
2293 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
2294 || t.cs() == "providecommand" || t.cs() == "providecommandx"
2295 || t.cs() == "DeclareRobustCommand"
2296 || t.cs() == "DeclareRobustCommandx"
2297 || t.cs() == "ProvideTextCommandDefault"
2298 || t.cs() == "DeclareMathAccent") {
2300 if (p.next_token().character() == '*') {
2304 string const name = p.verbatim_item();
2305 string const opt1 = p.getFullOpt();
2306 string const opt2 = p.getFullOpt();
2307 string const body = p.verbatim_item();
2308 // store the in_lyx_preamble setting
2309 bool const was_in_lyx_preamble = in_lyx_preamble;
2311 if (name == "\\rmdefault")
2312 if (is_known(body, known_roman_font_packages)) {
2313 h_font_roman[0] = body;
2315 in_lyx_preamble = true;
2317 if (name == "\\sfdefault")
2318 if (is_known(body, known_sans_font_packages)) {
2319 h_font_sans[0] = body;
2321 in_lyx_preamble = true;
2323 if (name == "\\ttdefault")
2324 if (is_known(body, known_typewriter_font_packages)) {
2325 h_font_typewriter[0] = body;
2327 in_lyx_preamble = true;
2329 if (name == "\\familydefault") {
2330 string family = body;
2331 // remove leading "\"
2332 h_font_default_family = family.erase(0,1);
2334 in_lyx_preamble = true;
2337 // remove LyX-specific definitions that are re-added by LyX
2339 // \lyxline is an ancient command that is converted by tex2lyx into
2340 // a \rule therefore remove its preamble code
2341 if (name == "\\lyxdot" || name == "\\lyxarrow"
2342 || name == "\\lyxline" || name == "\\LyX") {
2344 in_lyx_preamble = true;
2347 // Add the command to the known commands
2348 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
2350 // only non-lyxspecific stuff
2351 if (!in_lyx_preamble) {
2353 ss << '\\' << t.cs();
2356 ss << '{' << name << '}' << opt1 << opt2
2357 << '{' << body << "}";
2358 if (prefixIs(t.cs(), "renew") || !contains(h_preamble.str(), ss.str()))
2359 h_preamble << ss.str();
2361 ostream & out = in_preamble ? h_preamble : os;
2362 out << "\\" << t.cs() << "{" << name << "}"
2363 << opts << "{" << body << "}";
2366 // restore the in_lyx_preamble setting
2367 in_lyx_preamble = was_in_lyx_preamble;
2371 if (t.cs() == "documentclass") {
2372 vector<string>::iterator it;
2373 vector<string> opts = split_options(p.getArg('[', ']'));
2374 handle_opt(opts, known_fontsizes, h_paperfontsize);
2375 delete_opt(opts, known_fontsizes);
2376 // delete "pt" at the end
2377 string::size_type i = h_paperfontsize.find("pt");
2378 if (i != string::npos)
2379 h_paperfontsize.erase(i);
2380 // The documentclass options are always parsed before the options
2381 // of the babel call so that a language cannot overwrite the babel
2383 handle_opt(opts, known_languages, h_language);
2384 delete_opt(opts, known_languages);
2387 if ((it = find(opts.begin(), opts.end(), "fleqn"))
2389 h_is_mathindent = "1";
2392 // formula numbering side
2393 if ((it = find(opts.begin(), opts.end(), "leqno"))
2395 h_math_numbering_side = "left";
2398 else if ((it = find(opts.begin(), opts.end(), "reqno"))
2400 h_math_numbering_side = "right";
2404 // paper orientation
2405 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
2406 h_paperorientation = "landscape";
2410 if ((it = find(opts.begin(), opts.end(), "oneside"))
2415 if ((it = find(opts.begin(), opts.end(), "twoside"))
2421 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
2423 h_papercolumns = "1";
2426 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
2428 h_papercolumns = "2";
2432 // some size options are known to any document classes, other sizes
2433 // are handled by the \geometry command of the geometry package
2434 handle_opt(opts, known_class_paper_sizes, h_papersize);
2435 delete_opt(opts, known_class_paper_sizes);
2436 // the remaining options
2437 h_options = join(opts, ",");
2438 // FIXME This does not work for classes that have a
2439 // different name in LyX than in LaTeX
2440 h_textclass = p.getArg('{', '}');
2445 if (t.cs() == "usepackage") {
2446 string const options = p.getArg('[', ']');
2447 string const name = p.getArg('{', '}');
2448 vector<string> vecnames;
2449 split(name, vecnames, ',');
2450 vector<string>::const_iterator it = vecnames.begin();
2451 vector<string>::const_iterator end = vecnames.end();
2452 for (; it != end; ++it)
2453 handle_package(p, trimSpaceAndEol(*it), options,
2454 in_lyx_preamble, detectEncoding);
2458 if (t.cs() == "inputencoding") {
2459 string const encoding = p.getArg('{','}');
2460 Encoding const * const enc = encodings.fromLaTeXName(
2461 encoding, Encoding::inputenc, true);
2463 if (!detectEncoding)
2464 cerr << "Unknown encoding " << encoding
2465 << ". Ignoring." << std::endl;
2468 h_inputencoding = enc->name();
2469 p.setEncoding(enc->iconvName());
2474 if (t.cs() == "newenvironment") {
2475 string const name = p.getArg('{', '}');
2476 string const opt1 = p.getFullOpt();
2477 string const opt2 = p.getFullOpt();
2478 string const beg = p.verbatim_item();
2479 string const end = p.verbatim_item();
2480 if (!in_lyx_preamble) {
2481 h_preamble << "\\newenvironment{" << name
2482 << '}' << opt1 << opt2 << '{'
2483 << beg << "}{" << end << '}';
2485 add_known_environment(name, opt1, !opt2.empty(),
2486 from_utf8(beg), from_utf8(end));
2490 if (t.cs() == "newtheorem") {
2492 if (p.next_token().character() == '*') {
2496 string const name = p.getArg('{', '}');
2497 string const opt1 = p.getFullOpt();
2498 string const opt2 = p.getFullOpt();
2499 string const body = p.verbatim_item();
2500 string const opt3 = p.getFullOpt();
2501 string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
2503 string const complete = cmd + "{" + name + '}' +
2504 opt1 + opt2 + '{' + body + '}' + opt3;
2506 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
2508 if (!in_lyx_preamble)
2509 h_preamble << complete;
2513 if (t.cs() == "def") {
2514 string name = p.get_token().cs();
2515 // In fact, name may be more than the name:
2516 // In the test case of bug 8116
2517 // name == "csname SF@gobble@opt \endcsname".
2518 // Therefore, we need to use asInput() instead of cs().
2519 while (p.next_token().cat() != catBegin)
2520 name += p.get_token().asInput();
2521 if (!in_lyx_preamble)
2522 h_preamble << "\\def\\" << name << '{'
2523 << p.verbatim_item() << "}";
2527 if (t.cs() == "newcolumntype") {
2528 string const name = p.getArg('{', '}');
2529 trimSpaceAndEol(name);
2531 string opts = p.getOpt();
2532 if (!opts.empty()) {
2533 istringstream is(string(opts, 1));
2536 special_columns_[name[0]] = nargs;
2537 h_preamble << "\\newcolumntype{" << name << "}";
2539 h_preamble << "[" << nargs << "]";
2540 h_preamble << "{" << p.verbatim_item() << "}";
2544 if (t.cs() == "setcounter") {
2545 string const name = p.getArg('{', '}');
2546 string const content = p.getArg('{', '}');
2547 if (name == "secnumdepth")
2548 h_secnumdepth = content;
2549 else if (name == "tocdepth")
2550 h_tocdepth = content;
2552 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
2556 if (t.cs() == "setlength") {
2557 string const name = p.verbatim_item();
2558 string const content = p.verbatim_item();
2559 // the paragraphs are only not indented when \parindent is set to zero
2560 if (name == "\\parindent" && content != "") {
2561 if (content[0] == '0')
2562 h_paragraph_separation = "skip";
2564 h_paragraph_indentation = translate_len(content);
2565 } else if (name == "\\parskip") {
2566 if (content == "\\smallskipamount")
2567 h_defskip = "smallskip";
2568 else if (content == "\\medskipamount")
2569 h_defskip = "medskip";
2570 else if (content == "\\bigskipamount")
2571 h_defskip = "bigskip";
2573 h_defskip = translate_len(content);
2574 } else if (name == "\\mathindent") {
2575 h_mathindentation = translate_len(content);
2577 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2581 if (t.cs() == "onehalfspacing") {
2582 h_spacing = "onehalf";
2586 if (t.cs() == "doublespacing") {
2587 h_spacing = "double";
2591 if (t.cs() == "setstretch") {
2592 h_spacing = "other " + p.verbatim_item();
2596 if (t.cs() == "synctex") {
2597 // the scheme is \synctex=value
2598 // where value can only be "1" or "-1"
2599 h_output_sync = "1";
2600 // there can be any character behind the value (e.g. a linebreak or a '\'
2601 // therefore we extract it char by char
2603 string value = p.get_token().asInput();
2605 value += p.get_token().asInput();
2606 h_output_sync_macro = "\\synctex=" + value;
2610 if (t.cs() == "begin") {
2611 string const name = p.getArg('{', '}');
2612 if (name == "document")
2614 h_preamble << "\\begin{" << name << "}";
2618 if (t.cs() == "geometry") {
2619 vector<string> opts = split_options(p.getArg('{', '}'));
2620 handle_geometry(opts);
2624 if (t.cs() == "definecolor") {
2625 string const color = p.getArg('{', '}');
2626 string const space = p.getArg('{', '}');
2627 string const value = p.getArg('{', '}');
2628 if (color == "document_fontcolor" && space == "rgb") {
2629 RGBColor c(RGBColorFromLaTeX(value));
2630 h_fontcolor = X11hexname(c);
2631 } else if (color == "note_fontcolor" && space == "rgb") {
2632 RGBColor c(RGBColorFromLaTeX(value));
2633 h_notefontcolor = X11hexname(c);
2634 } else if (color == "page_backgroundcolor" && space == "rgb") {
2635 RGBColor c(RGBColorFromLaTeX(value));
2636 h_backgroundcolor = X11hexname(c);
2637 } else if (color == "shadecolor" && space == "rgb") {
2638 RGBColor c(RGBColorFromLaTeX(value));
2639 h_boxbgcolor = X11hexname(c);
2641 h_preamble << "\\definecolor{" << color
2642 << "}{" << space << "}{" << value
2648 if (t.cs() == "bibliographystyle") {
2649 h_biblio_style = p.verbatim_item();
2653 if (t.cs() == "jurabibsetup") {
2654 // FIXME p.getArg('{', '}') is most probably wrong (it
2655 // does not handle nested braces).
2656 // Use p.verbatim_item() instead.
2657 vector<string> jurabibsetup =
2658 split_options(p.getArg('{', '}'));
2659 // add jurabibsetup to the jurabib package options
2660 add_package("jurabib", jurabibsetup);
2661 if (!jurabibsetup.empty()) {
2662 h_preamble << "\\jurabibsetup{"
2663 << join(jurabibsetup, ",") << '}';
2668 if (t.cs() == "hypersetup") {
2669 vector<string> hypersetup =
2670 split_options(p.verbatim_item());
2671 // add hypersetup to the hyperref package options
2672 handle_hyperref(hypersetup);
2673 if (!hypersetup.empty()) {
2674 h_preamble << "\\hypersetup{"
2675 << join(hypersetup, ",") << '}';
2680 if (t.cs() == "includeonly") {
2681 vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
2682 for (auto & iofile : includeonlys) {
2683 string filename(normalize_filename(iofile));
2684 string const path = getMasterFilePath(true);
2685 // We want to preserve relative/absolute filenames,
2686 // therefore path is only used for testing
2687 if (!makeAbsPath(filename, path).exists()) {
2688 // The file extension is probably missing.
2689 // Now try to find it out.
2690 string const tex_name =
2691 find_file(filename, path,
2692 known_tex_extensions);
2693 if (!tex_name.empty())
2694 filename = tex_name;
2697 if (makeAbsPath(filename, path).exists())
2698 fix_child_filename(filename);
2700 cerr << "Warning: Could not find included file '"
2701 << filename << "'." << endl;
2702 outname = changeExtension(filename, "lyx");
2703 h_includeonlys.push_back(outname);
2708 if (is_known(t.cs(), known_if_3arg_commands)) {
2709 // prevent misparsing of \usepackage if it is used
2710 // as an argument (see e.g. our own output of
2711 // \@ifundefined above)
2712 string const arg1 = p.verbatim_item();
2713 string const arg2 = p.verbatim_item();
2714 string const arg3 = p.verbatim_item();
2715 // test case \@ifundefined{date}{}{\date{}}
2716 if (t.cs() == "@ifundefined" && arg1 == "date" &&
2717 arg2.empty() && arg3 == "\\date{}") {
2718 h_suppress_date = "true";
2719 // older tex2lyx versions did output
2720 // \@ifundefined{definecolor}{\usepackage{color}}{}
2721 } else if (t.cs() == "@ifundefined" &&
2722 arg1 == "definecolor" &&
2723 arg2 == "\\usepackage{color}" &&
2725 if (!in_lyx_preamble)
2726 h_preamble << package_beg_sep
2729 << "\\@ifundefined{definecolor}{color}{}"
2732 //\@ifundefined{showcaptionsetup}{}{%
2733 // \PassOptionsToPackage{caption=false}{subfig}}
2734 // that LyX uses for subfloats
2735 } else if (t.cs() == "@ifundefined" &&
2736 arg1 == "showcaptionsetup" && arg2.empty()
2737 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
2739 } else if (!in_lyx_preamble) {
2740 h_preamble << t.asInput()
2741 << '{' << arg1 << '}'
2742 << '{' << arg2 << '}'
2743 << '{' << arg3 << '}';
2748 if (is_known(t.cs(), known_if_commands)) {
2749 // must not parse anything in conditional code, since
2750 // LyX would output the parsed contents unconditionally
2751 if (!in_lyx_preamble)
2752 h_preamble << t.asInput();
2753 handle_if(p, in_lyx_preamble);
2757 if (!t.cs().empty() && !in_lyx_preamble) {
2758 h_preamble << '\\' << t.cs();
2763 // remove the whitespace
2766 // Force textclass if the user wanted it
2767 if (!forceclass.empty())
2768 h_textclass = forceclass;
2769 tc.setName(h_textclass);
2770 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2771 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2774 if (h_papersides.empty()) {
2777 h_papersides = ss.str();
2780 // If the CJK package is used we cannot set the document language from
2781 // the babel options. Instead, we guess which language is used most
2782 // and set this one.
2783 default_language = h_language;
2784 if (is_full_document &&
2785 (auto_packages.find("CJK") != auto_packages.end() ||
2786 auto_packages.find("CJKutf8") != auto_packages.end())) {
2788 h_language = guessLanguage(p, default_language);
2790 if (explicit_babel && h_language != default_language) {
2791 // We set the document language to a CJK language,
2792 // but babel is explicitly called in the user preamble
2793 // without options. LyX will not add the default
2794 // language to the document options if it is either
2795 // english, or no text is set as default language.
2796 // Therefore we need to add a language option explicitly.
2797 // FIXME: It would be better to remove all babel calls
2798 // from the user preamble, but this is difficult
2799 // without re-introducing bug 7861.
2800 if (h_options.empty())
2801 h_options = lyx2babel(default_language);
2803 h_options += ',' + lyx2babel(default_language);
2807 // Finally, set the quote style.
2808 // LyX knows the following quotes styles:
2809 // british, cjk, cjkangle, danish, english, french, german,
2810 // polish, russian, swedish and swiss
2811 // conversion list taken from
2812 // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
2813 // (quotes for kazakh are unknown)
2815 if (is_known(h_language, known_british_quotes_languages))
2816 h_quotes_style = "british";
2818 else if (is_known(h_language, known_cjk_quotes_languages))
2819 h_quotes_style = "cjk";
2821 else if (is_known(h_language, known_cjkangle_quotes_languages))
2822 h_quotes_style = "cjkangle";
2824 else if (is_known(h_language, known_danish_quotes_languages))
2825 h_quotes_style = "danish";
2827 else if (is_known(h_language, known_french_quotes_languages))
2828 h_quotes_style = "french";
2830 else if (is_known(h_language, known_german_quotes_languages))
2831 h_quotes_style = "german";
2833 else if (is_known(h_language, known_polish_quotes_languages))
2834 h_quotes_style = "polish";
2836 else if (is_known(h_language, known_russian_quotes_languages))
2837 h_quotes_style = "russian";
2839 else if (is_known(h_language, known_swedish_quotes_languages))
2840 h_quotes_style = "swedish";
2842 else if (is_known(h_language, known_swiss_quotes_languages))
2843 h_quotes_style = "swiss";
2845 else if (is_known(h_language, known_english_quotes_languages))
2846 h_quotes_style = "english";
2850 string Preamble::parseEncoding(Parser & p, string const & forceclass)
2852 TeX2LyXDocClass dummy;
2853 parse(p, forceclass, true, dummy);
2854 if (h_inputencoding != "auto-legacy" && h_inputencoding != "auto-legacy-plain")
2855 return h_inputencoding;
2860 string babel2lyx(string const & language)
2862 char const * const * where = is_known(language, known_languages);
2864 return known_coded_languages[where - known_languages];
2869 string lyx2babel(string const & language)
2871 char const * const * where = is_known(language, known_coded_languages);
2873 return known_languages[where - known_coded_languages];
2878 string Preamble::polyglossia2lyx(string const & language)
2880 char const * const * where = is_known(language, polyglossia_languages);
2882 return coded_polyglossia_languages[where - polyglossia_languages];
2887 string rgbcolor2code(string const & name)
2889 char const * const * where = is_known(name, known_basic_colors);
2891 // "red", "green" etc
2892 return known_basic_color_codes[where - known_basic_colors];
2894 // "255,0,0", "0,255,0" etc
2895 RGBColor c(RGBColorFromLaTeX(name));
2896 return X11hexname(c);