3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
9 * Full author contact details are available in file CREDITS.
20 #include "LaTeXPackages.h"
21 #include "LayoutFile.h"
22 #include "TextClass.h"
25 #include "support/convert.h"
26 #include "support/FileName.h"
27 #include "support/filetools.h"
28 #include "support/Lexer.h"
29 #include "support/lstrings.h"
36 using namespace lyx::support;
45 // CJK languages are handled in text.cpp, polyglossia languages are listed
48 * known babel language names (including synonyms)
49 * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
50 * please keep this in sync with known_coded_languages line by line!
52 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
53 "american", "arabic", "arabtex", "australian", "austrian", "azerbaijani", "bahasa", "bahasai",
54 "bahasam", "basque", "belarusian", "bosnian", "brazil", "brazilian", "breton", "british",
55 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
56 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "francais",
57 "french", "frenchb", "frenchle", "frenchpro", "friulan", "galician", "german", "germanb",
58 "georgian", "greek", "hebrew", "hungarian", "icelandic", "indon", "indonesian",
59 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
60 "latvian", "lithuanian", "lowersorbian", "lsorbian", "macedonian", "magyar", "malay", "meyalu",
61 "mongolian", "naustrian", "newzealand", "ngerman", "ngermanb", "norsk", "nswissgerman",
62 "nynorsk", "piedmontese", "polutonikogreek", "polish", "portuges", "portuguese",
63 "romanian", "romansh", "russian", "russianb", "samin", "scottish", "serbian", "serbian-latin",
64 "slovak", "slovene", "spanish", "swedish", "swissgerman", "thai", "turkish", "turkmen",
65 "ukraineb", "ukrainian", "uppersorbian", "UKenglish", "USenglish", "usorbian",
70 * the same as known_languages with .lyx names
71 * please keep this in sync with known_languages line by line!
73 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
74 "american", "arabic_arabi", "arabic_arabtex", "australian", "austrian", "azerbaijani", "bahasa", "bahasa",
75 "bahasam", "basque", "belarusian", "bosnian", "brazilian", "brazilian", "breton", "british",
76 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
77 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "french",
78 "french", "french", "french", "french", "friulan", "galician", "german", "german",
79 "georgian", "greek", "hebrew", "magyar", "icelandic", "bahasa", "bahasa",
80 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
81 "latvian", "lithuanian", "lowersorbian", "lowersorbian", "macedonian", "magyar", "bahasam", "bahasam",
82 "mongolian", "naustrian", "newzealand", "ngerman", "ngerman", "norsk", "german-ch",
83 "nynorsk", "piedmontese", "polutonikogreek", "polish", "portuguese", "portuguese",
84 "romanian", "romansh", "russian", "russian", "samin", "scottish", "serbian", "serbian-latin",
85 "slovak", "slovene", "spanish", "swedish", "german-ch-old", "thai", "turkish", "turkmen",
86 "ukrainian", "ukrainian", "uppersorbian", "english", "english", "uppersorbian",
87 "vietnamese", "welsh",
90 /// languages with british quotes (.lyx names)
91 const char * const known_british_quotes_languages[] = {"british", "welsh", 0};
93 /// languages with cjk quotes (.lyx names)
94 const char * const known_cjk_quotes_languages[] = {"chinese-traditional",
95 "japanese", "japanese-cjk", 0};
97 /// languages with cjk-angle quotes (.lyx names)
98 const char * const known_cjkangle_quotes_languages[] = {"korean", 0};
100 /// languages with danish quotes (.lyx names)
101 const char * const known_danish_quotes_languages[] = {"danish", 0};
103 /// languages with english quotes (.lyx names)
104 const char * const known_english_quotes_languages[] = {"american", "australian",
105 "bahasa", "bahasam", "bengali", "brazilian", "canadian", "chinese-simplified", "english",
106 "esperanto", "farsi", "interlingua", "irish", "newzealand", "scottish",
107 "thai", "turkish", "vietnamese", 0};
109 /// languages with french quotes (.lyx names)
110 const char * const known_french_quotes_languages[] = {"ancientgreek",
111 "arabic_arabi", "arabic_arabtex", "asturian", "belarusian", "breton",
112 "canadien", "catalan", "french", "friulan", "galician", "italian", "occitan",
113 "piedmontese", "portuguese", "spanish", "spanish-mexico", 0};
115 /// languages with german quotes (.lyx names)
116 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
117 "czech", "estonian", "georgian", "german", "icelandic", "latvian", "lithuanian",
118 "lowersorbian", "macedonian", "naustrian", "ngerman", "romansh", "slovak", "slovene",
121 /// languages with polish quotes (.lyx names)
122 const char * const known_polish_quotes_languages[] = {"afrikaans", "bosnian", "croatian",
123 "dutch", "magyar", "polish", "romanian", "serbian", "serbian-latin", 0};
125 /// languages with hungarian quotes (.lyx names)
126 const char * const known_hungarian_quotes_languages[] = {"magyar", 0};
128 /// languages with russian quotes (.lyx names)
129 const char * const known_russian_quotes_languages[] = {"azerbaijani", "oldrussian",
130 "russian", "ukrainian", 0};
132 /// languages with swedish quotes (.lyx names)
133 const char * const known_swedish_quotes_languages[] = {"finnish", "swedish", 0};
135 /// languages with swiss quotes (.lyx names)
136 const char * const known_swiss_quotes_languages[] = {"albanian",
137 "armenian", "basque", "churchslavonic", "german-ch", "german-ch-old",
138 "norsk", "nynorsk", "turkmen", "ukrainian", "vietnamese", 0};
140 /// languages with hebrew quotes (.lyx names)
141 const char * const known_hebrew_quotes_languages[] = {"hebrew", 0};
143 /// known language packages from the times before babel
144 const char * const known_old_language_packages[] = {"french", "frenchle",
145 "frenchpro", "german", "ngerman", "pmfrench", 0};
147 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
149 const char * const known_roman_font_packages[] = { "ae", "beraserif", "bookman",
150 "ccfonts", "chancery", "charter", "cmr", "cochineal", "crimson", "CrimsonPro", "DejaVuSerif",
151 "DejaVuSerifCondensed", "fourier", "garamondx", "libertine", "libertineRoman", "libertine-type1",
152 "lmodern", "mathdesign", "mathpazo", "mathptmx", "MinionPro", "newcent", "noto", "noto-serif",
153 "PTSerif", "tgbonum", "tgchorus", "tgpagella", "tgschola", "tgtermes", "utopia", "xcharter", 0 };
155 const char * const known_sans_font_packages[] = { "avant", "berasans", "biolinum",
156 "biolinum-type1", "cantarell", "Chivo", "cmbr", "cmss", "DejaVuSans", "DejaVuSansCondensed", "FiraSans", "helvet", "iwona",
157 "iwonac", "iwonal", "iwonalc", "kurier", "kurierc", "kurierl", "kurierlc", "LibertinusSans-LF", "lmss", "noto-sans", "PTSans",
158 "tgadventor", "tgheros", "uop", 0 };
160 const char * const known_typewriter_font_packages[] = { "beramono", "cmtl", "cmtt", "courier", "DejaVuSansMono",
161 "FiraMono", "lmtt", "luximono", "libertineMono", "libertineMono-type1", "LibertinusMono-TLF", "lmodern",
162 "mathpazo", "mathptmx", "newcent", "noto-mono", "PTMono", "tgcursor", "txtt", 0 };
164 const char * const known_math_font_packages[] = { "eulervm", "newtxmath", 0};
166 const char * const known_latex_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
167 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
168 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
169 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
170 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
172 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
173 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
175 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
176 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
179 /// commands that can start an \if...\else...\endif sequence
180 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
181 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
182 "ifsidecap", "ifupgreek", 0};
184 const char * const known_basic_colors[] = {"black", "blue", "brown", "cyan",
185 "darkgray", "gray", "green", "lightgray", "lime", "magenta", "orange", "olive",
186 "pink", "purple", "red", "teal", "violet", "white", "yellow", 0};
188 const char * const known_basic_color_codes[] = {"#000000", "#0000ff", "#964B00", "#00ffff",
189 "#a9a9a9", "#808080", "#00ff00", "#d3d3d3", "#bfff00", "#ff00ff", "#ff7f00", "#808000",
190 "#ffc0cb", "#800080", "#ff0000", "#008080", "#8f00ff", "#ffffff", "#ffff00", 0};
192 /// conditional commands with three arguments like \@ifundefined{}{}{}
193 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
197 * Known file extensions for TeX files as used by \\includeonly
199 char const * const known_tex_extensions[] = {"tex", 0};
201 /// packages that work only in xetex
202 /// polyglossia is handled separately
203 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
204 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
205 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
207 /// packages that are automatically skipped if loaded by LyX
208 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
209 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color",
210 "float", "fontspec", "framed", "graphicx", "hhline", "ifthen", "longtable",
211 "makeidx", "minted", "multirow", "nomencl", "parskip", "pdfpages", "prettyref", "refstyle",
212 "rotating", "rotfloat", "splitidx", "setspace", "subscript", "tabularx","textcomp", "tipa",
213 "tipx", "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xcolor", "xltabular",
216 // codes used to remove packages that are loaded automatically by LyX.
217 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
218 const char package_beg_sep = '\001';
219 const char package_mid_sep = '\002';
220 const char package_end_sep = '\003';
223 // returns true if at least one of the options in what has been found
224 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
230 // the last language option is the document language (for babel and LyX)
231 // the last size option is the document font size
232 vector<string>::iterator it;
233 vector<string>::iterator position = opts.begin();
234 for (; *what; ++what) {
235 it = find(opts.begin(), opts.end(), *what);
236 if (it != opts.end()) {
237 if (it >= position) {
248 void delete_opt(vector<string> & opts, char const * const * what)
253 // remove found options from the list
254 // do this after handle_opt to avoid potential memory leaks
255 vector<string>::iterator it;
256 for (; *what; ++what) {
257 it = find(opts.begin(), opts.end(), *what);
258 if (it != opts.end())
265 * Split a package options string (keyval format) into a vector.
267 * authorformat=smallcaps,
269 * titleformat=colonsep,
270 * bibformat={tabular,ibidem,numbered}
272 vector<string> split_options(string const & input)
274 vector<string> options;
278 Token const & t = p.get_token();
279 if (t.asInput() == ",") {
280 options.push_back(trimSpaceAndEol(option));
282 } else if (t.asInput() == "=") {
285 if (p.next_token().asInput() == "{")
286 option += '{' + p.getArg('{', '}') + '}';
287 } else if (t.cat() != catSpace && t.cat() != catComment)
288 option += t.asInput();
292 options.push_back(trimSpaceAndEol(option));
299 * Retrieve a keyval option "name={value with=sign}" named \p name from
300 * \p options and return the value.
301 * The found option is also removed from \p options.
303 string process_keyval_opt(vector<string> & options, string const & name)
305 for (size_t i = 0; i < options.size(); ++i) {
306 vector<string> option;
307 split(options[i], option, '=');
308 if (option.size() < 2)
310 if (option[0] == name) {
311 options.erase(options.begin() + i);
312 option.erase(option.begin());
313 return join(option, "=");
319 } // anonymous namespace
323 * known polyglossia language names (including variants)
324 * FIXME: support spelling=old for german variants (german vs. ngerman LyX names etc)
326 const char * const Preamble::polyglossia_languages[] = {
327 "albanian", "american", "amharic", "ancient", "arabic", "armenian", "asturian", "australian",
328 "bahasai", "bahasam", "basque", "bengali", "brazil", "brazilian", "breton", "british", "bulgarian",
329 "catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
330 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
331 "galician", "greek", "monotonic", "hebrew", "hindi",
332 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean",
333 "lao", "latin", "latvian", "lithuanian", "lsorbian", "magyar", "malayalam", "marathi",
334 "austrian", "newzealand", "german", "norsk", "nynorsk", "occitan", "oldrussian",
335 "piedmontese", "polish", "polytonic", "portuges", "romanian", "romansh", "russian",
336 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovenian", "spanish", "swedish", "syriac",
337 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
338 "ukrainian", "urdu", "usorbian", "vietnamese", "welsh", 0};
339 // not yet supported by LyX: "korean", "nko"
342 * the same as polyglossia_languages with .lyx names
343 * please keep this in sync with polyglossia_languages line by line!
345 const char * const Preamble::coded_polyglossia_languages[] = {
346 "albanian", "american", "amharic", "ancientgreek", "arabic_arabi", "armenian", "asturian", "australian",
347 "bahasa", "bahasam", "basque", "bengali", "brazilian", "brazilian", "breton", "british", "bulgarian",
348 "catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
349 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
350 "galician", "greek", "greek", "hebrew", "hindi",
351 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean",
352 "lao", "latin", "latvian", "lithuanian", "lowersorbian", "magyar", "malayalam", "marathi",
353 "naustrian","newzealand", "ngerman", "norsk", "nynorsk", "occitan", "oldrussian",
354 "piedmontese", "polish", "polutonikogreek", "portuges", "romanian", "romansh", "russian",
355 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovene", "spanish", "swedish", "syriac",
356 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
357 "ukrainian", "urdu", "uppersorbian", "vietnamese", "welsh", 0};
358 // not yet supported by LyX: "korean-polyglossia", "nko"
361 bool Preamble::usePolyglossia() const
363 return h_use_non_tex_fonts && h_language_package == "default";
367 bool Preamble::indentParagraphs() const
369 return h_paragraph_separation == "indent";
373 bool Preamble::isPackageUsed(string const & package) const
375 return used_packages.find(package) != used_packages.end();
379 bool Preamble::isPackageAutoLoaded(string const & package) const
381 return auto_packages.find(package) != auto_packages.end();
385 vector<string> Preamble::getPackageOptions(string const & package) const
387 map<string, vector<string> >::const_iterator it = used_packages.find(package);
388 if (it != used_packages.end())
390 return vector<string>();
394 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
396 auto_packages.insert(package);
400 void Preamble::addModule(string const & module)
402 for (auto const & m : used_modules) {
406 used_modules.push_back(module);
410 void Preamble::suppressDate(bool suppress)
413 h_suppress_date = "true";
415 h_suppress_date = "false";
419 void Preamble::registerAuthor(std::string const & name, string const & initials)
421 Author author(from_utf8(name), empty_docstring(), from_utf8(initials));
422 author.setUsed(true);
423 authors_.record(author);
424 h_tracking_changes = "true";
425 h_output_changes = "true";
429 Author const & Preamble::getAuthor(std::string const & name) const
431 Author author(from_utf8(name), empty_docstring(), empty_docstring());
432 for (AuthorList::Authors::const_iterator it = authors_.begin();
433 it != authors_.end(); ++it)
436 static Author const dummy;
441 int Preamble::getSpecialTableColumnArguments(char c) const
443 map<char, int>::const_iterator it = special_columns_.find(c);
444 if (it == special_columns_.end())
450 void Preamble::add_package(string const & name, vector<string> & options)
452 // every package inherits the global options
453 used_packages.insert({name, split_options(h_options)});
455 // Insert options passed via PassOptionsToPackage
456 for (auto const & p : extra_package_options_) {
457 if (p.first == name) {
458 vector<string> eo = getVectorFromString(p.second);
459 for (auto const & eoi : eo)
460 options.push_back(eoi);
464 vector<string> & v = used_packages[name];
465 v.insert(v.end(), options.begin(), options.end());
466 if (name == "jurabib") {
467 // Don't output the order argument (see the cite command
468 // handling code in text.cpp).
469 vector<string>::iterator end =
470 remove(options.begin(), options.end(), "natbiborder");
471 end = remove(options.begin(), end, "jurabiborder");
472 options.erase(end, options.end());
476 void Preamble::setTextClass(string const & tclass, TeX2LyXDocClass & tc)
478 h_textclass = tclass;
479 tc.setName(h_textclass);
480 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
481 error_message("Could not read layout file for textclass \"" + h_textclass + "\".");
489 // Given is a string like "scaled=0.9" or "scale=0.9", return 0.9 * 100
490 bool scale_as_percentage(string const & scale, string & percentage)
492 if (contains(scale, '=')) {
493 string const value = support::split(scale, '=');
494 if (isStrDbl(value)) {
495 percentage = convert<string>(
496 static_cast<int>(100 * convert<double>(value)));
504 string remove_braces(string const & value)
508 if (value[0] == '{' && value[value.length()-1] == '}')
509 return value.substr(1, value.length()-2);
513 } // anonymous namespace
516 Preamble::Preamble() : one_language(true), explicit_babel(false),
517 title_layout_found(false), index_number(0), h_font_cjk_set(false)
521 h_biblio_style = "plain";
522 h_bibtex_command = "default";
523 h_cite_engine = "basic";
524 h_cite_engine_type = "default";
526 h_defskip = "medskip";
527 h_dynamic_quotes = false;
530 h_fontencoding = "default";
531 h_font_roman[0] = "default";
532 h_font_roman[1] = "default";
533 h_font_sans[0] = "default";
534 h_font_sans[1] = "default";
535 h_font_typewriter[0] = "default";
536 h_font_typewriter[1] = "default";
537 h_font_math[0] = "auto";
538 h_font_math[1] = "auto";
539 h_font_default_family = "default";
540 h_use_non_tex_fonts = false;
542 h_font_roman_osf = "false";
543 h_font_sans_osf = "false";
544 h_font_typewriter_osf = "false";
545 h_font_sf_scale[0] = "100";
546 h_font_sf_scale[1] = "100";
547 h_font_tt_scale[0] = "100";
548 h_font_tt_scale[1] = "100";
549 // h_font_roman_opts;
551 // h_font_typewriter_opts;
553 h_is_mathindent = "0";
554 h_math_numbering_side = "default";
555 h_graphics = "default";
556 h_default_output_format = "default";
557 h_html_be_strict = "false";
558 h_html_css_as_file = "0";
559 h_html_math_output = "0";
560 h_docbook_table_output = "0";
561 h_docbook_mathml_prefix = "1";
562 h_index[0] = "Index";
563 h_index_command = "default";
564 h_inputencoding = "auto-legacy";
565 h_justification = "true";
566 h_language = "english";
567 h_language_package = "none";
569 h_maintain_unincluded_children = "no";
573 h_output_changes = "false";
574 h_change_bars = "false";
576 //h_output_sync_macro
577 h_papercolumns = "1";
578 h_paperfontsize = "default";
579 h_paperorientation = "portrait";
580 h_paperpagestyle = "default";
582 h_papersize = "default";
583 h_paragraph_indentation = "default";
584 h_paragraph_separation = "indent";
589 h_pdf_bookmarks = "0";
590 h_pdf_bookmarksnumbered = "0";
591 h_pdf_bookmarksopen = "0";
592 h_pdf_bookmarksopenlevel = "1";
593 h_pdf_breaklinks = "0";
594 h_pdf_pdfborder = "0";
595 h_pdf_colorlinks = "0";
596 h_pdf_backref = "section";
597 h_pdf_pdfusetitle = "0";
599 //h_pdf_quoted_options;
600 h_quotes_style = "english";
602 h_shortcut[0] = "idx";
603 h_spacing = "single";
604 h_save_transient_properties = "true";
605 h_suppress_date = "false";
606 h_textclass = "article";
608 h_tracking_changes = "false";
609 h_use_bibtopic = "false";
610 h_use_dash_ligatures = "true";
611 h_use_indices = "false";
612 h_use_geometry = "false";
613 h_use_default_options = "false";
614 h_use_hyperref = "false";
615 h_use_microtype = "false";
616 h_use_lineno = "false";
617 h_use_refstyle = false;
618 h_use_minted = false;
619 h_use_packages["amsmath"] = "1";
620 h_use_packages["amssymb"] = "0";
621 h_use_packages["cancel"] = "0";
622 h_use_packages["esint"] = "1";
623 h_use_packages["mhchem"] = "0";
624 h_use_packages["mathdots"] = "0";
625 h_use_packages["mathtools"] = "0";
626 h_use_packages["stackrel"] = "0";
627 h_use_packages["stmaryrd"] = "0";
628 h_use_packages["undertilde"] = "0";
632 void Preamble::handle_hyperref(vector<string> & options)
634 // FIXME swallow inputencoding changes that might surround the
635 // hyperref setup if it was written by LyX
636 h_use_hyperref = "true";
637 // swallow "unicode=true", since LyX does always write that
638 vector<string>::iterator it =
639 find(options.begin(), options.end(), "unicode=true");
640 if (it != options.end())
642 it = find(options.begin(), options.end(), "pdfusetitle");
643 if (it != options.end()) {
644 h_pdf_pdfusetitle = "1";
647 string bookmarks = process_keyval_opt(options, "bookmarks");
648 if (bookmarks == "true")
649 h_pdf_bookmarks = "1";
650 else if (bookmarks == "false")
651 h_pdf_bookmarks = "0";
652 if (h_pdf_bookmarks == "1") {
653 string bookmarksnumbered =
654 process_keyval_opt(options, "bookmarksnumbered");
655 if (bookmarksnumbered == "true")
656 h_pdf_bookmarksnumbered = "1";
657 else if (bookmarksnumbered == "false")
658 h_pdf_bookmarksnumbered = "0";
659 string bookmarksopen =
660 process_keyval_opt(options, "bookmarksopen");
661 if (bookmarksopen == "true")
662 h_pdf_bookmarksopen = "1";
663 else if (bookmarksopen == "false")
664 h_pdf_bookmarksopen = "0";
665 if (h_pdf_bookmarksopen == "1") {
666 string bookmarksopenlevel =
667 process_keyval_opt(options, "bookmarksopenlevel");
668 if (!bookmarksopenlevel.empty())
669 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
672 string breaklinks = process_keyval_opt(options, "breaklinks");
673 if (breaklinks == "true")
674 h_pdf_breaklinks = "1";
675 else if (breaklinks == "false")
676 h_pdf_breaklinks = "0";
677 string pdfborder = process_keyval_opt(options, "pdfborder");
678 if (pdfborder == "{0 0 0}")
679 h_pdf_pdfborder = "1";
680 else if (pdfborder == "{0 0 1}")
681 h_pdf_pdfborder = "0";
682 string backref = process_keyval_opt(options, "backref");
683 if (!backref.empty())
684 h_pdf_backref = backref;
685 string colorlinks = process_keyval_opt(options, "colorlinks");
686 if (colorlinks == "true")
687 h_pdf_colorlinks = "1";
688 else if (colorlinks == "false")
689 h_pdf_colorlinks = "0";
690 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
691 if (!pdfpagemode.empty())
692 h_pdf_pagemode = pdfpagemode;
693 string pdftitle = process_keyval_opt(options, "pdftitle");
694 if (!pdftitle.empty()) {
695 h_pdf_title = remove_braces(pdftitle);
697 string pdfauthor = process_keyval_opt(options, "pdfauthor");
698 if (!pdfauthor.empty()) {
699 h_pdf_author = remove_braces(pdfauthor);
701 string pdfsubject = process_keyval_opt(options, "pdfsubject");
702 if (!pdfsubject.empty())
703 h_pdf_subject = remove_braces(pdfsubject);
704 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
705 if (!pdfkeywords.empty())
706 h_pdf_keywords = remove_braces(pdfkeywords);
707 if (!options.empty()) {
708 if (!h_pdf_quoted_options.empty())
709 h_pdf_quoted_options += ',';
710 h_pdf_quoted_options += join(options, ",");
716 void Preamble::handle_geometry(vector<string> & options)
718 h_use_geometry = "true";
719 vector<string>::iterator it;
721 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
722 h_paperorientation = "landscape";
726 // keyval version: "paper=letter" or "paper=letterpaper"
727 string paper = process_keyval_opt(options, "paper");
729 if (suffixIs(paper, "paper"))
730 paper = subst(paper, "paper", "");
731 // alternative version: "letterpaper"
732 handle_opt(options, known_latex_paper_sizes, paper);
733 if (suffixIs(paper, "paper"))
734 paper = subst(paper, "paper", "");
735 delete_opt(options, known_latex_paper_sizes);
739 char const * const * margin = known_paper_margins;
740 for (; *margin; ++margin) {
741 string value = process_keyval_opt(options, *margin);
742 if (!value.empty()) {
743 int k = margin - known_paper_margins;
744 string name = known_coded_paper_margins[k];
745 h_margins += '\\' + name + ' ' + value + '\n';
751 void Preamble::handle_package(Parser &p, string const & name,
752 string const & opts, bool in_lyx_preamble,
755 vector<string> options = split_options(opts);
756 add_package(name, options);
758 if (is_known(name, known_xetex_packages)) {
760 h_use_non_tex_fonts = true;
761 registerAutomaticallyLoadedPackage("fontspec");
762 if (h_inputencoding == "auto-legacy")
763 p.setEncoding("UTF-8");
764 } else if (h_inputencoding == "auto-legacy"
765 && LaTeXPackages::isAvailableAtLeastFrom("LaTeX", 2018, 04))
766 // As of LaTeX 2018/04/01, utf8 is the default input encoding
767 // So use that if no inputencoding is set
768 h_inputencoding = "utf8";
770 // vector of all options for easier parsing and
772 vector<string> allopts = getVectorFromString(opts);
773 // this stores the potential extra options
780 // By default, we use the package name as LyX font name,
781 // so this only needs to be reset if these names differ
782 if (is_known(name, known_roman_font_packages))
783 h_font_roman[0] = name;
785 if (name == "ccfonts") {
786 for (auto const & opt : allopts) {
792 h_font_roman_opts = xopts;
796 if (name == "lmodern") {
797 for (auto const & opt : allopts) {
803 h_font_roman_opts = xopts;
807 if (name == "fourier") {
808 h_font_roman[0] = "utopia";
809 for (auto const & opt : allopts) {
811 h_font_roman_osf = "true";
814 if (opt == "expert") {
823 h_font_roman_opts = xopts;
827 if (name == "garamondx") {
828 for (auto const & opt : allopts) {
830 h_font_roman_osf = "true";
838 h_font_roman_opts = xopts;
842 if (name == "libertine") {
843 // this automatically invokes biolinum
844 h_font_sans[0] = "biolinum";
845 // as well as libertineMono
846 h_font_typewriter[0] = "libertine-mono";
847 for (auto const & opt : allopts) {
849 h_font_roman_osf = "true";
852 if (opt == "lining") {
853 h_font_roman_osf = "false";
861 h_font_roman_opts = xopts;
865 if (name == "libertineRoman" || name == "libertine-type1") {
866 h_font_roman[0] = "libertine";
867 // NOTE: contrary to libertine.sty, libertineRoman
868 // and libertine-type1 do not automatically invoke
869 // biolinum and libertineMono
870 if (opts == "lining")
871 h_font_roman_osf = "false";
872 else if (opts == "osf")
873 h_font_roman_osf = "true";
876 if (name == "libertinus" || name == "libertinus-type1") {
883 for (auto const & opt : allopts) {
884 if (opt == "rm" || opt == "serif") {
889 if (opt == "sf" || opt == "sans") {
894 if (opt == "tt=false" || opt == "mono=false") {
902 if (opt == "scaleSF") {
906 if (opt == "scaleTT") {
910 if (opt == "lining") {
911 h_font_roman_osf = "false";
919 h_font_roman[0] = "libertinus";
921 h_font_roman_osf = "true";
923 h_font_roman_osf = "false";
926 h_font_sans[0] = "LibertinusSans-LF";
928 h_font_sans_osf = "true";
930 h_font_sans_osf = "false";
931 if (!scalesf.empty())
932 scale_as_percentage(scalesf, h_font_sf_scale[0]);
935 h_font_typewriter[0] = "LibertinusMono-TLF";
936 if (!scalett.empty())
937 scale_as_percentage(scalett, h_font_tt_scale[0]);
940 h_font_roman_opts = xopts;
944 if (name == "MinionPro") {
945 h_font_roman[0] = "minionpro";
946 h_font_roman_osf = "true";
947 h_font_math[0] = "auto";
948 for (auto const & opt : allopts) {
950 h_font_roman_osf = "false";
953 if (opt == "onlytext") {
954 h_font_math[0] = "default";
962 h_font_roman_opts = xopts;
966 if (name == "mathdesign") {
967 for (auto const & opt : allopts) {
968 if (opt == "charter") {
969 h_font_roman[0] = "md-charter";
972 if (opt == "garamond") {
973 h_font_roman[0] = "md-garamond";
976 if (opt == "utopia") {
977 h_font_roman[0] = "md-utopia";
980 if (opt == "expert") {
982 h_font_roman_osf = "true";
988 else if (name == "mathpazo") {
989 h_font_roman[0] = "palatino";
990 for (auto const & opt : allopts) {
992 h_font_roman_osf = "true";
1004 h_font_roman_opts = xopts;
1008 else if (name == "mathptmx") {
1009 h_font_roman[0] = "times";
1010 for (auto const & opt : allopts) {
1016 h_font_roman_opts = xopts;
1020 if (name == "crimson")
1021 h_font_roman[0] = "cochineal";
1023 if (name == "cochineal") {
1024 for (auto const & opt : allopts) {
1025 if (opt == "osf" || opt == "oldstyle") {
1026 h_font_roman_osf = "true";
1029 if (opt == "proportional" || opt == "p")
1036 h_font_roman_opts = xopts;
1040 if (name == "CrimsonPro") {
1041 h_font_roman_osf = "true";
1042 for (auto const & opt : allopts) {
1043 if (opt == "lf" || opt == "lining") {
1044 h_font_roman_osf = "false";
1047 if (opt == "proportional" || opt == "p")
1049 if (opt == "medium") {
1050 h_font_roman[0] = "CrimsonProMedium";
1053 if (opt == "extralight") {
1054 h_font_roman[0] = "CrimsonProExtraLight";
1057 if (opt == "light") {
1058 h_font_roman[0] = "CrimsonProLight";
1066 h_font_roman_opts = xopts;
1072 // font uses old-style figure
1073 h_font_roman_osf = "true";
1075 if (name == "paratype") {
1076 // in this case all fonts are ParaType
1077 h_font_roman[0] = "PTSerif-TLF";
1078 h_font_sans[0] = "default";
1079 h_font_typewriter[0] = "default";
1082 if (name == "PTSerif")
1083 h_font_roman[0] = "PTSerif-TLF";
1085 if (name == "XCharter") {
1086 h_font_roman[0] = "xcharter";
1087 for (auto const & opt : allopts) {
1089 h_font_roman_osf = "true";
1097 h_font_roman_opts = xopts;
1101 if (name == "plex-serif") {
1102 h_font_roman[0] = "IBMPlexSerif";
1103 for (auto const & opt : allopts) {
1104 if (opt == "thin") {
1105 h_font_roman[0] = "IBMPlexSerifThin";
1108 if (opt == "extralight") {
1109 h_font_roman[0] = "IBMPlexSerifExtraLight";
1112 if (opt == "light") {
1113 h_font_roman[0] = "IBMPlexSerifLight";
1121 h_font_roman_opts = xopts;
1125 if (name == "noto-serif" || name == "noto") {
1132 bool extralight = false;
1134 bool medium = false;
1137 if (name == "noto") {
1142 // Since the options might apply to different shapes,
1143 // we need to parse all options first and then handle them.
1144 for (auto const & opt : allopts) {
1145 if (opt == "regular")
1155 if (opt == "thin") {
1159 if (opt == "extralight") {
1163 if (opt == "light") {
1167 if (opt == "medium") {
1178 if (opt == "nott") {
1186 if (prefixIs(opt, "scaled=")) {
1195 // handle options that might affect different shapes
1196 if (name == "noto-serif" || rm) {
1198 h_font_roman[0] = "NotoSerifThin";
1199 else if (extralight)
1200 h_font_roman[0] = "NotoSerifExtralight";
1202 h_font_roman[0] = "NotoSerifLight";
1204 h_font_roman[0] = "NotoSerifMedium";
1206 h_font_roman[0] = "NotoSerifRegular";
1208 h_font_roman_osf = "true";
1210 h_font_roman_opts = xopts;
1212 if (name == "noto" && sf) {
1214 h_font_sans[0] = "NotoSansThin";
1215 else if (extralight)
1216 h_font_sans[0] = "NotoSansExtralight";
1218 h_font_sans[0] = "NotoSansLight";
1220 h_font_sans[0] = "NotoSansMedium";
1222 h_font_sans[0] = "NotoSansRegular";
1224 h_font_sans_osf = "true";
1226 scale_as_percentage(scl, h_font_sf_scale[0]);
1228 h_font_sans_opts = xopts;
1230 if (name == "noto" && tt) {
1231 h_font_typewriter[0] = "NotoMonoRegular";
1233 h_font_typewriter_osf = "true";
1235 scale_as_percentage(scl, h_font_tt_scale[0]);
1237 h_font_typewriter_opts = xopts;
1241 if (name == "sourceserifpro") {
1242 h_font_roman[0] = "ADOBESourceSerifPro";
1243 for (auto const & opt : allopts) {
1245 h_font_roman_osf = "true";
1253 h_font_roman_opts = xopts;
1261 // By default, we use the package name as LyX font name,
1262 // so this only needs to be reset if these names differ.
1263 // Also, we handle the scaling option here generally.
1264 if (is_known(name, known_sans_font_packages)) {
1265 h_font_sans[0] = name;
1266 if (contains(opts, "scale")) {
1267 vector<string>::iterator it = allopts.begin();
1268 for (; it != allopts.end() ; ++it) {
1269 string const opt = *it;
1270 if (prefixIs(opt, "scaled=") || prefixIs(opt, "scale=")) {
1271 if (scale_as_percentage(opt, h_font_sf_scale[0])) {
1280 if (name == "biolinum" || name == "biolinum-type1") {
1281 h_font_sans[0] = "biolinum";
1282 for (auto const & opt : allopts) {
1283 if (prefixIs(opt, "osf")) {
1284 h_font_sans_osf = "true";
1292 h_font_sans_opts = xopts;
1296 if (name == "cantarell") {
1297 for (auto const & opt : allopts) {
1298 if (opt == "defaultsans")
1300 if (prefixIs(opt, "oldstyle")) {
1301 h_font_sans_osf = "true";
1309 h_font_sans_opts = xopts;
1313 if (name == "Chivo") {
1314 for (auto const & opt : allopts) {
1315 if (opt == "thin") {
1316 h_font_roman[0] = "ChivoThin";
1319 if (opt == "light") {
1320 h_font_roman[0] = "ChivoLight";
1323 if (opt == "regular") {
1324 h_font_roman[0] = "Chivo";
1327 if (opt == "medium") {
1328 h_font_roman[0] = "ChivoMedium";
1331 if (prefixIs(opt, "oldstyle")) {
1332 h_font_sans_osf = "true";
1340 h_font_sans_opts = xopts;
1344 if (name == "PTSans") {
1345 h_font_sans[0] = "PTSans-TLF";
1348 if (name == "FiraSans") {
1349 h_font_sans_osf = "true";
1350 for (auto const & opt : allopts) {
1351 if (opt == "book") {
1352 h_font_sans[0] = "FiraSansBook";
1355 if (opt == "thin") {
1358 if (opt == "extralight") {
1359 h_font_sans[0] = "FiraSansExtralight";
1362 if (opt == "light") {
1363 h_font_sans[0] = "FiraSansLight";
1366 if (opt == "ultralight") {
1367 h_font_sans[0] = "FiraSansUltralight";
1370 if (opt == "thin") {
1371 h_font_sans[0] = "FiraSansThin";
1374 if (opt == "lf" || opt == "lining") {
1375 h_font_sans_osf = "false";
1383 h_font_sans_opts = xopts;
1387 if (name == "plex-sans") {
1388 h_font_sans[0] = "IBMPlexSans";
1389 for (auto const & opt : allopts) {
1390 if (opt == "condensed") {
1391 h_font_sans[0] = "IBMPlexSansCondensed";
1394 if (opt == "thin") {
1395 h_font_sans[0] = "IBMPlexSansThin";
1398 if (opt == "extralight") {
1399 h_font_sans[0] = "IBMPlexSansExtraLight";
1402 if (opt == "light") {
1403 h_font_sans[0] = "IBMPlexSansLight";
1411 h_font_sans_opts = xopts;
1415 if (name == "noto-sans") {
1416 h_font_sans[0] = "NotoSansRegular";
1417 for (auto const & opt : allopts) {
1418 if (opt == "regular")
1420 if (opt == "medium") {
1421 h_font_sans[0] = "NotoSansMedium";
1424 if (opt == "thin") {
1425 h_font_sans[0] = "NotoSansThin";
1428 if (opt == "extralight") {
1429 h_font_sans[0] = "NotoSansExtralight";
1432 if (opt == "light") {
1433 h_font_sans[0] = "NotoSansLight";
1437 h_font_sans_osf = "true";
1445 h_font_sans_opts = xopts;
1449 if (name == "sourcesanspro") {
1450 h_font_sans[0] = "ADOBESourceSansPro";
1451 for (auto const & opt : allopts) {
1453 h_font_sans_osf = "true";
1461 h_font_sans_opts = xopts;
1469 // By default, we use the package name as LyX font name,
1470 // so this only needs to be reset if these names differ.
1471 // Also, we handle the scaling option here generally.
1472 if (is_known(name, known_typewriter_font_packages)) {
1473 h_font_typewriter[0] = name;
1474 if (contains(opts, "scale")) {
1475 vector<string>::iterator it = allopts.begin();
1476 for (; it != allopts.end() ; ++it) {
1477 string const opt = *it;
1478 if (prefixIs(opt, "scaled=") || prefixIs(opt, "scale=")) {
1479 if (scale_as_percentage(opt, h_font_tt_scale[0])) {
1488 if (name == "libertineMono" || name == "libertineMono-type1")
1489 h_font_typewriter[0] = "libertine-mono";
1491 if (name == "FiraMono") {
1492 h_font_typewriter_osf = "true";
1493 for (auto const & opt : allopts) {
1494 if (opt == "lf" || opt == "lining") {
1495 h_font_typewriter_osf = "false";
1503 h_font_typewriter_opts = xopts;
1507 if (name == "PTMono")
1508 h_font_typewriter[0] = "PTMono-TLF";
1510 if (name == "plex-mono") {
1511 h_font_typewriter[0] = "IBMPlexMono";
1512 for (auto const & opt : allopts) {
1513 if (opt == "thin") {
1514 h_font_typewriter[0] = "IBMPlexMonoThin";
1517 if (opt == "extralight") {
1518 h_font_typewriter[0] = "IBMPlexMonoExtraLight";
1521 if (opt == "light") {
1522 h_font_typewriter[0] = "IBMPlexMonoLight";
1530 h_font_typewriter_opts = xopts;
1534 if (name == "noto-mono") {
1535 h_font_typewriter[0] = "NotoMonoRegular";
1536 for (auto const & opt : allopts) {
1537 if (opt == "regular")
1544 h_font_typewriter_opts = xopts;
1548 if (name == "sourcecodepro") {
1549 h_font_typewriter[0] = "ADOBESourceCodePro";
1550 for (auto const & opt : allopts) {
1552 h_font_typewriter_osf = "true";
1560 h_font_typewriter_opts = xopts;
1568 // By default, we use the package name as LyX font name,
1569 // so this only needs to be reset if these names differ.
1570 if (is_known(name, known_math_font_packages))
1571 h_font_math[0] = name;
1573 if (name == "newtxmath") {
1575 h_font_math[0] = "newtxmath";
1576 else if (opts == "garamondx")
1577 h_font_math[0] = "garamondx-ntxm";
1578 else if (opts == "libertine")
1579 h_font_math[0] = "libertine-ntxm";
1580 else if (opts == "minion")
1581 h_font_math[0] = "minion-ntxm";
1582 else if (opts == "cochineal")
1583 h_font_math[0] = "cochineal-ntxm";
1586 if (name == "iwona")
1588 h_font_math[0] = "iwona-math";
1590 if (name == "kurier")
1592 h_font_math[0] = "kurier-math";
1594 // after the detection and handling of special cases, we can remove the
1595 // fonts, otherwise they would appear in the preamble, see bug #7856
1596 if (is_known(name, known_roman_font_packages) || is_known(name, known_sans_font_packages)
1597 || is_known(name, known_typewriter_font_packages) || is_known(name, known_math_font_packages))
1599 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
1600 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
1601 name == "esint" || name == "mhchem" || name == "mathdots" ||
1602 name == "mathtools" || name == "stackrel" ||
1603 name == "stmaryrd" || name == "undertilde") {
1604 h_use_packages[name] = "2";
1605 registerAutomaticallyLoadedPackage(name);
1608 else if (name == "babel") {
1609 h_language_package = "default";
1610 // One might think we would have to do nothing if babel is loaded
1611 // without any options to prevent pollution of the preamble with this
1612 // babel call in every roundtrip.
1613 // But the user could have defined babel-specific things afterwards. So
1614 // we need to keep it in the preamble to prevent cases like bug #7861.
1615 if (!opts.empty()) {
1616 // check if more than one option was used - used later for inputenc
1617 if (options.begin() != options.end() - 1)
1618 one_language = false;
1619 // babel takes the last language of the option of its \usepackage
1620 // call as document language. If there is no such language option, the
1621 // last language in the documentclass options is used.
1622 handle_opt(options, known_languages, h_language);
1623 // translate the babel name to a LyX name
1624 h_language = babel2lyx(h_language);
1625 if (h_language == "japanese") {
1626 // For Japanese, the encoding isn't indicated in the source
1627 // file, and there's really not much we can do. We could
1628 // 1) offer a list of possible encodings to choose from, or
1629 // 2) determine the encoding of the file by inspecting it.
1630 // For the time being, we leave the encoding alone so that
1631 // we don't get iconv errors when making a wrong guess, and
1632 // we will output a note at the top of the document
1633 // explaining what to do.
1634 Encoding const * const enc = encodings.fromIconvName(
1635 p.getEncoding(), Encoding::japanese, false);
1637 h_inputencoding = enc->name();
1638 is_nonCJKJapanese = true;
1639 // in this case babel can be removed from the preamble
1640 registerAutomaticallyLoadedPackage("babel");
1642 // If babel is called with options, LyX puts them by default into the
1643 // document class options. This works for most languages, except
1644 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
1645 // perhaps in future others.
1646 // Therefore keep the babel call as it is as the user might have
1648 string const babelcall = "\\usepackage[" + opts + "]{babel}\n";
1649 if (!contains(h_preamble.str(), babelcall))
1650 h_preamble << babelcall;
1652 delete_opt(options, known_languages);
1654 if (!contains(h_preamble.str(), "\\usepackage{babel}\n"))
1655 h_preamble << "\\usepackage{babel}\n";
1656 explicit_babel = true;
1660 else if (name == "polyglossia") {
1661 h_language_package = "default";
1662 h_default_output_format = "pdf4";
1663 h_use_non_tex_fonts = true;
1665 registerAutomaticallyLoadedPackage("xunicode");
1666 if (h_inputencoding == "auto-legacy")
1667 p.setEncoding("UTF-8");
1670 else if (name == "CJK") {
1671 // set the encoding to "auto-legacy" because it might be set to "auto-legacy-plain" by the babel handling
1672 // and this would not be correct for CJK
1673 if (h_inputencoding == "auto-legacy-plain")
1674 h_inputencoding = "auto-legacy";
1675 registerAutomaticallyLoadedPackage("CJK");
1678 else if (name == "CJKutf8") {
1679 h_inputencoding = "utf8-cjk";
1680 p.setEncoding("UTF-8");
1681 registerAutomaticallyLoadedPackage("CJKutf8");
1684 else if (name == "fontenc") {
1685 h_fontencoding = getStringFromVector(options, ",");
1689 else if (name == "inputenc" || name == "luainputenc") {
1690 // h_inputencoding is only set when there is not more than one
1691 // inputenc option because otherwise h_inputencoding must be
1692 // set to "auto-legacy" (the default encodings of the document's languages)
1693 // Therefore check that exactly one option is passed to inputenc.
1694 // It is also only set when there is not more than one babel
1696 if (!options.empty()) {
1697 string const encoding = options.back();
1698 Encoding const * const enc = encodings.fromLaTeXName(
1699 encoding, Encoding::inputenc, true);
1701 if (!detectEncoding)
1702 warning_message("Unknown encoding " + encoding + ". Ignoring.");
1704 if (!enc->unsafe() && options.size() == 1 && one_language == true) {
1705 h_inputencoding = enc->name();
1706 docencoding = enc->iconvName();
1708 p.setEncoding(enc->iconvName());
1714 else if (name == "srcltx") {
1715 h_output_sync = "1";
1716 if (!opts.empty()) {
1717 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
1720 h_output_sync_macro = "\\usepackage{srcltx}";
1723 else if (is_known(name, known_old_language_packages)) {
1724 // known language packages from the times before babel
1725 // if they are found and not also babel, they will be used as
1726 // custom language package
1727 h_language_package = "\\usepackage{" + name + "}";
1730 else if (name == "lyxskak") {
1731 // ignore this and its options
1732 const char * const o[] = {"ps", "mover", 0};
1733 delete_opt(options, o);
1736 else if (name == "parskip" && options.size() < 2 && (opts.empty() || prefixIs(opts, "skip="))) {
1738 h_paragraph_separation = "halfline";
1740 if (opts == "skip=\\smallskipamount")
1741 h_defskip = "smallskip";
1742 else if (opts == "skip=\\medskipamount")
1743 h_defskip = "medskip";
1744 else if (opts == "skip=\\bigskipamount")
1745 h_defskip = "bigskip";
1746 else if (opts == "skip=\\baselineskip")
1747 h_defskip = "fullline";
1750 h_paragraph_separation = "skip";
1754 else if (is_known(name, known_lyx_packages) && options.empty()) {
1755 if (name == "splitidx")
1756 h_use_indices = "true";
1757 else if (name == "minted")
1758 h_use_minted = true;
1759 else if (name == "refstyle")
1760 h_use_refstyle = true;
1761 else if (name == "prettyref")
1762 h_use_refstyle = false;
1764 if (!in_lyx_preamble) {
1765 h_preamble << package_beg_sep << name
1766 << package_mid_sep << "\\usepackage{"
1768 if (p.next_token().cat() == catNewline ||
1769 (p.next_token().cat() == catSpace &&
1770 p.next_next_token().cat() == catNewline))
1772 h_preamble << package_end_sep;
1776 else if (name == "geometry")
1777 handle_geometry(options);
1779 else if (name == "subfig")
1780 ; // ignore this FIXME: Use the package separator mechanism instead
1782 else if (char const * const * where = is_known(name, known_languages))
1783 h_language = known_coded_languages[where - known_languages];
1785 else if (name == "natbib") {
1786 h_biblio_style = "plainnat";
1787 h_cite_engine = "natbib";
1788 h_cite_engine_type = "authoryear";
1789 vector<string>::iterator it =
1790 find(options.begin(), options.end(), "authoryear");
1791 if (it != options.end())
1794 it = find(options.begin(), options.end(), "numbers");
1795 if (it != options.end()) {
1796 h_cite_engine_type = "numerical";
1800 if (!options.empty())
1801 h_biblio_options = join(options, ",");
1804 else if (name == "biblatex") {
1805 h_biblio_style = "plainnat";
1806 h_cite_engine = "biblatex";
1807 h_cite_engine_type = "authoryear";
1809 vector<string>::iterator it =
1810 find(options.begin(), options.end(), "natbib");
1811 if (it != options.end()) {
1813 h_cite_engine = "biblatex-natbib";
1815 opt = process_keyval_opt(options, "natbib");
1817 h_cite_engine = "biblatex-natbib";
1819 opt = process_keyval_opt(options, "style");
1821 h_biblatex_citestyle = opt;
1822 h_biblatex_bibstyle = opt;
1824 opt = process_keyval_opt(options, "citestyle");
1826 h_biblatex_citestyle = opt;
1827 opt = process_keyval_opt(options, "bibstyle");
1829 h_biblatex_bibstyle = opt;
1831 opt = process_keyval_opt(options, "refsection");
1833 if (opt == "none" || opt == "part"
1834 || opt == "chapter" || opt == "section"
1835 || opt == "subsection")
1838 warning_message("Ignoring unknown refsection value '" + opt + "'.");
1840 opt = process_keyval_opt(options, "bibencoding");
1843 if (!options.empty()) {
1844 h_biblio_options = join(options, ",");
1849 else if (name == "jurabib") {
1850 h_biblio_style = "jurabib";
1851 h_cite_engine = "jurabib";
1852 h_cite_engine_type = "authoryear";
1853 if (!options.empty())
1854 h_biblio_options = join(options, ",");
1857 else if (name == "bibtopic")
1858 h_use_bibtopic = "true";
1860 else if (name == "chapterbib")
1861 h_multibib = "child";
1863 else if (name == "hyperref")
1864 handle_hyperref(options);
1866 else if (name == "algorithm2e") {
1867 // Load "algorithm2e" module
1868 addModule("algorithm2e");
1869 // Add the package options to the global document options
1870 if (!options.empty()) {
1871 if (h_options.empty())
1872 h_options = join(options, ",");
1874 h_options += ',' + join(options, ",");
1877 else if (name == "microtype") {
1878 //we internally support only microtype without params
1879 if (options.empty())
1880 h_use_microtype = "true";
1882 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1885 else if (name == "lineno") {
1886 h_use_lineno = "true";
1887 if (!options.empty()) {
1888 h_lineno_options = join(options, ",");
1893 else if (name == "changebar")
1894 h_output_changes = "true";
1896 else if (!in_lyx_preamble) {
1897 if (options.empty())
1898 h_preamble << "\\usepackage{" << name << '}';
1900 h_preamble << "\\usepackage[" << opts << "]{"
1904 if (p.next_token().cat() == catNewline ||
1905 (p.next_token().cat() == catSpace &&
1906 p.next_next_token().cat() == catNewline))
1910 // We need to do something with the options...
1911 if (!options.empty() && !detectEncoding)
1912 warning_message("Ignoring options '" + join(options, ",")
1913 + "' of package " + name + '.');
1915 // remove the whitespace
1920 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1923 Token t = p.get_token();
1924 if (t.cat() == catEscape &&
1925 is_known(t.cs(), known_if_commands))
1926 handle_if(p, in_lyx_preamble);
1928 if (!in_lyx_preamble)
1929 h_preamble << t.asInput();
1930 if (t.cat() == catEscape && t.cs() == "fi")
1937 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1939 if (contains(h_float_placement, "H"))
1940 registerAutomaticallyLoadedPackage("float");
1941 if (h_spacing != "single" && h_spacing != "default")
1942 registerAutomaticallyLoadedPackage("setspace");
1943 if (h_use_packages["amsmath"] == "2") {
1944 // amsbsy and amstext are already provided by amsmath
1945 registerAutomaticallyLoadedPackage("amsbsy");
1946 registerAutomaticallyLoadedPackage("amstext");
1949 // output the LyX file settings
1950 // Important: Keep the version formatting in sync with LyX and
1951 // lyx2lyx (bug 7951)
1952 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1953 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1954 << lyx_version_minor << '\n'
1955 << "\\lyxformat " << LYX_FORMAT << '\n'
1956 << "\\begin_document\n"
1957 << "\\begin_header\n"
1958 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1959 << "\\origin " << origin << "\n"
1960 << "\\textclass " << h_textclass << "\n";
1961 if (!h_doc_metadata.empty()) {
1962 os << "\\begin_metadata\n"
1964 << "\n\\end_metadata\n";
1966 string const raw = subdoc ? empty_string() : h_preamble.str();
1968 os << "\\begin_preamble\n";
1969 for (string::size_type i = 0; i < raw.size(); ++i) {
1970 if (raw[i] == package_beg_sep) {
1971 // Here follows some package loading code that
1972 // must be skipped if the package is loaded
1974 string::size_type j = raw.find(package_mid_sep, i);
1975 if (j == string::npos)
1977 string::size_type k = raw.find(package_end_sep, j);
1978 if (k == string::npos)
1980 string const package = raw.substr(i + 1, j - i - 1);
1981 string const replacement = raw.substr(j + 1, k - j - 1);
1982 if (auto_packages.find(package) == auto_packages.end())
1988 os << "\n\\end_preamble\n";
1990 if (!h_options.empty())
1991 os << "\\options " << h_options << "\n";
1992 os << "\\use_default_options " << h_use_default_options << "\n";
1993 if (!used_modules.empty()) {
1994 os << "\\begin_modules\n";
1995 vector<string>::const_iterator const end = used_modules.end();
1996 vector<string>::const_iterator it = used_modules.begin();
1997 for (; it != end; ++it)
1999 os << "\\end_modules\n";
2001 if (!h_includeonlys.empty()) {
2002 os << "\\begin_includeonly\n";
2003 for (auto const & iofile : h_includeonlys)
2004 os << iofile << '\n';
2005 os << "\\end_includeonly\n";
2007 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
2008 << "\\language " << h_language << "\n"
2009 << "\\language_package " << h_language_package << "\n"
2010 << "\\inputencoding " << h_inputencoding << "\n"
2011 << "\\fontencoding " << h_fontencoding << "\n"
2012 << "\\font_roman \"" << h_font_roman[0]
2013 << "\" \"" << h_font_roman[1] << "\"\n"
2014 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
2015 << "\\font_typewriter \"" << h_font_typewriter[0]
2016 << "\" \"" << h_font_typewriter[1] << "\"\n"
2017 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
2018 << "\\font_default_family " << h_font_default_family << "\n"
2019 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
2020 << "\\font_sc " << h_font_sc << "\n"
2021 << "\\font_roman_osf " << h_font_roman_osf << "\n"
2022 << "\\font_sans_osf " << h_font_sans_osf << "\n"
2023 << "\\font_typewriter_osf " << h_font_typewriter_osf << "\n";
2024 if (!h_font_roman_opts.empty())
2025 os << "\\font_roman_opts \"" << h_font_roman_opts << "\"" << '\n';
2026 os << "\\font_sf_scale " << h_font_sf_scale[0]
2027 << ' ' << h_font_sf_scale[1] << '\n';
2028 if (!h_font_sans_opts.empty())
2029 os << "\\font_sans_opts \"" << h_font_sans_opts << "\"" << '\n';
2030 os << "\\font_tt_scale " << h_font_tt_scale[0]
2031 << ' ' << h_font_tt_scale[1] << '\n';
2032 if (!h_font_cjk.empty())
2033 os << "\\font_cjk " << h_font_cjk << '\n';
2034 if (!h_font_typewriter_opts.empty())
2035 os << "\\font_typewriter_opts \"" << h_font_typewriter_opts << "\"" << '\n';
2036 os << "\\use_microtype " << h_use_microtype << '\n'
2037 << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
2038 << "\\graphics " << h_graphics << '\n'
2039 << "\\default_output_format " << h_default_output_format << "\n"
2040 << "\\output_sync " << h_output_sync << "\n";
2041 if (h_output_sync == "1")
2042 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
2043 os << "\\bibtex_command " << h_bibtex_command << "\n"
2044 << "\\index_command " << h_index_command << "\n";
2045 if (!h_float_placement.empty())
2046 os << "\\float_placement " << h_float_placement << "\n";
2047 os << "\\paperfontsize " << h_paperfontsize << "\n"
2048 << "\\spacing " << h_spacing << "\n"
2049 << "\\use_hyperref " << h_use_hyperref << '\n';
2050 if (h_use_hyperref == "true") {
2051 if (!h_pdf_title.empty())
2052 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
2053 if (!h_pdf_author.empty())
2054 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
2055 if (!h_pdf_subject.empty())
2056 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
2057 if (!h_pdf_keywords.empty())
2058 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
2059 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
2060 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
2061 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
2062 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
2063 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
2064 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
2065 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
2066 "\\pdf_backref " << h_pdf_backref << "\n"
2067 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
2068 if (!h_pdf_pagemode.empty())
2069 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
2070 if (!h_pdf_quoted_options.empty())
2071 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
2073 os << "\\papersize " << h_papersize << "\n"
2074 << "\\use_geometry " << h_use_geometry << '\n';
2075 for (map<string, string>::const_iterator it = h_use_packages.begin();
2076 it != h_use_packages.end(); ++it)
2077 os << "\\use_package " << it->first << ' ' << it->second << '\n';
2078 os << "\\cite_engine " << h_cite_engine << '\n'
2079 << "\\cite_engine_type " << h_cite_engine_type << '\n'
2080 << "\\biblio_style " << h_biblio_style << "\n"
2081 << "\\use_bibtopic " << h_use_bibtopic << "\n";
2082 if (!h_biblio_options.empty())
2083 os << "\\biblio_options " << h_biblio_options << "\n";
2084 if (!h_biblatex_bibstyle.empty())
2085 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
2086 if (!h_biblatex_citestyle.empty())
2087 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
2088 if (!h_multibib.empty())
2089 os << "\\multibib " << h_multibib << "\n";
2090 os << "\\use_indices " << h_use_indices << "\n"
2091 << "\\paperorientation " << h_paperorientation << '\n'
2092 << "\\suppress_date " << h_suppress_date << '\n'
2093 << "\\justification " << h_justification << '\n'
2094 << "\\use_refstyle " << h_use_refstyle << '\n'
2095 << "\\use_minted " << h_use_minted << '\n'
2096 << "\\use_lineno " << h_use_lineno << '\n';
2097 if (!h_lineno_options.empty())
2098 os << "\\lineno_options " << h_lineno_options << '\n';
2099 if (!h_fontcolor.empty())
2100 os << "\\fontcolor " << h_fontcolor << '\n';
2101 if (!h_notefontcolor.empty())
2102 os << "\\notefontcolor " << h_notefontcolor << '\n';
2103 if (!h_backgroundcolor.empty())
2104 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
2105 if (!h_boxbgcolor.empty())
2106 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
2107 if (index_number != 0)
2108 for (int i = 0; i < index_number; i++) {
2109 os << "\\index " << h_index[i] << '\n'
2110 << "\\shortcut " << h_shortcut[i] << '\n'
2111 << "\\color " << h_color << '\n'
2115 os << "\\index " << h_index[0] << '\n'
2116 << "\\shortcut " << h_shortcut[0] << '\n'
2117 << "\\color " << h_color << '\n'
2121 << "\\secnumdepth " << h_secnumdepth << "\n"
2122 << "\\tocdepth " << h_tocdepth << "\n"
2123 << "\\paragraph_separation " << h_paragraph_separation << "\n";
2124 if (h_paragraph_separation == "skip")
2125 os << "\\defskip " << h_defskip << "\n";
2127 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
2128 os << "\\is_math_indent " << h_is_mathindent << "\n";
2129 if (!h_mathindentation.empty())
2130 os << "\\math_indentation " << h_mathindentation << "\n";
2131 os << "\\math_numbering_side " << h_math_numbering_side << "\n";
2132 os << "\\quotes_style " << h_quotes_style << "\n"
2133 << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
2134 << "\\papercolumns " << h_papercolumns << "\n"
2135 << "\\papersides " << h_papersides << "\n"
2136 << "\\paperpagestyle " << h_paperpagestyle << "\n";
2137 if (!h_listings_params.empty())
2138 os << "\\listings_params " << h_listings_params << "\n";
2139 os << "\\tracking_changes " << h_tracking_changes << "\n"
2140 << "\\output_changes " << h_output_changes << "\n"
2141 << "\\change_bars " << h_change_bars << "\n"
2142 << "\\html_math_output " << h_html_math_output << "\n"
2143 << "\\html_css_as_file " << h_html_css_as_file << "\n"
2144 << "\\html_be_strict " << h_html_be_strict << "\n"
2145 << "\\docbook_table_output " << h_docbook_table_output << "\n"
2146 << "\\docbook_mathml_prefix " << h_docbook_mathml_prefix << "\n"
2148 << "\\end_header\n\n"
2149 << "\\begin_body\n";
2154 void Preamble::parse(Parser & p, string const & forceclass,
2155 TeX2LyXDocClass & tc)
2157 // initialize fixed types
2158 special_columns_['D'] = 3;
2159 parse(p, forceclass, false, tc);
2163 void Preamble::parse(Parser & p, string const & forceclass,
2164 bool detectEncoding, TeX2LyXDocClass & tc)
2166 bool is_full_document = false;
2167 bool is_lyx_file = false;
2168 bool in_lyx_preamble = false;
2169 bool class_set = false;
2171 // determine whether this is a full document or a fragment for inclusion
2173 Token const & t = p.get_token();
2175 if (t.cat() == catEscape && t.cs() == "documentclass") {
2176 is_full_document = true;
2182 if (detectEncoding && !is_full_document)
2185 // Force textclass if the user wanted it
2186 if (!forceclass.empty()) {
2187 setTextClass(forceclass, tc);
2191 while (is_full_document && p.good()) {
2192 if (detectEncoding && h_inputencoding != "auto-legacy" &&
2193 h_inputencoding != "auto-legacy-plain")
2196 Token const & t = p.get_token();
2198 if (!detectEncoding)
2199 debug_message("t: " + t.asInput());
2204 if (!in_lyx_preamble &&
2205 (t.cat() == catLetter ||
2206 t.cat() == catSuper ||
2207 t.cat() == catSub ||
2208 t.cat() == catOther ||
2209 t.cat() == catMath ||
2210 t.cat() == catActive ||
2211 t.cat() == catBegin ||
2212 t.cat() == catEnd ||
2213 t.cat() == catAlign ||
2214 t.cat() == catParameter)) {
2215 h_preamble << t.cs();
2219 if (!in_lyx_preamble &&
2220 (t.cat() == catSpace || t.cat() == catNewline)) {
2221 h_preamble << t.asInput();
2225 if (t.cat() == catComment) {
2226 static regex const islyxfile("%% LyX .* created this file");
2227 static regex const usercommands("User specified LaTeX commands");
2229 string const comment = t.asInput();
2231 // magically switch encoding default if it looks like XeLaTeX
2232 static string const magicXeLaTeX =
2233 "% This document must be compiled with XeLaTeX ";
2234 if (comment.size() > magicXeLaTeX.size()
2235 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
2236 && h_inputencoding == "auto-legacy") {
2237 if (!detectEncoding)
2238 warning_message("XeLaTeX comment found, switching to UTF8");
2239 h_inputencoding = "utf8";
2242 if (regex_search(comment, sub, islyxfile)) {
2244 in_lyx_preamble = true;
2245 } else if (is_lyx_file
2246 && regex_search(comment, sub, usercommands))
2247 in_lyx_preamble = false;
2248 else if (!in_lyx_preamble)
2249 h_preamble << t.asInput();
2253 if (t.cs() == "PassOptionsToPackage") {
2254 string const poptions = p.getArg('{', '}');
2255 string const package = p.verbatim_item();
2256 extra_package_options_.insert(make_pair(package, poptions));
2260 if (t.cs() == "pagestyle") {
2261 h_paperpagestyle = p.verbatim_item();
2265 if (t.cs() == "setdefaultlanguage") {
2267 // We don't yet care about non-language variant options
2268 // because LyX doesn't support this yet, see bug #8214
2270 string langopts = p.getOpt();
2271 // check if the option contains a variant, if yes, extract it
2272 string::size_type pos_var = langopts.find("variant");
2273 string::size_type i = langopts.find(',', pos_var);
2274 string::size_type k = langopts.find('=', pos_var);
2275 if (pos_var != string::npos){
2277 if (i == string::npos)
2278 variant = langopts.substr(k + 1, langopts.length() - k - 2);
2280 variant = langopts.substr(k + 1, i - k - 1);
2281 h_language = variant;
2285 h_language = p.verbatim_item();
2286 //finally translate the poyglossia name to a LyX name
2287 h_language = polyglossia2lyx(h_language);
2291 if (t.cs() == "setotherlanguage") {
2292 // We don't yet care about the option because LyX doesn't
2293 // support this yet, see bug #8214
2294 p.hasOpt() ? p.getOpt() : string();
2299 if (t.cs() == "setmainfont") {
2300 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2301 h_font_roman[1] = p.getArg('{', '}');
2302 if (!fontopts.empty()) {
2303 vector<string> opts = getVectorFromString(fontopts);
2305 for (auto const & opt : opts) {
2306 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2309 if (!fontopts.empty())
2313 h_font_roman_opts = fontopts;
2318 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
2319 // LyX currently only supports the scale option
2320 string scale, fontopts;
2322 fontopts = p.getArg('[', ']');
2323 if (!fontopts.empty()) {
2324 vector<string> opts = getVectorFromString(fontopts);
2326 for (auto const & opt : opts) {
2327 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2330 if (prefixIs(opt, "Scale=")) {
2331 scale_as_percentage(opt, scale);
2334 if (!fontopts.empty())
2340 if (t.cs() == "setsansfont") {
2342 h_font_sf_scale[1] = scale;
2343 h_font_sans[1] = p.getArg('{', '}');
2344 if (!fontopts.empty())
2345 h_font_sans_opts = fontopts;
2348 h_font_tt_scale[1] = scale;
2349 h_font_typewriter[1] = p.getArg('{', '}');
2350 if (!fontopts.empty())
2351 h_font_typewriter_opts = fontopts;
2356 if (t.cs() == "babelfont") {
2358 h_use_non_tex_fonts = true;
2359 h_language_package = "babel";
2360 if (h_inputencoding == "auto-legacy")
2361 p.setEncoding("UTF-8");
2362 // we don't care about the lang option
2363 string const lang = p.hasOpt() ? p.getArg('[', ']') : string();
2364 string const family = p.getArg('{', '}');
2365 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2366 string const fontname = p.getArg('{', '}');
2367 if (lang.empty() && family == "rm") {
2368 h_font_roman[1] = fontname;
2369 if (!fontopts.empty()) {
2370 vector<string> opts = getVectorFromString(fontopts);
2372 for (auto const & opt : opts) {
2373 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2376 if (!fontopts.empty())
2380 h_font_roman_opts = fontopts;
2383 } else if (lang.empty() && (family == "sf" || family == "tt")) {
2385 if (!fontopts.empty()) {
2386 vector<string> opts = getVectorFromString(fontopts);
2388 for (auto const & opt : opts) {
2389 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2392 if (prefixIs(opt, "Scale=")) {
2393 scale_as_percentage(opt, scale);
2396 if (!fontopts.empty())
2401 if (family == "sf") {
2403 h_font_sf_scale[1] = scale;
2404 h_font_sans[1] = fontname;
2405 if (!fontopts.empty())
2406 h_font_sans_opts = fontopts;
2409 h_font_tt_scale[1] = scale;
2410 h_font_typewriter[1] = fontname;
2411 if (!fontopts.empty())
2412 h_font_typewriter_opts = fontopts;
2416 // not rm, sf or tt or lang specific
2417 h_preamble << '\\' << t.cs();
2419 h_preamble << '[' << lang << ']';
2420 h_preamble << '{' << family << '}';
2421 if (!fontopts.empty())
2422 h_preamble << '[' << fontopts << ']';
2423 h_preamble << '{' << fontname << '}' << '\n';
2428 if (t.cs() == "date") {
2429 string argument = p.getArg('{', '}');
2430 if (argument.empty())
2431 h_suppress_date = "true";
2433 h_preamble << t.asInput() << '{' << argument << '}';
2437 if (t.cs() == "color") {
2438 string const space =
2439 (p.hasOpt() ? p.getOpt() : string());
2440 string argument = p.getArg('{', '}');
2441 // check the case that a standard color is used
2442 if (space.empty() && is_known(argument, known_basic_colors)) {
2443 h_fontcolor = rgbcolor2code(argument);
2444 registerAutomaticallyLoadedPackage("color");
2445 } else if (space.empty() && argument == "document_fontcolor")
2446 registerAutomaticallyLoadedPackage("color");
2447 // check the case that LyX's document_fontcolor is defined
2448 // but not used for \color
2450 h_preamble << t.asInput();
2452 h_preamble << space;
2453 h_preamble << '{' << argument << '}';
2454 // the color might already be set because \definecolor
2455 // is parsed before this
2461 if (t.cs() == "pagecolor") {
2462 string argument = p.getArg('{', '}');
2463 // check the case that a standard color is used
2464 if (is_known(argument, known_basic_colors)) {
2465 h_backgroundcolor = rgbcolor2code(argument);
2466 } else if (argument == "page_backgroundcolor")
2467 registerAutomaticallyLoadedPackage("color");
2468 // check the case that LyX's page_backgroundcolor is defined
2469 // but not used for \pagecolor
2471 h_preamble << t.asInput() << '{' << argument << '}';
2472 // the color might already be set because \definecolor
2473 // is parsed before this
2474 h_backgroundcolor = "";
2479 if (t.cs() == "makeatletter") {
2480 // LyX takes care of this
2481 p.setCatcode('@', catLetter);
2485 if (t.cs() == "makeatother") {
2486 // LyX takes care of this
2487 p.setCatcode('@', catOther);
2491 if (t.cs() == "makeindex") {
2492 // LyX will re-add this if a print index command is found
2497 if (t.cs() == "newindex") {
2498 string const indexname = p.getArg('[', ']');
2499 string const shortcut = p.verbatim_item();
2500 if (!indexname.empty())
2501 h_index[index_number] = indexname;
2503 h_index[index_number] = shortcut;
2504 h_shortcut[index_number] = shortcut;
2510 if (t.cs() == "addbibresource") {
2511 string const options = p.getArg('[', ']');
2512 string const arg = removeExtension(p.getArg('{', '}'));
2513 if (!options.empty()) {
2514 // check if the option contains a bibencoding, if yes, extract it
2515 string::size_type pos = options.find("bibencoding=");
2517 if (pos != string::npos) {
2518 string::size_type i = options.find(',', pos);
2519 if (i == string::npos)
2520 encoding = options.substr(pos + 1);
2522 encoding = options.substr(pos, i - pos);
2523 pos = encoding.find('=');
2524 if (pos == string::npos)
2527 encoding = encoding.substr(pos + 1);
2529 if (!encoding.empty())
2530 biblatex_encodings.push_back(normalize_filename(arg) + ' ' + encoding);
2532 biblatex_bibliographies.push_back(arg);
2536 if (t.cs() == "bibliography") {
2537 vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
2538 biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
2542 if (t.cs() == "RS@ifundefined") {
2543 string const name = p.verbatim_item();
2544 string const body1 = p.verbatim_item();
2545 string const body2 = p.verbatim_item();
2546 // only non-lyxspecific stuff
2547 if (in_lyx_preamble &&
2548 (name == "subsecref" || name == "thmref" || name == "lemref"))
2552 ss << '\\' << t.cs();
2553 ss << '{' << name << '}'
2554 << '{' << body1 << '}'
2555 << '{' << body2 << '}';
2556 h_preamble << ss.str();
2561 if (t.cs() == "AtBeginDocument") {
2562 string const name = p.verbatim_item();
2563 // only non-lyxspecific stuff
2564 if (in_lyx_preamble &&
2565 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
2566 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
2567 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
2568 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
2569 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
2570 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
2571 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
2572 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
2573 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
2574 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
2575 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
2576 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
2577 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
2578 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
2579 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
2583 ss << '\\' << t.cs();
2584 ss << '{' << name << '}';
2585 h_preamble << ss.str();
2590 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
2591 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
2592 || t.cs() == "providecommand" || t.cs() == "providecommandx"
2593 || t.cs() == "DeclareRobustCommand"
2594 || t.cs() == "DeclareRobustCommandx"
2595 || t.cs() == "ProvideTextCommandDefault"
2596 || t.cs() == "DeclareMathAccent") {
2598 if (p.next_token().character() == '*') {
2602 string const name = p.verbatim_item();
2603 string const opt1 = p.getFullOpt();
2604 string const opt2 = p.getFullOpt();
2605 string const body = p.verbatim_item();
2606 // store the in_lyx_preamble setting
2607 bool const was_in_lyx_preamble = in_lyx_preamble;
2609 if (name == "\\rmdefault")
2610 if (is_known(body, known_roman_font_packages)) {
2611 h_font_roman[0] = body;
2613 in_lyx_preamble = true;
2615 if (name == "\\sfdefault") {
2616 if (is_known(body, known_sans_font_packages)) {
2617 h_font_sans[0] = body;
2619 in_lyx_preamble = true;
2621 if (body == "LibertinusSans-OsF") {
2622 h_font_sans[0] = "LibertinusSans-LF";
2623 h_font_sans_osf = "true";
2625 in_lyx_preamble = true;
2628 if (name == "\\ttdefault")
2629 if (is_known(body, known_typewriter_font_packages)) {
2630 h_font_typewriter[0] = body;
2632 in_lyx_preamble = true;
2634 if (name == "\\familydefault") {
2635 string family = body;
2636 // remove leading "\"
2637 h_font_default_family = family.erase(0,1);
2639 in_lyx_preamble = true;
2641 if (name == "\\LibertinusSans@scale") {
2642 if (isStrDbl(body)) {
2643 h_font_sf_scale[0] = convert<string>(
2644 static_cast<int>(100 * convert<double>(body)));
2647 if (name == "\\LibertinusMono@scale") {
2648 if (isStrDbl(body)) {
2649 h_font_tt_scale[0] = convert<string>(
2650 static_cast<int>(100 * convert<double>(body)));
2654 // remove LyX-specific definitions that are re-added by LyX
2656 // \lyxline is an ancient command that is converted by tex2lyx into
2657 // a \rule therefore remove its preamble code
2658 if (name == "\\lyxdot" || name == "\\lyxarrow"
2659 || name == "\\lyxline" || name == "\\LyX") {
2661 in_lyx_preamble = true;
2664 // Add the command to the known commands
2665 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
2667 // only non-lyxspecific stuff
2668 if (!in_lyx_preamble) {
2670 ss << '\\' << t.cs();
2673 ss << '{' << name << '}' << opt1 << opt2
2674 << '{' << body << "}";
2675 if (prefixIs(t.cs(), "renew") || !contains(h_preamble.str(), ss.str()))
2676 h_preamble << ss.str();
2678 ostream & out = in_preamble ? h_preamble : os;
2679 out << "\\" << t.cs() << "{" << name << "}"
2680 << opts << "{" << body << "}";
2683 // restore the in_lyx_preamble setting
2684 in_lyx_preamble = was_in_lyx_preamble;
2688 if (t.cs() == "documentclass") {
2689 vector<string>::iterator it;
2690 vector<string> opts = split_options(p.getArg('[', ']'));
2691 // FIXME This does not work for classes that have a
2692 // different name in LyX than in LaTeX
2693 string tclass = p.getArg('{', '}');
2694 if (contains(tclass, '/')) {
2695 // It's considered bad practice, but it is still
2696 // sometimes done (and possible) to enter the documentclass
2697 // as a path, e.g. \documentclass{subdir/class} (#12284)
2698 // we strip the name in this case.
2700 tclass = rsplit(tclass, dummy, '/');
2703 // Only set text class if a class hasn't been forced
2704 // (this was set above)
2706 // textclass needs to be set at this place (if not already done)
2707 // as we need to know it for other parameters
2708 // (such as class-dependent paper size)
2709 setTextClass(tclass, tc);
2714 // Try those who are (most likely) known to all packages first
2715 handle_opt(opts, known_fontsizes, h_paperfontsize);
2716 delete_opt(opts, known_fontsizes);
2717 // delete "pt" at the end
2718 string::size_type i = h_paperfontsize.find("pt");
2719 if (i != string::npos)
2720 h_paperfontsize.erase(i);
2721 // Now those known specifically to the class
2722 vector<string> class_fsizes = getVectorFromString(tc.opt_fontsize(), "|");
2723 string const fsize_format = tc.fontsizeformat();
2724 for (auto const & fsize : class_fsizes) {
2725 string latexsize = subst(fsize_format, "$$s", fsize);
2726 vector<string>::iterator it = find(opts.begin(), opts.end(), latexsize);
2727 if (it != opts.end()) {
2728 h_paperfontsize = fsize;
2734 // The documentclass options are always parsed before the options
2735 // of the babel call so that a language cannot overwrite the babel
2737 handle_opt(opts, known_languages, h_language);
2738 delete_opt(opts, known_languages);
2741 if ((it = find(opts.begin(), opts.end(), "fleqn"))
2743 h_is_mathindent = "1";
2746 // formula numbering side
2747 if ((it = find(opts.begin(), opts.end(), "leqno"))
2749 h_math_numbering_side = "left";
2752 else if ((it = find(opts.begin(), opts.end(), "reqno"))
2754 h_math_numbering_side = "right";
2758 // paper orientation
2759 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
2760 h_paperorientation = "landscape";
2764 if ((it = find(opts.begin(), opts.end(), "oneside"))
2769 if ((it = find(opts.begin(), opts.end(), "twoside"))
2775 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
2777 h_papercolumns = "1";
2780 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
2782 h_papercolumns = "2";
2786 // some size options are known by the document class, other sizes
2787 // are handled by the \geometry command of the geometry package
2788 vector<string> class_psizes = getVectorFromString(tc.opt_pagesize(), "|");
2789 string const psize_format = tc.pagesizeformat();
2790 for (auto const & psize : class_psizes) {
2791 string latexsize = subst(psize_format, "$$s", psize);
2792 vector<string>::iterator it = find(opts.begin(), opts.end(), latexsize);
2793 if (it != opts.end()) {
2794 h_papersize = psize;
2798 if (psize_format == "$$spaper")
2800 // Also try with the default format since this is understood by
2802 latexsize = psize + "paper";
2803 it = find(opts.begin(), opts.end(), latexsize);
2804 if (it != opts.end()) {
2805 h_papersize = psize;
2810 // the remaining options
2811 h_options = join(opts, ",");
2815 if (t.cs() == "DocumentMetadata") {
2816 h_doc_metadata = trimSpaceAndEol(p.getArg('{', '}'));
2820 if (t.cs() == "usepackage") {
2821 string const options = p.getArg('[', ']');
2822 string const name = p.getArg('{', '}');
2823 vector<string> vecnames;
2824 split(name, vecnames, ',');
2825 vector<string>::const_iterator it = vecnames.begin();
2826 vector<string>::const_iterator end = vecnames.end();
2827 for (; it != end; ++it)
2828 handle_package(p, trimSpaceAndEol(*it), options,
2829 in_lyx_preamble, detectEncoding);
2833 if (t.cs() == "inputencoding") {
2834 string const encoding = p.getArg('{','}');
2835 Encoding const * const enc = encodings.fromLaTeXName(
2836 encoding, Encoding::inputenc, true);
2838 if (!detectEncoding)
2839 warning_message("Unknown encoding " + encoding + ". Ignoring.");
2842 h_inputencoding = enc->name();
2843 p.setEncoding(enc->iconvName());
2848 if (t.cs() == "newenvironment") {
2849 string const name = p.getArg('{', '}');
2850 string const opt1 = p.getFullOpt();
2851 string const opt2 = p.getFullOpt();
2852 string const beg = p.verbatim_item();
2853 string const end = p.verbatim_item();
2854 if (!in_lyx_preamble) {
2855 h_preamble << "\\newenvironment{" << name
2856 << '}' << opt1 << opt2 << '{'
2857 << beg << "}{" << end << '}';
2859 add_known_environment(name, opt1, !opt2.empty(),
2860 from_utf8(beg), from_utf8(end));
2864 if (t.cs() == "newtheorem") {
2866 if (p.next_token().character() == '*') {
2870 string const name = p.getArg('{', '}');
2871 string const opt1 = p.getFullOpt();
2872 string const opt2 = p.getFullOpt();
2873 string const body = p.verbatim_item();
2874 string const opt3 = p.getFullOpt();
2875 string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
2877 string const complete = cmd + "{" + name + '}' +
2878 opt1 + opt2 + '{' + body + '}' + opt3;
2880 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
2882 if (!in_lyx_preamble)
2883 h_preamble << complete;
2887 if (t.cs() == "def") {
2888 string name = p.get_token().cs();
2889 // In fact, name may be more than the name:
2890 // In the test case of bug 8116
2891 // name == "csname SF@gobble@opt \endcsname".
2892 // Therefore, we need to use asInput() instead of cs().
2893 while (p.next_token().cat() != catBegin)
2894 name += p.get_token().asInput();
2895 if (!in_lyx_preamble)
2896 h_preamble << "\\def\\" << name << '{'
2897 << p.verbatim_item() << "}";
2901 if (t.cs() == "newcolumntype") {
2902 string const name = p.getArg('{', '}');
2903 trimSpaceAndEol(name);
2905 string opts = p.getOpt();
2906 if (!opts.empty()) {
2907 istringstream is(string(opts, 1));
2910 special_columns_[name[0]] = nargs;
2911 h_preamble << "\\newcolumntype{" << name << "}";
2913 h_preamble << "[" << nargs << "]";
2914 h_preamble << "{" << p.verbatim_item() << "}";
2918 if (t.cs() == "setcounter") {
2919 string const name = p.getArg('{', '}');
2920 string const content = p.getArg('{', '}');
2921 if (name == "secnumdepth")
2922 h_secnumdepth = content;
2923 else if (name == "tocdepth")
2924 h_tocdepth = content;
2926 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
2930 if (t.cs() == "setlength") {
2931 string const name = p.verbatim_item();
2932 string const content = p.verbatim_item();
2933 // the paragraphs are only not indented when \parindent is set to zero
2934 if (name == "\\parindent" && content != "")
2935 h_paragraph_indentation = translate_len(content);
2936 else if (name == "\\parskip" && isPackageUsed("parskip")) {
2937 if (content == "\\smallskipamount")
2938 h_defskip = "smallskip";
2939 else if (content == "\\medskipamount")
2940 h_defskip = "medskip";
2941 else if (content == "\\bigskipamount")
2942 h_defskip = "bigskip";
2943 else if (content == "\\baselineskip")
2944 h_defskip = "fullline";
2946 h_defskip = translate_len(content);
2947 } else if (name == "\\mathindent") {
2948 h_mathindentation = translate_len(content);
2950 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2954 if (t.cs() == "onehalfspacing") {
2955 h_spacing = "onehalf";
2959 if (t.cs() == "doublespacing") {
2960 h_spacing = "double";
2964 if (t.cs() == "setstretch") {
2965 h_spacing = "other " + p.verbatim_item();
2969 if (t.cs() == "synctex") {
2970 // the scheme is \synctex=value
2971 // where value can only be "1" or "-1"
2972 h_output_sync = "1";
2973 // there can be any character behind the value (e.g. a linebreak or a '\'
2974 // therefore we extract it char by char
2976 string value = p.get_token().asInput();
2978 value += p.get_token().asInput();
2979 h_output_sync_macro = "\\synctex=" + value;
2983 if (t.cs() == "begin") {
2984 string const name = p.getArg('{', '}');
2985 if (name == "document")
2987 h_preamble << "\\begin{" << name << "}";
2991 if (t.cs() == "geometry") {
2992 vector<string> opts = split_options(p.getArg('{', '}'));
2993 handle_geometry(opts);
2997 if (t.cs() == "definecolor") {
2998 string const color = p.getArg('{', '}');
2999 string const space = p.getArg('{', '}');
3000 string const value = p.getArg('{', '}');
3001 if (color == "document_fontcolor" && space == "rgb") {
3002 RGBColor c(RGBColorFromLaTeX(value));
3003 h_fontcolor = X11hexname(c);
3004 } else if (color == "note_fontcolor" && space == "rgb") {
3005 RGBColor c(RGBColorFromLaTeX(value));
3006 h_notefontcolor = X11hexname(c);
3007 } else if (color == "page_backgroundcolor" && space == "rgb") {
3008 RGBColor c(RGBColorFromLaTeX(value));
3009 h_backgroundcolor = X11hexname(c);
3010 } else if (color == "shadecolor" && space == "rgb") {
3011 RGBColor c(RGBColorFromLaTeX(value));
3012 h_boxbgcolor = X11hexname(c);
3014 h_preamble << "\\definecolor{" << color
3015 << "}{" << space << "}{" << value
3021 if (t.cs() == "bibliographystyle") {
3022 h_biblio_style = p.verbatim_item();
3026 if (t.cs() == "jurabibsetup") {
3027 // FIXME p.getArg('{', '}') is most probably wrong (it
3028 // does not handle nested braces).
3029 // Use p.verbatim_item() instead.
3030 vector<string> jurabibsetup =
3031 split_options(p.getArg('{', '}'));
3032 // add jurabibsetup to the jurabib package options
3033 add_package("jurabib", jurabibsetup);
3034 if (!jurabibsetup.empty()) {
3035 h_preamble << "\\jurabibsetup{"
3036 << join(jurabibsetup, ",") << '}';
3041 if (t.cs() == "hypersetup") {
3042 vector<string> hypersetup =
3043 split_options(p.verbatim_item());
3044 // add hypersetup to the hyperref package options
3045 handle_hyperref(hypersetup);
3046 if (!hypersetup.empty()) {
3047 h_preamble << "\\hypersetup{"
3048 << join(hypersetup, ",") << '}';
3053 if (t.cs() == "includeonly") {
3054 vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
3055 for (auto & iofile : includeonlys) {
3056 string filename(normalize_filename(iofile));
3057 string const path = getMasterFilePath(true);
3058 // We want to preserve relative/absolute filenames,
3059 // therefore path is only used for testing
3060 if (!makeAbsPath(filename, path).exists()) {
3061 // The file extension is probably missing.
3062 // Now try to find it out.
3063 string const tex_name =
3064 find_file(filename, path,
3065 known_tex_extensions);
3066 if (!tex_name.empty())
3067 filename = tex_name;
3070 if (makeAbsPath(filename, path).exists())
3071 fix_child_filename(filename);
3073 warning_message("Warning: Could not find included file '"
3075 outname = changeExtension(filename, "lyx");
3076 h_includeonlys.push_back(outname);
3081 if (is_known(t.cs(), known_if_3arg_commands)) {
3082 // prevent misparsing of \usepackage if it is used
3083 // as an argument (see e.g. our own output of
3084 // \@ifundefined above)
3085 string const arg1 = p.verbatim_item();
3086 string const arg2 = p.verbatim_item();
3087 string const arg3 = p.verbatim_item();
3088 // test case \@ifundefined{date}{}{\date{}}
3089 if (t.cs() == "@ifundefined" && arg1 == "date" &&
3090 arg2.empty() && arg3 == "\\date{}") {
3091 h_suppress_date = "true";
3092 // older tex2lyx versions did output
3093 // \@ifundefined{definecolor}{\usepackage{color}}{}
3094 } else if (t.cs() == "@ifundefined" &&
3095 arg1 == "definecolor" &&
3096 arg2 == "\\usepackage{color}" &&
3098 if (!in_lyx_preamble)
3099 h_preamble << package_beg_sep
3102 << "\\@ifundefined{definecolor}{color}{}"
3105 //\@ifundefined{showcaptionsetup}{}{%
3106 // \PassOptionsToPackage{caption=false}{subfig}}
3107 // that LyX uses for subfloats
3108 } else if (t.cs() == "@ifundefined" &&
3109 arg1 == "showcaptionsetup" && arg2.empty()
3110 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
3112 } else if (!in_lyx_preamble) {
3113 h_preamble << t.asInput()
3114 << '{' << arg1 << '}'
3115 << '{' << arg2 << '}'
3116 << '{' << arg3 << '}';
3121 if (is_known(t.cs(), known_if_commands)) {
3122 // must not parse anything in conditional code, since
3123 // LyX would output the parsed contents unconditionally
3124 if (!in_lyx_preamble)
3125 h_preamble << t.asInput();
3126 handle_if(p, in_lyx_preamble);
3130 if (!t.cs().empty() && !in_lyx_preamble) {
3131 h_preamble << '\\' << t.cs();
3136 // set textclass if not yet done (snippets without \documentclass and forced class)
3138 setTextClass(h_textclass, tc);
3140 // remove the whitespace
3143 if (h_papersides.empty()) {
3146 h_papersides = ss.str();
3149 // If the CJK package is used we cannot set the document language from
3150 // the babel options. Instead, we guess which language is used most
3151 // and set this one.
3152 default_language = h_language;
3153 if (is_full_document &&
3154 (auto_packages.find("CJK") != auto_packages.end() ||
3155 auto_packages.find("CJKutf8") != auto_packages.end())) {
3157 h_language = guessLanguage(p, default_language);
3159 if (explicit_babel && h_language != default_language) {
3160 // We set the document language to a CJK language,
3161 // but babel is explicitly called in the user preamble
3162 // without options. LyX will not add the default
3163 // language to the document options if it is either
3164 // english, or no text is set as default language.
3165 // Therefore we need to add a language option explicitly.
3166 // FIXME: It would be better to remove all babel calls
3167 // from the user preamble, but this is difficult
3168 // without re-introducing bug 7861.
3169 if (h_options.empty())
3170 h_options = lyx2babel(default_language);
3172 h_options += ',' + lyx2babel(default_language);
3176 // Finally, set the quote style.
3177 // LyX knows the following quotes styles:
3178 // british, cjk, cjkangle, danish, english, french, german,
3179 // polish, russian, swedish, swiss, and hebrew
3180 // conversion list taken from
3181 // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
3182 // (quotes for kazakh are unknown)
3184 if (is_known(h_language, known_british_quotes_languages))
3185 h_quotes_style = "british";
3187 else if (is_known(h_language, known_cjk_quotes_languages))
3188 h_quotes_style = "cjk";
3190 else if (is_known(h_language, known_cjkangle_quotes_languages))
3191 h_quotes_style = "cjkangle";
3193 else if (is_known(h_language, known_danish_quotes_languages))
3194 h_quotes_style = "danish";
3196 else if (is_known(h_language, known_french_quotes_languages))
3197 h_quotes_style = "french";
3199 else if (is_known(h_language, known_german_quotes_languages))
3200 h_quotes_style = "german";
3202 else if (is_known(h_language, known_polish_quotes_languages))
3203 h_quotes_style = "polish";
3205 else if (is_known(h_language, known_hungarian_quotes_languages))
3206 h_quotes_style = "hungarian";
3208 else if (is_known(h_language, known_russian_quotes_languages))
3209 h_quotes_style = "russian";
3211 else if (is_known(h_language, known_swedish_quotes_languages))
3212 h_quotes_style = "swedish";
3214 else if (is_known(h_language, known_swiss_quotes_languages))
3215 h_quotes_style = "swiss";
3217 else if (is_known(h_language, known_hebrew_quotes_languages))
3218 h_quotes_style = "hebrew";
3221 else if (is_known(h_language, known_english_quotes_languages))
3222 h_quotes_style = "english";
3226 string Preamble::parseEncoding(Parser & p, string const & forceclass)
3228 TeX2LyXDocClass dummy;
3229 parse(p, forceclass, true, dummy);
3230 if (h_inputencoding != "auto-legacy" && h_inputencoding != "auto-legacy-plain")
3231 return h_inputencoding;
3236 string babel2lyx(string const & language)
3238 char const * const * where = is_known(language, known_languages);
3240 return known_coded_languages[where - known_languages];
3245 string lyx2babel(string const & language)
3247 char const * const * where = is_known(language, known_coded_languages);
3249 return known_languages[where - known_coded_languages];
3254 string Preamble::polyglossia2lyx(string const & language)
3256 char const * const * where = is_known(language, polyglossia_languages);
3258 return coded_polyglossia_languages[where - polyglossia_languages];
3263 string rgbcolor2code(string const & name)
3265 char const * const * where = is_known(name, known_basic_colors);
3267 // "red", "green" etc
3268 return known_basic_color_codes[where - known_basic_colors];
3270 // "255,0,0", "0,255,0" etc
3271 RGBColor c(RGBColorFromLaTeX(name));
3272 return X11hexname(c);