3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
9 * Full author contact details are available in file CREDITS.
20 #include "LayoutFile.h"
23 #include "TextClass.h"
26 #include "support/convert.h"
27 #include "support/FileName.h"
28 #include "support/filetools.h"
29 #include "support/lstrings.h"
31 #include "support/regex.h"
37 using namespace lyx::support;
46 // CJK languages are handled in text.cpp, polyglossia languages are listed
49 * known babel language names (including synonyms)
50 * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
51 * please keep this in sync with known_coded_languages line by line!
53 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
54 "american", "arabic", "arabtex", "australian", "austrian", "azerbaijani", "bahasa", "bahasai",
55 "bahasam", "basque", "belarusian", "bosnian", "brazil", "brazilian", "breton", "british",
56 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
57 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "francais",
58 "french", "frenchb", "frenchle", "frenchpro", "friulan", "galician", "german", "germanb",
59 "georgian", "greek", "hebrew", "hungarian", "icelandic", "indon", "indonesian",
60 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
61 "latvian", "lithuanian", "lowersorbian", "lsorbian", "macedonian", "magyar", "malay", "meyalu",
62 "mongolian", "naustrian", "newzealand", "ngerman", "ngermanb", "norsk", "nswissgerman",
63 "nynorsk", "piedmontese", "polutonikogreek", "polish", "portuges", "portuguese",
64 "romanian", "romansh", "russian", "russianb", "samin", "scottish", "serbian", "serbian-latin",
65 "slovak", "slovene", "spanish", "swedish", "swissgerman", "thai", "turkish", "turkmen",
66 "ukraineb", "ukrainian", "uppersorbian", "UKenglish", "USenglish", "usorbian",
71 * the same as known_languages with .lyx names
72 * please keep this in sync with known_languages line by line!
74 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
75 "american", "arabic_arabi", "arabic_arabtex", "australian", "austrian", "azerbaijani", "bahasa", "bahasa",
76 "bahasam", "basque", "belarusian", "bosnian", "brazilian", "brazilian", "breton", "british",
77 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
78 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "french",
79 "french", "french", "french", "french", "friulan", "galician", "german", "german",
80 "georgian", "greek", "hebrew", "magyar", "icelandic", "bahasa", "bahasa",
81 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
82 "latvian", "lithuanian", "lowersorbian", "lowersorbian", "macedonian", "magyar", "bahasam", "bahasam",
83 "mongolian", "naustrian", "newzealand", "ngerman", "ngerman", "norsk", "german-ch",
84 "nynorsk", "piedmontese", "polutonikogreek", "polish", "portuguese", "portuguese",
85 "romanian", "romansh", "russian", "russian", "samin", "scottish", "serbian", "serbian-latin",
86 "slovak", "slovene", "spanish", "swedish", "german-ch-old", "thai", "turkish", "turkmen",
87 "ukrainian", "ukrainian", "uppersorbian", "english", "english", "uppersorbian",
88 "vietnamese", "welsh",
91 /// languages with british quotes (.lyx names)
92 const char * const known_british_quotes_languages[] = {"british", "welsh", 0};
94 /// languages with cjk quotes (.lyx names)
95 const char * const known_cjk_quotes_languages[] = {"chinese-traditional",
96 "japanese", "japanese-cjk", 0};
98 /// languages with cjk-angle quotes (.lyx names)
99 const char * const known_cjkangle_quotes_languages[] = {"korean", 0};
101 /// languages with danish quotes (.lyx names)
102 const char * const known_danish_quotes_languages[] = {"danish", 0};
104 /// languages with english quotes (.lyx names)
105 const char * const known_english_quotes_languages[] = {"american", "australian",
106 "bahasa", "bahasam", "bengali", "brazilian", "canadian", "chinese-simplified", "english",
107 "esperanto", "farsi", "interlingua", "irish", "newzealand", "scottish",
108 "thai", "turkish", "vietnamese", 0};
110 /// languages with french quotes (.lyx names)
111 const char * const known_french_quotes_languages[] = {"ancientgreek",
112 "arabic_arabi", "arabic_arabtex", "asturian", "belarusian", "breton",
113 "canadien", "catalan", "french", "friulan", "galician", "italian", "occitan",
114 "piedmontese", "portuguese", "spanish", "spanish-mexico", 0};
116 /// languages with german quotes (.lyx names)
117 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
118 "czech", "estonian", "georgian", "german", "icelandic", "latvian", "lithuanian",
119 "lowersorbian", "macedonian", "naustrian", "ngerman", "romansh", "slovak", "slovene",
122 /// languages with polish quotes (.lyx names)
123 const char * const known_polish_quotes_languages[] = {"afrikaans", "bosnian", "croatian",
124 "dutch", "magyar", "polish", "romanian", "serbian", "serbian-latin", 0};
126 /// languages with russian quotes (.lyx names)
127 const char * const known_russian_quotes_languages[] = {"azerbaijani", "oldrussian",
128 "russian", "ukrainian", 0};
130 /// languages with swedish quotes (.lyx names)
131 const char * const known_swedish_quotes_languages[] = {"finnish", "swedish", 0};
133 /// languages with swiss quotes (.lyx names)
134 const char * const known_swiss_quotes_languages[] = {"albanian",
135 "armenian", "basque", "churchslavonic", "german-ch", "german-ch-old",
136 "norsk", "nynorsk", "turkmen", "ukrainian", "vietnamese", 0};
138 /// known language packages from the times before babel
139 const char * const known_old_language_packages[] = {"french", "frenchle",
140 "frenchpro", "german", "ngerman", "pmfrench", 0};
142 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
144 const char * const known_roman_font_packages[] = { "ae", "beraserif", "bookman",
145 "ccfonts", "chancery", "charter", "cmr", "cochineal", "crimson", "CrimsonPro", "DejaVuSerif",
146 "DejaVuSerifCondensed", "fourier", "garamondx", "libertine", "libertineRoman", "libertine-type1",
147 "lmodern", "mathdesign", "mathpazo", "mathptmx", "MinionPro", "newcent", "noto", "noto-serif",
148 "PTSerif", "tgbonum", "tgchorus", "tgpagella", "tgschola", "tgtermes", "utopia", "xcharter", 0 };
150 const char * const known_sans_font_packages[] = { "avant", "berasans", "biolinum",
151 "biolinum-type1", "cantarell", "Chivo", "cmbr", "cmss", "DejaVuSans", "DejaVuSansCondensed", "FiraSans", "helvet", "iwona",
152 "iwonac", "iwonal", "iwonalc", "kurier", "kurierc", "kurierl", "kurierlc", "LibertinusSans-LF", "lmss", "noto-sans", "PTSans",
153 "tgadventor", "tgheros", "uop", 0 };
155 const char * const known_typewriter_font_packages[] = { "beramono", "cmtl", "cmtt", "courier", "DejaVuSansMono",
156 "FiraMono", "lmtt", "luximono", "libertineMono", "libertineMono-type1", "LibertinusMono-TLF", "lmodern",
157 "mathpazo", "mathptmx", "newcent", "noto-mono", "PTMono", "tgcursor", "txtt", 0 };
159 const char * const known_math_font_packages[] = { "eulervm", "newtxmath", 0};
161 const char * const known_latex_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
162 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
163 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
164 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
165 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
167 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
168 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
170 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
171 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
174 /// commands that can start an \if...\else...\endif sequence
175 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
176 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
177 "ifsidecap", "ifupgreek", 0};
179 const char * const known_basic_colors[] = {"black", "blue", "brown", "cyan",
180 "darkgray", "gray", "green", "lightgray", "lime", "magenta", "orange", "olive",
181 "pink", "purple", "red", "teal", "violet", "white", "yellow", 0};
183 const char * const known_basic_color_codes[] = {"#000000", "#0000ff", "#964B00", "#00ffff",
184 "#a9a9a9", "#808080", "#00ff00", "#d3d3d3", "#bfff00", "#ff00ff", "#ff7f00", "#808000",
185 "#ffc0cb", "#800080", "#ff0000", "#008080", "#8f00ff", "#ffffff", "#ffff00", 0};
187 /// conditional commands with three arguments like \@ifundefined{}{}{}
188 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
192 * Known file extensions for TeX files as used by \\includeonly
194 char const * const known_tex_extensions[] = {"tex", 0};
196 /// packages that work only in xetex
197 /// polyglossia is handled separately
198 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
199 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
200 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
202 /// packages that are automatically skipped if loaded by LyX
203 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
204 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color",
205 "float", "fontspec", "framed", "graphicx", "hhline", "ifthen", "longtable",
206 "makeidx", "minted", "multirow", "nomencl", "parskip", "pdfpages", "prettyref", "refstyle",
207 "rotating", "rotfloat", "splitidx", "setspace", "subscript", "tabularx","textcomp", "tipa",
208 "tipx", "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xcolor", "xltabular",
211 // codes used to remove packages that are loaded automatically by LyX.
212 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
213 const char package_beg_sep = '\001';
214 const char package_mid_sep = '\002';
215 const char package_end_sep = '\003';
218 // returns true if at least one of the options in what has been found
219 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
225 // the last language option is the document language (for babel and LyX)
226 // the last size option is the document font size
227 vector<string>::iterator it;
228 vector<string>::iterator position = opts.begin();
229 for (; *what; ++what) {
230 it = find(opts.begin(), opts.end(), *what);
231 if (it != opts.end()) {
232 if (it >= position) {
243 void delete_opt(vector<string> & opts, char const * const * what)
248 // remove found options from the list
249 // do this after handle_opt to avoid potential memory leaks
250 vector<string>::iterator it;
251 for (; *what; ++what) {
252 it = find(opts.begin(), opts.end(), *what);
253 if (it != opts.end())
260 * Split a package options string (keyval format) into a vector.
262 * authorformat=smallcaps,
264 * titleformat=colonsep,
265 * bibformat={tabular,ibidem,numbered}
267 vector<string> split_options(string const & input)
269 vector<string> options;
273 Token const & t = p.get_token();
274 if (t.asInput() == ",") {
275 options.push_back(trimSpaceAndEol(option));
277 } else if (t.asInput() == "=") {
280 if (p.next_token().asInput() == "{")
281 option += '{' + p.getArg('{', '}') + '}';
282 } else if (t.cat() != catSpace && t.cat() != catComment)
283 option += t.asInput();
287 options.push_back(trimSpaceAndEol(option));
294 * Retrieve a keyval option "name={value with=sign}" named \p name from
295 * \p options and return the value.
296 * The found option is also removed from \p options.
298 string process_keyval_opt(vector<string> & options, string name)
300 for (size_t i = 0; i < options.size(); ++i) {
301 vector<string> option;
302 split(options[i], option, '=');
303 if (option.size() < 2)
305 if (option[0] == name) {
306 options.erase(options.begin() + i);
307 option.erase(option.begin());
308 return join(option, "=");
314 } // anonymous namespace
318 * known polyglossia language names (including variants)
319 * FIXME: support spelling=old for german variants (german vs. ngerman LyX names etc)
321 const char * const Preamble::polyglossia_languages[] = {
322 "albanian", "american", "amharic", "ancient", "arabic", "armenian", "asturian", "australian",
323 "bahasai", "bahasam", "basque", "bengali", "brazil", "brazilian", "breton", "british", "bulgarian",
324 "catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
325 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
326 "galician", "greek", "monotonic", "hebrew", "hindi",
327 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean",
328 "lao", "latin", "latvian", "lithuanian", "lsorbian", "magyar", "malayalam", "marathi",
329 "austrian", "newzealand", "german", "norsk", "nynorsk", "occitan", "oldrussian",
330 "piedmontese", "polish", "polytonic", "portuges", "romanian", "romansh", "russian",
331 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovenian", "spanish", "swedish", "syriac",
332 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
333 "ukrainian", "urdu", "usorbian", "vietnamese", "welsh", 0};
334 // not yet supported by LyX: "korean", "nko"
337 * the same as polyglossia_languages with .lyx names
338 * please keep this in sync with polyglossia_languages line by line!
340 const char * const Preamble::coded_polyglossia_languages[] = {
341 "albanian", "american", "amharic", "ancientgreek", "arabic_arabi", "armenian", "asturian", "australian",
342 "bahasa", "bahasam", "basque", "bengali", "brazilian", "brazilian", "breton", "british", "bulgarian",
343 "catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
344 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
345 "galician", "greek", "greek", "hebrew", "hindi",
346 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean",
347 "lao", "latin", "latvian", "lithuanian", "lowersorbian", "magyar", "malayalam", "marathi",
348 "naustrian","newzealand", "ngerman", "norsk", "nynorsk", "occitan", "oldrussian",
349 "piedmontese", "polish", "polutonikogreek", "portuges", "romanian", "romansh", "russian",
350 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovene", "spanish", "swedish", "syriac",
351 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
352 "ukrainian", "urdu", "uppersorbian", "vietnamese", "welsh", 0};
353 // not yet supported by LyX: "korean-polyglossia", "nko"
356 bool Preamble::usePolyglossia() const
358 return h_use_non_tex_fonts && h_language_package == "default";
362 bool Preamble::indentParagraphs() const
364 return h_paragraph_separation == "indent";
368 bool Preamble::isPackageUsed(string const & package) const
370 return used_packages.find(package) != used_packages.end();
374 bool Preamble::isPackageAutoLoaded(string const & package) const
376 return auto_packages.find(package) != auto_packages.end();
380 vector<string> Preamble::getPackageOptions(string const & package) const
382 map<string, vector<string> >::const_iterator it = used_packages.find(package);
383 if (it != used_packages.end())
385 return vector<string>();
389 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
391 auto_packages.insert(package);
395 void Preamble::addModule(string const & module)
397 for (auto const & m : used_modules) {
401 used_modules.push_back(module);
405 void Preamble::suppressDate(bool suppress)
408 h_suppress_date = "true";
410 h_suppress_date = "false";
414 void Preamble::registerAuthor(std::string const & name, string const & initials)
416 Author author(from_utf8(name), empty_docstring(), from_utf8(initials));
417 author.setUsed(true);
418 authors_.record(author);
419 h_tracking_changes = "true";
420 h_output_changes = "true";
424 Author const & Preamble::getAuthor(std::string const & name) const
426 Author author(from_utf8(name), empty_docstring(), empty_docstring());
427 for (AuthorList::Authors::const_iterator it = authors_.begin();
428 it != authors_.end(); ++it)
431 static Author const dummy;
436 int Preamble::getSpecialTableColumnArguments(char c) const
438 map<char, int>::const_iterator it = special_columns_.find(c);
439 if (it == special_columns_.end())
445 void Preamble::add_package(string const & name, vector<string> & options)
447 // every package inherits the global options
448 if (used_packages.find(name) == used_packages.end())
449 used_packages[name] = split_options(h_options);
451 // Insert options passed via PassOptionsToPackage
452 for (auto const & p : extra_package_options_) {
453 if (p.first == name) {
454 vector<string> eo = getVectorFromString(p.second);
455 for (auto const & eoi : eo)
456 options.push_back(eoi);
460 vector<string> & v = used_packages[name];
461 v.insert(v.end(), options.begin(), options.end());
462 if (name == "jurabib") {
463 // Don't output the order argument (see the cite command
464 // handling code in text.cpp).
465 vector<string>::iterator end =
466 remove(options.begin(), options.end(), "natbiborder");
467 end = remove(options.begin(), end, "jurabiborder");
468 options.erase(end, options.end());
472 void Preamble::setTextClass(string const tclass, TeX2LyXDocClass & tc)
474 h_textclass = tclass;
475 tc.setName(h_textclass);
476 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
477 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
485 // Given is a string like "scaled=0.9" or "scale=0.9", return 0.9 * 100
486 bool scale_as_percentage(string const & scale, string & percentage)
488 if (contains(scale, '=')) {
489 string const value = support::split(scale, '=');
490 if (isStrDbl(value)) {
491 percentage = convert<string>(
492 static_cast<int>(100 * convert<double>(value)));
500 string remove_braces(string const & value)
504 if (value[0] == '{' && value[value.length()-1] == '}')
505 return value.substr(1, value.length()-2);
509 } // anonymous namespace
512 Preamble::Preamble() : one_language(true), explicit_babel(false),
513 title_layout_found(false), index_number(0), h_font_cjk_set(false)
517 h_biblio_style = "plain";
518 h_bibtex_command = "default";
519 h_cite_engine = "basic";
520 h_cite_engine_type = "default";
522 h_defskip = "medskip";
523 h_dynamic_quotes = false;
526 h_fontencoding = "default";
527 h_font_roman[0] = "default";
528 h_font_roman[1] = "default";
529 h_font_sans[0] = "default";
530 h_font_sans[1] = "default";
531 h_font_typewriter[0] = "default";
532 h_font_typewriter[1] = "default";
533 h_font_math[0] = "auto";
534 h_font_math[1] = "auto";
535 h_font_default_family = "default";
536 h_use_non_tex_fonts = false;
538 h_font_roman_osf = "false";
539 h_font_sans_osf = "false";
540 h_font_typewriter_osf = "false";
541 h_font_sf_scale[0] = "100";
542 h_font_sf_scale[1] = "100";
543 h_font_tt_scale[0] = "100";
544 h_font_tt_scale[1] = "100";
545 // h_font_roman_opts;
547 // h_font_typewriter_opts;
549 h_is_mathindent = "0";
550 h_math_numbering_side = "default";
551 h_graphics = "default";
552 h_default_output_format = "default";
553 h_html_be_strict = "false";
554 h_html_css_as_file = "0";
555 h_html_math_output = "0";
556 h_docbook_table_output = "0";
557 h_index[0] = "Index";
558 h_index_command = "default";
559 h_inputencoding = "auto-legacy";
560 h_justification = "true";
561 h_language = "english";
562 h_language_package = "none";
564 h_maintain_unincluded_children = "no";
568 h_output_changes = "false";
569 h_change_bars = "false";
571 //h_output_sync_macro
572 h_papercolumns = "1";
573 h_paperfontsize = "default";
574 h_paperorientation = "portrait";
575 h_paperpagestyle = "default";
577 h_papersize = "default";
578 h_paragraph_indentation = "default";
579 h_paragraph_separation = "indent";
584 h_pdf_bookmarks = "0";
585 h_pdf_bookmarksnumbered = "0";
586 h_pdf_bookmarksopen = "0";
587 h_pdf_bookmarksopenlevel = "1";
588 h_pdf_breaklinks = "0";
589 h_pdf_pdfborder = "0";
590 h_pdf_colorlinks = "0";
591 h_pdf_backref = "section";
592 h_pdf_pdfusetitle = "0";
594 //h_pdf_quoted_options;
595 h_quotes_style = "english";
597 h_shortcut[0] = "idx";
598 h_spacing = "single";
599 h_save_transient_properties = "true";
600 h_suppress_date = "false";
601 h_textclass = "article";
603 h_tracking_changes = "false";
604 h_use_bibtopic = "false";
605 h_use_dash_ligatures = "true";
606 h_use_indices = "false";
607 h_use_geometry = "false";
608 h_use_default_options = "false";
609 h_use_hyperref = "false";
610 h_use_microtype = "false";
611 h_use_lineno = "false";
612 h_use_refstyle = false;
613 h_use_minted = false;
614 h_use_packages["amsmath"] = "1";
615 h_use_packages["amssymb"] = "0";
616 h_use_packages["cancel"] = "0";
617 h_use_packages["esint"] = "1";
618 h_use_packages["mhchem"] = "0";
619 h_use_packages["mathdots"] = "0";
620 h_use_packages["mathtools"] = "0";
621 h_use_packages["stackrel"] = "0";
622 h_use_packages["stmaryrd"] = "0";
623 h_use_packages["undertilde"] = "0";
627 void Preamble::handle_hyperref(vector<string> & options)
629 // FIXME swallow inputencoding changes that might surround the
630 // hyperref setup if it was written by LyX
631 h_use_hyperref = "true";
632 // swallow "unicode=true", since LyX does always write that
633 vector<string>::iterator it =
634 find(options.begin(), options.end(), "unicode=true");
635 if (it != options.end())
637 it = find(options.begin(), options.end(), "pdfusetitle");
638 if (it != options.end()) {
639 h_pdf_pdfusetitle = "1";
642 string bookmarks = process_keyval_opt(options, "bookmarks");
643 if (bookmarks == "true")
644 h_pdf_bookmarks = "1";
645 else if (bookmarks == "false")
646 h_pdf_bookmarks = "0";
647 if (h_pdf_bookmarks == "1") {
648 string bookmarksnumbered =
649 process_keyval_opt(options, "bookmarksnumbered");
650 if (bookmarksnumbered == "true")
651 h_pdf_bookmarksnumbered = "1";
652 else if (bookmarksnumbered == "false")
653 h_pdf_bookmarksnumbered = "0";
654 string bookmarksopen =
655 process_keyval_opt(options, "bookmarksopen");
656 if (bookmarksopen == "true")
657 h_pdf_bookmarksopen = "1";
658 else if (bookmarksopen == "false")
659 h_pdf_bookmarksopen = "0";
660 if (h_pdf_bookmarksopen == "1") {
661 string bookmarksopenlevel =
662 process_keyval_opt(options, "bookmarksopenlevel");
663 if (!bookmarksopenlevel.empty())
664 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
667 string breaklinks = process_keyval_opt(options, "breaklinks");
668 if (breaklinks == "true")
669 h_pdf_breaklinks = "1";
670 else if (breaklinks == "false")
671 h_pdf_breaklinks = "0";
672 string pdfborder = process_keyval_opt(options, "pdfborder");
673 if (pdfborder == "{0 0 0}")
674 h_pdf_pdfborder = "1";
675 else if (pdfborder == "{0 0 1}")
676 h_pdf_pdfborder = "0";
677 string backref = process_keyval_opt(options, "backref");
678 if (!backref.empty())
679 h_pdf_backref = backref;
680 string colorlinks = process_keyval_opt(options, "colorlinks");
681 if (colorlinks == "true")
682 h_pdf_colorlinks = "1";
683 else if (colorlinks == "false")
684 h_pdf_colorlinks = "0";
685 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
686 if (!pdfpagemode.empty())
687 h_pdf_pagemode = pdfpagemode;
688 string pdftitle = process_keyval_opt(options, "pdftitle");
689 if (!pdftitle.empty()) {
690 h_pdf_title = remove_braces(pdftitle);
692 string pdfauthor = process_keyval_opt(options, "pdfauthor");
693 if (!pdfauthor.empty()) {
694 h_pdf_author = remove_braces(pdfauthor);
696 string pdfsubject = process_keyval_opt(options, "pdfsubject");
697 if (!pdfsubject.empty())
698 h_pdf_subject = remove_braces(pdfsubject);
699 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
700 if (!pdfkeywords.empty())
701 h_pdf_keywords = remove_braces(pdfkeywords);
702 if (!options.empty()) {
703 if (!h_pdf_quoted_options.empty())
704 h_pdf_quoted_options += ',';
705 h_pdf_quoted_options += join(options, ",");
711 void Preamble::handle_geometry(vector<string> & options)
713 h_use_geometry = "true";
714 vector<string>::iterator it;
716 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
717 h_paperorientation = "landscape";
721 // keyval version: "paper=letter" or "paper=letterpaper"
722 string paper = process_keyval_opt(options, "paper");
724 if (suffixIs(paper, "paper"))
725 paper = subst(paper, "paper", "");
726 // alternative version: "letterpaper"
727 handle_opt(options, known_latex_paper_sizes, paper);
728 if (suffixIs(paper, "paper"))
729 paper = subst(paper, "paper", "");
730 delete_opt(options, known_latex_paper_sizes);
734 char const * const * margin = known_paper_margins;
735 for (; *margin; ++margin) {
736 string value = process_keyval_opt(options, *margin);
737 if (!value.empty()) {
738 int k = margin - known_paper_margins;
739 string name = known_coded_paper_margins[k];
740 h_margins += '\\' + name + ' ' + value + '\n';
746 void Preamble::handle_package(Parser &p, string const & name,
747 string const & opts, bool in_lyx_preamble,
750 vector<string> options = split_options(opts);
751 add_package(name, options);
753 if (is_known(name, known_xetex_packages)) {
755 h_use_non_tex_fonts = true;
756 registerAutomaticallyLoadedPackage("fontspec");
757 if (h_inputencoding == "auto-legacy")
758 p.setEncoding("UTF-8");
761 // vector of all options for easier parsing and
763 vector<string> allopts = getVectorFromString(opts);
764 // this stores the potential extra options
771 // By default, we use the package name as LyX font name,
772 // so this only needs to be reset if these names differ
773 if (is_known(name, known_roman_font_packages))
774 h_font_roman[0] = name;
776 if (name == "ccfonts") {
777 for (auto const & opt : allopts) {
783 h_font_roman_opts = xopts;
787 if (name == "lmodern") {
788 for (auto const & opt : allopts) {
794 h_font_roman_opts = xopts;
798 if (name == "fourier") {
799 h_font_roman[0] = "utopia";
800 for (auto const & opt : allopts) {
802 h_font_roman_osf = "true";
805 if (opt == "expert") {
814 h_font_roman_opts = xopts;
818 if (name == "garamondx") {
819 for (auto const & opt : allopts) {
821 h_font_roman_osf = "true";
829 h_font_roman_opts = xopts;
833 if (name == "libertine") {
834 // this automatically invokes biolinum
835 h_font_sans[0] = "biolinum";
836 // as well as libertineMono
837 h_font_typewriter[0] = "libertine-mono";
838 for (auto const & opt : allopts) {
840 h_font_roman_osf = "true";
843 if (opt == "lining") {
844 h_font_roman_osf = "false";
852 h_font_roman_opts = xopts;
856 if (name == "libertineRoman" || name == "libertine-type1") {
857 h_font_roman[0] = "libertine";
858 // NOTE: contrary to libertine.sty, libertineRoman
859 // and libertine-type1 do not automatically invoke
860 // biolinum and libertineMono
861 if (opts == "lining")
862 h_font_roman_osf = "false";
863 else if (opts == "osf")
864 h_font_roman_osf = "true";
867 if (name == "libertinus" || name == "libertinus-type1") {
874 for (auto const & opt : allopts) {
875 if (opt == "rm" || opt == "serif") {
880 if (opt == "sf" || opt == "sans") {
885 if (opt == "tt=false" || opt == "mono=false") {
893 if (opt == "scaleSF") {
897 if (opt == "scaleTT") {
901 if (opt == "lining") {
902 h_font_roman_osf = "false";
910 h_font_roman[0] = "libertinus";
912 h_font_roman_osf = "true";
914 h_font_roman_osf = "false";
917 h_font_sans[0] = "LibertinusSans-LF";
919 h_font_sans_osf = "true";
921 h_font_sans_osf = "false";
922 if (!scalesf.empty())
923 scale_as_percentage(scalesf, h_font_sf_scale[0]);
926 h_font_typewriter[0] = "LibertinusMono-TLF";
927 if (!scalett.empty())
928 scale_as_percentage(scalett, h_font_tt_scale[0]);
931 h_font_roman_opts = xopts;
935 if (name == "MinionPro") {
936 h_font_roman[0] = "minionpro";
937 h_font_roman_osf = "true";
938 h_font_math[0] = "auto";
939 for (auto const & opt : allopts) {
941 h_font_roman_osf = "false";
944 if (opt == "onlytext") {
945 h_font_math[0] = "default";
953 h_font_roman_opts = xopts;
957 if (name == "mathdesign") {
958 for (auto const & opt : allopts) {
959 if (opt == "charter") {
960 h_font_roman[0] = "md-charter";
963 if (opt == "garamond") {
964 h_font_roman[0] = "md-garamond";
967 if (opt == "utopia") {
968 h_font_roman[0] = "md-utopia";
971 if (opt == "expert") {
973 h_font_roman_osf = "true";
979 else if (name == "mathpazo") {
980 h_font_roman[0] = "palatino";
981 for (auto const & opt : allopts) {
983 h_font_roman_osf = "true";
995 h_font_roman_opts = xopts;
999 else if (name == "mathptmx") {
1000 h_font_roman[0] = "times";
1001 for (auto const & opt : allopts) {
1007 h_font_roman_opts = xopts;
1011 if (name == "crimson")
1012 h_font_roman[0] = "cochineal";
1014 if (name == "cochineal") {
1015 for (auto const & opt : allopts) {
1016 if (opt == "osf" || opt == "oldstyle") {
1017 h_font_roman_osf = "true";
1020 if (opt == "proportional" || opt == "p")
1027 h_font_roman_opts = xopts;
1031 if (name == "CrimsonPro") {
1032 h_font_roman_osf = "true";
1033 for (auto const & opt : allopts) {
1034 if (opt == "lf" || opt == "lining") {
1035 h_font_roman_osf = "false";
1038 if (opt == "proportional" || opt == "p")
1040 if (opt == "medium") {
1041 h_font_roman[0] = "CrimsonProMedium";
1044 if (opt == "extralight") {
1045 h_font_roman[0] = "CrimsonProExtraLight";
1048 if (opt == "light") {
1049 h_font_roman[0] = "CrimsonProLight";
1057 h_font_roman_opts = xopts;
1063 // font uses old-style figure
1064 h_font_roman_osf = "true";
1066 if (name == "paratype") {
1067 // in this case all fonts are ParaType
1068 h_font_roman[0] = "PTSerif-TLF";
1069 h_font_sans[0] = "default";
1070 h_font_typewriter[0] = "default";
1073 if (name == "PTSerif")
1074 h_font_roman[0] = "PTSerif-TLF";
1076 if (name == "XCharter") {
1077 h_font_roman[0] = "xcharter";
1078 for (auto const & opt : allopts) {
1080 h_font_roman_osf = "true";
1088 h_font_roman_opts = xopts;
1092 if (name == "plex-serif") {
1093 h_font_roman[0] = "IBMPlexSerif";
1094 for (auto const & opt : allopts) {
1095 if (opt == "thin") {
1096 h_font_roman[0] = "IBMPlexSerifThin";
1099 if (opt == "extralight") {
1100 h_font_roman[0] = "IBMPlexSerifExtraLight";
1103 if (opt == "light") {
1104 h_font_roman[0] = "IBMPlexSerifLight";
1112 h_font_roman_opts = xopts;
1116 if (name == "noto-serif" || name == "noto") {
1123 bool extralight = false;
1125 bool medium = false;
1128 if (name == "noto") {
1133 // Since the options might apply to different shapes,
1134 // we need to parse all options first and then handle them.
1135 for (auto const & opt : allopts) {
1136 if (opt == "regular")
1146 if (opt == "thin") {
1150 if (opt == "extralight") {
1154 if (opt == "light") {
1158 if (opt == "medium") {
1169 if (opt == "nott") {
1177 if (prefixIs(opt, "scaled=")) {
1186 // handle options that might affect different shapes
1187 if (name == "noto-serif" || rm) {
1189 h_font_roman[0] = "NotoSerifThin";
1190 else if (extralight)
1191 h_font_roman[0] = "NotoSerifExtralight";
1193 h_font_roman[0] = "NotoSerifLight";
1195 h_font_roman[0] = "NotoSerifMedium";
1197 h_font_roman[0] = "NotoSerifRegular";
1199 h_font_roman_osf = "true";
1201 h_font_roman_opts = xopts;
1203 if (name == "noto" && sf) {
1205 h_font_sans[0] = "NotoSansThin";
1206 else if (extralight)
1207 h_font_sans[0] = "NotoSansExtralight";
1209 h_font_sans[0] = "NotoSansLight";
1211 h_font_sans[0] = "NotoSansMedium";
1213 h_font_sans[0] = "NotoSansRegular";
1215 h_font_sans_osf = "true";
1217 scale_as_percentage(scl, h_font_sf_scale[0]);
1219 h_font_sans_opts = xopts;
1221 if (name == "noto" && tt) {
1222 h_font_typewriter[0] = "NotoMonoRegular";
1224 h_font_typewriter_osf = "true";
1226 scale_as_percentage(scl, h_font_tt_scale[0]);
1228 h_font_typewriter_opts = xopts;
1232 if (name == "sourceserifpro") {
1233 h_font_roman[0] = "ADOBESourceSerifPro";
1234 for (auto const & opt : allopts) {
1236 h_font_roman_osf = "true";
1244 h_font_roman_opts = xopts;
1252 // By default, we use the package name as LyX font name,
1253 // so this only needs to be reset if these names differ.
1254 // Also, we handle the scaling option here generally.
1255 if (is_known(name, known_sans_font_packages)) {
1256 h_font_sans[0] = name;
1257 if (contains(opts, "scale")) {
1258 vector<string>::iterator it = allopts.begin();
1259 for (; it != allopts.end() ; ++it) {
1260 string const opt = *it;
1261 if (prefixIs(opt, "scaled=") || prefixIs(opt, "scale=")) {
1262 if (scale_as_percentage(opt, h_font_sf_scale[0])) {
1271 if (name == "biolinum" || name == "biolinum-type1") {
1272 h_font_sans[0] = "biolinum";
1273 for (auto const & opt : allopts) {
1274 if (prefixIs(opt, "osf")) {
1275 h_font_sans_osf = "true";
1283 h_font_sans_opts = xopts;
1287 if (name == "cantarell") {
1288 for (auto const & opt : allopts) {
1289 if (opt == "defaultsans")
1291 if (prefixIs(opt, "oldstyle")) {
1292 h_font_sans_osf = "true";
1300 h_font_sans_opts = xopts;
1304 if (name == "Chivo") {
1305 for (auto const & opt : allopts) {
1306 if (opt == "thin") {
1307 h_font_roman[0] = "ChivoThin";
1310 if (opt == "light") {
1311 h_font_roman[0] = "ChivoLight";
1314 if (opt == "regular") {
1315 h_font_roman[0] = "Chivo";
1318 if (opt == "medium") {
1319 h_font_roman[0] = "ChivoMedium";
1322 if (prefixIs(opt, "oldstyle")) {
1323 h_font_sans_osf = "true";
1331 h_font_sans_opts = xopts;
1335 if (name == "PTSans") {
1336 h_font_sans[0] = "PTSans-TLF";
1339 if (name == "FiraSans") {
1340 h_font_sans_osf = "true";
1341 for (auto const & opt : allopts) {
1342 if (opt == "book") {
1343 h_font_sans[0] = "FiraSansBook";
1346 if (opt == "thin") {
1349 if (opt == "extralight") {
1350 h_font_sans[0] = "FiraSansExtralight";
1353 if (opt == "light") {
1354 h_font_sans[0] = "FiraSansLight";
1357 if (opt == "ultralight") {
1358 h_font_sans[0] = "FiraSansUltralight";
1361 if (opt == "thin") {
1362 h_font_sans[0] = "FiraSansThin";
1365 if (opt == "lf" || opt == "lining") {
1366 h_font_sans_osf = "false";
1374 h_font_sans_opts = xopts;
1378 if (name == "plex-sans") {
1379 h_font_sans[0] = "IBMPlexSans";
1380 for (auto const & opt : allopts) {
1381 if (opt == "condensed") {
1382 h_font_sans[0] = "IBMPlexSansCondensed";
1385 if (opt == "thin") {
1386 h_font_sans[0] = "IBMPlexSansThin";
1389 if (opt == "extralight") {
1390 h_font_sans[0] = "IBMPlexSansExtraLight";
1393 if (opt == "light") {
1394 h_font_sans[0] = "IBMPlexSansLight";
1402 h_font_sans_opts = xopts;
1406 if (name == "noto-sans") {
1407 h_font_sans[0] = "NotoSansRegular";
1408 for (auto const & opt : allopts) {
1409 if (opt == "regular")
1411 if (opt == "medium") {
1412 h_font_sans[0] = "NotoSansMedium";
1415 if (opt == "thin") {
1416 h_font_sans[0] = "NotoSansThin";
1419 if (opt == "extralight") {
1420 h_font_sans[0] = "NotoSansExtralight";
1423 if (opt == "light") {
1424 h_font_sans[0] = "NotoSansLight";
1428 h_font_sans_osf = "true";
1436 h_font_sans_opts = xopts;
1440 if (name == "sourcesanspro") {
1441 h_font_sans[0] = "ADOBESourceSansPro";
1442 for (auto const & opt : allopts) {
1444 h_font_sans_osf = "true";
1452 h_font_sans_opts = xopts;
1460 // By default, we use the package name as LyX font name,
1461 // so this only needs to be reset if these names differ.
1462 // Also, we handle the scaling option here generally.
1463 if (is_known(name, known_typewriter_font_packages)) {
1464 h_font_typewriter[0] = name;
1465 if (contains(opts, "scale")) {
1466 vector<string>::iterator it = allopts.begin();
1467 for (; it != allopts.end() ; ++it) {
1468 string const opt = *it;
1469 if (prefixIs(opt, "scaled=") || prefixIs(opt, "scale=")) {
1470 if (scale_as_percentage(opt, h_font_tt_scale[0])) {
1479 if (name == "libertineMono" || name == "libertineMono-type1")
1480 h_font_typewriter[0] = "libertine-mono";
1482 if (name == "FiraMono") {
1483 h_font_typewriter_osf = "true";
1484 for (auto const & opt : allopts) {
1485 if (opt == "lf" || opt == "lining") {
1486 h_font_typewriter_osf = "false";
1494 h_font_typewriter_opts = xopts;
1498 if (name == "PTMono")
1499 h_font_typewriter[0] = "PTMono-TLF";
1501 if (name == "plex-mono") {
1502 h_font_typewriter[0] = "IBMPlexMono";
1503 for (auto const & opt : allopts) {
1504 if (opt == "thin") {
1505 h_font_typewriter[0] = "IBMPlexMonoThin";
1508 if (opt == "extralight") {
1509 h_font_typewriter[0] = "IBMPlexMonoExtraLight";
1512 if (opt == "light") {
1513 h_font_typewriter[0] = "IBMPlexMonoLight";
1521 h_font_typewriter_opts = xopts;
1525 if (name == "noto-mono") {
1526 h_font_typewriter[0] = "NotoMonoRegular";
1527 for (auto const & opt : allopts) {
1528 if (opt == "regular")
1535 h_font_typewriter_opts = xopts;
1539 if (name == "sourcecodepro") {
1540 h_font_typewriter[0] = "ADOBESourceCodePro";
1541 for (auto const & opt : allopts) {
1543 h_font_typewriter_osf = "true";
1551 h_font_typewriter_opts = xopts;
1559 // By default, we use the package name as LyX font name,
1560 // so this only needs to be reset if these names differ.
1561 if (is_known(name, known_math_font_packages))
1562 h_font_math[0] = name;
1564 if (name == "newtxmath") {
1566 h_font_math[0] = "newtxmath";
1567 else if (opts == "garamondx")
1568 h_font_math[0] = "garamondx-ntxm";
1569 else if (opts == "libertine")
1570 h_font_math[0] = "libertine-ntxm";
1571 else if (opts == "minion")
1572 h_font_math[0] = "minion-ntxm";
1573 else if (opts == "cochineal")
1574 h_font_math[0] = "cochineal-ntxm";
1577 if (name == "iwona")
1579 h_font_math[0] = "iwona-math";
1581 if (name == "kurier")
1583 h_font_math[0] = "kurier-math";
1585 // after the detection and handling of special cases, we can remove the
1586 // fonts, otherwise they would appear in the preamble, see bug #7856
1587 if (is_known(name, known_roman_font_packages) || is_known(name, known_sans_font_packages)
1588 || is_known(name, known_typewriter_font_packages) || is_known(name, known_math_font_packages))
1590 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
1591 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
1592 name == "esint" || name == "mhchem" || name == "mathdots" ||
1593 name == "mathtools" || name == "stackrel" ||
1594 name == "stmaryrd" || name == "undertilde") {
1595 h_use_packages[name] = "2";
1596 registerAutomaticallyLoadedPackage(name);
1599 else if (name == "babel") {
1600 h_language_package = "default";
1601 // One might think we would have to do nothing if babel is loaded
1602 // without any options to prevent pollution of the preamble with this
1603 // babel call in every roundtrip.
1604 // But the user could have defined babel-specific things afterwards. So
1605 // we need to keep it in the preamble to prevent cases like bug #7861.
1606 if (!opts.empty()) {
1607 // check if more than one option was used - used later for inputenc
1608 if (options.begin() != options.end() - 1)
1609 one_language = false;
1610 // babel takes the last language of the option of its \usepackage
1611 // call as document language. If there is no such language option, the
1612 // last language in the documentclass options is used.
1613 handle_opt(options, known_languages, h_language);
1614 // translate the babel name to a LyX name
1615 h_language = babel2lyx(h_language);
1616 if (h_language == "japanese") {
1617 // For Japanese, the encoding isn't indicated in the source
1618 // file, and there's really not much we can do. We could
1619 // 1) offer a list of possible encodings to choose from, or
1620 // 2) determine the encoding of the file by inspecting it.
1621 // For the time being, we leave the encoding alone so that
1622 // we don't get iconv errors when making a wrong guess, and
1623 // we will output a note at the top of the document
1624 // explaining what to do.
1625 Encoding const * const enc = encodings.fromIconvName(
1626 p.getEncoding(), Encoding::japanese, false);
1628 h_inputencoding = enc->name();
1629 is_nonCJKJapanese = true;
1630 // in this case babel can be removed from the preamble
1631 registerAutomaticallyLoadedPackage("babel");
1633 // If babel is called with options, LyX puts them by default into the
1634 // document class options. This works for most languages, except
1635 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
1636 // perhaps in future others.
1637 // Therefore keep the babel call as it is as the user might have
1639 string const babelcall = "\\usepackage[" + opts + "]{babel}\n";
1640 if (!contains(h_preamble.str(), babelcall))
1641 h_preamble << babelcall;
1643 delete_opt(options, known_languages);
1645 if (!contains(h_preamble.str(), "\\usepackage{babel}\n"))
1646 h_preamble << "\\usepackage{babel}\n";
1647 explicit_babel = true;
1651 else if (name == "polyglossia") {
1652 h_language_package = "default";
1653 h_default_output_format = "pdf4";
1654 h_use_non_tex_fonts = true;
1656 registerAutomaticallyLoadedPackage("xunicode");
1657 if (h_inputencoding == "auto-legacy")
1658 p.setEncoding("UTF-8");
1661 else if (name == "CJK") {
1662 // set the encoding to "auto-legacy" because it might be set to "auto-legacy-plain" by the babel handling
1663 // and this would not be correct for CJK
1664 if (h_inputencoding == "auto-legacy-plain")
1665 h_inputencoding = "auto-legacy";
1666 registerAutomaticallyLoadedPackage("CJK");
1669 else if (name == "CJKutf8") {
1670 h_inputencoding = "utf8-cjk";
1671 p.setEncoding("UTF-8");
1672 registerAutomaticallyLoadedPackage("CJKutf8");
1675 else if (name == "fontenc") {
1676 h_fontencoding = getStringFromVector(options, ",");
1680 else if (name == "inputenc" || name == "luainputenc") {
1681 // h_inputencoding is only set when there is not more than one
1682 // inputenc option because otherwise h_inputencoding must be
1683 // set to "auto-legacy" (the default encodings of the document's languages)
1684 // Therefore check that exactly one option is passed to inputenc.
1685 // It is also only set when there is not more than one babel
1687 if (!options.empty()) {
1688 string const encoding = options.back();
1689 Encoding const * const enc = encodings.fromLaTeXName(
1690 encoding, Encoding::inputenc, true);
1692 if (!detectEncoding)
1693 cerr << "Unknown encoding " << encoding
1694 << ". Ignoring." << std::endl;
1696 if (!enc->unsafe() && options.size() == 1 && one_language == true)
1697 h_inputencoding = enc->name();
1698 p.setEncoding(enc->iconvName());
1704 else if (name == "srcltx") {
1705 h_output_sync = "1";
1706 if (!opts.empty()) {
1707 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
1710 h_output_sync_macro = "\\usepackage{srcltx}";
1713 else if (is_known(name, known_old_language_packages)) {
1714 // known language packages from the times before babel
1715 // if they are found and not also babel, they will be used as
1716 // custom language package
1717 h_language_package = "\\usepackage{" + name + "}";
1720 else if (name == "lyxskak") {
1721 // ignore this and its options
1722 const char * const o[] = {"ps", "mover", 0};
1723 delete_opt(options, o);
1726 else if (name == "parskip" && options.size() < 2 && (opts.empty() || prefixIs(opts, "skip="))) {
1728 h_paragraph_separation = "halfline";
1730 if (opts == "skip=\\smallskipamount")
1731 h_defskip = "smallskip";
1732 else if (opts == "skip=\\medskipamount")
1733 h_defskip = "medskip";
1734 else if (opts == "skip=\\bigskipamount")
1735 h_defskip = "bigskip";
1736 else if (opts == "skip=\\baselineskip")
1737 h_defskip = "fullline";
1740 h_paragraph_separation = "skip";
1744 else if (is_known(name, known_lyx_packages) && options.empty()) {
1745 if (name == "splitidx")
1746 h_use_indices = "true";
1747 else if (name == "minted")
1748 h_use_minted = true;
1749 else if (name == "refstyle")
1750 h_use_refstyle = true;
1751 else if (name == "prettyref")
1752 h_use_refstyle = false;
1754 if (!in_lyx_preamble) {
1755 h_preamble << package_beg_sep << name
1756 << package_mid_sep << "\\usepackage{"
1758 if (p.next_token().cat() == catNewline ||
1759 (p.next_token().cat() == catSpace &&
1760 p.next_next_token().cat() == catNewline))
1762 h_preamble << package_end_sep;
1766 else if (name == "geometry")
1767 handle_geometry(options);
1769 else if (name == "subfig")
1770 ; // ignore this FIXME: Use the package separator mechanism instead
1772 else if (char const * const * where = is_known(name, known_languages))
1773 h_language = known_coded_languages[where - known_languages];
1775 else if (name == "natbib") {
1776 h_biblio_style = "plainnat";
1777 h_cite_engine = "natbib";
1778 h_cite_engine_type = "authoryear";
1779 vector<string>::iterator it =
1780 find(options.begin(), options.end(), "authoryear");
1781 if (it != options.end())
1784 it = find(options.begin(), options.end(), "numbers");
1785 if (it != options.end()) {
1786 h_cite_engine_type = "numerical";
1790 if (!options.empty())
1791 h_biblio_options = join(options, ",");
1794 else if (name == "biblatex") {
1795 h_biblio_style = "plainnat";
1796 h_cite_engine = "biblatex";
1797 h_cite_engine_type = "authoryear";
1799 vector<string>::iterator it =
1800 find(options.begin(), options.end(), "natbib");
1801 if (it != options.end()) {
1803 h_cite_engine = "biblatex-natbib";
1805 opt = process_keyval_opt(options, "natbib");
1807 h_cite_engine = "biblatex-natbib";
1809 opt = process_keyval_opt(options, "style");
1811 h_biblatex_citestyle = opt;
1812 h_biblatex_bibstyle = opt;
1814 opt = process_keyval_opt(options, "citestyle");
1816 h_biblatex_citestyle = opt;
1817 opt = process_keyval_opt(options, "bibstyle");
1819 h_biblatex_bibstyle = opt;
1821 opt = process_keyval_opt(options, "refsection");
1823 if (opt == "none" || opt == "part"
1824 || opt == "chapter" || opt == "section"
1825 || opt == "subsection")
1828 cerr << "Ignoring unknown refsection value '"
1831 opt = process_keyval_opt(options, "bibencoding");
1834 if (!options.empty()) {
1835 h_biblio_options = join(options, ",");
1840 else if (name == "jurabib") {
1841 h_biblio_style = "jurabib";
1842 h_cite_engine = "jurabib";
1843 h_cite_engine_type = "authoryear";
1844 if (!options.empty())
1845 h_biblio_options = join(options, ",");
1848 else if (name == "bibtopic")
1849 h_use_bibtopic = "true";
1851 else if (name == "chapterbib")
1852 h_multibib = "child";
1854 else if (name == "hyperref")
1855 handle_hyperref(options);
1857 else if (name == "algorithm2e") {
1858 // Load "algorithm2e" module
1859 addModule("algorithm2e");
1860 // Add the package options to the global document options
1861 if (!options.empty()) {
1862 if (h_options.empty())
1863 h_options = join(options, ",");
1865 h_options += ',' + join(options, ",");
1868 else if (name == "microtype") {
1869 //we internally support only microtype without params
1870 if (options.empty())
1871 h_use_microtype = "true";
1873 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1876 else if (name == "lineno") {
1877 h_use_lineno = "true";
1878 if (!options.empty()) {
1879 h_lineno_options = join(options, ",");
1884 else if (name == "changebar")
1885 h_output_changes = "true";
1887 else if (!in_lyx_preamble) {
1888 if (options.empty())
1889 h_preamble << "\\usepackage{" << name << '}';
1891 h_preamble << "\\usepackage[" << opts << "]{"
1895 if (p.next_token().cat() == catNewline ||
1896 (p.next_token().cat() == catSpace &&
1897 p.next_next_token().cat() == catNewline))
1901 // We need to do something with the options...
1902 if (!options.empty() && !detectEncoding)
1903 cerr << "Ignoring options '" << join(options, ",")
1904 << "' of package " << name << '.' << endl;
1906 // remove the whitespace
1911 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1914 Token t = p.get_token();
1915 if (t.cat() == catEscape &&
1916 is_known(t.cs(), known_if_commands))
1917 handle_if(p, in_lyx_preamble);
1919 if (!in_lyx_preamble)
1920 h_preamble << t.asInput();
1921 if (t.cat() == catEscape && t.cs() == "fi")
1928 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1930 if (contains(h_float_placement, "H"))
1931 registerAutomaticallyLoadedPackage("float");
1932 if (h_spacing != "single" && h_spacing != "default")
1933 registerAutomaticallyLoadedPackage("setspace");
1934 if (h_use_packages["amsmath"] == "2") {
1935 // amsbsy and amstext are already provided by amsmath
1936 registerAutomaticallyLoadedPackage("amsbsy");
1937 registerAutomaticallyLoadedPackage("amstext");
1940 // output the LyX file settings
1941 // Important: Keep the version formatting in sync with LyX and
1942 // lyx2lyx (bug 7951)
1943 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1944 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1945 << lyx_version_minor << '\n'
1946 << "\\lyxformat " << LYX_FORMAT << '\n'
1947 << "\\begin_document\n"
1948 << "\\begin_header\n"
1949 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1950 << "\\origin " << origin << "\n"
1951 << "\\textclass " << h_textclass << "\n";
1952 string const raw = subdoc ? empty_string() : h_preamble.str();
1954 os << "\\begin_preamble\n";
1955 for (string::size_type i = 0; i < raw.size(); ++i) {
1956 if (raw[i] == package_beg_sep) {
1957 // Here follows some package loading code that
1958 // must be skipped if the package is loaded
1960 string::size_type j = raw.find(package_mid_sep, i);
1961 if (j == string::npos)
1963 string::size_type k = raw.find(package_end_sep, j);
1964 if (k == string::npos)
1966 string const package = raw.substr(i + 1, j - i - 1);
1967 string const replacement = raw.substr(j + 1, k - j - 1);
1968 if (auto_packages.find(package) == auto_packages.end())
1974 os << "\n\\end_preamble\n";
1976 if (!h_options.empty())
1977 os << "\\options " << h_options << "\n";
1978 os << "\\use_default_options " << h_use_default_options << "\n";
1979 if (!used_modules.empty()) {
1980 os << "\\begin_modules\n";
1981 vector<string>::const_iterator const end = used_modules.end();
1982 vector<string>::const_iterator it = used_modules.begin();
1983 for (; it != end; ++it)
1985 os << "\\end_modules\n";
1987 if (!h_includeonlys.empty()) {
1988 os << "\\begin_includeonly\n";
1989 for (auto const & iofile : h_includeonlys)
1990 os << iofile << '\n';
1991 os << "\\end_includeonly\n";
1993 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1994 << "\\language " << h_language << "\n"
1995 << "\\language_package " << h_language_package << "\n"
1996 << "\\inputencoding " << h_inputencoding << "\n"
1997 << "\\fontencoding " << h_fontencoding << "\n"
1998 << "\\font_roman \"" << h_font_roman[0]
1999 << "\" \"" << h_font_roman[1] << "\"\n"
2000 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
2001 << "\\font_typewriter \"" << h_font_typewriter[0]
2002 << "\" \"" << h_font_typewriter[1] << "\"\n"
2003 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
2004 << "\\font_default_family " << h_font_default_family << "\n"
2005 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
2006 << "\\font_sc " << h_font_sc << "\n"
2007 << "\\font_roman_osf " << h_font_roman_osf << "\n"
2008 << "\\font_sans_osf " << h_font_sans_osf << "\n"
2009 << "\\font_typewriter_osf " << h_font_typewriter_osf << "\n";
2010 if (!h_font_roman_opts.empty())
2011 os << "\\font_roman_opts \"" << h_font_roman_opts << "\"" << '\n';
2012 os << "\\font_sf_scale " << h_font_sf_scale[0]
2013 << ' ' << h_font_sf_scale[1] << '\n';
2014 if (!h_font_sans_opts.empty())
2015 os << "\\font_sans_opts \"" << h_font_sans_opts << "\"" << '\n';
2016 os << "\\font_tt_scale " << h_font_tt_scale[0]
2017 << ' ' << h_font_tt_scale[1] << '\n';
2018 if (!h_font_cjk.empty())
2019 os << "\\font_cjk " << h_font_cjk << '\n';
2020 if (!h_font_typewriter_opts.empty())
2021 os << "\\font_typewriter_opts \"" << h_font_typewriter_opts << "\"" << '\n';
2022 os << "\\use_microtype " << h_use_microtype << '\n'
2023 << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
2024 << "\\graphics " << h_graphics << '\n'
2025 << "\\default_output_format " << h_default_output_format << "\n"
2026 << "\\output_sync " << h_output_sync << "\n";
2027 if (h_output_sync == "1")
2028 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
2029 os << "\\bibtex_command " << h_bibtex_command << "\n"
2030 << "\\index_command " << h_index_command << "\n";
2031 if (!h_float_placement.empty())
2032 os << "\\float_placement " << h_float_placement << "\n";
2033 os << "\\paperfontsize " << h_paperfontsize << "\n"
2034 << "\\spacing " << h_spacing << "\n"
2035 << "\\use_hyperref " << h_use_hyperref << '\n';
2036 if (h_use_hyperref == "true") {
2037 if (!h_pdf_title.empty())
2038 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
2039 if (!h_pdf_author.empty())
2040 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
2041 if (!h_pdf_subject.empty())
2042 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
2043 if (!h_pdf_keywords.empty())
2044 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
2045 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
2046 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
2047 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
2048 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
2049 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
2050 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
2051 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
2052 "\\pdf_backref " << h_pdf_backref << "\n"
2053 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
2054 if (!h_pdf_pagemode.empty())
2055 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
2056 if (!h_pdf_quoted_options.empty())
2057 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
2059 os << "\\papersize " << h_papersize << "\n"
2060 << "\\use_geometry " << h_use_geometry << '\n';
2061 for (map<string, string>::const_iterator it = h_use_packages.begin();
2062 it != h_use_packages.end(); ++it)
2063 os << "\\use_package " << it->first << ' ' << it->second << '\n';
2064 os << "\\cite_engine " << h_cite_engine << '\n'
2065 << "\\cite_engine_type " << h_cite_engine_type << '\n'
2066 << "\\biblio_style " << h_biblio_style << "\n"
2067 << "\\use_bibtopic " << h_use_bibtopic << "\n";
2068 if (!h_biblio_options.empty())
2069 os << "\\biblio_options " << h_biblio_options << "\n";
2070 if (!h_biblatex_bibstyle.empty())
2071 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
2072 if (!h_biblatex_citestyle.empty())
2073 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
2074 if (!h_multibib.empty())
2075 os << "\\multibib " << h_multibib << "\n";
2076 os << "\\use_indices " << h_use_indices << "\n"
2077 << "\\paperorientation " << h_paperorientation << '\n'
2078 << "\\suppress_date " << h_suppress_date << '\n'
2079 << "\\justification " << h_justification << '\n'
2080 << "\\use_refstyle " << h_use_refstyle << '\n'
2081 << "\\use_minted " << h_use_minted << '\n'
2082 << "\\use_lineno " << h_use_lineno << '\n';
2083 if (!h_lineno_options.empty())
2084 os << "\\lineno_options " << h_lineno_options << '\n';
2085 if (!h_fontcolor.empty())
2086 os << "\\fontcolor " << h_fontcolor << '\n';
2087 if (!h_notefontcolor.empty())
2088 os << "\\notefontcolor " << h_notefontcolor << '\n';
2089 if (!h_backgroundcolor.empty())
2090 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
2091 if (!h_boxbgcolor.empty())
2092 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
2093 if (index_number != 0)
2094 for (int i = 0; i < index_number; i++) {
2095 os << "\\index " << h_index[i] << '\n'
2096 << "\\shortcut " << h_shortcut[i] << '\n'
2097 << "\\color " << h_color << '\n'
2101 os << "\\index " << h_index[0] << '\n'
2102 << "\\shortcut " << h_shortcut[0] << '\n'
2103 << "\\color " << h_color << '\n'
2107 << "\\secnumdepth " << h_secnumdepth << "\n"
2108 << "\\tocdepth " << h_tocdepth << "\n"
2109 << "\\paragraph_separation " << h_paragraph_separation << "\n";
2110 if (h_paragraph_separation == "skip")
2111 os << "\\defskip " << h_defskip << "\n";
2113 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
2114 os << "\\is_math_indent " << h_is_mathindent << "\n";
2115 if (!h_mathindentation.empty())
2116 os << "\\math_indentation " << h_mathindentation << "\n";
2117 os << "\\math_numbering_side " << h_math_numbering_side << "\n";
2118 os << "\\quotes_style " << h_quotes_style << "\n"
2119 << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
2120 << "\\papercolumns " << h_papercolumns << "\n"
2121 << "\\papersides " << h_papersides << "\n"
2122 << "\\paperpagestyle " << h_paperpagestyle << "\n";
2123 if (!h_listings_params.empty())
2124 os << "\\listings_params " << h_listings_params << "\n";
2125 os << "\\tracking_changes " << h_tracking_changes << "\n"
2126 << "\\output_changes " << h_output_changes << "\n"
2127 << "\\change_bars " << h_change_bars << "\n"
2128 << "\\html_math_output " << h_html_math_output << "\n"
2129 << "\\html_css_as_file " << h_html_css_as_file << "\n"
2130 << "\\html_be_strict " << h_html_be_strict << "\n"
2131 << "\\docbook_table_output " << h_docbook_table_output << "\n"
2133 << "\\end_header\n\n"
2134 << "\\begin_body\n";
2139 void Preamble::parse(Parser & p, string const & forceclass,
2140 TeX2LyXDocClass & tc)
2142 // initialize fixed types
2143 special_columns_['D'] = 3;
2144 parse(p, forceclass, false, tc);
2148 void Preamble::parse(Parser & p, string const & forceclass,
2149 bool detectEncoding, TeX2LyXDocClass & tc)
2151 bool is_full_document = false;
2152 bool is_lyx_file = false;
2153 bool in_lyx_preamble = false;
2154 bool class_set = false;
2156 // determine whether this is a full document or a fragment for inclusion
2158 Token const & t = p.get_token();
2160 if (t.cat() == catEscape && t.cs() == "documentclass") {
2161 is_full_document = true;
2167 if (detectEncoding && !is_full_document)
2170 while (is_full_document && p.good()) {
2171 if (detectEncoding && h_inputencoding != "auto-legacy" &&
2172 h_inputencoding != "auto-legacy-plain")
2175 // Force textclass if the user wanted it
2176 if (!forceclass.empty()) {
2177 setTextClass(forceclass, tc);
2181 Token const & t = p.get_token();
2184 if (!detectEncoding)
2185 cerr << "t: " << t << '\n';
2191 if (!in_lyx_preamble &&
2192 (t.cat() == catLetter ||
2193 t.cat() == catSuper ||
2194 t.cat() == catSub ||
2195 t.cat() == catOther ||
2196 t.cat() == catMath ||
2197 t.cat() == catActive ||
2198 t.cat() == catBegin ||
2199 t.cat() == catEnd ||
2200 t.cat() == catAlign ||
2201 t.cat() == catParameter)) {
2202 h_preamble << t.cs();
2206 if (!in_lyx_preamble &&
2207 (t.cat() == catSpace || t.cat() == catNewline)) {
2208 h_preamble << t.asInput();
2212 if (t.cat() == catComment) {
2213 static regex const islyxfile("%% LyX .* created this file");
2214 static regex const usercommands("User specified LaTeX commands");
2216 string const comment = t.asInput();
2218 // magically switch encoding default if it looks like XeLaTeX
2219 static string const magicXeLaTeX =
2220 "% This document must be compiled with XeLaTeX ";
2221 if (comment.size() > magicXeLaTeX.size()
2222 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
2223 && h_inputencoding == "auto-legacy") {
2224 if (!detectEncoding)
2225 cerr << "XeLaTeX comment found, switching to UTF8\n";
2226 h_inputencoding = "utf8";
2229 if (regex_search(comment, sub, islyxfile)) {
2231 in_lyx_preamble = true;
2232 } else if (is_lyx_file
2233 && regex_search(comment, sub, usercommands))
2234 in_lyx_preamble = false;
2235 else if (!in_lyx_preamble)
2236 h_preamble << t.asInput();
2240 if (t.cs() == "PassOptionsToPackage") {
2241 string const poptions = p.getArg('{', '}');
2242 string const package = p.verbatim_item();
2243 extra_package_options_.insert(make_pair(package, poptions));
2247 if (t.cs() == "pagestyle") {
2248 h_paperpagestyle = p.verbatim_item();
2252 if (t.cs() == "setdefaultlanguage") {
2254 // We don't yet care about non-language variant options
2255 // because LyX doesn't support this yet, see bug #8214
2257 string langopts = p.getOpt();
2258 // check if the option contains a variant, if yes, extract it
2259 string::size_type pos_var = langopts.find("variant");
2260 string::size_type i = langopts.find(',', pos_var);
2261 string::size_type k = langopts.find('=', pos_var);
2262 if (pos_var != string::npos){
2264 if (i == string::npos)
2265 variant = langopts.substr(k + 1, langopts.length() - k - 2);
2267 variant = langopts.substr(k + 1, i - k - 1);
2268 h_language = variant;
2272 h_language = p.verbatim_item();
2273 //finally translate the poyglossia name to a LyX name
2274 h_language = polyglossia2lyx(h_language);
2278 if (t.cs() == "setotherlanguage") {
2279 // We don't yet care about the option because LyX doesn't
2280 // support this yet, see bug #8214
2281 p.hasOpt() ? p.getOpt() : string();
2286 if (t.cs() == "setmainfont") {
2287 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2288 h_font_roman[1] = p.getArg('{', '}');
2289 if (!fontopts.empty()) {
2290 vector<string> opts = getVectorFromString(fontopts);
2292 for (auto const & opt : opts) {
2293 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2296 if (!fontopts.empty())
2300 h_font_roman_opts = fontopts;
2305 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
2306 // LyX currently only supports the scale option
2307 string scale, fontopts;
2309 fontopts = p.getArg('[', ']');
2310 if (!fontopts.empty()) {
2311 vector<string> opts = getVectorFromString(fontopts);
2313 for (auto const & opt : opts) {
2314 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2317 if (prefixIs(opt, "Scale=")) {
2318 scale_as_percentage(opt, scale);
2321 if (!fontopts.empty())
2327 if (t.cs() == "setsansfont") {
2329 h_font_sf_scale[1] = scale;
2330 h_font_sans[1] = p.getArg('{', '}');
2331 if (!fontopts.empty())
2332 h_font_sans_opts = fontopts;
2335 h_font_tt_scale[1] = scale;
2336 h_font_typewriter[1] = p.getArg('{', '}');
2337 if (!fontopts.empty())
2338 h_font_typewriter_opts = fontopts;
2343 if (t.cs() == "babelfont") {
2345 h_use_non_tex_fonts = true;
2346 h_language_package = "babel";
2347 if (h_inputencoding == "auto-legacy")
2348 p.setEncoding("UTF-8");
2349 // we don't care about the lang option
2350 string const lang = p.hasOpt() ? p.getArg('[', ']') : string();
2351 string const family = p.getArg('{', '}');
2352 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2353 string const fontname = p.getArg('{', '}');
2354 if (lang.empty() && family == "rm") {
2355 h_font_roman[1] = fontname;
2356 if (!fontopts.empty()) {
2357 vector<string> opts = getVectorFromString(fontopts);
2359 for (auto const & opt : opts) {
2360 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2363 if (!fontopts.empty())
2367 h_font_roman_opts = fontopts;
2370 } else if (lang.empty() && (family == "sf" || family == "tt")) {
2372 if (!fontopts.empty()) {
2373 vector<string> opts = getVectorFromString(fontopts);
2375 for (auto const & opt : opts) {
2376 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2379 if (prefixIs(opt, "Scale=")) {
2380 scale_as_percentage(opt, scale);
2383 if (!fontopts.empty())
2388 if (family == "sf") {
2390 h_font_sf_scale[1] = scale;
2391 h_font_sans[1] = fontname;
2392 if (!fontopts.empty())
2393 h_font_sans_opts = fontopts;
2396 h_font_tt_scale[1] = scale;
2397 h_font_typewriter[1] = fontname;
2398 if (!fontopts.empty())
2399 h_font_typewriter_opts = fontopts;
2403 // not rm, sf or tt or lang specific
2404 h_preamble << '\\' << t.cs();
2406 h_preamble << '[' << lang << ']';
2407 h_preamble << '{' << family << '}';
2408 if (!fontopts.empty())
2409 h_preamble << '[' << fontopts << ']';
2410 h_preamble << '{' << fontname << '}' << '\n';
2415 if (t.cs() == "date") {
2416 string argument = p.getArg('{', '}');
2417 if (argument.empty())
2418 h_suppress_date = "true";
2420 h_preamble << t.asInput() << '{' << argument << '}';
2424 if (t.cs() == "color") {
2425 string const space =
2426 (p.hasOpt() ? p.getOpt() : string());
2427 string argument = p.getArg('{', '}');
2428 // check the case that a standard color is used
2429 if (space.empty() && is_known(argument, known_basic_colors)) {
2430 h_fontcolor = rgbcolor2code(argument);
2431 registerAutomaticallyLoadedPackage("color");
2432 } else if (space.empty() && argument == "document_fontcolor")
2433 registerAutomaticallyLoadedPackage("color");
2434 // check the case that LyX's document_fontcolor is defined
2435 // but not used for \color
2437 h_preamble << t.asInput();
2439 h_preamble << space;
2440 h_preamble << '{' << argument << '}';
2441 // the color might already be set because \definecolor
2442 // is parsed before this
2448 if (t.cs() == "pagecolor") {
2449 string argument = p.getArg('{', '}');
2450 // check the case that a standard color is used
2451 if (is_known(argument, known_basic_colors)) {
2452 h_backgroundcolor = rgbcolor2code(argument);
2453 } else if (argument == "page_backgroundcolor")
2454 registerAutomaticallyLoadedPackage("color");
2455 // check the case that LyX's page_backgroundcolor is defined
2456 // but not used for \pagecolor
2458 h_preamble << t.asInput() << '{' << argument << '}';
2459 // the color might already be set because \definecolor
2460 // is parsed before this
2461 h_backgroundcolor = "";
2466 if (t.cs() == "makeatletter") {
2467 // LyX takes care of this
2468 p.setCatcode('@', catLetter);
2472 if (t.cs() == "makeatother") {
2473 // LyX takes care of this
2474 p.setCatcode('@', catOther);
2478 if (t.cs() == "makeindex") {
2479 // LyX will re-add this if a print index command is found
2484 if (t.cs() == "newindex") {
2485 string const indexname = p.getArg('[', ']');
2486 string const shortcut = p.verbatim_item();
2487 if (!indexname.empty())
2488 h_index[index_number] = indexname;
2490 h_index[index_number] = shortcut;
2491 h_shortcut[index_number] = shortcut;
2497 if (t.cs() == "addbibresource") {
2498 string const options = p.getArg('[', ']');
2499 string const arg = removeExtension(p.getArg('{', '}'));
2500 if (!options.empty()) {
2501 // check if the option contains a bibencoding, if yes, extract it
2502 string::size_type pos = options.find("bibencoding=");
2504 if (pos != string::npos) {
2505 string::size_type i = options.find(',', pos);
2506 if (i == string::npos)
2507 encoding = options.substr(pos + 1);
2509 encoding = options.substr(pos, i - pos);
2510 pos = encoding.find('=');
2511 if (pos == string::npos)
2514 encoding = encoding.substr(pos + 1);
2516 if (!encoding.empty())
2517 biblatex_encodings.push_back(normalize_filename(arg) + ' ' + encoding);
2519 biblatex_bibliographies.push_back(arg);
2523 if (t.cs() == "bibliography") {
2524 vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
2525 biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
2529 if (t.cs() == "RS@ifundefined") {
2530 string const name = p.verbatim_item();
2531 string const body1 = p.verbatim_item();
2532 string const body2 = p.verbatim_item();
2533 // only non-lyxspecific stuff
2534 if (in_lyx_preamble &&
2535 (name == "subsecref" || name == "thmref" || name == "lemref"))
2539 ss << '\\' << t.cs();
2540 ss << '{' << name << '}'
2541 << '{' << body1 << '}'
2542 << '{' << body2 << '}';
2543 h_preamble << ss.str();
2548 if (t.cs() == "AtBeginDocument") {
2549 string const name = p.verbatim_item();
2550 // only non-lyxspecific stuff
2551 if (in_lyx_preamble &&
2552 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
2553 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
2554 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
2555 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
2556 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
2557 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
2558 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
2559 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
2560 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
2561 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
2562 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
2563 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
2564 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
2565 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
2566 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
2570 ss << '\\' << t.cs();
2571 ss << '{' << name << '}';
2572 h_preamble << ss.str();
2577 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
2578 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
2579 || t.cs() == "providecommand" || t.cs() == "providecommandx"
2580 || t.cs() == "DeclareRobustCommand"
2581 || t.cs() == "DeclareRobustCommandx"
2582 || t.cs() == "ProvideTextCommandDefault"
2583 || t.cs() == "DeclareMathAccent") {
2585 if (p.next_token().character() == '*') {
2589 string const name = p.verbatim_item();
2590 string const opt1 = p.getFullOpt();
2591 string const opt2 = p.getFullOpt();
2592 string const body = p.verbatim_item();
2593 // store the in_lyx_preamble setting
2594 bool const was_in_lyx_preamble = in_lyx_preamble;
2596 if (name == "\\rmdefault")
2597 if (is_known(body, known_roman_font_packages)) {
2598 h_font_roman[0] = body;
2600 in_lyx_preamble = true;
2602 if (name == "\\sfdefault") {
2603 if (is_known(body, known_sans_font_packages)) {
2604 h_font_sans[0] = body;
2606 in_lyx_preamble = true;
2608 if (body == "LibertinusSans-OsF") {
2609 h_font_sans[0] = "LibertinusSans-LF";
2610 h_font_sans_osf = "true";
2612 in_lyx_preamble = true;
2615 if (name == "\\ttdefault")
2616 if (is_known(body, known_typewriter_font_packages)) {
2617 h_font_typewriter[0] = body;
2619 in_lyx_preamble = true;
2621 if (name == "\\familydefault") {
2622 string family = body;
2623 // remove leading "\"
2624 h_font_default_family = family.erase(0,1);
2626 in_lyx_preamble = true;
2628 if (name == "\\LibertinusSans@scale") {
2629 if (isStrDbl(body)) {
2630 h_font_sf_scale[0] = convert<string>(
2631 static_cast<int>(100 * convert<double>(body)));
2634 if (name == "\\LibertinusMono@scale") {
2635 if (isStrDbl(body)) {
2636 h_font_tt_scale[0] = convert<string>(
2637 static_cast<int>(100 * convert<double>(body)));
2641 // remove LyX-specific definitions that are re-added by LyX
2643 // \lyxline is an ancient command that is converted by tex2lyx into
2644 // a \rule therefore remove its preamble code
2645 if (name == "\\lyxdot" || name == "\\lyxarrow"
2646 || name == "\\lyxline" || name == "\\LyX") {
2648 in_lyx_preamble = true;
2651 // Add the command to the known commands
2652 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
2654 // only non-lyxspecific stuff
2655 if (!in_lyx_preamble) {
2657 ss << '\\' << t.cs();
2660 ss << '{' << name << '}' << opt1 << opt2
2661 << '{' << body << "}";
2662 if (prefixIs(t.cs(), "renew") || !contains(h_preamble.str(), ss.str()))
2663 h_preamble << ss.str();
2665 ostream & out = in_preamble ? h_preamble : os;
2666 out << "\\" << t.cs() << "{" << name << "}"
2667 << opts << "{" << body << "}";
2670 // restore the in_lyx_preamble setting
2671 in_lyx_preamble = was_in_lyx_preamble;
2675 if (t.cs() == "documentclass") {
2676 vector<string>::iterator it;
2677 vector<string> opts = split_options(p.getArg('[', ']'));
2678 // FIXME This does not work for classes that have a
2679 // different name in LyX than in LaTeX
2680 string const tclass = p.getArg('{', '}');
2682 // Only set text class if a class hasn't been forced
2683 // (this was set above)
2685 // textclass needs to be set at this place (if not already done)
2686 // as we need to know it for other parameters
2687 // (such as class-dependent paper size)
2688 setTextClass(tclass, tc);
2693 // Try those who are (most likely) known to all packages first
2694 handle_opt(opts, known_fontsizes, h_paperfontsize);
2695 delete_opt(opts, known_fontsizes);
2696 // delete "pt" at the end
2697 string::size_type i = h_paperfontsize.find("pt");
2698 if (i != string::npos)
2699 h_paperfontsize.erase(i);
2700 // Now those known specifically to the class
2701 vector<string> class_fsizes = getVectorFromString(tc.opt_fontsize(), "|");
2702 string const fsize_format = tc.fontsizeformat();
2703 for (auto const & fsize : class_fsizes) {
2704 string latexsize = subst(fsize_format, "$$s", fsize);
2705 vector<string>::iterator it = find(opts.begin(), opts.end(), latexsize);
2706 if (it != opts.end()) {
2707 h_paperfontsize = fsize;
2713 // The documentclass options are always parsed before the options
2714 // of the babel call so that a language cannot overwrite the babel
2716 handle_opt(opts, known_languages, h_language);
2717 delete_opt(opts, known_languages);
2720 if ((it = find(opts.begin(), opts.end(), "fleqn"))
2722 h_is_mathindent = "1";
2725 // formula numbering side
2726 if ((it = find(opts.begin(), opts.end(), "leqno"))
2728 h_math_numbering_side = "left";
2731 else if ((it = find(opts.begin(), opts.end(), "reqno"))
2733 h_math_numbering_side = "right";
2737 // paper orientation
2738 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
2739 h_paperorientation = "landscape";
2743 if ((it = find(opts.begin(), opts.end(), "oneside"))
2748 if ((it = find(opts.begin(), opts.end(), "twoside"))
2754 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
2756 h_papercolumns = "1";
2759 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
2761 h_papercolumns = "2";
2765 // some size options are known by the document class, other sizes
2766 // are handled by the \geometry command of the geometry package
2768 vector<string> class_psizes = getVectorFromString(tc.opt_pagesize(), "|");
2769 string const psize_format = tc.pagesizeformat();
2770 for (auto const & psize : class_psizes) {
2771 string latexsize = subst(psize_format, "$$s", psize);
2772 vector<string>::iterator it = find(opts.begin(), opts.end(), latexsize);
2773 if (it != opts.end()) {
2774 h_papersize = psize;
2778 if (psize_format == "$$spaper")
2780 // Also try with the default format since this is understood by
2782 latexsize = psize + "paper";
2783 it = find(opts.begin(), opts.end(), latexsize);
2784 if (it != opts.end()) {
2785 h_papersize = psize;
2790 // the remaining options
2791 h_options = join(opts, ",");
2795 if (t.cs() == "usepackage") {
2796 string const options = p.getArg('[', ']');
2797 string const name = p.getArg('{', '}');
2798 vector<string> vecnames;
2799 split(name, vecnames, ',');
2800 vector<string>::const_iterator it = vecnames.begin();
2801 vector<string>::const_iterator end = vecnames.end();
2802 for (; it != end; ++it)
2803 handle_package(p, trimSpaceAndEol(*it), options,
2804 in_lyx_preamble, detectEncoding);
2808 if (t.cs() == "inputencoding") {
2809 string const encoding = p.getArg('{','}');
2810 Encoding const * const enc = encodings.fromLaTeXName(
2811 encoding, Encoding::inputenc, true);
2813 if (!detectEncoding)
2814 cerr << "Unknown encoding " << encoding
2815 << ". Ignoring." << std::endl;
2818 h_inputencoding = enc->name();
2819 p.setEncoding(enc->iconvName());
2824 if (t.cs() == "newenvironment") {
2825 string const name = p.getArg('{', '}');
2826 string const opt1 = p.getFullOpt();
2827 string const opt2 = p.getFullOpt();
2828 string const beg = p.verbatim_item();
2829 string const end = p.verbatim_item();
2830 if (!in_lyx_preamble) {
2831 h_preamble << "\\newenvironment{" << name
2832 << '}' << opt1 << opt2 << '{'
2833 << beg << "}{" << end << '}';
2835 add_known_environment(name, opt1, !opt2.empty(),
2836 from_utf8(beg), from_utf8(end));
2840 if (t.cs() == "newtheorem") {
2842 if (p.next_token().character() == '*') {
2846 string const name = p.getArg('{', '}');
2847 string const opt1 = p.getFullOpt();
2848 string const opt2 = p.getFullOpt();
2849 string const body = p.verbatim_item();
2850 string const opt3 = p.getFullOpt();
2851 string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
2853 string const complete = cmd + "{" + name + '}' +
2854 opt1 + opt2 + '{' + body + '}' + opt3;
2856 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
2858 if (!in_lyx_preamble)
2859 h_preamble << complete;
2863 if (t.cs() == "def") {
2864 string name = p.get_token().cs();
2865 // In fact, name may be more than the name:
2866 // In the test case of bug 8116
2867 // name == "csname SF@gobble@opt \endcsname".
2868 // Therefore, we need to use asInput() instead of cs().
2869 while (p.next_token().cat() != catBegin)
2870 name += p.get_token().asInput();
2871 if (!in_lyx_preamble)
2872 h_preamble << "\\def\\" << name << '{'
2873 << p.verbatim_item() << "}";
2877 if (t.cs() == "newcolumntype") {
2878 string const name = p.getArg('{', '}');
2879 trimSpaceAndEol(name);
2881 string opts = p.getOpt();
2882 if (!opts.empty()) {
2883 istringstream is(string(opts, 1));
2886 special_columns_[name[0]] = nargs;
2887 h_preamble << "\\newcolumntype{" << name << "}";
2889 h_preamble << "[" << nargs << "]";
2890 h_preamble << "{" << p.verbatim_item() << "}";
2894 if (t.cs() == "setcounter") {
2895 string const name = p.getArg('{', '}');
2896 string const content = p.getArg('{', '}');
2897 if (name == "secnumdepth")
2898 h_secnumdepth = content;
2899 else if (name == "tocdepth")
2900 h_tocdepth = content;
2902 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
2906 if (t.cs() == "setlength") {
2907 string const name = p.verbatim_item();
2908 string const content = p.verbatim_item();
2909 // the paragraphs are only not indented when \parindent is set to zero
2910 if (name == "\\parindent" && content != "")
2911 h_paragraph_indentation = translate_len(content);
2912 else if (name == "\\parskip" && isPackageUsed("parskip")) {
2913 if (content == "\\smallskipamount")
2914 h_defskip = "smallskip";
2915 else if (content == "\\medskipamount")
2916 h_defskip = "medskip";
2917 else if (content == "\\bigskipamount")
2918 h_defskip = "bigskip";
2919 else if (content == "\\baselineskip")
2920 h_defskip = "fullline";
2922 h_defskip = translate_len(content);
2923 } else if (name == "\\mathindent") {
2924 h_mathindentation = translate_len(content);
2926 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2930 if (t.cs() == "onehalfspacing") {
2931 h_spacing = "onehalf";
2935 if (t.cs() == "doublespacing") {
2936 h_spacing = "double";
2940 if (t.cs() == "setstretch") {
2941 h_spacing = "other " + p.verbatim_item();
2945 if (t.cs() == "synctex") {
2946 // the scheme is \synctex=value
2947 // where value can only be "1" or "-1"
2948 h_output_sync = "1";
2949 // there can be any character behind the value (e.g. a linebreak or a '\'
2950 // therefore we extract it char by char
2952 string value = p.get_token().asInput();
2954 value += p.get_token().asInput();
2955 h_output_sync_macro = "\\synctex=" + value;
2959 if (t.cs() == "begin") {
2960 string const name = p.getArg('{', '}');
2961 if (name == "document")
2963 h_preamble << "\\begin{" << name << "}";
2967 if (t.cs() == "geometry") {
2968 vector<string> opts = split_options(p.getArg('{', '}'));
2969 handle_geometry(opts);
2973 if (t.cs() == "definecolor") {
2974 string const color = p.getArg('{', '}');
2975 string const space = p.getArg('{', '}');
2976 string const value = p.getArg('{', '}');
2977 if (color == "document_fontcolor" && space == "rgb") {
2978 RGBColor c(RGBColorFromLaTeX(value));
2979 h_fontcolor = X11hexname(c);
2980 } else if (color == "note_fontcolor" && space == "rgb") {
2981 RGBColor c(RGBColorFromLaTeX(value));
2982 h_notefontcolor = X11hexname(c);
2983 } else if (color == "page_backgroundcolor" && space == "rgb") {
2984 RGBColor c(RGBColorFromLaTeX(value));
2985 h_backgroundcolor = X11hexname(c);
2986 } else if (color == "shadecolor" && space == "rgb") {
2987 RGBColor c(RGBColorFromLaTeX(value));
2988 h_boxbgcolor = X11hexname(c);
2990 h_preamble << "\\definecolor{" << color
2991 << "}{" << space << "}{" << value
2997 if (t.cs() == "bibliographystyle") {
2998 h_biblio_style = p.verbatim_item();
3002 if (t.cs() == "jurabibsetup") {
3003 // FIXME p.getArg('{', '}') is most probably wrong (it
3004 // does not handle nested braces).
3005 // Use p.verbatim_item() instead.
3006 vector<string> jurabibsetup =
3007 split_options(p.getArg('{', '}'));
3008 // add jurabibsetup to the jurabib package options
3009 add_package("jurabib", jurabibsetup);
3010 if (!jurabibsetup.empty()) {
3011 h_preamble << "\\jurabibsetup{"
3012 << join(jurabibsetup, ",") << '}';
3017 if (t.cs() == "hypersetup") {
3018 vector<string> hypersetup =
3019 split_options(p.verbatim_item());
3020 // add hypersetup to the hyperref package options
3021 handle_hyperref(hypersetup);
3022 if (!hypersetup.empty()) {
3023 h_preamble << "\\hypersetup{"
3024 << join(hypersetup, ",") << '}';
3029 if (t.cs() == "includeonly") {
3030 vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
3031 for (auto & iofile : includeonlys) {
3032 string filename(normalize_filename(iofile));
3033 string const path = getMasterFilePath(true);
3034 // We want to preserve relative/absolute filenames,
3035 // therefore path is only used for testing
3036 if (!makeAbsPath(filename, path).exists()) {
3037 // The file extension is probably missing.
3038 // Now try to find it out.
3039 string const tex_name =
3040 find_file(filename, path,
3041 known_tex_extensions);
3042 if (!tex_name.empty())
3043 filename = tex_name;
3046 if (makeAbsPath(filename, path).exists())
3047 fix_child_filename(filename);
3049 cerr << "Warning: Could not find included file '"
3050 << filename << "'." << endl;
3051 outname = changeExtension(filename, "lyx");
3052 h_includeonlys.push_back(outname);
3057 if (is_known(t.cs(), known_if_3arg_commands)) {
3058 // prevent misparsing of \usepackage if it is used
3059 // as an argument (see e.g. our own output of
3060 // \@ifundefined above)
3061 string const arg1 = p.verbatim_item();
3062 string const arg2 = p.verbatim_item();
3063 string const arg3 = p.verbatim_item();
3064 // test case \@ifundefined{date}{}{\date{}}
3065 if (t.cs() == "@ifundefined" && arg1 == "date" &&
3066 arg2.empty() && arg3 == "\\date{}") {
3067 h_suppress_date = "true";
3068 // older tex2lyx versions did output
3069 // \@ifundefined{definecolor}{\usepackage{color}}{}
3070 } else if (t.cs() == "@ifundefined" &&
3071 arg1 == "definecolor" &&
3072 arg2 == "\\usepackage{color}" &&
3074 if (!in_lyx_preamble)
3075 h_preamble << package_beg_sep
3078 << "\\@ifundefined{definecolor}{color}{}"
3081 //\@ifundefined{showcaptionsetup}{}{%
3082 // \PassOptionsToPackage{caption=false}{subfig}}
3083 // that LyX uses for subfloats
3084 } else if (t.cs() == "@ifundefined" &&
3085 arg1 == "showcaptionsetup" && arg2.empty()
3086 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
3088 } else if (!in_lyx_preamble) {
3089 h_preamble << t.asInput()
3090 << '{' << arg1 << '}'
3091 << '{' << arg2 << '}'
3092 << '{' << arg3 << '}';
3097 if (is_known(t.cs(), known_if_commands)) {
3098 // must not parse anything in conditional code, since
3099 // LyX would output the parsed contents unconditionally
3100 if (!in_lyx_preamble)
3101 h_preamble << t.asInput();
3102 handle_if(p, in_lyx_preamble);
3106 if (!t.cs().empty() && !in_lyx_preamble) {
3107 h_preamble << '\\' << t.cs();
3112 // set textclass if not yet done (snippets without \documentclass and forced class)
3114 setTextClass(h_textclass, tc);
3116 // remove the whitespace
3119 if (h_papersides.empty()) {
3122 h_papersides = ss.str();
3125 // If the CJK package is used we cannot set the document language from
3126 // the babel options. Instead, we guess which language is used most
3127 // and set this one.
3128 default_language = h_language;
3129 if (is_full_document &&
3130 (auto_packages.find("CJK") != auto_packages.end() ||
3131 auto_packages.find("CJKutf8") != auto_packages.end())) {
3133 h_language = guessLanguage(p, default_language);
3135 if (explicit_babel && h_language != default_language) {
3136 // We set the document language to a CJK language,
3137 // but babel is explicitly called in the user preamble
3138 // without options. LyX will not add the default
3139 // language to the document options if it is either
3140 // english, or no text is set as default language.
3141 // Therefore we need to add a language option explicitly.
3142 // FIXME: It would be better to remove all babel calls
3143 // from the user preamble, but this is difficult
3144 // without re-introducing bug 7861.
3145 if (h_options.empty())
3146 h_options = lyx2babel(default_language);
3148 h_options += ',' + lyx2babel(default_language);
3152 // Finally, set the quote style.
3153 // LyX knows the following quotes styles:
3154 // british, cjk, cjkangle, danish, english, french, german,
3155 // polish, russian, swedish and swiss
3156 // conversion list taken from
3157 // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
3158 // (quotes for kazakh are unknown)
3160 if (is_known(h_language, known_british_quotes_languages))
3161 h_quotes_style = "british";
3163 else if (is_known(h_language, known_cjk_quotes_languages))
3164 h_quotes_style = "cjk";
3166 else if (is_known(h_language, known_cjkangle_quotes_languages))
3167 h_quotes_style = "cjkangle";
3169 else if (is_known(h_language, known_danish_quotes_languages))
3170 h_quotes_style = "danish";
3172 else if (is_known(h_language, known_french_quotes_languages))
3173 h_quotes_style = "french";
3175 else if (is_known(h_language, known_german_quotes_languages))
3176 h_quotes_style = "german";
3178 else if (is_known(h_language, known_polish_quotes_languages))
3179 h_quotes_style = "polish";
3181 else if (is_known(h_language, known_russian_quotes_languages))
3182 h_quotes_style = "russian";
3184 else if (is_known(h_language, known_swedish_quotes_languages))
3185 h_quotes_style = "swedish";
3187 else if (is_known(h_language, known_swiss_quotes_languages))
3188 h_quotes_style = "swiss";
3190 else if (is_known(h_language, known_english_quotes_languages))
3191 h_quotes_style = "english";
3195 string Preamble::parseEncoding(Parser & p, string const & forceclass)
3197 TeX2LyXDocClass dummy;
3198 parse(p, forceclass, true, dummy);
3199 if (h_inputencoding != "auto-legacy" && h_inputencoding != "auto-legacy-plain")
3200 return h_inputencoding;
3205 string babel2lyx(string const & language)
3207 char const * const * where = is_known(language, known_languages);
3209 return known_coded_languages[where - known_languages];
3214 string lyx2babel(string const & language)
3216 char const * const * where = is_known(language, known_coded_languages);
3218 return known_languages[where - known_coded_languages];
3223 string Preamble::polyglossia2lyx(string const & language)
3225 char const * const * where = is_known(language, polyglossia_languages);
3227 return coded_polyglossia_languages[where - polyglossia_languages];
3232 string rgbcolor2code(string const & name)
3234 char const * const * where = is_known(name, known_basic_colors);
3236 // "red", "green" etc
3237 return known_basic_color_codes[where - known_basic_colors];
3239 // "255,0,0", "0,255,0" etc
3240 RGBColor c(RGBColorFromLaTeX(name));
3241 return X11hexname(c);