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"
35 using namespace lyx::support;
44 // CJK languages are handled in text.cpp, polyglossia languages are listed
47 * known babel language names (including synonyms)
48 * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
49 * please keep this in sync with known_coded_languages line by line!
51 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
52 "american", "arabic", "arabtex", "australian", "austrian", "azerbaijani", "bahasa", "bahasai",
53 "bahasam", "basque", "belarusian", "bosnian", "brazil", "brazilian", "breton", "british",
54 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
55 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "francais",
56 "french", "frenchb", "frenchle", "frenchpro", "friulan", "galician", "german", "germanb",
57 "georgian", "greek", "hebrew", "hungarian", "icelandic", "indon", "indonesian",
58 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
59 "latvian", "lithuanian", "lowersorbian", "lsorbian", "macedonian", "magyar", "malay", "meyalu",
60 "mongolian", "naustrian", "newzealand", "ngerman", "ngermanb", "norsk", "nswissgerman",
61 "nynorsk", "piedmontese", "polutonikogreek", "polish", "portuges", "portuguese",
62 "romanian", "romansh", "russian", "russianb", "samin", "scottish", "serbian", "serbian-latin",
63 "slovak", "slovene", "spanish", "swedish", "swissgerman", "thai", "turkish", "turkmen",
64 "ukraineb", "ukrainian", "uppersorbian", "UKenglish", "USenglish", "usorbian",
69 * the same as known_languages with .lyx names
70 * please keep this in sync with known_languages line by line!
72 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
73 "american", "arabic_arabi", "arabic_arabtex", "australian", "austrian", "azerbaijani", "bahasa", "bahasa",
74 "bahasam", "basque", "belarusian", "bosnian", "brazilian", "brazilian", "breton", "british",
75 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
76 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "french",
77 "french", "french", "french", "french", "friulan", "galician", "german", "german",
78 "georgian", "greek", "hebrew", "magyar", "icelandic", "bahasa", "bahasa",
79 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
80 "latvian", "lithuanian", "lowersorbian", "lowersorbian", "macedonian", "magyar", "bahasam", "bahasam",
81 "mongolian", "naustrian", "newzealand", "ngerman", "ngerman", "norsk", "german-ch",
82 "nynorsk", "piedmontese", "polutonikogreek", "polish", "portuguese", "portuguese",
83 "romanian", "romansh", "russian", "russian", "samin", "scottish", "serbian", "serbian-latin",
84 "slovak", "slovene", "spanish", "swedish", "german-ch-old", "thai", "turkish", "turkmen",
85 "ukrainian", "ukrainian", "uppersorbian", "english", "english", "uppersorbian",
86 "vietnamese", "welsh",
89 /// languages with british quotes (.lyx names)
90 const char * const known_british_quotes_languages[] = {"british", "welsh", 0};
92 /// languages with cjk quotes (.lyx names)
93 const char * const known_cjk_quotes_languages[] = {"chinese-traditional",
94 "japanese", "japanese-cjk", 0};
96 /// languages with cjk-angle quotes (.lyx names)
97 const char * const known_cjkangle_quotes_languages[] = {"korean", 0};
99 /// languages with danish quotes (.lyx names)
100 const char * const known_danish_quotes_languages[] = {"danish", 0};
102 /// languages with english quotes (.lyx names)
103 const char * const known_english_quotes_languages[] = {"american", "australian",
104 "bahasa", "bahasam", "bengali", "brazilian", "canadian", "chinese-simplified", "english",
105 "esperanto", "farsi", "interlingua", "irish", "newzealand", "scottish",
106 "thai", "turkish", "vietnamese", 0};
108 /// languages with french quotes (.lyx names)
109 const char * const known_french_quotes_languages[] = {"ancientgreek",
110 "arabic_arabi", "arabic_arabtex", "asturian", "belarusian", "breton",
111 "canadien", "catalan", "french", "friulan", "galician", "italian", "occitan",
112 "piedmontese", "portuguese", "spanish", "spanish-mexico", 0};
114 /// languages with german quotes (.lyx names)
115 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
116 "czech", "estonian", "georgian", "german", "icelandic", "latvian", "lithuanian",
117 "lowersorbian", "macedonian", "naustrian", "ngerman", "romansh", "slovak", "slovene",
120 /// languages with polish quotes (.lyx names)
121 const char * const known_polish_quotes_languages[] = {"afrikaans", "bosnian", "croatian",
122 "dutch", "magyar", "polish", "romanian", "serbian", "serbian-latin", 0};
124 /// languages with hungarian quotes (.lyx names)
125 const char * const known_hungarian_quotes_languages[] = {"magyar", 0};
127 /// languages with russian quotes (.lyx names)
128 const char * const known_russian_quotes_languages[] = {"azerbaijani", "oldrussian",
129 "russian", "ukrainian", 0};
131 /// languages with swedish quotes (.lyx names)
132 const char * const known_swedish_quotes_languages[] = {"finnish", "swedish", 0};
134 /// languages with swiss quotes (.lyx names)
135 const char * const known_swiss_quotes_languages[] = {"albanian",
136 "armenian", "basque", "churchslavonic", "german-ch", "german-ch-old",
137 "norsk", "nynorsk", "turkmen", "ukrainian", "vietnamese", 0};
139 /// known language packages from the times before babel
140 const char * const known_old_language_packages[] = {"french", "frenchle",
141 "frenchpro", "german", "ngerman", "pmfrench", 0};
143 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
145 const char * const known_roman_font_packages[] = { "ae", "beraserif", "bookman",
146 "ccfonts", "chancery", "charter", "cmr", "cochineal", "crimson", "CrimsonPro", "DejaVuSerif",
147 "DejaVuSerifCondensed", "fourier", "garamondx", "libertine", "libertineRoman", "libertine-type1",
148 "lmodern", "mathdesign", "mathpazo", "mathptmx", "MinionPro", "newcent", "noto", "noto-serif",
149 "PTSerif", "tgbonum", "tgchorus", "tgpagella", "tgschola", "tgtermes", "utopia", "xcharter", 0 };
151 const char * const known_sans_font_packages[] = { "avant", "berasans", "biolinum",
152 "biolinum-type1", "cantarell", "Chivo", "cmbr", "cmss", "DejaVuSans", "DejaVuSansCondensed", "FiraSans", "helvet", "iwona",
153 "iwonac", "iwonal", "iwonalc", "kurier", "kurierc", "kurierl", "kurierlc", "LibertinusSans-LF", "lmss", "noto-sans", "PTSans",
154 "tgadventor", "tgheros", "uop", 0 };
156 const char * const known_typewriter_font_packages[] = { "beramono", "cmtl", "cmtt", "courier", "DejaVuSansMono",
157 "FiraMono", "lmtt", "luximono", "libertineMono", "libertineMono-type1", "LibertinusMono-TLF", "lmodern",
158 "mathpazo", "mathptmx", "newcent", "noto-mono", "PTMono", "tgcursor", "txtt", 0 };
160 const char * const known_math_font_packages[] = { "eulervm", "newtxmath", 0};
162 const char * const known_latex_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
163 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
164 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
165 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
166 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
168 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
169 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
171 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
172 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
175 /// commands that can start an \if...\else...\endif sequence
176 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
177 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
178 "ifsidecap", "ifupgreek", 0};
180 const char * const known_basic_colors[] = {"black", "blue", "brown", "cyan",
181 "darkgray", "gray", "green", "lightgray", "lime", "magenta", "orange", "olive",
182 "pink", "purple", "red", "teal", "violet", "white", "yellow", 0};
184 const char * const known_basic_color_codes[] = {"#000000", "#0000ff", "#964B00", "#00ffff",
185 "#a9a9a9", "#808080", "#00ff00", "#d3d3d3", "#bfff00", "#ff00ff", "#ff7f00", "#808000",
186 "#ffc0cb", "#800080", "#ff0000", "#008080", "#8f00ff", "#ffffff", "#ffff00", 0};
188 /// conditional commands with three arguments like \@ifundefined{}{}{}
189 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
193 * Known file extensions for TeX files as used by \\includeonly
195 char const * const known_tex_extensions[] = {"tex", 0};
197 /// packages that work only in xetex
198 /// polyglossia is handled separately
199 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
200 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
201 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
203 /// packages that are automatically skipped if loaded by LyX
204 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
205 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color",
206 "float", "fontspec", "framed", "graphicx", "hhline", "ifthen", "longtable",
207 "makeidx", "minted", "multirow", "nomencl", "parskip", "pdfpages", "prettyref", "refstyle",
208 "rotating", "rotfloat", "splitidx", "setspace", "subscript", "tabularx","textcomp", "tipa",
209 "tipx", "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xcolor", "xltabular",
212 // codes used to remove packages that are loaded automatically by LyX.
213 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
214 const char package_beg_sep = '\001';
215 const char package_mid_sep = '\002';
216 const char package_end_sep = '\003';
219 // returns true if at least one of the options in what has been found
220 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
226 // the last language option is the document language (for babel and LyX)
227 // the last size option is the document font size
228 vector<string>::iterator it;
229 vector<string>::iterator position = opts.begin();
230 for (; *what; ++what) {
231 it = find(opts.begin(), opts.end(), *what);
232 if (it != opts.end()) {
233 if (it >= position) {
244 void delete_opt(vector<string> & opts, char const * const * what)
249 // remove found options from the list
250 // do this after handle_opt to avoid potential memory leaks
251 vector<string>::iterator it;
252 for (; *what; ++what) {
253 it = find(opts.begin(), opts.end(), *what);
254 if (it != opts.end())
261 * Split a package options string (keyval format) into a vector.
263 * authorformat=smallcaps,
265 * titleformat=colonsep,
266 * bibformat={tabular,ibidem,numbered}
268 vector<string> split_options(string const & input)
270 vector<string> options;
274 Token const & t = p.get_token();
275 if (t.asInput() == ",") {
276 options.push_back(trimSpaceAndEol(option));
278 } else if (t.asInput() == "=") {
281 if (p.next_token().asInput() == "{")
282 option += '{' + p.getArg('{', '}') + '}';
283 } else if (t.cat() != catSpace && t.cat() != catComment)
284 option += t.asInput();
288 options.push_back(trimSpaceAndEol(option));
295 * Retrieve a keyval option "name={value with=sign}" named \p name from
296 * \p options and return the value.
297 * The found option is also removed from \p options.
299 string process_keyval_opt(vector<string> & options, string const & name)
301 for (size_t i = 0; i < options.size(); ++i) {
302 vector<string> option;
303 split(options[i], option, '=');
304 if (option.size() < 2)
306 if (option[0] == name) {
307 options.erase(options.begin() + i);
308 option.erase(option.begin());
309 return join(option, "=");
315 } // anonymous namespace
319 * known polyglossia language names (including variants)
320 * FIXME: support spelling=old for german variants (german vs. ngerman LyX names etc)
322 const char * const Preamble::polyglossia_languages[] = {
323 "albanian", "american", "amharic", "ancient", "arabic", "armenian", "asturian", "australian",
324 "bahasai", "bahasam", "basque", "bengali", "brazil", "brazilian", "breton", "british", "bulgarian",
325 "catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
326 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
327 "galician", "greek", "monotonic", "hebrew", "hindi",
328 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean",
329 "lao", "latin", "latvian", "lithuanian", "lsorbian", "magyar", "malayalam", "marathi",
330 "austrian", "newzealand", "german", "norsk", "nynorsk", "occitan", "oldrussian",
331 "piedmontese", "polish", "polytonic", "portuges", "romanian", "romansh", "russian",
332 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovenian", "spanish", "swedish", "syriac",
333 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
334 "ukrainian", "urdu", "usorbian", "vietnamese", "welsh", 0};
335 // not yet supported by LyX: "korean", "nko"
338 * the same as polyglossia_languages with .lyx names
339 * please keep this in sync with polyglossia_languages line by line!
341 const char * const Preamble::coded_polyglossia_languages[] = {
342 "albanian", "american", "amharic", "ancientgreek", "arabic_arabi", "armenian", "asturian", "australian",
343 "bahasa", "bahasam", "basque", "bengali", "brazilian", "brazilian", "breton", "british", "bulgarian",
344 "catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
345 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
346 "galician", "greek", "greek", "hebrew", "hindi",
347 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean",
348 "lao", "latin", "latvian", "lithuanian", "lowersorbian", "magyar", "malayalam", "marathi",
349 "naustrian","newzealand", "ngerman", "norsk", "nynorsk", "occitan", "oldrussian",
350 "piedmontese", "polish", "polutonikogreek", "portuges", "romanian", "romansh", "russian",
351 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovene", "spanish", "swedish", "syriac",
352 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
353 "ukrainian", "urdu", "uppersorbian", "vietnamese", "welsh", 0};
354 // not yet supported by LyX: "korean-polyglossia", "nko"
357 bool Preamble::usePolyglossia() const
359 return h_use_non_tex_fonts && h_language_package == "default";
363 bool Preamble::indentParagraphs() const
365 return h_paragraph_separation == "indent";
369 bool Preamble::isPackageUsed(string const & package) const
371 return used_packages.find(package) != used_packages.end();
375 bool Preamble::isPackageAutoLoaded(string const & package) const
377 return auto_packages.find(package) != auto_packages.end();
381 vector<string> Preamble::getPackageOptions(string const & package) const
383 map<string, vector<string> >::const_iterator it = used_packages.find(package);
384 if (it != used_packages.end())
386 return vector<string>();
390 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
392 auto_packages.insert(package);
396 void Preamble::addModule(string const & module)
398 for (auto const & m : used_modules) {
402 used_modules.push_back(module);
406 void Preamble::suppressDate(bool suppress)
409 h_suppress_date = "true";
411 h_suppress_date = "false";
415 void Preamble::registerAuthor(std::string const & name, string const & initials)
417 Author author(from_utf8(name), empty_docstring(), from_utf8(initials));
418 author.setUsed(true);
419 authors_.record(author);
420 h_tracking_changes = "true";
421 h_output_changes = "true";
425 Author const & Preamble::getAuthor(std::string const & name) const
427 Author author(from_utf8(name), empty_docstring(), empty_docstring());
428 for (AuthorList::Authors::const_iterator it = authors_.begin();
429 it != authors_.end(); ++it)
432 static Author const dummy;
437 int Preamble::getSpecialTableColumnArguments(char c) const
439 map<char, int>::const_iterator it = special_columns_.find(c);
440 if (it == special_columns_.end())
446 void Preamble::add_package(string const & name, vector<string> & options)
448 // every package inherits the global options
449 used_packages.insert({name, split_options(h_options)});
451 // Insert options passed via PassOptionsToPackage
452 for (auto const & p : extra_package_options_) {
453 if (p.first == name) {
454 vector<string> eo = getVectorFromString(p.second);
455 for (auto const & eoi : eo)
456 options.push_back(eoi);
460 vector<string> & v = used_packages[name];
461 v.insert(v.end(), options.begin(), options.end());
462 if (name == "jurabib") {
463 // Don't output the order argument (see the cite command
464 // handling code in text.cpp).
465 vector<string>::iterator end =
466 remove(options.begin(), options.end(), "natbiborder");
467 end = remove(options.begin(), end, "jurabiborder");
468 options.erase(end, options.end());
472 void Preamble::setTextClass(string const & tclass, TeX2LyXDocClass & tc)
474 h_textclass = tclass;
475 tc.setName(h_textclass);
476 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
477 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
485 // Given is a string like "scaled=0.9" or "scale=0.9", return 0.9 * 100
486 bool scale_as_percentage(string const & scale, string & percentage)
488 if (contains(scale, '=')) {
489 string const value = support::split(scale, '=');
490 if (isStrDbl(value)) {
491 percentage = convert<string>(
492 static_cast<int>(100 * convert<double>(value)));
500 string remove_braces(string const & value)
504 if (value[0] == '{' && value[value.length()-1] == '}')
505 return value.substr(1, value.length()-2);
509 } // anonymous namespace
512 Preamble::Preamble() : one_language(true), explicit_babel(false),
513 title_layout_found(false), index_number(0), h_font_cjk_set(false)
517 h_biblio_style = "plain";
518 h_bibtex_command = "default";
519 h_cite_engine = "basic";
520 h_cite_engine_type = "default";
522 h_defskip = "medskip";
523 h_dynamic_quotes = false;
526 h_fontencoding = "default";
527 h_font_roman[0] = "default";
528 h_font_roman[1] = "default";
529 h_font_sans[0] = "default";
530 h_font_sans[1] = "default";
531 h_font_typewriter[0] = "default";
532 h_font_typewriter[1] = "default";
533 h_font_math[0] = "auto";
534 h_font_math[1] = "auto";
535 h_font_default_family = "default";
536 h_use_non_tex_fonts = false;
538 h_font_roman_osf = "false";
539 h_font_sans_osf = "false";
540 h_font_typewriter_osf = "false";
541 h_font_sf_scale[0] = "100";
542 h_font_sf_scale[1] = "100";
543 h_font_tt_scale[0] = "100";
544 h_font_tt_scale[1] = "100";
545 // h_font_roman_opts;
547 // h_font_typewriter_opts;
549 h_is_mathindent = "0";
550 h_math_numbering_side = "default";
551 h_graphics = "default";
552 h_default_output_format = "default";
553 h_html_be_strict = "false";
554 h_html_css_as_file = "0";
555 h_html_math_output = "0";
556 h_docbook_table_output = "0";
557 h_docbook_mathml_prefix = "1";
558 h_index[0] = "Index";
559 h_index_command = "default";
560 h_inputencoding = "auto-legacy";
561 h_justification = "true";
562 h_language = "english";
563 h_language_package = "none";
565 h_maintain_unincluded_children = "no";
569 h_output_changes = "false";
570 h_change_bars = "false";
572 //h_output_sync_macro
573 h_papercolumns = "1";
574 h_paperfontsize = "default";
575 h_paperorientation = "portrait";
576 h_paperpagestyle = "default";
578 h_papersize = "default";
579 h_paragraph_indentation = "default";
580 h_paragraph_separation = "indent";
585 h_pdf_bookmarks = "0";
586 h_pdf_bookmarksnumbered = "0";
587 h_pdf_bookmarksopen = "0";
588 h_pdf_bookmarksopenlevel = "1";
589 h_pdf_breaklinks = "0";
590 h_pdf_pdfborder = "0";
591 h_pdf_colorlinks = "0";
592 h_pdf_backref = "section";
593 h_pdf_pdfusetitle = "0";
595 //h_pdf_quoted_options;
596 h_quotes_style = "english";
598 h_shortcut[0] = "idx";
599 h_spacing = "single";
600 h_save_transient_properties = "true";
601 h_suppress_date = "false";
602 h_textclass = "article";
604 h_tracking_changes = "false";
605 h_use_bibtopic = "false";
606 h_use_dash_ligatures = "true";
607 h_use_indices = "false";
608 h_use_geometry = "false";
609 h_use_default_options = "false";
610 h_use_hyperref = "false";
611 h_use_microtype = "false";
612 h_use_lineno = "false";
613 h_use_refstyle = false;
614 h_use_minted = false;
615 h_use_packages["amsmath"] = "1";
616 h_use_packages["amssymb"] = "0";
617 h_use_packages["cancel"] = "0";
618 h_use_packages["esint"] = "1";
619 h_use_packages["mhchem"] = "0";
620 h_use_packages["mathdots"] = "0";
621 h_use_packages["mathtools"] = "0";
622 h_use_packages["stackrel"] = "0";
623 h_use_packages["stmaryrd"] = "0";
624 h_use_packages["undertilde"] = "0";
628 void Preamble::handle_hyperref(vector<string> & options)
630 // FIXME swallow inputencoding changes that might surround the
631 // hyperref setup if it was written by LyX
632 h_use_hyperref = "true";
633 // swallow "unicode=true", since LyX does always write that
634 vector<string>::iterator it =
635 find(options.begin(), options.end(), "unicode=true");
636 if (it != options.end())
638 it = find(options.begin(), options.end(), "pdfusetitle");
639 if (it != options.end()) {
640 h_pdf_pdfusetitle = "1";
643 string bookmarks = process_keyval_opt(options, "bookmarks");
644 if (bookmarks == "true")
645 h_pdf_bookmarks = "1";
646 else if (bookmarks == "false")
647 h_pdf_bookmarks = "0";
648 if (h_pdf_bookmarks == "1") {
649 string bookmarksnumbered =
650 process_keyval_opt(options, "bookmarksnumbered");
651 if (bookmarksnumbered == "true")
652 h_pdf_bookmarksnumbered = "1";
653 else if (bookmarksnumbered == "false")
654 h_pdf_bookmarksnumbered = "0";
655 string bookmarksopen =
656 process_keyval_opt(options, "bookmarksopen");
657 if (bookmarksopen == "true")
658 h_pdf_bookmarksopen = "1";
659 else if (bookmarksopen == "false")
660 h_pdf_bookmarksopen = "0";
661 if (h_pdf_bookmarksopen == "1") {
662 string bookmarksopenlevel =
663 process_keyval_opt(options, "bookmarksopenlevel");
664 if (!bookmarksopenlevel.empty())
665 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
668 string breaklinks = process_keyval_opt(options, "breaklinks");
669 if (breaklinks == "true")
670 h_pdf_breaklinks = "1";
671 else if (breaklinks == "false")
672 h_pdf_breaklinks = "0";
673 string pdfborder = process_keyval_opt(options, "pdfborder");
674 if (pdfborder == "{0 0 0}")
675 h_pdf_pdfborder = "1";
676 else if (pdfborder == "{0 0 1}")
677 h_pdf_pdfborder = "0";
678 string backref = process_keyval_opt(options, "backref");
679 if (!backref.empty())
680 h_pdf_backref = backref;
681 string colorlinks = process_keyval_opt(options, "colorlinks");
682 if (colorlinks == "true")
683 h_pdf_colorlinks = "1";
684 else if (colorlinks == "false")
685 h_pdf_colorlinks = "0";
686 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
687 if (!pdfpagemode.empty())
688 h_pdf_pagemode = pdfpagemode;
689 string pdftitle = process_keyval_opt(options, "pdftitle");
690 if (!pdftitle.empty()) {
691 h_pdf_title = remove_braces(pdftitle);
693 string pdfauthor = process_keyval_opt(options, "pdfauthor");
694 if (!pdfauthor.empty()) {
695 h_pdf_author = remove_braces(pdfauthor);
697 string pdfsubject = process_keyval_opt(options, "pdfsubject");
698 if (!pdfsubject.empty())
699 h_pdf_subject = remove_braces(pdfsubject);
700 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
701 if (!pdfkeywords.empty())
702 h_pdf_keywords = remove_braces(pdfkeywords);
703 if (!options.empty()) {
704 if (!h_pdf_quoted_options.empty())
705 h_pdf_quoted_options += ',';
706 h_pdf_quoted_options += join(options, ",");
712 void Preamble::handle_geometry(vector<string> & options)
714 h_use_geometry = "true";
715 vector<string>::iterator it;
717 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
718 h_paperorientation = "landscape";
722 // keyval version: "paper=letter" or "paper=letterpaper"
723 string paper = process_keyval_opt(options, "paper");
725 if (suffixIs(paper, "paper"))
726 paper = subst(paper, "paper", "");
727 // alternative version: "letterpaper"
728 handle_opt(options, known_latex_paper_sizes, paper);
729 if (suffixIs(paper, "paper"))
730 paper = subst(paper, "paper", "");
731 delete_opt(options, known_latex_paper_sizes);
735 char const * const * margin = known_paper_margins;
736 for (; *margin; ++margin) {
737 string value = process_keyval_opt(options, *margin);
738 if (!value.empty()) {
739 int k = margin - known_paper_margins;
740 string name = known_coded_paper_margins[k];
741 h_margins += '\\' + name + ' ' + value + '\n';
747 void Preamble::handle_package(Parser &p, string const & name,
748 string const & opts, bool in_lyx_preamble,
751 vector<string> options = split_options(opts);
752 add_package(name, options);
754 if (is_known(name, known_xetex_packages)) {
756 h_use_non_tex_fonts = true;
757 registerAutomaticallyLoadedPackage("fontspec");
758 if (h_inputencoding == "auto-legacy")
759 p.setEncoding("UTF-8");
762 // vector of all options for easier parsing and
764 vector<string> allopts = getVectorFromString(opts);
765 // this stores the potential extra options
772 // By default, we use the package name as LyX font name,
773 // so this only needs to be reset if these names differ
774 if (is_known(name, known_roman_font_packages))
775 h_font_roman[0] = name;
777 if (name == "ccfonts") {
778 for (auto const & opt : allopts) {
784 h_font_roman_opts = xopts;
788 if (name == "lmodern") {
789 for (auto const & opt : allopts) {
795 h_font_roman_opts = xopts;
799 if (name == "fourier") {
800 h_font_roman[0] = "utopia";
801 for (auto const & opt : allopts) {
803 h_font_roman_osf = "true";
806 if (opt == "expert") {
815 h_font_roman_opts = xopts;
819 if (name == "garamondx") {
820 for (auto const & opt : allopts) {
822 h_font_roman_osf = "true";
830 h_font_roman_opts = xopts;
834 if (name == "libertine") {
835 // this automatically invokes biolinum
836 h_font_sans[0] = "biolinum";
837 // as well as libertineMono
838 h_font_typewriter[0] = "libertine-mono";
839 for (auto const & opt : allopts) {
841 h_font_roman_osf = "true";
844 if (opt == "lining") {
845 h_font_roman_osf = "false";
853 h_font_roman_opts = xopts;
857 if (name == "libertineRoman" || name == "libertine-type1") {
858 h_font_roman[0] = "libertine";
859 // NOTE: contrary to libertine.sty, libertineRoman
860 // and libertine-type1 do not automatically invoke
861 // biolinum and libertineMono
862 if (opts == "lining")
863 h_font_roman_osf = "false";
864 else if (opts == "osf")
865 h_font_roman_osf = "true";
868 if (name == "libertinus" || name == "libertinus-type1") {
875 for (auto const & opt : allopts) {
876 if (opt == "rm" || opt == "serif") {
881 if (opt == "sf" || opt == "sans") {
886 if (opt == "tt=false" || opt == "mono=false") {
894 if (opt == "scaleSF") {
898 if (opt == "scaleTT") {
902 if (opt == "lining") {
903 h_font_roman_osf = "false";
911 h_font_roman[0] = "libertinus";
913 h_font_roman_osf = "true";
915 h_font_roman_osf = "false";
918 h_font_sans[0] = "LibertinusSans-LF";
920 h_font_sans_osf = "true";
922 h_font_sans_osf = "false";
923 if (!scalesf.empty())
924 scale_as_percentage(scalesf, h_font_sf_scale[0]);
927 h_font_typewriter[0] = "LibertinusMono-TLF";
928 if (!scalett.empty())
929 scale_as_percentage(scalett, h_font_tt_scale[0]);
932 h_font_roman_opts = xopts;
936 if (name == "MinionPro") {
937 h_font_roman[0] = "minionpro";
938 h_font_roman_osf = "true";
939 h_font_math[0] = "auto";
940 for (auto const & opt : allopts) {
942 h_font_roman_osf = "false";
945 if (opt == "onlytext") {
946 h_font_math[0] = "default";
954 h_font_roman_opts = xopts;
958 if (name == "mathdesign") {
959 for (auto const & opt : allopts) {
960 if (opt == "charter") {
961 h_font_roman[0] = "md-charter";
964 if (opt == "garamond") {
965 h_font_roman[0] = "md-garamond";
968 if (opt == "utopia") {
969 h_font_roman[0] = "md-utopia";
972 if (opt == "expert") {
974 h_font_roman_osf = "true";
980 else if (name == "mathpazo") {
981 h_font_roman[0] = "palatino";
982 for (auto const & opt : allopts) {
984 h_font_roman_osf = "true";
996 h_font_roman_opts = xopts;
1000 else if (name == "mathptmx") {
1001 h_font_roman[0] = "times";
1002 for (auto const & opt : allopts) {
1008 h_font_roman_opts = xopts;
1012 if (name == "crimson")
1013 h_font_roman[0] = "cochineal";
1015 if (name == "cochineal") {
1016 for (auto const & opt : allopts) {
1017 if (opt == "osf" || opt == "oldstyle") {
1018 h_font_roman_osf = "true";
1021 if (opt == "proportional" || opt == "p")
1028 h_font_roman_opts = xopts;
1032 if (name == "CrimsonPro") {
1033 h_font_roman_osf = "true";
1034 for (auto const & opt : allopts) {
1035 if (opt == "lf" || opt == "lining") {
1036 h_font_roman_osf = "false";
1039 if (opt == "proportional" || opt == "p")
1041 if (opt == "medium") {
1042 h_font_roman[0] = "CrimsonProMedium";
1045 if (opt == "extralight") {
1046 h_font_roman[0] = "CrimsonProExtraLight";
1049 if (opt == "light") {
1050 h_font_roman[0] = "CrimsonProLight";
1058 h_font_roman_opts = xopts;
1064 // font uses old-style figure
1065 h_font_roman_osf = "true";
1067 if (name == "paratype") {
1068 // in this case all fonts are ParaType
1069 h_font_roman[0] = "PTSerif-TLF";
1070 h_font_sans[0] = "default";
1071 h_font_typewriter[0] = "default";
1074 if (name == "PTSerif")
1075 h_font_roman[0] = "PTSerif-TLF";
1077 if (name == "XCharter") {
1078 h_font_roman[0] = "xcharter";
1079 for (auto const & opt : allopts) {
1081 h_font_roman_osf = "true";
1089 h_font_roman_opts = xopts;
1093 if (name == "plex-serif") {
1094 h_font_roman[0] = "IBMPlexSerif";
1095 for (auto const & opt : allopts) {
1096 if (opt == "thin") {
1097 h_font_roman[0] = "IBMPlexSerifThin";
1100 if (opt == "extralight") {
1101 h_font_roman[0] = "IBMPlexSerifExtraLight";
1104 if (opt == "light") {
1105 h_font_roman[0] = "IBMPlexSerifLight";
1113 h_font_roman_opts = xopts;
1117 if (name == "noto-serif" || name == "noto") {
1124 bool extralight = false;
1126 bool medium = false;
1129 if (name == "noto") {
1134 // Since the options might apply to different shapes,
1135 // we need to parse all options first and then handle them.
1136 for (auto const & opt : allopts) {
1137 if (opt == "regular")
1147 if (opt == "thin") {
1151 if (opt == "extralight") {
1155 if (opt == "light") {
1159 if (opt == "medium") {
1170 if (opt == "nott") {
1178 if (prefixIs(opt, "scaled=")) {
1187 // handle options that might affect different shapes
1188 if (name == "noto-serif" || rm) {
1190 h_font_roman[0] = "NotoSerifThin";
1191 else if (extralight)
1192 h_font_roman[0] = "NotoSerifExtralight";
1194 h_font_roman[0] = "NotoSerifLight";
1196 h_font_roman[0] = "NotoSerifMedium";
1198 h_font_roman[0] = "NotoSerifRegular";
1200 h_font_roman_osf = "true";
1202 h_font_roman_opts = xopts;
1204 if (name == "noto" && sf) {
1206 h_font_sans[0] = "NotoSansThin";
1207 else if (extralight)
1208 h_font_sans[0] = "NotoSansExtralight";
1210 h_font_sans[0] = "NotoSansLight";
1212 h_font_sans[0] = "NotoSansMedium";
1214 h_font_sans[0] = "NotoSansRegular";
1216 h_font_sans_osf = "true";
1218 scale_as_percentage(scl, h_font_sf_scale[0]);
1220 h_font_sans_opts = xopts;
1222 if (name == "noto" && tt) {
1223 h_font_typewriter[0] = "NotoMonoRegular";
1225 h_font_typewriter_osf = "true";
1227 scale_as_percentage(scl, h_font_tt_scale[0]);
1229 h_font_typewriter_opts = xopts;
1233 if (name == "sourceserifpro") {
1234 h_font_roman[0] = "ADOBESourceSerifPro";
1235 for (auto const & opt : allopts) {
1237 h_font_roman_osf = "true";
1245 h_font_roman_opts = xopts;
1253 // By default, we use the package name as LyX font name,
1254 // so this only needs to be reset if these names differ.
1255 // Also, we handle the scaling option here generally.
1256 if (is_known(name, known_sans_font_packages)) {
1257 h_font_sans[0] = name;
1258 if (contains(opts, "scale")) {
1259 vector<string>::iterator it = allopts.begin();
1260 for (; it != allopts.end() ; ++it) {
1261 string const opt = *it;
1262 if (prefixIs(opt, "scaled=") || prefixIs(opt, "scale=")) {
1263 if (scale_as_percentage(opt, h_font_sf_scale[0])) {
1272 if (name == "biolinum" || name == "biolinum-type1") {
1273 h_font_sans[0] = "biolinum";
1274 for (auto const & opt : allopts) {
1275 if (prefixIs(opt, "osf")) {
1276 h_font_sans_osf = "true";
1284 h_font_sans_opts = xopts;
1288 if (name == "cantarell") {
1289 for (auto const & opt : allopts) {
1290 if (opt == "defaultsans")
1292 if (prefixIs(opt, "oldstyle")) {
1293 h_font_sans_osf = "true";
1301 h_font_sans_opts = xopts;
1305 if (name == "Chivo") {
1306 for (auto const & opt : allopts) {
1307 if (opt == "thin") {
1308 h_font_roman[0] = "ChivoThin";
1311 if (opt == "light") {
1312 h_font_roman[0] = "ChivoLight";
1315 if (opt == "regular") {
1316 h_font_roman[0] = "Chivo";
1319 if (opt == "medium") {
1320 h_font_roman[0] = "ChivoMedium";
1323 if (prefixIs(opt, "oldstyle")) {
1324 h_font_sans_osf = "true";
1332 h_font_sans_opts = xopts;
1336 if (name == "PTSans") {
1337 h_font_sans[0] = "PTSans-TLF";
1340 if (name == "FiraSans") {
1341 h_font_sans_osf = "true";
1342 for (auto const & opt : allopts) {
1343 if (opt == "book") {
1344 h_font_sans[0] = "FiraSansBook";
1347 if (opt == "thin") {
1350 if (opt == "extralight") {
1351 h_font_sans[0] = "FiraSansExtralight";
1354 if (opt == "light") {
1355 h_font_sans[0] = "FiraSansLight";
1358 if (opt == "ultralight") {
1359 h_font_sans[0] = "FiraSansUltralight";
1362 if (opt == "thin") {
1363 h_font_sans[0] = "FiraSansThin";
1366 if (opt == "lf" || opt == "lining") {
1367 h_font_sans_osf = "false";
1375 h_font_sans_opts = xopts;
1379 if (name == "plex-sans") {
1380 h_font_sans[0] = "IBMPlexSans";
1381 for (auto const & opt : allopts) {
1382 if (opt == "condensed") {
1383 h_font_sans[0] = "IBMPlexSansCondensed";
1386 if (opt == "thin") {
1387 h_font_sans[0] = "IBMPlexSansThin";
1390 if (opt == "extralight") {
1391 h_font_sans[0] = "IBMPlexSansExtraLight";
1394 if (opt == "light") {
1395 h_font_sans[0] = "IBMPlexSansLight";
1403 h_font_sans_opts = xopts;
1407 if (name == "noto-sans") {
1408 h_font_sans[0] = "NotoSansRegular";
1409 for (auto const & opt : allopts) {
1410 if (opt == "regular")
1412 if (opt == "medium") {
1413 h_font_sans[0] = "NotoSansMedium";
1416 if (opt == "thin") {
1417 h_font_sans[0] = "NotoSansThin";
1420 if (opt == "extralight") {
1421 h_font_sans[0] = "NotoSansExtralight";
1424 if (opt == "light") {
1425 h_font_sans[0] = "NotoSansLight";
1429 h_font_sans_osf = "true";
1437 h_font_sans_opts = xopts;
1441 if (name == "sourcesanspro") {
1442 h_font_sans[0] = "ADOBESourceSansPro";
1443 for (auto const & opt : allopts) {
1445 h_font_sans_osf = "true";
1453 h_font_sans_opts = xopts;
1461 // By default, we use the package name as LyX font name,
1462 // so this only needs to be reset if these names differ.
1463 // Also, we handle the scaling option here generally.
1464 if (is_known(name, known_typewriter_font_packages)) {
1465 h_font_typewriter[0] = name;
1466 if (contains(opts, "scale")) {
1467 vector<string>::iterator it = allopts.begin();
1468 for (; it != allopts.end() ; ++it) {
1469 string const opt = *it;
1470 if (prefixIs(opt, "scaled=") || prefixIs(opt, "scale=")) {
1471 if (scale_as_percentage(opt, h_font_tt_scale[0])) {
1480 if (name == "libertineMono" || name == "libertineMono-type1")
1481 h_font_typewriter[0] = "libertine-mono";
1483 if (name == "FiraMono") {
1484 h_font_typewriter_osf = "true";
1485 for (auto const & opt : allopts) {
1486 if (opt == "lf" || opt == "lining") {
1487 h_font_typewriter_osf = "false";
1495 h_font_typewriter_opts = xopts;
1499 if (name == "PTMono")
1500 h_font_typewriter[0] = "PTMono-TLF";
1502 if (name == "plex-mono") {
1503 h_font_typewriter[0] = "IBMPlexMono";
1504 for (auto const & opt : allopts) {
1505 if (opt == "thin") {
1506 h_font_typewriter[0] = "IBMPlexMonoThin";
1509 if (opt == "extralight") {
1510 h_font_typewriter[0] = "IBMPlexMonoExtraLight";
1513 if (opt == "light") {
1514 h_font_typewriter[0] = "IBMPlexMonoLight";
1522 h_font_typewriter_opts = xopts;
1526 if (name == "noto-mono") {
1527 h_font_typewriter[0] = "NotoMonoRegular";
1528 for (auto const & opt : allopts) {
1529 if (opt == "regular")
1536 h_font_typewriter_opts = xopts;
1540 if (name == "sourcecodepro") {
1541 h_font_typewriter[0] = "ADOBESourceCodePro";
1542 for (auto const & opt : allopts) {
1544 h_font_typewriter_osf = "true";
1552 h_font_typewriter_opts = xopts;
1560 // By default, we use the package name as LyX font name,
1561 // so this only needs to be reset if these names differ.
1562 if (is_known(name, known_math_font_packages))
1563 h_font_math[0] = name;
1565 if (name == "newtxmath") {
1567 h_font_math[0] = "newtxmath";
1568 else if (opts == "garamondx")
1569 h_font_math[0] = "garamondx-ntxm";
1570 else if (opts == "libertine")
1571 h_font_math[0] = "libertine-ntxm";
1572 else if (opts == "minion")
1573 h_font_math[0] = "minion-ntxm";
1574 else if (opts == "cochineal")
1575 h_font_math[0] = "cochineal-ntxm";
1578 if (name == "iwona")
1580 h_font_math[0] = "iwona-math";
1582 if (name == "kurier")
1584 h_font_math[0] = "kurier-math";
1586 // after the detection and handling of special cases, we can remove the
1587 // fonts, otherwise they would appear in the preamble, see bug #7856
1588 if (is_known(name, known_roman_font_packages) || is_known(name, known_sans_font_packages)
1589 || is_known(name, known_typewriter_font_packages) || is_known(name, known_math_font_packages))
1591 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
1592 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
1593 name == "esint" || name == "mhchem" || name == "mathdots" ||
1594 name == "mathtools" || name == "stackrel" ||
1595 name == "stmaryrd" || name == "undertilde") {
1596 h_use_packages[name] = "2";
1597 registerAutomaticallyLoadedPackage(name);
1600 else if (name == "babel") {
1601 h_language_package = "default";
1602 // One might think we would have to do nothing if babel is loaded
1603 // without any options to prevent pollution of the preamble with this
1604 // babel call in every roundtrip.
1605 // But the user could have defined babel-specific things afterwards. So
1606 // we need to keep it in the preamble to prevent cases like bug #7861.
1607 if (!opts.empty()) {
1608 // check if more than one option was used - used later for inputenc
1609 if (options.begin() != options.end() - 1)
1610 one_language = false;
1611 // babel takes the last language of the option of its \usepackage
1612 // call as document language. If there is no such language option, the
1613 // last language in the documentclass options is used.
1614 handle_opt(options, known_languages, h_language);
1615 // translate the babel name to a LyX name
1616 h_language = babel2lyx(h_language);
1617 if (h_language == "japanese") {
1618 // For Japanese, the encoding isn't indicated in the source
1619 // file, and there's really not much we can do. We could
1620 // 1) offer a list of possible encodings to choose from, or
1621 // 2) determine the encoding of the file by inspecting it.
1622 // For the time being, we leave the encoding alone so that
1623 // we don't get iconv errors when making a wrong guess, and
1624 // we will output a note at the top of the document
1625 // explaining what to do.
1626 Encoding const * const enc = encodings.fromIconvName(
1627 p.getEncoding(), Encoding::japanese, false);
1629 h_inputencoding = enc->name();
1630 is_nonCJKJapanese = true;
1631 // in this case babel can be removed from the preamble
1632 registerAutomaticallyLoadedPackage("babel");
1634 // If babel is called with options, LyX puts them by default into the
1635 // document class options. This works for most languages, except
1636 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
1637 // perhaps in future others.
1638 // Therefore keep the babel call as it is as the user might have
1640 string const babelcall = "\\usepackage[" + opts + "]{babel}\n";
1641 if (!contains(h_preamble.str(), babelcall))
1642 h_preamble << babelcall;
1644 delete_opt(options, known_languages);
1646 if (!contains(h_preamble.str(), "\\usepackage{babel}\n"))
1647 h_preamble << "\\usepackage{babel}\n";
1648 explicit_babel = true;
1652 else if (name == "polyglossia") {
1653 h_language_package = "default";
1654 h_default_output_format = "pdf4";
1655 h_use_non_tex_fonts = true;
1657 registerAutomaticallyLoadedPackage("xunicode");
1658 if (h_inputencoding == "auto-legacy")
1659 p.setEncoding("UTF-8");
1662 else if (name == "CJK") {
1663 // set the encoding to "auto-legacy" because it might be set to "auto-legacy-plain" by the babel handling
1664 // and this would not be correct for CJK
1665 if (h_inputencoding == "auto-legacy-plain")
1666 h_inputencoding = "auto-legacy";
1667 registerAutomaticallyLoadedPackage("CJK");
1670 else if (name == "CJKutf8") {
1671 h_inputencoding = "utf8-cjk";
1672 p.setEncoding("UTF-8");
1673 registerAutomaticallyLoadedPackage("CJKutf8");
1676 else if (name == "fontenc") {
1677 h_fontencoding = getStringFromVector(options, ",");
1681 else if (name == "inputenc" || name == "luainputenc") {
1682 // h_inputencoding is only set when there is not more than one
1683 // inputenc option because otherwise h_inputencoding must be
1684 // set to "auto-legacy" (the default encodings of the document's languages)
1685 // Therefore check that exactly one option is passed to inputenc.
1686 // It is also only set when there is not more than one babel
1688 if (!options.empty()) {
1689 string const encoding = options.back();
1690 Encoding const * const enc = encodings.fromLaTeXName(
1691 encoding, Encoding::inputenc, true);
1693 if (!detectEncoding)
1694 cerr << "Unknown encoding " << encoding
1695 << ". Ignoring." << std::endl;
1697 if (!enc->unsafe() && options.size() == 1 && one_language == true)
1698 h_inputencoding = enc->name();
1699 p.setEncoding(enc->iconvName());
1705 else if (name == "srcltx") {
1706 h_output_sync = "1";
1707 if (!opts.empty()) {
1708 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
1711 h_output_sync_macro = "\\usepackage{srcltx}";
1714 else if (is_known(name, known_old_language_packages)) {
1715 // known language packages from the times before babel
1716 // if they are found and not also babel, they will be used as
1717 // custom language package
1718 h_language_package = "\\usepackage{" + name + "}";
1721 else if (name == "lyxskak") {
1722 // ignore this and its options
1723 const char * const o[] = {"ps", "mover", 0};
1724 delete_opt(options, o);
1727 else if (name == "parskip" && options.size() < 2 && (opts.empty() || prefixIs(opts, "skip="))) {
1729 h_paragraph_separation = "halfline";
1731 if (opts == "skip=\\smallskipamount")
1732 h_defskip = "smallskip";
1733 else if (opts == "skip=\\medskipamount")
1734 h_defskip = "medskip";
1735 else if (opts == "skip=\\bigskipamount")
1736 h_defskip = "bigskip";
1737 else if (opts == "skip=\\baselineskip")
1738 h_defskip = "fullline";
1741 h_paragraph_separation = "skip";
1745 else if (is_known(name, known_lyx_packages) && options.empty()) {
1746 if (name == "splitidx")
1747 h_use_indices = "true";
1748 else if (name == "minted")
1749 h_use_minted = true;
1750 else if (name == "refstyle")
1751 h_use_refstyle = true;
1752 else if (name == "prettyref")
1753 h_use_refstyle = false;
1755 if (!in_lyx_preamble) {
1756 h_preamble << package_beg_sep << name
1757 << package_mid_sep << "\\usepackage{"
1759 if (p.next_token().cat() == catNewline ||
1760 (p.next_token().cat() == catSpace &&
1761 p.next_next_token().cat() == catNewline))
1763 h_preamble << package_end_sep;
1767 else if (name == "geometry")
1768 handle_geometry(options);
1770 else if (name == "subfig")
1771 ; // ignore this FIXME: Use the package separator mechanism instead
1773 else if (char const * const * where = is_known(name, known_languages))
1774 h_language = known_coded_languages[where - known_languages];
1776 else if (name == "natbib") {
1777 h_biblio_style = "plainnat";
1778 h_cite_engine = "natbib";
1779 h_cite_engine_type = "authoryear";
1780 vector<string>::iterator it =
1781 find(options.begin(), options.end(), "authoryear");
1782 if (it != options.end())
1785 it = find(options.begin(), options.end(), "numbers");
1786 if (it != options.end()) {
1787 h_cite_engine_type = "numerical";
1791 if (!options.empty())
1792 h_biblio_options = join(options, ",");
1795 else if (name == "biblatex") {
1796 h_biblio_style = "plainnat";
1797 h_cite_engine = "biblatex";
1798 h_cite_engine_type = "authoryear";
1800 vector<string>::iterator it =
1801 find(options.begin(), options.end(), "natbib");
1802 if (it != options.end()) {
1804 h_cite_engine = "biblatex-natbib";
1806 opt = process_keyval_opt(options, "natbib");
1808 h_cite_engine = "biblatex-natbib";
1810 opt = process_keyval_opt(options, "style");
1812 h_biblatex_citestyle = opt;
1813 h_biblatex_bibstyle = opt;
1815 opt = process_keyval_opt(options, "citestyle");
1817 h_biblatex_citestyle = opt;
1818 opt = process_keyval_opt(options, "bibstyle");
1820 h_biblatex_bibstyle = opt;
1822 opt = process_keyval_opt(options, "refsection");
1824 if (opt == "none" || opt == "part"
1825 || opt == "chapter" || opt == "section"
1826 || opt == "subsection")
1829 cerr << "Ignoring unknown refsection value '"
1832 opt = process_keyval_opt(options, "bibencoding");
1835 if (!options.empty()) {
1836 h_biblio_options = join(options, ",");
1841 else if (name == "jurabib") {
1842 h_biblio_style = "jurabib";
1843 h_cite_engine = "jurabib";
1844 h_cite_engine_type = "authoryear";
1845 if (!options.empty())
1846 h_biblio_options = join(options, ",");
1849 else if (name == "bibtopic")
1850 h_use_bibtopic = "true";
1852 else if (name == "chapterbib")
1853 h_multibib = "child";
1855 else if (name == "hyperref")
1856 handle_hyperref(options);
1858 else if (name == "algorithm2e") {
1859 // Load "algorithm2e" module
1860 addModule("algorithm2e");
1861 // Add the package options to the global document options
1862 if (!options.empty()) {
1863 if (h_options.empty())
1864 h_options = join(options, ",");
1866 h_options += ',' + join(options, ",");
1869 else if (name == "microtype") {
1870 //we internally support only microtype without params
1871 if (options.empty())
1872 h_use_microtype = "true";
1874 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1877 else if (name == "lineno") {
1878 h_use_lineno = "true";
1879 if (!options.empty()) {
1880 h_lineno_options = join(options, ",");
1885 else if (name == "changebar")
1886 h_output_changes = "true";
1888 else if (!in_lyx_preamble) {
1889 if (options.empty())
1890 h_preamble << "\\usepackage{" << name << '}';
1892 h_preamble << "\\usepackage[" << opts << "]{"
1896 if (p.next_token().cat() == catNewline ||
1897 (p.next_token().cat() == catSpace &&
1898 p.next_next_token().cat() == catNewline))
1902 // We need to do something with the options...
1903 if (!options.empty() && !detectEncoding)
1904 cerr << "Ignoring options '" << join(options, ",")
1905 << "' of package " << name << '.' << endl;
1907 // remove the whitespace
1912 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1915 Token t = p.get_token();
1916 if (t.cat() == catEscape &&
1917 is_known(t.cs(), known_if_commands))
1918 handle_if(p, in_lyx_preamble);
1920 if (!in_lyx_preamble)
1921 h_preamble << t.asInput();
1922 if (t.cat() == catEscape && t.cs() == "fi")
1929 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1931 if (contains(h_float_placement, "H"))
1932 registerAutomaticallyLoadedPackage("float");
1933 if (h_spacing != "single" && h_spacing != "default")
1934 registerAutomaticallyLoadedPackage("setspace");
1935 if (h_use_packages["amsmath"] == "2") {
1936 // amsbsy and amstext are already provided by amsmath
1937 registerAutomaticallyLoadedPackage("amsbsy");
1938 registerAutomaticallyLoadedPackage("amstext");
1941 // output the LyX file settings
1942 // Important: Keep the version formatting in sync with LyX and
1943 // lyx2lyx (bug 7951)
1944 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1945 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1946 << lyx_version_minor << '\n'
1947 << "\\lyxformat " << LYX_FORMAT << '\n'
1948 << "\\begin_document\n"
1949 << "\\begin_header\n"
1950 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1951 << "\\origin " << origin << "\n"
1952 << "\\textclass " << h_textclass << "\n";
1953 if (!h_doc_metadata.empty()) {
1954 os << "\\begin_metadata\n"
1956 << "\n\\end_metadata\n";
1958 string const raw = subdoc ? empty_string() : h_preamble.str();
1960 os << "\\begin_preamble\n";
1961 for (string::size_type i = 0; i < raw.size(); ++i) {
1962 if (raw[i] == package_beg_sep) {
1963 // Here follows some package loading code that
1964 // must be skipped if the package is loaded
1966 string::size_type j = raw.find(package_mid_sep, i);
1967 if (j == string::npos)
1969 string::size_type k = raw.find(package_end_sep, j);
1970 if (k == string::npos)
1972 string const package = raw.substr(i + 1, j - i - 1);
1973 string const replacement = raw.substr(j + 1, k - j - 1);
1974 if (auto_packages.find(package) == auto_packages.end())
1980 os << "\n\\end_preamble\n";
1982 if (!h_options.empty())
1983 os << "\\options " << h_options << "\n";
1984 os << "\\use_default_options " << h_use_default_options << "\n";
1985 if (!used_modules.empty()) {
1986 os << "\\begin_modules\n";
1987 vector<string>::const_iterator const end = used_modules.end();
1988 vector<string>::const_iterator it = used_modules.begin();
1989 for (; it != end; ++it)
1991 os << "\\end_modules\n";
1993 if (!h_includeonlys.empty()) {
1994 os << "\\begin_includeonly\n";
1995 for (auto const & iofile : h_includeonlys)
1996 os << iofile << '\n';
1997 os << "\\end_includeonly\n";
1999 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
2000 << "\\language " << h_language << "\n"
2001 << "\\language_package " << h_language_package << "\n"
2002 << "\\inputencoding " << h_inputencoding << "\n"
2003 << "\\fontencoding " << h_fontencoding << "\n"
2004 << "\\font_roman \"" << h_font_roman[0]
2005 << "\" \"" << h_font_roman[1] << "\"\n"
2006 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
2007 << "\\font_typewriter \"" << h_font_typewriter[0]
2008 << "\" \"" << h_font_typewriter[1] << "\"\n"
2009 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
2010 << "\\font_default_family " << h_font_default_family << "\n"
2011 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
2012 << "\\font_sc " << h_font_sc << "\n"
2013 << "\\font_roman_osf " << h_font_roman_osf << "\n"
2014 << "\\font_sans_osf " << h_font_sans_osf << "\n"
2015 << "\\font_typewriter_osf " << h_font_typewriter_osf << "\n";
2016 if (!h_font_roman_opts.empty())
2017 os << "\\font_roman_opts \"" << h_font_roman_opts << "\"" << '\n';
2018 os << "\\font_sf_scale " << h_font_sf_scale[0]
2019 << ' ' << h_font_sf_scale[1] << '\n';
2020 if (!h_font_sans_opts.empty())
2021 os << "\\font_sans_opts \"" << h_font_sans_opts << "\"" << '\n';
2022 os << "\\font_tt_scale " << h_font_tt_scale[0]
2023 << ' ' << h_font_tt_scale[1] << '\n';
2024 if (!h_font_cjk.empty())
2025 os << "\\font_cjk " << h_font_cjk << '\n';
2026 if (!h_font_typewriter_opts.empty())
2027 os << "\\font_typewriter_opts \"" << h_font_typewriter_opts << "\"" << '\n';
2028 os << "\\use_microtype " << h_use_microtype << '\n'
2029 << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
2030 << "\\graphics " << h_graphics << '\n'
2031 << "\\default_output_format " << h_default_output_format << "\n"
2032 << "\\output_sync " << h_output_sync << "\n";
2033 if (h_output_sync == "1")
2034 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
2035 os << "\\bibtex_command " << h_bibtex_command << "\n"
2036 << "\\index_command " << h_index_command << "\n";
2037 if (!h_float_placement.empty())
2038 os << "\\float_placement " << h_float_placement << "\n";
2039 os << "\\paperfontsize " << h_paperfontsize << "\n"
2040 << "\\spacing " << h_spacing << "\n"
2041 << "\\use_hyperref " << h_use_hyperref << '\n';
2042 if (h_use_hyperref == "true") {
2043 if (!h_pdf_title.empty())
2044 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
2045 if (!h_pdf_author.empty())
2046 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
2047 if (!h_pdf_subject.empty())
2048 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
2049 if (!h_pdf_keywords.empty())
2050 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
2051 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
2052 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
2053 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
2054 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
2055 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
2056 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
2057 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
2058 "\\pdf_backref " << h_pdf_backref << "\n"
2059 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
2060 if (!h_pdf_pagemode.empty())
2061 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
2062 if (!h_pdf_quoted_options.empty())
2063 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
2065 os << "\\papersize " << h_papersize << "\n"
2066 << "\\use_geometry " << h_use_geometry << '\n';
2067 for (map<string, string>::const_iterator it = h_use_packages.begin();
2068 it != h_use_packages.end(); ++it)
2069 os << "\\use_package " << it->first << ' ' << it->second << '\n';
2070 os << "\\cite_engine " << h_cite_engine << '\n'
2071 << "\\cite_engine_type " << h_cite_engine_type << '\n'
2072 << "\\biblio_style " << h_biblio_style << "\n"
2073 << "\\use_bibtopic " << h_use_bibtopic << "\n";
2074 if (!h_biblio_options.empty())
2075 os << "\\biblio_options " << h_biblio_options << "\n";
2076 if (!h_biblatex_bibstyle.empty())
2077 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
2078 if (!h_biblatex_citestyle.empty())
2079 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
2080 if (!h_multibib.empty())
2081 os << "\\multibib " << h_multibib << "\n";
2082 os << "\\use_indices " << h_use_indices << "\n"
2083 << "\\paperorientation " << h_paperorientation << '\n'
2084 << "\\suppress_date " << h_suppress_date << '\n'
2085 << "\\justification " << h_justification << '\n'
2086 << "\\use_refstyle " << h_use_refstyle << '\n'
2087 << "\\use_minted " << h_use_minted << '\n'
2088 << "\\use_lineno " << h_use_lineno << '\n';
2089 if (!h_lineno_options.empty())
2090 os << "\\lineno_options " << h_lineno_options << '\n';
2091 if (!h_fontcolor.empty())
2092 os << "\\fontcolor " << h_fontcolor << '\n';
2093 if (!h_notefontcolor.empty())
2094 os << "\\notefontcolor " << h_notefontcolor << '\n';
2095 if (!h_backgroundcolor.empty())
2096 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
2097 if (!h_boxbgcolor.empty())
2098 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
2099 if (index_number != 0)
2100 for (int i = 0; i < index_number; i++) {
2101 os << "\\index " << h_index[i] << '\n'
2102 << "\\shortcut " << h_shortcut[i] << '\n'
2103 << "\\color " << h_color << '\n'
2107 os << "\\index " << h_index[0] << '\n'
2108 << "\\shortcut " << h_shortcut[0] << '\n'
2109 << "\\color " << h_color << '\n'
2113 << "\\secnumdepth " << h_secnumdepth << "\n"
2114 << "\\tocdepth " << h_tocdepth << "\n"
2115 << "\\paragraph_separation " << h_paragraph_separation << "\n";
2116 if (h_paragraph_separation == "skip")
2117 os << "\\defskip " << h_defskip << "\n";
2119 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
2120 os << "\\is_math_indent " << h_is_mathindent << "\n";
2121 if (!h_mathindentation.empty())
2122 os << "\\math_indentation " << h_mathindentation << "\n";
2123 os << "\\math_numbering_side " << h_math_numbering_side << "\n";
2124 os << "\\quotes_style " << h_quotes_style << "\n"
2125 << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
2126 << "\\papercolumns " << h_papercolumns << "\n"
2127 << "\\papersides " << h_papersides << "\n"
2128 << "\\paperpagestyle " << h_paperpagestyle << "\n";
2129 if (!h_listings_params.empty())
2130 os << "\\listings_params " << h_listings_params << "\n";
2131 os << "\\tracking_changes " << h_tracking_changes << "\n"
2132 << "\\output_changes " << h_output_changes << "\n"
2133 << "\\change_bars " << h_change_bars << "\n"
2134 << "\\html_math_output " << h_html_math_output << "\n"
2135 << "\\html_css_as_file " << h_html_css_as_file << "\n"
2136 << "\\html_be_strict " << h_html_be_strict << "\n"
2137 << "\\docbook_table_output " << h_docbook_table_output << "\n"
2138 << "\\docbook_mathml_prefix " << h_docbook_mathml_prefix << "\n"
2140 << "\\end_header\n\n"
2141 << "\\begin_body\n";
2146 void Preamble::parse(Parser & p, string const & forceclass,
2147 TeX2LyXDocClass & tc)
2149 // initialize fixed types
2150 special_columns_['D'] = 3;
2151 parse(p, forceclass, false, tc);
2155 void Preamble::parse(Parser & p, string const & forceclass,
2156 bool detectEncoding, TeX2LyXDocClass & tc)
2158 bool is_full_document = false;
2159 bool is_lyx_file = false;
2160 bool in_lyx_preamble = false;
2161 bool class_set = false;
2163 // determine whether this is a full document or a fragment for inclusion
2165 Token const & t = p.get_token();
2167 if (t.cat() == catEscape && t.cs() == "documentclass") {
2168 is_full_document = true;
2174 if (detectEncoding && !is_full_document)
2177 while (is_full_document && p.good()) {
2178 if (detectEncoding && h_inputencoding != "auto-legacy" &&
2179 h_inputencoding != "auto-legacy-plain")
2182 // Force textclass if the user wanted it
2183 if (!forceclass.empty()) {
2184 setTextClass(forceclass, tc);
2188 Token const & t = p.get_token();
2191 if (!detectEncoding)
2192 cerr << "t: " << t << '\n';
2198 if (!in_lyx_preamble &&
2199 (t.cat() == catLetter ||
2200 t.cat() == catSuper ||
2201 t.cat() == catSub ||
2202 t.cat() == catOther ||
2203 t.cat() == catMath ||
2204 t.cat() == catActive ||
2205 t.cat() == catBegin ||
2206 t.cat() == catEnd ||
2207 t.cat() == catAlign ||
2208 t.cat() == catParameter)) {
2209 h_preamble << t.cs();
2213 if (!in_lyx_preamble &&
2214 (t.cat() == catSpace || t.cat() == catNewline)) {
2215 h_preamble << t.asInput();
2219 if (t.cat() == catComment) {
2220 static regex const islyxfile("%% LyX .* created this file");
2221 static regex const usercommands("User specified LaTeX commands");
2223 string const comment = t.asInput();
2225 // magically switch encoding default if it looks like XeLaTeX
2226 static string const magicXeLaTeX =
2227 "% This document must be compiled with XeLaTeX ";
2228 if (comment.size() > magicXeLaTeX.size()
2229 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
2230 && h_inputencoding == "auto-legacy") {
2231 if (!detectEncoding)
2232 cerr << "XeLaTeX comment found, switching to UTF8\n";
2233 h_inputencoding = "utf8";
2236 if (regex_search(comment, sub, islyxfile)) {
2238 in_lyx_preamble = true;
2239 } else if (is_lyx_file
2240 && regex_search(comment, sub, usercommands))
2241 in_lyx_preamble = false;
2242 else if (!in_lyx_preamble)
2243 h_preamble << t.asInput();
2247 if (t.cs() == "PassOptionsToPackage") {
2248 string const poptions = p.getArg('{', '}');
2249 string const package = p.verbatim_item();
2250 extra_package_options_.insert(make_pair(package, poptions));
2254 if (t.cs() == "pagestyle") {
2255 h_paperpagestyle = p.verbatim_item();
2259 if (t.cs() == "setdefaultlanguage") {
2261 // We don't yet care about non-language variant options
2262 // because LyX doesn't support this yet, see bug #8214
2264 string langopts = p.getOpt();
2265 // check if the option contains a variant, if yes, extract it
2266 string::size_type pos_var = langopts.find("variant");
2267 string::size_type i = langopts.find(',', pos_var);
2268 string::size_type k = langopts.find('=', pos_var);
2269 if (pos_var != string::npos){
2271 if (i == string::npos)
2272 variant = langopts.substr(k + 1, langopts.length() - k - 2);
2274 variant = langopts.substr(k + 1, i - k - 1);
2275 h_language = variant;
2279 h_language = p.verbatim_item();
2280 //finally translate the poyglossia name to a LyX name
2281 h_language = polyglossia2lyx(h_language);
2285 if (t.cs() == "setotherlanguage") {
2286 // We don't yet care about the option because LyX doesn't
2287 // support this yet, see bug #8214
2288 p.hasOpt() ? p.getOpt() : string();
2293 if (t.cs() == "setmainfont") {
2294 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2295 h_font_roman[1] = p.getArg('{', '}');
2296 if (!fontopts.empty()) {
2297 vector<string> opts = getVectorFromString(fontopts);
2299 for (auto const & opt : opts) {
2300 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2303 if (!fontopts.empty())
2307 h_font_roman_opts = fontopts;
2312 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
2313 // LyX currently only supports the scale option
2314 string scale, fontopts;
2316 fontopts = p.getArg('[', ']');
2317 if (!fontopts.empty()) {
2318 vector<string> opts = getVectorFromString(fontopts);
2320 for (auto const & opt : opts) {
2321 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2324 if (prefixIs(opt, "Scale=")) {
2325 scale_as_percentage(opt, scale);
2328 if (!fontopts.empty())
2334 if (t.cs() == "setsansfont") {
2336 h_font_sf_scale[1] = scale;
2337 h_font_sans[1] = p.getArg('{', '}');
2338 if (!fontopts.empty())
2339 h_font_sans_opts = fontopts;
2342 h_font_tt_scale[1] = scale;
2343 h_font_typewriter[1] = p.getArg('{', '}');
2344 if (!fontopts.empty())
2345 h_font_typewriter_opts = fontopts;
2350 if (t.cs() == "babelfont") {
2352 h_use_non_tex_fonts = true;
2353 h_language_package = "babel";
2354 if (h_inputencoding == "auto-legacy")
2355 p.setEncoding("UTF-8");
2356 // we don't care about the lang option
2357 string const lang = p.hasOpt() ? p.getArg('[', ']') : string();
2358 string const family = p.getArg('{', '}');
2359 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2360 string const fontname = p.getArg('{', '}');
2361 if (lang.empty() && family == "rm") {
2362 h_font_roman[1] = fontname;
2363 if (!fontopts.empty()) {
2364 vector<string> opts = getVectorFromString(fontopts);
2366 for (auto const & opt : opts) {
2367 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2370 if (!fontopts.empty())
2374 h_font_roman_opts = fontopts;
2377 } else if (lang.empty() && (family == "sf" || family == "tt")) {
2379 if (!fontopts.empty()) {
2380 vector<string> opts = getVectorFromString(fontopts);
2382 for (auto const & opt : opts) {
2383 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2386 if (prefixIs(opt, "Scale=")) {
2387 scale_as_percentage(opt, scale);
2390 if (!fontopts.empty())
2395 if (family == "sf") {
2397 h_font_sf_scale[1] = scale;
2398 h_font_sans[1] = fontname;
2399 if (!fontopts.empty())
2400 h_font_sans_opts = fontopts;
2403 h_font_tt_scale[1] = scale;
2404 h_font_typewriter[1] = fontname;
2405 if (!fontopts.empty())
2406 h_font_typewriter_opts = fontopts;
2410 // not rm, sf or tt or lang specific
2411 h_preamble << '\\' << t.cs();
2413 h_preamble << '[' << lang << ']';
2414 h_preamble << '{' << family << '}';
2415 if (!fontopts.empty())
2416 h_preamble << '[' << fontopts << ']';
2417 h_preamble << '{' << fontname << '}' << '\n';
2422 if (t.cs() == "date") {
2423 string argument = p.getArg('{', '}');
2424 if (argument.empty())
2425 h_suppress_date = "true";
2427 h_preamble << t.asInput() << '{' << argument << '}';
2431 if (t.cs() == "color") {
2432 string const space =
2433 (p.hasOpt() ? p.getOpt() : string());
2434 string argument = p.getArg('{', '}');
2435 // check the case that a standard color is used
2436 if (space.empty() && is_known(argument, known_basic_colors)) {
2437 h_fontcolor = rgbcolor2code(argument);
2438 registerAutomaticallyLoadedPackage("color");
2439 } else if (space.empty() && argument == "document_fontcolor")
2440 registerAutomaticallyLoadedPackage("color");
2441 // check the case that LyX's document_fontcolor is defined
2442 // but not used for \color
2444 h_preamble << t.asInput();
2446 h_preamble << space;
2447 h_preamble << '{' << argument << '}';
2448 // the color might already be set because \definecolor
2449 // is parsed before this
2455 if (t.cs() == "pagecolor") {
2456 string argument = p.getArg('{', '}');
2457 // check the case that a standard color is used
2458 if (is_known(argument, known_basic_colors)) {
2459 h_backgroundcolor = rgbcolor2code(argument);
2460 } else if (argument == "page_backgroundcolor")
2461 registerAutomaticallyLoadedPackage("color");
2462 // check the case that LyX's page_backgroundcolor is defined
2463 // but not used for \pagecolor
2465 h_preamble << t.asInput() << '{' << argument << '}';
2466 // the color might already be set because \definecolor
2467 // is parsed before this
2468 h_backgroundcolor = "";
2473 if (t.cs() == "makeatletter") {
2474 // LyX takes care of this
2475 p.setCatcode('@', catLetter);
2479 if (t.cs() == "makeatother") {
2480 // LyX takes care of this
2481 p.setCatcode('@', catOther);
2485 if (t.cs() == "makeindex") {
2486 // LyX will re-add this if a print index command is found
2491 if (t.cs() == "newindex") {
2492 string const indexname = p.getArg('[', ']');
2493 string const shortcut = p.verbatim_item();
2494 if (!indexname.empty())
2495 h_index[index_number] = indexname;
2497 h_index[index_number] = shortcut;
2498 h_shortcut[index_number] = shortcut;
2504 if (t.cs() == "addbibresource") {
2505 string const options = p.getArg('[', ']');
2506 string const arg = removeExtension(p.getArg('{', '}'));
2507 if (!options.empty()) {
2508 // check if the option contains a bibencoding, if yes, extract it
2509 string::size_type pos = options.find("bibencoding=");
2511 if (pos != string::npos) {
2512 string::size_type i = options.find(',', pos);
2513 if (i == string::npos)
2514 encoding = options.substr(pos + 1);
2516 encoding = options.substr(pos, i - pos);
2517 pos = encoding.find('=');
2518 if (pos == string::npos)
2521 encoding = encoding.substr(pos + 1);
2523 if (!encoding.empty())
2524 biblatex_encodings.push_back(normalize_filename(arg) + ' ' + encoding);
2526 biblatex_bibliographies.push_back(arg);
2530 if (t.cs() == "bibliography") {
2531 vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
2532 biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
2536 if (t.cs() == "RS@ifundefined") {
2537 string const name = p.verbatim_item();
2538 string const body1 = p.verbatim_item();
2539 string const body2 = p.verbatim_item();
2540 // only non-lyxspecific stuff
2541 if (in_lyx_preamble &&
2542 (name == "subsecref" || name == "thmref" || name == "lemref"))
2546 ss << '\\' << t.cs();
2547 ss << '{' << name << '}'
2548 << '{' << body1 << '}'
2549 << '{' << body2 << '}';
2550 h_preamble << ss.str();
2555 if (t.cs() == "AtBeginDocument") {
2556 string const name = p.verbatim_item();
2557 // only non-lyxspecific stuff
2558 if (in_lyx_preamble &&
2559 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
2560 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
2561 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
2562 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
2563 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
2564 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
2565 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
2566 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
2567 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
2568 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
2569 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
2570 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
2571 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
2572 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
2573 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
2577 ss << '\\' << t.cs();
2578 ss << '{' << name << '}';
2579 h_preamble << ss.str();
2584 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
2585 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
2586 || t.cs() == "providecommand" || t.cs() == "providecommandx"
2587 || t.cs() == "DeclareRobustCommand"
2588 || t.cs() == "DeclareRobustCommandx"
2589 || t.cs() == "ProvideTextCommandDefault"
2590 || t.cs() == "DeclareMathAccent") {
2592 if (p.next_token().character() == '*') {
2596 string const name = p.verbatim_item();
2597 string const opt1 = p.getFullOpt();
2598 string const opt2 = p.getFullOpt();
2599 string const body = p.verbatim_item();
2600 // store the in_lyx_preamble setting
2601 bool const was_in_lyx_preamble = in_lyx_preamble;
2603 if (name == "\\rmdefault")
2604 if (is_known(body, known_roman_font_packages)) {
2605 h_font_roman[0] = body;
2607 in_lyx_preamble = true;
2609 if (name == "\\sfdefault") {
2610 if (is_known(body, known_sans_font_packages)) {
2611 h_font_sans[0] = body;
2613 in_lyx_preamble = true;
2615 if (body == "LibertinusSans-OsF") {
2616 h_font_sans[0] = "LibertinusSans-LF";
2617 h_font_sans_osf = "true";
2619 in_lyx_preamble = true;
2622 if (name == "\\ttdefault")
2623 if (is_known(body, known_typewriter_font_packages)) {
2624 h_font_typewriter[0] = body;
2626 in_lyx_preamble = true;
2628 if (name == "\\familydefault") {
2629 string family = body;
2630 // remove leading "\"
2631 h_font_default_family = family.erase(0,1);
2633 in_lyx_preamble = true;
2635 if (name == "\\LibertinusSans@scale") {
2636 if (isStrDbl(body)) {
2637 h_font_sf_scale[0] = convert<string>(
2638 static_cast<int>(100 * convert<double>(body)));
2641 if (name == "\\LibertinusMono@scale") {
2642 if (isStrDbl(body)) {
2643 h_font_tt_scale[0] = convert<string>(
2644 static_cast<int>(100 * convert<double>(body)));
2648 // remove LyX-specific definitions that are re-added by LyX
2650 // \lyxline is an ancient command that is converted by tex2lyx into
2651 // a \rule therefore remove its preamble code
2652 if (name == "\\lyxdot" || name == "\\lyxarrow"
2653 || name == "\\lyxline" || name == "\\LyX") {
2655 in_lyx_preamble = true;
2658 // Add the command to the known commands
2659 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
2661 // only non-lyxspecific stuff
2662 if (!in_lyx_preamble) {
2664 ss << '\\' << t.cs();
2667 ss << '{' << name << '}' << opt1 << opt2
2668 << '{' << body << "}";
2669 if (prefixIs(t.cs(), "renew") || !contains(h_preamble.str(), ss.str()))
2670 h_preamble << ss.str();
2672 ostream & out = in_preamble ? h_preamble : os;
2673 out << "\\" << t.cs() << "{" << name << "}"
2674 << opts << "{" << body << "}";
2677 // restore the in_lyx_preamble setting
2678 in_lyx_preamble = was_in_lyx_preamble;
2682 if (t.cs() == "documentclass") {
2683 vector<string>::iterator it;
2684 vector<string> opts = split_options(p.getArg('[', ']'));
2685 // FIXME This does not work for classes that have a
2686 // different name in LyX than in LaTeX
2687 string const tclass = p.getArg('{', '}');
2689 // Only set text class if a class hasn't been forced
2690 // (this was set above)
2692 // textclass needs to be set at this place (if not already done)
2693 // as we need to know it for other parameters
2694 // (such as class-dependent paper size)
2695 setTextClass(tclass, tc);
2700 // Try those who are (most likely) known to all packages first
2701 handle_opt(opts, known_fontsizes, h_paperfontsize);
2702 delete_opt(opts, known_fontsizes);
2703 // delete "pt" at the end
2704 string::size_type i = h_paperfontsize.find("pt");
2705 if (i != string::npos)
2706 h_paperfontsize.erase(i);
2707 // Now those known specifically to the class
2708 vector<string> class_fsizes = getVectorFromString(tc.opt_fontsize(), "|");
2709 string const fsize_format = tc.fontsizeformat();
2710 for (auto const & fsize : class_fsizes) {
2711 string latexsize = subst(fsize_format, "$$s", fsize);
2712 vector<string>::iterator it = find(opts.begin(), opts.end(), latexsize);
2713 if (it != opts.end()) {
2714 h_paperfontsize = fsize;
2720 // The documentclass options are always parsed before the options
2721 // of the babel call so that a language cannot overwrite the babel
2723 handle_opt(opts, known_languages, h_language);
2724 delete_opt(opts, known_languages);
2727 if ((it = find(opts.begin(), opts.end(), "fleqn"))
2729 h_is_mathindent = "1";
2732 // formula numbering side
2733 if ((it = find(opts.begin(), opts.end(), "leqno"))
2735 h_math_numbering_side = "left";
2738 else if ((it = find(opts.begin(), opts.end(), "reqno"))
2740 h_math_numbering_side = "right";
2744 // paper orientation
2745 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
2746 h_paperorientation = "landscape";
2750 if ((it = find(opts.begin(), opts.end(), "oneside"))
2755 if ((it = find(opts.begin(), opts.end(), "twoside"))
2761 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
2763 h_papercolumns = "1";
2766 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
2768 h_papercolumns = "2";
2772 // some size options are known by the document class, other sizes
2773 // are handled by the \geometry command of the geometry package
2774 vector<string> class_psizes = getVectorFromString(tc.opt_pagesize(), "|");
2775 string const psize_format = tc.pagesizeformat();
2776 for (auto const & psize : class_psizes) {
2777 string latexsize = subst(psize_format, "$$s", psize);
2778 vector<string>::iterator it = find(opts.begin(), opts.end(), latexsize);
2779 if (it != opts.end()) {
2780 h_papersize = psize;
2784 if (psize_format == "$$spaper")
2786 // Also try with the default format since this is understood by
2788 latexsize = psize + "paper";
2789 it = find(opts.begin(), opts.end(), latexsize);
2790 if (it != opts.end()) {
2791 h_papersize = psize;
2796 // the remaining options
2797 h_options = join(opts, ",");
2801 if (t.cs() == "DocumentMetadata") {
2802 h_doc_metadata = trimSpaceAndEol(p.getArg('{', '}'));
2806 if (t.cs() == "usepackage") {
2807 string const options = p.getArg('[', ']');
2808 string const name = p.getArg('{', '}');
2809 vector<string> vecnames;
2810 split(name, vecnames, ',');
2811 vector<string>::const_iterator it = vecnames.begin();
2812 vector<string>::const_iterator end = vecnames.end();
2813 for (; it != end; ++it)
2814 handle_package(p, trimSpaceAndEol(*it), options,
2815 in_lyx_preamble, detectEncoding);
2819 if (t.cs() == "inputencoding") {
2820 string const encoding = p.getArg('{','}');
2821 Encoding const * const enc = encodings.fromLaTeXName(
2822 encoding, Encoding::inputenc, true);
2824 if (!detectEncoding)
2825 cerr << "Unknown encoding " << encoding
2826 << ". Ignoring." << std::endl;
2829 h_inputencoding = enc->name();
2830 p.setEncoding(enc->iconvName());
2835 if (t.cs() == "newenvironment") {
2836 string const name = p.getArg('{', '}');
2837 string const opt1 = p.getFullOpt();
2838 string const opt2 = p.getFullOpt();
2839 string const beg = p.verbatim_item();
2840 string const end = p.verbatim_item();
2841 if (!in_lyx_preamble) {
2842 h_preamble << "\\newenvironment{" << name
2843 << '}' << opt1 << opt2 << '{'
2844 << beg << "}{" << end << '}';
2846 add_known_environment(name, opt1, !opt2.empty(),
2847 from_utf8(beg), from_utf8(end));
2851 if (t.cs() == "newtheorem") {
2853 if (p.next_token().character() == '*') {
2857 string const name = p.getArg('{', '}');
2858 string const opt1 = p.getFullOpt();
2859 string const opt2 = p.getFullOpt();
2860 string const body = p.verbatim_item();
2861 string const opt3 = p.getFullOpt();
2862 string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
2864 string const complete = cmd + "{" + name + '}' +
2865 opt1 + opt2 + '{' + body + '}' + opt3;
2867 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
2869 if (!in_lyx_preamble)
2870 h_preamble << complete;
2874 if (t.cs() == "def") {
2875 string name = p.get_token().cs();
2876 // In fact, name may be more than the name:
2877 // In the test case of bug 8116
2878 // name == "csname SF@gobble@opt \endcsname".
2879 // Therefore, we need to use asInput() instead of cs().
2880 while (p.next_token().cat() != catBegin)
2881 name += p.get_token().asInput();
2882 if (!in_lyx_preamble)
2883 h_preamble << "\\def\\" << name << '{'
2884 << p.verbatim_item() << "}";
2888 if (t.cs() == "newcolumntype") {
2889 string const name = p.getArg('{', '}');
2890 trimSpaceAndEol(name);
2892 string opts = p.getOpt();
2893 if (!opts.empty()) {
2894 istringstream is(string(opts, 1));
2897 special_columns_[name[0]] = nargs;
2898 h_preamble << "\\newcolumntype{" << name << "}";
2900 h_preamble << "[" << nargs << "]";
2901 h_preamble << "{" << p.verbatim_item() << "}";
2905 if (t.cs() == "setcounter") {
2906 string const name = p.getArg('{', '}');
2907 string const content = p.getArg('{', '}');
2908 if (name == "secnumdepth")
2909 h_secnumdepth = content;
2910 else if (name == "tocdepth")
2911 h_tocdepth = content;
2913 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
2917 if (t.cs() == "setlength") {
2918 string const name = p.verbatim_item();
2919 string const content = p.verbatim_item();
2920 // the paragraphs are only not indented when \parindent is set to zero
2921 if (name == "\\parindent" && content != "")
2922 h_paragraph_indentation = translate_len(content);
2923 else if (name == "\\parskip" && isPackageUsed("parskip")) {
2924 if (content == "\\smallskipamount")
2925 h_defskip = "smallskip";
2926 else if (content == "\\medskipamount")
2927 h_defskip = "medskip";
2928 else if (content == "\\bigskipamount")
2929 h_defskip = "bigskip";
2930 else if (content == "\\baselineskip")
2931 h_defskip = "fullline";
2933 h_defskip = translate_len(content);
2934 } else if (name == "\\mathindent") {
2935 h_mathindentation = translate_len(content);
2937 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2941 if (t.cs() == "onehalfspacing") {
2942 h_spacing = "onehalf";
2946 if (t.cs() == "doublespacing") {
2947 h_spacing = "double";
2951 if (t.cs() == "setstretch") {
2952 h_spacing = "other " + p.verbatim_item();
2956 if (t.cs() == "synctex") {
2957 // the scheme is \synctex=value
2958 // where value can only be "1" or "-1"
2959 h_output_sync = "1";
2960 // there can be any character behind the value (e.g. a linebreak or a '\'
2961 // therefore we extract it char by char
2963 string value = p.get_token().asInput();
2965 value += p.get_token().asInput();
2966 h_output_sync_macro = "\\synctex=" + value;
2970 if (t.cs() == "begin") {
2971 string const name = p.getArg('{', '}');
2972 if (name == "document")
2974 h_preamble << "\\begin{" << name << "}";
2978 if (t.cs() == "geometry") {
2979 vector<string> opts = split_options(p.getArg('{', '}'));
2980 handle_geometry(opts);
2984 if (t.cs() == "definecolor") {
2985 string const color = p.getArg('{', '}');
2986 string const space = p.getArg('{', '}');
2987 string const value = p.getArg('{', '}');
2988 if (color == "document_fontcolor" && space == "rgb") {
2989 RGBColor c(RGBColorFromLaTeX(value));
2990 h_fontcolor = X11hexname(c);
2991 } else if (color == "note_fontcolor" && space == "rgb") {
2992 RGBColor c(RGBColorFromLaTeX(value));
2993 h_notefontcolor = X11hexname(c);
2994 } else if (color == "page_backgroundcolor" && space == "rgb") {
2995 RGBColor c(RGBColorFromLaTeX(value));
2996 h_backgroundcolor = X11hexname(c);
2997 } else if (color == "shadecolor" && space == "rgb") {
2998 RGBColor c(RGBColorFromLaTeX(value));
2999 h_boxbgcolor = X11hexname(c);
3001 h_preamble << "\\definecolor{" << color
3002 << "}{" << space << "}{" << value
3008 if (t.cs() == "bibliographystyle") {
3009 h_biblio_style = p.verbatim_item();
3013 if (t.cs() == "jurabibsetup") {
3014 // FIXME p.getArg('{', '}') is most probably wrong (it
3015 // does not handle nested braces).
3016 // Use p.verbatim_item() instead.
3017 vector<string> jurabibsetup =
3018 split_options(p.getArg('{', '}'));
3019 // add jurabibsetup to the jurabib package options
3020 add_package("jurabib", jurabibsetup);
3021 if (!jurabibsetup.empty()) {
3022 h_preamble << "\\jurabibsetup{"
3023 << join(jurabibsetup, ",") << '}';
3028 if (t.cs() == "hypersetup") {
3029 vector<string> hypersetup =
3030 split_options(p.verbatim_item());
3031 // add hypersetup to the hyperref package options
3032 handle_hyperref(hypersetup);
3033 if (!hypersetup.empty()) {
3034 h_preamble << "\\hypersetup{"
3035 << join(hypersetup, ",") << '}';
3040 if (t.cs() == "includeonly") {
3041 vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
3042 for (auto & iofile : includeonlys) {
3043 string filename(normalize_filename(iofile));
3044 string const path = getMasterFilePath(true);
3045 // We want to preserve relative/absolute filenames,
3046 // therefore path is only used for testing
3047 if (!makeAbsPath(filename, path).exists()) {
3048 // The file extension is probably missing.
3049 // Now try to find it out.
3050 string const tex_name =
3051 find_file(filename, path,
3052 known_tex_extensions);
3053 if (!tex_name.empty())
3054 filename = tex_name;
3057 if (makeAbsPath(filename, path).exists())
3058 fix_child_filename(filename);
3060 cerr << "Warning: Could not find included file '"
3061 << filename << "'." << endl;
3062 outname = changeExtension(filename, "lyx");
3063 h_includeonlys.push_back(outname);
3068 if (is_known(t.cs(), known_if_3arg_commands)) {
3069 // prevent misparsing of \usepackage if it is used
3070 // as an argument (see e.g. our own output of
3071 // \@ifundefined above)
3072 string const arg1 = p.verbatim_item();
3073 string const arg2 = p.verbatim_item();
3074 string const arg3 = p.verbatim_item();
3075 // test case \@ifundefined{date}{}{\date{}}
3076 if (t.cs() == "@ifundefined" && arg1 == "date" &&
3077 arg2.empty() && arg3 == "\\date{}") {
3078 h_suppress_date = "true";
3079 // older tex2lyx versions did output
3080 // \@ifundefined{definecolor}{\usepackage{color}}{}
3081 } else if (t.cs() == "@ifundefined" &&
3082 arg1 == "definecolor" &&
3083 arg2 == "\\usepackage{color}" &&
3085 if (!in_lyx_preamble)
3086 h_preamble << package_beg_sep
3089 << "\\@ifundefined{definecolor}{color}{}"
3092 //\@ifundefined{showcaptionsetup}{}{%
3093 // \PassOptionsToPackage{caption=false}{subfig}}
3094 // that LyX uses for subfloats
3095 } else if (t.cs() == "@ifundefined" &&
3096 arg1 == "showcaptionsetup" && arg2.empty()
3097 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
3099 } else if (!in_lyx_preamble) {
3100 h_preamble << t.asInput()
3101 << '{' << arg1 << '}'
3102 << '{' << arg2 << '}'
3103 << '{' << arg3 << '}';
3108 if (is_known(t.cs(), known_if_commands)) {
3109 // must not parse anything in conditional code, since
3110 // LyX would output the parsed contents unconditionally
3111 if (!in_lyx_preamble)
3112 h_preamble << t.asInput();
3113 handle_if(p, in_lyx_preamble);
3117 if (!t.cs().empty() && !in_lyx_preamble) {
3118 h_preamble << '\\' << t.cs();
3123 // set textclass if not yet done (snippets without \documentclass and forced class)
3125 setTextClass(h_textclass, tc);
3127 // remove the whitespace
3130 if (h_papersides.empty()) {
3133 h_papersides = ss.str();
3136 // If the CJK package is used we cannot set the document language from
3137 // the babel options. Instead, we guess which language is used most
3138 // and set this one.
3139 default_language = h_language;
3140 if (is_full_document &&
3141 (auto_packages.find("CJK") != auto_packages.end() ||
3142 auto_packages.find("CJKutf8") != auto_packages.end())) {
3144 h_language = guessLanguage(p, default_language);
3146 if (explicit_babel && h_language != default_language) {
3147 // We set the document language to a CJK language,
3148 // but babel is explicitly called in the user preamble
3149 // without options. LyX will not add the default
3150 // language to the document options if it is either
3151 // english, or no text is set as default language.
3152 // Therefore we need to add a language option explicitly.
3153 // FIXME: It would be better to remove all babel calls
3154 // from the user preamble, but this is difficult
3155 // without re-introducing bug 7861.
3156 if (h_options.empty())
3157 h_options = lyx2babel(default_language);
3159 h_options += ',' + lyx2babel(default_language);
3163 // Finally, set the quote style.
3164 // LyX knows the following quotes styles:
3165 // british, cjk, cjkangle, danish, english, french, german,
3166 // polish, russian, swedish and swiss
3167 // conversion list taken from
3168 // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
3169 // (quotes for kazakh are unknown)
3171 if (is_known(h_language, known_british_quotes_languages))
3172 h_quotes_style = "british";
3174 else if (is_known(h_language, known_cjk_quotes_languages))
3175 h_quotes_style = "cjk";
3177 else if (is_known(h_language, known_cjkangle_quotes_languages))
3178 h_quotes_style = "cjkangle";
3180 else if (is_known(h_language, known_danish_quotes_languages))
3181 h_quotes_style = "danish";
3183 else if (is_known(h_language, known_french_quotes_languages))
3184 h_quotes_style = "french";
3186 else if (is_known(h_language, known_german_quotes_languages))
3187 h_quotes_style = "german";
3189 else if (is_known(h_language, known_polish_quotes_languages))
3190 h_quotes_style = "polish";
3192 else if (is_known(h_language, known_hungarian_quotes_languages))
3193 h_quotes_style = "hungarian";
3195 else if (is_known(h_language, known_russian_quotes_languages))
3196 h_quotes_style = "russian";
3198 else if (is_known(h_language, known_swedish_quotes_languages))
3199 h_quotes_style = "swedish";
3201 else if (is_known(h_language, known_swiss_quotes_languages))
3202 h_quotes_style = "swiss";
3204 else if (is_known(h_language, known_english_quotes_languages))
3205 h_quotes_style = "english";
3209 string Preamble::parseEncoding(Parser & p, string const & forceclass)
3211 TeX2LyXDocClass dummy;
3212 parse(p, forceclass, true, dummy);
3213 if (h_inputencoding != "auto-legacy" && h_inputencoding != "auto-legacy-plain")
3214 return h_inputencoding;
3219 string babel2lyx(string const & language)
3221 char const * const * where = is_known(language, known_languages);
3223 return known_coded_languages[where - known_languages];
3228 string lyx2babel(string const & language)
3230 char const * const * where = is_known(language, known_coded_languages);
3232 return known_languages[where - known_coded_languages];
3237 string Preamble::polyglossia2lyx(string const & language)
3239 char const * const * where = is_known(language, polyglossia_languages);
3241 return coded_polyglossia_languages[where - polyglossia_languages];
3246 string rgbcolor2code(string const & name)
3248 char const * const * where = is_known(name, known_basic_colors);
3250 // "red", "green" etc
3251 return known_basic_color_codes[where - known_basic_colors];
3253 // "255,0,0", "0,255,0" etc
3254 RGBColor c(RGBColorFromLaTeX(name));
3255 return X11hexname(c);