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 string::size_type pos = scale.find('=');
482 if (pos != string::npos) {
483 string value = scale.substr(pos + 1);
484 if (isStrDbl(value)) {
485 percentage = convert<string>(
486 static_cast<int>(100 * convert<double>(value)));
494 string remove_braces(string const & value)
498 if (value[0] == '{' && value[value.length()-1] == '}')
499 return value.substr(1, value.length()-2);
503 } // anonymous namespace
506 Preamble::Preamble() : one_language(true), explicit_babel(false),
507 title_layout_found(false), index_number(0), h_font_cjk_set(false)
511 h_biblio_style = "plain";
512 h_bibtex_command = "default";
513 h_cite_engine = "basic";
514 h_cite_engine_type = "default";
516 h_defskip = "medskip";
517 h_dynamic_quotes = false;
520 h_fontencoding = "default";
521 h_font_roman[0] = "default";
522 h_font_roman[1] = "default";
523 h_font_sans[0] = "default";
524 h_font_sans[1] = "default";
525 h_font_typewriter[0] = "default";
526 h_font_typewriter[1] = "default";
527 h_font_math[0] = "auto";
528 h_font_math[1] = "auto";
529 h_font_default_family = "default";
530 h_use_non_tex_fonts = false;
532 h_font_roman_osf = "false";
533 h_font_sans_osf = "false";
534 h_font_typewriter_osf = "false";
535 h_font_sf_scale[0] = "100";
536 h_font_sf_scale[1] = "100";
537 h_font_tt_scale[0] = "100";
538 h_font_tt_scale[1] = "100";
539 // h_font_roman_opts;
541 // h_font_typewriter_opts;
543 h_is_mathindent = "0";
544 h_math_numbering_side = "default";
545 h_graphics = "default";
546 h_default_output_format = "default";
547 h_html_be_strict = "false";
548 h_html_css_as_file = "0";
549 h_html_math_output = "0";
550 h_index[0] = "Index";
551 h_index_command = "default";
552 h_inputencoding = "auto-legacy";
553 h_justification = "true";
554 h_language = "english";
555 h_language_package = "none";
557 h_maintain_unincluded_children = "false";
561 h_output_changes = "false";
563 //h_output_sync_macro
564 h_papercolumns = "1";
565 h_paperfontsize = "default";
566 h_paperorientation = "portrait";
567 h_paperpagestyle = "default";
569 h_papersize = "default";
570 h_paragraph_indentation = "default";
571 h_paragraph_separation = "indent";
576 h_pdf_bookmarks = "0";
577 h_pdf_bookmarksnumbered = "0";
578 h_pdf_bookmarksopen = "0";
579 h_pdf_bookmarksopenlevel = "1";
580 h_pdf_breaklinks = "0";
581 h_pdf_pdfborder = "0";
582 h_pdf_colorlinks = "0";
583 h_pdf_backref = "section";
584 h_pdf_pdfusetitle = "0";
586 //h_pdf_quoted_options;
587 h_quotes_style = "english";
589 h_shortcut[0] = "idx";
590 h_spacing = "single";
591 h_save_transient_properties = "true";
592 h_suppress_date = "false";
593 h_textclass = "article";
595 h_tracking_changes = "false";
596 h_use_bibtopic = "false";
597 h_use_dash_ligatures = "true";
598 h_use_indices = "false";
599 h_use_geometry = "false";
600 h_use_default_options = "false";
601 h_use_hyperref = "false";
602 h_use_microtype = "false";
603 h_use_lineno = "false";
604 h_use_refstyle = false;
605 h_use_minted = false;
606 h_use_packages["amsmath"] = "1";
607 h_use_packages["amssymb"] = "0";
608 h_use_packages["cancel"] = "0";
609 h_use_packages["esint"] = "1";
610 h_use_packages["mhchem"] = "0";
611 h_use_packages["mathdots"] = "0";
612 h_use_packages["mathtools"] = "0";
613 h_use_packages["stackrel"] = "0";
614 h_use_packages["stmaryrd"] = "0";
615 h_use_packages["undertilde"] = "0";
619 void Preamble::handle_hyperref(vector<string> & options)
621 // FIXME swallow inputencoding changes that might surround the
622 // hyperref setup if it was written by LyX
623 h_use_hyperref = "true";
624 // swallow "unicode=true", since LyX does always write that
625 vector<string>::iterator it =
626 find(options.begin(), options.end(), "unicode=true");
627 if (it != options.end())
629 it = find(options.begin(), options.end(), "pdfusetitle");
630 if (it != options.end()) {
631 h_pdf_pdfusetitle = "1";
634 string bookmarks = process_keyval_opt(options, "bookmarks");
635 if (bookmarks == "true")
636 h_pdf_bookmarks = "1";
637 else if (bookmarks == "false")
638 h_pdf_bookmarks = "0";
639 if (h_pdf_bookmarks == "1") {
640 string bookmarksnumbered =
641 process_keyval_opt(options, "bookmarksnumbered");
642 if (bookmarksnumbered == "true")
643 h_pdf_bookmarksnumbered = "1";
644 else if (bookmarksnumbered == "false")
645 h_pdf_bookmarksnumbered = "0";
646 string bookmarksopen =
647 process_keyval_opt(options, "bookmarksopen");
648 if (bookmarksopen == "true")
649 h_pdf_bookmarksopen = "1";
650 else if (bookmarksopen == "false")
651 h_pdf_bookmarksopen = "0";
652 if (h_pdf_bookmarksopen == "1") {
653 string bookmarksopenlevel =
654 process_keyval_opt(options, "bookmarksopenlevel");
655 if (!bookmarksopenlevel.empty())
656 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
659 string breaklinks = process_keyval_opt(options, "breaklinks");
660 if (breaklinks == "true")
661 h_pdf_breaklinks = "1";
662 else if (breaklinks == "false")
663 h_pdf_breaklinks = "0";
664 string pdfborder = process_keyval_opt(options, "pdfborder");
665 if (pdfborder == "{0 0 0}")
666 h_pdf_pdfborder = "1";
667 else if (pdfborder == "{0 0 1}")
668 h_pdf_pdfborder = "0";
669 string backref = process_keyval_opt(options, "backref");
670 if (!backref.empty())
671 h_pdf_backref = backref;
672 string colorlinks = process_keyval_opt(options, "colorlinks");
673 if (colorlinks == "true")
674 h_pdf_colorlinks = "1";
675 else if (colorlinks == "false")
676 h_pdf_colorlinks = "0";
677 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
678 if (!pdfpagemode.empty())
679 h_pdf_pagemode = pdfpagemode;
680 string pdftitle = process_keyval_opt(options, "pdftitle");
681 if (!pdftitle.empty()) {
682 h_pdf_title = remove_braces(pdftitle);
684 string pdfauthor = process_keyval_opt(options, "pdfauthor");
685 if (!pdfauthor.empty()) {
686 h_pdf_author = remove_braces(pdfauthor);
688 string pdfsubject = process_keyval_opt(options, "pdfsubject");
689 if (!pdfsubject.empty())
690 h_pdf_subject = remove_braces(pdfsubject);
691 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
692 if (!pdfkeywords.empty())
693 h_pdf_keywords = remove_braces(pdfkeywords);
694 if (!options.empty()) {
695 if (!h_pdf_quoted_options.empty())
696 h_pdf_quoted_options += ',';
697 h_pdf_quoted_options += join(options, ",");
703 void Preamble::handle_geometry(vector<string> & options)
705 h_use_geometry = "true";
706 vector<string>::iterator it;
708 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
709 h_paperorientation = "landscape";
713 // keyval version: "paper=letter"
714 string paper = process_keyval_opt(options, "paper");
716 h_papersize = paper + "paper";
717 // alternative version: "letterpaper"
718 handle_opt(options, known_paper_sizes, h_papersize);
719 delete_opt(options, known_paper_sizes);
721 char const * const * margin = known_paper_margins;
722 for (; *margin; ++margin) {
723 string value = process_keyval_opt(options, *margin);
724 if (!value.empty()) {
725 int k = margin - known_paper_margins;
726 string name = known_coded_paper_margins[k];
727 h_margins += '\\' + name + ' ' + value + '\n';
733 void Preamble::handle_package(Parser &p, string const & name,
734 string const & opts, bool in_lyx_preamble,
737 vector<string> options = split_options(opts);
738 add_package(name, options);
740 if (is_known(name, known_xetex_packages)) {
742 h_use_non_tex_fonts = true;
743 registerAutomaticallyLoadedPackage("fontspec");
744 if (h_inputencoding == "auto-legacy")
745 p.setEncoding("UTF-8");
749 if (is_known(name, known_roman_font_packages))
750 h_font_roman[0] = name;
752 vector<string> allopts = getVectorFromString(opts);
755 if (name == "ccfonts") {
756 for (auto const & opt : allopts) {
762 h_font_roman_opts = xopts;
766 if (name == "lmodern") {
767 for (auto const & opt : allopts) {
773 h_font_roman_opts = xopts;
777 if (name == "fourier") {
778 h_font_roman[0] = "utopia";
779 // when font uses real small capitals
780 for (auto const & opt : allopts) {
782 h_font_roman_osf = "true";
785 if (opt == "expert") {
794 h_font_roman_opts = xopts;
798 if (name == "garamondx") {
799 h_font_roman[0] = "garamondx";
800 for (auto const & opt : allopts) {
802 h_font_roman_osf = "true";
810 h_font_roman_opts = xopts;
814 if (name == "libertine") {
815 h_font_roman[0] = "libertine";
816 // this automatically invokes biolinum
817 h_font_sans[0] = "biolinum";
818 // as well as libertineMono
819 h_font_typewriter[0] = "libertine-mono";
820 for (auto const & opt : allopts) {
822 h_font_roman_osf = "true";
825 if (opt == "lining") {
826 h_font_roman_osf = "false";
834 h_font_roman_opts = xopts;
838 if (name == "libertineRoman" || name == "libertine-type1") {
839 h_font_roman[0] = "libertine";
840 // NOTE: contrary to libertine.sty, libertineRoman
841 // and libertine-type1 do not automatically invoke
842 // biolinum and libertineMono
843 if (opts == "lining")
844 h_font_roman_osf = "false";
845 else if (opts == "osf")
846 h_font_roman_osf = "true";
849 if (name == "MinionPro") {
850 h_font_roman[0] = "minionpro";
851 h_font_roman_osf = "true";
852 h_font_math[0] = "auto";
853 for (auto const & opt : allopts) {
855 h_font_roman_osf = "false";
858 if (opt == "onlytext") {
859 h_font_math[0] = "default";
867 h_font_roman_opts = xopts;
871 if (name == "mathdesign") {
872 if (opts.find("charter") != string::npos)
873 h_font_roman[0] = "md-charter";
874 if (opts.find("garamond") != string::npos)
875 h_font_roman[0] = "md-garamond";
876 if (opts.find("utopia") != string::npos)
877 h_font_roman[0] = "md-utopia";
878 if (opts.find("expert") != string::npos) {
880 h_font_roman_osf = "true";
884 else if (name == "mathpazo") {
885 h_font_roman[0] = "palatino";
886 for (auto const & opt : allopts) {
888 h_font_roman_osf = "true";
900 h_font_roman_opts = xopts;
904 else if (name == "mathptmx") {
905 h_font_roman[0] = "times";
906 for (auto const & opt : allopts) {
912 h_font_roman_opts = xopts;
916 if (name == "crimson")
917 h_font_roman[0] = "cochineal";
919 if (name == "cochineal") {
920 h_font_roman[0] = "cochineal";
921 // cochineal can have several options, e.g. [proportional,osf]
922 for (auto const & opt : allopts) {
924 h_font_roman_osf = "true";
927 if (opt == "proportional")
934 h_font_roman_opts = xopts;
938 if (name == "noto") {
939 // noto can have several options
941 h_font_roman[0] = "NotoSerif-TLF";
942 string::size_type pos = opts.find("rm");
943 if (pos != string::npos)
944 h_font_roman[0] = "NotoSerif-TLF";
945 pos = opts.find("sf");
946 if (pos != string::npos)
947 h_font_sans[0] = "NotoSans-TLF";
948 pos = opts.find("nott");
949 if (pos != string::npos) {
950 h_font_roman[0] = "NotoSerif-TLF";
951 h_font_sans[0] = "NotoSans-TLF";
953 // noto as typewriter is handled in handling of \ttdefault
954 // special cases are handled in handling of \rmdefault and \sfdefault
955 for (auto const & opt : allopts) {
963 h_font_roman_osf = "true";
972 if (name == "paratype") {
973 // in this case all fonts are ParaType
974 h_font_roman[0] = "PTSerif-TLF";
975 h_font_sans[0] = "default";
976 h_font_typewriter[0] = "default";
979 if (name == "PTSerif")
980 h_font_roman[0] = "PTSerif-TLF";
982 if (name == "XCharter") {
983 h_font_roman[0] = "xcharter";
984 for (auto const & opt : allopts) {
986 h_font_roman_osf = "true";
994 h_font_roman_opts = xopts;
998 if (name == "plex-serif") {
1000 h_font_roman[0] = "IBMPlexSerif";
1001 else if (opts.find("thin") != string::npos)
1002 h_font_roman[0] = "IBMPlexSerifThin";
1003 else if (opts.find("extralight") != string::npos)
1004 h_font_roman[0] = "IBMPlexSerifExtraLight";
1005 else if (opts.find("light") != string::npos)
1006 h_font_roman[0] = "IBMPlexSerifLight";
1007 else if (opts.find("semibold") != string::npos)
1008 h_font_roman[0] = "IBMPlexSerifSemibold";
1009 for (auto const & opt : allopts) {
1012 if (opt == "extralight")
1016 if (opt == "semibold")
1023 h_font_roman_opts = xopts;
1026 if (name == "noto-serif") {
1027 h_font_roman[0] = "NotoSerifRegular";
1028 if (!opts.empty()) {
1029 if (opts.find("thin") != string::npos)
1030 h_font_roman[0] = "NotoSerifThin";
1031 else if (opts.find("medium") != string::npos)
1032 h_font_roman[0] = "NotoSerifMedium";
1033 else if (opts.find("extralight") != string::npos)
1034 h_font_roman[0] = "NotoSerifExtralight";
1035 else if (opts.find("light") != string::npos)
1036 h_font_roman[0] = "NotoSerifLight";
1038 for (auto const & opt : allopts) {
1039 if (opt == "regular")
1043 if (opt == "extralight")
1047 if (opt == "semibold")
1054 h_font_roman_opts = xopts;
1058 if (name == "sourceserifpro") {
1059 h_font_roman[0] = "ADOBESourceSerifPro";
1060 for (auto const & opt : allopts) {
1062 h_font_roman_osf = "true";
1070 h_font_roman_opts = xopts;
1075 if (is_known(name, known_sans_font_packages)) {
1076 h_font_sans[0] = name;
1077 if (options.size() >= 1) {
1078 if (scale_as_percentage(opts, h_font_sf_scale[0]))
1083 if (name == "biolinum" || name == "biolinum-type1") {
1084 h_font_sans[0] = "biolinum";
1085 // biolinum can have several options, e.g. [osf,scaled=0.97]
1086 string::size_type pos = opts.find("osf");
1087 if (pos != string::npos)
1088 h_font_sans_osf = "true";
1091 if (name == "cantarell") {
1092 for (auto const & opt : allopts) {
1093 if (opt == "defaultsans")
1095 if (prefixIs(opt, "oldstyle")) {
1096 h_font_sans_osf = "true";
1099 if (prefixIs(opt, "scale=")) {
1100 scale_as_percentage(opt, h_font_sf_scale[0]);
1108 h_font_sans_opts = xopts;
1112 if (name == "PTSans") {
1113 h_font_sans[0] = "PTSans-TLF";
1114 if (options.size() >= 1) {
1115 if (scale_as_percentage(opts, h_font_sf_scale[0]))
1120 if (name == "FiraSans") {
1121 h_font_sans[0] = "FiraSans";
1122 h_font_sans_osf = "true";
1123 for (auto const & opt : allopts) {
1124 if (opt == "book") {
1125 h_font_sans[0] = "FiraSansBook";
1128 if (opt == "thin") {
1131 if (opt == "extralight") {
1132 h_font_sans[0] = "FiraSansExtralight";
1135 if (opt == "light") {
1136 h_font_sans[0] = "FiraSansLight";
1139 if (opt == "ultralight") {
1140 h_font_sans[0] = "FiraSansUltralight";
1143 if (opt == "thin") {
1144 h_font_sans[0] = "FiraSansThin";
1147 if (opt == "lf" || opt == "lining") {
1148 h_font_sans_osf = "false";
1151 if (prefixIs(opt, "scale=") || prefixIs(opt, "scaled=")) {
1152 scale_as_percentage(opt, h_font_sf_scale[0]);
1160 h_font_sans_opts = xopts;
1164 if (name == "plex-sans") {
1165 if (opts.find("condensed") != string::npos)
1166 h_font_sans[0] = "IBMPlexSansCondensed";
1167 else if (opts.find("thin") != string::npos)
1168 h_font_sans[0] = "IBMPlexSansThin";
1169 else if (opts.find("extralight") != string::npos)
1170 h_font_sans[0] = "IBMPlexSansExtraLight";
1171 else if (opts.find("light") != string::npos)
1172 h_font_sans[0] = "IBMPlexSansLight";
1173 else if (opts.find("semibold") != string::npos)
1174 h_font_sans[0] = "IBMPlexSansSemibold";
1176 h_font_sans[0] = "IBMPlexSans";
1177 for (auto const & opt : allopts) {
1180 if (opt == "extralight")
1184 if (opt == "semibold")
1186 if (prefixIs(opt, "scale=")) {
1187 scale_as_percentage(opt, h_font_sf_scale[0]);
1195 h_font_sans_opts = xopts;
1199 if (name == "noto-sans") {
1200 h_font_sans[0] = "NotoSansRegular";
1201 if (!opts.empty()) {
1202 if (opts.find("medium") != string::npos)
1203 h_font_sans[0] = "NotoSansMedium";
1204 else if (opts.find("thin") != string::npos)
1205 h_font_sans[0] = "NotoSansThin";
1206 else if (opts.find("extralight") != string::npos)
1207 h_font_sans[0] = "NotoSansExtralight";
1208 else if (opts.find("light") != string::npos)
1209 h_font_sans[0] = "NotoSansLight";
1211 for (auto const & opt : allopts) {
1212 if (opt == "regular")
1216 if (opt == "extralight")
1220 if (opt == "semibold")
1223 h_font_sans_osf = "true";
1231 h_font_sans_opts = xopts;
1235 if (name == "sourcesanspro") {
1236 h_font_sans[0] = "ADOBESourceSansPro";
1237 for (auto const & opt : allopts) {
1238 if (prefixIs(opt, "scaled=")) {
1239 scale_as_percentage(opt, h_font_sf_scale[0]);
1243 h_font_sans_osf = "true";
1251 h_font_sans_opts = xopts;
1256 if (is_known(name, known_typewriter_font_packages)) {
1257 // fourier can be set as roman font _only_
1258 // fourier as typewriter is handled in handling of \ttdefault
1259 if (name != "fourier") {
1260 h_font_typewriter[0] = name;
1261 if (options.size() >= 1) {
1262 if (scale_as_percentage(opts, h_font_tt_scale[0]))
1268 if (name == "libertineMono" || name == "libertineMono-type1")
1269 h_font_typewriter[0] = "libertine-mono";
1271 if (name == "FiraMono") {
1272 h_font_typewriter_osf = "true";
1273 for (auto const & opt : allopts) {
1274 if (opt == "lf" || opt == "lining") {
1275 h_font_typewriter_osf = "false";
1278 if (prefixIs(opt, "scale=") || prefixIs(opt, "scaled=")) {
1279 scale_as_percentage(opt, h_font_tt_scale[0]);
1287 h_font_typewriter_opts = xopts;
1291 if (name == "PTMono") {
1292 h_font_typewriter[0] = "PTMono-TLF";
1293 if (options.size() >= 1) {
1294 if (scale_as_percentage(opts, h_font_tt_scale[0]))
1299 if (name == "plex-mono") {
1300 if (opts.find("thin") != string::npos)
1301 h_font_typewriter[0] = "IBMPlexMonoThin";
1302 else if (opts.find("extralight") != string::npos)
1303 h_font_typewriter[0] = "IBMPlexMonoExtraLight";
1304 else if (opts.find("light") != string::npos)
1305 h_font_typewriter[0] = "IBMPlexMonoLight";
1306 else if (opts.find("semibold") != string::npos)
1307 h_font_typewriter[0] = "IBMPlexMonoSemibold";
1309 h_font_typewriter[0] = "IBMPlexMono";
1310 for (auto const & opt : allopts) {
1313 if (opt == "extralight")
1317 if (opt == "semibold")
1319 if (prefixIs(opt, "scale=")) {
1320 scale_as_percentage(opt, h_font_tt_scale[0]);
1328 h_font_typewriter_opts = xopts;
1332 if (name == "noto-mono") {
1333 h_font_typewriter[0] = "NotoMonoRegular";
1334 for (auto const & opt : allopts) {
1335 if (opt == "regular")
1342 h_font_typewriter_opts = xopts;
1346 if (name == "sourcecodepro") {
1347 h_font_typewriter[0] = "ADOBESourceCodePro";
1348 for (auto const & opt : allopts) {
1349 if (prefixIs(opt, "scaled=")) {
1350 scale_as_percentage(opt, h_font_tt_scale[0]);
1354 h_font_typewriter_osf = "true";
1362 h_font_typewriter_opts = xopts;
1366 // font uses old-style figure
1368 h_font_roman_osf = "true";
1371 if (is_known(name, known_math_font_packages))
1372 h_font_math[0] = name;
1374 if (name == "newtxmath") {
1376 h_font_math[0] = "newtxmath";
1377 else if (opts == "garamondx")
1378 h_font_math[0] = "garamondx-ntxm";
1379 else if (opts == "libertine")
1380 h_font_math[0] = "libertine-ntxm";
1381 else if (opts == "minion")
1382 h_font_math[0] = "minion-ntxm";
1383 else if (opts == "cochineal")
1384 h_font_math[0] = "cochineal-ntxm";
1387 if (name == "iwona")
1389 h_font_math[0] = "iwona-math";
1391 if (name == "kurier")
1393 h_font_math[0] = "kurier-math";
1395 // after the detection and handling of special cases, we can remove the
1396 // fonts, otherwise they would appear in the preamble, see bug #7856
1397 if (is_known(name, known_roman_font_packages) || is_known(name, known_sans_font_packages)
1398 || is_known(name, known_typewriter_font_packages) || is_known(name, known_math_font_packages))
1400 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
1401 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
1402 name == "esint" || name == "mhchem" || name == "mathdots" ||
1403 name == "mathtools" || name == "stackrel" ||
1404 name == "stmaryrd" || name == "undertilde") {
1405 h_use_packages[name] = "2";
1406 registerAutomaticallyLoadedPackage(name);
1409 else if (name == "babel") {
1410 h_language_package = "default";
1411 // One might think we would have to do nothing if babel is loaded
1412 // without any options to prevent pollution of the preamble with this
1413 // babel call in every roundtrip.
1414 // But the user could have defined babel-specific things afterwards. So
1415 // we need to keep it in the preamble to prevent cases like bug #7861.
1416 if (!opts.empty()) {
1417 // check if more than one option was used - used later for inputenc
1418 if (options.begin() != options.end() - 1)
1419 one_language = false;
1420 // babel takes the last language of the option of its \usepackage
1421 // call as document language. If there is no such language option, the
1422 // last language in the documentclass options is used.
1423 handle_opt(options, known_languages, h_language);
1424 // translate the babel name to a LyX name
1425 h_language = babel2lyx(h_language);
1426 if (h_language == "japanese") {
1427 // For Japanese, the encoding isn't indicated in the source
1428 // file, and there's really not much we can do. We could
1429 // 1) offer a list of possible encodings to choose from, or
1430 // 2) determine the encoding of the file by inspecting it.
1431 // For the time being, we leave the encoding alone so that
1432 // we don't get iconv errors when making a wrong guess, and
1433 // we will output a note at the top of the document
1434 // explaining what to do.
1435 Encoding const * const enc = encodings.fromIconvName(
1436 p.getEncoding(), Encoding::japanese, false);
1438 h_inputencoding = enc->name();
1439 is_nonCJKJapanese = true;
1440 // in this case babel can be removed from the preamble
1441 registerAutomaticallyLoadedPackage("babel");
1443 // If babel is called with options, LyX puts them by default into the
1444 // document class options. This works for most languages, except
1445 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
1446 // perhaps in future others.
1447 // Therefore keep the babel call as it is as the user might have
1449 string const babelcall = "\\usepackage[" + opts + "]{babel}\n";
1450 if (!contains(h_preamble.str(), babelcall))
1451 h_preamble << babelcall;
1453 delete_opt(options, known_languages);
1455 if (!contains(h_preamble.str(), "\\usepackage{babel}\n"))
1456 h_preamble << "\\usepackage{babel}\n";
1457 explicit_babel = true;
1461 else if (name == "polyglossia") {
1462 h_language_package = "default";
1463 h_default_output_format = "pdf4";
1464 h_use_non_tex_fonts = true;
1466 registerAutomaticallyLoadedPackage("xunicode");
1467 if (h_inputencoding == "auto-legacy")
1468 p.setEncoding("UTF-8");
1471 else if (name == "CJK") {
1472 // set the encoding to "auto-legacy" because it might be set to "auto-legacy-plain" by the babel handling
1473 // and this would not be correct for CJK
1474 if (h_inputencoding == "auto-legacy-plain")
1475 h_inputencoding = "auto-legacy";
1476 registerAutomaticallyLoadedPackage("CJK");
1479 else if (name == "CJKutf8") {
1480 h_inputencoding = "utf8-cjk";
1481 p.setEncoding("UTF-8");
1482 registerAutomaticallyLoadedPackage("CJKutf8");
1485 else if (name == "fontenc") {
1486 h_fontencoding = getStringFromVector(options, ",");
1490 else if (name == "inputenc" || name == "luainputenc") {
1491 // h_inputencoding is only set when there is not more than one
1492 // inputenc option because otherwise h_inputencoding must be
1493 // set to "auto-legacy" (the default encodings of the document's languages)
1494 // Therefore check that exactly one option is passed to inputenc.
1495 // It is also only set when there is not more than one babel
1497 if (!options.empty()) {
1498 string const encoding = options.back();
1499 Encoding const * const enc = encodings.fromLaTeXName(
1500 encoding, Encoding::inputenc, true);
1502 if (!detectEncoding)
1503 cerr << "Unknown encoding " << encoding
1504 << ". Ignoring." << std::endl;
1506 if (!enc->unsafe() && options.size() == 1 && one_language == true)
1507 h_inputencoding = enc->name();
1508 p.setEncoding(enc->iconvName());
1514 else if (name == "srcltx") {
1515 h_output_sync = "1";
1516 if (!opts.empty()) {
1517 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
1520 h_output_sync_macro = "\\usepackage{srcltx}";
1523 else if (is_known(name, known_old_language_packages)) {
1524 // known language packages from the times before babel
1525 // if they are found and not also babel, they will be used as
1526 // custom language package
1527 h_language_package = "\\usepackage{" + name + "}";
1530 else if (name == "lyxskak") {
1531 // ignore this and its options
1532 const char * const o[] = {"ps", "mover", 0};
1533 delete_opt(options, o);
1536 else if (is_known(name, known_lyx_packages) && options.empty()) {
1537 if (name == "splitidx")
1538 h_use_indices = "true";
1539 else if (name == "minted")
1540 h_use_minted = true;
1541 else if (name == "refstyle")
1542 h_use_refstyle = true;
1543 else if (name == "prettyref")
1544 h_use_refstyle = false;
1545 if (!in_lyx_preamble) {
1546 h_preamble << package_beg_sep << name
1547 << package_mid_sep << "\\usepackage{"
1549 if (p.next_token().cat() == catNewline ||
1550 (p.next_token().cat() == catSpace &&
1551 p.next_next_token().cat() == catNewline))
1553 h_preamble << package_end_sep;
1557 else if (name == "geometry")
1558 handle_geometry(options);
1560 else if (name == "subfig")
1561 ; // ignore this FIXME: Use the package separator mechanism instead
1563 else if (char const * const * where = is_known(name, known_languages))
1564 h_language = known_coded_languages[where - known_languages];
1566 else if (name == "natbib") {
1567 h_biblio_style = "plainnat";
1568 h_cite_engine = "natbib";
1569 h_cite_engine_type = "authoryear";
1570 vector<string>::iterator it =
1571 find(options.begin(), options.end(), "authoryear");
1572 if (it != options.end())
1575 it = find(options.begin(), options.end(), "numbers");
1576 if (it != options.end()) {
1577 h_cite_engine_type = "numerical";
1581 if (!options.empty())
1582 h_biblio_options = join(options, ",");
1585 else if (name == "biblatex") {
1586 h_biblio_style = "plainnat";
1587 h_cite_engine = "biblatex";
1588 h_cite_engine_type = "authoryear";
1590 vector<string>::iterator it =
1591 find(options.begin(), options.end(), "natbib");
1592 if (it != options.end()) {
1594 h_cite_engine = "biblatex-natbib";
1596 opt = process_keyval_opt(options, "natbib");
1598 h_cite_engine = "biblatex-natbib";
1600 opt = process_keyval_opt(options, "style");
1602 h_biblatex_citestyle = opt;
1603 h_biblatex_bibstyle = opt;
1605 opt = process_keyval_opt(options, "citestyle");
1607 h_biblatex_citestyle = opt;
1608 opt = process_keyval_opt(options, "bibstyle");
1610 h_biblatex_bibstyle = opt;
1612 opt = process_keyval_opt(options, "refsection");
1614 if (opt == "none" || opt == "part"
1615 || opt == "chapter" || opt == "section"
1616 || opt == "subsection")
1619 cerr << "Ignoring unkown refesection value '"
1622 opt = process_keyval_opt(options, "bibencoding");
1625 if (!options.empty()) {
1626 h_biblio_options = join(options, ",");
1631 else if (name == "jurabib") {
1632 h_biblio_style = "jurabib";
1633 h_cite_engine = "jurabib";
1634 h_cite_engine_type = "authoryear";
1635 if (!options.empty())
1636 h_biblio_options = join(options, ",");
1639 else if (name == "bibtopic")
1640 h_use_bibtopic = "true";
1642 else if (name == "chapterbib")
1643 h_multibib = "child";
1645 else if (name == "hyperref")
1646 handle_hyperref(options);
1648 else if (name == "algorithm2e") {
1649 // Load "algorithm2e" module
1650 addModule("algorithm2e");
1651 // Add the package options to the global document options
1652 if (!options.empty()) {
1653 if (h_options.empty())
1654 h_options = join(options, ",");
1656 h_options += ',' + join(options, ",");
1659 else if (name == "microtype") {
1660 //we internally support only microtype without params
1661 if (options.empty())
1662 h_use_microtype = "true";
1664 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1667 else if (name == "lineno") {
1668 h_use_lineno = "true";
1669 if (!options.empty()) {
1670 h_lineno_options = join(options, ",");
1675 else if (!in_lyx_preamble) {
1676 if (options.empty())
1677 h_preamble << "\\usepackage{" << name << '}';
1679 h_preamble << "\\usepackage[" << opts << "]{"
1683 if (p.next_token().cat() == catNewline ||
1684 (p.next_token().cat() == catSpace &&
1685 p.next_next_token().cat() == catNewline))
1689 // We need to do something with the options...
1690 if (!options.empty() && !detectEncoding)
1691 cerr << "Ignoring options '" << join(options, ",")
1692 << "' of package " << name << '.' << endl;
1694 // remove the whitespace
1699 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1702 Token t = p.get_token();
1703 if (t.cat() == catEscape &&
1704 is_known(t.cs(), known_if_commands))
1705 handle_if(p, in_lyx_preamble);
1707 if (!in_lyx_preamble)
1708 h_preamble << t.asInput();
1709 if (t.cat() == catEscape && t.cs() == "fi")
1716 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1718 if (contains(h_float_placement, "H"))
1719 registerAutomaticallyLoadedPackage("float");
1720 if (h_spacing != "single" && h_spacing != "default")
1721 registerAutomaticallyLoadedPackage("setspace");
1722 if (h_use_packages["amsmath"] == "2") {
1723 // amsbsy and amstext are already provided by amsmath
1724 registerAutomaticallyLoadedPackage("amsbsy");
1725 registerAutomaticallyLoadedPackage("amstext");
1728 // output the LyX file settings
1729 // Important: Keep the version formatting in sync with LyX and
1730 // lyx2lyx (bug 7951)
1731 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1732 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1733 << lyx_version_minor << '\n'
1734 << "\\lyxformat " << LYX_FORMAT << '\n'
1735 << "\\begin_document\n"
1736 << "\\begin_header\n"
1737 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1738 << "\\origin " << origin << "\n"
1739 << "\\textclass " << h_textclass << "\n";
1740 string const raw = subdoc ? empty_string() : h_preamble.str();
1742 os << "\\begin_preamble\n";
1743 for (string::size_type i = 0; i < raw.size(); ++i) {
1744 if (raw[i] == package_beg_sep) {
1745 // Here follows some package loading code that
1746 // must be skipped if the package is loaded
1748 string::size_type j = raw.find(package_mid_sep, i);
1749 if (j == string::npos)
1751 string::size_type k = raw.find(package_end_sep, j);
1752 if (k == string::npos)
1754 string const package = raw.substr(i + 1, j - i - 1);
1755 string const replacement = raw.substr(j + 1, k - j - 1);
1756 if (auto_packages.find(package) == auto_packages.end())
1762 os << "\n\\end_preamble\n";
1764 if (!h_options.empty())
1765 os << "\\options " << h_options << "\n";
1766 os << "\\use_default_options " << h_use_default_options << "\n";
1767 if (!used_modules.empty()) {
1768 os << "\\begin_modules\n";
1769 vector<string>::const_iterator const end = used_modules.end();
1770 vector<string>::const_iterator it = used_modules.begin();
1771 for (; it != end; ++it)
1773 os << "\\end_modules\n";
1775 if (!h_includeonlys.empty()) {
1776 os << "\\begin_includeonly\n";
1777 for (auto const & iofile : h_includeonlys)
1778 os << iofile << '\n';
1779 os << "\\end_includeonly\n";
1781 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1782 << "\\language " << h_language << "\n"
1783 << "\\language_package " << h_language_package << "\n"
1784 << "\\inputencoding " << h_inputencoding << "\n"
1785 << "\\fontencoding " << h_fontencoding << "\n"
1786 << "\\font_roman \"" << h_font_roman[0]
1787 << "\" \"" << h_font_roman[1] << "\"\n"
1788 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1789 << "\\font_typewriter \"" << h_font_typewriter[0]
1790 << "\" \"" << h_font_typewriter[1] << "\"\n"
1791 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1792 << "\\font_default_family " << h_font_default_family << "\n"
1793 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1794 << "\\font_sc " << h_font_sc << "\n"
1795 << "\\font_roman_osf " << h_font_roman_osf << "\n"
1796 << "\\font_sans_osf " << h_font_sans_osf << "\n"
1797 << "\\font_typewriter_osf " << h_font_typewriter_osf << "\n";
1798 if (!h_font_roman_opts.empty())
1799 os << "\\font_roman_opts \"" << h_font_roman_opts << "\"" << '\n';
1800 os << "\\font_sf_scale " << h_font_sf_scale[0]
1801 << ' ' << h_font_sf_scale[1] << '\n';
1802 if (!h_font_sans_opts.empty())
1803 os << "\\font_sans_opts \"" << h_font_sans_opts << "\"" << '\n';
1804 os << "\\font_tt_scale " << h_font_tt_scale[0]
1805 << ' ' << h_font_tt_scale[1] << '\n';
1806 if (!h_font_cjk.empty())
1807 os << "\\font_cjk " << h_font_cjk << '\n';
1808 if (!h_font_typewriter_opts.empty())
1809 os << "\\font_typewriter_opts \"" << h_font_typewriter_opts << "\"" << '\n';
1810 os << "\\use_microtype " << h_use_microtype << '\n'
1811 << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
1812 << "\\graphics " << h_graphics << '\n'
1813 << "\\default_output_format " << h_default_output_format << "\n"
1814 << "\\output_sync " << h_output_sync << "\n";
1815 if (h_output_sync == "1")
1816 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1817 os << "\\bibtex_command " << h_bibtex_command << "\n"
1818 << "\\index_command " << h_index_command << "\n";
1819 if (!h_float_placement.empty())
1820 os << "\\float_placement " << h_float_placement << "\n";
1821 os << "\\paperfontsize " << h_paperfontsize << "\n"
1822 << "\\spacing " << h_spacing << "\n"
1823 << "\\use_hyperref " << h_use_hyperref << '\n';
1824 if (h_use_hyperref == "true") {
1825 if (!h_pdf_title.empty())
1826 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1827 if (!h_pdf_author.empty())
1828 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1829 if (!h_pdf_subject.empty())
1830 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1831 if (!h_pdf_keywords.empty())
1832 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1833 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1834 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1835 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1836 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1837 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1838 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1839 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1840 "\\pdf_backref " << h_pdf_backref << "\n"
1841 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1842 if (!h_pdf_pagemode.empty())
1843 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1844 if (!h_pdf_quoted_options.empty())
1845 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1847 os << "\\papersize " << h_papersize << "\n"
1848 << "\\use_geometry " << h_use_geometry << '\n';
1849 for (map<string, string>::const_iterator it = h_use_packages.begin();
1850 it != h_use_packages.end(); ++it)
1851 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1852 os << "\\cite_engine " << h_cite_engine << '\n'
1853 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1854 << "\\biblio_style " << h_biblio_style << "\n"
1855 << "\\use_bibtopic " << h_use_bibtopic << "\n";
1856 if (!h_biblio_options.empty())
1857 os << "\\biblio_options " << h_biblio_options << "\n";
1858 if (!h_biblatex_bibstyle.empty())
1859 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
1860 if (!h_biblatex_citestyle.empty())
1861 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
1862 if (!h_multibib.empty())
1863 os << "\\multibib " << h_multibib << "\n";
1864 os << "\\use_indices " << h_use_indices << "\n"
1865 << "\\paperorientation " << h_paperorientation << '\n'
1866 << "\\suppress_date " << h_suppress_date << '\n'
1867 << "\\justification " << h_justification << '\n'
1868 << "\\use_refstyle " << h_use_refstyle << '\n'
1869 << "\\use_minted " << h_use_minted << '\n'
1870 << "\\use_lineno " << h_use_lineno << '\n';
1871 if (!h_lineno_options.empty())
1872 os << "\\lineno_options " << h_lineno_options << '\n';
1873 if (!h_fontcolor.empty())
1874 os << "\\fontcolor " << h_fontcolor << '\n';
1875 if (!h_notefontcolor.empty())
1876 os << "\\notefontcolor " << h_notefontcolor << '\n';
1877 if (!h_backgroundcolor.empty())
1878 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1879 if (!h_boxbgcolor.empty())
1880 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1881 if (index_number != 0)
1882 for (int i = 0; i < index_number; i++) {
1883 os << "\\index " << h_index[i] << '\n'
1884 << "\\shortcut " << h_shortcut[i] << '\n'
1885 << "\\color " << h_color << '\n'
1889 os << "\\index " << h_index[0] << '\n'
1890 << "\\shortcut " << h_shortcut[0] << '\n'
1891 << "\\color " << h_color << '\n'
1895 << "\\secnumdepth " << h_secnumdepth << "\n"
1896 << "\\tocdepth " << h_tocdepth << "\n"
1897 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1898 if (h_paragraph_separation == "skip")
1899 os << "\\defskip " << h_defskip << "\n";
1901 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1902 os << "\\is_math_indent " << h_is_mathindent << "\n";
1903 if (!h_mathindentation.empty())
1904 os << "\\math_indentation " << h_mathindentation << "\n";
1905 os << "\\math_numbering_side " << h_math_numbering_side << "\n";
1906 os << "\\quotes_style " << h_quotes_style << "\n"
1907 << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
1908 << "\\papercolumns " << h_papercolumns << "\n"
1909 << "\\papersides " << h_papersides << "\n"
1910 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1911 if (!h_listings_params.empty())
1912 os << "\\listings_params " << h_listings_params << "\n";
1913 os << "\\tracking_changes " << h_tracking_changes << "\n"
1914 << "\\output_changes " << h_output_changes << "\n"
1915 << "\\html_math_output " << h_html_math_output << "\n"
1916 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1917 << "\\html_be_strict " << h_html_be_strict << "\n"
1919 << "\\end_header\n\n"
1920 << "\\begin_body\n";
1925 void Preamble::parse(Parser & p, string const & forceclass,
1926 TeX2LyXDocClass & tc)
1928 // initialize fixed types
1929 special_columns_['D'] = 3;
1930 parse(p, forceclass, false, tc);
1934 void Preamble::parse(Parser & p, string const & forceclass,
1935 bool detectEncoding, TeX2LyXDocClass & tc)
1937 bool is_full_document = false;
1938 bool is_lyx_file = false;
1939 bool in_lyx_preamble = false;
1941 // determine whether this is a full document or a fragment for inclusion
1943 Token const & t = p.get_token();
1945 if (t.cat() == catEscape && t.cs() == "documentclass") {
1946 is_full_document = true;
1952 if (detectEncoding && !is_full_document)
1955 while (is_full_document && p.good()) {
1956 if (detectEncoding && h_inputencoding != "auto-legacy" &&
1957 h_inputencoding != "auto-legacy-plain")
1960 Token const & t = p.get_token();
1963 if (!detectEncoding)
1964 cerr << "t: " << t << '\n';
1970 if (!in_lyx_preamble &&
1971 (t.cat() == catLetter ||
1972 t.cat() == catSuper ||
1973 t.cat() == catSub ||
1974 t.cat() == catOther ||
1975 t.cat() == catMath ||
1976 t.cat() == catActive ||
1977 t.cat() == catBegin ||
1978 t.cat() == catEnd ||
1979 t.cat() == catAlign ||
1980 t.cat() == catParameter)) {
1981 h_preamble << t.cs();
1985 if (!in_lyx_preamble &&
1986 (t.cat() == catSpace || t.cat() == catNewline)) {
1987 h_preamble << t.asInput();
1991 if (t.cat() == catComment) {
1992 static regex const islyxfile("%% LyX .* created this file");
1993 static regex const usercommands("User specified LaTeX commands");
1995 string const comment = t.asInput();
1997 // magically switch encoding default if it looks like XeLaTeX
1998 static string const magicXeLaTeX =
1999 "% This document must be compiled with XeLaTeX ";
2000 if (comment.size() > magicXeLaTeX.size()
2001 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
2002 && h_inputencoding == "auto-legacy") {
2003 if (!detectEncoding)
2004 cerr << "XeLaTeX comment found, switching to UTF8\n";
2005 h_inputencoding = "utf8";
2008 if (regex_search(comment, sub, islyxfile)) {
2010 in_lyx_preamble = true;
2011 } else if (is_lyx_file
2012 && regex_search(comment, sub, usercommands))
2013 in_lyx_preamble = false;
2014 else if (!in_lyx_preamble)
2015 h_preamble << t.asInput();
2019 if (t.cs() == "PassOptionsToPackage") {
2020 string const poptions = p.getArg('{', '}');
2021 string const package = p.verbatim_item();
2022 extra_package_options_.insert(make_pair(package, poptions));
2026 if (t.cs() == "pagestyle") {
2027 h_paperpagestyle = p.verbatim_item();
2031 if (t.cs() == "setdefaultlanguage") {
2033 // We don't yet care about non-language variant options
2034 // because LyX doesn't support this yet, see bug #8214
2036 string langopts = p.getOpt();
2037 // check if the option contains a variant, if yes, extract it
2038 string::size_type pos_var = langopts.find("variant");
2039 string::size_type i = langopts.find(',', pos_var);
2040 string::size_type k = langopts.find('=', pos_var);
2041 if (pos_var != string::npos){
2043 if (i == string::npos)
2044 variant = langopts.substr(k + 1, langopts.length() - k - 2);
2046 variant = langopts.substr(k + 1, i - k - 1);
2047 h_language = variant;
2051 h_language = p.verbatim_item();
2052 //finally translate the poyglossia name to a LyX name
2053 h_language = polyglossia2lyx(h_language);
2057 if (t.cs() == "setotherlanguage") {
2058 // We don't yet care about the option because LyX doesn't
2059 // support this yet, see bug #8214
2060 p.hasOpt() ? p.getOpt() : string();
2065 if (t.cs() == "setmainfont") {
2066 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2067 h_font_roman[1] = p.getArg('{', '}');
2068 if (!fontopts.empty()) {
2069 vector<string> opts = getVectorFromString(fontopts);
2071 for (auto const & opt : opts) {
2072 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2075 if (!fontopts.empty())
2079 h_font_roman_opts = fontopts;
2084 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
2085 // LyX currently only supports the scale option
2086 string scale, fontopts;
2088 fontopts = p.getArg('[', ']');
2089 if (!fontopts.empty()) {
2090 vector<string> opts = getVectorFromString(fontopts);
2092 for (auto const & opt : opts) {
2093 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2096 if (prefixIs(opt, "Scale=")) {
2097 scale_as_percentage(opt, scale);
2100 if (!fontopts.empty())
2106 if (t.cs() == "setsansfont") {
2108 h_font_sf_scale[1] = scale;
2109 h_font_sans[1] = p.getArg('{', '}');
2110 if (!fontopts.empty())
2111 h_font_sans_opts = fontopts;
2114 h_font_tt_scale[1] = scale;
2115 h_font_typewriter[1] = p.getArg('{', '}');
2116 if (!fontopts.empty())
2117 h_font_typewriter_opts = fontopts;
2122 if (t.cs() == "babelfont") {
2124 h_use_non_tex_fonts = true;
2125 h_language_package = "babel";
2126 if (h_inputencoding == "auto-legacy")
2127 p.setEncoding("UTF-8");
2128 // we don't care about the lang option
2129 string const lang = p.hasOpt() ? p.getArg('[', ']') : string();
2130 string const family = p.getArg('{', '}');
2131 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2132 string const fontname = p.getArg('{', '}');
2133 if (lang.empty() && family == "rm") {
2134 h_font_roman[1] = fontname;
2135 if (!fontopts.empty()) {
2136 vector<string> opts = getVectorFromString(fontopts);
2138 for (auto const & opt : opts) {
2139 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2142 if (!fontopts.empty())
2146 h_font_roman_opts = fontopts;
2149 } else if (lang.empty() && (family == "sf" || family == "tt")) {
2150 // LyX currently only supports the scale option
2152 if (!fontopts.empty()) {
2153 vector<string> opts = getVectorFromString(fontopts);
2155 for (auto const & opt : opts) {
2156 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2159 if (prefixIs(opt, "Scale=")) {
2160 scale_as_percentage(opt, scale);
2163 if (!fontopts.empty())
2168 if (family == "sf") {
2170 h_font_sf_scale[1] = scale;
2171 h_font_sans[1] = fontname;
2172 if (!fontopts.empty())
2173 h_font_sans_opts = fontopts;
2176 h_font_tt_scale[1] = scale;
2177 h_font_typewriter[1] = fontname;
2178 if (!fontopts.empty())
2179 h_font_typewriter_opts = fontopts;
2183 // not rm, sf or tt or lang specific
2184 h_preamble << '\\' << t.cs();
2186 h_preamble << '[' << lang << ']';
2187 h_preamble << '{' << family << '}';
2188 if (!fontopts.empty())
2189 h_preamble << '[' << fontopts << ']';
2190 h_preamble << '{' << fontname << '}' << '\n';
2195 if (t.cs() == "date") {
2196 string argument = p.getArg('{', '}');
2197 if (argument.empty())
2198 h_suppress_date = "true";
2200 h_preamble << t.asInput() << '{' << argument << '}';
2204 if (t.cs() == "color") {
2205 string const space =
2206 (p.hasOpt() ? p.getOpt() : string());
2207 string argument = p.getArg('{', '}');
2208 // check the case that a standard color is used
2209 if (space.empty() && is_known(argument, known_basic_colors)) {
2210 h_fontcolor = rgbcolor2code(argument);
2211 registerAutomaticallyLoadedPackage("color");
2212 } else if (space.empty() && argument == "document_fontcolor")
2213 registerAutomaticallyLoadedPackage("color");
2214 // check the case that LyX's document_fontcolor is defined
2215 // but not used for \color
2217 h_preamble << t.asInput();
2219 h_preamble << space;
2220 h_preamble << '{' << argument << '}';
2221 // the color might already be set because \definecolor
2222 // is parsed before this
2228 if (t.cs() == "pagecolor") {
2229 string argument = p.getArg('{', '}');
2230 // check the case that a standard color is used
2231 if (is_known(argument, known_basic_colors)) {
2232 h_backgroundcolor = rgbcolor2code(argument);
2233 } else if (argument == "page_backgroundcolor")
2234 registerAutomaticallyLoadedPackage("color");
2235 // check the case that LyX's page_backgroundcolor is defined
2236 // but not used for \pagecolor
2238 h_preamble << t.asInput() << '{' << argument << '}';
2239 // the color might already be set because \definecolor
2240 // is parsed before this
2241 h_backgroundcolor = "";
2246 if (t.cs() == "makeatletter") {
2247 // LyX takes care of this
2248 p.setCatcode('@', catLetter);
2252 if (t.cs() == "makeatother") {
2253 // LyX takes care of this
2254 p.setCatcode('@', catOther);
2258 if (t.cs() == "makeindex") {
2259 // LyX will re-add this if a print index command is found
2264 if (t.cs() == "newindex") {
2265 string const indexname = p.getArg('[', ']');
2266 string const shortcut = p.verbatim_item();
2267 if (!indexname.empty())
2268 h_index[index_number] = indexname;
2270 h_index[index_number] = shortcut;
2271 h_shortcut[index_number] = shortcut;
2277 if (t.cs() == "addbibresource") {
2278 string const options = p.getArg('[', ']');
2279 string const arg = removeExtension(p.getArg('{', '}'));
2280 if (!options.empty()) {
2281 // check if the option contains a bibencoding, if yes, extract it
2282 string::size_type pos = options.find("bibencoding=");
2284 if (pos != string::npos) {
2285 string::size_type i = options.find(',', pos);
2286 if (i == string::npos)
2287 encoding = options.substr(pos + 1);
2289 encoding = options.substr(pos, i - pos);
2290 pos = encoding.find('=');
2291 if (pos == string::npos)
2294 encoding = encoding.substr(pos + 1);
2296 if (!encoding.empty())
2297 biblatex_encodings.push_back(normalize_filename(arg) + ' ' + encoding);
2299 biblatex_bibliographies.push_back(arg);
2303 if (t.cs() == "bibliography") {
2304 vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
2305 biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
2309 if (t.cs() == "RS@ifundefined") {
2310 string const name = p.verbatim_item();
2311 string const body1 = p.verbatim_item();
2312 string const body2 = p.verbatim_item();
2313 // only non-lyxspecific stuff
2314 if (in_lyx_preamble &&
2315 (name == "subsecref" || name == "thmref" || name == "lemref"))
2319 ss << '\\' << t.cs();
2320 ss << '{' << name << '}'
2321 << '{' << body1 << '}'
2322 << '{' << body2 << '}';
2323 h_preamble << ss.str();
2328 if (t.cs() == "AtBeginDocument") {
2329 string const name = p.verbatim_item();
2330 // only non-lyxspecific stuff
2331 if (in_lyx_preamble &&
2332 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
2333 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
2334 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
2335 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
2336 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
2337 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
2338 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
2339 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
2340 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
2341 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
2342 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
2343 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
2344 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
2345 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
2346 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
2350 ss << '\\' << t.cs();
2351 ss << '{' << name << '}';
2352 h_preamble << ss.str();
2357 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
2358 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
2359 || t.cs() == "providecommand" || t.cs() == "providecommandx"
2360 || t.cs() == "DeclareRobustCommand"
2361 || t.cs() == "DeclareRobustCommandx"
2362 || t.cs() == "ProvideTextCommandDefault"
2363 || t.cs() == "DeclareMathAccent") {
2365 if (p.next_token().character() == '*') {
2369 string const name = p.verbatim_item();
2370 string const opt1 = p.getFullOpt();
2371 string const opt2 = p.getFullOpt();
2372 string const body = p.verbatim_item();
2373 // store the in_lyx_preamble setting
2374 bool const was_in_lyx_preamble = in_lyx_preamble;
2376 if (name == "\\rmdefault")
2377 if (is_known(body, known_roman_font_packages)) {
2378 h_font_roman[0] = body;
2380 in_lyx_preamble = true;
2382 if (name == "\\sfdefault")
2383 if (is_known(body, known_sans_font_packages)) {
2384 h_font_sans[0] = body;
2386 in_lyx_preamble = true;
2388 if (name == "\\ttdefault")
2389 if (is_known(body, known_typewriter_font_packages)) {
2390 h_font_typewriter[0] = body;
2392 in_lyx_preamble = true;
2394 if (name == "\\familydefault") {
2395 string family = body;
2396 // remove leading "\"
2397 h_font_default_family = family.erase(0,1);
2399 in_lyx_preamble = true;
2402 // remove LyX-specific definitions that are re-added by LyX
2404 // \lyxline is an ancient command that is converted by tex2lyx into
2405 // a \rule therefore remove its preamble code
2406 if (name == "\\lyxdot" || name == "\\lyxarrow"
2407 || name == "\\lyxline" || name == "\\LyX") {
2409 in_lyx_preamble = true;
2412 // Add the command to the known commands
2413 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
2415 // only non-lyxspecific stuff
2416 if (!in_lyx_preamble) {
2418 ss << '\\' << t.cs();
2421 ss << '{' << name << '}' << opt1 << opt2
2422 << '{' << body << "}";
2423 if (prefixIs(t.cs(), "renew") || !contains(h_preamble.str(), ss.str()))
2424 h_preamble << ss.str();
2426 ostream & out = in_preamble ? h_preamble : os;
2427 out << "\\" << t.cs() << "{" << name << "}"
2428 << opts << "{" << body << "}";
2431 // restore the in_lyx_preamble setting
2432 in_lyx_preamble = was_in_lyx_preamble;
2436 if (t.cs() == "documentclass") {
2437 vector<string>::iterator it;
2438 vector<string> opts = split_options(p.getArg('[', ']'));
2439 handle_opt(opts, known_fontsizes, h_paperfontsize);
2440 delete_opt(opts, known_fontsizes);
2441 // delete "pt" at the end
2442 string::size_type i = h_paperfontsize.find("pt");
2443 if (i != string::npos)
2444 h_paperfontsize.erase(i);
2445 // The documentclass options are always parsed before the options
2446 // of the babel call so that a language cannot overwrite the babel
2448 handle_opt(opts, known_languages, h_language);
2449 delete_opt(opts, known_languages);
2452 if ((it = find(opts.begin(), opts.end(), "fleqn"))
2454 h_is_mathindent = "1";
2457 // formula numbering side
2458 if ((it = find(opts.begin(), opts.end(), "leqno"))
2460 h_math_numbering_side = "left";
2463 else if ((it = find(opts.begin(), opts.end(), "reqno"))
2465 h_math_numbering_side = "right";
2469 // paper orientation
2470 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
2471 h_paperorientation = "landscape";
2475 if ((it = find(opts.begin(), opts.end(), "oneside"))
2480 if ((it = find(opts.begin(), opts.end(), "twoside"))
2486 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
2488 h_papercolumns = "1";
2491 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
2493 h_papercolumns = "2";
2497 // some size options are known to any document classes, other sizes
2498 // are handled by the \geometry command of the geometry package
2499 handle_opt(opts, known_class_paper_sizes, h_papersize);
2500 delete_opt(opts, known_class_paper_sizes);
2501 // the remaining options
2502 h_options = join(opts, ",");
2503 // FIXME This does not work for classes that have a
2504 // different name in LyX than in LaTeX
2505 h_textclass = p.getArg('{', '}');
2510 if (t.cs() == "usepackage") {
2511 string const options = p.getArg('[', ']');
2512 string const name = p.getArg('{', '}');
2513 vector<string> vecnames;
2514 split(name, vecnames, ',');
2515 vector<string>::const_iterator it = vecnames.begin();
2516 vector<string>::const_iterator end = vecnames.end();
2517 for (; it != end; ++it)
2518 handle_package(p, trimSpaceAndEol(*it), options,
2519 in_lyx_preamble, detectEncoding);
2523 if (t.cs() == "inputencoding") {
2524 string const encoding = p.getArg('{','}');
2525 Encoding const * const enc = encodings.fromLaTeXName(
2526 encoding, Encoding::inputenc, true);
2528 if (!detectEncoding)
2529 cerr << "Unknown encoding " << encoding
2530 << ". Ignoring." << std::endl;
2533 h_inputencoding = enc->name();
2534 p.setEncoding(enc->iconvName());
2539 if (t.cs() == "newenvironment") {
2540 string const name = p.getArg('{', '}');
2541 string const opt1 = p.getFullOpt();
2542 string const opt2 = p.getFullOpt();
2543 string const beg = p.verbatim_item();
2544 string const end = p.verbatim_item();
2545 if (!in_lyx_preamble) {
2546 h_preamble << "\\newenvironment{" << name
2547 << '}' << opt1 << opt2 << '{'
2548 << beg << "}{" << end << '}';
2550 add_known_environment(name, opt1, !opt2.empty(),
2551 from_utf8(beg), from_utf8(end));
2555 if (t.cs() == "newtheorem") {
2557 if (p.next_token().character() == '*') {
2561 string const name = p.getArg('{', '}');
2562 string const opt1 = p.getFullOpt();
2563 string const opt2 = p.getFullOpt();
2564 string const body = p.verbatim_item();
2565 string const opt3 = p.getFullOpt();
2566 string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
2568 string const complete = cmd + "{" + name + '}' +
2569 opt1 + opt2 + '{' + body + '}' + opt3;
2571 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
2573 if (!in_lyx_preamble)
2574 h_preamble << complete;
2578 if (t.cs() == "def") {
2579 string name = p.get_token().cs();
2580 // In fact, name may be more than the name:
2581 // In the test case of bug 8116
2582 // name == "csname SF@gobble@opt \endcsname".
2583 // Therefore, we need to use asInput() instead of cs().
2584 while (p.next_token().cat() != catBegin)
2585 name += p.get_token().asInput();
2586 if (!in_lyx_preamble)
2587 h_preamble << "\\def\\" << name << '{'
2588 << p.verbatim_item() << "}";
2592 if (t.cs() == "newcolumntype") {
2593 string const name = p.getArg('{', '}');
2594 trimSpaceAndEol(name);
2596 string opts = p.getOpt();
2597 if (!opts.empty()) {
2598 istringstream is(string(opts, 1));
2601 special_columns_[name[0]] = nargs;
2602 h_preamble << "\\newcolumntype{" << name << "}";
2604 h_preamble << "[" << nargs << "]";
2605 h_preamble << "{" << p.verbatim_item() << "}";
2609 if (t.cs() == "setcounter") {
2610 string const name = p.getArg('{', '}');
2611 string const content = p.getArg('{', '}');
2612 if (name == "secnumdepth")
2613 h_secnumdepth = content;
2614 else if (name == "tocdepth")
2615 h_tocdepth = content;
2617 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
2621 if (t.cs() == "setlength") {
2622 string const name = p.verbatim_item();
2623 string const content = p.verbatim_item();
2624 // the paragraphs are only not indented when \parindent is set to zero
2625 if (name == "\\parindent" && content != "") {
2626 if (content[0] == '0')
2627 h_paragraph_separation = "skip";
2629 h_paragraph_indentation = translate_len(content);
2630 } else if (name == "\\parskip") {
2631 if (content == "\\smallskipamount")
2632 h_defskip = "smallskip";
2633 else if (content == "\\medskipamount")
2634 h_defskip = "medskip";
2635 else if (content == "\\bigskipamount")
2636 h_defskip = "bigskip";
2638 h_defskip = translate_len(content);
2639 } else if (name == "\\mathindent") {
2640 h_mathindentation = translate_len(content);
2642 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2646 if (t.cs() == "onehalfspacing") {
2647 h_spacing = "onehalf";
2651 if (t.cs() == "doublespacing") {
2652 h_spacing = "double";
2656 if (t.cs() == "setstretch") {
2657 h_spacing = "other " + p.verbatim_item();
2661 if (t.cs() == "synctex") {
2662 // the scheme is \synctex=value
2663 // where value can only be "1" or "-1"
2664 h_output_sync = "1";
2665 // there can be any character behind the value (e.g. a linebreak or a '\'
2666 // therefore we extract it char by char
2668 string value = p.get_token().asInput();
2670 value += p.get_token().asInput();
2671 h_output_sync_macro = "\\synctex=" + value;
2675 if (t.cs() == "begin") {
2676 string const name = p.getArg('{', '}');
2677 if (name == "document")
2679 h_preamble << "\\begin{" << name << "}";
2683 if (t.cs() == "geometry") {
2684 vector<string> opts = split_options(p.getArg('{', '}'));
2685 handle_geometry(opts);
2689 if (t.cs() == "definecolor") {
2690 string const color = p.getArg('{', '}');
2691 string const space = p.getArg('{', '}');
2692 string const value = p.getArg('{', '}');
2693 if (color == "document_fontcolor" && space == "rgb") {
2694 RGBColor c(RGBColorFromLaTeX(value));
2695 h_fontcolor = X11hexname(c);
2696 } else if (color == "note_fontcolor" && space == "rgb") {
2697 RGBColor c(RGBColorFromLaTeX(value));
2698 h_notefontcolor = X11hexname(c);
2699 } else if (color == "page_backgroundcolor" && space == "rgb") {
2700 RGBColor c(RGBColorFromLaTeX(value));
2701 h_backgroundcolor = X11hexname(c);
2702 } else if (color == "shadecolor" && space == "rgb") {
2703 RGBColor c(RGBColorFromLaTeX(value));
2704 h_boxbgcolor = X11hexname(c);
2706 h_preamble << "\\definecolor{" << color
2707 << "}{" << space << "}{" << value
2713 if (t.cs() == "bibliographystyle") {
2714 h_biblio_style = p.verbatim_item();
2718 if (t.cs() == "jurabibsetup") {
2719 // FIXME p.getArg('{', '}') is most probably wrong (it
2720 // does not handle nested braces).
2721 // Use p.verbatim_item() instead.
2722 vector<string> jurabibsetup =
2723 split_options(p.getArg('{', '}'));
2724 // add jurabibsetup to the jurabib package options
2725 add_package("jurabib", jurabibsetup);
2726 if (!jurabibsetup.empty()) {
2727 h_preamble << "\\jurabibsetup{"
2728 << join(jurabibsetup, ",") << '}';
2733 if (t.cs() == "hypersetup") {
2734 vector<string> hypersetup =
2735 split_options(p.verbatim_item());
2736 // add hypersetup to the hyperref package options
2737 handle_hyperref(hypersetup);
2738 if (!hypersetup.empty()) {
2739 h_preamble << "\\hypersetup{"
2740 << join(hypersetup, ",") << '}';
2745 if (t.cs() == "includeonly") {
2746 vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
2747 for (auto & iofile : includeonlys) {
2748 string filename(normalize_filename(iofile));
2749 string const path = getMasterFilePath(true);
2750 // We want to preserve relative/absolute filenames,
2751 // therefore path is only used for testing
2752 if (!makeAbsPath(filename, path).exists()) {
2753 // The file extension is probably missing.
2754 // Now try to find it out.
2755 string const tex_name =
2756 find_file(filename, path,
2757 known_tex_extensions);
2758 if (!tex_name.empty())
2759 filename = tex_name;
2762 if (makeAbsPath(filename, path).exists())
2763 fix_child_filename(filename);
2765 cerr << "Warning: Could not find included file '"
2766 << filename << "'." << endl;
2767 outname = changeExtension(filename, "lyx");
2768 h_includeonlys.push_back(outname);
2773 if (is_known(t.cs(), known_if_3arg_commands)) {
2774 // prevent misparsing of \usepackage if it is used
2775 // as an argument (see e.g. our own output of
2776 // \@ifundefined above)
2777 string const arg1 = p.verbatim_item();
2778 string const arg2 = p.verbatim_item();
2779 string const arg3 = p.verbatim_item();
2780 // test case \@ifundefined{date}{}{\date{}}
2781 if (t.cs() == "@ifundefined" && arg1 == "date" &&
2782 arg2.empty() && arg3 == "\\date{}") {
2783 h_suppress_date = "true";
2784 // older tex2lyx versions did output
2785 // \@ifundefined{definecolor}{\usepackage{color}}{}
2786 } else if (t.cs() == "@ifundefined" &&
2787 arg1 == "definecolor" &&
2788 arg2 == "\\usepackage{color}" &&
2790 if (!in_lyx_preamble)
2791 h_preamble << package_beg_sep
2794 << "\\@ifundefined{definecolor}{color}{}"
2797 //\@ifundefined{showcaptionsetup}{}{%
2798 // \PassOptionsToPackage{caption=false}{subfig}}
2799 // that LyX uses for subfloats
2800 } else if (t.cs() == "@ifundefined" &&
2801 arg1 == "showcaptionsetup" && arg2.empty()
2802 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
2804 } else if (!in_lyx_preamble) {
2805 h_preamble << t.asInput()
2806 << '{' << arg1 << '}'
2807 << '{' << arg2 << '}'
2808 << '{' << arg3 << '}';
2813 if (is_known(t.cs(), known_if_commands)) {
2814 // must not parse anything in conditional code, since
2815 // LyX would output the parsed contents unconditionally
2816 if (!in_lyx_preamble)
2817 h_preamble << t.asInput();
2818 handle_if(p, in_lyx_preamble);
2822 if (!t.cs().empty() && !in_lyx_preamble) {
2823 h_preamble << '\\' << t.cs();
2828 // remove the whitespace
2831 // Force textclass if the user wanted it
2832 if (!forceclass.empty())
2833 h_textclass = forceclass;
2834 tc.setName(h_textclass);
2835 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2836 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2839 if (h_papersides.empty()) {
2842 h_papersides = ss.str();
2845 // If the CJK package is used we cannot set the document language from
2846 // the babel options. Instead, we guess which language is used most
2847 // and set this one.
2848 default_language = h_language;
2849 if (is_full_document &&
2850 (auto_packages.find("CJK") != auto_packages.end() ||
2851 auto_packages.find("CJKutf8") != auto_packages.end())) {
2853 h_language = guessLanguage(p, default_language);
2855 if (explicit_babel && h_language != default_language) {
2856 // We set the document language to a CJK language,
2857 // but babel is explicitly called in the user preamble
2858 // without options. LyX will not add the default
2859 // language to the document options if it is either
2860 // english, or no text is set as default language.
2861 // Therefore we need to add a language option explicitly.
2862 // FIXME: It would be better to remove all babel calls
2863 // from the user preamble, but this is difficult
2864 // without re-introducing bug 7861.
2865 if (h_options.empty())
2866 h_options = lyx2babel(default_language);
2868 h_options += ',' + lyx2babel(default_language);
2872 // Finally, set the quote style.
2873 // LyX knows the following quotes styles:
2874 // british, cjk, cjkangle, danish, english, french, german,
2875 // polish, russian, swedish and swiss
2876 // conversion list taken from
2877 // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
2878 // (quotes for kazakh are unknown)
2880 if (is_known(h_language, known_british_quotes_languages))
2881 h_quotes_style = "british";
2883 else if (is_known(h_language, known_cjk_quotes_languages))
2884 h_quotes_style = "cjk";
2886 else if (is_known(h_language, known_cjkangle_quotes_languages))
2887 h_quotes_style = "cjkangle";
2889 else if (is_known(h_language, known_danish_quotes_languages))
2890 h_quotes_style = "danish";
2892 else if (is_known(h_language, known_french_quotes_languages))
2893 h_quotes_style = "french";
2895 else if (is_known(h_language, known_german_quotes_languages))
2896 h_quotes_style = "german";
2898 else if (is_known(h_language, known_polish_quotes_languages))
2899 h_quotes_style = "polish";
2901 else if (is_known(h_language, known_russian_quotes_languages))
2902 h_quotes_style = "russian";
2904 else if (is_known(h_language, known_swedish_quotes_languages))
2905 h_quotes_style = "swedish";
2907 else if (is_known(h_language, known_swiss_quotes_languages))
2908 h_quotes_style = "swiss";
2910 else if (is_known(h_language, known_english_quotes_languages))
2911 h_quotes_style = "english";
2915 string Preamble::parseEncoding(Parser & p, string const & forceclass)
2917 TeX2LyXDocClass dummy;
2918 parse(p, forceclass, true, dummy);
2919 if (h_inputencoding != "auto-legacy" && h_inputencoding != "auto-legacy-plain")
2920 return h_inputencoding;
2925 string babel2lyx(string const & language)
2927 char const * const * where = is_known(language, known_languages);
2929 return known_coded_languages[where - known_languages];
2934 string lyx2babel(string const & language)
2936 char const * const * where = is_known(language, known_coded_languages);
2938 return known_languages[where - known_coded_languages];
2943 string Preamble::polyglossia2lyx(string const & language)
2945 char const * const * where = is_known(language, polyglossia_languages);
2947 return coded_polyglossia_languages[where - polyglossia_languages];
2952 string rgbcolor2code(string const & name)
2954 char const * const * where = is_known(name, known_basic_colors);
2956 // "red", "green" etc
2957 return known_basic_color_codes[where - known_basic_colors];
2959 // "255,0,0", "0,255,0" etc
2960 RGBColor c(RGBColorFromLaTeX(name));
2961 return X11hexname(c);