3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
9 * Full author contact details are available in file CREDITS.
20 #include "LayoutFile.h"
23 #include "TextClass.h"
26 #include "support/convert.h"
27 #include "support/FileName.h"
28 #include "support/filetools.h"
29 #include "support/lstrings.h"
31 #include "support/regex.h"
37 using namespace lyx::support;
46 // CJK languages are handled in text.cpp, polyglossia languages are listed
49 * known babel language names (including synonyms)
50 * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
51 * please keep this in sync with known_coded_languages line by line!
53 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
54 "american", "arabic", "arabtex", "australian", "austrian", "azerbaijani", "bahasa", "bahasai",
55 "bahasam", "basque", "belarusian", "bosnian", "brazil", "brazilian", "breton", "british",
56 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
57 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "francais",
58 "french", "frenchb", "frenchle", "frenchpro", "friulan", "galician", "german", "germanb",
59 "georgian", "greek", "hebrew", "hungarian", "icelandic", "indon", "indonesian",
60 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
61 "latvian", "lithuanian", "lowersorbian", "lsorbian", "macedonian", "magyar", "malay", "meyalu",
62 "mongolian", "naustrian", "newzealand", "ngerman", "ngermanb", "norsk", "nswissgerman",
63 "nynorsk", "piedmontese", "polutonikogreek", "polish", "portuges", "portuguese",
64 "romanian", "romansh", "russian", "russianb", "samin", "scottish", "serbian", "serbian-latin",
65 "slovak", "slovene", "spanish", "swedish", "swissgerman", "thai", "turkish", "turkmen",
66 "ukraineb", "ukrainian", "uppersorbian", "UKenglish", "USenglish", "usorbian",
71 * the same as known_languages with .lyx names
72 * please keep this in sync with known_languages line by line!
74 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
75 "american", "arabic_arabi", "arabic_arabtex", "australian", "austrian", "azerbaijani", "bahasa", "bahasa",
76 "bahasam", "basque", "belarusian", "bosnian", "brazilian", "brazilian", "breton", "british",
77 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
78 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "french",
79 "french", "french", "french", "french", "friulan", "galician", "german", "german",
80 "georgian", "greek", "hebrew", "magyar", "icelandic", "bahasa", "bahasa",
81 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
82 "latvian", "lithuanian", "lowersorbian", "lowersorbian", "macedonian", "magyar", "bahasam", "bahasam",
83 "mongolian", "naustrian", "newzealand", "ngerman", "ngerman", "norsk", "german-ch",
84 "nynorsk", "piedmontese", "polutonikogreek", "polish", "portuguese", "portuguese",
85 "romanian", "romansh", "russian", "russian", "samin", "scottish", "serbian", "serbian-latin",
86 "slovak", "slovene", "spanish", "swedish", "german-ch-old", "thai", "turkish", "turkmen",
87 "ukrainian", "ukrainian", "uppersorbian", "english", "english", "uppersorbian",
88 "vietnamese", "welsh",
91 /// languages with british quotes (.lyx names)
92 const char * const known_british_quotes_languages[] = {"british", "welsh", 0};
94 /// languages with cjk quotes (.lyx names)
95 const char * const known_cjk_quotes_languages[] = {"chinese-traditional",
96 "japanese", "japanese-cjk", 0};
98 /// languages with cjk-angle quotes (.lyx names)
99 const char * const known_cjkangle_quotes_languages[] = {"korean", 0};
101 /// languages with danish quotes (.lyx names)
102 const char * const known_danish_quotes_languages[] = {"danish", 0};
104 /// languages with english quotes (.lyx names)
105 const char * const known_english_quotes_languages[] = {"american", "australian",
106 "bahasa", "bahasam", "bengali", "brazilian", "canadian", "chinese-simplified", "english",
107 "esperanto", "farsi", "interlingua", "irish", "newzealand", "scottish",
108 "thai", "turkish", "vietnamese", 0};
110 /// languages with french quotes (.lyx names)
111 const char * const known_french_quotes_languages[] = {"ancientgreek",
112 "arabic_arabi", "arabic_arabtex", "asturian", "belarusian", "breton",
113 "canadien", "catalan", "french", "friulan", "galician", "italian", "occitan",
114 "piedmontese", "portuguese", "spanish", "spanish-mexico", 0};
116 /// languages with german quotes (.lyx names)
117 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
118 "czech", "estonian", "georgian", "german", "icelandic", "latvian", "lithuanian",
119 "lowersorbian", "macedonian", "naustrian", "ngerman", "romansh", "slovak", "slovene",
122 /// languages with polish quotes (.lyx names)
123 const char * const known_polish_quotes_languages[] = {"afrikaans", "bosnian", "croatian",
124 "dutch", "magyar", "polish", "romanian", "serbian", "serbian-latin", 0};
126 /// languages with russian quotes (.lyx names)
127 const char * const known_russian_quotes_languages[] = {"azerbaijani", "oldrussian",
128 "russian", "ukrainian", 0};
130 /// languages with swedish quotes (.lyx names)
131 const char * const known_swedish_quotes_languages[] = {"finnish", "swedish", 0};
133 /// languages with swiss quotes (.lyx names)
134 const char * const known_swiss_quotes_languages[] = {"albanian",
135 "armenian", "basque", "churchslavonic", "german-ch", "german-ch-old",
136 "norsk", "nynorsk", "turkmen", "ukrainian", "vietnamese", 0};
138 /// known language packages from the times before babel
139 const char * const known_old_language_packages[] = {"french", "frenchle",
140 "frenchpro", "german", "ngerman", "pmfrench", 0};
142 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
144 const char * const known_roman_font_packages[] = { "ae", "beraserif", "bookman",
145 "ccfonts", "chancery", "charter", "cmr", "cochineal", "crimson", "CrimsonPro", "DejaVuSerif",
146 "DejaVuSerifCondensed", "fourier", "garamondx", "libertine", "libertineRoman", "libertine-type1",
147 "lmodern", "mathdesign", "mathpazo", "mathptmx", "MinionPro", "newcent", "noto", "noto-serif",
148 "PTSerif", "tgbonum", "tgchorus", "tgpagella", "tgschola", "tgtermes", "utopia", "xcharter", 0 };
150 const char * const known_sans_font_packages[] = { "avant", "berasans", "biolinum",
151 "biolinum-type1", "cantarell", "Chivo", "cmbr", "cmss", "DejaVuSans", "DejaVuSansCondensed", "FiraSans", "helvet", "iwona",
152 "iwonac", "iwonal", "iwonalc", "kurier", "kurierc", "kurierl", "kurierlc", "lmss", "noto", "noto-sans", "PTSans",
153 "tgadventor", "tgheros", "uop", 0 };
155 const char * const known_typewriter_font_packages[] = { "beramono", "cmtl", "cmtt", "courier", "DejaVuSansMono",
156 "FiraMono", "lmtt", "luximono", "fourier", "libertineMono", "libertineMono-type1", "lmodern",
157 "mathpazo", "mathptmx", "newcent", "noto", "noto-mono", "PTMono", "tgcursor", "txtt", 0 };
159 const char * const known_math_font_packages[] = { "eulervm", "newtxmath", 0};
161 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
162 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
163 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
164 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
165 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
167 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
168 "executivepaper", "legalpaper", "letterpaper", 0};
170 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
171 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
173 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
174 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
177 /// commands that can start an \if...\else...\endif sequence
178 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
179 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
180 "ifsidecap", "ifupgreek", 0};
182 const char * const known_basic_colors[] = {"black", "blue", "brown", "cyan",
183 "darkgray", "gray", "green", "lightgray", "lime", "magenta", "orange", "olive",
184 "pink", "purple", "red", "teal", "violet", "white", "yellow", 0};
186 const char * const known_basic_color_codes[] = {"#000000", "#0000ff", "#964B00", "#00ffff",
187 "#a9a9a9", "#808080", "#00ff00", "#d3d3d3", "#bfff00", "#ff00ff", "#ff7f00", "#808000",
188 "#ffc0cb", "#800080", "#ff0000", "#008080", "#8f00ff", "#ffffff", "#ffff00", 0};
190 /// conditional commands with three arguments like \@ifundefined{}{}{}
191 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
195 * Known file extensions for TeX files as used by \\includeonly
197 char const * const known_tex_extensions[] = {"tex", 0};
199 /// packages that work only in xetex
200 /// polyglossia is handled separately
201 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
202 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
203 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
205 /// packages that are automatically skipped if loaded by LyX
206 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
207 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color",
208 "float", "fontspec", "framed", "graphicx", "hhline", "ifthen", "longtable",
209 "makeidx", "minted", "multirow", "nomencl", "pdfpages", "prettyref", "refstyle",
210 "rotating", "rotfloat", "splitidx", "setspace", "subscript", "tabularx","textcomp", "tipa",
211 "tipx", "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xcolor", "xltabular",
214 // codes used to remove packages that are loaded automatically by LyX.
215 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
216 const char package_beg_sep = '\001';
217 const char package_mid_sep = '\002';
218 const char package_end_sep = '\003';
221 // returns true if at least one of the options in what has been found
222 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
228 // the last language option is the document language (for babel and LyX)
229 // the last size option is the document font size
230 vector<string>::iterator it;
231 vector<string>::iterator position = opts.begin();
232 for (; *what; ++what) {
233 it = find(opts.begin(), opts.end(), *what);
234 if (it != opts.end()) {
235 if (it >= position) {
246 void delete_opt(vector<string> & opts, char const * const * what)
251 // remove found options from the list
252 // do this after handle_opt to avoid potential memory leaks
253 vector<string>::iterator it;
254 for (; *what; ++what) {
255 it = find(opts.begin(), opts.end(), *what);
256 if (it != opts.end())
263 * Split a package options string (keyval format) into a vector.
265 * authorformat=smallcaps,
267 * titleformat=colonsep,
268 * bibformat={tabular,ibidem,numbered}
270 vector<string> split_options(string const & input)
272 vector<string> options;
276 Token const & t = p.get_token();
277 if (t.asInput() == ",") {
278 options.push_back(trimSpaceAndEol(option));
280 } else if (t.asInput() == "=") {
283 if (p.next_token().asInput() == "{")
284 option += '{' + p.getArg('{', '}') + '}';
285 } else if (t.cat() != catSpace && t.cat() != catComment)
286 option += t.asInput();
290 options.push_back(trimSpaceAndEol(option));
297 * Retrieve a keyval option "name={value with=sign}" named \p name from
298 * \p options and return the value.
299 * The found option is also removed from \p options.
301 string process_keyval_opt(vector<string> & options, string name)
303 for (size_t i = 0; i < options.size(); ++i) {
304 vector<string> option;
305 split(options[i], option, '=');
306 if (option.size() < 2)
308 if (option[0] == name) {
309 options.erase(options.begin() + i);
310 option.erase(option.begin());
311 return join(option, "=");
317 } // anonymous namespace
321 * known polyglossia language names (including variants)
322 * FIXME: support spelling=old for german variants (german vs. ngerman LyX names etc)
324 const char * const Preamble::polyglossia_languages[] = {
325 "albanian", "american", "amharic", "ancient", "arabic", "armenian", "asturian", "australian",
326 "bahasai", "bahasam", "basque", "bengali", "brazil", "brazilian", "breton", "british", "bulgarian",
327 "catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
328 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
329 "galician", "greek", "monotonic", "hebrew", "hindi",
330 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean",
331 "lao", "latin", "latvian", "lithuanian", "lsorbian", "magyar", "malayalam", "marathi",
332 "austrian", "newzealand", "german", "norsk", "nynorsk", "occitan", "oldrussian",
333 "piedmontese", "polish", "polytonic", "portuges", "romanian", "romansh", "russian",
334 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovenian", "spanish", "swedish", "syriac",
335 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
336 "ukrainian", "urdu", "usorbian", "vietnamese", "welsh", 0};
337 // not yet supported by LyX: "korean", "nko"
340 * the same as polyglossia_languages with .lyx names
341 * please keep this in sync with polyglossia_languages line by line!
343 const char * const Preamble::coded_polyglossia_languages[] = {
344 "albanian", "american", "amharic", "ancientgreek", "arabic_arabi", "armenian", "asturian", "australian",
345 "bahasa", "bahasam", "basque", "bengali", "brazilian", "brazilian", "breton", "british", "bulgarian",
346 "catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
347 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
348 "galician", "greek", "greek", "hebrew", "hindi",
349 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean",
350 "lao", "latin", "latvian", "lithuanian", "lowersorbian", "magyar", "malayalam", "marathi",
351 "naustrian","newzealand", "ngerman", "norsk", "nynorsk", "occitan", "oldrussian",
352 "piedmontese", "polish", "polutonikogreek", "portuges", "romanian", "romansh", "russian",
353 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovene", "spanish", "swedish", "syriac",
354 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
355 "ukrainian", "urdu", "uppersorbian", "vietnamese", "welsh", 0};
356 // not yet supported by LyX: "korean-polyglossia", "nko"
359 bool Preamble::usePolyglossia() const
361 return h_use_non_tex_fonts && h_language_package == "default";
365 bool Preamble::indentParagraphs() const
367 return h_paragraph_separation == "indent";
371 bool Preamble::isPackageUsed(string const & package) const
373 return used_packages.find(package) != used_packages.end();
377 bool Preamble::isPackageAutoLoaded(string const & package) const
379 return auto_packages.find(package) != auto_packages.end();
383 vector<string> Preamble::getPackageOptions(string const & package) const
385 map<string, vector<string> >::const_iterator it = used_packages.find(package);
386 if (it != used_packages.end())
388 return vector<string>();
392 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
394 auto_packages.insert(package);
398 void Preamble::addModule(string const & module)
400 for (auto const & m : used_modules) {
404 used_modules.push_back(module);
408 void Preamble::suppressDate(bool suppress)
411 h_suppress_date = "true";
413 h_suppress_date = "false";
417 void Preamble::registerAuthor(std::string const & name)
419 Author author(from_utf8(name), empty_docstring());
420 author.setUsed(true);
421 authors_.record(author);
422 h_tracking_changes = "true";
423 h_output_changes = "true";
427 Author const & Preamble::getAuthor(std::string const & name) const
429 Author author(from_utf8(name), empty_docstring());
430 for (AuthorList::Authors::const_iterator it = authors_.begin();
431 it != authors_.end(); ++it)
434 static Author const dummy;
439 int Preamble::getSpecialTableColumnArguments(char c) const
441 map<char, int>::const_iterator it = special_columns_.find(c);
442 if (it == special_columns_.end())
448 void Preamble::add_package(string const & name, vector<string> & options)
450 // every package inherits the global options
451 if (used_packages.find(name) == used_packages.end())
452 used_packages[name] = split_options(h_options);
454 // Insert options passed via PassOptionsToPackage
455 for (auto const & p : extra_package_options_) {
456 if (p.first == name) {
457 vector<string> eo = getVectorFromString(p.second);
458 for (auto const & eoi : eo)
459 options.push_back(eoi);
463 vector<string> & v = used_packages[name];
464 v.insert(v.end(), options.begin(), options.end());
465 if (name == "jurabib") {
466 // Don't output the order argument (see the cite command
467 // handling code in text.cpp).
468 vector<string>::iterator end =
469 remove(options.begin(), options.end(), "natbiborder");
470 end = remove(options.begin(), end, "jurabiborder");
471 options.erase(end, options.end());
478 // Given is a string like "scaled=0.9" or "scale=0.9", return 0.9 * 100
479 bool scale_as_percentage(string const & scale, string & percentage)
481 if (contains(scale, '=')) {
482 string const value = support::split(scale, '=');
483 if (isStrDbl(value)) {
484 percentage = convert<string>(
485 static_cast<int>(100 * convert<double>(value)));
493 string remove_braces(string const & value)
497 if (value[0] == '{' && value[value.length()-1] == '}')
498 return value.substr(1, value.length()-2);
502 } // anonymous namespace
505 Preamble::Preamble() : one_language(true), explicit_babel(false),
506 title_layout_found(false), index_number(0), h_font_cjk_set(false)
510 h_biblio_style = "plain";
511 h_bibtex_command = "default";
512 h_cite_engine = "basic";
513 h_cite_engine_type = "default";
515 h_defskip = "medskip";
516 h_dynamic_quotes = false;
519 h_fontencoding = "default";
520 h_font_roman[0] = "default";
521 h_font_roman[1] = "default";
522 h_font_sans[0] = "default";
523 h_font_sans[1] = "default";
524 h_font_typewriter[0] = "default";
525 h_font_typewriter[1] = "default";
526 h_font_math[0] = "auto";
527 h_font_math[1] = "auto";
528 h_font_default_family = "default";
529 h_use_non_tex_fonts = false;
531 h_font_roman_osf = "false";
532 h_font_sans_osf = "false";
533 h_font_typewriter_osf = "false";
534 h_font_sf_scale[0] = "100";
535 h_font_sf_scale[1] = "100";
536 h_font_tt_scale[0] = "100";
537 h_font_tt_scale[1] = "100";
538 // h_font_roman_opts;
540 // h_font_typewriter_opts;
542 h_is_mathindent = "0";
543 h_math_numbering_side = "default";
544 h_graphics = "default";
545 h_default_output_format = "default";
546 h_html_be_strict = "false";
547 h_html_css_as_file = "0";
548 h_html_math_output = "0";
549 h_index[0] = "Index";
550 h_index_command = "default";
551 h_inputencoding = "auto-legacy";
552 h_justification = "true";
553 h_language = "english";
554 h_language_package = "none";
556 h_maintain_unincluded_children = "false";
560 h_output_changes = "false";
562 //h_output_sync_macro
563 h_papercolumns = "1";
564 h_paperfontsize = "default";
565 h_paperorientation = "portrait";
566 h_paperpagestyle = "default";
568 h_papersize = "default";
569 h_paragraph_indentation = "default";
570 h_paragraph_separation = "indent";
575 h_pdf_bookmarks = "0";
576 h_pdf_bookmarksnumbered = "0";
577 h_pdf_bookmarksopen = "0";
578 h_pdf_bookmarksopenlevel = "1";
579 h_pdf_breaklinks = "0";
580 h_pdf_pdfborder = "0";
581 h_pdf_colorlinks = "0";
582 h_pdf_backref = "section";
583 h_pdf_pdfusetitle = "0";
585 //h_pdf_quoted_options;
586 h_quotes_style = "english";
588 h_shortcut[0] = "idx";
589 h_spacing = "single";
590 h_save_transient_properties = "true";
591 h_suppress_date = "false";
592 h_textclass = "article";
594 h_tracking_changes = "false";
595 h_use_bibtopic = "false";
596 h_use_dash_ligatures = "true";
597 h_use_indices = "false";
598 h_use_geometry = "false";
599 h_use_default_options = "false";
600 h_use_hyperref = "false";
601 h_use_microtype = "false";
602 h_use_lineno = "false";
603 h_use_refstyle = false;
604 h_use_minted = false;
605 h_use_packages["amsmath"] = "1";
606 h_use_packages["amssymb"] = "0";
607 h_use_packages["cancel"] = "0";
608 h_use_packages["esint"] = "1";
609 h_use_packages["mhchem"] = "0";
610 h_use_packages["mathdots"] = "0";
611 h_use_packages["mathtools"] = "0";
612 h_use_packages["stackrel"] = "0";
613 h_use_packages["stmaryrd"] = "0";
614 h_use_packages["undertilde"] = "0";
618 void Preamble::handle_hyperref(vector<string> & options)
620 // FIXME swallow inputencoding changes that might surround the
621 // hyperref setup if it was written by LyX
622 h_use_hyperref = "true";
623 // swallow "unicode=true", since LyX does always write that
624 vector<string>::iterator it =
625 find(options.begin(), options.end(), "unicode=true");
626 if (it != options.end())
628 it = find(options.begin(), options.end(), "pdfusetitle");
629 if (it != options.end()) {
630 h_pdf_pdfusetitle = "1";
633 string bookmarks = process_keyval_opt(options, "bookmarks");
634 if (bookmarks == "true")
635 h_pdf_bookmarks = "1";
636 else if (bookmarks == "false")
637 h_pdf_bookmarks = "0";
638 if (h_pdf_bookmarks == "1") {
639 string bookmarksnumbered =
640 process_keyval_opt(options, "bookmarksnumbered");
641 if (bookmarksnumbered == "true")
642 h_pdf_bookmarksnumbered = "1";
643 else if (bookmarksnumbered == "false")
644 h_pdf_bookmarksnumbered = "0";
645 string bookmarksopen =
646 process_keyval_opt(options, "bookmarksopen");
647 if (bookmarksopen == "true")
648 h_pdf_bookmarksopen = "1";
649 else if (bookmarksopen == "false")
650 h_pdf_bookmarksopen = "0";
651 if (h_pdf_bookmarksopen == "1") {
652 string bookmarksopenlevel =
653 process_keyval_opt(options, "bookmarksopenlevel");
654 if (!bookmarksopenlevel.empty())
655 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
658 string breaklinks = process_keyval_opt(options, "breaklinks");
659 if (breaklinks == "true")
660 h_pdf_breaklinks = "1";
661 else if (breaklinks == "false")
662 h_pdf_breaklinks = "0";
663 string pdfborder = process_keyval_opt(options, "pdfborder");
664 if (pdfborder == "{0 0 0}")
665 h_pdf_pdfborder = "1";
666 else if (pdfborder == "{0 0 1}")
667 h_pdf_pdfborder = "0";
668 string backref = process_keyval_opt(options, "backref");
669 if (!backref.empty())
670 h_pdf_backref = backref;
671 string colorlinks = process_keyval_opt(options, "colorlinks");
672 if (colorlinks == "true")
673 h_pdf_colorlinks = "1";
674 else if (colorlinks == "false")
675 h_pdf_colorlinks = "0";
676 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
677 if (!pdfpagemode.empty())
678 h_pdf_pagemode = pdfpagemode;
679 string pdftitle = process_keyval_opt(options, "pdftitle");
680 if (!pdftitle.empty()) {
681 h_pdf_title = remove_braces(pdftitle);
683 string pdfauthor = process_keyval_opt(options, "pdfauthor");
684 if (!pdfauthor.empty()) {
685 h_pdf_author = remove_braces(pdfauthor);
687 string pdfsubject = process_keyval_opt(options, "pdfsubject");
688 if (!pdfsubject.empty())
689 h_pdf_subject = remove_braces(pdfsubject);
690 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
691 if (!pdfkeywords.empty())
692 h_pdf_keywords = remove_braces(pdfkeywords);
693 if (!options.empty()) {
694 if (!h_pdf_quoted_options.empty())
695 h_pdf_quoted_options += ',';
696 h_pdf_quoted_options += join(options, ",");
702 void Preamble::handle_geometry(vector<string> & options)
704 h_use_geometry = "true";
705 vector<string>::iterator it;
707 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
708 h_paperorientation = "landscape";
712 // keyval version: "paper=letter"
713 string paper = process_keyval_opt(options, "paper");
715 h_papersize = paper + "paper";
716 // alternative version: "letterpaper"
717 handle_opt(options, known_paper_sizes, h_papersize);
718 delete_opt(options, known_paper_sizes);
720 char const * const * margin = known_paper_margins;
721 for (; *margin; ++margin) {
722 string value = process_keyval_opt(options, *margin);
723 if (!value.empty()) {
724 int k = margin - known_paper_margins;
725 string name = known_coded_paper_margins[k];
726 h_margins += '\\' + name + ' ' + value + '\n';
732 void Preamble::handle_package(Parser &p, string const & name,
733 string const & opts, bool in_lyx_preamble,
736 vector<string> options = split_options(opts);
737 add_package(name, options);
739 if (is_known(name, known_xetex_packages)) {
741 h_use_non_tex_fonts = true;
742 registerAutomaticallyLoadedPackage("fontspec");
743 if (h_inputencoding == "auto-legacy")
744 p.setEncoding("UTF-8");
747 // vector of all options for easier parsing and
749 vector<string> allopts = getVectorFromString(opts);
750 // this stores the potential extra options
757 // By default, we use the package name as LyX font name,
758 // so this only needs to be reset if these names differ
760 if (is_known(name, known_roman_font_packages) && name != "noto")
761 h_font_roman[0] = name;
763 if (name == "ccfonts") {
764 for (auto const & opt : allopts) {
770 h_font_roman_opts = xopts;
774 if (name == "lmodern") {
775 for (auto const & opt : allopts) {
781 h_font_roman_opts = xopts;
785 if (name == "fourier") {
786 h_font_roman[0] = "utopia";
787 for (auto const & opt : allopts) {
789 h_font_roman_osf = "true";
792 if (opt == "expert") {
801 h_font_roman_opts = xopts;
805 if (name == "garamondx") {
806 for (auto const & opt : allopts) {
808 h_font_roman_osf = "true";
816 h_font_roman_opts = xopts;
820 if (name == "libertine") {
821 // this automatically invokes biolinum
822 h_font_sans[0] = "biolinum";
823 // as well as libertineMono
824 h_font_typewriter[0] = "libertine-mono";
825 for (auto const & opt : allopts) {
827 h_font_roman_osf = "true";
830 if (opt == "lining") {
831 h_font_roman_osf = "false";
839 h_font_roman_opts = xopts;
843 if (name == "libertineRoman" || name == "libertine-type1") {
844 h_font_roman[0] = "libertine";
845 // NOTE: contrary to libertine.sty, libertineRoman
846 // and libertine-type1 do not automatically invoke
847 // biolinum and libertineMono
848 if (opts == "lining")
849 h_font_roman_osf = "false";
850 else if (opts == "osf")
851 h_font_roman_osf = "true";
854 if (name == "MinionPro") {
855 h_font_roman[0] = "minionpro";
856 h_font_roman_osf = "true";
857 h_font_math[0] = "auto";
858 for (auto const & opt : allopts) {
860 h_font_roman_osf = "false";
863 if (opt == "onlytext") {
864 h_font_math[0] = "default";
872 h_font_roman_opts = xopts;
876 if (name == "mathdesign") {
877 for (auto const & opt : allopts) {
878 if (opt == "charter") {
879 h_font_roman[0] = "md-charter";
882 if (opt == "garamond") {
883 h_font_roman[0] = "md-garamond";
886 if (opt == "utopia") {
887 h_font_roman[0] = "md-utopia";
890 if (opt == "expert") {
892 h_font_roman_osf = "true";
898 else if (name == "mathpazo") {
899 h_font_roman[0] = "palatino";
900 for (auto const & opt : allopts) {
902 h_font_roman_osf = "true";
914 h_font_roman_opts = xopts;
918 else if (name == "mathptmx") {
919 h_font_roman[0] = "times";
920 for (auto const & opt : allopts) {
926 h_font_roman_opts = xopts;
930 if (name == "crimson")
931 h_font_roman[0] = "cochineal";
933 if (name == "cochineal") {
934 for (auto const & opt : allopts) {
935 if (opt == "osf" || opt == "oldstyle") {
936 h_font_roman_osf = "true";
939 if (opt == "proportional" || opt == "p")
946 h_font_roman_opts = xopts;
950 if (name == "CrimsonPro") {
951 h_font_roman_osf = "true";
952 for (auto const & opt : allopts) {
953 if (opt == "lf" || opt == "lining") {
954 h_font_roman_osf = "false";
957 if (opt == "proportional" || opt == "p")
959 if (opt == "medium") {
960 h_font_roman[0] = "CrimsonProMedium";
963 if (opt == "extralight") {
964 h_font_roman[0] = "CrimsonProExtraLight";
967 if (opt == "light") {
968 h_font_roman[0] = "CrimsonProLight";
976 h_font_roman_opts = xopts;
982 // font uses old-style figure
983 h_font_roman_osf = "true";
985 if (name == "paratype") {
986 // in this case all fonts are ParaType
987 h_font_roman[0] = "PTSerif-TLF";
988 h_font_sans[0] = "default";
989 h_font_typewriter[0] = "default";
992 if (name == "PTSerif")
993 h_font_roman[0] = "PTSerif-TLF";
995 if (name == "XCharter") {
996 h_font_roman[0] = "xcharter";
997 for (auto const & opt : allopts) {
999 h_font_roman_osf = "true";
1007 h_font_roman_opts = xopts;
1011 if (name == "plex-serif") {
1012 h_font_roman[0] = "IBMPlexSerif";
1013 for (auto const & opt : allopts) {
1014 if (opt == "thin") {
1015 h_font_roman[0] = "IBMPlexSerifThin";
1018 if (opt == "extralight") {
1019 h_font_roman[0] = "IBMPlexSerifExtraLight";
1022 if (opt == "light") {
1023 h_font_roman[0] = "IBMPlexSerifLight";
1031 h_font_roman_opts = xopts;
1035 if (name == "noto-serif" || name == "noto") {
1042 bool extralight = false;
1044 bool medium = false;
1047 if (name == "noto") {
1052 // Since the options might apply to different shapes,
1053 // we need to parse all options first and then handle them.
1054 for (auto const & opt : allopts) {
1055 if (opt == "regular")
1065 if (opt == "thin") {
1069 if (opt == "extralight") {
1073 if (opt == "light") {
1077 if (opt == "medium") {
1088 if (opt == "nott") {
1096 if (prefixIs(opt, "scaled=")) {
1105 // handle options that might affect different shapes
1106 if (name == "noto-serif" || rm) {
1108 h_font_roman[0] = "NotoSerifThin";
1109 else if (extralight)
1110 h_font_roman[0] = "NotoSerifExtralight";
1112 h_font_roman[0] = "NotoSerifLight";
1114 h_font_roman[0] = "NotoSerifMedium";
1116 h_font_roman[0] = "NotoSerifRegular";
1118 h_font_roman_osf = "true";
1120 h_font_roman_opts = xopts;
1122 if (name == "noto" && sf) {
1124 h_font_sans[0] = "NotoSansThin";
1125 else if (extralight)
1126 h_font_sans[0] = "NotoSansExtralight";
1128 h_font_sans[0] = "NotoSansLight";
1130 h_font_sans[0] = "NotoSansMedium";
1132 h_font_sans[0] = "NotoSansRegular";
1134 h_font_sans_osf = "true";
1136 scale_as_percentage(scl, h_font_sf_scale[0]);
1138 h_font_sans_opts = xopts;
1140 if (name == "noto" && tt) {
1141 h_font_typewriter[0] = "NotoMonoRegular";
1143 h_font_typewriter_osf = "true";
1145 scale_as_percentage(scl, h_font_tt_scale[0]);
1147 h_font_typewriter_opts = xopts;
1151 if (name == "sourceserifpro") {
1152 h_font_roman[0] = "ADOBESourceSerifPro";
1153 for (auto const & opt : allopts) {
1155 h_font_roman_osf = "true";
1163 h_font_roman_opts = xopts;
1171 // By default, we use the package name as LyX font name,
1172 // so this only needs to be reset if these names differ.
1173 // Also, we handle the scaling option here generally.
1175 if (is_known(name, known_sans_font_packages) && name != "noto") {
1176 h_font_sans[0] = name;
1177 if (contains(opts, "scale")) {
1178 vector<string>::const_iterator it = allopts.begin();
1179 for (; it != allopts.end() ; ++it) {
1180 string const opt = *it;
1181 if (prefixIs(opt, "scaled=") || prefixIs(opt, "scale=")) {
1182 if (scale_as_percentage(opt, h_font_sf_scale[0])) {
1191 if (name == "biolinum" || name == "biolinum-type1") {
1192 h_font_sans[0] = "biolinum";
1193 for (auto const & opt : allopts) {
1194 if (prefixIs(opt, "osf")) {
1195 h_font_sans_osf = "true";
1203 h_font_sans_opts = xopts;
1207 if (name == "cantarell") {
1208 for (auto const & opt : allopts) {
1209 if (opt == "defaultsans")
1211 if (prefixIs(opt, "oldstyle")) {
1212 h_font_sans_osf = "true";
1220 h_font_sans_opts = xopts;
1224 if (name == "Chivo") {
1225 for (auto const & opt : allopts) {
1226 if (opt == "thin") {
1227 h_font_roman[0] = "ChivoThin";
1230 if (opt == "light") {
1231 h_font_roman[0] = "ChivoLight";
1234 if (opt == "regular") {
1235 h_font_roman[0] = "Chivo";
1238 if (opt == "medium") {
1239 h_font_roman[0] = "ChivoMedium";
1242 if (prefixIs(opt, "oldstyle")) {
1243 h_font_sans_osf = "true";
1251 h_font_sans_opts = xopts;
1255 if (name == "PTSans") {
1256 h_font_sans[0] = "PTSans-TLF";
1259 if (name == "FiraSans") {
1260 h_font_sans_osf = "true";
1261 for (auto const & opt : allopts) {
1262 if (opt == "book") {
1263 h_font_sans[0] = "FiraSansBook";
1266 if (opt == "thin") {
1269 if (opt == "extralight") {
1270 h_font_sans[0] = "FiraSansExtralight";
1273 if (opt == "light") {
1274 h_font_sans[0] = "FiraSansLight";
1277 if (opt == "ultralight") {
1278 h_font_sans[0] = "FiraSansUltralight";
1281 if (opt == "thin") {
1282 h_font_sans[0] = "FiraSansThin";
1285 if (opt == "lf" || opt == "lining") {
1286 h_font_sans_osf = "false";
1294 h_font_sans_opts = xopts;
1298 if (name == "plex-sans") {
1299 h_font_sans[0] = "IBMPlexSans";
1300 for (auto const & opt : allopts) {
1301 if (opt == "condensed") {
1302 h_font_sans[0] = "IBMPlexSansCondensed";
1305 if (opt == "thin") {
1306 h_font_sans[0] = "IBMPlexSansThin";
1309 if (opt == "extralight") {
1310 h_font_sans[0] = "IBMPlexSansExtraLight";
1313 if (opt == "light") {
1314 h_font_sans[0] = "IBMPlexSansLight";
1322 h_font_sans_opts = xopts;
1326 if (name == "noto-sans") {
1327 h_font_sans[0] = "NotoSansRegular";
1328 for (auto const & opt : allopts) {
1329 if (opt == "regular")
1331 if (opt == "medium") {
1332 h_font_sans[0] = "NotoSansMedium";
1335 if (opt == "thin") {
1336 h_font_sans[0] = "NotoSansThin";
1339 if (opt == "extralight") {
1340 h_font_sans[0] = "NotoSansExtralight";
1343 if (opt == "light") {
1344 h_font_sans[0] = "NotoSansLight";
1348 h_font_sans_osf = "true";
1356 h_font_sans_opts = xopts;
1360 if (name == "sourcesanspro") {
1361 h_font_sans[0] = "ADOBESourceSansPro";
1362 for (auto const & opt : allopts) {
1364 h_font_sans_osf = "true";
1372 h_font_sans_opts = xopts;
1380 // By default, we use the package name as LyX font name,
1381 // so this only needs to be reset if these names differ.
1382 // Also, we handle the scaling option here generally.
1383 // Exceptions: fourier, noto
1384 if (is_known(name, known_typewriter_font_packages) && name != "fourier" && name != "noto") {
1385 h_font_typewriter[0] = name;
1386 if (contains(opts, "scale")) {
1387 vector<string>::const_iterator it = allopts.begin();
1388 for (; it != allopts.end() ; ++it) {
1389 string const opt = *it;
1390 if (prefixIs(opt, "scaled=") || prefixIs(opt, "scale=")) {
1391 if (scale_as_percentage(opt, h_font_tt_scale[0])) {
1400 if (name == "libertineMono" || name == "libertineMono-type1")
1401 h_font_typewriter[0] = "libertine-mono";
1403 if (name == "FiraMono") {
1404 h_font_typewriter_osf = "true";
1405 for (auto const & opt : allopts) {
1406 if (opt == "lf" || opt == "lining") {
1407 h_font_typewriter_osf = "false";
1415 h_font_typewriter_opts = xopts;
1419 if (name == "PTMono")
1420 h_font_typewriter[0] = "PTMono-TLF";
1422 if (name == "plex-mono") {
1423 h_font_typewriter[0] = "IBMPlexMono";
1424 for (auto const & opt : allopts) {
1425 if (opt == "thin") {
1426 h_font_typewriter[0] = "IBMPlexMonoThin";
1429 if (opt == "extralight") {
1430 h_font_typewriter[0] = "IBMPlexMonoExtraLight";
1433 if (opt == "light") {
1434 h_font_typewriter[0] = "IBMPlexMonoLight";
1442 h_font_typewriter_opts = xopts;
1446 if (name == "noto-mono") {
1447 h_font_typewriter[0] = "NotoMonoRegular";
1448 for (auto const & opt : allopts) {
1449 if (opt == "regular")
1456 h_font_typewriter_opts = xopts;
1460 if (name == "sourcecodepro") {
1461 h_font_typewriter[0] = "ADOBESourceCodePro";
1462 for (auto const & opt : allopts) {
1464 h_font_typewriter_osf = "true";
1472 h_font_typewriter_opts = xopts;
1480 // By default, we use the package name as LyX font name,
1481 // so this only needs to be reset if these names differ.
1482 if (is_known(name, known_math_font_packages))
1483 h_font_math[0] = name;
1485 if (name == "newtxmath") {
1487 h_font_math[0] = "newtxmath";
1488 else if (opts == "garamondx")
1489 h_font_math[0] = "garamondx-ntxm";
1490 else if (opts == "libertine")
1491 h_font_math[0] = "libertine-ntxm";
1492 else if (opts == "minion")
1493 h_font_math[0] = "minion-ntxm";
1494 else if (opts == "cochineal")
1495 h_font_math[0] = "cochineal-ntxm";
1498 if (name == "iwona")
1500 h_font_math[0] = "iwona-math";
1502 if (name == "kurier")
1504 h_font_math[0] = "kurier-math";
1506 // after the detection and handling of special cases, we can remove the
1507 // fonts, otherwise they would appear in the preamble, see bug #7856
1508 if (is_known(name, known_roman_font_packages) || is_known(name, known_sans_font_packages)
1509 || is_known(name, known_typewriter_font_packages) || is_known(name, known_math_font_packages))
1511 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
1512 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
1513 name == "esint" || name == "mhchem" || name == "mathdots" ||
1514 name == "mathtools" || name == "stackrel" ||
1515 name == "stmaryrd" || name == "undertilde") {
1516 h_use_packages[name] = "2";
1517 registerAutomaticallyLoadedPackage(name);
1520 else if (name == "babel") {
1521 h_language_package = "default";
1522 // One might think we would have to do nothing if babel is loaded
1523 // without any options to prevent pollution of the preamble with this
1524 // babel call in every roundtrip.
1525 // But the user could have defined babel-specific things afterwards. So
1526 // we need to keep it in the preamble to prevent cases like bug #7861.
1527 if (!opts.empty()) {
1528 // check if more than one option was used - used later for inputenc
1529 if (options.begin() != options.end() - 1)
1530 one_language = false;
1531 // babel takes the last language of the option of its \usepackage
1532 // call as document language. If there is no such language option, the
1533 // last language in the documentclass options is used.
1534 handle_opt(options, known_languages, h_language);
1535 // translate the babel name to a LyX name
1536 h_language = babel2lyx(h_language);
1537 if (h_language == "japanese") {
1538 // For Japanese, the encoding isn't indicated in the source
1539 // file, and there's really not much we can do. We could
1540 // 1) offer a list of possible encodings to choose from, or
1541 // 2) determine the encoding of the file by inspecting it.
1542 // For the time being, we leave the encoding alone so that
1543 // we don't get iconv errors when making a wrong guess, and
1544 // we will output a note at the top of the document
1545 // explaining what to do.
1546 Encoding const * const enc = encodings.fromIconvName(
1547 p.getEncoding(), Encoding::japanese, false);
1549 h_inputencoding = enc->name();
1550 is_nonCJKJapanese = true;
1551 // in this case babel can be removed from the preamble
1552 registerAutomaticallyLoadedPackage("babel");
1554 // If babel is called with options, LyX puts them by default into the
1555 // document class options. This works for most languages, except
1556 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
1557 // perhaps in future others.
1558 // Therefore keep the babel call as it is as the user might have
1560 string const babelcall = "\\usepackage[" + opts + "]{babel}\n";
1561 if (!contains(h_preamble.str(), babelcall))
1562 h_preamble << babelcall;
1564 delete_opt(options, known_languages);
1566 if (!contains(h_preamble.str(), "\\usepackage{babel}\n"))
1567 h_preamble << "\\usepackage{babel}\n";
1568 explicit_babel = true;
1572 else if (name == "polyglossia") {
1573 h_language_package = "default";
1574 h_default_output_format = "pdf4";
1575 h_use_non_tex_fonts = true;
1577 registerAutomaticallyLoadedPackage("xunicode");
1578 if (h_inputencoding == "auto-legacy")
1579 p.setEncoding("UTF-8");
1582 else if (name == "CJK") {
1583 // set the encoding to "auto-legacy" because it might be set to "auto-legacy-plain" by the babel handling
1584 // and this would not be correct for CJK
1585 if (h_inputencoding == "auto-legacy-plain")
1586 h_inputencoding = "auto-legacy";
1587 registerAutomaticallyLoadedPackage("CJK");
1590 else if (name == "CJKutf8") {
1591 h_inputencoding = "utf8-cjk";
1592 p.setEncoding("UTF-8");
1593 registerAutomaticallyLoadedPackage("CJKutf8");
1596 else if (name == "fontenc") {
1597 h_fontencoding = getStringFromVector(options, ",");
1601 else if (name == "inputenc" || name == "luainputenc") {
1602 // h_inputencoding is only set when there is not more than one
1603 // inputenc option because otherwise h_inputencoding must be
1604 // set to "auto-legacy" (the default encodings of the document's languages)
1605 // Therefore check that exactly one option is passed to inputenc.
1606 // It is also only set when there is not more than one babel
1608 if (!options.empty()) {
1609 string const encoding = options.back();
1610 Encoding const * const enc = encodings.fromLaTeXName(
1611 encoding, Encoding::inputenc, true);
1613 if (!detectEncoding)
1614 cerr << "Unknown encoding " << encoding
1615 << ". Ignoring." << std::endl;
1617 if (!enc->unsafe() && options.size() == 1 && one_language == true)
1618 h_inputencoding = enc->name();
1619 p.setEncoding(enc->iconvName());
1625 else if (name == "srcltx") {
1626 h_output_sync = "1";
1627 if (!opts.empty()) {
1628 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
1631 h_output_sync_macro = "\\usepackage{srcltx}";
1634 else if (is_known(name, known_old_language_packages)) {
1635 // known language packages from the times before babel
1636 // if they are found and not also babel, they will be used as
1637 // custom language package
1638 h_language_package = "\\usepackage{" + name + "}";
1641 else if (name == "lyxskak") {
1642 // ignore this and its options
1643 const char * const o[] = {"ps", "mover", 0};
1644 delete_opt(options, o);
1647 else if (is_known(name, known_lyx_packages) && options.empty()) {
1648 if (name == "splitidx")
1649 h_use_indices = "true";
1650 else if (name == "minted")
1651 h_use_minted = true;
1652 else if (name == "refstyle")
1653 h_use_refstyle = true;
1654 else if (name == "prettyref")
1655 h_use_refstyle = false;
1656 if (!in_lyx_preamble) {
1657 h_preamble << package_beg_sep << name
1658 << package_mid_sep << "\\usepackage{"
1660 if (p.next_token().cat() == catNewline ||
1661 (p.next_token().cat() == catSpace &&
1662 p.next_next_token().cat() == catNewline))
1664 h_preamble << package_end_sep;
1668 else if (name == "geometry")
1669 handle_geometry(options);
1671 else if (name == "subfig")
1672 ; // ignore this FIXME: Use the package separator mechanism instead
1674 else if (char const * const * where = is_known(name, known_languages))
1675 h_language = known_coded_languages[where - known_languages];
1677 else if (name == "natbib") {
1678 h_biblio_style = "plainnat";
1679 h_cite_engine = "natbib";
1680 h_cite_engine_type = "authoryear";
1681 vector<string>::iterator it =
1682 find(options.begin(), options.end(), "authoryear");
1683 if (it != options.end())
1686 it = find(options.begin(), options.end(), "numbers");
1687 if (it != options.end()) {
1688 h_cite_engine_type = "numerical";
1692 if (!options.empty())
1693 h_biblio_options = join(options, ",");
1696 else if (name == "biblatex") {
1697 h_biblio_style = "plainnat";
1698 h_cite_engine = "biblatex";
1699 h_cite_engine_type = "authoryear";
1701 vector<string>::iterator it =
1702 find(options.begin(), options.end(), "natbib");
1703 if (it != options.end()) {
1705 h_cite_engine = "biblatex-natbib";
1707 opt = process_keyval_opt(options, "natbib");
1709 h_cite_engine = "biblatex-natbib";
1711 opt = process_keyval_opt(options, "style");
1713 h_biblatex_citestyle = opt;
1714 h_biblatex_bibstyle = opt;
1716 opt = process_keyval_opt(options, "citestyle");
1718 h_biblatex_citestyle = opt;
1719 opt = process_keyval_opt(options, "bibstyle");
1721 h_biblatex_bibstyle = opt;
1723 opt = process_keyval_opt(options, "refsection");
1725 if (opt == "none" || opt == "part"
1726 || opt == "chapter" || opt == "section"
1727 || opt == "subsection")
1730 cerr << "Ignoring unkown refesection value '"
1733 opt = process_keyval_opt(options, "bibencoding");
1736 if (!options.empty()) {
1737 h_biblio_options = join(options, ",");
1742 else if (name == "jurabib") {
1743 h_biblio_style = "jurabib";
1744 h_cite_engine = "jurabib";
1745 h_cite_engine_type = "authoryear";
1746 if (!options.empty())
1747 h_biblio_options = join(options, ",");
1750 else if (name == "bibtopic")
1751 h_use_bibtopic = "true";
1753 else if (name == "chapterbib")
1754 h_multibib = "child";
1756 else if (name == "hyperref")
1757 handle_hyperref(options);
1759 else if (name == "algorithm2e") {
1760 // Load "algorithm2e" module
1761 addModule("algorithm2e");
1762 // Add the package options to the global document options
1763 if (!options.empty()) {
1764 if (h_options.empty())
1765 h_options = join(options, ",");
1767 h_options += ',' + join(options, ",");
1770 else if (name == "microtype") {
1771 //we internally support only microtype without params
1772 if (options.empty())
1773 h_use_microtype = "true";
1775 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1778 else if (name == "lineno") {
1779 h_use_lineno = "true";
1780 if (!options.empty()) {
1781 h_lineno_options = join(options, ",");
1786 else if (!in_lyx_preamble) {
1787 if (options.empty())
1788 h_preamble << "\\usepackage{" << name << '}';
1790 h_preamble << "\\usepackage[" << opts << "]{"
1794 if (p.next_token().cat() == catNewline ||
1795 (p.next_token().cat() == catSpace &&
1796 p.next_next_token().cat() == catNewline))
1800 // We need to do something with the options...
1801 if (!options.empty() && !detectEncoding)
1802 cerr << "Ignoring options '" << join(options, ",")
1803 << "' of package " << name << '.' << endl;
1805 // remove the whitespace
1810 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1813 Token t = p.get_token();
1814 if (t.cat() == catEscape &&
1815 is_known(t.cs(), known_if_commands))
1816 handle_if(p, in_lyx_preamble);
1818 if (!in_lyx_preamble)
1819 h_preamble << t.asInput();
1820 if (t.cat() == catEscape && t.cs() == "fi")
1827 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1829 if (contains(h_float_placement, "H"))
1830 registerAutomaticallyLoadedPackage("float");
1831 if (h_spacing != "single" && h_spacing != "default")
1832 registerAutomaticallyLoadedPackage("setspace");
1833 if (h_use_packages["amsmath"] == "2") {
1834 // amsbsy and amstext are already provided by amsmath
1835 registerAutomaticallyLoadedPackage("amsbsy");
1836 registerAutomaticallyLoadedPackage("amstext");
1839 // output the LyX file settings
1840 // Important: Keep the version formatting in sync with LyX and
1841 // lyx2lyx (bug 7951)
1842 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1843 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1844 << lyx_version_minor << '\n'
1845 << "\\lyxformat " << LYX_FORMAT << '\n'
1846 << "\\begin_document\n"
1847 << "\\begin_header\n"
1848 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1849 << "\\origin " << origin << "\n"
1850 << "\\textclass " << h_textclass << "\n";
1851 string const raw = subdoc ? empty_string() : h_preamble.str();
1853 os << "\\begin_preamble\n";
1854 for (string::size_type i = 0; i < raw.size(); ++i) {
1855 if (raw[i] == package_beg_sep) {
1856 // Here follows some package loading code that
1857 // must be skipped if the package is loaded
1859 string::size_type j = raw.find(package_mid_sep, i);
1860 if (j == string::npos)
1862 string::size_type k = raw.find(package_end_sep, j);
1863 if (k == string::npos)
1865 string const package = raw.substr(i + 1, j - i - 1);
1866 string const replacement = raw.substr(j + 1, k - j - 1);
1867 if (auto_packages.find(package) == auto_packages.end())
1873 os << "\n\\end_preamble\n";
1875 if (!h_options.empty())
1876 os << "\\options " << h_options << "\n";
1877 os << "\\use_default_options " << h_use_default_options << "\n";
1878 if (!used_modules.empty()) {
1879 os << "\\begin_modules\n";
1880 vector<string>::const_iterator const end = used_modules.end();
1881 vector<string>::const_iterator it = used_modules.begin();
1882 for (; it != end; ++it)
1884 os << "\\end_modules\n";
1886 if (!h_includeonlys.empty()) {
1887 os << "\\begin_includeonly\n";
1888 for (auto const & iofile : h_includeonlys)
1889 os << iofile << '\n';
1890 os << "\\end_includeonly\n";
1892 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1893 << "\\language " << h_language << "\n"
1894 << "\\language_package " << h_language_package << "\n"
1895 << "\\inputencoding " << h_inputencoding << "\n"
1896 << "\\fontencoding " << h_fontencoding << "\n"
1897 << "\\font_roman \"" << h_font_roman[0]
1898 << "\" \"" << h_font_roman[1] << "\"\n"
1899 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1900 << "\\font_typewriter \"" << h_font_typewriter[0]
1901 << "\" \"" << h_font_typewriter[1] << "\"\n"
1902 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1903 << "\\font_default_family " << h_font_default_family << "\n"
1904 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1905 << "\\font_sc " << h_font_sc << "\n"
1906 << "\\font_roman_osf " << h_font_roman_osf << "\n"
1907 << "\\font_sans_osf " << h_font_sans_osf << "\n"
1908 << "\\font_typewriter_osf " << h_font_typewriter_osf << "\n";
1909 if (!h_font_roman_opts.empty())
1910 os << "\\font_roman_opts \"" << h_font_roman_opts << "\"" << '\n';
1911 os << "\\font_sf_scale " << h_font_sf_scale[0]
1912 << ' ' << h_font_sf_scale[1] << '\n';
1913 if (!h_font_sans_opts.empty())
1914 os << "\\font_sans_opts \"" << h_font_sans_opts << "\"" << '\n';
1915 os << "\\font_tt_scale " << h_font_tt_scale[0]
1916 << ' ' << h_font_tt_scale[1] << '\n';
1917 if (!h_font_cjk.empty())
1918 os << "\\font_cjk " << h_font_cjk << '\n';
1919 if (!h_font_typewriter_opts.empty())
1920 os << "\\font_typewriter_opts \"" << h_font_typewriter_opts << "\"" << '\n';
1921 os << "\\use_microtype " << h_use_microtype << '\n'
1922 << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
1923 << "\\graphics " << h_graphics << '\n'
1924 << "\\default_output_format " << h_default_output_format << "\n"
1925 << "\\output_sync " << h_output_sync << "\n";
1926 if (h_output_sync == "1")
1927 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1928 os << "\\bibtex_command " << h_bibtex_command << "\n"
1929 << "\\index_command " << h_index_command << "\n";
1930 if (!h_float_placement.empty())
1931 os << "\\float_placement " << h_float_placement << "\n";
1932 os << "\\paperfontsize " << h_paperfontsize << "\n"
1933 << "\\spacing " << h_spacing << "\n"
1934 << "\\use_hyperref " << h_use_hyperref << '\n';
1935 if (h_use_hyperref == "true") {
1936 if (!h_pdf_title.empty())
1937 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1938 if (!h_pdf_author.empty())
1939 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1940 if (!h_pdf_subject.empty())
1941 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1942 if (!h_pdf_keywords.empty())
1943 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1944 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1945 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1946 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1947 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1948 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1949 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1950 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1951 "\\pdf_backref " << h_pdf_backref << "\n"
1952 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1953 if (!h_pdf_pagemode.empty())
1954 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1955 if (!h_pdf_quoted_options.empty())
1956 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1958 os << "\\papersize " << h_papersize << "\n"
1959 << "\\use_geometry " << h_use_geometry << '\n';
1960 for (map<string, string>::const_iterator it = h_use_packages.begin();
1961 it != h_use_packages.end(); ++it)
1962 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1963 os << "\\cite_engine " << h_cite_engine << '\n'
1964 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1965 << "\\biblio_style " << h_biblio_style << "\n"
1966 << "\\use_bibtopic " << h_use_bibtopic << "\n";
1967 if (!h_biblio_options.empty())
1968 os << "\\biblio_options " << h_biblio_options << "\n";
1969 if (!h_biblatex_bibstyle.empty())
1970 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
1971 if (!h_biblatex_citestyle.empty())
1972 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
1973 if (!h_multibib.empty())
1974 os << "\\multibib " << h_multibib << "\n";
1975 os << "\\use_indices " << h_use_indices << "\n"
1976 << "\\paperorientation " << h_paperorientation << '\n'
1977 << "\\suppress_date " << h_suppress_date << '\n'
1978 << "\\justification " << h_justification << '\n'
1979 << "\\use_refstyle " << h_use_refstyle << '\n'
1980 << "\\use_minted " << h_use_minted << '\n'
1981 << "\\use_lineno " << h_use_lineno << '\n';
1982 if (!h_lineno_options.empty())
1983 os << "\\lineno_options " << h_lineno_options << '\n';
1984 if (!h_fontcolor.empty())
1985 os << "\\fontcolor " << h_fontcolor << '\n';
1986 if (!h_notefontcolor.empty())
1987 os << "\\notefontcolor " << h_notefontcolor << '\n';
1988 if (!h_backgroundcolor.empty())
1989 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1990 if (!h_boxbgcolor.empty())
1991 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1992 if (index_number != 0)
1993 for (int i = 0; i < index_number; i++) {
1994 os << "\\index " << h_index[i] << '\n'
1995 << "\\shortcut " << h_shortcut[i] << '\n'
1996 << "\\color " << h_color << '\n'
2000 os << "\\index " << h_index[0] << '\n'
2001 << "\\shortcut " << h_shortcut[0] << '\n'
2002 << "\\color " << h_color << '\n'
2006 << "\\secnumdepth " << h_secnumdepth << "\n"
2007 << "\\tocdepth " << h_tocdepth << "\n"
2008 << "\\paragraph_separation " << h_paragraph_separation << "\n";
2009 if (h_paragraph_separation == "skip")
2010 os << "\\defskip " << h_defskip << "\n";
2012 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
2013 os << "\\is_math_indent " << h_is_mathindent << "\n";
2014 if (!h_mathindentation.empty())
2015 os << "\\math_indentation " << h_mathindentation << "\n";
2016 os << "\\math_numbering_side " << h_math_numbering_side << "\n";
2017 os << "\\quotes_style " << h_quotes_style << "\n"
2018 << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
2019 << "\\papercolumns " << h_papercolumns << "\n"
2020 << "\\papersides " << h_papersides << "\n"
2021 << "\\paperpagestyle " << h_paperpagestyle << "\n";
2022 if (!h_listings_params.empty())
2023 os << "\\listings_params " << h_listings_params << "\n";
2024 os << "\\tracking_changes " << h_tracking_changes << "\n"
2025 << "\\output_changes " << h_output_changes << "\n"
2026 << "\\html_math_output " << h_html_math_output << "\n"
2027 << "\\html_css_as_file " << h_html_css_as_file << "\n"
2028 << "\\html_be_strict " << h_html_be_strict << "\n"
2030 << "\\end_header\n\n"
2031 << "\\begin_body\n";
2036 void Preamble::parse(Parser & p, string const & forceclass,
2037 TeX2LyXDocClass & tc)
2039 // initialize fixed types
2040 special_columns_['D'] = 3;
2041 parse(p, forceclass, false, tc);
2045 void Preamble::parse(Parser & p, string const & forceclass,
2046 bool detectEncoding, TeX2LyXDocClass & tc)
2048 bool is_full_document = false;
2049 bool is_lyx_file = false;
2050 bool in_lyx_preamble = false;
2052 // determine whether this is a full document or a fragment for inclusion
2054 Token const & t = p.get_token();
2056 if (t.cat() == catEscape && t.cs() == "documentclass") {
2057 is_full_document = true;
2063 if (detectEncoding && !is_full_document)
2066 while (is_full_document && p.good()) {
2067 if (detectEncoding && h_inputencoding != "auto-legacy" &&
2068 h_inputencoding != "auto-legacy-plain")
2071 Token const & t = p.get_token();
2074 if (!detectEncoding)
2075 cerr << "t: " << t << '\n';
2081 if (!in_lyx_preamble &&
2082 (t.cat() == catLetter ||
2083 t.cat() == catSuper ||
2084 t.cat() == catSub ||
2085 t.cat() == catOther ||
2086 t.cat() == catMath ||
2087 t.cat() == catActive ||
2088 t.cat() == catBegin ||
2089 t.cat() == catEnd ||
2090 t.cat() == catAlign ||
2091 t.cat() == catParameter)) {
2092 h_preamble << t.cs();
2096 if (!in_lyx_preamble &&
2097 (t.cat() == catSpace || t.cat() == catNewline)) {
2098 h_preamble << t.asInput();
2102 if (t.cat() == catComment) {
2103 static regex const islyxfile("%% LyX .* created this file");
2104 static regex const usercommands("User specified LaTeX commands");
2106 string const comment = t.asInput();
2108 // magically switch encoding default if it looks like XeLaTeX
2109 static string const magicXeLaTeX =
2110 "% This document must be compiled with XeLaTeX ";
2111 if (comment.size() > magicXeLaTeX.size()
2112 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
2113 && h_inputencoding == "auto-legacy") {
2114 if (!detectEncoding)
2115 cerr << "XeLaTeX comment found, switching to UTF8\n";
2116 h_inputencoding = "utf8";
2119 if (regex_search(comment, sub, islyxfile)) {
2121 in_lyx_preamble = true;
2122 } else if (is_lyx_file
2123 && regex_search(comment, sub, usercommands))
2124 in_lyx_preamble = false;
2125 else if (!in_lyx_preamble)
2126 h_preamble << t.asInput();
2130 if (t.cs() == "PassOptionsToPackage") {
2131 string const poptions = p.getArg('{', '}');
2132 string const package = p.verbatim_item();
2133 extra_package_options_.insert(make_pair(package, poptions));
2137 if (t.cs() == "pagestyle") {
2138 h_paperpagestyle = p.verbatim_item();
2142 if (t.cs() == "setdefaultlanguage") {
2144 // We don't yet care about non-language variant options
2145 // because LyX doesn't support this yet, see bug #8214
2147 string langopts = p.getOpt();
2148 // check if the option contains a variant, if yes, extract it
2149 string::size_type pos_var = langopts.find("variant");
2150 string::size_type i = langopts.find(',', pos_var);
2151 string::size_type k = langopts.find('=', pos_var);
2152 if (pos_var != string::npos){
2154 if (i == string::npos)
2155 variant = langopts.substr(k + 1, langopts.length() - k - 2);
2157 variant = langopts.substr(k + 1, i - k - 1);
2158 h_language = variant;
2162 h_language = p.verbatim_item();
2163 //finally translate the poyglossia name to a LyX name
2164 h_language = polyglossia2lyx(h_language);
2168 if (t.cs() == "setotherlanguage") {
2169 // We don't yet care about the option because LyX doesn't
2170 // support this yet, see bug #8214
2171 p.hasOpt() ? p.getOpt() : string();
2176 if (t.cs() == "setmainfont") {
2177 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2178 h_font_roman[1] = p.getArg('{', '}');
2179 if (!fontopts.empty()) {
2180 vector<string> opts = getVectorFromString(fontopts);
2182 for (auto const & opt : opts) {
2183 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2186 if (!fontopts.empty())
2190 h_font_roman_opts = fontopts;
2195 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
2196 // LyX currently only supports the scale option
2197 string scale, fontopts;
2199 fontopts = p.getArg('[', ']');
2200 if (!fontopts.empty()) {
2201 vector<string> opts = getVectorFromString(fontopts);
2203 for (auto const & opt : opts) {
2204 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2207 if (prefixIs(opt, "Scale=")) {
2208 scale_as_percentage(opt, scale);
2211 if (!fontopts.empty())
2217 if (t.cs() == "setsansfont") {
2219 h_font_sf_scale[1] = scale;
2220 h_font_sans[1] = p.getArg('{', '}');
2221 if (!fontopts.empty())
2222 h_font_sans_opts = fontopts;
2225 h_font_tt_scale[1] = scale;
2226 h_font_typewriter[1] = p.getArg('{', '}');
2227 if (!fontopts.empty())
2228 h_font_typewriter_opts = fontopts;
2233 if (t.cs() == "babelfont") {
2235 h_use_non_tex_fonts = true;
2236 h_language_package = "babel";
2237 if (h_inputencoding == "auto-legacy")
2238 p.setEncoding("UTF-8");
2239 // we don't care about the lang option
2240 string const lang = p.hasOpt() ? p.getArg('[', ']') : string();
2241 string const family = p.getArg('{', '}');
2242 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2243 string const fontname = p.getArg('{', '}');
2244 if (lang.empty() && family == "rm") {
2245 h_font_roman[1] = fontname;
2246 if (!fontopts.empty()) {
2247 vector<string> opts = getVectorFromString(fontopts);
2249 for (auto const & opt : opts) {
2250 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2253 if (!fontopts.empty())
2257 h_font_roman_opts = fontopts;
2260 } else if (lang.empty() && (family == "sf" || family == "tt")) {
2262 if (!fontopts.empty()) {
2263 vector<string> opts = getVectorFromString(fontopts);
2265 for (auto const & opt : opts) {
2266 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2269 if (prefixIs(opt, "Scale=")) {
2270 scale_as_percentage(opt, scale);
2273 if (!fontopts.empty())
2278 if (family == "sf") {
2280 h_font_sf_scale[1] = scale;
2281 h_font_sans[1] = fontname;
2282 if (!fontopts.empty())
2283 h_font_sans_opts = fontopts;
2286 h_font_tt_scale[1] = scale;
2287 h_font_typewriter[1] = fontname;
2288 if (!fontopts.empty())
2289 h_font_typewriter_opts = fontopts;
2293 // not rm, sf or tt or lang specific
2294 h_preamble << '\\' << t.cs();
2296 h_preamble << '[' << lang << ']';
2297 h_preamble << '{' << family << '}';
2298 if (!fontopts.empty())
2299 h_preamble << '[' << fontopts << ']';
2300 h_preamble << '{' << fontname << '}' << '\n';
2305 if (t.cs() == "date") {
2306 string argument = p.getArg('{', '}');
2307 if (argument.empty())
2308 h_suppress_date = "true";
2310 h_preamble << t.asInput() << '{' << argument << '}';
2314 if (t.cs() == "color") {
2315 string const space =
2316 (p.hasOpt() ? p.getOpt() : string());
2317 string argument = p.getArg('{', '}');
2318 // check the case that a standard color is used
2319 if (space.empty() && is_known(argument, known_basic_colors)) {
2320 h_fontcolor = rgbcolor2code(argument);
2321 registerAutomaticallyLoadedPackage("color");
2322 } else if (space.empty() && argument == "document_fontcolor")
2323 registerAutomaticallyLoadedPackage("color");
2324 // check the case that LyX's document_fontcolor is defined
2325 // but not used for \color
2327 h_preamble << t.asInput();
2329 h_preamble << space;
2330 h_preamble << '{' << argument << '}';
2331 // the color might already be set because \definecolor
2332 // is parsed before this
2338 if (t.cs() == "pagecolor") {
2339 string argument = p.getArg('{', '}');
2340 // check the case that a standard color is used
2341 if (is_known(argument, known_basic_colors)) {
2342 h_backgroundcolor = rgbcolor2code(argument);
2343 } else if (argument == "page_backgroundcolor")
2344 registerAutomaticallyLoadedPackage("color");
2345 // check the case that LyX's page_backgroundcolor is defined
2346 // but not used for \pagecolor
2348 h_preamble << t.asInput() << '{' << argument << '}';
2349 // the color might already be set because \definecolor
2350 // is parsed before this
2351 h_backgroundcolor = "";
2356 if (t.cs() == "makeatletter") {
2357 // LyX takes care of this
2358 p.setCatcode('@', catLetter);
2362 if (t.cs() == "makeatother") {
2363 // LyX takes care of this
2364 p.setCatcode('@', catOther);
2368 if (t.cs() == "makeindex") {
2369 // LyX will re-add this if a print index command is found
2374 if (t.cs() == "newindex") {
2375 string const indexname = p.getArg('[', ']');
2376 string const shortcut = p.verbatim_item();
2377 if (!indexname.empty())
2378 h_index[index_number] = indexname;
2380 h_index[index_number] = shortcut;
2381 h_shortcut[index_number] = shortcut;
2387 if (t.cs() == "addbibresource") {
2388 string const options = p.getArg('[', ']');
2389 string const arg = removeExtension(p.getArg('{', '}'));
2390 if (!options.empty()) {
2391 // check if the option contains a bibencoding, if yes, extract it
2392 string::size_type pos = options.find("bibencoding=");
2394 if (pos != string::npos) {
2395 string::size_type i = options.find(',', pos);
2396 if (i == string::npos)
2397 encoding = options.substr(pos + 1);
2399 encoding = options.substr(pos, i - pos);
2400 pos = encoding.find('=');
2401 if (pos == string::npos)
2404 encoding = encoding.substr(pos + 1);
2406 if (!encoding.empty())
2407 biblatex_encodings.push_back(normalize_filename(arg) + ' ' + encoding);
2409 biblatex_bibliographies.push_back(arg);
2413 if (t.cs() == "bibliography") {
2414 vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
2415 biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
2419 if (t.cs() == "RS@ifundefined") {
2420 string const name = p.verbatim_item();
2421 string const body1 = p.verbatim_item();
2422 string const body2 = p.verbatim_item();
2423 // only non-lyxspecific stuff
2424 if (in_lyx_preamble &&
2425 (name == "subsecref" || name == "thmref" || name == "lemref"))
2429 ss << '\\' << t.cs();
2430 ss << '{' << name << '}'
2431 << '{' << body1 << '}'
2432 << '{' << body2 << '}';
2433 h_preamble << ss.str();
2438 if (t.cs() == "AtBeginDocument") {
2439 string const name = p.verbatim_item();
2440 // only non-lyxspecific stuff
2441 if (in_lyx_preamble &&
2442 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
2443 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
2444 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
2445 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
2446 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
2447 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
2448 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
2449 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
2450 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
2451 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
2452 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
2453 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
2454 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
2455 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
2456 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
2460 ss << '\\' << t.cs();
2461 ss << '{' << name << '}';
2462 h_preamble << ss.str();
2467 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
2468 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
2469 || t.cs() == "providecommand" || t.cs() == "providecommandx"
2470 || t.cs() == "DeclareRobustCommand"
2471 || t.cs() == "DeclareRobustCommandx"
2472 || t.cs() == "ProvideTextCommandDefault"
2473 || t.cs() == "DeclareMathAccent") {
2475 if (p.next_token().character() == '*') {
2479 string const name = p.verbatim_item();
2480 string const opt1 = p.getFullOpt();
2481 string const opt2 = p.getFullOpt();
2482 string const body = p.verbatim_item();
2483 // store the in_lyx_preamble setting
2484 bool const was_in_lyx_preamble = in_lyx_preamble;
2486 if (name == "\\rmdefault")
2487 if (is_known(body, known_roman_font_packages)) {
2488 h_font_roman[0] = body;
2490 in_lyx_preamble = true;
2492 if (name == "\\sfdefault")
2493 if (is_known(body, known_sans_font_packages)) {
2494 h_font_sans[0] = body;
2496 in_lyx_preamble = true;
2498 if (name == "\\ttdefault")
2499 if (is_known(body, known_typewriter_font_packages)) {
2500 h_font_typewriter[0] = body;
2502 in_lyx_preamble = true;
2504 if (name == "\\familydefault") {
2505 string family = body;
2506 // remove leading "\"
2507 h_font_default_family = family.erase(0,1);
2509 in_lyx_preamble = true;
2512 // remove LyX-specific definitions that are re-added by LyX
2514 // \lyxline is an ancient command that is converted by tex2lyx into
2515 // a \rule therefore remove its preamble code
2516 if (name == "\\lyxdot" || name == "\\lyxarrow"
2517 || name == "\\lyxline" || name == "\\LyX") {
2519 in_lyx_preamble = true;
2522 // Add the command to the known commands
2523 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
2525 // only non-lyxspecific stuff
2526 if (!in_lyx_preamble) {
2528 ss << '\\' << t.cs();
2531 ss << '{' << name << '}' << opt1 << opt2
2532 << '{' << body << "}";
2533 if (prefixIs(t.cs(), "renew") || !contains(h_preamble.str(), ss.str()))
2534 h_preamble << ss.str();
2536 ostream & out = in_preamble ? h_preamble : os;
2537 out << "\\" << t.cs() << "{" << name << "}"
2538 << opts << "{" << body << "}";
2541 // restore the in_lyx_preamble setting
2542 in_lyx_preamble = was_in_lyx_preamble;
2546 if (t.cs() == "documentclass") {
2547 vector<string>::iterator it;
2548 vector<string> opts = split_options(p.getArg('[', ']'));
2549 handle_opt(opts, known_fontsizes, h_paperfontsize);
2550 delete_opt(opts, known_fontsizes);
2551 // delete "pt" at the end
2552 string::size_type i = h_paperfontsize.find("pt");
2553 if (i != string::npos)
2554 h_paperfontsize.erase(i);
2555 // The documentclass options are always parsed before the options
2556 // of the babel call so that a language cannot overwrite the babel
2558 handle_opt(opts, known_languages, h_language);
2559 delete_opt(opts, known_languages);
2562 if ((it = find(opts.begin(), opts.end(), "fleqn"))
2564 h_is_mathindent = "1";
2567 // formula numbering side
2568 if ((it = find(opts.begin(), opts.end(), "leqno"))
2570 h_math_numbering_side = "left";
2573 else if ((it = find(opts.begin(), opts.end(), "reqno"))
2575 h_math_numbering_side = "right";
2579 // paper orientation
2580 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
2581 h_paperorientation = "landscape";
2585 if ((it = find(opts.begin(), opts.end(), "oneside"))
2590 if ((it = find(opts.begin(), opts.end(), "twoside"))
2596 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
2598 h_papercolumns = "1";
2601 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
2603 h_papercolumns = "2";
2607 // some size options are known to any document classes, other sizes
2608 // are handled by the \geometry command of the geometry package
2609 handle_opt(opts, known_class_paper_sizes, h_papersize);
2610 delete_opt(opts, known_class_paper_sizes);
2611 // the remaining options
2612 h_options = join(opts, ",");
2613 // FIXME This does not work for classes that have a
2614 // different name in LyX than in LaTeX
2615 h_textclass = p.getArg('{', '}');
2620 if (t.cs() == "usepackage") {
2621 string const options = p.getArg('[', ']');
2622 string const name = p.getArg('{', '}');
2623 vector<string> vecnames;
2624 split(name, vecnames, ',');
2625 vector<string>::const_iterator it = vecnames.begin();
2626 vector<string>::const_iterator end = vecnames.end();
2627 for (; it != end; ++it)
2628 handle_package(p, trimSpaceAndEol(*it), options,
2629 in_lyx_preamble, detectEncoding);
2633 if (t.cs() == "inputencoding") {
2634 string const encoding = p.getArg('{','}');
2635 Encoding const * const enc = encodings.fromLaTeXName(
2636 encoding, Encoding::inputenc, true);
2638 if (!detectEncoding)
2639 cerr << "Unknown encoding " << encoding
2640 << ". Ignoring." << std::endl;
2643 h_inputencoding = enc->name();
2644 p.setEncoding(enc->iconvName());
2649 if (t.cs() == "newenvironment") {
2650 string const name = p.getArg('{', '}');
2651 string const opt1 = p.getFullOpt();
2652 string const opt2 = p.getFullOpt();
2653 string const beg = p.verbatim_item();
2654 string const end = p.verbatim_item();
2655 if (!in_lyx_preamble) {
2656 h_preamble << "\\newenvironment{" << name
2657 << '}' << opt1 << opt2 << '{'
2658 << beg << "}{" << end << '}';
2660 add_known_environment(name, opt1, !opt2.empty(),
2661 from_utf8(beg), from_utf8(end));
2665 if (t.cs() == "newtheorem") {
2667 if (p.next_token().character() == '*') {
2671 string const name = p.getArg('{', '}');
2672 string const opt1 = p.getFullOpt();
2673 string const opt2 = p.getFullOpt();
2674 string const body = p.verbatim_item();
2675 string const opt3 = p.getFullOpt();
2676 string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
2678 string const complete = cmd + "{" + name + '}' +
2679 opt1 + opt2 + '{' + body + '}' + opt3;
2681 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
2683 if (!in_lyx_preamble)
2684 h_preamble << complete;
2688 if (t.cs() == "def") {
2689 string name = p.get_token().cs();
2690 // In fact, name may be more than the name:
2691 // In the test case of bug 8116
2692 // name == "csname SF@gobble@opt \endcsname".
2693 // Therefore, we need to use asInput() instead of cs().
2694 while (p.next_token().cat() != catBegin)
2695 name += p.get_token().asInput();
2696 if (!in_lyx_preamble)
2697 h_preamble << "\\def\\" << name << '{'
2698 << p.verbatim_item() << "}";
2702 if (t.cs() == "newcolumntype") {
2703 string const name = p.getArg('{', '}');
2704 trimSpaceAndEol(name);
2706 string opts = p.getOpt();
2707 if (!opts.empty()) {
2708 istringstream is(string(opts, 1));
2711 special_columns_[name[0]] = nargs;
2712 h_preamble << "\\newcolumntype{" << name << "}";
2714 h_preamble << "[" << nargs << "]";
2715 h_preamble << "{" << p.verbatim_item() << "}";
2719 if (t.cs() == "setcounter") {
2720 string const name = p.getArg('{', '}');
2721 string const content = p.getArg('{', '}');
2722 if (name == "secnumdepth")
2723 h_secnumdepth = content;
2724 else if (name == "tocdepth")
2725 h_tocdepth = content;
2727 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
2731 if (t.cs() == "setlength") {
2732 string const name = p.verbatim_item();
2733 string const content = p.verbatim_item();
2734 // the paragraphs are only not indented when \parindent is set to zero
2735 if (name == "\\parindent" && content != "") {
2736 if (content[0] == '0')
2737 h_paragraph_separation = "skip";
2739 h_paragraph_indentation = translate_len(content);
2740 } else if (name == "\\parskip") {
2741 if (content == "\\smallskipamount")
2742 h_defskip = "smallskip";
2743 else if (content == "\\medskipamount")
2744 h_defskip = "medskip";
2745 else if (content == "\\bigskipamount")
2746 h_defskip = "bigskip";
2748 h_defskip = translate_len(content);
2749 } else if (name == "\\mathindent") {
2750 h_mathindentation = translate_len(content);
2752 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2756 if (t.cs() == "onehalfspacing") {
2757 h_spacing = "onehalf";
2761 if (t.cs() == "doublespacing") {
2762 h_spacing = "double";
2766 if (t.cs() == "setstretch") {
2767 h_spacing = "other " + p.verbatim_item();
2771 if (t.cs() == "synctex") {
2772 // the scheme is \synctex=value
2773 // where value can only be "1" or "-1"
2774 h_output_sync = "1";
2775 // there can be any character behind the value (e.g. a linebreak or a '\'
2776 // therefore we extract it char by char
2778 string value = p.get_token().asInput();
2780 value += p.get_token().asInput();
2781 h_output_sync_macro = "\\synctex=" + value;
2785 if (t.cs() == "begin") {
2786 string const name = p.getArg('{', '}');
2787 if (name == "document")
2789 h_preamble << "\\begin{" << name << "}";
2793 if (t.cs() == "geometry") {
2794 vector<string> opts = split_options(p.getArg('{', '}'));
2795 handle_geometry(opts);
2799 if (t.cs() == "definecolor") {
2800 string const color = p.getArg('{', '}');
2801 string const space = p.getArg('{', '}');
2802 string const value = p.getArg('{', '}');
2803 if (color == "document_fontcolor" && space == "rgb") {
2804 RGBColor c(RGBColorFromLaTeX(value));
2805 h_fontcolor = X11hexname(c);
2806 } else if (color == "note_fontcolor" && space == "rgb") {
2807 RGBColor c(RGBColorFromLaTeX(value));
2808 h_notefontcolor = X11hexname(c);
2809 } else if (color == "page_backgroundcolor" && space == "rgb") {
2810 RGBColor c(RGBColorFromLaTeX(value));
2811 h_backgroundcolor = X11hexname(c);
2812 } else if (color == "shadecolor" && space == "rgb") {
2813 RGBColor c(RGBColorFromLaTeX(value));
2814 h_boxbgcolor = X11hexname(c);
2816 h_preamble << "\\definecolor{" << color
2817 << "}{" << space << "}{" << value
2823 if (t.cs() == "bibliographystyle") {
2824 h_biblio_style = p.verbatim_item();
2828 if (t.cs() == "jurabibsetup") {
2829 // FIXME p.getArg('{', '}') is most probably wrong (it
2830 // does not handle nested braces).
2831 // Use p.verbatim_item() instead.
2832 vector<string> jurabibsetup =
2833 split_options(p.getArg('{', '}'));
2834 // add jurabibsetup to the jurabib package options
2835 add_package("jurabib", jurabibsetup);
2836 if (!jurabibsetup.empty()) {
2837 h_preamble << "\\jurabibsetup{"
2838 << join(jurabibsetup, ",") << '}';
2843 if (t.cs() == "hypersetup") {
2844 vector<string> hypersetup =
2845 split_options(p.verbatim_item());
2846 // add hypersetup to the hyperref package options
2847 handle_hyperref(hypersetup);
2848 if (!hypersetup.empty()) {
2849 h_preamble << "\\hypersetup{"
2850 << join(hypersetup, ",") << '}';
2855 if (t.cs() == "includeonly") {
2856 vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
2857 for (auto & iofile : includeonlys) {
2858 string filename(normalize_filename(iofile));
2859 string const path = getMasterFilePath(true);
2860 // We want to preserve relative/absolute filenames,
2861 // therefore path is only used for testing
2862 if (!makeAbsPath(filename, path).exists()) {
2863 // The file extension is probably missing.
2864 // Now try to find it out.
2865 string const tex_name =
2866 find_file(filename, path,
2867 known_tex_extensions);
2868 if (!tex_name.empty())
2869 filename = tex_name;
2872 if (makeAbsPath(filename, path).exists())
2873 fix_child_filename(filename);
2875 cerr << "Warning: Could not find included file '"
2876 << filename << "'." << endl;
2877 outname = changeExtension(filename, "lyx");
2878 h_includeonlys.push_back(outname);
2883 if (is_known(t.cs(), known_if_3arg_commands)) {
2884 // prevent misparsing of \usepackage if it is used
2885 // as an argument (see e.g. our own output of
2886 // \@ifundefined above)
2887 string const arg1 = p.verbatim_item();
2888 string const arg2 = p.verbatim_item();
2889 string const arg3 = p.verbatim_item();
2890 // test case \@ifundefined{date}{}{\date{}}
2891 if (t.cs() == "@ifundefined" && arg1 == "date" &&
2892 arg2.empty() && arg3 == "\\date{}") {
2893 h_suppress_date = "true";
2894 // older tex2lyx versions did output
2895 // \@ifundefined{definecolor}{\usepackage{color}}{}
2896 } else if (t.cs() == "@ifundefined" &&
2897 arg1 == "definecolor" &&
2898 arg2 == "\\usepackage{color}" &&
2900 if (!in_lyx_preamble)
2901 h_preamble << package_beg_sep
2904 << "\\@ifundefined{definecolor}{color}{}"
2907 //\@ifundefined{showcaptionsetup}{}{%
2908 // \PassOptionsToPackage{caption=false}{subfig}}
2909 // that LyX uses for subfloats
2910 } else if (t.cs() == "@ifundefined" &&
2911 arg1 == "showcaptionsetup" && arg2.empty()
2912 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
2914 } else if (!in_lyx_preamble) {
2915 h_preamble << t.asInput()
2916 << '{' << arg1 << '}'
2917 << '{' << arg2 << '}'
2918 << '{' << arg3 << '}';
2923 if (is_known(t.cs(), known_if_commands)) {
2924 // must not parse anything in conditional code, since
2925 // LyX would output the parsed contents unconditionally
2926 if (!in_lyx_preamble)
2927 h_preamble << t.asInput();
2928 handle_if(p, in_lyx_preamble);
2932 if (!t.cs().empty() && !in_lyx_preamble) {
2933 h_preamble << '\\' << t.cs();
2938 // remove the whitespace
2941 // Force textclass if the user wanted it
2942 if (!forceclass.empty())
2943 h_textclass = forceclass;
2944 tc.setName(h_textclass);
2945 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2946 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2949 if (h_papersides.empty()) {
2952 h_papersides = ss.str();
2955 // If the CJK package is used we cannot set the document language from
2956 // the babel options. Instead, we guess which language is used most
2957 // and set this one.
2958 default_language = h_language;
2959 if (is_full_document &&
2960 (auto_packages.find("CJK") != auto_packages.end() ||
2961 auto_packages.find("CJKutf8") != auto_packages.end())) {
2963 h_language = guessLanguage(p, default_language);
2965 if (explicit_babel && h_language != default_language) {
2966 // We set the document language to a CJK language,
2967 // but babel is explicitly called in the user preamble
2968 // without options. LyX will not add the default
2969 // language to the document options if it is either
2970 // english, or no text is set as default language.
2971 // Therefore we need to add a language option explicitly.
2972 // FIXME: It would be better to remove all babel calls
2973 // from the user preamble, but this is difficult
2974 // without re-introducing bug 7861.
2975 if (h_options.empty())
2976 h_options = lyx2babel(default_language);
2978 h_options += ',' + lyx2babel(default_language);
2982 // Finally, set the quote style.
2983 // LyX knows the following quotes styles:
2984 // british, cjk, cjkangle, danish, english, french, german,
2985 // polish, russian, swedish and swiss
2986 // conversion list taken from
2987 // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
2988 // (quotes for kazakh are unknown)
2990 if (is_known(h_language, known_british_quotes_languages))
2991 h_quotes_style = "british";
2993 else if (is_known(h_language, known_cjk_quotes_languages))
2994 h_quotes_style = "cjk";
2996 else if (is_known(h_language, known_cjkangle_quotes_languages))
2997 h_quotes_style = "cjkangle";
2999 else if (is_known(h_language, known_danish_quotes_languages))
3000 h_quotes_style = "danish";
3002 else if (is_known(h_language, known_french_quotes_languages))
3003 h_quotes_style = "french";
3005 else if (is_known(h_language, known_german_quotes_languages))
3006 h_quotes_style = "german";
3008 else if (is_known(h_language, known_polish_quotes_languages))
3009 h_quotes_style = "polish";
3011 else if (is_known(h_language, known_russian_quotes_languages))
3012 h_quotes_style = "russian";
3014 else if (is_known(h_language, known_swedish_quotes_languages))
3015 h_quotes_style = "swedish";
3017 else if (is_known(h_language, known_swiss_quotes_languages))
3018 h_quotes_style = "swiss";
3020 else if (is_known(h_language, known_english_quotes_languages))
3021 h_quotes_style = "english";
3025 string Preamble::parseEncoding(Parser & p, string const & forceclass)
3027 TeX2LyXDocClass dummy;
3028 parse(p, forceclass, true, dummy);
3029 if (h_inputencoding != "auto-legacy" && h_inputencoding != "auto-legacy-plain")
3030 return h_inputencoding;
3035 string babel2lyx(string const & language)
3037 char const * const * where = is_known(language, known_languages);
3039 return known_coded_languages[where - known_languages];
3044 string lyx2babel(string const & language)
3046 char const * const * where = is_known(language, known_coded_languages);
3048 return known_languages[where - known_coded_languages];
3053 string Preamble::polyglossia2lyx(string const & language)
3055 char const * const * where = is_known(language, polyglossia_languages);
3057 return coded_polyglossia_languages[where - polyglossia_languages];
3062 string rgbcolor2code(string const & name)
3064 char const * const * where = is_known(name, known_basic_colors);
3066 // "red", "green" etc
3067 return known_basic_color_codes[where - known_basic_colors];
3069 // "255,0,0", "0,255,0" etc
3070 RGBColor c(RGBColorFromLaTeX(name));
3071 return X11hexname(c);