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", "DejaVuSerif", "DejaVuSerifCondensed", "fourier",
146 "garamondx", "libertine", "libertineRoman", "libertine-type1", "lmodern", "mathdesign", "mathpazo",
147 "mathptmx", "MinionPro", "newcent", "noto", "noto-serif", "PTSerif", "tgbonum", "tgchorus",
148 "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", "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", "fourier", "libertineMono", "libertineMono-type1", "lmodern",
157 "mathpazo", "mathptmx", "newcent", "noto", "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;
950 // font uses old-style figure
951 h_font_roman_osf = "true";
953 if (name == "noto") {
954 h_font_roman[0] = "NotoSerif-TLF";
955 // noto as typewriter is handled in handling of \ttdefault
956 // special cases are handled in handling of \rmdefault and \sfdefault
957 for (auto const & opt : allopts) {
959 h_font_roman[0] = "NotoSerif-TLF";
963 h_font_sans[0] = "NotoSans-TLF";
967 h_font_roman[0] = "NotoSerif-TLF";
968 h_font_sans[0] = "NotoSans-TLF";
972 h_font_roman_osf = "true";
981 if (name == "paratype") {
982 // in this case all fonts are ParaType
983 h_font_roman[0] = "PTSerif-TLF";
984 h_font_sans[0] = "default";
985 h_font_typewriter[0] = "default";
988 if (name == "PTSerif")
989 h_font_roman[0] = "PTSerif-TLF";
991 if (name == "XCharter") {
992 h_font_roman[0] = "xcharter";
993 for (auto const & opt : allopts) {
995 h_font_roman_osf = "true";
1003 h_font_roman_opts = xopts;
1007 if (name == "plex-serif") {
1008 h_font_roman[0] = "IBMPlexSerif";
1009 for (auto const & opt : allopts) {
1010 if (opt == "thin") {
1011 h_font_roman[0] = "IBMPlexSerifThin";
1014 if (opt == "extralight") {
1015 h_font_roman[0] = "IBMPlexSerifExtraLight";
1018 if (opt == "light") {
1019 h_font_roman[0] = "IBMPlexSerifLight";
1022 if (opt == "semibold") {
1023 h_font_roman[0] = "IBMPlexSerifSemibold";
1031 h_font_roman_opts = xopts;
1035 if (name == "noto-serif") {
1036 h_font_roman[0] = "NotoSerifRegular";
1037 for (auto const & opt : allopts) {
1038 if (opt == "regular")
1041 if (opt == "thin") {
1042 h_font_roman[0] = "NotoSerifThin";
1045 if (opt == "extralight") {
1046 h_font_roman[0] = "NotoSerifExtralight";
1049 if (opt == "light") {
1050 h_font_roman[0] = "NotoSerifLight";
1053 if (opt == "medium") {
1054 h_font_roman[0] = "NotoSerifMedium";
1062 h_font_roman_opts = xopts;
1066 if (name == "sourceserifpro") {
1067 h_font_roman[0] = "ADOBESourceSerifPro";
1068 for (auto const & opt : allopts) {
1070 h_font_roman_osf = "true";
1078 h_font_roman_opts = xopts;
1086 // By default, we use the package name as LyX font name,
1087 // so this only needs to be reset if these names differ.
1088 // Also, we handle the scaling option here generally.
1089 if (is_known(name, known_sans_font_packages)) {
1090 h_font_sans[0] = name;
1091 if (contains(opts, "scale")) {
1092 vector<string>::const_iterator it = allopts.begin();
1093 for (; it != allopts.end() ; ++it) {
1094 string const opt = *it;
1095 if (prefixIs(opt, "scaled=") || prefixIs(opt, "scale=")) {
1096 if (scale_as_percentage(opt, h_font_sf_scale[0])) {
1105 if (name == "biolinum" || name == "biolinum-type1") {
1106 h_font_sans[0] = "biolinum";
1107 for (auto const & opt : allopts) {
1108 if (prefixIs(opt, "osf")) {
1109 h_font_sans_osf = "true";
1117 h_font_sans_opts = xopts;
1121 if (name == "cantarell") {
1122 for (auto const & opt : allopts) {
1123 if (opt == "defaultsans")
1125 if (prefixIs(opt, "oldstyle")) {
1126 h_font_sans_osf = "true";
1134 h_font_sans_opts = xopts;
1138 if (name == "Chivo") {
1139 for (auto const & opt : allopts) {
1140 if (opt == "thin") {
1141 h_font_roman[0] = "ChivoThin";
1144 if (opt == "light") {
1145 h_font_roman[0] = "ChivoLight";
1148 if (opt == "regular") {
1149 h_font_roman[0] = "Chivo";
1152 if (opt == "medium") {
1153 h_font_roman[0] = "ChivoMedium";
1156 if (prefixIs(opt, "oldstyle")) {
1157 h_font_sans_osf = "true";
1165 h_font_sans_opts = xopts;
1169 if (name == "PTSans") {
1170 h_font_sans[0] = "PTSans-TLF";
1173 if (name == "FiraSans") {
1174 h_font_sans_osf = "true";
1175 for (auto const & opt : allopts) {
1176 if (opt == "book") {
1177 h_font_sans[0] = "FiraSansBook";
1180 if (opt == "thin") {
1183 if (opt == "extralight") {
1184 h_font_sans[0] = "FiraSansExtralight";
1187 if (opt == "light") {
1188 h_font_sans[0] = "FiraSansLight";
1191 if (opt == "ultralight") {
1192 h_font_sans[0] = "FiraSansUltralight";
1195 if (opt == "thin") {
1196 h_font_sans[0] = "FiraSansThin";
1199 if (opt == "lf" || opt == "lining") {
1200 h_font_sans_osf = "false";
1208 h_font_sans_opts = xopts;
1212 if (name == "plex-sans") {
1213 h_font_sans[0] = "IBMPlexSans";
1214 for (auto const & opt : allopts) {
1215 if (opt == "condensed") {
1216 h_font_sans[0] = "IBMPlexSansCondensed";
1219 if (opt == "thin") {
1220 h_font_sans[0] = "IBMPlexSansThin";
1223 if (opt == "extralight") {
1224 h_font_sans[0] = "IBMPlexSansExtraLight";
1227 if (opt == "light") {
1228 h_font_sans[0] = "IBMPlexSansLight";
1231 if (opt == "semibold") {
1232 h_font_sans[0] = "IBMPlexSansSemibold";
1240 h_font_sans_opts = xopts;
1244 if (name == "noto-sans") {
1245 h_font_sans[0] = "NotoSansRegular";
1246 for (auto const & opt : allopts) {
1247 if (opt == "regular")
1249 if (opt == "medium") {
1250 h_font_sans[0] = "NotoSansMedium";
1253 if (opt == "thin") {
1254 h_font_sans[0] = "NotoSansThin";
1257 if (opt == "extralight") {
1258 h_font_sans[0] = "NotoSansExtralight";
1261 if (opt == "light") {
1262 h_font_sans[0] = "NotoSansLight";
1266 h_font_sans_osf = "true";
1274 h_font_sans_opts = xopts;
1278 if (name == "sourcesanspro") {
1279 h_font_sans[0] = "ADOBESourceSansPro";
1280 for (auto const & opt : allopts) {
1282 h_font_sans_osf = "true";
1290 h_font_sans_opts = xopts;
1298 // By default, we use the package name as LyX font name,
1299 // so this only needs to be reset if these names differ.
1300 // Also, we handle the scaling option here generally.
1301 // Note: fourier can be set as roman font _only_
1302 // fourier as typewriter is handled in handling of \ttdefault
1303 if (is_known(name, known_typewriter_font_packages) && name != "fourier") {
1304 h_font_typewriter[0] = name;
1305 if (contains(opts, "scale")) {
1306 vector<string>::const_iterator it = allopts.begin();
1307 for (; it != allopts.end() ; ++it) {
1308 string const opt = *it;
1309 if (prefixIs(opt, "scaled=") || prefixIs(opt, "scale=")) {
1310 if (scale_as_percentage(opt, h_font_tt_scale[0])) {
1319 if (name == "libertineMono" || name == "libertineMono-type1")
1320 h_font_typewriter[0] = "libertine-mono";
1322 if (name == "FiraMono") {
1323 h_font_typewriter_osf = "true";
1324 for (auto const & opt : allopts) {
1325 if (opt == "lf" || opt == "lining") {
1326 h_font_typewriter_osf = "false";
1334 h_font_typewriter_opts = xopts;
1338 if (name == "PTMono")
1339 h_font_typewriter[0] = "PTMono-TLF";
1341 if (name == "plex-mono") {
1342 h_font_typewriter[0] = "IBMPlexMono";
1343 for (auto const & opt : allopts) {
1344 if (opt == "thin") {
1345 h_font_typewriter[0] = "IBMPlexMonoThin";
1348 if (opt == "extralight") {
1349 h_font_typewriter[0] = "IBMPlexMonoExtraLight";
1352 if (opt == "light") {
1353 h_font_typewriter[0] = "IBMPlexMonoLight";
1356 if (opt == "semibold"){
1357 h_font_typewriter[0] = "IBMPlexMonoSemibold";
1365 h_font_typewriter_opts = xopts;
1369 if (name == "noto-mono") {
1370 h_font_typewriter[0] = "NotoMonoRegular";
1371 for (auto const & opt : allopts) {
1372 if (opt == "regular")
1379 h_font_typewriter_opts = xopts;
1383 if (name == "sourcecodepro") {
1384 h_font_typewriter[0] = "ADOBESourceCodePro";
1385 for (auto const & opt : allopts) {
1387 h_font_typewriter_osf = "true";
1395 h_font_typewriter_opts = xopts;
1403 // By default, we use the package name as LyX font name,
1404 // so this only needs to be reset if these names differ.
1405 if (is_known(name, known_math_font_packages))
1406 h_font_math[0] = name;
1408 if (name == "newtxmath") {
1410 h_font_math[0] = "newtxmath";
1411 else if (opts == "garamondx")
1412 h_font_math[0] = "garamondx-ntxm";
1413 else if (opts == "libertine")
1414 h_font_math[0] = "libertine-ntxm";
1415 else if (opts == "minion")
1416 h_font_math[0] = "minion-ntxm";
1417 else if (opts == "cochineal")
1418 h_font_math[0] = "cochineal-ntxm";
1421 if (name == "iwona")
1423 h_font_math[0] = "iwona-math";
1425 if (name == "kurier")
1427 h_font_math[0] = "kurier-math";
1429 // after the detection and handling of special cases, we can remove the
1430 // fonts, otherwise they would appear in the preamble, see bug #7856
1431 if (is_known(name, known_roman_font_packages) || is_known(name, known_sans_font_packages)
1432 || is_known(name, known_typewriter_font_packages) || is_known(name, known_math_font_packages))
1434 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
1435 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
1436 name == "esint" || name == "mhchem" || name == "mathdots" ||
1437 name == "mathtools" || name == "stackrel" ||
1438 name == "stmaryrd" || name == "undertilde") {
1439 h_use_packages[name] = "2";
1440 registerAutomaticallyLoadedPackage(name);
1443 else if (name == "babel") {
1444 h_language_package = "default";
1445 // One might think we would have to do nothing if babel is loaded
1446 // without any options to prevent pollution of the preamble with this
1447 // babel call in every roundtrip.
1448 // But the user could have defined babel-specific things afterwards. So
1449 // we need to keep it in the preamble to prevent cases like bug #7861.
1450 if (!opts.empty()) {
1451 // check if more than one option was used - used later for inputenc
1452 if (options.begin() != options.end() - 1)
1453 one_language = false;
1454 // babel takes the last language of the option of its \usepackage
1455 // call as document language. If there is no such language option, the
1456 // last language in the documentclass options is used.
1457 handle_opt(options, known_languages, h_language);
1458 // translate the babel name to a LyX name
1459 h_language = babel2lyx(h_language);
1460 if (h_language == "japanese") {
1461 // For Japanese, the encoding isn't indicated in the source
1462 // file, and there's really not much we can do. We could
1463 // 1) offer a list of possible encodings to choose from, or
1464 // 2) determine the encoding of the file by inspecting it.
1465 // For the time being, we leave the encoding alone so that
1466 // we don't get iconv errors when making a wrong guess, and
1467 // we will output a note at the top of the document
1468 // explaining what to do.
1469 Encoding const * const enc = encodings.fromIconvName(
1470 p.getEncoding(), Encoding::japanese, false);
1472 h_inputencoding = enc->name();
1473 is_nonCJKJapanese = true;
1474 // in this case babel can be removed from the preamble
1475 registerAutomaticallyLoadedPackage("babel");
1477 // If babel is called with options, LyX puts them by default into the
1478 // document class options. This works for most languages, except
1479 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
1480 // perhaps in future others.
1481 // Therefore keep the babel call as it is as the user might have
1483 string const babelcall = "\\usepackage[" + opts + "]{babel}\n";
1484 if (!contains(h_preamble.str(), babelcall))
1485 h_preamble << babelcall;
1487 delete_opt(options, known_languages);
1489 if (!contains(h_preamble.str(), "\\usepackage{babel}\n"))
1490 h_preamble << "\\usepackage{babel}\n";
1491 explicit_babel = true;
1495 else if (name == "polyglossia") {
1496 h_language_package = "default";
1497 h_default_output_format = "pdf4";
1498 h_use_non_tex_fonts = true;
1500 registerAutomaticallyLoadedPackage("xunicode");
1501 if (h_inputencoding == "auto-legacy")
1502 p.setEncoding("UTF-8");
1505 else if (name == "CJK") {
1506 // set the encoding to "auto-legacy" because it might be set to "auto-legacy-plain" by the babel handling
1507 // and this would not be correct for CJK
1508 if (h_inputencoding == "auto-legacy-plain")
1509 h_inputencoding = "auto-legacy";
1510 registerAutomaticallyLoadedPackage("CJK");
1513 else if (name == "CJKutf8") {
1514 h_inputencoding = "utf8-cjk";
1515 p.setEncoding("UTF-8");
1516 registerAutomaticallyLoadedPackage("CJKutf8");
1519 else if (name == "fontenc") {
1520 h_fontencoding = getStringFromVector(options, ",");
1524 else if (name == "inputenc" || name == "luainputenc") {
1525 // h_inputencoding is only set when there is not more than one
1526 // inputenc option because otherwise h_inputencoding must be
1527 // set to "auto-legacy" (the default encodings of the document's languages)
1528 // Therefore check that exactly one option is passed to inputenc.
1529 // It is also only set when there is not more than one babel
1531 if (!options.empty()) {
1532 string const encoding = options.back();
1533 Encoding const * const enc = encodings.fromLaTeXName(
1534 encoding, Encoding::inputenc, true);
1536 if (!detectEncoding)
1537 cerr << "Unknown encoding " << encoding
1538 << ". Ignoring." << std::endl;
1540 if (!enc->unsafe() && options.size() == 1 && one_language == true)
1541 h_inputencoding = enc->name();
1542 p.setEncoding(enc->iconvName());
1548 else if (name == "srcltx") {
1549 h_output_sync = "1";
1550 if (!opts.empty()) {
1551 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
1554 h_output_sync_macro = "\\usepackage{srcltx}";
1557 else if (is_known(name, known_old_language_packages)) {
1558 // known language packages from the times before babel
1559 // if they are found and not also babel, they will be used as
1560 // custom language package
1561 h_language_package = "\\usepackage{" + name + "}";
1564 else if (name == "lyxskak") {
1565 // ignore this and its options
1566 const char * const o[] = {"ps", "mover", 0};
1567 delete_opt(options, o);
1570 else if (is_known(name, known_lyx_packages) && options.empty()) {
1571 if (name == "splitidx")
1572 h_use_indices = "true";
1573 else if (name == "minted")
1574 h_use_minted = true;
1575 else if (name == "refstyle")
1576 h_use_refstyle = true;
1577 else if (name == "prettyref")
1578 h_use_refstyle = false;
1579 if (!in_lyx_preamble) {
1580 h_preamble << package_beg_sep << name
1581 << package_mid_sep << "\\usepackage{"
1583 if (p.next_token().cat() == catNewline ||
1584 (p.next_token().cat() == catSpace &&
1585 p.next_next_token().cat() == catNewline))
1587 h_preamble << package_end_sep;
1591 else if (name == "geometry")
1592 handle_geometry(options);
1594 else if (name == "subfig")
1595 ; // ignore this FIXME: Use the package separator mechanism instead
1597 else if (char const * const * where = is_known(name, known_languages))
1598 h_language = known_coded_languages[where - known_languages];
1600 else if (name == "natbib") {
1601 h_biblio_style = "plainnat";
1602 h_cite_engine = "natbib";
1603 h_cite_engine_type = "authoryear";
1604 vector<string>::iterator it =
1605 find(options.begin(), options.end(), "authoryear");
1606 if (it != options.end())
1609 it = find(options.begin(), options.end(), "numbers");
1610 if (it != options.end()) {
1611 h_cite_engine_type = "numerical";
1615 if (!options.empty())
1616 h_biblio_options = join(options, ",");
1619 else if (name == "biblatex") {
1620 h_biblio_style = "plainnat";
1621 h_cite_engine = "biblatex";
1622 h_cite_engine_type = "authoryear";
1624 vector<string>::iterator it =
1625 find(options.begin(), options.end(), "natbib");
1626 if (it != options.end()) {
1628 h_cite_engine = "biblatex-natbib";
1630 opt = process_keyval_opt(options, "natbib");
1632 h_cite_engine = "biblatex-natbib";
1634 opt = process_keyval_opt(options, "style");
1636 h_biblatex_citestyle = opt;
1637 h_biblatex_bibstyle = opt;
1639 opt = process_keyval_opt(options, "citestyle");
1641 h_biblatex_citestyle = opt;
1642 opt = process_keyval_opt(options, "bibstyle");
1644 h_biblatex_bibstyle = opt;
1646 opt = process_keyval_opt(options, "refsection");
1648 if (opt == "none" || opt == "part"
1649 || opt == "chapter" || opt == "section"
1650 || opt == "subsection")
1653 cerr << "Ignoring unkown refesection value '"
1656 opt = process_keyval_opt(options, "bibencoding");
1659 if (!options.empty()) {
1660 h_biblio_options = join(options, ",");
1665 else if (name == "jurabib") {
1666 h_biblio_style = "jurabib";
1667 h_cite_engine = "jurabib";
1668 h_cite_engine_type = "authoryear";
1669 if (!options.empty())
1670 h_biblio_options = join(options, ",");
1673 else if (name == "bibtopic")
1674 h_use_bibtopic = "true";
1676 else if (name == "chapterbib")
1677 h_multibib = "child";
1679 else if (name == "hyperref")
1680 handle_hyperref(options);
1682 else if (name == "algorithm2e") {
1683 // Load "algorithm2e" module
1684 addModule("algorithm2e");
1685 // Add the package options to the global document options
1686 if (!options.empty()) {
1687 if (h_options.empty())
1688 h_options = join(options, ",");
1690 h_options += ',' + join(options, ",");
1693 else if (name == "microtype") {
1694 //we internally support only microtype without params
1695 if (options.empty())
1696 h_use_microtype = "true";
1698 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1701 else if (name == "lineno") {
1702 h_use_lineno = "true";
1703 if (!options.empty()) {
1704 h_lineno_options = join(options, ",");
1709 else if (!in_lyx_preamble) {
1710 if (options.empty())
1711 h_preamble << "\\usepackage{" << name << '}';
1713 h_preamble << "\\usepackage[" << opts << "]{"
1717 if (p.next_token().cat() == catNewline ||
1718 (p.next_token().cat() == catSpace &&
1719 p.next_next_token().cat() == catNewline))
1723 // We need to do something with the options...
1724 if (!options.empty() && !detectEncoding)
1725 cerr << "Ignoring options '" << join(options, ",")
1726 << "' of package " << name << '.' << endl;
1728 // remove the whitespace
1733 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1736 Token t = p.get_token();
1737 if (t.cat() == catEscape &&
1738 is_known(t.cs(), known_if_commands))
1739 handle_if(p, in_lyx_preamble);
1741 if (!in_lyx_preamble)
1742 h_preamble << t.asInput();
1743 if (t.cat() == catEscape && t.cs() == "fi")
1750 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1752 if (contains(h_float_placement, "H"))
1753 registerAutomaticallyLoadedPackage("float");
1754 if (h_spacing != "single" && h_spacing != "default")
1755 registerAutomaticallyLoadedPackage("setspace");
1756 if (h_use_packages["amsmath"] == "2") {
1757 // amsbsy and amstext are already provided by amsmath
1758 registerAutomaticallyLoadedPackage("amsbsy");
1759 registerAutomaticallyLoadedPackage("amstext");
1762 // output the LyX file settings
1763 // Important: Keep the version formatting in sync with LyX and
1764 // lyx2lyx (bug 7951)
1765 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1766 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1767 << lyx_version_minor << '\n'
1768 << "\\lyxformat " << LYX_FORMAT << '\n'
1769 << "\\begin_document\n"
1770 << "\\begin_header\n"
1771 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1772 << "\\origin " << origin << "\n"
1773 << "\\textclass " << h_textclass << "\n";
1774 string const raw = subdoc ? empty_string() : h_preamble.str();
1776 os << "\\begin_preamble\n";
1777 for (string::size_type i = 0; i < raw.size(); ++i) {
1778 if (raw[i] == package_beg_sep) {
1779 // Here follows some package loading code that
1780 // must be skipped if the package is loaded
1782 string::size_type j = raw.find(package_mid_sep, i);
1783 if (j == string::npos)
1785 string::size_type k = raw.find(package_end_sep, j);
1786 if (k == string::npos)
1788 string const package = raw.substr(i + 1, j - i - 1);
1789 string const replacement = raw.substr(j + 1, k - j - 1);
1790 if (auto_packages.find(package) == auto_packages.end())
1796 os << "\n\\end_preamble\n";
1798 if (!h_options.empty())
1799 os << "\\options " << h_options << "\n";
1800 os << "\\use_default_options " << h_use_default_options << "\n";
1801 if (!used_modules.empty()) {
1802 os << "\\begin_modules\n";
1803 vector<string>::const_iterator const end = used_modules.end();
1804 vector<string>::const_iterator it = used_modules.begin();
1805 for (; it != end; ++it)
1807 os << "\\end_modules\n";
1809 if (!h_includeonlys.empty()) {
1810 os << "\\begin_includeonly\n";
1811 for (auto const & iofile : h_includeonlys)
1812 os << iofile << '\n';
1813 os << "\\end_includeonly\n";
1815 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1816 << "\\language " << h_language << "\n"
1817 << "\\language_package " << h_language_package << "\n"
1818 << "\\inputencoding " << h_inputencoding << "\n"
1819 << "\\fontencoding " << h_fontencoding << "\n"
1820 << "\\font_roman \"" << h_font_roman[0]
1821 << "\" \"" << h_font_roman[1] << "\"\n"
1822 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1823 << "\\font_typewriter \"" << h_font_typewriter[0]
1824 << "\" \"" << h_font_typewriter[1] << "\"\n"
1825 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1826 << "\\font_default_family " << h_font_default_family << "\n"
1827 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1828 << "\\font_sc " << h_font_sc << "\n"
1829 << "\\font_roman_osf " << h_font_roman_osf << "\n"
1830 << "\\font_sans_osf " << h_font_sans_osf << "\n"
1831 << "\\font_typewriter_osf " << h_font_typewriter_osf << "\n";
1832 if (!h_font_roman_opts.empty())
1833 os << "\\font_roman_opts \"" << h_font_roman_opts << "\"" << '\n';
1834 os << "\\font_sf_scale " << h_font_sf_scale[0]
1835 << ' ' << h_font_sf_scale[1] << '\n';
1836 if (!h_font_sans_opts.empty())
1837 os << "\\font_sans_opts \"" << h_font_sans_opts << "\"" << '\n';
1838 os << "\\font_tt_scale " << h_font_tt_scale[0]
1839 << ' ' << h_font_tt_scale[1] << '\n';
1840 if (!h_font_cjk.empty())
1841 os << "\\font_cjk " << h_font_cjk << '\n';
1842 if (!h_font_typewriter_opts.empty())
1843 os << "\\font_typewriter_opts \"" << h_font_typewriter_opts << "\"" << '\n';
1844 os << "\\use_microtype " << h_use_microtype << '\n'
1845 << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
1846 << "\\graphics " << h_graphics << '\n'
1847 << "\\default_output_format " << h_default_output_format << "\n"
1848 << "\\output_sync " << h_output_sync << "\n";
1849 if (h_output_sync == "1")
1850 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1851 os << "\\bibtex_command " << h_bibtex_command << "\n"
1852 << "\\index_command " << h_index_command << "\n";
1853 if (!h_float_placement.empty())
1854 os << "\\float_placement " << h_float_placement << "\n";
1855 os << "\\paperfontsize " << h_paperfontsize << "\n"
1856 << "\\spacing " << h_spacing << "\n"
1857 << "\\use_hyperref " << h_use_hyperref << '\n';
1858 if (h_use_hyperref == "true") {
1859 if (!h_pdf_title.empty())
1860 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1861 if (!h_pdf_author.empty())
1862 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1863 if (!h_pdf_subject.empty())
1864 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1865 if (!h_pdf_keywords.empty())
1866 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1867 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1868 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1869 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1870 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1871 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1872 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1873 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1874 "\\pdf_backref " << h_pdf_backref << "\n"
1875 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1876 if (!h_pdf_pagemode.empty())
1877 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1878 if (!h_pdf_quoted_options.empty())
1879 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1881 os << "\\papersize " << h_papersize << "\n"
1882 << "\\use_geometry " << h_use_geometry << '\n';
1883 for (map<string, string>::const_iterator it = h_use_packages.begin();
1884 it != h_use_packages.end(); ++it)
1885 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1886 os << "\\cite_engine " << h_cite_engine << '\n'
1887 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1888 << "\\biblio_style " << h_biblio_style << "\n"
1889 << "\\use_bibtopic " << h_use_bibtopic << "\n";
1890 if (!h_biblio_options.empty())
1891 os << "\\biblio_options " << h_biblio_options << "\n";
1892 if (!h_biblatex_bibstyle.empty())
1893 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
1894 if (!h_biblatex_citestyle.empty())
1895 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
1896 if (!h_multibib.empty())
1897 os << "\\multibib " << h_multibib << "\n";
1898 os << "\\use_indices " << h_use_indices << "\n"
1899 << "\\paperorientation " << h_paperorientation << '\n'
1900 << "\\suppress_date " << h_suppress_date << '\n'
1901 << "\\justification " << h_justification << '\n'
1902 << "\\use_refstyle " << h_use_refstyle << '\n'
1903 << "\\use_minted " << h_use_minted << '\n'
1904 << "\\use_lineno " << h_use_lineno << '\n';
1905 if (!h_lineno_options.empty())
1906 os << "\\lineno_options " << h_lineno_options << '\n';
1907 if (!h_fontcolor.empty())
1908 os << "\\fontcolor " << h_fontcolor << '\n';
1909 if (!h_notefontcolor.empty())
1910 os << "\\notefontcolor " << h_notefontcolor << '\n';
1911 if (!h_backgroundcolor.empty())
1912 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1913 if (!h_boxbgcolor.empty())
1914 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1915 if (index_number != 0)
1916 for (int i = 0; i < index_number; i++) {
1917 os << "\\index " << h_index[i] << '\n'
1918 << "\\shortcut " << h_shortcut[i] << '\n'
1919 << "\\color " << h_color << '\n'
1923 os << "\\index " << h_index[0] << '\n'
1924 << "\\shortcut " << h_shortcut[0] << '\n'
1925 << "\\color " << h_color << '\n'
1929 << "\\secnumdepth " << h_secnumdepth << "\n"
1930 << "\\tocdepth " << h_tocdepth << "\n"
1931 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1932 if (h_paragraph_separation == "skip")
1933 os << "\\defskip " << h_defskip << "\n";
1935 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1936 os << "\\is_math_indent " << h_is_mathindent << "\n";
1937 if (!h_mathindentation.empty())
1938 os << "\\math_indentation " << h_mathindentation << "\n";
1939 os << "\\math_numbering_side " << h_math_numbering_side << "\n";
1940 os << "\\quotes_style " << h_quotes_style << "\n"
1941 << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
1942 << "\\papercolumns " << h_papercolumns << "\n"
1943 << "\\papersides " << h_papersides << "\n"
1944 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1945 if (!h_listings_params.empty())
1946 os << "\\listings_params " << h_listings_params << "\n";
1947 os << "\\tracking_changes " << h_tracking_changes << "\n"
1948 << "\\output_changes " << h_output_changes << "\n"
1949 << "\\html_math_output " << h_html_math_output << "\n"
1950 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1951 << "\\html_be_strict " << h_html_be_strict << "\n"
1953 << "\\end_header\n\n"
1954 << "\\begin_body\n";
1959 void Preamble::parse(Parser & p, string const & forceclass,
1960 TeX2LyXDocClass & tc)
1962 // initialize fixed types
1963 special_columns_['D'] = 3;
1964 parse(p, forceclass, false, tc);
1968 void Preamble::parse(Parser & p, string const & forceclass,
1969 bool detectEncoding, TeX2LyXDocClass & tc)
1971 bool is_full_document = false;
1972 bool is_lyx_file = false;
1973 bool in_lyx_preamble = false;
1975 // determine whether this is a full document or a fragment for inclusion
1977 Token const & t = p.get_token();
1979 if (t.cat() == catEscape && t.cs() == "documentclass") {
1980 is_full_document = true;
1986 if (detectEncoding && !is_full_document)
1989 while (is_full_document && p.good()) {
1990 if (detectEncoding && h_inputencoding != "auto-legacy" &&
1991 h_inputencoding != "auto-legacy-plain")
1994 Token const & t = p.get_token();
1997 if (!detectEncoding)
1998 cerr << "t: " << t << '\n';
2004 if (!in_lyx_preamble &&
2005 (t.cat() == catLetter ||
2006 t.cat() == catSuper ||
2007 t.cat() == catSub ||
2008 t.cat() == catOther ||
2009 t.cat() == catMath ||
2010 t.cat() == catActive ||
2011 t.cat() == catBegin ||
2012 t.cat() == catEnd ||
2013 t.cat() == catAlign ||
2014 t.cat() == catParameter)) {
2015 h_preamble << t.cs();
2019 if (!in_lyx_preamble &&
2020 (t.cat() == catSpace || t.cat() == catNewline)) {
2021 h_preamble << t.asInput();
2025 if (t.cat() == catComment) {
2026 static regex const islyxfile("%% LyX .* created this file");
2027 static regex const usercommands("User specified LaTeX commands");
2029 string const comment = t.asInput();
2031 // magically switch encoding default if it looks like XeLaTeX
2032 static string const magicXeLaTeX =
2033 "% This document must be compiled with XeLaTeX ";
2034 if (comment.size() > magicXeLaTeX.size()
2035 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
2036 && h_inputencoding == "auto-legacy") {
2037 if (!detectEncoding)
2038 cerr << "XeLaTeX comment found, switching to UTF8\n";
2039 h_inputencoding = "utf8";
2042 if (regex_search(comment, sub, islyxfile)) {
2044 in_lyx_preamble = true;
2045 } else if (is_lyx_file
2046 && regex_search(comment, sub, usercommands))
2047 in_lyx_preamble = false;
2048 else if (!in_lyx_preamble)
2049 h_preamble << t.asInput();
2053 if (t.cs() == "PassOptionsToPackage") {
2054 string const poptions = p.getArg('{', '}');
2055 string const package = p.verbatim_item();
2056 extra_package_options_.insert(make_pair(package, poptions));
2060 if (t.cs() == "pagestyle") {
2061 h_paperpagestyle = p.verbatim_item();
2065 if (t.cs() == "setdefaultlanguage") {
2067 // We don't yet care about non-language variant options
2068 // because LyX doesn't support this yet, see bug #8214
2070 string langopts = p.getOpt();
2071 // check if the option contains a variant, if yes, extract it
2072 string::size_type pos_var = langopts.find("variant");
2073 string::size_type i = langopts.find(',', pos_var);
2074 string::size_type k = langopts.find('=', pos_var);
2075 if (pos_var != string::npos){
2077 if (i == string::npos)
2078 variant = langopts.substr(k + 1, langopts.length() - k - 2);
2080 variant = langopts.substr(k + 1, i - k - 1);
2081 h_language = variant;
2085 h_language = p.verbatim_item();
2086 //finally translate the poyglossia name to a LyX name
2087 h_language = polyglossia2lyx(h_language);
2091 if (t.cs() == "setotherlanguage") {
2092 // We don't yet care about the option because LyX doesn't
2093 // support this yet, see bug #8214
2094 p.hasOpt() ? p.getOpt() : string();
2099 if (t.cs() == "setmainfont") {
2100 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2101 h_font_roman[1] = p.getArg('{', '}');
2102 if (!fontopts.empty()) {
2103 vector<string> opts = getVectorFromString(fontopts);
2105 for (auto const & opt : opts) {
2106 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2109 if (!fontopts.empty())
2113 h_font_roman_opts = fontopts;
2118 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
2119 // LyX currently only supports the scale option
2120 string scale, fontopts;
2122 fontopts = p.getArg('[', ']');
2123 if (!fontopts.empty()) {
2124 vector<string> opts = getVectorFromString(fontopts);
2126 for (auto const & opt : opts) {
2127 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2130 if (prefixIs(opt, "Scale=")) {
2131 scale_as_percentage(opt, scale);
2134 if (!fontopts.empty())
2140 if (t.cs() == "setsansfont") {
2142 h_font_sf_scale[1] = scale;
2143 h_font_sans[1] = p.getArg('{', '}');
2144 if (!fontopts.empty())
2145 h_font_sans_opts = fontopts;
2148 h_font_tt_scale[1] = scale;
2149 h_font_typewriter[1] = p.getArg('{', '}');
2150 if (!fontopts.empty())
2151 h_font_typewriter_opts = fontopts;
2156 if (t.cs() == "babelfont") {
2158 h_use_non_tex_fonts = true;
2159 h_language_package = "babel";
2160 if (h_inputencoding == "auto-legacy")
2161 p.setEncoding("UTF-8");
2162 // we don't care about the lang option
2163 string const lang = p.hasOpt() ? p.getArg('[', ']') : string();
2164 string const family = p.getArg('{', '}');
2165 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2166 string const fontname = p.getArg('{', '}');
2167 if (lang.empty() && family == "rm") {
2168 h_font_roman[1] = fontname;
2169 if (!fontopts.empty()) {
2170 vector<string> opts = getVectorFromString(fontopts);
2172 for (auto const & opt : opts) {
2173 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2176 if (!fontopts.empty())
2180 h_font_roman_opts = fontopts;
2183 } else if (lang.empty() && (family == "sf" || family == "tt")) {
2185 if (!fontopts.empty()) {
2186 vector<string> opts = getVectorFromString(fontopts);
2188 for (auto const & opt : opts) {
2189 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2192 if (prefixIs(opt, "Scale=")) {
2193 scale_as_percentage(opt, scale);
2196 if (!fontopts.empty())
2201 if (family == "sf") {
2203 h_font_sf_scale[1] = scale;
2204 h_font_sans[1] = fontname;
2205 if (!fontopts.empty())
2206 h_font_sans_opts = fontopts;
2209 h_font_tt_scale[1] = scale;
2210 h_font_typewriter[1] = fontname;
2211 if (!fontopts.empty())
2212 h_font_typewriter_opts = fontopts;
2216 // not rm, sf or tt or lang specific
2217 h_preamble << '\\' << t.cs();
2219 h_preamble << '[' << lang << ']';
2220 h_preamble << '{' << family << '}';
2221 if (!fontopts.empty())
2222 h_preamble << '[' << fontopts << ']';
2223 h_preamble << '{' << fontname << '}' << '\n';
2228 if (t.cs() == "date") {
2229 string argument = p.getArg('{', '}');
2230 if (argument.empty())
2231 h_suppress_date = "true";
2233 h_preamble << t.asInput() << '{' << argument << '}';
2237 if (t.cs() == "color") {
2238 string const space =
2239 (p.hasOpt() ? p.getOpt() : string());
2240 string argument = p.getArg('{', '}');
2241 // check the case that a standard color is used
2242 if (space.empty() && is_known(argument, known_basic_colors)) {
2243 h_fontcolor = rgbcolor2code(argument);
2244 registerAutomaticallyLoadedPackage("color");
2245 } else if (space.empty() && argument == "document_fontcolor")
2246 registerAutomaticallyLoadedPackage("color");
2247 // check the case that LyX's document_fontcolor is defined
2248 // but not used for \color
2250 h_preamble << t.asInput();
2252 h_preamble << space;
2253 h_preamble << '{' << argument << '}';
2254 // the color might already be set because \definecolor
2255 // is parsed before this
2261 if (t.cs() == "pagecolor") {
2262 string argument = p.getArg('{', '}');
2263 // check the case that a standard color is used
2264 if (is_known(argument, known_basic_colors)) {
2265 h_backgroundcolor = rgbcolor2code(argument);
2266 } else if (argument == "page_backgroundcolor")
2267 registerAutomaticallyLoadedPackage("color");
2268 // check the case that LyX's page_backgroundcolor is defined
2269 // but not used for \pagecolor
2271 h_preamble << t.asInput() << '{' << argument << '}';
2272 // the color might already be set because \definecolor
2273 // is parsed before this
2274 h_backgroundcolor = "";
2279 if (t.cs() == "makeatletter") {
2280 // LyX takes care of this
2281 p.setCatcode('@', catLetter);
2285 if (t.cs() == "makeatother") {
2286 // LyX takes care of this
2287 p.setCatcode('@', catOther);
2291 if (t.cs() == "makeindex") {
2292 // LyX will re-add this if a print index command is found
2297 if (t.cs() == "newindex") {
2298 string const indexname = p.getArg('[', ']');
2299 string const shortcut = p.verbatim_item();
2300 if (!indexname.empty())
2301 h_index[index_number] = indexname;
2303 h_index[index_number] = shortcut;
2304 h_shortcut[index_number] = shortcut;
2310 if (t.cs() == "addbibresource") {
2311 string const options = p.getArg('[', ']');
2312 string const arg = removeExtension(p.getArg('{', '}'));
2313 if (!options.empty()) {
2314 // check if the option contains a bibencoding, if yes, extract it
2315 string::size_type pos = options.find("bibencoding=");
2317 if (pos != string::npos) {
2318 string::size_type i = options.find(',', pos);
2319 if (i == string::npos)
2320 encoding = options.substr(pos + 1);
2322 encoding = options.substr(pos, i - pos);
2323 pos = encoding.find('=');
2324 if (pos == string::npos)
2327 encoding = encoding.substr(pos + 1);
2329 if (!encoding.empty())
2330 biblatex_encodings.push_back(normalize_filename(arg) + ' ' + encoding);
2332 biblatex_bibliographies.push_back(arg);
2336 if (t.cs() == "bibliography") {
2337 vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
2338 biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
2342 if (t.cs() == "RS@ifundefined") {
2343 string const name = p.verbatim_item();
2344 string const body1 = p.verbatim_item();
2345 string const body2 = p.verbatim_item();
2346 // only non-lyxspecific stuff
2347 if (in_lyx_preamble &&
2348 (name == "subsecref" || name == "thmref" || name == "lemref"))
2352 ss << '\\' << t.cs();
2353 ss << '{' << name << '}'
2354 << '{' << body1 << '}'
2355 << '{' << body2 << '}';
2356 h_preamble << ss.str();
2361 if (t.cs() == "AtBeginDocument") {
2362 string const name = p.verbatim_item();
2363 // only non-lyxspecific stuff
2364 if (in_lyx_preamble &&
2365 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
2366 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
2367 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
2368 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
2369 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
2370 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
2371 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
2372 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
2373 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
2374 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
2375 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
2376 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
2377 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
2378 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
2379 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
2383 ss << '\\' << t.cs();
2384 ss << '{' << name << '}';
2385 h_preamble << ss.str();
2390 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
2391 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
2392 || t.cs() == "providecommand" || t.cs() == "providecommandx"
2393 || t.cs() == "DeclareRobustCommand"
2394 || t.cs() == "DeclareRobustCommandx"
2395 || t.cs() == "ProvideTextCommandDefault"
2396 || t.cs() == "DeclareMathAccent") {
2398 if (p.next_token().character() == '*') {
2402 string const name = p.verbatim_item();
2403 string const opt1 = p.getFullOpt();
2404 string const opt2 = p.getFullOpt();
2405 string const body = p.verbatim_item();
2406 // store the in_lyx_preamble setting
2407 bool const was_in_lyx_preamble = in_lyx_preamble;
2409 if (name == "\\rmdefault")
2410 if (is_known(body, known_roman_font_packages)) {
2411 h_font_roman[0] = body;
2413 in_lyx_preamble = true;
2415 if (name == "\\sfdefault")
2416 if (is_known(body, known_sans_font_packages)) {
2417 h_font_sans[0] = body;
2419 in_lyx_preamble = true;
2421 if (name == "\\ttdefault")
2422 if (is_known(body, known_typewriter_font_packages)) {
2423 h_font_typewriter[0] = body;
2425 in_lyx_preamble = true;
2427 if (name == "\\familydefault") {
2428 string family = body;
2429 // remove leading "\"
2430 h_font_default_family = family.erase(0,1);
2432 in_lyx_preamble = true;
2435 // remove LyX-specific definitions that are re-added by LyX
2437 // \lyxline is an ancient command that is converted by tex2lyx into
2438 // a \rule therefore remove its preamble code
2439 if (name == "\\lyxdot" || name == "\\lyxarrow"
2440 || name == "\\lyxline" || name == "\\LyX") {
2442 in_lyx_preamble = true;
2445 // Add the command to the known commands
2446 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
2448 // only non-lyxspecific stuff
2449 if (!in_lyx_preamble) {
2451 ss << '\\' << t.cs();
2454 ss << '{' << name << '}' << opt1 << opt2
2455 << '{' << body << "}";
2456 if (prefixIs(t.cs(), "renew") || !contains(h_preamble.str(), ss.str()))
2457 h_preamble << ss.str();
2459 ostream & out = in_preamble ? h_preamble : os;
2460 out << "\\" << t.cs() << "{" << name << "}"
2461 << opts << "{" << body << "}";
2464 // restore the in_lyx_preamble setting
2465 in_lyx_preamble = was_in_lyx_preamble;
2469 if (t.cs() == "documentclass") {
2470 vector<string>::iterator it;
2471 vector<string> opts = split_options(p.getArg('[', ']'));
2472 handle_opt(opts, known_fontsizes, h_paperfontsize);
2473 delete_opt(opts, known_fontsizes);
2474 // delete "pt" at the end
2475 string::size_type i = h_paperfontsize.find("pt");
2476 if (i != string::npos)
2477 h_paperfontsize.erase(i);
2478 // The documentclass options are always parsed before the options
2479 // of the babel call so that a language cannot overwrite the babel
2481 handle_opt(opts, known_languages, h_language);
2482 delete_opt(opts, known_languages);
2485 if ((it = find(opts.begin(), opts.end(), "fleqn"))
2487 h_is_mathindent = "1";
2490 // formula numbering side
2491 if ((it = find(opts.begin(), opts.end(), "leqno"))
2493 h_math_numbering_side = "left";
2496 else if ((it = find(opts.begin(), opts.end(), "reqno"))
2498 h_math_numbering_side = "right";
2502 // paper orientation
2503 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
2504 h_paperorientation = "landscape";
2508 if ((it = find(opts.begin(), opts.end(), "oneside"))
2513 if ((it = find(opts.begin(), opts.end(), "twoside"))
2519 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
2521 h_papercolumns = "1";
2524 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
2526 h_papercolumns = "2";
2530 // some size options are known to any document classes, other sizes
2531 // are handled by the \geometry command of the geometry package
2532 handle_opt(opts, known_class_paper_sizes, h_papersize);
2533 delete_opt(opts, known_class_paper_sizes);
2534 // the remaining options
2535 h_options = join(opts, ",");
2536 // FIXME This does not work for classes that have a
2537 // different name in LyX than in LaTeX
2538 h_textclass = p.getArg('{', '}');
2543 if (t.cs() == "usepackage") {
2544 string const options = p.getArg('[', ']');
2545 string const name = p.getArg('{', '}');
2546 vector<string> vecnames;
2547 split(name, vecnames, ',');
2548 vector<string>::const_iterator it = vecnames.begin();
2549 vector<string>::const_iterator end = vecnames.end();
2550 for (; it != end; ++it)
2551 handle_package(p, trimSpaceAndEol(*it), options,
2552 in_lyx_preamble, detectEncoding);
2556 if (t.cs() == "inputencoding") {
2557 string const encoding = p.getArg('{','}');
2558 Encoding const * const enc = encodings.fromLaTeXName(
2559 encoding, Encoding::inputenc, true);
2561 if (!detectEncoding)
2562 cerr << "Unknown encoding " << encoding
2563 << ". Ignoring." << std::endl;
2566 h_inputencoding = enc->name();
2567 p.setEncoding(enc->iconvName());
2572 if (t.cs() == "newenvironment") {
2573 string const name = p.getArg('{', '}');
2574 string const opt1 = p.getFullOpt();
2575 string const opt2 = p.getFullOpt();
2576 string const beg = p.verbatim_item();
2577 string const end = p.verbatim_item();
2578 if (!in_lyx_preamble) {
2579 h_preamble << "\\newenvironment{" << name
2580 << '}' << opt1 << opt2 << '{'
2581 << beg << "}{" << end << '}';
2583 add_known_environment(name, opt1, !opt2.empty(),
2584 from_utf8(beg), from_utf8(end));
2588 if (t.cs() == "newtheorem") {
2590 if (p.next_token().character() == '*') {
2594 string const name = p.getArg('{', '}');
2595 string const opt1 = p.getFullOpt();
2596 string const opt2 = p.getFullOpt();
2597 string const body = p.verbatim_item();
2598 string const opt3 = p.getFullOpt();
2599 string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
2601 string const complete = cmd + "{" + name + '}' +
2602 opt1 + opt2 + '{' + body + '}' + opt3;
2604 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
2606 if (!in_lyx_preamble)
2607 h_preamble << complete;
2611 if (t.cs() == "def") {
2612 string name = p.get_token().cs();
2613 // In fact, name may be more than the name:
2614 // In the test case of bug 8116
2615 // name == "csname SF@gobble@opt \endcsname".
2616 // Therefore, we need to use asInput() instead of cs().
2617 while (p.next_token().cat() != catBegin)
2618 name += p.get_token().asInput();
2619 if (!in_lyx_preamble)
2620 h_preamble << "\\def\\" << name << '{'
2621 << p.verbatim_item() << "}";
2625 if (t.cs() == "newcolumntype") {
2626 string const name = p.getArg('{', '}');
2627 trimSpaceAndEol(name);
2629 string opts = p.getOpt();
2630 if (!opts.empty()) {
2631 istringstream is(string(opts, 1));
2634 special_columns_[name[0]] = nargs;
2635 h_preamble << "\\newcolumntype{" << name << "}";
2637 h_preamble << "[" << nargs << "]";
2638 h_preamble << "{" << p.verbatim_item() << "}";
2642 if (t.cs() == "setcounter") {
2643 string const name = p.getArg('{', '}');
2644 string const content = p.getArg('{', '}');
2645 if (name == "secnumdepth")
2646 h_secnumdepth = content;
2647 else if (name == "tocdepth")
2648 h_tocdepth = content;
2650 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
2654 if (t.cs() == "setlength") {
2655 string const name = p.verbatim_item();
2656 string const content = p.verbatim_item();
2657 // the paragraphs are only not indented when \parindent is set to zero
2658 if (name == "\\parindent" && content != "") {
2659 if (content[0] == '0')
2660 h_paragraph_separation = "skip";
2662 h_paragraph_indentation = translate_len(content);
2663 } else if (name == "\\parskip") {
2664 if (content == "\\smallskipamount")
2665 h_defskip = "smallskip";
2666 else if (content == "\\medskipamount")
2667 h_defskip = "medskip";
2668 else if (content == "\\bigskipamount")
2669 h_defskip = "bigskip";
2671 h_defskip = translate_len(content);
2672 } else if (name == "\\mathindent") {
2673 h_mathindentation = translate_len(content);
2675 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2679 if (t.cs() == "onehalfspacing") {
2680 h_spacing = "onehalf";
2684 if (t.cs() == "doublespacing") {
2685 h_spacing = "double";
2689 if (t.cs() == "setstretch") {
2690 h_spacing = "other " + p.verbatim_item();
2694 if (t.cs() == "synctex") {
2695 // the scheme is \synctex=value
2696 // where value can only be "1" or "-1"
2697 h_output_sync = "1";
2698 // there can be any character behind the value (e.g. a linebreak or a '\'
2699 // therefore we extract it char by char
2701 string value = p.get_token().asInput();
2703 value += p.get_token().asInput();
2704 h_output_sync_macro = "\\synctex=" + value;
2708 if (t.cs() == "begin") {
2709 string const name = p.getArg('{', '}');
2710 if (name == "document")
2712 h_preamble << "\\begin{" << name << "}";
2716 if (t.cs() == "geometry") {
2717 vector<string> opts = split_options(p.getArg('{', '}'));
2718 handle_geometry(opts);
2722 if (t.cs() == "definecolor") {
2723 string const color = p.getArg('{', '}');
2724 string const space = p.getArg('{', '}');
2725 string const value = p.getArg('{', '}');
2726 if (color == "document_fontcolor" && space == "rgb") {
2727 RGBColor c(RGBColorFromLaTeX(value));
2728 h_fontcolor = X11hexname(c);
2729 } else if (color == "note_fontcolor" && space == "rgb") {
2730 RGBColor c(RGBColorFromLaTeX(value));
2731 h_notefontcolor = X11hexname(c);
2732 } else if (color == "page_backgroundcolor" && space == "rgb") {
2733 RGBColor c(RGBColorFromLaTeX(value));
2734 h_backgroundcolor = X11hexname(c);
2735 } else if (color == "shadecolor" && space == "rgb") {
2736 RGBColor c(RGBColorFromLaTeX(value));
2737 h_boxbgcolor = X11hexname(c);
2739 h_preamble << "\\definecolor{" << color
2740 << "}{" << space << "}{" << value
2746 if (t.cs() == "bibliographystyle") {
2747 h_biblio_style = p.verbatim_item();
2751 if (t.cs() == "jurabibsetup") {
2752 // FIXME p.getArg('{', '}') is most probably wrong (it
2753 // does not handle nested braces).
2754 // Use p.verbatim_item() instead.
2755 vector<string> jurabibsetup =
2756 split_options(p.getArg('{', '}'));
2757 // add jurabibsetup to the jurabib package options
2758 add_package("jurabib", jurabibsetup);
2759 if (!jurabibsetup.empty()) {
2760 h_preamble << "\\jurabibsetup{"
2761 << join(jurabibsetup, ",") << '}';
2766 if (t.cs() == "hypersetup") {
2767 vector<string> hypersetup =
2768 split_options(p.verbatim_item());
2769 // add hypersetup to the hyperref package options
2770 handle_hyperref(hypersetup);
2771 if (!hypersetup.empty()) {
2772 h_preamble << "\\hypersetup{"
2773 << join(hypersetup, ",") << '}';
2778 if (t.cs() == "includeonly") {
2779 vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
2780 for (auto & iofile : includeonlys) {
2781 string filename(normalize_filename(iofile));
2782 string const path = getMasterFilePath(true);
2783 // We want to preserve relative/absolute filenames,
2784 // therefore path is only used for testing
2785 if (!makeAbsPath(filename, path).exists()) {
2786 // The file extension is probably missing.
2787 // Now try to find it out.
2788 string const tex_name =
2789 find_file(filename, path,
2790 known_tex_extensions);
2791 if (!tex_name.empty())
2792 filename = tex_name;
2795 if (makeAbsPath(filename, path).exists())
2796 fix_child_filename(filename);
2798 cerr << "Warning: Could not find included file '"
2799 << filename << "'." << endl;
2800 outname = changeExtension(filename, "lyx");
2801 h_includeonlys.push_back(outname);
2806 if (is_known(t.cs(), known_if_3arg_commands)) {
2807 // prevent misparsing of \usepackage if it is used
2808 // as an argument (see e.g. our own output of
2809 // \@ifundefined above)
2810 string const arg1 = p.verbatim_item();
2811 string const arg2 = p.verbatim_item();
2812 string const arg3 = p.verbatim_item();
2813 // test case \@ifundefined{date}{}{\date{}}
2814 if (t.cs() == "@ifundefined" && arg1 == "date" &&
2815 arg2.empty() && arg3 == "\\date{}") {
2816 h_suppress_date = "true";
2817 // older tex2lyx versions did output
2818 // \@ifundefined{definecolor}{\usepackage{color}}{}
2819 } else if (t.cs() == "@ifundefined" &&
2820 arg1 == "definecolor" &&
2821 arg2 == "\\usepackage{color}" &&
2823 if (!in_lyx_preamble)
2824 h_preamble << package_beg_sep
2827 << "\\@ifundefined{definecolor}{color}{}"
2830 //\@ifundefined{showcaptionsetup}{}{%
2831 // \PassOptionsToPackage{caption=false}{subfig}}
2832 // that LyX uses for subfloats
2833 } else if (t.cs() == "@ifundefined" &&
2834 arg1 == "showcaptionsetup" && arg2.empty()
2835 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
2837 } else if (!in_lyx_preamble) {
2838 h_preamble << t.asInput()
2839 << '{' << arg1 << '}'
2840 << '{' << arg2 << '}'
2841 << '{' << arg3 << '}';
2846 if (is_known(t.cs(), known_if_commands)) {
2847 // must not parse anything in conditional code, since
2848 // LyX would output the parsed contents unconditionally
2849 if (!in_lyx_preamble)
2850 h_preamble << t.asInput();
2851 handle_if(p, in_lyx_preamble);
2855 if (!t.cs().empty() && !in_lyx_preamble) {
2856 h_preamble << '\\' << t.cs();
2861 // remove the whitespace
2864 // Force textclass if the user wanted it
2865 if (!forceclass.empty())
2866 h_textclass = forceclass;
2867 tc.setName(h_textclass);
2868 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2869 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2872 if (h_papersides.empty()) {
2875 h_papersides = ss.str();
2878 // If the CJK package is used we cannot set the document language from
2879 // the babel options. Instead, we guess which language is used most
2880 // and set this one.
2881 default_language = h_language;
2882 if (is_full_document &&
2883 (auto_packages.find("CJK") != auto_packages.end() ||
2884 auto_packages.find("CJKutf8") != auto_packages.end())) {
2886 h_language = guessLanguage(p, default_language);
2888 if (explicit_babel && h_language != default_language) {
2889 // We set the document language to a CJK language,
2890 // but babel is explicitly called in the user preamble
2891 // without options. LyX will not add the default
2892 // language to the document options if it is either
2893 // english, or no text is set as default language.
2894 // Therefore we need to add a language option explicitly.
2895 // FIXME: It would be better to remove all babel calls
2896 // from the user preamble, but this is difficult
2897 // without re-introducing bug 7861.
2898 if (h_options.empty())
2899 h_options = lyx2babel(default_language);
2901 h_options += ',' + lyx2babel(default_language);
2905 // Finally, set the quote style.
2906 // LyX knows the following quotes styles:
2907 // british, cjk, cjkangle, danish, english, french, german,
2908 // polish, russian, swedish and swiss
2909 // conversion list taken from
2910 // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
2911 // (quotes for kazakh are unknown)
2913 if (is_known(h_language, known_british_quotes_languages))
2914 h_quotes_style = "british";
2916 else if (is_known(h_language, known_cjk_quotes_languages))
2917 h_quotes_style = "cjk";
2919 else if (is_known(h_language, known_cjkangle_quotes_languages))
2920 h_quotes_style = "cjkangle";
2922 else if (is_known(h_language, known_danish_quotes_languages))
2923 h_quotes_style = "danish";
2925 else if (is_known(h_language, known_french_quotes_languages))
2926 h_quotes_style = "french";
2928 else if (is_known(h_language, known_german_quotes_languages))
2929 h_quotes_style = "german";
2931 else if (is_known(h_language, known_polish_quotes_languages))
2932 h_quotes_style = "polish";
2934 else if (is_known(h_language, known_russian_quotes_languages))
2935 h_quotes_style = "russian";
2937 else if (is_known(h_language, known_swedish_quotes_languages))
2938 h_quotes_style = "swedish";
2940 else if (is_known(h_language, known_swiss_quotes_languages))
2941 h_quotes_style = "swiss";
2943 else if (is_known(h_language, known_english_quotes_languages))
2944 h_quotes_style = "english";
2948 string Preamble::parseEncoding(Parser & p, string const & forceclass)
2950 TeX2LyXDocClass dummy;
2951 parse(p, forceclass, true, dummy);
2952 if (h_inputencoding != "auto-legacy" && h_inputencoding != "auto-legacy-plain")
2953 return h_inputencoding;
2958 string babel2lyx(string const & language)
2960 char const * const * where = is_known(language, known_languages);
2962 return known_coded_languages[where - known_languages];
2967 string lyx2babel(string const & language)
2969 char const * const * where = is_known(language, known_coded_languages);
2971 return known_languages[where - known_coded_languages];
2976 string Preamble::polyglossia2lyx(string const & language)
2978 char const * const * where = is_known(language, polyglossia_languages);
2980 return coded_polyglossia_languages[where - polyglossia_languages];
2985 string rgbcolor2code(string const & name)
2987 char const * const * where = is_known(name, known_basic_colors);
2989 // "red", "green" etc
2990 return known_basic_color_codes[where - known_basic_colors];
2992 // "255,0,0", "0,255,0" etc
2993 RGBColor c(RGBColorFromLaTeX(name));
2994 return X11hexname(c);