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"
22 #include "TextClass.h"
25 #include "support/convert.h"
26 #include "support/FileName.h"
27 #include "support/filetools.h"
28 #include "support/lstrings.h"
30 #include "support/regex.h"
36 using namespace lyx::support;
45 // CJK languages are handled in text.cpp, polyglossia languages are listed
48 * known babel language names (including synonyms)
49 * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
50 * please keep this in sync with known_coded_languages line by line!
52 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
53 "american", "arabic", "arabtex", "australian", "austrian", "azerbaijani", "bahasa", "bahasai",
54 "bahasam", "basque", "belarusian", "bosnian", "brazil", "brazilian", "breton", "british",
55 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
56 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "francais",
57 "french", "frenchb", "frenchle", "frenchpro", "friulan", "galician", "german", "germanb",
58 "georgian", "greek", "hebrew", "hungarian", "icelandic", "indon", "indonesian",
59 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
60 "latvian", "lithuanian", "lowersorbian", "lsorbian", "macedonian", "magyar", "malay", "meyalu",
61 "mongolian", "naustrian", "newzealand", "ngerman", "ngermanb", "norsk", "nswissgerman",
62 "nynorsk", "piedmontese", "polutonikogreek", "polish", "portuges", "portuguese",
63 "romanian", "romansh", "russian", "russianb", "samin", "scottish", "serbian", "serbian-latin",
64 "slovak", "slovene", "spanish", "swedish", "swissgerman", "thai", "turkish", "turkmen",
65 "ukraineb", "ukrainian", "uppersorbian", "UKenglish", "USenglish", "usorbian",
70 * the same as known_languages with .lyx names
71 * please keep this in sync with known_languages line by line!
73 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
74 "american", "arabic_arabi", "arabic_arabtex", "australian", "austrian", "azerbaijani", "bahasa", "bahasa",
75 "bahasam", "basque", "belarusian", "bosnian", "brazilian", "brazilian", "breton", "british",
76 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
77 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "french",
78 "french", "french", "french", "french", "friulan", "galician", "german", "german",
79 "georgian", "greek", "hebrew", "magyar", "icelandic", "bahasa", "bahasa",
80 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
81 "latvian", "lithuanian", "lowersorbian", "lowersorbian", "macedonian", "magyar", "bahasam", "bahasam",
82 "mongolian", "naustrian", "newzealand", "ngerman", "ngerman", "norsk", "german-ch",
83 "nynorsk", "piedmontese", "polutonikogreek", "polish", "portuguese", "portuguese",
84 "romanian", "romansh", "russian", "russian", "samin", "scottish", "serbian", "serbian-latin",
85 "slovak", "slovene", "spanish", "swedish", "german-ch-old", "thai", "turkish", "turkmen",
86 "ukrainian", "ukrainian", "uppersorbian", "english", "english", "uppersorbian",
87 "vietnamese", "welsh",
90 /// languages with british quotes (.lyx names)
91 const char * const known_british_quotes_languages[] = {"british", "welsh", 0};
93 /// languages with cjk quotes (.lyx names)
94 const char * const known_cjk_quotes_languages[] = {"chinese-traditional",
95 "japanese", "japanese-cjk", 0};
97 /// languages with cjk-angle quotes (.lyx names)
98 const char * const known_cjkangle_quotes_languages[] = {"korean", 0};
100 /// languages with danish quotes (.lyx names)
101 const char * const known_danish_quotes_languages[] = {"danish", 0};
103 /// languages with english quotes (.lyx names)
104 const char * const known_english_quotes_languages[] = {"american", "australian",
105 "bahasa", "bahasam", "bengali", "brazilian", "canadian", "chinese-simplified", "english",
106 "esperanto", "farsi", "interlingua", "irish", "newzealand", "scottish",
107 "thai", "turkish", "vietnamese", 0};
109 /// languages with french quotes (.lyx names)
110 const char * const known_french_quotes_languages[] = {"ancientgreek",
111 "arabic_arabi", "arabic_arabtex", "asturian", "belarusian", "breton",
112 "canadien", "catalan", "french", "friulan", "galician", "italian", "occitan",
113 "piedmontese", "portuguese", "spanish", "spanish-mexico", 0};
115 /// languages with german quotes (.lyx names)
116 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
117 "czech", "estonian", "georgian", "german", "icelandic", "latvian", "lithuanian",
118 "lowersorbian", "macedonian", "naustrian", "ngerman", "romansh", "slovak", "slovene",
121 /// languages with polish quotes (.lyx names)
122 const char * const known_polish_quotes_languages[] = {"afrikaans", "bosnian", "croatian",
123 "dutch", "magyar", "polish", "romanian", "serbian", "serbian-latin", 0};
125 /// languages with russian quotes (.lyx names)
126 const char * const known_russian_quotes_languages[] = {"azerbaijani", "oldrussian",
127 "russian", "ukrainian", 0};
129 /// languages with swedish quotes (.lyx names)
130 const char * const known_swedish_quotes_languages[] = {"finnish", "swedish", 0};
132 /// languages with swiss quotes (.lyx names)
133 const char * const known_swiss_quotes_languages[] = {"albanian",
134 "armenian", "basque", "churchslavonic", "german-ch", "german-ch-old",
135 "norsk", "nynorsk", "turkmen", "ukrainian", "vietnamese", 0};
137 /// known language packages from the times before babel
138 const char * const known_old_language_packages[] = {"french", "frenchle",
139 "frenchpro", "german", "ngerman", "pmfrench", 0};
141 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
143 const char * const known_roman_font_packages[] = { "ae", "beraserif", "bookman",
144 "ccfonts", "chancery", "charter", "cmr", "cochineal", "crimson", "CrimsonPro", "DejaVuSerif",
145 "DejaVuSerifCondensed", "fourier", "garamondx", "libertine", "libertineRoman", "libertine-type1",
146 "lmodern", "mathdesign", "mathpazo", "mathptmx", "MinionPro", "newcent", "noto", "noto-serif",
147 "PTSerif", "tgbonum", "tgchorus", "tgpagella", "tgschola", "tgtermes", "utopia", "xcharter", 0 };
149 const char * const known_sans_font_packages[] = { "avant", "berasans", "biolinum",
150 "biolinum-type1", "cantarell", "Chivo", "cmbr", "cmss", "DejaVuSans", "DejaVuSansCondensed", "FiraSans", "helvet", "iwona",
151 "iwonac", "iwonal", "iwonalc", "kurier", "kurierc", "kurierl", "kurierlc", "LibertinusSans-LF", "lmss", "noto-sans", "PTSans",
152 "tgadventor", "tgheros", "uop", 0 };
154 const char * const known_typewriter_font_packages[] = { "beramono", "cmtl", "cmtt", "courier", "DejaVuSansMono",
155 "FiraMono", "lmtt", "luximono", "libertineMono", "libertineMono-type1", "LibertinusMono-TLF", "lmodern",
156 "mathpazo", "mathptmx", "newcent", "noto-mono", "PTMono", "tgcursor", "txtt", 0 };
158 const char * const known_math_font_packages[] = { "eulervm", "newtxmath", 0};
160 const char * const known_latex_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
161 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
162 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
163 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
164 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
166 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
167 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
169 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
170 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
173 /// commands that can start an \if...\else...\endif sequence
174 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
175 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
176 "ifsidecap", "ifupgreek", 0};
178 const char * const known_basic_colors[] = {"black", "blue", "brown", "cyan",
179 "darkgray", "gray", "green", "lightgray", "lime", "magenta", "orange", "olive",
180 "pink", "purple", "red", "teal", "violet", "white", "yellow", 0};
182 const char * const known_basic_color_codes[] = {"#000000", "#0000ff", "#964B00", "#00ffff",
183 "#a9a9a9", "#808080", "#00ff00", "#d3d3d3", "#bfff00", "#ff00ff", "#ff7f00", "#808000",
184 "#ffc0cb", "#800080", "#ff0000", "#008080", "#8f00ff", "#ffffff", "#ffff00", 0};
186 /// conditional commands with three arguments like \@ifundefined{}{}{}
187 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
191 * Known file extensions for TeX files as used by \\includeonly
193 char const * const known_tex_extensions[] = {"tex", 0};
195 /// packages that work only in xetex
196 /// polyglossia is handled separately
197 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
198 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
199 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
201 /// packages that are automatically skipped if loaded by LyX
202 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
203 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color",
204 "float", "fontspec", "framed", "graphicx", "hhline", "ifthen", "longtable",
205 "makeidx", "minted", "multirow", "nomencl", "parskip", "pdfpages", "prettyref", "refstyle",
206 "rotating", "rotfloat", "splitidx", "setspace", "subscript", "tabularx","textcomp", "tipa",
207 "tipx", "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xcolor", "xltabular",
210 // codes used to remove packages that are loaded automatically by LyX.
211 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
212 const char package_beg_sep = '\001';
213 const char package_mid_sep = '\002';
214 const char package_end_sep = '\003';
217 // returns true if at least one of the options in what has been found
218 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
224 // the last language option is the document language (for babel and LyX)
225 // the last size option is the document font size
226 vector<string>::iterator it;
227 vector<string>::iterator position = opts.begin();
228 for (; *what; ++what) {
229 it = find(opts.begin(), opts.end(), *what);
230 if (it != opts.end()) {
231 if (it >= position) {
242 void delete_opt(vector<string> & opts, char const * const * what)
247 // remove found options from the list
248 // do this after handle_opt to avoid potential memory leaks
249 vector<string>::iterator it;
250 for (; *what; ++what) {
251 it = find(opts.begin(), opts.end(), *what);
252 if (it != opts.end())
259 * Split a package options string (keyval format) into a vector.
261 * authorformat=smallcaps,
263 * titleformat=colonsep,
264 * bibformat={tabular,ibidem,numbered}
266 vector<string> split_options(string const & input)
268 vector<string> options;
272 Token const & t = p.get_token();
273 if (t.asInput() == ",") {
274 options.push_back(trimSpaceAndEol(option));
276 } else if (t.asInput() == "=") {
279 if (p.next_token().asInput() == "{")
280 option += '{' + p.getArg('{', '}') + '}';
281 } else if (t.cat() != catSpace && t.cat() != catComment)
282 option += t.asInput();
286 options.push_back(trimSpaceAndEol(option));
293 * Retrieve a keyval option "name={value with=sign}" named \p name from
294 * \p options and return the value.
295 * The found option is also removed from \p options.
297 string process_keyval_opt(vector<string> & options, string const & name)
299 for (size_t i = 0; i < options.size(); ++i) {
300 vector<string> option;
301 split(options[i], option, '=');
302 if (option.size() < 2)
304 if (option[0] == name) {
305 options.erase(options.begin() + i);
306 option.erase(option.begin());
307 return join(option, "=");
313 } // anonymous namespace
317 * known polyglossia language names (including variants)
318 * FIXME: support spelling=old for german variants (german vs. ngerman LyX names etc)
320 const char * const Preamble::polyglossia_languages[] = {
321 "albanian", "american", "amharic", "ancient", "arabic", "armenian", "asturian", "australian",
322 "bahasai", "bahasam", "basque", "bengali", "brazil", "brazilian", "breton", "british", "bulgarian",
323 "catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
324 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
325 "galician", "greek", "monotonic", "hebrew", "hindi",
326 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean",
327 "lao", "latin", "latvian", "lithuanian", "lsorbian", "magyar", "malayalam", "marathi",
328 "austrian", "newzealand", "german", "norsk", "nynorsk", "occitan", "oldrussian",
329 "piedmontese", "polish", "polytonic", "portuges", "romanian", "romansh", "russian",
330 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovenian", "spanish", "swedish", "syriac",
331 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
332 "ukrainian", "urdu", "usorbian", "vietnamese", "welsh", 0};
333 // not yet supported by LyX: "korean", "nko"
336 * the same as polyglossia_languages with .lyx names
337 * please keep this in sync with polyglossia_languages line by line!
339 const char * const Preamble::coded_polyglossia_languages[] = {
340 "albanian", "american", "amharic", "ancientgreek", "arabic_arabi", "armenian", "asturian", "australian",
341 "bahasa", "bahasam", "basque", "bengali", "brazilian", "brazilian", "breton", "british", "bulgarian",
342 "catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
343 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
344 "galician", "greek", "greek", "hebrew", "hindi",
345 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean",
346 "lao", "latin", "latvian", "lithuanian", "lowersorbian", "magyar", "malayalam", "marathi",
347 "naustrian","newzealand", "ngerman", "norsk", "nynorsk", "occitan", "oldrussian",
348 "piedmontese", "polish", "polutonikogreek", "portuges", "romanian", "romansh", "russian",
349 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovene", "spanish", "swedish", "syriac",
350 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
351 "ukrainian", "urdu", "uppersorbian", "vietnamese", "welsh", 0};
352 // not yet supported by LyX: "korean-polyglossia", "nko"
355 bool Preamble::usePolyglossia() const
357 return h_use_non_tex_fonts && h_language_package == "default";
361 bool Preamble::indentParagraphs() const
363 return h_paragraph_separation == "indent";
367 bool Preamble::isPackageUsed(string const & package) const
369 return used_packages.find(package) != used_packages.end();
373 bool Preamble::isPackageAutoLoaded(string const & package) const
375 return auto_packages.find(package) != auto_packages.end();
379 vector<string> Preamble::getPackageOptions(string const & package) const
381 map<string, vector<string> >::const_iterator it = used_packages.find(package);
382 if (it != used_packages.end())
384 return vector<string>();
388 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
390 auto_packages.insert(package);
394 void Preamble::addModule(string const & module)
396 for (auto const & m : used_modules) {
400 used_modules.push_back(module);
404 void Preamble::suppressDate(bool suppress)
407 h_suppress_date = "true";
409 h_suppress_date = "false";
413 void Preamble::registerAuthor(std::string const & name, string const & initials)
415 Author author(from_utf8(name), empty_docstring(), from_utf8(initials));
416 author.setUsed(true);
417 authors_.record(author);
418 h_tracking_changes = "true";
419 h_output_changes = "true";
423 Author const & Preamble::getAuthor(std::string const & name) const
425 Author author(from_utf8(name), empty_docstring(), empty_docstring());
426 for (AuthorList::Authors::const_iterator it = authors_.begin();
427 it != authors_.end(); ++it)
430 static Author const dummy;
435 int Preamble::getSpecialTableColumnArguments(char c) const
437 map<char, int>::const_iterator it = special_columns_.find(c);
438 if (it == special_columns_.end())
444 void Preamble::add_package(string const & name, vector<string> & options)
446 // every package inherits the global options
447 used_packages.insert({name, split_options(h_options)});
449 // Insert options passed via PassOptionsToPackage
450 for (auto const & p : extra_package_options_) {
451 if (p.first == name) {
452 vector<string> eo = getVectorFromString(p.second);
453 for (auto const & eoi : eo)
454 options.push_back(eoi);
458 vector<string> & v = used_packages[name];
459 v.insert(v.end(), options.begin(), options.end());
460 if (name == "jurabib") {
461 // Don't output the order argument (see the cite command
462 // handling code in text.cpp).
463 vector<string>::iterator end =
464 remove(options.begin(), options.end(), "natbiborder");
465 end = remove(options.begin(), end, "jurabiborder");
466 options.erase(end, options.end());
470 void Preamble::setTextClass(string const & tclass, TeX2LyXDocClass & tc)
472 h_textclass = tclass;
473 tc.setName(h_textclass);
474 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
475 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
483 // Given is a string like "scaled=0.9" or "scale=0.9", return 0.9 * 100
484 bool scale_as_percentage(string const & scale, string & percentage)
486 if (contains(scale, '=')) {
487 string const value = support::split(scale, '=');
488 if (isStrDbl(value)) {
489 percentage = convert<string>(
490 static_cast<int>(100 * convert<double>(value)));
498 string remove_braces(string const & value)
502 if (value[0] == '{' && value[value.length()-1] == '}')
503 return value.substr(1, value.length()-2);
507 } // anonymous namespace
510 Preamble::Preamble() : one_language(true), explicit_babel(false),
511 title_layout_found(false), index_number(0), h_font_cjk_set(false)
515 h_biblio_style = "plain";
516 h_bibtex_command = "default";
517 h_cite_engine = "basic";
518 h_cite_engine_type = "default";
520 h_defskip = "medskip";
521 h_dynamic_quotes = false;
524 h_fontencoding = "default";
525 h_font_roman[0] = "default";
526 h_font_roman[1] = "default";
527 h_font_sans[0] = "default";
528 h_font_sans[1] = "default";
529 h_font_typewriter[0] = "default";
530 h_font_typewriter[1] = "default";
531 h_font_math[0] = "auto";
532 h_font_math[1] = "auto";
533 h_font_default_family = "default";
534 h_use_non_tex_fonts = false;
536 h_font_roman_osf = "false";
537 h_font_sans_osf = "false";
538 h_font_typewriter_osf = "false";
539 h_font_sf_scale[0] = "100";
540 h_font_sf_scale[1] = "100";
541 h_font_tt_scale[0] = "100";
542 h_font_tt_scale[1] = "100";
543 // h_font_roman_opts;
545 // h_font_typewriter_opts;
547 h_is_mathindent = "0";
548 h_math_numbering_side = "default";
549 h_graphics = "default";
550 h_default_output_format = "default";
551 h_html_be_strict = "false";
552 h_html_css_as_file = "0";
553 h_html_math_output = "0";
554 h_docbook_table_output = "0";
555 h_index[0] = "Index";
556 h_index_command = "default";
557 h_inputencoding = "auto-legacy";
558 h_justification = "true";
559 h_language = "english";
560 h_language_package = "none";
562 h_maintain_unincluded_children = "no";
566 h_output_changes = "false";
567 h_change_bars = "false";
569 //h_output_sync_macro
570 h_papercolumns = "1";
571 h_paperfontsize = "default";
572 h_paperorientation = "portrait";
573 h_paperpagestyle = "default";
575 h_papersize = "default";
576 h_paragraph_indentation = "default";
577 h_paragraph_separation = "indent";
582 h_pdf_bookmarks = "0";
583 h_pdf_bookmarksnumbered = "0";
584 h_pdf_bookmarksopen = "0";
585 h_pdf_bookmarksopenlevel = "1";
586 h_pdf_breaklinks = "0";
587 h_pdf_pdfborder = "0";
588 h_pdf_colorlinks = "0";
589 h_pdf_backref = "section";
590 h_pdf_pdfusetitle = "0";
592 //h_pdf_quoted_options;
593 h_quotes_style = "english";
595 h_shortcut[0] = "idx";
596 h_spacing = "single";
597 h_save_transient_properties = "true";
598 h_suppress_date = "false";
599 h_textclass = "article";
601 h_tracking_changes = "false";
602 h_use_bibtopic = "false";
603 h_use_dash_ligatures = "true";
604 h_use_indices = "false";
605 h_use_geometry = "false";
606 h_use_default_options = "false";
607 h_use_hyperref = "false";
608 h_use_microtype = "false";
609 h_use_lineno = "false";
610 h_use_refstyle = false;
611 h_use_minted = false;
612 h_use_packages["amsmath"] = "1";
613 h_use_packages["amssymb"] = "0";
614 h_use_packages["cancel"] = "0";
615 h_use_packages["esint"] = "1";
616 h_use_packages["mhchem"] = "0";
617 h_use_packages["mathdots"] = "0";
618 h_use_packages["mathtools"] = "0";
619 h_use_packages["stackrel"] = "0";
620 h_use_packages["stmaryrd"] = "0";
621 h_use_packages["undertilde"] = "0";
625 void Preamble::handle_hyperref(vector<string> & options)
627 // FIXME swallow inputencoding changes that might surround the
628 // hyperref setup if it was written by LyX
629 h_use_hyperref = "true";
630 // swallow "unicode=true", since LyX does always write that
631 vector<string>::iterator it =
632 find(options.begin(), options.end(), "unicode=true");
633 if (it != options.end())
635 it = find(options.begin(), options.end(), "pdfusetitle");
636 if (it != options.end()) {
637 h_pdf_pdfusetitle = "1";
640 string bookmarks = process_keyval_opt(options, "bookmarks");
641 if (bookmarks == "true")
642 h_pdf_bookmarks = "1";
643 else if (bookmarks == "false")
644 h_pdf_bookmarks = "0";
645 if (h_pdf_bookmarks == "1") {
646 string bookmarksnumbered =
647 process_keyval_opt(options, "bookmarksnumbered");
648 if (bookmarksnumbered == "true")
649 h_pdf_bookmarksnumbered = "1";
650 else if (bookmarksnumbered == "false")
651 h_pdf_bookmarksnumbered = "0";
652 string bookmarksopen =
653 process_keyval_opt(options, "bookmarksopen");
654 if (bookmarksopen == "true")
655 h_pdf_bookmarksopen = "1";
656 else if (bookmarksopen == "false")
657 h_pdf_bookmarksopen = "0";
658 if (h_pdf_bookmarksopen == "1") {
659 string bookmarksopenlevel =
660 process_keyval_opt(options, "bookmarksopenlevel");
661 if (!bookmarksopenlevel.empty())
662 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
665 string breaklinks = process_keyval_opt(options, "breaklinks");
666 if (breaklinks == "true")
667 h_pdf_breaklinks = "1";
668 else if (breaklinks == "false")
669 h_pdf_breaklinks = "0";
670 string pdfborder = process_keyval_opt(options, "pdfborder");
671 if (pdfborder == "{0 0 0}")
672 h_pdf_pdfborder = "1";
673 else if (pdfborder == "{0 0 1}")
674 h_pdf_pdfborder = "0";
675 string backref = process_keyval_opt(options, "backref");
676 if (!backref.empty())
677 h_pdf_backref = backref;
678 string colorlinks = process_keyval_opt(options, "colorlinks");
679 if (colorlinks == "true")
680 h_pdf_colorlinks = "1";
681 else if (colorlinks == "false")
682 h_pdf_colorlinks = "0";
683 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
684 if (!pdfpagemode.empty())
685 h_pdf_pagemode = pdfpagemode;
686 string pdftitle = process_keyval_opt(options, "pdftitle");
687 if (!pdftitle.empty()) {
688 h_pdf_title = remove_braces(pdftitle);
690 string pdfauthor = process_keyval_opt(options, "pdfauthor");
691 if (!pdfauthor.empty()) {
692 h_pdf_author = remove_braces(pdfauthor);
694 string pdfsubject = process_keyval_opt(options, "pdfsubject");
695 if (!pdfsubject.empty())
696 h_pdf_subject = remove_braces(pdfsubject);
697 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
698 if (!pdfkeywords.empty())
699 h_pdf_keywords = remove_braces(pdfkeywords);
700 if (!options.empty()) {
701 if (!h_pdf_quoted_options.empty())
702 h_pdf_quoted_options += ',';
703 h_pdf_quoted_options += join(options, ",");
709 void Preamble::handle_geometry(vector<string> & options)
711 h_use_geometry = "true";
712 vector<string>::iterator it;
714 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
715 h_paperorientation = "landscape";
719 // keyval version: "paper=letter" or "paper=letterpaper"
720 string paper = process_keyval_opt(options, "paper");
722 if (suffixIs(paper, "paper"))
723 paper = subst(paper, "paper", "");
724 // alternative version: "letterpaper"
725 handle_opt(options, known_latex_paper_sizes, paper);
726 if (suffixIs(paper, "paper"))
727 paper = subst(paper, "paper", "");
728 delete_opt(options, known_latex_paper_sizes);
732 char const * const * margin = known_paper_margins;
733 for (; *margin; ++margin) {
734 string value = process_keyval_opt(options, *margin);
735 if (!value.empty()) {
736 int k = margin - known_paper_margins;
737 string name = known_coded_paper_margins[k];
738 h_margins += '\\' + name + ' ' + value + '\n';
744 void Preamble::handle_package(Parser &p, string const & name,
745 string const & opts, bool in_lyx_preamble,
748 vector<string> options = split_options(opts);
749 add_package(name, options);
751 if (is_known(name, known_xetex_packages)) {
753 h_use_non_tex_fonts = true;
754 registerAutomaticallyLoadedPackage("fontspec");
755 if (h_inputencoding == "auto-legacy")
756 p.setEncoding("UTF-8");
759 // vector of all options for easier parsing and
761 vector<string> allopts = getVectorFromString(opts);
762 // this stores the potential extra options
769 // By default, we use the package name as LyX font name,
770 // so this only needs to be reset if these names differ
771 if (is_known(name, known_roman_font_packages))
772 h_font_roman[0] = name;
774 if (name == "ccfonts") {
775 for (auto const & opt : allopts) {
781 h_font_roman_opts = xopts;
785 if (name == "lmodern") {
786 for (auto const & opt : allopts) {
792 h_font_roman_opts = xopts;
796 if (name == "fourier") {
797 h_font_roman[0] = "utopia";
798 for (auto const & opt : allopts) {
800 h_font_roman_osf = "true";
803 if (opt == "expert") {
812 h_font_roman_opts = xopts;
816 if (name == "garamondx") {
817 for (auto const & opt : allopts) {
819 h_font_roman_osf = "true";
827 h_font_roman_opts = xopts;
831 if (name == "libertine") {
832 // this automatically invokes biolinum
833 h_font_sans[0] = "biolinum";
834 // as well as libertineMono
835 h_font_typewriter[0] = "libertine-mono";
836 for (auto const & opt : allopts) {
838 h_font_roman_osf = "true";
841 if (opt == "lining") {
842 h_font_roman_osf = "false";
850 h_font_roman_opts = xopts;
854 if (name == "libertineRoman" || name == "libertine-type1") {
855 h_font_roman[0] = "libertine";
856 // NOTE: contrary to libertine.sty, libertineRoman
857 // and libertine-type1 do not automatically invoke
858 // biolinum and libertineMono
859 if (opts == "lining")
860 h_font_roman_osf = "false";
861 else if (opts == "osf")
862 h_font_roman_osf = "true";
865 if (name == "libertinus" || name == "libertinus-type1") {
872 for (auto const & opt : allopts) {
873 if (opt == "rm" || opt == "serif") {
878 if (opt == "sf" || opt == "sans") {
883 if (opt == "tt=false" || opt == "mono=false") {
891 if (opt == "scaleSF") {
895 if (opt == "scaleTT") {
899 if (opt == "lining") {
900 h_font_roman_osf = "false";
908 h_font_roman[0] = "libertinus";
910 h_font_roman_osf = "true";
912 h_font_roman_osf = "false";
915 h_font_sans[0] = "LibertinusSans-LF";
917 h_font_sans_osf = "true";
919 h_font_sans_osf = "false";
920 if (!scalesf.empty())
921 scale_as_percentage(scalesf, h_font_sf_scale[0]);
924 h_font_typewriter[0] = "LibertinusMono-TLF";
925 if (!scalett.empty())
926 scale_as_percentage(scalett, h_font_tt_scale[0]);
929 h_font_roman_opts = xopts;
933 if (name == "MinionPro") {
934 h_font_roman[0] = "minionpro";
935 h_font_roman_osf = "true";
936 h_font_math[0] = "auto";
937 for (auto const & opt : allopts) {
939 h_font_roman_osf = "false";
942 if (opt == "onlytext") {
943 h_font_math[0] = "default";
951 h_font_roman_opts = xopts;
955 if (name == "mathdesign") {
956 for (auto const & opt : allopts) {
957 if (opt == "charter") {
958 h_font_roman[0] = "md-charter";
961 if (opt == "garamond") {
962 h_font_roman[0] = "md-garamond";
965 if (opt == "utopia") {
966 h_font_roman[0] = "md-utopia";
969 if (opt == "expert") {
971 h_font_roman_osf = "true";
977 else if (name == "mathpazo") {
978 h_font_roman[0] = "palatino";
979 for (auto const & opt : allopts) {
981 h_font_roman_osf = "true";
993 h_font_roman_opts = xopts;
997 else if (name == "mathptmx") {
998 h_font_roman[0] = "times";
999 for (auto const & opt : allopts) {
1005 h_font_roman_opts = xopts;
1009 if (name == "crimson")
1010 h_font_roman[0] = "cochineal";
1012 if (name == "cochineal") {
1013 for (auto const & opt : allopts) {
1014 if (opt == "osf" || opt == "oldstyle") {
1015 h_font_roman_osf = "true";
1018 if (opt == "proportional" || opt == "p")
1025 h_font_roman_opts = xopts;
1029 if (name == "CrimsonPro") {
1030 h_font_roman_osf = "true";
1031 for (auto const & opt : allopts) {
1032 if (opt == "lf" || opt == "lining") {
1033 h_font_roman_osf = "false";
1036 if (opt == "proportional" || opt == "p")
1038 if (opt == "medium") {
1039 h_font_roman[0] = "CrimsonProMedium";
1042 if (opt == "extralight") {
1043 h_font_roman[0] = "CrimsonProExtraLight";
1046 if (opt == "light") {
1047 h_font_roman[0] = "CrimsonProLight";
1055 h_font_roman_opts = xopts;
1061 // font uses old-style figure
1062 h_font_roman_osf = "true";
1064 if (name == "paratype") {
1065 // in this case all fonts are ParaType
1066 h_font_roman[0] = "PTSerif-TLF";
1067 h_font_sans[0] = "default";
1068 h_font_typewriter[0] = "default";
1071 if (name == "PTSerif")
1072 h_font_roman[0] = "PTSerif-TLF";
1074 if (name == "XCharter") {
1075 h_font_roman[0] = "xcharter";
1076 for (auto const & opt : allopts) {
1078 h_font_roman_osf = "true";
1086 h_font_roman_opts = xopts;
1090 if (name == "plex-serif") {
1091 h_font_roman[0] = "IBMPlexSerif";
1092 for (auto const & opt : allopts) {
1093 if (opt == "thin") {
1094 h_font_roman[0] = "IBMPlexSerifThin";
1097 if (opt == "extralight") {
1098 h_font_roman[0] = "IBMPlexSerifExtraLight";
1101 if (opt == "light") {
1102 h_font_roman[0] = "IBMPlexSerifLight";
1110 h_font_roman_opts = xopts;
1114 if (name == "noto-serif" || name == "noto") {
1121 bool extralight = false;
1123 bool medium = false;
1126 if (name == "noto") {
1131 // Since the options might apply to different shapes,
1132 // we need to parse all options first and then handle them.
1133 for (auto const & opt : allopts) {
1134 if (opt == "regular")
1144 if (opt == "thin") {
1148 if (opt == "extralight") {
1152 if (opt == "light") {
1156 if (opt == "medium") {
1167 if (opt == "nott") {
1175 if (prefixIs(opt, "scaled=")) {
1184 // handle options that might affect different shapes
1185 if (name == "noto-serif" || rm) {
1187 h_font_roman[0] = "NotoSerifThin";
1188 else if (extralight)
1189 h_font_roman[0] = "NotoSerifExtralight";
1191 h_font_roman[0] = "NotoSerifLight";
1193 h_font_roman[0] = "NotoSerifMedium";
1195 h_font_roman[0] = "NotoSerifRegular";
1197 h_font_roman_osf = "true";
1199 h_font_roman_opts = xopts;
1201 if (name == "noto" && sf) {
1203 h_font_sans[0] = "NotoSansThin";
1204 else if (extralight)
1205 h_font_sans[0] = "NotoSansExtralight";
1207 h_font_sans[0] = "NotoSansLight";
1209 h_font_sans[0] = "NotoSansMedium";
1211 h_font_sans[0] = "NotoSansRegular";
1213 h_font_sans_osf = "true";
1215 scale_as_percentage(scl, h_font_sf_scale[0]);
1217 h_font_sans_opts = xopts;
1219 if (name == "noto" && tt) {
1220 h_font_typewriter[0] = "NotoMonoRegular";
1222 h_font_typewriter_osf = "true";
1224 scale_as_percentage(scl, h_font_tt_scale[0]);
1226 h_font_typewriter_opts = xopts;
1230 if (name == "sourceserifpro") {
1231 h_font_roman[0] = "ADOBESourceSerifPro";
1232 for (auto const & opt : allopts) {
1234 h_font_roman_osf = "true";
1242 h_font_roman_opts = xopts;
1250 // By default, we use the package name as LyX font name,
1251 // so this only needs to be reset if these names differ.
1252 // Also, we handle the scaling option here generally.
1253 if (is_known(name, known_sans_font_packages)) {
1254 h_font_sans[0] = name;
1255 if (contains(opts, "scale")) {
1256 vector<string>::iterator it = allopts.begin();
1257 for (; it != allopts.end() ; ++it) {
1258 string const opt = *it;
1259 if (prefixIs(opt, "scaled=") || prefixIs(opt, "scale=")) {
1260 if (scale_as_percentage(opt, h_font_sf_scale[0])) {
1269 if (name == "biolinum" || name == "biolinum-type1") {
1270 h_font_sans[0] = "biolinum";
1271 for (auto const & opt : allopts) {
1272 if (prefixIs(opt, "osf")) {
1273 h_font_sans_osf = "true";
1281 h_font_sans_opts = xopts;
1285 if (name == "cantarell") {
1286 for (auto const & opt : allopts) {
1287 if (opt == "defaultsans")
1289 if (prefixIs(opt, "oldstyle")) {
1290 h_font_sans_osf = "true";
1298 h_font_sans_opts = xopts;
1302 if (name == "Chivo") {
1303 for (auto const & opt : allopts) {
1304 if (opt == "thin") {
1305 h_font_roman[0] = "ChivoThin";
1308 if (opt == "light") {
1309 h_font_roman[0] = "ChivoLight";
1312 if (opt == "regular") {
1313 h_font_roman[0] = "Chivo";
1316 if (opt == "medium") {
1317 h_font_roman[0] = "ChivoMedium";
1320 if (prefixIs(opt, "oldstyle")) {
1321 h_font_sans_osf = "true";
1329 h_font_sans_opts = xopts;
1333 if (name == "PTSans") {
1334 h_font_sans[0] = "PTSans-TLF";
1337 if (name == "FiraSans") {
1338 h_font_sans_osf = "true";
1339 for (auto const & opt : allopts) {
1340 if (opt == "book") {
1341 h_font_sans[0] = "FiraSansBook";
1344 if (opt == "thin") {
1347 if (opt == "extralight") {
1348 h_font_sans[0] = "FiraSansExtralight";
1351 if (opt == "light") {
1352 h_font_sans[0] = "FiraSansLight";
1355 if (opt == "ultralight") {
1356 h_font_sans[0] = "FiraSansUltralight";
1359 if (opt == "thin") {
1360 h_font_sans[0] = "FiraSansThin";
1363 if (opt == "lf" || opt == "lining") {
1364 h_font_sans_osf = "false";
1372 h_font_sans_opts = xopts;
1376 if (name == "plex-sans") {
1377 h_font_sans[0] = "IBMPlexSans";
1378 for (auto const & opt : allopts) {
1379 if (opt == "condensed") {
1380 h_font_sans[0] = "IBMPlexSansCondensed";
1383 if (opt == "thin") {
1384 h_font_sans[0] = "IBMPlexSansThin";
1387 if (opt == "extralight") {
1388 h_font_sans[0] = "IBMPlexSansExtraLight";
1391 if (opt == "light") {
1392 h_font_sans[0] = "IBMPlexSansLight";
1400 h_font_sans_opts = xopts;
1404 if (name == "noto-sans") {
1405 h_font_sans[0] = "NotoSansRegular";
1406 for (auto const & opt : allopts) {
1407 if (opt == "regular")
1409 if (opt == "medium") {
1410 h_font_sans[0] = "NotoSansMedium";
1413 if (opt == "thin") {
1414 h_font_sans[0] = "NotoSansThin";
1417 if (opt == "extralight") {
1418 h_font_sans[0] = "NotoSansExtralight";
1421 if (opt == "light") {
1422 h_font_sans[0] = "NotoSansLight";
1426 h_font_sans_osf = "true";
1434 h_font_sans_opts = xopts;
1438 if (name == "sourcesanspro") {
1439 h_font_sans[0] = "ADOBESourceSansPro";
1440 for (auto const & opt : allopts) {
1442 h_font_sans_osf = "true";
1450 h_font_sans_opts = xopts;
1458 // By default, we use the package name as LyX font name,
1459 // so this only needs to be reset if these names differ.
1460 // Also, we handle the scaling option here generally.
1461 if (is_known(name, known_typewriter_font_packages)) {
1462 h_font_typewriter[0] = name;
1463 if (contains(opts, "scale")) {
1464 vector<string>::iterator it = allopts.begin();
1465 for (; it != allopts.end() ; ++it) {
1466 string const opt = *it;
1467 if (prefixIs(opt, "scaled=") || prefixIs(opt, "scale=")) {
1468 if (scale_as_percentage(opt, h_font_tt_scale[0])) {
1477 if (name == "libertineMono" || name == "libertineMono-type1")
1478 h_font_typewriter[0] = "libertine-mono";
1480 if (name == "FiraMono") {
1481 h_font_typewriter_osf = "true";
1482 for (auto const & opt : allopts) {
1483 if (opt == "lf" || opt == "lining") {
1484 h_font_typewriter_osf = "false";
1492 h_font_typewriter_opts = xopts;
1496 if (name == "PTMono")
1497 h_font_typewriter[0] = "PTMono-TLF";
1499 if (name == "plex-mono") {
1500 h_font_typewriter[0] = "IBMPlexMono";
1501 for (auto const & opt : allopts) {
1502 if (opt == "thin") {
1503 h_font_typewriter[0] = "IBMPlexMonoThin";
1506 if (opt == "extralight") {
1507 h_font_typewriter[0] = "IBMPlexMonoExtraLight";
1510 if (opt == "light") {
1511 h_font_typewriter[0] = "IBMPlexMonoLight";
1519 h_font_typewriter_opts = xopts;
1523 if (name == "noto-mono") {
1524 h_font_typewriter[0] = "NotoMonoRegular";
1525 for (auto const & opt : allopts) {
1526 if (opt == "regular")
1533 h_font_typewriter_opts = xopts;
1537 if (name == "sourcecodepro") {
1538 h_font_typewriter[0] = "ADOBESourceCodePro";
1539 for (auto const & opt : allopts) {
1541 h_font_typewriter_osf = "true";
1549 h_font_typewriter_opts = xopts;
1557 // By default, we use the package name as LyX font name,
1558 // so this only needs to be reset if these names differ.
1559 if (is_known(name, known_math_font_packages))
1560 h_font_math[0] = name;
1562 if (name == "newtxmath") {
1564 h_font_math[0] = "newtxmath";
1565 else if (opts == "garamondx")
1566 h_font_math[0] = "garamondx-ntxm";
1567 else if (opts == "libertine")
1568 h_font_math[0] = "libertine-ntxm";
1569 else if (opts == "minion")
1570 h_font_math[0] = "minion-ntxm";
1571 else if (opts == "cochineal")
1572 h_font_math[0] = "cochineal-ntxm";
1575 if (name == "iwona")
1577 h_font_math[0] = "iwona-math";
1579 if (name == "kurier")
1581 h_font_math[0] = "kurier-math";
1583 // after the detection and handling of special cases, we can remove the
1584 // fonts, otherwise they would appear in the preamble, see bug #7856
1585 if (is_known(name, known_roman_font_packages) || is_known(name, known_sans_font_packages)
1586 || is_known(name, known_typewriter_font_packages) || is_known(name, known_math_font_packages))
1588 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
1589 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
1590 name == "esint" || name == "mhchem" || name == "mathdots" ||
1591 name == "mathtools" || name == "stackrel" ||
1592 name == "stmaryrd" || name == "undertilde") {
1593 h_use_packages[name] = "2";
1594 registerAutomaticallyLoadedPackage(name);
1597 else if (name == "babel") {
1598 h_language_package = "default";
1599 // One might think we would have to do nothing if babel is loaded
1600 // without any options to prevent pollution of the preamble with this
1601 // babel call in every roundtrip.
1602 // But the user could have defined babel-specific things afterwards. So
1603 // we need to keep it in the preamble to prevent cases like bug #7861.
1604 if (!opts.empty()) {
1605 // check if more than one option was used - used later for inputenc
1606 if (options.begin() != options.end() - 1)
1607 one_language = false;
1608 // babel takes the last language of the option of its \usepackage
1609 // call as document language. If there is no such language option, the
1610 // last language in the documentclass options is used.
1611 handle_opt(options, known_languages, h_language);
1612 // translate the babel name to a LyX name
1613 h_language = babel2lyx(h_language);
1614 if (h_language == "japanese") {
1615 // For Japanese, the encoding isn't indicated in the source
1616 // file, and there's really not much we can do. We could
1617 // 1) offer a list of possible encodings to choose from, or
1618 // 2) determine the encoding of the file by inspecting it.
1619 // For the time being, we leave the encoding alone so that
1620 // we don't get iconv errors when making a wrong guess, and
1621 // we will output a note at the top of the document
1622 // explaining what to do.
1623 Encoding const * const enc = encodings.fromIconvName(
1624 p.getEncoding(), Encoding::japanese, false);
1626 h_inputencoding = enc->name();
1627 is_nonCJKJapanese = true;
1628 // in this case babel can be removed from the preamble
1629 registerAutomaticallyLoadedPackage("babel");
1631 // If babel is called with options, LyX puts them by default into the
1632 // document class options. This works for most languages, except
1633 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
1634 // perhaps in future others.
1635 // Therefore keep the babel call as it is as the user might have
1637 string const babelcall = "\\usepackage[" + opts + "]{babel}\n";
1638 if (!contains(h_preamble.str(), babelcall))
1639 h_preamble << babelcall;
1641 delete_opt(options, known_languages);
1643 if (!contains(h_preamble.str(), "\\usepackage{babel}\n"))
1644 h_preamble << "\\usepackage{babel}\n";
1645 explicit_babel = true;
1649 else if (name == "polyglossia") {
1650 h_language_package = "default";
1651 h_default_output_format = "pdf4";
1652 h_use_non_tex_fonts = true;
1654 registerAutomaticallyLoadedPackage("xunicode");
1655 if (h_inputencoding == "auto-legacy")
1656 p.setEncoding("UTF-8");
1659 else if (name == "CJK") {
1660 // set the encoding to "auto-legacy" because it might be set to "auto-legacy-plain" by the babel handling
1661 // and this would not be correct for CJK
1662 if (h_inputencoding == "auto-legacy-plain")
1663 h_inputencoding = "auto-legacy";
1664 registerAutomaticallyLoadedPackage("CJK");
1667 else if (name == "CJKutf8") {
1668 h_inputencoding = "utf8-cjk";
1669 p.setEncoding("UTF-8");
1670 registerAutomaticallyLoadedPackage("CJKutf8");
1673 else if (name == "fontenc") {
1674 h_fontencoding = getStringFromVector(options, ",");
1678 else if (name == "inputenc" || name == "luainputenc") {
1679 // h_inputencoding is only set when there is not more than one
1680 // inputenc option because otherwise h_inputencoding must be
1681 // set to "auto-legacy" (the default encodings of the document's languages)
1682 // Therefore check that exactly one option is passed to inputenc.
1683 // It is also only set when there is not more than one babel
1685 if (!options.empty()) {
1686 string const encoding = options.back();
1687 Encoding const * const enc = encodings.fromLaTeXName(
1688 encoding, Encoding::inputenc, true);
1690 if (!detectEncoding)
1691 cerr << "Unknown encoding " << encoding
1692 << ". Ignoring." << std::endl;
1694 if (!enc->unsafe() && options.size() == 1 && one_language == true)
1695 h_inputencoding = enc->name();
1696 p.setEncoding(enc->iconvName());
1702 else if (name == "srcltx") {
1703 h_output_sync = "1";
1704 if (!opts.empty()) {
1705 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
1708 h_output_sync_macro = "\\usepackage{srcltx}";
1711 else if (is_known(name, known_old_language_packages)) {
1712 // known language packages from the times before babel
1713 // if they are found and not also babel, they will be used as
1714 // custom language package
1715 h_language_package = "\\usepackage{" + name + "}";
1718 else if (name == "lyxskak") {
1719 // ignore this and its options
1720 const char * const o[] = {"ps", "mover", 0};
1721 delete_opt(options, o);
1724 else if (name == "parskip" && options.size() < 2 && (opts.empty() || prefixIs(opts, "skip="))) {
1726 h_paragraph_separation = "halfline";
1728 if (opts == "skip=\\smallskipamount")
1729 h_defskip = "smallskip";
1730 else if (opts == "skip=\\medskipamount")
1731 h_defskip = "medskip";
1732 else if (opts == "skip=\\bigskipamount")
1733 h_defskip = "bigskip";
1734 else if (opts == "skip=\\baselineskip")
1735 h_defskip = "fullline";
1738 h_paragraph_separation = "skip";
1742 else if (is_known(name, known_lyx_packages) && options.empty()) {
1743 if (name == "splitidx")
1744 h_use_indices = "true";
1745 else if (name == "minted")
1746 h_use_minted = true;
1747 else if (name == "refstyle")
1748 h_use_refstyle = true;
1749 else if (name == "prettyref")
1750 h_use_refstyle = false;
1752 if (!in_lyx_preamble) {
1753 h_preamble << package_beg_sep << name
1754 << package_mid_sep << "\\usepackage{"
1756 if (p.next_token().cat() == catNewline ||
1757 (p.next_token().cat() == catSpace &&
1758 p.next_next_token().cat() == catNewline))
1760 h_preamble << package_end_sep;
1764 else if (name == "geometry")
1765 handle_geometry(options);
1767 else if (name == "subfig")
1768 ; // ignore this FIXME: Use the package separator mechanism instead
1770 else if (char const * const * where = is_known(name, known_languages))
1771 h_language = known_coded_languages[where - known_languages];
1773 else if (name == "natbib") {
1774 h_biblio_style = "plainnat";
1775 h_cite_engine = "natbib";
1776 h_cite_engine_type = "authoryear";
1777 vector<string>::iterator it =
1778 find(options.begin(), options.end(), "authoryear");
1779 if (it != options.end())
1782 it = find(options.begin(), options.end(), "numbers");
1783 if (it != options.end()) {
1784 h_cite_engine_type = "numerical";
1788 if (!options.empty())
1789 h_biblio_options = join(options, ",");
1792 else if (name == "biblatex") {
1793 h_biblio_style = "plainnat";
1794 h_cite_engine = "biblatex";
1795 h_cite_engine_type = "authoryear";
1797 vector<string>::iterator it =
1798 find(options.begin(), options.end(), "natbib");
1799 if (it != options.end()) {
1801 h_cite_engine = "biblatex-natbib";
1803 opt = process_keyval_opt(options, "natbib");
1805 h_cite_engine = "biblatex-natbib";
1807 opt = process_keyval_opt(options, "style");
1809 h_biblatex_citestyle = opt;
1810 h_biblatex_bibstyle = opt;
1812 opt = process_keyval_opt(options, "citestyle");
1814 h_biblatex_citestyle = opt;
1815 opt = process_keyval_opt(options, "bibstyle");
1817 h_biblatex_bibstyle = opt;
1819 opt = process_keyval_opt(options, "refsection");
1821 if (opt == "none" || opt == "part"
1822 || opt == "chapter" || opt == "section"
1823 || opt == "subsection")
1826 cerr << "Ignoring unknown refsection value '"
1829 opt = process_keyval_opt(options, "bibencoding");
1832 if (!options.empty()) {
1833 h_biblio_options = join(options, ",");
1838 else if (name == "jurabib") {
1839 h_biblio_style = "jurabib";
1840 h_cite_engine = "jurabib";
1841 h_cite_engine_type = "authoryear";
1842 if (!options.empty())
1843 h_biblio_options = join(options, ",");
1846 else if (name == "bibtopic")
1847 h_use_bibtopic = "true";
1849 else if (name == "chapterbib")
1850 h_multibib = "child";
1852 else if (name == "hyperref")
1853 handle_hyperref(options);
1855 else if (name == "algorithm2e") {
1856 // Load "algorithm2e" module
1857 addModule("algorithm2e");
1858 // Add the package options to the global document options
1859 if (!options.empty()) {
1860 if (h_options.empty())
1861 h_options = join(options, ",");
1863 h_options += ',' + join(options, ",");
1866 else if (name == "microtype") {
1867 //we internally support only microtype without params
1868 if (options.empty())
1869 h_use_microtype = "true";
1871 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1874 else if (name == "lineno") {
1875 h_use_lineno = "true";
1876 if (!options.empty()) {
1877 h_lineno_options = join(options, ",");
1882 else if (name == "changebar")
1883 h_output_changes = "true";
1885 else if (!in_lyx_preamble) {
1886 if (options.empty())
1887 h_preamble << "\\usepackage{" << name << '}';
1889 h_preamble << "\\usepackage[" << opts << "]{"
1893 if (p.next_token().cat() == catNewline ||
1894 (p.next_token().cat() == catSpace &&
1895 p.next_next_token().cat() == catNewline))
1899 // We need to do something with the options...
1900 if (!options.empty() && !detectEncoding)
1901 cerr << "Ignoring options '" << join(options, ",")
1902 << "' of package " << name << '.' << endl;
1904 // remove the whitespace
1909 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1912 Token t = p.get_token();
1913 if (t.cat() == catEscape &&
1914 is_known(t.cs(), known_if_commands))
1915 handle_if(p, in_lyx_preamble);
1917 if (!in_lyx_preamble)
1918 h_preamble << t.asInput();
1919 if (t.cat() == catEscape && t.cs() == "fi")
1926 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1928 if (contains(h_float_placement, "H"))
1929 registerAutomaticallyLoadedPackage("float");
1930 if (h_spacing != "single" && h_spacing != "default")
1931 registerAutomaticallyLoadedPackage("setspace");
1932 if (h_use_packages["amsmath"] == "2") {
1933 // amsbsy and amstext are already provided by amsmath
1934 registerAutomaticallyLoadedPackage("amsbsy");
1935 registerAutomaticallyLoadedPackage("amstext");
1938 // output the LyX file settings
1939 // Important: Keep the version formatting in sync with LyX and
1940 // lyx2lyx (bug 7951)
1941 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1942 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1943 << lyx_version_minor << '\n'
1944 << "\\lyxformat " << LYX_FORMAT << '\n'
1945 << "\\begin_document\n"
1946 << "\\begin_header\n"
1947 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1948 << "\\origin " << origin << "\n"
1949 << "\\textclass " << h_textclass << "\n";
1950 string const raw = subdoc ? empty_string() : h_preamble.str();
1952 os << "\\begin_preamble\n";
1953 for (string::size_type i = 0; i < raw.size(); ++i) {
1954 if (raw[i] == package_beg_sep) {
1955 // Here follows some package loading code that
1956 // must be skipped if the package is loaded
1958 string::size_type j = raw.find(package_mid_sep, i);
1959 if (j == string::npos)
1961 string::size_type k = raw.find(package_end_sep, j);
1962 if (k == string::npos)
1964 string const package = raw.substr(i + 1, j - i - 1);
1965 string const replacement = raw.substr(j + 1, k - j - 1);
1966 if (auto_packages.find(package) == auto_packages.end())
1972 os << "\n\\end_preamble\n";
1974 if (!h_options.empty())
1975 os << "\\options " << h_options << "\n";
1976 os << "\\use_default_options " << h_use_default_options << "\n";
1977 if (!used_modules.empty()) {
1978 os << "\\begin_modules\n";
1979 vector<string>::const_iterator const end = used_modules.end();
1980 vector<string>::const_iterator it = used_modules.begin();
1981 for (; it != end; ++it)
1983 os << "\\end_modules\n";
1985 if (!h_includeonlys.empty()) {
1986 os << "\\begin_includeonly\n";
1987 for (auto const & iofile : h_includeonlys)
1988 os << iofile << '\n';
1989 os << "\\end_includeonly\n";
1991 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1992 << "\\language " << h_language << "\n"
1993 << "\\language_package " << h_language_package << "\n"
1994 << "\\inputencoding " << h_inputencoding << "\n"
1995 << "\\fontencoding " << h_fontencoding << "\n"
1996 << "\\font_roman \"" << h_font_roman[0]
1997 << "\" \"" << h_font_roman[1] << "\"\n"
1998 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1999 << "\\font_typewriter \"" << h_font_typewriter[0]
2000 << "\" \"" << h_font_typewriter[1] << "\"\n"
2001 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
2002 << "\\font_default_family " << h_font_default_family << "\n"
2003 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
2004 << "\\font_sc " << h_font_sc << "\n"
2005 << "\\font_roman_osf " << h_font_roman_osf << "\n"
2006 << "\\font_sans_osf " << h_font_sans_osf << "\n"
2007 << "\\font_typewriter_osf " << h_font_typewriter_osf << "\n";
2008 if (!h_font_roman_opts.empty())
2009 os << "\\font_roman_opts \"" << h_font_roman_opts << "\"" << '\n';
2010 os << "\\font_sf_scale " << h_font_sf_scale[0]
2011 << ' ' << h_font_sf_scale[1] << '\n';
2012 if (!h_font_sans_opts.empty())
2013 os << "\\font_sans_opts \"" << h_font_sans_opts << "\"" << '\n';
2014 os << "\\font_tt_scale " << h_font_tt_scale[0]
2015 << ' ' << h_font_tt_scale[1] << '\n';
2016 if (!h_font_cjk.empty())
2017 os << "\\font_cjk " << h_font_cjk << '\n';
2018 if (!h_font_typewriter_opts.empty())
2019 os << "\\font_typewriter_opts \"" << h_font_typewriter_opts << "\"" << '\n';
2020 os << "\\use_microtype " << h_use_microtype << '\n'
2021 << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
2022 << "\\graphics " << h_graphics << '\n'
2023 << "\\default_output_format " << h_default_output_format << "\n"
2024 << "\\output_sync " << h_output_sync << "\n";
2025 if (h_output_sync == "1")
2026 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
2027 os << "\\bibtex_command " << h_bibtex_command << "\n"
2028 << "\\index_command " << h_index_command << "\n";
2029 if (!h_float_placement.empty())
2030 os << "\\float_placement " << h_float_placement << "\n";
2031 os << "\\paperfontsize " << h_paperfontsize << "\n"
2032 << "\\spacing " << h_spacing << "\n"
2033 << "\\use_hyperref " << h_use_hyperref << '\n';
2034 if (h_use_hyperref == "true") {
2035 if (!h_pdf_title.empty())
2036 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
2037 if (!h_pdf_author.empty())
2038 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
2039 if (!h_pdf_subject.empty())
2040 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
2041 if (!h_pdf_keywords.empty())
2042 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
2043 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
2044 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
2045 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
2046 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
2047 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
2048 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
2049 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
2050 "\\pdf_backref " << h_pdf_backref << "\n"
2051 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
2052 if (!h_pdf_pagemode.empty())
2053 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
2054 if (!h_pdf_quoted_options.empty())
2055 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
2057 os << "\\papersize " << h_papersize << "\n"
2058 << "\\use_geometry " << h_use_geometry << '\n';
2059 for (map<string, string>::const_iterator it = h_use_packages.begin();
2060 it != h_use_packages.end(); ++it)
2061 os << "\\use_package " << it->first << ' ' << it->second << '\n';
2062 os << "\\cite_engine " << h_cite_engine << '\n'
2063 << "\\cite_engine_type " << h_cite_engine_type << '\n'
2064 << "\\biblio_style " << h_biblio_style << "\n"
2065 << "\\use_bibtopic " << h_use_bibtopic << "\n";
2066 if (!h_biblio_options.empty())
2067 os << "\\biblio_options " << h_biblio_options << "\n";
2068 if (!h_biblatex_bibstyle.empty())
2069 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
2070 if (!h_biblatex_citestyle.empty())
2071 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
2072 if (!h_multibib.empty())
2073 os << "\\multibib " << h_multibib << "\n";
2074 os << "\\use_indices " << h_use_indices << "\n"
2075 << "\\paperorientation " << h_paperorientation << '\n'
2076 << "\\suppress_date " << h_suppress_date << '\n'
2077 << "\\justification " << h_justification << '\n'
2078 << "\\use_refstyle " << h_use_refstyle << '\n'
2079 << "\\use_minted " << h_use_minted << '\n'
2080 << "\\use_lineno " << h_use_lineno << '\n';
2081 if (!h_lineno_options.empty())
2082 os << "\\lineno_options " << h_lineno_options << '\n';
2083 if (!h_fontcolor.empty())
2084 os << "\\fontcolor " << h_fontcolor << '\n';
2085 if (!h_notefontcolor.empty())
2086 os << "\\notefontcolor " << h_notefontcolor << '\n';
2087 if (!h_backgroundcolor.empty())
2088 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
2089 if (!h_boxbgcolor.empty())
2090 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
2091 if (index_number != 0)
2092 for (int i = 0; i < index_number; i++) {
2093 os << "\\index " << h_index[i] << '\n'
2094 << "\\shortcut " << h_shortcut[i] << '\n'
2095 << "\\color " << h_color << '\n'
2099 os << "\\index " << h_index[0] << '\n'
2100 << "\\shortcut " << h_shortcut[0] << '\n'
2101 << "\\color " << h_color << '\n'
2105 << "\\secnumdepth " << h_secnumdepth << "\n"
2106 << "\\tocdepth " << h_tocdepth << "\n"
2107 << "\\paragraph_separation " << h_paragraph_separation << "\n";
2108 if (h_paragraph_separation == "skip")
2109 os << "\\defskip " << h_defskip << "\n";
2111 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
2112 os << "\\is_math_indent " << h_is_mathindent << "\n";
2113 if (!h_mathindentation.empty())
2114 os << "\\math_indentation " << h_mathindentation << "\n";
2115 os << "\\math_numbering_side " << h_math_numbering_side << "\n";
2116 os << "\\quotes_style " << h_quotes_style << "\n"
2117 << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
2118 << "\\papercolumns " << h_papercolumns << "\n"
2119 << "\\papersides " << h_papersides << "\n"
2120 << "\\paperpagestyle " << h_paperpagestyle << "\n";
2121 if (!h_listings_params.empty())
2122 os << "\\listings_params " << h_listings_params << "\n";
2123 os << "\\tracking_changes " << h_tracking_changes << "\n"
2124 << "\\output_changes " << h_output_changes << "\n"
2125 << "\\change_bars " << h_change_bars << "\n"
2126 << "\\html_math_output " << h_html_math_output << "\n"
2127 << "\\html_css_as_file " << h_html_css_as_file << "\n"
2128 << "\\html_be_strict " << h_html_be_strict << "\n"
2129 << "\\docbook_table_output " << h_docbook_table_output << "\n"
2131 << "\\end_header\n\n"
2132 << "\\begin_body\n";
2137 void Preamble::parse(Parser & p, string const & forceclass,
2138 TeX2LyXDocClass & tc)
2140 // initialize fixed types
2141 special_columns_['D'] = 3;
2142 parse(p, forceclass, false, tc);
2146 void Preamble::parse(Parser & p, string const & forceclass,
2147 bool detectEncoding, TeX2LyXDocClass & tc)
2149 bool is_full_document = false;
2150 bool is_lyx_file = false;
2151 bool in_lyx_preamble = false;
2152 bool class_set = false;
2154 // determine whether this is a full document or a fragment for inclusion
2156 Token const & t = p.get_token();
2158 if (t.cat() == catEscape && t.cs() == "documentclass") {
2159 is_full_document = true;
2165 if (detectEncoding && !is_full_document)
2168 while (is_full_document && p.good()) {
2169 if (detectEncoding && h_inputencoding != "auto-legacy" &&
2170 h_inputencoding != "auto-legacy-plain")
2173 // Force textclass if the user wanted it
2174 if (!forceclass.empty()) {
2175 setTextClass(forceclass, tc);
2179 Token const & t = p.get_token();
2182 if (!detectEncoding)
2183 cerr << "t: " << t << '\n';
2189 if (!in_lyx_preamble &&
2190 (t.cat() == catLetter ||
2191 t.cat() == catSuper ||
2192 t.cat() == catSub ||
2193 t.cat() == catOther ||
2194 t.cat() == catMath ||
2195 t.cat() == catActive ||
2196 t.cat() == catBegin ||
2197 t.cat() == catEnd ||
2198 t.cat() == catAlign ||
2199 t.cat() == catParameter)) {
2200 h_preamble << t.cs();
2204 if (!in_lyx_preamble &&
2205 (t.cat() == catSpace || t.cat() == catNewline)) {
2206 h_preamble << t.asInput();
2210 if (t.cat() == catComment) {
2211 static regex const islyxfile("%% LyX .* created this file");
2212 static regex const usercommands("User specified LaTeX commands");
2214 string const comment = t.asInput();
2216 // magically switch encoding default if it looks like XeLaTeX
2217 static string const magicXeLaTeX =
2218 "% This document must be compiled with XeLaTeX ";
2219 if (comment.size() > magicXeLaTeX.size()
2220 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
2221 && h_inputencoding == "auto-legacy") {
2222 if (!detectEncoding)
2223 cerr << "XeLaTeX comment found, switching to UTF8\n";
2224 h_inputencoding = "utf8";
2227 if (regex_search(comment, sub, islyxfile)) {
2229 in_lyx_preamble = true;
2230 } else if (is_lyx_file
2231 && regex_search(comment, sub, usercommands))
2232 in_lyx_preamble = false;
2233 else if (!in_lyx_preamble)
2234 h_preamble << t.asInput();
2238 if (t.cs() == "PassOptionsToPackage") {
2239 string const poptions = p.getArg('{', '}');
2240 string const package = p.verbatim_item();
2241 extra_package_options_.insert(make_pair(package, poptions));
2245 if (t.cs() == "pagestyle") {
2246 h_paperpagestyle = p.verbatim_item();
2250 if (t.cs() == "setdefaultlanguage") {
2252 // We don't yet care about non-language variant options
2253 // because LyX doesn't support this yet, see bug #8214
2255 string langopts = p.getOpt();
2256 // check if the option contains a variant, if yes, extract it
2257 string::size_type pos_var = langopts.find("variant");
2258 string::size_type i = langopts.find(',', pos_var);
2259 string::size_type k = langopts.find('=', pos_var);
2260 if (pos_var != string::npos){
2262 if (i == string::npos)
2263 variant = langopts.substr(k + 1, langopts.length() - k - 2);
2265 variant = langopts.substr(k + 1, i - k - 1);
2266 h_language = variant;
2270 h_language = p.verbatim_item();
2271 //finally translate the poyglossia name to a LyX name
2272 h_language = polyglossia2lyx(h_language);
2276 if (t.cs() == "setotherlanguage") {
2277 // We don't yet care about the option because LyX doesn't
2278 // support this yet, see bug #8214
2279 p.hasOpt() ? p.getOpt() : string();
2284 if (t.cs() == "setmainfont") {
2285 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2286 h_font_roman[1] = p.getArg('{', '}');
2287 if (!fontopts.empty()) {
2288 vector<string> opts = getVectorFromString(fontopts);
2290 for (auto const & opt : opts) {
2291 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2294 if (!fontopts.empty())
2298 h_font_roman_opts = fontopts;
2303 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
2304 // LyX currently only supports the scale option
2305 string scale, fontopts;
2307 fontopts = p.getArg('[', ']');
2308 if (!fontopts.empty()) {
2309 vector<string> opts = getVectorFromString(fontopts);
2311 for (auto const & opt : opts) {
2312 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2315 if (prefixIs(opt, "Scale=")) {
2316 scale_as_percentage(opt, scale);
2319 if (!fontopts.empty())
2325 if (t.cs() == "setsansfont") {
2327 h_font_sf_scale[1] = scale;
2328 h_font_sans[1] = p.getArg('{', '}');
2329 if (!fontopts.empty())
2330 h_font_sans_opts = fontopts;
2333 h_font_tt_scale[1] = scale;
2334 h_font_typewriter[1] = p.getArg('{', '}');
2335 if (!fontopts.empty())
2336 h_font_typewriter_opts = fontopts;
2341 if (t.cs() == "babelfont") {
2343 h_use_non_tex_fonts = true;
2344 h_language_package = "babel";
2345 if (h_inputencoding == "auto-legacy")
2346 p.setEncoding("UTF-8");
2347 // we don't care about the lang option
2348 string const lang = p.hasOpt() ? p.getArg('[', ']') : string();
2349 string const family = p.getArg('{', '}');
2350 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2351 string const fontname = p.getArg('{', '}');
2352 if (lang.empty() && family == "rm") {
2353 h_font_roman[1] = fontname;
2354 if (!fontopts.empty()) {
2355 vector<string> opts = getVectorFromString(fontopts);
2357 for (auto const & opt : opts) {
2358 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2361 if (!fontopts.empty())
2365 h_font_roman_opts = fontopts;
2368 } else if (lang.empty() && (family == "sf" || family == "tt")) {
2370 if (!fontopts.empty()) {
2371 vector<string> opts = getVectorFromString(fontopts);
2373 for (auto const & opt : opts) {
2374 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2377 if (prefixIs(opt, "Scale=")) {
2378 scale_as_percentage(opt, scale);
2381 if (!fontopts.empty())
2386 if (family == "sf") {
2388 h_font_sf_scale[1] = scale;
2389 h_font_sans[1] = fontname;
2390 if (!fontopts.empty())
2391 h_font_sans_opts = fontopts;
2394 h_font_tt_scale[1] = scale;
2395 h_font_typewriter[1] = fontname;
2396 if (!fontopts.empty())
2397 h_font_typewriter_opts = fontopts;
2401 // not rm, sf or tt or lang specific
2402 h_preamble << '\\' << t.cs();
2404 h_preamble << '[' << lang << ']';
2405 h_preamble << '{' << family << '}';
2406 if (!fontopts.empty())
2407 h_preamble << '[' << fontopts << ']';
2408 h_preamble << '{' << fontname << '}' << '\n';
2413 if (t.cs() == "date") {
2414 string argument = p.getArg('{', '}');
2415 if (argument.empty())
2416 h_suppress_date = "true";
2418 h_preamble << t.asInput() << '{' << argument << '}';
2422 if (t.cs() == "color") {
2423 string const space =
2424 (p.hasOpt() ? p.getOpt() : string());
2425 string argument = p.getArg('{', '}');
2426 // check the case that a standard color is used
2427 if (space.empty() && is_known(argument, known_basic_colors)) {
2428 h_fontcolor = rgbcolor2code(argument);
2429 registerAutomaticallyLoadedPackage("color");
2430 } else if (space.empty() && argument == "document_fontcolor")
2431 registerAutomaticallyLoadedPackage("color");
2432 // check the case that LyX's document_fontcolor is defined
2433 // but not used for \color
2435 h_preamble << t.asInput();
2437 h_preamble << space;
2438 h_preamble << '{' << argument << '}';
2439 // the color might already be set because \definecolor
2440 // is parsed before this
2446 if (t.cs() == "pagecolor") {
2447 string argument = p.getArg('{', '}');
2448 // check the case that a standard color is used
2449 if (is_known(argument, known_basic_colors)) {
2450 h_backgroundcolor = rgbcolor2code(argument);
2451 } else if (argument == "page_backgroundcolor")
2452 registerAutomaticallyLoadedPackage("color");
2453 // check the case that LyX's page_backgroundcolor is defined
2454 // but not used for \pagecolor
2456 h_preamble << t.asInput() << '{' << argument << '}';
2457 // the color might already be set because \definecolor
2458 // is parsed before this
2459 h_backgroundcolor = "";
2464 if (t.cs() == "makeatletter") {
2465 // LyX takes care of this
2466 p.setCatcode('@', catLetter);
2470 if (t.cs() == "makeatother") {
2471 // LyX takes care of this
2472 p.setCatcode('@', catOther);
2476 if (t.cs() == "makeindex") {
2477 // LyX will re-add this if a print index command is found
2482 if (t.cs() == "newindex") {
2483 string const indexname = p.getArg('[', ']');
2484 string const shortcut = p.verbatim_item();
2485 if (!indexname.empty())
2486 h_index[index_number] = indexname;
2488 h_index[index_number] = shortcut;
2489 h_shortcut[index_number] = shortcut;
2495 if (t.cs() == "addbibresource") {
2496 string const options = p.getArg('[', ']');
2497 string const arg = removeExtension(p.getArg('{', '}'));
2498 if (!options.empty()) {
2499 // check if the option contains a bibencoding, if yes, extract it
2500 string::size_type pos = options.find("bibencoding=");
2502 if (pos != string::npos) {
2503 string::size_type i = options.find(',', pos);
2504 if (i == string::npos)
2505 encoding = options.substr(pos + 1);
2507 encoding = options.substr(pos, i - pos);
2508 pos = encoding.find('=');
2509 if (pos == string::npos)
2512 encoding = encoding.substr(pos + 1);
2514 if (!encoding.empty())
2515 biblatex_encodings.push_back(normalize_filename(arg) + ' ' + encoding);
2517 biblatex_bibliographies.push_back(arg);
2521 if (t.cs() == "bibliography") {
2522 vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
2523 biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
2527 if (t.cs() == "RS@ifundefined") {
2528 string const name = p.verbatim_item();
2529 string const body1 = p.verbatim_item();
2530 string const body2 = p.verbatim_item();
2531 // only non-lyxspecific stuff
2532 if (in_lyx_preamble &&
2533 (name == "subsecref" || name == "thmref" || name == "lemref"))
2537 ss << '\\' << t.cs();
2538 ss << '{' << name << '}'
2539 << '{' << body1 << '}'
2540 << '{' << body2 << '}';
2541 h_preamble << ss.str();
2546 if (t.cs() == "AtBeginDocument") {
2547 string const name = p.verbatim_item();
2548 // only non-lyxspecific stuff
2549 if (in_lyx_preamble &&
2550 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
2551 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
2552 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
2553 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
2554 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
2555 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
2556 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
2557 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
2558 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
2559 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
2560 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
2561 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
2562 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
2563 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
2564 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
2568 ss << '\\' << t.cs();
2569 ss << '{' << name << '}';
2570 h_preamble << ss.str();
2575 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
2576 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
2577 || t.cs() == "providecommand" || t.cs() == "providecommandx"
2578 || t.cs() == "DeclareRobustCommand"
2579 || t.cs() == "DeclareRobustCommandx"
2580 || t.cs() == "ProvideTextCommandDefault"
2581 || t.cs() == "DeclareMathAccent") {
2583 if (p.next_token().character() == '*') {
2587 string const name = p.verbatim_item();
2588 string const opt1 = p.getFullOpt();
2589 string const opt2 = p.getFullOpt();
2590 string const body = p.verbatim_item();
2591 // store the in_lyx_preamble setting
2592 bool const was_in_lyx_preamble = in_lyx_preamble;
2594 if (name == "\\rmdefault")
2595 if (is_known(body, known_roman_font_packages)) {
2596 h_font_roman[0] = body;
2598 in_lyx_preamble = true;
2600 if (name == "\\sfdefault") {
2601 if (is_known(body, known_sans_font_packages)) {
2602 h_font_sans[0] = body;
2604 in_lyx_preamble = true;
2606 if (body == "LibertinusSans-OsF") {
2607 h_font_sans[0] = "LibertinusSans-LF";
2608 h_font_sans_osf = "true";
2610 in_lyx_preamble = true;
2613 if (name == "\\ttdefault")
2614 if (is_known(body, known_typewriter_font_packages)) {
2615 h_font_typewriter[0] = body;
2617 in_lyx_preamble = true;
2619 if (name == "\\familydefault") {
2620 string family = body;
2621 // remove leading "\"
2622 h_font_default_family = family.erase(0,1);
2624 in_lyx_preamble = true;
2626 if (name == "\\LibertinusSans@scale") {
2627 if (isStrDbl(body)) {
2628 h_font_sf_scale[0] = convert<string>(
2629 static_cast<int>(100 * convert<double>(body)));
2632 if (name == "\\LibertinusMono@scale") {
2633 if (isStrDbl(body)) {
2634 h_font_tt_scale[0] = convert<string>(
2635 static_cast<int>(100 * convert<double>(body)));
2639 // remove LyX-specific definitions that are re-added by LyX
2641 // \lyxline is an ancient command that is converted by tex2lyx into
2642 // a \rule therefore remove its preamble code
2643 if (name == "\\lyxdot" || name == "\\lyxarrow"
2644 || name == "\\lyxline" || name == "\\LyX") {
2646 in_lyx_preamble = true;
2649 // Add the command to the known commands
2650 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
2652 // only non-lyxspecific stuff
2653 if (!in_lyx_preamble) {
2655 ss << '\\' << t.cs();
2658 ss << '{' << name << '}' << opt1 << opt2
2659 << '{' << body << "}";
2660 if (prefixIs(t.cs(), "renew") || !contains(h_preamble.str(), ss.str()))
2661 h_preamble << ss.str();
2663 ostream & out = in_preamble ? h_preamble : os;
2664 out << "\\" << t.cs() << "{" << name << "}"
2665 << opts << "{" << body << "}";
2668 // restore the in_lyx_preamble setting
2669 in_lyx_preamble = was_in_lyx_preamble;
2673 if (t.cs() == "documentclass") {
2674 vector<string>::iterator it;
2675 vector<string> opts = split_options(p.getArg('[', ']'));
2676 // FIXME This does not work for classes that have a
2677 // different name in LyX than in LaTeX
2678 string const tclass = p.getArg('{', '}');
2680 // Only set text class if a class hasn't been forced
2681 // (this was set above)
2683 // textclass needs to be set at this place (if not already done)
2684 // as we need to know it for other parameters
2685 // (such as class-dependent paper size)
2686 setTextClass(tclass, tc);
2691 // Try those who are (most likely) known to all packages first
2692 handle_opt(opts, known_fontsizes, h_paperfontsize);
2693 delete_opt(opts, known_fontsizes);
2694 // delete "pt" at the end
2695 string::size_type i = h_paperfontsize.find("pt");
2696 if (i != string::npos)
2697 h_paperfontsize.erase(i);
2698 // Now those known specifically to the class
2699 vector<string> class_fsizes = getVectorFromString(tc.opt_fontsize(), "|");
2700 string const fsize_format = tc.fontsizeformat();
2701 for (auto const & fsize : class_fsizes) {
2702 string latexsize = subst(fsize_format, "$$s", fsize);
2703 vector<string>::iterator it = find(opts.begin(), opts.end(), latexsize);
2704 if (it != opts.end()) {
2705 h_paperfontsize = fsize;
2711 // The documentclass options are always parsed before the options
2712 // of the babel call so that a language cannot overwrite the babel
2714 handle_opt(opts, known_languages, h_language);
2715 delete_opt(opts, known_languages);
2718 if ((it = find(opts.begin(), opts.end(), "fleqn"))
2720 h_is_mathindent = "1";
2723 // formula numbering side
2724 if ((it = find(opts.begin(), opts.end(), "leqno"))
2726 h_math_numbering_side = "left";
2729 else if ((it = find(opts.begin(), opts.end(), "reqno"))
2731 h_math_numbering_side = "right";
2735 // paper orientation
2736 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
2737 h_paperorientation = "landscape";
2741 if ((it = find(opts.begin(), opts.end(), "oneside"))
2746 if ((it = find(opts.begin(), opts.end(), "twoside"))
2752 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
2754 h_papercolumns = "1";
2757 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
2759 h_papercolumns = "2";
2763 // some size options are known by the document class, other sizes
2764 // are handled by the \geometry command of the geometry package
2765 vector<string> class_psizes = getVectorFromString(tc.opt_pagesize(), "|");
2766 string const psize_format = tc.pagesizeformat();
2767 for (auto const & psize : class_psizes) {
2768 string latexsize = subst(psize_format, "$$s", psize);
2769 vector<string>::iterator it = find(opts.begin(), opts.end(), latexsize);
2770 if (it != opts.end()) {
2771 h_papersize = psize;
2775 if (psize_format == "$$spaper")
2777 // Also try with the default format since this is understood by
2779 latexsize = psize + "paper";
2780 it = find(opts.begin(), opts.end(), latexsize);
2781 if (it != opts.end()) {
2782 h_papersize = psize;
2787 // the remaining options
2788 h_options = join(opts, ",");
2792 if (t.cs() == "usepackage") {
2793 string const options = p.getArg('[', ']');
2794 string const name = p.getArg('{', '}');
2795 vector<string> vecnames;
2796 split(name, vecnames, ',');
2797 vector<string>::const_iterator it = vecnames.begin();
2798 vector<string>::const_iterator end = vecnames.end();
2799 for (; it != end; ++it)
2800 handle_package(p, trimSpaceAndEol(*it), options,
2801 in_lyx_preamble, detectEncoding);
2805 if (t.cs() == "inputencoding") {
2806 string const encoding = p.getArg('{','}');
2807 Encoding const * const enc = encodings.fromLaTeXName(
2808 encoding, Encoding::inputenc, true);
2810 if (!detectEncoding)
2811 cerr << "Unknown encoding " << encoding
2812 << ". Ignoring." << std::endl;
2815 h_inputencoding = enc->name();
2816 p.setEncoding(enc->iconvName());
2821 if (t.cs() == "newenvironment") {
2822 string const name = p.getArg('{', '}');
2823 string const opt1 = p.getFullOpt();
2824 string const opt2 = p.getFullOpt();
2825 string const beg = p.verbatim_item();
2826 string const end = p.verbatim_item();
2827 if (!in_lyx_preamble) {
2828 h_preamble << "\\newenvironment{" << name
2829 << '}' << opt1 << opt2 << '{'
2830 << beg << "}{" << end << '}';
2832 add_known_environment(name, opt1, !opt2.empty(),
2833 from_utf8(beg), from_utf8(end));
2837 if (t.cs() == "newtheorem") {
2839 if (p.next_token().character() == '*') {
2843 string const name = p.getArg('{', '}');
2844 string const opt1 = p.getFullOpt();
2845 string const opt2 = p.getFullOpt();
2846 string const body = p.verbatim_item();
2847 string const opt3 = p.getFullOpt();
2848 string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
2850 string const complete = cmd + "{" + name + '}' +
2851 opt1 + opt2 + '{' + body + '}' + opt3;
2853 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
2855 if (!in_lyx_preamble)
2856 h_preamble << complete;
2860 if (t.cs() == "def") {
2861 string name = p.get_token().cs();
2862 // In fact, name may be more than the name:
2863 // In the test case of bug 8116
2864 // name == "csname SF@gobble@opt \endcsname".
2865 // Therefore, we need to use asInput() instead of cs().
2866 while (p.next_token().cat() != catBegin)
2867 name += p.get_token().asInput();
2868 if (!in_lyx_preamble)
2869 h_preamble << "\\def\\" << name << '{'
2870 << p.verbatim_item() << "}";
2874 if (t.cs() == "newcolumntype") {
2875 string const name = p.getArg('{', '}');
2876 trimSpaceAndEol(name);
2878 string opts = p.getOpt();
2879 if (!opts.empty()) {
2880 istringstream is(string(opts, 1));
2883 special_columns_[name[0]] = nargs;
2884 h_preamble << "\\newcolumntype{" << name << "}";
2886 h_preamble << "[" << nargs << "]";
2887 h_preamble << "{" << p.verbatim_item() << "}";
2891 if (t.cs() == "setcounter") {
2892 string const name = p.getArg('{', '}');
2893 string const content = p.getArg('{', '}');
2894 if (name == "secnumdepth")
2895 h_secnumdepth = content;
2896 else if (name == "tocdepth")
2897 h_tocdepth = content;
2899 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
2903 if (t.cs() == "setlength") {
2904 string const name = p.verbatim_item();
2905 string const content = p.verbatim_item();
2906 // the paragraphs are only not indented when \parindent is set to zero
2907 if (name == "\\parindent" && content != "")
2908 h_paragraph_indentation = translate_len(content);
2909 else if (name == "\\parskip" && isPackageUsed("parskip")) {
2910 if (content == "\\smallskipamount")
2911 h_defskip = "smallskip";
2912 else if (content == "\\medskipamount")
2913 h_defskip = "medskip";
2914 else if (content == "\\bigskipamount")
2915 h_defskip = "bigskip";
2916 else if (content == "\\baselineskip")
2917 h_defskip = "fullline";
2919 h_defskip = translate_len(content);
2920 } else if (name == "\\mathindent") {
2921 h_mathindentation = translate_len(content);
2923 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2927 if (t.cs() == "onehalfspacing") {
2928 h_spacing = "onehalf";
2932 if (t.cs() == "doublespacing") {
2933 h_spacing = "double";
2937 if (t.cs() == "setstretch") {
2938 h_spacing = "other " + p.verbatim_item();
2942 if (t.cs() == "synctex") {
2943 // the scheme is \synctex=value
2944 // where value can only be "1" or "-1"
2945 h_output_sync = "1";
2946 // there can be any character behind the value (e.g. a linebreak or a '\'
2947 // therefore we extract it char by char
2949 string value = p.get_token().asInput();
2951 value += p.get_token().asInput();
2952 h_output_sync_macro = "\\synctex=" + value;
2956 if (t.cs() == "begin") {
2957 string const name = p.getArg('{', '}');
2958 if (name == "document")
2960 h_preamble << "\\begin{" << name << "}";
2964 if (t.cs() == "geometry") {
2965 vector<string> opts = split_options(p.getArg('{', '}'));
2966 handle_geometry(opts);
2970 if (t.cs() == "definecolor") {
2971 string const color = p.getArg('{', '}');
2972 string const space = p.getArg('{', '}');
2973 string const value = p.getArg('{', '}');
2974 if (color == "document_fontcolor" && space == "rgb") {
2975 RGBColor c(RGBColorFromLaTeX(value));
2976 h_fontcolor = X11hexname(c);
2977 } else if (color == "note_fontcolor" && space == "rgb") {
2978 RGBColor c(RGBColorFromLaTeX(value));
2979 h_notefontcolor = X11hexname(c);
2980 } else if (color == "page_backgroundcolor" && space == "rgb") {
2981 RGBColor c(RGBColorFromLaTeX(value));
2982 h_backgroundcolor = X11hexname(c);
2983 } else if (color == "shadecolor" && space == "rgb") {
2984 RGBColor c(RGBColorFromLaTeX(value));
2985 h_boxbgcolor = X11hexname(c);
2987 h_preamble << "\\definecolor{" << color
2988 << "}{" << space << "}{" << value
2994 if (t.cs() == "bibliographystyle") {
2995 h_biblio_style = p.verbatim_item();
2999 if (t.cs() == "jurabibsetup") {
3000 // FIXME p.getArg('{', '}') is most probably wrong (it
3001 // does not handle nested braces).
3002 // Use p.verbatim_item() instead.
3003 vector<string> jurabibsetup =
3004 split_options(p.getArg('{', '}'));
3005 // add jurabibsetup to the jurabib package options
3006 add_package("jurabib", jurabibsetup);
3007 if (!jurabibsetup.empty()) {
3008 h_preamble << "\\jurabibsetup{"
3009 << join(jurabibsetup, ",") << '}';
3014 if (t.cs() == "hypersetup") {
3015 vector<string> hypersetup =
3016 split_options(p.verbatim_item());
3017 // add hypersetup to the hyperref package options
3018 handle_hyperref(hypersetup);
3019 if (!hypersetup.empty()) {
3020 h_preamble << "\\hypersetup{"
3021 << join(hypersetup, ",") << '}';
3026 if (t.cs() == "includeonly") {
3027 vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
3028 for (auto & iofile : includeonlys) {
3029 string filename(normalize_filename(iofile));
3030 string const path = getMasterFilePath(true);
3031 // We want to preserve relative/absolute filenames,
3032 // therefore path is only used for testing
3033 if (!makeAbsPath(filename, path).exists()) {
3034 // The file extension is probably missing.
3035 // Now try to find it out.
3036 string const tex_name =
3037 find_file(filename, path,
3038 known_tex_extensions);
3039 if (!tex_name.empty())
3040 filename = tex_name;
3043 if (makeAbsPath(filename, path).exists())
3044 fix_child_filename(filename);
3046 cerr << "Warning: Could not find included file '"
3047 << filename << "'." << endl;
3048 outname = changeExtension(filename, "lyx");
3049 h_includeonlys.push_back(outname);
3054 if (is_known(t.cs(), known_if_3arg_commands)) {
3055 // prevent misparsing of \usepackage if it is used
3056 // as an argument (see e.g. our own output of
3057 // \@ifundefined above)
3058 string const arg1 = p.verbatim_item();
3059 string const arg2 = p.verbatim_item();
3060 string const arg3 = p.verbatim_item();
3061 // test case \@ifundefined{date}{}{\date{}}
3062 if (t.cs() == "@ifundefined" && arg1 == "date" &&
3063 arg2.empty() && arg3 == "\\date{}") {
3064 h_suppress_date = "true";
3065 // older tex2lyx versions did output
3066 // \@ifundefined{definecolor}{\usepackage{color}}{}
3067 } else if (t.cs() == "@ifundefined" &&
3068 arg1 == "definecolor" &&
3069 arg2 == "\\usepackage{color}" &&
3071 if (!in_lyx_preamble)
3072 h_preamble << package_beg_sep
3075 << "\\@ifundefined{definecolor}{color}{}"
3078 //\@ifundefined{showcaptionsetup}{}{%
3079 // \PassOptionsToPackage{caption=false}{subfig}}
3080 // that LyX uses for subfloats
3081 } else if (t.cs() == "@ifundefined" &&
3082 arg1 == "showcaptionsetup" && arg2.empty()
3083 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
3085 } else if (!in_lyx_preamble) {
3086 h_preamble << t.asInput()
3087 << '{' << arg1 << '}'
3088 << '{' << arg2 << '}'
3089 << '{' << arg3 << '}';
3094 if (is_known(t.cs(), known_if_commands)) {
3095 // must not parse anything in conditional code, since
3096 // LyX would output the parsed contents unconditionally
3097 if (!in_lyx_preamble)
3098 h_preamble << t.asInput();
3099 handle_if(p, in_lyx_preamble);
3103 if (!t.cs().empty() && !in_lyx_preamble) {
3104 h_preamble << '\\' << t.cs();
3109 // set textclass if not yet done (snippets without \documentclass and forced class)
3111 setTextClass(h_textclass, tc);
3113 // remove the whitespace
3116 if (h_papersides.empty()) {
3119 h_papersides = ss.str();
3122 // If the CJK package is used we cannot set the document language from
3123 // the babel options. Instead, we guess which language is used most
3124 // and set this one.
3125 default_language = h_language;
3126 if (is_full_document &&
3127 (auto_packages.find("CJK") != auto_packages.end() ||
3128 auto_packages.find("CJKutf8") != auto_packages.end())) {
3130 h_language = guessLanguage(p, default_language);
3132 if (explicit_babel && h_language != default_language) {
3133 // We set the document language to a CJK language,
3134 // but babel is explicitly called in the user preamble
3135 // without options. LyX will not add the default
3136 // language to the document options if it is either
3137 // english, or no text is set as default language.
3138 // Therefore we need to add a language option explicitly.
3139 // FIXME: It would be better to remove all babel calls
3140 // from the user preamble, but this is difficult
3141 // without re-introducing bug 7861.
3142 if (h_options.empty())
3143 h_options = lyx2babel(default_language);
3145 h_options += ',' + lyx2babel(default_language);
3149 // Finally, set the quote style.
3150 // LyX knows the following quotes styles:
3151 // british, cjk, cjkangle, danish, english, french, german,
3152 // polish, russian, swedish and swiss
3153 // conversion list taken from
3154 // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
3155 // (quotes for kazakh are unknown)
3157 if (is_known(h_language, known_british_quotes_languages))
3158 h_quotes_style = "british";
3160 else if (is_known(h_language, known_cjk_quotes_languages))
3161 h_quotes_style = "cjk";
3163 else if (is_known(h_language, known_cjkangle_quotes_languages))
3164 h_quotes_style = "cjkangle";
3166 else if (is_known(h_language, known_danish_quotes_languages))
3167 h_quotes_style = "danish";
3169 else if (is_known(h_language, known_french_quotes_languages))
3170 h_quotes_style = "french";
3172 else if (is_known(h_language, known_german_quotes_languages))
3173 h_quotes_style = "german";
3175 else if (is_known(h_language, known_polish_quotes_languages))
3176 h_quotes_style = "polish";
3178 else if (is_known(h_language, known_russian_quotes_languages))
3179 h_quotes_style = "russian";
3181 else if (is_known(h_language, known_swedish_quotes_languages))
3182 h_quotes_style = "swedish";
3184 else if (is_known(h_language, known_swiss_quotes_languages))
3185 h_quotes_style = "swiss";
3187 else if (is_known(h_language, known_english_quotes_languages))
3188 h_quotes_style = "english";
3192 string Preamble::parseEncoding(Parser & p, string const & forceclass)
3194 TeX2LyXDocClass dummy;
3195 parse(p, forceclass, true, dummy);
3196 if (h_inputencoding != "auto-legacy" && h_inputencoding != "auto-legacy-plain")
3197 return h_inputencoding;
3202 string babel2lyx(string const & language)
3204 char const * const * where = is_known(language, known_languages);
3206 return known_coded_languages[where - known_languages];
3211 string lyx2babel(string const & language)
3213 char const * const * where = is_known(language, known_coded_languages);
3215 return known_languages[where - known_coded_languages];
3220 string Preamble::polyglossia2lyx(string const & language)
3222 char const * const * where = is_known(language, polyglossia_languages);
3224 return coded_polyglossia_languages[where - polyglossia_languages];
3229 string rgbcolor2code(string const & name)
3231 char const * const * where = is_known(name, known_basic_colors);
3233 // "red", "green" etc
3234 return known_basic_color_codes[where - known_basic_colors];
3236 // "255,0,0", "0,255,0" etc
3237 RGBColor c(RGBColorFromLaTeX(name));
3238 return X11hexname(c);