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 "LaTeXPackages.h"
21 #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/Lexer.h"
29 #include "support/lstrings.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 hungarian quotes (.lyx names)
126 const char * const known_hungarian_quotes_languages[] = {"magyar", 0};
128 /// languages with russian quotes (.lyx names)
129 const char * const known_russian_quotes_languages[] = {"azerbaijani", "oldrussian",
130 "russian", "ukrainian", 0};
132 /// languages with swedish quotes (.lyx names)
133 const char * const known_swedish_quotes_languages[] = {"finnish", "swedish", 0};
135 /// languages with swiss quotes (.lyx names)
136 const char * const known_swiss_quotes_languages[] = {"albanian",
137 "armenian", "basque", "churchslavonic", "german-ch", "german-ch-old",
138 "norsk", "nynorsk", "turkmen", "ukrainian", "vietnamese", 0};
140 /// languages with hebrew quotes (.lyx names)
141 const char * const known_hebrew_quotes_languages[] = {"hebrew", 0};
143 /// known language packages from the times before babel
144 const char * const known_old_language_packages[] = {"french", "frenchle",
145 "frenchpro", "german", "ngerman", "pmfrench", 0};
147 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
149 const char * const known_roman_font_packages[] = { "ae", "beraserif", "bookman",
150 "ccfonts", "chancery", "charter", "cmr", "cochineal", "crimson", "CrimsonPro", "DejaVuSerif",
151 "DejaVuSerifCondensed", "fourier", "garamondx", "libertine", "libertineRoman", "libertine-type1",
152 "lmodern", "mathdesign", "mathpazo", "mathptmx", "MinionPro", "newcent", "noto", "noto-serif",
153 "PTSerif", "tgbonum", "tgchorus", "tgpagella", "tgschola", "tgtermes", "utopia", "xcharter", 0 };
155 const char * const known_sans_font_packages[] = { "avant", "berasans", "biolinum",
156 "biolinum-type1", "cantarell", "Chivo", "cmbr", "cmss", "DejaVuSans", "DejaVuSansCondensed", "FiraSans", "helvet", "iwona",
157 "iwonac", "iwonal", "iwonalc", "kurier", "kurierc", "kurierl", "kurierlc", "LibertinusSans-LF", "lmss", "noto-sans", "PTSans",
158 "tgadventor", "tgheros", "uop", 0 };
160 const char * const known_typewriter_font_packages[] = { "beramono", "cmtl", "cmtt", "courier", "DejaVuSansMono",
161 "FiraMono", "lmtt", "luximono", "libertineMono", "libertineMono-type1", "LibertinusMono-TLF", "lmodern",
162 "mathpazo", "mathptmx", "newcent", "noto-mono", "PTMono", "tgcursor", "txtt", 0 };
164 const char * const known_math_font_packages[] = { "eulervm", "newtxmath", 0};
166 const char * const known_latex_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
167 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
168 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
169 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
170 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
172 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
173 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
175 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
176 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
179 /// commands that can start an \if...\else...\endif sequence
180 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
181 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
182 "ifsidecap", "ifupgreek", 0};
184 const char * const known_basic_colors[] = {"black", "blue", "brown", "cyan",
185 "darkgray", "gray", "green", "lightgray", "lime", "magenta", "orange", "olive",
186 "pink", "purple", "red", "teal", "violet", "white", "yellow", 0};
188 const char * const known_basic_color_codes[] = {"#000000", "#0000ff", "#964B00", "#00ffff",
189 "#a9a9a9", "#808080", "#00ff00", "#d3d3d3", "#bfff00", "#ff00ff", "#ff7f00", "#808000",
190 "#ffc0cb", "#800080", "#ff0000", "#008080", "#8f00ff", "#ffffff", "#ffff00", 0};
192 /// conditional commands with three arguments like \@ifundefined{}{}{}
193 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
197 * Known file extensions for TeX files as used by \\includeonly
199 char const * const known_tex_extensions[] = {"tex", 0};
201 /// packages that work only in xetex
202 /// polyglossia is handled separately
203 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
204 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
205 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
207 /// packages that are automatically skipped if loaded by LyX
208 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
209 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color",
210 "float", "fontspec", "framed", "graphicx", "hhline", "ifthen", "longtable",
211 "makeidx", "minted", "multirow", "nomencl", "parskip", "pdfpages", "prettyref", "refstyle",
212 "rotating", "rotfloat", "splitidx", "setspace", "subscript", "tabularx","textcomp", "tipa",
213 "tipx", "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xcolor", "xltabular",
216 // codes used to remove packages that are loaded automatically by LyX.
217 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
218 const char package_beg_sep = '\001';
219 const char package_mid_sep = '\002';
220 const char package_end_sep = '\003';
223 // returns true if at least one of the options in what has been found
224 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
230 // the last language option is the document language (for babel and LyX)
231 // the last size option is the document font size
232 vector<string>::iterator it;
233 vector<string>::iterator position = opts.begin();
234 for (; *what; ++what) {
235 it = find(opts.begin(), opts.end(), *what);
236 if (it != opts.end()) {
237 if (it >= position) {
248 void delete_opt(vector<string> & opts, char const * const * what)
253 // remove found options from the list
254 // do this after handle_opt to avoid potential memory leaks
255 vector<string>::iterator it;
256 for (; *what; ++what) {
257 it = find(opts.begin(), opts.end(), *what);
258 if (it != opts.end())
265 * Split a package options string (keyval format) into a vector.
267 * authorformat=smallcaps,
269 * titleformat=colonsep,
270 * bibformat={tabular,ibidem,numbered}
272 vector<string> split_options(string const & input)
274 vector<string> options;
278 Token const & t = p.get_token();
279 if (t.asInput() == ",") {
280 options.push_back(trimSpaceAndEol(option));
282 } else if (t.asInput() == "=") {
285 if (p.next_token().asInput() == "{")
286 option += '{' + p.getArg('{', '}') + '}';
287 } else if (t.cat() != catSpace && t.cat() != catComment)
288 option += t.asInput();
292 options.push_back(trimSpaceAndEol(option));
299 * Retrieve a keyval option "name={value with=sign}" named \p name from
300 * \p options and return the value.
301 * The found option is also removed from \p options.
303 string process_keyval_opt(vector<string> & options, string const & name)
305 for (size_t i = 0; i < options.size(); ++i) {
306 vector<string> option;
307 split(options[i], option, '=');
308 if (option.size() < 2)
310 if (option[0] == name) {
311 options.erase(options.begin() + i);
312 option.erase(option.begin());
313 return join(option, "=");
319 } // anonymous namespace
323 * known polyglossia language names (including variants)
324 * FIXME: support spelling=old for german variants (german vs. ngerman LyX names etc)
326 const char * const Preamble::polyglossia_languages[] = {
327 "albanian", "american", "amharic", "ancient", "arabic", "armenian", "asturian", "australian",
328 "bahasai", "bahasam", "basque", "bengali", "brazil", "brazilian", "breton", "british", "bulgarian",
329 "catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
330 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
331 "galician", "greek", "monotonic", "hebrew", "hindi",
332 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean",
333 "lao", "latin", "latvian", "lithuanian", "lsorbian", "magyar", "malayalam", "marathi",
334 "austrian", "newzealand", "german", "norsk", "nynorsk", "occitan", "oldrussian",
335 "piedmontese", "polish", "polytonic", "portuges", "romanian", "romansh", "russian",
336 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovenian", "spanish", "swedish", "syriac",
337 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
338 "ukrainian", "urdu", "usorbian", "vietnamese", "welsh", 0};
339 // not yet supported by LyX: "korean", "nko"
342 * the same as polyglossia_languages with .lyx names
343 * please keep this in sync with polyglossia_languages line by line!
345 const char * const Preamble::coded_polyglossia_languages[] = {
346 "albanian", "american", "amharic", "ancientgreek", "arabic_arabi", "armenian", "asturian", "australian",
347 "bahasa", "bahasam", "basque", "bengali", "brazilian", "brazilian", "breton", "british", "bulgarian",
348 "catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
349 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
350 "galician", "greek", "greek", "hebrew", "hindi",
351 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean",
352 "lao", "latin", "latvian", "lithuanian", "lowersorbian", "magyar", "malayalam", "marathi",
353 "naustrian","newzealand", "ngerman", "norsk", "nynorsk", "occitan", "oldrussian",
354 "piedmontese", "polish", "polutonikogreek", "portuges", "romanian", "romansh", "russian",
355 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovene", "spanish", "swedish", "syriac",
356 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
357 "ukrainian", "urdu", "uppersorbian", "vietnamese", "welsh", 0};
358 // not yet supported by LyX: "korean-polyglossia", "nko"
361 bool Preamble::usePolyglossia() const
363 return h_use_non_tex_fonts && h_language_package == "default";
367 bool Preamble::indentParagraphs() const
369 return h_paragraph_separation == "indent";
373 bool Preamble::isPackageUsed(string const & package) const
375 return used_packages.find(package) != used_packages.end();
379 bool Preamble::isPackageAutoLoaded(string const & package) const
381 return auto_packages.find(package) != auto_packages.end();
385 vector<string> Preamble::getPackageOptions(string const & package) const
387 map<string, vector<string> >::const_iterator it = used_packages.find(package);
388 if (it != used_packages.end())
390 return vector<string>();
394 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
396 auto_packages.insert(package);
400 void Preamble::addModule(string const & module)
402 for (auto const & m : used_modules) {
406 used_modules.push_back(module);
410 void Preamble::suppressDate(bool suppress)
413 h_suppress_date = "true";
415 h_suppress_date = "false";
419 void Preamble::registerAuthor(std::string const & name, string const & initials)
421 Author author(from_utf8(name), empty_docstring(), from_utf8(initials));
422 author.setUsed(true);
423 authors_.record(author);
424 h_tracking_changes = "true";
425 h_output_changes = "true";
429 Author const & Preamble::getAuthor(std::string const & name) const
431 Author author(from_utf8(name), empty_docstring(), empty_docstring());
432 for (AuthorList::Authors::const_iterator it = authors_.begin();
433 it != authors_.end(); ++it)
436 static Author const dummy;
441 int Preamble::getSpecialTableColumnArguments(char c) const
443 map<char, int>::const_iterator it = special_columns_.find(c);
444 if (it == special_columns_.end())
450 void Preamble::add_package(string const & name, vector<string> & options)
452 // every package inherits the global options
453 used_packages.insert({name, split_options(h_options)});
455 // Insert options passed via PassOptionsToPackage
456 for (auto const & p : extra_package_options_) {
457 if (p.first == name) {
458 vector<string> eo = getVectorFromString(p.second);
459 for (auto const & eoi : eo)
460 options.push_back(eoi);
464 vector<string> & v = used_packages[name];
465 v.insert(v.end(), options.begin(), options.end());
466 if (name == "jurabib") {
467 // Don't output the order argument (see the cite command
468 // handling code in text.cpp).
469 vector<string>::iterator end =
470 remove(options.begin(), options.end(), "natbiborder");
471 end = remove(options.begin(), end, "jurabiborder");
472 options.erase(end, options.end());
476 void Preamble::setTextClass(string const & tclass, TeX2LyXDocClass & tc)
478 h_textclass = tclass;
479 tc.setName(h_textclass);
480 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
481 error_message("Could not read layout file for textclass \"" + h_textclass + "\".");
489 // Given is a string like "scaled=0.9" or "scale=0.9", return 0.9 * 100
490 bool scale_as_percentage(string const & scale, string & percentage)
492 if (contains(scale, '=')) {
493 string const value = support::split(scale, '=');
494 if (isStrDbl(value)) {
495 percentage = convert<string>(
496 static_cast<int>(100 * convert<double>(value)));
504 string remove_braces(string const & value)
508 if (value[0] == '{' && value[value.length()-1] == '}')
509 return value.substr(1, value.length()-2);
513 } // anonymous namespace
516 Preamble::Preamble() : one_language(true), explicit_babel(false),
517 title_layout_found(false), index_number(0), h_font_cjk_set(false)
521 h_biblio_style = "plain";
522 h_bibtex_command = "default";
523 h_cite_engine = "basic";
524 h_cite_engine_type = "default";
526 h_defskip = "medskip";
527 h_dynamic_quotes = false;
530 h_fontencoding = "default";
531 h_font_roman[0] = "default";
532 h_font_roman[1] = "default";
533 h_font_sans[0] = "default";
534 h_font_sans[1] = "default";
535 h_font_typewriter[0] = "default";
536 h_font_typewriter[1] = "default";
537 h_font_math[0] = "auto";
538 h_font_math[1] = "auto";
539 h_font_default_family = "default";
540 h_use_non_tex_fonts = false;
542 h_font_roman_osf = "false";
543 h_font_sans_osf = "false";
544 h_font_typewriter_osf = "false";
545 h_font_sf_scale[0] = "100";
546 h_font_sf_scale[1] = "100";
547 h_font_tt_scale[0] = "100";
548 h_font_tt_scale[1] = "100";
549 // h_font_roman_opts;
551 // h_font_typewriter_opts;
553 h_is_mathindent = "0";
554 h_math_numbering_side = "default";
555 h_graphics = "default";
556 h_default_output_format = "default";
557 h_html_be_strict = "false";
558 h_html_css_as_file = "0";
559 h_html_math_output = "0";
560 h_docbook_table_output = "0";
561 h_docbook_mathml_prefix = "1";
562 h_index[0] = "Index";
563 h_index_command = "default";
564 h_inputencoding = "auto-legacy";
565 h_justification = "true";
566 h_language = "english";
567 h_language_package = "none";
569 h_maintain_unincluded_children = "no";
573 h_output_changes = "false";
574 h_change_bars = "false";
576 //h_output_sync_macro
577 h_papercolumns = "1";
578 h_paperfontsize = "default";
579 h_paperorientation = "portrait";
580 h_paperpagestyle = "default";
582 h_papersize = "default";
583 h_paragraph_indentation = "default";
584 h_paragraph_separation = "indent";
589 h_pdf_bookmarks = "0";
590 h_pdf_bookmarksnumbered = "0";
591 h_pdf_bookmarksopen = "0";
592 h_pdf_bookmarksopenlevel = "1";
593 h_pdf_breaklinks = "0";
594 h_pdf_pdfborder = "0";
595 h_pdf_colorlinks = "0";
596 h_pdf_backref = "section";
597 h_pdf_pdfusetitle = "0";
599 //h_pdf_quoted_options;
600 h_quotes_style = "english";
602 h_shortcut[0] = "idx";
603 h_spacing = "single";
604 h_save_transient_properties = "true";
605 h_suppress_date = "false";
606 h_textclass = "article";
608 h_tracking_changes = "false";
609 h_use_bibtopic = "false";
610 h_use_dash_ligatures = "true";
611 h_use_indices = "false";
612 h_use_geometry = "false";
613 h_use_default_options = "false";
614 h_use_hyperref = "false";
615 h_use_microtype = "false";
616 h_use_lineno = "false";
617 h_use_refstyle = false;
618 h_use_minted = false;
619 h_use_packages["amsmath"] = "1";
620 h_use_packages["amssymb"] = "0";
621 h_use_packages["cancel"] = "0";
622 h_use_packages["esint"] = "1";
623 h_use_packages["mhchem"] = "0";
624 h_use_packages["mathdots"] = "0";
625 h_use_packages["mathtools"] = "0";
626 h_use_packages["stackrel"] = "0";
627 h_use_packages["stmaryrd"] = "0";
628 h_use_packages["undertilde"] = "0";
632 void Preamble::handle_hyperref(vector<string> & options)
634 // FIXME swallow inputencoding changes that might surround the
635 // hyperref setup if it was written by LyX
636 h_use_hyperref = "true";
637 // swallow "unicode=true", since LyX does always write that
638 vector<string>::iterator it =
639 find(options.begin(), options.end(), "unicode=true");
640 if (it != options.end())
642 it = find(options.begin(), options.end(), "pdfusetitle");
643 if (it != options.end()) {
644 h_pdf_pdfusetitle = "1";
647 string bookmarks = process_keyval_opt(options, "bookmarks");
648 if (bookmarks == "true")
649 h_pdf_bookmarks = "1";
650 else if (bookmarks == "false")
651 h_pdf_bookmarks = "0";
652 if (h_pdf_bookmarks == "1") {
653 string bookmarksnumbered =
654 process_keyval_opt(options, "bookmarksnumbered");
655 if (bookmarksnumbered == "true")
656 h_pdf_bookmarksnumbered = "1";
657 else if (bookmarksnumbered == "false")
658 h_pdf_bookmarksnumbered = "0";
659 string bookmarksopen =
660 process_keyval_opt(options, "bookmarksopen");
661 if (bookmarksopen == "true")
662 h_pdf_bookmarksopen = "1";
663 else if (bookmarksopen == "false")
664 h_pdf_bookmarksopen = "0";
665 if (h_pdf_bookmarksopen == "1") {
666 string bookmarksopenlevel =
667 process_keyval_opt(options, "bookmarksopenlevel");
668 if (!bookmarksopenlevel.empty())
669 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
672 string breaklinks = process_keyval_opt(options, "breaklinks");
673 if (breaklinks == "true")
674 h_pdf_breaklinks = "1";
675 else if (breaklinks == "false")
676 h_pdf_breaklinks = "0";
677 string pdfborder = process_keyval_opt(options, "pdfborder");
678 if (pdfborder == "{0 0 0}")
679 h_pdf_pdfborder = "1";
680 else if (pdfborder == "{0 0 1}")
681 h_pdf_pdfborder = "0";
682 string backref = process_keyval_opt(options, "backref");
683 if (!backref.empty())
684 h_pdf_backref = backref;
685 string colorlinks = process_keyval_opt(options, "colorlinks");
686 if (colorlinks == "true")
687 h_pdf_colorlinks = "1";
688 else if (colorlinks == "false")
689 h_pdf_colorlinks = "0";
690 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
691 if (!pdfpagemode.empty())
692 h_pdf_pagemode = pdfpagemode;
693 string pdftitle = process_keyval_opt(options, "pdftitle");
694 if (!pdftitle.empty()) {
695 h_pdf_title = remove_braces(pdftitle);
697 string pdfauthor = process_keyval_opt(options, "pdfauthor");
698 if (!pdfauthor.empty()) {
699 h_pdf_author = remove_braces(pdfauthor);
701 string pdfsubject = process_keyval_opt(options, "pdfsubject");
702 if (!pdfsubject.empty())
703 h_pdf_subject = remove_braces(pdfsubject);
704 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
705 if (!pdfkeywords.empty())
706 h_pdf_keywords = remove_braces(pdfkeywords);
707 if (!options.empty()) {
708 if (!h_pdf_quoted_options.empty())
709 h_pdf_quoted_options += ',';
710 h_pdf_quoted_options += join(options, ",");
716 void Preamble::handle_geometry(vector<string> & options)
718 h_use_geometry = "true";
719 vector<string>::iterator it;
721 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
722 h_paperorientation = "landscape";
726 // keyval version: "paper=letter" or "paper=letterpaper"
727 string paper = process_keyval_opt(options, "paper");
729 if (suffixIs(paper, "paper"))
730 paper = subst(paper, "paper", "");
731 // alternative version: "letterpaper"
732 handle_opt(options, known_latex_paper_sizes, paper);
733 if (suffixIs(paper, "paper"))
734 paper = subst(paper, "paper", "");
735 delete_opt(options, known_latex_paper_sizes);
739 char const * const * margin = known_paper_margins;
740 for (; *margin; ++margin) {
741 string value = process_keyval_opt(options, *margin);
742 if (!value.empty()) {
743 int k = margin - known_paper_margins;
744 string name = known_coded_paper_margins[k];
745 h_margins += '\\' + name + ' ' + value + '\n';
751 void Preamble::handle_package(Parser &p, string const & name,
752 string const & opts, bool in_lyx_preamble,
755 vector<string> options = split_options(opts);
756 add_package(name, options);
758 if (is_known(name, known_xetex_packages)) {
760 h_use_non_tex_fonts = true;
761 registerAutomaticallyLoadedPackage("fontspec");
762 if (h_inputencoding == "auto-legacy")
763 p.setEncoding("UTF-8");
764 } else if (h_inputencoding == "auto-legacy"
765 && LaTeXPackages::isAvailableAtLeastFrom("LaTeX", 2018, 04))
766 // As of LaTeX 2018/04/01, utf8 is the default input encoding
767 // So use that if no inputencoding is set
768 h_inputencoding = "utf8";
770 // vector of all options for easier parsing and
772 vector<string> allopts = getVectorFromString(opts);
773 // this stores the potential extra options
780 // By default, we use the package name as LyX font name,
781 // so this only needs to be reset if these names differ
782 if (is_known(name, known_roman_font_packages))
783 h_font_roman[0] = name;
785 if (name == "ccfonts") {
786 for (auto const & opt : allopts) {
792 h_font_roman_opts = xopts;
796 if (name == "lmodern") {
797 for (auto const & opt : allopts) {
803 h_font_roman_opts = xopts;
807 if (name == "fourier") {
808 h_font_roman[0] = "utopia";
809 for (auto const & opt : allopts) {
811 h_font_roman_osf = "true";
814 if (opt == "expert") {
823 h_font_roman_opts = xopts;
827 if (name == "garamondx") {
828 for (auto const & opt : allopts) {
830 h_font_roman_osf = "true";
838 h_font_roman_opts = xopts;
842 if (name == "libertine") {
843 // this automatically invokes biolinum
844 h_font_sans[0] = "biolinum";
845 // as well as libertineMono
846 h_font_typewriter[0] = "libertine-mono";
847 for (auto const & opt : allopts) {
849 h_font_roman_osf = "true";
852 if (opt == "lining") {
853 h_font_roman_osf = "false";
861 h_font_roman_opts = xopts;
865 if (name == "libertineRoman" || name == "libertine-type1") {
866 h_font_roman[0] = "libertine";
867 // NOTE: contrary to libertine.sty, libertineRoman
868 // and libertine-type1 do not automatically invoke
869 // biolinum and libertineMono
870 if (opts == "lining")
871 h_font_roman_osf = "false";
872 else if (opts == "osf")
873 h_font_roman_osf = "true";
876 if (name == "libertinus" || name == "libertinus-type1") {
883 for (auto const & opt : allopts) {
884 if (opt == "rm" || opt == "serif") {
889 if (opt == "sf" || opt == "sans") {
894 if (opt == "tt=false" || opt == "mono=false") {
902 if (opt == "scaleSF") {
906 if (opt == "scaleTT") {
910 if (opt == "lining") {
911 h_font_roman_osf = "false";
919 h_font_roman[0] = "libertinus";
921 h_font_roman_osf = "true";
923 h_font_roman_osf = "false";
926 h_font_sans[0] = "LibertinusSans-LF";
928 h_font_sans_osf = "true";
930 h_font_sans_osf = "false";
931 if (!scalesf.empty())
932 scale_as_percentage(scalesf, h_font_sf_scale[0]);
935 h_font_typewriter[0] = "LibertinusMono-TLF";
936 if (!scalett.empty())
937 scale_as_percentage(scalett, h_font_tt_scale[0]);
940 h_font_roman_opts = xopts;
944 if (name == "MinionPro") {
945 h_font_roman[0] = "minionpro";
946 h_font_roman_osf = "true";
947 h_font_math[0] = "auto";
948 for (auto const & opt : allopts) {
950 h_font_roman_osf = "false";
953 if (opt == "onlytext") {
954 h_font_math[0] = "default";
962 h_font_roman_opts = xopts;
966 if (name == "mathdesign") {
967 for (auto const & opt : allopts) {
968 if (opt == "charter") {
969 h_font_roman[0] = "md-charter";
972 if (opt == "garamond") {
973 h_font_roman[0] = "md-garamond";
976 if (opt == "utopia") {
977 h_font_roman[0] = "md-utopia";
980 if (opt == "expert") {
982 h_font_roman_osf = "true";
988 else if (name == "mathpazo") {
989 h_font_roman[0] = "palatino";
990 for (auto const & opt : allopts) {
992 h_font_roman_osf = "true";
1004 h_font_roman_opts = xopts;
1008 else if (name == "mathptmx") {
1009 h_font_roman[0] = "times";
1010 for (auto const & opt : allopts) {
1016 h_font_roman_opts = xopts;
1020 if (name == "crimson")
1021 h_font_roman[0] = "cochineal";
1023 if (name == "cochineal") {
1024 for (auto const & opt : allopts) {
1025 if (opt == "osf" || opt == "oldstyle") {
1026 h_font_roman_osf = "true";
1029 if (opt == "proportional" || opt == "p")
1036 h_font_roman_opts = xopts;
1040 if (name == "CrimsonPro") {
1041 h_font_roman_osf = "true";
1042 for (auto const & opt : allopts) {
1043 if (opt == "lf" || opt == "lining") {
1044 h_font_roman_osf = "false";
1047 if (opt == "proportional" || opt == "p")
1049 if (opt == "medium") {
1050 h_font_roman[0] = "CrimsonProMedium";
1053 if (opt == "extralight") {
1054 h_font_roman[0] = "CrimsonProExtraLight";
1057 if (opt == "light") {
1058 h_font_roman[0] = "CrimsonProLight";
1066 h_font_roman_opts = xopts;
1072 // font uses old-style figure
1073 h_font_roman_osf = "true";
1075 if (name == "paratype") {
1076 // in this case all fonts are ParaType
1077 h_font_roman[0] = "PTSerif-TLF";
1078 h_font_sans[0] = "default";
1079 h_font_typewriter[0] = "default";
1082 if (name == "PTSerif")
1083 h_font_roman[0] = "PTSerif-TLF";
1085 if (name == "XCharter") {
1086 h_font_roman[0] = "xcharter";
1087 for (auto const & opt : allopts) {
1089 h_font_roman_osf = "true";
1097 h_font_roman_opts = xopts;
1101 if (name == "plex-serif") {
1102 h_font_roman[0] = "IBMPlexSerif";
1103 for (auto const & opt : allopts) {
1104 if (opt == "thin") {
1105 h_font_roman[0] = "IBMPlexSerifThin";
1108 if (opt == "extralight") {
1109 h_font_roman[0] = "IBMPlexSerifExtraLight";
1112 if (opt == "light") {
1113 h_font_roman[0] = "IBMPlexSerifLight";
1121 h_font_roman_opts = xopts;
1125 if (name == "noto-serif" || name == "noto") {
1132 bool extralight = false;
1134 bool medium = false;
1137 if (name == "noto") {
1142 // Since the options might apply to different shapes,
1143 // we need to parse all options first and then handle them.
1144 for (auto const & opt : allopts) {
1145 if (opt == "regular")
1155 if (opt == "thin") {
1159 if (opt == "extralight") {
1163 if (opt == "light") {
1167 if (opt == "medium") {
1178 if (opt == "nott") {
1186 if (prefixIs(opt, "scaled=")) {
1195 // handle options that might affect different shapes
1196 if (name == "noto-serif" || rm) {
1198 h_font_roman[0] = "NotoSerifThin";
1199 else if (extralight)
1200 h_font_roman[0] = "NotoSerifExtralight";
1202 h_font_roman[0] = "NotoSerifLight";
1204 h_font_roman[0] = "NotoSerifMedium";
1206 h_font_roman[0] = "NotoSerifRegular";
1208 h_font_roman_osf = "true";
1210 h_font_roman_opts = xopts;
1212 if (name == "noto" && sf) {
1214 h_font_sans[0] = "NotoSansThin";
1215 else if (extralight)
1216 h_font_sans[0] = "NotoSansExtralight";
1218 h_font_sans[0] = "NotoSansLight";
1220 h_font_sans[0] = "NotoSansMedium";
1222 h_font_sans[0] = "NotoSansRegular";
1224 h_font_sans_osf = "true";
1226 scale_as_percentage(scl, h_font_sf_scale[0]);
1228 h_font_sans_opts = xopts;
1230 if (name == "noto" && tt) {
1231 h_font_typewriter[0] = "NotoMonoRegular";
1233 h_font_typewriter_osf = "true";
1235 scale_as_percentage(scl, h_font_tt_scale[0]);
1237 h_font_typewriter_opts = xopts;
1241 if (name == "sourceserifpro") {
1242 h_font_roman[0] = "ADOBESourceSerifPro";
1243 for (auto const & opt : allopts) {
1245 h_font_roman_osf = "true";
1253 h_font_roman_opts = xopts;
1261 // By default, we use the package name as LyX font name,
1262 // so this only needs to be reset if these names differ.
1263 // Also, we handle the scaling option here generally.
1264 if (is_known(name, known_sans_font_packages)) {
1265 h_font_sans[0] = name;
1266 if (contains(opts, "scale")) {
1267 vector<string>::iterator it = allopts.begin();
1268 for (; it != allopts.end() ; ++it) {
1269 string const opt = *it;
1270 if (prefixIs(opt, "scaled=") || prefixIs(opt, "scale=")) {
1271 if (scale_as_percentage(opt, h_font_sf_scale[0])) {
1280 if (name == "biolinum" || name == "biolinum-type1") {
1281 h_font_sans[0] = "biolinum";
1282 for (auto const & opt : allopts) {
1283 if (prefixIs(opt, "osf")) {
1284 h_font_sans_osf = "true";
1292 h_font_sans_opts = xopts;
1296 if (name == "cantarell") {
1297 for (auto const & opt : allopts) {
1298 if (opt == "defaultsans")
1300 if (prefixIs(opt, "oldstyle")) {
1301 h_font_sans_osf = "true";
1309 h_font_sans_opts = xopts;
1313 if (name == "Chivo") {
1314 for (auto const & opt : allopts) {
1315 if (opt == "thin") {
1316 h_font_roman[0] = "ChivoThin";
1319 if (opt == "light") {
1320 h_font_roman[0] = "ChivoLight";
1323 if (opt == "regular") {
1324 h_font_roman[0] = "Chivo";
1327 if (opt == "medium") {
1328 h_font_roman[0] = "ChivoMedium";
1331 if (prefixIs(opt, "oldstyle")) {
1332 h_font_sans_osf = "true";
1340 h_font_sans_opts = xopts;
1344 if (name == "PTSans") {
1345 h_font_sans[0] = "PTSans-TLF";
1348 if (name == "FiraSans") {
1349 h_font_sans_osf = "true";
1350 for (auto const & opt : allopts) {
1351 if (opt == "book") {
1352 h_font_sans[0] = "FiraSansBook";
1355 if (opt == "thin") {
1358 if (opt == "extralight") {
1359 h_font_sans[0] = "FiraSansExtralight";
1362 if (opt == "light") {
1363 h_font_sans[0] = "FiraSansLight";
1366 if (opt == "ultralight") {
1367 h_font_sans[0] = "FiraSansUltralight";
1370 if (opt == "thin") {
1371 h_font_sans[0] = "FiraSansThin";
1374 if (opt == "lf" || opt == "lining") {
1375 h_font_sans_osf = "false";
1383 h_font_sans_opts = xopts;
1387 if (name == "plex-sans") {
1388 h_font_sans[0] = "IBMPlexSans";
1389 for (auto const & opt : allopts) {
1390 if (opt == "condensed") {
1391 h_font_sans[0] = "IBMPlexSansCondensed";
1394 if (opt == "thin") {
1395 h_font_sans[0] = "IBMPlexSansThin";
1398 if (opt == "extralight") {
1399 h_font_sans[0] = "IBMPlexSansExtraLight";
1402 if (opt == "light") {
1403 h_font_sans[0] = "IBMPlexSansLight";
1411 h_font_sans_opts = xopts;
1415 if (name == "noto-sans") {
1416 h_font_sans[0] = "NotoSansRegular";
1417 for (auto const & opt : allopts) {
1418 if (opt == "regular")
1420 if (opt == "medium") {
1421 h_font_sans[0] = "NotoSansMedium";
1424 if (opt == "thin") {
1425 h_font_sans[0] = "NotoSansThin";
1428 if (opt == "extralight") {
1429 h_font_sans[0] = "NotoSansExtralight";
1432 if (opt == "light") {
1433 h_font_sans[0] = "NotoSansLight";
1437 h_font_sans_osf = "true";
1445 h_font_sans_opts = xopts;
1449 if (name == "sourcesanspro") {
1450 h_font_sans[0] = "ADOBESourceSansPro";
1451 for (auto const & opt : allopts) {
1453 h_font_sans_osf = "true";
1461 h_font_sans_opts = xopts;
1469 // By default, we use the package name as LyX font name,
1470 // so this only needs to be reset if these names differ.
1471 // Also, we handle the scaling option here generally.
1472 if (is_known(name, known_typewriter_font_packages)) {
1473 h_font_typewriter[0] = name;
1474 if (contains(opts, "scale")) {
1475 vector<string>::iterator it = allopts.begin();
1476 for (; it != allopts.end() ; ++it) {
1477 string const opt = *it;
1478 if (prefixIs(opt, "scaled=") || prefixIs(opt, "scale=")) {
1479 if (scale_as_percentage(opt, h_font_tt_scale[0])) {
1488 if (name == "libertineMono" || name == "libertineMono-type1")
1489 h_font_typewriter[0] = "libertine-mono";
1491 if (name == "FiraMono") {
1492 h_font_typewriter_osf = "true";
1493 for (auto const & opt : allopts) {
1494 if (opt == "lf" || opt == "lining") {
1495 h_font_typewriter_osf = "false";
1503 h_font_typewriter_opts = xopts;
1507 if (name == "PTMono")
1508 h_font_typewriter[0] = "PTMono-TLF";
1510 if (name == "plex-mono") {
1511 h_font_typewriter[0] = "IBMPlexMono";
1512 for (auto const & opt : allopts) {
1513 if (opt == "thin") {
1514 h_font_typewriter[0] = "IBMPlexMonoThin";
1517 if (opt == "extralight") {
1518 h_font_typewriter[0] = "IBMPlexMonoExtraLight";
1521 if (opt == "light") {
1522 h_font_typewriter[0] = "IBMPlexMonoLight";
1530 h_font_typewriter_opts = xopts;
1534 if (name == "noto-mono") {
1535 h_font_typewriter[0] = "NotoMonoRegular";
1536 for (auto const & opt : allopts) {
1537 if (opt == "regular")
1544 h_font_typewriter_opts = xopts;
1548 if (name == "sourcecodepro") {
1549 h_font_typewriter[0] = "ADOBESourceCodePro";
1550 for (auto const & opt : allopts) {
1552 h_font_typewriter_osf = "true";
1560 h_font_typewriter_opts = xopts;
1568 // By default, we use the package name as LyX font name,
1569 // so this only needs to be reset if these names differ.
1570 if (is_known(name, known_math_font_packages))
1571 h_font_math[0] = name;
1573 if (name == "newtxmath") {
1575 h_font_math[0] = "newtxmath";
1576 else if (opts == "garamondx")
1577 h_font_math[0] = "garamondx-ntxm";
1578 else if (opts == "libertine")
1579 h_font_math[0] = "libertine-ntxm";
1580 else if (opts == "minion")
1581 h_font_math[0] = "minion-ntxm";
1582 else if (opts == "cochineal")
1583 h_font_math[0] = "cochineal-ntxm";
1586 if (name == "iwona")
1588 h_font_math[0] = "iwona-math";
1590 if (name == "kurier")
1592 h_font_math[0] = "kurier-math";
1594 // after the detection and handling of special cases, we can remove the
1595 // fonts, otherwise they would appear in the preamble, see bug #7856
1596 if (is_known(name, known_roman_font_packages) || is_known(name, known_sans_font_packages)
1597 || is_known(name, known_typewriter_font_packages) || is_known(name, known_math_font_packages))
1599 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
1600 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
1601 name == "esint" || name == "mhchem" || name == "mathdots" ||
1602 name == "mathtools" || name == "stackrel" ||
1603 name == "stmaryrd" || name == "undertilde") {
1604 h_use_packages[name] = "2";
1605 registerAutomaticallyLoadedPackage(name);
1608 else if (name == "babel") {
1609 h_language_package = "default";
1610 // One might think we would have to do nothing if babel is loaded
1611 // without any options to prevent pollution of the preamble with this
1612 // babel call in every roundtrip.
1613 // But the user could have defined babel-specific things afterwards. So
1614 // we need to keep it in the preamble to prevent cases like bug #7861.
1615 if (!opts.empty()) {
1616 // check if more than one option was used - used later for inputenc
1617 if (options.begin() != options.end() - 1)
1618 one_language = false;
1619 // babel takes the last language of the option of its \usepackage
1620 // call as document language. If there is no such language option, the
1621 // last language in the documentclass options is used.
1622 handle_opt(options, known_languages, h_language);
1623 // translate the babel name to a LyX name
1624 h_language = babel2lyx(h_language);
1625 if (h_language == "japanese") {
1626 // For Japanese, the encoding isn't indicated in the source
1627 // file, and there's really not much we can do. We could
1628 // 1) offer a list of possible encodings to choose from, or
1629 // 2) determine the encoding of the file by inspecting it.
1630 // For the time being, we leave the encoding alone so that
1631 // we don't get iconv errors when making a wrong guess, and
1632 // we will output a note at the top of the document
1633 // explaining what to do.
1634 Encoding const * const enc = encodings.fromIconvName(
1635 p.getEncoding(), Encoding::japanese, false);
1637 h_inputencoding = enc->name();
1638 is_nonCJKJapanese = true;
1639 // in this case babel can be removed from the preamble
1640 registerAutomaticallyLoadedPackage("babel");
1642 // If babel is called with options, LyX puts them by default into the
1643 // document class options. This works for most languages, except
1644 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
1645 // perhaps in future others.
1646 // Therefore keep the babel call as it is as the user might have
1648 string const babelcall = "\\usepackage[" + opts + "]{babel}\n";
1649 if (!contains(h_preamble.str(), babelcall))
1650 h_preamble << babelcall;
1652 delete_opt(options, known_languages);
1654 if (!contains(h_preamble.str(), "\\usepackage{babel}\n"))
1655 h_preamble << "\\usepackage{babel}\n";
1656 explicit_babel = true;
1660 else if (name == "polyglossia") {
1661 h_language_package = "default";
1662 h_default_output_format = "pdf4";
1663 h_use_non_tex_fonts = true;
1665 registerAutomaticallyLoadedPackage("xunicode");
1666 if (h_inputencoding == "auto-legacy")
1667 p.setEncoding("UTF-8");
1670 else if (name == "CJK") {
1671 // set the encoding to "auto-legacy" because it might be set to "auto-legacy-plain" by the babel handling
1672 // and this would not be correct for CJK
1673 if (h_inputencoding == "auto-legacy-plain")
1674 h_inputencoding = "auto-legacy";
1675 registerAutomaticallyLoadedPackage("CJK");
1678 else if (name == "CJKutf8") {
1679 h_inputencoding = "utf8-cjk";
1680 p.setEncoding("UTF-8");
1681 registerAutomaticallyLoadedPackage("CJKutf8");
1684 else if (name == "fontenc") {
1685 h_fontencoding = getStringFromVector(options, ",");
1689 else if (name == "inputenc" || name == "luainputenc") {
1690 // h_inputencoding is only set when there is not more than one
1691 // inputenc option because otherwise h_inputencoding must be
1692 // set to "auto-legacy" (the default encodings of the document's languages)
1693 // Therefore check that exactly one option is passed to inputenc.
1694 // It is also only set when there is not more than one babel
1696 if (!options.empty()) {
1697 string const encoding = options.back();
1698 Encoding const * const enc = encodings.fromLaTeXName(
1699 encoding, Encoding::inputenc, true);
1701 if (!detectEncoding)
1702 warning_message("Unknown encoding " + encoding + ". Ignoring.");
1704 if (!enc->unsafe() && options.size() == 1 && one_language == true) {
1705 h_inputencoding = enc->name();
1706 docencoding = enc->iconvName();
1708 p.setEncoding(enc->iconvName());
1714 else if (name == "srcltx") {
1715 h_output_sync = "1";
1716 if (!opts.empty()) {
1717 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
1720 h_output_sync_macro = "\\usepackage{srcltx}";
1723 else if (is_known(name, known_old_language_packages)) {
1724 // known language packages from the times before babel
1725 // if they are found and not also babel, they will be used as
1726 // custom language package
1727 h_language_package = "\\usepackage{" + name + "}";
1730 else if (name == "lyxskak") {
1731 // ignore this and its options
1732 const char * const o[] = {"ps", "mover", 0};
1733 delete_opt(options, o);
1736 else if (name == "parskip" && options.size() < 2 && (opts.empty() || prefixIs(opts, "skip="))) {
1738 h_paragraph_separation = "halfline";
1740 if (opts == "skip=\\smallskipamount")
1741 h_defskip = "smallskip";
1742 else if (opts == "skip=\\medskipamount")
1743 h_defskip = "medskip";
1744 else if (opts == "skip=\\bigskipamount")
1745 h_defskip = "bigskip";
1746 else if (opts == "skip=\\baselineskip")
1747 h_defskip = "fullline";
1749 // get value, unbraced
1750 string length = rtrim(ltrim(token(opts, '=', 1), "{"), "}");
1751 // transform glue length if we have one
1752 is_glue_length(length);
1755 h_paragraph_separation = "skip";
1759 else if (is_known(name, known_lyx_packages) && options.empty()) {
1760 if (name == "splitidx")
1761 h_use_indices = "true";
1762 else if (name == "minted")
1763 h_use_minted = true;
1764 else if (name == "refstyle")
1765 h_use_refstyle = true;
1766 else if (name == "prettyref")
1767 h_use_refstyle = false;
1769 if (!in_lyx_preamble) {
1770 h_preamble << package_beg_sep << name
1771 << package_mid_sep << "\\usepackage{"
1773 if (p.next_token().cat() == catNewline ||
1774 (p.next_token().cat() == catSpace &&
1775 p.next_next_token().cat() == catNewline))
1777 h_preamble << package_end_sep;
1781 else if (name == "geometry")
1782 handle_geometry(options);
1784 else if (name == "subfig")
1785 ; // ignore this FIXME: Use the package separator mechanism instead
1787 else if (char const * const * where = is_known(name, known_languages))
1788 h_language = known_coded_languages[where - known_languages];
1790 else if (name == "natbib") {
1791 h_biblio_style = "plainnat";
1792 h_cite_engine = "natbib";
1793 h_cite_engine_type = "authoryear";
1794 vector<string>::iterator it =
1795 find(options.begin(), options.end(), "authoryear");
1796 if (it != options.end())
1799 it = find(options.begin(), options.end(), "numbers");
1800 if (it != options.end()) {
1801 h_cite_engine_type = "numerical";
1805 if (!options.empty())
1806 h_biblio_options = join(options, ",");
1809 else if (name == "biblatex") {
1810 h_biblio_style = "plainnat";
1811 h_cite_engine = "biblatex";
1812 h_cite_engine_type = "authoryear";
1814 vector<string>::iterator it =
1815 find(options.begin(), options.end(), "natbib");
1816 if (it != options.end()) {
1818 h_cite_engine = "biblatex-natbib";
1820 opt = process_keyval_opt(options, "natbib");
1822 h_cite_engine = "biblatex-natbib";
1824 opt = process_keyval_opt(options, "style");
1826 h_biblatex_citestyle = opt;
1827 h_biblatex_bibstyle = opt;
1829 opt = process_keyval_opt(options, "citestyle");
1831 h_biblatex_citestyle = opt;
1832 opt = process_keyval_opt(options, "bibstyle");
1834 h_biblatex_bibstyle = opt;
1836 opt = process_keyval_opt(options, "refsection");
1838 if (opt == "none" || opt == "part"
1839 || opt == "chapter" || opt == "section"
1840 || opt == "subsection")
1843 warning_message("Ignoring unknown refsection value '" + opt + "'.");
1845 opt = process_keyval_opt(options, "bibencoding");
1848 if (!options.empty()) {
1849 h_biblio_options = join(options, ",");
1854 else if (name == "jurabib") {
1855 h_biblio_style = "jurabib";
1856 h_cite_engine = "jurabib";
1857 h_cite_engine_type = "authoryear";
1858 if (!options.empty())
1859 h_biblio_options = join(options, ",");
1862 else if (name == "bibtopic")
1863 h_use_bibtopic = "true";
1865 else if (name == "chapterbib")
1866 h_multibib = "child";
1868 else if (name == "hyperref")
1869 handle_hyperref(options);
1871 else if (name == "algorithm2e") {
1872 // Load "algorithm2e" module
1873 addModule("algorithm2e");
1874 // Add the package options to the global document options
1875 if (!options.empty()) {
1876 if (h_options.empty())
1877 h_options = join(options, ",");
1879 h_options += ',' + join(options, ",");
1882 else if (name == "microtype") {
1883 //we internally support only microtype without params
1884 if (options.empty())
1885 h_use_microtype = "true";
1887 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1890 else if (name == "lineno") {
1891 h_use_lineno = "true";
1892 if (!options.empty()) {
1893 h_lineno_options = join(options, ",");
1898 else if (name == "changebar")
1899 h_output_changes = "true";
1901 else if (!in_lyx_preamble) {
1902 if (options.empty())
1903 h_preamble << "\\usepackage{" << name << '}';
1905 h_preamble << "\\usepackage[" << opts << "]{"
1909 if (p.next_token().cat() == catNewline ||
1910 (p.next_token().cat() == catSpace &&
1911 p.next_next_token().cat() == catNewline))
1915 // We need to do something with the options...
1916 if (!options.empty() && !detectEncoding)
1917 warning_message("Ignoring options '" + join(options, ",")
1918 + "' of package " + name + '.');
1920 // remove the whitespace
1925 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1928 Token t = p.get_token();
1929 if (t.cat() == catEscape &&
1930 is_known(t.cs(), known_if_commands))
1931 handle_if(p, in_lyx_preamble);
1933 if (!in_lyx_preamble)
1934 h_preamble << t.asInput();
1935 if (t.cat() == catEscape && t.cs() == "fi")
1942 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1944 if (contains(h_float_placement, "H"))
1945 registerAutomaticallyLoadedPackage("float");
1946 if (h_spacing != "single" && h_spacing != "default")
1947 registerAutomaticallyLoadedPackage("setspace");
1948 if (h_use_packages["amsmath"] == "2") {
1949 // amsbsy and amstext are already provided by amsmath
1950 registerAutomaticallyLoadedPackage("amsbsy");
1951 registerAutomaticallyLoadedPackage("amstext");
1954 // output the LyX file settings
1955 // Important: Keep the version formatting in sync with LyX and
1956 // lyx2lyx (bug 7951)
1957 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1958 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1959 << lyx_version_minor << '\n'
1960 << "\\lyxformat " << LYX_FORMAT << '\n'
1961 << "\\begin_document\n"
1962 << "\\begin_header\n"
1963 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1964 << "\\origin " << origin << "\n"
1965 << "\\textclass " << h_textclass << "\n";
1966 if (!h_doc_metadata.empty()) {
1967 os << "\\begin_metadata\n"
1969 << "\n\\end_metadata\n";
1971 string const raw = subdoc ? empty_string() : h_preamble.str();
1973 os << "\\begin_preamble\n";
1974 for (string::size_type i = 0; i < raw.size(); ++i) {
1975 if (raw[i] == package_beg_sep) {
1976 // Here follows some package loading code that
1977 // must be skipped if the package is loaded
1979 string::size_type j = raw.find(package_mid_sep, i);
1980 if (j == string::npos)
1982 string::size_type k = raw.find(package_end_sep, j);
1983 if (k == string::npos)
1985 string const package = raw.substr(i + 1, j - i - 1);
1986 string const replacement = raw.substr(j + 1, k - j - 1);
1987 if (auto_packages.find(package) == auto_packages.end())
1993 os << "\n\\end_preamble\n";
1995 if (!h_options.empty())
1996 os << "\\options " << h_options << "\n";
1997 os << "\\use_default_options " << h_use_default_options << "\n";
1998 if (!used_modules.empty()) {
1999 os << "\\begin_modules\n";
2000 vector<string>::const_iterator const end = used_modules.end();
2001 vector<string>::const_iterator it = used_modules.begin();
2002 for (; it != end; ++it)
2004 os << "\\end_modules\n";
2006 if (!h_includeonlys.empty()) {
2007 os << "\\begin_includeonly\n";
2008 for (auto const & iofile : h_includeonlys)
2009 os << iofile << '\n';
2010 os << "\\end_includeonly\n";
2012 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
2013 << "\\language " << h_language << "\n"
2014 << "\\language_package " << h_language_package << "\n"
2015 << "\\inputencoding " << h_inputencoding << "\n"
2016 << "\\fontencoding " << h_fontencoding << "\n"
2017 << "\\font_roman \"" << h_font_roman[0]
2018 << "\" \"" << h_font_roman[1] << "\"\n"
2019 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
2020 << "\\font_typewriter \"" << h_font_typewriter[0]
2021 << "\" \"" << h_font_typewriter[1] << "\"\n"
2022 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
2023 << "\\font_default_family " << h_font_default_family << "\n"
2024 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
2025 << "\\font_sc " << h_font_sc << "\n"
2026 << "\\font_roman_osf " << h_font_roman_osf << "\n"
2027 << "\\font_sans_osf " << h_font_sans_osf << "\n"
2028 << "\\font_typewriter_osf " << h_font_typewriter_osf << "\n";
2029 if (!h_font_roman_opts.empty())
2030 os << "\\font_roman_opts \"" << h_font_roman_opts << "\"" << '\n';
2031 os << "\\font_sf_scale " << h_font_sf_scale[0]
2032 << ' ' << h_font_sf_scale[1] << '\n';
2033 if (!h_font_sans_opts.empty())
2034 os << "\\font_sans_opts \"" << h_font_sans_opts << "\"" << '\n';
2035 os << "\\font_tt_scale " << h_font_tt_scale[0]
2036 << ' ' << h_font_tt_scale[1] << '\n';
2037 if (!h_font_cjk.empty())
2038 os << "\\font_cjk " << h_font_cjk << '\n';
2039 if (!h_font_typewriter_opts.empty())
2040 os << "\\font_typewriter_opts \"" << h_font_typewriter_opts << "\"" << '\n';
2041 os << "\\use_microtype " << h_use_microtype << '\n'
2042 << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
2043 << "\\graphics " << h_graphics << '\n'
2044 << "\\default_output_format " << h_default_output_format << "\n"
2045 << "\\output_sync " << h_output_sync << "\n";
2046 if (h_output_sync == "1")
2047 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
2048 os << "\\bibtex_command " << h_bibtex_command << "\n"
2049 << "\\index_command " << h_index_command << "\n";
2050 if (!h_float_placement.empty())
2051 os << "\\float_placement " << h_float_placement << "\n";
2052 os << "\\paperfontsize " << h_paperfontsize << "\n"
2053 << "\\spacing " << h_spacing << "\n"
2054 << "\\use_hyperref " << h_use_hyperref << '\n';
2055 if (h_use_hyperref == "true") {
2056 if (!h_pdf_title.empty())
2057 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
2058 if (!h_pdf_author.empty())
2059 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
2060 if (!h_pdf_subject.empty())
2061 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
2062 if (!h_pdf_keywords.empty())
2063 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
2064 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
2065 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
2066 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
2067 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
2068 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
2069 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
2070 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
2071 "\\pdf_backref " << h_pdf_backref << "\n"
2072 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
2073 if (!h_pdf_pagemode.empty())
2074 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
2075 if (!h_pdf_quoted_options.empty())
2076 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
2078 os << "\\papersize " << h_papersize << "\n"
2079 << "\\use_geometry " << h_use_geometry << '\n';
2080 for (map<string, string>::const_iterator it = h_use_packages.begin();
2081 it != h_use_packages.end(); ++it)
2082 os << "\\use_package " << it->first << ' ' << it->second << '\n';
2083 os << "\\cite_engine " << h_cite_engine << '\n'
2084 << "\\cite_engine_type " << h_cite_engine_type << '\n'
2085 << "\\biblio_style " << h_biblio_style << "\n"
2086 << "\\use_bibtopic " << h_use_bibtopic << "\n";
2087 if (!h_biblio_options.empty())
2088 os << "\\biblio_options " << h_biblio_options << "\n";
2089 if (!h_biblatex_bibstyle.empty())
2090 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
2091 if (!h_biblatex_citestyle.empty())
2092 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
2093 if (!h_multibib.empty())
2094 os << "\\multibib " << h_multibib << "\n";
2095 os << "\\use_indices " << h_use_indices << "\n"
2096 << "\\paperorientation " << h_paperorientation << '\n'
2097 << "\\suppress_date " << h_suppress_date << '\n'
2098 << "\\justification " << h_justification << '\n'
2099 << "\\use_refstyle " << h_use_refstyle << '\n'
2100 << "\\use_minted " << h_use_minted << '\n'
2101 << "\\use_lineno " << h_use_lineno << '\n';
2102 if (!h_lineno_options.empty())
2103 os << "\\lineno_options " << h_lineno_options << '\n';
2104 if (!h_fontcolor.empty())
2105 os << "\\fontcolor " << h_fontcolor << '\n';
2106 if (!h_notefontcolor.empty())
2107 os << "\\notefontcolor " << h_notefontcolor << '\n';
2108 if (!h_backgroundcolor.empty())
2109 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
2110 if (!h_boxbgcolor.empty())
2111 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
2112 if (index_number != 0)
2113 for (int i = 0; i < index_number; i++) {
2114 os << "\\index " << h_index[i] << '\n'
2115 << "\\shortcut " << h_shortcut[i] << '\n'
2116 << "\\color " << h_color << '\n'
2120 os << "\\index " << h_index[0] << '\n'
2121 << "\\shortcut " << h_shortcut[0] << '\n'
2122 << "\\color " << h_color << '\n'
2126 << "\\secnumdepth " << h_secnumdepth << "\n"
2127 << "\\tocdepth " << h_tocdepth << "\n"
2128 << "\\paragraph_separation " << h_paragraph_separation << "\n";
2129 if (h_paragraph_separation == "skip")
2130 os << "\\defskip " << h_defskip << "\n";
2132 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
2133 os << "\\is_math_indent " << h_is_mathindent << "\n";
2134 if (!h_mathindentation.empty())
2135 os << "\\math_indentation " << h_mathindentation << "\n";
2136 os << "\\math_numbering_side " << h_math_numbering_side << "\n";
2137 os << "\\quotes_style " << h_quotes_style << "\n"
2138 << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
2139 << "\\papercolumns " << h_papercolumns << "\n"
2140 << "\\papersides " << h_papersides << "\n"
2141 << "\\paperpagestyle " << h_paperpagestyle << "\n";
2142 if (!h_listings_params.empty())
2143 os << "\\listings_params " << h_listings_params << "\n";
2144 os << "\\tracking_changes " << h_tracking_changes << "\n"
2145 << "\\output_changes " << h_output_changes << "\n"
2146 << "\\change_bars " << h_change_bars << "\n"
2147 << "\\html_math_output " << h_html_math_output << "\n"
2148 << "\\html_css_as_file " << h_html_css_as_file << "\n"
2149 << "\\html_be_strict " << h_html_be_strict << "\n"
2150 << "\\docbook_table_output " << h_docbook_table_output << "\n"
2151 << "\\docbook_mathml_prefix " << h_docbook_mathml_prefix << "\n"
2153 << "\\end_header\n\n"
2154 << "\\begin_body\n";
2159 void Preamble::parse(Parser & p, string const & forceclass,
2160 TeX2LyXDocClass & tc)
2162 // initialize fixed types
2163 special_columns_['D'] = 3;
2164 parse(p, forceclass, false, tc);
2168 void Preamble::parse(Parser & p, string const & forceclass,
2169 bool detectEncoding, TeX2LyXDocClass & tc)
2171 bool is_full_document = false;
2172 bool is_lyx_file = false;
2173 bool in_lyx_preamble = false;
2174 bool class_set = false;
2176 // determine whether this is a full document or a fragment for inclusion
2178 Token const & t = p.get_token();
2180 if (t.cat() == catEscape && t.cs() == "documentclass") {
2181 is_full_document = true;
2187 if (detectEncoding && !is_full_document)
2190 // Force textclass if the user wanted it
2191 if (!forceclass.empty()) {
2192 setTextClass(forceclass, tc);
2196 while (is_full_document && p.good()) {
2197 if (detectEncoding && h_inputencoding != "auto-legacy" &&
2198 h_inputencoding != "auto-legacy-plain")
2201 Token const & t = p.get_token();
2203 if (!detectEncoding)
2204 debug_message("t: " + t.asInput());
2209 if (!in_lyx_preamble &&
2210 (t.cat() == catLetter ||
2211 t.cat() == catSuper ||
2212 t.cat() == catSub ||
2213 t.cat() == catOther ||
2214 t.cat() == catMath ||
2215 t.cat() == catActive ||
2216 t.cat() == catBegin ||
2217 t.cat() == catEnd ||
2218 t.cat() == catAlign ||
2219 t.cat() == catParameter)) {
2220 h_preamble << t.cs();
2224 if (!in_lyx_preamble &&
2225 (t.cat() == catSpace || t.cat() == catNewline)) {
2226 h_preamble << t.asInput();
2230 if (t.cat() == catComment) {
2231 static regex const islyxfile("%% LyX .* created this file");
2232 static regex const usercommands("User specified LaTeX commands");
2234 string const comment = t.asInput();
2236 // magically switch encoding default if it looks like XeLaTeX
2237 static string const magicXeLaTeX =
2238 "% This document must be compiled with XeLaTeX ";
2239 if (comment.size() > magicXeLaTeX.size()
2240 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
2241 && h_inputencoding == "auto-legacy") {
2242 if (!detectEncoding)
2243 warning_message("XeLaTeX comment found, switching to UTF8");
2244 h_inputencoding = "utf8";
2247 if (regex_search(comment, sub, islyxfile)) {
2249 in_lyx_preamble = true;
2250 } else if (is_lyx_file
2251 && regex_search(comment, sub, usercommands))
2252 in_lyx_preamble = false;
2253 else if (!in_lyx_preamble)
2254 h_preamble << t.asInput();
2258 if (t.cs() == "PassOptionsToPackage") {
2259 string const poptions = p.getArg('{', '}');
2260 string const package = p.verbatim_item();
2261 extra_package_options_.insert(make_pair(package, poptions));
2265 if (t.cs() == "pagestyle") {
2266 h_paperpagestyle = p.verbatim_item();
2270 if (t.cs() == "setdefaultlanguage") {
2272 // We don't yet care about non-language variant options
2273 // because LyX doesn't support this yet, see bug #8214
2275 string langopts = p.getOpt();
2276 // check if the option contains a variant, if yes, extract it
2277 string::size_type pos_var = langopts.find("variant");
2278 string::size_type i = langopts.find(',', pos_var);
2279 string::size_type k = langopts.find('=', pos_var);
2280 if (pos_var != string::npos){
2282 if (i == string::npos)
2283 variant = langopts.substr(k + 1, langopts.length() - k - 2);
2285 variant = langopts.substr(k + 1, i - k - 1);
2286 h_language = variant;
2290 h_language = p.verbatim_item();
2291 //finally translate the poyglossia name to a LyX name
2292 h_language = polyglossia2lyx(h_language);
2296 if (t.cs() == "setotherlanguage") {
2297 // We don't yet care about the option because LyX doesn't
2298 // support this yet, see bug #8214
2299 p.hasOpt() ? p.getOpt() : string();
2304 if (t.cs() == "setmainfont") {
2305 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2306 h_font_roman[1] = p.getArg('{', '}');
2307 if (!fontopts.empty()) {
2308 vector<string> opts = getVectorFromString(fontopts);
2310 for (auto const & opt : opts) {
2311 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2314 if (!fontopts.empty())
2318 h_font_roman_opts = fontopts;
2323 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
2324 // LyX currently only supports the scale option
2325 string scale, fontopts;
2327 fontopts = p.getArg('[', ']');
2328 if (!fontopts.empty()) {
2329 vector<string> opts = getVectorFromString(fontopts);
2331 for (auto const & opt : opts) {
2332 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2335 if (prefixIs(opt, "Scale=")) {
2336 scale_as_percentage(opt, scale);
2339 if (!fontopts.empty())
2345 if (t.cs() == "setsansfont") {
2347 h_font_sf_scale[1] = scale;
2348 h_font_sans[1] = p.getArg('{', '}');
2349 if (!fontopts.empty())
2350 h_font_sans_opts = fontopts;
2353 h_font_tt_scale[1] = scale;
2354 h_font_typewriter[1] = p.getArg('{', '}');
2355 if (!fontopts.empty())
2356 h_font_typewriter_opts = fontopts;
2361 if (t.cs() == "babelfont") {
2363 h_use_non_tex_fonts = true;
2364 h_language_package = "babel";
2365 if (h_inputencoding == "auto-legacy")
2366 p.setEncoding("UTF-8");
2367 // we don't care about the lang option
2368 string const lang = p.hasOpt() ? p.getArg('[', ']') : string();
2369 string const family = p.getArg('{', '}');
2370 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2371 string const fontname = p.getArg('{', '}');
2372 if (lang.empty() && family == "rm") {
2373 h_font_roman[1] = fontname;
2374 if (!fontopts.empty()) {
2375 vector<string> opts = getVectorFromString(fontopts);
2377 for (auto const & opt : opts) {
2378 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2381 if (!fontopts.empty())
2385 h_font_roman_opts = fontopts;
2388 } else if (lang.empty() && (family == "sf" || family == "tt")) {
2390 if (!fontopts.empty()) {
2391 vector<string> opts = getVectorFromString(fontopts);
2393 for (auto const & opt : opts) {
2394 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2397 if (prefixIs(opt, "Scale=")) {
2398 scale_as_percentage(opt, scale);
2401 if (!fontopts.empty())
2406 if (family == "sf") {
2408 h_font_sf_scale[1] = scale;
2409 h_font_sans[1] = fontname;
2410 if (!fontopts.empty())
2411 h_font_sans_opts = fontopts;
2414 h_font_tt_scale[1] = scale;
2415 h_font_typewriter[1] = fontname;
2416 if (!fontopts.empty())
2417 h_font_typewriter_opts = fontopts;
2421 // not rm, sf or tt or lang specific
2422 h_preamble << '\\' << t.cs();
2424 h_preamble << '[' << lang << ']';
2425 h_preamble << '{' << family << '}';
2426 if (!fontopts.empty())
2427 h_preamble << '[' << fontopts << ']';
2428 h_preamble << '{' << fontname << '}' << '\n';
2433 if (t.cs() == "date") {
2434 string argument = p.getArg('{', '}');
2435 if (argument.empty())
2436 h_suppress_date = "true";
2438 h_preamble << t.asInput() << '{' << argument << '}';
2442 if (t.cs() == "color") {
2443 string const space =
2444 (p.hasOpt() ? p.getOpt() : string());
2445 string argument = p.getArg('{', '}');
2446 // check the case that a standard color is used
2447 if (space.empty() && is_known(argument, known_basic_colors)) {
2448 h_fontcolor = rgbcolor2code(argument);
2449 registerAutomaticallyLoadedPackage("color");
2450 } else if (space.empty() && argument == "document_fontcolor")
2451 registerAutomaticallyLoadedPackage("color");
2452 // check the case that LyX's document_fontcolor is defined
2453 // but not used for \color
2455 h_preamble << t.asInput();
2457 h_preamble << space;
2458 h_preamble << '{' << argument << '}';
2459 // the color might already be set because \definecolor
2460 // is parsed before this
2466 if (t.cs() == "pagecolor") {
2467 string argument = p.getArg('{', '}');
2468 // check the case that a standard color is used
2469 if (is_known(argument, known_basic_colors)) {
2470 h_backgroundcolor = rgbcolor2code(argument);
2471 } else if (argument == "page_backgroundcolor")
2472 registerAutomaticallyLoadedPackage("color");
2473 // check the case that LyX's page_backgroundcolor is defined
2474 // but not used for \pagecolor
2476 h_preamble << t.asInput() << '{' << argument << '}';
2477 // the color might already be set because \definecolor
2478 // is parsed before this
2479 h_backgroundcolor = "";
2484 if (t.cs() == "makeatletter") {
2485 // LyX takes care of this
2486 p.setCatcode('@', catLetter);
2490 if (t.cs() == "makeatother") {
2491 // LyX takes care of this
2492 p.setCatcode('@', catOther);
2496 if (t.cs() == "makeindex") {
2497 // LyX will re-add this if a print index command is found
2502 if (t.cs() == "newindex") {
2503 string const indexname = p.getArg('[', ']');
2504 string const shortcut = p.verbatim_item();
2505 if (!indexname.empty())
2506 h_index[index_number] = indexname;
2508 h_index[index_number] = shortcut;
2509 h_shortcut[index_number] = shortcut;
2515 if (t.cs() == "addbibresource") {
2516 string const options = p.getArg('[', ']');
2517 string const arg = removeExtension(p.getArg('{', '}'));
2518 if (!options.empty()) {
2519 // check if the option contains a bibencoding, if yes, extract it
2520 string::size_type pos = options.find("bibencoding=");
2522 if (pos != string::npos) {
2523 string::size_type i = options.find(',', pos);
2524 if (i == string::npos)
2525 encoding = options.substr(pos + 1);
2527 encoding = options.substr(pos, i - pos);
2528 pos = encoding.find('=');
2529 if (pos == string::npos)
2532 encoding = encoding.substr(pos + 1);
2534 if (!encoding.empty())
2535 biblatex_encodings.push_back(normalize_filename(arg) + ' ' + encoding);
2537 biblatex_bibliographies.push_back(arg);
2541 if (t.cs() == "bibliography") {
2542 vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
2543 biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
2547 if (t.cs() == "RS@ifundefined") {
2548 string const name = p.verbatim_item();
2549 string const body1 = p.verbatim_item();
2550 string const body2 = p.verbatim_item();
2551 // only non-lyxspecific stuff
2552 if (in_lyx_preamble &&
2553 (name == "subsecref" || name == "thmref" || name == "lemref"))
2557 ss << '\\' << t.cs();
2558 ss << '{' << name << '}'
2559 << '{' << body1 << '}'
2560 << '{' << body2 << '}';
2561 h_preamble << ss.str();
2566 if (t.cs() == "AtBeginDocument") {
2567 string const name = p.verbatim_item();
2568 // only non-lyxspecific stuff
2569 if (in_lyx_preamble &&
2570 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
2571 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
2572 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
2573 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
2574 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
2575 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
2576 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
2577 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
2578 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
2579 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
2580 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
2581 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
2582 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
2583 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
2584 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
2588 ss << '\\' << t.cs();
2589 ss << '{' << name << '}';
2590 h_preamble << ss.str();
2595 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
2596 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
2597 || t.cs() == "providecommand" || t.cs() == "providecommandx"
2598 || t.cs() == "DeclareRobustCommand"
2599 || t.cs() == "DeclareRobustCommandx"
2600 || t.cs() == "ProvideTextCommandDefault"
2601 || t.cs() == "DeclareMathAccent") {
2603 if (p.next_token().character() == '*') {
2607 string const name = p.verbatim_item();
2608 string const opt1 = p.getFullOpt();
2609 string const opt2 = p.getFullOpt();
2610 string const body = p.verbatim_item();
2611 // store the in_lyx_preamble setting
2612 bool const was_in_lyx_preamble = in_lyx_preamble;
2614 if (name == "\\rmdefault")
2615 if (is_known(body, known_roman_font_packages)) {
2616 h_font_roman[0] = body;
2618 in_lyx_preamble = true;
2620 if (name == "\\sfdefault") {
2621 if (is_known(body, known_sans_font_packages)) {
2622 h_font_sans[0] = body;
2624 in_lyx_preamble = true;
2626 if (body == "LibertinusSans-OsF") {
2627 h_font_sans[0] = "LibertinusSans-LF";
2628 h_font_sans_osf = "true";
2630 in_lyx_preamble = true;
2633 if (name == "\\ttdefault")
2634 if (is_known(body, known_typewriter_font_packages)) {
2635 h_font_typewriter[0] = body;
2637 in_lyx_preamble = true;
2639 if (name == "\\familydefault") {
2640 string family = body;
2641 // remove leading "\"
2642 h_font_default_family = family.erase(0,1);
2644 in_lyx_preamble = true;
2646 if (name == "\\LibertinusSans@scale") {
2647 if (isStrDbl(body)) {
2648 h_font_sf_scale[0] = convert<string>(
2649 static_cast<int>(100 * convert<double>(body)));
2652 if (name == "\\LibertinusMono@scale") {
2653 if (isStrDbl(body)) {
2654 h_font_tt_scale[0] = convert<string>(
2655 static_cast<int>(100 * convert<double>(body)));
2659 // remove LyX-specific definitions that are re-added by LyX
2661 // \lyxline is an ancient command that is converted by tex2lyx into
2662 // a \rule therefore remove its preamble code
2663 if (name == "\\lyxdot" || name == "\\lyxarrow"
2664 || name == "\\lyxline" || name == "\\LyX") {
2666 in_lyx_preamble = true;
2669 // Add the command to the known commands
2670 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
2672 // only non-lyxspecific stuff
2673 if (!in_lyx_preamble) {
2675 ss << '\\' << t.cs();
2678 ss << '{' << name << '}' << opt1 << opt2
2679 << '{' << body << "}";
2680 if (prefixIs(t.cs(), "renew") || !contains(h_preamble.str(), ss.str()))
2681 h_preamble << ss.str();
2683 ostream & out = in_preamble ? h_preamble : os;
2684 out << "\\" << t.cs() << "{" << name << "}"
2685 << opts << "{" << body << "}";
2688 // restore the in_lyx_preamble setting
2689 in_lyx_preamble = was_in_lyx_preamble;
2693 if (t.cs() == "documentclass") {
2694 vector<string>::iterator it;
2695 vector<string> opts = split_options(p.getArg('[', ']'));
2696 // FIXME This does not work for classes that have a
2697 // different name in LyX than in LaTeX
2698 string tclass = p.getArg('{', '}');
2699 if (contains(tclass, '/')) {
2700 // It's considered bad practice, but it is still
2701 // sometimes done (and possible) to enter the documentclass
2702 // as a path, e.g. \documentclass{subdir/class} (#12284)
2703 // we strip the name in this case.
2705 tclass = rsplit(tclass, dummy, '/');
2708 // Only set text class if a class hasn't been forced
2709 // (this was set above)
2711 // textclass needs to be set at this place (if not already done)
2712 // as we need to know it for other parameters
2713 // (such as class-dependent paper size)
2714 setTextClass(tclass, tc);
2719 // Try those who are (most likely) known to all packages first
2720 handle_opt(opts, known_fontsizes, h_paperfontsize);
2721 delete_opt(opts, known_fontsizes);
2722 // delete "pt" at the end
2723 string::size_type i = h_paperfontsize.find("pt");
2724 if (i != string::npos)
2725 h_paperfontsize.erase(i);
2726 // Now those known specifically to the class
2727 vector<string> class_fsizes = getVectorFromString(tc.opt_fontsize(), "|");
2728 string const fsize_format = tc.fontsizeformat();
2729 for (auto const & fsize : class_fsizes) {
2730 string latexsize = subst(fsize_format, "$$s", fsize);
2731 vector<string>::iterator it = find(opts.begin(), opts.end(), latexsize);
2732 if (it != opts.end()) {
2733 h_paperfontsize = fsize;
2739 // The documentclass options are always parsed before the options
2740 // of the babel call so that a language cannot overwrite the babel
2742 handle_opt(opts, known_languages, h_language);
2743 delete_opt(opts, known_languages);
2746 if ((it = find(opts.begin(), opts.end(), "fleqn"))
2748 h_is_mathindent = "1";
2751 // formula numbering side
2752 if ((it = find(opts.begin(), opts.end(), "leqno"))
2754 h_math_numbering_side = "left";
2757 else if ((it = find(opts.begin(), opts.end(), "reqno"))
2759 h_math_numbering_side = "right";
2763 // paper orientation
2764 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
2765 h_paperorientation = "landscape";
2769 if ((it = find(opts.begin(), opts.end(), "oneside"))
2774 if ((it = find(opts.begin(), opts.end(), "twoside"))
2780 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
2782 h_papercolumns = "1";
2785 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
2787 h_papercolumns = "2";
2791 // some size options are known by the document class, other sizes
2792 // are handled by the \geometry command of the geometry package
2793 vector<string> class_psizes = getVectorFromString(tc.opt_pagesize(), "|");
2794 string const psize_format = tc.pagesizeformat();
2795 for (auto const & psize : class_psizes) {
2796 string latexsize = subst(psize_format, "$$s", psize);
2797 vector<string>::iterator it = find(opts.begin(), opts.end(), latexsize);
2798 if (it != opts.end()) {
2799 h_papersize = psize;
2803 if (psize_format == "$$spaper")
2805 // Also try with the default format since this is understood by
2807 latexsize = psize + "paper";
2808 it = find(opts.begin(), opts.end(), latexsize);
2809 if (it != opts.end()) {
2810 h_papersize = psize;
2815 // the remaining options
2816 h_options = join(opts, ",");
2820 if (t.cs() == "DocumentMetadata") {
2821 h_doc_metadata = trimSpaceAndEol(p.getArg('{', '}'));
2825 if (t.cs() == "usepackage") {
2826 string const options = p.getArg('[', ']');
2827 string const name = p.getArg('{', '}');
2828 vector<string> vecnames;
2829 split(name, vecnames, ',');
2830 vector<string>::const_iterator it = vecnames.begin();
2831 vector<string>::const_iterator end = vecnames.end();
2832 for (; it != end; ++it)
2833 handle_package(p, trimSpaceAndEol(*it), options,
2834 in_lyx_preamble, detectEncoding);
2838 if (t.cs() == "inputencoding") {
2839 string const encoding = p.getArg('{','}');
2840 Encoding const * const enc = encodings.fromLaTeXName(
2841 encoding, Encoding::inputenc, true);
2843 if (!detectEncoding)
2844 warning_message("Unknown encoding " + encoding + ". Ignoring.");
2847 h_inputencoding = enc->name();
2848 p.setEncoding(enc->iconvName());
2853 if (t.cs() == "newenvironment") {
2854 string const name = p.getArg('{', '}');
2855 string const opt1 = p.getFullOpt();
2856 string const opt2 = p.getFullOpt();
2857 string const beg = p.verbatim_item();
2858 string const end = p.verbatim_item();
2859 if (!in_lyx_preamble) {
2860 h_preamble << "\\newenvironment{" << name
2861 << '}' << opt1 << opt2 << '{'
2862 << beg << "}{" << end << '}';
2864 add_known_environment(name, opt1, !opt2.empty(),
2865 from_utf8(beg), from_utf8(end));
2869 if (t.cs() == "newtheorem") {
2871 if (p.next_token().character() == '*') {
2875 string const name = p.getArg('{', '}');
2876 string const opt1 = p.getFullOpt();
2877 string const opt2 = p.getFullOpt();
2878 string const body = p.verbatim_item();
2879 string const opt3 = p.getFullOpt();
2880 string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
2882 string const complete = cmd + "{" + name + '}' +
2883 opt1 + opt2 + '{' + body + '}' + opt3;
2885 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
2887 if (!in_lyx_preamble)
2888 h_preamble << complete;
2892 if (t.cs() == "def") {
2893 string name = p.get_token().cs();
2894 // In fact, name may be more than the name:
2895 // In the test case of bug 8116
2896 // name == "csname SF@gobble@opt \endcsname".
2897 // Therefore, we need to use asInput() instead of cs().
2898 while (p.next_token().cat() != catBegin)
2899 name += p.get_token().asInput();
2900 if (!in_lyx_preamble)
2901 h_preamble << "\\def\\" << name << '{'
2902 << p.verbatim_item() << "}";
2906 if (t.cs() == "newcolumntype") {
2907 string const name = p.getArg('{', '}');
2908 trimSpaceAndEol(name);
2910 string opts = p.getOpt();
2911 if (!opts.empty()) {
2912 istringstream is(string(opts, 1));
2915 special_columns_[name[0]] = nargs;
2916 h_preamble << "\\newcolumntype{" << name << "}";
2918 h_preamble << "[" << nargs << "]";
2919 h_preamble << "{" << p.verbatim_item() << "}";
2923 if (t.cs() == "setcounter") {
2924 string const name = p.getArg('{', '}');
2925 string const content = p.getArg('{', '}');
2926 if (name == "secnumdepth")
2927 h_secnumdepth = content;
2928 else if (name == "tocdepth")
2929 h_tocdepth = content;
2931 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
2935 if (t.cs() == "setlength") {
2936 string const name = p.verbatim_item();
2937 string const content = p.verbatim_item();
2938 // the paragraphs are only not indented when \parindent is set to zero
2939 if (name == "\\parindent" && content != "")
2940 h_paragraph_indentation = translate_len(content);
2941 else if (name == "\\parskip" && isPackageUsed("parskip")) {
2942 if (content == "\\smallskipamount")
2943 h_defskip = "smallskip";
2944 else if (content == "\\medskipamount")
2945 h_defskip = "medskip";
2946 else if (content == "\\bigskipamount")
2947 h_defskip = "bigskip";
2948 else if (content == "\\baselineskip")
2949 h_defskip = "fullline";
2951 h_defskip = translate_len(content);
2952 } else if (name == "\\mathindent") {
2953 h_mathindentation = translate_len(content);
2955 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2959 if (t.cs() == "onehalfspacing") {
2960 h_spacing = "onehalf";
2964 if (t.cs() == "doublespacing") {
2965 h_spacing = "double";
2969 if (t.cs() == "setstretch") {
2970 h_spacing = "other " + p.verbatim_item();
2974 if (t.cs() == "synctex") {
2975 // the scheme is \synctex=value
2976 // where value can only be "1" or "-1"
2977 h_output_sync = "1";
2978 // there can be any character behind the value (e.g. a linebreak or a '\'
2979 // therefore we extract it char by char
2981 string value = p.get_token().asInput();
2983 value += p.get_token().asInput();
2984 h_output_sync_macro = "\\synctex=" + value;
2988 if (t.cs() == "begin") {
2989 string const name = p.getArg('{', '}');
2990 if (name == "document")
2992 h_preamble << "\\begin{" << name << "}";
2996 if (t.cs() == "geometry") {
2997 vector<string> opts = split_options(p.getArg('{', '}'));
2998 handle_geometry(opts);
3002 if (t.cs() == "definecolor") {
3003 string const color = p.getArg('{', '}');
3004 string const space = p.getArg('{', '}');
3005 string const value = p.getArg('{', '}');
3006 if (color == "document_fontcolor" && space == "rgb") {
3007 RGBColor c(RGBColorFromLaTeX(value));
3008 h_fontcolor = X11hexname(c);
3009 } else if (color == "note_fontcolor" && space == "rgb") {
3010 RGBColor c(RGBColorFromLaTeX(value));
3011 h_notefontcolor = X11hexname(c);
3012 } else if (color == "page_backgroundcolor" && space == "rgb") {
3013 RGBColor c(RGBColorFromLaTeX(value));
3014 h_backgroundcolor = X11hexname(c);
3015 } else if (color == "shadecolor" && space == "rgb") {
3016 RGBColor c(RGBColorFromLaTeX(value));
3017 h_boxbgcolor = X11hexname(c);
3019 h_preamble << "\\definecolor{" << color
3020 << "}{" << space << "}{" << value
3026 if (t.cs() == "bibliographystyle") {
3027 h_biblio_style = p.verbatim_item();
3031 if (t.cs() == "jurabibsetup") {
3032 // FIXME p.getArg('{', '}') is most probably wrong (it
3033 // does not handle nested braces).
3034 // Use p.verbatim_item() instead.
3035 vector<string> jurabibsetup =
3036 split_options(p.getArg('{', '}'));
3037 // add jurabibsetup to the jurabib package options
3038 add_package("jurabib", jurabibsetup);
3039 if (!jurabibsetup.empty()) {
3040 h_preamble << "\\jurabibsetup{"
3041 << join(jurabibsetup, ",") << '}';
3046 if (t.cs() == "hypersetup") {
3047 vector<string> hypersetup =
3048 split_options(p.verbatim_item());
3049 // add hypersetup to the hyperref package options
3050 handle_hyperref(hypersetup);
3051 if (!hypersetup.empty()) {
3052 h_preamble << "\\hypersetup{"
3053 << join(hypersetup, ",") << '}';
3058 if (t.cs() == "includeonly") {
3059 vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
3060 for (auto & iofile : includeonlys) {
3061 string filename(normalize_filename(iofile));
3062 string const path = getMasterFilePath(true);
3063 // We want to preserve relative/absolute filenames,
3064 // therefore path is only used for testing
3065 if (!makeAbsPath(filename, path).exists()) {
3066 // The file extension is probably missing.
3067 // Now try to find it out.
3068 string const tex_name =
3069 find_file(filename, path,
3070 known_tex_extensions);
3071 if (!tex_name.empty())
3072 filename = tex_name;
3075 if (makeAbsPath(filename, path).exists())
3076 fix_child_filename(filename);
3078 warning_message("Warning: Could not find included file '"
3080 outname = changeExtension(filename, "lyx");
3081 h_includeonlys.push_back(outname);
3086 if (is_known(t.cs(), known_if_3arg_commands)) {
3087 // prevent misparsing of \usepackage if it is used
3088 // as an argument (see e.g. our own output of
3089 // \@ifundefined above)
3090 string const arg1 = p.verbatim_item();
3091 string const arg2 = p.verbatim_item();
3092 string const arg3 = p.verbatim_item();
3093 // test case \@ifundefined{date}{}{\date{}}
3094 if (t.cs() == "@ifundefined" && arg1 == "date" &&
3095 arg2.empty() && arg3 == "\\date{}") {
3096 h_suppress_date = "true";
3097 // older tex2lyx versions did output
3098 // \@ifundefined{definecolor}{\usepackage{color}}{}
3099 } else if (t.cs() == "@ifundefined" &&
3100 arg1 == "definecolor" &&
3101 arg2 == "\\usepackage{color}" &&
3103 if (!in_lyx_preamble)
3104 h_preamble << package_beg_sep
3107 << "\\@ifundefined{definecolor}{color}{}"
3110 //\@ifundefined{showcaptionsetup}{}{%
3111 // \PassOptionsToPackage{caption=false}{subfig}}
3112 // that LyX uses for subfloats
3113 } else if (t.cs() == "@ifundefined" &&
3114 arg1 == "showcaptionsetup" && arg2.empty()
3115 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
3117 } else if (!in_lyx_preamble) {
3118 h_preamble << t.asInput()
3119 << '{' << arg1 << '}'
3120 << '{' << arg2 << '}'
3121 << '{' << arg3 << '}';
3126 if (is_known(t.cs(), known_if_commands)) {
3127 // must not parse anything in conditional code, since
3128 // LyX would output the parsed contents unconditionally
3129 if (!in_lyx_preamble)
3130 h_preamble << t.asInput();
3131 handle_if(p, in_lyx_preamble);
3135 if (!t.cs().empty() && !in_lyx_preamble) {
3136 h_preamble << '\\' << t.cs();
3141 // set textclass if not yet done (snippets without \documentclass and forced class)
3143 setTextClass(h_textclass, tc);
3145 // remove the whitespace
3148 if (h_papersides.empty()) {
3151 h_papersides = ss.str();
3154 // If the CJK package is used we cannot set the document language from
3155 // the babel options. Instead, we guess which language is used most
3156 // and set this one.
3157 default_language = h_language;
3158 if (is_full_document &&
3159 (auto_packages.find("CJK") != auto_packages.end() ||
3160 auto_packages.find("CJKutf8") != auto_packages.end())) {
3162 h_language = guessLanguage(p, default_language);
3164 if (explicit_babel && h_language != default_language) {
3165 // We set the document language to a CJK language,
3166 // but babel is explicitly called in the user preamble
3167 // without options. LyX will not add the default
3168 // language to the document options if it is either
3169 // english, or no text is set as default language.
3170 // Therefore we need to add a language option explicitly.
3171 // FIXME: It would be better to remove all babel calls
3172 // from the user preamble, but this is difficult
3173 // without re-introducing bug 7861.
3174 if (h_options.empty())
3175 h_options = lyx2babel(default_language);
3177 h_options += ',' + lyx2babel(default_language);
3181 // Finally, set the quote style.
3182 // LyX knows the following quotes styles:
3183 // british, cjk, cjkangle, danish, english, french, german,
3184 // polish, russian, swedish, swiss, and hebrew
3185 // conversion list taken from
3186 // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
3187 // (quotes for kazakh are unknown)
3189 if (is_known(h_language, known_british_quotes_languages))
3190 h_quotes_style = "british";
3192 else if (is_known(h_language, known_cjk_quotes_languages))
3193 h_quotes_style = "cjk";
3195 else if (is_known(h_language, known_cjkangle_quotes_languages))
3196 h_quotes_style = "cjkangle";
3198 else if (is_known(h_language, known_danish_quotes_languages))
3199 h_quotes_style = "danish";
3201 else if (is_known(h_language, known_french_quotes_languages))
3202 h_quotes_style = "french";
3204 else if (is_known(h_language, known_german_quotes_languages))
3205 h_quotes_style = "german";
3207 else if (is_known(h_language, known_polish_quotes_languages))
3208 h_quotes_style = "polish";
3210 else if (is_known(h_language, known_hungarian_quotes_languages))
3211 h_quotes_style = "hungarian";
3213 else if (is_known(h_language, known_russian_quotes_languages))
3214 h_quotes_style = "russian";
3216 else if (is_known(h_language, known_swedish_quotes_languages))
3217 h_quotes_style = "swedish";
3219 else if (is_known(h_language, known_swiss_quotes_languages))
3220 h_quotes_style = "swiss";
3222 else if (is_known(h_language, known_hebrew_quotes_languages))
3223 h_quotes_style = "hebrew";
3226 else if (is_known(h_language, known_english_quotes_languages))
3227 h_quotes_style = "english";
3231 string Preamble::parseEncoding(Parser & p, string const & forceclass)
3233 TeX2LyXDocClass dummy;
3234 parse(p, forceclass, true, dummy);
3235 if (h_inputencoding != "auto-legacy" && h_inputencoding != "auto-legacy-plain")
3236 return h_inputencoding;
3241 string babel2lyx(string const & language)
3243 char const * const * where = is_known(language, known_languages);
3245 return known_coded_languages[where - known_languages];
3250 string lyx2babel(string const & language)
3252 char const * const * where = is_known(language, known_coded_languages);
3254 return known_languages[where - known_coded_languages];
3259 string Preamble::polyglossia2lyx(string const & language)
3261 char const * const * where = is_known(language, polyglossia_languages);
3263 return coded_polyglossia_languages[where - polyglossia_languages];
3268 string rgbcolor2code(string const & name)
3270 char const * const * where = is_known(name, known_basic_colors);
3272 // "red", "green" etc
3273 return known_basic_color_codes[where - known_basic_colors];
3275 // "255,0,0", "0,255,0" etc
3276 RGBColor c(RGBColorFromLaTeX(name));
3277 return X11hexname(c);