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", "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", "lmodern",
157 "mathpazo", "mathptmx", "newcent", "noto-mono", "PTMono", "tgcursor", "txtt", 0 };
159 const char * const known_math_font_packages[] = { "eulervm", "newtxmath", 0};
161 const char * const known_latex_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
162 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
163 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
164 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
165 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
167 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
168 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
170 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
171 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
174 /// commands that can start an \if...\else...\endif sequence
175 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
176 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
177 "ifsidecap", "ifupgreek", 0};
179 const char * const known_basic_colors[] = {"black", "blue", "brown", "cyan",
180 "darkgray", "gray", "green", "lightgray", "lime", "magenta", "orange", "olive",
181 "pink", "purple", "red", "teal", "violet", "white", "yellow", 0};
183 const char * const known_basic_color_codes[] = {"#000000", "#0000ff", "#964B00", "#00ffff",
184 "#a9a9a9", "#808080", "#00ff00", "#d3d3d3", "#bfff00", "#ff00ff", "#ff7f00", "#808000",
185 "#ffc0cb", "#800080", "#ff0000", "#008080", "#8f00ff", "#ffffff", "#ffff00", 0};
187 /// conditional commands with three arguments like \@ifundefined{}{}{}
188 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
192 * Known file extensions for TeX files as used by \\includeonly
194 char const * const known_tex_extensions[] = {"tex", 0};
196 /// packages that work only in xetex
197 /// polyglossia is handled separately
198 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
199 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
200 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
202 /// packages that are automatically skipped if loaded by LyX
203 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
204 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color",
205 "float", "fontspec", "framed", "graphicx", "hhline", "ifthen", "longtable",
206 "makeidx", "minted", "multirow", "nomencl", "parskip", "pdfpages", "prettyref", "refstyle",
207 "rotating", "rotfloat", "splitidx", "setspace", "subscript", "tabularx","textcomp", "tipa",
208 "tipx", "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xcolor", "xltabular",
211 // codes used to remove packages that are loaded automatically by LyX.
212 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
213 const char package_beg_sep = '\001';
214 const char package_mid_sep = '\002';
215 const char package_end_sep = '\003';
218 // returns true if at least one of the options in what has been found
219 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
225 // the last language option is the document language (for babel and LyX)
226 // the last size option is the document font size
227 vector<string>::iterator it;
228 vector<string>::iterator position = opts.begin();
229 for (; *what; ++what) {
230 it = find(opts.begin(), opts.end(), *what);
231 if (it != opts.end()) {
232 if (it >= position) {
243 void delete_opt(vector<string> & opts, char const * const * what)
248 // remove found options from the list
249 // do this after handle_opt to avoid potential memory leaks
250 vector<string>::iterator it;
251 for (; *what; ++what) {
252 it = find(opts.begin(), opts.end(), *what);
253 if (it != opts.end())
260 * Split a package options string (keyval format) into a vector.
262 * authorformat=smallcaps,
264 * titleformat=colonsep,
265 * bibformat={tabular,ibidem,numbered}
267 vector<string> split_options(string const & input)
269 vector<string> options;
273 Token const & t = p.get_token();
274 if (t.asInput() == ",") {
275 options.push_back(trimSpaceAndEol(option));
277 } else if (t.asInput() == "=") {
280 if (p.next_token().asInput() == "{")
281 option += '{' + p.getArg('{', '}') + '}';
282 } else if (t.cat() != catSpace && t.cat() != catComment)
283 option += t.asInput();
287 options.push_back(trimSpaceAndEol(option));
294 * Retrieve a keyval option "name={value with=sign}" named \p name from
295 * \p options and return the value.
296 * The found option is also removed from \p options.
298 string process_keyval_opt(vector<string> & options, string name)
300 for (size_t i = 0; i < options.size(); ++i) {
301 vector<string> option;
302 split(options[i], option, '=');
303 if (option.size() < 2)
305 if (option[0] == name) {
306 options.erase(options.begin() + i);
307 option.erase(option.begin());
308 return join(option, "=");
314 } // anonymous namespace
318 * known polyglossia language names (including variants)
319 * FIXME: support spelling=old for german variants (german vs. ngerman LyX names etc)
321 const char * const Preamble::polyglossia_languages[] = {
322 "albanian", "american", "amharic", "ancient", "arabic", "armenian", "asturian", "australian",
323 "bahasai", "bahasam", "basque", "bengali", "brazil", "brazilian", "breton", "british", "bulgarian",
324 "catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
325 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
326 "galician", "greek", "monotonic", "hebrew", "hindi",
327 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean",
328 "lao", "latin", "latvian", "lithuanian", "lsorbian", "magyar", "malayalam", "marathi",
329 "austrian", "newzealand", "german", "norsk", "nynorsk", "occitan", "oldrussian",
330 "piedmontese", "polish", "polytonic", "portuges", "romanian", "romansh", "russian",
331 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovenian", "spanish", "swedish", "syriac",
332 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
333 "ukrainian", "urdu", "usorbian", "vietnamese", "welsh", 0};
334 // not yet supported by LyX: "korean", "nko"
337 * the same as polyglossia_languages with .lyx names
338 * please keep this in sync with polyglossia_languages line by line!
340 const char * const Preamble::coded_polyglossia_languages[] = {
341 "albanian", "american", "amharic", "ancientgreek", "arabic_arabi", "armenian", "asturian", "australian",
342 "bahasa", "bahasam", "basque", "bengali", "brazilian", "brazilian", "breton", "british", "bulgarian",
343 "catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
344 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
345 "galician", "greek", "greek", "hebrew", "hindi",
346 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean",
347 "lao", "latin", "latvian", "lithuanian", "lowersorbian", "magyar", "malayalam", "marathi",
348 "naustrian","newzealand", "ngerman", "norsk", "nynorsk", "occitan", "oldrussian",
349 "piedmontese", "polish", "polutonikogreek", "portuges", "romanian", "romansh", "russian",
350 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovene", "spanish", "swedish", "syriac",
351 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
352 "ukrainian", "urdu", "uppersorbian", "vietnamese", "welsh", 0};
353 // not yet supported by LyX: "korean-polyglossia", "nko"
356 bool Preamble::usePolyglossia() const
358 return h_use_non_tex_fonts && h_language_package == "default";
362 bool Preamble::indentParagraphs() const
364 return h_paragraph_separation == "indent";
368 bool Preamble::isPackageUsed(string const & package) const
370 return used_packages.find(package) != used_packages.end();
374 bool Preamble::isPackageAutoLoaded(string const & package) const
376 return auto_packages.find(package) != auto_packages.end();
380 vector<string> Preamble::getPackageOptions(string const & package) const
382 map<string, vector<string> >::const_iterator it = used_packages.find(package);
383 if (it != used_packages.end())
385 return vector<string>();
389 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
391 auto_packages.insert(package);
395 void Preamble::addModule(string const & module)
397 for (auto const & m : used_modules) {
401 used_modules.push_back(module);
405 void Preamble::suppressDate(bool suppress)
408 h_suppress_date = "true";
410 h_suppress_date = "false";
414 void Preamble::registerAuthor(std::string const & name, string const & initials)
416 Author author(from_utf8(name), empty_docstring(), from_utf8(initials));
417 author.setUsed(true);
418 authors_.record(author);
419 h_tracking_changes = "true";
420 h_output_changes = "true";
424 Author const & Preamble::getAuthor(std::string const & name) const
426 Author author(from_utf8(name), empty_docstring(), empty_docstring());
427 for (AuthorList::Authors::const_iterator it = authors_.begin();
428 it != authors_.end(); ++it)
431 static Author const dummy;
436 int Preamble::getSpecialTableColumnArguments(char c) const
438 map<char, int>::const_iterator it = special_columns_.find(c);
439 if (it == special_columns_.end())
445 void Preamble::add_package(string const & name, vector<string> & options)
447 // every package inherits the global options
448 if (used_packages.find(name) == used_packages.end())
449 used_packages[name] = split_options(h_options);
451 // Insert options passed via PassOptionsToPackage
452 for (auto const & p : extra_package_options_) {
453 if (p.first == name) {
454 vector<string> eo = getVectorFromString(p.second);
455 for (auto const & eoi : eo)
456 options.push_back(eoi);
460 vector<string> & v = used_packages[name];
461 v.insert(v.end(), options.begin(), options.end());
462 if (name == "jurabib") {
463 // Don't output the order argument (see the cite command
464 // handling code in text.cpp).
465 vector<string>::iterator end =
466 remove(options.begin(), options.end(), "natbiborder");
467 end = remove(options.begin(), end, "jurabiborder");
468 options.erase(end, options.end());
475 // Given is a string like "scaled=0.9" or "scale=0.9", return 0.9 * 100
476 bool scale_as_percentage(string const & scale, string & percentage)
478 if (contains(scale, '=')) {
479 string const value = support::split(scale, '=');
480 if (isStrDbl(value)) {
481 percentage = convert<string>(
482 static_cast<int>(100 * convert<double>(value)));
490 string remove_braces(string const & value)
494 if (value[0] == '{' && value[value.length()-1] == '}')
495 return value.substr(1, value.length()-2);
499 } // anonymous namespace
502 Preamble::Preamble() : one_language(true), explicit_babel(false),
503 title_layout_found(false), index_number(0), h_font_cjk_set(false)
507 h_biblio_style = "plain";
508 h_bibtex_command = "default";
509 h_cite_engine = "basic";
510 h_cite_engine_type = "default";
512 h_defskip = "medskip";
513 h_dynamic_quotes = false;
516 h_fontencoding = "default";
517 h_font_roman[0] = "default";
518 h_font_roman[1] = "default";
519 h_font_sans[0] = "default";
520 h_font_sans[1] = "default";
521 h_font_typewriter[0] = "default";
522 h_font_typewriter[1] = "default";
523 h_font_math[0] = "auto";
524 h_font_math[1] = "auto";
525 h_font_default_family = "default";
526 h_use_non_tex_fonts = false;
528 h_font_roman_osf = "false";
529 h_font_sans_osf = "false";
530 h_font_typewriter_osf = "false";
531 h_font_sf_scale[0] = "100";
532 h_font_sf_scale[1] = "100";
533 h_font_tt_scale[0] = "100";
534 h_font_tt_scale[1] = "100";
535 // h_font_roman_opts;
537 // h_font_typewriter_opts;
539 h_is_mathindent = "0";
540 h_math_numbering_side = "default";
541 h_graphics = "default";
542 h_default_output_format = "default";
543 h_html_be_strict = "false";
544 h_html_css_as_file = "0";
545 h_html_math_output = "0";
546 h_index[0] = "Index";
547 h_index_command = "default";
548 h_inputencoding = "auto-legacy";
549 h_justification = "true";
550 h_language = "english";
551 h_language_package = "none";
553 h_maintain_unincluded_children = "no";
557 h_output_changes = "false";
558 h_change_bars = "false";
560 //h_output_sync_macro
561 h_papercolumns = "1";
562 h_paperfontsize = "default";
563 h_paperorientation = "portrait";
564 h_paperpagestyle = "default";
566 h_papersize = "default";
567 h_paragraph_indentation = "default";
568 h_paragraph_separation = "indent";
573 h_pdf_bookmarks = "0";
574 h_pdf_bookmarksnumbered = "0";
575 h_pdf_bookmarksopen = "0";
576 h_pdf_bookmarksopenlevel = "1";
577 h_pdf_breaklinks = "0";
578 h_pdf_pdfborder = "0";
579 h_pdf_colorlinks = "0";
580 h_pdf_backref = "section";
581 h_pdf_pdfusetitle = "0";
583 //h_pdf_quoted_options;
584 h_quotes_style = "english";
586 h_shortcut[0] = "idx";
587 h_spacing = "single";
588 h_save_transient_properties = "true";
589 h_suppress_date = "false";
590 h_textclass = "article";
592 h_tracking_changes = "false";
593 h_use_bibtopic = "false";
594 h_use_dash_ligatures = "true";
595 h_use_indices = "false";
596 h_use_geometry = "false";
597 h_use_default_options = "false";
598 h_use_hyperref = "false";
599 h_use_microtype = "false";
600 h_use_lineno = "false";
601 h_use_refstyle = false;
602 h_use_minted = false;
603 h_use_packages["amsmath"] = "1";
604 h_use_packages["amssymb"] = "0";
605 h_use_packages["cancel"] = "0";
606 h_use_packages["esint"] = "1";
607 h_use_packages["mhchem"] = "0";
608 h_use_packages["mathdots"] = "0";
609 h_use_packages["mathtools"] = "0";
610 h_use_packages["stackrel"] = "0";
611 h_use_packages["stmaryrd"] = "0";
612 h_use_packages["undertilde"] = "0";
616 void Preamble::handle_hyperref(vector<string> & options)
618 // FIXME swallow inputencoding changes that might surround the
619 // hyperref setup if it was written by LyX
620 h_use_hyperref = "true";
621 // swallow "unicode=true", since LyX does always write that
622 vector<string>::iterator it =
623 find(options.begin(), options.end(), "unicode=true");
624 if (it != options.end())
626 it = find(options.begin(), options.end(), "pdfusetitle");
627 if (it != options.end()) {
628 h_pdf_pdfusetitle = "1";
631 string bookmarks = process_keyval_opt(options, "bookmarks");
632 if (bookmarks == "true")
633 h_pdf_bookmarks = "1";
634 else if (bookmarks == "false")
635 h_pdf_bookmarks = "0";
636 if (h_pdf_bookmarks == "1") {
637 string bookmarksnumbered =
638 process_keyval_opt(options, "bookmarksnumbered");
639 if (bookmarksnumbered == "true")
640 h_pdf_bookmarksnumbered = "1";
641 else if (bookmarksnumbered == "false")
642 h_pdf_bookmarksnumbered = "0";
643 string bookmarksopen =
644 process_keyval_opt(options, "bookmarksopen");
645 if (bookmarksopen == "true")
646 h_pdf_bookmarksopen = "1";
647 else if (bookmarksopen == "false")
648 h_pdf_bookmarksopen = "0";
649 if (h_pdf_bookmarksopen == "1") {
650 string bookmarksopenlevel =
651 process_keyval_opt(options, "bookmarksopenlevel");
652 if (!bookmarksopenlevel.empty())
653 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
656 string breaklinks = process_keyval_opt(options, "breaklinks");
657 if (breaklinks == "true")
658 h_pdf_breaklinks = "1";
659 else if (breaklinks == "false")
660 h_pdf_breaklinks = "0";
661 string pdfborder = process_keyval_opt(options, "pdfborder");
662 if (pdfborder == "{0 0 0}")
663 h_pdf_pdfborder = "1";
664 else if (pdfborder == "{0 0 1}")
665 h_pdf_pdfborder = "0";
666 string backref = process_keyval_opt(options, "backref");
667 if (!backref.empty())
668 h_pdf_backref = backref;
669 string colorlinks = process_keyval_opt(options, "colorlinks");
670 if (colorlinks == "true")
671 h_pdf_colorlinks = "1";
672 else if (colorlinks == "false")
673 h_pdf_colorlinks = "0";
674 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
675 if (!pdfpagemode.empty())
676 h_pdf_pagemode = pdfpagemode;
677 string pdftitle = process_keyval_opt(options, "pdftitle");
678 if (!pdftitle.empty()) {
679 h_pdf_title = remove_braces(pdftitle);
681 string pdfauthor = process_keyval_opt(options, "pdfauthor");
682 if (!pdfauthor.empty()) {
683 h_pdf_author = remove_braces(pdfauthor);
685 string pdfsubject = process_keyval_opt(options, "pdfsubject");
686 if (!pdfsubject.empty())
687 h_pdf_subject = remove_braces(pdfsubject);
688 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
689 if (!pdfkeywords.empty())
690 h_pdf_keywords = remove_braces(pdfkeywords);
691 if (!options.empty()) {
692 if (!h_pdf_quoted_options.empty())
693 h_pdf_quoted_options += ',';
694 h_pdf_quoted_options += join(options, ",");
700 void Preamble::handle_geometry(vector<string> & options)
702 h_use_geometry = "true";
703 vector<string>::iterator it;
705 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
706 h_paperorientation = "landscape";
710 // keyval version: "paper=letter" or "paper=letterpaper"
711 string paper = process_keyval_opt(options, "paper");
713 if (suffixIs(paper, "paper"))
714 paper = subst(paper, "paper", "");
715 // alternative version: "letterpaper"
716 handle_opt(options, known_latex_paper_sizes, paper);
717 if (suffixIs(paper, "paper"))
718 paper = subst(paper, "paper", "");
719 delete_opt(options, known_latex_paper_sizes);
723 char const * const * margin = known_paper_margins;
724 for (; *margin; ++margin) {
725 string value = process_keyval_opt(options, *margin);
726 if (!value.empty()) {
727 int k = margin - known_paper_margins;
728 string name = known_coded_paper_margins[k];
729 h_margins += '\\' + name + ' ' + value + '\n';
735 void Preamble::handle_package(Parser &p, string const & name,
736 string const & opts, bool in_lyx_preamble,
739 vector<string> options = split_options(opts);
740 add_package(name, options);
742 if (is_known(name, known_xetex_packages)) {
744 h_use_non_tex_fonts = true;
745 registerAutomaticallyLoadedPackage("fontspec");
746 if (h_inputencoding == "auto-legacy")
747 p.setEncoding("UTF-8");
750 // vector of all options for easier parsing and
752 vector<string> allopts = getVectorFromString(opts);
753 // this stores the potential extra options
760 // By default, we use the package name as LyX font name,
761 // so this only needs to be reset if these names differ
762 if (is_known(name, known_roman_font_packages))
763 h_font_roman[0] = name;
765 if (name == "ccfonts") {
766 for (auto const & opt : allopts) {
772 h_font_roman_opts = xopts;
776 if (name == "lmodern") {
777 for (auto const & opt : allopts) {
783 h_font_roman_opts = xopts;
787 if (name == "fourier") {
788 h_font_roman[0] = "utopia";
789 for (auto const & opt : allopts) {
791 h_font_roman_osf = "true";
794 if (opt == "expert") {
803 h_font_roman_opts = xopts;
807 if (name == "garamondx") {
808 for (auto const & opt : allopts) {
810 h_font_roman_osf = "true";
818 h_font_roman_opts = xopts;
822 if (name == "libertine") {
823 // this automatically invokes biolinum
824 h_font_sans[0] = "biolinum";
825 // as well as libertineMono
826 h_font_typewriter[0] = "libertine-mono";
827 for (auto const & opt : allopts) {
829 h_font_roman_osf = "true";
832 if (opt == "lining") {
833 h_font_roman_osf = "false";
841 h_font_roman_opts = xopts;
845 if (name == "libertineRoman" || name == "libertine-type1") {
846 h_font_roman[0] = "libertine";
847 // NOTE: contrary to libertine.sty, libertineRoman
848 // and libertine-type1 do not automatically invoke
849 // biolinum and libertineMono
850 if (opts == "lining")
851 h_font_roman_osf = "false";
852 else if (opts == "osf")
853 h_font_roman_osf = "true";
856 if (name == "MinionPro") {
857 h_font_roman[0] = "minionpro";
858 h_font_roman_osf = "true";
859 h_font_math[0] = "auto";
860 for (auto const & opt : allopts) {
862 h_font_roman_osf = "false";
865 if (opt == "onlytext") {
866 h_font_math[0] = "default";
874 h_font_roman_opts = xopts;
878 if (name == "mathdesign") {
879 for (auto const & opt : allopts) {
880 if (opt == "charter") {
881 h_font_roman[0] = "md-charter";
884 if (opt == "garamond") {
885 h_font_roman[0] = "md-garamond";
888 if (opt == "utopia") {
889 h_font_roman[0] = "md-utopia";
892 if (opt == "expert") {
894 h_font_roman_osf = "true";
900 else if (name == "mathpazo") {
901 h_font_roman[0] = "palatino";
902 for (auto const & opt : allopts) {
904 h_font_roman_osf = "true";
916 h_font_roman_opts = xopts;
920 else if (name == "mathptmx") {
921 h_font_roman[0] = "times";
922 for (auto const & opt : allopts) {
928 h_font_roman_opts = xopts;
932 if (name == "crimson")
933 h_font_roman[0] = "cochineal";
935 if (name == "cochineal") {
936 for (auto const & opt : allopts) {
937 if (opt == "osf" || opt == "oldstyle") {
938 h_font_roman_osf = "true";
941 if (opt == "proportional" || opt == "p")
948 h_font_roman_opts = xopts;
952 if (name == "CrimsonPro") {
953 h_font_roman_osf = "true";
954 for (auto const & opt : allopts) {
955 if (opt == "lf" || opt == "lining") {
956 h_font_roman_osf = "false";
959 if (opt == "proportional" || opt == "p")
961 if (opt == "medium") {
962 h_font_roman[0] = "CrimsonProMedium";
965 if (opt == "extralight") {
966 h_font_roman[0] = "CrimsonProExtraLight";
969 if (opt == "light") {
970 h_font_roman[0] = "CrimsonProLight";
978 h_font_roman_opts = xopts;
984 // font uses old-style figure
985 h_font_roman_osf = "true";
987 if (name == "paratype") {
988 // in this case all fonts are ParaType
989 h_font_roman[0] = "PTSerif-TLF";
990 h_font_sans[0] = "default";
991 h_font_typewriter[0] = "default";
994 if (name == "PTSerif")
995 h_font_roman[0] = "PTSerif-TLF";
997 if (name == "XCharter") {
998 h_font_roman[0] = "xcharter";
999 for (auto const & opt : allopts) {
1001 h_font_roman_osf = "true";
1009 h_font_roman_opts = xopts;
1013 if (name == "plex-serif") {
1014 h_font_roman[0] = "IBMPlexSerif";
1015 for (auto const & opt : allopts) {
1016 if (opt == "thin") {
1017 h_font_roman[0] = "IBMPlexSerifThin";
1020 if (opt == "extralight") {
1021 h_font_roman[0] = "IBMPlexSerifExtraLight";
1024 if (opt == "light") {
1025 h_font_roman[0] = "IBMPlexSerifLight";
1033 h_font_roman_opts = xopts;
1037 if (name == "noto-serif" || name == "noto") {
1044 bool extralight = false;
1046 bool medium = false;
1049 if (name == "noto") {
1054 // Since the options might apply to different shapes,
1055 // we need to parse all options first and then handle them.
1056 for (auto const & opt : allopts) {
1057 if (opt == "regular")
1067 if (opt == "thin") {
1071 if (opt == "extralight") {
1075 if (opt == "light") {
1079 if (opt == "medium") {
1090 if (opt == "nott") {
1098 if (prefixIs(opt, "scaled=")) {
1107 // handle options that might affect different shapes
1108 if (name == "noto-serif" || rm) {
1110 h_font_roman[0] = "NotoSerifThin";
1111 else if (extralight)
1112 h_font_roman[0] = "NotoSerifExtralight";
1114 h_font_roman[0] = "NotoSerifLight";
1116 h_font_roman[0] = "NotoSerifMedium";
1118 h_font_roman[0] = "NotoSerifRegular";
1120 h_font_roman_osf = "true";
1122 h_font_roman_opts = xopts;
1124 if (name == "noto" && sf) {
1126 h_font_sans[0] = "NotoSansThin";
1127 else if (extralight)
1128 h_font_sans[0] = "NotoSansExtralight";
1130 h_font_sans[0] = "NotoSansLight";
1132 h_font_sans[0] = "NotoSansMedium";
1134 h_font_sans[0] = "NotoSansRegular";
1136 h_font_sans_osf = "true";
1138 scale_as_percentage(scl, h_font_sf_scale[0]);
1140 h_font_sans_opts = xopts;
1142 if (name == "noto" && tt) {
1143 h_font_typewriter[0] = "NotoMonoRegular";
1145 h_font_typewriter_osf = "true";
1147 scale_as_percentage(scl, h_font_tt_scale[0]);
1149 h_font_typewriter_opts = xopts;
1153 if (name == "sourceserifpro") {
1154 h_font_roman[0] = "ADOBESourceSerifPro";
1155 for (auto const & opt : allopts) {
1157 h_font_roman_osf = "true";
1165 h_font_roman_opts = xopts;
1173 // By default, we use the package name as LyX font name,
1174 // so this only needs to be reset if these names differ.
1175 // Also, we handle the scaling option here generally.
1176 if (is_known(name, known_sans_font_packages)) {
1177 h_font_sans[0] = name;
1178 if (contains(opts, "scale")) {
1179 vector<string>::iterator it = allopts.begin();
1180 for (; it != allopts.end() ; ++it) {
1181 string const opt = *it;
1182 if (prefixIs(opt, "scaled=") || prefixIs(opt, "scale=")) {
1183 if (scale_as_percentage(opt, h_font_sf_scale[0])) {
1192 if (name == "biolinum" || name == "biolinum-type1") {
1193 h_font_sans[0] = "biolinum";
1194 for (auto const & opt : allopts) {
1195 if (prefixIs(opt, "osf")) {
1196 h_font_sans_osf = "true";
1204 h_font_sans_opts = xopts;
1208 if (name == "cantarell") {
1209 for (auto const & opt : allopts) {
1210 if (opt == "defaultsans")
1212 if (prefixIs(opt, "oldstyle")) {
1213 h_font_sans_osf = "true";
1221 h_font_sans_opts = xopts;
1225 if (name == "Chivo") {
1226 for (auto const & opt : allopts) {
1227 if (opt == "thin") {
1228 h_font_roman[0] = "ChivoThin";
1231 if (opt == "light") {
1232 h_font_roman[0] = "ChivoLight";
1235 if (opt == "regular") {
1236 h_font_roman[0] = "Chivo";
1239 if (opt == "medium") {
1240 h_font_roman[0] = "ChivoMedium";
1243 if (prefixIs(opt, "oldstyle")) {
1244 h_font_sans_osf = "true";
1252 h_font_sans_opts = xopts;
1256 if (name == "PTSans") {
1257 h_font_sans[0] = "PTSans-TLF";
1260 if (name == "FiraSans") {
1261 h_font_sans_osf = "true";
1262 for (auto const & opt : allopts) {
1263 if (opt == "book") {
1264 h_font_sans[0] = "FiraSansBook";
1267 if (opt == "thin") {
1270 if (opt == "extralight") {
1271 h_font_sans[0] = "FiraSansExtralight";
1274 if (opt == "light") {
1275 h_font_sans[0] = "FiraSansLight";
1278 if (opt == "ultralight") {
1279 h_font_sans[0] = "FiraSansUltralight";
1282 if (opt == "thin") {
1283 h_font_sans[0] = "FiraSansThin";
1286 if (opt == "lf" || opt == "lining") {
1287 h_font_sans_osf = "false";
1295 h_font_sans_opts = xopts;
1299 if (name == "plex-sans") {
1300 h_font_sans[0] = "IBMPlexSans";
1301 for (auto const & opt : allopts) {
1302 if (opt == "condensed") {
1303 h_font_sans[0] = "IBMPlexSansCondensed";
1306 if (opt == "thin") {
1307 h_font_sans[0] = "IBMPlexSansThin";
1310 if (opt == "extralight") {
1311 h_font_sans[0] = "IBMPlexSansExtraLight";
1314 if (opt == "light") {
1315 h_font_sans[0] = "IBMPlexSansLight";
1323 h_font_sans_opts = xopts;
1327 if (name == "noto-sans") {
1328 h_font_sans[0] = "NotoSansRegular";
1329 for (auto const & opt : allopts) {
1330 if (opt == "regular")
1332 if (opt == "medium") {
1333 h_font_sans[0] = "NotoSansMedium";
1336 if (opt == "thin") {
1337 h_font_sans[0] = "NotoSansThin";
1340 if (opt == "extralight") {
1341 h_font_sans[0] = "NotoSansExtralight";
1344 if (opt == "light") {
1345 h_font_sans[0] = "NotoSansLight";
1349 h_font_sans_osf = "true";
1357 h_font_sans_opts = xopts;
1361 if (name == "sourcesanspro") {
1362 h_font_sans[0] = "ADOBESourceSansPro";
1363 for (auto const & opt : allopts) {
1365 h_font_sans_osf = "true";
1373 h_font_sans_opts = xopts;
1381 // By default, we use the package name as LyX font name,
1382 // so this only needs to be reset if these names differ.
1383 // Also, we handle the scaling option here generally.
1384 if (is_known(name, known_typewriter_font_packages)) {
1385 h_font_typewriter[0] = name;
1386 if (contains(opts, "scale")) {
1387 vector<string>::iterator it = allopts.begin();
1388 for (; it != allopts.end() ; ++it) {
1389 string const opt = *it;
1390 if (prefixIs(opt, "scaled=") || prefixIs(opt, "scale=")) {
1391 if (scale_as_percentage(opt, h_font_tt_scale[0])) {
1400 if (name == "libertineMono" || name == "libertineMono-type1")
1401 h_font_typewriter[0] = "libertine-mono";
1403 if (name == "FiraMono") {
1404 h_font_typewriter_osf = "true";
1405 for (auto const & opt : allopts) {
1406 if (opt == "lf" || opt == "lining") {
1407 h_font_typewriter_osf = "false";
1415 h_font_typewriter_opts = xopts;
1419 if (name == "PTMono")
1420 h_font_typewriter[0] = "PTMono-TLF";
1422 if (name == "plex-mono") {
1423 h_font_typewriter[0] = "IBMPlexMono";
1424 for (auto const & opt : allopts) {
1425 if (opt == "thin") {
1426 h_font_typewriter[0] = "IBMPlexMonoThin";
1429 if (opt == "extralight") {
1430 h_font_typewriter[0] = "IBMPlexMonoExtraLight";
1433 if (opt == "light") {
1434 h_font_typewriter[0] = "IBMPlexMonoLight";
1442 h_font_typewriter_opts = xopts;
1446 if (name == "noto-mono") {
1447 h_font_typewriter[0] = "NotoMonoRegular";
1448 for (auto const & opt : allopts) {
1449 if (opt == "regular")
1456 h_font_typewriter_opts = xopts;
1460 if (name == "sourcecodepro") {
1461 h_font_typewriter[0] = "ADOBESourceCodePro";
1462 for (auto const & opt : allopts) {
1464 h_font_typewriter_osf = "true";
1472 h_font_typewriter_opts = xopts;
1480 // By default, we use the package name as LyX font name,
1481 // so this only needs to be reset if these names differ.
1482 if (is_known(name, known_math_font_packages))
1483 h_font_math[0] = name;
1485 if (name == "newtxmath") {
1487 h_font_math[0] = "newtxmath";
1488 else if (opts == "garamondx")
1489 h_font_math[0] = "garamondx-ntxm";
1490 else if (opts == "libertine")
1491 h_font_math[0] = "libertine-ntxm";
1492 else if (opts == "minion")
1493 h_font_math[0] = "minion-ntxm";
1494 else if (opts == "cochineal")
1495 h_font_math[0] = "cochineal-ntxm";
1498 if (name == "iwona")
1500 h_font_math[0] = "iwona-math";
1502 if (name == "kurier")
1504 h_font_math[0] = "kurier-math";
1506 // after the detection and handling of special cases, we can remove the
1507 // fonts, otherwise they would appear in the preamble, see bug #7856
1508 if (is_known(name, known_roman_font_packages) || is_known(name, known_sans_font_packages)
1509 || is_known(name, known_typewriter_font_packages) || is_known(name, known_math_font_packages))
1511 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
1512 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
1513 name == "esint" || name == "mhchem" || name == "mathdots" ||
1514 name == "mathtools" || name == "stackrel" ||
1515 name == "stmaryrd" || name == "undertilde") {
1516 h_use_packages[name] = "2";
1517 registerAutomaticallyLoadedPackage(name);
1520 else if (name == "babel") {
1521 h_language_package = "default";
1522 // One might think we would have to do nothing if babel is loaded
1523 // without any options to prevent pollution of the preamble with this
1524 // babel call in every roundtrip.
1525 // But the user could have defined babel-specific things afterwards. So
1526 // we need to keep it in the preamble to prevent cases like bug #7861.
1527 if (!opts.empty()) {
1528 // check if more than one option was used - used later for inputenc
1529 if (options.begin() != options.end() - 1)
1530 one_language = false;
1531 // babel takes the last language of the option of its \usepackage
1532 // call as document language. If there is no such language option, the
1533 // last language in the documentclass options is used.
1534 handle_opt(options, known_languages, h_language);
1535 // translate the babel name to a LyX name
1536 h_language = babel2lyx(h_language);
1537 if (h_language == "japanese") {
1538 // For Japanese, the encoding isn't indicated in the source
1539 // file, and there's really not much we can do. We could
1540 // 1) offer a list of possible encodings to choose from, or
1541 // 2) determine the encoding of the file by inspecting it.
1542 // For the time being, we leave the encoding alone so that
1543 // we don't get iconv errors when making a wrong guess, and
1544 // we will output a note at the top of the document
1545 // explaining what to do.
1546 Encoding const * const enc = encodings.fromIconvName(
1547 p.getEncoding(), Encoding::japanese, false);
1549 h_inputencoding = enc->name();
1550 is_nonCJKJapanese = true;
1551 // in this case babel can be removed from the preamble
1552 registerAutomaticallyLoadedPackage("babel");
1554 // If babel is called with options, LyX puts them by default into the
1555 // document class options. This works for most languages, except
1556 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
1557 // perhaps in future others.
1558 // Therefore keep the babel call as it is as the user might have
1560 string const babelcall = "\\usepackage[" + opts + "]{babel}\n";
1561 if (!contains(h_preamble.str(), babelcall))
1562 h_preamble << babelcall;
1564 delete_opt(options, known_languages);
1566 if (!contains(h_preamble.str(), "\\usepackage{babel}\n"))
1567 h_preamble << "\\usepackage{babel}\n";
1568 explicit_babel = true;
1572 else if (name == "polyglossia") {
1573 h_language_package = "default";
1574 h_default_output_format = "pdf4";
1575 h_use_non_tex_fonts = true;
1577 registerAutomaticallyLoadedPackage("xunicode");
1578 if (h_inputencoding == "auto-legacy")
1579 p.setEncoding("UTF-8");
1582 else if (name == "CJK") {
1583 // set the encoding to "auto-legacy" because it might be set to "auto-legacy-plain" by the babel handling
1584 // and this would not be correct for CJK
1585 if (h_inputencoding == "auto-legacy-plain")
1586 h_inputencoding = "auto-legacy";
1587 registerAutomaticallyLoadedPackage("CJK");
1590 else if (name == "CJKutf8") {
1591 h_inputencoding = "utf8-cjk";
1592 p.setEncoding("UTF-8");
1593 registerAutomaticallyLoadedPackage("CJKutf8");
1596 else if (name == "fontenc") {
1597 h_fontencoding = getStringFromVector(options, ",");
1601 else if (name == "inputenc" || name == "luainputenc") {
1602 // h_inputencoding is only set when there is not more than one
1603 // inputenc option because otherwise h_inputencoding must be
1604 // set to "auto-legacy" (the default encodings of the document's languages)
1605 // Therefore check that exactly one option is passed to inputenc.
1606 // It is also only set when there is not more than one babel
1608 if (!options.empty()) {
1609 string const encoding = options.back();
1610 Encoding const * const enc = encodings.fromLaTeXName(
1611 encoding, Encoding::inputenc, true);
1613 if (!detectEncoding)
1614 cerr << "Unknown encoding " << encoding
1615 << ". Ignoring." << std::endl;
1617 if (!enc->unsafe() && options.size() == 1 && one_language == true)
1618 h_inputencoding = enc->name();
1619 p.setEncoding(enc->iconvName());
1625 else if (name == "srcltx") {
1626 h_output_sync = "1";
1627 if (!opts.empty()) {
1628 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
1631 h_output_sync_macro = "\\usepackage{srcltx}";
1634 else if (is_known(name, known_old_language_packages)) {
1635 // known language packages from the times before babel
1636 // if they are found and not also babel, they will be used as
1637 // custom language package
1638 h_language_package = "\\usepackage{" + name + "}";
1641 else if (name == "lyxskak") {
1642 // ignore this and its options
1643 const char * const o[] = {"ps", "mover", 0};
1644 delete_opt(options, o);
1647 else if (name == "parskip" && options.size() < 2 && (opts.empty() || prefixIs(opts, "skip="))) {
1649 h_paragraph_separation = "halfline";
1651 if (opts == "skip=\\smallskipamount")
1652 h_defskip = "smallskip";
1653 else if (opts == "skip=\\medskipamount")
1654 h_defskip = "medskip";
1655 else if (opts == "skip=\\bigskipamount")
1656 h_defskip = "bigskip";
1657 else if (opts == "skip=\\baselineskip")
1658 h_defskip = "fullline";
1661 h_paragraph_separation = "skip";
1665 else if (is_known(name, known_lyx_packages) && options.empty()) {
1666 if (name == "splitidx")
1667 h_use_indices = "true";
1668 else if (name == "minted")
1669 h_use_minted = true;
1670 else if (name == "refstyle")
1671 h_use_refstyle = true;
1672 else if (name == "prettyref")
1673 h_use_refstyle = false;
1675 if (!in_lyx_preamble) {
1676 h_preamble << package_beg_sep << name
1677 << package_mid_sep << "\\usepackage{"
1679 if (p.next_token().cat() == catNewline ||
1680 (p.next_token().cat() == catSpace &&
1681 p.next_next_token().cat() == catNewline))
1683 h_preamble << package_end_sep;
1687 else if (name == "geometry")
1688 handle_geometry(options);
1690 else if (name == "subfig")
1691 ; // ignore this FIXME: Use the package separator mechanism instead
1693 else if (char const * const * where = is_known(name, known_languages))
1694 h_language = known_coded_languages[where - known_languages];
1696 else if (name == "natbib") {
1697 h_biblio_style = "plainnat";
1698 h_cite_engine = "natbib";
1699 h_cite_engine_type = "authoryear";
1700 vector<string>::iterator it =
1701 find(options.begin(), options.end(), "authoryear");
1702 if (it != options.end())
1705 it = find(options.begin(), options.end(), "numbers");
1706 if (it != options.end()) {
1707 h_cite_engine_type = "numerical";
1711 if (!options.empty())
1712 h_biblio_options = join(options, ",");
1715 else if (name == "biblatex") {
1716 h_biblio_style = "plainnat";
1717 h_cite_engine = "biblatex";
1718 h_cite_engine_type = "authoryear";
1720 vector<string>::iterator it =
1721 find(options.begin(), options.end(), "natbib");
1722 if (it != options.end()) {
1724 h_cite_engine = "biblatex-natbib";
1726 opt = process_keyval_opt(options, "natbib");
1728 h_cite_engine = "biblatex-natbib";
1730 opt = process_keyval_opt(options, "style");
1732 h_biblatex_citestyle = opt;
1733 h_biblatex_bibstyle = opt;
1735 opt = process_keyval_opt(options, "citestyle");
1737 h_biblatex_citestyle = opt;
1738 opt = process_keyval_opt(options, "bibstyle");
1740 h_biblatex_bibstyle = opt;
1742 opt = process_keyval_opt(options, "refsection");
1744 if (opt == "none" || opt == "part"
1745 || opt == "chapter" || opt == "section"
1746 || opt == "subsection")
1749 cerr << "Ignoring unknown refsection value '"
1752 opt = process_keyval_opt(options, "bibencoding");
1755 if (!options.empty()) {
1756 h_biblio_options = join(options, ",");
1761 else if (name == "jurabib") {
1762 h_biblio_style = "jurabib";
1763 h_cite_engine = "jurabib";
1764 h_cite_engine_type = "authoryear";
1765 if (!options.empty())
1766 h_biblio_options = join(options, ",");
1769 else if (name == "bibtopic")
1770 h_use_bibtopic = "true";
1772 else if (name == "chapterbib")
1773 h_multibib = "child";
1775 else if (name == "hyperref")
1776 handle_hyperref(options);
1778 else if (name == "algorithm2e") {
1779 // Load "algorithm2e" module
1780 addModule("algorithm2e");
1781 // Add the package options to the global document options
1782 if (!options.empty()) {
1783 if (h_options.empty())
1784 h_options = join(options, ",");
1786 h_options += ',' + join(options, ",");
1789 else if (name == "microtype") {
1790 //we internally support only microtype without params
1791 if (options.empty())
1792 h_use_microtype = "true";
1794 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1797 else if (name == "lineno") {
1798 h_use_lineno = "true";
1799 if (!options.empty()) {
1800 h_lineno_options = join(options, ",");
1805 else if (name == "changebar")
1806 h_output_changes = "true";
1808 else if (!in_lyx_preamble) {
1809 if (options.empty())
1810 h_preamble << "\\usepackage{" << name << '}';
1812 h_preamble << "\\usepackage[" << opts << "]{"
1816 if (p.next_token().cat() == catNewline ||
1817 (p.next_token().cat() == catSpace &&
1818 p.next_next_token().cat() == catNewline))
1822 // We need to do something with the options...
1823 if (!options.empty() && !detectEncoding)
1824 cerr << "Ignoring options '" << join(options, ",")
1825 << "' of package " << name << '.' << endl;
1827 // remove the whitespace
1832 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1835 Token t = p.get_token();
1836 if (t.cat() == catEscape &&
1837 is_known(t.cs(), known_if_commands))
1838 handle_if(p, in_lyx_preamble);
1840 if (!in_lyx_preamble)
1841 h_preamble << t.asInput();
1842 if (t.cat() == catEscape && t.cs() == "fi")
1849 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1851 if (contains(h_float_placement, "H"))
1852 registerAutomaticallyLoadedPackage("float");
1853 if (h_spacing != "single" && h_spacing != "default")
1854 registerAutomaticallyLoadedPackage("setspace");
1855 if (h_use_packages["amsmath"] == "2") {
1856 // amsbsy and amstext are already provided by amsmath
1857 registerAutomaticallyLoadedPackage("amsbsy");
1858 registerAutomaticallyLoadedPackage("amstext");
1861 // output the LyX file settings
1862 // Important: Keep the version formatting in sync with LyX and
1863 // lyx2lyx (bug 7951)
1864 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1865 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1866 << lyx_version_minor << '\n'
1867 << "\\lyxformat " << LYX_FORMAT << '\n'
1868 << "\\begin_document\n"
1869 << "\\begin_header\n"
1870 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1871 << "\\origin " << origin << "\n"
1872 << "\\textclass " << h_textclass << "\n";
1873 string const raw = subdoc ? empty_string() : h_preamble.str();
1875 os << "\\begin_preamble\n";
1876 for (string::size_type i = 0; i < raw.size(); ++i) {
1877 if (raw[i] == package_beg_sep) {
1878 // Here follows some package loading code that
1879 // must be skipped if the package is loaded
1881 string::size_type j = raw.find(package_mid_sep, i);
1882 if (j == string::npos)
1884 string::size_type k = raw.find(package_end_sep, j);
1885 if (k == string::npos)
1887 string const package = raw.substr(i + 1, j - i - 1);
1888 string const replacement = raw.substr(j + 1, k - j - 1);
1889 if (auto_packages.find(package) == auto_packages.end())
1895 os << "\n\\end_preamble\n";
1897 if (!h_options.empty())
1898 os << "\\options " << h_options << "\n";
1899 os << "\\use_default_options " << h_use_default_options << "\n";
1900 if (!used_modules.empty()) {
1901 os << "\\begin_modules\n";
1902 vector<string>::const_iterator const end = used_modules.end();
1903 vector<string>::const_iterator it = used_modules.begin();
1904 for (; it != end; ++it)
1906 os << "\\end_modules\n";
1908 if (!h_includeonlys.empty()) {
1909 os << "\\begin_includeonly\n";
1910 for (auto const & iofile : h_includeonlys)
1911 os << iofile << '\n';
1912 os << "\\end_includeonly\n";
1914 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1915 << "\\language " << h_language << "\n"
1916 << "\\language_package " << h_language_package << "\n"
1917 << "\\inputencoding " << h_inputencoding << "\n"
1918 << "\\fontencoding " << h_fontencoding << "\n"
1919 << "\\font_roman \"" << h_font_roman[0]
1920 << "\" \"" << h_font_roman[1] << "\"\n"
1921 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1922 << "\\font_typewriter \"" << h_font_typewriter[0]
1923 << "\" \"" << h_font_typewriter[1] << "\"\n"
1924 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1925 << "\\font_default_family " << h_font_default_family << "\n"
1926 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1927 << "\\font_sc " << h_font_sc << "\n"
1928 << "\\font_roman_osf " << h_font_roman_osf << "\n"
1929 << "\\font_sans_osf " << h_font_sans_osf << "\n"
1930 << "\\font_typewriter_osf " << h_font_typewriter_osf << "\n";
1931 if (!h_font_roman_opts.empty())
1932 os << "\\font_roman_opts \"" << h_font_roman_opts << "\"" << '\n';
1933 os << "\\font_sf_scale " << h_font_sf_scale[0]
1934 << ' ' << h_font_sf_scale[1] << '\n';
1935 if (!h_font_sans_opts.empty())
1936 os << "\\font_sans_opts \"" << h_font_sans_opts << "\"" << '\n';
1937 os << "\\font_tt_scale " << h_font_tt_scale[0]
1938 << ' ' << h_font_tt_scale[1] << '\n';
1939 if (!h_font_cjk.empty())
1940 os << "\\font_cjk " << h_font_cjk << '\n';
1941 if (!h_font_typewriter_opts.empty())
1942 os << "\\font_typewriter_opts \"" << h_font_typewriter_opts << "\"" << '\n';
1943 os << "\\use_microtype " << h_use_microtype << '\n'
1944 << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
1945 << "\\graphics " << h_graphics << '\n'
1946 << "\\default_output_format " << h_default_output_format << "\n"
1947 << "\\output_sync " << h_output_sync << "\n";
1948 if (h_output_sync == "1")
1949 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1950 os << "\\bibtex_command " << h_bibtex_command << "\n"
1951 << "\\index_command " << h_index_command << "\n";
1952 if (!h_float_placement.empty())
1953 os << "\\float_placement " << h_float_placement << "\n";
1954 os << "\\paperfontsize " << h_paperfontsize << "\n"
1955 << "\\spacing " << h_spacing << "\n"
1956 << "\\use_hyperref " << h_use_hyperref << '\n';
1957 if (h_use_hyperref == "true") {
1958 if (!h_pdf_title.empty())
1959 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1960 if (!h_pdf_author.empty())
1961 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1962 if (!h_pdf_subject.empty())
1963 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1964 if (!h_pdf_keywords.empty())
1965 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1966 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1967 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1968 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1969 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1970 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1971 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1972 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1973 "\\pdf_backref " << h_pdf_backref << "\n"
1974 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1975 if (!h_pdf_pagemode.empty())
1976 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1977 if (!h_pdf_quoted_options.empty())
1978 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1980 os << "\\papersize " << h_papersize << "\n"
1981 << "\\use_geometry " << h_use_geometry << '\n';
1982 for (map<string, string>::const_iterator it = h_use_packages.begin();
1983 it != h_use_packages.end(); ++it)
1984 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1985 os << "\\cite_engine " << h_cite_engine << '\n'
1986 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1987 << "\\biblio_style " << h_biblio_style << "\n"
1988 << "\\use_bibtopic " << h_use_bibtopic << "\n";
1989 if (!h_biblio_options.empty())
1990 os << "\\biblio_options " << h_biblio_options << "\n";
1991 if (!h_biblatex_bibstyle.empty())
1992 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
1993 if (!h_biblatex_citestyle.empty())
1994 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
1995 if (!h_multibib.empty())
1996 os << "\\multibib " << h_multibib << "\n";
1997 os << "\\use_indices " << h_use_indices << "\n"
1998 << "\\paperorientation " << h_paperorientation << '\n'
1999 << "\\suppress_date " << h_suppress_date << '\n'
2000 << "\\justification " << h_justification << '\n'
2001 << "\\use_refstyle " << h_use_refstyle << '\n'
2002 << "\\use_minted " << h_use_minted << '\n'
2003 << "\\use_lineno " << h_use_lineno << '\n';
2004 if (!h_lineno_options.empty())
2005 os << "\\lineno_options " << h_lineno_options << '\n';
2006 if (!h_fontcolor.empty())
2007 os << "\\fontcolor " << h_fontcolor << '\n';
2008 if (!h_notefontcolor.empty())
2009 os << "\\notefontcolor " << h_notefontcolor << '\n';
2010 if (!h_backgroundcolor.empty())
2011 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
2012 if (!h_boxbgcolor.empty())
2013 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
2014 if (index_number != 0)
2015 for (int i = 0; i < index_number; i++) {
2016 os << "\\index " << h_index[i] << '\n'
2017 << "\\shortcut " << h_shortcut[i] << '\n'
2018 << "\\color " << h_color << '\n'
2022 os << "\\index " << h_index[0] << '\n'
2023 << "\\shortcut " << h_shortcut[0] << '\n'
2024 << "\\color " << h_color << '\n'
2028 << "\\secnumdepth " << h_secnumdepth << "\n"
2029 << "\\tocdepth " << h_tocdepth << "\n"
2030 << "\\paragraph_separation " << h_paragraph_separation << "\n";
2031 if (h_paragraph_separation == "skip")
2032 os << "\\defskip " << h_defskip << "\n";
2034 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
2035 os << "\\is_math_indent " << h_is_mathindent << "\n";
2036 if (!h_mathindentation.empty())
2037 os << "\\math_indentation " << h_mathindentation << "\n";
2038 os << "\\math_numbering_side " << h_math_numbering_side << "\n";
2039 os << "\\quotes_style " << h_quotes_style << "\n"
2040 << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
2041 << "\\papercolumns " << h_papercolumns << "\n"
2042 << "\\papersides " << h_papersides << "\n"
2043 << "\\paperpagestyle " << h_paperpagestyle << "\n";
2044 if (!h_listings_params.empty())
2045 os << "\\listings_params " << h_listings_params << "\n";
2046 os << "\\tracking_changes " << h_tracking_changes << "\n"
2047 << "\\output_changes " << h_output_changes << "\n"
2048 << "\\change_bars " << h_change_bars << "\n"
2049 << "\\html_math_output " << h_html_math_output << "\n"
2050 << "\\html_css_as_file " << h_html_css_as_file << "\n"
2051 << "\\html_be_strict " << h_html_be_strict << "\n"
2053 << "\\end_header\n\n"
2054 << "\\begin_body\n";
2059 void Preamble::parse(Parser & p, string const & forceclass,
2060 TeX2LyXDocClass & tc)
2062 // initialize fixed types
2063 special_columns_['D'] = 3;
2064 parse(p, forceclass, false, tc);
2068 void Preamble::parse(Parser & p, string const & forceclass,
2069 bool detectEncoding, TeX2LyXDocClass & tc)
2071 bool is_full_document = false;
2072 bool is_lyx_file = false;
2073 bool in_lyx_preamble = false;
2075 // determine whether this is a full document or a fragment for inclusion
2077 Token const & t = p.get_token();
2079 if (t.cat() == catEscape && t.cs() == "documentclass") {
2080 is_full_document = true;
2086 if (detectEncoding && !is_full_document)
2089 while (is_full_document && p.good()) {
2090 if (detectEncoding && h_inputencoding != "auto-legacy" &&
2091 h_inputencoding != "auto-legacy-plain")
2094 Token const & t = p.get_token();
2097 if (!detectEncoding)
2098 cerr << "t: " << t << '\n';
2104 if (!in_lyx_preamble &&
2105 (t.cat() == catLetter ||
2106 t.cat() == catSuper ||
2107 t.cat() == catSub ||
2108 t.cat() == catOther ||
2109 t.cat() == catMath ||
2110 t.cat() == catActive ||
2111 t.cat() == catBegin ||
2112 t.cat() == catEnd ||
2113 t.cat() == catAlign ||
2114 t.cat() == catParameter)) {
2115 h_preamble << t.cs();
2119 if (!in_lyx_preamble &&
2120 (t.cat() == catSpace || t.cat() == catNewline)) {
2121 h_preamble << t.asInput();
2125 if (t.cat() == catComment) {
2126 static regex const islyxfile("%% LyX .* created this file");
2127 static regex const usercommands("User specified LaTeX commands");
2129 string const comment = t.asInput();
2131 // magically switch encoding default if it looks like XeLaTeX
2132 static string const magicXeLaTeX =
2133 "% This document must be compiled with XeLaTeX ";
2134 if (comment.size() > magicXeLaTeX.size()
2135 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
2136 && h_inputencoding == "auto-legacy") {
2137 if (!detectEncoding)
2138 cerr << "XeLaTeX comment found, switching to UTF8\n";
2139 h_inputencoding = "utf8";
2142 if (regex_search(comment, sub, islyxfile)) {
2144 in_lyx_preamble = true;
2145 } else if (is_lyx_file
2146 && regex_search(comment, sub, usercommands))
2147 in_lyx_preamble = false;
2148 else if (!in_lyx_preamble)
2149 h_preamble << t.asInput();
2153 if (t.cs() == "PassOptionsToPackage") {
2154 string const poptions = p.getArg('{', '}');
2155 string const package = p.verbatim_item();
2156 extra_package_options_.insert(make_pair(package, poptions));
2160 if (t.cs() == "pagestyle") {
2161 h_paperpagestyle = p.verbatim_item();
2165 if (t.cs() == "setdefaultlanguage") {
2167 // We don't yet care about non-language variant options
2168 // because LyX doesn't support this yet, see bug #8214
2170 string langopts = p.getOpt();
2171 // check if the option contains a variant, if yes, extract it
2172 string::size_type pos_var = langopts.find("variant");
2173 string::size_type i = langopts.find(',', pos_var);
2174 string::size_type k = langopts.find('=', pos_var);
2175 if (pos_var != string::npos){
2177 if (i == string::npos)
2178 variant = langopts.substr(k + 1, langopts.length() - k - 2);
2180 variant = langopts.substr(k + 1, i - k - 1);
2181 h_language = variant;
2185 h_language = p.verbatim_item();
2186 //finally translate the poyglossia name to a LyX name
2187 h_language = polyglossia2lyx(h_language);
2191 if (t.cs() == "setotherlanguage") {
2192 // We don't yet care about the option because LyX doesn't
2193 // support this yet, see bug #8214
2194 p.hasOpt() ? p.getOpt() : string();
2199 if (t.cs() == "setmainfont") {
2200 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2201 h_font_roman[1] = p.getArg('{', '}');
2202 if (!fontopts.empty()) {
2203 vector<string> opts = getVectorFromString(fontopts);
2205 for (auto const & opt : opts) {
2206 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2209 if (!fontopts.empty())
2213 h_font_roman_opts = fontopts;
2218 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
2219 // LyX currently only supports the scale option
2220 string scale, fontopts;
2222 fontopts = p.getArg('[', ']');
2223 if (!fontopts.empty()) {
2224 vector<string> opts = getVectorFromString(fontopts);
2226 for (auto const & opt : opts) {
2227 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2230 if (prefixIs(opt, "Scale=")) {
2231 scale_as_percentage(opt, scale);
2234 if (!fontopts.empty())
2240 if (t.cs() == "setsansfont") {
2242 h_font_sf_scale[1] = scale;
2243 h_font_sans[1] = p.getArg('{', '}');
2244 if (!fontopts.empty())
2245 h_font_sans_opts = fontopts;
2248 h_font_tt_scale[1] = scale;
2249 h_font_typewriter[1] = p.getArg('{', '}');
2250 if (!fontopts.empty())
2251 h_font_typewriter_opts = fontopts;
2256 if (t.cs() == "babelfont") {
2258 h_use_non_tex_fonts = true;
2259 h_language_package = "babel";
2260 if (h_inputencoding == "auto-legacy")
2261 p.setEncoding("UTF-8");
2262 // we don't care about the lang option
2263 string const lang = p.hasOpt() ? p.getArg('[', ']') : string();
2264 string const family = p.getArg('{', '}');
2265 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2266 string const fontname = p.getArg('{', '}');
2267 if (lang.empty() && family == "rm") {
2268 h_font_roman[1] = fontname;
2269 if (!fontopts.empty()) {
2270 vector<string> opts = getVectorFromString(fontopts);
2272 for (auto const & opt : opts) {
2273 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2276 if (!fontopts.empty())
2280 h_font_roman_opts = fontopts;
2283 } else if (lang.empty() && (family == "sf" || family == "tt")) {
2285 if (!fontopts.empty()) {
2286 vector<string> opts = getVectorFromString(fontopts);
2288 for (auto const & opt : opts) {
2289 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2292 if (prefixIs(opt, "Scale=")) {
2293 scale_as_percentage(opt, scale);
2296 if (!fontopts.empty())
2301 if (family == "sf") {
2303 h_font_sf_scale[1] = scale;
2304 h_font_sans[1] = fontname;
2305 if (!fontopts.empty())
2306 h_font_sans_opts = fontopts;
2309 h_font_tt_scale[1] = scale;
2310 h_font_typewriter[1] = fontname;
2311 if (!fontopts.empty())
2312 h_font_typewriter_opts = fontopts;
2316 // not rm, sf or tt or lang specific
2317 h_preamble << '\\' << t.cs();
2319 h_preamble << '[' << lang << ']';
2320 h_preamble << '{' << family << '}';
2321 if (!fontopts.empty())
2322 h_preamble << '[' << fontopts << ']';
2323 h_preamble << '{' << fontname << '}' << '\n';
2328 if (t.cs() == "date") {
2329 string argument = p.getArg('{', '}');
2330 if (argument.empty())
2331 h_suppress_date = "true";
2333 h_preamble << t.asInput() << '{' << argument << '}';
2337 if (t.cs() == "color") {
2338 string const space =
2339 (p.hasOpt() ? p.getOpt() : string());
2340 string argument = p.getArg('{', '}');
2341 // check the case that a standard color is used
2342 if (space.empty() && is_known(argument, known_basic_colors)) {
2343 h_fontcolor = rgbcolor2code(argument);
2344 registerAutomaticallyLoadedPackage("color");
2345 } else if (space.empty() && argument == "document_fontcolor")
2346 registerAutomaticallyLoadedPackage("color");
2347 // check the case that LyX's document_fontcolor is defined
2348 // but not used for \color
2350 h_preamble << t.asInput();
2352 h_preamble << space;
2353 h_preamble << '{' << argument << '}';
2354 // the color might already be set because \definecolor
2355 // is parsed before this
2361 if (t.cs() == "pagecolor") {
2362 string argument = p.getArg('{', '}');
2363 // check the case that a standard color is used
2364 if (is_known(argument, known_basic_colors)) {
2365 h_backgroundcolor = rgbcolor2code(argument);
2366 } else if (argument == "page_backgroundcolor")
2367 registerAutomaticallyLoadedPackage("color");
2368 // check the case that LyX's page_backgroundcolor is defined
2369 // but not used for \pagecolor
2371 h_preamble << t.asInput() << '{' << argument << '}';
2372 // the color might already be set because \definecolor
2373 // is parsed before this
2374 h_backgroundcolor = "";
2379 if (t.cs() == "makeatletter") {
2380 // LyX takes care of this
2381 p.setCatcode('@', catLetter);
2385 if (t.cs() == "makeatother") {
2386 // LyX takes care of this
2387 p.setCatcode('@', catOther);
2391 if (t.cs() == "makeindex") {
2392 // LyX will re-add this if a print index command is found
2397 if (t.cs() == "newindex") {
2398 string const indexname = p.getArg('[', ']');
2399 string const shortcut = p.verbatim_item();
2400 if (!indexname.empty())
2401 h_index[index_number] = indexname;
2403 h_index[index_number] = shortcut;
2404 h_shortcut[index_number] = shortcut;
2410 if (t.cs() == "addbibresource") {
2411 string const options = p.getArg('[', ']');
2412 string const arg = removeExtension(p.getArg('{', '}'));
2413 if (!options.empty()) {
2414 // check if the option contains a bibencoding, if yes, extract it
2415 string::size_type pos = options.find("bibencoding=");
2417 if (pos != string::npos) {
2418 string::size_type i = options.find(',', pos);
2419 if (i == string::npos)
2420 encoding = options.substr(pos + 1);
2422 encoding = options.substr(pos, i - pos);
2423 pos = encoding.find('=');
2424 if (pos == string::npos)
2427 encoding = encoding.substr(pos + 1);
2429 if (!encoding.empty())
2430 biblatex_encodings.push_back(normalize_filename(arg) + ' ' + encoding);
2432 biblatex_bibliographies.push_back(arg);
2436 if (t.cs() == "bibliography") {
2437 vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
2438 biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
2442 if (t.cs() == "RS@ifundefined") {
2443 string const name = p.verbatim_item();
2444 string const body1 = p.verbatim_item();
2445 string const body2 = p.verbatim_item();
2446 // only non-lyxspecific stuff
2447 if (in_lyx_preamble &&
2448 (name == "subsecref" || name == "thmref" || name == "lemref"))
2452 ss << '\\' << t.cs();
2453 ss << '{' << name << '}'
2454 << '{' << body1 << '}'
2455 << '{' << body2 << '}';
2456 h_preamble << ss.str();
2461 if (t.cs() == "AtBeginDocument") {
2462 string const name = p.verbatim_item();
2463 // only non-lyxspecific stuff
2464 if (in_lyx_preamble &&
2465 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
2466 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
2467 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
2468 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
2469 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
2470 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
2471 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
2472 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
2473 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
2474 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
2475 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
2476 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
2477 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
2478 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
2479 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
2483 ss << '\\' << t.cs();
2484 ss << '{' << name << '}';
2485 h_preamble << ss.str();
2490 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
2491 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
2492 || t.cs() == "providecommand" || t.cs() == "providecommandx"
2493 || t.cs() == "DeclareRobustCommand"
2494 || t.cs() == "DeclareRobustCommandx"
2495 || t.cs() == "ProvideTextCommandDefault"
2496 || t.cs() == "DeclareMathAccent") {
2498 if (p.next_token().character() == '*') {
2502 string const name = p.verbatim_item();
2503 string const opt1 = p.getFullOpt();
2504 string const opt2 = p.getFullOpt();
2505 string const body = p.verbatim_item();
2506 // store the in_lyx_preamble setting
2507 bool const was_in_lyx_preamble = in_lyx_preamble;
2509 if (name == "\\rmdefault")
2510 if (is_known(body, known_roman_font_packages)) {
2511 h_font_roman[0] = body;
2513 in_lyx_preamble = true;
2515 if (name == "\\sfdefault")
2516 if (is_known(body, known_sans_font_packages)) {
2517 h_font_sans[0] = body;
2519 in_lyx_preamble = true;
2521 if (name == "\\ttdefault")
2522 if (is_known(body, known_typewriter_font_packages)) {
2523 h_font_typewriter[0] = body;
2525 in_lyx_preamble = true;
2527 if (name == "\\familydefault") {
2528 string family = body;
2529 // remove leading "\"
2530 h_font_default_family = family.erase(0,1);
2532 in_lyx_preamble = true;
2535 // remove LyX-specific definitions that are re-added by LyX
2537 // \lyxline is an ancient command that is converted by tex2lyx into
2538 // a \rule therefore remove its preamble code
2539 if (name == "\\lyxdot" || name == "\\lyxarrow"
2540 || name == "\\lyxline" || name == "\\LyX") {
2542 in_lyx_preamble = true;
2545 // Add the command to the known commands
2546 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
2548 // only non-lyxspecific stuff
2549 if (!in_lyx_preamble) {
2551 ss << '\\' << t.cs();
2554 ss << '{' << name << '}' << opt1 << opt2
2555 << '{' << body << "}";
2556 if (prefixIs(t.cs(), "renew") || !contains(h_preamble.str(), ss.str()))
2557 h_preamble << ss.str();
2559 ostream & out = in_preamble ? h_preamble : os;
2560 out << "\\" << t.cs() << "{" << name << "}"
2561 << opts << "{" << body << "}";
2564 // restore the in_lyx_preamble setting
2565 in_lyx_preamble = was_in_lyx_preamble;
2569 if (t.cs() == "documentclass") {
2570 vector<string>::iterator it;
2571 vector<string> opts = split_options(p.getArg('[', ']'));
2572 // FIXME This does not work for classes that have a
2573 // different name in LyX than in LaTeX
2574 h_textclass = p.getArg('{', '}');
2576 // Force textclass if the user wanted it
2577 if (!forceclass.empty())
2578 h_textclass = forceclass;
2579 tc.setName(h_textclass);
2580 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2581 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2586 // Try those who are (most likely) known to all packages first
2587 handle_opt(opts, known_fontsizes, h_paperfontsize);
2588 delete_opt(opts, known_fontsizes);
2589 // delete "pt" at the end
2590 string::size_type i = h_paperfontsize.find("pt");
2591 if (i != string::npos)
2592 h_paperfontsize.erase(i);
2593 // Now those known specifically to the class
2594 vector<string> class_fsizes = getVectorFromString(tc.opt_fontsize(), "|");
2595 string const fsize_format = tc.fontsizeformat();
2596 for (auto const & fsize : class_fsizes) {
2597 string latexsize = subst(fsize_format, "$$s", fsize);
2598 vector<string>::iterator it = find(opts.begin(), opts.end(), latexsize);
2599 if (it != opts.end()) {
2600 h_paperfontsize = fsize;
2606 // The documentclass options are always parsed before the options
2607 // of the babel call so that a language cannot overwrite the babel
2609 handle_opt(opts, known_languages, h_language);
2610 delete_opt(opts, known_languages);
2613 if ((it = find(opts.begin(), opts.end(), "fleqn"))
2615 h_is_mathindent = "1";
2618 // formula numbering side
2619 if ((it = find(opts.begin(), opts.end(), "leqno"))
2621 h_math_numbering_side = "left";
2624 else if ((it = find(opts.begin(), opts.end(), "reqno"))
2626 h_math_numbering_side = "right";
2630 // paper orientation
2631 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
2632 h_paperorientation = "landscape";
2636 if ((it = find(opts.begin(), opts.end(), "oneside"))
2641 if ((it = find(opts.begin(), opts.end(), "twoside"))
2647 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
2649 h_papercolumns = "1";
2652 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
2654 h_papercolumns = "2";
2658 // some size options are known by the document class, other sizes
2659 // are handled by the \geometry command of the geometry package
2661 vector<string> class_psizes = getVectorFromString(tc.opt_pagesize(), "|");
2662 string const psize_format = tc.pagesizeformat();
2663 for (auto const & psize : class_psizes) {
2664 string latexsize = subst(psize_format, "$$s", psize);
2665 vector<string>::iterator it = find(opts.begin(), opts.end(), latexsize);
2666 if (it != opts.end()) {
2667 h_papersize = psize;
2671 if (psize_format == "$$spaper")
2673 // Also try with the default format since this is understood by
2675 latexsize = psize + "paper";
2676 it = find(opts.begin(), opts.end(), latexsize);
2677 if (it != opts.end()) {
2678 h_papersize = psize;
2683 // the remaining options
2684 h_options = join(opts, ",");
2688 if (t.cs() == "usepackage") {
2689 string const options = p.getArg('[', ']');
2690 string const name = p.getArg('{', '}');
2691 vector<string> vecnames;
2692 split(name, vecnames, ',');
2693 vector<string>::const_iterator it = vecnames.begin();
2694 vector<string>::const_iterator end = vecnames.end();
2695 for (; it != end; ++it)
2696 handle_package(p, trimSpaceAndEol(*it), options,
2697 in_lyx_preamble, detectEncoding);
2701 if (t.cs() == "inputencoding") {
2702 string const encoding = p.getArg('{','}');
2703 Encoding const * const enc = encodings.fromLaTeXName(
2704 encoding, Encoding::inputenc, true);
2706 if (!detectEncoding)
2707 cerr << "Unknown encoding " << encoding
2708 << ". Ignoring." << std::endl;
2711 h_inputencoding = enc->name();
2712 p.setEncoding(enc->iconvName());
2717 if (t.cs() == "newenvironment") {
2718 string const name = p.getArg('{', '}');
2719 string const opt1 = p.getFullOpt();
2720 string const opt2 = p.getFullOpt();
2721 string const beg = p.verbatim_item();
2722 string const end = p.verbatim_item();
2723 if (!in_lyx_preamble) {
2724 h_preamble << "\\newenvironment{" << name
2725 << '}' << opt1 << opt2 << '{'
2726 << beg << "}{" << end << '}';
2728 add_known_environment(name, opt1, !opt2.empty(),
2729 from_utf8(beg), from_utf8(end));
2733 if (t.cs() == "newtheorem") {
2735 if (p.next_token().character() == '*') {
2739 string const name = p.getArg('{', '}');
2740 string const opt1 = p.getFullOpt();
2741 string const opt2 = p.getFullOpt();
2742 string const body = p.verbatim_item();
2743 string const opt3 = p.getFullOpt();
2744 string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
2746 string const complete = cmd + "{" + name + '}' +
2747 opt1 + opt2 + '{' + body + '}' + opt3;
2749 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
2751 if (!in_lyx_preamble)
2752 h_preamble << complete;
2756 if (t.cs() == "def") {
2757 string name = p.get_token().cs();
2758 // In fact, name may be more than the name:
2759 // In the test case of bug 8116
2760 // name == "csname SF@gobble@opt \endcsname".
2761 // Therefore, we need to use asInput() instead of cs().
2762 while (p.next_token().cat() != catBegin)
2763 name += p.get_token().asInput();
2764 if (!in_lyx_preamble)
2765 h_preamble << "\\def\\" << name << '{'
2766 << p.verbatim_item() << "}";
2770 if (t.cs() == "newcolumntype") {
2771 string const name = p.getArg('{', '}');
2772 trimSpaceAndEol(name);
2774 string opts = p.getOpt();
2775 if (!opts.empty()) {
2776 istringstream is(string(opts, 1));
2779 special_columns_[name[0]] = nargs;
2780 h_preamble << "\\newcolumntype{" << name << "}";
2782 h_preamble << "[" << nargs << "]";
2783 h_preamble << "{" << p.verbatim_item() << "}";
2787 if (t.cs() == "setcounter") {
2788 string const name = p.getArg('{', '}');
2789 string const content = p.getArg('{', '}');
2790 if (name == "secnumdepth")
2791 h_secnumdepth = content;
2792 else if (name == "tocdepth")
2793 h_tocdepth = content;
2795 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
2799 if (t.cs() == "setlength") {
2800 string const name = p.verbatim_item();
2801 string const content = p.verbatim_item();
2802 // the paragraphs are only not indented when \parindent is set to zero
2803 if (name == "\\parindent" && content != "")
2804 h_paragraph_indentation = translate_len(content);
2805 else if (name == "\\parskip" && isPackageUsed("parskip")) {
2806 if (content == "\\smallskipamount")
2807 h_defskip = "smallskip";
2808 else if (content == "\\medskipamount")
2809 h_defskip = "medskip";
2810 else if (content == "\\bigskipamount")
2811 h_defskip = "bigskip";
2812 else if (content == "\\baselineskip")
2813 h_defskip = "fullline";
2815 h_defskip = translate_len(content);
2816 } else if (name == "\\mathindent") {
2817 h_mathindentation = translate_len(content);
2819 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2823 if (t.cs() == "onehalfspacing") {
2824 h_spacing = "onehalf";
2828 if (t.cs() == "doublespacing") {
2829 h_spacing = "double";
2833 if (t.cs() == "setstretch") {
2834 h_spacing = "other " + p.verbatim_item();
2838 if (t.cs() == "synctex") {
2839 // the scheme is \synctex=value
2840 // where value can only be "1" or "-1"
2841 h_output_sync = "1";
2842 // there can be any character behind the value (e.g. a linebreak or a '\'
2843 // therefore we extract it char by char
2845 string value = p.get_token().asInput();
2847 value += p.get_token().asInput();
2848 h_output_sync_macro = "\\synctex=" + value;
2852 if (t.cs() == "begin") {
2853 string const name = p.getArg('{', '}');
2854 if (name == "document")
2856 h_preamble << "\\begin{" << name << "}";
2860 if (t.cs() == "geometry") {
2861 vector<string> opts = split_options(p.getArg('{', '}'));
2862 handle_geometry(opts);
2866 if (t.cs() == "definecolor") {
2867 string const color = p.getArg('{', '}');
2868 string const space = p.getArg('{', '}');
2869 string const value = p.getArg('{', '}');
2870 if (color == "document_fontcolor" && space == "rgb") {
2871 RGBColor c(RGBColorFromLaTeX(value));
2872 h_fontcolor = X11hexname(c);
2873 } else if (color == "note_fontcolor" && space == "rgb") {
2874 RGBColor c(RGBColorFromLaTeX(value));
2875 h_notefontcolor = X11hexname(c);
2876 } else if (color == "page_backgroundcolor" && space == "rgb") {
2877 RGBColor c(RGBColorFromLaTeX(value));
2878 h_backgroundcolor = X11hexname(c);
2879 } else if (color == "shadecolor" && space == "rgb") {
2880 RGBColor c(RGBColorFromLaTeX(value));
2881 h_boxbgcolor = X11hexname(c);
2883 h_preamble << "\\definecolor{" << color
2884 << "}{" << space << "}{" << value
2890 if (t.cs() == "bibliographystyle") {
2891 h_biblio_style = p.verbatim_item();
2895 if (t.cs() == "jurabibsetup") {
2896 // FIXME p.getArg('{', '}') is most probably wrong (it
2897 // does not handle nested braces).
2898 // Use p.verbatim_item() instead.
2899 vector<string> jurabibsetup =
2900 split_options(p.getArg('{', '}'));
2901 // add jurabibsetup to the jurabib package options
2902 add_package("jurabib", jurabibsetup);
2903 if (!jurabibsetup.empty()) {
2904 h_preamble << "\\jurabibsetup{"
2905 << join(jurabibsetup, ",") << '}';
2910 if (t.cs() == "hypersetup") {
2911 vector<string> hypersetup =
2912 split_options(p.verbatim_item());
2913 // add hypersetup to the hyperref package options
2914 handle_hyperref(hypersetup);
2915 if (!hypersetup.empty()) {
2916 h_preamble << "\\hypersetup{"
2917 << join(hypersetup, ",") << '}';
2922 if (t.cs() == "includeonly") {
2923 vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
2924 for (auto & iofile : includeonlys) {
2925 string filename(normalize_filename(iofile));
2926 string const path = getMasterFilePath(true);
2927 // We want to preserve relative/absolute filenames,
2928 // therefore path is only used for testing
2929 if (!makeAbsPath(filename, path).exists()) {
2930 // The file extension is probably missing.
2931 // Now try to find it out.
2932 string const tex_name =
2933 find_file(filename, path,
2934 known_tex_extensions);
2935 if (!tex_name.empty())
2936 filename = tex_name;
2939 if (makeAbsPath(filename, path).exists())
2940 fix_child_filename(filename);
2942 cerr << "Warning: Could not find included file '"
2943 << filename << "'." << endl;
2944 outname = changeExtension(filename, "lyx");
2945 h_includeonlys.push_back(outname);
2950 if (is_known(t.cs(), known_if_3arg_commands)) {
2951 // prevent misparsing of \usepackage if it is used
2952 // as an argument (see e.g. our own output of
2953 // \@ifundefined above)
2954 string const arg1 = p.verbatim_item();
2955 string const arg2 = p.verbatim_item();
2956 string const arg3 = p.verbatim_item();
2957 // test case \@ifundefined{date}{}{\date{}}
2958 if (t.cs() == "@ifundefined" && arg1 == "date" &&
2959 arg2.empty() && arg3 == "\\date{}") {
2960 h_suppress_date = "true";
2961 // older tex2lyx versions did output
2962 // \@ifundefined{definecolor}{\usepackage{color}}{}
2963 } else if (t.cs() == "@ifundefined" &&
2964 arg1 == "definecolor" &&
2965 arg2 == "\\usepackage{color}" &&
2967 if (!in_lyx_preamble)
2968 h_preamble << package_beg_sep
2971 << "\\@ifundefined{definecolor}{color}{}"
2974 //\@ifundefined{showcaptionsetup}{}{%
2975 // \PassOptionsToPackage{caption=false}{subfig}}
2976 // that LyX uses for subfloats
2977 } else if (t.cs() == "@ifundefined" &&
2978 arg1 == "showcaptionsetup" && arg2.empty()
2979 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
2981 } else if (!in_lyx_preamble) {
2982 h_preamble << t.asInput()
2983 << '{' << arg1 << '}'
2984 << '{' << arg2 << '}'
2985 << '{' << arg3 << '}';
2990 if (is_known(t.cs(), known_if_commands)) {
2991 // must not parse anything in conditional code, since
2992 // LyX would output the parsed contents unconditionally
2993 if (!in_lyx_preamble)
2994 h_preamble << t.asInput();
2995 handle_if(p, in_lyx_preamble);
2999 if (!t.cs().empty() && !in_lyx_preamble) {
3000 h_preamble << '\\' << t.cs();
3005 // remove the whitespace
3008 if (h_papersides.empty()) {
3011 h_papersides = ss.str();
3014 // If the CJK package is used we cannot set the document language from
3015 // the babel options. Instead, we guess which language is used most
3016 // and set this one.
3017 default_language = h_language;
3018 if (is_full_document &&
3019 (auto_packages.find("CJK") != auto_packages.end() ||
3020 auto_packages.find("CJKutf8") != auto_packages.end())) {
3022 h_language = guessLanguage(p, default_language);
3024 if (explicit_babel && h_language != default_language) {
3025 // We set the document language to a CJK language,
3026 // but babel is explicitly called in the user preamble
3027 // without options. LyX will not add the default
3028 // language to the document options if it is either
3029 // english, or no text is set as default language.
3030 // Therefore we need to add a language option explicitly.
3031 // FIXME: It would be better to remove all babel calls
3032 // from the user preamble, but this is difficult
3033 // without re-introducing bug 7861.
3034 if (h_options.empty())
3035 h_options = lyx2babel(default_language);
3037 h_options += ',' + lyx2babel(default_language);
3041 // Finally, set the quote style.
3042 // LyX knows the following quotes styles:
3043 // british, cjk, cjkangle, danish, english, french, german,
3044 // polish, russian, swedish and swiss
3045 // conversion list taken from
3046 // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
3047 // (quotes for kazakh are unknown)
3049 if (is_known(h_language, known_british_quotes_languages))
3050 h_quotes_style = "british";
3052 else if (is_known(h_language, known_cjk_quotes_languages))
3053 h_quotes_style = "cjk";
3055 else if (is_known(h_language, known_cjkangle_quotes_languages))
3056 h_quotes_style = "cjkangle";
3058 else if (is_known(h_language, known_danish_quotes_languages))
3059 h_quotes_style = "danish";
3061 else if (is_known(h_language, known_french_quotes_languages))
3062 h_quotes_style = "french";
3064 else if (is_known(h_language, known_german_quotes_languages))
3065 h_quotes_style = "german";
3067 else if (is_known(h_language, known_polish_quotes_languages))
3068 h_quotes_style = "polish";
3070 else if (is_known(h_language, known_russian_quotes_languages))
3071 h_quotes_style = "russian";
3073 else if (is_known(h_language, known_swedish_quotes_languages))
3074 h_quotes_style = "swedish";
3076 else if (is_known(h_language, known_swiss_quotes_languages))
3077 h_quotes_style = "swiss";
3079 else if (is_known(h_language, known_english_quotes_languages))
3080 h_quotes_style = "english";
3084 string Preamble::parseEncoding(Parser & p, string const & forceclass)
3086 TeX2LyXDocClass dummy;
3087 parse(p, forceclass, true, dummy);
3088 if (h_inputencoding != "auto-legacy" && h_inputencoding != "auto-legacy-plain")
3089 return h_inputencoding;
3094 string babel2lyx(string const & language)
3096 char const * const * where = is_known(language, known_languages);
3098 return known_coded_languages[where - known_languages];
3103 string lyx2babel(string const & language)
3105 char const * const * where = is_known(language, known_coded_languages);
3107 return known_languages[where - known_coded_languages];
3112 string Preamble::polyglossia2lyx(string const & language)
3114 char const * const * where = is_known(language, polyglossia_languages);
3116 return coded_polyglossia_languages[where - polyglossia_languages];
3121 string rgbcolor2code(string const & name)
3123 char const * const * where = is_known(name, known_basic_colors);
3125 // "red", "green" etc
3126 return known_basic_color_codes[where - known_basic_colors];
3128 // "255,0,0", "0,255,0" etc
3129 RGBColor c(RGBColorFromLaTeX(name));
3130 return X11hexname(c);