3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
9 * Full author contact details are available in file CREDITS.
20 #include "LayoutFile.h"
23 #include "TextClass.h"
26 #include "support/convert.h"
27 #include "support/FileName.h"
28 #include "support/filetools.h"
29 #include "support/lstrings.h"
31 #include "support/regex.h"
37 using namespace lyx::support;
46 // CJK languages are handled in text.cpp, polyglossia languages are listed
49 * known babel language names (including synonyms)
50 * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
51 * please keep this in sync with known_coded_languages line by line!
53 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
54 "american", "arabic", "arabtex", "australian", "austrian", "azerbaijani", "bahasa", "bahasai",
55 "bahasam", "basque", "belarusian", "bosnian", "brazil", "brazilian", "breton", "british",
56 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
57 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "francais",
58 "french", "frenchb", "frenchle", "frenchpro", "friulan", "galician", "german", "germanb",
59 "georgian", "greek", "hebrew", "hungarian", "icelandic", "indon", "indonesian",
60 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
61 "latvian", "lithuanian", "lowersorbian", "lsorbian", "macedonian", "magyar", "malay", "meyalu",
62 "mongolian", "naustrian", "newzealand", "ngerman", "ngermanb", "norsk", "nswissgerman",
63 "nynorsk", "piedmontese", "polutonikogreek", "polish", "portuges", "portuguese",
64 "romanian", "romansh", "russian", "russianb", "samin", "scottish", "serbian", "serbian-latin",
65 "slovak", "slovene", "spanish", "swedish", "swissgerman", "thai", "turkish", "turkmen",
66 "ukraineb", "ukrainian", "uppersorbian", "UKenglish", "USenglish", "usorbian",
71 * the same as known_languages with .lyx names
72 * please keep this in sync with known_languages line by line!
74 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
75 "american", "arabic_arabi", "arabic_arabtex", "australian", "austrian", "azerbaijani", "bahasa", "bahasa",
76 "bahasam", "basque", "belarusian", "bosnian", "brazilian", "brazilian", "breton", "british",
77 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
78 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "french",
79 "french", "french", "french", "french", "friulan", "galician", "german", "german",
80 "georgian", "greek", "hebrew", "magyar", "icelandic", "bahasa", "bahasa",
81 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
82 "latvian", "lithuanian", "lowersorbian", "lowersorbian", "macedonian", "magyar", "bahasam", "bahasam",
83 "mongolian", "naustrian", "newzealand", "ngerman", "ngerman", "norsk", "german-ch",
84 "nynorsk", "piedmontese", "polutonikogreek", "polish", "portuguese", "portuguese",
85 "romanian", "romansh", "russian", "russian", "samin", "scottish", "serbian", "serbian-latin",
86 "slovak", "slovene", "spanish", "swedish", "german-ch-old", "thai", "turkish", "turkmen",
87 "ukrainian", "ukrainian", "uppersorbian", "english", "english", "uppersorbian",
88 "vietnamese", "welsh",
91 /// languages with british quotes (.lyx names)
92 const char * const known_british_quotes_languages[] = {"british", "welsh", 0};
94 /// languages with cjk quotes (.lyx names)
95 const char * const known_cjk_quotes_languages[] = {"chinese-traditional",
96 "japanese", "japanese-cjk", 0};
98 /// languages with cjk-angle quotes (.lyx names)
99 const char * const known_cjkangle_quotes_languages[] = {"korean", 0};
101 /// languages with danish quotes (.lyx names)
102 const char * const known_danish_quotes_languages[] = {"danish", 0};
104 /// languages with english quotes (.lyx names)
105 const char * const known_english_quotes_languages[] = {"american", "australian",
106 "bahasa", "bahasam", "bengali", "brazilian", "canadian", "chinese-simplified", "english",
107 "esperanto", "farsi", "interlingua", "irish", "newzealand", "scottish",
108 "thai", "turkish", "vietnamese", 0};
110 /// languages with french quotes (.lyx names)
111 const char * const known_french_quotes_languages[] = {"ancientgreek",
112 "arabic_arabi", "arabic_arabtex", "asturian", "belarusian", "breton",
113 "canadien", "catalan", "french", "friulan", "galician", "italian", "occitan",
114 "piedmontese", "portuguese", "spanish", "spanish-mexico", 0};
116 /// languages with german quotes (.lyx names)
117 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
118 "czech", "estonian", "georgian", "german", "icelandic", "latvian", "lithuanian",
119 "lowersorbian", "macedonian", "naustrian", "ngerman", "romansh", "slovak", "slovene",
122 /// languages with polish quotes (.lyx names)
123 const char * const known_polish_quotes_languages[] = {"afrikaans", "bosnian", "croatian",
124 "dutch", "magyar", "polish", "romanian", "serbian", "serbian-latin", 0};
126 /// languages with russian quotes (.lyx names)
127 const char * const known_russian_quotes_languages[] = {"azerbaijani", "oldrussian",
128 "russian", "ukrainian", 0};
130 /// languages with swedish quotes (.lyx names)
131 const char * const known_swedish_quotes_languages[] = {"finnish", "swedish", 0};
133 /// languages with swiss quotes (.lyx names)
134 const char * const known_swiss_quotes_languages[] = {"albanian",
135 "armenian", "basque", "churchslavonic", "german-ch", "german-ch-old",
136 "norsk", "nynorsk", "turkmen", "ukrainian", "vietnamese", 0};
138 /// known language packages from the times before babel
139 const char * const known_old_language_packages[] = {"french", "frenchle",
140 "frenchpro", "german", "ngerman", "pmfrench", 0};
142 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
144 const char * const known_roman_font_packages[] = { "ae", "beraserif", "bookman",
145 "ccfonts", "chancery", "charter", "cmr", "cochineal", "crimson", "DejaVuSerif", "DejaVuSerifCondensed", "fourier",
146 "garamondx", "libertine", "libertineRoman", "libertine-type1", "lmodern", "mathdesign", "mathpazo",
147 "mathptmx", "MinionPro", "newcent", "noto", "noto-serif", "PTSerif", "tgbonum", "tgchorus",
148 "tgpagella", "tgschola", "tgtermes", "utopia", "xcharter", 0 };
150 const char * const known_sans_font_packages[] = { "avant", "berasans", "biolinum",
151 "biolinum-type1", "cantarell", "Chivo", "cmbr", "cmss", "DejaVuSans", "DejaVuSansCondensed", "FiraSans", "helvet", "iwona",
152 "iwonac", "iwonal", "iwonalc", "kurier", "kurierc", "kurierl", "kurierlc", "lmss", "noto", "noto-sans", "PTSans",
153 "tgadventor", "tgheros", "uop", 0 };
155 const char * const known_typewriter_font_packages[] = { "beramono", "cmtl", "cmtt", "courier", "DejaVuSansMono",
156 "FiraMono", "lmtt", "luximono", "fourier", "libertineMono", "libertineMono-type1", "lmodern",
157 "mathpazo", "mathptmx", "newcent", "noto", "noto-mono", "PTMono", "tgcursor", "txtt", 0 };
159 const char * const known_math_font_packages[] = { "eulervm", "newtxmath", 0};
161 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
162 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
163 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
164 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
165 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
167 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
168 "executivepaper", "legalpaper", "letterpaper", 0};
170 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
171 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
173 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
174 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
177 /// commands that can start an \if...\else...\endif sequence
178 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
179 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
180 "ifsidecap", "ifupgreek", 0};
182 const char * const known_basic_colors[] = {"black", "blue", "brown", "cyan",
183 "darkgray", "gray", "green", "lightgray", "lime", "magenta", "orange", "olive",
184 "pink", "purple", "red", "teal", "violet", "white", "yellow", 0};
186 const char * const known_basic_color_codes[] = {"#000000", "#0000ff", "#964B00", "#00ffff",
187 "#a9a9a9", "#808080", "#00ff00", "#d3d3d3", "#bfff00", "#ff00ff", "#ff7f00", "#808000",
188 "#ffc0cb", "#800080", "#ff0000", "#008080", "#8f00ff", "#ffffff", "#ffff00", 0};
190 /// conditional commands with three arguments like \@ifundefined{}{}{}
191 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
195 * Known file extensions for TeX files as used by \\includeonly
197 char const * const known_tex_extensions[] = {"tex", 0};
199 /// packages that work only in xetex
200 /// polyglossia is handled separately
201 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
202 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
203 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
205 /// packages that are automatically skipped if loaded by LyX
206 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
207 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color",
208 "float", "fontspec", "framed", "graphicx", "hhline", "ifthen", "longtable",
209 "makeidx", "minted", "multirow", "nomencl", "pdfpages", "prettyref", "refstyle",
210 "rotating", "rotfloat", "splitidx", "setspace", "subscript", "tabularx","textcomp", "tipa",
211 "tipx", "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xcolor", "xltabular",
214 // codes used to remove packages that are loaded automatically by LyX.
215 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
216 const char package_beg_sep = '\001';
217 const char package_mid_sep = '\002';
218 const char package_end_sep = '\003';
221 // returns true if at least one of the options in what has been found
222 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
228 // the last language option is the document language (for babel and LyX)
229 // the last size option is the document font size
230 vector<string>::iterator it;
231 vector<string>::iterator position = opts.begin();
232 for (; *what; ++what) {
233 it = find(opts.begin(), opts.end(), *what);
234 if (it != opts.end()) {
235 if (it >= position) {
246 void delete_opt(vector<string> & opts, char const * const * what)
251 // remove found options from the list
252 // do this after handle_opt to avoid potential memory leaks
253 vector<string>::iterator it;
254 for (; *what; ++what) {
255 it = find(opts.begin(), opts.end(), *what);
256 if (it != opts.end())
263 * Split a package options string (keyval format) into a vector.
265 * authorformat=smallcaps,
267 * titleformat=colonsep,
268 * bibformat={tabular,ibidem,numbered}
270 vector<string> split_options(string const & input)
272 vector<string> options;
276 Token const & t = p.get_token();
277 if (t.asInput() == ",") {
278 options.push_back(trimSpaceAndEol(option));
280 } else if (t.asInput() == "=") {
283 if (p.next_token().asInput() == "{")
284 option += '{' + p.getArg('{', '}') + '}';
285 } else if (t.cat() != catSpace && t.cat() != catComment)
286 option += t.asInput();
290 options.push_back(trimSpaceAndEol(option));
297 * Retrieve a keyval option "name={value with=sign}" named \p name from
298 * \p options and return the value.
299 * The found option is also removed from \p options.
301 string process_keyval_opt(vector<string> & options, string name)
303 for (size_t i = 0; i < options.size(); ++i) {
304 vector<string> option;
305 split(options[i], option, '=');
306 if (option.size() < 2)
308 if (option[0] == name) {
309 options.erase(options.begin() + i);
310 option.erase(option.begin());
311 return join(option, "=");
317 } // anonymous namespace
321 * known polyglossia language names (including variants)
322 * FIXME: support spelling=old for german variants (german vs. ngerman LyX names etc)
324 const char * const Preamble::polyglossia_languages[] = {
325 "albanian", "american", "amharic", "ancient", "arabic", "armenian", "asturian", "australian",
326 "bahasai", "bahasam", "basque", "bengali", "brazil", "brazilian", "breton", "british", "bulgarian",
327 "catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
328 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
329 "galician", "greek", "monotonic", "hebrew", "hindi",
330 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean",
331 "lao", "latin", "latvian", "lithuanian", "lsorbian", "magyar", "malayalam", "marathi",
332 "austrian", "newzealand", "german", "norsk", "nynorsk", "occitan", "oldrussian",
333 "piedmontese", "polish", "polytonic", "portuges", "romanian", "romansh", "russian",
334 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovenian", "spanish", "swedish", "syriac",
335 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
336 "ukrainian", "urdu", "usorbian", "vietnamese", "welsh", 0};
337 // not yet supported by LyX: "korean", "nko"
340 * the same as polyglossia_languages with .lyx names
341 * please keep this in sync with polyglossia_languages line by line!
343 const char * const Preamble::coded_polyglossia_languages[] = {
344 "albanian", "american", "amharic", "ancientgreek", "arabic_arabi", "armenian", "asturian", "australian",
345 "bahasa", "bahasam", "basque", "bengali", "brazilian", "brazilian", "breton", "british", "bulgarian",
346 "catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
347 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
348 "galician", "greek", "greek", "hebrew", "hindi",
349 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean",
350 "lao", "latin", "latvian", "lithuanian", "lowersorbian", "magyar", "malayalam", "marathi",
351 "naustrian","newzealand", "ngerman", "norsk", "nynorsk", "occitan", "oldrussian",
352 "piedmontese", "polish", "polutonikogreek", "portuges", "romanian", "romansh", "russian",
353 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovene", "spanish", "swedish", "syriac",
354 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
355 "ukrainian", "urdu", "uppersorbian", "vietnamese", "welsh", 0};
356 // not yet supported by LyX: "korean-polyglossia", "nko"
359 bool Preamble::usePolyglossia() const
361 return h_use_non_tex_fonts && h_language_package == "default";
365 bool Preamble::indentParagraphs() const
367 return h_paragraph_separation == "indent";
371 bool Preamble::isPackageUsed(string const & package) const
373 return used_packages.find(package) != used_packages.end();
377 bool Preamble::isPackageAutoLoaded(string const & package) const
379 return auto_packages.find(package) != auto_packages.end();
383 vector<string> Preamble::getPackageOptions(string const & package) const
385 map<string, vector<string> >::const_iterator it = used_packages.find(package);
386 if (it != used_packages.end())
388 return vector<string>();
392 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
394 auto_packages.insert(package);
398 void Preamble::addModule(string const & module)
400 for (auto const & m : used_modules) {
404 used_modules.push_back(module);
408 void Preamble::suppressDate(bool suppress)
411 h_suppress_date = "true";
413 h_suppress_date = "false";
417 void Preamble::registerAuthor(std::string const & name)
419 Author author(from_utf8(name), empty_docstring());
420 author.setUsed(true);
421 authors_.record(author);
422 h_tracking_changes = "true";
423 h_output_changes = "true";
427 Author const & Preamble::getAuthor(std::string const & name) const
429 Author author(from_utf8(name), empty_docstring());
430 for (AuthorList::Authors::const_iterator it = authors_.begin();
431 it != authors_.end(); ++it)
434 static Author const dummy;
439 int Preamble::getSpecialTableColumnArguments(char c) const
441 map<char, int>::const_iterator it = special_columns_.find(c);
442 if (it == special_columns_.end())
448 void Preamble::add_package(string const & name, vector<string> & options)
450 // every package inherits the global options
451 if (used_packages.find(name) == used_packages.end())
452 used_packages[name] = split_options(h_options);
454 // Insert options passed via PassOptionsToPackage
455 for (auto const & p : extra_package_options_) {
456 if (p.first == name) {
457 vector<string> eo = getVectorFromString(p.second);
458 for (auto const & eoi : eo)
459 options.push_back(eoi);
463 vector<string> & v = used_packages[name];
464 v.insert(v.end(), options.begin(), options.end());
465 if (name == "jurabib") {
466 // Don't output the order argument (see the cite command
467 // handling code in text.cpp).
468 vector<string>::iterator end =
469 remove(options.begin(), options.end(), "natbiborder");
470 end = remove(options.begin(), end, "jurabiborder");
471 options.erase(end, options.end());
478 // Given is a string like "scaled=0.9" or "scale=0.9", return 0.9 * 100
479 bool scale_as_percentage(string const & scale, string & percentage)
481 if (contains(scale, '=')) {
482 string const value = support::split(scale, '=');
483 if (isStrDbl(value)) {
484 percentage = convert<string>(
485 static_cast<int>(100 * convert<double>(value)));
493 string remove_braces(string const & value)
497 if (value[0] == '{' && value[value.length()-1] == '}')
498 return value.substr(1, value.length()-2);
502 } // anonymous namespace
505 Preamble::Preamble() : one_language(true), explicit_babel(false),
506 title_layout_found(false), index_number(0), h_font_cjk_set(false)
510 h_biblio_style = "plain";
511 h_bibtex_command = "default";
512 h_cite_engine = "basic";
513 h_cite_engine_type = "default";
515 h_defskip = "medskip";
516 h_dynamic_quotes = false;
519 h_fontencoding = "default";
520 h_font_roman[0] = "default";
521 h_font_roman[1] = "default";
522 h_font_sans[0] = "default";
523 h_font_sans[1] = "default";
524 h_font_typewriter[0] = "default";
525 h_font_typewriter[1] = "default";
526 h_font_math[0] = "auto";
527 h_font_math[1] = "auto";
528 h_font_default_family = "default";
529 h_use_non_tex_fonts = false;
531 h_font_roman_osf = "false";
532 h_font_sans_osf = "false";
533 h_font_typewriter_osf = "false";
534 h_font_sf_scale[0] = "100";
535 h_font_sf_scale[1] = "100";
536 h_font_tt_scale[0] = "100";
537 h_font_tt_scale[1] = "100";
538 // h_font_roman_opts;
540 // h_font_typewriter_opts;
542 h_is_mathindent = "0";
543 h_math_numbering_side = "default";
544 h_graphics = "default";
545 h_default_output_format = "default";
546 h_html_be_strict = "false";
547 h_html_css_as_file = "0";
548 h_html_math_output = "0";
549 h_index[0] = "Index";
550 h_index_command = "default";
551 h_inputencoding = "auto-legacy";
552 h_justification = "true";
553 h_language = "english";
554 h_language_package = "none";
556 h_maintain_unincluded_children = "false";
560 h_output_changes = "false";
562 //h_output_sync_macro
563 h_papercolumns = "1";
564 h_paperfontsize = "default";
565 h_paperorientation = "portrait";
566 h_paperpagestyle = "default";
568 h_papersize = "default";
569 h_paragraph_indentation = "default";
570 h_paragraph_separation = "indent";
575 h_pdf_bookmarks = "0";
576 h_pdf_bookmarksnumbered = "0";
577 h_pdf_bookmarksopen = "0";
578 h_pdf_bookmarksopenlevel = "1";
579 h_pdf_breaklinks = "0";
580 h_pdf_pdfborder = "0";
581 h_pdf_colorlinks = "0";
582 h_pdf_backref = "section";
583 h_pdf_pdfusetitle = "0";
585 //h_pdf_quoted_options;
586 h_quotes_style = "english";
588 h_shortcut[0] = "idx";
589 h_spacing = "single";
590 h_save_transient_properties = "true";
591 h_suppress_date = "false";
592 h_textclass = "article";
594 h_tracking_changes = "false";
595 h_use_bibtopic = "false";
596 h_use_dash_ligatures = "true";
597 h_use_indices = "false";
598 h_use_geometry = "false";
599 h_use_default_options = "false";
600 h_use_hyperref = "false";
601 h_use_microtype = "false";
602 h_use_lineno = "false";
603 h_use_refstyle = false;
604 h_use_minted = false;
605 h_use_packages["amsmath"] = "1";
606 h_use_packages["amssymb"] = "0";
607 h_use_packages["cancel"] = "0";
608 h_use_packages["esint"] = "1";
609 h_use_packages["mhchem"] = "0";
610 h_use_packages["mathdots"] = "0";
611 h_use_packages["mathtools"] = "0";
612 h_use_packages["stackrel"] = "0";
613 h_use_packages["stmaryrd"] = "0";
614 h_use_packages["undertilde"] = "0";
618 void Preamble::handle_hyperref(vector<string> & options)
620 // FIXME swallow inputencoding changes that might surround the
621 // hyperref setup if it was written by LyX
622 h_use_hyperref = "true";
623 // swallow "unicode=true", since LyX does always write that
624 vector<string>::iterator it =
625 find(options.begin(), options.end(), "unicode=true");
626 if (it != options.end())
628 it = find(options.begin(), options.end(), "pdfusetitle");
629 if (it != options.end()) {
630 h_pdf_pdfusetitle = "1";
633 string bookmarks = process_keyval_opt(options, "bookmarks");
634 if (bookmarks == "true")
635 h_pdf_bookmarks = "1";
636 else if (bookmarks == "false")
637 h_pdf_bookmarks = "0";
638 if (h_pdf_bookmarks == "1") {
639 string bookmarksnumbered =
640 process_keyval_opt(options, "bookmarksnumbered");
641 if (bookmarksnumbered == "true")
642 h_pdf_bookmarksnumbered = "1";
643 else if (bookmarksnumbered == "false")
644 h_pdf_bookmarksnumbered = "0";
645 string bookmarksopen =
646 process_keyval_opt(options, "bookmarksopen");
647 if (bookmarksopen == "true")
648 h_pdf_bookmarksopen = "1";
649 else if (bookmarksopen == "false")
650 h_pdf_bookmarksopen = "0";
651 if (h_pdf_bookmarksopen == "1") {
652 string bookmarksopenlevel =
653 process_keyval_opt(options, "bookmarksopenlevel");
654 if (!bookmarksopenlevel.empty())
655 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
658 string breaklinks = process_keyval_opt(options, "breaklinks");
659 if (breaklinks == "true")
660 h_pdf_breaklinks = "1";
661 else if (breaklinks == "false")
662 h_pdf_breaklinks = "0";
663 string pdfborder = process_keyval_opt(options, "pdfborder");
664 if (pdfborder == "{0 0 0}")
665 h_pdf_pdfborder = "1";
666 else if (pdfborder == "{0 0 1}")
667 h_pdf_pdfborder = "0";
668 string backref = process_keyval_opt(options, "backref");
669 if (!backref.empty())
670 h_pdf_backref = backref;
671 string colorlinks = process_keyval_opt(options, "colorlinks");
672 if (colorlinks == "true")
673 h_pdf_colorlinks = "1";
674 else if (colorlinks == "false")
675 h_pdf_colorlinks = "0";
676 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
677 if (!pdfpagemode.empty())
678 h_pdf_pagemode = pdfpagemode;
679 string pdftitle = process_keyval_opt(options, "pdftitle");
680 if (!pdftitle.empty()) {
681 h_pdf_title = remove_braces(pdftitle);
683 string pdfauthor = process_keyval_opt(options, "pdfauthor");
684 if (!pdfauthor.empty()) {
685 h_pdf_author = remove_braces(pdfauthor);
687 string pdfsubject = process_keyval_opt(options, "pdfsubject");
688 if (!pdfsubject.empty())
689 h_pdf_subject = remove_braces(pdfsubject);
690 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
691 if (!pdfkeywords.empty())
692 h_pdf_keywords = remove_braces(pdfkeywords);
693 if (!options.empty()) {
694 if (!h_pdf_quoted_options.empty())
695 h_pdf_quoted_options += ',';
696 h_pdf_quoted_options += join(options, ",");
702 void Preamble::handle_geometry(vector<string> & options)
704 h_use_geometry = "true";
705 vector<string>::iterator it;
707 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
708 h_paperorientation = "landscape";
712 // keyval version: "paper=letter"
713 string paper = process_keyval_opt(options, "paper");
715 h_papersize = paper + "paper";
716 // alternative version: "letterpaper"
717 handle_opt(options, known_paper_sizes, h_papersize);
718 delete_opt(options, known_paper_sizes);
720 char const * const * margin = known_paper_margins;
721 for (; *margin; ++margin) {
722 string value = process_keyval_opt(options, *margin);
723 if (!value.empty()) {
724 int k = margin - known_paper_margins;
725 string name = known_coded_paper_margins[k];
726 h_margins += '\\' + name + ' ' + value + '\n';
732 void Preamble::handle_package(Parser &p, string const & name,
733 string const & opts, bool in_lyx_preamble,
736 vector<string> options = split_options(opts);
737 add_package(name, options);
739 if (is_known(name, known_xetex_packages)) {
741 h_use_non_tex_fonts = true;
742 registerAutomaticallyLoadedPackage("fontspec");
743 if (h_inputencoding == "auto-legacy")
744 p.setEncoding("UTF-8");
747 // vector of all options for easier parsing and
749 vector<string> allopts = getVectorFromString(opts);
750 // this stores the potential extra options
757 // By default, we use the package name as LyX font name,
758 // so this only needs to be reset if these names differ
759 if (is_known(name, known_roman_font_packages))
760 h_font_roman[0] = name;
762 if (name == "ccfonts") {
763 for (auto const & opt : allopts) {
769 h_font_roman_opts = xopts;
773 if (name == "lmodern") {
774 for (auto const & opt : allopts) {
780 h_font_roman_opts = xopts;
784 if (name == "fourier") {
785 h_font_roman[0] = "utopia";
786 for (auto const & opt : allopts) {
788 h_font_roman_osf = "true";
791 if (opt == "expert") {
800 h_font_roman_opts = xopts;
804 if (name == "garamondx") {
805 for (auto const & opt : allopts) {
807 h_font_roman_osf = "true";
815 h_font_roman_opts = xopts;
819 if (name == "libertine") {
820 // this automatically invokes biolinum
821 h_font_sans[0] = "biolinum";
822 // as well as libertineMono
823 h_font_typewriter[0] = "libertine-mono";
824 for (auto const & opt : allopts) {
826 h_font_roman_osf = "true";
829 if (opt == "lining") {
830 h_font_roman_osf = "false";
838 h_font_roman_opts = xopts;
842 if (name == "libertineRoman" || name == "libertine-type1") {
843 h_font_roman[0] = "libertine";
844 // NOTE: contrary to libertine.sty, libertineRoman
845 // and libertine-type1 do not automatically invoke
846 // biolinum and libertineMono
847 if (opts == "lining")
848 h_font_roman_osf = "false";
849 else if (opts == "osf")
850 h_font_roman_osf = "true";
853 if (name == "MinionPro") {
854 h_font_roman[0] = "minionpro";
855 h_font_roman_osf = "true";
856 h_font_math[0] = "auto";
857 for (auto const & opt : allopts) {
859 h_font_roman_osf = "false";
862 if (opt == "onlytext") {
863 h_font_math[0] = "default";
871 h_font_roman_opts = xopts;
875 if (name == "mathdesign") {
876 for (auto const & opt : allopts) {
877 if (opt == "charter") {
878 h_font_roman[0] = "md-charter";
881 if (opt == "garamond") {
882 h_font_roman[0] = "md-garamond";
885 if (opt == "utopia") {
886 h_font_roman[0] = "md-utopia";
889 if (opt == "expert") {
891 h_font_roman_osf = "true";
897 else if (name == "mathpazo") {
898 h_font_roman[0] = "palatino";
899 for (auto const & opt : allopts) {
901 h_font_roman_osf = "true";
913 h_font_roman_opts = xopts;
917 else if (name == "mathptmx") {
918 h_font_roman[0] = "times";
919 for (auto const & opt : allopts) {
925 h_font_roman_opts = xopts;
929 if (name == "crimson")
930 h_font_roman[0] = "cochineal";
932 if (name == "cochineal") {
933 for (auto const & opt : allopts) {
934 if (opt == "osf" || opt == "oldstyle") {
935 h_font_roman_osf = "true";
938 if (opt == "proportional" || opt == "p")
945 h_font_roman_opts = xopts;
950 // font uses old-style figure
951 h_font_roman_osf = "true";
953 if (name == "noto") {
954 h_font_roman[0] = "NotoSerif-TLF";
955 // noto as typewriter is handled in handling of \ttdefault
956 // special cases are handled in handling of \rmdefault and \sfdefault
957 for (auto const & opt : allopts) {
959 h_font_roman[0] = "NotoSerif-TLF";
963 h_font_sans[0] = "NotoSans-TLF";
967 h_font_roman[0] = "NotoSerif-TLF";
968 h_font_sans[0] = "NotoSans-TLF";
972 h_font_roman_osf = "true";
981 if (name == "paratype") {
982 // in this case all fonts are ParaType
983 h_font_roman[0] = "PTSerif-TLF";
984 h_font_sans[0] = "default";
985 h_font_typewriter[0] = "default";
988 if (name == "PTSerif")
989 h_font_roman[0] = "PTSerif-TLF";
991 if (name == "XCharter") {
992 h_font_roman[0] = "xcharter";
993 for (auto const & opt : allopts) {
995 h_font_roman_osf = "true";
1003 h_font_roman_opts = xopts;
1007 if (name == "plex-serif") {
1008 h_font_roman[0] = "IBMPlexSerif";
1009 for (auto const & opt : allopts) {
1010 if (opt == "thin") {
1011 h_font_roman[0] = "IBMPlexSerifThin";
1014 if (opt == "extralight") {
1015 h_font_roman[0] = "IBMPlexSerifExtraLight";
1018 if (opt == "light") {
1019 h_font_roman[0] = "IBMPlexSerifLight";
1022 if (opt == "semibold") {
1023 h_font_roman[0] = "IBMPlexSerifSemibold";
1031 h_font_roman_opts = xopts;
1035 if (name == "noto-serif") {
1036 h_font_roman[0] = "NotoSerifRegular";
1037 for (auto const & opt : allopts) {
1038 if (opt == "regular")
1041 if (opt == "thin") {
1042 h_font_roman[0] = "NotoSerifThin";
1045 if (opt == "extralight") {
1046 h_font_roman[0] = "NotoSerifExtralight";
1049 if (opt == "light") {
1050 h_font_roman[0] = "NotoSerifLight";
1053 if (opt == "medium") {
1054 h_font_roman[0] = "NotoSerifMedium";
1062 h_font_roman_opts = xopts;
1066 if (name == "sourceserifpro") {
1067 h_font_roman[0] = "ADOBESourceSerifPro";
1068 for (auto const & opt : allopts) {
1070 h_font_roman_osf = "true";
1078 h_font_roman_opts = xopts;
1086 // By default, we use the package name as LyX font name,
1087 // so this only needs to be reset if these names differ.
1088 // Also, we handle the scaling option here generally.
1089 if (is_known(name, known_sans_font_packages)) {
1090 h_font_sans[0] = name;
1091 if (contains(opts, "scale")) {
1092 vector<string>::const_iterator it = allopts.begin();
1093 for (; it != allopts.end() ; ++it) {
1094 string const opt = *it;
1095 if (prefixIs(opt, "scaled=") || prefixIs(opt, "scale=")) {
1096 if (scale_as_percentage(opt, h_font_sf_scale[0])) {
1105 if (name == "biolinum" || name == "biolinum-type1") {
1106 h_font_sans[0] = "biolinum";
1107 for (auto const & opt : allopts) {
1108 if (prefixIs(opt, "osf")) {
1109 h_font_sans_osf = "true";
1117 h_font_sans_opts = xopts;
1121 if (name == "cantarell") {
1122 for (auto const & opt : allopts) {
1123 if (opt == "defaultsans")
1125 if (prefixIs(opt, "oldstyle")) {
1126 h_font_sans_osf = "true";
1134 h_font_sans_opts = xopts;
1138 if (name == "Chivo") {
1139 h_font_roman[0] = "Chivo";
1140 for (auto const & opt : allopts) {
1141 if (opt == "thin") {
1142 h_font_roman[0] = "ChivoThin";
1145 if (opt == "light") {
1146 h_font_roman[0] = "ChivoLight";
1149 if (opt == "regular") {
1150 h_font_roman[0] = "Chivo";
1153 if (opt == "medium") {
1154 h_font_roman[0] = "ChivoMedium";
1157 if (prefixIs(opt, "oldstyle")) {
1158 h_font_sans_osf = "true";
1166 h_font_sans_opts = xopts;
1170 if (name == "PTSans") {
1171 h_font_sans[0] = "PTSans-TLF";
1174 if (name == "FiraSans") {
1175 h_font_sans_osf = "true";
1176 for (auto const & opt : allopts) {
1177 if (opt == "book") {
1178 h_font_sans[0] = "FiraSansBook";
1181 if (opt == "thin") {
1184 if (opt == "extralight") {
1185 h_font_sans[0] = "FiraSansExtralight";
1188 if (opt == "light") {
1189 h_font_sans[0] = "FiraSansLight";
1192 if (opt == "ultralight") {
1193 h_font_sans[0] = "FiraSansUltralight";
1196 if (opt == "thin") {
1197 h_font_sans[0] = "FiraSansThin";
1200 if (opt == "lf" || opt == "lining") {
1201 h_font_sans_osf = "false";
1209 h_font_sans_opts = xopts;
1213 if (name == "plex-sans") {
1214 h_font_sans[0] = "IBMPlexSans";
1215 for (auto const & opt : allopts) {
1216 if (opt == "condensed") {
1217 h_font_sans[0] = "IBMPlexSansCondensed";
1220 if (opt == "thin") {
1221 h_font_sans[0] = "IBMPlexSansThin";
1224 if (opt == "extralight") {
1225 h_font_sans[0] = "IBMPlexSansExtraLight";
1228 if (opt == "light") {
1229 h_font_sans[0] = "IBMPlexSansLight";
1232 if (opt == "semibold") {
1233 h_font_sans[0] = "IBMPlexSansSemibold";
1241 h_font_sans_opts = xopts;
1245 if (name == "noto-sans") {
1246 h_font_sans[0] = "NotoSansRegular";
1247 for (auto const & opt : allopts) {
1248 if (opt == "regular")
1250 if (opt == "medium") {
1251 h_font_sans[0] = "NotoSansMedium";
1254 if (opt == "thin") {
1255 h_font_sans[0] = "NotoSansThin";
1258 if (opt == "extralight") {
1259 h_font_sans[0] = "NotoSansExtralight";
1262 if (opt == "light") {
1263 h_font_sans[0] = "NotoSansLight";
1267 h_font_sans_osf = "true";
1275 h_font_sans_opts = xopts;
1279 if (name == "sourcesanspro") {
1280 h_font_sans[0] = "ADOBESourceSansPro";
1281 for (auto const & opt : allopts) {
1283 h_font_sans_osf = "true";
1291 h_font_sans_opts = xopts;
1299 // By default, we use the package name as LyX font name,
1300 // so this only needs to be reset if these names differ.
1301 // Also, we handle the scaling option here generally.
1302 // Note: fourier can be set as roman font _only_
1303 // fourier as typewriter is handled in handling of \ttdefault
1304 if (is_known(name, known_typewriter_font_packages) && name != "fourier") {
1305 h_font_typewriter[0] = name;
1306 if (contains(opts, "scale")) {
1307 vector<string>::const_iterator it = allopts.begin();
1308 for (; it != allopts.end() ; ++it) {
1309 string const opt = *it;
1310 if (prefixIs(opt, "scaled=") || prefixIs(opt, "scale=")) {
1311 if (scale_as_percentage(opt, h_font_tt_scale[0])) {
1320 if (name == "libertineMono" || name == "libertineMono-type1")
1321 h_font_typewriter[0] = "libertine-mono";
1323 if (name == "FiraMono") {
1324 h_font_typewriter_osf = "true";
1325 for (auto const & opt : allopts) {
1326 if (opt == "lf" || opt == "lining") {
1327 h_font_typewriter_osf = "false";
1335 h_font_typewriter_opts = xopts;
1339 if (name == "PTMono")
1340 h_font_typewriter[0] = "PTMono-TLF";
1342 if (name == "plex-mono") {
1343 h_font_typewriter[0] = "IBMPlexMono";
1344 for (auto const & opt : allopts) {
1345 if (opt == "thin") {
1346 h_font_typewriter[0] = "IBMPlexMonoThin";
1349 if (opt == "extralight") {
1350 h_font_typewriter[0] = "IBMPlexMonoExtraLight";
1353 if (opt == "light") {
1354 h_font_typewriter[0] = "IBMPlexMonoLight";
1357 if (opt == "semibold"){
1358 h_font_typewriter[0] = "IBMPlexMonoSemibold";
1366 h_font_typewriter_opts = xopts;
1370 if (name == "noto-mono") {
1371 h_font_typewriter[0] = "NotoMonoRegular";
1372 for (auto const & opt : allopts) {
1373 if (opt == "regular")
1380 h_font_typewriter_opts = xopts;
1384 if (name == "sourcecodepro") {
1385 h_font_typewriter[0] = "ADOBESourceCodePro";
1386 for (auto const & opt : allopts) {
1388 h_font_typewriter_osf = "true";
1396 h_font_typewriter_opts = xopts;
1404 // By default, we use the package name as LyX font name,
1405 // so this only needs to be reset if these names differ.
1406 if (is_known(name, known_math_font_packages))
1407 h_font_math[0] = name;
1409 if (name == "newtxmath") {
1411 h_font_math[0] = "newtxmath";
1412 else if (opts == "garamondx")
1413 h_font_math[0] = "garamondx-ntxm";
1414 else if (opts == "libertine")
1415 h_font_math[0] = "libertine-ntxm";
1416 else if (opts == "minion")
1417 h_font_math[0] = "minion-ntxm";
1418 else if (opts == "cochineal")
1419 h_font_math[0] = "cochineal-ntxm";
1422 if (name == "iwona")
1424 h_font_math[0] = "iwona-math";
1426 if (name == "kurier")
1428 h_font_math[0] = "kurier-math";
1430 // after the detection and handling of special cases, we can remove the
1431 // fonts, otherwise they would appear in the preamble, see bug #7856
1432 if (is_known(name, known_roman_font_packages) || is_known(name, known_sans_font_packages)
1433 || is_known(name, known_typewriter_font_packages) || is_known(name, known_math_font_packages))
1435 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
1436 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
1437 name == "esint" || name == "mhchem" || name == "mathdots" ||
1438 name == "mathtools" || name == "stackrel" ||
1439 name == "stmaryrd" || name == "undertilde") {
1440 h_use_packages[name] = "2";
1441 registerAutomaticallyLoadedPackage(name);
1444 else if (name == "babel") {
1445 h_language_package = "default";
1446 // One might think we would have to do nothing if babel is loaded
1447 // without any options to prevent pollution of the preamble with this
1448 // babel call in every roundtrip.
1449 // But the user could have defined babel-specific things afterwards. So
1450 // we need to keep it in the preamble to prevent cases like bug #7861.
1451 if (!opts.empty()) {
1452 // check if more than one option was used - used later for inputenc
1453 if (options.begin() != options.end() - 1)
1454 one_language = false;
1455 // babel takes the last language of the option of its \usepackage
1456 // call as document language. If there is no such language option, the
1457 // last language in the documentclass options is used.
1458 handle_opt(options, known_languages, h_language);
1459 // translate the babel name to a LyX name
1460 h_language = babel2lyx(h_language);
1461 if (h_language == "japanese") {
1462 // For Japanese, the encoding isn't indicated in the source
1463 // file, and there's really not much we can do. We could
1464 // 1) offer a list of possible encodings to choose from, or
1465 // 2) determine the encoding of the file by inspecting it.
1466 // For the time being, we leave the encoding alone so that
1467 // we don't get iconv errors when making a wrong guess, and
1468 // we will output a note at the top of the document
1469 // explaining what to do.
1470 Encoding const * const enc = encodings.fromIconvName(
1471 p.getEncoding(), Encoding::japanese, false);
1473 h_inputencoding = enc->name();
1474 is_nonCJKJapanese = true;
1475 // in this case babel can be removed from the preamble
1476 registerAutomaticallyLoadedPackage("babel");
1478 // If babel is called with options, LyX puts them by default into the
1479 // document class options. This works for most languages, except
1480 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
1481 // perhaps in future others.
1482 // Therefore keep the babel call as it is as the user might have
1484 string const babelcall = "\\usepackage[" + opts + "]{babel}\n";
1485 if (!contains(h_preamble.str(), babelcall))
1486 h_preamble << babelcall;
1488 delete_opt(options, known_languages);
1490 if (!contains(h_preamble.str(), "\\usepackage{babel}\n"))
1491 h_preamble << "\\usepackage{babel}\n";
1492 explicit_babel = true;
1496 else if (name == "polyglossia") {
1497 h_language_package = "default";
1498 h_default_output_format = "pdf4";
1499 h_use_non_tex_fonts = true;
1501 registerAutomaticallyLoadedPackage("xunicode");
1502 if (h_inputencoding == "auto-legacy")
1503 p.setEncoding("UTF-8");
1506 else if (name == "CJK") {
1507 // set the encoding to "auto-legacy" because it might be set to "auto-legacy-plain" by the babel handling
1508 // and this would not be correct for CJK
1509 if (h_inputencoding == "auto-legacy-plain")
1510 h_inputencoding = "auto-legacy";
1511 registerAutomaticallyLoadedPackage("CJK");
1514 else if (name == "CJKutf8") {
1515 h_inputencoding = "utf8-cjk";
1516 p.setEncoding("UTF-8");
1517 registerAutomaticallyLoadedPackage("CJKutf8");
1520 else if (name == "fontenc") {
1521 h_fontencoding = getStringFromVector(options, ",");
1525 else if (name == "inputenc" || name == "luainputenc") {
1526 // h_inputencoding is only set when there is not more than one
1527 // inputenc option because otherwise h_inputencoding must be
1528 // set to "auto-legacy" (the default encodings of the document's languages)
1529 // Therefore check that exactly one option is passed to inputenc.
1530 // It is also only set when there is not more than one babel
1532 if (!options.empty()) {
1533 string const encoding = options.back();
1534 Encoding const * const enc = encodings.fromLaTeXName(
1535 encoding, Encoding::inputenc, true);
1537 if (!detectEncoding)
1538 cerr << "Unknown encoding " << encoding
1539 << ". Ignoring." << std::endl;
1541 if (!enc->unsafe() && options.size() == 1 && one_language == true)
1542 h_inputencoding = enc->name();
1543 p.setEncoding(enc->iconvName());
1549 else if (name == "srcltx") {
1550 h_output_sync = "1";
1551 if (!opts.empty()) {
1552 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
1555 h_output_sync_macro = "\\usepackage{srcltx}";
1558 else if (is_known(name, known_old_language_packages)) {
1559 // known language packages from the times before babel
1560 // if they are found and not also babel, they will be used as
1561 // custom language package
1562 h_language_package = "\\usepackage{" + name + "}";
1565 else if (name == "lyxskak") {
1566 // ignore this and its options
1567 const char * const o[] = {"ps", "mover", 0};
1568 delete_opt(options, o);
1571 else if (is_known(name, known_lyx_packages) && options.empty()) {
1572 if (name == "splitidx")
1573 h_use_indices = "true";
1574 else if (name == "minted")
1575 h_use_minted = true;
1576 else if (name == "refstyle")
1577 h_use_refstyle = true;
1578 else if (name == "prettyref")
1579 h_use_refstyle = false;
1580 if (!in_lyx_preamble) {
1581 h_preamble << package_beg_sep << name
1582 << package_mid_sep << "\\usepackage{"
1584 if (p.next_token().cat() == catNewline ||
1585 (p.next_token().cat() == catSpace &&
1586 p.next_next_token().cat() == catNewline))
1588 h_preamble << package_end_sep;
1592 else if (name == "geometry")
1593 handle_geometry(options);
1595 else if (name == "subfig")
1596 ; // ignore this FIXME: Use the package separator mechanism instead
1598 else if (char const * const * where = is_known(name, known_languages))
1599 h_language = known_coded_languages[where - known_languages];
1601 else if (name == "natbib") {
1602 h_biblio_style = "plainnat";
1603 h_cite_engine = "natbib";
1604 h_cite_engine_type = "authoryear";
1605 vector<string>::iterator it =
1606 find(options.begin(), options.end(), "authoryear");
1607 if (it != options.end())
1610 it = find(options.begin(), options.end(), "numbers");
1611 if (it != options.end()) {
1612 h_cite_engine_type = "numerical";
1616 if (!options.empty())
1617 h_biblio_options = join(options, ",");
1620 else if (name == "biblatex") {
1621 h_biblio_style = "plainnat";
1622 h_cite_engine = "biblatex";
1623 h_cite_engine_type = "authoryear";
1625 vector<string>::iterator it =
1626 find(options.begin(), options.end(), "natbib");
1627 if (it != options.end()) {
1629 h_cite_engine = "biblatex-natbib";
1631 opt = process_keyval_opt(options, "natbib");
1633 h_cite_engine = "biblatex-natbib";
1635 opt = process_keyval_opt(options, "style");
1637 h_biblatex_citestyle = opt;
1638 h_biblatex_bibstyle = opt;
1640 opt = process_keyval_opt(options, "citestyle");
1642 h_biblatex_citestyle = opt;
1643 opt = process_keyval_opt(options, "bibstyle");
1645 h_biblatex_bibstyle = opt;
1647 opt = process_keyval_opt(options, "refsection");
1649 if (opt == "none" || opt == "part"
1650 || opt == "chapter" || opt == "section"
1651 || opt == "subsection")
1654 cerr << "Ignoring unkown refesection value '"
1657 opt = process_keyval_opt(options, "bibencoding");
1660 if (!options.empty()) {
1661 h_biblio_options = join(options, ",");
1666 else if (name == "jurabib") {
1667 h_biblio_style = "jurabib";
1668 h_cite_engine = "jurabib";
1669 h_cite_engine_type = "authoryear";
1670 if (!options.empty())
1671 h_biblio_options = join(options, ",");
1674 else if (name == "bibtopic")
1675 h_use_bibtopic = "true";
1677 else if (name == "chapterbib")
1678 h_multibib = "child";
1680 else if (name == "hyperref")
1681 handle_hyperref(options);
1683 else if (name == "algorithm2e") {
1684 // Load "algorithm2e" module
1685 addModule("algorithm2e");
1686 // Add the package options to the global document options
1687 if (!options.empty()) {
1688 if (h_options.empty())
1689 h_options = join(options, ",");
1691 h_options += ',' + join(options, ",");
1694 else if (name == "microtype") {
1695 //we internally support only microtype without params
1696 if (options.empty())
1697 h_use_microtype = "true";
1699 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1702 else if (name == "lineno") {
1703 h_use_lineno = "true";
1704 if (!options.empty()) {
1705 h_lineno_options = join(options, ",");
1710 else if (!in_lyx_preamble) {
1711 if (options.empty())
1712 h_preamble << "\\usepackage{" << name << '}';
1714 h_preamble << "\\usepackage[" << opts << "]{"
1718 if (p.next_token().cat() == catNewline ||
1719 (p.next_token().cat() == catSpace &&
1720 p.next_next_token().cat() == catNewline))
1724 // We need to do something with the options...
1725 if (!options.empty() && !detectEncoding)
1726 cerr << "Ignoring options '" << join(options, ",")
1727 << "' of package " << name << '.' << endl;
1729 // remove the whitespace
1734 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1737 Token t = p.get_token();
1738 if (t.cat() == catEscape &&
1739 is_known(t.cs(), known_if_commands))
1740 handle_if(p, in_lyx_preamble);
1742 if (!in_lyx_preamble)
1743 h_preamble << t.asInput();
1744 if (t.cat() == catEscape && t.cs() == "fi")
1751 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1753 if (contains(h_float_placement, "H"))
1754 registerAutomaticallyLoadedPackage("float");
1755 if (h_spacing != "single" && h_spacing != "default")
1756 registerAutomaticallyLoadedPackage("setspace");
1757 if (h_use_packages["amsmath"] == "2") {
1758 // amsbsy and amstext are already provided by amsmath
1759 registerAutomaticallyLoadedPackage("amsbsy");
1760 registerAutomaticallyLoadedPackage("amstext");
1763 // output the LyX file settings
1764 // Important: Keep the version formatting in sync with LyX and
1765 // lyx2lyx (bug 7951)
1766 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1767 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1768 << lyx_version_minor << '\n'
1769 << "\\lyxformat " << LYX_FORMAT << '\n'
1770 << "\\begin_document\n"
1771 << "\\begin_header\n"
1772 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1773 << "\\origin " << origin << "\n"
1774 << "\\textclass " << h_textclass << "\n";
1775 string const raw = subdoc ? empty_string() : h_preamble.str();
1777 os << "\\begin_preamble\n";
1778 for (string::size_type i = 0; i < raw.size(); ++i) {
1779 if (raw[i] == package_beg_sep) {
1780 // Here follows some package loading code that
1781 // must be skipped if the package is loaded
1783 string::size_type j = raw.find(package_mid_sep, i);
1784 if (j == string::npos)
1786 string::size_type k = raw.find(package_end_sep, j);
1787 if (k == string::npos)
1789 string const package = raw.substr(i + 1, j - i - 1);
1790 string const replacement = raw.substr(j + 1, k - j - 1);
1791 if (auto_packages.find(package) == auto_packages.end())
1797 os << "\n\\end_preamble\n";
1799 if (!h_options.empty())
1800 os << "\\options " << h_options << "\n";
1801 os << "\\use_default_options " << h_use_default_options << "\n";
1802 if (!used_modules.empty()) {
1803 os << "\\begin_modules\n";
1804 vector<string>::const_iterator const end = used_modules.end();
1805 vector<string>::const_iterator it = used_modules.begin();
1806 for (; it != end; ++it)
1808 os << "\\end_modules\n";
1810 if (!h_includeonlys.empty()) {
1811 os << "\\begin_includeonly\n";
1812 for (auto const & iofile : h_includeonlys)
1813 os << iofile << '\n';
1814 os << "\\end_includeonly\n";
1816 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1817 << "\\language " << h_language << "\n"
1818 << "\\language_package " << h_language_package << "\n"
1819 << "\\inputencoding " << h_inputencoding << "\n"
1820 << "\\fontencoding " << h_fontencoding << "\n"
1821 << "\\font_roman \"" << h_font_roman[0]
1822 << "\" \"" << h_font_roman[1] << "\"\n"
1823 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1824 << "\\font_typewriter \"" << h_font_typewriter[0]
1825 << "\" \"" << h_font_typewriter[1] << "\"\n"
1826 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1827 << "\\font_default_family " << h_font_default_family << "\n"
1828 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1829 << "\\font_sc " << h_font_sc << "\n"
1830 << "\\font_roman_osf " << h_font_roman_osf << "\n"
1831 << "\\font_sans_osf " << h_font_sans_osf << "\n"
1832 << "\\font_typewriter_osf " << h_font_typewriter_osf << "\n";
1833 if (!h_font_roman_opts.empty())
1834 os << "\\font_roman_opts \"" << h_font_roman_opts << "\"" << '\n';
1835 os << "\\font_sf_scale " << h_font_sf_scale[0]
1836 << ' ' << h_font_sf_scale[1] << '\n';
1837 if (!h_font_sans_opts.empty())
1838 os << "\\font_sans_opts \"" << h_font_sans_opts << "\"" << '\n';
1839 os << "\\font_tt_scale " << h_font_tt_scale[0]
1840 << ' ' << h_font_tt_scale[1] << '\n';
1841 if (!h_font_cjk.empty())
1842 os << "\\font_cjk " << h_font_cjk << '\n';
1843 if (!h_font_typewriter_opts.empty())
1844 os << "\\font_typewriter_opts \"" << h_font_typewriter_opts << "\"" << '\n';
1845 os << "\\use_microtype " << h_use_microtype << '\n'
1846 << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
1847 << "\\graphics " << h_graphics << '\n'
1848 << "\\default_output_format " << h_default_output_format << "\n"
1849 << "\\output_sync " << h_output_sync << "\n";
1850 if (h_output_sync == "1")
1851 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1852 os << "\\bibtex_command " << h_bibtex_command << "\n"
1853 << "\\index_command " << h_index_command << "\n";
1854 if (!h_float_placement.empty())
1855 os << "\\float_placement " << h_float_placement << "\n";
1856 os << "\\paperfontsize " << h_paperfontsize << "\n"
1857 << "\\spacing " << h_spacing << "\n"
1858 << "\\use_hyperref " << h_use_hyperref << '\n';
1859 if (h_use_hyperref == "true") {
1860 if (!h_pdf_title.empty())
1861 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1862 if (!h_pdf_author.empty())
1863 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1864 if (!h_pdf_subject.empty())
1865 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1866 if (!h_pdf_keywords.empty())
1867 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1868 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1869 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1870 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1871 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1872 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1873 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1874 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1875 "\\pdf_backref " << h_pdf_backref << "\n"
1876 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1877 if (!h_pdf_pagemode.empty())
1878 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1879 if (!h_pdf_quoted_options.empty())
1880 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1882 os << "\\papersize " << h_papersize << "\n"
1883 << "\\use_geometry " << h_use_geometry << '\n';
1884 for (map<string, string>::const_iterator it = h_use_packages.begin();
1885 it != h_use_packages.end(); ++it)
1886 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1887 os << "\\cite_engine " << h_cite_engine << '\n'
1888 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1889 << "\\biblio_style " << h_biblio_style << "\n"
1890 << "\\use_bibtopic " << h_use_bibtopic << "\n";
1891 if (!h_biblio_options.empty())
1892 os << "\\biblio_options " << h_biblio_options << "\n";
1893 if (!h_biblatex_bibstyle.empty())
1894 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
1895 if (!h_biblatex_citestyle.empty())
1896 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
1897 if (!h_multibib.empty())
1898 os << "\\multibib " << h_multibib << "\n";
1899 os << "\\use_indices " << h_use_indices << "\n"
1900 << "\\paperorientation " << h_paperorientation << '\n'
1901 << "\\suppress_date " << h_suppress_date << '\n'
1902 << "\\justification " << h_justification << '\n'
1903 << "\\use_refstyle " << h_use_refstyle << '\n'
1904 << "\\use_minted " << h_use_minted << '\n'
1905 << "\\use_lineno " << h_use_lineno << '\n';
1906 if (!h_lineno_options.empty())
1907 os << "\\lineno_options " << h_lineno_options << '\n';
1908 if (!h_fontcolor.empty())
1909 os << "\\fontcolor " << h_fontcolor << '\n';
1910 if (!h_notefontcolor.empty())
1911 os << "\\notefontcolor " << h_notefontcolor << '\n';
1912 if (!h_backgroundcolor.empty())
1913 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1914 if (!h_boxbgcolor.empty())
1915 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1916 if (index_number != 0)
1917 for (int i = 0; i < index_number; i++) {
1918 os << "\\index " << h_index[i] << '\n'
1919 << "\\shortcut " << h_shortcut[i] << '\n'
1920 << "\\color " << h_color << '\n'
1924 os << "\\index " << h_index[0] << '\n'
1925 << "\\shortcut " << h_shortcut[0] << '\n'
1926 << "\\color " << h_color << '\n'
1930 << "\\secnumdepth " << h_secnumdepth << "\n"
1931 << "\\tocdepth " << h_tocdepth << "\n"
1932 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1933 if (h_paragraph_separation == "skip")
1934 os << "\\defskip " << h_defskip << "\n";
1936 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1937 os << "\\is_math_indent " << h_is_mathindent << "\n";
1938 if (!h_mathindentation.empty())
1939 os << "\\math_indentation " << h_mathindentation << "\n";
1940 os << "\\math_numbering_side " << h_math_numbering_side << "\n";
1941 os << "\\quotes_style " << h_quotes_style << "\n"
1942 << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
1943 << "\\papercolumns " << h_papercolumns << "\n"
1944 << "\\papersides " << h_papersides << "\n"
1945 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1946 if (!h_listings_params.empty())
1947 os << "\\listings_params " << h_listings_params << "\n";
1948 os << "\\tracking_changes " << h_tracking_changes << "\n"
1949 << "\\output_changes " << h_output_changes << "\n"
1950 << "\\html_math_output " << h_html_math_output << "\n"
1951 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1952 << "\\html_be_strict " << h_html_be_strict << "\n"
1954 << "\\end_header\n\n"
1955 << "\\begin_body\n";
1960 void Preamble::parse(Parser & p, string const & forceclass,
1961 TeX2LyXDocClass & tc)
1963 // initialize fixed types
1964 special_columns_['D'] = 3;
1965 parse(p, forceclass, false, tc);
1969 void Preamble::parse(Parser & p, string const & forceclass,
1970 bool detectEncoding, TeX2LyXDocClass & tc)
1972 bool is_full_document = false;
1973 bool is_lyx_file = false;
1974 bool in_lyx_preamble = false;
1976 // determine whether this is a full document or a fragment for inclusion
1978 Token const & t = p.get_token();
1980 if (t.cat() == catEscape && t.cs() == "documentclass") {
1981 is_full_document = true;
1987 if (detectEncoding && !is_full_document)
1990 while (is_full_document && p.good()) {
1991 if (detectEncoding && h_inputencoding != "auto-legacy" &&
1992 h_inputencoding != "auto-legacy-plain")
1995 Token const & t = p.get_token();
1998 if (!detectEncoding)
1999 cerr << "t: " << t << '\n';
2005 if (!in_lyx_preamble &&
2006 (t.cat() == catLetter ||
2007 t.cat() == catSuper ||
2008 t.cat() == catSub ||
2009 t.cat() == catOther ||
2010 t.cat() == catMath ||
2011 t.cat() == catActive ||
2012 t.cat() == catBegin ||
2013 t.cat() == catEnd ||
2014 t.cat() == catAlign ||
2015 t.cat() == catParameter)) {
2016 h_preamble << t.cs();
2020 if (!in_lyx_preamble &&
2021 (t.cat() == catSpace || t.cat() == catNewline)) {
2022 h_preamble << t.asInput();
2026 if (t.cat() == catComment) {
2027 static regex const islyxfile("%% LyX .* created this file");
2028 static regex const usercommands("User specified LaTeX commands");
2030 string const comment = t.asInput();
2032 // magically switch encoding default if it looks like XeLaTeX
2033 static string const magicXeLaTeX =
2034 "% This document must be compiled with XeLaTeX ";
2035 if (comment.size() > magicXeLaTeX.size()
2036 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
2037 && h_inputencoding == "auto-legacy") {
2038 if (!detectEncoding)
2039 cerr << "XeLaTeX comment found, switching to UTF8\n";
2040 h_inputencoding = "utf8";
2043 if (regex_search(comment, sub, islyxfile)) {
2045 in_lyx_preamble = true;
2046 } else if (is_lyx_file
2047 && regex_search(comment, sub, usercommands))
2048 in_lyx_preamble = false;
2049 else if (!in_lyx_preamble)
2050 h_preamble << t.asInput();
2054 if (t.cs() == "PassOptionsToPackage") {
2055 string const poptions = p.getArg('{', '}');
2056 string const package = p.verbatim_item();
2057 extra_package_options_.insert(make_pair(package, poptions));
2061 if (t.cs() == "pagestyle") {
2062 h_paperpagestyle = p.verbatim_item();
2066 if (t.cs() == "setdefaultlanguage") {
2068 // We don't yet care about non-language variant options
2069 // because LyX doesn't support this yet, see bug #8214
2071 string langopts = p.getOpt();
2072 // check if the option contains a variant, if yes, extract it
2073 string::size_type pos_var = langopts.find("variant");
2074 string::size_type i = langopts.find(',', pos_var);
2075 string::size_type k = langopts.find('=', pos_var);
2076 if (pos_var != string::npos){
2078 if (i == string::npos)
2079 variant = langopts.substr(k + 1, langopts.length() - k - 2);
2081 variant = langopts.substr(k + 1, i - k - 1);
2082 h_language = variant;
2086 h_language = p.verbatim_item();
2087 //finally translate the poyglossia name to a LyX name
2088 h_language = polyglossia2lyx(h_language);
2092 if (t.cs() == "setotherlanguage") {
2093 // We don't yet care about the option because LyX doesn't
2094 // support this yet, see bug #8214
2095 p.hasOpt() ? p.getOpt() : string();
2100 if (t.cs() == "setmainfont") {
2101 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2102 h_font_roman[1] = p.getArg('{', '}');
2103 if (!fontopts.empty()) {
2104 vector<string> opts = getVectorFromString(fontopts);
2106 for (auto const & opt : opts) {
2107 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2110 if (!fontopts.empty())
2114 h_font_roman_opts = fontopts;
2119 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
2120 // LyX currently only supports the scale option
2121 string scale, fontopts;
2123 fontopts = p.getArg('[', ']');
2124 if (!fontopts.empty()) {
2125 vector<string> opts = getVectorFromString(fontopts);
2127 for (auto const & opt : opts) {
2128 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2131 if (prefixIs(opt, "Scale=")) {
2132 scale_as_percentage(opt, scale);
2135 if (!fontopts.empty())
2141 if (t.cs() == "setsansfont") {
2143 h_font_sf_scale[1] = scale;
2144 h_font_sans[1] = p.getArg('{', '}');
2145 if (!fontopts.empty())
2146 h_font_sans_opts = fontopts;
2149 h_font_tt_scale[1] = scale;
2150 h_font_typewriter[1] = p.getArg('{', '}');
2151 if (!fontopts.empty())
2152 h_font_typewriter_opts = fontopts;
2157 if (t.cs() == "babelfont") {
2159 h_use_non_tex_fonts = true;
2160 h_language_package = "babel";
2161 if (h_inputencoding == "auto-legacy")
2162 p.setEncoding("UTF-8");
2163 // we don't care about the lang option
2164 string const lang = p.hasOpt() ? p.getArg('[', ']') : string();
2165 string const family = p.getArg('{', '}');
2166 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2167 string const fontname = p.getArg('{', '}');
2168 if (lang.empty() && family == "rm") {
2169 h_font_roman[1] = fontname;
2170 if (!fontopts.empty()) {
2171 vector<string> opts = getVectorFromString(fontopts);
2173 for (auto const & opt : opts) {
2174 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2177 if (!fontopts.empty())
2181 h_font_roman_opts = fontopts;
2184 } else if (lang.empty() && (family == "sf" || family == "tt")) {
2186 if (!fontopts.empty()) {
2187 vector<string> opts = getVectorFromString(fontopts);
2189 for (auto const & opt : opts) {
2190 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2193 if (prefixIs(opt, "Scale=")) {
2194 scale_as_percentage(opt, scale);
2197 if (!fontopts.empty())
2202 if (family == "sf") {
2204 h_font_sf_scale[1] = scale;
2205 h_font_sans[1] = fontname;
2206 if (!fontopts.empty())
2207 h_font_sans_opts = fontopts;
2210 h_font_tt_scale[1] = scale;
2211 h_font_typewriter[1] = fontname;
2212 if (!fontopts.empty())
2213 h_font_typewriter_opts = fontopts;
2217 // not rm, sf or tt or lang specific
2218 h_preamble << '\\' << t.cs();
2220 h_preamble << '[' << lang << ']';
2221 h_preamble << '{' << family << '}';
2222 if (!fontopts.empty())
2223 h_preamble << '[' << fontopts << ']';
2224 h_preamble << '{' << fontname << '}' << '\n';
2229 if (t.cs() == "date") {
2230 string argument = p.getArg('{', '}');
2231 if (argument.empty())
2232 h_suppress_date = "true";
2234 h_preamble << t.asInput() << '{' << argument << '}';
2238 if (t.cs() == "color") {
2239 string const space =
2240 (p.hasOpt() ? p.getOpt() : string());
2241 string argument = p.getArg('{', '}');
2242 // check the case that a standard color is used
2243 if (space.empty() && is_known(argument, known_basic_colors)) {
2244 h_fontcolor = rgbcolor2code(argument);
2245 registerAutomaticallyLoadedPackage("color");
2246 } else if (space.empty() && argument == "document_fontcolor")
2247 registerAutomaticallyLoadedPackage("color");
2248 // check the case that LyX's document_fontcolor is defined
2249 // but not used for \color
2251 h_preamble << t.asInput();
2253 h_preamble << space;
2254 h_preamble << '{' << argument << '}';
2255 // the color might already be set because \definecolor
2256 // is parsed before this
2262 if (t.cs() == "pagecolor") {
2263 string argument = p.getArg('{', '}');
2264 // check the case that a standard color is used
2265 if (is_known(argument, known_basic_colors)) {
2266 h_backgroundcolor = rgbcolor2code(argument);
2267 } else if (argument == "page_backgroundcolor")
2268 registerAutomaticallyLoadedPackage("color");
2269 // check the case that LyX's page_backgroundcolor is defined
2270 // but not used for \pagecolor
2272 h_preamble << t.asInput() << '{' << argument << '}';
2273 // the color might already be set because \definecolor
2274 // is parsed before this
2275 h_backgroundcolor = "";
2280 if (t.cs() == "makeatletter") {
2281 // LyX takes care of this
2282 p.setCatcode('@', catLetter);
2286 if (t.cs() == "makeatother") {
2287 // LyX takes care of this
2288 p.setCatcode('@', catOther);
2292 if (t.cs() == "makeindex") {
2293 // LyX will re-add this if a print index command is found
2298 if (t.cs() == "newindex") {
2299 string const indexname = p.getArg('[', ']');
2300 string const shortcut = p.verbatim_item();
2301 if (!indexname.empty())
2302 h_index[index_number] = indexname;
2304 h_index[index_number] = shortcut;
2305 h_shortcut[index_number] = shortcut;
2311 if (t.cs() == "addbibresource") {
2312 string const options = p.getArg('[', ']');
2313 string const arg = removeExtension(p.getArg('{', '}'));
2314 if (!options.empty()) {
2315 // check if the option contains a bibencoding, if yes, extract it
2316 string::size_type pos = options.find("bibencoding=");
2318 if (pos != string::npos) {
2319 string::size_type i = options.find(',', pos);
2320 if (i == string::npos)
2321 encoding = options.substr(pos + 1);
2323 encoding = options.substr(pos, i - pos);
2324 pos = encoding.find('=');
2325 if (pos == string::npos)
2328 encoding = encoding.substr(pos + 1);
2330 if (!encoding.empty())
2331 biblatex_encodings.push_back(normalize_filename(arg) + ' ' + encoding);
2333 biblatex_bibliographies.push_back(arg);
2337 if (t.cs() == "bibliography") {
2338 vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
2339 biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
2343 if (t.cs() == "RS@ifundefined") {
2344 string const name = p.verbatim_item();
2345 string const body1 = p.verbatim_item();
2346 string const body2 = p.verbatim_item();
2347 // only non-lyxspecific stuff
2348 if (in_lyx_preamble &&
2349 (name == "subsecref" || name == "thmref" || name == "lemref"))
2353 ss << '\\' << t.cs();
2354 ss << '{' << name << '}'
2355 << '{' << body1 << '}'
2356 << '{' << body2 << '}';
2357 h_preamble << ss.str();
2362 if (t.cs() == "AtBeginDocument") {
2363 string const name = p.verbatim_item();
2364 // only non-lyxspecific stuff
2365 if (in_lyx_preamble &&
2366 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
2367 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
2368 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
2369 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
2370 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
2371 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
2372 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
2373 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
2374 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
2375 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
2376 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
2377 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
2378 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
2379 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
2380 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
2384 ss << '\\' << t.cs();
2385 ss << '{' << name << '}';
2386 h_preamble << ss.str();
2391 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
2392 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
2393 || t.cs() == "providecommand" || t.cs() == "providecommandx"
2394 || t.cs() == "DeclareRobustCommand"
2395 || t.cs() == "DeclareRobustCommandx"
2396 || t.cs() == "ProvideTextCommandDefault"
2397 || t.cs() == "DeclareMathAccent") {
2399 if (p.next_token().character() == '*') {
2403 string const name = p.verbatim_item();
2404 string const opt1 = p.getFullOpt();
2405 string const opt2 = p.getFullOpt();
2406 string const body = p.verbatim_item();
2407 // store the in_lyx_preamble setting
2408 bool const was_in_lyx_preamble = in_lyx_preamble;
2410 if (name == "\\rmdefault")
2411 if (is_known(body, known_roman_font_packages)) {
2412 h_font_roman[0] = body;
2414 in_lyx_preamble = true;
2416 if (name == "\\sfdefault")
2417 if (is_known(body, known_sans_font_packages)) {
2418 h_font_sans[0] = body;
2420 in_lyx_preamble = true;
2422 if (name == "\\ttdefault")
2423 if (is_known(body, known_typewriter_font_packages)) {
2424 h_font_typewriter[0] = body;
2426 in_lyx_preamble = true;
2428 if (name == "\\familydefault") {
2429 string family = body;
2430 // remove leading "\"
2431 h_font_default_family = family.erase(0,1);
2433 in_lyx_preamble = true;
2436 // remove LyX-specific definitions that are re-added by LyX
2438 // \lyxline is an ancient command that is converted by tex2lyx into
2439 // a \rule therefore remove its preamble code
2440 if (name == "\\lyxdot" || name == "\\lyxarrow"
2441 || name == "\\lyxline" || name == "\\LyX") {
2443 in_lyx_preamble = true;
2446 // Add the command to the known commands
2447 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
2449 // only non-lyxspecific stuff
2450 if (!in_lyx_preamble) {
2452 ss << '\\' << t.cs();
2455 ss << '{' << name << '}' << opt1 << opt2
2456 << '{' << body << "}";
2457 if (prefixIs(t.cs(), "renew") || !contains(h_preamble.str(), ss.str()))
2458 h_preamble << ss.str();
2460 ostream & out = in_preamble ? h_preamble : os;
2461 out << "\\" << t.cs() << "{" << name << "}"
2462 << opts << "{" << body << "}";
2465 // restore the in_lyx_preamble setting
2466 in_lyx_preamble = was_in_lyx_preamble;
2470 if (t.cs() == "documentclass") {
2471 vector<string>::iterator it;
2472 vector<string> opts = split_options(p.getArg('[', ']'));
2473 handle_opt(opts, known_fontsizes, h_paperfontsize);
2474 delete_opt(opts, known_fontsizes);
2475 // delete "pt" at the end
2476 string::size_type i = h_paperfontsize.find("pt");
2477 if (i != string::npos)
2478 h_paperfontsize.erase(i);
2479 // The documentclass options are always parsed before the options
2480 // of the babel call so that a language cannot overwrite the babel
2482 handle_opt(opts, known_languages, h_language);
2483 delete_opt(opts, known_languages);
2486 if ((it = find(opts.begin(), opts.end(), "fleqn"))
2488 h_is_mathindent = "1";
2491 // formula numbering side
2492 if ((it = find(opts.begin(), opts.end(), "leqno"))
2494 h_math_numbering_side = "left";
2497 else if ((it = find(opts.begin(), opts.end(), "reqno"))
2499 h_math_numbering_side = "right";
2503 // paper orientation
2504 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
2505 h_paperorientation = "landscape";
2509 if ((it = find(opts.begin(), opts.end(), "oneside"))
2514 if ((it = find(opts.begin(), opts.end(), "twoside"))
2520 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
2522 h_papercolumns = "1";
2525 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
2527 h_papercolumns = "2";
2531 // some size options are known to any document classes, other sizes
2532 // are handled by the \geometry command of the geometry package
2533 handle_opt(opts, known_class_paper_sizes, h_papersize);
2534 delete_opt(opts, known_class_paper_sizes);
2535 // the remaining options
2536 h_options = join(opts, ",");
2537 // FIXME This does not work for classes that have a
2538 // different name in LyX than in LaTeX
2539 h_textclass = p.getArg('{', '}');
2544 if (t.cs() == "usepackage") {
2545 string const options = p.getArg('[', ']');
2546 string const name = p.getArg('{', '}');
2547 vector<string> vecnames;
2548 split(name, vecnames, ',');
2549 vector<string>::const_iterator it = vecnames.begin();
2550 vector<string>::const_iterator end = vecnames.end();
2551 for (; it != end; ++it)
2552 handle_package(p, trimSpaceAndEol(*it), options,
2553 in_lyx_preamble, detectEncoding);
2557 if (t.cs() == "inputencoding") {
2558 string const encoding = p.getArg('{','}');
2559 Encoding const * const enc = encodings.fromLaTeXName(
2560 encoding, Encoding::inputenc, true);
2562 if (!detectEncoding)
2563 cerr << "Unknown encoding " << encoding
2564 << ". Ignoring." << std::endl;
2567 h_inputencoding = enc->name();
2568 p.setEncoding(enc->iconvName());
2573 if (t.cs() == "newenvironment") {
2574 string const name = p.getArg('{', '}');
2575 string const opt1 = p.getFullOpt();
2576 string const opt2 = p.getFullOpt();
2577 string const beg = p.verbatim_item();
2578 string const end = p.verbatim_item();
2579 if (!in_lyx_preamble) {
2580 h_preamble << "\\newenvironment{" << name
2581 << '}' << opt1 << opt2 << '{'
2582 << beg << "}{" << end << '}';
2584 add_known_environment(name, opt1, !opt2.empty(),
2585 from_utf8(beg), from_utf8(end));
2589 if (t.cs() == "newtheorem") {
2591 if (p.next_token().character() == '*') {
2595 string const name = p.getArg('{', '}');
2596 string const opt1 = p.getFullOpt();
2597 string const opt2 = p.getFullOpt();
2598 string const body = p.verbatim_item();
2599 string const opt3 = p.getFullOpt();
2600 string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
2602 string const complete = cmd + "{" + name + '}' +
2603 opt1 + opt2 + '{' + body + '}' + opt3;
2605 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
2607 if (!in_lyx_preamble)
2608 h_preamble << complete;
2612 if (t.cs() == "def") {
2613 string name = p.get_token().cs();
2614 // In fact, name may be more than the name:
2615 // In the test case of bug 8116
2616 // name == "csname SF@gobble@opt \endcsname".
2617 // Therefore, we need to use asInput() instead of cs().
2618 while (p.next_token().cat() != catBegin)
2619 name += p.get_token().asInput();
2620 if (!in_lyx_preamble)
2621 h_preamble << "\\def\\" << name << '{'
2622 << p.verbatim_item() << "}";
2626 if (t.cs() == "newcolumntype") {
2627 string const name = p.getArg('{', '}');
2628 trimSpaceAndEol(name);
2630 string opts = p.getOpt();
2631 if (!opts.empty()) {
2632 istringstream is(string(opts, 1));
2635 special_columns_[name[0]] = nargs;
2636 h_preamble << "\\newcolumntype{" << name << "}";
2638 h_preamble << "[" << nargs << "]";
2639 h_preamble << "{" << p.verbatim_item() << "}";
2643 if (t.cs() == "setcounter") {
2644 string const name = p.getArg('{', '}');
2645 string const content = p.getArg('{', '}');
2646 if (name == "secnumdepth")
2647 h_secnumdepth = content;
2648 else if (name == "tocdepth")
2649 h_tocdepth = content;
2651 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
2655 if (t.cs() == "setlength") {
2656 string const name = p.verbatim_item();
2657 string const content = p.verbatim_item();
2658 // the paragraphs are only not indented when \parindent is set to zero
2659 if (name == "\\parindent" && content != "") {
2660 if (content[0] == '0')
2661 h_paragraph_separation = "skip";
2663 h_paragraph_indentation = translate_len(content);
2664 } else if (name == "\\parskip") {
2665 if (content == "\\smallskipamount")
2666 h_defskip = "smallskip";
2667 else if (content == "\\medskipamount")
2668 h_defskip = "medskip";
2669 else if (content == "\\bigskipamount")
2670 h_defskip = "bigskip";
2672 h_defskip = translate_len(content);
2673 } else if (name == "\\mathindent") {
2674 h_mathindentation = translate_len(content);
2676 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2680 if (t.cs() == "onehalfspacing") {
2681 h_spacing = "onehalf";
2685 if (t.cs() == "doublespacing") {
2686 h_spacing = "double";
2690 if (t.cs() == "setstretch") {
2691 h_spacing = "other " + p.verbatim_item();
2695 if (t.cs() == "synctex") {
2696 // the scheme is \synctex=value
2697 // where value can only be "1" or "-1"
2698 h_output_sync = "1";
2699 // there can be any character behind the value (e.g. a linebreak or a '\'
2700 // therefore we extract it char by char
2702 string value = p.get_token().asInput();
2704 value += p.get_token().asInput();
2705 h_output_sync_macro = "\\synctex=" + value;
2709 if (t.cs() == "begin") {
2710 string const name = p.getArg('{', '}');
2711 if (name == "document")
2713 h_preamble << "\\begin{" << name << "}";
2717 if (t.cs() == "geometry") {
2718 vector<string> opts = split_options(p.getArg('{', '}'));
2719 handle_geometry(opts);
2723 if (t.cs() == "definecolor") {
2724 string const color = p.getArg('{', '}');
2725 string const space = p.getArg('{', '}');
2726 string const value = p.getArg('{', '}');
2727 if (color == "document_fontcolor" && space == "rgb") {
2728 RGBColor c(RGBColorFromLaTeX(value));
2729 h_fontcolor = X11hexname(c);
2730 } else if (color == "note_fontcolor" && space == "rgb") {
2731 RGBColor c(RGBColorFromLaTeX(value));
2732 h_notefontcolor = X11hexname(c);
2733 } else if (color == "page_backgroundcolor" && space == "rgb") {
2734 RGBColor c(RGBColorFromLaTeX(value));
2735 h_backgroundcolor = X11hexname(c);
2736 } else if (color == "shadecolor" && space == "rgb") {
2737 RGBColor c(RGBColorFromLaTeX(value));
2738 h_boxbgcolor = X11hexname(c);
2740 h_preamble << "\\definecolor{" << color
2741 << "}{" << space << "}{" << value
2747 if (t.cs() == "bibliographystyle") {
2748 h_biblio_style = p.verbatim_item();
2752 if (t.cs() == "jurabibsetup") {
2753 // FIXME p.getArg('{', '}') is most probably wrong (it
2754 // does not handle nested braces).
2755 // Use p.verbatim_item() instead.
2756 vector<string> jurabibsetup =
2757 split_options(p.getArg('{', '}'));
2758 // add jurabibsetup to the jurabib package options
2759 add_package("jurabib", jurabibsetup);
2760 if (!jurabibsetup.empty()) {
2761 h_preamble << "\\jurabibsetup{"
2762 << join(jurabibsetup, ",") << '}';
2767 if (t.cs() == "hypersetup") {
2768 vector<string> hypersetup =
2769 split_options(p.verbatim_item());
2770 // add hypersetup to the hyperref package options
2771 handle_hyperref(hypersetup);
2772 if (!hypersetup.empty()) {
2773 h_preamble << "\\hypersetup{"
2774 << join(hypersetup, ",") << '}';
2779 if (t.cs() == "includeonly") {
2780 vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
2781 for (auto & iofile : includeonlys) {
2782 string filename(normalize_filename(iofile));
2783 string const path = getMasterFilePath(true);
2784 // We want to preserve relative/absolute filenames,
2785 // therefore path is only used for testing
2786 if (!makeAbsPath(filename, path).exists()) {
2787 // The file extension is probably missing.
2788 // Now try to find it out.
2789 string const tex_name =
2790 find_file(filename, path,
2791 known_tex_extensions);
2792 if (!tex_name.empty())
2793 filename = tex_name;
2796 if (makeAbsPath(filename, path).exists())
2797 fix_child_filename(filename);
2799 cerr << "Warning: Could not find included file '"
2800 << filename << "'." << endl;
2801 outname = changeExtension(filename, "lyx");
2802 h_includeonlys.push_back(outname);
2807 if (is_known(t.cs(), known_if_3arg_commands)) {
2808 // prevent misparsing of \usepackage if it is used
2809 // as an argument (see e.g. our own output of
2810 // \@ifundefined above)
2811 string const arg1 = p.verbatim_item();
2812 string const arg2 = p.verbatim_item();
2813 string const arg3 = p.verbatim_item();
2814 // test case \@ifundefined{date}{}{\date{}}
2815 if (t.cs() == "@ifundefined" && arg1 == "date" &&
2816 arg2.empty() && arg3 == "\\date{}") {
2817 h_suppress_date = "true";
2818 // older tex2lyx versions did output
2819 // \@ifundefined{definecolor}{\usepackage{color}}{}
2820 } else if (t.cs() == "@ifundefined" &&
2821 arg1 == "definecolor" &&
2822 arg2 == "\\usepackage{color}" &&
2824 if (!in_lyx_preamble)
2825 h_preamble << package_beg_sep
2828 << "\\@ifundefined{definecolor}{color}{}"
2831 //\@ifundefined{showcaptionsetup}{}{%
2832 // \PassOptionsToPackage{caption=false}{subfig}}
2833 // that LyX uses for subfloats
2834 } else if (t.cs() == "@ifundefined" &&
2835 arg1 == "showcaptionsetup" && arg2.empty()
2836 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
2838 } else if (!in_lyx_preamble) {
2839 h_preamble << t.asInput()
2840 << '{' << arg1 << '}'
2841 << '{' << arg2 << '}'
2842 << '{' << arg3 << '}';
2847 if (is_known(t.cs(), known_if_commands)) {
2848 // must not parse anything in conditional code, since
2849 // LyX would output the parsed contents unconditionally
2850 if (!in_lyx_preamble)
2851 h_preamble << t.asInput();
2852 handle_if(p, in_lyx_preamble);
2856 if (!t.cs().empty() && !in_lyx_preamble) {
2857 h_preamble << '\\' << t.cs();
2862 // remove the whitespace
2865 // Force textclass if the user wanted it
2866 if (!forceclass.empty())
2867 h_textclass = forceclass;
2868 tc.setName(h_textclass);
2869 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2870 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2873 if (h_papersides.empty()) {
2876 h_papersides = ss.str();
2879 // If the CJK package is used we cannot set the document language from
2880 // the babel options. Instead, we guess which language is used most
2881 // and set this one.
2882 default_language = h_language;
2883 if (is_full_document &&
2884 (auto_packages.find("CJK") != auto_packages.end() ||
2885 auto_packages.find("CJKutf8") != auto_packages.end())) {
2887 h_language = guessLanguage(p, default_language);
2889 if (explicit_babel && h_language != default_language) {
2890 // We set the document language to a CJK language,
2891 // but babel is explicitly called in the user preamble
2892 // without options. LyX will not add the default
2893 // language to the document options if it is either
2894 // english, or no text is set as default language.
2895 // Therefore we need to add a language option explicitly.
2896 // FIXME: It would be better to remove all babel calls
2897 // from the user preamble, but this is difficult
2898 // without re-introducing bug 7861.
2899 if (h_options.empty())
2900 h_options = lyx2babel(default_language);
2902 h_options += ',' + lyx2babel(default_language);
2906 // Finally, set the quote style.
2907 // LyX knows the following quotes styles:
2908 // british, cjk, cjkangle, danish, english, french, german,
2909 // polish, russian, swedish and swiss
2910 // conversion list taken from
2911 // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
2912 // (quotes for kazakh are unknown)
2914 if (is_known(h_language, known_british_quotes_languages))
2915 h_quotes_style = "british";
2917 else if (is_known(h_language, known_cjk_quotes_languages))
2918 h_quotes_style = "cjk";
2920 else if (is_known(h_language, known_cjkangle_quotes_languages))
2921 h_quotes_style = "cjkangle";
2923 else if (is_known(h_language, known_danish_quotes_languages))
2924 h_quotes_style = "danish";
2926 else if (is_known(h_language, known_french_quotes_languages))
2927 h_quotes_style = "french";
2929 else if (is_known(h_language, known_german_quotes_languages))
2930 h_quotes_style = "german";
2932 else if (is_known(h_language, known_polish_quotes_languages))
2933 h_quotes_style = "polish";
2935 else if (is_known(h_language, known_russian_quotes_languages))
2936 h_quotes_style = "russian";
2938 else if (is_known(h_language, known_swedish_quotes_languages))
2939 h_quotes_style = "swedish";
2941 else if (is_known(h_language, known_swiss_quotes_languages))
2942 h_quotes_style = "swiss";
2944 else if (is_known(h_language, known_english_quotes_languages))
2945 h_quotes_style = "english";
2949 string Preamble::parseEncoding(Parser & p, string const & forceclass)
2951 TeX2LyXDocClass dummy;
2952 parse(p, forceclass, true, dummy);
2953 if (h_inputencoding != "auto-legacy" && h_inputencoding != "auto-legacy-plain")
2954 return h_inputencoding;
2959 string babel2lyx(string const & language)
2961 char const * const * where = is_known(language, known_languages);
2963 return known_coded_languages[where - known_languages];
2968 string lyx2babel(string const & language)
2970 char const * const * where = is_known(language, known_coded_languages);
2972 return known_languages[where - known_coded_languages];
2977 string Preamble::polyglossia2lyx(string const & language)
2979 char const * const * where = is_known(language, polyglossia_languages);
2981 return coded_polyglossia_languages[where - polyglossia_languages];
2986 string rgbcolor2code(string const & name)
2988 char const * const * where = is_known(name, known_basic_colors);
2990 // "red", "green" etc
2991 return known_basic_color_codes[where - known_basic_colors];
2993 // "255,0,0", "0,255,0" etc
2994 RGBColor c(RGBColorFromLaTeX(name));
2995 return X11hexname(c);