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_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_class_paper_sizes[] = { "a4paper", "a5paper",
168 "executivepaper", "legalpaper", "letterpaper", 0};
170 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
171 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
173 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
174 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
177 /// commands that can start an \if...\else...\endif sequence
178 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
179 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
180 "ifsidecap", "ifupgreek", 0};
182 const char * const known_basic_colors[] = {"black", "blue", "brown", "cyan",
183 "darkgray", "gray", "green", "lightgray", "lime", "magenta", "orange", "olive",
184 "pink", "purple", "red", "teal", "violet", "white", "yellow", 0};
186 const char * const known_basic_color_codes[] = {"#000000", "#0000ff", "#964B00", "#00ffff",
187 "#a9a9a9", "#808080", "#00ff00", "#d3d3d3", "#bfff00", "#ff00ff", "#ff7f00", "#808000",
188 "#ffc0cb", "#800080", "#ff0000", "#008080", "#8f00ff", "#ffffff", "#ffff00", 0};
190 /// conditional commands with three arguments like \@ifundefined{}{}{}
191 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
195 * Known file extensions for TeX files as used by \\includeonly
197 char const * const known_tex_extensions[] = {"tex", 0};
199 /// packages that work only in xetex
200 /// polyglossia is handled separately
201 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
202 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
203 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
205 /// packages that are automatically skipped if loaded by LyX
206 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
207 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color",
208 "float", "fontspec", "framed", "graphicx", "hhline", "ifthen", "longtable",
209 "makeidx", "minted", "multirow", "nomencl", "pdfpages", "prettyref", "refstyle",
210 "rotating", "rotfloat", "splitidx", "setspace", "subscript", "tabularx","textcomp", "tipa",
211 "tipx", "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xcolor", "xltabular",
214 // codes used to remove packages that are loaded automatically by LyX.
215 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
216 const char package_beg_sep = '\001';
217 const char package_mid_sep = '\002';
218 const char package_end_sep = '\003';
221 // returns true if at least one of the options in what has been found
222 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
228 // the last language option is the document language (for babel and LyX)
229 // the last size option is the document font size
230 vector<string>::iterator it;
231 vector<string>::iterator position = opts.begin();
232 for (; *what; ++what) {
233 it = find(opts.begin(), opts.end(), *what);
234 if (it != opts.end()) {
235 if (it >= position) {
246 void delete_opt(vector<string> & opts, char const * const * what)
251 // remove found options from the list
252 // do this after handle_opt to avoid potential memory leaks
253 vector<string>::iterator it;
254 for (; *what; ++what) {
255 it = find(opts.begin(), opts.end(), *what);
256 if (it != opts.end())
263 * Split a package options string (keyval format) into a vector.
265 * authorformat=smallcaps,
267 * titleformat=colonsep,
268 * bibformat={tabular,ibidem,numbered}
270 vector<string> split_options(string const & input)
272 vector<string> options;
276 Token const & t = p.get_token();
277 if (t.asInput() == ",") {
278 options.push_back(trimSpaceAndEol(option));
280 } else if (t.asInput() == "=") {
283 if (p.next_token().asInput() == "{")
284 option += '{' + p.getArg('{', '}') + '}';
285 } else if (t.cat() != catSpace && t.cat() != catComment)
286 option += t.asInput();
290 options.push_back(trimSpaceAndEol(option));
297 * Retrieve a keyval option "name={value with=sign}" named \p name from
298 * \p options and return the value.
299 * The found option is also removed from \p options.
301 string process_keyval_opt(vector<string> & options, string name)
303 for (size_t i = 0; i < options.size(); ++i) {
304 vector<string> option;
305 split(options[i], option, '=');
306 if (option.size() < 2)
308 if (option[0] == name) {
309 options.erase(options.begin() + i);
310 option.erase(option.begin());
311 return join(option, "=");
317 } // anonymous namespace
321 * known polyglossia language names (including variants)
322 * FIXME: support spelling=old for german variants (german vs. ngerman LyX names etc)
324 const char * const Preamble::polyglossia_languages[] = {
325 "albanian", "american", "amharic", "ancient", "arabic", "armenian", "asturian", "australian",
326 "bahasai", "bahasam", "basque", "bengali", "brazil", "brazilian", "breton", "british", "bulgarian",
327 "catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
328 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
329 "galician", "greek", "monotonic", "hebrew", "hindi",
330 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean",
331 "lao", "latin", "latvian", "lithuanian", "lsorbian", "magyar", "malayalam", "marathi",
332 "austrian", "newzealand", "german", "norsk", "nynorsk", "occitan", "oldrussian",
333 "piedmontese", "polish", "polytonic", "portuges", "romanian", "romansh", "russian",
334 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovenian", "spanish", "swedish", "syriac",
335 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
336 "ukrainian", "urdu", "usorbian", "vietnamese", "welsh", 0};
337 // not yet supported by LyX: "korean", "nko"
340 * the same as polyglossia_languages with .lyx names
341 * please keep this in sync with polyglossia_languages line by line!
343 const char * const Preamble::coded_polyglossia_languages[] = {
344 "albanian", "american", "amharic", "ancientgreek", "arabic_arabi", "armenian", "asturian", "australian",
345 "bahasa", "bahasam", "basque", "bengali", "brazilian", "brazilian", "breton", "british", "bulgarian",
346 "catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
347 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
348 "galician", "greek", "greek", "hebrew", "hindi",
349 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean",
350 "lao", "latin", "latvian", "lithuanian", "lowersorbian", "magyar", "malayalam", "marathi",
351 "naustrian","newzealand", "ngerman", "norsk", "nynorsk", "occitan", "oldrussian",
352 "piedmontese", "polish", "polutonikogreek", "portuges", "romanian", "romansh", "russian",
353 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovene", "spanish", "swedish", "syriac",
354 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
355 "ukrainian", "urdu", "uppersorbian", "vietnamese", "welsh", 0};
356 // not yet supported by LyX: "korean-polyglossia", "nko"
359 bool Preamble::usePolyglossia() const
361 return h_use_non_tex_fonts && h_language_package == "default";
365 bool Preamble::indentParagraphs() const
367 return h_paragraph_separation == "indent";
371 bool Preamble::isPackageUsed(string const & package) const
373 return used_packages.find(package) != used_packages.end();
377 bool Preamble::isPackageAutoLoaded(string const & package) const
379 return auto_packages.find(package) != auto_packages.end();
383 vector<string> Preamble::getPackageOptions(string const & package) const
385 map<string, vector<string> >::const_iterator it = used_packages.find(package);
386 if (it != used_packages.end())
388 return vector<string>();
392 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
394 auto_packages.insert(package);
398 void Preamble::addModule(string const & module)
400 for (auto const & m : used_modules) {
404 used_modules.push_back(module);
408 void Preamble::suppressDate(bool suppress)
411 h_suppress_date = "true";
413 h_suppress_date = "false";
417 void Preamble::registerAuthor(std::string const & name)
419 Author author(from_utf8(name), empty_docstring());
420 author.setUsed(true);
421 authors_.record(author);
422 h_tracking_changes = "true";
423 h_output_changes = "true";
427 Author const & Preamble::getAuthor(std::string const & name) const
429 Author author(from_utf8(name), empty_docstring());
430 for (AuthorList::Authors::const_iterator it = authors_.begin();
431 it != authors_.end(); ++it)
434 static Author const dummy;
439 int Preamble::getSpecialTableColumnArguments(char c) const
441 map<char, int>::const_iterator it = special_columns_.find(c);
442 if (it == special_columns_.end())
448 void Preamble::add_package(string const & name, vector<string> & options)
450 // every package inherits the global options
451 if (used_packages.find(name) == used_packages.end())
452 used_packages[name] = split_options(h_options);
454 // Insert options passed via PassOptionsToPackage
455 for (auto const & p : extra_package_options_) {
456 if (p.first == name) {
457 vector<string> eo = getVectorFromString(p.second);
458 for (auto const & eoi : eo)
459 options.push_back(eoi);
463 vector<string> & v = used_packages[name];
464 v.insert(v.end(), options.begin(), options.end());
465 if (name == "jurabib") {
466 // Don't output the order argument (see the cite command
467 // handling code in text.cpp).
468 vector<string>::iterator end =
469 remove(options.begin(), options.end(), "natbiborder");
470 end = remove(options.begin(), end, "jurabiborder");
471 options.erase(end, options.end());
478 // Given is a string like "scaled=0.9" or "scale=0.9", return 0.9 * 100
479 bool scale_as_percentage(string const & scale, string & percentage)
481 if (contains(scale, '=')) {
482 string const value = support::split(scale, '=');
483 if (isStrDbl(value)) {
484 percentage = convert<string>(
485 static_cast<int>(100 * convert<double>(value)));
493 string remove_braces(string const & value)
497 if (value[0] == '{' && value[value.length()-1] == '}')
498 return value.substr(1, value.length()-2);
502 } // anonymous namespace
505 Preamble::Preamble() : one_language(true), explicit_babel(false),
506 title_layout_found(false), index_number(0), h_font_cjk_set(false)
510 h_biblio_style = "plain";
511 h_bibtex_command = "default";
512 h_cite_engine = "basic";
513 h_cite_engine_type = "default";
515 h_defskip = "medskip";
516 h_dynamic_quotes = false;
519 h_fontencoding = "default";
520 h_font_roman[0] = "default";
521 h_font_roman[1] = "default";
522 h_font_sans[0] = "default";
523 h_font_sans[1] = "default";
524 h_font_typewriter[0] = "default";
525 h_font_typewriter[1] = "default";
526 h_font_math[0] = "auto";
527 h_font_math[1] = "auto";
528 h_font_default_family = "default";
529 h_use_non_tex_fonts = false;
531 h_font_roman_osf = "false";
532 h_font_sans_osf = "false";
533 h_font_typewriter_osf = "false";
534 h_font_sf_scale[0] = "100";
535 h_font_sf_scale[1] = "100";
536 h_font_tt_scale[0] = "100";
537 h_font_tt_scale[1] = "100";
538 // h_font_roman_opts;
540 // h_font_typewriter_opts;
542 h_is_mathindent = "0";
543 h_math_numbering_side = "default";
544 h_graphics = "default";
545 h_default_output_format = "default";
546 h_html_be_strict = "false";
547 h_html_css_as_file = "0";
548 h_html_math_output = "0";
549 h_index[0] = "Index";
550 h_index_command = "default";
551 h_inputencoding = "auto-legacy";
552 h_justification = "true";
553 h_language = "english";
554 h_language_package = "none";
556 h_maintain_unincluded_children = "false";
560 h_output_changes = "false";
562 //h_output_sync_macro
563 h_papercolumns = "1";
564 h_paperfontsize = "default";
565 h_paperorientation = "portrait";
566 h_paperpagestyle = "default";
568 h_papersize = "default";
569 h_paragraph_indentation = "default";
570 h_paragraph_separation = "indent";
575 h_pdf_bookmarks = "0";
576 h_pdf_bookmarksnumbered = "0";
577 h_pdf_bookmarksopen = "0";
578 h_pdf_bookmarksopenlevel = "1";
579 h_pdf_breaklinks = "0";
580 h_pdf_pdfborder = "0";
581 h_pdf_colorlinks = "0";
582 h_pdf_backref = "section";
583 h_pdf_pdfusetitle = "0";
585 //h_pdf_quoted_options;
586 h_quotes_style = "english";
588 h_shortcut[0] = "idx";
589 h_spacing = "single";
590 h_save_transient_properties = "true";
591 h_suppress_date = "false";
592 h_textclass = "article";
594 h_tracking_changes = "false";
595 h_use_bibtopic = "false";
596 h_use_dash_ligatures = "true";
597 h_use_indices = "false";
598 h_use_geometry = "false";
599 h_use_default_options = "false";
600 h_use_hyperref = "false";
601 h_use_microtype = "false";
602 h_use_lineno = "false";
603 h_use_refstyle = false;
604 h_use_minted = false;
605 h_use_packages["amsmath"] = "1";
606 h_use_packages["amssymb"] = "0";
607 h_use_packages["cancel"] = "0";
608 h_use_packages["esint"] = "1";
609 h_use_packages["mhchem"] = "0";
610 h_use_packages["mathdots"] = "0";
611 h_use_packages["mathtools"] = "0";
612 h_use_packages["stackrel"] = "0";
613 h_use_packages["stmaryrd"] = "0";
614 h_use_packages["undertilde"] = "0";
618 void Preamble::handle_hyperref(vector<string> & options)
620 // FIXME swallow inputencoding changes that might surround the
621 // hyperref setup if it was written by LyX
622 h_use_hyperref = "true";
623 // swallow "unicode=true", since LyX does always write that
624 vector<string>::iterator it =
625 find(options.begin(), options.end(), "unicode=true");
626 if (it != options.end())
628 it = find(options.begin(), options.end(), "pdfusetitle");
629 if (it != options.end()) {
630 h_pdf_pdfusetitle = "1";
633 string bookmarks = process_keyval_opt(options, "bookmarks");
634 if (bookmarks == "true")
635 h_pdf_bookmarks = "1";
636 else if (bookmarks == "false")
637 h_pdf_bookmarks = "0";
638 if (h_pdf_bookmarks == "1") {
639 string bookmarksnumbered =
640 process_keyval_opt(options, "bookmarksnumbered");
641 if (bookmarksnumbered == "true")
642 h_pdf_bookmarksnumbered = "1";
643 else if (bookmarksnumbered == "false")
644 h_pdf_bookmarksnumbered = "0";
645 string bookmarksopen =
646 process_keyval_opt(options, "bookmarksopen");
647 if (bookmarksopen == "true")
648 h_pdf_bookmarksopen = "1";
649 else if (bookmarksopen == "false")
650 h_pdf_bookmarksopen = "0";
651 if (h_pdf_bookmarksopen == "1") {
652 string bookmarksopenlevel =
653 process_keyval_opt(options, "bookmarksopenlevel");
654 if (!bookmarksopenlevel.empty())
655 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
658 string breaklinks = process_keyval_opt(options, "breaklinks");
659 if (breaklinks == "true")
660 h_pdf_breaklinks = "1";
661 else if (breaklinks == "false")
662 h_pdf_breaklinks = "0";
663 string pdfborder = process_keyval_opt(options, "pdfborder");
664 if (pdfborder == "{0 0 0}")
665 h_pdf_pdfborder = "1";
666 else if (pdfborder == "{0 0 1}")
667 h_pdf_pdfborder = "0";
668 string backref = process_keyval_opt(options, "backref");
669 if (!backref.empty())
670 h_pdf_backref = backref;
671 string colorlinks = process_keyval_opt(options, "colorlinks");
672 if (colorlinks == "true")
673 h_pdf_colorlinks = "1";
674 else if (colorlinks == "false")
675 h_pdf_colorlinks = "0";
676 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
677 if (!pdfpagemode.empty())
678 h_pdf_pagemode = pdfpagemode;
679 string pdftitle = process_keyval_opt(options, "pdftitle");
680 if (!pdftitle.empty()) {
681 h_pdf_title = remove_braces(pdftitle);
683 string pdfauthor = process_keyval_opt(options, "pdfauthor");
684 if (!pdfauthor.empty()) {
685 h_pdf_author = remove_braces(pdfauthor);
687 string pdfsubject = process_keyval_opt(options, "pdfsubject");
688 if (!pdfsubject.empty())
689 h_pdf_subject = remove_braces(pdfsubject);
690 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
691 if (!pdfkeywords.empty())
692 h_pdf_keywords = remove_braces(pdfkeywords);
693 if (!options.empty()) {
694 if (!h_pdf_quoted_options.empty())
695 h_pdf_quoted_options += ',';
696 h_pdf_quoted_options += join(options, ",");
702 void Preamble::handle_geometry(vector<string> & options)
704 h_use_geometry = "true";
705 vector<string>::iterator it;
707 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
708 h_paperorientation = "landscape";
712 // keyval version: "paper=letter"
713 string paper = process_keyval_opt(options, "paper");
715 h_papersize = paper + "paper";
716 // alternative version: "letterpaper"
717 handle_opt(options, known_paper_sizes, h_papersize);
718 delete_opt(options, known_paper_sizes);
720 char const * const * margin = known_paper_margins;
721 for (; *margin; ++margin) {
722 string value = process_keyval_opt(options, *margin);
723 if (!value.empty()) {
724 int k = margin - known_paper_margins;
725 string name = known_coded_paper_margins[k];
726 h_margins += '\\' + name + ' ' + value + '\n';
732 void Preamble::handle_package(Parser &p, string const & name,
733 string const & opts, bool in_lyx_preamble,
736 vector<string> options = split_options(opts);
737 add_package(name, options);
739 if (is_known(name, known_xetex_packages)) {
741 h_use_non_tex_fonts = true;
742 registerAutomaticallyLoadedPackage("fontspec");
743 if (h_inputencoding == "auto-legacy")
744 p.setEncoding("UTF-8");
747 // vector of all options for easier parsing and
749 vector<string> allopts = getVectorFromString(opts);
750 // this stores the potential extra options
757 // By default, we use the package name as LyX font name,
758 // so this only needs to be reset if these names differ
759 if (is_known(name, known_roman_font_packages))
760 h_font_roman[0] = name;
762 if (name == "ccfonts") {
763 for (auto const & opt : allopts) {
769 h_font_roman_opts = xopts;
773 if (name == "lmodern") {
774 for (auto const & opt : allopts) {
780 h_font_roman_opts = xopts;
784 if (name == "fourier") {
785 h_font_roman[0] = "utopia";
786 for (auto const & opt : allopts) {
788 h_font_roman_osf = "true";
791 if (opt == "expert") {
800 h_font_roman_opts = xopts;
804 if (name == "garamondx") {
805 for (auto const & opt : allopts) {
807 h_font_roman_osf = "true";
815 h_font_roman_opts = xopts;
819 if (name == "libertine") {
820 // this automatically invokes biolinum
821 h_font_sans[0] = "biolinum";
822 // as well as libertineMono
823 h_font_typewriter[0] = "libertine-mono";
824 for (auto const & opt : allopts) {
826 h_font_roman_osf = "true";
829 if (opt == "lining") {
830 h_font_roman_osf = "false";
838 h_font_roman_opts = xopts;
842 if (name == "libertineRoman" || name == "libertine-type1") {
843 h_font_roman[0] = "libertine";
844 // NOTE: contrary to libertine.sty, libertineRoman
845 // and libertine-type1 do not automatically invoke
846 // biolinum and libertineMono
847 if (opts == "lining")
848 h_font_roman_osf = "false";
849 else if (opts == "osf")
850 h_font_roman_osf = "true";
853 if (name == "MinionPro") {
854 h_font_roman[0] = "minionpro";
855 h_font_roman_osf = "true";
856 h_font_math[0] = "auto";
857 for (auto const & opt : allopts) {
859 h_font_roman_osf = "false";
862 if (opt == "onlytext") {
863 h_font_math[0] = "default";
871 h_font_roman_opts = xopts;
875 if (name == "mathdesign") {
876 for (auto const & opt : allopts) {
877 if (opt == "charter") {
878 h_font_roman[0] = "md-charter";
881 if (opt == "garamond") {
882 h_font_roman[0] = "md-garamond";
885 if (opt == "utopia") {
886 h_font_roman[0] = "md-utopia";
889 if (opt == "expert") {
891 h_font_roman_osf = "true";
897 else if (name == "mathpazo") {
898 h_font_roman[0] = "palatino";
899 for (auto const & opt : allopts) {
901 h_font_roman_osf = "true";
913 h_font_roman_opts = xopts;
917 else if (name == "mathptmx") {
918 h_font_roman[0] = "times";
919 for (auto const & opt : allopts) {
925 h_font_roman_opts = xopts;
929 if (name == "crimson")
930 h_font_roman[0] = "cochineal";
932 if (name == "cochineal") {
933 for (auto const & opt : allopts) {
934 if (opt == "osf" || opt == "oldstyle") {
935 h_font_roman_osf = "true";
938 if (opt == "proportional" || opt == "p")
945 h_font_roman_opts = xopts;
949 if (name == "CrimsonPro") {
950 h_font_roman_osf = "true";
951 for (auto const & opt : allopts) {
952 if (opt == "lf" || opt == "lining") {
953 h_font_roman_osf = "false";
956 if (opt == "proportional" || opt == "p")
958 if (opt == "medium") {
959 h_font_roman[0] = "CrimsonProMedium";
962 if (opt == "extralight") {
963 h_font_roman[0] = "CrimsonProExtraLight";
966 if (opt == "light") {
967 h_font_roman[0] = "CrimsonProLight";
975 h_font_roman_opts = xopts;
981 // font uses old-style figure
982 h_font_roman_osf = "true";
984 if (name == "paratype") {
985 // in this case all fonts are ParaType
986 h_font_roman[0] = "PTSerif-TLF";
987 h_font_sans[0] = "default";
988 h_font_typewriter[0] = "default";
991 if (name == "PTSerif")
992 h_font_roman[0] = "PTSerif-TLF";
994 if (name == "XCharter") {
995 h_font_roman[0] = "xcharter";
996 for (auto const & opt : allopts) {
998 h_font_roman_osf = "true";
1006 h_font_roman_opts = xopts;
1010 if (name == "plex-serif") {
1011 h_font_roman[0] = "IBMPlexSerif";
1012 for (auto const & opt : allopts) {
1013 if (opt == "thin") {
1014 h_font_roman[0] = "IBMPlexSerifThin";
1017 if (opt == "extralight") {
1018 h_font_roman[0] = "IBMPlexSerifExtraLight";
1021 if (opt == "light") {
1022 h_font_roman[0] = "IBMPlexSerifLight";
1030 h_font_roman_opts = xopts;
1034 if (name == "noto-serif" || name == "noto") {
1041 bool extralight = false;
1043 bool medium = false;
1046 if (name == "noto") {
1051 // Since the options might apply to different shapes,
1052 // we need to parse all options first and then handle them.
1053 for (auto const & opt : allopts) {
1054 if (opt == "regular")
1064 if (opt == "thin") {
1068 if (opt == "extralight") {
1072 if (opt == "light") {
1076 if (opt == "medium") {
1087 if (opt == "nott") {
1095 if (prefixIs(opt, "scaled=")) {
1104 // handle options that might affect different shapes
1105 if (name == "noto-serif" || rm) {
1107 h_font_roman[0] = "NotoSerifThin";
1108 else if (extralight)
1109 h_font_roman[0] = "NotoSerifExtralight";
1111 h_font_roman[0] = "NotoSerifLight";
1113 h_font_roman[0] = "NotoSerifMedium";
1115 h_font_roman[0] = "NotoSerifRegular";
1117 h_font_roman_osf = "true";
1119 h_font_roman_opts = xopts;
1121 if (name == "noto" && sf) {
1123 h_font_sans[0] = "NotoSansThin";
1124 else if (extralight)
1125 h_font_sans[0] = "NotoSansExtralight";
1127 h_font_sans[0] = "NotoSansLight";
1129 h_font_sans[0] = "NotoSansMedium";
1131 h_font_sans[0] = "NotoSansRegular";
1133 h_font_sans_osf = "true";
1135 scale_as_percentage(scl, h_font_sf_scale[0]);
1137 h_font_sans_opts = xopts;
1139 if (name == "noto" && tt) {
1140 h_font_typewriter[0] = "NotoMonoRegular";
1142 h_font_typewriter_osf = "true";
1144 scale_as_percentage(scl, h_font_tt_scale[0]);
1146 h_font_typewriter_opts = xopts;
1150 if (name == "sourceserifpro") {
1151 h_font_roman[0] = "ADOBESourceSerifPro";
1152 for (auto const & opt : allopts) {
1154 h_font_roman_osf = "true";
1162 h_font_roman_opts = xopts;
1170 // By default, we use the package name as LyX font name,
1171 // so this only needs to be reset if these names differ.
1172 // Also, we handle the scaling option here generally.
1173 if (is_known(name, known_sans_font_packages)) {
1174 h_font_sans[0] = name;
1175 if (contains(opts, "scale")) {
1176 vector<string>::const_iterator it = allopts.begin();
1177 for (; it != allopts.end() ; ++it) {
1178 string const opt = *it;
1179 if (prefixIs(opt, "scaled=") || prefixIs(opt, "scale=")) {
1180 if (scale_as_percentage(opt, h_font_sf_scale[0])) {
1189 if (name == "biolinum" || name == "biolinum-type1") {
1190 h_font_sans[0] = "biolinum";
1191 for (auto const & opt : allopts) {
1192 if (prefixIs(opt, "osf")) {
1193 h_font_sans_osf = "true";
1201 h_font_sans_opts = xopts;
1205 if (name == "cantarell") {
1206 for (auto const & opt : allopts) {
1207 if (opt == "defaultsans")
1209 if (prefixIs(opt, "oldstyle")) {
1210 h_font_sans_osf = "true";
1218 h_font_sans_opts = xopts;
1222 if (name == "Chivo") {
1223 for (auto const & opt : allopts) {
1224 if (opt == "thin") {
1225 h_font_roman[0] = "ChivoThin";
1228 if (opt == "light") {
1229 h_font_roman[0] = "ChivoLight";
1232 if (opt == "regular") {
1233 h_font_roman[0] = "Chivo";
1236 if (opt == "medium") {
1237 h_font_roman[0] = "ChivoMedium";
1240 if (prefixIs(opt, "oldstyle")) {
1241 h_font_sans_osf = "true";
1249 h_font_sans_opts = xopts;
1253 if (name == "PTSans") {
1254 h_font_sans[0] = "PTSans-TLF";
1257 if (name == "FiraSans") {
1258 h_font_sans_osf = "true";
1259 for (auto const & opt : allopts) {
1260 if (opt == "book") {
1261 h_font_sans[0] = "FiraSansBook";
1264 if (opt == "thin") {
1267 if (opt == "extralight") {
1268 h_font_sans[0] = "FiraSansExtralight";
1271 if (opt == "light") {
1272 h_font_sans[0] = "FiraSansLight";
1275 if (opt == "ultralight") {
1276 h_font_sans[0] = "FiraSansUltralight";
1279 if (opt == "thin") {
1280 h_font_sans[0] = "FiraSansThin";
1283 if (opt == "lf" || opt == "lining") {
1284 h_font_sans_osf = "false";
1292 h_font_sans_opts = xopts;
1296 if (name == "plex-sans") {
1297 h_font_sans[0] = "IBMPlexSans";
1298 for (auto const & opt : allopts) {
1299 if (opt == "condensed") {
1300 h_font_sans[0] = "IBMPlexSansCondensed";
1303 if (opt == "thin") {
1304 h_font_sans[0] = "IBMPlexSansThin";
1307 if (opt == "extralight") {
1308 h_font_sans[0] = "IBMPlexSansExtraLight";
1311 if (opt == "light") {
1312 h_font_sans[0] = "IBMPlexSansLight";
1320 h_font_sans_opts = xopts;
1324 if (name == "noto-sans") {
1325 h_font_sans[0] = "NotoSansRegular";
1326 for (auto const & opt : allopts) {
1327 if (opt == "regular")
1329 if (opt == "medium") {
1330 h_font_sans[0] = "NotoSansMedium";
1333 if (opt == "thin") {
1334 h_font_sans[0] = "NotoSansThin";
1337 if (opt == "extralight") {
1338 h_font_sans[0] = "NotoSansExtralight";
1341 if (opt == "light") {
1342 h_font_sans[0] = "NotoSansLight";
1346 h_font_sans_osf = "true";
1354 h_font_sans_opts = xopts;
1358 if (name == "sourcesanspro") {
1359 h_font_sans[0] = "ADOBESourceSansPro";
1360 for (auto const & opt : allopts) {
1362 h_font_sans_osf = "true";
1370 h_font_sans_opts = xopts;
1378 // By default, we use the package name as LyX font name,
1379 // so this only needs to be reset if these names differ.
1380 // Also, we handle the scaling option here generally.
1381 if (is_known(name, known_typewriter_font_packages)) {
1382 h_font_typewriter[0] = name;
1383 if (contains(opts, "scale")) {
1384 vector<string>::const_iterator it = allopts.begin();
1385 for (; it != allopts.end() ; ++it) {
1386 string const opt = *it;
1387 if (prefixIs(opt, "scaled=") || prefixIs(opt, "scale=")) {
1388 if (scale_as_percentage(opt, h_font_tt_scale[0])) {
1397 if (name == "libertineMono" || name == "libertineMono-type1")
1398 h_font_typewriter[0] = "libertine-mono";
1400 if (name == "FiraMono") {
1401 h_font_typewriter_osf = "true";
1402 for (auto const & opt : allopts) {
1403 if (opt == "lf" || opt == "lining") {
1404 h_font_typewriter_osf = "false";
1412 h_font_typewriter_opts = xopts;
1416 if (name == "PTMono")
1417 h_font_typewriter[0] = "PTMono-TLF";
1419 if (name == "plex-mono") {
1420 h_font_typewriter[0] = "IBMPlexMono";
1421 for (auto const & opt : allopts) {
1422 if (opt == "thin") {
1423 h_font_typewriter[0] = "IBMPlexMonoThin";
1426 if (opt == "extralight") {
1427 h_font_typewriter[0] = "IBMPlexMonoExtraLight";
1430 if (opt == "light") {
1431 h_font_typewriter[0] = "IBMPlexMonoLight";
1439 h_font_typewriter_opts = xopts;
1443 if (name == "noto-mono") {
1444 h_font_typewriter[0] = "NotoMonoRegular";
1445 for (auto const & opt : allopts) {
1446 if (opt == "regular")
1453 h_font_typewriter_opts = xopts;
1457 if (name == "sourcecodepro") {
1458 h_font_typewriter[0] = "ADOBESourceCodePro";
1459 for (auto const & opt : allopts) {
1461 h_font_typewriter_osf = "true";
1469 h_font_typewriter_opts = xopts;
1477 // By default, we use the package name as LyX font name,
1478 // so this only needs to be reset if these names differ.
1479 if (is_known(name, known_math_font_packages))
1480 h_font_math[0] = name;
1482 if (name == "newtxmath") {
1484 h_font_math[0] = "newtxmath";
1485 else if (opts == "garamondx")
1486 h_font_math[0] = "garamondx-ntxm";
1487 else if (opts == "libertine")
1488 h_font_math[0] = "libertine-ntxm";
1489 else if (opts == "minion")
1490 h_font_math[0] = "minion-ntxm";
1491 else if (opts == "cochineal")
1492 h_font_math[0] = "cochineal-ntxm";
1495 if (name == "iwona")
1497 h_font_math[0] = "iwona-math";
1499 if (name == "kurier")
1501 h_font_math[0] = "kurier-math";
1503 // after the detection and handling of special cases, we can remove the
1504 // fonts, otherwise they would appear in the preamble, see bug #7856
1505 if (is_known(name, known_roman_font_packages) || is_known(name, known_sans_font_packages)
1506 || is_known(name, known_typewriter_font_packages) || is_known(name, known_math_font_packages))
1508 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
1509 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
1510 name == "esint" || name == "mhchem" || name == "mathdots" ||
1511 name == "mathtools" || name == "stackrel" ||
1512 name == "stmaryrd" || name == "undertilde") {
1513 h_use_packages[name] = "2";
1514 registerAutomaticallyLoadedPackage(name);
1517 else if (name == "babel") {
1518 h_language_package = "default";
1519 // One might think we would have to do nothing if babel is loaded
1520 // without any options to prevent pollution of the preamble with this
1521 // babel call in every roundtrip.
1522 // But the user could have defined babel-specific things afterwards. So
1523 // we need to keep it in the preamble to prevent cases like bug #7861.
1524 if (!opts.empty()) {
1525 // check if more than one option was used - used later for inputenc
1526 if (options.begin() != options.end() - 1)
1527 one_language = false;
1528 // babel takes the last language of the option of its \usepackage
1529 // call as document language. If there is no such language option, the
1530 // last language in the documentclass options is used.
1531 handle_opt(options, known_languages, h_language);
1532 // translate the babel name to a LyX name
1533 h_language = babel2lyx(h_language);
1534 if (h_language == "japanese") {
1535 // For Japanese, the encoding isn't indicated in the source
1536 // file, and there's really not much we can do. We could
1537 // 1) offer a list of possible encodings to choose from, or
1538 // 2) determine the encoding of the file by inspecting it.
1539 // For the time being, we leave the encoding alone so that
1540 // we don't get iconv errors when making a wrong guess, and
1541 // we will output a note at the top of the document
1542 // explaining what to do.
1543 Encoding const * const enc = encodings.fromIconvName(
1544 p.getEncoding(), Encoding::japanese, false);
1546 h_inputencoding = enc->name();
1547 is_nonCJKJapanese = true;
1548 // in this case babel can be removed from the preamble
1549 registerAutomaticallyLoadedPackage("babel");
1551 // If babel is called with options, LyX puts them by default into the
1552 // document class options. This works for most languages, except
1553 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
1554 // perhaps in future others.
1555 // Therefore keep the babel call as it is as the user might have
1557 string const babelcall = "\\usepackage[" + opts + "]{babel}\n";
1558 if (!contains(h_preamble.str(), babelcall))
1559 h_preamble << babelcall;
1561 delete_opt(options, known_languages);
1563 if (!contains(h_preamble.str(), "\\usepackage{babel}\n"))
1564 h_preamble << "\\usepackage{babel}\n";
1565 explicit_babel = true;
1569 else if (name == "polyglossia") {
1570 h_language_package = "default";
1571 h_default_output_format = "pdf4";
1572 h_use_non_tex_fonts = true;
1574 registerAutomaticallyLoadedPackage("xunicode");
1575 if (h_inputencoding == "auto-legacy")
1576 p.setEncoding("UTF-8");
1579 else if (name == "CJK") {
1580 // set the encoding to "auto-legacy" because it might be set to "auto-legacy-plain" by the babel handling
1581 // and this would not be correct for CJK
1582 if (h_inputencoding == "auto-legacy-plain")
1583 h_inputencoding = "auto-legacy";
1584 registerAutomaticallyLoadedPackage("CJK");
1587 else if (name == "CJKutf8") {
1588 h_inputencoding = "utf8-cjk";
1589 p.setEncoding("UTF-8");
1590 registerAutomaticallyLoadedPackage("CJKutf8");
1593 else if (name == "fontenc") {
1594 h_fontencoding = getStringFromVector(options, ",");
1598 else if (name == "inputenc" || name == "luainputenc") {
1599 // h_inputencoding is only set when there is not more than one
1600 // inputenc option because otherwise h_inputencoding must be
1601 // set to "auto-legacy" (the default encodings of the document's languages)
1602 // Therefore check that exactly one option is passed to inputenc.
1603 // It is also only set when there is not more than one babel
1605 if (!options.empty()) {
1606 string const encoding = options.back();
1607 Encoding const * const enc = encodings.fromLaTeXName(
1608 encoding, Encoding::inputenc, true);
1610 if (!detectEncoding)
1611 cerr << "Unknown encoding " << encoding
1612 << ". Ignoring." << std::endl;
1614 if (!enc->unsafe() && options.size() == 1 && one_language == true)
1615 h_inputencoding = enc->name();
1616 p.setEncoding(enc->iconvName());
1622 else if (name == "srcltx") {
1623 h_output_sync = "1";
1624 if (!opts.empty()) {
1625 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
1628 h_output_sync_macro = "\\usepackage{srcltx}";
1631 else if (is_known(name, known_old_language_packages)) {
1632 // known language packages from the times before babel
1633 // if they are found and not also babel, they will be used as
1634 // custom language package
1635 h_language_package = "\\usepackage{" + name + "}";
1638 else if (name == "lyxskak") {
1639 // ignore this and its options
1640 const char * const o[] = {"ps", "mover", 0};
1641 delete_opt(options, o);
1644 else if (is_known(name, known_lyx_packages) && options.empty()) {
1645 if (name == "splitidx")
1646 h_use_indices = "true";
1647 else if (name == "minted")
1648 h_use_minted = true;
1649 else if (name == "refstyle")
1650 h_use_refstyle = true;
1651 else if (name == "prettyref")
1652 h_use_refstyle = false;
1653 if (!in_lyx_preamble) {
1654 h_preamble << package_beg_sep << name
1655 << package_mid_sep << "\\usepackage{"
1657 if (p.next_token().cat() == catNewline ||
1658 (p.next_token().cat() == catSpace &&
1659 p.next_next_token().cat() == catNewline))
1661 h_preamble << package_end_sep;
1665 else if (name == "geometry")
1666 handle_geometry(options);
1668 else if (name == "subfig")
1669 ; // ignore this FIXME: Use the package separator mechanism instead
1671 else if (char const * const * where = is_known(name, known_languages))
1672 h_language = known_coded_languages[where - known_languages];
1674 else if (name == "natbib") {
1675 h_biblio_style = "plainnat";
1676 h_cite_engine = "natbib";
1677 h_cite_engine_type = "authoryear";
1678 vector<string>::iterator it =
1679 find(options.begin(), options.end(), "authoryear");
1680 if (it != options.end())
1683 it = find(options.begin(), options.end(), "numbers");
1684 if (it != options.end()) {
1685 h_cite_engine_type = "numerical";
1689 if (!options.empty())
1690 h_biblio_options = join(options, ",");
1693 else if (name == "biblatex") {
1694 h_biblio_style = "plainnat";
1695 h_cite_engine = "biblatex";
1696 h_cite_engine_type = "authoryear";
1698 vector<string>::iterator it =
1699 find(options.begin(), options.end(), "natbib");
1700 if (it != options.end()) {
1702 h_cite_engine = "biblatex-natbib";
1704 opt = process_keyval_opt(options, "natbib");
1706 h_cite_engine = "biblatex-natbib";
1708 opt = process_keyval_opt(options, "style");
1710 h_biblatex_citestyle = opt;
1711 h_biblatex_bibstyle = opt;
1713 opt = process_keyval_opt(options, "citestyle");
1715 h_biblatex_citestyle = opt;
1716 opt = process_keyval_opt(options, "bibstyle");
1718 h_biblatex_bibstyle = opt;
1720 opt = process_keyval_opt(options, "refsection");
1722 if (opt == "none" || opt == "part"
1723 || opt == "chapter" || opt == "section"
1724 || opt == "subsection")
1727 cerr << "Ignoring unkown refesection value '"
1730 opt = process_keyval_opt(options, "bibencoding");
1733 if (!options.empty()) {
1734 h_biblio_options = join(options, ",");
1739 else if (name == "jurabib") {
1740 h_biblio_style = "jurabib";
1741 h_cite_engine = "jurabib";
1742 h_cite_engine_type = "authoryear";
1743 if (!options.empty())
1744 h_biblio_options = join(options, ",");
1747 else if (name == "bibtopic")
1748 h_use_bibtopic = "true";
1750 else if (name == "chapterbib")
1751 h_multibib = "child";
1753 else if (name == "hyperref")
1754 handle_hyperref(options);
1756 else if (name == "algorithm2e") {
1757 // Load "algorithm2e" module
1758 addModule("algorithm2e");
1759 // Add the package options to the global document options
1760 if (!options.empty()) {
1761 if (h_options.empty())
1762 h_options = join(options, ",");
1764 h_options += ',' + join(options, ",");
1767 else if (name == "microtype") {
1768 //we internally support only microtype without params
1769 if (options.empty())
1770 h_use_microtype = "true";
1772 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1775 else if (name == "lineno") {
1776 h_use_lineno = "true";
1777 if (!options.empty()) {
1778 h_lineno_options = join(options, ",");
1783 else if (!in_lyx_preamble) {
1784 if (options.empty())
1785 h_preamble << "\\usepackage{" << name << '}';
1787 h_preamble << "\\usepackage[" << opts << "]{"
1791 if (p.next_token().cat() == catNewline ||
1792 (p.next_token().cat() == catSpace &&
1793 p.next_next_token().cat() == catNewline))
1797 // We need to do something with the options...
1798 if (!options.empty() && !detectEncoding)
1799 cerr << "Ignoring options '" << join(options, ",")
1800 << "' of package " << name << '.' << endl;
1802 // remove the whitespace
1807 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1810 Token t = p.get_token();
1811 if (t.cat() == catEscape &&
1812 is_known(t.cs(), known_if_commands))
1813 handle_if(p, in_lyx_preamble);
1815 if (!in_lyx_preamble)
1816 h_preamble << t.asInput();
1817 if (t.cat() == catEscape && t.cs() == "fi")
1824 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1826 if (contains(h_float_placement, "H"))
1827 registerAutomaticallyLoadedPackage("float");
1828 if (h_spacing != "single" && h_spacing != "default")
1829 registerAutomaticallyLoadedPackage("setspace");
1830 if (h_use_packages["amsmath"] == "2") {
1831 // amsbsy and amstext are already provided by amsmath
1832 registerAutomaticallyLoadedPackage("amsbsy");
1833 registerAutomaticallyLoadedPackage("amstext");
1836 // output the LyX file settings
1837 // Important: Keep the version formatting in sync with LyX and
1838 // lyx2lyx (bug 7951)
1839 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1840 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1841 << lyx_version_minor << '\n'
1842 << "\\lyxformat " << LYX_FORMAT << '\n'
1843 << "\\begin_document\n"
1844 << "\\begin_header\n"
1845 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1846 << "\\origin " << origin << "\n"
1847 << "\\textclass " << h_textclass << "\n";
1848 string const raw = subdoc ? empty_string() : h_preamble.str();
1850 os << "\\begin_preamble\n";
1851 for (string::size_type i = 0; i < raw.size(); ++i) {
1852 if (raw[i] == package_beg_sep) {
1853 // Here follows some package loading code that
1854 // must be skipped if the package is loaded
1856 string::size_type j = raw.find(package_mid_sep, i);
1857 if (j == string::npos)
1859 string::size_type k = raw.find(package_end_sep, j);
1860 if (k == string::npos)
1862 string const package = raw.substr(i + 1, j - i - 1);
1863 string const replacement = raw.substr(j + 1, k - j - 1);
1864 if (auto_packages.find(package) == auto_packages.end())
1870 os << "\n\\end_preamble\n";
1872 if (!h_options.empty())
1873 os << "\\options " << h_options << "\n";
1874 os << "\\use_default_options " << h_use_default_options << "\n";
1875 if (!used_modules.empty()) {
1876 os << "\\begin_modules\n";
1877 vector<string>::const_iterator const end = used_modules.end();
1878 vector<string>::const_iterator it = used_modules.begin();
1879 for (; it != end; ++it)
1881 os << "\\end_modules\n";
1883 if (!h_includeonlys.empty()) {
1884 os << "\\begin_includeonly\n";
1885 for (auto const & iofile : h_includeonlys)
1886 os << iofile << '\n';
1887 os << "\\end_includeonly\n";
1889 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1890 << "\\language " << h_language << "\n"
1891 << "\\language_package " << h_language_package << "\n"
1892 << "\\inputencoding " << h_inputencoding << "\n"
1893 << "\\fontencoding " << h_fontencoding << "\n"
1894 << "\\font_roman \"" << h_font_roman[0]
1895 << "\" \"" << h_font_roman[1] << "\"\n"
1896 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1897 << "\\font_typewriter \"" << h_font_typewriter[0]
1898 << "\" \"" << h_font_typewriter[1] << "\"\n"
1899 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1900 << "\\font_default_family " << h_font_default_family << "\n"
1901 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1902 << "\\font_sc " << h_font_sc << "\n"
1903 << "\\font_roman_osf " << h_font_roman_osf << "\n"
1904 << "\\font_sans_osf " << h_font_sans_osf << "\n"
1905 << "\\font_typewriter_osf " << h_font_typewriter_osf << "\n";
1906 if (!h_font_roman_opts.empty())
1907 os << "\\font_roman_opts \"" << h_font_roman_opts << "\"" << '\n';
1908 os << "\\font_sf_scale " << h_font_sf_scale[0]
1909 << ' ' << h_font_sf_scale[1] << '\n';
1910 if (!h_font_sans_opts.empty())
1911 os << "\\font_sans_opts \"" << h_font_sans_opts << "\"" << '\n';
1912 os << "\\font_tt_scale " << h_font_tt_scale[0]
1913 << ' ' << h_font_tt_scale[1] << '\n';
1914 if (!h_font_cjk.empty())
1915 os << "\\font_cjk " << h_font_cjk << '\n';
1916 if (!h_font_typewriter_opts.empty())
1917 os << "\\font_typewriter_opts \"" << h_font_typewriter_opts << "\"" << '\n';
1918 os << "\\use_microtype " << h_use_microtype << '\n'
1919 << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
1920 << "\\graphics " << h_graphics << '\n'
1921 << "\\default_output_format " << h_default_output_format << "\n"
1922 << "\\output_sync " << h_output_sync << "\n";
1923 if (h_output_sync == "1")
1924 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1925 os << "\\bibtex_command " << h_bibtex_command << "\n"
1926 << "\\index_command " << h_index_command << "\n";
1927 if (!h_float_placement.empty())
1928 os << "\\float_placement " << h_float_placement << "\n";
1929 os << "\\paperfontsize " << h_paperfontsize << "\n"
1930 << "\\spacing " << h_spacing << "\n"
1931 << "\\use_hyperref " << h_use_hyperref << '\n';
1932 if (h_use_hyperref == "true") {
1933 if (!h_pdf_title.empty())
1934 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1935 if (!h_pdf_author.empty())
1936 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1937 if (!h_pdf_subject.empty())
1938 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1939 if (!h_pdf_keywords.empty())
1940 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1941 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1942 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1943 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1944 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1945 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1946 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1947 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1948 "\\pdf_backref " << h_pdf_backref << "\n"
1949 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1950 if (!h_pdf_pagemode.empty())
1951 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1952 if (!h_pdf_quoted_options.empty())
1953 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1955 os << "\\papersize " << h_papersize << "\n"
1956 << "\\use_geometry " << h_use_geometry << '\n';
1957 for (map<string, string>::const_iterator it = h_use_packages.begin();
1958 it != h_use_packages.end(); ++it)
1959 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1960 os << "\\cite_engine " << h_cite_engine << '\n'
1961 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1962 << "\\biblio_style " << h_biblio_style << "\n"
1963 << "\\use_bibtopic " << h_use_bibtopic << "\n";
1964 if (!h_biblio_options.empty())
1965 os << "\\biblio_options " << h_biblio_options << "\n";
1966 if (!h_biblatex_bibstyle.empty())
1967 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
1968 if (!h_biblatex_citestyle.empty())
1969 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
1970 if (!h_multibib.empty())
1971 os << "\\multibib " << h_multibib << "\n";
1972 os << "\\use_indices " << h_use_indices << "\n"
1973 << "\\paperorientation " << h_paperorientation << '\n'
1974 << "\\suppress_date " << h_suppress_date << '\n'
1975 << "\\justification " << h_justification << '\n'
1976 << "\\use_refstyle " << h_use_refstyle << '\n'
1977 << "\\use_minted " << h_use_minted << '\n'
1978 << "\\use_lineno " << h_use_lineno << '\n';
1979 if (!h_lineno_options.empty())
1980 os << "\\lineno_options " << h_lineno_options << '\n';
1981 if (!h_fontcolor.empty())
1982 os << "\\fontcolor " << h_fontcolor << '\n';
1983 if (!h_notefontcolor.empty())
1984 os << "\\notefontcolor " << h_notefontcolor << '\n';
1985 if (!h_backgroundcolor.empty())
1986 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1987 if (!h_boxbgcolor.empty())
1988 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1989 if (index_number != 0)
1990 for (int i = 0; i < index_number; i++) {
1991 os << "\\index " << h_index[i] << '\n'
1992 << "\\shortcut " << h_shortcut[i] << '\n'
1993 << "\\color " << h_color << '\n'
1997 os << "\\index " << h_index[0] << '\n'
1998 << "\\shortcut " << h_shortcut[0] << '\n'
1999 << "\\color " << h_color << '\n'
2003 << "\\secnumdepth " << h_secnumdepth << "\n"
2004 << "\\tocdepth " << h_tocdepth << "\n"
2005 << "\\paragraph_separation " << h_paragraph_separation << "\n";
2006 if (h_paragraph_separation == "skip")
2007 os << "\\defskip " << h_defskip << "\n";
2009 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
2010 os << "\\is_math_indent " << h_is_mathindent << "\n";
2011 if (!h_mathindentation.empty())
2012 os << "\\math_indentation " << h_mathindentation << "\n";
2013 os << "\\math_numbering_side " << h_math_numbering_side << "\n";
2014 os << "\\quotes_style " << h_quotes_style << "\n"
2015 << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
2016 << "\\papercolumns " << h_papercolumns << "\n"
2017 << "\\papersides " << h_papersides << "\n"
2018 << "\\paperpagestyle " << h_paperpagestyle << "\n";
2019 if (!h_listings_params.empty())
2020 os << "\\listings_params " << h_listings_params << "\n";
2021 os << "\\tracking_changes " << h_tracking_changes << "\n"
2022 << "\\output_changes " << h_output_changes << "\n"
2023 << "\\html_math_output " << h_html_math_output << "\n"
2024 << "\\html_css_as_file " << h_html_css_as_file << "\n"
2025 << "\\html_be_strict " << h_html_be_strict << "\n"
2027 << "\\end_header\n\n"
2028 << "\\begin_body\n";
2033 void Preamble::parse(Parser & p, string const & forceclass,
2034 TeX2LyXDocClass & tc)
2036 // initialize fixed types
2037 special_columns_['D'] = 3;
2038 parse(p, forceclass, false, tc);
2042 void Preamble::parse(Parser & p, string const & forceclass,
2043 bool detectEncoding, TeX2LyXDocClass & tc)
2045 bool is_full_document = false;
2046 bool is_lyx_file = false;
2047 bool in_lyx_preamble = false;
2049 // determine whether this is a full document or a fragment for inclusion
2051 Token const & t = p.get_token();
2053 if (t.cat() == catEscape && t.cs() == "documentclass") {
2054 is_full_document = true;
2060 if (detectEncoding && !is_full_document)
2063 while (is_full_document && p.good()) {
2064 if (detectEncoding && h_inputencoding != "auto-legacy" &&
2065 h_inputencoding != "auto-legacy-plain")
2068 Token const & t = p.get_token();
2071 if (!detectEncoding)
2072 cerr << "t: " << t << '\n';
2078 if (!in_lyx_preamble &&
2079 (t.cat() == catLetter ||
2080 t.cat() == catSuper ||
2081 t.cat() == catSub ||
2082 t.cat() == catOther ||
2083 t.cat() == catMath ||
2084 t.cat() == catActive ||
2085 t.cat() == catBegin ||
2086 t.cat() == catEnd ||
2087 t.cat() == catAlign ||
2088 t.cat() == catParameter)) {
2089 h_preamble << t.cs();
2093 if (!in_lyx_preamble &&
2094 (t.cat() == catSpace || t.cat() == catNewline)) {
2095 h_preamble << t.asInput();
2099 if (t.cat() == catComment) {
2100 static regex const islyxfile("%% LyX .* created this file");
2101 static regex const usercommands("User specified LaTeX commands");
2103 string const comment = t.asInput();
2105 // magically switch encoding default if it looks like XeLaTeX
2106 static string const magicXeLaTeX =
2107 "% This document must be compiled with XeLaTeX ";
2108 if (comment.size() > magicXeLaTeX.size()
2109 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
2110 && h_inputencoding == "auto-legacy") {
2111 if (!detectEncoding)
2112 cerr << "XeLaTeX comment found, switching to UTF8\n";
2113 h_inputencoding = "utf8";
2116 if (regex_search(comment, sub, islyxfile)) {
2118 in_lyx_preamble = true;
2119 } else if (is_lyx_file
2120 && regex_search(comment, sub, usercommands))
2121 in_lyx_preamble = false;
2122 else if (!in_lyx_preamble)
2123 h_preamble << t.asInput();
2127 if (t.cs() == "PassOptionsToPackage") {
2128 string const poptions = p.getArg('{', '}');
2129 string const package = p.verbatim_item();
2130 extra_package_options_.insert(make_pair(package, poptions));
2134 if (t.cs() == "pagestyle") {
2135 h_paperpagestyle = p.verbatim_item();
2139 if (t.cs() == "setdefaultlanguage") {
2141 // We don't yet care about non-language variant options
2142 // because LyX doesn't support this yet, see bug #8214
2144 string langopts = p.getOpt();
2145 // check if the option contains a variant, if yes, extract it
2146 string::size_type pos_var = langopts.find("variant");
2147 string::size_type i = langopts.find(',', pos_var);
2148 string::size_type k = langopts.find('=', pos_var);
2149 if (pos_var != string::npos){
2151 if (i == string::npos)
2152 variant = langopts.substr(k + 1, langopts.length() - k - 2);
2154 variant = langopts.substr(k + 1, i - k - 1);
2155 h_language = variant;
2159 h_language = p.verbatim_item();
2160 //finally translate the poyglossia name to a LyX name
2161 h_language = polyglossia2lyx(h_language);
2165 if (t.cs() == "setotherlanguage") {
2166 // We don't yet care about the option because LyX doesn't
2167 // support this yet, see bug #8214
2168 p.hasOpt() ? p.getOpt() : string();
2173 if (t.cs() == "setmainfont") {
2174 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2175 h_font_roman[1] = p.getArg('{', '}');
2176 if (!fontopts.empty()) {
2177 vector<string> opts = getVectorFromString(fontopts);
2179 for (auto const & opt : opts) {
2180 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2183 if (!fontopts.empty())
2187 h_font_roman_opts = fontopts;
2192 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
2193 // LyX currently only supports the scale option
2194 string scale, fontopts;
2196 fontopts = p.getArg('[', ']');
2197 if (!fontopts.empty()) {
2198 vector<string> opts = getVectorFromString(fontopts);
2200 for (auto const & opt : opts) {
2201 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2204 if (prefixIs(opt, "Scale=")) {
2205 scale_as_percentage(opt, scale);
2208 if (!fontopts.empty())
2214 if (t.cs() == "setsansfont") {
2216 h_font_sf_scale[1] = scale;
2217 h_font_sans[1] = p.getArg('{', '}');
2218 if (!fontopts.empty())
2219 h_font_sans_opts = fontopts;
2222 h_font_tt_scale[1] = scale;
2223 h_font_typewriter[1] = p.getArg('{', '}');
2224 if (!fontopts.empty())
2225 h_font_typewriter_opts = fontopts;
2230 if (t.cs() == "babelfont") {
2232 h_use_non_tex_fonts = true;
2233 h_language_package = "babel";
2234 if (h_inputencoding == "auto-legacy")
2235 p.setEncoding("UTF-8");
2236 // we don't care about the lang option
2237 string const lang = p.hasOpt() ? p.getArg('[', ']') : string();
2238 string const family = p.getArg('{', '}');
2239 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2240 string const fontname = p.getArg('{', '}');
2241 if (lang.empty() && family == "rm") {
2242 h_font_roman[1] = fontname;
2243 if (!fontopts.empty()) {
2244 vector<string> opts = getVectorFromString(fontopts);
2246 for (auto const & opt : opts) {
2247 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2250 if (!fontopts.empty())
2254 h_font_roman_opts = fontopts;
2257 } else if (lang.empty() && (family == "sf" || family == "tt")) {
2259 if (!fontopts.empty()) {
2260 vector<string> opts = getVectorFromString(fontopts);
2262 for (auto const & opt : opts) {
2263 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2266 if (prefixIs(opt, "Scale=")) {
2267 scale_as_percentage(opt, scale);
2270 if (!fontopts.empty())
2275 if (family == "sf") {
2277 h_font_sf_scale[1] = scale;
2278 h_font_sans[1] = fontname;
2279 if (!fontopts.empty())
2280 h_font_sans_opts = fontopts;
2283 h_font_tt_scale[1] = scale;
2284 h_font_typewriter[1] = fontname;
2285 if (!fontopts.empty())
2286 h_font_typewriter_opts = fontopts;
2290 // not rm, sf or tt or lang specific
2291 h_preamble << '\\' << t.cs();
2293 h_preamble << '[' << lang << ']';
2294 h_preamble << '{' << family << '}';
2295 if (!fontopts.empty())
2296 h_preamble << '[' << fontopts << ']';
2297 h_preamble << '{' << fontname << '}' << '\n';
2302 if (t.cs() == "date") {
2303 string argument = p.getArg('{', '}');
2304 if (argument.empty())
2305 h_suppress_date = "true";
2307 h_preamble << t.asInput() << '{' << argument << '}';
2311 if (t.cs() == "color") {
2312 string const space =
2313 (p.hasOpt() ? p.getOpt() : string());
2314 string argument = p.getArg('{', '}');
2315 // check the case that a standard color is used
2316 if (space.empty() && is_known(argument, known_basic_colors)) {
2317 h_fontcolor = rgbcolor2code(argument);
2318 registerAutomaticallyLoadedPackage("color");
2319 } else if (space.empty() && argument == "document_fontcolor")
2320 registerAutomaticallyLoadedPackage("color");
2321 // check the case that LyX's document_fontcolor is defined
2322 // but not used for \color
2324 h_preamble << t.asInput();
2326 h_preamble << space;
2327 h_preamble << '{' << argument << '}';
2328 // the color might already be set because \definecolor
2329 // is parsed before this
2335 if (t.cs() == "pagecolor") {
2336 string argument = p.getArg('{', '}');
2337 // check the case that a standard color is used
2338 if (is_known(argument, known_basic_colors)) {
2339 h_backgroundcolor = rgbcolor2code(argument);
2340 } else if (argument == "page_backgroundcolor")
2341 registerAutomaticallyLoadedPackage("color");
2342 // check the case that LyX's page_backgroundcolor is defined
2343 // but not used for \pagecolor
2345 h_preamble << t.asInput() << '{' << argument << '}';
2346 // the color might already be set because \definecolor
2347 // is parsed before this
2348 h_backgroundcolor = "";
2353 if (t.cs() == "makeatletter") {
2354 // LyX takes care of this
2355 p.setCatcode('@', catLetter);
2359 if (t.cs() == "makeatother") {
2360 // LyX takes care of this
2361 p.setCatcode('@', catOther);
2365 if (t.cs() == "makeindex") {
2366 // LyX will re-add this if a print index command is found
2371 if (t.cs() == "newindex") {
2372 string const indexname = p.getArg('[', ']');
2373 string const shortcut = p.verbatim_item();
2374 if (!indexname.empty())
2375 h_index[index_number] = indexname;
2377 h_index[index_number] = shortcut;
2378 h_shortcut[index_number] = shortcut;
2384 if (t.cs() == "addbibresource") {
2385 string const options = p.getArg('[', ']');
2386 string const arg = removeExtension(p.getArg('{', '}'));
2387 if (!options.empty()) {
2388 // check if the option contains a bibencoding, if yes, extract it
2389 string::size_type pos = options.find("bibencoding=");
2391 if (pos != string::npos) {
2392 string::size_type i = options.find(',', pos);
2393 if (i == string::npos)
2394 encoding = options.substr(pos + 1);
2396 encoding = options.substr(pos, i - pos);
2397 pos = encoding.find('=');
2398 if (pos == string::npos)
2401 encoding = encoding.substr(pos + 1);
2403 if (!encoding.empty())
2404 biblatex_encodings.push_back(normalize_filename(arg) + ' ' + encoding);
2406 biblatex_bibliographies.push_back(arg);
2410 if (t.cs() == "bibliography") {
2411 vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
2412 biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
2416 if (t.cs() == "RS@ifundefined") {
2417 string const name = p.verbatim_item();
2418 string const body1 = p.verbatim_item();
2419 string const body2 = p.verbatim_item();
2420 // only non-lyxspecific stuff
2421 if (in_lyx_preamble &&
2422 (name == "subsecref" || name == "thmref" || name == "lemref"))
2426 ss << '\\' << t.cs();
2427 ss << '{' << name << '}'
2428 << '{' << body1 << '}'
2429 << '{' << body2 << '}';
2430 h_preamble << ss.str();
2435 if (t.cs() == "AtBeginDocument") {
2436 string const name = p.verbatim_item();
2437 // only non-lyxspecific stuff
2438 if (in_lyx_preamble &&
2439 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
2440 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
2441 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
2442 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
2443 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
2444 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
2445 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
2446 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
2447 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
2448 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
2449 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
2450 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
2451 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
2452 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
2453 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
2457 ss << '\\' << t.cs();
2458 ss << '{' << name << '}';
2459 h_preamble << ss.str();
2464 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
2465 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
2466 || t.cs() == "providecommand" || t.cs() == "providecommandx"
2467 || t.cs() == "DeclareRobustCommand"
2468 || t.cs() == "DeclareRobustCommandx"
2469 || t.cs() == "ProvideTextCommandDefault"
2470 || t.cs() == "DeclareMathAccent") {
2472 if (p.next_token().character() == '*') {
2476 string const name = p.verbatim_item();
2477 string const opt1 = p.getFullOpt();
2478 string const opt2 = p.getFullOpt();
2479 string const body = p.verbatim_item();
2480 // store the in_lyx_preamble setting
2481 bool const was_in_lyx_preamble = in_lyx_preamble;
2483 if (name == "\\rmdefault")
2484 if (is_known(body, known_roman_font_packages)) {
2485 h_font_roman[0] = body;
2487 in_lyx_preamble = true;
2489 if (name == "\\sfdefault")
2490 if (is_known(body, known_sans_font_packages)) {
2491 h_font_sans[0] = body;
2493 in_lyx_preamble = true;
2495 if (name == "\\ttdefault")
2496 if (is_known(body, known_typewriter_font_packages)) {
2497 h_font_typewriter[0] = body;
2499 in_lyx_preamble = true;
2501 if (name == "\\familydefault") {
2502 string family = body;
2503 // remove leading "\"
2504 h_font_default_family = family.erase(0,1);
2506 in_lyx_preamble = true;
2509 // remove LyX-specific definitions that are re-added by LyX
2511 // \lyxline is an ancient command that is converted by tex2lyx into
2512 // a \rule therefore remove its preamble code
2513 if (name == "\\lyxdot" || name == "\\lyxarrow"
2514 || name == "\\lyxline" || name == "\\LyX") {
2516 in_lyx_preamble = true;
2519 // Add the command to the known commands
2520 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
2522 // only non-lyxspecific stuff
2523 if (!in_lyx_preamble) {
2525 ss << '\\' << t.cs();
2528 ss << '{' << name << '}' << opt1 << opt2
2529 << '{' << body << "}";
2530 if (prefixIs(t.cs(), "renew") || !contains(h_preamble.str(), ss.str()))
2531 h_preamble << ss.str();
2533 ostream & out = in_preamble ? h_preamble : os;
2534 out << "\\" << t.cs() << "{" << name << "}"
2535 << opts << "{" << body << "}";
2538 // restore the in_lyx_preamble setting
2539 in_lyx_preamble = was_in_lyx_preamble;
2543 if (t.cs() == "documentclass") {
2544 vector<string>::iterator it;
2545 vector<string> opts = split_options(p.getArg('[', ']'));
2546 handle_opt(opts, known_fontsizes, h_paperfontsize);
2547 delete_opt(opts, known_fontsizes);
2548 // delete "pt" at the end
2549 string::size_type i = h_paperfontsize.find("pt");
2550 if (i != string::npos)
2551 h_paperfontsize.erase(i);
2552 // The documentclass options are always parsed before the options
2553 // of the babel call so that a language cannot overwrite the babel
2555 handle_opt(opts, known_languages, h_language);
2556 delete_opt(opts, known_languages);
2559 if ((it = find(opts.begin(), opts.end(), "fleqn"))
2561 h_is_mathindent = "1";
2564 // formula numbering side
2565 if ((it = find(opts.begin(), opts.end(), "leqno"))
2567 h_math_numbering_side = "left";
2570 else if ((it = find(opts.begin(), opts.end(), "reqno"))
2572 h_math_numbering_side = "right";
2576 // paper orientation
2577 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
2578 h_paperorientation = "landscape";
2582 if ((it = find(opts.begin(), opts.end(), "oneside"))
2587 if ((it = find(opts.begin(), opts.end(), "twoside"))
2593 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
2595 h_papercolumns = "1";
2598 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
2600 h_papercolumns = "2";
2604 // some size options are known to any document classes, other sizes
2605 // are handled by the \geometry command of the geometry package
2606 handle_opt(opts, known_class_paper_sizes, h_papersize);
2607 delete_opt(opts, known_class_paper_sizes);
2608 // the remaining options
2609 h_options = join(opts, ",");
2610 // FIXME This does not work for classes that have a
2611 // different name in LyX than in LaTeX
2612 h_textclass = p.getArg('{', '}');
2617 if (t.cs() == "usepackage") {
2618 string const options = p.getArg('[', ']');
2619 string const name = p.getArg('{', '}');
2620 vector<string> vecnames;
2621 split(name, vecnames, ',');
2622 vector<string>::const_iterator it = vecnames.begin();
2623 vector<string>::const_iterator end = vecnames.end();
2624 for (; it != end; ++it)
2625 handle_package(p, trimSpaceAndEol(*it), options,
2626 in_lyx_preamble, detectEncoding);
2630 if (t.cs() == "inputencoding") {
2631 string const encoding = p.getArg('{','}');
2632 Encoding const * const enc = encodings.fromLaTeXName(
2633 encoding, Encoding::inputenc, true);
2635 if (!detectEncoding)
2636 cerr << "Unknown encoding " << encoding
2637 << ". Ignoring." << std::endl;
2640 h_inputencoding = enc->name();
2641 p.setEncoding(enc->iconvName());
2646 if (t.cs() == "newenvironment") {
2647 string const name = p.getArg('{', '}');
2648 string const opt1 = p.getFullOpt();
2649 string const opt2 = p.getFullOpt();
2650 string const beg = p.verbatim_item();
2651 string const end = p.verbatim_item();
2652 if (!in_lyx_preamble) {
2653 h_preamble << "\\newenvironment{" << name
2654 << '}' << opt1 << opt2 << '{'
2655 << beg << "}{" << end << '}';
2657 add_known_environment(name, opt1, !opt2.empty(),
2658 from_utf8(beg), from_utf8(end));
2662 if (t.cs() == "newtheorem") {
2664 if (p.next_token().character() == '*') {
2668 string const name = p.getArg('{', '}');
2669 string const opt1 = p.getFullOpt();
2670 string const opt2 = p.getFullOpt();
2671 string const body = p.verbatim_item();
2672 string const opt3 = p.getFullOpt();
2673 string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
2675 string const complete = cmd + "{" + name + '}' +
2676 opt1 + opt2 + '{' + body + '}' + opt3;
2678 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
2680 if (!in_lyx_preamble)
2681 h_preamble << complete;
2685 if (t.cs() == "def") {
2686 string name = p.get_token().cs();
2687 // In fact, name may be more than the name:
2688 // In the test case of bug 8116
2689 // name == "csname SF@gobble@opt \endcsname".
2690 // Therefore, we need to use asInput() instead of cs().
2691 while (p.next_token().cat() != catBegin)
2692 name += p.get_token().asInput();
2693 if (!in_lyx_preamble)
2694 h_preamble << "\\def\\" << name << '{'
2695 << p.verbatim_item() << "}";
2699 if (t.cs() == "newcolumntype") {
2700 string const name = p.getArg('{', '}');
2701 trimSpaceAndEol(name);
2703 string opts = p.getOpt();
2704 if (!opts.empty()) {
2705 istringstream is(string(opts, 1));
2708 special_columns_[name[0]] = nargs;
2709 h_preamble << "\\newcolumntype{" << name << "}";
2711 h_preamble << "[" << nargs << "]";
2712 h_preamble << "{" << p.verbatim_item() << "}";
2716 if (t.cs() == "setcounter") {
2717 string const name = p.getArg('{', '}');
2718 string const content = p.getArg('{', '}');
2719 if (name == "secnumdepth")
2720 h_secnumdepth = content;
2721 else if (name == "tocdepth")
2722 h_tocdepth = content;
2724 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
2728 if (t.cs() == "setlength") {
2729 string const name = p.verbatim_item();
2730 string const content = p.verbatim_item();
2731 // the paragraphs are only not indented when \parindent is set to zero
2732 if (name == "\\parindent" && content != "") {
2733 if (content[0] == '0')
2734 h_paragraph_separation = "skip";
2736 h_paragraph_indentation = translate_len(content);
2737 } else if (name == "\\parskip") {
2738 if (content == "\\smallskipamount")
2739 h_defskip = "smallskip";
2740 else if (content == "\\medskipamount")
2741 h_defskip = "medskip";
2742 else if (content == "\\bigskipamount")
2743 h_defskip = "bigskip";
2745 h_defskip = translate_len(content);
2746 } else if (name == "\\mathindent") {
2747 h_mathindentation = translate_len(content);
2749 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2753 if (t.cs() == "onehalfspacing") {
2754 h_spacing = "onehalf";
2758 if (t.cs() == "doublespacing") {
2759 h_spacing = "double";
2763 if (t.cs() == "setstretch") {
2764 h_spacing = "other " + p.verbatim_item();
2768 if (t.cs() == "synctex") {
2769 // the scheme is \synctex=value
2770 // where value can only be "1" or "-1"
2771 h_output_sync = "1";
2772 // there can be any character behind the value (e.g. a linebreak or a '\'
2773 // therefore we extract it char by char
2775 string value = p.get_token().asInput();
2777 value += p.get_token().asInput();
2778 h_output_sync_macro = "\\synctex=" + value;
2782 if (t.cs() == "begin") {
2783 string const name = p.getArg('{', '}');
2784 if (name == "document")
2786 h_preamble << "\\begin{" << name << "}";
2790 if (t.cs() == "geometry") {
2791 vector<string> opts = split_options(p.getArg('{', '}'));
2792 handle_geometry(opts);
2796 if (t.cs() == "definecolor") {
2797 string const color = p.getArg('{', '}');
2798 string const space = p.getArg('{', '}');
2799 string const value = p.getArg('{', '}');
2800 if (color == "document_fontcolor" && space == "rgb") {
2801 RGBColor c(RGBColorFromLaTeX(value));
2802 h_fontcolor = X11hexname(c);
2803 } else if (color == "note_fontcolor" && space == "rgb") {
2804 RGBColor c(RGBColorFromLaTeX(value));
2805 h_notefontcolor = X11hexname(c);
2806 } else if (color == "page_backgroundcolor" && space == "rgb") {
2807 RGBColor c(RGBColorFromLaTeX(value));
2808 h_backgroundcolor = X11hexname(c);
2809 } else if (color == "shadecolor" && space == "rgb") {
2810 RGBColor c(RGBColorFromLaTeX(value));
2811 h_boxbgcolor = X11hexname(c);
2813 h_preamble << "\\definecolor{" << color
2814 << "}{" << space << "}{" << value
2820 if (t.cs() == "bibliographystyle") {
2821 h_biblio_style = p.verbatim_item();
2825 if (t.cs() == "jurabibsetup") {
2826 // FIXME p.getArg('{', '}') is most probably wrong (it
2827 // does not handle nested braces).
2828 // Use p.verbatim_item() instead.
2829 vector<string> jurabibsetup =
2830 split_options(p.getArg('{', '}'));
2831 // add jurabibsetup to the jurabib package options
2832 add_package("jurabib", jurabibsetup);
2833 if (!jurabibsetup.empty()) {
2834 h_preamble << "\\jurabibsetup{"
2835 << join(jurabibsetup, ",") << '}';
2840 if (t.cs() == "hypersetup") {
2841 vector<string> hypersetup =
2842 split_options(p.verbatim_item());
2843 // add hypersetup to the hyperref package options
2844 handle_hyperref(hypersetup);
2845 if (!hypersetup.empty()) {
2846 h_preamble << "\\hypersetup{"
2847 << join(hypersetup, ",") << '}';
2852 if (t.cs() == "includeonly") {
2853 vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
2854 for (auto & iofile : includeonlys) {
2855 string filename(normalize_filename(iofile));
2856 string const path = getMasterFilePath(true);
2857 // We want to preserve relative/absolute filenames,
2858 // therefore path is only used for testing
2859 if (!makeAbsPath(filename, path).exists()) {
2860 // The file extension is probably missing.
2861 // Now try to find it out.
2862 string const tex_name =
2863 find_file(filename, path,
2864 known_tex_extensions);
2865 if (!tex_name.empty())
2866 filename = tex_name;
2869 if (makeAbsPath(filename, path).exists())
2870 fix_child_filename(filename);
2872 cerr << "Warning: Could not find included file '"
2873 << filename << "'." << endl;
2874 outname = changeExtension(filename, "lyx");
2875 h_includeonlys.push_back(outname);
2880 if (is_known(t.cs(), known_if_3arg_commands)) {
2881 // prevent misparsing of \usepackage if it is used
2882 // as an argument (see e.g. our own output of
2883 // \@ifundefined above)
2884 string const arg1 = p.verbatim_item();
2885 string const arg2 = p.verbatim_item();
2886 string const arg3 = p.verbatim_item();
2887 // test case \@ifundefined{date}{}{\date{}}
2888 if (t.cs() == "@ifundefined" && arg1 == "date" &&
2889 arg2.empty() && arg3 == "\\date{}") {
2890 h_suppress_date = "true";
2891 // older tex2lyx versions did output
2892 // \@ifundefined{definecolor}{\usepackage{color}}{}
2893 } else if (t.cs() == "@ifundefined" &&
2894 arg1 == "definecolor" &&
2895 arg2 == "\\usepackage{color}" &&
2897 if (!in_lyx_preamble)
2898 h_preamble << package_beg_sep
2901 << "\\@ifundefined{definecolor}{color}{}"
2904 //\@ifundefined{showcaptionsetup}{}{%
2905 // \PassOptionsToPackage{caption=false}{subfig}}
2906 // that LyX uses for subfloats
2907 } else if (t.cs() == "@ifundefined" &&
2908 arg1 == "showcaptionsetup" && arg2.empty()
2909 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
2911 } else if (!in_lyx_preamble) {
2912 h_preamble << t.asInput()
2913 << '{' << arg1 << '}'
2914 << '{' << arg2 << '}'
2915 << '{' << arg3 << '}';
2920 if (is_known(t.cs(), known_if_commands)) {
2921 // must not parse anything in conditional code, since
2922 // LyX would output the parsed contents unconditionally
2923 if (!in_lyx_preamble)
2924 h_preamble << t.asInput();
2925 handle_if(p, in_lyx_preamble);
2929 if (!t.cs().empty() && !in_lyx_preamble) {
2930 h_preamble << '\\' << t.cs();
2935 // remove the whitespace
2938 // Force textclass if the user wanted it
2939 if (!forceclass.empty())
2940 h_textclass = forceclass;
2941 tc.setName(h_textclass);
2942 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2943 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2946 if (h_papersides.empty()) {
2949 h_papersides = ss.str();
2952 // If the CJK package is used we cannot set the document language from
2953 // the babel options. Instead, we guess which language is used most
2954 // and set this one.
2955 default_language = h_language;
2956 if (is_full_document &&
2957 (auto_packages.find("CJK") != auto_packages.end() ||
2958 auto_packages.find("CJKutf8") != auto_packages.end())) {
2960 h_language = guessLanguage(p, default_language);
2962 if (explicit_babel && h_language != default_language) {
2963 // We set the document language to a CJK language,
2964 // but babel is explicitly called in the user preamble
2965 // without options. LyX will not add the default
2966 // language to the document options if it is either
2967 // english, or no text is set as default language.
2968 // Therefore we need to add a language option explicitly.
2969 // FIXME: It would be better to remove all babel calls
2970 // from the user preamble, but this is difficult
2971 // without re-introducing bug 7861.
2972 if (h_options.empty())
2973 h_options = lyx2babel(default_language);
2975 h_options += ',' + lyx2babel(default_language);
2979 // Finally, set the quote style.
2980 // LyX knows the following quotes styles:
2981 // british, cjk, cjkangle, danish, english, french, german,
2982 // polish, russian, swedish and swiss
2983 // conversion list taken from
2984 // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
2985 // (quotes for kazakh are unknown)
2987 if (is_known(h_language, known_british_quotes_languages))
2988 h_quotes_style = "british";
2990 else if (is_known(h_language, known_cjk_quotes_languages))
2991 h_quotes_style = "cjk";
2993 else if (is_known(h_language, known_cjkangle_quotes_languages))
2994 h_quotes_style = "cjkangle";
2996 else if (is_known(h_language, known_danish_quotes_languages))
2997 h_quotes_style = "danish";
2999 else if (is_known(h_language, known_french_quotes_languages))
3000 h_quotes_style = "french";
3002 else if (is_known(h_language, known_german_quotes_languages))
3003 h_quotes_style = "german";
3005 else if (is_known(h_language, known_polish_quotes_languages))
3006 h_quotes_style = "polish";
3008 else if (is_known(h_language, known_russian_quotes_languages))
3009 h_quotes_style = "russian";
3011 else if (is_known(h_language, known_swedish_quotes_languages))
3012 h_quotes_style = "swedish";
3014 else if (is_known(h_language, known_swiss_quotes_languages))
3015 h_quotes_style = "swiss";
3017 else if (is_known(h_language, known_english_quotes_languages))
3018 h_quotes_style = "english";
3022 string Preamble::parseEncoding(Parser & p, string const & forceclass)
3024 TeX2LyXDocClass dummy;
3025 parse(p, forceclass, true, dummy);
3026 if (h_inputencoding != "auto-legacy" && h_inputencoding != "auto-legacy-plain")
3027 return h_inputencoding;
3032 string babel2lyx(string const & language)
3034 char const * const * where = is_known(language, known_languages);
3036 return known_coded_languages[where - known_languages];
3041 string lyx2babel(string const & language)
3043 char const * const * where = is_known(language, known_coded_languages);
3045 return known_languages[where - known_coded_languages];
3050 string Preamble::polyglossia2lyx(string const & language)
3052 char const * const * where = is_known(language, polyglossia_languages);
3054 return coded_polyglossia_languages[where - polyglossia_languages];
3059 string rgbcolor2code(string const & name)
3061 char const * const * where = is_known(name, known_basic_colors);
3063 // "red", "green" etc
3064 return known_basic_color_codes[where - known_basic_colors];
3066 // "255,0,0", "0,255,0" etc
3067 RGBColor c(RGBColorFromLaTeX(name));
3068 return X11hexname(c);