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
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;
951 // font uses old-style figure
952 h_font_roman_osf = "true";
954 if (name == "paratype") {
955 // in this case all fonts are ParaType
956 h_font_roman[0] = "PTSerif-TLF";
957 h_font_sans[0] = "default";
958 h_font_typewriter[0] = "default";
961 if (name == "PTSerif")
962 h_font_roman[0] = "PTSerif-TLF";
964 if (name == "XCharter") {
965 h_font_roman[0] = "xcharter";
966 for (auto const & opt : allopts) {
968 h_font_roman_osf = "true";
976 h_font_roman_opts = xopts;
980 if (name == "plex-serif") {
981 h_font_roman[0] = "IBMPlexSerif";
982 for (auto const & opt : allopts) {
984 h_font_roman[0] = "IBMPlexSerifThin";
987 if (opt == "extralight") {
988 h_font_roman[0] = "IBMPlexSerifExtraLight";
991 if (opt == "light") {
992 h_font_roman[0] = "IBMPlexSerifLight";
1000 h_font_roman_opts = xopts;
1004 if (name == "noto-serif" || name == "noto") {
1011 bool extralight = false;
1013 bool medium = false;
1016 if (name == "noto") {
1021 // Since the options might apply to different shapes,
1022 // we need to parse all options first and then handle them.
1023 for (auto const & opt : allopts) {
1024 if (opt == "regular")
1034 if (opt == "thin") {
1038 if (opt == "extralight") {
1042 if (opt == "light") {
1046 if (opt == "medium") {
1057 if (opt == "nott") {
1065 if (prefixIs(opt, "scaled=")) {
1074 // handle options that might affect different shapes
1075 if (name == "noto-serif" || rm) {
1077 h_font_roman[0] = "NotoSerifThin";
1078 else if (extralight)
1079 h_font_roman[0] = "NotoSerifExtralight";
1081 h_font_roman[0] = "NotoSerifLight";
1083 h_font_roman[0] = "NotoSerifMedium";
1085 h_font_roman[0] = "NotoSerifRegular";
1087 h_font_roman_osf = "true";
1089 h_font_roman_opts = xopts;
1091 if (name == "noto" && sf) {
1093 h_font_sans[0] = "NotoSansThin";
1094 else if (extralight)
1095 h_font_sans[0] = "NotoSansExtralight";
1097 h_font_sans[0] = "NotoSansLight";
1099 h_font_sans[0] = "NotoSansMedium";
1101 h_font_sans[0] = "NotoSansRegular";
1103 h_font_sans_osf = "true";
1105 scale_as_percentage(scl, h_font_sf_scale[0]);
1107 h_font_sans_opts = xopts;
1109 if (name == "noto" && tt) {
1110 h_font_typewriter[0] = "NotoMonoRegular";
1112 h_font_typewriter_osf = "true";
1114 scale_as_percentage(scl, h_font_tt_scale[0]);
1116 h_font_typewriter_opts = xopts;
1120 if (name == "sourceserifpro") {
1121 h_font_roman[0] = "ADOBESourceSerifPro";
1122 for (auto const & opt : allopts) {
1124 h_font_roman_osf = "true";
1132 h_font_roman_opts = xopts;
1140 // By default, we use the package name as LyX font name,
1141 // so this only needs to be reset if these names differ.
1142 // Also, we handle the scaling option here generally.
1144 if (is_known(name, known_sans_font_packages) && name != "noto") {
1145 h_font_sans[0] = name;
1146 if (contains(opts, "scale")) {
1147 vector<string>::const_iterator it = allopts.begin();
1148 for (; it != allopts.end() ; ++it) {
1149 string const opt = *it;
1150 if (prefixIs(opt, "scaled=") || prefixIs(opt, "scale=")) {
1151 if (scale_as_percentage(opt, h_font_sf_scale[0])) {
1160 if (name == "biolinum" || name == "biolinum-type1") {
1161 h_font_sans[0] = "biolinum";
1162 for (auto const & opt : allopts) {
1163 if (prefixIs(opt, "osf")) {
1164 h_font_sans_osf = "true";
1172 h_font_sans_opts = xopts;
1176 if (name == "cantarell") {
1177 for (auto const & opt : allopts) {
1178 if (opt == "defaultsans")
1180 if (prefixIs(opt, "oldstyle")) {
1181 h_font_sans_osf = "true";
1189 h_font_sans_opts = xopts;
1193 if (name == "Chivo") {
1194 for (auto const & opt : allopts) {
1195 if (opt == "thin") {
1196 h_font_roman[0] = "ChivoThin";
1199 if (opt == "light") {
1200 h_font_roman[0] = "ChivoLight";
1203 if (opt == "regular") {
1204 h_font_roman[0] = "Chivo";
1207 if (opt == "medium") {
1208 h_font_roman[0] = "ChivoMedium";
1211 if (prefixIs(opt, "oldstyle")) {
1212 h_font_sans_osf = "true";
1220 h_font_sans_opts = xopts;
1224 if (name == "PTSans") {
1225 h_font_sans[0] = "PTSans-TLF";
1228 if (name == "FiraSans") {
1229 h_font_sans_osf = "true";
1230 for (auto const & opt : allopts) {
1231 if (opt == "book") {
1232 h_font_sans[0] = "FiraSansBook";
1235 if (opt == "thin") {
1238 if (opt == "extralight") {
1239 h_font_sans[0] = "FiraSansExtralight";
1242 if (opt == "light") {
1243 h_font_sans[0] = "FiraSansLight";
1246 if (opt == "ultralight") {
1247 h_font_sans[0] = "FiraSansUltralight";
1250 if (opt == "thin") {
1251 h_font_sans[0] = "FiraSansThin";
1254 if (opt == "lf" || opt == "lining") {
1255 h_font_sans_osf = "false";
1263 h_font_sans_opts = xopts;
1267 if (name == "plex-sans") {
1268 h_font_sans[0] = "IBMPlexSans";
1269 for (auto const & opt : allopts) {
1270 if (opt == "condensed") {
1271 h_font_sans[0] = "IBMPlexSansCondensed";
1274 if (opt == "thin") {
1275 h_font_sans[0] = "IBMPlexSansThin";
1278 if (opt == "extralight") {
1279 h_font_sans[0] = "IBMPlexSansExtraLight";
1282 if (opt == "light") {
1283 h_font_sans[0] = "IBMPlexSansLight";
1291 h_font_sans_opts = xopts;
1295 if (name == "noto-sans") {
1296 h_font_sans[0] = "NotoSansRegular";
1297 for (auto const & opt : allopts) {
1298 if (opt == "regular")
1300 if (opt == "medium") {
1301 h_font_sans[0] = "NotoSansMedium";
1304 if (opt == "thin") {
1305 h_font_sans[0] = "NotoSansThin";
1308 if (opt == "extralight") {
1309 h_font_sans[0] = "NotoSansExtralight";
1312 if (opt == "light") {
1313 h_font_sans[0] = "NotoSansLight";
1317 h_font_sans_osf = "true";
1325 h_font_sans_opts = xopts;
1329 if (name == "sourcesanspro") {
1330 h_font_sans[0] = "ADOBESourceSansPro";
1331 for (auto const & opt : allopts) {
1333 h_font_sans_osf = "true";
1341 h_font_sans_opts = xopts;
1349 // By default, we use the package name as LyX font name,
1350 // so this only needs to be reset if these names differ.
1351 // Also, we handle the scaling option here generally.
1352 // Exceptions: fourier, noto
1353 if (is_known(name, known_typewriter_font_packages) && name != "fourier" && name != "noto") {
1354 h_font_typewriter[0] = name;
1355 if (contains(opts, "scale")) {
1356 vector<string>::const_iterator it = allopts.begin();
1357 for (; it != allopts.end() ; ++it) {
1358 string const opt = *it;
1359 if (prefixIs(opt, "scaled=") || prefixIs(opt, "scale=")) {
1360 if (scale_as_percentage(opt, h_font_tt_scale[0])) {
1369 if (name == "libertineMono" || name == "libertineMono-type1")
1370 h_font_typewriter[0] = "libertine-mono";
1372 if (name == "FiraMono") {
1373 h_font_typewriter_osf = "true";
1374 for (auto const & opt : allopts) {
1375 if (opt == "lf" || opt == "lining") {
1376 h_font_typewriter_osf = "false";
1384 h_font_typewriter_opts = xopts;
1388 if (name == "PTMono")
1389 h_font_typewriter[0] = "PTMono-TLF";
1391 if (name == "plex-mono") {
1392 h_font_typewriter[0] = "IBMPlexMono";
1393 for (auto const & opt : allopts) {
1394 if (opt == "thin") {
1395 h_font_typewriter[0] = "IBMPlexMonoThin";
1398 if (opt == "extralight") {
1399 h_font_typewriter[0] = "IBMPlexMonoExtraLight";
1402 if (opt == "light") {
1403 h_font_typewriter[0] = "IBMPlexMonoLight";
1411 h_font_typewriter_opts = xopts;
1415 if (name == "noto-mono") {
1416 h_font_typewriter[0] = "NotoMonoRegular";
1417 for (auto const & opt : allopts) {
1418 if (opt == "regular")
1425 h_font_typewriter_opts = xopts;
1429 if (name == "sourcecodepro") {
1430 h_font_typewriter[0] = "ADOBESourceCodePro";
1431 for (auto const & opt : allopts) {
1433 h_font_typewriter_osf = "true";
1441 h_font_typewriter_opts = xopts;
1449 // By default, we use the package name as LyX font name,
1450 // so this only needs to be reset if these names differ.
1451 if (is_known(name, known_math_font_packages))
1452 h_font_math[0] = name;
1454 if (name == "newtxmath") {
1456 h_font_math[0] = "newtxmath";
1457 else if (opts == "garamondx")
1458 h_font_math[0] = "garamondx-ntxm";
1459 else if (opts == "libertine")
1460 h_font_math[0] = "libertine-ntxm";
1461 else if (opts == "minion")
1462 h_font_math[0] = "minion-ntxm";
1463 else if (opts == "cochineal")
1464 h_font_math[0] = "cochineal-ntxm";
1467 if (name == "iwona")
1469 h_font_math[0] = "iwona-math";
1471 if (name == "kurier")
1473 h_font_math[0] = "kurier-math";
1475 // after the detection and handling of special cases, we can remove the
1476 // fonts, otherwise they would appear in the preamble, see bug #7856
1477 if (is_known(name, known_roman_font_packages) || is_known(name, known_sans_font_packages)
1478 || is_known(name, known_typewriter_font_packages) || is_known(name, known_math_font_packages))
1480 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
1481 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
1482 name == "esint" || name == "mhchem" || name == "mathdots" ||
1483 name == "mathtools" || name == "stackrel" ||
1484 name == "stmaryrd" || name == "undertilde") {
1485 h_use_packages[name] = "2";
1486 registerAutomaticallyLoadedPackage(name);
1489 else if (name == "babel") {
1490 h_language_package = "default";
1491 // One might think we would have to do nothing if babel is loaded
1492 // without any options to prevent pollution of the preamble with this
1493 // babel call in every roundtrip.
1494 // But the user could have defined babel-specific things afterwards. So
1495 // we need to keep it in the preamble to prevent cases like bug #7861.
1496 if (!opts.empty()) {
1497 // check if more than one option was used - used later for inputenc
1498 if (options.begin() != options.end() - 1)
1499 one_language = false;
1500 // babel takes the last language of the option of its \usepackage
1501 // call as document language. If there is no such language option, the
1502 // last language in the documentclass options is used.
1503 handle_opt(options, known_languages, h_language);
1504 // translate the babel name to a LyX name
1505 h_language = babel2lyx(h_language);
1506 if (h_language == "japanese") {
1507 // For Japanese, the encoding isn't indicated in the source
1508 // file, and there's really not much we can do. We could
1509 // 1) offer a list of possible encodings to choose from, or
1510 // 2) determine the encoding of the file by inspecting it.
1511 // For the time being, we leave the encoding alone so that
1512 // we don't get iconv errors when making a wrong guess, and
1513 // we will output a note at the top of the document
1514 // explaining what to do.
1515 Encoding const * const enc = encodings.fromIconvName(
1516 p.getEncoding(), Encoding::japanese, false);
1518 h_inputencoding = enc->name();
1519 is_nonCJKJapanese = true;
1520 // in this case babel can be removed from the preamble
1521 registerAutomaticallyLoadedPackage("babel");
1523 // If babel is called with options, LyX puts them by default into the
1524 // document class options. This works for most languages, except
1525 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
1526 // perhaps in future others.
1527 // Therefore keep the babel call as it is as the user might have
1529 string const babelcall = "\\usepackage[" + opts + "]{babel}\n";
1530 if (!contains(h_preamble.str(), babelcall))
1531 h_preamble << babelcall;
1533 delete_opt(options, known_languages);
1535 if (!contains(h_preamble.str(), "\\usepackage{babel}\n"))
1536 h_preamble << "\\usepackage{babel}\n";
1537 explicit_babel = true;
1541 else if (name == "polyglossia") {
1542 h_language_package = "default";
1543 h_default_output_format = "pdf4";
1544 h_use_non_tex_fonts = true;
1546 registerAutomaticallyLoadedPackage("xunicode");
1547 if (h_inputencoding == "auto-legacy")
1548 p.setEncoding("UTF-8");
1551 else if (name == "CJK") {
1552 // set the encoding to "auto-legacy" because it might be set to "auto-legacy-plain" by the babel handling
1553 // and this would not be correct for CJK
1554 if (h_inputencoding == "auto-legacy-plain")
1555 h_inputencoding = "auto-legacy";
1556 registerAutomaticallyLoadedPackage("CJK");
1559 else if (name == "CJKutf8") {
1560 h_inputencoding = "utf8-cjk";
1561 p.setEncoding("UTF-8");
1562 registerAutomaticallyLoadedPackage("CJKutf8");
1565 else if (name == "fontenc") {
1566 h_fontencoding = getStringFromVector(options, ",");
1570 else if (name == "inputenc" || name == "luainputenc") {
1571 // h_inputencoding is only set when there is not more than one
1572 // inputenc option because otherwise h_inputencoding must be
1573 // set to "auto-legacy" (the default encodings of the document's languages)
1574 // Therefore check that exactly one option is passed to inputenc.
1575 // It is also only set when there is not more than one babel
1577 if (!options.empty()) {
1578 string const encoding = options.back();
1579 Encoding const * const enc = encodings.fromLaTeXName(
1580 encoding, Encoding::inputenc, true);
1582 if (!detectEncoding)
1583 cerr << "Unknown encoding " << encoding
1584 << ". Ignoring." << std::endl;
1586 if (!enc->unsafe() && options.size() == 1 && one_language == true)
1587 h_inputencoding = enc->name();
1588 p.setEncoding(enc->iconvName());
1594 else if (name == "srcltx") {
1595 h_output_sync = "1";
1596 if (!opts.empty()) {
1597 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
1600 h_output_sync_macro = "\\usepackage{srcltx}";
1603 else if (is_known(name, known_old_language_packages)) {
1604 // known language packages from the times before babel
1605 // if they are found and not also babel, they will be used as
1606 // custom language package
1607 h_language_package = "\\usepackage{" + name + "}";
1610 else if (name == "lyxskak") {
1611 // ignore this and its options
1612 const char * const o[] = {"ps", "mover", 0};
1613 delete_opt(options, o);
1616 else if (is_known(name, known_lyx_packages) && options.empty()) {
1617 if (name == "splitidx")
1618 h_use_indices = "true";
1619 else if (name == "minted")
1620 h_use_minted = true;
1621 else if (name == "refstyle")
1622 h_use_refstyle = true;
1623 else if (name == "prettyref")
1624 h_use_refstyle = false;
1625 if (!in_lyx_preamble) {
1626 h_preamble << package_beg_sep << name
1627 << package_mid_sep << "\\usepackage{"
1629 if (p.next_token().cat() == catNewline ||
1630 (p.next_token().cat() == catSpace &&
1631 p.next_next_token().cat() == catNewline))
1633 h_preamble << package_end_sep;
1637 else if (name == "geometry")
1638 handle_geometry(options);
1640 else if (name == "subfig")
1641 ; // ignore this FIXME: Use the package separator mechanism instead
1643 else if (char const * const * where = is_known(name, known_languages))
1644 h_language = known_coded_languages[where - known_languages];
1646 else if (name == "natbib") {
1647 h_biblio_style = "plainnat";
1648 h_cite_engine = "natbib";
1649 h_cite_engine_type = "authoryear";
1650 vector<string>::iterator it =
1651 find(options.begin(), options.end(), "authoryear");
1652 if (it != options.end())
1655 it = find(options.begin(), options.end(), "numbers");
1656 if (it != options.end()) {
1657 h_cite_engine_type = "numerical";
1661 if (!options.empty())
1662 h_biblio_options = join(options, ",");
1665 else if (name == "biblatex") {
1666 h_biblio_style = "plainnat";
1667 h_cite_engine = "biblatex";
1668 h_cite_engine_type = "authoryear";
1670 vector<string>::iterator it =
1671 find(options.begin(), options.end(), "natbib");
1672 if (it != options.end()) {
1674 h_cite_engine = "biblatex-natbib";
1676 opt = process_keyval_opt(options, "natbib");
1678 h_cite_engine = "biblatex-natbib";
1680 opt = process_keyval_opt(options, "style");
1682 h_biblatex_citestyle = opt;
1683 h_biblatex_bibstyle = opt;
1685 opt = process_keyval_opt(options, "citestyle");
1687 h_biblatex_citestyle = opt;
1688 opt = process_keyval_opt(options, "bibstyle");
1690 h_biblatex_bibstyle = opt;
1692 opt = process_keyval_opt(options, "refsection");
1694 if (opt == "none" || opt == "part"
1695 || opt == "chapter" || opt == "section"
1696 || opt == "subsection")
1699 cerr << "Ignoring unkown refesection value '"
1702 opt = process_keyval_opt(options, "bibencoding");
1705 if (!options.empty()) {
1706 h_biblio_options = join(options, ",");
1711 else if (name == "jurabib") {
1712 h_biblio_style = "jurabib";
1713 h_cite_engine = "jurabib";
1714 h_cite_engine_type = "authoryear";
1715 if (!options.empty())
1716 h_biblio_options = join(options, ",");
1719 else if (name == "bibtopic")
1720 h_use_bibtopic = "true";
1722 else if (name == "chapterbib")
1723 h_multibib = "child";
1725 else if (name == "hyperref")
1726 handle_hyperref(options);
1728 else if (name == "algorithm2e") {
1729 // Load "algorithm2e" module
1730 addModule("algorithm2e");
1731 // Add the package options to the global document options
1732 if (!options.empty()) {
1733 if (h_options.empty())
1734 h_options = join(options, ",");
1736 h_options += ',' + join(options, ",");
1739 else if (name == "microtype") {
1740 //we internally support only microtype without params
1741 if (options.empty())
1742 h_use_microtype = "true";
1744 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1747 else if (name == "lineno") {
1748 h_use_lineno = "true";
1749 if (!options.empty()) {
1750 h_lineno_options = join(options, ",");
1755 else if (!in_lyx_preamble) {
1756 if (options.empty())
1757 h_preamble << "\\usepackage{" << name << '}';
1759 h_preamble << "\\usepackage[" << opts << "]{"
1763 if (p.next_token().cat() == catNewline ||
1764 (p.next_token().cat() == catSpace &&
1765 p.next_next_token().cat() == catNewline))
1769 // We need to do something with the options...
1770 if (!options.empty() && !detectEncoding)
1771 cerr << "Ignoring options '" << join(options, ",")
1772 << "' of package " << name << '.' << endl;
1774 // remove the whitespace
1779 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1782 Token t = p.get_token();
1783 if (t.cat() == catEscape &&
1784 is_known(t.cs(), known_if_commands))
1785 handle_if(p, in_lyx_preamble);
1787 if (!in_lyx_preamble)
1788 h_preamble << t.asInput();
1789 if (t.cat() == catEscape && t.cs() == "fi")
1796 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1798 if (contains(h_float_placement, "H"))
1799 registerAutomaticallyLoadedPackage("float");
1800 if (h_spacing != "single" && h_spacing != "default")
1801 registerAutomaticallyLoadedPackage("setspace");
1802 if (h_use_packages["amsmath"] == "2") {
1803 // amsbsy and amstext are already provided by amsmath
1804 registerAutomaticallyLoadedPackage("amsbsy");
1805 registerAutomaticallyLoadedPackage("amstext");
1808 // output the LyX file settings
1809 // Important: Keep the version formatting in sync with LyX and
1810 // lyx2lyx (bug 7951)
1811 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1812 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1813 << lyx_version_minor << '\n'
1814 << "\\lyxformat " << LYX_FORMAT << '\n'
1815 << "\\begin_document\n"
1816 << "\\begin_header\n"
1817 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1818 << "\\origin " << origin << "\n"
1819 << "\\textclass " << h_textclass << "\n";
1820 string const raw = subdoc ? empty_string() : h_preamble.str();
1822 os << "\\begin_preamble\n";
1823 for (string::size_type i = 0; i < raw.size(); ++i) {
1824 if (raw[i] == package_beg_sep) {
1825 // Here follows some package loading code that
1826 // must be skipped if the package is loaded
1828 string::size_type j = raw.find(package_mid_sep, i);
1829 if (j == string::npos)
1831 string::size_type k = raw.find(package_end_sep, j);
1832 if (k == string::npos)
1834 string const package = raw.substr(i + 1, j - i - 1);
1835 string const replacement = raw.substr(j + 1, k - j - 1);
1836 if (auto_packages.find(package) == auto_packages.end())
1842 os << "\n\\end_preamble\n";
1844 if (!h_options.empty())
1845 os << "\\options " << h_options << "\n";
1846 os << "\\use_default_options " << h_use_default_options << "\n";
1847 if (!used_modules.empty()) {
1848 os << "\\begin_modules\n";
1849 vector<string>::const_iterator const end = used_modules.end();
1850 vector<string>::const_iterator it = used_modules.begin();
1851 for (; it != end; ++it)
1853 os << "\\end_modules\n";
1855 if (!h_includeonlys.empty()) {
1856 os << "\\begin_includeonly\n";
1857 for (auto const & iofile : h_includeonlys)
1858 os << iofile << '\n';
1859 os << "\\end_includeonly\n";
1861 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1862 << "\\language " << h_language << "\n"
1863 << "\\language_package " << h_language_package << "\n"
1864 << "\\inputencoding " << h_inputencoding << "\n"
1865 << "\\fontencoding " << h_fontencoding << "\n"
1866 << "\\font_roman \"" << h_font_roman[0]
1867 << "\" \"" << h_font_roman[1] << "\"\n"
1868 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1869 << "\\font_typewriter \"" << h_font_typewriter[0]
1870 << "\" \"" << h_font_typewriter[1] << "\"\n"
1871 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1872 << "\\font_default_family " << h_font_default_family << "\n"
1873 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1874 << "\\font_sc " << h_font_sc << "\n"
1875 << "\\font_roman_osf " << h_font_roman_osf << "\n"
1876 << "\\font_sans_osf " << h_font_sans_osf << "\n"
1877 << "\\font_typewriter_osf " << h_font_typewriter_osf << "\n";
1878 if (!h_font_roman_opts.empty())
1879 os << "\\font_roman_opts \"" << h_font_roman_opts << "\"" << '\n';
1880 os << "\\font_sf_scale " << h_font_sf_scale[0]
1881 << ' ' << h_font_sf_scale[1] << '\n';
1882 if (!h_font_sans_opts.empty())
1883 os << "\\font_sans_opts \"" << h_font_sans_opts << "\"" << '\n';
1884 os << "\\font_tt_scale " << h_font_tt_scale[0]
1885 << ' ' << h_font_tt_scale[1] << '\n';
1886 if (!h_font_cjk.empty())
1887 os << "\\font_cjk " << h_font_cjk << '\n';
1888 if (!h_font_typewriter_opts.empty())
1889 os << "\\font_typewriter_opts \"" << h_font_typewriter_opts << "\"" << '\n';
1890 os << "\\use_microtype " << h_use_microtype << '\n'
1891 << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
1892 << "\\graphics " << h_graphics << '\n'
1893 << "\\default_output_format " << h_default_output_format << "\n"
1894 << "\\output_sync " << h_output_sync << "\n";
1895 if (h_output_sync == "1")
1896 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1897 os << "\\bibtex_command " << h_bibtex_command << "\n"
1898 << "\\index_command " << h_index_command << "\n";
1899 if (!h_float_placement.empty())
1900 os << "\\float_placement " << h_float_placement << "\n";
1901 os << "\\paperfontsize " << h_paperfontsize << "\n"
1902 << "\\spacing " << h_spacing << "\n"
1903 << "\\use_hyperref " << h_use_hyperref << '\n';
1904 if (h_use_hyperref == "true") {
1905 if (!h_pdf_title.empty())
1906 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1907 if (!h_pdf_author.empty())
1908 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1909 if (!h_pdf_subject.empty())
1910 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1911 if (!h_pdf_keywords.empty())
1912 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1913 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1914 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1915 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1916 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1917 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1918 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1919 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1920 "\\pdf_backref " << h_pdf_backref << "\n"
1921 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1922 if (!h_pdf_pagemode.empty())
1923 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1924 if (!h_pdf_quoted_options.empty())
1925 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1927 os << "\\papersize " << h_papersize << "\n"
1928 << "\\use_geometry " << h_use_geometry << '\n';
1929 for (map<string, string>::const_iterator it = h_use_packages.begin();
1930 it != h_use_packages.end(); ++it)
1931 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1932 os << "\\cite_engine " << h_cite_engine << '\n'
1933 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1934 << "\\biblio_style " << h_biblio_style << "\n"
1935 << "\\use_bibtopic " << h_use_bibtopic << "\n";
1936 if (!h_biblio_options.empty())
1937 os << "\\biblio_options " << h_biblio_options << "\n";
1938 if (!h_biblatex_bibstyle.empty())
1939 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
1940 if (!h_biblatex_citestyle.empty())
1941 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
1942 if (!h_multibib.empty())
1943 os << "\\multibib " << h_multibib << "\n";
1944 os << "\\use_indices " << h_use_indices << "\n"
1945 << "\\paperorientation " << h_paperorientation << '\n'
1946 << "\\suppress_date " << h_suppress_date << '\n'
1947 << "\\justification " << h_justification << '\n'
1948 << "\\use_refstyle " << h_use_refstyle << '\n'
1949 << "\\use_minted " << h_use_minted << '\n'
1950 << "\\use_lineno " << h_use_lineno << '\n';
1951 if (!h_lineno_options.empty())
1952 os << "\\lineno_options " << h_lineno_options << '\n';
1953 if (!h_fontcolor.empty())
1954 os << "\\fontcolor " << h_fontcolor << '\n';
1955 if (!h_notefontcolor.empty())
1956 os << "\\notefontcolor " << h_notefontcolor << '\n';
1957 if (!h_backgroundcolor.empty())
1958 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1959 if (!h_boxbgcolor.empty())
1960 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1961 if (index_number != 0)
1962 for (int i = 0; i < index_number; i++) {
1963 os << "\\index " << h_index[i] << '\n'
1964 << "\\shortcut " << h_shortcut[i] << '\n'
1965 << "\\color " << h_color << '\n'
1969 os << "\\index " << h_index[0] << '\n'
1970 << "\\shortcut " << h_shortcut[0] << '\n'
1971 << "\\color " << h_color << '\n'
1975 << "\\secnumdepth " << h_secnumdepth << "\n"
1976 << "\\tocdepth " << h_tocdepth << "\n"
1977 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1978 if (h_paragraph_separation == "skip")
1979 os << "\\defskip " << h_defskip << "\n";
1981 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1982 os << "\\is_math_indent " << h_is_mathindent << "\n";
1983 if (!h_mathindentation.empty())
1984 os << "\\math_indentation " << h_mathindentation << "\n";
1985 os << "\\math_numbering_side " << h_math_numbering_side << "\n";
1986 os << "\\quotes_style " << h_quotes_style << "\n"
1987 << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
1988 << "\\papercolumns " << h_papercolumns << "\n"
1989 << "\\papersides " << h_papersides << "\n"
1990 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1991 if (!h_listings_params.empty())
1992 os << "\\listings_params " << h_listings_params << "\n";
1993 os << "\\tracking_changes " << h_tracking_changes << "\n"
1994 << "\\output_changes " << h_output_changes << "\n"
1995 << "\\html_math_output " << h_html_math_output << "\n"
1996 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1997 << "\\html_be_strict " << h_html_be_strict << "\n"
1999 << "\\end_header\n\n"
2000 << "\\begin_body\n";
2005 void Preamble::parse(Parser & p, string const & forceclass,
2006 TeX2LyXDocClass & tc)
2008 // initialize fixed types
2009 special_columns_['D'] = 3;
2010 parse(p, forceclass, false, tc);
2014 void Preamble::parse(Parser & p, string const & forceclass,
2015 bool detectEncoding, TeX2LyXDocClass & tc)
2017 bool is_full_document = false;
2018 bool is_lyx_file = false;
2019 bool in_lyx_preamble = false;
2021 // determine whether this is a full document or a fragment for inclusion
2023 Token const & t = p.get_token();
2025 if (t.cat() == catEscape && t.cs() == "documentclass") {
2026 is_full_document = true;
2032 if (detectEncoding && !is_full_document)
2035 while (is_full_document && p.good()) {
2036 if (detectEncoding && h_inputencoding != "auto-legacy" &&
2037 h_inputencoding != "auto-legacy-plain")
2040 Token const & t = p.get_token();
2043 if (!detectEncoding)
2044 cerr << "t: " << t << '\n';
2050 if (!in_lyx_preamble &&
2051 (t.cat() == catLetter ||
2052 t.cat() == catSuper ||
2053 t.cat() == catSub ||
2054 t.cat() == catOther ||
2055 t.cat() == catMath ||
2056 t.cat() == catActive ||
2057 t.cat() == catBegin ||
2058 t.cat() == catEnd ||
2059 t.cat() == catAlign ||
2060 t.cat() == catParameter)) {
2061 h_preamble << t.cs();
2065 if (!in_lyx_preamble &&
2066 (t.cat() == catSpace || t.cat() == catNewline)) {
2067 h_preamble << t.asInput();
2071 if (t.cat() == catComment) {
2072 static regex const islyxfile("%% LyX .* created this file");
2073 static regex const usercommands("User specified LaTeX commands");
2075 string const comment = t.asInput();
2077 // magically switch encoding default if it looks like XeLaTeX
2078 static string const magicXeLaTeX =
2079 "% This document must be compiled with XeLaTeX ";
2080 if (comment.size() > magicXeLaTeX.size()
2081 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
2082 && h_inputencoding == "auto-legacy") {
2083 if (!detectEncoding)
2084 cerr << "XeLaTeX comment found, switching to UTF8\n";
2085 h_inputencoding = "utf8";
2088 if (regex_search(comment, sub, islyxfile)) {
2090 in_lyx_preamble = true;
2091 } else if (is_lyx_file
2092 && regex_search(comment, sub, usercommands))
2093 in_lyx_preamble = false;
2094 else if (!in_lyx_preamble)
2095 h_preamble << t.asInput();
2099 if (t.cs() == "PassOptionsToPackage") {
2100 string const poptions = p.getArg('{', '}');
2101 string const package = p.verbatim_item();
2102 extra_package_options_.insert(make_pair(package, poptions));
2106 if (t.cs() == "pagestyle") {
2107 h_paperpagestyle = p.verbatim_item();
2111 if (t.cs() == "setdefaultlanguage") {
2113 // We don't yet care about non-language variant options
2114 // because LyX doesn't support this yet, see bug #8214
2116 string langopts = p.getOpt();
2117 // check if the option contains a variant, if yes, extract it
2118 string::size_type pos_var = langopts.find("variant");
2119 string::size_type i = langopts.find(',', pos_var);
2120 string::size_type k = langopts.find('=', pos_var);
2121 if (pos_var != string::npos){
2123 if (i == string::npos)
2124 variant = langopts.substr(k + 1, langopts.length() - k - 2);
2126 variant = langopts.substr(k + 1, i - k - 1);
2127 h_language = variant;
2131 h_language = p.verbatim_item();
2132 //finally translate the poyglossia name to a LyX name
2133 h_language = polyglossia2lyx(h_language);
2137 if (t.cs() == "setotherlanguage") {
2138 // We don't yet care about the option because LyX doesn't
2139 // support this yet, see bug #8214
2140 p.hasOpt() ? p.getOpt() : string();
2145 if (t.cs() == "setmainfont") {
2146 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2147 h_font_roman[1] = p.getArg('{', '}');
2148 if (!fontopts.empty()) {
2149 vector<string> opts = getVectorFromString(fontopts);
2151 for (auto const & opt : opts) {
2152 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2155 if (!fontopts.empty())
2159 h_font_roman_opts = fontopts;
2164 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
2165 // LyX currently only supports the scale option
2166 string scale, fontopts;
2168 fontopts = p.getArg('[', ']');
2169 if (!fontopts.empty()) {
2170 vector<string> opts = getVectorFromString(fontopts);
2172 for (auto const & opt : opts) {
2173 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2176 if (prefixIs(opt, "Scale=")) {
2177 scale_as_percentage(opt, scale);
2180 if (!fontopts.empty())
2186 if (t.cs() == "setsansfont") {
2188 h_font_sf_scale[1] = scale;
2189 h_font_sans[1] = p.getArg('{', '}');
2190 if (!fontopts.empty())
2191 h_font_sans_opts = fontopts;
2194 h_font_tt_scale[1] = scale;
2195 h_font_typewriter[1] = p.getArg('{', '}');
2196 if (!fontopts.empty())
2197 h_font_typewriter_opts = fontopts;
2202 if (t.cs() == "babelfont") {
2204 h_use_non_tex_fonts = true;
2205 h_language_package = "babel";
2206 if (h_inputencoding == "auto-legacy")
2207 p.setEncoding("UTF-8");
2208 // we don't care about the lang option
2209 string const lang = p.hasOpt() ? p.getArg('[', ']') : string();
2210 string const family = p.getArg('{', '}');
2211 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2212 string const fontname = p.getArg('{', '}');
2213 if (lang.empty() && family == "rm") {
2214 h_font_roman[1] = fontname;
2215 if (!fontopts.empty()) {
2216 vector<string> opts = getVectorFromString(fontopts);
2218 for (auto const & opt : opts) {
2219 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2222 if (!fontopts.empty())
2226 h_font_roman_opts = fontopts;
2229 } else if (lang.empty() && (family == "sf" || family == "tt")) {
2231 if (!fontopts.empty()) {
2232 vector<string> opts = getVectorFromString(fontopts);
2234 for (auto const & opt : opts) {
2235 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2238 if (prefixIs(opt, "Scale=")) {
2239 scale_as_percentage(opt, scale);
2242 if (!fontopts.empty())
2247 if (family == "sf") {
2249 h_font_sf_scale[1] = scale;
2250 h_font_sans[1] = fontname;
2251 if (!fontopts.empty())
2252 h_font_sans_opts = fontopts;
2255 h_font_tt_scale[1] = scale;
2256 h_font_typewriter[1] = fontname;
2257 if (!fontopts.empty())
2258 h_font_typewriter_opts = fontopts;
2262 // not rm, sf or tt or lang specific
2263 h_preamble << '\\' << t.cs();
2265 h_preamble << '[' << lang << ']';
2266 h_preamble << '{' << family << '}';
2267 if (!fontopts.empty())
2268 h_preamble << '[' << fontopts << ']';
2269 h_preamble << '{' << fontname << '}' << '\n';
2274 if (t.cs() == "date") {
2275 string argument = p.getArg('{', '}');
2276 if (argument.empty())
2277 h_suppress_date = "true";
2279 h_preamble << t.asInput() << '{' << argument << '}';
2283 if (t.cs() == "color") {
2284 string const space =
2285 (p.hasOpt() ? p.getOpt() : string());
2286 string argument = p.getArg('{', '}');
2287 // check the case that a standard color is used
2288 if (space.empty() && is_known(argument, known_basic_colors)) {
2289 h_fontcolor = rgbcolor2code(argument);
2290 registerAutomaticallyLoadedPackage("color");
2291 } else if (space.empty() && argument == "document_fontcolor")
2292 registerAutomaticallyLoadedPackage("color");
2293 // check the case that LyX's document_fontcolor is defined
2294 // but not used for \color
2296 h_preamble << t.asInput();
2298 h_preamble << space;
2299 h_preamble << '{' << argument << '}';
2300 // the color might already be set because \definecolor
2301 // is parsed before this
2307 if (t.cs() == "pagecolor") {
2308 string argument = p.getArg('{', '}');
2309 // check the case that a standard color is used
2310 if (is_known(argument, known_basic_colors)) {
2311 h_backgroundcolor = rgbcolor2code(argument);
2312 } else if (argument == "page_backgroundcolor")
2313 registerAutomaticallyLoadedPackage("color");
2314 // check the case that LyX's page_backgroundcolor is defined
2315 // but not used for \pagecolor
2317 h_preamble << t.asInput() << '{' << argument << '}';
2318 // the color might already be set because \definecolor
2319 // is parsed before this
2320 h_backgroundcolor = "";
2325 if (t.cs() == "makeatletter") {
2326 // LyX takes care of this
2327 p.setCatcode('@', catLetter);
2331 if (t.cs() == "makeatother") {
2332 // LyX takes care of this
2333 p.setCatcode('@', catOther);
2337 if (t.cs() == "makeindex") {
2338 // LyX will re-add this if a print index command is found
2343 if (t.cs() == "newindex") {
2344 string const indexname = p.getArg('[', ']');
2345 string const shortcut = p.verbatim_item();
2346 if (!indexname.empty())
2347 h_index[index_number] = indexname;
2349 h_index[index_number] = shortcut;
2350 h_shortcut[index_number] = shortcut;
2356 if (t.cs() == "addbibresource") {
2357 string const options = p.getArg('[', ']');
2358 string const arg = removeExtension(p.getArg('{', '}'));
2359 if (!options.empty()) {
2360 // check if the option contains a bibencoding, if yes, extract it
2361 string::size_type pos = options.find("bibencoding=");
2363 if (pos != string::npos) {
2364 string::size_type i = options.find(',', pos);
2365 if (i == string::npos)
2366 encoding = options.substr(pos + 1);
2368 encoding = options.substr(pos, i - pos);
2369 pos = encoding.find('=');
2370 if (pos == string::npos)
2373 encoding = encoding.substr(pos + 1);
2375 if (!encoding.empty())
2376 biblatex_encodings.push_back(normalize_filename(arg) + ' ' + encoding);
2378 biblatex_bibliographies.push_back(arg);
2382 if (t.cs() == "bibliography") {
2383 vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
2384 biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
2388 if (t.cs() == "RS@ifundefined") {
2389 string const name = p.verbatim_item();
2390 string const body1 = p.verbatim_item();
2391 string const body2 = p.verbatim_item();
2392 // only non-lyxspecific stuff
2393 if (in_lyx_preamble &&
2394 (name == "subsecref" || name == "thmref" || name == "lemref"))
2398 ss << '\\' << t.cs();
2399 ss << '{' << name << '}'
2400 << '{' << body1 << '}'
2401 << '{' << body2 << '}';
2402 h_preamble << ss.str();
2407 if (t.cs() == "AtBeginDocument") {
2408 string const name = p.verbatim_item();
2409 // only non-lyxspecific stuff
2410 if (in_lyx_preamble &&
2411 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
2412 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
2413 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
2414 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
2415 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
2416 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
2417 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
2418 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
2419 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
2420 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
2421 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
2422 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
2423 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
2424 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
2425 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
2429 ss << '\\' << t.cs();
2430 ss << '{' << name << '}';
2431 h_preamble << ss.str();
2436 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
2437 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
2438 || t.cs() == "providecommand" || t.cs() == "providecommandx"
2439 || t.cs() == "DeclareRobustCommand"
2440 || t.cs() == "DeclareRobustCommandx"
2441 || t.cs() == "ProvideTextCommandDefault"
2442 || t.cs() == "DeclareMathAccent") {
2444 if (p.next_token().character() == '*') {
2448 string const name = p.verbatim_item();
2449 string const opt1 = p.getFullOpt();
2450 string const opt2 = p.getFullOpt();
2451 string const body = p.verbatim_item();
2452 // store the in_lyx_preamble setting
2453 bool const was_in_lyx_preamble = in_lyx_preamble;
2455 if (name == "\\rmdefault")
2456 if (is_known(body, known_roman_font_packages)) {
2457 h_font_roman[0] = body;
2459 in_lyx_preamble = true;
2461 if (name == "\\sfdefault")
2462 if (is_known(body, known_sans_font_packages)) {
2463 h_font_sans[0] = body;
2465 in_lyx_preamble = true;
2467 if (name == "\\ttdefault")
2468 if (is_known(body, known_typewriter_font_packages)) {
2469 h_font_typewriter[0] = body;
2471 in_lyx_preamble = true;
2473 if (name == "\\familydefault") {
2474 string family = body;
2475 // remove leading "\"
2476 h_font_default_family = family.erase(0,1);
2478 in_lyx_preamble = true;
2481 // remove LyX-specific definitions that are re-added by LyX
2483 // \lyxline is an ancient command that is converted by tex2lyx into
2484 // a \rule therefore remove its preamble code
2485 if (name == "\\lyxdot" || name == "\\lyxarrow"
2486 || name == "\\lyxline" || name == "\\LyX") {
2488 in_lyx_preamble = true;
2491 // Add the command to the known commands
2492 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
2494 // only non-lyxspecific stuff
2495 if (!in_lyx_preamble) {
2497 ss << '\\' << t.cs();
2500 ss << '{' << name << '}' << opt1 << opt2
2501 << '{' << body << "}";
2502 if (prefixIs(t.cs(), "renew") || !contains(h_preamble.str(), ss.str()))
2503 h_preamble << ss.str();
2505 ostream & out = in_preamble ? h_preamble : os;
2506 out << "\\" << t.cs() << "{" << name << "}"
2507 << opts << "{" << body << "}";
2510 // restore the in_lyx_preamble setting
2511 in_lyx_preamble = was_in_lyx_preamble;
2515 if (t.cs() == "documentclass") {
2516 vector<string>::iterator it;
2517 vector<string> opts = split_options(p.getArg('[', ']'));
2518 handle_opt(opts, known_fontsizes, h_paperfontsize);
2519 delete_opt(opts, known_fontsizes);
2520 // delete "pt" at the end
2521 string::size_type i = h_paperfontsize.find("pt");
2522 if (i != string::npos)
2523 h_paperfontsize.erase(i);
2524 // The documentclass options are always parsed before the options
2525 // of the babel call so that a language cannot overwrite the babel
2527 handle_opt(opts, known_languages, h_language);
2528 delete_opt(opts, known_languages);
2531 if ((it = find(opts.begin(), opts.end(), "fleqn"))
2533 h_is_mathindent = "1";
2536 // formula numbering side
2537 if ((it = find(opts.begin(), opts.end(), "leqno"))
2539 h_math_numbering_side = "left";
2542 else if ((it = find(opts.begin(), opts.end(), "reqno"))
2544 h_math_numbering_side = "right";
2548 // paper orientation
2549 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
2550 h_paperorientation = "landscape";
2554 if ((it = find(opts.begin(), opts.end(), "oneside"))
2559 if ((it = find(opts.begin(), opts.end(), "twoside"))
2565 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
2567 h_papercolumns = "1";
2570 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
2572 h_papercolumns = "2";
2576 // some size options are known to any document classes, other sizes
2577 // are handled by the \geometry command of the geometry package
2578 handle_opt(opts, known_class_paper_sizes, h_papersize);
2579 delete_opt(opts, known_class_paper_sizes);
2580 // the remaining options
2581 h_options = join(opts, ",");
2582 // FIXME This does not work for classes that have a
2583 // different name in LyX than in LaTeX
2584 h_textclass = p.getArg('{', '}');
2589 if (t.cs() == "usepackage") {
2590 string const options = p.getArg('[', ']');
2591 string const name = p.getArg('{', '}');
2592 vector<string> vecnames;
2593 split(name, vecnames, ',');
2594 vector<string>::const_iterator it = vecnames.begin();
2595 vector<string>::const_iterator end = vecnames.end();
2596 for (; it != end; ++it)
2597 handle_package(p, trimSpaceAndEol(*it), options,
2598 in_lyx_preamble, detectEncoding);
2602 if (t.cs() == "inputencoding") {
2603 string const encoding = p.getArg('{','}');
2604 Encoding const * const enc = encodings.fromLaTeXName(
2605 encoding, Encoding::inputenc, true);
2607 if (!detectEncoding)
2608 cerr << "Unknown encoding " << encoding
2609 << ". Ignoring." << std::endl;
2612 h_inputencoding = enc->name();
2613 p.setEncoding(enc->iconvName());
2618 if (t.cs() == "newenvironment") {
2619 string const name = p.getArg('{', '}');
2620 string const opt1 = p.getFullOpt();
2621 string const opt2 = p.getFullOpt();
2622 string const beg = p.verbatim_item();
2623 string const end = p.verbatim_item();
2624 if (!in_lyx_preamble) {
2625 h_preamble << "\\newenvironment{" << name
2626 << '}' << opt1 << opt2 << '{'
2627 << beg << "}{" << end << '}';
2629 add_known_environment(name, opt1, !opt2.empty(),
2630 from_utf8(beg), from_utf8(end));
2634 if (t.cs() == "newtheorem") {
2636 if (p.next_token().character() == '*') {
2640 string const name = p.getArg('{', '}');
2641 string const opt1 = p.getFullOpt();
2642 string const opt2 = p.getFullOpt();
2643 string const body = p.verbatim_item();
2644 string const opt3 = p.getFullOpt();
2645 string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
2647 string const complete = cmd + "{" + name + '}' +
2648 opt1 + opt2 + '{' + body + '}' + opt3;
2650 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
2652 if (!in_lyx_preamble)
2653 h_preamble << complete;
2657 if (t.cs() == "def") {
2658 string name = p.get_token().cs();
2659 // In fact, name may be more than the name:
2660 // In the test case of bug 8116
2661 // name == "csname SF@gobble@opt \endcsname".
2662 // Therefore, we need to use asInput() instead of cs().
2663 while (p.next_token().cat() != catBegin)
2664 name += p.get_token().asInput();
2665 if (!in_lyx_preamble)
2666 h_preamble << "\\def\\" << name << '{'
2667 << p.verbatim_item() << "}";
2671 if (t.cs() == "newcolumntype") {
2672 string const name = p.getArg('{', '}');
2673 trimSpaceAndEol(name);
2675 string opts = p.getOpt();
2676 if (!opts.empty()) {
2677 istringstream is(string(opts, 1));
2680 special_columns_[name[0]] = nargs;
2681 h_preamble << "\\newcolumntype{" << name << "}";
2683 h_preamble << "[" << nargs << "]";
2684 h_preamble << "{" << p.verbatim_item() << "}";
2688 if (t.cs() == "setcounter") {
2689 string const name = p.getArg('{', '}');
2690 string const content = p.getArg('{', '}');
2691 if (name == "secnumdepth")
2692 h_secnumdepth = content;
2693 else if (name == "tocdepth")
2694 h_tocdepth = content;
2696 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
2700 if (t.cs() == "setlength") {
2701 string const name = p.verbatim_item();
2702 string const content = p.verbatim_item();
2703 // the paragraphs are only not indented when \parindent is set to zero
2704 if (name == "\\parindent" && content != "") {
2705 if (content[0] == '0')
2706 h_paragraph_separation = "skip";
2708 h_paragraph_indentation = translate_len(content);
2709 } else if (name == "\\parskip") {
2710 if (content == "\\smallskipamount")
2711 h_defskip = "smallskip";
2712 else if (content == "\\medskipamount")
2713 h_defskip = "medskip";
2714 else if (content == "\\bigskipamount")
2715 h_defskip = "bigskip";
2717 h_defskip = translate_len(content);
2718 } else if (name == "\\mathindent") {
2719 h_mathindentation = translate_len(content);
2721 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2725 if (t.cs() == "onehalfspacing") {
2726 h_spacing = "onehalf";
2730 if (t.cs() == "doublespacing") {
2731 h_spacing = "double";
2735 if (t.cs() == "setstretch") {
2736 h_spacing = "other " + p.verbatim_item();
2740 if (t.cs() == "synctex") {
2741 // the scheme is \synctex=value
2742 // where value can only be "1" or "-1"
2743 h_output_sync = "1";
2744 // there can be any character behind the value (e.g. a linebreak or a '\'
2745 // therefore we extract it char by char
2747 string value = p.get_token().asInput();
2749 value += p.get_token().asInput();
2750 h_output_sync_macro = "\\synctex=" + value;
2754 if (t.cs() == "begin") {
2755 string const name = p.getArg('{', '}');
2756 if (name == "document")
2758 h_preamble << "\\begin{" << name << "}";
2762 if (t.cs() == "geometry") {
2763 vector<string> opts = split_options(p.getArg('{', '}'));
2764 handle_geometry(opts);
2768 if (t.cs() == "definecolor") {
2769 string const color = p.getArg('{', '}');
2770 string const space = p.getArg('{', '}');
2771 string const value = p.getArg('{', '}');
2772 if (color == "document_fontcolor" && space == "rgb") {
2773 RGBColor c(RGBColorFromLaTeX(value));
2774 h_fontcolor = X11hexname(c);
2775 } else if (color == "note_fontcolor" && space == "rgb") {
2776 RGBColor c(RGBColorFromLaTeX(value));
2777 h_notefontcolor = X11hexname(c);
2778 } else if (color == "page_backgroundcolor" && space == "rgb") {
2779 RGBColor c(RGBColorFromLaTeX(value));
2780 h_backgroundcolor = X11hexname(c);
2781 } else if (color == "shadecolor" && space == "rgb") {
2782 RGBColor c(RGBColorFromLaTeX(value));
2783 h_boxbgcolor = X11hexname(c);
2785 h_preamble << "\\definecolor{" << color
2786 << "}{" << space << "}{" << value
2792 if (t.cs() == "bibliographystyle") {
2793 h_biblio_style = p.verbatim_item();
2797 if (t.cs() == "jurabibsetup") {
2798 // FIXME p.getArg('{', '}') is most probably wrong (it
2799 // does not handle nested braces).
2800 // Use p.verbatim_item() instead.
2801 vector<string> jurabibsetup =
2802 split_options(p.getArg('{', '}'));
2803 // add jurabibsetup to the jurabib package options
2804 add_package("jurabib", jurabibsetup);
2805 if (!jurabibsetup.empty()) {
2806 h_preamble << "\\jurabibsetup{"
2807 << join(jurabibsetup, ",") << '}';
2812 if (t.cs() == "hypersetup") {
2813 vector<string> hypersetup =
2814 split_options(p.verbatim_item());
2815 // add hypersetup to the hyperref package options
2816 handle_hyperref(hypersetup);
2817 if (!hypersetup.empty()) {
2818 h_preamble << "\\hypersetup{"
2819 << join(hypersetup, ",") << '}';
2824 if (t.cs() == "includeonly") {
2825 vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
2826 for (auto & iofile : includeonlys) {
2827 string filename(normalize_filename(iofile));
2828 string const path = getMasterFilePath(true);
2829 // We want to preserve relative/absolute filenames,
2830 // therefore path is only used for testing
2831 if (!makeAbsPath(filename, path).exists()) {
2832 // The file extension is probably missing.
2833 // Now try to find it out.
2834 string const tex_name =
2835 find_file(filename, path,
2836 known_tex_extensions);
2837 if (!tex_name.empty())
2838 filename = tex_name;
2841 if (makeAbsPath(filename, path).exists())
2842 fix_child_filename(filename);
2844 cerr << "Warning: Could not find included file '"
2845 << filename << "'." << endl;
2846 outname = changeExtension(filename, "lyx");
2847 h_includeonlys.push_back(outname);
2852 if (is_known(t.cs(), known_if_3arg_commands)) {
2853 // prevent misparsing of \usepackage if it is used
2854 // as an argument (see e.g. our own output of
2855 // \@ifundefined above)
2856 string const arg1 = p.verbatim_item();
2857 string const arg2 = p.verbatim_item();
2858 string const arg3 = p.verbatim_item();
2859 // test case \@ifundefined{date}{}{\date{}}
2860 if (t.cs() == "@ifundefined" && arg1 == "date" &&
2861 arg2.empty() && arg3 == "\\date{}") {
2862 h_suppress_date = "true";
2863 // older tex2lyx versions did output
2864 // \@ifundefined{definecolor}{\usepackage{color}}{}
2865 } else if (t.cs() == "@ifundefined" &&
2866 arg1 == "definecolor" &&
2867 arg2 == "\\usepackage{color}" &&
2869 if (!in_lyx_preamble)
2870 h_preamble << package_beg_sep
2873 << "\\@ifundefined{definecolor}{color}{}"
2876 //\@ifundefined{showcaptionsetup}{}{%
2877 // \PassOptionsToPackage{caption=false}{subfig}}
2878 // that LyX uses for subfloats
2879 } else if (t.cs() == "@ifundefined" &&
2880 arg1 == "showcaptionsetup" && arg2.empty()
2881 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
2883 } else if (!in_lyx_preamble) {
2884 h_preamble << t.asInput()
2885 << '{' << arg1 << '}'
2886 << '{' << arg2 << '}'
2887 << '{' << arg3 << '}';
2892 if (is_known(t.cs(), known_if_commands)) {
2893 // must not parse anything in conditional code, since
2894 // LyX would output the parsed contents unconditionally
2895 if (!in_lyx_preamble)
2896 h_preamble << t.asInput();
2897 handle_if(p, in_lyx_preamble);
2901 if (!t.cs().empty() && !in_lyx_preamble) {
2902 h_preamble << '\\' << t.cs();
2907 // remove the whitespace
2910 // Force textclass if the user wanted it
2911 if (!forceclass.empty())
2912 h_textclass = forceclass;
2913 tc.setName(h_textclass);
2914 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2915 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2918 if (h_papersides.empty()) {
2921 h_papersides = ss.str();
2924 // If the CJK package is used we cannot set the document language from
2925 // the babel options. Instead, we guess which language is used most
2926 // and set this one.
2927 default_language = h_language;
2928 if (is_full_document &&
2929 (auto_packages.find("CJK") != auto_packages.end() ||
2930 auto_packages.find("CJKutf8") != auto_packages.end())) {
2932 h_language = guessLanguage(p, default_language);
2934 if (explicit_babel && h_language != default_language) {
2935 // We set the document language to a CJK language,
2936 // but babel is explicitly called in the user preamble
2937 // without options. LyX will not add the default
2938 // language to the document options if it is either
2939 // english, or no text is set as default language.
2940 // Therefore we need to add a language option explicitly.
2941 // FIXME: It would be better to remove all babel calls
2942 // from the user preamble, but this is difficult
2943 // without re-introducing bug 7861.
2944 if (h_options.empty())
2945 h_options = lyx2babel(default_language);
2947 h_options += ',' + lyx2babel(default_language);
2951 // Finally, set the quote style.
2952 // LyX knows the following quotes styles:
2953 // british, cjk, cjkangle, danish, english, french, german,
2954 // polish, russian, swedish and swiss
2955 // conversion list taken from
2956 // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
2957 // (quotes for kazakh are unknown)
2959 if (is_known(h_language, known_british_quotes_languages))
2960 h_quotes_style = "british";
2962 else if (is_known(h_language, known_cjk_quotes_languages))
2963 h_quotes_style = "cjk";
2965 else if (is_known(h_language, known_cjkangle_quotes_languages))
2966 h_quotes_style = "cjkangle";
2968 else if (is_known(h_language, known_danish_quotes_languages))
2969 h_quotes_style = "danish";
2971 else if (is_known(h_language, known_french_quotes_languages))
2972 h_quotes_style = "french";
2974 else if (is_known(h_language, known_german_quotes_languages))
2975 h_quotes_style = "german";
2977 else if (is_known(h_language, known_polish_quotes_languages))
2978 h_quotes_style = "polish";
2980 else if (is_known(h_language, known_russian_quotes_languages))
2981 h_quotes_style = "russian";
2983 else if (is_known(h_language, known_swedish_quotes_languages))
2984 h_quotes_style = "swedish";
2986 else if (is_known(h_language, known_swiss_quotes_languages))
2987 h_quotes_style = "swiss";
2989 else if (is_known(h_language, known_english_quotes_languages))
2990 h_quotes_style = "english";
2994 string Preamble::parseEncoding(Parser & p, string const & forceclass)
2996 TeX2LyXDocClass dummy;
2997 parse(p, forceclass, true, dummy);
2998 if (h_inputencoding != "auto-legacy" && h_inputencoding != "auto-legacy-plain")
2999 return h_inputencoding;
3004 string babel2lyx(string const & language)
3006 char const * const * where = is_known(language, known_languages);
3008 return known_coded_languages[where - known_languages];
3013 string lyx2babel(string const & language)
3015 char const * const * where = is_known(language, known_coded_languages);
3017 return known_languages[where - known_coded_languages];
3022 string Preamble::polyglossia2lyx(string const & language)
3024 char const * const * where = is_known(language, polyglossia_languages);
3026 return coded_polyglossia_languages[where - polyglossia_languages];
3031 string rgbcolor2code(string const & name)
3033 char const * const * where = is_known(name, known_basic_colors);
3035 // "red", "green" etc
3036 return known_basic_color_codes[where - known_basic_colors];
3038 // "255,0,0", "0,255,0" etc
3039 RGBColor c(RGBColorFromLaTeX(name));
3040 return X11hexname(c);