3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
9 * Full author contact details are available in file CREDITS.
20 #include "LaTeXPackages.h"
21 #include "LayoutFile.h"
23 #include "TextClass.h"
26 #include "support/convert.h"
27 #include "support/FileName.h"
28 #include "support/filetools.h"
29 #include "support/lstrings.h"
36 using namespace lyx::support;
45 // CJK languages are handled in text.cpp, polyglossia languages are listed
48 * known babel language names (including synonyms)
49 * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
50 * please keep this in sync with known_coded_languages line by line!
52 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
53 "american", "arabic", "arabtex", "australian", "austrian", "azerbaijani", "bahasa", "bahasai",
54 "bahasam", "basque", "belarusian", "bosnian", "brazil", "brazilian", "breton", "british",
55 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
56 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "francais",
57 "french", "frenchb", "frenchle", "frenchpro", "friulan", "galician", "german", "germanb",
58 "georgian", "greek", "hebrew", "hungarian", "icelandic", "indon", "indonesian",
59 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
60 "latvian", "lithuanian", "lowersorbian", "lsorbian", "macedonian", "magyar", "malay", "meyalu",
61 "mongolian", "naustrian", "newzealand", "ngerman", "ngermanb", "norsk", "nswissgerman",
62 "nynorsk", "piedmontese", "polutonikogreek", "polish", "portuges", "portuguese",
63 "romanian", "romansh", "russian", "russianb", "samin", "scottish", "serbian", "serbian-latin",
64 "slovak", "slovene", "spanish", "swedish", "swissgerman", "thai", "turkish", "turkmen",
65 "ukraineb", "ukrainian", "uppersorbian", "UKenglish", "USenglish", "usorbian",
70 * the same as known_languages with .lyx names
71 * please keep this in sync with known_languages line by line!
73 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
74 "american", "arabic_arabi", "arabic_arabtex", "australian", "austrian", "azerbaijani", "bahasa", "bahasa",
75 "bahasam", "basque", "belarusian", "bosnian", "brazilian", "brazilian", "breton", "british",
76 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
77 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "french",
78 "french", "french", "french", "french", "friulan", "galician", "german", "german",
79 "georgian", "greek", "hebrew", "magyar", "icelandic", "bahasa", "bahasa",
80 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
81 "latvian", "lithuanian", "lowersorbian", "lowersorbian", "macedonian", "magyar", "bahasam", "bahasam",
82 "mongolian", "naustrian", "newzealand", "ngerman", "ngerman", "norsk", "german-ch",
83 "nynorsk", "piedmontese", "polutonikogreek", "polish", "portuguese", "portuguese",
84 "romanian", "romansh", "russian", "russian", "samin", "scottish", "serbian", "serbian-latin",
85 "slovak", "slovene", "spanish", "swedish", "german-ch-old", "thai", "turkish", "turkmen",
86 "ukrainian", "ukrainian", "uppersorbian", "english", "english", "uppersorbian",
87 "vietnamese", "welsh",
90 /// languages with british quotes (.lyx names)
91 const char * const known_british_quotes_languages[] = {"british", "welsh", 0};
93 /// languages with cjk quotes (.lyx names)
94 const char * const known_cjk_quotes_languages[] = {"chinese-traditional",
95 "japanese", "japanese-cjk", 0};
97 /// languages with cjk-angle quotes (.lyx names)
98 const char * const known_cjkangle_quotes_languages[] = {"korean", 0};
100 /// languages with danish quotes (.lyx names)
101 const char * const known_danish_quotes_languages[] = {"danish", 0};
103 /// languages with english quotes (.lyx names)
104 const char * const known_english_quotes_languages[] = {"american", "australian",
105 "bahasa", "bahasam", "bengali", "brazilian", "canadian", "chinese-simplified", "english",
106 "esperanto", "farsi", "interlingua", "irish", "newzealand", "scottish",
107 "thai", "turkish", "vietnamese", 0};
109 /// languages with french quotes (.lyx names)
110 const char * const known_french_quotes_languages[] = {"ancientgreek",
111 "arabic_arabi", "arabic_arabtex", "asturian", "belarusian", "breton",
112 "canadien", "catalan", "french", "friulan", "galician", "italian", "occitan",
113 "piedmontese", "portuguese", "spanish", "spanish-mexico", 0};
115 /// languages with german quotes (.lyx names)
116 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
117 "czech", "estonian", "georgian", "german", "icelandic", "latvian", "lithuanian",
118 "lowersorbian", "macedonian", "naustrian", "ngerman", "romansh", "slovak", "slovene",
121 /// languages with polish quotes (.lyx names)
122 const char * const known_polish_quotes_languages[] = {"afrikaans", "bosnian", "croatian",
123 "dutch", "magyar", "polish", "romanian", "serbian", "serbian-latin", 0};
125 /// languages with hungarian quotes (.lyx names)
126 const char * const known_hungarian_quotes_languages[] = {"magyar", 0};
128 /// languages with russian quotes (.lyx names)
129 const char * const known_russian_quotes_languages[] = {"azerbaijani", "oldrussian",
130 "russian", "ukrainian", 0};
132 /// languages with swedish quotes (.lyx names)
133 const char * const known_swedish_quotes_languages[] = {"finnish", "swedish", 0};
135 /// languages with swiss quotes (.lyx names)
136 const char * const known_swiss_quotes_languages[] = {"albanian",
137 "armenian", "basque", "churchslavonic", "german-ch", "german-ch-old",
138 "norsk", "nynorsk", "turkmen", "ukrainian", "vietnamese", 0};
140 /// known language packages from the times before babel
141 const char * const known_old_language_packages[] = {"french", "frenchle",
142 "frenchpro", "german", "ngerman", "pmfrench", 0};
144 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
146 const char * const known_roman_font_packages[] = { "ae", "beraserif", "bookman",
147 "ccfonts", "chancery", "charter", "cmr", "cochineal", "crimson", "CrimsonPro", "DejaVuSerif",
148 "DejaVuSerifCondensed", "fourier", "garamondx", "libertine", "libertineRoman", "libertine-type1",
149 "lmodern", "mathdesign", "mathpazo", "mathptmx", "MinionPro", "newcent", "noto", "noto-serif",
150 "PTSerif", "tgbonum", "tgchorus", "tgpagella", "tgschola", "tgtermes", "utopia", "xcharter", 0 };
152 const char * const known_sans_font_packages[] = { "avant", "berasans", "biolinum",
153 "biolinum-type1", "cantarell", "Chivo", "cmbr", "cmss", "DejaVuSans", "DejaVuSansCondensed", "FiraSans", "helvet", "iwona",
154 "iwonac", "iwonal", "iwonalc", "kurier", "kurierc", "kurierl", "kurierlc", "LibertinusSans-LF", "lmss", "noto-sans", "PTSans",
155 "tgadventor", "tgheros", "uop", 0 };
157 const char * const known_typewriter_font_packages[] = { "beramono", "cmtl", "cmtt", "courier", "DejaVuSansMono",
158 "FiraMono", "lmtt", "luximono", "libertineMono", "libertineMono-type1", "LibertinusMono-TLF", "lmodern",
159 "mathpazo", "mathptmx", "newcent", "noto-mono", "PTMono", "tgcursor", "txtt", 0 };
161 const char * const known_math_font_packages[] = { "eulervm", "newtxmath", 0};
163 const char * const known_latex_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
164 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
165 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
166 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
167 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
169 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
170 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
172 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
173 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
176 /// commands that can start an \if...\else...\endif sequence
177 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
178 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
179 "ifsidecap", "ifupgreek", 0};
181 const char * const known_basic_colors[] = {"black", "blue", "brown", "cyan",
182 "darkgray", "gray", "green", "lightgray", "lime", "magenta", "orange", "olive",
183 "pink", "purple", "red", "teal", "violet", "white", "yellow", 0};
185 const char * const known_basic_color_codes[] = {"#000000", "#0000ff", "#964B00", "#00ffff",
186 "#a9a9a9", "#808080", "#00ff00", "#d3d3d3", "#bfff00", "#ff00ff", "#ff7f00", "#808000",
187 "#ffc0cb", "#800080", "#ff0000", "#008080", "#8f00ff", "#ffffff", "#ffff00", 0};
189 /// conditional commands with three arguments like \@ifundefined{}{}{}
190 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
194 * Known file extensions for TeX files as used by \\includeonly
196 char const * const known_tex_extensions[] = {"tex", 0};
198 /// packages that work only in xetex
199 /// polyglossia is handled separately
200 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
201 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
202 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
204 /// packages that are automatically skipped if loaded by LyX
205 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
206 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color",
207 "float", "fontspec", "framed", "graphicx", "hhline", "ifthen", "longtable",
208 "makeidx", "minted", "multirow", "nomencl", "parskip", "pdfpages", "prettyref", "refstyle",
209 "rotating", "rotfloat", "splitidx", "setspace", "subscript", "tabularx","textcomp", "tipa",
210 "tipx", "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xcolor", "xltabular",
213 // codes used to remove packages that are loaded automatically by LyX.
214 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
215 const char package_beg_sep = '\001';
216 const char package_mid_sep = '\002';
217 const char package_end_sep = '\003';
220 // returns true if at least one of the options in what has been found
221 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
227 // the last language option is the document language (for babel and LyX)
228 // the last size option is the document font size
229 vector<string>::iterator it;
230 vector<string>::iterator position = opts.begin();
231 for (; *what; ++what) {
232 it = find(opts.begin(), opts.end(), *what);
233 if (it != opts.end()) {
234 if (it >= position) {
245 void delete_opt(vector<string> & opts, char const * const * what)
250 // remove found options from the list
251 // do this after handle_opt to avoid potential memory leaks
252 vector<string>::iterator it;
253 for (; *what; ++what) {
254 it = find(opts.begin(), opts.end(), *what);
255 if (it != opts.end())
262 * Split a package options string (keyval format) into a vector.
264 * authorformat=smallcaps,
266 * titleformat=colonsep,
267 * bibformat={tabular,ibidem,numbered}
269 vector<string> split_options(string const & input)
271 vector<string> options;
275 Token const & t = p.get_token();
276 if (t.asInput() == ",") {
277 options.push_back(trimSpaceAndEol(option));
279 } else if (t.asInput() == "=") {
282 if (p.next_token().asInput() == "{")
283 option += '{' + p.getArg('{', '}') + '}';
284 } else if (t.cat() != catSpace && t.cat() != catComment)
285 option += t.asInput();
289 options.push_back(trimSpaceAndEol(option));
296 * Retrieve a keyval option "name={value with=sign}" named \p name from
297 * \p options and return the value.
298 * The found option is also removed from \p options.
300 string process_keyval_opt(vector<string> & options, string const & name)
302 for (size_t i = 0; i < options.size(); ++i) {
303 vector<string> option;
304 split(options[i], option, '=');
305 if (option.size() < 2)
307 if (option[0] == name) {
308 options.erase(options.begin() + i);
309 option.erase(option.begin());
310 return join(option, "=");
316 } // anonymous namespace
320 * known polyglossia language names (including variants)
321 * FIXME: support spelling=old for german variants (german vs. ngerman LyX names etc)
323 const char * const Preamble::polyglossia_languages[] = {
324 "albanian", "american", "amharic", "ancient", "arabic", "armenian", "asturian", "australian",
325 "bahasai", "bahasam", "basque", "bengali", "brazil", "brazilian", "breton", "british", "bulgarian",
326 "catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
327 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
328 "galician", "greek", "monotonic", "hebrew", "hindi",
329 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean",
330 "lao", "latin", "latvian", "lithuanian", "lsorbian", "magyar", "malayalam", "marathi",
331 "austrian", "newzealand", "german", "norsk", "nynorsk", "occitan", "oldrussian",
332 "piedmontese", "polish", "polytonic", "portuges", "romanian", "romansh", "russian",
333 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovenian", "spanish", "swedish", "syriac",
334 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
335 "ukrainian", "urdu", "usorbian", "vietnamese", "welsh", 0};
336 // not yet supported by LyX: "korean", "nko"
339 * the same as polyglossia_languages with .lyx names
340 * please keep this in sync with polyglossia_languages line by line!
342 const char * const Preamble::coded_polyglossia_languages[] = {
343 "albanian", "american", "amharic", "ancientgreek", "arabic_arabi", "armenian", "asturian", "australian",
344 "bahasa", "bahasam", "basque", "bengali", "brazilian", "brazilian", "breton", "british", "bulgarian",
345 "catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
346 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
347 "galician", "greek", "greek", "hebrew", "hindi",
348 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean",
349 "lao", "latin", "latvian", "lithuanian", "lowersorbian", "magyar", "malayalam", "marathi",
350 "naustrian","newzealand", "ngerman", "norsk", "nynorsk", "occitan", "oldrussian",
351 "piedmontese", "polish", "polutonikogreek", "portuges", "romanian", "romansh", "russian",
352 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovene", "spanish", "swedish", "syriac",
353 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
354 "ukrainian", "urdu", "uppersorbian", "vietnamese", "welsh", 0};
355 // not yet supported by LyX: "korean-polyglossia", "nko"
358 bool Preamble::usePolyglossia() const
360 return h_use_non_tex_fonts && h_language_package == "default";
364 bool Preamble::indentParagraphs() const
366 return h_paragraph_separation == "indent";
370 bool Preamble::isPackageUsed(string const & package) const
372 return used_packages.find(package) != used_packages.end();
376 bool Preamble::isPackageAutoLoaded(string const & package) const
378 return auto_packages.find(package) != auto_packages.end();
382 vector<string> Preamble::getPackageOptions(string const & package) const
384 map<string, vector<string> >::const_iterator it = used_packages.find(package);
385 if (it != used_packages.end())
387 return vector<string>();
391 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
393 auto_packages.insert(package);
397 void Preamble::addModule(string const & module)
399 for (auto const & m : used_modules) {
403 used_modules.push_back(module);
407 void Preamble::suppressDate(bool suppress)
410 h_suppress_date = "true";
412 h_suppress_date = "false";
416 void Preamble::registerAuthor(std::string const & name, string const & initials)
418 Author author(from_utf8(name), empty_docstring(), from_utf8(initials));
419 author.setUsed(true);
420 authors_.record(author);
421 h_tracking_changes = "true";
422 h_output_changes = "true";
426 Author const & Preamble::getAuthor(std::string const & name) const
428 Author author(from_utf8(name), empty_docstring(), empty_docstring());
429 for (AuthorList::Authors::const_iterator it = authors_.begin();
430 it != authors_.end(); ++it)
433 static Author const dummy;
438 int Preamble::getSpecialTableColumnArguments(char c) const
440 map<char, int>::const_iterator it = special_columns_.find(c);
441 if (it == special_columns_.end())
447 void Preamble::add_package(string const & name, vector<string> & options)
449 // every package inherits the global options
450 used_packages.insert({name, split_options(h_options)});
452 // Insert options passed via PassOptionsToPackage
453 for (auto const & p : extra_package_options_) {
454 if (p.first == name) {
455 vector<string> eo = getVectorFromString(p.second);
456 for (auto const & eoi : eo)
457 options.push_back(eoi);
461 vector<string> & v = used_packages[name];
462 v.insert(v.end(), options.begin(), options.end());
463 if (name == "jurabib") {
464 // Don't output the order argument (see the cite command
465 // handling code in text.cpp).
466 vector<string>::iterator end =
467 remove(options.begin(), options.end(), "natbiborder");
468 end = remove(options.begin(), end, "jurabiborder");
469 options.erase(end, options.end());
473 void Preamble::setTextClass(string const & tclass, TeX2LyXDocClass & tc)
475 h_textclass = tclass;
476 tc.setName(h_textclass);
477 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
478 error_message("Could not read layout file for textclass \"" + h_textclass + "\".");
486 // Given is a string like "scaled=0.9" or "scale=0.9", return 0.9 * 100
487 bool scale_as_percentage(string const & scale, string & percentage)
489 if (contains(scale, '=')) {
490 string const value = support::split(scale, '=');
491 if (isStrDbl(value)) {
492 percentage = convert<string>(
493 static_cast<int>(100 * convert<double>(value)));
501 string remove_braces(string const & value)
505 if (value[0] == '{' && value[value.length()-1] == '}')
506 return value.substr(1, value.length()-2);
510 } // anonymous namespace
513 Preamble::Preamble() : one_language(true), explicit_babel(false),
514 title_layout_found(false), index_number(0), h_font_cjk_set(false)
518 h_biblio_style = "plain";
519 h_bibtex_command = "default";
520 h_cite_engine = "basic";
521 h_cite_engine_type = "default";
523 h_defskip = "medskip";
524 h_dynamic_quotes = false;
527 h_fontencoding = "default";
528 h_font_roman[0] = "default";
529 h_font_roman[1] = "default";
530 h_font_sans[0] = "default";
531 h_font_sans[1] = "default";
532 h_font_typewriter[0] = "default";
533 h_font_typewriter[1] = "default";
534 h_font_math[0] = "auto";
535 h_font_math[1] = "auto";
536 h_font_default_family = "default";
537 h_use_non_tex_fonts = false;
539 h_font_roman_osf = "false";
540 h_font_sans_osf = "false";
541 h_font_typewriter_osf = "false";
542 h_font_sf_scale[0] = "100";
543 h_font_sf_scale[1] = "100";
544 h_font_tt_scale[0] = "100";
545 h_font_tt_scale[1] = "100";
546 // h_font_roman_opts;
548 // h_font_typewriter_opts;
550 h_is_mathindent = "0";
551 h_math_numbering_side = "default";
552 h_graphics = "default";
553 h_default_output_format = "default";
554 h_html_be_strict = "false";
555 h_html_css_as_file = "0";
556 h_html_math_output = "0";
557 h_docbook_table_output = "0";
558 h_docbook_mathml_prefix = "1";
559 h_index[0] = "Index";
560 h_index_command = "default";
561 h_inputencoding = "auto-legacy";
562 h_justification = "true";
563 h_language = "english";
564 h_language_package = "none";
566 h_maintain_unincluded_children = "no";
570 h_output_changes = "false";
571 h_change_bars = "false";
573 //h_output_sync_macro
574 h_papercolumns = "1";
575 h_paperfontsize = "default";
576 h_paperorientation = "portrait";
577 h_paperpagestyle = "default";
579 h_papersize = "default";
580 h_paragraph_indentation = "default";
581 h_paragraph_separation = "indent";
586 h_pdf_bookmarks = "0";
587 h_pdf_bookmarksnumbered = "0";
588 h_pdf_bookmarksopen = "0";
589 h_pdf_bookmarksopenlevel = "1";
590 h_pdf_breaklinks = "0";
591 h_pdf_pdfborder = "0";
592 h_pdf_colorlinks = "0";
593 h_pdf_backref = "section";
594 h_pdf_pdfusetitle = "0";
596 //h_pdf_quoted_options;
597 h_quotes_style = "english";
599 h_shortcut[0] = "idx";
600 h_spacing = "single";
601 h_save_transient_properties = "true";
602 h_suppress_date = "false";
603 h_textclass = "article";
605 h_tracking_changes = "false";
606 h_use_bibtopic = "false";
607 h_use_dash_ligatures = "true";
608 h_use_indices = "false";
609 h_use_geometry = "false";
610 h_use_default_options = "false";
611 h_use_hyperref = "false";
612 h_use_microtype = "false";
613 h_use_lineno = "false";
614 h_use_refstyle = false;
615 h_use_minted = false;
616 h_use_packages["amsmath"] = "1";
617 h_use_packages["amssymb"] = "0";
618 h_use_packages["cancel"] = "0";
619 h_use_packages["esint"] = "1";
620 h_use_packages["mhchem"] = "0";
621 h_use_packages["mathdots"] = "0";
622 h_use_packages["mathtools"] = "0";
623 h_use_packages["stackrel"] = "0";
624 h_use_packages["stmaryrd"] = "0";
625 h_use_packages["undertilde"] = "0";
629 void Preamble::handle_hyperref(vector<string> & options)
631 // FIXME swallow inputencoding changes that might surround the
632 // hyperref setup if it was written by LyX
633 h_use_hyperref = "true";
634 // swallow "unicode=true", since LyX does always write that
635 vector<string>::iterator it =
636 find(options.begin(), options.end(), "unicode=true");
637 if (it != options.end())
639 it = find(options.begin(), options.end(), "pdfusetitle");
640 if (it != options.end()) {
641 h_pdf_pdfusetitle = "1";
644 string bookmarks = process_keyval_opt(options, "bookmarks");
645 if (bookmarks == "true")
646 h_pdf_bookmarks = "1";
647 else if (bookmarks == "false")
648 h_pdf_bookmarks = "0";
649 if (h_pdf_bookmarks == "1") {
650 string bookmarksnumbered =
651 process_keyval_opt(options, "bookmarksnumbered");
652 if (bookmarksnumbered == "true")
653 h_pdf_bookmarksnumbered = "1";
654 else if (bookmarksnumbered == "false")
655 h_pdf_bookmarksnumbered = "0";
656 string bookmarksopen =
657 process_keyval_opt(options, "bookmarksopen");
658 if (bookmarksopen == "true")
659 h_pdf_bookmarksopen = "1";
660 else if (bookmarksopen == "false")
661 h_pdf_bookmarksopen = "0";
662 if (h_pdf_bookmarksopen == "1") {
663 string bookmarksopenlevel =
664 process_keyval_opt(options, "bookmarksopenlevel");
665 if (!bookmarksopenlevel.empty())
666 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
669 string breaklinks = process_keyval_opt(options, "breaklinks");
670 if (breaklinks == "true")
671 h_pdf_breaklinks = "1";
672 else if (breaklinks == "false")
673 h_pdf_breaklinks = "0";
674 string pdfborder = process_keyval_opt(options, "pdfborder");
675 if (pdfborder == "{0 0 0}")
676 h_pdf_pdfborder = "1";
677 else if (pdfborder == "{0 0 1}")
678 h_pdf_pdfborder = "0";
679 string backref = process_keyval_opt(options, "backref");
680 if (!backref.empty())
681 h_pdf_backref = backref;
682 string colorlinks = process_keyval_opt(options, "colorlinks");
683 if (colorlinks == "true")
684 h_pdf_colorlinks = "1";
685 else if (colorlinks == "false")
686 h_pdf_colorlinks = "0";
687 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
688 if (!pdfpagemode.empty())
689 h_pdf_pagemode = pdfpagemode;
690 string pdftitle = process_keyval_opt(options, "pdftitle");
691 if (!pdftitle.empty()) {
692 h_pdf_title = remove_braces(pdftitle);
694 string pdfauthor = process_keyval_opt(options, "pdfauthor");
695 if (!pdfauthor.empty()) {
696 h_pdf_author = remove_braces(pdfauthor);
698 string pdfsubject = process_keyval_opt(options, "pdfsubject");
699 if (!pdfsubject.empty())
700 h_pdf_subject = remove_braces(pdfsubject);
701 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
702 if (!pdfkeywords.empty())
703 h_pdf_keywords = remove_braces(pdfkeywords);
704 if (!options.empty()) {
705 if (!h_pdf_quoted_options.empty())
706 h_pdf_quoted_options += ',';
707 h_pdf_quoted_options += join(options, ",");
713 void Preamble::handle_geometry(vector<string> & options)
715 h_use_geometry = "true";
716 vector<string>::iterator it;
718 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
719 h_paperorientation = "landscape";
723 // keyval version: "paper=letter" or "paper=letterpaper"
724 string paper = process_keyval_opt(options, "paper");
726 if (suffixIs(paper, "paper"))
727 paper = subst(paper, "paper", "");
728 // alternative version: "letterpaper"
729 handle_opt(options, known_latex_paper_sizes, paper);
730 if (suffixIs(paper, "paper"))
731 paper = subst(paper, "paper", "");
732 delete_opt(options, known_latex_paper_sizes);
736 char const * const * margin = known_paper_margins;
737 for (; *margin; ++margin) {
738 string value = process_keyval_opt(options, *margin);
739 if (!value.empty()) {
740 int k = margin - known_paper_margins;
741 string name = known_coded_paper_margins[k];
742 h_margins += '\\' + name + ' ' + value + '\n';
748 void Preamble::handle_package(Parser &p, string const & name,
749 string const & opts, bool in_lyx_preamble,
752 vector<string> options = split_options(opts);
753 add_package(name, options);
755 if (is_known(name, known_xetex_packages)) {
757 h_use_non_tex_fonts = true;
758 registerAutomaticallyLoadedPackage("fontspec");
759 if (h_inputencoding == "auto-legacy")
760 p.setEncoding("UTF-8");
761 } else if (h_inputencoding == "auto-legacy"
762 && LaTeXPackages::isAvailableAtLeastFrom("LaTeX", 2018, 04))
763 // As of LaTeX 2018/04/01, utf8 is the default input encoding
764 // So use that if no inputencoding is set
765 h_inputencoding = "utf8";
767 // vector of all options for easier parsing and
769 vector<string> allopts = getVectorFromString(opts);
770 // this stores the potential extra options
777 // By default, we use the package name as LyX font name,
778 // so this only needs to be reset if these names differ
779 if (is_known(name, known_roman_font_packages))
780 h_font_roman[0] = name;
782 if (name == "ccfonts") {
783 for (auto const & opt : allopts) {
789 h_font_roman_opts = xopts;
793 if (name == "lmodern") {
794 for (auto const & opt : allopts) {
800 h_font_roman_opts = xopts;
804 if (name == "fourier") {
805 h_font_roman[0] = "utopia";
806 for (auto const & opt : allopts) {
808 h_font_roman_osf = "true";
811 if (opt == "expert") {
820 h_font_roman_opts = xopts;
824 if (name == "garamondx") {
825 for (auto const & opt : allopts) {
827 h_font_roman_osf = "true";
835 h_font_roman_opts = xopts;
839 if (name == "libertine") {
840 // this automatically invokes biolinum
841 h_font_sans[0] = "biolinum";
842 // as well as libertineMono
843 h_font_typewriter[0] = "libertine-mono";
844 for (auto const & opt : allopts) {
846 h_font_roman_osf = "true";
849 if (opt == "lining") {
850 h_font_roman_osf = "false";
858 h_font_roman_opts = xopts;
862 if (name == "libertineRoman" || name == "libertine-type1") {
863 h_font_roman[0] = "libertine";
864 // NOTE: contrary to libertine.sty, libertineRoman
865 // and libertine-type1 do not automatically invoke
866 // biolinum and libertineMono
867 if (opts == "lining")
868 h_font_roman_osf = "false";
869 else if (opts == "osf")
870 h_font_roman_osf = "true";
873 if (name == "libertinus" || name == "libertinus-type1") {
880 for (auto const & opt : allopts) {
881 if (opt == "rm" || opt == "serif") {
886 if (opt == "sf" || opt == "sans") {
891 if (opt == "tt=false" || opt == "mono=false") {
899 if (opt == "scaleSF") {
903 if (opt == "scaleTT") {
907 if (opt == "lining") {
908 h_font_roman_osf = "false";
916 h_font_roman[0] = "libertinus";
918 h_font_roman_osf = "true";
920 h_font_roman_osf = "false";
923 h_font_sans[0] = "LibertinusSans-LF";
925 h_font_sans_osf = "true";
927 h_font_sans_osf = "false";
928 if (!scalesf.empty())
929 scale_as_percentage(scalesf, h_font_sf_scale[0]);
932 h_font_typewriter[0] = "LibertinusMono-TLF";
933 if (!scalett.empty())
934 scale_as_percentage(scalett, h_font_tt_scale[0]);
937 h_font_roman_opts = xopts;
941 if (name == "MinionPro") {
942 h_font_roman[0] = "minionpro";
943 h_font_roman_osf = "true";
944 h_font_math[0] = "auto";
945 for (auto const & opt : allopts) {
947 h_font_roman_osf = "false";
950 if (opt == "onlytext") {
951 h_font_math[0] = "default";
959 h_font_roman_opts = xopts;
963 if (name == "mathdesign") {
964 for (auto const & opt : allopts) {
965 if (opt == "charter") {
966 h_font_roman[0] = "md-charter";
969 if (opt == "garamond") {
970 h_font_roman[0] = "md-garamond";
973 if (opt == "utopia") {
974 h_font_roman[0] = "md-utopia";
977 if (opt == "expert") {
979 h_font_roman_osf = "true";
985 else if (name == "mathpazo") {
986 h_font_roman[0] = "palatino";
987 for (auto const & opt : allopts) {
989 h_font_roman_osf = "true";
1001 h_font_roman_opts = xopts;
1005 else if (name == "mathptmx") {
1006 h_font_roman[0] = "times";
1007 for (auto const & opt : allopts) {
1013 h_font_roman_opts = xopts;
1017 if (name == "crimson")
1018 h_font_roman[0] = "cochineal";
1020 if (name == "cochineal") {
1021 for (auto const & opt : allopts) {
1022 if (opt == "osf" || opt == "oldstyle") {
1023 h_font_roman_osf = "true";
1026 if (opt == "proportional" || opt == "p")
1033 h_font_roman_opts = xopts;
1037 if (name == "CrimsonPro") {
1038 h_font_roman_osf = "true";
1039 for (auto const & opt : allopts) {
1040 if (opt == "lf" || opt == "lining") {
1041 h_font_roman_osf = "false";
1044 if (opt == "proportional" || opt == "p")
1046 if (opt == "medium") {
1047 h_font_roman[0] = "CrimsonProMedium";
1050 if (opt == "extralight") {
1051 h_font_roman[0] = "CrimsonProExtraLight";
1054 if (opt == "light") {
1055 h_font_roman[0] = "CrimsonProLight";
1063 h_font_roman_opts = xopts;
1069 // font uses old-style figure
1070 h_font_roman_osf = "true";
1072 if (name == "paratype") {
1073 // in this case all fonts are ParaType
1074 h_font_roman[0] = "PTSerif-TLF";
1075 h_font_sans[0] = "default";
1076 h_font_typewriter[0] = "default";
1079 if (name == "PTSerif")
1080 h_font_roman[0] = "PTSerif-TLF";
1082 if (name == "XCharter") {
1083 h_font_roman[0] = "xcharter";
1084 for (auto const & opt : allopts) {
1086 h_font_roman_osf = "true";
1094 h_font_roman_opts = xopts;
1098 if (name == "plex-serif") {
1099 h_font_roman[0] = "IBMPlexSerif";
1100 for (auto const & opt : allopts) {
1101 if (opt == "thin") {
1102 h_font_roman[0] = "IBMPlexSerifThin";
1105 if (opt == "extralight") {
1106 h_font_roman[0] = "IBMPlexSerifExtraLight";
1109 if (opt == "light") {
1110 h_font_roman[0] = "IBMPlexSerifLight";
1118 h_font_roman_opts = xopts;
1122 if (name == "noto-serif" || name == "noto") {
1129 bool extralight = false;
1131 bool medium = false;
1134 if (name == "noto") {
1139 // Since the options might apply to different shapes,
1140 // we need to parse all options first and then handle them.
1141 for (auto const & opt : allopts) {
1142 if (opt == "regular")
1152 if (opt == "thin") {
1156 if (opt == "extralight") {
1160 if (opt == "light") {
1164 if (opt == "medium") {
1175 if (opt == "nott") {
1183 if (prefixIs(opt, "scaled=")) {
1192 // handle options that might affect different shapes
1193 if (name == "noto-serif" || rm) {
1195 h_font_roman[0] = "NotoSerifThin";
1196 else if (extralight)
1197 h_font_roman[0] = "NotoSerifExtralight";
1199 h_font_roman[0] = "NotoSerifLight";
1201 h_font_roman[0] = "NotoSerifMedium";
1203 h_font_roman[0] = "NotoSerifRegular";
1205 h_font_roman_osf = "true";
1207 h_font_roman_opts = xopts;
1209 if (name == "noto" && sf) {
1211 h_font_sans[0] = "NotoSansThin";
1212 else if (extralight)
1213 h_font_sans[0] = "NotoSansExtralight";
1215 h_font_sans[0] = "NotoSansLight";
1217 h_font_sans[0] = "NotoSansMedium";
1219 h_font_sans[0] = "NotoSansRegular";
1221 h_font_sans_osf = "true";
1223 scale_as_percentage(scl, h_font_sf_scale[0]);
1225 h_font_sans_opts = xopts;
1227 if (name == "noto" && tt) {
1228 h_font_typewriter[0] = "NotoMonoRegular";
1230 h_font_typewriter_osf = "true";
1232 scale_as_percentage(scl, h_font_tt_scale[0]);
1234 h_font_typewriter_opts = xopts;
1238 if (name == "sourceserifpro") {
1239 h_font_roman[0] = "ADOBESourceSerifPro";
1240 for (auto const & opt : allopts) {
1242 h_font_roman_osf = "true";
1250 h_font_roman_opts = xopts;
1258 // By default, we use the package name as LyX font name,
1259 // so this only needs to be reset if these names differ.
1260 // Also, we handle the scaling option here generally.
1261 if (is_known(name, known_sans_font_packages)) {
1262 h_font_sans[0] = name;
1263 if (contains(opts, "scale")) {
1264 vector<string>::iterator it = allopts.begin();
1265 for (; it != allopts.end() ; ++it) {
1266 string const opt = *it;
1267 if (prefixIs(opt, "scaled=") || prefixIs(opt, "scale=")) {
1268 if (scale_as_percentage(opt, h_font_sf_scale[0])) {
1277 if (name == "biolinum" || name == "biolinum-type1") {
1278 h_font_sans[0] = "biolinum";
1279 for (auto const & opt : allopts) {
1280 if (prefixIs(opt, "osf")) {
1281 h_font_sans_osf = "true";
1289 h_font_sans_opts = xopts;
1293 if (name == "cantarell") {
1294 for (auto const & opt : allopts) {
1295 if (opt == "defaultsans")
1297 if (prefixIs(opt, "oldstyle")) {
1298 h_font_sans_osf = "true";
1306 h_font_sans_opts = xopts;
1310 if (name == "Chivo") {
1311 for (auto const & opt : allopts) {
1312 if (opt == "thin") {
1313 h_font_roman[0] = "ChivoThin";
1316 if (opt == "light") {
1317 h_font_roman[0] = "ChivoLight";
1320 if (opt == "regular") {
1321 h_font_roman[0] = "Chivo";
1324 if (opt == "medium") {
1325 h_font_roman[0] = "ChivoMedium";
1328 if (prefixIs(opt, "oldstyle")) {
1329 h_font_sans_osf = "true";
1337 h_font_sans_opts = xopts;
1341 if (name == "PTSans") {
1342 h_font_sans[0] = "PTSans-TLF";
1345 if (name == "FiraSans") {
1346 h_font_sans_osf = "true";
1347 for (auto const & opt : allopts) {
1348 if (opt == "book") {
1349 h_font_sans[0] = "FiraSansBook";
1352 if (opt == "thin") {
1355 if (opt == "extralight") {
1356 h_font_sans[0] = "FiraSansExtralight";
1359 if (opt == "light") {
1360 h_font_sans[0] = "FiraSansLight";
1363 if (opt == "ultralight") {
1364 h_font_sans[0] = "FiraSansUltralight";
1367 if (opt == "thin") {
1368 h_font_sans[0] = "FiraSansThin";
1371 if (opt == "lf" || opt == "lining") {
1372 h_font_sans_osf = "false";
1380 h_font_sans_opts = xopts;
1384 if (name == "plex-sans") {
1385 h_font_sans[0] = "IBMPlexSans";
1386 for (auto const & opt : allopts) {
1387 if (opt == "condensed") {
1388 h_font_sans[0] = "IBMPlexSansCondensed";
1391 if (opt == "thin") {
1392 h_font_sans[0] = "IBMPlexSansThin";
1395 if (opt == "extralight") {
1396 h_font_sans[0] = "IBMPlexSansExtraLight";
1399 if (opt == "light") {
1400 h_font_sans[0] = "IBMPlexSansLight";
1408 h_font_sans_opts = xopts;
1412 if (name == "noto-sans") {
1413 h_font_sans[0] = "NotoSansRegular";
1414 for (auto const & opt : allopts) {
1415 if (opt == "regular")
1417 if (opt == "medium") {
1418 h_font_sans[0] = "NotoSansMedium";
1421 if (opt == "thin") {
1422 h_font_sans[0] = "NotoSansThin";
1425 if (opt == "extralight") {
1426 h_font_sans[0] = "NotoSansExtralight";
1429 if (opt == "light") {
1430 h_font_sans[0] = "NotoSansLight";
1434 h_font_sans_osf = "true";
1442 h_font_sans_opts = xopts;
1446 if (name == "sourcesanspro") {
1447 h_font_sans[0] = "ADOBESourceSansPro";
1448 for (auto const & opt : allopts) {
1450 h_font_sans_osf = "true";
1458 h_font_sans_opts = xopts;
1466 // By default, we use the package name as LyX font name,
1467 // so this only needs to be reset if these names differ.
1468 // Also, we handle the scaling option here generally.
1469 if (is_known(name, known_typewriter_font_packages)) {
1470 h_font_typewriter[0] = name;
1471 if (contains(opts, "scale")) {
1472 vector<string>::iterator it = allopts.begin();
1473 for (; it != allopts.end() ; ++it) {
1474 string const opt = *it;
1475 if (prefixIs(opt, "scaled=") || prefixIs(opt, "scale=")) {
1476 if (scale_as_percentage(opt, h_font_tt_scale[0])) {
1485 if (name == "libertineMono" || name == "libertineMono-type1")
1486 h_font_typewriter[0] = "libertine-mono";
1488 if (name == "FiraMono") {
1489 h_font_typewriter_osf = "true";
1490 for (auto const & opt : allopts) {
1491 if (opt == "lf" || opt == "lining") {
1492 h_font_typewriter_osf = "false";
1500 h_font_typewriter_opts = xopts;
1504 if (name == "PTMono")
1505 h_font_typewriter[0] = "PTMono-TLF";
1507 if (name == "plex-mono") {
1508 h_font_typewriter[0] = "IBMPlexMono";
1509 for (auto const & opt : allopts) {
1510 if (opt == "thin") {
1511 h_font_typewriter[0] = "IBMPlexMonoThin";
1514 if (opt == "extralight") {
1515 h_font_typewriter[0] = "IBMPlexMonoExtraLight";
1518 if (opt == "light") {
1519 h_font_typewriter[0] = "IBMPlexMonoLight";
1527 h_font_typewriter_opts = xopts;
1531 if (name == "noto-mono") {
1532 h_font_typewriter[0] = "NotoMonoRegular";
1533 for (auto const & opt : allopts) {
1534 if (opt == "regular")
1541 h_font_typewriter_opts = xopts;
1545 if (name == "sourcecodepro") {
1546 h_font_typewriter[0] = "ADOBESourceCodePro";
1547 for (auto const & opt : allopts) {
1549 h_font_typewriter_osf = "true";
1557 h_font_typewriter_opts = xopts;
1565 // By default, we use the package name as LyX font name,
1566 // so this only needs to be reset if these names differ.
1567 if (is_known(name, known_math_font_packages))
1568 h_font_math[0] = name;
1570 if (name == "newtxmath") {
1572 h_font_math[0] = "newtxmath";
1573 else if (opts == "garamondx")
1574 h_font_math[0] = "garamondx-ntxm";
1575 else if (opts == "libertine")
1576 h_font_math[0] = "libertine-ntxm";
1577 else if (opts == "minion")
1578 h_font_math[0] = "minion-ntxm";
1579 else if (opts == "cochineal")
1580 h_font_math[0] = "cochineal-ntxm";
1583 if (name == "iwona")
1585 h_font_math[0] = "iwona-math";
1587 if (name == "kurier")
1589 h_font_math[0] = "kurier-math";
1591 // after the detection and handling of special cases, we can remove the
1592 // fonts, otherwise they would appear in the preamble, see bug #7856
1593 if (is_known(name, known_roman_font_packages) || is_known(name, known_sans_font_packages)
1594 || is_known(name, known_typewriter_font_packages) || is_known(name, known_math_font_packages))
1596 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
1597 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
1598 name == "esint" || name == "mhchem" || name == "mathdots" ||
1599 name == "mathtools" || name == "stackrel" ||
1600 name == "stmaryrd" || name == "undertilde") {
1601 h_use_packages[name] = "2";
1602 registerAutomaticallyLoadedPackage(name);
1605 else if (name == "babel") {
1606 h_language_package = "default";
1607 // One might think we would have to do nothing if babel is loaded
1608 // without any options to prevent pollution of the preamble with this
1609 // babel call in every roundtrip.
1610 // But the user could have defined babel-specific things afterwards. So
1611 // we need to keep it in the preamble to prevent cases like bug #7861.
1612 if (!opts.empty()) {
1613 // check if more than one option was used - used later for inputenc
1614 if (options.begin() != options.end() - 1)
1615 one_language = false;
1616 // babel takes the last language of the option of its \usepackage
1617 // call as document language. If there is no such language option, the
1618 // last language in the documentclass options is used.
1619 handle_opt(options, known_languages, h_language);
1620 // translate the babel name to a LyX name
1621 h_language = babel2lyx(h_language);
1622 if (h_language == "japanese") {
1623 // For Japanese, the encoding isn't indicated in the source
1624 // file, and there's really not much we can do. We could
1625 // 1) offer a list of possible encodings to choose from, or
1626 // 2) determine the encoding of the file by inspecting it.
1627 // For the time being, we leave the encoding alone so that
1628 // we don't get iconv errors when making a wrong guess, and
1629 // we will output a note at the top of the document
1630 // explaining what to do.
1631 Encoding const * const enc = encodings.fromIconvName(
1632 p.getEncoding(), Encoding::japanese, false);
1634 h_inputencoding = enc->name();
1635 is_nonCJKJapanese = true;
1636 // in this case babel can be removed from the preamble
1637 registerAutomaticallyLoadedPackage("babel");
1639 // If babel is called with options, LyX puts them by default into the
1640 // document class options. This works for most languages, except
1641 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
1642 // perhaps in future others.
1643 // Therefore keep the babel call as it is as the user might have
1645 string const babelcall = "\\usepackage[" + opts + "]{babel}\n";
1646 if (!contains(h_preamble.str(), babelcall))
1647 h_preamble << babelcall;
1649 delete_opt(options, known_languages);
1651 if (!contains(h_preamble.str(), "\\usepackage{babel}\n"))
1652 h_preamble << "\\usepackage{babel}\n";
1653 explicit_babel = true;
1657 else if (name == "polyglossia") {
1658 h_language_package = "default";
1659 h_default_output_format = "pdf4";
1660 h_use_non_tex_fonts = true;
1662 registerAutomaticallyLoadedPackage("xunicode");
1663 if (h_inputencoding == "auto-legacy")
1664 p.setEncoding("UTF-8");
1667 else if (name == "CJK") {
1668 // set the encoding to "auto-legacy" because it might be set to "auto-legacy-plain" by the babel handling
1669 // and this would not be correct for CJK
1670 if (h_inputencoding == "auto-legacy-plain")
1671 h_inputencoding = "auto-legacy";
1672 registerAutomaticallyLoadedPackage("CJK");
1675 else if (name == "CJKutf8") {
1676 h_inputencoding = "utf8-cjk";
1677 p.setEncoding("UTF-8");
1678 registerAutomaticallyLoadedPackage("CJKutf8");
1681 else if (name == "fontenc") {
1682 h_fontencoding = getStringFromVector(options, ",");
1686 else if (name == "inputenc" || name == "luainputenc") {
1687 // h_inputencoding is only set when there is not more than one
1688 // inputenc option because otherwise h_inputencoding must be
1689 // set to "auto-legacy" (the default encodings of the document's languages)
1690 // Therefore check that exactly one option is passed to inputenc.
1691 // It is also only set when there is not more than one babel
1693 if (!options.empty()) {
1694 string const encoding = options.back();
1695 Encoding const * const enc = encodings.fromLaTeXName(
1696 encoding, Encoding::inputenc, true);
1698 if (!detectEncoding)
1699 warning_message("Unknown encoding " + encoding + ". Ignoring.");
1701 if (!enc->unsafe() && options.size() == 1 && one_language == true) {
1702 h_inputencoding = enc->name();
1703 docencoding = enc->iconvName();
1705 p.setEncoding(enc->iconvName());
1711 else if (name == "srcltx") {
1712 h_output_sync = "1";
1713 if (!opts.empty()) {
1714 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
1717 h_output_sync_macro = "\\usepackage{srcltx}";
1720 else if (is_known(name, known_old_language_packages)) {
1721 // known language packages from the times before babel
1722 // if they are found and not also babel, they will be used as
1723 // custom language package
1724 h_language_package = "\\usepackage{" + name + "}";
1727 else if (name == "lyxskak") {
1728 // ignore this and its options
1729 const char * const o[] = {"ps", "mover", 0};
1730 delete_opt(options, o);
1733 else if (name == "parskip" && options.size() < 2 && (opts.empty() || prefixIs(opts, "skip="))) {
1735 h_paragraph_separation = "halfline";
1737 if (opts == "skip=\\smallskipamount")
1738 h_defskip = "smallskip";
1739 else if (opts == "skip=\\medskipamount")
1740 h_defskip = "medskip";
1741 else if (opts == "skip=\\bigskipamount")
1742 h_defskip = "bigskip";
1743 else if (opts == "skip=\\baselineskip")
1744 h_defskip = "fullline";
1747 h_paragraph_separation = "skip";
1751 else if (is_known(name, known_lyx_packages) && options.empty()) {
1752 if (name == "splitidx")
1753 h_use_indices = "true";
1754 else if (name == "minted")
1755 h_use_minted = true;
1756 else if (name == "refstyle")
1757 h_use_refstyle = true;
1758 else if (name == "prettyref")
1759 h_use_refstyle = false;
1761 if (!in_lyx_preamble) {
1762 h_preamble << package_beg_sep << name
1763 << package_mid_sep << "\\usepackage{"
1765 if (p.next_token().cat() == catNewline ||
1766 (p.next_token().cat() == catSpace &&
1767 p.next_next_token().cat() == catNewline))
1769 h_preamble << package_end_sep;
1773 else if (name == "geometry")
1774 handle_geometry(options);
1776 else if (name == "subfig")
1777 ; // ignore this FIXME: Use the package separator mechanism instead
1779 else if (char const * const * where = is_known(name, known_languages))
1780 h_language = known_coded_languages[where - known_languages];
1782 else if (name == "natbib") {
1783 h_biblio_style = "plainnat";
1784 h_cite_engine = "natbib";
1785 h_cite_engine_type = "authoryear";
1786 vector<string>::iterator it =
1787 find(options.begin(), options.end(), "authoryear");
1788 if (it != options.end())
1791 it = find(options.begin(), options.end(), "numbers");
1792 if (it != options.end()) {
1793 h_cite_engine_type = "numerical";
1797 if (!options.empty())
1798 h_biblio_options = join(options, ",");
1801 else if (name == "biblatex") {
1802 h_biblio_style = "plainnat";
1803 h_cite_engine = "biblatex";
1804 h_cite_engine_type = "authoryear";
1806 vector<string>::iterator it =
1807 find(options.begin(), options.end(), "natbib");
1808 if (it != options.end()) {
1810 h_cite_engine = "biblatex-natbib";
1812 opt = process_keyval_opt(options, "natbib");
1814 h_cite_engine = "biblatex-natbib";
1816 opt = process_keyval_opt(options, "style");
1818 h_biblatex_citestyle = opt;
1819 h_biblatex_bibstyle = opt;
1821 opt = process_keyval_opt(options, "citestyle");
1823 h_biblatex_citestyle = opt;
1824 opt = process_keyval_opt(options, "bibstyle");
1826 h_biblatex_bibstyle = opt;
1828 opt = process_keyval_opt(options, "refsection");
1830 if (opt == "none" || opt == "part"
1831 || opt == "chapter" || opt == "section"
1832 || opt == "subsection")
1835 warning_message("Ignoring unknown refsection value '" + opt + "'.");
1837 opt = process_keyval_opt(options, "bibencoding");
1840 if (!options.empty()) {
1841 h_biblio_options = join(options, ",");
1846 else if (name == "jurabib") {
1847 h_biblio_style = "jurabib";
1848 h_cite_engine = "jurabib";
1849 h_cite_engine_type = "authoryear";
1850 if (!options.empty())
1851 h_biblio_options = join(options, ",");
1854 else if (name == "bibtopic")
1855 h_use_bibtopic = "true";
1857 else if (name == "chapterbib")
1858 h_multibib = "child";
1860 else if (name == "hyperref")
1861 handle_hyperref(options);
1863 else if (name == "algorithm2e") {
1864 // Load "algorithm2e" module
1865 addModule("algorithm2e");
1866 // Add the package options to the global document options
1867 if (!options.empty()) {
1868 if (h_options.empty())
1869 h_options = join(options, ",");
1871 h_options += ',' + join(options, ",");
1874 else if (name == "microtype") {
1875 //we internally support only microtype without params
1876 if (options.empty())
1877 h_use_microtype = "true";
1879 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1882 else if (name == "lineno") {
1883 h_use_lineno = "true";
1884 if (!options.empty()) {
1885 h_lineno_options = join(options, ",");
1890 else if (name == "changebar")
1891 h_output_changes = "true";
1893 else if (!in_lyx_preamble) {
1894 if (options.empty())
1895 h_preamble << "\\usepackage{" << name << '}';
1897 h_preamble << "\\usepackage[" << opts << "]{"
1901 if (p.next_token().cat() == catNewline ||
1902 (p.next_token().cat() == catSpace &&
1903 p.next_next_token().cat() == catNewline))
1907 // We need to do something with the options...
1908 if (!options.empty() && !detectEncoding)
1909 warning_message("Ignoring options '" + join(options, ",")
1910 + "' of package " + name + '.');
1912 // remove the whitespace
1917 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1920 Token t = p.get_token();
1921 if (t.cat() == catEscape &&
1922 is_known(t.cs(), known_if_commands))
1923 handle_if(p, in_lyx_preamble);
1925 if (!in_lyx_preamble)
1926 h_preamble << t.asInput();
1927 if (t.cat() == catEscape && t.cs() == "fi")
1934 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1936 if (contains(h_float_placement, "H"))
1937 registerAutomaticallyLoadedPackage("float");
1938 if (h_spacing != "single" && h_spacing != "default")
1939 registerAutomaticallyLoadedPackage("setspace");
1940 if (h_use_packages["amsmath"] == "2") {
1941 // amsbsy and amstext are already provided by amsmath
1942 registerAutomaticallyLoadedPackage("amsbsy");
1943 registerAutomaticallyLoadedPackage("amstext");
1946 // output the LyX file settings
1947 // Important: Keep the version formatting in sync with LyX and
1948 // lyx2lyx (bug 7951)
1949 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1950 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1951 << lyx_version_minor << '\n'
1952 << "\\lyxformat " << LYX_FORMAT << '\n'
1953 << "\\begin_document\n"
1954 << "\\begin_header\n"
1955 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1956 << "\\origin " << origin << "\n"
1957 << "\\textclass " << h_textclass << "\n";
1958 if (!h_doc_metadata.empty()) {
1959 os << "\\begin_metadata\n"
1961 << "\n\\end_metadata\n";
1963 string const raw = subdoc ? empty_string() : h_preamble.str();
1965 os << "\\begin_preamble\n";
1966 for (string::size_type i = 0; i < raw.size(); ++i) {
1967 if (raw[i] == package_beg_sep) {
1968 // Here follows some package loading code that
1969 // must be skipped if the package is loaded
1971 string::size_type j = raw.find(package_mid_sep, i);
1972 if (j == string::npos)
1974 string::size_type k = raw.find(package_end_sep, j);
1975 if (k == string::npos)
1977 string const package = raw.substr(i + 1, j - i - 1);
1978 string const replacement = raw.substr(j + 1, k - j - 1);
1979 if (auto_packages.find(package) == auto_packages.end())
1985 os << "\n\\end_preamble\n";
1987 if (!h_options.empty())
1988 os << "\\options " << h_options << "\n";
1989 os << "\\use_default_options " << h_use_default_options << "\n";
1990 if (!used_modules.empty()) {
1991 os << "\\begin_modules\n";
1992 vector<string>::const_iterator const end = used_modules.end();
1993 vector<string>::const_iterator it = used_modules.begin();
1994 for (; it != end; ++it)
1996 os << "\\end_modules\n";
1998 if (!h_includeonlys.empty()) {
1999 os << "\\begin_includeonly\n";
2000 for (auto const & iofile : h_includeonlys)
2001 os << iofile << '\n';
2002 os << "\\end_includeonly\n";
2004 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
2005 << "\\language " << h_language << "\n"
2006 << "\\language_package " << h_language_package << "\n"
2007 << "\\inputencoding " << h_inputencoding << "\n"
2008 << "\\fontencoding " << h_fontencoding << "\n"
2009 << "\\font_roman \"" << h_font_roman[0]
2010 << "\" \"" << h_font_roman[1] << "\"\n"
2011 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
2012 << "\\font_typewriter \"" << h_font_typewriter[0]
2013 << "\" \"" << h_font_typewriter[1] << "\"\n"
2014 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
2015 << "\\font_default_family " << h_font_default_family << "\n"
2016 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
2017 << "\\font_sc " << h_font_sc << "\n"
2018 << "\\font_roman_osf " << h_font_roman_osf << "\n"
2019 << "\\font_sans_osf " << h_font_sans_osf << "\n"
2020 << "\\font_typewriter_osf " << h_font_typewriter_osf << "\n";
2021 if (!h_font_roman_opts.empty())
2022 os << "\\font_roman_opts \"" << h_font_roman_opts << "\"" << '\n';
2023 os << "\\font_sf_scale " << h_font_sf_scale[0]
2024 << ' ' << h_font_sf_scale[1] << '\n';
2025 if (!h_font_sans_opts.empty())
2026 os << "\\font_sans_opts \"" << h_font_sans_opts << "\"" << '\n';
2027 os << "\\font_tt_scale " << h_font_tt_scale[0]
2028 << ' ' << h_font_tt_scale[1] << '\n';
2029 if (!h_font_cjk.empty())
2030 os << "\\font_cjk " << h_font_cjk << '\n';
2031 if (!h_font_typewriter_opts.empty())
2032 os << "\\font_typewriter_opts \"" << h_font_typewriter_opts << "\"" << '\n';
2033 os << "\\use_microtype " << h_use_microtype << '\n'
2034 << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
2035 << "\\graphics " << h_graphics << '\n'
2036 << "\\default_output_format " << h_default_output_format << "\n"
2037 << "\\output_sync " << h_output_sync << "\n";
2038 if (h_output_sync == "1")
2039 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
2040 os << "\\bibtex_command " << h_bibtex_command << "\n"
2041 << "\\index_command " << h_index_command << "\n";
2042 if (!h_float_placement.empty())
2043 os << "\\float_placement " << h_float_placement << "\n";
2044 os << "\\paperfontsize " << h_paperfontsize << "\n"
2045 << "\\spacing " << h_spacing << "\n"
2046 << "\\use_hyperref " << h_use_hyperref << '\n';
2047 if (h_use_hyperref == "true") {
2048 if (!h_pdf_title.empty())
2049 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
2050 if (!h_pdf_author.empty())
2051 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
2052 if (!h_pdf_subject.empty())
2053 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
2054 if (!h_pdf_keywords.empty())
2055 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
2056 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
2057 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
2058 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
2059 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
2060 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
2061 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
2062 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
2063 "\\pdf_backref " << h_pdf_backref << "\n"
2064 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
2065 if (!h_pdf_pagemode.empty())
2066 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
2067 if (!h_pdf_quoted_options.empty())
2068 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
2070 os << "\\papersize " << h_papersize << "\n"
2071 << "\\use_geometry " << h_use_geometry << '\n';
2072 for (map<string, string>::const_iterator it = h_use_packages.begin();
2073 it != h_use_packages.end(); ++it)
2074 os << "\\use_package " << it->first << ' ' << it->second << '\n';
2075 os << "\\cite_engine " << h_cite_engine << '\n'
2076 << "\\cite_engine_type " << h_cite_engine_type << '\n'
2077 << "\\biblio_style " << h_biblio_style << "\n"
2078 << "\\use_bibtopic " << h_use_bibtopic << "\n";
2079 if (!h_biblio_options.empty())
2080 os << "\\biblio_options " << h_biblio_options << "\n";
2081 if (!h_biblatex_bibstyle.empty())
2082 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
2083 if (!h_biblatex_citestyle.empty())
2084 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
2085 if (!h_multibib.empty())
2086 os << "\\multibib " << h_multibib << "\n";
2087 os << "\\use_indices " << h_use_indices << "\n"
2088 << "\\paperorientation " << h_paperorientation << '\n'
2089 << "\\suppress_date " << h_suppress_date << '\n'
2090 << "\\justification " << h_justification << '\n'
2091 << "\\use_refstyle " << h_use_refstyle << '\n'
2092 << "\\use_minted " << h_use_minted << '\n'
2093 << "\\use_lineno " << h_use_lineno << '\n';
2094 if (!h_lineno_options.empty())
2095 os << "\\lineno_options " << h_lineno_options << '\n';
2096 if (!h_fontcolor.empty())
2097 os << "\\fontcolor " << h_fontcolor << '\n';
2098 if (!h_notefontcolor.empty())
2099 os << "\\notefontcolor " << h_notefontcolor << '\n';
2100 if (!h_backgroundcolor.empty())
2101 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
2102 if (!h_boxbgcolor.empty())
2103 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
2104 if (index_number != 0)
2105 for (int i = 0; i < index_number; i++) {
2106 os << "\\index " << h_index[i] << '\n'
2107 << "\\shortcut " << h_shortcut[i] << '\n'
2108 << "\\color " << h_color << '\n'
2112 os << "\\index " << h_index[0] << '\n'
2113 << "\\shortcut " << h_shortcut[0] << '\n'
2114 << "\\color " << h_color << '\n'
2118 << "\\secnumdepth " << h_secnumdepth << "\n"
2119 << "\\tocdepth " << h_tocdepth << "\n"
2120 << "\\paragraph_separation " << h_paragraph_separation << "\n";
2121 if (h_paragraph_separation == "skip")
2122 os << "\\defskip " << h_defskip << "\n";
2124 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
2125 os << "\\is_math_indent " << h_is_mathindent << "\n";
2126 if (!h_mathindentation.empty())
2127 os << "\\math_indentation " << h_mathindentation << "\n";
2128 os << "\\math_numbering_side " << h_math_numbering_side << "\n";
2129 os << "\\quotes_style " << h_quotes_style << "\n"
2130 << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
2131 << "\\papercolumns " << h_papercolumns << "\n"
2132 << "\\papersides " << h_papersides << "\n"
2133 << "\\paperpagestyle " << h_paperpagestyle << "\n";
2134 if (!h_listings_params.empty())
2135 os << "\\listings_params " << h_listings_params << "\n";
2136 os << "\\tracking_changes " << h_tracking_changes << "\n"
2137 << "\\output_changes " << h_output_changes << "\n"
2138 << "\\change_bars " << h_change_bars << "\n"
2139 << "\\html_math_output " << h_html_math_output << "\n"
2140 << "\\html_css_as_file " << h_html_css_as_file << "\n"
2141 << "\\html_be_strict " << h_html_be_strict << "\n"
2142 << "\\docbook_table_output " << h_docbook_table_output << "\n"
2143 << "\\docbook_mathml_prefix " << h_docbook_mathml_prefix << "\n"
2145 << "\\end_header\n\n"
2146 << "\\begin_body\n";
2151 void Preamble::parse(Parser & p, string const & forceclass,
2152 TeX2LyXDocClass & tc)
2154 // initialize fixed types
2155 special_columns_['D'] = 3;
2156 parse(p, forceclass, false, tc);
2160 void Preamble::parse(Parser & p, string const & forceclass,
2161 bool detectEncoding, TeX2LyXDocClass & tc)
2163 bool is_full_document = false;
2164 bool is_lyx_file = false;
2165 bool in_lyx_preamble = false;
2166 bool class_set = false;
2168 // determine whether this is a full document or a fragment for inclusion
2170 Token const & t = p.get_token();
2172 if (t.cat() == catEscape && t.cs() == "documentclass") {
2173 is_full_document = true;
2179 if (detectEncoding && !is_full_document)
2182 // Force textclass if the user wanted it
2183 if (!forceclass.empty()) {
2184 setTextClass(forceclass, tc);
2188 while (is_full_document && p.good()) {
2189 if (detectEncoding && h_inputencoding != "auto-legacy" &&
2190 h_inputencoding != "auto-legacy-plain")
2193 Token const & t = p.get_token();
2195 if (!detectEncoding)
2196 debug_message("t: " + t.asInput());
2201 if (!in_lyx_preamble &&
2202 (t.cat() == catLetter ||
2203 t.cat() == catSuper ||
2204 t.cat() == catSub ||
2205 t.cat() == catOther ||
2206 t.cat() == catMath ||
2207 t.cat() == catActive ||
2208 t.cat() == catBegin ||
2209 t.cat() == catEnd ||
2210 t.cat() == catAlign ||
2211 t.cat() == catParameter)) {
2212 h_preamble << t.cs();
2216 if (!in_lyx_preamble &&
2217 (t.cat() == catSpace || t.cat() == catNewline)) {
2218 h_preamble << t.asInput();
2222 if (t.cat() == catComment) {
2223 static regex const islyxfile("%% LyX .* created this file");
2224 static regex const usercommands("User specified LaTeX commands");
2226 string const comment = t.asInput();
2228 // magically switch encoding default if it looks like XeLaTeX
2229 static string const magicXeLaTeX =
2230 "% This document must be compiled with XeLaTeX ";
2231 if (comment.size() > magicXeLaTeX.size()
2232 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
2233 && h_inputencoding == "auto-legacy") {
2234 if (!detectEncoding)
2235 warning_message("XeLaTeX comment found, switching to UTF8");
2236 h_inputencoding = "utf8";
2239 if (regex_search(comment, sub, islyxfile)) {
2241 in_lyx_preamble = true;
2242 } else if (is_lyx_file
2243 && regex_search(comment, sub, usercommands))
2244 in_lyx_preamble = false;
2245 else if (!in_lyx_preamble)
2246 h_preamble << t.asInput();
2250 if (t.cs() == "PassOptionsToPackage") {
2251 string const poptions = p.getArg('{', '}');
2252 string const package = p.verbatim_item();
2253 extra_package_options_.insert(make_pair(package, poptions));
2257 if (t.cs() == "pagestyle") {
2258 h_paperpagestyle = p.verbatim_item();
2262 if (t.cs() == "setdefaultlanguage") {
2264 // We don't yet care about non-language variant options
2265 // because LyX doesn't support this yet, see bug #8214
2267 string langopts = p.getOpt();
2268 // check if the option contains a variant, if yes, extract it
2269 string::size_type pos_var = langopts.find("variant");
2270 string::size_type i = langopts.find(',', pos_var);
2271 string::size_type k = langopts.find('=', pos_var);
2272 if (pos_var != string::npos){
2274 if (i == string::npos)
2275 variant = langopts.substr(k + 1, langopts.length() - k - 2);
2277 variant = langopts.substr(k + 1, i - k - 1);
2278 h_language = variant;
2282 h_language = p.verbatim_item();
2283 //finally translate the poyglossia name to a LyX name
2284 h_language = polyglossia2lyx(h_language);
2288 if (t.cs() == "setotherlanguage") {
2289 // We don't yet care about the option because LyX doesn't
2290 // support this yet, see bug #8214
2291 p.hasOpt() ? p.getOpt() : string();
2296 if (t.cs() == "setmainfont") {
2297 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2298 h_font_roman[1] = p.getArg('{', '}');
2299 if (!fontopts.empty()) {
2300 vector<string> opts = getVectorFromString(fontopts);
2302 for (auto const & opt : opts) {
2303 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2306 if (!fontopts.empty())
2310 h_font_roman_opts = fontopts;
2315 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
2316 // LyX currently only supports the scale option
2317 string scale, fontopts;
2319 fontopts = p.getArg('[', ']');
2320 if (!fontopts.empty()) {
2321 vector<string> opts = getVectorFromString(fontopts);
2323 for (auto const & opt : opts) {
2324 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2327 if (prefixIs(opt, "Scale=")) {
2328 scale_as_percentage(opt, scale);
2331 if (!fontopts.empty())
2337 if (t.cs() == "setsansfont") {
2339 h_font_sf_scale[1] = scale;
2340 h_font_sans[1] = p.getArg('{', '}');
2341 if (!fontopts.empty())
2342 h_font_sans_opts = fontopts;
2345 h_font_tt_scale[1] = scale;
2346 h_font_typewriter[1] = p.getArg('{', '}');
2347 if (!fontopts.empty())
2348 h_font_typewriter_opts = fontopts;
2353 if (t.cs() == "babelfont") {
2355 h_use_non_tex_fonts = true;
2356 h_language_package = "babel";
2357 if (h_inputencoding == "auto-legacy")
2358 p.setEncoding("UTF-8");
2359 // we don't care about the lang option
2360 string const lang = p.hasOpt() ? p.getArg('[', ']') : string();
2361 string const family = p.getArg('{', '}');
2362 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2363 string const fontname = p.getArg('{', '}');
2364 if (lang.empty() && family == "rm") {
2365 h_font_roman[1] = fontname;
2366 if (!fontopts.empty()) {
2367 vector<string> opts = getVectorFromString(fontopts);
2369 for (auto const & opt : opts) {
2370 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2373 if (!fontopts.empty())
2377 h_font_roman_opts = fontopts;
2380 } else if (lang.empty() && (family == "sf" || family == "tt")) {
2382 if (!fontopts.empty()) {
2383 vector<string> opts = getVectorFromString(fontopts);
2385 for (auto const & opt : opts) {
2386 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2389 if (prefixIs(opt, "Scale=")) {
2390 scale_as_percentage(opt, scale);
2393 if (!fontopts.empty())
2398 if (family == "sf") {
2400 h_font_sf_scale[1] = scale;
2401 h_font_sans[1] = fontname;
2402 if (!fontopts.empty())
2403 h_font_sans_opts = fontopts;
2406 h_font_tt_scale[1] = scale;
2407 h_font_typewriter[1] = fontname;
2408 if (!fontopts.empty())
2409 h_font_typewriter_opts = fontopts;
2413 // not rm, sf or tt or lang specific
2414 h_preamble << '\\' << t.cs();
2416 h_preamble << '[' << lang << ']';
2417 h_preamble << '{' << family << '}';
2418 if (!fontopts.empty())
2419 h_preamble << '[' << fontopts << ']';
2420 h_preamble << '{' << fontname << '}' << '\n';
2425 if (t.cs() == "date") {
2426 string argument = p.getArg('{', '}');
2427 if (argument.empty())
2428 h_suppress_date = "true";
2430 h_preamble << t.asInput() << '{' << argument << '}';
2434 if (t.cs() == "color") {
2435 string const space =
2436 (p.hasOpt() ? p.getOpt() : string());
2437 string argument = p.getArg('{', '}');
2438 // check the case that a standard color is used
2439 if (space.empty() && is_known(argument, known_basic_colors)) {
2440 h_fontcolor = rgbcolor2code(argument);
2441 registerAutomaticallyLoadedPackage("color");
2442 } else if (space.empty() && argument == "document_fontcolor")
2443 registerAutomaticallyLoadedPackage("color");
2444 // check the case that LyX's document_fontcolor is defined
2445 // but not used for \color
2447 h_preamble << t.asInput();
2449 h_preamble << space;
2450 h_preamble << '{' << argument << '}';
2451 // the color might already be set because \definecolor
2452 // is parsed before this
2458 if (t.cs() == "pagecolor") {
2459 string argument = p.getArg('{', '}');
2460 // check the case that a standard color is used
2461 if (is_known(argument, known_basic_colors)) {
2462 h_backgroundcolor = rgbcolor2code(argument);
2463 } else if (argument == "page_backgroundcolor")
2464 registerAutomaticallyLoadedPackage("color");
2465 // check the case that LyX's page_backgroundcolor is defined
2466 // but not used for \pagecolor
2468 h_preamble << t.asInput() << '{' << argument << '}';
2469 // the color might already be set because \definecolor
2470 // is parsed before this
2471 h_backgroundcolor = "";
2476 if (t.cs() == "makeatletter") {
2477 // LyX takes care of this
2478 p.setCatcode('@', catLetter);
2482 if (t.cs() == "makeatother") {
2483 // LyX takes care of this
2484 p.setCatcode('@', catOther);
2488 if (t.cs() == "makeindex") {
2489 // LyX will re-add this if a print index command is found
2494 if (t.cs() == "newindex") {
2495 string const indexname = p.getArg('[', ']');
2496 string const shortcut = p.verbatim_item();
2497 if (!indexname.empty())
2498 h_index[index_number] = indexname;
2500 h_index[index_number] = shortcut;
2501 h_shortcut[index_number] = shortcut;
2507 if (t.cs() == "addbibresource") {
2508 string const options = p.getArg('[', ']');
2509 string const arg = removeExtension(p.getArg('{', '}'));
2510 if (!options.empty()) {
2511 // check if the option contains a bibencoding, if yes, extract it
2512 string::size_type pos = options.find("bibencoding=");
2514 if (pos != string::npos) {
2515 string::size_type i = options.find(',', pos);
2516 if (i == string::npos)
2517 encoding = options.substr(pos + 1);
2519 encoding = options.substr(pos, i - pos);
2520 pos = encoding.find('=');
2521 if (pos == string::npos)
2524 encoding = encoding.substr(pos + 1);
2526 if (!encoding.empty())
2527 biblatex_encodings.push_back(normalize_filename(arg) + ' ' + encoding);
2529 biblatex_bibliographies.push_back(arg);
2533 if (t.cs() == "bibliography") {
2534 vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
2535 biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
2539 if (t.cs() == "RS@ifundefined") {
2540 string const name = p.verbatim_item();
2541 string const body1 = p.verbatim_item();
2542 string const body2 = p.verbatim_item();
2543 // only non-lyxspecific stuff
2544 if (in_lyx_preamble &&
2545 (name == "subsecref" || name == "thmref" || name == "lemref"))
2549 ss << '\\' << t.cs();
2550 ss << '{' << name << '}'
2551 << '{' << body1 << '}'
2552 << '{' << body2 << '}';
2553 h_preamble << ss.str();
2558 if (t.cs() == "AtBeginDocument") {
2559 string const name = p.verbatim_item();
2560 // only non-lyxspecific stuff
2561 if (in_lyx_preamble &&
2562 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
2563 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
2564 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
2565 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
2566 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
2567 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
2568 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
2569 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
2570 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
2571 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
2572 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
2573 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
2574 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
2575 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
2576 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
2580 ss << '\\' << t.cs();
2581 ss << '{' << name << '}';
2582 h_preamble << ss.str();
2587 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
2588 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
2589 || t.cs() == "providecommand" || t.cs() == "providecommandx"
2590 || t.cs() == "DeclareRobustCommand"
2591 || t.cs() == "DeclareRobustCommandx"
2592 || t.cs() == "ProvideTextCommandDefault"
2593 || t.cs() == "DeclareMathAccent") {
2595 if (p.next_token().character() == '*') {
2599 string const name = p.verbatim_item();
2600 string const opt1 = p.getFullOpt();
2601 string const opt2 = p.getFullOpt();
2602 string const body = p.verbatim_item();
2603 // store the in_lyx_preamble setting
2604 bool const was_in_lyx_preamble = in_lyx_preamble;
2606 if (name == "\\rmdefault")
2607 if (is_known(body, known_roman_font_packages)) {
2608 h_font_roman[0] = body;
2610 in_lyx_preamble = true;
2612 if (name == "\\sfdefault") {
2613 if (is_known(body, known_sans_font_packages)) {
2614 h_font_sans[0] = body;
2616 in_lyx_preamble = true;
2618 if (body == "LibertinusSans-OsF") {
2619 h_font_sans[0] = "LibertinusSans-LF";
2620 h_font_sans_osf = "true";
2622 in_lyx_preamble = true;
2625 if (name == "\\ttdefault")
2626 if (is_known(body, known_typewriter_font_packages)) {
2627 h_font_typewriter[0] = body;
2629 in_lyx_preamble = true;
2631 if (name == "\\familydefault") {
2632 string family = body;
2633 // remove leading "\"
2634 h_font_default_family = family.erase(0,1);
2636 in_lyx_preamble = true;
2638 if (name == "\\LibertinusSans@scale") {
2639 if (isStrDbl(body)) {
2640 h_font_sf_scale[0] = convert<string>(
2641 static_cast<int>(100 * convert<double>(body)));
2644 if (name == "\\LibertinusMono@scale") {
2645 if (isStrDbl(body)) {
2646 h_font_tt_scale[0] = convert<string>(
2647 static_cast<int>(100 * convert<double>(body)));
2651 // remove LyX-specific definitions that are re-added by LyX
2653 // \lyxline is an ancient command that is converted by tex2lyx into
2654 // a \rule therefore remove its preamble code
2655 if (name == "\\lyxdot" || name == "\\lyxarrow"
2656 || name == "\\lyxline" || name == "\\LyX") {
2658 in_lyx_preamble = true;
2661 // Add the command to the known commands
2662 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
2664 // only non-lyxspecific stuff
2665 if (!in_lyx_preamble) {
2667 ss << '\\' << t.cs();
2670 ss << '{' << name << '}' << opt1 << opt2
2671 << '{' << body << "}";
2672 if (prefixIs(t.cs(), "renew") || !contains(h_preamble.str(), ss.str()))
2673 h_preamble << ss.str();
2675 ostream & out = in_preamble ? h_preamble : os;
2676 out << "\\" << t.cs() << "{" << name << "}"
2677 << opts << "{" << body << "}";
2680 // restore the in_lyx_preamble setting
2681 in_lyx_preamble = was_in_lyx_preamble;
2685 if (t.cs() == "documentclass") {
2686 vector<string>::iterator it;
2687 vector<string> opts = split_options(p.getArg('[', ']'));
2688 // FIXME This does not work for classes that have a
2689 // different name in LyX than in LaTeX
2690 string tclass = p.getArg('{', '}');
2691 if (contains(tclass, '/')) {
2692 // It's considered bad practice, but it is still
2693 // sometimes done (and possible) to enter the documentclass
2694 // as a path, e.g. \documentclass{subdir/class} (#12284)
2695 // we strip the name in this case.
2697 tclass = rsplit(tclass, dummy, '/');
2700 // Only set text class if a class hasn't been forced
2701 // (this was set above)
2703 // textclass needs to be set at this place (if not already done)
2704 // as we need to know it for other parameters
2705 // (such as class-dependent paper size)
2706 setTextClass(tclass, tc);
2711 // Try those who are (most likely) known to all packages first
2712 handle_opt(opts, known_fontsizes, h_paperfontsize);
2713 delete_opt(opts, known_fontsizes);
2714 // delete "pt" at the end
2715 string::size_type i = h_paperfontsize.find("pt");
2716 if (i != string::npos)
2717 h_paperfontsize.erase(i);
2718 // Now those known specifically to the class
2719 vector<string> class_fsizes = getVectorFromString(tc.opt_fontsize(), "|");
2720 string const fsize_format = tc.fontsizeformat();
2721 for (auto const & fsize : class_fsizes) {
2722 string latexsize = subst(fsize_format, "$$s", fsize);
2723 vector<string>::iterator it = find(opts.begin(), opts.end(), latexsize);
2724 if (it != opts.end()) {
2725 h_paperfontsize = fsize;
2731 // The documentclass options are always parsed before the options
2732 // of the babel call so that a language cannot overwrite the babel
2734 handle_opt(opts, known_languages, h_language);
2735 delete_opt(opts, known_languages);
2738 if ((it = find(opts.begin(), opts.end(), "fleqn"))
2740 h_is_mathindent = "1";
2743 // formula numbering side
2744 if ((it = find(opts.begin(), opts.end(), "leqno"))
2746 h_math_numbering_side = "left";
2749 else if ((it = find(opts.begin(), opts.end(), "reqno"))
2751 h_math_numbering_side = "right";
2755 // paper orientation
2756 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
2757 h_paperorientation = "landscape";
2761 if ((it = find(opts.begin(), opts.end(), "oneside"))
2766 if ((it = find(opts.begin(), opts.end(), "twoside"))
2772 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
2774 h_papercolumns = "1";
2777 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
2779 h_papercolumns = "2";
2783 // some size options are known by the document class, other sizes
2784 // are handled by the \geometry command of the geometry package
2785 vector<string> class_psizes = getVectorFromString(tc.opt_pagesize(), "|");
2786 string const psize_format = tc.pagesizeformat();
2787 for (auto const & psize : class_psizes) {
2788 string latexsize = subst(psize_format, "$$s", psize);
2789 vector<string>::iterator it = find(opts.begin(), opts.end(), latexsize);
2790 if (it != opts.end()) {
2791 h_papersize = psize;
2795 if (psize_format == "$$spaper")
2797 // Also try with the default format since this is understood by
2799 latexsize = psize + "paper";
2800 it = find(opts.begin(), opts.end(), latexsize);
2801 if (it != opts.end()) {
2802 h_papersize = psize;
2807 // the remaining options
2808 h_options = join(opts, ",");
2812 if (t.cs() == "DocumentMetadata") {
2813 h_doc_metadata = trimSpaceAndEol(p.getArg('{', '}'));
2817 if (t.cs() == "usepackage") {
2818 string const options = p.getArg('[', ']');
2819 string const name = p.getArg('{', '}');
2820 vector<string> vecnames;
2821 split(name, vecnames, ',');
2822 vector<string>::const_iterator it = vecnames.begin();
2823 vector<string>::const_iterator end = vecnames.end();
2824 for (; it != end; ++it)
2825 handle_package(p, trimSpaceAndEol(*it), options,
2826 in_lyx_preamble, detectEncoding);
2830 if (t.cs() == "inputencoding") {
2831 string const encoding = p.getArg('{','}');
2832 Encoding const * const enc = encodings.fromLaTeXName(
2833 encoding, Encoding::inputenc, true);
2835 if (!detectEncoding)
2836 warning_message("Unknown encoding " + encoding + ". Ignoring.");
2839 h_inputencoding = enc->name();
2840 p.setEncoding(enc->iconvName());
2845 if (t.cs() == "newenvironment") {
2846 string const name = p.getArg('{', '}');
2847 string const opt1 = p.getFullOpt();
2848 string const opt2 = p.getFullOpt();
2849 string const beg = p.verbatim_item();
2850 string const end = p.verbatim_item();
2851 if (!in_lyx_preamble) {
2852 h_preamble << "\\newenvironment{" << name
2853 << '}' << opt1 << opt2 << '{'
2854 << beg << "}{" << end << '}';
2856 add_known_environment(name, opt1, !opt2.empty(),
2857 from_utf8(beg), from_utf8(end));
2861 if (t.cs() == "newtheorem") {
2863 if (p.next_token().character() == '*') {
2867 string const name = p.getArg('{', '}');
2868 string const opt1 = p.getFullOpt();
2869 string const opt2 = p.getFullOpt();
2870 string const body = p.verbatim_item();
2871 string const opt3 = p.getFullOpt();
2872 string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
2874 string const complete = cmd + "{" + name + '}' +
2875 opt1 + opt2 + '{' + body + '}' + opt3;
2877 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
2879 if (!in_lyx_preamble)
2880 h_preamble << complete;
2884 if (t.cs() == "def") {
2885 string name = p.get_token().cs();
2886 // In fact, name may be more than the name:
2887 // In the test case of bug 8116
2888 // name == "csname SF@gobble@opt \endcsname".
2889 // Therefore, we need to use asInput() instead of cs().
2890 while (p.next_token().cat() != catBegin)
2891 name += p.get_token().asInput();
2892 if (!in_lyx_preamble)
2893 h_preamble << "\\def\\" << name << '{'
2894 << p.verbatim_item() << "}";
2898 if (t.cs() == "newcolumntype") {
2899 string const name = p.getArg('{', '}');
2900 trimSpaceAndEol(name);
2902 string opts = p.getOpt();
2903 if (!opts.empty()) {
2904 istringstream is(string(opts, 1));
2907 special_columns_[name[0]] = nargs;
2908 h_preamble << "\\newcolumntype{" << name << "}";
2910 h_preamble << "[" << nargs << "]";
2911 h_preamble << "{" << p.verbatim_item() << "}";
2915 if (t.cs() == "setcounter") {
2916 string const name = p.getArg('{', '}');
2917 string const content = p.getArg('{', '}');
2918 if (name == "secnumdepth")
2919 h_secnumdepth = content;
2920 else if (name == "tocdepth")
2921 h_tocdepth = content;
2923 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
2927 if (t.cs() == "setlength") {
2928 string const name = p.verbatim_item();
2929 string const content = p.verbatim_item();
2930 // the paragraphs are only not indented when \parindent is set to zero
2931 if (name == "\\parindent" && content != "")
2932 h_paragraph_indentation = translate_len(content);
2933 else if (name == "\\parskip" && isPackageUsed("parskip")) {
2934 if (content == "\\smallskipamount")
2935 h_defskip = "smallskip";
2936 else if (content == "\\medskipamount")
2937 h_defskip = "medskip";
2938 else if (content == "\\bigskipamount")
2939 h_defskip = "bigskip";
2940 else if (content == "\\baselineskip")
2941 h_defskip = "fullline";
2943 h_defskip = translate_len(content);
2944 } else if (name == "\\mathindent") {
2945 h_mathindentation = translate_len(content);
2947 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2951 if (t.cs() == "onehalfspacing") {
2952 h_spacing = "onehalf";
2956 if (t.cs() == "doublespacing") {
2957 h_spacing = "double";
2961 if (t.cs() == "setstretch") {
2962 h_spacing = "other " + p.verbatim_item();
2966 if (t.cs() == "synctex") {
2967 // the scheme is \synctex=value
2968 // where value can only be "1" or "-1"
2969 h_output_sync = "1";
2970 // there can be any character behind the value (e.g. a linebreak or a '\'
2971 // therefore we extract it char by char
2973 string value = p.get_token().asInput();
2975 value += p.get_token().asInput();
2976 h_output_sync_macro = "\\synctex=" + value;
2980 if (t.cs() == "begin") {
2981 string const name = p.getArg('{', '}');
2982 if (name == "document")
2984 h_preamble << "\\begin{" << name << "}";
2988 if (t.cs() == "geometry") {
2989 vector<string> opts = split_options(p.getArg('{', '}'));
2990 handle_geometry(opts);
2994 if (t.cs() == "definecolor") {
2995 string const color = p.getArg('{', '}');
2996 string const space = p.getArg('{', '}');
2997 string const value = p.getArg('{', '}');
2998 if (color == "document_fontcolor" && space == "rgb") {
2999 RGBColor c(RGBColorFromLaTeX(value));
3000 h_fontcolor = X11hexname(c);
3001 } else if (color == "note_fontcolor" && space == "rgb") {
3002 RGBColor c(RGBColorFromLaTeX(value));
3003 h_notefontcolor = X11hexname(c);
3004 } else if (color == "page_backgroundcolor" && space == "rgb") {
3005 RGBColor c(RGBColorFromLaTeX(value));
3006 h_backgroundcolor = X11hexname(c);
3007 } else if (color == "shadecolor" && space == "rgb") {
3008 RGBColor c(RGBColorFromLaTeX(value));
3009 h_boxbgcolor = X11hexname(c);
3011 h_preamble << "\\definecolor{" << color
3012 << "}{" << space << "}{" << value
3018 if (t.cs() == "bibliographystyle") {
3019 h_biblio_style = p.verbatim_item();
3023 if (t.cs() == "jurabibsetup") {
3024 // FIXME p.getArg('{', '}') is most probably wrong (it
3025 // does not handle nested braces).
3026 // Use p.verbatim_item() instead.
3027 vector<string> jurabibsetup =
3028 split_options(p.getArg('{', '}'));
3029 // add jurabibsetup to the jurabib package options
3030 add_package("jurabib", jurabibsetup);
3031 if (!jurabibsetup.empty()) {
3032 h_preamble << "\\jurabibsetup{"
3033 << join(jurabibsetup, ",") << '}';
3038 if (t.cs() == "hypersetup") {
3039 vector<string> hypersetup =
3040 split_options(p.verbatim_item());
3041 // add hypersetup to the hyperref package options
3042 handle_hyperref(hypersetup);
3043 if (!hypersetup.empty()) {
3044 h_preamble << "\\hypersetup{"
3045 << join(hypersetup, ",") << '}';
3050 if (t.cs() == "includeonly") {
3051 vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
3052 for (auto & iofile : includeonlys) {
3053 string filename(normalize_filename(iofile));
3054 string const path = getMasterFilePath(true);
3055 // We want to preserve relative/absolute filenames,
3056 // therefore path is only used for testing
3057 if (!makeAbsPath(filename, path).exists()) {
3058 // The file extension is probably missing.
3059 // Now try to find it out.
3060 string const tex_name =
3061 find_file(filename, path,
3062 known_tex_extensions);
3063 if (!tex_name.empty())
3064 filename = tex_name;
3067 if (makeAbsPath(filename, path).exists())
3068 fix_child_filename(filename);
3070 warning_message("Warning: Could not find included file '"
3072 outname = changeExtension(filename, "lyx");
3073 h_includeonlys.push_back(outname);
3078 if (is_known(t.cs(), known_if_3arg_commands)) {
3079 // prevent misparsing of \usepackage if it is used
3080 // as an argument (see e.g. our own output of
3081 // \@ifundefined above)
3082 string const arg1 = p.verbatim_item();
3083 string const arg2 = p.verbatim_item();
3084 string const arg3 = p.verbatim_item();
3085 // test case \@ifundefined{date}{}{\date{}}
3086 if (t.cs() == "@ifundefined" && arg1 == "date" &&
3087 arg2.empty() && arg3 == "\\date{}") {
3088 h_suppress_date = "true";
3089 // older tex2lyx versions did output
3090 // \@ifundefined{definecolor}{\usepackage{color}}{}
3091 } else if (t.cs() == "@ifundefined" &&
3092 arg1 == "definecolor" &&
3093 arg2 == "\\usepackage{color}" &&
3095 if (!in_lyx_preamble)
3096 h_preamble << package_beg_sep
3099 << "\\@ifundefined{definecolor}{color}{}"
3102 //\@ifundefined{showcaptionsetup}{}{%
3103 // \PassOptionsToPackage{caption=false}{subfig}}
3104 // that LyX uses for subfloats
3105 } else if (t.cs() == "@ifundefined" &&
3106 arg1 == "showcaptionsetup" && arg2.empty()
3107 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
3109 } else if (!in_lyx_preamble) {
3110 h_preamble << t.asInput()
3111 << '{' << arg1 << '}'
3112 << '{' << arg2 << '}'
3113 << '{' << arg3 << '}';
3118 if (is_known(t.cs(), known_if_commands)) {
3119 // must not parse anything in conditional code, since
3120 // LyX would output the parsed contents unconditionally
3121 if (!in_lyx_preamble)
3122 h_preamble << t.asInput();
3123 handle_if(p, in_lyx_preamble);
3127 if (!t.cs().empty() && !in_lyx_preamble) {
3128 h_preamble << '\\' << t.cs();
3133 // set textclass if not yet done (snippets without \documentclass and forced class)
3135 setTextClass(h_textclass, tc);
3137 // remove the whitespace
3140 if (h_papersides.empty()) {
3143 h_papersides = ss.str();
3146 // If the CJK package is used we cannot set the document language from
3147 // the babel options. Instead, we guess which language is used most
3148 // and set this one.
3149 default_language = h_language;
3150 if (is_full_document &&
3151 (auto_packages.find("CJK") != auto_packages.end() ||
3152 auto_packages.find("CJKutf8") != auto_packages.end())) {
3154 h_language = guessLanguage(p, default_language);
3156 if (explicit_babel && h_language != default_language) {
3157 // We set the document language to a CJK language,
3158 // but babel is explicitly called in the user preamble
3159 // without options. LyX will not add the default
3160 // language to the document options if it is either
3161 // english, or no text is set as default language.
3162 // Therefore we need to add a language option explicitly.
3163 // FIXME: It would be better to remove all babel calls
3164 // from the user preamble, but this is difficult
3165 // without re-introducing bug 7861.
3166 if (h_options.empty())
3167 h_options = lyx2babel(default_language);
3169 h_options += ',' + lyx2babel(default_language);
3173 // Finally, set the quote style.
3174 // LyX knows the following quotes styles:
3175 // british, cjk, cjkangle, danish, english, french, german,
3176 // polish, russian, swedish and swiss
3177 // conversion list taken from
3178 // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
3179 // (quotes for kazakh are unknown)
3181 if (is_known(h_language, known_british_quotes_languages))
3182 h_quotes_style = "british";
3184 else if (is_known(h_language, known_cjk_quotes_languages))
3185 h_quotes_style = "cjk";
3187 else if (is_known(h_language, known_cjkangle_quotes_languages))
3188 h_quotes_style = "cjkangle";
3190 else if (is_known(h_language, known_danish_quotes_languages))
3191 h_quotes_style = "danish";
3193 else if (is_known(h_language, known_french_quotes_languages))
3194 h_quotes_style = "french";
3196 else if (is_known(h_language, known_german_quotes_languages))
3197 h_quotes_style = "german";
3199 else if (is_known(h_language, known_polish_quotes_languages))
3200 h_quotes_style = "polish";
3202 else if (is_known(h_language, known_hungarian_quotes_languages))
3203 h_quotes_style = "hungarian";
3205 else if (is_known(h_language, known_russian_quotes_languages))
3206 h_quotes_style = "russian";
3208 else if (is_known(h_language, known_swedish_quotes_languages))
3209 h_quotes_style = "swedish";
3211 else if (is_known(h_language, known_swiss_quotes_languages))
3212 h_quotes_style = "swiss";
3214 else if (is_known(h_language, known_english_quotes_languages))
3215 h_quotes_style = "english";
3219 string Preamble::parseEncoding(Parser & p, string const & forceclass)
3221 TeX2LyXDocClass dummy;
3222 parse(p, forceclass, true, dummy);
3223 if (h_inputencoding != "auto-legacy" && h_inputencoding != "auto-legacy-plain")
3224 return h_inputencoding;
3229 string babel2lyx(string const & language)
3231 char const * const * where = is_known(language, known_languages);
3233 return known_coded_languages[where - known_languages];
3238 string lyx2babel(string const & language)
3240 char const * const * where = is_known(language, known_coded_languages);
3242 return known_languages[where - known_coded_languages];
3247 string Preamble::polyglossia2lyx(string const & language)
3249 char const * const * where = is_known(language, polyglossia_languages);
3251 return coded_polyglossia_languages[where - polyglossia_languages];
3256 string rgbcolor2code(string const & name)
3258 char const * const * where = is_known(name, known_basic_colors);
3260 // "red", "green" etc
3261 return known_basic_color_codes[where - known_basic_colors];
3263 // "255,0,0", "0,255,0" etc
3264 RGBColor c(RGBColorFromLaTeX(name));
3265 return X11hexname(c);