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", "CrimsonPro", "DejaVuSerif",
146 "DejaVuSerifCondensed", "fourier", "garamondx", "libertine", "libertineRoman", "libertine-type1",
147 "lmodern", "mathdesign", "mathpazo", "mathptmx", "MinionPro", "newcent", "noto", "noto-serif",
148 "PTSerif", "tgbonum", "tgchorus", "tgpagella", "tgschola", "tgtermes", "utopia", "xcharter", 0 };
150 const char * const known_sans_font_packages[] = { "avant", "berasans", "biolinum",
151 "biolinum-type1", "cantarell", "Chivo", "cmbr", "cmss", "DejaVuSans", "DejaVuSansCondensed", "FiraSans", "helvet", "iwona",
152 "iwonac", "iwonal", "iwonalc", "kurier", "kurierc", "kurierl", "kurierlc", "LibertinusSans-LF", "lmss", "noto-sans", "PTSans",
153 "tgadventor", "tgheros", "uop", 0 };
155 const char * const known_typewriter_font_packages[] = { "beramono", "cmtl", "cmtt", "courier", "DejaVuSansMono",
156 "FiraMono", "lmtt", "luximono", "libertineMono", "libertineMono-type1", "LibertinusMono-TLF", "lmodern",
157 "mathpazo", "mathptmx", "newcent", "noto-mono", "PTMono", "tgcursor", "txtt", 0 };
159 const char * const known_math_font_packages[] = { "eulervm", "newtxmath", 0};
161 const char * const known_latex_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_paper_margins[] = { "lmargin", "tmargin", "rmargin",
168 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
170 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
171 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
174 /// commands that can start an \if...\else...\endif sequence
175 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
176 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
177 "ifsidecap", "ifupgreek", 0};
179 const char * const known_basic_colors[] = {"black", "blue", "brown", "cyan",
180 "darkgray", "gray", "green", "lightgray", "lime", "magenta", "orange", "olive",
181 "pink", "purple", "red", "teal", "violet", "white", "yellow", 0};
183 const char * const known_basic_color_codes[] = {"#000000", "#0000ff", "#964B00", "#00ffff",
184 "#a9a9a9", "#808080", "#00ff00", "#d3d3d3", "#bfff00", "#ff00ff", "#ff7f00", "#808000",
185 "#ffc0cb", "#800080", "#ff0000", "#008080", "#8f00ff", "#ffffff", "#ffff00", 0};
187 /// conditional commands with three arguments like \@ifundefined{}{}{}
188 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
192 * Known file extensions for TeX files as used by \\includeonly
194 char const * const known_tex_extensions[] = {"tex", 0};
196 /// packages that work only in xetex
197 /// polyglossia is handled separately
198 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
199 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
200 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
202 /// packages that are automatically skipped if loaded by LyX
203 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
204 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color",
205 "float", "fontspec", "framed", "graphicx", "hhline", "ifthen", "longtable",
206 "makeidx", "minted", "multirow", "nomencl", "parskip", "pdfpages", "prettyref", "refstyle",
207 "rotating", "rotfloat", "splitidx", "setspace", "subscript", "tabularx","textcomp", "tipa",
208 "tipx", "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xcolor", "xltabular",
211 // codes used to remove packages that are loaded automatically by LyX.
212 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
213 const char package_beg_sep = '\001';
214 const char package_mid_sep = '\002';
215 const char package_end_sep = '\003';
218 // returns true if at least one of the options in what has been found
219 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
225 // the last language option is the document language (for babel and LyX)
226 // the last size option is the document font size
227 vector<string>::iterator it;
228 vector<string>::iterator position = opts.begin();
229 for (; *what; ++what) {
230 it = find(opts.begin(), opts.end(), *what);
231 if (it != opts.end()) {
232 if (it >= position) {
243 void delete_opt(vector<string> & opts, char const * const * what)
248 // remove found options from the list
249 // do this after handle_opt to avoid potential memory leaks
250 vector<string>::iterator it;
251 for (; *what; ++what) {
252 it = find(opts.begin(), opts.end(), *what);
253 if (it != opts.end())
260 * Split a package options string (keyval format) into a vector.
262 * authorformat=smallcaps,
264 * titleformat=colonsep,
265 * bibformat={tabular,ibidem,numbered}
267 vector<string> split_options(string const & input)
269 vector<string> options;
273 Token const & t = p.get_token();
274 if (t.asInput() == ",") {
275 options.push_back(trimSpaceAndEol(option));
277 } else if (t.asInput() == "=") {
280 if (p.next_token().asInput() == "{")
281 option += '{' + p.getArg('{', '}') + '}';
282 } else if (t.cat() != catSpace && t.cat() != catComment)
283 option += t.asInput();
287 options.push_back(trimSpaceAndEol(option));
294 * Retrieve a keyval option "name={value with=sign}" named \p name from
295 * \p options and return the value.
296 * The found option is also removed from \p options.
298 string process_keyval_opt(vector<string> & options, string name)
300 for (size_t i = 0; i < options.size(); ++i) {
301 vector<string> option;
302 split(options[i], option, '=');
303 if (option.size() < 2)
305 if (option[0] == name) {
306 options.erase(options.begin() + i);
307 option.erase(option.begin());
308 return join(option, "=");
314 } // anonymous namespace
318 * known polyglossia language names (including variants)
319 * FIXME: support spelling=old for german variants (german vs. ngerman LyX names etc)
321 const char * const Preamble::polyglossia_languages[] = {
322 "albanian", "american", "amharic", "ancient", "arabic", "armenian", "asturian", "australian",
323 "bahasai", "bahasam", "basque", "bengali", "brazil", "brazilian", "breton", "british", "bulgarian",
324 "catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
325 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
326 "galician", "greek", "monotonic", "hebrew", "hindi",
327 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean",
328 "lao", "latin", "latvian", "lithuanian", "lsorbian", "magyar", "malayalam", "marathi",
329 "austrian", "newzealand", "german", "norsk", "nynorsk", "occitan", "oldrussian",
330 "piedmontese", "polish", "polytonic", "portuges", "romanian", "romansh", "russian",
331 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovenian", "spanish", "swedish", "syriac",
332 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
333 "ukrainian", "urdu", "usorbian", "vietnamese", "welsh", 0};
334 // not yet supported by LyX: "korean", "nko"
337 * the same as polyglossia_languages with .lyx names
338 * please keep this in sync with polyglossia_languages line by line!
340 const char * const Preamble::coded_polyglossia_languages[] = {
341 "albanian", "american", "amharic", "ancientgreek", "arabic_arabi", "armenian", "asturian", "australian",
342 "bahasa", "bahasam", "basque", "bengali", "brazilian", "brazilian", "breton", "british", "bulgarian",
343 "catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
344 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
345 "galician", "greek", "greek", "hebrew", "hindi",
346 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean",
347 "lao", "latin", "latvian", "lithuanian", "lowersorbian", "magyar", "malayalam", "marathi",
348 "naustrian","newzealand", "ngerman", "norsk", "nynorsk", "occitan", "oldrussian",
349 "piedmontese", "polish", "polutonikogreek", "portuges", "romanian", "romansh", "russian",
350 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovene", "spanish", "swedish", "syriac",
351 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
352 "ukrainian", "urdu", "uppersorbian", "vietnamese", "welsh", 0};
353 // not yet supported by LyX: "korean-polyglossia", "nko"
356 bool Preamble::usePolyglossia() const
358 return h_use_non_tex_fonts && h_language_package == "default";
362 bool Preamble::indentParagraphs() const
364 return h_paragraph_separation == "indent";
368 bool Preamble::isPackageUsed(string const & package) const
370 return used_packages.find(package) != used_packages.end();
374 bool Preamble::isPackageAutoLoaded(string const & package) const
376 return auto_packages.find(package) != auto_packages.end();
380 vector<string> Preamble::getPackageOptions(string const & package) const
382 map<string, vector<string> >::const_iterator it = used_packages.find(package);
383 if (it != used_packages.end())
385 return vector<string>();
389 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
391 auto_packages.insert(package);
395 void Preamble::addModule(string const & module)
397 for (auto const & m : used_modules) {
401 used_modules.push_back(module);
405 void Preamble::suppressDate(bool suppress)
408 h_suppress_date = "true";
410 h_suppress_date = "false";
414 void Preamble::registerAuthor(std::string const & name, string const & initials)
416 Author author(from_utf8(name), empty_docstring(), from_utf8(initials));
417 author.setUsed(true);
418 authors_.record(author);
419 h_tracking_changes = "true";
420 h_output_changes = "true";
424 Author const & Preamble::getAuthor(std::string const & name) const
426 Author author(from_utf8(name), empty_docstring(), empty_docstring());
427 for (AuthorList::Authors::const_iterator it = authors_.begin();
428 it != authors_.end(); ++it)
431 static Author const dummy;
436 int Preamble::getSpecialTableColumnArguments(char c) const
438 map<char, int>::const_iterator it = special_columns_.find(c);
439 if (it == special_columns_.end())
445 void Preamble::add_package(string const & name, vector<string> & options)
447 // every package inherits the global options
448 if (used_packages.find(name) == used_packages.end())
449 used_packages[name] = split_options(h_options);
451 // Insert options passed via PassOptionsToPackage
452 for (auto const & p : extra_package_options_) {
453 if (p.first == name) {
454 vector<string> eo = getVectorFromString(p.second);
455 for (auto const & eoi : eo)
456 options.push_back(eoi);
460 vector<string> & v = used_packages[name];
461 v.insert(v.end(), options.begin(), options.end());
462 if (name == "jurabib") {
463 // Don't output the order argument (see the cite command
464 // handling code in text.cpp).
465 vector<string>::iterator end =
466 remove(options.begin(), options.end(), "natbiborder");
467 end = remove(options.begin(), end, "jurabiborder");
468 options.erase(end, options.end());
475 // Given is a string like "scaled=0.9" or "scale=0.9", return 0.9 * 100
476 bool scale_as_percentage(string const & scale, string & percentage)
478 if (contains(scale, '=')) {
479 string const value = support::split(scale, '=');
480 if (isStrDbl(value)) {
481 percentage = convert<string>(
482 static_cast<int>(100 * convert<double>(value)));
490 string remove_braces(string const & value)
494 if (value[0] == '{' && value[value.length()-1] == '}')
495 return value.substr(1, value.length()-2);
499 } // anonymous namespace
502 Preamble::Preamble() : one_language(true), explicit_babel(false),
503 title_layout_found(false), index_number(0), h_font_cjk_set(false)
507 h_biblio_style = "plain";
508 h_bibtex_command = "default";
509 h_cite_engine = "basic";
510 h_cite_engine_type = "default";
512 h_defskip = "medskip";
513 h_dynamic_quotes = false;
516 h_fontencoding = "default";
517 h_font_roman[0] = "default";
518 h_font_roman[1] = "default";
519 h_font_sans[0] = "default";
520 h_font_sans[1] = "default";
521 h_font_typewriter[0] = "default";
522 h_font_typewriter[1] = "default";
523 h_font_math[0] = "auto";
524 h_font_math[1] = "auto";
525 h_font_default_family = "default";
526 h_use_non_tex_fonts = false;
528 h_font_roman_osf = "false";
529 h_font_sans_osf = "false";
530 h_font_typewriter_osf = "false";
531 h_font_sf_scale[0] = "100";
532 h_font_sf_scale[1] = "100";
533 h_font_tt_scale[0] = "100";
534 h_font_tt_scale[1] = "100";
535 // h_font_roman_opts;
537 // h_font_typewriter_opts;
539 h_is_mathindent = "0";
540 h_math_numbering_side = "default";
541 h_graphics = "default";
542 h_default_output_format = "default";
543 h_html_be_strict = "false";
544 h_html_css_as_file = "0";
545 h_html_math_output = "0";
546 h_docbook_table_output = "0";
547 h_index[0] = "Index";
548 h_index_command = "default";
549 h_inputencoding = "auto-legacy";
550 h_justification = "true";
551 h_language = "english";
552 h_language_package = "none";
554 h_maintain_unincluded_children = "no";
558 h_output_changes = "false";
559 h_change_bars = "false";
561 //h_output_sync_macro
562 h_papercolumns = "1";
563 h_paperfontsize = "default";
564 h_paperorientation = "portrait";
565 h_paperpagestyle = "default";
567 h_papersize = "default";
568 h_paragraph_indentation = "default";
569 h_paragraph_separation = "indent";
574 h_pdf_bookmarks = "0";
575 h_pdf_bookmarksnumbered = "0";
576 h_pdf_bookmarksopen = "0";
577 h_pdf_bookmarksopenlevel = "1";
578 h_pdf_breaklinks = "0";
579 h_pdf_pdfborder = "0";
580 h_pdf_colorlinks = "0";
581 h_pdf_backref = "section";
582 h_pdf_pdfusetitle = "0";
584 //h_pdf_quoted_options;
585 h_quotes_style = "english";
587 h_shortcut[0] = "idx";
588 h_spacing = "single";
589 h_save_transient_properties = "true";
590 h_suppress_date = "false";
591 h_textclass = "article";
593 h_tracking_changes = "false";
594 h_use_bibtopic = "false";
595 h_use_dash_ligatures = "true";
596 h_use_indices = "false";
597 h_use_geometry = "false";
598 h_use_default_options = "false";
599 h_use_hyperref = "false";
600 h_use_microtype = "false";
601 h_use_lineno = "false";
602 h_use_refstyle = false;
603 h_use_minted = false;
604 h_use_packages["amsmath"] = "1";
605 h_use_packages["amssymb"] = "0";
606 h_use_packages["cancel"] = "0";
607 h_use_packages["esint"] = "1";
608 h_use_packages["mhchem"] = "0";
609 h_use_packages["mathdots"] = "0";
610 h_use_packages["mathtools"] = "0";
611 h_use_packages["stackrel"] = "0";
612 h_use_packages["stmaryrd"] = "0";
613 h_use_packages["undertilde"] = "0";
617 void Preamble::handle_hyperref(vector<string> & options)
619 // FIXME swallow inputencoding changes that might surround the
620 // hyperref setup if it was written by LyX
621 h_use_hyperref = "true";
622 // swallow "unicode=true", since LyX does always write that
623 vector<string>::iterator it =
624 find(options.begin(), options.end(), "unicode=true");
625 if (it != options.end())
627 it = find(options.begin(), options.end(), "pdfusetitle");
628 if (it != options.end()) {
629 h_pdf_pdfusetitle = "1";
632 string bookmarks = process_keyval_opt(options, "bookmarks");
633 if (bookmarks == "true")
634 h_pdf_bookmarks = "1";
635 else if (bookmarks == "false")
636 h_pdf_bookmarks = "0";
637 if (h_pdf_bookmarks == "1") {
638 string bookmarksnumbered =
639 process_keyval_opt(options, "bookmarksnumbered");
640 if (bookmarksnumbered == "true")
641 h_pdf_bookmarksnumbered = "1";
642 else if (bookmarksnumbered == "false")
643 h_pdf_bookmarksnumbered = "0";
644 string bookmarksopen =
645 process_keyval_opt(options, "bookmarksopen");
646 if (bookmarksopen == "true")
647 h_pdf_bookmarksopen = "1";
648 else if (bookmarksopen == "false")
649 h_pdf_bookmarksopen = "0";
650 if (h_pdf_bookmarksopen == "1") {
651 string bookmarksopenlevel =
652 process_keyval_opt(options, "bookmarksopenlevel");
653 if (!bookmarksopenlevel.empty())
654 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
657 string breaklinks = process_keyval_opt(options, "breaklinks");
658 if (breaklinks == "true")
659 h_pdf_breaklinks = "1";
660 else if (breaklinks == "false")
661 h_pdf_breaklinks = "0";
662 string pdfborder = process_keyval_opt(options, "pdfborder");
663 if (pdfborder == "{0 0 0}")
664 h_pdf_pdfborder = "1";
665 else if (pdfborder == "{0 0 1}")
666 h_pdf_pdfborder = "0";
667 string backref = process_keyval_opt(options, "backref");
668 if (!backref.empty())
669 h_pdf_backref = backref;
670 string colorlinks = process_keyval_opt(options, "colorlinks");
671 if (colorlinks == "true")
672 h_pdf_colorlinks = "1";
673 else if (colorlinks == "false")
674 h_pdf_colorlinks = "0";
675 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
676 if (!pdfpagemode.empty())
677 h_pdf_pagemode = pdfpagemode;
678 string pdftitle = process_keyval_opt(options, "pdftitle");
679 if (!pdftitle.empty()) {
680 h_pdf_title = remove_braces(pdftitle);
682 string pdfauthor = process_keyval_opt(options, "pdfauthor");
683 if (!pdfauthor.empty()) {
684 h_pdf_author = remove_braces(pdfauthor);
686 string pdfsubject = process_keyval_opt(options, "pdfsubject");
687 if (!pdfsubject.empty())
688 h_pdf_subject = remove_braces(pdfsubject);
689 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
690 if (!pdfkeywords.empty())
691 h_pdf_keywords = remove_braces(pdfkeywords);
692 if (!options.empty()) {
693 if (!h_pdf_quoted_options.empty())
694 h_pdf_quoted_options += ',';
695 h_pdf_quoted_options += join(options, ",");
701 void Preamble::handle_geometry(vector<string> & options)
703 h_use_geometry = "true";
704 vector<string>::iterator it;
706 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
707 h_paperorientation = "landscape";
711 // keyval version: "paper=letter" or "paper=letterpaper"
712 string paper = process_keyval_opt(options, "paper");
714 if (suffixIs(paper, "paper"))
715 paper = subst(paper, "paper", "");
716 // alternative version: "letterpaper"
717 handle_opt(options, known_latex_paper_sizes, paper);
718 if (suffixIs(paper, "paper"))
719 paper = subst(paper, "paper", "");
720 delete_opt(options, known_latex_paper_sizes);
724 char const * const * margin = known_paper_margins;
725 for (; *margin; ++margin) {
726 string value = process_keyval_opt(options, *margin);
727 if (!value.empty()) {
728 int k = margin - known_paper_margins;
729 string name = known_coded_paper_margins[k];
730 h_margins += '\\' + name + ' ' + value + '\n';
736 void Preamble::handle_package(Parser &p, string const & name,
737 string const & opts, bool in_lyx_preamble,
740 vector<string> options = split_options(opts);
741 add_package(name, options);
743 if (is_known(name, known_xetex_packages)) {
745 h_use_non_tex_fonts = true;
746 registerAutomaticallyLoadedPackage("fontspec");
747 if (h_inputencoding == "auto-legacy")
748 p.setEncoding("UTF-8");
751 // vector of all options for easier parsing and
753 vector<string> allopts = getVectorFromString(opts);
754 // this stores the potential extra options
761 // By default, we use the package name as LyX font name,
762 // so this only needs to be reset if these names differ
763 if (is_known(name, known_roman_font_packages))
764 h_font_roman[0] = name;
766 if (name == "ccfonts") {
767 for (auto const & opt : allopts) {
773 h_font_roman_opts = xopts;
777 if (name == "lmodern") {
778 for (auto const & opt : allopts) {
784 h_font_roman_opts = xopts;
788 if (name == "fourier") {
789 h_font_roman[0] = "utopia";
790 for (auto const & opt : allopts) {
792 h_font_roman_osf = "true";
795 if (opt == "expert") {
804 h_font_roman_opts = xopts;
808 if (name == "garamondx") {
809 for (auto const & opt : allopts) {
811 h_font_roman_osf = "true";
819 h_font_roman_opts = xopts;
823 if (name == "libertine") {
824 // this automatically invokes biolinum
825 h_font_sans[0] = "biolinum";
826 // as well as libertineMono
827 h_font_typewriter[0] = "libertine-mono";
828 for (auto const & opt : allopts) {
830 h_font_roman_osf = "true";
833 if (opt == "lining") {
834 h_font_roman_osf = "false";
842 h_font_roman_opts = xopts;
846 if (name == "libertineRoman" || name == "libertine-type1") {
847 h_font_roman[0] = "libertine";
848 // NOTE: contrary to libertine.sty, libertineRoman
849 // and libertine-type1 do not automatically invoke
850 // biolinum and libertineMono
851 if (opts == "lining")
852 h_font_roman_osf = "false";
853 else if (opts == "osf")
854 h_font_roman_osf = "true";
857 if (name == "libertinus" || name == "libertinus-type1") {
864 for (auto const & opt : allopts) {
865 if (opt == "rm" || opt == "serif") {
870 if (opt == "sf" || opt == "sans") {
875 if (opt == "tt=false" || opt == "mono=false") {
883 if (opt == "scaleSF") {
887 if (opt == "scaleTT") {
891 if (opt == "lining") {
892 h_font_roman_osf = "false";
900 h_font_roman[0] = "libertinus";
902 h_font_roman_osf = "true";
904 h_font_roman_osf = "false";
907 h_font_sans[0] = "LibertinusSans-LF";
909 h_font_sans_osf = "true";
911 h_font_sans_osf = "false";
912 if (!scalesf.empty())
913 scale_as_percentage(scalesf, h_font_sf_scale[0]);
916 h_font_typewriter[0] = "LibertinusMono-TLF";
917 if (!scalett.empty())
918 scale_as_percentage(scalett, h_font_tt_scale[0]);
921 h_font_roman_opts = xopts;
925 if (name == "MinionPro") {
926 h_font_roman[0] = "minionpro";
927 h_font_roman_osf = "true";
928 h_font_math[0] = "auto";
929 for (auto const & opt : allopts) {
931 h_font_roman_osf = "false";
934 if (opt == "onlytext") {
935 h_font_math[0] = "default";
943 h_font_roman_opts = xopts;
947 if (name == "mathdesign") {
948 for (auto const & opt : allopts) {
949 if (opt == "charter") {
950 h_font_roman[0] = "md-charter";
953 if (opt == "garamond") {
954 h_font_roman[0] = "md-garamond";
957 if (opt == "utopia") {
958 h_font_roman[0] = "md-utopia";
961 if (opt == "expert") {
963 h_font_roman_osf = "true";
969 else if (name == "mathpazo") {
970 h_font_roman[0] = "palatino";
971 for (auto const & opt : allopts) {
973 h_font_roman_osf = "true";
985 h_font_roman_opts = xopts;
989 else if (name == "mathptmx") {
990 h_font_roman[0] = "times";
991 for (auto const & opt : allopts) {
997 h_font_roman_opts = xopts;
1001 if (name == "crimson")
1002 h_font_roman[0] = "cochineal";
1004 if (name == "cochineal") {
1005 for (auto const & opt : allopts) {
1006 if (opt == "osf" || opt == "oldstyle") {
1007 h_font_roman_osf = "true";
1010 if (opt == "proportional" || opt == "p")
1017 h_font_roman_opts = xopts;
1021 if (name == "CrimsonPro") {
1022 h_font_roman_osf = "true";
1023 for (auto const & opt : allopts) {
1024 if (opt == "lf" || opt == "lining") {
1025 h_font_roman_osf = "false";
1028 if (opt == "proportional" || opt == "p")
1030 if (opt == "medium") {
1031 h_font_roman[0] = "CrimsonProMedium";
1034 if (opt == "extralight") {
1035 h_font_roman[0] = "CrimsonProExtraLight";
1038 if (opt == "light") {
1039 h_font_roman[0] = "CrimsonProLight";
1047 h_font_roman_opts = xopts;
1053 // font uses old-style figure
1054 h_font_roman_osf = "true";
1056 if (name == "paratype") {
1057 // in this case all fonts are ParaType
1058 h_font_roman[0] = "PTSerif-TLF";
1059 h_font_sans[0] = "default";
1060 h_font_typewriter[0] = "default";
1063 if (name == "PTSerif")
1064 h_font_roman[0] = "PTSerif-TLF";
1066 if (name == "XCharter") {
1067 h_font_roman[0] = "xcharter";
1068 for (auto const & opt : allopts) {
1070 h_font_roman_osf = "true";
1078 h_font_roman_opts = xopts;
1082 if (name == "plex-serif") {
1083 h_font_roman[0] = "IBMPlexSerif";
1084 for (auto const & opt : allopts) {
1085 if (opt == "thin") {
1086 h_font_roman[0] = "IBMPlexSerifThin";
1089 if (opt == "extralight") {
1090 h_font_roman[0] = "IBMPlexSerifExtraLight";
1093 if (opt == "light") {
1094 h_font_roman[0] = "IBMPlexSerifLight";
1102 h_font_roman_opts = xopts;
1106 if (name == "noto-serif" || name == "noto") {
1113 bool extralight = false;
1115 bool medium = false;
1118 if (name == "noto") {
1123 // Since the options might apply to different shapes,
1124 // we need to parse all options first and then handle them.
1125 for (auto const & opt : allopts) {
1126 if (opt == "regular")
1136 if (opt == "thin") {
1140 if (opt == "extralight") {
1144 if (opt == "light") {
1148 if (opt == "medium") {
1159 if (opt == "nott") {
1167 if (prefixIs(opt, "scaled=")) {
1176 // handle options that might affect different shapes
1177 if (name == "noto-serif" || rm) {
1179 h_font_roman[0] = "NotoSerifThin";
1180 else if (extralight)
1181 h_font_roman[0] = "NotoSerifExtralight";
1183 h_font_roman[0] = "NotoSerifLight";
1185 h_font_roman[0] = "NotoSerifMedium";
1187 h_font_roman[0] = "NotoSerifRegular";
1189 h_font_roman_osf = "true";
1191 h_font_roman_opts = xopts;
1193 if (name == "noto" && sf) {
1195 h_font_sans[0] = "NotoSansThin";
1196 else if (extralight)
1197 h_font_sans[0] = "NotoSansExtralight";
1199 h_font_sans[0] = "NotoSansLight";
1201 h_font_sans[0] = "NotoSansMedium";
1203 h_font_sans[0] = "NotoSansRegular";
1205 h_font_sans_osf = "true";
1207 scale_as_percentage(scl, h_font_sf_scale[0]);
1209 h_font_sans_opts = xopts;
1211 if (name == "noto" && tt) {
1212 h_font_typewriter[0] = "NotoMonoRegular";
1214 h_font_typewriter_osf = "true";
1216 scale_as_percentage(scl, h_font_tt_scale[0]);
1218 h_font_typewriter_opts = xopts;
1222 if (name == "sourceserifpro") {
1223 h_font_roman[0] = "ADOBESourceSerifPro";
1224 for (auto const & opt : allopts) {
1226 h_font_roman_osf = "true";
1234 h_font_roman_opts = xopts;
1242 // By default, we use the package name as LyX font name,
1243 // so this only needs to be reset if these names differ.
1244 // Also, we handle the scaling option here generally.
1245 if (is_known(name, known_sans_font_packages)) {
1246 h_font_sans[0] = name;
1247 if (contains(opts, "scale")) {
1248 vector<string>::iterator it = allopts.begin();
1249 for (; it != allopts.end() ; ++it) {
1250 string const opt = *it;
1251 if (prefixIs(opt, "scaled=") || prefixIs(opt, "scale=")) {
1252 if (scale_as_percentage(opt, h_font_sf_scale[0])) {
1261 if (name == "biolinum" || name == "biolinum-type1") {
1262 h_font_sans[0] = "biolinum";
1263 for (auto const & opt : allopts) {
1264 if (prefixIs(opt, "osf")) {
1265 h_font_sans_osf = "true";
1273 h_font_sans_opts = xopts;
1277 if (name == "cantarell") {
1278 for (auto const & opt : allopts) {
1279 if (opt == "defaultsans")
1281 if (prefixIs(opt, "oldstyle")) {
1282 h_font_sans_osf = "true";
1290 h_font_sans_opts = xopts;
1294 if (name == "Chivo") {
1295 for (auto const & opt : allopts) {
1296 if (opt == "thin") {
1297 h_font_roman[0] = "ChivoThin";
1300 if (opt == "light") {
1301 h_font_roman[0] = "ChivoLight";
1304 if (opt == "regular") {
1305 h_font_roman[0] = "Chivo";
1308 if (opt == "medium") {
1309 h_font_roman[0] = "ChivoMedium";
1312 if (prefixIs(opt, "oldstyle")) {
1313 h_font_sans_osf = "true";
1321 h_font_sans_opts = xopts;
1325 if (name == "PTSans") {
1326 h_font_sans[0] = "PTSans-TLF";
1329 if (name == "FiraSans") {
1330 h_font_sans_osf = "true";
1331 for (auto const & opt : allopts) {
1332 if (opt == "book") {
1333 h_font_sans[0] = "FiraSansBook";
1336 if (opt == "thin") {
1339 if (opt == "extralight") {
1340 h_font_sans[0] = "FiraSansExtralight";
1343 if (opt == "light") {
1344 h_font_sans[0] = "FiraSansLight";
1347 if (opt == "ultralight") {
1348 h_font_sans[0] = "FiraSansUltralight";
1351 if (opt == "thin") {
1352 h_font_sans[0] = "FiraSansThin";
1355 if (opt == "lf" || opt == "lining") {
1356 h_font_sans_osf = "false";
1364 h_font_sans_opts = xopts;
1368 if (name == "plex-sans") {
1369 h_font_sans[0] = "IBMPlexSans";
1370 for (auto const & opt : allopts) {
1371 if (opt == "condensed") {
1372 h_font_sans[0] = "IBMPlexSansCondensed";
1375 if (opt == "thin") {
1376 h_font_sans[0] = "IBMPlexSansThin";
1379 if (opt == "extralight") {
1380 h_font_sans[0] = "IBMPlexSansExtraLight";
1383 if (opt == "light") {
1384 h_font_sans[0] = "IBMPlexSansLight";
1392 h_font_sans_opts = xopts;
1396 if (name == "noto-sans") {
1397 h_font_sans[0] = "NotoSansRegular";
1398 for (auto const & opt : allopts) {
1399 if (opt == "regular")
1401 if (opt == "medium") {
1402 h_font_sans[0] = "NotoSansMedium";
1405 if (opt == "thin") {
1406 h_font_sans[0] = "NotoSansThin";
1409 if (opt == "extralight") {
1410 h_font_sans[0] = "NotoSansExtralight";
1413 if (opt == "light") {
1414 h_font_sans[0] = "NotoSansLight";
1418 h_font_sans_osf = "true";
1426 h_font_sans_opts = xopts;
1430 if (name == "sourcesanspro") {
1431 h_font_sans[0] = "ADOBESourceSansPro";
1432 for (auto const & opt : allopts) {
1434 h_font_sans_osf = "true";
1442 h_font_sans_opts = xopts;
1450 // By default, we use the package name as LyX font name,
1451 // so this only needs to be reset if these names differ.
1452 // Also, we handle the scaling option here generally.
1453 if (is_known(name, known_typewriter_font_packages)) {
1454 h_font_typewriter[0] = name;
1455 if (contains(opts, "scale")) {
1456 vector<string>::iterator it = allopts.begin();
1457 for (; it != allopts.end() ; ++it) {
1458 string const opt = *it;
1459 if (prefixIs(opt, "scaled=") || prefixIs(opt, "scale=")) {
1460 if (scale_as_percentage(opt, h_font_tt_scale[0])) {
1469 if (name == "libertineMono" || name == "libertineMono-type1")
1470 h_font_typewriter[0] = "libertine-mono";
1472 if (name == "FiraMono") {
1473 h_font_typewriter_osf = "true";
1474 for (auto const & opt : allopts) {
1475 if (opt == "lf" || opt == "lining") {
1476 h_font_typewriter_osf = "false";
1484 h_font_typewriter_opts = xopts;
1488 if (name == "PTMono")
1489 h_font_typewriter[0] = "PTMono-TLF";
1491 if (name == "plex-mono") {
1492 h_font_typewriter[0] = "IBMPlexMono";
1493 for (auto const & opt : allopts) {
1494 if (opt == "thin") {
1495 h_font_typewriter[0] = "IBMPlexMonoThin";
1498 if (opt == "extralight") {
1499 h_font_typewriter[0] = "IBMPlexMonoExtraLight";
1502 if (opt == "light") {
1503 h_font_typewriter[0] = "IBMPlexMonoLight";
1511 h_font_typewriter_opts = xopts;
1515 if (name == "noto-mono") {
1516 h_font_typewriter[0] = "NotoMonoRegular";
1517 for (auto const & opt : allopts) {
1518 if (opt == "regular")
1525 h_font_typewriter_opts = xopts;
1529 if (name == "sourcecodepro") {
1530 h_font_typewriter[0] = "ADOBESourceCodePro";
1531 for (auto const & opt : allopts) {
1533 h_font_typewriter_osf = "true";
1541 h_font_typewriter_opts = xopts;
1549 // By default, we use the package name as LyX font name,
1550 // so this only needs to be reset if these names differ.
1551 if (is_known(name, known_math_font_packages))
1552 h_font_math[0] = name;
1554 if (name == "newtxmath") {
1556 h_font_math[0] = "newtxmath";
1557 else if (opts == "garamondx")
1558 h_font_math[0] = "garamondx-ntxm";
1559 else if (opts == "libertine")
1560 h_font_math[0] = "libertine-ntxm";
1561 else if (opts == "minion")
1562 h_font_math[0] = "minion-ntxm";
1563 else if (opts == "cochineal")
1564 h_font_math[0] = "cochineal-ntxm";
1567 if (name == "iwona")
1569 h_font_math[0] = "iwona-math";
1571 if (name == "kurier")
1573 h_font_math[0] = "kurier-math";
1575 // after the detection and handling of special cases, we can remove the
1576 // fonts, otherwise they would appear in the preamble, see bug #7856
1577 if (is_known(name, known_roman_font_packages) || is_known(name, known_sans_font_packages)
1578 || is_known(name, known_typewriter_font_packages) || is_known(name, known_math_font_packages))
1580 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
1581 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
1582 name == "esint" || name == "mhchem" || name == "mathdots" ||
1583 name == "mathtools" || name == "stackrel" ||
1584 name == "stmaryrd" || name == "undertilde") {
1585 h_use_packages[name] = "2";
1586 registerAutomaticallyLoadedPackage(name);
1589 else if (name == "babel") {
1590 h_language_package = "default";
1591 // One might think we would have to do nothing if babel is loaded
1592 // without any options to prevent pollution of the preamble with this
1593 // babel call in every roundtrip.
1594 // But the user could have defined babel-specific things afterwards. So
1595 // we need to keep it in the preamble to prevent cases like bug #7861.
1596 if (!opts.empty()) {
1597 // check if more than one option was used - used later for inputenc
1598 if (options.begin() != options.end() - 1)
1599 one_language = false;
1600 // babel takes the last language of the option of its \usepackage
1601 // call as document language. If there is no such language option, the
1602 // last language in the documentclass options is used.
1603 handle_opt(options, known_languages, h_language);
1604 // translate the babel name to a LyX name
1605 h_language = babel2lyx(h_language);
1606 if (h_language == "japanese") {
1607 // For Japanese, the encoding isn't indicated in the source
1608 // file, and there's really not much we can do. We could
1609 // 1) offer a list of possible encodings to choose from, or
1610 // 2) determine the encoding of the file by inspecting it.
1611 // For the time being, we leave the encoding alone so that
1612 // we don't get iconv errors when making a wrong guess, and
1613 // we will output a note at the top of the document
1614 // explaining what to do.
1615 Encoding const * const enc = encodings.fromIconvName(
1616 p.getEncoding(), Encoding::japanese, false);
1618 h_inputencoding = enc->name();
1619 is_nonCJKJapanese = true;
1620 // in this case babel can be removed from the preamble
1621 registerAutomaticallyLoadedPackage("babel");
1623 // If babel is called with options, LyX puts them by default into the
1624 // document class options. This works for most languages, except
1625 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
1626 // perhaps in future others.
1627 // Therefore keep the babel call as it is as the user might have
1629 string const babelcall = "\\usepackage[" + opts + "]{babel}\n";
1630 if (!contains(h_preamble.str(), babelcall))
1631 h_preamble << babelcall;
1633 delete_opt(options, known_languages);
1635 if (!contains(h_preamble.str(), "\\usepackage{babel}\n"))
1636 h_preamble << "\\usepackage{babel}\n";
1637 explicit_babel = true;
1641 else if (name == "polyglossia") {
1642 h_language_package = "default";
1643 h_default_output_format = "pdf4";
1644 h_use_non_tex_fonts = true;
1646 registerAutomaticallyLoadedPackage("xunicode");
1647 if (h_inputencoding == "auto-legacy")
1648 p.setEncoding("UTF-8");
1651 else if (name == "CJK") {
1652 // set the encoding to "auto-legacy" because it might be set to "auto-legacy-plain" by the babel handling
1653 // and this would not be correct for CJK
1654 if (h_inputencoding == "auto-legacy-plain")
1655 h_inputencoding = "auto-legacy";
1656 registerAutomaticallyLoadedPackage("CJK");
1659 else if (name == "CJKutf8") {
1660 h_inputencoding = "utf8-cjk";
1661 p.setEncoding("UTF-8");
1662 registerAutomaticallyLoadedPackage("CJKutf8");
1665 else if (name == "fontenc") {
1666 h_fontencoding = getStringFromVector(options, ",");
1670 else if (name == "inputenc" || name == "luainputenc") {
1671 // h_inputencoding is only set when there is not more than one
1672 // inputenc option because otherwise h_inputencoding must be
1673 // set to "auto-legacy" (the default encodings of the document's languages)
1674 // Therefore check that exactly one option is passed to inputenc.
1675 // It is also only set when there is not more than one babel
1677 if (!options.empty()) {
1678 string const encoding = options.back();
1679 Encoding const * const enc = encodings.fromLaTeXName(
1680 encoding, Encoding::inputenc, true);
1682 if (!detectEncoding)
1683 cerr << "Unknown encoding " << encoding
1684 << ". Ignoring." << std::endl;
1686 if (!enc->unsafe() && options.size() == 1 && one_language == true)
1687 h_inputencoding = enc->name();
1688 p.setEncoding(enc->iconvName());
1694 else if (name == "srcltx") {
1695 h_output_sync = "1";
1696 if (!opts.empty()) {
1697 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
1700 h_output_sync_macro = "\\usepackage{srcltx}";
1703 else if (is_known(name, known_old_language_packages)) {
1704 // known language packages from the times before babel
1705 // if they are found and not also babel, they will be used as
1706 // custom language package
1707 h_language_package = "\\usepackage{" + name + "}";
1710 else if (name == "lyxskak") {
1711 // ignore this and its options
1712 const char * const o[] = {"ps", "mover", 0};
1713 delete_opt(options, o);
1716 else if (name == "parskip" && options.size() < 2 && (opts.empty() || prefixIs(opts, "skip="))) {
1718 h_paragraph_separation = "halfline";
1720 if (opts == "skip=\\smallskipamount")
1721 h_defskip = "smallskip";
1722 else if (opts == "skip=\\medskipamount")
1723 h_defskip = "medskip";
1724 else if (opts == "skip=\\bigskipamount")
1725 h_defskip = "bigskip";
1726 else if (opts == "skip=\\baselineskip")
1727 h_defskip = "fullline";
1730 h_paragraph_separation = "skip";
1734 else if (is_known(name, known_lyx_packages) && options.empty()) {
1735 if (name == "splitidx")
1736 h_use_indices = "true";
1737 else if (name == "minted")
1738 h_use_minted = true;
1739 else if (name == "refstyle")
1740 h_use_refstyle = true;
1741 else if (name == "prettyref")
1742 h_use_refstyle = false;
1744 if (!in_lyx_preamble) {
1745 h_preamble << package_beg_sep << name
1746 << package_mid_sep << "\\usepackage{"
1748 if (p.next_token().cat() == catNewline ||
1749 (p.next_token().cat() == catSpace &&
1750 p.next_next_token().cat() == catNewline))
1752 h_preamble << package_end_sep;
1756 else if (name == "geometry")
1757 handle_geometry(options);
1759 else if (name == "subfig")
1760 ; // ignore this FIXME: Use the package separator mechanism instead
1762 else if (char const * const * where = is_known(name, known_languages))
1763 h_language = known_coded_languages[where - known_languages];
1765 else if (name == "natbib") {
1766 h_biblio_style = "plainnat";
1767 h_cite_engine = "natbib";
1768 h_cite_engine_type = "authoryear";
1769 vector<string>::iterator it =
1770 find(options.begin(), options.end(), "authoryear");
1771 if (it != options.end())
1774 it = find(options.begin(), options.end(), "numbers");
1775 if (it != options.end()) {
1776 h_cite_engine_type = "numerical";
1780 if (!options.empty())
1781 h_biblio_options = join(options, ",");
1784 else if (name == "biblatex") {
1785 h_biblio_style = "plainnat";
1786 h_cite_engine = "biblatex";
1787 h_cite_engine_type = "authoryear";
1789 vector<string>::iterator it =
1790 find(options.begin(), options.end(), "natbib");
1791 if (it != options.end()) {
1793 h_cite_engine = "biblatex-natbib";
1795 opt = process_keyval_opt(options, "natbib");
1797 h_cite_engine = "biblatex-natbib";
1799 opt = process_keyval_opt(options, "style");
1801 h_biblatex_citestyle = opt;
1802 h_biblatex_bibstyle = opt;
1804 opt = process_keyval_opt(options, "citestyle");
1806 h_biblatex_citestyle = opt;
1807 opt = process_keyval_opt(options, "bibstyle");
1809 h_biblatex_bibstyle = opt;
1811 opt = process_keyval_opt(options, "refsection");
1813 if (opt == "none" || opt == "part"
1814 || opt == "chapter" || opt == "section"
1815 || opt == "subsection")
1818 cerr << "Ignoring unknown refsection value '"
1821 opt = process_keyval_opt(options, "bibencoding");
1824 if (!options.empty()) {
1825 h_biblio_options = join(options, ",");
1830 else if (name == "jurabib") {
1831 h_biblio_style = "jurabib";
1832 h_cite_engine = "jurabib";
1833 h_cite_engine_type = "authoryear";
1834 if (!options.empty())
1835 h_biblio_options = join(options, ",");
1838 else if (name == "bibtopic")
1839 h_use_bibtopic = "true";
1841 else if (name == "chapterbib")
1842 h_multibib = "child";
1844 else if (name == "hyperref")
1845 handle_hyperref(options);
1847 else if (name == "algorithm2e") {
1848 // Load "algorithm2e" module
1849 addModule("algorithm2e");
1850 // Add the package options to the global document options
1851 if (!options.empty()) {
1852 if (h_options.empty())
1853 h_options = join(options, ",");
1855 h_options += ',' + join(options, ",");
1858 else if (name == "microtype") {
1859 //we internally support only microtype without params
1860 if (options.empty())
1861 h_use_microtype = "true";
1863 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1866 else if (name == "lineno") {
1867 h_use_lineno = "true";
1868 if (!options.empty()) {
1869 h_lineno_options = join(options, ",");
1874 else if (name == "changebar")
1875 h_output_changes = "true";
1877 else if (!in_lyx_preamble) {
1878 if (options.empty())
1879 h_preamble << "\\usepackage{" << name << '}';
1881 h_preamble << "\\usepackage[" << opts << "]{"
1885 if (p.next_token().cat() == catNewline ||
1886 (p.next_token().cat() == catSpace &&
1887 p.next_next_token().cat() == catNewline))
1891 // We need to do something with the options...
1892 if (!options.empty() && !detectEncoding)
1893 cerr << "Ignoring options '" << join(options, ",")
1894 << "' of package " << name << '.' << endl;
1896 // remove the whitespace
1901 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1904 Token t = p.get_token();
1905 if (t.cat() == catEscape &&
1906 is_known(t.cs(), known_if_commands))
1907 handle_if(p, in_lyx_preamble);
1909 if (!in_lyx_preamble)
1910 h_preamble << t.asInput();
1911 if (t.cat() == catEscape && t.cs() == "fi")
1918 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1920 if (contains(h_float_placement, "H"))
1921 registerAutomaticallyLoadedPackage("float");
1922 if (h_spacing != "single" && h_spacing != "default")
1923 registerAutomaticallyLoadedPackage("setspace");
1924 if (h_use_packages["amsmath"] == "2") {
1925 // amsbsy and amstext are already provided by amsmath
1926 registerAutomaticallyLoadedPackage("amsbsy");
1927 registerAutomaticallyLoadedPackage("amstext");
1930 // output the LyX file settings
1931 // Important: Keep the version formatting in sync with LyX and
1932 // lyx2lyx (bug 7951)
1933 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1934 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1935 << lyx_version_minor << '\n'
1936 << "\\lyxformat " << LYX_FORMAT << '\n'
1937 << "\\begin_document\n"
1938 << "\\begin_header\n"
1939 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1940 << "\\origin " << origin << "\n"
1941 << "\\textclass " << h_textclass << "\n";
1942 string const raw = subdoc ? empty_string() : h_preamble.str();
1944 os << "\\begin_preamble\n";
1945 for (string::size_type i = 0; i < raw.size(); ++i) {
1946 if (raw[i] == package_beg_sep) {
1947 // Here follows some package loading code that
1948 // must be skipped if the package is loaded
1950 string::size_type j = raw.find(package_mid_sep, i);
1951 if (j == string::npos)
1953 string::size_type k = raw.find(package_end_sep, j);
1954 if (k == string::npos)
1956 string const package = raw.substr(i + 1, j - i - 1);
1957 string const replacement = raw.substr(j + 1, k - j - 1);
1958 if (auto_packages.find(package) == auto_packages.end())
1964 os << "\n\\end_preamble\n";
1966 if (!h_options.empty())
1967 os << "\\options " << h_options << "\n";
1968 os << "\\use_default_options " << h_use_default_options << "\n";
1969 if (!used_modules.empty()) {
1970 os << "\\begin_modules\n";
1971 vector<string>::const_iterator const end = used_modules.end();
1972 vector<string>::const_iterator it = used_modules.begin();
1973 for (; it != end; ++it)
1975 os << "\\end_modules\n";
1977 if (!h_includeonlys.empty()) {
1978 os << "\\begin_includeonly\n";
1979 for (auto const & iofile : h_includeonlys)
1980 os << iofile << '\n';
1981 os << "\\end_includeonly\n";
1983 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1984 << "\\language " << h_language << "\n"
1985 << "\\language_package " << h_language_package << "\n"
1986 << "\\inputencoding " << h_inputencoding << "\n"
1987 << "\\fontencoding " << h_fontencoding << "\n"
1988 << "\\font_roman \"" << h_font_roman[0]
1989 << "\" \"" << h_font_roman[1] << "\"\n"
1990 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1991 << "\\font_typewriter \"" << h_font_typewriter[0]
1992 << "\" \"" << h_font_typewriter[1] << "\"\n"
1993 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1994 << "\\font_default_family " << h_font_default_family << "\n"
1995 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1996 << "\\font_sc " << h_font_sc << "\n"
1997 << "\\font_roman_osf " << h_font_roman_osf << "\n"
1998 << "\\font_sans_osf " << h_font_sans_osf << "\n"
1999 << "\\font_typewriter_osf " << h_font_typewriter_osf << "\n";
2000 if (!h_font_roman_opts.empty())
2001 os << "\\font_roman_opts \"" << h_font_roman_opts << "\"" << '\n';
2002 os << "\\font_sf_scale " << h_font_sf_scale[0]
2003 << ' ' << h_font_sf_scale[1] << '\n';
2004 if (!h_font_sans_opts.empty())
2005 os << "\\font_sans_opts \"" << h_font_sans_opts << "\"" << '\n';
2006 os << "\\font_tt_scale " << h_font_tt_scale[0]
2007 << ' ' << h_font_tt_scale[1] << '\n';
2008 if (!h_font_cjk.empty())
2009 os << "\\font_cjk " << h_font_cjk << '\n';
2010 if (!h_font_typewriter_opts.empty())
2011 os << "\\font_typewriter_opts \"" << h_font_typewriter_opts << "\"" << '\n';
2012 os << "\\use_microtype " << h_use_microtype << '\n'
2013 << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
2014 << "\\graphics " << h_graphics << '\n'
2015 << "\\default_output_format " << h_default_output_format << "\n"
2016 << "\\output_sync " << h_output_sync << "\n";
2017 if (h_output_sync == "1")
2018 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
2019 os << "\\bibtex_command " << h_bibtex_command << "\n"
2020 << "\\index_command " << h_index_command << "\n";
2021 if (!h_float_placement.empty())
2022 os << "\\float_placement " << h_float_placement << "\n";
2023 os << "\\paperfontsize " << h_paperfontsize << "\n"
2024 << "\\spacing " << h_spacing << "\n"
2025 << "\\use_hyperref " << h_use_hyperref << '\n';
2026 if (h_use_hyperref == "true") {
2027 if (!h_pdf_title.empty())
2028 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
2029 if (!h_pdf_author.empty())
2030 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
2031 if (!h_pdf_subject.empty())
2032 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
2033 if (!h_pdf_keywords.empty())
2034 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
2035 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
2036 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
2037 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
2038 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
2039 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
2040 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
2041 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
2042 "\\pdf_backref " << h_pdf_backref << "\n"
2043 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
2044 if (!h_pdf_pagemode.empty())
2045 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
2046 if (!h_pdf_quoted_options.empty())
2047 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
2049 os << "\\papersize " << h_papersize << "\n"
2050 << "\\use_geometry " << h_use_geometry << '\n';
2051 for (map<string, string>::const_iterator it = h_use_packages.begin();
2052 it != h_use_packages.end(); ++it)
2053 os << "\\use_package " << it->first << ' ' << it->second << '\n';
2054 os << "\\cite_engine " << h_cite_engine << '\n'
2055 << "\\cite_engine_type " << h_cite_engine_type << '\n'
2056 << "\\biblio_style " << h_biblio_style << "\n"
2057 << "\\use_bibtopic " << h_use_bibtopic << "\n";
2058 if (!h_biblio_options.empty())
2059 os << "\\biblio_options " << h_biblio_options << "\n";
2060 if (!h_biblatex_bibstyle.empty())
2061 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
2062 if (!h_biblatex_citestyle.empty())
2063 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
2064 if (!h_multibib.empty())
2065 os << "\\multibib " << h_multibib << "\n";
2066 os << "\\use_indices " << h_use_indices << "\n"
2067 << "\\paperorientation " << h_paperorientation << '\n'
2068 << "\\suppress_date " << h_suppress_date << '\n'
2069 << "\\justification " << h_justification << '\n'
2070 << "\\use_refstyle " << h_use_refstyle << '\n'
2071 << "\\use_minted " << h_use_minted << '\n'
2072 << "\\use_lineno " << h_use_lineno << '\n';
2073 if (!h_lineno_options.empty())
2074 os << "\\lineno_options " << h_lineno_options << '\n';
2075 if (!h_fontcolor.empty())
2076 os << "\\fontcolor " << h_fontcolor << '\n';
2077 if (!h_notefontcolor.empty())
2078 os << "\\notefontcolor " << h_notefontcolor << '\n';
2079 if (!h_backgroundcolor.empty())
2080 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
2081 if (!h_boxbgcolor.empty())
2082 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
2083 if (index_number != 0)
2084 for (int i = 0; i < index_number; i++) {
2085 os << "\\index " << h_index[i] << '\n'
2086 << "\\shortcut " << h_shortcut[i] << '\n'
2087 << "\\color " << h_color << '\n'
2091 os << "\\index " << h_index[0] << '\n'
2092 << "\\shortcut " << h_shortcut[0] << '\n'
2093 << "\\color " << h_color << '\n'
2097 << "\\secnumdepth " << h_secnumdepth << "\n"
2098 << "\\tocdepth " << h_tocdepth << "\n"
2099 << "\\paragraph_separation " << h_paragraph_separation << "\n";
2100 if (h_paragraph_separation == "skip")
2101 os << "\\defskip " << h_defskip << "\n";
2103 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
2104 os << "\\is_math_indent " << h_is_mathindent << "\n";
2105 if (!h_mathindentation.empty())
2106 os << "\\math_indentation " << h_mathindentation << "\n";
2107 os << "\\math_numbering_side " << h_math_numbering_side << "\n";
2108 os << "\\quotes_style " << h_quotes_style << "\n"
2109 << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
2110 << "\\papercolumns " << h_papercolumns << "\n"
2111 << "\\papersides " << h_papersides << "\n"
2112 << "\\paperpagestyle " << h_paperpagestyle << "\n";
2113 if (!h_listings_params.empty())
2114 os << "\\listings_params " << h_listings_params << "\n";
2115 os << "\\tracking_changes " << h_tracking_changes << "\n"
2116 << "\\output_changes " << h_output_changes << "\n"
2117 << "\\change_bars " << h_change_bars << "\n"
2118 << "\\html_math_output " << h_html_math_output << "\n"
2119 << "\\html_css_as_file " << h_html_css_as_file << "\n"
2120 << "\\html_be_strict " << h_html_be_strict << "\n"
2121 << "\\docbook_table_output " << h_docbook_table_output << "\n"
2123 << "\\end_header\n\n"
2124 << "\\begin_body\n";
2129 void Preamble::parse(Parser & p, string const & forceclass,
2130 TeX2LyXDocClass & tc)
2132 // initialize fixed types
2133 special_columns_['D'] = 3;
2134 parse(p, forceclass, false, tc);
2138 void Preamble::parse(Parser & p, string const & forceclass,
2139 bool detectEncoding, TeX2LyXDocClass & tc)
2141 bool is_full_document = false;
2142 bool is_lyx_file = false;
2143 bool in_lyx_preamble = false;
2145 // determine whether this is a full document or a fragment for inclusion
2147 Token const & t = p.get_token();
2149 if (t.cat() == catEscape && t.cs() == "documentclass") {
2150 is_full_document = true;
2156 if (detectEncoding && !is_full_document)
2159 while (is_full_document && p.good()) {
2160 if (detectEncoding && h_inputencoding != "auto-legacy" &&
2161 h_inputencoding != "auto-legacy-plain")
2164 Token const & t = p.get_token();
2167 if (!detectEncoding)
2168 cerr << "t: " << t << '\n';
2174 if (!in_lyx_preamble &&
2175 (t.cat() == catLetter ||
2176 t.cat() == catSuper ||
2177 t.cat() == catSub ||
2178 t.cat() == catOther ||
2179 t.cat() == catMath ||
2180 t.cat() == catActive ||
2181 t.cat() == catBegin ||
2182 t.cat() == catEnd ||
2183 t.cat() == catAlign ||
2184 t.cat() == catParameter)) {
2185 h_preamble << t.cs();
2189 if (!in_lyx_preamble &&
2190 (t.cat() == catSpace || t.cat() == catNewline)) {
2191 h_preamble << t.asInput();
2195 if (t.cat() == catComment) {
2196 static regex const islyxfile("%% LyX .* created this file");
2197 static regex const usercommands("User specified LaTeX commands");
2199 string const comment = t.asInput();
2201 // magically switch encoding default if it looks like XeLaTeX
2202 static string const magicXeLaTeX =
2203 "% This document must be compiled with XeLaTeX ";
2204 if (comment.size() > magicXeLaTeX.size()
2205 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
2206 && h_inputencoding == "auto-legacy") {
2207 if (!detectEncoding)
2208 cerr << "XeLaTeX comment found, switching to UTF8\n";
2209 h_inputencoding = "utf8";
2212 if (regex_search(comment, sub, islyxfile)) {
2214 in_lyx_preamble = true;
2215 } else if (is_lyx_file
2216 && regex_search(comment, sub, usercommands))
2217 in_lyx_preamble = false;
2218 else if (!in_lyx_preamble)
2219 h_preamble << t.asInput();
2223 if (t.cs() == "PassOptionsToPackage") {
2224 string const poptions = p.getArg('{', '}');
2225 string const package = p.verbatim_item();
2226 extra_package_options_.insert(make_pair(package, poptions));
2230 if (t.cs() == "pagestyle") {
2231 h_paperpagestyle = p.verbatim_item();
2235 if (t.cs() == "setdefaultlanguage") {
2237 // We don't yet care about non-language variant options
2238 // because LyX doesn't support this yet, see bug #8214
2240 string langopts = p.getOpt();
2241 // check if the option contains a variant, if yes, extract it
2242 string::size_type pos_var = langopts.find("variant");
2243 string::size_type i = langopts.find(',', pos_var);
2244 string::size_type k = langopts.find('=', pos_var);
2245 if (pos_var != string::npos){
2247 if (i == string::npos)
2248 variant = langopts.substr(k + 1, langopts.length() - k - 2);
2250 variant = langopts.substr(k + 1, i - k - 1);
2251 h_language = variant;
2255 h_language = p.verbatim_item();
2256 //finally translate the poyglossia name to a LyX name
2257 h_language = polyglossia2lyx(h_language);
2261 if (t.cs() == "setotherlanguage") {
2262 // We don't yet care about the option because LyX doesn't
2263 // support this yet, see bug #8214
2264 p.hasOpt() ? p.getOpt() : string();
2269 if (t.cs() == "setmainfont") {
2270 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2271 h_font_roman[1] = p.getArg('{', '}');
2272 if (!fontopts.empty()) {
2273 vector<string> opts = getVectorFromString(fontopts);
2275 for (auto const & opt : opts) {
2276 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2279 if (!fontopts.empty())
2283 h_font_roman_opts = fontopts;
2288 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
2289 // LyX currently only supports the scale option
2290 string scale, fontopts;
2292 fontopts = p.getArg('[', ']');
2293 if (!fontopts.empty()) {
2294 vector<string> opts = getVectorFromString(fontopts);
2296 for (auto const & opt : opts) {
2297 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2300 if (prefixIs(opt, "Scale=")) {
2301 scale_as_percentage(opt, scale);
2304 if (!fontopts.empty())
2310 if (t.cs() == "setsansfont") {
2312 h_font_sf_scale[1] = scale;
2313 h_font_sans[1] = p.getArg('{', '}');
2314 if (!fontopts.empty())
2315 h_font_sans_opts = fontopts;
2318 h_font_tt_scale[1] = scale;
2319 h_font_typewriter[1] = p.getArg('{', '}');
2320 if (!fontopts.empty())
2321 h_font_typewriter_opts = fontopts;
2326 if (t.cs() == "babelfont") {
2328 h_use_non_tex_fonts = true;
2329 h_language_package = "babel";
2330 if (h_inputencoding == "auto-legacy")
2331 p.setEncoding("UTF-8");
2332 // we don't care about the lang option
2333 string const lang = p.hasOpt() ? p.getArg('[', ']') : string();
2334 string const family = p.getArg('{', '}');
2335 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2336 string const fontname = p.getArg('{', '}');
2337 if (lang.empty() && family == "rm") {
2338 h_font_roman[1] = fontname;
2339 if (!fontopts.empty()) {
2340 vector<string> opts = getVectorFromString(fontopts);
2342 for (auto const & opt : opts) {
2343 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2346 if (!fontopts.empty())
2350 h_font_roman_opts = fontopts;
2353 } else if (lang.empty() && (family == "sf" || family == "tt")) {
2355 if (!fontopts.empty()) {
2356 vector<string> opts = getVectorFromString(fontopts);
2358 for (auto const & opt : opts) {
2359 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2362 if (prefixIs(opt, "Scale=")) {
2363 scale_as_percentage(opt, scale);
2366 if (!fontopts.empty())
2371 if (family == "sf") {
2373 h_font_sf_scale[1] = scale;
2374 h_font_sans[1] = fontname;
2375 if (!fontopts.empty())
2376 h_font_sans_opts = fontopts;
2379 h_font_tt_scale[1] = scale;
2380 h_font_typewriter[1] = fontname;
2381 if (!fontopts.empty())
2382 h_font_typewriter_opts = fontopts;
2386 // not rm, sf or tt or lang specific
2387 h_preamble << '\\' << t.cs();
2389 h_preamble << '[' << lang << ']';
2390 h_preamble << '{' << family << '}';
2391 if (!fontopts.empty())
2392 h_preamble << '[' << fontopts << ']';
2393 h_preamble << '{' << fontname << '}' << '\n';
2398 if (t.cs() == "date") {
2399 string argument = p.getArg('{', '}');
2400 if (argument.empty())
2401 h_suppress_date = "true";
2403 h_preamble << t.asInput() << '{' << argument << '}';
2407 if (t.cs() == "color") {
2408 string const space =
2409 (p.hasOpt() ? p.getOpt() : string());
2410 string argument = p.getArg('{', '}');
2411 // check the case that a standard color is used
2412 if (space.empty() && is_known(argument, known_basic_colors)) {
2413 h_fontcolor = rgbcolor2code(argument);
2414 registerAutomaticallyLoadedPackage("color");
2415 } else if (space.empty() && argument == "document_fontcolor")
2416 registerAutomaticallyLoadedPackage("color");
2417 // check the case that LyX's document_fontcolor is defined
2418 // but not used for \color
2420 h_preamble << t.asInput();
2422 h_preamble << space;
2423 h_preamble << '{' << argument << '}';
2424 // the color might already be set because \definecolor
2425 // is parsed before this
2431 if (t.cs() == "pagecolor") {
2432 string argument = p.getArg('{', '}');
2433 // check the case that a standard color is used
2434 if (is_known(argument, known_basic_colors)) {
2435 h_backgroundcolor = rgbcolor2code(argument);
2436 } else if (argument == "page_backgroundcolor")
2437 registerAutomaticallyLoadedPackage("color");
2438 // check the case that LyX's page_backgroundcolor is defined
2439 // but not used for \pagecolor
2441 h_preamble << t.asInput() << '{' << argument << '}';
2442 // the color might already be set because \definecolor
2443 // is parsed before this
2444 h_backgroundcolor = "";
2449 if (t.cs() == "makeatletter") {
2450 // LyX takes care of this
2451 p.setCatcode('@', catLetter);
2455 if (t.cs() == "makeatother") {
2456 // LyX takes care of this
2457 p.setCatcode('@', catOther);
2461 if (t.cs() == "makeindex") {
2462 // LyX will re-add this if a print index command is found
2467 if (t.cs() == "newindex") {
2468 string const indexname = p.getArg('[', ']');
2469 string const shortcut = p.verbatim_item();
2470 if (!indexname.empty())
2471 h_index[index_number] = indexname;
2473 h_index[index_number] = shortcut;
2474 h_shortcut[index_number] = shortcut;
2480 if (t.cs() == "addbibresource") {
2481 string const options = p.getArg('[', ']');
2482 string const arg = removeExtension(p.getArg('{', '}'));
2483 if (!options.empty()) {
2484 // check if the option contains a bibencoding, if yes, extract it
2485 string::size_type pos = options.find("bibencoding=");
2487 if (pos != string::npos) {
2488 string::size_type i = options.find(',', pos);
2489 if (i == string::npos)
2490 encoding = options.substr(pos + 1);
2492 encoding = options.substr(pos, i - pos);
2493 pos = encoding.find('=');
2494 if (pos == string::npos)
2497 encoding = encoding.substr(pos + 1);
2499 if (!encoding.empty())
2500 biblatex_encodings.push_back(normalize_filename(arg) + ' ' + encoding);
2502 biblatex_bibliographies.push_back(arg);
2506 if (t.cs() == "bibliography") {
2507 vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
2508 biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
2512 if (t.cs() == "RS@ifundefined") {
2513 string const name = p.verbatim_item();
2514 string const body1 = p.verbatim_item();
2515 string const body2 = p.verbatim_item();
2516 // only non-lyxspecific stuff
2517 if (in_lyx_preamble &&
2518 (name == "subsecref" || name == "thmref" || name == "lemref"))
2522 ss << '\\' << t.cs();
2523 ss << '{' << name << '}'
2524 << '{' << body1 << '}'
2525 << '{' << body2 << '}';
2526 h_preamble << ss.str();
2531 if (t.cs() == "AtBeginDocument") {
2532 string const name = p.verbatim_item();
2533 // only non-lyxspecific stuff
2534 if (in_lyx_preamble &&
2535 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
2536 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
2537 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
2538 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
2539 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
2540 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
2541 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
2542 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
2543 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
2544 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
2545 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
2546 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
2547 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
2548 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
2549 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
2553 ss << '\\' << t.cs();
2554 ss << '{' << name << '}';
2555 h_preamble << ss.str();
2560 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
2561 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
2562 || t.cs() == "providecommand" || t.cs() == "providecommandx"
2563 || t.cs() == "DeclareRobustCommand"
2564 || t.cs() == "DeclareRobustCommandx"
2565 || t.cs() == "ProvideTextCommandDefault"
2566 || t.cs() == "DeclareMathAccent") {
2568 if (p.next_token().character() == '*') {
2572 string const name = p.verbatim_item();
2573 string const opt1 = p.getFullOpt();
2574 string const opt2 = p.getFullOpt();
2575 string const body = p.verbatim_item();
2576 // store the in_lyx_preamble setting
2577 bool const was_in_lyx_preamble = in_lyx_preamble;
2579 if (name == "\\rmdefault")
2580 if (is_known(body, known_roman_font_packages)) {
2581 h_font_roman[0] = body;
2583 in_lyx_preamble = true;
2585 if (name == "\\sfdefault") {
2586 if (is_known(body, known_sans_font_packages)) {
2587 h_font_sans[0] = body;
2589 in_lyx_preamble = true;
2591 if (body == "LibertinusSans-OsF") {
2592 h_font_sans[0] = "LibertinusSans-LF";
2593 h_font_sans_osf = "true";
2595 in_lyx_preamble = true;
2598 if (name == "\\ttdefault")
2599 if (is_known(body, known_typewriter_font_packages)) {
2600 h_font_typewriter[0] = body;
2602 in_lyx_preamble = true;
2604 if (name == "\\familydefault") {
2605 string family = body;
2606 // remove leading "\"
2607 h_font_default_family = family.erase(0,1);
2609 in_lyx_preamble = true;
2611 if (name == "\\LibertinusSans@scale") {
2612 if (isStrDbl(body)) {
2613 h_font_sf_scale[0] = convert<string>(
2614 static_cast<int>(100 * convert<double>(body)));
2617 if (name == "\\LibertinusMono@scale") {
2618 if (isStrDbl(body)) {
2619 h_font_tt_scale[0] = convert<string>(
2620 static_cast<int>(100 * convert<double>(body)));
2624 // remove LyX-specific definitions that are re-added by LyX
2626 // \lyxline is an ancient command that is converted by tex2lyx into
2627 // a \rule therefore remove its preamble code
2628 if (name == "\\lyxdot" || name == "\\lyxarrow"
2629 || name == "\\lyxline" || name == "\\LyX") {
2631 in_lyx_preamble = true;
2634 // Add the command to the known commands
2635 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
2637 // only non-lyxspecific stuff
2638 if (!in_lyx_preamble) {
2640 ss << '\\' << t.cs();
2643 ss << '{' << name << '}' << opt1 << opt2
2644 << '{' << body << "}";
2645 if (prefixIs(t.cs(), "renew") || !contains(h_preamble.str(), ss.str()))
2646 h_preamble << ss.str();
2648 ostream & out = in_preamble ? h_preamble : os;
2649 out << "\\" << t.cs() << "{" << name << "}"
2650 << opts << "{" << body << "}";
2653 // restore the in_lyx_preamble setting
2654 in_lyx_preamble = was_in_lyx_preamble;
2658 if (t.cs() == "documentclass") {
2659 vector<string>::iterator it;
2660 vector<string> opts = split_options(p.getArg('[', ']'));
2661 // FIXME This does not work for classes that have a
2662 // different name in LyX than in LaTeX
2663 h_textclass = p.getArg('{', '}');
2665 // Force textclass if the user wanted it
2666 if (!forceclass.empty())
2667 h_textclass = forceclass;
2668 tc.setName(h_textclass);
2669 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2670 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2675 // Try those who are (most likely) known to all packages first
2676 handle_opt(opts, known_fontsizes, h_paperfontsize);
2677 delete_opt(opts, known_fontsizes);
2678 // delete "pt" at the end
2679 string::size_type i = h_paperfontsize.find("pt");
2680 if (i != string::npos)
2681 h_paperfontsize.erase(i);
2682 // Now those known specifically to the class
2683 vector<string> class_fsizes = getVectorFromString(tc.opt_fontsize(), "|");
2684 string const fsize_format = tc.fontsizeformat();
2685 for (auto const & fsize : class_fsizes) {
2686 string latexsize = subst(fsize_format, "$$s", fsize);
2687 vector<string>::iterator it = find(opts.begin(), opts.end(), latexsize);
2688 if (it != opts.end()) {
2689 h_paperfontsize = fsize;
2695 // The documentclass options are always parsed before the options
2696 // of the babel call so that a language cannot overwrite the babel
2698 handle_opt(opts, known_languages, h_language);
2699 delete_opt(opts, known_languages);
2702 if ((it = find(opts.begin(), opts.end(), "fleqn"))
2704 h_is_mathindent = "1";
2707 // formula numbering side
2708 if ((it = find(opts.begin(), opts.end(), "leqno"))
2710 h_math_numbering_side = "left";
2713 else if ((it = find(opts.begin(), opts.end(), "reqno"))
2715 h_math_numbering_side = "right";
2719 // paper orientation
2720 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
2721 h_paperorientation = "landscape";
2725 if ((it = find(opts.begin(), opts.end(), "oneside"))
2730 if ((it = find(opts.begin(), opts.end(), "twoside"))
2736 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
2738 h_papercolumns = "1";
2741 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
2743 h_papercolumns = "2";
2747 // some size options are known by the document class, other sizes
2748 // are handled by the \geometry command of the geometry package
2750 vector<string> class_psizes = getVectorFromString(tc.opt_pagesize(), "|");
2751 string const psize_format = tc.pagesizeformat();
2752 for (auto const & psize : class_psizes) {
2753 string latexsize = subst(psize_format, "$$s", psize);
2754 vector<string>::iterator it = find(opts.begin(), opts.end(), latexsize);
2755 if (it != opts.end()) {
2756 h_papersize = psize;
2760 if (psize_format == "$$spaper")
2762 // Also try with the default format since this is understood by
2764 latexsize = psize + "paper";
2765 it = find(opts.begin(), opts.end(), latexsize);
2766 if (it != opts.end()) {
2767 h_papersize = psize;
2772 // the remaining options
2773 h_options = join(opts, ",");
2777 if (t.cs() == "usepackage") {
2778 string const options = p.getArg('[', ']');
2779 string const name = p.getArg('{', '}');
2780 vector<string> vecnames;
2781 split(name, vecnames, ',');
2782 vector<string>::const_iterator it = vecnames.begin();
2783 vector<string>::const_iterator end = vecnames.end();
2784 for (; it != end; ++it)
2785 handle_package(p, trimSpaceAndEol(*it), options,
2786 in_lyx_preamble, detectEncoding);
2790 if (t.cs() == "inputencoding") {
2791 string const encoding = p.getArg('{','}');
2792 Encoding const * const enc = encodings.fromLaTeXName(
2793 encoding, Encoding::inputenc, true);
2795 if (!detectEncoding)
2796 cerr << "Unknown encoding " << encoding
2797 << ". Ignoring." << std::endl;
2800 h_inputencoding = enc->name();
2801 p.setEncoding(enc->iconvName());
2806 if (t.cs() == "newenvironment") {
2807 string const name = p.getArg('{', '}');
2808 string const opt1 = p.getFullOpt();
2809 string const opt2 = p.getFullOpt();
2810 string const beg = p.verbatim_item();
2811 string const end = p.verbatim_item();
2812 if (!in_lyx_preamble) {
2813 h_preamble << "\\newenvironment{" << name
2814 << '}' << opt1 << opt2 << '{'
2815 << beg << "}{" << end << '}';
2817 add_known_environment(name, opt1, !opt2.empty(),
2818 from_utf8(beg), from_utf8(end));
2822 if (t.cs() == "newtheorem") {
2824 if (p.next_token().character() == '*') {
2828 string const name = p.getArg('{', '}');
2829 string const opt1 = p.getFullOpt();
2830 string const opt2 = p.getFullOpt();
2831 string const body = p.verbatim_item();
2832 string const opt3 = p.getFullOpt();
2833 string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
2835 string const complete = cmd + "{" + name + '}' +
2836 opt1 + opt2 + '{' + body + '}' + opt3;
2838 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
2840 if (!in_lyx_preamble)
2841 h_preamble << complete;
2845 if (t.cs() == "def") {
2846 string name = p.get_token().cs();
2847 // In fact, name may be more than the name:
2848 // In the test case of bug 8116
2849 // name == "csname SF@gobble@opt \endcsname".
2850 // Therefore, we need to use asInput() instead of cs().
2851 while (p.next_token().cat() != catBegin)
2852 name += p.get_token().asInput();
2853 if (!in_lyx_preamble)
2854 h_preamble << "\\def\\" << name << '{'
2855 << p.verbatim_item() << "}";
2859 if (t.cs() == "newcolumntype") {
2860 string const name = p.getArg('{', '}');
2861 trimSpaceAndEol(name);
2863 string opts = p.getOpt();
2864 if (!opts.empty()) {
2865 istringstream is(string(opts, 1));
2868 special_columns_[name[0]] = nargs;
2869 h_preamble << "\\newcolumntype{" << name << "}";
2871 h_preamble << "[" << nargs << "]";
2872 h_preamble << "{" << p.verbatim_item() << "}";
2876 if (t.cs() == "setcounter") {
2877 string const name = p.getArg('{', '}');
2878 string const content = p.getArg('{', '}');
2879 if (name == "secnumdepth")
2880 h_secnumdepth = content;
2881 else if (name == "tocdepth")
2882 h_tocdepth = content;
2884 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
2888 if (t.cs() == "setlength") {
2889 string const name = p.verbatim_item();
2890 string const content = p.verbatim_item();
2891 // the paragraphs are only not indented when \parindent is set to zero
2892 if (name == "\\parindent" && content != "")
2893 h_paragraph_indentation = translate_len(content);
2894 else if (name == "\\parskip" && isPackageUsed("parskip")) {
2895 if (content == "\\smallskipamount")
2896 h_defskip = "smallskip";
2897 else if (content == "\\medskipamount")
2898 h_defskip = "medskip";
2899 else if (content == "\\bigskipamount")
2900 h_defskip = "bigskip";
2901 else if (content == "\\baselineskip")
2902 h_defskip = "fullline";
2904 h_defskip = translate_len(content);
2905 } else if (name == "\\mathindent") {
2906 h_mathindentation = translate_len(content);
2908 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2912 if (t.cs() == "onehalfspacing") {
2913 h_spacing = "onehalf";
2917 if (t.cs() == "doublespacing") {
2918 h_spacing = "double";
2922 if (t.cs() == "setstretch") {
2923 h_spacing = "other " + p.verbatim_item();
2927 if (t.cs() == "synctex") {
2928 // the scheme is \synctex=value
2929 // where value can only be "1" or "-1"
2930 h_output_sync = "1";
2931 // there can be any character behind the value (e.g. a linebreak or a '\'
2932 // therefore we extract it char by char
2934 string value = p.get_token().asInput();
2936 value += p.get_token().asInput();
2937 h_output_sync_macro = "\\synctex=" + value;
2941 if (t.cs() == "begin") {
2942 string const name = p.getArg('{', '}');
2943 if (name == "document")
2945 h_preamble << "\\begin{" << name << "}";
2949 if (t.cs() == "geometry") {
2950 vector<string> opts = split_options(p.getArg('{', '}'));
2951 handle_geometry(opts);
2955 if (t.cs() == "definecolor") {
2956 string const color = p.getArg('{', '}');
2957 string const space = p.getArg('{', '}');
2958 string const value = p.getArg('{', '}');
2959 if (color == "document_fontcolor" && space == "rgb") {
2960 RGBColor c(RGBColorFromLaTeX(value));
2961 h_fontcolor = X11hexname(c);
2962 } else if (color == "note_fontcolor" && space == "rgb") {
2963 RGBColor c(RGBColorFromLaTeX(value));
2964 h_notefontcolor = X11hexname(c);
2965 } else if (color == "page_backgroundcolor" && space == "rgb") {
2966 RGBColor c(RGBColorFromLaTeX(value));
2967 h_backgroundcolor = X11hexname(c);
2968 } else if (color == "shadecolor" && space == "rgb") {
2969 RGBColor c(RGBColorFromLaTeX(value));
2970 h_boxbgcolor = X11hexname(c);
2972 h_preamble << "\\definecolor{" << color
2973 << "}{" << space << "}{" << value
2979 if (t.cs() == "bibliographystyle") {
2980 h_biblio_style = p.verbatim_item();
2984 if (t.cs() == "jurabibsetup") {
2985 // FIXME p.getArg('{', '}') is most probably wrong (it
2986 // does not handle nested braces).
2987 // Use p.verbatim_item() instead.
2988 vector<string> jurabibsetup =
2989 split_options(p.getArg('{', '}'));
2990 // add jurabibsetup to the jurabib package options
2991 add_package("jurabib", jurabibsetup);
2992 if (!jurabibsetup.empty()) {
2993 h_preamble << "\\jurabibsetup{"
2994 << join(jurabibsetup, ",") << '}';
2999 if (t.cs() == "hypersetup") {
3000 vector<string> hypersetup =
3001 split_options(p.verbatim_item());
3002 // add hypersetup to the hyperref package options
3003 handle_hyperref(hypersetup);
3004 if (!hypersetup.empty()) {
3005 h_preamble << "\\hypersetup{"
3006 << join(hypersetup, ",") << '}';
3011 if (t.cs() == "includeonly") {
3012 vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
3013 for (auto & iofile : includeonlys) {
3014 string filename(normalize_filename(iofile));
3015 string const path = getMasterFilePath(true);
3016 // We want to preserve relative/absolute filenames,
3017 // therefore path is only used for testing
3018 if (!makeAbsPath(filename, path).exists()) {
3019 // The file extension is probably missing.
3020 // Now try to find it out.
3021 string const tex_name =
3022 find_file(filename, path,
3023 known_tex_extensions);
3024 if (!tex_name.empty())
3025 filename = tex_name;
3028 if (makeAbsPath(filename, path).exists())
3029 fix_child_filename(filename);
3031 cerr << "Warning: Could not find included file '"
3032 << filename << "'." << endl;
3033 outname = changeExtension(filename, "lyx");
3034 h_includeonlys.push_back(outname);
3039 if (is_known(t.cs(), known_if_3arg_commands)) {
3040 // prevent misparsing of \usepackage if it is used
3041 // as an argument (see e.g. our own output of
3042 // \@ifundefined above)
3043 string const arg1 = p.verbatim_item();
3044 string const arg2 = p.verbatim_item();
3045 string const arg3 = p.verbatim_item();
3046 // test case \@ifundefined{date}{}{\date{}}
3047 if (t.cs() == "@ifundefined" && arg1 == "date" &&
3048 arg2.empty() && arg3 == "\\date{}") {
3049 h_suppress_date = "true";
3050 // older tex2lyx versions did output
3051 // \@ifundefined{definecolor}{\usepackage{color}}{}
3052 } else if (t.cs() == "@ifundefined" &&
3053 arg1 == "definecolor" &&
3054 arg2 == "\\usepackage{color}" &&
3056 if (!in_lyx_preamble)
3057 h_preamble << package_beg_sep
3060 << "\\@ifundefined{definecolor}{color}{}"
3063 //\@ifundefined{showcaptionsetup}{}{%
3064 // \PassOptionsToPackage{caption=false}{subfig}}
3065 // that LyX uses for subfloats
3066 } else if (t.cs() == "@ifundefined" &&
3067 arg1 == "showcaptionsetup" && arg2.empty()
3068 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
3070 } else if (!in_lyx_preamble) {
3071 h_preamble << t.asInput()
3072 << '{' << arg1 << '}'
3073 << '{' << arg2 << '}'
3074 << '{' << arg3 << '}';
3079 if (is_known(t.cs(), known_if_commands)) {
3080 // must not parse anything in conditional code, since
3081 // LyX would output the parsed contents unconditionally
3082 if (!in_lyx_preamble)
3083 h_preamble << t.asInput();
3084 handle_if(p, in_lyx_preamble);
3088 if (!t.cs().empty() && !in_lyx_preamble) {
3089 h_preamble << '\\' << t.cs();
3094 // remove the whitespace
3097 if (h_papersides.empty()) {
3100 h_papersides = ss.str();
3103 // If the CJK package is used we cannot set the document language from
3104 // the babel options. Instead, we guess which language is used most
3105 // and set this one.
3106 default_language = h_language;
3107 if (is_full_document &&
3108 (auto_packages.find("CJK") != auto_packages.end() ||
3109 auto_packages.find("CJKutf8") != auto_packages.end())) {
3111 h_language = guessLanguage(p, default_language);
3113 if (explicit_babel && h_language != default_language) {
3114 // We set the document language to a CJK language,
3115 // but babel is explicitly called in the user preamble
3116 // without options. LyX will not add the default
3117 // language to the document options if it is either
3118 // english, or no text is set as default language.
3119 // Therefore we need to add a language option explicitly.
3120 // FIXME: It would be better to remove all babel calls
3121 // from the user preamble, but this is difficult
3122 // without re-introducing bug 7861.
3123 if (h_options.empty())
3124 h_options = lyx2babel(default_language);
3126 h_options += ',' + lyx2babel(default_language);
3130 // Finally, set the quote style.
3131 // LyX knows the following quotes styles:
3132 // british, cjk, cjkangle, danish, english, french, german,
3133 // polish, russian, swedish and swiss
3134 // conversion list taken from
3135 // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
3136 // (quotes for kazakh are unknown)
3138 if (is_known(h_language, known_british_quotes_languages))
3139 h_quotes_style = "british";
3141 else if (is_known(h_language, known_cjk_quotes_languages))
3142 h_quotes_style = "cjk";
3144 else if (is_known(h_language, known_cjkangle_quotes_languages))
3145 h_quotes_style = "cjkangle";
3147 else if (is_known(h_language, known_danish_quotes_languages))
3148 h_quotes_style = "danish";
3150 else if (is_known(h_language, known_french_quotes_languages))
3151 h_quotes_style = "french";
3153 else if (is_known(h_language, known_german_quotes_languages))
3154 h_quotes_style = "german";
3156 else if (is_known(h_language, known_polish_quotes_languages))
3157 h_quotes_style = "polish";
3159 else if (is_known(h_language, known_russian_quotes_languages))
3160 h_quotes_style = "russian";
3162 else if (is_known(h_language, known_swedish_quotes_languages))
3163 h_quotes_style = "swedish";
3165 else if (is_known(h_language, known_swiss_quotes_languages))
3166 h_quotes_style = "swiss";
3168 else if (is_known(h_language, known_english_quotes_languages))
3169 h_quotes_style = "english";
3173 string Preamble::parseEncoding(Parser & p, string const & forceclass)
3175 TeX2LyXDocClass dummy;
3176 parse(p, forceclass, true, dummy);
3177 if (h_inputencoding != "auto-legacy" && h_inputencoding != "auto-legacy-plain")
3178 return h_inputencoding;
3183 string babel2lyx(string const & language)
3185 char const * const * where = is_known(language, known_languages);
3187 return known_coded_languages[where - known_languages];
3192 string lyx2babel(string const & language)
3194 char const * const * where = is_known(language, known_coded_languages);
3196 return known_languages[where - known_coded_languages];
3201 string Preamble::polyglossia2lyx(string const & language)
3203 char const * const * where = is_known(language, polyglossia_languages);
3205 return coded_polyglossia_languages[where - polyglossia_languages];
3210 string rgbcolor2code(string const & name)
3212 char const * const * where = is_known(name, known_basic_colors);
3214 // "red", "green" etc
3215 return known_basic_color_codes[where - known_basic_colors];
3217 // "255,0,0", "0,255,0" etc
3218 RGBColor c(RGBColorFromLaTeX(name));
3219 return X11hexname(c);