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";
1027 h_font_roman_opts = xopts;
1031 if (name == "noto-serif") {
1032 h_font_roman[0] = "NotoSerifRegular";
1033 for (auto const & opt : allopts) {
1034 if (opt == "regular")
1037 if (opt == "thin") {
1038 h_font_roman[0] = "NotoSerifThin";
1041 if (opt == "extralight") {
1042 h_font_roman[0] = "NotoSerifExtralight";
1045 if (opt == "light") {
1046 h_font_roman[0] = "NotoSerifLight";
1049 if (opt == "medium") {
1050 h_font_roman[0] = "NotoSerifMedium";
1058 h_font_roman_opts = xopts;
1062 if (name == "sourceserifpro") {
1063 h_font_roman[0] = "ADOBESourceSerifPro";
1064 for (auto const & opt : allopts) {
1066 h_font_roman_osf = "true";
1074 h_font_roman_opts = xopts;
1082 // By default, we use the package name as LyX font name,
1083 // so this only needs to be reset if these names differ.
1084 // Also, we handle the scaling option here generally.
1085 if (is_known(name, known_sans_font_packages)) {
1086 h_font_sans[0] = name;
1087 if (contains(opts, "scale")) {
1088 vector<string>::const_iterator it = allopts.begin();
1089 for (; it != allopts.end() ; ++it) {
1090 string const opt = *it;
1091 if (prefixIs(opt, "scaled=") || prefixIs(opt, "scale=")) {
1092 if (scale_as_percentage(opt, h_font_sf_scale[0])) {
1101 if (name == "biolinum" || name == "biolinum-type1") {
1102 h_font_sans[0] = "biolinum";
1103 for (auto const & opt : allopts) {
1104 if (prefixIs(opt, "osf")) {
1105 h_font_sans_osf = "true";
1113 h_font_sans_opts = xopts;
1117 if (name == "cantarell") {
1118 for (auto const & opt : allopts) {
1119 if (opt == "defaultsans")
1121 if (prefixIs(opt, "oldstyle")) {
1122 h_font_sans_osf = "true";
1130 h_font_sans_opts = xopts;
1134 if (name == "Chivo") {
1135 for (auto const & opt : allopts) {
1136 if (opt == "thin") {
1137 h_font_roman[0] = "ChivoThin";
1140 if (opt == "light") {
1141 h_font_roman[0] = "ChivoLight";
1144 if (opt == "regular") {
1145 h_font_roman[0] = "Chivo";
1148 if (opt == "medium") {
1149 h_font_roman[0] = "ChivoMedium";
1152 if (prefixIs(opt, "oldstyle")) {
1153 h_font_sans_osf = "true";
1161 h_font_sans_opts = xopts;
1165 if (name == "PTSans") {
1166 h_font_sans[0] = "PTSans-TLF";
1169 if (name == "FiraSans") {
1170 h_font_sans_osf = "true";
1171 for (auto const & opt : allopts) {
1172 if (opt == "book") {
1173 h_font_sans[0] = "FiraSansBook";
1176 if (opt == "thin") {
1179 if (opt == "extralight") {
1180 h_font_sans[0] = "FiraSansExtralight";
1183 if (opt == "light") {
1184 h_font_sans[0] = "FiraSansLight";
1187 if (opt == "ultralight") {
1188 h_font_sans[0] = "FiraSansUltralight";
1191 if (opt == "thin") {
1192 h_font_sans[0] = "FiraSansThin";
1195 if (opt == "lf" || opt == "lining") {
1196 h_font_sans_osf = "false";
1204 h_font_sans_opts = xopts;
1208 if (name == "plex-sans") {
1209 h_font_sans[0] = "IBMPlexSans";
1210 for (auto const & opt : allopts) {
1211 if (opt == "condensed") {
1212 h_font_sans[0] = "IBMPlexSansCondensed";
1215 if (opt == "thin") {
1216 h_font_sans[0] = "IBMPlexSansThin";
1219 if (opt == "extralight") {
1220 h_font_sans[0] = "IBMPlexSansExtraLight";
1223 if (opt == "light") {
1224 h_font_sans[0] = "IBMPlexSansLight";
1232 h_font_sans_opts = xopts;
1236 if (name == "noto-sans") {
1237 h_font_sans[0] = "NotoSansRegular";
1238 for (auto const & opt : allopts) {
1239 if (opt == "regular")
1241 if (opt == "medium") {
1242 h_font_sans[0] = "NotoSansMedium";
1245 if (opt == "thin") {
1246 h_font_sans[0] = "NotoSansThin";
1249 if (opt == "extralight") {
1250 h_font_sans[0] = "NotoSansExtralight";
1253 if (opt == "light") {
1254 h_font_sans[0] = "NotoSansLight";
1258 h_font_sans_osf = "true";
1266 h_font_sans_opts = xopts;
1270 if (name == "sourcesanspro") {
1271 h_font_sans[0] = "ADOBESourceSansPro";
1272 for (auto const & opt : allopts) {
1274 h_font_sans_osf = "true";
1282 h_font_sans_opts = xopts;
1290 // By default, we use the package name as LyX font name,
1291 // so this only needs to be reset if these names differ.
1292 // Also, we handle the scaling option here generally.
1293 // Note: fourier can be set as roman font _only_
1294 // fourier as typewriter is handled in handling of \ttdefault
1295 if (is_known(name, known_typewriter_font_packages) && name != "fourier") {
1296 h_font_typewriter[0] = name;
1297 if (contains(opts, "scale")) {
1298 vector<string>::const_iterator it = allopts.begin();
1299 for (; it != allopts.end() ; ++it) {
1300 string const opt = *it;
1301 if (prefixIs(opt, "scaled=") || prefixIs(opt, "scale=")) {
1302 if (scale_as_percentage(opt, h_font_tt_scale[0])) {
1311 if (name == "libertineMono" || name == "libertineMono-type1")
1312 h_font_typewriter[0] = "libertine-mono";
1314 if (name == "FiraMono") {
1315 h_font_typewriter_osf = "true";
1316 for (auto const & opt : allopts) {
1317 if (opt == "lf" || opt == "lining") {
1318 h_font_typewriter_osf = "false";
1326 h_font_typewriter_opts = xopts;
1330 if (name == "PTMono")
1331 h_font_typewriter[0] = "PTMono-TLF";
1333 if (name == "plex-mono") {
1334 h_font_typewriter[0] = "IBMPlexMono";
1335 for (auto const & opt : allopts) {
1336 if (opt == "thin") {
1337 h_font_typewriter[0] = "IBMPlexMonoThin";
1340 if (opt == "extralight") {
1341 h_font_typewriter[0] = "IBMPlexMonoExtraLight";
1344 if (opt == "light") {
1345 h_font_typewriter[0] = "IBMPlexMonoLight";
1353 h_font_typewriter_opts = xopts;
1357 if (name == "noto-mono") {
1358 h_font_typewriter[0] = "NotoMonoRegular";
1359 for (auto const & opt : allopts) {
1360 if (opt == "regular")
1367 h_font_typewriter_opts = xopts;
1371 if (name == "sourcecodepro") {
1372 h_font_typewriter[0] = "ADOBESourceCodePro";
1373 for (auto const & opt : allopts) {
1375 h_font_typewriter_osf = "true";
1383 h_font_typewriter_opts = xopts;
1391 // By default, we use the package name as LyX font name,
1392 // so this only needs to be reset if these names differ.
1393 if (is_known(name, known_math_font_packages))
1394 h_font_math[0] = name;
1396 if (name == "newtxmath") {
1398 h_font_math[0] = "newtxmath";
1399 else if (opts == "garamondx")
1400 h_font_math[0] = "garamondx-ntxm";
1401 else if (opts == "libertine")
1402 h_font_math[0] = "libertine-ntxm";
1403 else if (opts == "minion")
1404 h_font_math[0] = "minion-ntxm";
1405 else if (opts == "cochineal")
1406 h_font_math[0] = "cochineal-ntxm";
1409 if (name == "iwona")
1411 h_font_math[0] = "iwona-math";
1413 if (name == "kurier")
1415 h_font_math[0] = "kurier-math";
1417 // after the detection and handling of special cases, we can remove the
1418 // fonts, otherwise they would appear in the preamble, see bug #7856
1419 if (is_known(name, known_roman_font_packages) || is_known(name, known_sans_font_packages)
1420 || is_known(name, known_typewriter_font_packages) || is_known(name, known_math_font_packages))
1422 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
1423 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
1424 name == "esint" || name == "mhchem" || name == "mathdots" ||
1425 name == "mathtools" || name == "stackrel" ||
1426 name == "stmaryrd" || name == "undertilde") {
1427 h_use_packages[name] = "2";
1428 registerAutomaticallyLoadedPackage(name);
1431 else if (name == "babel") {
1432 h_language_package = "default";
1433 // One might think we would have to do nothing if babel is loaded
1434 // without any options to prevent pollution of the preamble with this
1435 // babel call in every roundtrip.
1436 // But the user could have defined babel-specific things afterwards. So
1437 // we need to keep it in the preamble to prevent cases like bug #7861.
1438 if (!opts.empty()) {
1439 // check if more than one option was used - used later for inputenc
1440 if (options.begin() != options.end() - 1)
1441 one_language = false;
1442 // babel takes the last language of the option of its \usepackage
1443 // call as document language. If there is no such language option, the
1444 // last language in the documentclass options is used.
1445 handle_opt(options, known_languages, h_language);
1446 // translate the babel name to a LyX name
1447 h_language = babel2lyx(h_language);
1448 if (h_language == "japanese") {
1449 // For Japanese, the encoding isn't indicated in the source
1450 // file, and there's really not much we can do. We could
1451 // 1) offer a list of possible encodings to choose from, or
1452 // 2) determine the encoding of the file by inspecting it.
1453 // For the time being, we leave the encoding alone so that
1454 // we don't get iconv errors when making a wrong guess, and
1455 // we will output a note at the top of the document
1456 // explaining what to do.
1457 Encoding const * const enc = encodings.fromIconvName(
1458 p.getEncoding(), Encoding::japanese, false);
1460 h_inputencoding = enc->name();
1461 is_nonCJKJapanese = true;
1462 // in this case babel can be removed from the preamble
1463 registerAutomaticallyLoadedPackage("babel");
1465 // If babel is called with options, LyX puts them by default into the
1466 // document class options. This works for most languages, except
1467 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
1468 // perhaps in future others.
1469 // Therefore keep the babel call as it is as the user might have
1471 string const babelcall = "\\usepackage[" + opts + "]{babel}\n";
1472 if (!contains(h_preamble.str(), babelcall))
1473 h_preamble << babelcall;
1475 delete_opt(options, known_languages);
1477 if (!contains(h_preamble.str(), "\\usepackage{babel}\n"))
1478 h_preamble << "\\usepackage{babel}\n";
1479 explicit_babel = true;
1483 else if (name == "polyglossia") {
1484 h_language_package = "default";
1485 h_default_output_format = "pdf4";
1486 h_use_non_tex_fonts = true;
1488 registerAutomaticallyLoadedPackage("xunicode");
1489 if (h_inputencoding == "auto-legacy")
1490 p.setEncoding("UTF-8");
1493 else if (name == "CJK") {
1494 // set the encoding to "auto-legacy" because it might be set to "auto-legacy-plain" by the babel handling
1495 // and this would not be correct for CJK
1496 if (h_inputencoding == "auto-legacy-plain")
1497 h_inputencoding = "auto-legacy";
1498 registerAutomaticallyLoadedPackage("CJK");
1501 else if (name == "CJKutf8") {
1502 h_inputencoding = "utf8-cjk";
1503 p.setEncoding("UTF-8");
1504 registerAutomaticallyLoadedPackage("CJKutf8");
1507 else if (name == "fontenc") {
1508 h_fontencoding = getStringFromVector(options, ",");
1512 else if (name == "inputenc" || name == "luainputenc") {
1513 // h_inputencoding is only set when there is not more than one
1514 // inputenc option because otherwise h_inputencoding must be
1515 // set to "auto-legacy" (the default encodings of the document's languages)
1516 // Therefore check that exactly one option is passed to inputenc.
1517 // It is also only set when there is not more than one babel
1519 if (!options.empty()) {
1520 string const encoding = options.back();
1521 Encoding const * const enc = encodings.fromLaTeXName(
1522 encoding, Encoding::inputenc, true);
1524 if (!detectEncoding)
1525 cerr << "Unknown encoding " << encoding
1526 << ". Ignoring." << std::endl;
1528 if (!enc->unsafe() && options.size() == 1 && one_language == true)
1529 h_inputencoding = enc->name();
1530 p.setEncoding(enc->iconvName());
1536 else if (name == "srcltx") {
1537 h_output_sync = "1";
1538 if (!opts.empty()) {
1539 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
1542 h_output_sync_macro = "\\usepackage{srcltx}";
1545 else if (is_known(name, known_old_language_packages)) {
1546 // known language packages from the times before babel
1547 // if they are found and not also babel, they will be used as
1548 // custom language package
1549 h_language_package = "\\usepackage{" + name + "}";
1552 else if (name == "lyxskak") {
1553 // ignore this and its options
1554 const char * const o[] = {"ps", "mover", 0};
1555 delete_opt(options, o);
1558 else if (is_known(name, known_lyx_packages) && options.empty()) {
1559 if (name == "splitidx")
1560 h_use_indices = "true";
1561 else if (name == "minted")
1562 h_use_minted = true;
1563 else if (name == "refstyle")
1564 h_use_refstyle = true;
1565 else if (name == "prettyref")
1566 h_use_refstyle = false;
1567 if (!in_lyx_preamble) {
1568 h_preamble << package_beg_sep << name
1569 << package_mid_sep << "\\usepackage{"
1571 if (p.next_token().cat() == catNewline ||
1572 (p.next_token().cat() == catSpace &&
1573 p.next_next_token().cat() == catNewline))
1575 h_preamble << package_end_sep;
1579 else if (name == "geometry")
1580 handle_geometry(options);
1582 else if (name == "subfig")
1583 ; // ignore this FIXME: Use the package separator mechanism instead
1585 else if (char const * const * where = is_known(name, known_languages))
1586 h_language = known_coded_languages[where - known_languages];
1588 else if (name == "natbib") {
1589 h_biblio_style = "plainnat";
1590 h_cite_engine = "natbib";
1591 h_cite_engine_type = "authoryear";
1592 vector<string>::iterator it =
1593 find(options.begin(), options.end(), "authoryear");
1594 if (it != options.end())
1597 it = find(options.begin(), options.end(), "numbers");
1598 if (it != options.end()) {
1599 h_cite_engine_type = "numerical";
1603 if (!options.empty())
1604 h_biblio_options = join(options, ",");
1607 else if (name == "biblatex") {
1608 h_biblio_style = "plainnat";
1609 h_cite_engine = "biblatex";
1610 h_cite_engine_type = "authoryear";
1612 vector<string>::iterator it =
1613 find(options.begin(), options.end(), "natbib");
1614 if (it != options.end()) {
1616 h_cite_engine = "biblatex-natbib";
1618 opt = process_keyval_opt(options, "natbib");
1620 h_cite_engine = "biblatex-natbib";
1622 opt = process_keyval_opt(options, "style");
1624 h_biblatex_citestyle = opt;
1625 h_biblatex_bibstyle = opt;
1627 opt = process_keyval_opt(options, "citestyle");
1629 h_biblatex_citestyle = opt;
1630 opt = process_keyval_opt(options, "bibstyle");
1632 h_biblatex_bibstyle = opt;
1634 opt = process_keyval_opt(options, "refsection");
1636 if (opt == "none" || opt == "part"
1637 || opt == "chapter" || opt == "section"
1638 || opt == "subsection")
1641 cerr << "Ignoring unkown refesection value '"
1644 opt = process_keyval_opt(options, "bibencoding");
1647 if (!options.empty()) {
1648 h_biblio_options = join(options, ",");
1653 else if (name == "jurabib") {
1654 h_biblio_style = "jurabib";
1655 h_cite_engine = "jurabib";
1656 h_cite_engine_type = "authoryear";
1657 if (!options.empty())
1658 h_biblio_options = join(options, ",");
1661 else if (name == "bibtopic")
1662 h_use_bibtopic = "true";
1664 else if (name == "chapterbib")
1665 h_multibib = "child";
1667 else if (name == "hyperref")
1668 handle_hyperref(options);
1670 else if (name == "algorithm2e") {
1671 // Load "algorithm2e" module
1672 addModule("algorithm2e");
1673 // Add the package options to the global document options
1674 if (!options.empty()) {
1675 if (h_options.empty())
1676 h_options = join(options, ",");
1678 h_options += ',' + join(options, ",");
1681 else if (name == "microtype") {
1682 //we internally support only microtype without params
1683 if (options.empty())
1684 h_use_microtype = "true";
1686 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1689 else if (name == "lineno") {
1690 h_use_lineno = "true";
1691 if (!options.empty()) {
1692 h_lineno_options = join(options, ",");
1697 else if (!in_lyx_preamble) {
1698 if (options.empty())
1699 h_preamble << "\\usepackage{" << name << '}';
1701 h_preamble << "\\usepackage[" << opts << "]{"
1705 if (p.next_token().cat() == catNewline ||
1706 (p.next_token().cat() == catSpace &&
1707 p.next_next_token().cat() == catNewline))
1711 // We need to do something with the options...
1712 if (!options.empty() && !detectEncoding)
1713 cerr << "Ignoring options '" << join(options, ",")
1714 << "' of package " << name << '.' << endl;
1716 // remove the whitespace
1721 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1724 Token t = p.get_token();
1725 if (t.cat() == catEscape &&
1726 is_known(t.cs(), known_if_commands))
1727 handle_if(p, in_lyx_preamble);
1729 if (!in_lyx_preamble)
1730 h_preamble << t.asInput();
1731 if (t.cat() == catEscape && t.cs() == "fi")
1738 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1740 if (contains(h_float_placement, "H"))
1741 registerAutomaticallyLoadedPackage("float");
1742 if (h_spacing != "single" && h_spacing != "default")
1743 registerAutomaticallyLoadedPackage("setspace");
1744 if (h_use_packages["amsmath"] == "2") {
1745 // amsbsy and amstext are already provided by amsmath
1746 registerAutomaticallyLoadedPackage("amsbsy");
1747 registerAutomaticallyLoadedPackage("amstext");
1750 // output the LyX file settings
1751 // Important: Keep the version formatting in sync with LyX and
1752 // lyx2lyx (bug 7951)
1753 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1754 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1755 << lyx_version_minor << '\n'
1756 << "\\lyxformat " << LYX_FORMAT << '\n'
1757 << "\\begin_document\n"
1758 << "\\begin_header\n"
1759 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1760 << "\\origin " << origin << "\n"
1761 << "\\textclass " << h_textclass << "\n";
1762 string const raw = subdoc ? empty_string() : h_preamble.str();
1764 os << "\\begin_preamble\n";
1765 for (string::size_type i = 0; i < raw.size(); ++i) {
1766 if (raw[i] == package_beg_sep) {
1767 // Here follows some package loading code that
1768 // must be skipped if the package is loaded
1770 string::size_type j = raw.find(package_mid_sep, i);
1771 if (j == string::npos)
1773 string::size_type k = raw.find(package_end_sep, j);
1774 if (k == string::npos)
1776 string const package = raw.substr(i + 1, j - i - 1);
1777 string const replacement = raw.substr(j + 1, k - j - 1);
1778 if (auto_packages.find(package) == auto_packages.end())
1784 os << "\n\\end_preamble\n";
1786 if (!h_options.empty())
1787 os << "\\options " << h_options << "\n";
1788 os << "\\use_default_options " << h_use_default_options << "\n";
1789 if (!used_modules.empty()) {
1790 os << "\\begin_modules\n";
1791 vector<string>::const_iterator const end = used_modules.end();
1792 vector<string>::const_iterator it = used_modules.begin();
1793 for (; it != end; ++it)
1795 os << "\\end_modules\n";
1797 if (!h_includeonlys.empty()) {
1798 os << "\\begin_includeonly\n";
1799 for (auto const & iofile : h_includeonlys)
1800 os << iofile << '\n';
1801 os << "\\end_includeonly\n";
1803 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1804 << "\\language " << h_language << "\n"
1805 << "\\language_package " << h_language_package << "\n"
1806 << "\\inputencoding " << h_inputencoding << "\n"
1807 << "\\fontencoding " << h_fontencoding << "\n"
1808 << "\\font_roman \"" << h_font_roman[0]
1809 << "\" \"" << h_font_roman[1] << "\"\n"
1810 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1811 << "\\font_typewriter \"" << h_font_typewriter[0]
1812 << "\" \"" << h_font_typewriter[1] << "\"\n"
1813 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1814 << "\\font_default_family " << h_font_default_family << "\n"
1815 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1816 << "\\font_sc " << h_font_sc << "\n"
1817 << "\\font_roman_osf " << h_font_roman_osf << "\n"
1818 << "\\font_sans_osf " << h_font_sans_osf << "\n"
1819 << "\\font_typewriter_osf " << h_font_typewriter_osf << "\n";
1820 if (!h_font_roman_opts.empty())
1821 os << "\\font_roman_opts \"" << h_font_roman_opts << "\"" << '\n';
1822 os << "\\font_sf_scale " << h_font_sf_scale[0]
1823 << ' ' << h_font_sf_scale[1] << '\n';
1824 if (!h_font_sans_opts.empty())
1825 os << "\\font_sans_opts \"" << h_font_sans_opts << "\"" << '\n';
1826 os << "\\font_tt_scale " << h_font_tt_scale[0]
1827 << ' ' << h_font_tt_scale[1] << '\n';
1828 if (!h_font_cjk.empty())
1829 os << "\\font_cjk " << h_font_cjk << '\n';
1830 if (!h_font_typewriter_opts.empty())
1831 os << "\\font_typewriter_opts \"" << h_font_typewriter_opts << "\"" << '\n';
1832 os << "\\use_microtype " << h_use_microtype << '\n'
1833 << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
1834 << "\\graphics " << h_graphics << '\n'
1835 << "\\default_output_format " << h_default_output_format << "\n"
1836 << "\\output_sync " << h_output_sync << "\n";
1837 if (h_output_sync == "1")
1838 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1839 os << "\\bibtex_command " << h_bibtex_command << "\n"
1840 << "\\index_command " << h_index_command << "\n";
1841 if (!h_float_placement.empty())
1842 os << "\\float_placement " << h_float_placement << "\n";
1843 os << "\\paperfontsize " << h_paperfontsize << "\n"
1844 << "\\spacing " << h_spacing << "\n"
1845 << "\\use_hyperref " << h_use_hyperref << '\n';
1846 if (h_use_hyperref == "true") {
1847 if (!h_pdf_title.empty())
1848 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1849 if (!h_pdf_author.empty())
1850 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1851 if (!h_pdf_subject.empty())
1852 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1853 if (!h_pdf_keywords.empty())
1854 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1855 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1856 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1857 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1858 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1859 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1860 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1861 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1862 "\\pdf_backref " << h_pdf_backref << "\n"
1863 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1864 if (!h_pdf_pagemode.empty())
1865 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1866 if (!h_pdf_quoted_options.empty())
1867 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1869 os << "\\papersize " << h_papersize << "\n"
1870 << "\\use_geometry " << h_use_geometry << '\n';
1871 for (map<string, string>::const_iterator it = h_use_packages.begin();
1872 it != h_use_packages.end(); ++it)
1873 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1874 os << "\\cite_engine " << h_cite_engine << '\n'
1875 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1876 << "\\biblio_style " << h_biblio_style << "\n"
1877 << "\\use_bibtopic " << h_use_bibtopic << "\n";
1878 if (!h_biblio_options.empty())
1879 os << "\\biblio_options " << h_biblio_options << "\n";
1880 if (!h_biblatex_bibstyle.empty())
1881 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
1882 if (!h_biblatex_citestyle.empty())
1883 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
1884 if (!h_multibib.empty())
1885 os << "\\multibib " << h_multibib << "\n";
1886 os << "\\use_indices " << h_use_indices << "\n"
1887 << "\\paperorientation " << h_paperorientation << '\n'
1888 << "\\suppress_date " << h_suppress_date << '\n'
1889 << "\\justification " << h_justification << '\n'
1890 << "\\use_refstyle " << h_use_refstyle << '\n'
1891 << "\\use_minted " << h_use_minted << '\n'
1892 << "\\use_lineno " << h_use_lineno << '\n';
1893 if (!h_lineno_options.empty())
1894 os << "\\lineno_options " << h_lineno_options << '\n';
1895 if (!h_fontcolor.empty())
1896 os << "\\fontcolor " << h_fontcolor << '\n';
1897 if (!h_notefontcolor.empty())
1898 os << "\\notefontcolor " << h_notefontcolor << '\n';
1899 if (!h_backgroundcolor.empty())
1900 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1901 if (!h_boxbgcolor.empty())
1902 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1903 if (index_number != 0)
1904 for (int i = 0; i < index_number; i++) {
1905 os << "\\index " << h_index[i] << '\n'
1906 << "\\shortcut " << h_shortcut[i] << '\n'
1907 << "\\color " << h_color << '\n'
1911 os << "\\index " << h_index[0] << '\n'
1912 << "\\shortcut " << h_shortcut[0] << '\n'
1913 << "\\color " << h_color << '\n'
1917 << "\\secnumdepth " << h_secnumdepth << "\n"
1918 << "\\tocdepth " << h_tocdepth << "\n"
1919 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1920 if (h_paragraph_separation == "skip")
1921 os << "\\defskip " << h_defskip << "\n";
1923 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1924 os << "\\is_math_indent " << h_is_mathindent << "\n";
1925 if (!h_mathindentation.empty())
1926 os << "\\math_indentation " << h_mathindentation << "\n";
1927 os << "\\math_numbering_side " << h_math_numbering_side << "\n";
1928 os << "\\quotes_style " << h_quotes_style << "\n"
1929 << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
1930 << "\\papercolumns " << h_papercolumns << "\n"
1931 << "\\papersides " << h_papersides << "\n"
1932 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1933 if (!h_listings_params.empty())
1934 os << "\\listings_params " << h_listings_params << "\n";
1935 os << "\\tracking_changes " << h_tracking_changes << "\n"
1936 << "\\output_changes " << h_output_changes << "\n"
1937 << "\\html_math_output " << h_html_math_output << "\n"
1938 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1939 << "\\html_be_strict " << h_html_be_strict << "\n"
1941 << "\\end_header\n\n"
1942 << "\\begin_body\n";
1947 void Preamble::parse(Parser & p, string const & forceclass,
1948 TeX2LyXDocClass & tc)
1950 // initialize fixed types
1951 special_columns_['D'] = 3;
1952 parse(p, forceclass, false, tc);
1956 void Preamble::parse(Parser & p, string const & forceclass,
1957 bool detectEncoding, TeX2LyXDocClass & tc)
1959 bool is_full_document = false;
1960 bool is_lyx_file = false;
1961 bool in_lyx_preamble = false;
1963 // determine whether this is a full document or a fragment for inclusion
1965 Token const & t = p.get_token();
1967 if (t.cat() == catEscape && t.cs() == "documentclass") {
1968 is_full_document = true;
1974 if (detectEncoding && !is_full_document)
1977 while (is_full_document && p.good()) {
1978 if (detectEncoding && h_inputencoding != "auto-legacy" &&
1979 h_inputencoding != "auto-legacy-plain")
1982 Token const & t = p.get_token();
1985 if (!detectEncoding)
1986 cerr << "t: " << t << '\n';
1992 if (!in_lyx_preamble &&
1993 (t.cat() == catLetter ||
1994 t.cat() == catSuper ||
1995 t.cat() == catSub ||
1996 t.cat() == catOther ||
1997 t.cat() == catMath ||
1998 t.cat() == catActive ||
1999 t.cat() == catBegin ||
2000 t.cat() == catEnd ||
2001 t.cat() == catAlign ||
2002 t.cat() == catParameter)) {
2003 h_preamble << t.cs();
2007 if (!in_lyx_preamble &&
2008 (t.cat() == catSpace || t.cat() == catNewline)) {
2009 h_preamble << t.asInput();
2013 if (t.cat() == catComment) {
2014 static regex const islyxfile("%% LyX .* created this file");
2015 static regex const usercommands("User specified LaTeX commands");
2017 string const comment = t.asInput();
2019 // magically switch encoding default if it looks like XeLaTeX
2020 static string const magicXeLaTeX =
2021 "% This document must be compiled with XeLaTeX ";
2022 if (comment.size() > magicXeLaTeX.size()
2023 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
2024 && h_inputencoding == "auto-legacy") {
2025 if (!detectEncoding)
2026 cerr << "XeLaTeX comment found, switching to UTF8\n";
2027 h_inputencoding = "utf8";
2030 if (regex_search(comment, sub, islyxfile)) {
2032 in_lyx_preamble = true;
2033 } else if (is_lyx_file
2034 && regex_search(comment, sub, usercommands))
2035 in_lyx_preamble = false;
2036 else if (!in_lyx_preamble)
2037 h_preamble << t.asInput();
2041 if (t.cs() == "PassOptionsToPackage") {
2042 string const poptions = p.getArg('{', '}');
2043 string const package = p.verbatim_item();
2044 extra_package_options_.insert(make_pair(package, poptions));
2048 if (t.cs() == "pagestyle") {
2049 h_paperpagestyle = p.verbatim_item();
2053 if (t.cs() == "setdefaultlanguage") {
2055 // We don't yet care about non-language variant options
2056 // because LyX doesn't support this yet, see bug #8214
2058 string langopts = p.getOpt();
2059 // check if the option contains a variant, if yes, extract it
2060 string::size_type pos_var = langopts.find("variant");
2061 string::size_type i = langopts.find(',', pos_var);
2062 string::size_type k = langopts.find('=', pos_var);
2063 if (pos_var != string::npos){
2065 if (i == string::npos)
2066 variant = langopts.substr(k + 1, langopts.length() - k - 2);
2068 variant = langopts.substr(k + 1, i - k - 1);
2069 h_language = variant;
2073 h_language = p.verbatim_item();
2074 //finally translate the poyglossia name to a LyX name
2075 h_language = polyglossia2lyx(h_language);
2079 if (t.cs() == "setotherlanguage") {
2080 // We don't yet care about the option because LyX doesn't
2081 // support this yet, see bug #8214
2082 p.hasOpt() ? p.getOpt() : string();
2087 if (t.cs() == "setmainfont") {
2088 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2089 h_font_roman[1] = p.getArg('{', '}');
2090 if (!fontopts.empty()) {
2091 vector<string> opts = getVectorFromString(fontopts);
2093 for (auto const & opt : opts) {
2094 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2097 if (!fontopts.empty())
2101 h_font_roman_opts = fontopts;
2106 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
2107 // LyX currently only supports the scale option
2108 string scale, fontopts;
2110 fontopts = p.getArg('[', ']');
2111 if (!fontopts.empty()) {
2112 vector<string> opts = getVectorFromString(fontopts);
2114 for (auto const & opt : opts) {
2115 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2118 if (prefixIs(opt, "Scale=")) {
2119 scale_as_percentage(opt, scale);
2122 if (!fontopts.empty())
2128 if (t.cs() == "setsansfont") {
2130 h_font_sf_scale[1] = scale;
2131 h_font_sans[1] = p.getArg('{', '}');
2132 if (!fontopts.empty())
2133 h_font_sans_opts = fontopts;
2136 h_font_tt_scale[1] = scale;
2137 h_font_typewriter[1] = p.getArg('{', '}');
2138 if (!fontopts.empty())
2139 h_font_typewriter_opts = fontopts;
2144 if (t.cs() == "babelfont") {
2146 h_use_non_tex_fonts = true;
2147 h_language_package = "babel";
2148 if (h_inputencoding == "auto-legacy")
2149 p.setEncoding("UTF-8");
2150 // we don't care about the lang option
2151 string const lang = p.hasOpt() ? p.getArg('[', ']') : string();
2152 string const family = p.getArg('{', '}');
2153 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2154 string const fontname = p.getArg('{', '}');
2155 if (lang.empty() && family == "rm") {
2156 h_font_roman[1] = fontname;
2157 if (!fontopts.empty()) {
2158 vector<string> opts = getVectorFromString(fontopts);
2160 for (auto const & opt : opts) {
2161 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2164 if (!fontopts.empty())
2168 h_font_roman_opts = fontopts;
2171 } else if (lang.empty() && (family == "sf" || family == "tt")) {
2173 if (!fontopts.empty()) {
2174 vector<string> opts = getVectorFromString(fontopts);
2176 for (auto const & opt : opts) {
2177 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2180 if (prefixIs(opt, "Scale=")) {
2181 scale_as_percentage(opt, scale);
2184 if (!fontopts.empty())
2189 if (family == "sf") {
2191 h_font_sf_scale[1] = scale;
2192 h_font_sans[1] = fontname;
2193 if (!fontopts.empty())
2194 h_font_sans_opts = fontopts;
2197 h_font_tt_scale[1] = scale;
2198 h_font_typewriter[1] = fontname;
2199 if (!fontopts.empty())
2200 h_font_typewriter_opts = fontopts;
2204 // not rm, sf or tt or lang specific
2205 h_preamble << '\\' << t.cs();
2207 h_preamble << '[' << lang << ']';
2208 h_preamble << '{' << family << '}';
2209 if (!fontopts.empty())
2210 h_preamble << '[' << fontopts << ']';
2211 h_preamble << '{' << fontname << '}' << '\n';
2216 if (t.cs() == "date") {
2217 string argument = p.getArg('{', '}');
2218 if (argument.empty())
2219 h_suppress_date = "true";
2221 h_preamble << t.asInput() << '{' << argument << '}';
2225 if (t.cs() == "color") {
2226 string const space =
2227 (p.hasOpt() ? p.getOpt() : string());
2228 string argument = p.getArg('{', '}');
2229 // check the case that a standard color is used
2230 if (space.empty() && is_known(argument, known_basic_colors)) {
2231 h_fontcolor = rgbcolor2code(argument);
2232 registerAutomaticallyLoadedPackage("color");
2233 } else if (space.empty() && argument == "document_fontcolor")
2234 registerAutomaticallyLoadedPackage("color");
2235 // check the case that LyX's document_fontcolor is defined
2236 // but not used for \color
2238 h_preamble << t.asInput();
2240 h_preamble << space;
2241 h_preamble << '{' << argument << '}';
2242 // the color might already be set because \definecolor
2243 // is parsed before this
2249 if (t.cs() == "pagecolor") {
2250 string argument = p.getArg('{', '}');
2251 // check the case that a standard color is used
2252 if (is_known(argument, known_basic_colors)) {
2253 h_backgroundcolor = rgbcolor2code(argument);
2254 } else if (argument == "page_backgroundcolor")
2255 registerAutomaticallyLoadedPackage("color");
2256 // check the case that LyX's page_backgroundcolor is defined
2257 // but not used for \pagecolor
2259 h_preamble << t.asInput() << '{' << argument << '}';
2260 // the color might already be set because \definecolor
2261 // is parsed before this
2262 h_backgroundcolor = "";
2267 if (t.cs() == "makeatletter") {
2268 // LyX takes care of this
2269 p.setCatcode('@', catLetter);
2273 if (t.cs() == "makeatother") {
2274 // LyX takes care of this
2275 p.setCatcode('@', catOther);
2279 if (t.cs() == "makeindex") {
2280 // LyX will re-add this if a print index command is found
2285 if (t.cs() == "newindex") {
2286 string const indexname = p.getArg('[', ']');
2287 string const shortcut = p.verbatim_item();
2288 if (!indexname.empty())
2289 h_index[index_number] = indexname;
2291 h_index[index_number] = shortcut;
2292 h_shortcut[index_number] = shortcut;
2298 if (t.cs() == "addbibresource") {
2299 string const options = p.getArg('[', ']');
2300 string const arg = removeExtension(p.getArg('{', '}'));
2301 if (!options.empty()) {
2302 // check if the option contains a bibencoding, if yes, extract it
2303 string::size_type pos = options.find("bibencoding=");
2305 if (pos != string::npos) {
2306 string::size_type i = options.find(',', pos);
2307 if (i == string::npos)
2308 encoding = options.substr(pos + 1);
2310 encoding = options.substr(pos, i - pos);
2311 pos = encoding.find('=');
2312 if (pos == string::npos)
2315 encoding = encoding.substr(pos + 1);
2317 if (!encoding.empty())
2318 biblatex_encodings.push_back(normalize_filename(arg) + ' ' + encoding);
2320 biblatex_bibliographies.push_back(arg);
2324 if (t.cs() == "bibliography") {
2325 vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
2326 biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
2330 if (t.cs() == "RS@ifundefined") {
2331 string const name = p.verbatim_item();
2332 string const body1 = p.verbatim_item();
2333 string const body2 = p.verbatim_item();
2334 // only non-lyxspecific stuff
2335 if (in_lyx_preamble &&
2336 (name == "subsecref" || name == "thmref" || name == "lemref"))
2340 ss << '\\' << t.cs();
2341 ss << '{' << name << '}'
2342 << '{' << body1 << '}'
2343 << '{' << body2 << '}';
2344 h_preamble << ss.str();
2349 if (t.cs() == "AtBeginDocument") {
2350 string const name = p.verbatim_item();
2351 // only non-lyxspecific stuff
2352 if (in_lyx_preamble &&
2353 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
2354 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
2355 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
2356 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
2357 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
2358 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
2359 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
2360 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
2361 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
2362 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
2363 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
2364 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
2365 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
2366 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
2367 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
2371 ss << '\\' << t.cs();
2372 ss << '{' << name << '}';
2373 h_preamble << ss.str();
2378 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
2379 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
2380 || t.cs() == "providecommand" || t.cs() == "providecommandx"
2381 || t.cs() == "DeclareRobustCommand"
2382 || t.cs() == "DeclareRobustCommandx"
2383 || t.cs() == "ProvideTextCommandDefault"
2384 || t.cs() == "DeclareMathAccent") {
2386 if (p.next_token().character() == '*') {
2390 string const name = p.verbatim_item();
2391 string const opt1 = p.getFullOpt();
2392 string const opt2 = p.getFullOpt();
2393 string const body = p.verbatim_item();
2394 // store the in_lyx_preamble setting
2395 bool const was_in_lyx_preamble = in_lyx_preamble;
2397 if (name == "\\rmdefault")
2398 if (is_known(body, known_roman_font_packages)) {
2399 h_font_roman[0] = body;
2401 in_lyx_preamble = true;
2403 if (name == "\\sfdefault")
2404 if (is_known(body, known_sans_font_packages)) {
2405 h_font_sans[0] = body;
2407 in_lyx_preamble = true;
2409 if (name == "\\ttdefault")
2410 if (is_known(body, known_typewriter_font_packages)) {
2411 h_font_typewriter[0] = body;
2413 in_lyx_preamble = true;
2415 if (name == "\\familydefault") {
2416 string family = body;
2417 // remove leading "\"
2418 h_font_default_family = family.erase(0,1);
2420 in_lyx_preamble = true;
2423 // remove LyX-specific definitions that are re-added by LyX
2425 // \lyxline is an ancient command that is converted by tex2lyx into
2426 // a \rule therefore remove its preamble code
2427 if (name == "\\lyxdot" || name == "\\lyxarrow"
2428 || name == "\\lyxline" || name == "\\LyX") {
2430 in_lyx_preamble = true;
2433 // Add the command to the known commands
2434 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
2436 // only non-lyxspecific stuff
2437 if (!in_lyx_preamble) {
2439 ss << '\\' << t.cs();
2442 ss << '{' << name << '}' << opt1 << opt2
2443 << '{' << body << "}";
2444 if (prefixIs(t.cs(), "renew") || !contains(h_preamble.str(), ss.str()))
2445 h_preamble << ss.str();
2447 ostream & out = in_preamble ? h_preamble : os;
2448 out << "\\" << t.cs() << "{" << name << "}"
2449 << opts << "{" << body << "}";
2452 // restore the in_lyx_preamble setting
2453 in_lyx_preamble = was_in_lyx_preamble;
2457 if (t.cs() == "documentclass") {
2458 vector<string>::iterator it;
2459 vector<string> opts = split_options(p.getArg('[', ']'));
2460 handle_opt(opts, known_fontsizes, h_paperfontsize);
2461 delete_opt(opts, known_fontsizes);
2462 // delete "pt" at the end
2463 string::size_type i = h_paperfontsize.find("pt");
2464 if (i != string::npos)
2465 h_paperfontsize.erase(i);
2466 // The documentclass options are always parsed before the options
2467 // of the babel call so that a language cannot overwrite the babel
2469 handle_opt(opts, known_languages, h_language);
2470 delete_opt(opts, known_languages);
2473 if ((it = find(opts.begin(), opts.end(), "fleqn"))
2475 h_is_mathindent = "1";
2478 // formula numbering side
2479 if ((it = find(opts.begin(), opts.end(), "leqno"))
2481 h_math_numbering_side = "left";
2484 else if ((it = find(opts.begin(), opts.end(), "reqno"))
2486 h_math_numbering_side = "right";
2490 // paper orientation
2491 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
2492 h_paperorientation = "landscape";
2496 if ((it = find(opts.begin(), opts.end(), "oneside"))
2501 if ((it = find(opts.begin(), opts.end(), "twoside"))
2507 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
2509 h_papercolumns = "1";
2512 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
2514 h_papercolumns = "2";
2518 // some size options are known to any document classes, other sizes
2519 // are handled by the \geometry command of the geometry package
2520 handle_opt(opts, known_class_paper_sizes, h_papersize);
2521 delete_opt(opts, known_class_paper_sizes);
2522 // the remaining options
2523 h_options = join(opts, ",");
2524 // FIXME This does not work for classes that have a
2525 // different name in LyX than in LaTeX
2526 h_textclass = p.getArg('{', '}');
2531 if (t.cs() == "usepackage") {
2532 string const options = p.getArg('[', ']');
2533 string const name = p.getArg('{', '}');
2534 vector<string> vecnames;
2535 split(name, vecnames, ',');
2536 vector<string>::const_iterator it = vecnames.begin();
2537 vector<string>::const_iterator end = vecnames.end();
2538 for (; it != end; ++it)
2539 handle_package(p, trimSpaceAndEol(*it), options,
2540 in_lyx_preamble, detectEncoding);
2544 if (t.cs() == "inputencoding") {
2545 string const encoding = p.getArg('{','}');
2546 Encoding const * const enc = encodings.fromLaTeXName(
2547 encoding, Encoding::inputenc, true);
2549 if (!detectEncoding)
2550 cerr << "Unknown encoding " << encoding
2551 << ". Ignoring." << std::endl;
2554 h_inputencoding = enc->name();
2555 p.setEncoding(enc->iconvName());
2560 if (t.cs() == "newenvironment") {
2561 string const name = p.getArg('{', '}');
2562 string const opt1 = p.getFullOpt();
2563 string const opt2 = p.getFullOpt();
2564 string const beg = p.verbatim_item();
2565 string const end = p.verbatim_item();
2566 if (!in_lyx_preamble) {
2567 h_preamble << "\\newenvironment{" << name
2568 << '}' << opt1 << opt2 << '{'
2569 << beg << "}{" << end << '}';
2571 add_known_environment(name, opt1, !opt2.empty(),
2572 from_utf8(beg), from_utf8(end));
2576 if (t.cs() == "newtheorem") {
2578 if (p.next_token().character() == '*') {
2582 string const name = p.getArg('{', '}');
2583 string const opt1 = p.getFullOpt();
2584 string const opt2 = p.getFullOpt();
2585 string const body = p.verbatim_item();
2586 string const opt3 = p.getFullOpt();
2587 string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
2589 string const complete = cmd + "{" + name + '}' +
2590 opt1 + opt2 + '{' + body + '}' + opt3;
2592 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
2594 if (!in_lyx_preamble)
2595 h_preamble << complete;
2599 if (t.cs() == "def") {
2600 string name = p.get_token().cs();
2601 // In fact, name may be more than the name:
2602 // In the test case of bug 8116
2603 // name == "csname SF@gobble@opt \endcsname".
2604 // Therefore, we need to use asInput() instead of cs().
2605 while (p.next_token().cat() != catBegin)
2606 name += p.get_token().asInput();
2607 if (!in_lyx_preamble)
2608 h_preamble << "\\def\\" << name << '{'
2609 << p.verbatim_item() << "}";
2613 if (t.cs() == "newcolumntype") {
2614 string const name = p.getArg('{', '}');
2615 trimSpaceAndEol(name);
2617 string opts = p.getOpt();
2618 if (!opts.empty()) {
2619 istringstream is(string(opts, 1));
2622 special_columns_[name[0]] = nargs;
2623 h_preamble << "\\newcolumntype{" << name << "}";
2625 h_preamble << "[" << nargs << "]";
2626 h_preamble << "{" << p.verbatim_item() << "}";
2630 if (t.cs() == "setcounter") {
2631 string const name = p.getArg('{', '}');
2632 string const content = p.getArg('{', '}');
2633 if (name == "secnumdepth")
2634 h_secnumdepth = content;
2635 else if (name == "tocdepth")
2636 h_tocdepth = content;
2638 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
2642 if (t.cs() == "setlength") {
2643 string const name = p.verbatim_item();
2644 string const content = p.verbatim_item();
2645 // the paragraphs are only not indented when \parindent is set to zero
2646 if (name == "\\parindent" && content != "") {
2647 if (content[0] == '0')
2648 h_paragraph_separation = "skip";
2650 h_paragraph_indentation = translate_len(content);
2651 } else if (name == "\\parskip") {
2652 if (content == "\\smallskipamount")
2653 h_defskip = "smallskip";
2654 else if (content == "\\medskipamount")
2655 h_defskip = "medskip";
2656 else if (content == "\\bigskipamount")
2657 h_defskip = "bigskip";
2659 h_defskip = translate_len(content);
2660 } else if (name == "\\mathindent") {
2661 h_mathindentation = translate_len(content);
2663 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2667 if (t.cs() == "onehalfspacing") {
2668 h_spacing = "onehalf";
2672 if (t.cs() == "doublespacing") {
2673 h_spacing = "double";
2677 if (t.cs() == "setstretch") {
2678 h_spacing = "other " + p.verbatim_item();
2682 if (t.cs() == "synctex") {
2683 // the scheme is \synctex=value
2684 // where value can only be "1" or "-1"
2685 h_output_sync = "1";
2686 // there can be any character behind the value (e.g. a linebreak or a '\'
2687 // therefore we extract it char by char
2689 string value = p.get_token().asInput();
2691 value += p.get_token().asInput();
2692 h_output_sync_macro = "\\synctex=" + value;
2696 if (t.cs() == "begin") {
2697 string const name = p.getArg('{', '}');
2698 if (name == "document")
2700 h_preamble << "\\begin{" << name << "}";
2704 if (t.cs() == "geometry") {
2705 vector<string> opts = split_options(p.getArg('{', '}'));
2706 handle_geometry(opts);
2710 if (t.cs() == "definecolor") {
2711 string const color = p.getArg('{', '}');
2712 string const space = p.getArg('{', '}');
2713 string const value = p.getArg('{', '}');
2714 if (color == "document_fontcolor" && space == "rgb") {
2715 RGBColor c(RGBColorFromLaTeX(value));
2716 h_fontcolor = X11hexname(c);
2717 } else if (color == "note_fontcolor" && space == "rgb") {
2718 RGBColor c(RGBColorFromLaTeX(value));
2719 h_notefontcolor = X11hexname(c);
2720 } else if (color == "page_backgroundcolor" && space == "rgb") {
2721 RGBColor c(RGBColorFromLaTeX(value));
2722 h_backgroundcolor = X11hexname(c);
2723 } else if (color == "shadecolor" && space == "rgb") {
2724 RGBColor c(RGBColorFromLaTeX(value));
2725 h_boxbgcolor = X11hexname(c);
2727 h_preamble << "\\definecolor{" << color
2728 << "}{" << space << "}{" << value
2734 if (t.cs() == "bibliographystyle") {
2735 h_biblio_style = p.verbatim_item();
2739 if (t.cs() == "jurabibsetup") {
2740 // FIXME p.getArg('{', '}') is most probably wrong (it
2741 // does not handle nested braces).
2742 // Use p.verbatim_item() instead.
2743 vector<string> jurabibsetup =
2744 split_options(p.getArg('{', '}'));
2745 // add jurabibsetup to the jurabib package options
2746 add_package("jurabib", jurabibsetup);
2747 if (!jurabibsetup.empty()) {
2748 h_preamble << "\\jurabibsetup{"
2749 << join(jurabibsetup, ",") << '}';
2754 if (t.cs() == "hypersetup") {
2755 vector<string> hypersetup =
2756 split_options(p.verbatim_item());
2757 // add hypersetup to the hyperref package options
2758 handle_hyperref(hypersetup);
2759 if (!hypersetup.empty()) {
2760 h_preamble << "\\hypersetup{"
2761 << join(hypersetup, ",") << '}';
2766 if (t.cs() == "includeonly") {
2767 vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
2768 for (auto & iofile : includeonlys) {
2769 string filename(normalize_filename(iofile));
2770 string const path = getMasterFilePath(true);
2771 // We want to preserve relative/absolute filenames,
2772 // therefore path is only used for testing
2773 if (!makeAbsPath(filename, path).exists()) {
2774 // The file extension is probably missing.
2775 // Now try to find it out.
2776 string const tex_name =
2777 find_file(filename, path,
2778 known_tex_extensions);
2779 if (!tex_name.empty())
2780 filename = tex_name;
2783 if (makeAbsPath(filename, path).exists())
2784 fix_child_filename(filename);
2786 cerr << "Warning: Could not find included file '"
2787 << filename << "'." << endl;
2788 outname = changeExtension(filename, "lyx");
2789 h_includeonlys.push_back(outname);
2794 if (is_known(t.cs(), known_if_3arg_commands)) {
2795 // prevent misparsing of \usepackage if it is used
2796 // as an argument (see e.g. our own output of
2797 // \@ifundefined above)
2798 string const arg1 = p.verbatim_item();
2799 string const arg2 = p.verbatim_item();
2800 string const arg3 = p.verbatim_item();
2801 // test case \@ifundefined{date}{}{\date{}}
2802 if (t.cs() == "@ifundefined" && arg1 == "date" &&
2803 arg2.empty() && arg3 == "\\date{}") {
2804 h_suppress_date = "true";
2805 // older tex2lyx versions did output
2806 // \@ifundefined{definecolor}{\usepackage{color}}{}
2807 } else if (t.cs() == "@ifundefined" &&
2808 arg1 == "definecolor" &&
2809 arg2 == "\\usepackage{color}" &&
2811 if (!in_lyx_preamble)
2812 h_preamble << package_beg_sep
2815 << "\\@ifundefined{definecolor}{color}{}"
2818 //\@ifundefined{showcaptionsetup}{}{%
2819 // \PassOptionsToPackage{caption=false}{subfig}}
2820 // that LyX uses for subfloats
2821 } else if (t.cs() == "@ifundefined" &&
2822 arg1 == "showcaptionsetup" && arg2.empty()
2823 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
2825 } else if (!in_lyx_preamble) {
2826 h_preamble << t.asInput()
2827 << '{' << arg1 << '}'
2828 << '{' << arg2 << '}'
2829 << '{' << arg3 << '}';
2834 if (is_known(t.cs(), known_if_commands)) {
2835 // must not parse anything in conditional code, since
2836 // LyX would output the parsed contents unconditionally
2837 if (!in_lyx_preamble)
2838 h_preamble << t.asInput();
2839 handle_if(p, in_lyx_preamble);
2843 if (!t.cs().empty() && !in_lyx_preamble) {
2844 h_preamble << '\\' << t.cs();
2849 // remove the whitespace
2852 // Force textclass if the user wanted it
2853 if (!forceclass.empty())
2854 h_textclass = forceclass;
2855 tc.setName(h_textclass);
2856 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2857 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2860 if (h_papersides.empty()) {
2863 h_papersides = ss.str();
2866 // If the CJK package is used we cannot set the document language from
2867 // the babel options. Instead, we guess which language is used most
2868 // and set this one.
2869 default_language = h_language;
2870 if (is_full_document &&
2871 (auto_packages.find("CJK") != auto_packages.end() ||
2872 auto_packages.find("CJKutf8") != auto_packages.end())) {
2874 h_language = guessLanguage(p, default_language);
2876 if (explicit_babel && h_language != default_language) {
2877 // We set the document language to a CJK language,
2878 // but babel is explicitly called in the user preamble
2879 // without options. LyX will not add the default
2880 // language to the document options if it is either
2881 // english, or no text is set as default language.
2882 // Therefore we need to add a language option explicitly.
2883 // FIXME: It would be better to remove all babel calls
2884 // from the user preamble, but this is difficult
2885 // without re-introducing bug 7861.
2886 if (h_options.empty())
2887 h_options = lyx2babel(default_language);
2889 h_options += ',' + lyx2babel(default_language);
2893 // Finally, set the quote style.
2894 // LyX knows the following quotes styles:
2895 // british, cjk, cjkangle, danish, english, french, german,
2896 // polish, russian, swedish and swiss
2897 // conversion list taken from
2898 // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
2899 // (quotes for kazakh are unknown)
2901 if (is_known(h_language, known_british_quotes_languages))
2902 h_quotes_style = "british";
2904 else if (is_known(h_language, known_cjk_quotes_languages))
2905 h_quotes_style = "cjk";
2907 else if (is_known(h_language, known_cjkangle_quotes_languages))
2908 h_quotes_style = "cjkangle";
2910 else if (is_known(h_language, known_danish_quotes_languages))
2911 h_quotes_style = "danish";
2913 else if (is_known(h_language, known_french_quotes_languages))
2914 h_quotes_style = "french";
2916 else if (is_known(h_language, known_german_quotes_languages))
2917 h_quotes_style = "german";
2919 else if (is_known(h_language, known_polish_quotes_languages))
2920 h_quotes_style = "polish";
2922 else if (is_known(h_language, known_russian_quotes_languages))
2923 h_quotes_style = "russian";
2925 else if (is_known(h_language, known_swedish_quotes_languages))
2926 h_quotes_style = "swedish";
2928 else if (is_known(h_language, known_swiss_quotes_languages))
2929 h_quotes_style = "swiss";
2931 else if (is_known(h_language, known_english_quotes_languages))
2932 h_quotes_style = "english";
2936 string Preamble::parseEncoding(Parser & p, string const & forceclass)
2938 TeX2LyXDocClass dummy;
2939 parse(p, forceclass, true, dummy);
2940 if (h_inputencoding != "auto-legacy" && h_inputencoding != "auto-legacy-plain")
2941 return h_inputencoding;
2946 string babel2lyx(string const & language)
2948 char const * const * where = is_known(language, known_languages);
2950 return known_coded_languages[where - known_languages];
2955 string lyx2babel(string const & language)
2957 char const * const * where = is_known(language, known_coded_languages);
2959 return known_languages[where - known_coded_languages];
2964 string Preamble::polyglossia2lyx(string const & language)
2966 char const * const * where = is_known(language, polyglossia_languages);
2968 return coded_polyglossia_languages[where - polyglossia_languages];
2973 string rgbcolor2code(string const & name)
2975 char const * const * where = is_known(name, known_basic_colors);
2977 // "red", "green" etc
2978 return known_basic_color_codes[where - known_basic_colors];
2980 // "255,0,0", "0,255,0" etc
2981 RGBColor c(RGBColorFromLaTeX(name));
2982 return X11hexname(c);