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