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 string const raw = subdoc ? empty_string() : h_preamble.str();
1955 os << "\\begin_preamble\n";
1956 for (string::size_type i = 0; i < raw.size(); ++i) {
1957 if (raw[i] == package_beg_sep) {
1958 // Here follows some package loading code that
1959 // must be skipped if the package is loaded
1961 string::size_type j = raw.find(package_mid_sep, i);
1962 if (j == string::npos)
1964 string::size_type k = raw.find(package_end_sep, j);
1965 if (k == string::npos)
1967 string const package = raw.substr(i + 1, j - i - 1);
1968 string const replacement = raw.substr(j + 1, k - j - 1);
1969 if (auto_packages.find(package) == auto_packages.end())
1975 os << "\n\\end_preamble\n";
1977 if (!h_options.empty())
1978 os << "\\options " << h_options << "\n";
1979 os << "\\use_default_options " << h_use_default_options << "\n";
1980 if (!used_modules.empty()) {
1981 os << "\\begin_modules\n";
1982 vector<string>::const_iterator const end = used_modules.end();
1983 vector<string>::const_iterator it = used_modules.begin();
1984 for (; it != end; ++it)
1986 os << "\\end_modules\n";
1988 if (!h_includeonlys.empty()) {
1989 os << "\\begin_includeonly\n";
1990 for (auto const & iofile : h_includeonlys)
1991 os << iofile << '\n';
1992 os << "\\end_includeonly\n";
1994 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1995 << "\\language " << h_language << "\n"
1996 << "\\language_package " << h_language_package << "\n"
1997 << "\\inputencoding " << h_inputencoding << "\n"
1998 << "\\fontencoding " << h_fontencoding << "\n"
1999 << "\\font_roman \"" << h_font_roman[0]
2000 << "\" \"" << h_font_roman[1] << "\"\n"
2001 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
2002 << "\\font_typewriter \"" << h_font_typewriter[0]
2003 << "\" \"" << h_font_typewriter[1] << "\"\n"
2004 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
2005 << "\\font_default_family " << h_font_default_family << "\n"
2006 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
2007 << "\\font_sc " << h_font_sc << "\n"
2008 << "\\font_roman_osf " << h_font_roman_osf << "\n"
2009 << "\\font_sans_osf " << h_font_sans_osf << "\n"
2010 << "\\font_typewriter_osf " << h_font_typewriter_osf << "\n";
2011 if (!h_font_roman_opts.empty())
2012 os << "\\font_roman_opts \"" << h_font_roman_opts << "\"" << '\n';
2013 os << "\\font_sf_scale " << h_font_sf_scale[0]
2014 << ' ' << h_font_sf_scale[1] << '\n';
2015 if (!h_font_sans_opts.empty())
2016 os << "\\font_sans_opts \"" << h_font_sans_opts << "\"" << '\n';
2017 os << "\\font_tt_scale " << h_font_tt_scale[0]
2018 << ' ' << h_font_tt_scale[1] << '\n';
2019 if (!h_font_cjk.empty())
2020 os << "\\font_cjk " << h_font_cjk << '\n';
2021 if (!h_font_typewriter_opts.empty())
2022 os << "\\font_typewriter_opts \"" << h_font_typewriter_opts << "\"" << '\n';
2023 os << "\\use_microtype " << h_use_microtype << '\n'
2024 << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
2025 << "\\graphics " << h_graphics << '\n'
2026 << "\\default_output_format " << h_default_output_format << "\n"
2027 << "\\output_sync " << h_output_sync << "\n";
2028 if (h_output_sync == "1")
2029 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
2030 os << "\\bibtex_command " << h_bibtex_command << "\n"
2031 << "\\index_command " << h_index_command << "\n";
2032 if (!h_float_placement.empty())
2033 os << "\\float_placement " << h_float_placement << "\n";
2034 os << "\\paperfontsize " << h_paperfontsize << "\n"
2035 << "\\spacing " << h_spacing << "\n"
2036 << "\\use_hyperref " << h_use_hyperref << '\n';
2037 if (h_use_hyperref == "true") {
2038 if (!h_pdf_title.empty())
2039 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
2040 if (!h_pdf_author.empty())
2041 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
2042 if (!h_pdf_subject.empty())
2043 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
2044 if (!h_pdf_keywords.empty())
2045 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
2046 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
2047 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
2048 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
2049 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
2050 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
2051 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
2052 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
2053 "\\pdf_backref " << h_pdf_backref << "\n"
2054 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
2055 if (!h_pdf_pagemode.empty())
2056 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
2057 if (!h_pdf_quoted_options.empty())
2058 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
2060 os << "\\papersize " << h_papersize << "\n"
2061 << "\\use_geometry " << h_use_geometry << '\n';
2062 for (map<string, string>::const_iterator it = h_use_packages.begin();
2063 it != h_use_packages.end(); ++it)
2064 os << "\\use_package " << it->first << ' ' << it->second << '\n';
2065 os << "\\cite_engine " << h_cite_engine << '\n'
2066 << "\\cite_engine_type " << h_cite_engine_type << '\n'
2067 << "\\biblio_style " << h_biblio_style << "\n"
2068 << "\\use_bibtopic " << h_use_bibtopic << "\n";
2069 if (!h_biblio_options.empty())
2070 os << "\\biblio_options " << h_biblio_options << "\n";
2071 if (!h_biblatex_bibstyle.empty())
2072 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
2073 if (!h_biblatex_citestyle.empty())
2074 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
2075 if (!h_multibib.empty())
2076 os << "\\multibib " << h_multibib << "\n";
2077 os << "\\use_indices " << h_use_indices << "\n"
2078 << "\\paperorientation " << h_paperorientation << '\n'
2079 << "\\suppress_date " << h_suppress_date << '\n'
2080 << "\\justification " << h_justification << '\n'
2081 << "\\use_refstyle " << h_use_refstyle << '\n'
2082 << "\\use_minted " << h_use_minted << '\n'
2083 << "\\use_lineno " << h_use_lineno << '\n';
2084 if (!h_lineno_options.empty())
2085 os << "\\lineno_options " << h_lineno_options << '\n';
2086 if (!h_fontcolor.empty())
2087 os << "\\fontcolor " << h_fontcolor << '\n';
2088 if (!h_notefontcolor.empty())
2089 os << "\\notefontcolor " << h_notefontcolor << '\n';
2090 if (!h_backgroundcolor.empty())
2091 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
2092 if (!h_boxbgcolor.empty())
2093 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
2094 if (index_number != 0)
2095 for (int i = 0; i < index_number; i++) {
2096 os << "\\index " << h_index[i] << '\n'
2097 << "\\shortcut " << h_shortcut[i] << '\n'
2098 << "\\color " << h_color << '\n'
2102 os << "\\index " << h_index[0] << '\n'
2103 << "\\shortcut " << h_shortcut[0] << '\n'
2104 << "\\color " << h_color << '\n'
2108 << "\\secnumdepth " << h_secnumdepth << "\n"
2109 << "\\tocdepth " << h_tocdepth << "\n"
2110 << "\\paragraph_separation " << h_paragraph_separation << "\n";
2111 if (h_paragraph_separation == "skip")
2112 os << "\\defskip " << h_defskip << "\n";
2114 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
2115 os << "\\is_math_indent " << h_is_mathindent << "\n";
2116 if (!h_mathindentation.empty())
2117 os << "\\math_indentation " << h_mathindentation << "\n";
2118 os << "\\math_numbering_side " << h_math_numbering_side << "\n";
2119 os << "\\quotes_style " << h_quotes_style << "\n"
2120 << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
2121 << "\\papercolumns " << h_papercolumns << "\n"
2122 << "\\papersides " << h_papersides << "\n"
2123 << "\\paperpagestyle " << h_paperpagestyle << "\n";
2124 if (!h_listings_params.empty())
2125 os << "\\listings_params " << h_listings_params << "\n";
2126 os << "\\tracking_changes " << h_tracking_changes << "\n"
2127 << "\\output_changes " << h_output_changes << "\n"
2128 << "\\change_bars " << h_change_bars << "\n"
2129 << "\\html_math_output " << h_html_math_output << "\n"
2130 << "\\html_css_as_file " << h_html_css_as_file << "\n"
2131 << "\\html_be_strict " << h_html_be_strict << "\n"
2132 << "\\docbook_table_output " << h_docbook_table_output << "\n"
2133 << "\\docbook_mathml_prefix " << h_docbook_mathml_prefix << "\n"
2135 << "\\end_header\n\n"
2136 << "\\begin_body\n";
2141 void Preamble::parse(Parser & p, string const & forceclass,
2142 TeX2LyXDocClass & tc)
2144 // initialize fixed types
2145 special_columns_['D'] = 3;
2146 parse(p, forceclass, false, tc);
2150 void Preamble::parse(Parser & p, string const & forceclass,
2151 bool detectEncoding, TeX2LyXDocClass & tc)
2153 bool is_full_document = false;
2154 bool is_lyx_file = false;
2155 bool in_lyx_preamble = false;
2156 bool class_set = false;
2158 // determine whether this is a full document or a fragment for inclusion
2160 Token const & t = p.get_token();
2162 if (t.cat() == catEscape && t.cs() == "documentclass") {
2163 is_full_document = true;
2169 if (detectEncoding && !is_full_document)
2172 while (is_full_document && p.good()) {
2173 if (detectEncoding && h_inputencoding != "auto-legacy" &&
2174 h_inputencoding != "auto-legacy-plain")
2177 // Force textclass if the user wanted it
2178 if (!forceclass.empty()) {
2179 setTextClass(forceclass, tc);
2183 Token const & t = p.get_token();
2186 if (!detectEncoding)
2187 cerr << "t: " << t << '\n';
2193 if (!in_lyx_preamble &&
2194 (t.cat() == catLetter ||
2195 t.cat() == catSuper ||
2196 t.cat() == catSub ||
2197 t.cat() == catOther ||
2198 t.cat() == catMath ||
2199 t.cat() == catActive ||
2200 t.cat() == catBegin ||
2201 t.cat() == catEnd ||
2202 t.cat() == catAlign ||
2203 t.cat() == catParameter)) {
2204 h_preamble << t.cs();
2208 if (!in_lyx_preamble &&
2209 (t.cat() == catSpace || t.cat() == catNewline)) {
2210 h_preamble << t.asInput();
2214 if (t.cat() == catComment) {
2215 static regex const islyxfile("%% LyX .* created this file");
2216 static regex const usercommands("User specified LaTeX commands");
2218 string const comment = t.asInput();
2220 // magically switch encoding default if it looks like XeLaTeX
2221 static string const magicXeLaTeX =
2222 "% This document must be compiled with XeLaTeX ";
2223 if (comment.size() > magicXeLaTeX.size()
2224 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
2225 && h_inputencoding == "auto-legacy") {
2226 if (!detectEncoding)
2227 cerr << "XeLaTeX comment found, switching to UTF8\n";
2228 h_inputencoding = "utf8";
2231 if (regex_search(comment, sub, islyxfile)) {
2233 in_lyx_preamble = true;
2234 } else if (is_lyx_file
2235 && regex_search(comment, sub, usercommands))
2236 in_lyx_preamble = false;
2237 else if (!in_lyx_preamble)
2238 h_preamble << t.asInput();
2242 if (t.cs() == "PassOptionsToPackage") {
2243 string const poptions = p.getArg('{', '}');
2244 string const package = p.verbatim_item();
2245 extra_package_options_.insert(make_pair(package, poptions));
2249 if (t.cs() == "pagestyle") {
2250 h_paperpagestyle = p.verbatim_item();
2254 if (t.cs() == "setdefaultlanguage") {
2256 // We don't yet care about non-language variant options
2257 // because LyX doesn't support this yet, see bug #8214
2259 string langopts = p.getOpt();
2260 // check if the option contains a variant, if yes, extract it
2261 string::size_type pos_var = langopts.find("variant");
2262 string::size_type i = langopts.find(',', pos_var);
2263 string::size_type k = langopts.find('=', pos_var);
2264 if (pos_var != string::npos){
2266 if (i == string::npos)
2267 variant = langopts.substr(k + 1, langopts.length() - k - 2);
2269 variant = langopts.substr(k + 1, i - k - 1);
2270 h_language = variant;
2274 h_language = p.verbatim_item();
2275 //finally translate the poyglossia name to a LyX name
2276 h_language = polyglossia2lyx(h_language);
2280 if (t.cs() == "setotherlanguage") {
2281 // We don't yet care about the option because LyX doesn't
2282 // support this yet, see bug #8214
2283 p.hasOpt() ? p.getOpt() : string();
2288 if (t.cs() == "setmainfont") {
2289 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2290 h_font_roman[1] = p.getArg('{', '}');
2291 if (!fontopts.empty()) {
2292 vector<string> opts = getVectorFromString(fontopts);
2294 for (auto const & opt : opts) {
2295 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2298 if (!fontopts.empty())
2302 h_font_roman_opts = fontopts;
2307 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
2308 // LyX currently only supports the scale option
2309 string scale, fontopts;
2311 fontopts = p.getArg('[', ']');
2312 if (!fontopts.empty()) {
2313 vector<string> opts = getVectorFromString(fontopts);
2315 for (auto const & opt : opts) {
2316 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2319 if (prefixIs(opt, "Scale=")) {
2320 scale_as_percentage(opt, scale);
2323 if (!fontopts.empty())
2329 if (t.cs() == "setsansfont") {
2331 h_font_sf_scale[1] = scale;
2332 h_font_sans[1] = p.getArg('{', '}');
2333 if (!fontopts.empty())
2334 h_font_sans_opts = fontopts;
2337 h_font_tt_scale[1] = scale;
2338 h_font_typewriter[1] = p.getArg('{', '}');
2339 if (!fontopts.empty())
2340 h_font_typewriter_opts = fontopts;
2345 if (t.cs() == "babelfont") {
2347 h_use_non_tex_fonts = true;
2348 h_language_package = "babel";
2349 if (h_inputencoding == "auto-legacy")
2350 p.setEncoding("UTF-8");
2351 // we don't care about the lang option
2352 string const lang = p.hasOpt() ? p.getArg('[', ']') : string();
2353 string const family = p.getArg('{', '}');
2354 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2355 string const fontname = p.getArg('{', '}');
2356 if (lang.empty() && family == "rm") {
2357 h_font_roman[1] = fontname;
2358 if (!fontopts.empty()) {
2359 vector<string> opts = getVectorFromString(fontopts);
2361 for (auto const & opt : opts) {
2362 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2365 if (!fontopts.empty())
2369 h_font_roman_opts = fontopts;
2372 } else if (lang.empty() && (family == "sf" || family == "tt")) {
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 (prefixIs(opt, "Scale=")) {
2382 scale_as_percentage(opt, scale);
2385 if (!fontopts.empty())
2390 if (family == "sf") {
2392 h_font_sf_scale[1] = scale;
2393 h_font_sans[1] = fontname;
2394 if (!fontopts.empty())
2395 h_font_sans_opts = fontopts;
2398 h_font_tt_scale[1] = scale;
2399 h_font_typewriter[1] = fontname;
2400 if (!fontopts.empty())
2401 h_font_typewriter_opts = fontopts;
2405 // not rm, sf or tt or lang specific
2406 h_preamble << '\\' << t.cs();
2408 h_preamble << '[' << lang << ']';
2409 h_preamble << '{' << family << '}';
2410 if (!fontopts.empty())
2411 h_preamble << '[' << fontopts << ']';
2412 h_preamble << '{' << fontname << '}' << '\n';
2417 if (t.cs() == "date") {
2418 string argument = p.getArg('{', '}');
2419 if (argument.empty())
2420 h_suppress_date = "true";
2422 h_preamble << t.asInput() << '{' << argument << '}';
2426 if (t.cs() == "color") {
2427 string const space =
2428 (p.hasOpt() ? p.getOpt() : string());
2429 string argument = p.getArg('{', '}');
2430 // check the case that a standard color is used
2431 if (space.empty() && is_known(argument, known_basic_colors)) {
2432 h_fontcolor = rgbcolor2code(argument);
2433 registerAutomaticallyLoadedPackage("color");
2434 } else if (space.empty() && argument == "document_fontcolor")
2435 registerAutomaticallyLoadedPackage("color");
2436 // check the case that LyX's document_fontcolor is defined
2437 // but not used for \color
2439 h_preamble << t.asInput();
2441 h_preamble << space;
2442 h_preamble << '{' << argument << '}';
2443 // the color might already be set because \definecolor
2444 // is parsed before this
2450 if (t.cs() == "pagecolor") {
2451 string argument = p.getArg('{', '}');
2452 // check the case that a standard color is used
2453 if (is_known(argument, known_basic_colors)) {
2454 h_backgroundcolor = rgbcolor2code(argument);
2455 } else if (argument == "page_backgroundcolor")
2456 registerAutomaticallyLoadedPackage("color");
2457 // check the case that LyX's page_backgroundcolor is defined
2458 // but not used for \pagecolor
2460 h_preamble << t.asInput() << '{' << argument << '}';
2461 // the color might already be set because \definecolor
2462 // is parsed before this
2463 h_backgroundcolor = "";
2468 if (t.cs() == "makeatletter") {
2469 // LyX takes care of this
2470 p.setCatcode('@', catLetter);
2474 if (t.cs() == "makeatother") {
2475 // LyX takes care of this
2476 p.setCatcode('@', catOther);
2480 if (t.cs() == "makeindex") {
2481 // LyX will re-add this if a print index command is found
2486 if (t.cs() == "newindex") {
2487 string const indexname = p.getArg('[', ']');
2488 string const shortcut = p.verbatim_item();
2489 if (!indexname.empty())
2490 h_index[index_number] = indexname;
2492 h_index[index_number] = shortcut;
2493 h_shortcut[index_number] = shortcut;
2499 if (t.cs() == "addbibresource") {
2500 string const options = p.getArg('[', ']');
2501 string const arg = removeExtension(p.getArg('{', '}'));
2502 if (!options.empty()) {
2503 // check if the option contains a bibencoding, if yes, extract it
2504 string::size_type pos = options.find("bibencoding=");
2506 if (pos != string::npos) {
2507 string::size_type i = options.find(',', pos);
2508 if (i == string::npos)
2509 encoding = options.substr(pos + 1);
2511 encoding = options.substr(pos, i - pos);
2512 pos = encoding.find('=');
2513 if (pos == string::npos)
2516 encoding = encoding.substr(pos + 1);
2518 if (!encoding.empty())
2519 biblatex_encodings.push_back(normalize_filename(arg) + ' ' + encoding);
2521 biblatex_bibliographies.push_back(arg);
2525 if (t.cs() == "bibliography") {
2526 vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
2527 biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
2531 if (t.cs() == "RS@ifundefined") {
2532 string const name = p.verbatim_item();
2533 string const body1 = p.verbatim_item();
2534 string const body2 = p.verbatim_item();
2535 // only non-lyxspecific stuff
2536 if (in_lyx_preamble &&
2537 (name == "subsecref" || name == "thmref" || name == "lemref"))
2541 ss << '\\' << t.cs();
2542 ss << '{' << name << '}'
2543 << '{' << body1 << '}'
2544 << '{' << body2 << '}';
2545 h_preamble << ss.str();
2550 if (t.cs() == "AtBeginDocument") {
2551 string const name = p.verbatim_item();
2552 // only non-lyxspecific stuff
2553 if (in_lyx_preamble &&
2554 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
2555 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
2556 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
2557 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
2558 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
2559 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
2560 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
2561 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
2562 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
2563 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
2564 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
2565 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
2566 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
2567 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
2568 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
2572 ss << '\\' << t.cs();
2573 ss << '{' << name << '}';
2574 h_preamble << ss.str();
2579 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
2580 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
2581 || t.cs() == "providecommand" || t.cs() == "providecommandx"
2582 || t.cs() == "DeclareRobustCommand"
2583 || t.cs() == "DeclareRobustCommandx"
2584 || t.cs() == "ProvideTextCommandDefault"
2585 || t.cs() == "DeclareMathAccent") {
2587 if (p.next_token().character() == '*') {
2591 string const name = p.verbatim_item();
2592 string const opt1 = p.getFullOpt();
2593 string const opt2 = p.getFullOpt();
2594 string const body = p.verbatim_item();
2595 // store the in_lyx_preamble setting
2596 bool const was_in_lyx_preamble = in_lyx_preamble;
2598 if (name == "\\rmdefault")
2599 if (is_known(body, known_roman_font_packages)) {
2600 h_font_roman[0] = body;
2602 in_lyx_preamble = true;
2604 if (name == "\\sfdefault") {
2605 if (is_known(body, known_sans_font_packages)) {
2606 h_font_sans[0] = body;
2608 in_lyx_preamble = true;
2610 if (body == "LibertinusSans-OsF") {
2611 h_font_sans[0] = "LibertinusSans-LF";
2612 h_font_sans_osf = "true";
2614 in_lyx_preamble = true;
2617 if (name == "\\ttdefault")
2618 if (is_known(body, known_typewriter_font_packages)) {
2619 h_font_typewriter[0] = body;
2621 in_lyx_preamble = true;
2623 if (name == "\\familydefault") {
2624 string family = body;
2625 // remove leading "\"
2626 h_font_default_family = family.erase(0,1);
2628 in_lyx_preamble = true;
2630 if (name == "\\LibertinusSans@scale") {
2631 if (isStrDbl(body)) {
2632 h_font_sf_scale[0] = convert<string>(
2633 static_cast<int>(100 * convert<double>(body)));
2636 if (name == "\\LibertinusMono@scale") {
2637 if (isStrDbl(body)) {
2638 h_font_tt_scale[0] = convert<string>(
2639 static_cast<int>(100 * convert<double>(body)));
2643 // remove LyX-specific definitions that are re-added by LyX
2645 // \lyxline is an ancient command that is converted by tex2lyx into
2646 // a \rule therefore remove its preamble code
2647 if (name == "\\lyxdot" || name == "\\lyxarrow"
2648 || name == "\\lyxline" || name == "\\LyX") {
2650 in_lyx_preamble = true;
2653 // Add the command to the known commands
2654 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
2656 // only non-lyxspecific stuff
2657 if (!in_lyx_preamble) {
2659 ss << '\\' << t.cs();
2662 ss << '{' << name << '}' << opt1 << opt2
2663 << '{' << body << "}";
2664 if (prefixIs(t.cs(), "renew") || !contains(h_preamble.str(), ss.str()))
2665 h_preamble << ss.str();
2667 ostream & out = in_preamble ? h_preamble : os;
2668 out << "\\" << t.cs() << "{" << name << "}"
2669 << opts << "{" << body << "}";
2672 // restore the in_lyx_preamble setting
2673 in_lyx_preamble = was_in_lyx_preamble;
2677 if (t.cs() == "documentclass") {
2678 vector<string>::iterator it;
2679 vector<string> opts = split_options(p.getArg('[', ']'));
2680 // FIXME This does not work for classes that have a
2681 // different name in LyX than in LaTeX
2682 string const tclass = p.getArg('{', '}');
2684 // Only set text class if a class hasn't been forced
2685 // (this was set above)
2687 // textclass needs to be set at this place (if not already done)
2688 // as we need to know it for other parameters
2689 // (such as class-dependent paper size)
2690 setTextClass(tclass, tc);
2695 // Try those who are (most likely) known to all packages first
2696 handle_opt(opts, known_fontsizes, h_paperfontsize);
2697 delete_opt(opts, known_fontsizes);
2698 // delete "pt" at the end
2699 string::size_type i = h_paperfontsize.find("pt");
2700 if (i != string::npos)
2701 h_paperfontsize.erase(i);
2702 // Now those known specifically to the class
2703 vector<string> class_fsizes = getVectorFromString(tc.opt_fontsize(), "|");
2704 string const fsize_format = tc.fontsizeformat();
2705 for (auto const & fsize : class_fsizes) {
2706 string latexsize = subst(fsize_format, "$$s", fsize);
2707 vector<string>::iterator it = find(opts.begin(), opts.end(), latexsize);
2708 if (it != opts.end()) {
2709 h_paperfontsize = fsize;
2715 // The documentclass options are always parsed before the options
2716 // of the babel call so that a language cannot overwrite the babel
2718 handle_opt(opts, known_languages, h_language);
2719 delete_opt(opts, known_languages);
2722 if ((it = find(opts.begin(), opts.end(), "fleqn"))
2724 h_is_mathindent = "1";
2727 // formula numbering side
2728 if ((it = find(opts.begin(), opts.end(), "leqno"))
2730 h_math_numbering_side = "left";
2733 else if ((it = find(opts.begin(), opts.end(), "reqno"))
2735 h_math_numbering_side = "right";
2739 // paper orientation
2740 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
2741 h_paperorientation = "landscape";
2745 if ((it = find(opts.begin(), opts.end(), "oneside"))
2750 if ((it = find(opts.begin(), opts.end(), "twoside"))
2756 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
2758 h_papercolumns = "1";
2761 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
2763 h_papercolumns = "2";
2767 // some size options are known by the document class, other sizes
2768 // are handled by the \geometry command of the geometry package
2769 vector<string> class_psizes = getVectorFromString(tc.opt_pagesize(), "|");
2770 string const psize_format = tc.pagesizeformat();
2771 for (auto const & psize : class_psizes) {
2772 string latexsize = subst(psize_format, "$$s", psize);
2773 vector<string>::iterator it = find(opts.begin(), opts.end(), latexsize);
2774 if (it != opts.end()) {
2775 h_papersize = psize;
2779 if (psize_format == "$$spaper")
2781 // Also try with the default format since this is understood by
2783 latexsize = psize + "paper";
2784 it = find(opts.begin(), opts.end(), latexsize);
2785 if (it != opts.end()) {
2786 h_papersize = psize;
2791 // the remaining options
2792 h_options = join(opts, ",");
2796 if (t.cs() == "usepackage") {
2797 string const options = p.getArg('[', ']');
2798 string const name = p.getArg('{', '}');
2799 vector<string> vecnames;
2800 split(name, vecnames, ',');
2801 vector<string>::const_iterator it = vecnames.begin();
2802 vector<string>::const_iterator end = vecnames.end();
2803 for (; it != end; ++it)
2804 handle_package(p, trimSpaceAndEol(*it), options,
2805 in_lyx_preamble, detectEncoding);
2809 if (t.cs() == "inputencoding") {
2810 string const encoding = p.getArg('{','}');
2811 Encoding const * const enc = encodings.fromLaTeXName(
2812 encoding, Encoding::inputenc, true);
2814 if (!detectEncoding)
2815 cerr << "Unknown encoding " << encoding
2816 << ". Ignoring." << std::endl;
2819 h_inputencoding = enc->name();
2820 p.setEncoding(enc->iconvName());
2825 if (t.cs() == "newenvironment") {
2826 string const name = p.getArg('{', '}');
2827 string const opt1 = p.getFullOpt();
2828 string const opt2 = p.getFullOpt();
2829 string const beg = p.verbatim_item();
2830 string const end = p.verbatim_item();
2831 if (!in_lyx_preamble) {
2832 h_preamble << "\\newenvironment{" << name
2833 << '}' << opt1 << opt2 << '{'
2834 << beg << "}{" << end << '}';
2836 add_known_environment(name, opt1, !opt2.empty(),
2837 from_utf8(beg), from_utf8(end));
2841 if (t.cs() == "newtheorem") {
2843 if (p.next_token().character() == '*') {
2847 string const name = p.getArg('{', '}');
2848 string const opt1 = p.getFullOpt();
2849 string const opt2 = p.getFullOpt();
2850 string const body = p.verbatim_item();
2851 string const opt3 = p.getFullOpt();
2852 string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
2854 string const complete = cmd + "{" + name + '}' +
2855 opt1 + opt2 + '{' + body + '}' + opt3;
2857 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
2859 if (!in_lyx_preamble)
2860 h_preamble << complete;
2864 if (t.cs() == "def") {
2865 string name = p.get_token().cs();
2866 // In fact, name may be more than the name:
2867 // In the test case of bug 8116
2868 // name == "csname SF@gobble@opt \endcsname".
2869 // Therefore, we need to use asInput() instead of cs().
2870 while (p.next_token().cat() != catBegin)
2871 name += p.get_token().asInput();
2872 if (!in_lyx_preamble)
2873 h_preamble << "\\def\\" << name << '{'
2874 << p.verbatim_item() << "}";
2878 if (t.cs() == "newcolumntype") {
2879 string const name = p.getArg('{', '}');
2880 trimSpaceAndEol(name);
2882 string opts = p.getOpt();
2883 if (!opts.empty()) {
2884 istringstream is(string(opts, 1));
2887 special_columns_[name[0]] = nargs;
2888 h_preamble << "\\newcolumntype{" << name << "}";
2890 h_preamble << "[" << nargs << "]";
2891 h_preamble << "{" << p.verbatim_item() << "}";
2895 if (t.cs() == "setcounter") {
2896 string const name = p.getArg('{', '}');
2897 string const content = p.getArg('{', '}');
2898 if (name == "secnumdepth")
2899 h_secnumdepth = content;
2900 else if (name == "tocdepth")
2901 h_tocdepth = content;
2903 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
2907 if (t.cs() == "setlength") {
2908 string const name = p.verbatim_item();
2909 string const content = p.verbatim_item();
2910 // the paragraphs are only not indented when \parindent is set to zero
2911 if (name == "\\parindent" && content != "")
2912 h_paragraph_indentation = translate_len(content);
2913 else if (name == "\\parskip" && isPackageUsed("parskip")) {
2914 if (content == "\\smallskipamount")
2915 h_defskip = "smallskip";
2916 else if (content == "\\medskipamount")
2917 h_defskip = "medskip";
2918 else if (content == "\\bigskipamount")
2919 h_defskip = "bigskip";
2920 else if (content == "\\baselineskip")
2921 h_defskip = "fullline";
2923 h_defskip = translate_len(content);
2924 } else if (name == "\\mathindent") {
2925 h_mathindentation = translate_len(content);
2927 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2931 if (t.cs() == "onehalfspacing") {
2932 h_spacing = "onehalf";
2936 if (t.cs() == "doublespacing") {
2937 h_spacing = "double";
2941 if (t.cs() == "setstretch") {
2942 h_spacing = "other " + p.verbatim_item();
2946 if (t.cs() == "synctex") {
2947 // the scheme is \synctex=value
2948 // where value can only be "1" or "-1"
2949 h_output_sync = "1";
2950 // there can be any character behind the value (e.g. a linebreak or a '\'
2951 // therefore we extract it char by char
2953 string value = p.get_token().asInput();
2955 value += p.get_token().asInput();
2956 h_output_sync_macro = "\\synctex=" + value;
2960 if (t.cs() == "begin") {
2961 string const name = p.getArg('{', '}');
2962 if (name == "document")
2964 h_preamble << "\\begin{" << name << "}";
2968 if (t.cs() == "geometry") {
2969 vector<string> opts = split_options(p.getArg('{', '}'));
2970 handle_geometry(opts);
2974 if (t.cs() == "definecolor") {
2975 string const color = p.getArg('{', '}');
2976 string const space = p.getArg('{', '}');
2977 string const value = p.getArg('{', '}');
2978 if (color == "document_fontcolor" && space == "rgb") {
2979 RGBColor c(RGBColorFromLaTeX(value));
2980 h_fontcolor = X11hexname(c);
2981 } else if (color == "note_fontcolor" && space == "rgb") {
2982 RGBColor c(RGBColorFromLaTeX(value));
2983 h_notefontcolor = X11hexname(c);
2984 } else if (color == "page_backgroundcolor" && space == "rgb") {
2985 RGBColor c(RGBColorFromLaTeX(value));
2986 h_backgroundcolor = X11hexname(c);
2987 } else if (color == "shadecolor" && space == "rgb") {
2988 RGBColor c(RGBColorFromLaTeX(value));
2989 h_boxbgcolor = X11hexname(c);
2991 h_preamble << "\\definecolor{" << color
2992 << "}{" << space << "}{" << value
2998 if (t.cs() == "bibliographystyle") {
2999 h_biblio_style = p.verbatim_item();
3003 if (t.cs() == "jurabibsetup") {
3004 // FIXME p.getArg('{', '}') is most probably wrong (it
3005 // does not handle nested braces).
3006 // Use p.verbatim_item() instead.
3007 vector<string> jurabibsetup =
3008 split_options(p.getArg('{', '}'));
3009 // add jurabibsetup to the jurabib package options
3010 add_package("jurabib", jurabibsetup);
3011 if (!jurabibsetup.empty()) {
3012 h_preamble << "\\jurabibsetup{"
3013 << join(jurabibsetup, ",") << '}';
3018 if (t.cs() == "hypersetup") {
3019 vector<string> hypersetup =
3020 split_options(p.verbatim_item());
3021 // add hypersetup to the hyperref package options
3022 handle_hyperref(hypersetup);
3023 if (!hypersetup.empty()) {
3024 h_preamble << "\\hypersetup{"
3025 << join(hypersetup, ",") << '}';
3030 if (t.cs() == "includeonly") {
3031 vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
3032 for (auto & iofile : includeonlys) {
3033 string filename(normalize_filename(iofile));
3034 string const path = getMasterFilePath(true);
3035 // We want to preserve relative/absolute filenames,
3036 // therefore path is only used for testing
3037 if (!makeAbsPath(filename, path).exists()) {
3038 // The file extension is probably missing.
3039 // Now try to find it out.
3040 string const tex_name =
3041 find_file(filename, path,
3042 known_tex_extensions);
3043 if (!tex_name.empty())
3044 filename = tex_name;
3047 if (makeAbsPath(filename, path).exists())
3048 fix_child_filename(filename);
3050 cerr << "Warning: Could not find included file '"
3051 << filename << "'." << endl;
3052 outname = changeExtension(filename, "lyx");
3053 h_includeonlys.push_back(outname);
3058 if (is_known(t.cs(), known_if_3arg_commands)) {
3059 // prevent misparsing of \usepackage if it is used
3060 // as an argument (see e.g. our own output of
3061 // \@ifundefined above)
3062 string const arg1 = p.verbatim_item();
3063 string const arg2 = p.verbatim_item();
3064 string const arg3 = p.verbatim_item();
3065 // test case \@ifundefined{date}{}{\date{}}
3066 if (t.cs() == "@ifundefined" && arg1 == "date" &&
3067 arg2.empty() && arg3 == "\\date{}") {
3068 h_suppress_date = "true";
3069 // older tex2lyx versions did output
3070 // \@ifundefined{definecolor}{\usepackage{color}}{}
3071 } else if (t.cs() == "@ifundefined" &&
3072 arg1 == "definecolor" &&
3073 arg2 == "\\usepackage{color}" &&
3075 if (!in_lyx_preamble)
3076 h_preamble << package_beg_sep
3079 << "\\@ifundefined{definecolor}{color}{}"
3082 //\@ifundefined{showcaptionsetup}{}{%
3083 // \PassOptionsToPackage{caption=false}{subfig}}
3084 // that LyX uses for subfloats
3085 } else if (t.cs() == "@ifundefined" &&
3086 arg1 == "showcaptionsetup" && arg2.empty()
3087 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
3089 } else if (!in_lyx_preamble) {
3090 h_preamble << t.asInput()
3091 << '{' << arg1 << '}'
3092 << '{' << arg2 << '}'
3093 << '{' << arg3 << '}';
3098 if (is_known(t.cs(), known_if_commands)) {
3099 // must not parse anything in conditional code, since
3100 // LyX would output the parsed contents unconditionally
3101 if (!in_lyx_preamble)
3102 h_preamble << t.asInput();
3103 handle_if(p, in_lyx_preamble);
3107 if (!t.cs().empty() && !in_lyx_preamble) {
3108 h_preamble << '\\' << t.cs();
3113 // set textclass if not yet done (snippets without \documentclass and forced class)
3115 setTextClass(h_textclass, tc);
3117 // remove the whitespace
3120 if (h_papersides.empty()) {
3123 h_papersides = ss.str();
3126 // If the CJK package is used we cannot set the document language from
3127 // the babel options. Instead, we guess which language is used most
3128 // and set this one.
3129 default_language = h_language;
3130 if (is_full_document &&
3131 (auto_packages.find("CJK") != auto_packages.end() ||
3132 auto_packages.find("CJKutf8") != auto_packages.end())) {
3134 h_language = guessLanguage(p, default_language);
3136 if (explicit_babel && h_language != default_language) {
3137 // We set the document language to a CJK language,
3138 // but babel is explicitly called in the user preamble
3139 // without options. LyX will not add the default
3140 // language to the document options if it is either
3141 // english, or no text is set as default language.
3142 // Therefore we need to add a language option explicitly.
3143 // FIXME: It would be better to remove all babel calls
3144 // from the user preamble, but this is difficult
3145 // without re-introducing bug 7861.
3146 if (h_options.empty())
3147 h_options = lyx2babel(default_language);
3149 h_options += ',' + lyx2babel(default_language);
3153 // Finally, set the quote style.
3154 // LyX knows the following quotes styles:
3155 // british, cjk, cjkangle, danish, english, french, german,
3156 // polish, russian, swedish and swiss
3157 // conversion list taken from
3158 // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
3159 // (quotes for kazakh are unknown)
3161 if (is_known(h_language, known_british_quotes_languages))
3162 h_quotes_style = "british";
3164 else if (is_known(h_language, known_cjk_quotes_languages))
3165 h_quotes_style = "cjk";
3167 else if (is_known(h_language, known_cjkangle_quotes_languages))
3168 h_quotes_style = "cjkangle";
3170 else if (is_known(h_language, known_danish_quotes_languages))
3171 h_quotes_style = "danish";
3173 else if (is_known(h_language, known_french_quotes_languages))
3174 h_quotes_style = "french";
3176 else if (is_known(h_language, known_german_quotes_languages))
3177 h_quotes_style = "german";
3179 else if (is_known(h_language, known_polish_quotes_languages))
3180 h_quotes_style = "polish";
3182 else if (is_known(h_language, known_hungarian_quotes_languages))
3183 h_quotes_style = "hungarian";
3185 else if (is_known(h_language, known_russian_quotes_languages))
3186 h_quotes_style = "russian";
3188 else if (is_known(h_language, known_swedish_quotes_languages))
3189 h_quotes_style = "swedish";
3191 else if (is_known(h_language, known_swiss_quotes_languages))
3192 h_quotes_style = "swiss";
3194 else if (is_known(h_language, known_english_quotes_languages))
3195 h_quotes_style = "english";
3199 string Preamble::parseEncoding(Parser & p, string const & forceclass)
3201 TeX2LyXDocClass dummy;
3202 parse(p, forceclass, true, dummy);
3203 if (h_inputencoding != "auto-legacy" && h_inputencoding != "auto-legacy-plain")
3204 return h_inputencoding;
3209 string babel2lyx(string const & language)
3211 char const * const * where = is_known(language, known_languages);
3213 return known_coded_languages[where - known_languages];
3218 string lyx2babel(string const & language)
3220 char const * const * where = is_known(language, known_coded_languages);
3222 return known_languages[where - known_coded_languages];
3227 string Preamble::polyglossia2lyx(string const & language)
3229 char const * const * where = is_known(language, polyglossia_languages);
3231 return coded_polyglossia_languages[where - polyglossia_languages];
3236 string rgbcolor2code(string const & name)
3238 char const * const * where = is_known(name, known_basic_colors);
3240 // "red", "green" etc
3241 return known_basic_color_codes[where - known_basic_colors];
3243 // "255,0,0", "0,255,0" etc
3244 RGBColor c(RGBColorFromLaTeX(name));
3245 return X11hexname(c);