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 const & 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 used_packages.insert({name, split_options(h_options)});
450 // Insert options passed via PassOptionsToPackage
451 for (auto const & p : extra_package_options_) {
452 if (p.first == name) {
453 vector<string> eo = getVectorFromString(p.second);
454 for (auto const & eoi : eo)
455 options.push_back(eoi);
459 vector<string> & v = used_packages[name];
460 v.insert(v.end(), options.begin(), options.end());
461 if (name == "jurabib") {
462 // Don't output the order argument (see the cite command
463 // handling code in text.cpp).
464 vector<string>::iterator end =
465 remove(options.begin(), options.end(), "natbiborder");
466 end = remove(options.begin(), end, "jurabiborder");
467 options.erase(end, options.end());
471 void Preamble::setTextClass(string const & tclass, TeX2LyXDocClass & tc)
473 h_textclass = tclass;
474 tc.setName(h_textclass);
475 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
476 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
484 // Given is a string like "scaled=0.9" or "scale=0.9", return 0.9 * 100
485 bool scale_as_percentage(string const & scale, string & percentage)
487 if (contains(scale, '=')) {
488 string const value = support::split(scale, '=');
489 if (isStrDbl(value)) {
490 percentage = convert<string>(
491 static_cast<int>(100 * convert<double>(value)));
499 string remove_braces(string const & value)
503 if (value[0] == '{' && value[value.length()-1] == '}')
504 return value.substr(1, value.length()-2);
508 } // anonymous namespace
511 Preamble::Preamble() : one_language(true), explicit_babel(false),
512 title_layout_found(false), index_number(0), h_font_cjk_set(false)
516 h_biblio_style = "plain";
517 h_bibtex_command = "default";
518 h_cite_engine = "basic";
519 h_cite_engine_type = "default";
521 h_defskip = "medskip";
522 h_dynamic_quotes = false;
525 h_fontencoding = "default";
526 h_font_roman[0] = "default";
527 h_font_roman[1] = "default";
528 h_font_sans[0] = "default";
529 h_font_sans[1] = "default";
530 h_font_typewriter[0] = "default";
531 h_font_typewriter[1] = "default";
532 h_font_math[0] = "auto";
533 h_font_math[1] = "auto";
534 h_font_default_family = "default";
535 h_use_non_tex_fonts = false;
537 h_font_roman_osf = "false";
538 h_font_sans_osf = "false";
539 h_font_typewriter_osf = "false";
540 h_font_sf_scale[0] = "100";
541 h_font_sf_scale[1] = "100";
542 h_font_tt_scale[0] = "100";
543 h_font_tt_scale[1] = "100";
544 // h_font_roman_opts;
546 // h_font_typewriter_opts;
548 h_is_mathindent = "0";
549 h_math_numbering_side = "default";
550 h_graphics = "default";
551 h_default_output_format = "default";
552 h_html_be_strict = "false";
553 h_html_css_as_file = "0";
554 h_html_math_output = "0";
555 h_docbook_table_output = "0";
556 h_index[0] = "Index";
557 h_index_command = "default";
558 h_inputencoding = "auto-legacy";
559 h_justification = "true";
560 h_language = "english";
561 h_language_package = "none";
563 h_maintain_unincluded_children = "no";
567 h_output_changes = "false";
568 h_change_bars = "false";
570 //h_output_sync_macro
571 h_papercolumns = "1";
572 h_paperfontsize = "default";
573 h_paperorientation = "portrait";
574 h_paperpagestyle = "default";
576 h_papersize = "default";
577 h_paragraph_indentation = "default";
578 h_paragraph_separation = "indent";
583 h_pdf_bookmarks = "0";
584 h_pdf_bookmarksnumbered = "0";
585 h_pdf_bookmarksopen = "0";
586 h_pdf_bookmarksopenlevel = "1";
587 h_pdf_breaklinks = "0";
588 h_pdf_pdfborder = "0";
589 h_pdf_colorlinks = "0";
590 h_pdf_backref = "section";
591 h_pdf_pdfusetitle = "0";
593 //h_pdf_quoted_options;
594 h_quotes_style = "english";
596 h_shortcut[0] = "idx";
597 h_spacing = "single";
598 h_save_transient_properties = "true";
599 h_suppress_date = "false";
600 h_textclass = "article";
602 h_tracking_changes = "false";
603 h_use_bibtopic = "false";
604 h_use_dash_ligatures = "true";
605 h_use_indices = "false";
606 h_use_geometry = "false";
607 h_use_default_options = "false";
608 h_use_hyperref = "false";
609 h_use_microtype = "false";
610 h_use_lineno = "false";
611 h_use_refstyle = false;
612 h_use_minted = false;
613 h_use_packages["amsmath"] = "1";
614 h_use_packages["amssymb"] = "0";
615 h_use_packages["cancel"] = "0";
616 h_use_packages["esint"] = "1";
617 h_use_packages["mhchem"] = "0";
618 h_use_packages["mathdots"] = "0";
619 h_use_packages["mathtools"] = "0";
620 h_use_packages["stackrel"] = "0";
621 h_use_packages["stmaryrd"] = "0";
622 h_use_packages["undertilde"] = "0";
626 void Preamble::handle_hyperref(vector<string> & options)
628 // FIXME swallow inputencoding changes that might surround the
629 // hyperref setup if it was written by LyX
630 h_use_hyperref = "true";
631 // swallow "unicode=true", since LyX does always write that
632 vector<string>::iterator it =
633 find(options.begin(), options.end(), "unicode=true");
634 if (it != options.end())
636 it = find(options.begin(), options.end(), "pdfusetitle");
637 if (it != options.end()) {
638 h_pdf_pdfusetitle = "1";
641 string bookmarks = process_keyval_opt(options, "bookmarks");
642 if (bookmarks == "true")
643 h_pdf_bookmarks = "1";
644 else if (bookmarks == "false")
645 h_pdf_bookmarks = "0";
646 if (h_pdf_bookmarks == "1") {
647 string bookmarksnumbered =
648 process_keyval_opt(options, "bookmarksnumbered");
649 if (bookmarksnumbered == "true")
650 h_pdf_bookmarksnumbered = "1";
651 else if (bookmarksnumbered == "false")
652 h_pdf_bookmarksnumbered = "0";
653 string bookmarksopen =
654 process_keyval_opt(options, "bookmarksopen");
655 if (bookmarksopen == "true")
656 h_pdf_bookmarksopen = "1";
657 else if (bookmarksopen == "false")
658 h_pdf_bookmarksopen = "0";
659 if (h_pdf_bookmarksopen == "1") {
660 string bookmarksopenlevel =
661 process_keyval_opt(options, "bookmarksopenlevel");
662 if (!bookmarksopenlevel.empty())
663 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
666 string breaklinks = process_keyval_opt(options, "breaklinks");
667 if (breaklinks == "true")
668 h_pdf_breaklinks = "1";
669 else if (breaklinks == "false")
670 h_pdf_breaklinks = "0";
671 string pdfborder = process_keyval_opt(options, "pdfborder");
672 if (pdfborder == "{0 0 0}")
673 h_pdf_pdfborder = "1";
674 else if (pdfborder == "{0 0 1}")
675 h_pdf_pdfborder = "0";
676 string backref = process_keyval_opt(options, "backref");
677 if (!backref.empty())
678 h_pdf_backref = backref;
679 string colorlinks = process_keyval_opt(options, "colorlinks");
680 if (colorlinks == "true")
681 h_pdf_colorlinks = "1";
682 else if (colorlinks == "false")
683 h_pdf_colorlinks = "0";
684 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
685 if (!pdfpagemode.empty())
686 h_pdf_pagemode = pdfpagemode;
687 string pdftitle = process_keyval_opt(options, "pdftitle");
688 if (!pdftitle.empty()) {
689 h_pdf_title = remove_braces(pdftitle);
691 string pdfauthor = process_keyval_opt(options, "pdfauthor");
692 if (!pdfauthor.empty()) {
693 h_pdf_author = remove_braces(pdfauthor);
695 string pdfsubject = process_keyval_opt(options, "pdfsubject");
696 if (!pdfsubject.empty())
697 h_pdf_subject = remove_braces(pdfsubject);
698 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
699 if (!pdfkeywords.empty())
700 h_pdf_keywords = remove_braces(pdfkeywords);
701 if (!options.empty()) {
702 if (!h_pdf_quoted_options.empty())
703 h_pdf_quoted_options += ',';
704 h_pdf_quoted_options += join(options, ",");
710 void Preamble::handle_geometry(vector<string> & options)
712 h_use_geometry = "true";
713 vector<string>::iterator it;
715 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
716 h_paperorientation = "landscape";
720 // keyval version: "paper=letter" or "paper=letterpaper"
721 string paper = process_keyval_opt(options, "paper");
723 if (suffixIs(paper, "paper"))
724 paper = subst(paper, "paper", "");
725 // alternative version: "letterpaper"
726 handle_opt(options, known_latex_paper_sizes, paper);
727 if (suffixIs(paper, "paper"))
728 paper = subst(paper, "paper", "");
729 delete_opt(options, known_latex_paper_sizes);
733 char const * const * margin = known_paper_margins;
734 for (; *margin; ++margin) {
735 string value = process_keyval_opt(options, *margin);
736 if (!value.empty()) {
737 int k = margin - known_paper_margins;
738 string name = known_coded_paper_margins[k];
739 h_margins += '\\' + name + ' ' + value + '\n';
745 void Preamble::handle_package(Parser &p, string const & name,
746 string const & opts, bool in_lyx_preamble,
749 vector<string> options = split_options(opts);
750 add_package(name, options);
752 if (is_known(name, known_xetex_packages)) {
754 h_use_non_tex_fonts = true;
755 registerAutomaticallyLoadedPackage("fontspec");
756 if (h_inputencoding == "auto-legacy")
757 p.setEncoding("UTF-8");
760 // vector of all options for easier parsing and
762 vector<string> allopts = getVectorFromString(opts);
763 // this stores the potential extra options
770 // By default, we use the package name as LyX font name,
771 // so this only needs to be reset if these names differ
772 if (is_known(name, known_roman_font_packages))
773 h_font_roman[0] = name;
775 if (name == "ccfonts") {
776 for (auto const & opt : allopts) {
782 h_font_roman_opts = xopts;
786 if (name == "lmodern") {
787 for (auto const & opt : allopts) {
793 h_font_roman_opts = xopts;
797 if (name == "fourier") {
798 h_font_roman[0] = "utopia";
799 for (auto const & opt : allopts) {
801 h_font_roman_osf = "true";
804 if (opt == "expert") {
813 h_font_roman_opts = xopts;
817 if (name == "garamondx") {
818 for (auto const & opt : allopts) {
820 h_font_roman_osf = "true";
828 h_font_roman_opts = xopts;
832 if (name == "libertine") {
833 // this automatically invokes biolinum
834 h_font_sans[0] = "biolinum";
835 // as well as libertineMono
836 h_font_typewriter[0] = "libertine-mono";
837 for (auto const & opt : allopts) {
839 h_font_roman_osf = "true";
842 if (opt == "lining") {
843 h_font_roman_osf = "false";
851 h_font_roman_opts = xopts;
855 if (name == "libertineRoman" || name == "libertine-type1") {
856 h_font_roman[0] = "libertine";
857 // NOTE: contrary to libertine.sty, libertineRoman
858 // and libertine-type1 do not automatically invoke
859 // biolinum and libertineMono
860 if (opts == "lining")
861 h_font_roman_osf = "false";
862 else if (opts == "osf")
863 h_font_roman_osf = "true";
866 if (name == "libertinus" || name == "libertinus-type1") {
873 for (auto const & opt : allopts) {
874 if (opt == "rm" || opt == "serif") {
879 if (opt == "sf" || opt == "sans") {
884 if (opt == "tt=false" || opt == "mono=false") {
892 if (opt == "scaleSF") {
896 if (opt == "scaleTT") {
900 if (opt == "lining") {
901 h_font_roman_osf = "false";
909 h_font_roman[0] = "libertinus";
911 h_font_roman_osf = "true";
913 h_font_roman_osf = "false";
916 h_font_sans[0] = "LibertinusSans-LF";
918 h_font_sans_osf = "true";
920 h_font_sans_osf = "false";
921 if (!scalesf.empty())
922 scale_as_percentage(scalesf, h_font_sf_scale[0]);
925 h_font_typewriter[0] = "LibertinusMono-TLF";
926 if (!scalett.empty())
927 scale_as_percentage(scalett, h_font_tt_scale[0]);
930 h_font_roman_opts = xopts;
934 if (name == "MinionPro") {
935 h_font_roman[0] = "minionpro";
936 h_font_roman_osf = "true";
937 h_font_math[0] = "auto";
938 for (auto const & opt : allopts) {
940 h_font_roman_osf = "false";
943 if (opt == "onlytext") {
944 h_font_math[0] = "default";
952 h_font_roman_opts = xopts;
956 if (name == "mathdesign") {
957 for (auto const & opt : allopts) {
958 if (opt == "charter") {
959 h_font_roman[0] = "md-charter";
962 if (opt == "garamond") {
963 h_font_roman[0] = "md-garamond";
966 if (opt == "utopia") {
967 h_font_roman[0] = "md-utopia";
970 if (opt == "expert") {
972 h_font_roman_osf = "true";
978 else if (name == "mathpazo") {
979 h_font_roman[0] = "palatino";
980 for (auto const & opt : allopts) {
982 h_font_roman_osf = "true";
994 h_font_roman_opts = xopts;
998 else if (name == "mathptmx") {
999 h_font_roman[0] = "times";
1000 for (auto const & opt : allopts) {
1006 h_font_roman_opts = xopts;
1010 if (name == "crimson")
1011 h_font_roman[0] = "cochineal";
1013 if (name == "cochineal") {
1014 for (auto const & opt : allopts) {
1015 if (opt == "osf" || opt == "oldstyle") {
1016 h_font_roman_osf = "true";
1019 if (opt == "proportional" || opt == "p")
1026 h_font_roman_opts = xopts;
1030 if (name == "CrimsonPro") {
1031 h_font_roman_osf = "true";
1032 for (auto const & opt : allopts) {
1033 if (opt == "lf" || opt == "lining") {
1034 h_font_roman_osf = "false";
1037 if (opt == "proportional" || opt == "p")
1039 if (opt == "medium") {
1040 h_font_roman[0] = "CrimsonProMedium";
1043 if (opt == "extralight") {
1044 h_font_roman[0] = "CrimsonProExtraLight";
1047 if (opt == "light") {
1048 h_font_roman[0] = "CrimsonProLight";
1056 h_font_roman_opts = xopts;
1062 // font uses old-style figure
1063 h_font_roman_osf = "true";
1065 if (name == "paratype") {
1066 // in this case all fonts are ParaType
1067 h_font_roman[0] = "PTSerif-TLF";
1068 h_font_sans[0] = "default";
1069 h_font_typewriter[0] = "default";
1072 if (name == "PTSerif")
1073 h_font_roman[0] = "PTSerif-TLF";
1075 if (name == "XCharter") {
1076 h_font_roman[0] = "xcharter";
1077 for (auto const & opt : allopts) {
1079 h_font_roman_osf = "true";
1087 h_font_roman_opts = xopts;
1091 if (name == "plex-serif") {
1092 h_font_roman[0] = "IBMPlexSerif";
1093 for (auto const & opt : allopts) {
1094 if (opt == "thin") {
1095 h_font_roman[0] = "IBMPlexSerifThin";
1098 if (opt == "extralight") {
1099 h_font_roman[0] = "IBMPlexSerifExtraLight";
1102 if (opt == "light") {
1103 h_font_roman[0] = "IBMPlexSerifLight";
1111 h_font_roman_opts = xopts;
1115 if (name == "noto-serif" || name == "noto") {
1122 bool extralight = false;
1124 bool medium = false;
1127 if (name == "noto") {
1132 // Since the options might apply to different shapes,
1133 // we need to parse all options first and then handle them.
1134 for (auto const & opt : allopts) {
1135 if (opt == "regular")
1145 if (opt == "thin") {
1149 if (opt == "extralight") {
1153 if (opt == "light") {
1157 if (opt == "medium") {
1168 if (opt == "nott") {
1176 if (prefixIs(opt, "scaled=")) {
1185 // handle options that might affect different shapes
1186 if (name == "noto-serif" || rm) {
1188 h_font_roman[0] = "NotoSerifThin";
1189 else if (extralight)
1190 h_font_roman[0] = "NotoSerifExtralight";
1192 h_font_roman[0] = "NotoSerifLight";
1194 h_font_roman[0] = "NotoSerifMedium";
1196 h_font_roman[0] = "NotoSerifRegular";
1198 h_font_roman_osf = "true";
1200 h_font_roman_opts = xopts;
1202 if (name == "noto" && sf) {
1204 h_font_sans[0] = "NotoSansThin";
1205 else if (extralight)
1206 h_font_sans[0] = "NotoSansExtralight";
1208 h_font_sans[0] = "NotoSansLight";
1210 h_font_sans[0] = "NotoSansMedium";
1212 h_font_sans[0] = "NotoSansRegular";
1214 h_font_sans_osf = "true";
1216 scale_as_percentage(scl, h_font_sf_scale[0]);
1218 h_font_sans_opts = xopts;
1220 if (name == "noto" && tt) {
1221 h_font_typewriter[0] = "NotoMonoRegular";
1223 h_font_typewriter_osf = "true";
1225 scale_as_percentage(scl, h_font_tt_scale[0]);
1227 h_font_typewriter_opts = xopts;
1231 if (name == "sourceserifpro") {
1232 h_font_roman[0] = "ADOBESourceSerifPro";
1233 for (auto const & opt : allopts) {
1235 h_font_roman_osf = "true";
1243 h_font_roman_opts = xopts;
1251 // By default, we use the package name as LyX font name,
1252 // so this only needs to be reset if these names differ.
1253 // Also, we handle the scaling option here generally.
1254 if (is_known(name, known_sans_font_packages)) {
1255 h_font_sans[0] = name;
1256 if (contains(opts, "scale")) {
1257 vector<string>::iterator it = allopts.begin();
1258 for (; it != allopts.end() ; ++it) {
1259 string const opt = *it;
1260 if (prefixIs(opt, "scaled=") || prefixIs(opt, "scale=")) {
1261 if (scale_as_percentage(opt, h_font_sf_scale[0])) {
1270 if (name == "biolinum" || name == "biolinum-type1") {
1271 h_font_sans[0] = "biolinum";
1272 for (auto const & opt : allopts) {
1273 if (prefixIs(opt, "osf")) {
1274 h_font_sans_osf = "true";
1282 h_font_sans_opts = xopts;
1286 if (name == "cantarell") {
1287 for (auto const & opt : allopts) {
1288 if (opt == "defaultsans")
1290 if (prefixIs(opt, "oldstyle")) {
1291 h_font_sans_osf = "true";
1299 h_font_sans_opts = xopts;
1303 if (name == "Chivo") {
1304 for (auto const & opt : allopts) {
1305 if (opt == "thin") {
1306 h_font_roman[0] = "ChivoThin";
1309 if (opt == "light") {
1310 h_font_roman[0] = "ChivoLight";
1313 if (opt == "regular") {
1314 h_font_roman[0] = "Chivo";
1317 if (opt == "medium") {
1318 h_font_roman[0] = "ChivoMedium";
1321 if (prefixIs(opt, "oldstyle")) {
1322 h_font_sans_osf = "true";
1330 h_font_sans_opts = xopts;
1334 if (name == "PTSans") {
1335 h_font_sans[0] = "PTSans-TLF";
1338 if (name == "FiraSans") {
1339 h_font_sans_osf = "true";
1340 for (auto const & opt : allopts) {
1341 if (opt == "book") {
1342 h_font_sans[0] = "FiraSansBook";
1345 if (opt == "thin") {
1348 if (opt == "extralight") {
1349 h_font_sans[0] = "FiraSansExtralight";
1352 if (opt == "light") {
1353 h_font_sans[0] = "FiraSansLight";
1356 if (opt == "ultralight") {
1357 h_font_sans[0] = "FiraSansUltralight";
1360 if (opt == "thin") {
1361 h_font_sans[0] = "FiraSansThin";
1364 if (opt == "lf" || opt == "lining") {
1365 h_font_sans_osf = "false";
1373 h_font_sans_opts = xopts;
1377 if (name == "plex-sans") {
1378 h_font_sans[0] = "IBMPlexSans";
1379 for (auto const & opt : allopts) {
1380 if (opt == "condensed") {
1381 h_font_sans[0] = "IBMPlexSansCondensed";
1384 if (opt == "thin") {
1385 h_font_sans[0] = "IBMPlexSansThin";
1388 if (opt == "extralight") {
1389 h_font_sans[0] = "IBMPlexSansExtraLight";
1392 if (opt == "light") {
1393 h_font_sans[0] = "IBMPlexSansLight";
1401 h_font_sans_opts = xopts;
1405 if (name == "noto-sans") {
1406 h_font_sans[0] = "NotoSansRegular";
1407 for (auto const & opt : allopts) {
1408 if (opt == "regular")
1410 if (opt == "medium") {
1411 h_font_sans[0] = "NotoSansMedium";
1414 if (opt == "thin") {
1415 h_font_sans[0] = "NotoSansThin";
1418 if (opt == "extralight") {
1419 h_font_sans[0] = "NotoSansExtralight";
1422 if (opt == "light") {
1423 h_font_sans[0] = "NotoSansLight";
1427 h_font_sans_osf = "true";
1435 h_font_sans_opts = xopts;
1439 if (name == "sourcesanspro") {
1440 h_font_sans[0] = "ADOBESourceSansPro";
1441 for (auto const & opt : allopts) {
1443 h_font_sans_osf = "true";
1451 h_font_sans_opts = xopts;
1459 // By default, we use the package name as LyX font name,
1460 // so this only needs to be reset if these names differ.
1461 // Also, we handle the scaling option here generally.
1462 if (is_known(name, known_typewriter_font_packages)) {
1463 h_font_typewriter[0] = name;
1464 if (contains(opts, "scale")) {
1465 vector<string>::iterator it = allopts.begin();
1466 for (; it != allopts.end() ; ++it) {
1467 string const opt = *it;
1468 if (prefixIs(opt, "scaled=") || prefixIs(opt, "scale=")) {
1469 if (scale_as_percentage(opt, h_font_tt_scale[0])) {
1478 if (name == "libertineMono" || name == "libertineMono-type1")
1479 h_font_typewriter[0] = "libertine-mono";
1481 if (name == "FiraMono") {
1482 h_font_typewriter_osf = "true";
1483 for (auto const & opt : allopts) {
1484 if (opt == "lf" || opt == "lining") {
1485 h_font_typewriter_osf = "false";
1493 h_font_typewriter_opts = xopts;
1497 if (name == "PTMono")
1498 h_font_typewriter[0] = "PTMono-TLF";
1500 if (name == "plex-mono") {
1501 h_font_typewriter[0] = "IBMPlexMono";
1502 for (auto const & opt : allopts) {
1503 if (opt == "thin") {
1504 h_font_typewriter[0] = "IBMPlexMonoThin";
1507 if (opt == "extralight") {
1508 h_font_typewriter[0] = "IBMPlexMonoExtraLight";
1511 if (opt == "light") {
1512 h_font_typewriter[0] = "IBMPlexMonoLight";
1520 h_font_typewriter_opts = xopts;
1524 if (name == "noto-mono") {
1525 h_font_typewriter[0] = "NotoMonoRegular";
1526 for (auto const & opt : allopts) {
1527 if (opt == "regular")
1534 h_font_typewriter_opts = xopts;
1538 if (name == "sourcecodepro") {
1539 h_font_typewriter[0] = "ADOBESourceCodePro";
1540 for (auto const & opt : allopts) {
1542 h_font_typewriter_osf = "true";
1550 h_font_typewriter_opts = xopts;
1558 // By default, we use the package name as LyX font name,
1559 // so this only needs to be reset if these names differ.
1560 if (is_known(name, known_math_font_packages))
1561 h_font_math[0] = name;
1563 if (name == "newtxmath") {
1565 h_font_math[0] = "newtxmath";
1566 else if (opts == "garamondx")
1567 h_font_math[0] = "garamondx-ntxm";
1568 else if (opts == "libertine")
1569 h_font_math[0] = "libertine-ntxm";
1570 else if (opts == "minion")
1571 h_font_math[0] = "minion-ntxm";
1572 else if (opts == "cochineal")
1573 h_font_math[0] = "cochineal-ntxm";
1576 if (name == "iwona")
1578 h_font_math[0] = "iwona-math";
1580 if (name == "kurier")
1582 h_font_math[0] = "kurier-math";
1584 // after the detection and handling of special cases, we can remove the
1585 // fonts, otherwise they would appear in the preamble, see bug #7856
1586 if (is_known(name, known_roman_font_packages) || is_known(name, known_sans_font_packages)
1587 || is_known(name, known_typewriter_font_packages) || is_known(name, known_math_font_packages))
1589 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
1590 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
1591 name == "esint" || name == "mhchem" || name == "mathdots" ||
1592 name == "mathtools" || name == "stackrel" ||
1593 name == "stmaryrd" || name == "undertilde") {
1594 h_use_packages[name] = "2";
1595 registerAutomaticallyLoadedPackage(name);
1598 else if (name == "babel") {
1599 h_language_package = "default";
1600 // One might think we would have to do nothing if babel is loaded
1601 // without any options to prevent pollution of the preamble with this
1602 // babel call in every roundtrip.
1603 // But the user could have defined babel-specific things afterwards. So
1604 // we need to keep it in the preamble to prevent cases like bug #7861.
1605 if (!opts.empty()) {
1606 // check if more than one option was used - used later for inputenc
1607 if (options.begin() != options.end() - 1)
1608 one_language = false;
1609 // babel takes the last language of the option of its \usepackage
1610 // call as document language. If there is no such language option, the
1611 // last language in the documentclass options is used.
1612 handle_opt(options, known_languages, h_language);
1613 // translate the babel name to a LyX name
1614 h_language = babel2lyx(h_language);
1615 if (h_language == "japanese") {
1616 // For Japanese, the encoding isn't indicated in the source
1617 // file, and there's really not much we can do. We could
1618 // 1) offer a list of possible encodings to choose from, or
1619 // 2) determine the encoding of the file by inspecting it.
1620 // For the time being, we leave the encoding alone so that
1621 // we don't get iconv errors when making a wrong guess, and
1622 // we will output a note at the top of the document
1623 // explaining what to do.
1624 Encoding const * const enc = encodings.fromIconvName(
1625 p.getEncoding(), Encoding::japanese, false);
1627 h_inputencoding = enc->name();
1628 is_nonCJKJapanese = true;
1629 // in this case babel can be removed from the preamble
1630 registerAutomaticallyLoadedPackage("babel");
1632 // If babel is called with options, LyX puts them by default into the
1633 // document class options. This works for most languages, except
1634 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
1635 // perhaps in future others.
1636 // Therefore keep the babel call as it is as the user might have
1638 string const babelcall = "\\usepackage[" + opts + "]{babel}\n";
1639 if (!contains(h_preamble.str(), babelcall))
1640 h_preamble << babelcall;
1642 delete_opt(options, known_languages);
1644 if (!contains(h_preamble.str(), "\\usepackage{babel}\n"))
1645 h_preamble << "\\usepackage{babel}\n";
1646 explicit_babel = true;
1650 else if (name == "polyglossia") {
1651 h_language_package = "default";
1652 h_default_output_format = "pdf4";
1653 h_use_non_tex_fonts = true;
1655 registerAutomaticallyLoadedPackage("xunicode");
1656 if (h_inputencoding == "auto-legacy")
1657 p.setEncoding("UTF-8");
1660 else if (name == "CJK") {
1661 // set the encoding to "auto-legacy" because it might be set to "auto-legacy-plain" by the babel handling
1662 // and this would not be correct for CJK
1663 if (h_inputencoding == "auto-legacy-plain")
1664 h_inputencoding = "auto-legacy";
1665 registerAutomaticallyLoadedPackage("CJK");
1668 else if (name == "CJKutf8") {
1669 h_inputencoding = "utf8-cjk";
1670 p.setEncoding("UTF-8");
1671 registerAutomaticallyLoadedPackage("CJKutf8");
1674 else if (name == "fontenc") {
1675 h_fontencoding = getStringFromVector(options, ",");
1679 else if (name == "inputenc" || name == "luainputenc") {
1680 // h_inputencoding is only set when there is not more than one
1681 // inputenc option because otherwise h_inputencoding must be
1682 // set to "auto-legacy" (the default encodings of the document's languages)
1683 // Therefore check that exactly one option is passed to inputenc.
1684 // It is also only set when there is not more than one babel
1686 if (!options.empty()) {
1687 string const encoding = options.back();
1688 Encoding const * const enc = encodings.fromLaTeXName(
1689 encoding, Encoding::inputenc, true);
1691 if (!detectEncoding)
1692 cerr << "Unknown encoding " << encoding
1693 << ". Ignoring." << std::endl;
1695 if (!enc->unsafe() && options.size() == 1 && one_language == true)
1696 h_inputencoding = enc->name();
1697 p.setEncoding(enc->iconvName());
1703 else if (name == "srcltx") {
1704 h_output_sync = "1";
1705 if (!opts.empty()) {
1706 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
1709 h_output_sync_macro = "\\usepackage{srcltx}";
1712 else if (is_known(name, known_old_language_packages)) {
1713 // known language packages from the times before babel
1714 // if they are found and not also babel, they will be used as
1715 // custom language package
1716 h_language_package = "\\usepackage{" + name + "}";
1719 else if (name == "lyxskak") {
1720 // ignore this and its options
1721 const char * const o[] = {"ps", "mover", 0};
1722 delete_opt(options, o);
1725 else if (name == "parskip" && options.size() < 2 && (opts.empty() || prefixIs(opts, "skip="))) {
1727 h_paragraph_separation = "halfline";
1729 if (opts == "skip=\\smallskipamount")
1730 h_defskip = "smallskip";
1731 else if (opts == "skip=\\medskipamount")
1732 h_defskip = "medskip";
1733 else if (opts == "skip=\\bigskipamount")
1734 h_defskip = "bigskip";
1735 else if (opts == "skip=\\baselineskip")
1736 h_defskip = "fullline";
1739 h_paragraph_separation = "skip";
1743 else if (is_known(name, known_lyx_packages) && options.empty()) {
1744 if (name == "splitidx")
1745 h_use_indices = "true";
1746 else if (name == "minted")
1747 h_use_minted = true;
1748 else if (name == "refstyle")
1749 h_use_refstyle = true;
1750 else if (name == "prettyref")
1751 h_use_refstyle = false;
1753 if (!in_lyx_preamble) {
1754 h_preamble << package_beg_sep << name
1755 << package_mid_sep << "\\usepackage{"
1757 if (p.next_token().cat() == catNewline ||
1758 (p.next_token().cat() == catSpace &&
1759 p.next_next_token().cat() == catNewline))
1761 h_preamble << package_end_sep;
1765 else if (name == "geometry")
1766 handle_geometry(options);
1768 else if (name == "subfig")
1769 ; // ignore this FIXME: Use the package separator mechanism instead
1771 else if (char const * const * where = is_known(name, known_languages))
1772 h_language = known_coded_languages[where - known_languages];
1774 else if (name == "natbib") {
1775 h_biblio_style = "plainnat";
1776 h_cite_engine = "natbib";
1777 h_cite_engine_type = "authoryear";
1778 vector<string>::iterator it =
1779 find(options.begin(), options.end(), "authoryear");
1780 if (it != options.end())
1783 it = find(options.begin(), options.end(), "numbers");
1784 if (it != options.end()) {
1785 h_cite_engine_type = "numerical";
1789 if (!options.empty())
1790 h_biblio_options = join(options, ",");
1793 else if (name == "biblatex") {
1794 h_biblio_style = "plainnat";
1795 h_cite_engine = "biblatex";
1796 h_cite_engine_type = "authoryear";
1798 vector<string>::iterator it =
1799 find(options.begin(), options.end(), "natbib");
1800 if (it != options.end()) {
1802 h_cite_engine = "biblatex-natbib";
1804 opt = process_keyval_opt(options, "natbib");
1806 h_cite_engine = "biblatex-natbib";
1808 opt = process_keyval_opt(options, "style");
1810 h_biblatex_citestyle = opt;
1811 h_biblatex_bibstyle = opt;
1813 opt = process_keyval_opt(options, "citestyle");
1815 h_biblatex_citestyle = opt;
1816 opt = process_keyval_opt(options, "bibstyle");
1818 h_biblatex_bibstyle = opt;
1820 opt = process_keyval_opt(options, "refsection");
1822 if (opt == "none" || opt == "part"
1823 || opt == "chapter" || opt == "section"
1824 || opt == "subsection")
1827 cerr << "Ignoring unknown refsection value '"
1830 opt = process_keyval_opt(options, "bibencoding");
1833 if (!options.empty()) {
1834 h_biblio_options = join(options, ",");
1839 else if (name == "jurabib") {
1840 h_biblio_style = "jurabib";
1841 h_cite_engine = "jurabib";
1842 h_cite_engine_type = "authoryear";
1843 if (!options.empty())
1844 h_biblio_options = join(options, ",");
1847 else if (name == "bibtopic")
1848 h_use_bibtopic = "true";
1850 else if (name == "chapterbib")
1851 h_multibib = "child";
1853 else if (name == "hyperref")
1854 handle_hyperref(options);
1856 else if (name == "algorithm2e") {
1857 // Load "algorithm2e" module
1858 addModule("algorithm2e");
1859 // Add the package options to the global document options
1860 if (!options.empty()) {
1861 if (h_options.empty())
1862 h_options = join(options, ",");
1864 h_options += ',' + join(options, ",");
1867 else if (name == "microtype") {
1868 //we internally support only microtype without params
1869 if (options.empty())
1870 h_use_microtype = "true";
1872 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1875 else if (name == "lineno") {
1876 h_use_lineno = "true";
1877 if (!options.empty()) {
1878 h_lineno_options = join(options, ",");
1883 else if (name == "changebar")
1884 h_output_changes = "true";
1886 else if (!in_lyx_preamble) {
1887 if (options.empty())
1888 h_preamble << "\\usepackage{" << name << '}';
1890 h_preamble << "\\usepackage[" << opts << "]{"
1894 if (p.next_token().cat() == catNewline ||
1895 (p.next_token().cat() == catSpace &&
1896 p.next_next_token().cat() == catNewline))
1900 // We need to do something with the options...
1901 if (!options.empty() && !detectEncoding)
1902 cerr << "Ignoring options '" << join(options, ",")
1903 << "' of package " << name << '.' << endl;
1905 // remove the whitespace
1910 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1913 Token t = p.get_token();
1914 if (t.cat() == catEscape &&
1915 is_known(t.cs(), known_if_commands))
1916 handle_if(p, in_lyx_preamble);
1918 if (!in_lyx_preamble)
1919 h_preamble << t.asInput();
1920 if (t.cat() == catEscape && t.cs() == "fi")
1927 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1929 if (contains(h_float_placement, "H"))
1930 registerAutomaticallyLoadedPackage("float");
1931 if (h_spacing != "single" && h_spacing != "default")
1932 registerAutomaticallyLoadedPackage("setspace");
1933 if (h_use_packages["amsmath"] == "2") {
1934 // amsbsy and amstext are already provided by amsmath
1935 registerAutomaticallyLoadedPackage("amsbsy");
1936 registerAutomaticallyLoadedPackage("amstext");
1939 // output the LyX file settings
1940 // Important: Keep the version formatting in sync with LyX and
1941 // lyx2lyx (bug 7951)
1942 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1943 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1944 << lyx_version_minor << '\n'
1945 << "\\lyxformat " << LYX_FORMAT << '\n'
1946 << "\\begin_document\n"
1947 << "\\begin_header\n"
1948 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1949 << "\\origin " << origin << "\n"
1950 << "\\textclass " << h_textclass << "\n";
1951 string const raw = subdoc ? empty_string() : h_preamble.str();
1953 os << "\\begin_preamble\n";
1954 for (string::size_type i = 0; i < raw.size(); ++i) {
1955 if (raw[i] == package_beg_sep) {
1956 // Here follows some package loading code that
1957 // must be skipped if the package is loaded
1959 string::size_type j = raw.find(package_mid_sep, i);
1960 if (j == string::npos)
1962 string::size_type k = raw.find(package_end_sep, j);
1963 if (k == string::npos)
1965 string const package = raw.substr(i + 1, j - i - 1);
1966 string const replacement = raw.substr(j + 1, k - j - 1);
1967 if (auto_packages.find(package) == auto_packages.end())
1973 os << "\n\\end_preamble\n";
1975 if (!h_options.empty())
1976 os << "\\options " << h_options << "\n";
1977 os << "\\use_default_options " << h_use_default_options << "\n";
1978 if (!used_modules.empty()) {
1979 os << "\\begin_modules\n";
1980 vector<string>::const_iterator const end = used_modules.end();
1981 vector<string>::const_iterator it = used_modules.begin();
1982 for (; it != end; ++it)
1984 os << "\\end_modules\n";
1986 if (!h_includeonlys.empty()) {
1987 os << "\\begin_includeonly\n";
1988 for (auto const & iofile : h_includeonlys)
1989 os << iofile << '\n';
1990 os << "\\end_includeonly\n";
1992 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1993 << "\\language " << h_language << "\n"
1994 << "\\language_package " << h_language_package << "\n"
1995 << "\\inputencoding " << h_inputencoding << "\n"
1996 << "\\fontencoding " << h_fontencoding << "\n"
1997 << "\\font_roman \"" << h_font_roman[0]
1998 << "\" \"" << h_font_roman[1] << "\"\n"
1999 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
2000 << "\\font_typewriter \"" << h_font_typewriter[0]
2001 << "\" \"" << h_font_typewriter[1] << "\"\n"
2002 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
2003 << "\\font_default_family " << h_font_default_family << "\n"
2004 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
2005 << "\\font_sc " << h_font_sc << "\n"
2006 << "\\font_roman_osf " << h_font_roman_osf << "\n"
2007 << "\\font_sans_osf " << h_font_sans_osf << "\n"
2008 << "\\font_typewriter_osf " << h_font_typewriter_osf << "\n";
2009 if (!h_font_roman_opts.empty())
2010 os << "\\font_roman_opts \"" << h_font_roman_opts << "\"" << '\n';
2011 os << "\\font_sf_scale " << h_font_sf_scale[0]
2012 << ' ' << h_font_sf_scale[1] << '\n';
2013 if (!h_font_sans_opts.empty())
2014 os << "\\font_sans_opts \"" << h_font_sans_opts << "\"" << '\n';
2015 os << "\\font_tt_scale " << h_font_tt_scale[0]
2016 << ' ' << h_font_tt_scale[1] << '\n';
2017 if (!h_font_cjk.empty())
2018 os << "\\font_cjk " << h_font_cjk << '\n';
2019 if (!h_font_typewriter_opts.empty())
2020 os << "\\font_typewriter_opts \"" << h_font_typewriter_opts << "\"" << '\n';
2021 os << "\\use_microtype " << h_use_microtype << '\n'
2022 << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
2023 << "\\graphics " << h_graphics << '\n'
2024 << "\\default_output_format " << h_default_output_format << "\n"
2025 << "\\output_sync " << h_output_sync << "\n";
2026 if (h_output_sync == "1")
2027 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
2028 os << "\\bibtex_command " << h_bibtex_command << "\n"
2029 << "\\index_command " << h_index_command << "\n";
2030 if (!h_float_placement.empty())
2031 os << "\\float_placement " << h_float_placement << "\n";
2032 os << "\\paperfontsize " << h_paperfontsize << "\n"
2033 << "\\spacing " << h_spacing << "\n"
2034 << "\\use_hyperref " << h_use_hyperref << '\n';
2035 if (h_use_hyperref == "true") {
2036 if (!h_pdf_title.empty())
2037 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
2038 if (!h_pdf_author.empty())
2039 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
2040 if (!h_pdf_subject.empty())
2041 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
2042 if (!h_pdf_keywords.empty())
2043 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
2044 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
2045 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
2046 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
2047 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
2048 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
2049 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
2050 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
2051 "\\pdf_backref " << h_pdf_backref << "\n"
2052 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
2053 if (!h_pdf_pagemode.empty())
2054 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
2055 if (!h_pdf_quoted_options.empty())
2056 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
2058 os << "\\papersize " << h_papersize << "\n"
2059 << "\\use_geometry " << h_use_geometry << '\n';
2060 for (map<string, string>::const_iterator it = h_use_packages.begin();
2061 it != h_use_packages.end(); ++it)
2062 os << "\\use_package " << it->first << ' ' << it->second << '\n';
2063 os << "\\cite_engine " << h_cite_engine << '\n'
2064 << "\\cite_engine_type " << h_cite_engine_type << '\n'
2065 << "\\biblio_style " << h_biblio_style << "\n"
2066 << "\\use_bibtopic " << h_use_bibtopic << "\n";
2067 if (!h_biblio_options.empty())
2068 os << "\\biblio_options " << h_biblio_options << "\n";
2069 if (!h_biblatex_bibstyle.empty())
2070 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
2071 if (!h_biblatex_citestyle.empty())
2072 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
2073 if (!h_multibib.empty())
2074 os << "\\multibib " << h_multibib << "\n";
2075 os << "\\use_indices " << h_use_indices << "\n"
2076 << "\\paperorientation " << h_paperorientation << '\n'
2077 << "\\suppress_date " << h_suppress_date << '\n'
2078 << "\\justification " << h_justification << '\n'
2079 << "\\use_refstyle " << h_use_refstyle << '\n'
2080 << "\\use_minted " << h_use_minted << '\n'
2081 << "\\use_lineno " << h_use_lineno << '\n';
2082 if (!h_lineno_options.empty())
2083 os << "\\lineno_options " << h_lineno_options << '\n';
2084 if (!h_fontcolor.empty())
2085 os << "\\fontcolor " << h_fontcolor << '\n';
2086 if (!h_notefontcolor.empty())
2087 os << "\\notefontcolor " << h_notefontcolor << '\n';
2088 if (!h_backgroundcolor.empty())
2089 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
2090 if (!h_boxbgcolor.empty())
2091 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
2092 if (index_number != 0)
2093 for (int i = 0; i < index_number; i++) {
2094 os << "\\index " << h_index[i] << '\n'
2095 << "\\shortcut " << h_shortcut[i] << '\n'
2096 << "\\color " << h_color << '\n'
2100 os << "\\index " << h_index[0] << '\n'
2101 << "\\shortcut " << h_shortcut[0] << '\n'
2102 << "\\color " << h_color << '\n'
2106 << "\\secnumdepth " << h_secnumdepth << "\n"
2107 << "\\tocdepth " << h_tocdepth << "\n"
2108 << "\\paragraph_separation " << h_paragraph_separation << "\n";
2109 if (h_paragraph_separation == "skip")
2110 os << "\\defskip " << h_defskip << "\n";
2112 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
2113 os << "\\is_math_indent " << h_is_mathindent << "\n";
2114 if (!h_mathindentation.empty())
2115 os << "\\math_indentation " << h_mathindentation << "\n";
2116 os << "\\math_numbering_side " << h_math_numbering_side << "\n";
2117 os << "\\quotes_style " << h_quotes_style << "\n"
2118 << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
2119 << "\\papercolumns " << h_papercolumns << "\n"
2120 << "\\papersides " << h_papersides << "\n"
2121 << "\\paperpagestyle " << h_paperpagestyle << "\n";
2122 if (!h_listings_params.empty())
2123 os << "\\listings_params " << h_listings_params << "\n";
2124 os << "\\tracking_changes " << h_tracking_changes << "\n"
2125 << "\\output_changes " << h_output_changes << "\n"
2126 << "\\change_bars " << h_change_bars << "\n"
2127 << "\\html_math_output " << h_html_math_output << "\n"
2128 << "\\html_css_as_file " << h_html_css_as_file << "\n"
2129 << "\\html_be_strict " << h_html_be_strict << "\n"
2130 << "\\docbook_table_output " << h_docbook_table_output << "\n"
2132 << "\\end_header\n\n"
2133 << "\\begin_body\n";
2138 void Preamble::parse(Parser & p, string const & forceclass,
2139 TeX2LyXDocClass & tc)
2141 // initialize fixed types
2142 special_columns_['D'] = 3;
2143 parse(p, forceclass, false, tc);
2147 void Preamble::parse(Parser & p, string const & forceclass,
2148 bool detectEncoding, TeX2LyXDocClass & tc)
2150 bool is_full_document = false;
2151 bool is_lyx_file = false;
2152 bool in_lyx_preamble = false;
2153 bool class_set = false;
2155 // determine whether this is a full document or a fragment for inclusion
2157 Token const & t = p.get_token();
2159 if (t.cat() == catEscape && t.cs() == "documentclass") {
2160 is_full_document = true;
2166 if (detectEncoding && !is_full_document)
2169 while (is_full_document && p.good()) {
2170 if (detectEncoding && h_inputencoding != "auto-legacy" &&
2171 h_inputencoding != "auto-legacy-plain")
2174 // Force textclass if the user wanted it
2175 if (!forceclass.empty()) {
2176 setTextClass(forceclass, tc);
2180 Token const & t = p.get_token();
2183 if (!detectEncoding)
2184 cerr << "t: " << t << '\n';
2190 if (!in_lyx_preamble &&
2191 (t.cat() == catLetter ||
2192 t.cat() == catSuper ||
2193 t.cat() == catSub ||
2194 t.cat() == catOther ||
2195 t.cat() == catMath ||
2196 t.cat() == catActive ||
2197 t.cat() == catBegin ||
2198 t.cat() == catEnd ||
2199 t.cat() == catAlign ||
2200 t.cat() == catParameter)) {
2201 h_preamble << t.cs();
2205 if (!in_lyx_preamble &&
2206 (t.cat() == catSpace || t.cat() == catNewline)) {
2207 h_preamble << t.asInput();
2211 if (t.cat() == catComment) {
2212 static regex const islyxfile("%% LyX .* created this file");
2213 static regex const usercommands("User specified LaTeX commands");
2215 string const comment = t.asInput();
2217 // magically switch encoding default if it looks like XeLaTeX
2218 static string const magicXeLaTeX =
2219 "% This document must be compiled with XeLaTeX ";
2220 if (comment.size() > magicXeLaTeX.size()
2221 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
2222 && h_inputencoding == "auto-legacy") {
2223 if (!detectEncoding)
2224 cerr << "XeLaTeX comment found, switching to UTF8\n";
2225 h_inputencoding = "utf8";
2228 if (regex_search(comment, sub, islyxfile)) {
2230 in_lyx_preamble = true;
2231 } else if (is_lyx_file
2232 && regex_search(comment, sub, usercommands))
2233 in_lyx_preamble = false;
2234 else if (!in_lyx_preamble)
2235 h_preamble << t.asInput();
2239 if (t.cs() == "PassOptionsToPackage") {
2240 string const poptions = p.getArg('{', '}');
2241 string const package = p.verbatim_item();
2242 extra_package_options_.insert(make_pair(package, poptions));
2246 if (t.cs() == "pagestyle") {
2247 h_paperpagestyle = p.verbatim_item();
2251 if (t.cs() == "setdefaultlanguage") {
2253 // We don't yet care about non-language variant options
2254 // because LyX doesn't support this yet, see bug #8214
2256 string langopts = p.getOpt();
2257 // check if the option contains a variant, if yes, extract it
2258 string::size_type pos_var = langopts.find("variant");
2259 string::size_type i = langopts.find(',', pos_var);
2260 string::size_type k = langopts.find('=', pos_var);
2261 if (pos_var != string::npos){
2263 if (i == string::npos)
2264 variant = langopts.substr(k + 1, langopts.length() - k - 2);
2266 variant = langopts.substr(k + 1, i - k - 1);
2267 h_language = variant;
2271 h_language = p.verbatim_item();
2272 //finally translate the poyglossia name to a LyX name
2273 h_language = polyglossia2lyx(h_language);
2277 if (t.cs() == "setotherlanguage") {
2278 // We don't yet care about the option because LyX doesn't
2279 // support this yet, see bug #8214
2280 p.hasOpt() ? p.getOpt() : string();
2285 if (t.cs() == "setmainfont") {
2286 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2287 h_font_roman[1] = p.getArg('{', '}');
2288 if (!fontopts.empty()) {
2289 vector<string> opts = getVectorFromString(fontopts);
2291 for (auto const & opt : opts) {
2292 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2295 if (!fontopts.empty())
2299 h_font_roman_opts = fontopts;
2304 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
2305 // LyX currently only supports the scale option
2306 string scale, fontopts;
2308 fontopts = p.getArg('[', ']');
2309 if (!fontopts.empty()) {
2310 vector<string> opts = getVectorFromString(fontopts);
2312 for (auto const & opt : opts) {
2313 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2316 if (prefixIs(opt, "Scale=")) {
2317 scale_as_percentage(opt, scale);
2320 if (!fontopts.empty())
2326 if (t.cs() == "setsansfont") {
2328 h_font_sf_scale[1] = scale;
2329 h_font_sans[1] = p.getArg('{', '}');
2330 if (!fontopts.empty())
2331 h_font_sans_opts = fontopts;
2334 h_font_tt_scale[1] = scale;
2335 h_font_typewriter[1] = p.getArg('{', '}');
2336 if (!fontopts.empty())
2337 h_font_typewriter_opts = fontopts;
2342 if (t.cs() == "babelfont") {
2344 h_use_non_tex_fonts = true;
2345 h_language_package = "babel";
2346 if (h_inputencoding == "auto-legacy")
2347 p.setEncoding("UTF-8");
2348 // we don't care about the lang option
2349 string const lang = p.hasOpt() ? p.getArg('[', ']') : string();
2350 string const family = p.getArg('{', '}');
2351 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2352 string const fontname = p.getArg('{', '}');
2353 if (lang.empty() && family == "rm") {
2354 h_font_roman[1] = fontname;
2355 if (!fontopts.empty()) {
2356 vector<string> opts = getVectorFromString(fontopts);
2358 for (auto const & opt : opts) {
2359 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2362 if (!fontopts.empty())
2366 h_font_roman_opts = fontopts;
2369 } else if (lang.empty() && (family == "sf" || family == "tt")) {
2371 if (!fontopts.empty()) {
2372 vector<string> opts = getVectorFromString(fontopts);
2374 for (auto const & opt : opts) {
2375 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2378 if (prefixIs(opt, "Scale=")) {
2379 scale_as_percentage(opt, scale);
2382 if (!fontopts.empty())
2387 if (family == "sf") {
2389 h_font_sf_scale[1] = scale;
2390 h_font_sans[1] = fontname;
2391 if (!fontopts.empty())
2392 h_font_sans_opts = fontopts;
2395 h_font_tt_scale[1] = scale;
2396 h_font_typewriter[1] = fontname;
2397 if (!fontopts.empty())
2398 h_font_typewriter_opts = fontopts;
2402 // not rm, sf or tt or lang specific
2403 h_preamble << '\\' << t.cs();
2405 h_preamble << '[' << lang << ']';
2406 h_preamble << '{' << family << '}';
2407 if (!fontopts.empty())
2408 h_preamble << '[' << fontopts << ']';
2409 h_preamble << '{' << fontname << '}' << '\n';
2414 if (t.cs() == "date") {
2415 string argument = p.getArg('{', '}');
2416 if (argument.empty())
2417 h_suppress_date = "true";
2419 h_preamble << t.asInput() << '{' << argument << '}';
2423 if (t.cs() == "color") {
2424 string const space =
2425 (p.hasOpt() ? p.getOpt() : string());
2426 string argument = p.getArg('{', '}');
2427 // check the case that a standard color is used
2428 if (space.empty() && is_known(argument, known_basic_colors)) {
2429 h_fontcolor = rgbcolor2code(argument);
2430 registerAutomaticallyLoadedPackage("color");
2431 } else if (space.empty() && argument == "document_fontcolor")
2432 registerAutomaticallyLoadedPackage("color");
2433 // check the case that LyX's document_fontcolor is defined
2434 // but not used for \color
2436 h_preamble << t.asInput();
2438 h_preamble << space;
2439 h_preamble << '{' << argument << '}';
2440 // the color might already be set because \definecolor
2441 // is parsed before this
2447 if (t.cs() == "pagecolor") {
2448 string argument = p.getArg('{', '}');
2449 // check the case that a standard color is used
2450 if (is_known(argument, known_basic_colors)) {
2451 h_backgroundcolor = rgbcolor2code(argument);
2452 } else if (argument == "page_backgroundcolor")
2453 registerAutomaticallyLoadedPackage("color");
2454 // check the case that LyX's page_backgroundcolor is defined
2455 // but not used for \pagecolor
2457 h_preamble << t.asInput() << '{' << argument << '}';
2458 // the color might already be set because \definecolor
2459 // is parsed before this
2460 h_backgroundcolor = "";
2465 if (t.cs() == "makeatletter") {
2466 // LyX takes care of this
2467 p.setCatcode('@', catLetter);
2471 if (t.cs() == "makeatother") {
2472 // LyX takes care of this
2473 p.setCatcode('@', catOther);
2477 if (t.cs() == "makeindex") {
2478 // LyX will re-add this if a print index command is found
2483 if (t.cs() == "newindex") {
2484 string const indexname = p.getArg('[', ']');
2485 string const shortcut = p.verbatim_item();
2486 if (!indexname.empty())
2487 h_index[index_number] = indexname;
2489 h_index[index_number] = shortcut;
2490 h_shortcut[index_number] = shortcut;
2496 if (t.cs() == "addbibresource") {
2497 string const options = p.getArg('[', ']');
2498 string const arg = removeExtension(p.getArg('{', '}'));
2499 if (!options.empty()) {
2500 // check if the option contains a bibencoding, if yes, extract it
2501 string::size_type pos = options.find("bibencoding=");
2503 if (pos != string::npos) {
2504 string::size_type i = options.find(',', pos);
2505 if (i == string::npos)
2506 encoding = options.substr(pos + 1);
2508 encoding = options.substr(pos, i - pos);
2509 pos = encoding.find('=');
2510 if (pos == string::npos)
2513 encoding = encoding.substr(pos + 1);
2515 if (!encoding.empty())
2516 biblatex_encodings.push_back(normalize_filename(arg) + ' ' + encoding);
2518 biblatex_bibliographies.push_back(arg);
2522 if (t.cs() == "bibliography") {
2523 vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
2524 biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
2528 if (t.cs() == "RS@ifundefined") {
2529 string const name = p.verbatim_item();
2530 string const body1 = p.verbatim_item();
2531 string const body2 = p.verbatim_item();
2532 // only non-lyxspecific stuff
2533 if (in_lyx_preamble &&
2534 (name == "subsecref" || name == "thmref" || name == "lemref"))
2538 ss << '\\' << t.cs();
2539 ss << '{' << name << '}'
2540 << '{' << body1 << '}'
2541 << '{' << body2 << '}';
2542 h_preamble << ss.str();
2547 if (t.cs() == "AtBeginDocument") {
2548 string const name = p.verbatim_item();
2549 // only non-lyxspecific stuff
2550 if (in_lyx_preamble &&
2551 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
2552 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
2553 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
2554 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
2555 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
2556 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
2557 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
2558 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
2559 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
2560 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
2561 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
2562 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
2563 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
2564 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
2565 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
2569 ss << '\\' << t.cs();
2570 ss << '{' << name << '}';
2571 h_preamble << ss.str();
2576 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
2577 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
2578 || t.cs() == "providecommand" || t.cs() == "providecommandx"
2579 || t.cs() == "DeclareRobustCommand"
2580 || t.cs() == "DeclareRobustCommandx"
2581 || t.cs() == "ProvideTextCommandDefault"
2582 || t.cs() == "DeclareMathAccent") {
2584 if (p.next_token().character() == '*') {
2588 string const name = p.verbatim_item();
2589 string const opt1 = p.getFullOpt();
2590 string const opt2 = p.getFullOpt();
2591 string const body = p.verbatim_item();
2592 // store the in_lyx_preamble setting
2593 bool const was_in_lyx_preamble = in_lyx_preamble;
2595 if (name == "\\rmdefault")
2596 if (is_known(body, known_roman_font_packages)) {
2597 h_font_roman[0] = body;
2599 in_lyx_preamble = true;
2601 if (name == "\\sfdefault") {
2602 if (is_known(body, known_sans_font_packages)) {
2603 h_font_sans[0] = body;
2605 in_lyx_preamble = true;
2607 if (body == "LibertinusSans-OsF") {
2608 h_font_sans[0] = "LibertinusSans-LF";
2609 h_font_sans_osf = "true";
2611 in_lyx_preamble = true;
2614 if (name == "\\ttdefault")
2615 if (is_known(body, known_typewriter_font_packages)) {
2616 h_font_typewriter[0] = body;
2618 in_lyx_preamble = true;
2620 if (name == "\\familydefault") {
2621 string family = body;
2622 // remove leading "\"
2623 h_font_default_family = family.erase(0,1);
2625 in_lyx_preamble = true;
2627 if (name == "\\LibertinusSans@scale") {
2628 if (isStrDbl(body)) {
2629 h_font_sf_scale[0] = convert<string>(
2630 static_cast<int>(100 * convert<double>(body)));
2633 if (name == "\\LibertinusMono@scale") {
2634 if (isStrDbl(body)) {
2635 h_font_tt_scale[0] = convert<string>(
2636 static_cast<int>(100 * convert<double>(body)));
2640 // remove LyX-specific definitions that are re-added by LyX
2642 // \lyxline is an ancient command that is converted by tex2lyx into
2643 // a \rule therefore remove its preamble code
2644 if (name == "\\lyxdot" || name == "\\lyxarrow"
2645 || name == "\\lyxline" || name == "\\LyX") {
2647 in_lyx_preamble = true;
2650 // Add the command to the known commands
2651 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
2653 // only non-lyxspecific stuff
2654 if (!in_lyx_preamble) {
2656 ss << '\\' << t.cs();
2659 ss << '{' << name << '}' << opt1 << opt2
2660 << '{' << body << "}";
2661 if (prefixIs(t.cs(), "renew") || !contains(h_preamble.str(), ss.str()))
2662 h_preamble << ss.str();
2664 ostream & out = in_preamble ? h_preamble : os;
2665 out << "\\" << t.cs() << "{" << name << "}"
2666 << opts << "{" << body << "}";
2669 // restore the in_lyx_preamble setting
2670 in_lyx_preamble = was_in_lyx_preamble;
2674 if (t.cs() == "documentclass") {
2675 vector<string>::iterator it;
2676 vector<string> opts = split_options(p.getArg('[', ']'));
2677 // FIXME This does not work for classes that have a
2678 // different name in LyX than in LaTeX
2679 string const tclass = p.getArg('{', '}');
2681 // Only set text class if a class hasn't been forced
2682 // (this was set above)
2684 // textclass needs to be set at this place (if not already done)
2685 // as we need to know it for other parameters
2686 // (such as class-dependent paper size)
2687 setTextClass(tclass, tc);
2692 // Try those who are (most likely) known to all packages first
2693 handle_opt(opts, known_fontsizes, h_paperfontsize);
2694 delete_opt(opts, known_fontsizes);
2695 // delete "pt" at the end
2696 string::size_type i = h_paperfontsize.find("pt");
2697 if (i != string::npos)
2698 h_paperfontsize.erase(i);
2699 // Now those known specifically to the class
2700 vector<string> class_fsizes = getVectorFromString(tc.opt_fontsize(), "|");
2701 string const fsize_format = tc.fontsizeformat();
2702 for (auto const & fsize : class_fsizes) {
2703 string latexsize = subst(fsize_format, "$$s", fsize);
2704 vector<string>::iterator it = find(opts.begin(), opts.end(), latexsize);
2705 if (it != opts.end()) {
2706 h_paperfontsize = fsize;
2712 // The documentclass options are always parsed before the options
2713 // of the babel call so that a language cannot overwrite the babel
2715 handle_opt(opts, known_languages, h_language);
2716 delete_opt(opts, known_languages);
2719 if ((it = find(opts.begin(), opts.end(), "fleqn"))
2721 h_is_mathindent = "1";
2724 // formula numbering side
2725 if ((it = find(opts.begin(), opts.end(), "leqno"))
2727 h_math_numbering_side = "left";
2730 else if ((it = find(opts.begin(), opts.end(), "reqno"))
2732 h_math_numbering_side = "right";
2736 // paper orientation
2737 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
2738 h_paperorientation = "landscape";
2742 if ((it = find(opts.begin(), opts.end(), "oneside"))
2747 if ((it = find(opts.begin(), opts.end(), "twoside"))
2753 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
2755 h_papercolumns = "1";
2758 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
2760 h_papercolumns = "2";
2764 // some size options are known by the document class, other sizes
2765 // are handled by the \geometry command of the geometry package
2766 vector<string> class_psizes = getVectorFromString(tc.opt_pagesize(), "|");
2767 string const psize_format = tc.pagesizeformat();
2768 for (auto const & psize : class_psizes) {
2769 string latexsize = subst(psize_format, "$$s", psize);
2770 vector<string>::iterator it = find(opts.begin(), opts.end(), latexsize);
2771 if (it != opts.end()) {
2772 h_papersize = psize;
2776 if (psize_format == "$$spaper")
2778 // Also try with the default format since this is understood by
2780 latexsize = psize + "paper";
2781 it = find(opts.begin(), opts.end(), latexsize);
2782 if (it != opts.end()) {
2783 h_papersize = psize;
2788 // the remaining options
2789 h_options = join(opts, ",");
2793 if (t.cs() == "usepackage") {
2794 string const options = p.getArg('[', ']');
2795 string const name = p.getArg('{', '}');
2796 vector<string> vecnames;
2797 split(name, vecnames, ',');
2798 vector<string>::const_iterator it = vecnames.begin();
2799 vector<string>::const_iterator end = vecnames.end();
2800 for (; it != end; ++it)
2801 handle_package(p, trimSpaceAndEol(*it), options,
2802 in_lyx_preamble, detectEncoding);
2806 if (t.cs() == "inputencoding") {
2807 string const encoding = p.getArg('{','}');
2808 Encoding const * const enc = encodings.fromLaTeXName(
2809 encoding, Encoding::inputenc, true);
2811 if (!detectEncoding)
2812 cerr << "Unknown encoding " << encoding
2813 << ". Ignoring." << std::endl;
2816 h_inputencoding = enc->name();
2817 p.setEncoding(enc->iconvName());
2822 if (t.cs() == "newenvironment") {
2823 string const name = p.getArg('{', '}');
2824 string const opt1 = p.getFullOpt();
2825 string const opt2 = p.getFullOpt();
2826 string const beg = p.verbatim_item();
2827 string const end = p.verbatim_item();
2828 if (!in_lyx_preamble) {
2829 h_preamble << "\\newenvironment{" << name
2830 << '}' << opt1 << opt2 << '{'
2831 << beg << "}{" << end << '}';
2833 add_known_environment(name, opt1, !opt2.empty(),
2834 from_utf8(beg), from_utf8(end));
2838 if (t.cs() == "newtheorem") {
2840 if (p.next_token().character() == '*') {
2844 string const name = p.getArg('{', '}');
2845 string const opt1 = p.getFullOpt();
2846 string const opt2 = p.getFullOpt();
2847 string const body = p.verbatim_item();
2848 string const opt3 = p.getFullOpt();
2849 string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
2851 string const complete = cmd + "{" + name + '}' +
2852 opt1 + opt2 + '{' + body + '}' + opt3;
2854 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
2856 if (!in_lyx_preamble)
2857 h_preamble << complete;
2861 if (t.cs() == "def") {
2862 string name = p.get_token().cs();
2863 // In fact, name may be more than the name:
2864 // In the test case of bug 8116
2865 // name == "csname SF@gobble@opt \endcsname".
2866 // Therefore, we need to use asInput() instead of cs().
2867 while (p.next_token().cat() != catBegin)
2868 name += p.get_token().asInput();
2869 if (!in_lyx_preamble)
2870 h_preamble << "\\def\\" << name << '{'
2871 << p.verbatim_item() << "}";
2875 if (t.cs() == "newcolumntype") {
2876 string const name = p.getArg('{', '}');
2877 trimSpaceAndEol(name);
2879 string opts = p.getOpt();
2880 if (!opts.empty()) {
2881 istringstream is(string(opts, 1));
2884 special_columns_[name[0]] = nargs;
2885 h_preamble << "\\newcolumntype{" << name << "}";
2887 h_preamble << "[" << nargs << "]";
2888 h_preamble << "{" << p.verbatim_item() << "}";
2892 if (t.cs() == "setcounter") {
2893 string const name = p.getArg('{', '}');
2894 string const content = p.getArg('{', '}');
2895 if (name == "secnumdepth")
2896 h_secnumdepth = content;
2897 else if (name == "tocdepth")
2898 h_tocdepth = content;
2900 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
2904 if (t.cs() == "setlength") {
2905 string const name = p.verbatim_item();
2906 string const content = p.verbatim_item();
2907 // the paragraphs are only not indented when \parindent is set to zero
2908 if (name == "\\parindent" && content != "")
2909 h_paragraph_indentation = translate_len(content);
2910 else if (name == "\\parskip" && isPackageUsed("parskip")) {
2911 if (content == "\\smallskipamount")
2912 h_defskip = "smallskip";
2913 else if (content == "\\medskipamount")
2914 h_defskip = "medskip";
2915 else if (content == "\\bigskipamount")
2916 h_defskip = "bigskip";
2917 else if (content == "\\baselineskip")
2918 h_defskip = "fullline";
2920 h_defskip = translate_len(content);
2921 } else if (name == "\\mathindent") {
2922 h_mathindentation = translate_len(content);
2924 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2928 if (t.cs() == "onehalfspacing") {
2929 h_spacing = "onehalf";
2933 if (t.cs() == "doublespacing") {
2934 h_spacing = "double";
2938 if (t.cs() == "setstretch") {
2939 h_spacing = "other " + p.verbatim_item();
2943 if (t.cs() == "synctex") {
2944 // the scheme is \synctex=value
2945 // where value can only be "1" or "-1"
2946 h_output_sync = "1";
2947 // there can be any character behind the value (e.g. a linebreak or a '\'
2948 // therefore we extract it char by char
2950 string value = p.get_token().asInput();
2952 value += p.get_token().asInput();
2953 h_output_sync_macro = "\\synctex=" + value;
2957 if (t.cs() == "begin") {
2958 string const name = p.getArg('{', '}');
2959 if (name == "document")
2961 h_preamble << "\\begin{" << name << "}";
2965 if (t.cs() == "geometry") {
2966 vector<string> opts = split_options(p.getArg('{', '}'));
2967 handle_geometry(opts);
2971 if (t.cs() == "definecolor") {
2972 string const color = p.getArg('{', '}');
2973 string const space = p.getArg('{', '}');
2974 string const value = p.getArg('{', '}');
2975 if (color == "document_fontcolor" && space == "rgb") {
2976 RGBColor c(RGBColorFromLaTeX(value));
2977 h_fontcolor = X11hexname(c);
2978 } else if (color == "note_fontcolor" && space == "rgb") {
2979 RGBColor c(RGBColorFromLaTeX(value));
2980 h_notefontcolor = X11hexname(c);
2981 } else if (color == "page_backgroundcolor" && space == "rgb") {
2982 RGBColor c(RGBColorFromLaTeX(value));
2983 h_backgroundcolor = X11hexname(c);
2984 } else if (color == "shadecolor" && space == "rgb") {
2985 RGBColor c(RGBColorFromLaTeX(value));
2986 h_boxbgcolor = X11hexname(c);
2988 h_preamble << "\\definecolor{" << color
2989 << "}{" << space << "}{" << value
2995 if (t.cs() == "bibliographystyle") {
2996 h_biblio_style = p.verbatim_item();
3000 if (t.cs() == "jurabibsetup") {
3001 // FIXME p.getArg('{', '}') is most probably wrong (it
3002 // does not handle nested braces).
3003 // Use p.verbatim_item() instead.
3004 vector<string> jurabibsetup =
3005 split_options(p.getArg('{', '}'));
3006 // add jurabibsetup to the jurabib package options
3007 add_package("jurabib", jurabibsetup);
3008 if (!jurabibsetup.empty()) {
3009 h_preamble << "\\jurabibsetup{"
3010 << join(jurabibsetup, ",") << '}';
3015 if (t.cs() == "hypersetup") {
3016 vector<string> hypersetup =
3017 split_options(p.verbatim_item());
3018 // add hypersetup to the hyperref package options
3019 handle_hyperref(hypersetup);
3020 if (!hypersetup.empty()) {
3021 h_preamble << "\\hypersetup{"
3022 << join(hypersetup, ",") << '}';
3027 if (t.cs() == "includeonly") {
3028 vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
3029 for (auto & iofile : includeonlys) {
3030 string filename(normalize_filename(iofile));
3031 string const path = getMasterFilePath(true);
3032 // We want to preserve relative/absolute filenames,
3033 // therefore path is only used for testing
3034 if (!makeAbsPath(filename, path).exists()) {
3035 // The file extension is probably missing.
3036 // Now try to find it out.
3037 string const tex_name =
3038 find_file(filename, path,
3039 known_tex_extensions);
3040 if (!tex_name.empty())
3041 filename = tex_name;
3044 if (makeAbsPath(filename, path).exists())
3045 fix_child_filename(filename);
3047 cerr << "Warning: Could not find included file '"
3048 << filename << "'." << endl;
3049 outname = changeExtension(filename, "lyx");
3050 h_includeonlys.push_back(outname);
3055 if (is_known(t.cs(), known_if_3arg_commands)) {
3056 // prevent misparsing of \usepackage if it is used
3057 // as an argument (see e.g. our own output of
3058 // \@ifundefined above)
3059 string const arg1 = p.verbatim_item();
3060 string const arg2 = p.verbatim_item();
3061 string const arg3 = p.verbatim_item();
3062 // test case \@ifundefined{date}{}{\date{}}
3063 if (t.cs() == "@ifundefined" && arg1 == "date" &&
3064 arg2.empty() && arg3 == "\\date{}") {
3065 h_suppress_date = "true";
3066 // older tex2lyx versions did output
3067 // \@ifundefined{definecolor}{\usepackage{color}}{}
3068 } else if (t.cs() == "@ifundefined" &&
3069 arg1 == "definecolor" &&
3070 arg2 == "\\usepackage{color}" &&
3072 if (!in_lyx_preamble)
3073 h_preamble << package_beg_sep
3076 << "\\@ifundefined{definecolor}{color}{}"
3079 //\@ifundefined{showcaptionsetup}{}{%
3080 // \PassOptionsToPackage{caption=false}{subfig}}
3081 // that LyX uses for subfloats
3082 } else if (t.cs() == "@ifundefined" &&
3083 arg1 == "showcaptionsetup" && arg2.empty()
3084 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
3086 } else if (!in_lyx_preamble) {
3087 h_preamble << t.asInput()
3088 << '{' << arg1 << '}'
3089 << '{' << arg2 << '}'
3090 << '{' << arg3 << '}';
3095 if (is_known(t.cs(), known_if_commands)) {
3096 // must not parse anything in conditional code, since
3097 // LyX would output the parsed contents unconditionally
3098 if (!in_lyx_preamble)
3099 h_preamble << t.asInput();
3100 handle_if(p, in_lyx_preamble);
3104 if (!t.cs().empty() && !in_lyx_preamble) {
3105 h_preamble << '\\' << t.cs();
3110 // set textclass if not yet done (snippets without \documentclass and forced class)
3112 setTextClass(h_textclass, tc);
3114 // remove the whitespace
3117 if (h_papersides.empty()) {
3120 h_papersides = ss.str();
3123 // If the CJK package is used we cannot set the document language from
3124 // the babel options. Instead, we guess which language is used most
3125 // and set this one.
3126 default_language = h_language;
3127 if (is_full_document &&
3128 (auto_packages.find("CJK") != auto_packages.end() ||
3129 auto_packages.find("CJKutf8") != auto_packages.end())) {
3131 h_language = guessLanguage(p, default_language);
3133 if (explicit_babel && h_language != default_language) {
3134 // We set the document language to a CJK language,
3135 // but babel is explicitly called in the user preamble
3136 // without options. LyX will not add the default
3137 // language to the document options if it is either
3138 // english, or no text is set as default language.
3139 // Therefore we need to add a language option explicitly.
3140 // FIXME: It would be better to remove all babel calls
3141 // from the user preamble, but this is difficult
3142 // without re-introducing bug 7861.
3143 if (h_options.empty())
3144 h_options = lyx2babel(default_language);
3146 h_options += ',' + lyx2babel(default_language);
3150 // Finally, set the quote style.
3151 // LyX knows the following quotes styles:
3152 // british, cjk, cjkangle, danish, english, french, german,
3153 // polish, russian, swedish and swiss
3154 // conversion list taken from
3155 // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
3156 // (quotes for kazakh are unknown)
3158 if (is_known(h_language, known_british_quotes_languages))
3159 h_quotes_style = "british";
3161 else if (is_known(h_language, known_cjk_quotes_languages))
3162 h_quotes_style = "cjk";
3164 else if (is_known(h_language, known_cjkangle_quotes_languages))
3165 h_quotes_style = "cjkangle";
3167 else if (is_known(h_language, known_danish_quotes_languages))
3168 h_quotes_style = "danish";
3170 else if (is_known(h_language, known_french_quotes_languages))
3171 h_quotes_style = "french";
3173 else if (is_known(h_language, known_german_quotes_languages))
3174 h_quotes_style = "german";
3176 else if (is_known(h_language, known_polish_quotes_languages))
3177 h_quotes_style = "polish";
3179 else if (is_known(h_language, known_russian_quotes_languages))
3180 h_quotes_style = "russian";
3182 else if (is_known(h_language, known_swedish_quotes_languages))
3183 h_quotes_style = "swedish";
3185 else if (is_known(h_language, known_swiss_quotes_languages))
3186 h_quotes_style = "swiss";
3188 else if (is_known(h_language, known_english_quotes_languages))
3189 h_quotes_style = "english";
3193 string Preamble::parseEncoding(Parser & p, string const & forceclass)
3195 TeX2LyXDocClass dummy;
3196 parse(p, forceclass, true, dummy);
3197 if (h_inputencoding != "auto-legacy" && h_inputencoding != "auto-legacy-plain")
3198 return h_inputencoding;
3203 string babel2lyx(string const & language)
3205 char const * const * where = is_known(language, known_languages);
3207 return known_coded_languages[where - known_languages];
3212 string lyx2babel(string const & language)
3214 char const * const * where = is_known(language, known_coded_languages);
3216 return known_languages[where - known_coded_languages];
3221 string Preamble::polyglossia2lyx(string const & language)
3223 char const * const * where = is_known(language, polyglossia_languages);
3225 return coded_polyglossia_languages[where - polyglossia_languages];
3230 string rgbcolor2code(string const & name)
3232 char const * const * where = is_known(name, known_basic_colors);
3234 // "red", "green" etc
3235 return known_basic_color_codes[where - known_basic_colors];
3237 // "255,0,0", "0,255,0" etc
3238 RGBColor c(RGBColorFromLaTeX(name));
3239 return X11hexname(c);