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", "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 pasing and
749 vector<string> allopts = getVectorFromString(opts);
750 // this stored 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 == "PTSans") {
1139 h_font_sans[0] = "PTSans-TLF";
1142 if (name == "FiraSans") {
1143 h_font_sans_osf = "true";
1144 for (auto const & opt : allopts) {
1145 if (opt == "book") {
1146 h_font_sans[0] = "FiraSansBook";
1149 if (opt == "thin") {
1152 if (opt == "extralight") {
1153 h_font_sans[0] = "FiraSansExtralight";
1156 if (opt == "light") {
1157 h_font_sans[0] = "FiraSansLight";
1160 if (opt == "ultralight") {
1161 h_font_sans[0] = "FiraSansUltralight";
1164 if (opt == "thin") {
1165 h_font_sans[0] = "FiraSansThin";
1168 if (opt == "lf" || opt == "lining") {
1169 h_font_sans_osf = "false";
1177 h_font_sans_opts = xopts;
1181 if (name == "plex-sans") {
1182 h_font_sans[0] = "IBMPlexSans";
1183 for (auto const & opt : allopts) {
1184 if (opt == "condensed") {
1185 h_font_sans[0] = "IBMPlexSansCondensed";
1188 if (opt == "thin") {
1189 h_font_sans[0] = "IBMPlexSansThin";
1192 if (opt == "extralight") {
1193 h_font_sans[0] = "IBMPlexSansExtraLight";
1196 if (opt == "light") {
1197 h_font_sans[0] = "IBMPlexSansLight";
1200 if (opt == "semibold") {
1201 h_font_sans[0] = "IBMPlexSansSemibold";
1209 h_font_sans_opts = xopts;
1213 if (name == "noto-sans") {
1214 h_font_sans[0] = "NotoSansRegular";
1215 for (auto const & opt : allopts) {
1216 if (opt == "regular")
1218 if (opt == "medium") {
1219 h_font_sans[0] = "NotoSansMedium";
1222 if (opt == "thin") {
1223 h_font_sans[0] = "NotoSansThin";
1226 if (opt == "extralight") {
1227 h_font_sans[0] = "NotoSansExtralight";
1230 if (opt == "light") {
1231 h_font_sans[0] = "NotoSansLight";
1235 h_font_sans_osf = "true";
1243 h_font_sans_opts = xopts;
1247 if (name == "sourcesanspro") {
1248 h_font_sans[0] = "ADOBESourceSansPro";
1249 for (auto const & opt : allopts) {
1251 h_font_sans_osf = "true";
1259 h_font_sans_opts = xopts;
1267 // By default, we use the package name as LyX font name,
1268 // so this only needs to be reset if these names differ.
1269 // Also, we handle the scaling option here generally.
1270 // Note: fourier can be set as roman font _only_
1271 // fourier as typewriter is handled in handling of \ttdefault
1272 if (is_known(name, known_typewriter_font_packages) && name != "fourier") {
1273 h_font_typewriter[0] = name;
1274 if (contains(opts, "scale")) {
1275 vector<string>::const_iterator it = allopts.begin();
1276 for (; it != allopts.end() ; ++it) {
1277 string const opt = *it;
1278 if (prefixIs(opt, "scaled=") || prefixIs(opt, "scale=")) {
1279 if (scale_as_percentage(opt, h_font_tt_scale[0])) {
1288 if (name == "libertineMono" || name == "libertineMono-type1")
1289 h_font_typewriter[0] = "libertine-mono";
1291 if (name == "FiraMono") {
1292 h_font_typewriter_osf = "true";
1293 for (auto const & opt : allopts) {
1294 if (opt == "lf" || opt == "lining") {
1295 h_font_typewriter_osf = "false";
1303 h_font_typewriter_opts = xopts;
1307 if (name == "PTMono") {
1308 h_font_typewriter[0] = "PTMono-TLF";
1309 if (options.size() >= 1) {
1310 if (scale_as_percentage(opts, h_font_tt_scale[0]))
1315 if (name == "plex-mono") {
1316 h_font_typewriter[0] = "IBMPlexMono";
1317 for (auto const & opt : allopts) {
1318 if (opt == "thin") {
1319 h_font_typewriter[0] = "IBMPlexMonoThin";
1322 if (opt == "extralight") {
1323 h_font_typewriter[0] = "IBMPlexMonoExtraLight";
1326 if (opt == "light") {
1327 h_font_typewriter[0] = "IBMPlexMonoLight";
1330 if (opt == "semibold"){
1331 h_font_typewriter[0] = "IBMPlexMonoSemibold";
1339 h_font_typewriter_opts = xopts;
1343 if (name == "noto-mono") {
1344 h_font_typewriter[0] = "NotoMonoRegular";
1345 for (auto const & opt : allopts) {
1346 if (opt == "regular")
1353 h_font_typewriter_opts = xopts;
1357 if (name == "sourcecodepro") {
1358 h_font_typewriter[0] = "ADOBESourceCodePro";
1359 for (auto const & opt : allopts) {
1361 h_font_typewriter_osf = "true";
1369 h_font_typewriter_opts = xopts;
1377 // By default, we use the package name as LyX font name,
1378 // so this only needs to be reset if these names differ.
1379 if (is_known(name, known_math_font_packages))
1380 h_font_math[0] = name;
1382 if (name == "newtxmath") {
1384 h_font_math[0] = "newtxmath";
1385 else if (opts == "garamondx")
1386 h_font_math[0] = "garamondx-ntxm";
1387 else if (opts == "libertine")
1388 h_font_math[0] = "libertine-ntxm";
1389 else if (opts == "minion")
1390 h_font_math[0] = "minion-ntxm";
1391 else if (opts == "cochineal")
1392 h_font_math[0] = "cochineal-ntxm";
1395 if (name == "iwona")
1397 h_font_math[0] = "iwona-math";
1399 if (name == "kurier")
1401 h_font_math[0] = "kurier-math";
1403 // after the detection and handling of special cases, we can remove the
1404 // fonts, otherwise they would appear in the preamble, see bug #7856
1405 if (is_known(name, known_roman_font_packages) || is_known(name, known_sans_font_packages)
1406 || is_known(name, known_typewriter_font_packages) || is_known(name, known_math_font_packages))
1408 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
1409 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
1410 name == "esint" || name == "mhchem" || name == "mathdots" ||
1411 name == "mathtools" || name == "stackrel" ||
1412 name == "stmaryrd" || name == "undertilde") {
1413 h_use_packages[name] = "2";
1414 registerAutomaticallyLoadedPackage(name);
1417 else if (name == "babel") {
1418 h_language_package = "default";
1419 // One might think we would have to do nothing if babel is loaded
1420 // without any options to prevent pollution of the preamble with this
1421 // babel call in every roundtrip.
1422 // But the user could have defined babel-specific things afterwards. So
1423 // we need to keep it in the preamble to prevent cases like bug #7861.
1424 if (!opts.empty()) {
1425 // check if more than one option was used - used later for inputenc
1426 if (options.begin() != options.end() - 1)
1427 one_language = false;
1428 // babel takes the last language of the option of its \usepackage
1429 // call as document language. If there is no such language option, the
1430 // last language in the documentclass options is used.
1431 handle_opt(options, known_languages, h_language);
1432 // translate the babel name to a LyX name
1433 h_language = babel2lyx(h_language);
1434 if (h_language == "japanese") {
1435 // For Japanese, the encoding isn't indicated in the source
1436 // file, and there's really not much we can do. We could
1437 // 1) offer a list of possible encodings to choose from, or
1438 // 2) determine the encoding of the file by inspecting it.
1439 // For the time being, we leave the encoding alone so that
1440 // we don't get iconv errors when making a wrong guess, and
1441 // we will output a note at the top of the document
1442 // explaining what to do.
1443 Encoding const * const enc = encodings.fromIconvName(
1444 p.getEncoding(), Encoding::japanese, false);
1446 h_inputencoding = enc->name();
1447 is_nonCJKJapanese = true;
1448 // in this case babel can be removed from the preamble
1449 registerAutomaticallyLoadedPackage("babel");
1451 // If babel is called with options, LyX puts them by default into the
1452 // document class options. This works for most languages, except
1453 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
1454 // perhaps in future others.
1455 // Therefore keep the babel call as it is as the user might have
1457 string const babelcall = "\\usepackage[" + opts + "]{babel}\n";
1458 if (!contains(h_preamble.str(), babelcall))
1459 h_preamble << babelcall;
1461 delete_opt(options, known_languages);
1463 if (!contains(h_preamble.str(), "\\usepackage{babel}\n"))
1464 h_preamble << "\\usepackage{babel}\n";
1465 explicit_babel = true;
1469 else if (name == "polyglossia") {
1470 h_language_package = "default";
1471 h_default_output_format = "pdf4";
1472 h_use_non_tex_fonts = true;
1474 registerAutomaticallyLoadedPackage("xunicode");
1475 if (h_inputencoding == "auto-legacy")
1476 p.setEncoding("UTF-8");
1479 else if (name == "CJK") {
1480 // set the encoding to "auto-legacy" because it might be set to "auto-legacy-plain" by the babel handling
1481 // and this would not be correct for CJK
1482 if (h_inputencoding == "auto-legacy-plain")
1483 h_inputencoding = "auto-legacy";
1484 registerAutomaticallyLoadedPackage("CJK");
1487 else if (name == "CJKutf8") {
1488 h_inputencoding = "utf8-cjk";
1489 p.setEncoding("UTF-8");
1490 registerAutomaticallyLoadedPackage("CJKutf8");
1493 else if (name == "fontenc") {
1494 h_fontencoding = getStringFromVector(options, ",");
1498 else if (name == "inputenc" || name == "luainputenc") {
1499 // h_inputencoding is only set when there is not more than one
1500 // inputenc option because otherwise h_inputencoding must be
1501 // set to "auto-legacy" (the default encodings of the document's languages)
1502 // Therefore check that exactly one option is passed to inputenc.
1503 // It is also only set when there is not more than one babel
1505 if (!options.empty()) {
1506 string const encoding = options.back();
1507 Encoding const * const enc = encodings.fromLaTeXName(
1508 encoding, Encoding::inputenc, true);
1510 if (!detectEncoding)
1511 cerr << "Unknown encoding " << encoding
1512 << ". Ignoring." << std::endl;
1514 if (!enc->unsafe() && options.size() == 1 && one_language == true)
1515 h_inputencoding = enc->name();
1516 p.setEncoding(enc->iconvName());
1522 else if (name == "srcltx") {
1523 h_output_sync = "1";
1524 if (!opts.empty()) {
1525 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
1528 h_output_sync_macro = "\\usepackage{srcltx}";
1531 else if (is_known(name, known_old_language_packages)) {
1532 // known language packages from the times before babel
1533 // if they are found and not also babel, they will be used as
1534 // custom language package
1535 h_language_package = "\\usepackage{" + name + "}";
1538 else if (name == "lyxskak") {
1539 // ignore this and its options
1540 const char * const o[] = {"ps", "mover", 0};
1541 delete_opt(options, o);
1544 else if (is_known(name, known_lyx_packages) && options.empty()) {
1545 if (name == "splitidx")
1546 h_use_indices = "true";
1547 else if (name == "minted")
1548 h_use_minted = true;
1549 else if (name == "refstyle")
1550 h_use_refstyle = true;
1551 else if (name == "prettyref")
1552 h_use_refstyle = false;
1553 if (!in_lyx_preamble) {
1554 h_preamble << package_beg_sep << name
1555 << package_mid_sep << "\\usepackage{"
1557 if (p.next_token().cat() == catNewline ||
1558 (p.next_token().cat() == catSpace &&
1559 p.next_next_token().cat() == catNewline))
1561 h_preamble << package_end_sep;
1565 else if (name == "geometry")
1566 handle_geometry(options);
1568 else if (name == "subfig")
1569 ; // ignore this FIXME: Use the package separator mechanism instead
1571 else if (char const * const * where = is_known(name, known_languages))
1572 h_language = known_coded_languages[where - known_languages];
1574 else if (name == "natbib") {
1575 h_biblio_style = "plainnat";
1576 h_cite_engine = "natbib";
1577 h_cite_engine_type = "authoryear";
1578 vector<string>::iterator it =
1579 find(options.begin(), options.end(), "authoryear");
1580 if (it != options.end())
1583 it = find(options.begin(), options.end(), "numbers");
1584 if (it != options.end()) {
1585 h_cite_engine_type = "numerical";
1589 if (!options.empty())
1590 h_biblio_options = join(options, ",");
1593 else if (name == "biblatex") {
1594 h_biblio_style = "plainnat";
1595 h_cite_engine = "biblatex";
1596 h_cite_engine_type = "authoryear";
1598 vector<string>::iterator it =
1599 find(options.begin(), options.end(), "natbib");
1600 if (it != options.end()) {
1602 h_cite_engine = "biblatex-natbib";
1604 opt = process_keyval_opt(options, "natbib");
1606 h_cite_engine = "biblatex-natbib";
1608 opt = process_keyval_opt(options, "style");
1610 h_biblatex_citestyle = opt;
1611 h_biblatex_bibstyle = opt;
1613 opt = process_keyval_opt(options, "citestyle");
1615 h_biblatex_citestyle = opt;
1616 opt = process_keyval_opt(options, "bibstyle");
1618 h_biblatex_bibstyle = opt;
1620 opt = process_keyval_opt(options, "refsection");
1622 if (opt == "none" || opt == "part"
1623 || opt == "chapter" || opt == "section"
1624 || opt == "subsection")
1627 cerr << "Ignoring unkown refesection value '"
1630 opt = process_keyval_opt(options, "bibencoding");
1633 if (!options.empty()) {
1634 h_biblio_options = join(options, ",");
1639 else if (name == "jurabib") {
1640 h_biblio_style = "jurabib";
1641 h_cite_engine = "jurabib";
1642 h_cite_engine_type = "authoryear";
1643 if (!options.empty())
1644 h_biblio_options = join(options, ",");
1647 else if (name == "bibtopic")
1648 h_use_bibtopic = "true";
1650 else if (name == "chapterbib")
1651 h_multibib = "child";
1653 else if (name == "hyperref")
1654 handle_hyperref(options);
1656 else if (name == "algorithm2e") {
1657 // Load "algorithm2e" module
1658 addModule("algorithm2e");
1659 // Add the package options to the global document options
1660 if (!options.empty()) {
1661 if (h_options.empty())
1662 h_options = join(options, ",");
1664 h_options += ',' + join(options, ",");
1667 else if (name == "microtype") {
1668 //we internally support only microtype without params
1669 if (options.empty())
1670 h_use_microtype = "true";
1672 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1675 else if (name == "lineno") {
1676 h_use_lineno = "true";
1677 if (!options.empty()) {
1678 h_lineno_options = join(options, ",");
1683 else if (!in_lyx_preamble) {
1684 if (options.empty())
1685 h_preamble << "\\usepackage{" << name << '}';
1687 h_preamble << "\\usepackage[" << opts << "]{"
1691 if (p.next_token().cat() == catNewline ||
1692 (p.next_token().cat() == catSpace &&
1693 p.next_next_token().cat() == catNewline))
1697 // We need to do something with the options...
1698 if (!options.empty() && !detectEncoding)
1699 cerr << "Ignoring options '" << join(options, ",")
1700 << "' of package " << name << '.' << endl;
1702 // remove the whitespace
1707 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1710 Token t = p.get_token();
1711 if (t.cat() == catEscape &&
1712 is_known(t.cs(), known_if_commands))
1713 handle_if(p, in_lyx_preamble);
1715 if (!in_lyx_preamble)
1716 h_preamble << t.asInput();
1717 if (t.cat() == catEscape && t.cs() == "fi")
1724 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1726 if (contains(h_float_placement, "H"))
1727 registerAutomaticallyLoadedPackage("float");
1728 if (h_spacing != "single" && h_spacing != "default")
1729 registerAutomaticallyLoadedPackage("setspace");
1730 if (h_use_packages["amsmath"] == "2") {
1731 // amsbsy and amstext are already provided by amsmath
1732 registerAutomaticallyLoadedPackage("amsbsy");
1733 registerAutomaticallyLoadedPackage("amstext");
1736 // output the LyX file settings
1737 // Important: Keep the version formatting in sync with LyX and
1738 // lyx2lyx (bug 7951)
1739 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1740 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1741 << lyx_version_minor << '\n'
1742 << "\\lyxformat " << LYX_FORMAT << '\n'
1743 << "\\begin_document\n"
1744 << "\\begin_header\n"
1745 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1746 << "\\origin " << origin << "\n"
1747 << "\\textclass " << h_textclass << "\n";
1748 string const raw = subdoc ? empty_string() : h_preamble.str();
1750 os << "\\begin_preamble\n";
1751 for (string::size_type i = 0; i < raw.size(); ++i) {
1752 if (raw[i] == package_beg_sep) {
1753 // Here follows some package loading code that
1754 // must be skipped if the package is loaded
1756 string::size_type j = raw.find(package_mid_sep, i);
1757 if (j == string::npos)
1759 string::size_type k = raw.find(package_end_sep, j);
1760 if (k == string::npos)
1762 string const package = raw.substr(i + 1, j - i - 1);
1763 string const replacement = raw.substr(j + 1, k - j - 1);
1764 if (auto_packages.find(package) == auto_packages.end())
1770 os << "\n\\end_preamble\n";
1772 if (!h_options.empty())
1773 os << "\\options " << h_options << "\n";
1774 os << "\\use_default_options " << h_use_default_options << "\n";
1775 if (!used_modules.empty()) {
1776 os << "\\begin_modules\n";
1777 vector<string>::const_iterator const end = used_modules.end();
1778 vector<string>::const_iterator it = used_modules.begin();
1779 for (; it != end; ++it)
1781 os << "\\end_modules\n";
1783 if (!h_includeonlys.empty()) {
1784 os << "\\begin_includeonly\n";
1785 for (auto const & iofile : h_includeonlys)
1786 os << iofile << '\n';
1787 os << "\\end_includeonly\n";
1789 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1790 << "\\language " << h_language << "\n"
1791 << "\\language_package " << h_language_package << "\n"
1792 << "\\inputencoding " << h_inputencoding << "\n"
1793 << "\\fontencoding " << h_fontencoding << "\n"
1794 << "\\font_roman \"" << h_font_roman[0]
1795 << "\" \"" << h_font_roman[1] << "\"\n"
1796 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1797 << "\\font_typewriter \"" << h_font_typewriter[0]
1798 << "\" \"" << h_font_typewriter[1] << "\"\n"
1799 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1800 << "\\font_default_family " << h_font_default_family << "\n"
1801 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1802 << "\\font_sc " << h_font_sc << "\n"
1803 << "\\font_roman_osf " << h_font_roman_osf << "\n"
1804 << "\\font_sans_osf " << h_font_sans_osf << "\n"
1805 << "\\font_typewriter_osf " << h_font_typewriter_osf << "\n";
1806 if (!h_font_roman_opts.empty())
1807 os << "\\font_roman_opts \"" << h_font_roman_opts << "\"" << '\n';
1808 os << "\\font_sf_scale " << h_font_sf_scale[0]
1809 << ' ' << h_font_sf_scale[1] << '\n';
1810 if (!h_font_sans_opts.empty())
1811 os << "\\font_sans_opts \"" << h_font_sans_opts << "\"" << '\n';
1812 os << "\\font_tt_scale " << h_font_tt_scale[0]
1813 << ' ' << h_font_tt_scale[1] << '\n';
1814 if (!h_font_cjk.empty())
1815 os << "\\font_cjk " << h_font_cjk << '\n';
1816 if (!h_font_typewriter_opts.empty())
1817 os << "\\font_typewriter_opts \"" << h_font_typewriter_opts << "\"" << '\n';
1818 os << "\\use_microtype " << h_use_microtype << '\n'
1819 << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
1820 << "\\graphics " << h_graphics << '\n'
1821 << "\\default_output_format " << h_default_output_format << "\n"
1822 << "\\output_sync " << h_output_sync << "\n";
1823 if (h_output_sync == "1")
1824 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1825 os << "\\bibtex_command " << h_bibtex_command << "\n"
1826 << "\\index_command " << h_index_command << "\n";
1827 if (!h_float_placement.empty())
1828 os << "\\float_placement " << h_float_placement << "\n";
1829 os << "\\paperfontsize " << h_paperfontsize << "\n"
1830 << "\\spacing " << h_spacing << "\n"
1831 << "\\use_hyperref " << h_use_hyperref << '\n';
1832 if (h_use_hyperref == "true") {
1833 if (!h_pdf_title.empty())
1834 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1835 if (!h_pdf_author.empty())
1836 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1837 if (!h_pdf_subject.empty())
1838 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1839 if (!h_pdf_keywords.empty())
1840 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1841 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1842 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1843 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1844 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1845 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1846 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1847 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1848 "\\pdf_backref " << h_pdf_backref << "\n"
1849 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1850 if (!h_pdf_pagemode.empty())
1851 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1852 if (!h_pdf_quoted_options.empty())
1853 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1855 os << "\\papersize " << h_papersize << "\n"
1856 << "\\use_geometry " << h_use_geometry << '\n';
1857 for (map<string, string>::const_iterator it = h_use_packages.begin();
1858 it != h_use_packages.end(); ++it)
1859 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1860 os << "\\cite_engine " << h_cite_engine << '\n'
1861 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1862 << "\\biblio_style " << h_biblio_style << "\n"
1863 << "\\use_bibtopic " << h_use_bibtopic << "\n";
1864 if (!h_biblio_options.empty())
1865 os << "\\biblio_options " << h_biblio_options << "\n";
1866 if (!h_biblatex_bibstyle.empty())
1867 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
1868 if (!h_biblatex_citestyle.empty())
1869 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
1870 if (!h_multibib.empty())
1871 os << "\\multibib " << h_multibib << "\n";
1872 os << "\\use_indices " << h_use_indices << "\n"
1873 << "\\paperorientation " << h_paperorientation << '\n'
1874 << "\\suppress_date " << h_suppress_date << '\n'
1875 << "\\justification " << h_justification << '\n'
1876 << "\\use_refstyle " << h_use_refstyle << '\n'
1877 << "\\use_minted " << h_use_minted << '\n'
1878 << "\\use_lineno " << h_use_lineno << '\n';
1879 if (!h_lineno_options.empty())
1880 os << "\\lineno_options " << h_lineno_options << '\n';
1881 if (!h_fontcolor.empty())
1882 os << "\\fontcolor " << h_fontcolor << '\n';
1883 if (!h_notefontcolor.empty())
1884 os << "\\notefontcolor " << h_notefontcolor << '\n';
1885 if (!h_backgroundcolor.empty())
1886 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1887 if (!h_boxbgcolor.empty())
1888 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1889 if (index_number != 0)
1890 for (int i = 0; i < index_number; i++) {
1891 os << "\\index " << h_index[i] << '\n'
1892 << "\\shortcut " << h_shortcut[i] << '\n'
1893 << "\\color " << h_color << '\n'
1897 os << "\\index " << h_index[0] << '\n'
1898 << "\\shortcut " << h_shortcut[0] << '\n'
1899 << "\\color " << h_color << '\n'
1903 << "\\secnumdepth " << h_secnumdepth << "\n"
1904 << "\\tocdepth " << h_tocdepth << "\n"
1905 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1906 if (h_paragraph_separation == "skip")
1907 os << "\\defskip " << h_defskip << "\n";
1909 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1910 os << "\\is_math_indent " << h_is_mathindent << "\n";
1911 if (!h_mathindentation.empty())
1912 os << "\\math_indentation " << h_mathindentation << "\n";
1913 os << "\\math_numbering_side " << h_math_numbering_side << "\n";
1914 os << "\\quotes_style " << h_quotes_style << "\n"
1915 << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
1916 << "\\papercolumns " << h_papercolumns << "\n"
1917 << "\\papersides " << h_papersides << "\n"
1918 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1919 if (!h_listings_params.empty())
1920 os << "\\listings_params " << h_listings_params << "\n";
1921 os << "\\tracking_changes " << h_tracking_changes << "\n"
1922 << "\\output_changes " << h_output_changes << "\n"
1923 << "\\html_math_output " << h_html_math_output << "\n"
1924 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1925 << "\\html_be_strict " << h_html_be_strict << "\n"
1927 << "\\end_header\n\n"
1928 << "\\begin_body\n";
1933 void Preamble::parse(Parser & p, string const & forceclass,
1934 TeX2LyXDocClass & tc)
1936 // initialize fixed types
1937 special_columns_['D'] = 3;
1938 parse(p, forceclass, false, tc);
1942 void Preamble::parse(Parser & p, string const & forceclass,
1943 bool detectEncoding, TeX2LyXDocClass & tc)
1945 bool is_full_document = false;
1946 bool is_lyx_file = false;
1947 bool in_lyx_preamble = false;
1949 // determine whether this is a full document or a fragment for inclusion
1951 Token const & t = p.get_token();
1953 if (t.cat() == catEscape && t.cs() == "documentclass") {
1954 is_full_document = true;
1960 if (detectEncoding && !is_full_document)
1963 while (is_full_document && p.good()) {
1964 if (detectEncoding && h_inputencoding != "auto-legacy" &&
1965 h_inputencoding != "auto-legacy-plain")
1968 Token const & t = p.get_token();
1971 if (!detectEncoding)
1972 cerr << "t: " << t << '\n';
1978 if (!in_lyx_preamble &&
1979 (t.cat() == catLetter ||
1980 t.cat() == catSuper ||
1981 t.cat() == catSub ||
1982 t.cat() == catOther ||
1983 t.cat() == catMath ||
1984 t.cat() == catActive ||
1985 t.cat() == catBegin ||
1986 t.cat() == catEnd ||
1987 t.cat() == catAlign ||
1988 t.cat() == catParameter)) {
1989 h_preamble << t.cs();
1993 if (!in_lyx_preamble &&
1994 (t.cat() == catSpace || t.cat() == catNewline)) {
1995 h_preamble << t.asInput();
1999 if (t.cat() == catComment) {
2000 static regex const islyxfile("%% LyX .* created this file");
2001 static regex const usercommands("User specified LaTeX commands");
2003 string const comment = t.asInput();
2005 // magically switch encoding default if it looks like XeLaTeX
2006 static string const magicXeLaTeX =
2007 "% This document must be compiled with XeLaTeX ";
2008 if (comment.size() > magicXeLaTeX.size()
2009 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
2010 && h_inputencoding == "auto-legacy") {
2011 if (!detectEncoding)
2012 cerr << "XeLaTeX comment found, switching to UTF8\n";
2013 h_inputencoding = "utf8";
2016 if (regex_search(comment, sub, islyxfile)) {
2018 in_lyx_preamble = true;
2019 } else if (is_lyx_file
2020 && regex_search(comment, sub, usercommands))
2021 in_lyx_preamble = false;
2022 else if (!in_lyx_preamble)
2023 h_preamble << t.asInput();
2027 if (t.cs() == "PassOptionsToPackage") {
2028 string const poptions = p.getArg('{', '}');
2029 string const package = p.verbatim_item();
2030 extra_package_options_.insert(make_pair(package, poptions));
2034 if (t.cs() == "pagestyle") {
2035 h_paperpagestyle = p.verbatim_item();
2039 if (t.cs() == "setdefaultlanguage") {
2041 // We don't yet care about non-language variant options
2042 // because LyX doesn't support this yet, see bug #8214
2044 string langopts = p.getOpt();
2045 // check if the option contains a variant, if yes, extract it
2046 string::size_type pos_var = langopts.find("variant");
2047 string::size_type i = langopts.find(',', pos_var);
2048 string::size_type k = langopts.find('=', pos_var);
2049 if (pos_var != string::npos){
2051 if (i == string::npos)
2052 variant = langopts.substr(k + 1, langopts.length() - k - 2);
2054 variant = langopts.substr(k + 1, i - k - 1);
2055 h_language = variant;
2059 h_language = p.verbatim_item();
2060 //finally translate the poyglossia name to a LyX name
2061 h_language = polyglossia2lyx(h_language);
2065 if (t.cs() == "setotherlanguage") {
2066 // We don't yet care about the option because LyX doesn't
2067 // support this yet, see bug #8214
2068 p.hasOpt() ? p.getOpt() : string();
2073 if (t.cs() == "setmainfont") {
2074 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2075 h_font_roman[1] = p.getArg('{', '}');
2076 if (!fontopts.empty()) {
2077 vector<string> opts = getVectorFromString(fontopts);
2079 for (auto const & opt : opts) {
2080 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2083 if (!fontopts.empty())
2087 h_font_roman_opts = fontopts;
2092 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
2093 // LyX currently only supports the scale option
2094 string scale, fontopts;
2096 fontopts = p.getArg('[', ']');
2097 if (!fontopts.empty()) {
2098 vector<string> opts = getVectorFromString(fontopts);
2100 for (auto const & opt : opts) {
2101 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2104 if (prefixIs(opt, "Scale=")) {
2105 scale_as_percentage(opt, scale);
2108 if (!fontopts.empty())
2114 if (t.cs() == "setsansfont") {
2116 h_font_sf_scale[1] = scale;
2117 h_font_sans[1] = p.getArg('{', '}');
2118 if (!fontopts.empty())
2119 h_font_sans_opts = fontopts;
2122 h_font_tt_scale[1] = scale;
2123 h_font_typewriter[1] = p.getArg('{', '}');
2124 if (!fontopts.empty())
2125 h_font_typewriter_opts = fontopts;
2130 if (t.cs() == "babelfont") {
2132 h_use_non_tex_fonts = true;
2133 h_language_package = "babel";
2134 if (h_inputencoding == "auto-legacy")
2135 p.setEncoding("UTF-8");
2136 // we don't care about the lang option
2137 string const lang = p.hasOpt() ? p.getArg('[', ']') : string();
2138 string const family = p.getArg('{', '}');
2139 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2140 string const fontname = p.getArg('{', '}');
2141 if (lang.empty() && family == "rm") {
2142 h_font_roman[1] = fontname;
2143 if (!fontopts.empty()) {
2144 vector<string> opts = getVectorFromString(fontopts);
2146 for (auto const & opt : opts) {
2147 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2150 if (!fontopts.empty())
2154 h_font_roman_opts = fontopts;
2157 } else if (lang.empty() && (family == "sf" || family == "tt")) {
2158 // LyX currently only supports the scale option
2160 if (!fontopts.empty()) {
2161 vector<string> opts = getVectorFromString(fontopts);
2163 for (auto const & opt : opts) {
2164 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2167 if (prefixIs(opt, "Scale=")) {
2168 scale_as_percentage(opt, scale);
2171 if (!fontopts.empty())
2176 if (family == "sf") {
2178 h_font_sf_scale[1] = scale;
2179 h_font_sans[1] = fontname;
2180 if (!fontopts.empty())
2181 h_font_sans_opts = fontopts;
2184 h_font_tt_scale[1] = scale;
2185 h_font_typewriter[1] = fontname;
2186 if (!fontopts.empty())
2187 h_font_typewriter_opts = fontopts;
2191 // not rm, sf or tt or lang specific
2192 h_preamble << '\\' << t.cs();
2194 h_preamble << '[' << lang << ']';
2195 h_preamble << '{' << family << '}';
2196 if (!fontopts.empty())
2197 h_preamble << '[' << fontopts << ']';
2198 h_preamble << '{' << fontname << '}' << '\n';
2203 if (t.cs() == "date") {
2204 string argument = p.getArg('{', '}');
2205 if (argument.empty())
2206 h_suppress_date = "true";
2208 h_preamble << t.asInput() << '{' << argument << '}';
2212 if (t.cs() == "color") {
2213 string const space =
2214 (p.hasOpt() ? p.getOpt() : string());
2215 string argument = p.getArg('{', '}');
2216 // check the case that a standard color is used
2217 if (space.empty() && is_known(argument, known_basic_colors)) {
2218 h_fontcolor = rgbcolor2code(argument);
2219 registerAutomaticallyLoadedPackage("color");
2220 } else if (space.empty() && argument == "document_fontcolor")
2221 registerAutomaticallyLoadedPackage("color");
2222 // check the case that LyX's document_fontcolor is defined
2223 // but not used for \color
2225 h_preamble << t.asInput();
2227 h_preamble << space;
2228 h_preamble << '{' << argument << '}';
2229 // the color might already be set because \definecolor
2230 // is parsed before this
2236 if (t.cs() == "pagecolor") {
2237 string argument = p.getArg('{', '}');
2238 // check the case that a standard color is used
2239 if (is_known(argument, known_basic_colors)) {
2240 h_backgroundcolor = rgbcolor2code(argument);
2241 } else if (argument == "page_backgroundcolor")
2242 registerAutomaticallyLoadedPackage("color");
2243 // check the case that LyX's page_backgroundcolor is defined
2244 // but not used for \pagecolor
2246 h_preamble << t.asInput() << '{' << argument << '}';
2247 // the color might already be set because \definecolor
2248 // is parsed before this
2249 h_backgroundcolor = "";
2254 if (t.cs() == "makeatletter") {
2255 // LyX takes care of this
2256 p.setCatcode('@', catLetter);
2260 if (t.cs() == "makeatother") {
2261 // LyX takes care of this
2262 p.setCatcode('@', catOther);
2266 if (t.cs() == "makeindex") {
2267 // LyX will re-add this if a print index command is found
2272 if (t.cs() == "newindex") {
2273 string const indexname = p.getArg('[', ']');
2274 string const shortcut = p.verbatim_item();
2275 if (!indexname.empty())
2276 h_index[index_number] = indexname;
2278 h_index[index_number] = shortcut;
2279 h_shortcut[index_number] = shortcut;
2285 if (t.cs() == "addbibresource") {
2286 string const options = p.getArg('[', ']');
2287 string const arg = removeExtension(p.getArg('{', '}'));
2288 if (!options.empty()) {
2289 // check if the option contains a bibencoding, if yes, extract it
2290 string::size_type pos = options.find("bibencoding=");
2292 if (pos != string::npos) {
2293 string::size_type i = options.find(',', pos);
2294 if (i == string::npos)
2295 encoding = options.substr(pos + 1);
2297 encoding = options.substr(pos, i - pos);
2298 pos = encoding.find('=');
2299 if (pos == string::npos)
2302 encoding = encoding.substr(pos + 1);
2304 if (!encoding.empty())
2305 biblatex_encodings.push_back(normalize_filename(arg) + ' ' + encoding);
2307 biblatex_bibliographies.push_back(arg);
2311 if (t.cs() == "bibliography") {
2312 vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
2313 biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
2317 if (t.cs() == "RS@ifundefined") {
2318 string const name = p.verbatim_item();
2319 string const body1 = p.verbatim_item();
2320 string const body2 = p.verbatim_item();
2321 // only non-lyxspecific stuff
2322 if (in_lyx_preamble &&
2323 (name == "subsecref" || name == "thmref" || name == "lemref"))
2327 ss << '\\' << t.cs();
2328 ss << '{' << name << '}'
2329 << '{' << body1 << '}'
2330 << '{' << body2 << '}';
2331 h_preamble << ss.str();
2336 if (t.cs() == "AtBeginDocument") {
2337 string const name = p.verbatim_item();
2338 // only non-lyxspecific stuff
2339 if (in_lyx_preamble &&
2340 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
2341 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
2342 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
2343 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
2344 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
2345 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
2346 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
2347 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
2348 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
2349 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
2350 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
2351 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
2352 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
2353 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
2354 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
2358 ss << '\\' << t.cs();
2359 ss << '{' << name << '}';
2360 h_preamble << ss.str();
2365 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
2366 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
2367 || t.cs() == "providecommand" || t.cs() == "providecommandx"
2368 || t.cs() == "DeclareRobustCommand"
2369 || t.cs() == "DeclareRobustCommandx"
2370 || t.cs() == "ProvideTextCommandDefault"
2371 || t.cs() == "DeclareMathAccent") {
2373 if (p.next_token().character() == '*') {
2377 string const name = p.verbatim_item();
2378 string const opt1 = p.getFullOpt();
2379 string const opt2 = p.getFullOpt();
2380 string const body = p.verbatim_item();
2381 // store the in_lyx_preamble setting
2382 bool const was_in_lyx_preamble = in_lyx_preamble;
2384 if (name == "\\rmdefault")
2385 if (is_known(body, known_roman_font_packages)) {
2386 h_font_roman[0] = body;
2388 in_lyx_preamble = true;
2390 if (name == "\\sfdefault")
2391 if (is_known(body, known_sans_font_packages)) {
2392 h_font_sans[0] = body;
2394 in_lyx_preamble = true;
2396 if (name == "\\ttdefault")
2397 if (is_known(body, known_typewriter_font_packages)) {
2398 h_font_typewriter[0] = body;
2400 in_lyx_preamble = true;
2402 if (name == "\\familydefault") {
2403 string family = body;
2404 // remove leading "\"
2405 h_font_default_family = family.erase(0,1);
2407 in_lyx_preamble = true;
2410 // remove LyX-specific definitions that are re-added by LyX
2412 // \lyxline is an ancient command that is converted by tex2lyx into
2413 // a \rule therefore remove its preamble code
2414 if (name == "\\lyxdot" || name == "\\lyxarrow"
2415 || name == "\\lyxline" || name == "\\LyX") {
2417 in_lyx_preamble = true;
2420 // Add the command to the known commands
2421 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
2423 // only non-lyxspecific stuff
2424 if (!in_lyx_preamble) {
2426 ss << '\\' << t.cs();
2429 ss << '{' << name << '}' << opt1 << opt2
2430 << '{' << body << "}";
2431 if (prefixIs(t.cs(), "renew") || !contains(h_preamble.str(), ss.str()))
2432 h_preamble << ss.str();
2434 ostream & out = in_preamble ? h_preamble : os;
2435 out << "\\" << t.cs() << "{" << name << "}"
2436 << opts << "{" << body << "}";
2439 // restore the in_lyx_preamble setting
2440 in_lyx_preamble = was_in_lyx_preamble;
2444 if (t.cs() == "documentclass") {
2445 vector<string>::iterator it;
2446 vector<string> opts = split_options(p.getArg('[', ']'));
2447 handle_opt(opts, known_fontsizes, h_paperfontsize);
2448 delete_opt(opts, known_fontsizes);
2449 // delete "pt" at the end
2450 string::size_type i = h_paperfontsize.find("pt");
2451 if (i != string::npos)
2452 h_paperfontsize.erase(i);
2453 // The documentclass options are always parsed before the options
2454 // of the babel call so that a language cannot overwrite the babel
2456 handle_opt(opts, known_languages, h_language);
2457 delete_opt(opts, known_languages);
2460 if ((it = find(opts.begin(), opts.end(), "fleqn"))
2462 h_is_mathindent = "1";
2465 // formula numbering side
2466 if ((it = find(opts.begin(), opts.end(), "leqno"))
2468 h_math_numbering_side = "left";
2471 else if ((it = find(opts.begin(), opts.end(), "reqno"))
2473 h_math_numbering_side = "right";
2477 // paper orientation
2478 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
2479 h_paperorientation = "landscape";
2483 if ((it = find(opts.begin(), opts.end(), "oneside"))
2488 if ((it = find(opts.begin(), opts.end(), "twoside"))
2494 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
2496 h_papercolumns = "1";
2499 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
2501 h_papercolumns = "2";
2505 // some size options are known to any document classes, other sizes
2506 // are handled by the \geometry command of the geometry package
2507 handle_opt(opts, known_class_paper_sizes, h_papersize);
2508 delete_opt(opts, known_class_paper_sizes);
2509 // the remaining options
2510 h_options = join(opts, ",");
2511 // FIXME This does not work for classes that have a
2512 // different name in LyX than in LaTeX
2513 h_textclass = p.getArg('{', '}');
2518 if (t.cs() == "usepackage") {
2519 string const options = p.getArg('[', ']');
2520 string const name = p.getArg('{', '}');
2521 vector<string> vecnames;
2522 split(name, vecnames, ',');
2523 vector<string>::const_iterator it = vecnames.begin();
2524 vector<string>::const_iterator end = vecnames.end();
2525 for (; it != end; ++it)
2526 handle_package(p, trimSpaceAndEol(*it), options,
2527 in_lyx_preamble, detectEncoding);
2531 if (t.cs() == "inputencoding") {
2532 string const encoding = p.getArg('{','}');
2533 Encoding const * const enc = encodings.fromLaTeXName(
2534 encoding, Encoding::inputenc, true);
2536 if (!detectEncoding)
2537 cerr << "Unknown encoding " << encoding
2538 << ". Ignoring." << std::endl;
2541 h_inputencoding = enc->name();
2542 p.setEncoding(enc->iconvName());
2547 if (t.cs() == "newenvironment") {
2548 string const name = p.getArg('{', '}');
2549 string const opt1 = p.getFullOpt();
2550 string const opt2 = p.getFullOpt();
2551 string const beg = p.verbatim_item();
2552 string const end = p.verbatim_item();
2553 if (!in_lyx_preamble) {
2554 h_preamble << "\\newenvironment{" << name
2555 << '}' << opt1 << opt2 << '{'
2556 << beg << "}{" << end << '}';
2558 add_known_environment(name, opt1, !opt2.empty(),
2559 from_utf8(beg), from_utf8(end));
2563 if (t.cs() == "newtheorem") {
2565 if (p.next_token().character() == '*') {
2569 string const name = p.getArg('{', '}');
2570 string const opt1 = p.getFullOpt();
2571 string const opt2 = p.getFullOpt();
2572 string const body = p.verbatim_item();
2573 string const opt3 = p.getFullOpt();
2574 string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
2576 string const complete = cmd + "{" + name + '}' +
2577 opt1 + opt2 + '{' + body + '}' + opt3;
2579 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
2581 if (!in_lyx_preamble)
2582 h_preamble << complete;
2586 if (t.cs() == "def") {
2587 string name = p.get_token().cs();
2588 // In fact, name may be more than the name:
2589 // In the test case of bug 8116
2590 // name == "csname SF@gobble@opt \endcsname".
2591 // Therefore, we need to use asInput() instead of cs().
2592 while (p.next_token().cat() != catBegin)
2593 name += p.get_token().asInput();
2594 if (!in_lyx_preamble)
2595 h_preamble << "\\def\\" << name << '{'
2596 << p.verbatim_item() << "}";
2600 if (t.cs() == "newcolumntype") {
2601 string const name = p.getArg('{', '}');
2602 trimSpaceAndEol(name);
2604 string opts = p.getOpt();
2605 if (!opts.empty()) {
2606 istringstream is(string(opts, 1));
2609 special_columns_[name[0]] = nargs;
2610 h_preamble << "\\newcolumntype{" << name << "}";
2612 h_preamble << "[" << nargs << "]";
2613 h_preamble << "{" << p.verbatim_item() << "}";
2617 if (t.cs() == "setcounter") {
2618 string const name = p.getArg('{', '}');
2619 string const content = p.getArg('{', '}');
2620 if (name == "secnumdepth")
2621 h_secnumdepth = content;
2622 else if (name == "tocdepth")
2623 h_tocdepth = content;
2625 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
2629 if (t.cs() == "setlength") {
2630 string const name = p.verbatim_item();
2631 string const content = p.verbatim_item();
2632 // the paragraphs are only not indented when \parindent is set to zero
2633 if (name == "\\parindent" && content != "") {
2634 if (content[0] == '0')
2635 h_paragraph_separation = "skip";
2637 h_paragraph_indentation = translate_len(content);
2638 } else if (name == "\\parskip") {
2639 if (content == "\\smallskipamount")
2640 h_defskip = "smallskip";
2641 else if (content == "\\medskipamount")
2642 h_defskip = "medskip";
2643 else if (content == "\\bigskipamount")
2644 h_defskip = "bigskip";
2646 h_defskip = translate_len(content);
2647 } else if (name == "\\mathindent") {
2648 h_mathindentation = translate_len(content);
2650 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2654 if (t.cs() == "onehalfspacing") {
2655 h_spacing = "onehalf";
2659 if (t.cs() == "doublespacing") {
2660 h_spacing = "double";
2664 if (t.cs() == "setstretch") {
2665 h_spacing = "other " + p.verbatim_item();
2669 if (t.cs() == "synctex") {
2670 // the scheme is \synctex=value
2671 // where value can only be "1" or "-1"
2672 h_output_sync = "1";
2673 // there can be any character behind the value (e.g. a linebreak or a '\'
2674 // therefore we extract it char by char
2676 string value = p.get_token().asInput();
2678 value += p.get_token().asInput();
2679 h_output_sync_macro = "\\synctex=" + value;
2683 if (t.cs() == "begin") {
2684 string const name = p.getArg('{', '}');
2685 if (name == "document")
2687 h_preamble << "\\begin{" << name << "}";
2691 if (t.cs() == "geometry") {
2692 vector<string> opts = split_options(p.getArg('{', '}'));
2693 handle_geometry(opts);
2697 if (t.cs() == "definecolor") {
2698 string const color = p.getArg('{', '}');
2699 string const space = p.getArg('{', '}');
2700 string const value = p.getArg('{', '}');
2701 if (color == "document_fontcolor" && space == "rgb") {
2702 RGBColor c(RGBColorFromLaTeX(value));
2703 h_fontcolor = X11hexname(c);
2704 } else if (color == "note_fontcolor" && space == "rgb") {
2705 RGBColor c(RGBColorFromLaTeX(value));
2706 h_notefontcolor = X11hexname(c);
2707 } else if (color == "page_backgroundcolor" && space == "rgb") {
2708 RGBColor c(RGBColorFromLaTeX(value));
2709 h_backgroundcolor = X11hexname(c);
2710 } else if (color == "shadecolor" && space == "rgb") {
2711 RGBColor c(RGBColorFromLaTeX(value));
2712 h_boxbgcolor = X11hexname(c);
2714 h_preamble << "\\definecolor{" << color
2715 << "}{" << space << "}{" << value
2721 if (t.cs() == "bibliographystyle") {
2722 h_biblio_style = p.verbatim_item();
2726 if (t.cs() == "jurabibsetup") {
2727 // FIXME p.getArg('{', '}') is most probably wrong (it
2728 // does not handle nested braces).
2729 // Use p.verbatim_item() instead.
2730 vector<string> jurabibsetup =
2731 split_options(p.getArg('{', '}'));
2732 // add jurabibsetup to the jurabib package options
2733 add_package("jurabib", jurabibsetup);
2734 if (!jurabibsetup.empty()) {
2735 h_preamble << "\\jurabibsetup{"
2736 << join(jurabibsetup, ",") << '}';
2741 if (t.cs() == "hypersetup") {
2742 vector<string> hypersetup =
2743 split_options(p.verbatim_item());
2744 // add hypersetup to the hyperref package options
2745 handle_hyperref(hypersetup);
2746 if (!hypersetup.empty()) {
2747 h_preamble << "\\hypersetup{"
2748 << join(hypersetup, ",") << '}';
2753 if (t.cs() == "includeonly") {
2754 vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
2755 for (auto & iofile : includeonlys) {
2756 string filename(normalize_filename(iofile));
2757 string const path = getMasterFilePath(true);
2758 // We want to preserve relative/absolute filenames,
2759 // therefore path is only used for testing
2760 if (!makeAbsPath(filename, path).exists()) {
2761 // The file extension is probably missing.
2762 // Now try to find it out.
2763 string const tex_name =
2764 find_file(filename, path,
2765 known_tex_extensions);
2766 if (!tex_name.empty())
2767 filename = tex_name;
2770 if (makeAbsPath(filename, path).exists())
2771 fix_child_filename(filename);
2773 cerr << "Warning: Could not find included file '"
2774 << filename << "'." << endl;
2775 outname = changeExtension(filename, "lyx");
2776 h_includeonlys.push_back(outname);
2781 if (is_known(t.cs(), known_if_3arg_commands)) {
2782 // prevent misparsing of \usepackage if it is used
2783 // as an argument (see e.g. our own output of
2784 // \@ifundefined above)
2785 string const arg1 = p.verbatim_item();
2786 string const arg2 = p.verbatim_item();
2787 string const arg3 = p.verbatim_item();
2788 // test case \@ifundefined{date}{}{\date{}}
2789 if (t.cs() == "@ifundefined" && arg1 == "date" &&
2790 arg2.empty() && arg3 == "\\date{}") {
2791 h_suppress_date = "true";
2792 // older tex2lyx versions did output
2793 // \@ifundefined{definecolor}{\usepackage{color}}{}
2794 } else if (t.cs() == "@ifundefined" &&
2795 arg1 == "definecolor" &&
2796 arg2 == "\\usepackage{color}" &&
2798 if (!in_lyx_preamble)
2799 h_preamble << package_beg_sep
2802 << "\\@ifundefined{definecolor}{color}{}"
2805 //\@ifundefined{showcaptionsetup}{}{%
2806 // \PassOptionsToPackage{caption=false}{subfig}}
2807 // that LyX uses for subfloats
2808 } else if (t.cs() == "@ifundefined" &&
2809 arg1 == "showcaptionsetup" && arg2.empty()
2810 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
2812 } else if (!in_lyx_preamble) {
2813 h_preamble << t.asInput()
2814 << '{' << arg1 << '}'
2815 << '{' << arg2 << '}'
2816 << '{' << arg3 << '}';
2821 if (is_known(t.cs(), known_if_commands)) {
2822 // must not parse anything in conditional code, since
2823 // LyX would output the parsed contents unconditionally
2824 if (!in_lyx_preamble)
2825 h_preamble << t.asInput();
2826 handle_if(p, in_lyx_preamble);
2830 if (!t.cs().empty() && !in_lyx_preamble) {
2831 h_preamble << '\\' << t.cs();
2836 // remove the whitespace
2839 // Force textclass if the user wanted it
2840 if (!forceclass.empty())
2841 h_textclass = forceclass;
2842 tc.setName(h_textclass);
2843 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2844 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2847 if (h_papersides.empty()) {
2850 h_papersides = ss.str();
2853 // If the CJK package is used we cannot set the document language from
2854 // the babel options. Instead, we guess which language is used most
2855 // and set this one.
2856 default_language = h_language;
2857 if (is_full_document &&
2858 (auto_packages.find("CJK") != auto_packages.end() ||
2859 auto_packages.find("CJKutf8") != auto_packages.end())) {
2861 h_language = guessLanguage(p, default_language);
2863 if (explicit_babel && h_language != default_language) {
2864 // We set the document language to a CJK language,
2865 // but babel is explicitly called in the user preamble
2866 // without options. LyX will not add the default
2867 // language to the document options if it is either
2868 // english, or no text is set as default language.
2869 // Therefore we need to add a language option explicitly.
2870 // FIXME: It would be better to remove all babel calls
2871 // from the user preamble, but this is difficult
2872 // without re-introducing bug 7861.
2873 if (h_options.empty())
2874 h_options = lyx2babel(default_language);
2876 h_options += ',' + lyx2babel(default_language);
2880 // Finally, set the quote style.
2881 // LyX knows the following quotes styles:
2882 // british, cjk, cjkangle, danish, english, french, german,
2883 // polish, russian, swedish and swiss
2884 // conversion list taken from
2885 // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
2886 // (quotes for kazakh are unknown)
2888 if (is_known(h_language, known_british_quotes_languages))
2889 h_quotes_style = "british";
2891 else if (is_known(h_language, known_cjk_quotes_languages))
2892 h_quotes_style = "cjk";
2894 else if (is_known(h_language, known_cjkangle_quotes_languages))
2895 h_quotes_style = "cjkangle";
2897 else if (is_known(h_language, known_danish_quotes_languages))
2898 h_quotes_style = "danish";
2900 else if (is_known(h_language, known_french_quotes_languages))
2901 h_quotes_style = "french";
2903 else if (is_known(h_language, known_german_quotes_languages))
2904 h_quotes_style = "german";
2906 else if (is_known(h_language, known_polish_quotes_languages))
2907 h_quotes_style = "polish";
2909 else if (is_known(h_language, known_russian_quotes_languages))
2910 h_quotes_style = "russian";
2912 else if (is_known(h_language, known_swedish_quotes_languages))
2913 h_quotes_style = "swedish";
2915 else if (is_known(h_language, known_swiss_quotes_languages))
2916 h_quotes_style = "swiss";
2918 else if (is_known(h_language, known_english_quotes_languages))
2919 h_quotes_style = "english";
2923 string Preamble::parseEncoding(Parser & p, string const & forceclass)
2925 TeX2LyXDocClass dummy;
2926 parse(p, forceclass, true, dummy);
2927 if (h_inputencoding != "auto-legacy" && h_inputencoding != "auto-legacy-plain")
2928 return h_inputencoding;
2933 string babel2lyx(string const & language)
2935 char const * const * where = is_known(language, known_languages);
2937 return known_coded_languages[where - known_languages];
2942 string lyx2babel(string const & language)
2944 char const * const * where = is_known(language, known_coded_languages);
2946 return known_languages[where - known_coded_languages];
2951 string Preamble::polyglossia2lyx(string const & language)
2953 char const * const * where = is_known(language, polyglossia_languages);
2955 return coded_polyglossia_languages[where - polyglossia_languages];
2960 string rgbcolor2code(string const & name)
2962 char const * const * where = is_known(name, known_basic_colors);
2964 // "red", "green" etc
2965 return known_basic_color_codes[where - known_basic_colors];
2967 // "255,0,0", "0,255,0" etc
2968 RGBColor c(RGBColorFromLaTeX(name));
2969 return X11hexname(c);