3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
9 * Full author contact details are available in file CREDITS.
20 #include "LayoutFile.h"
23 #include "TextClass.h"
26 #include "support/convert.h"
27 #include "support/FileName.h"
28 #include "support/filetools.h"
29 #include "support/lstrings.h"
31 #include "support/regex.h"
37 using namespace lyx::support;
46 // CJK languages are handled in text.cpp, polyglossia languages are listed
49 * known babel language names (including synonyms)
50 * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
51 * please keep this in sync with known_coded_languages line by line!
53 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
54 "american", "arabic", "arabtex", "australian", "austrian", "azerbaijani", "bahasa", "bahasai",
55 "bahasam", "basque", "belarusian", "bosnian", "brazil", "brazilian", "breton", "british",
56 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
57 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "francais",
58 "french", "frenchb", "frenchle", "frenchpro", "friulan", "galician", "german", "germanb",
59 "georgian", "greek", "hebrew", "hungarian", "icelandic", "indon", "indonesian",
60 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
61 "latvian", "lithuanian", "lowersorbian", "lsorbian", "macedonian", "magyar", "malay", "meyalu",
62 "mongolian", "naustrian", "newzealand", "ngerman", "ngermanb", "norsk", "nswissgerman",
63 "nynorsk", "piedmontese", "polutonikogreek", "polish", "portuges", "portuguese",
64 "romanian", "romansh", "russian", "russianb", "samin", "scottish", "serbian", "serbian-latin",
65 "slovak", "slovene", "spanish", "swedish", "swissgerman", "thai", "turkish", "turkmen",
66 "ukraineb", "ukrainian", "uppersorbian", "UKenglish", "USenglish", "usorbian",
71 * the same as known_languages with .lyx names
72 * please keep this in sync with known_languages line by line!
74 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
75 "american", "arabic_arabi", "arabic_arabtex", "australian", "austrian", "azerbaijani", "bahasa", "bahasa",
76 "bahasam", "basque", "belarusian", "bosnian", "brazilian", "brazilian", "breton", "british",
77 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
78 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "french",
79 "french", "french", "french", "french", "friulan", "galician", "german", "german",
80 "georgian", "greek", "hebrew", "magyar", "icelandic", "bahasa", "bahasa",
81 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
82 "latvian", "lithuanian", "lowersorbian", "lowersorbian", "macedonian", "magyar", "bahasam", "bahasam",
83 "mongolian", "naustrian", "newzealand", "ngerman", "ngerman", "norsk", "german-ch",
84 "nynorsk", "piedmontese", "polutonikogreek", "polish", "portuguese", "portuguese",
85 "romanian", "romansh", "russian", "russian", "samin", "scottish", "serbian", "serbian-latin",
86 "slovak", "slovene", "spanish", "swedish", "german-ch-old", "thai", "turkish", "turkmen",
87 "ukrainian", "ukrainian", "uppersorbian", "english", "english", "uppersorbian",
88 "vietnamese", "welsh",
91 /// languages with british quotes (.lyx names)
92 const char * const known_british_quotes_languages[] = {"british", "welsh", 0};
94 /// languages with cjk quotes (.lyx names)
95 const char * const known_cjk_quotes_languages[] = {"chinese-traditional",
96 "japanese", "japanese-cjk", 0};
98 /// languages with cjk-angle quotes (.lyx names)
99 const char * const known_cjkangle_quotes_languages[] = {"korean", 0};
101 /// languages with danish quotes (.lyx names)
102 const char * const known_danish_quotes_languages[] = {"danish", 0};
104 /// languages with english quotes (.lyx names)
105 const char * const known_english_quotes_languages[] = {"american", "australian",
106 "bahasa", "bahasam", "bengali", "brazilian", "canadian", "chinese-simplified", "english",
107 "esperanto", "farsi", "interlingua", "irish", "newzealand", "scottish",
108 "thai", "turkish", "vietnamese", 0};
110 /// languages with french quotes (.lyx names)
111 const char * const known_french_quotes_languages[] = {"ancientgreek",
112 "arabic_arabi", "arabic_arabtex", "asturian", "belarusian", "breton",
113 "canadien", "catalan", "french", "friulan", "galician", "italian", "occitan",
114 "piedmontese", "portuguese", "spanish", "spanish-mexico", 0};
116 /// languages with german quotes (.lyx names)
117 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
118 "czech", "estonian", "georgian", "german", "icelandic", "latvian", "lithuanian",
119 "lowersorbian", "macedonian", "naustrian", "ngerman", "romansh", "slovak", "slovene",
122 /// languages with polish quotes (.lyx names)
123 const char * const known_polish_quotes_languages[] = {"afrikaans", "bosnian", "croatian",
124 "dutch", "magyar", "polish", "romanian", "serbian", "serbian-latin", 0};
126 /// languages with russian quotes (.lyx names)
127 const char * const known_russian_quotes_languages[] = {"azerbaijani", "oldrussian",
128 "russian", "ukrainian", 0};
130 /// languages with swedish quotes (.lyx names)
131 const char * const known_swedish_quotes_languages[] = {"finnish", "swedish", 0};
133 /// languages with swiss quotes (.lyx names)
134 const char * const known_swiss_quotes_languages[] = {"albanian",
135 "armenian", "basque", "churchslavonic", "german-ch", "german-ch-old",
136 "norsk", "nynorsk", "turkmen", "ukrainian", "vietnamese", 0};
138 /// known language packages from the times before babel
139 const char * const known_old_language_packages[] = {"french", "frenchle",
140 "frenchpro", "german", "ngerman", "pmfrench", 0};
142 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
144 const char * const known_roman_font_packages[] = { "ae", "beraserif", "bookman",
145 "ccfonts", "chancery", "charter", "cmr", "cochineal", "crimson", "CrimsonPro", "DejaVuSerif",
146 "DejaVuSerifCondensed", "fourier", "garamondx", "libertine", "libertineRoman", "libertine-type1",
147 "lmodern", "mathdesign", "mathpazo", "mathptmx", "MinionPro", "newcent", "noto", "noto-serif",
148 "PTSerif", "tgbonum", "tgchorus", "tgpagella", "tgschola", "tgtermes", "utopia", "xcharter", 0 };
150 const char * const known_sans_font_packages[] = { "avant", "berasans", "biolinum",
151 "biolinum-type1", "cantarell", "Chivo", "cmbr", "cmss", "DejaVuSans", "DejaVuSansCondensed", "FiraSans", "helvet", "iwona",
152 "iwonac", "iwonal", "iwonalc", "kurier", "kurierc", "kurierl", "kurierlc", "lmss", "noto-sans", "PTSans",
153 "tgadventor", "tgheros", "uop", 0 };
155 const char * const known_typewriter_font_packages[] = { "beramono", "cmtl", "cmtt", "courier", "DejaVuSansMono",
156 "FiraMono", "lmtt", "luximono", "libertineMono", "libertineMono-type1", "lmodern",
157 "mathpazo", "mathptmx", "newcent", "noto-mono", "PTMono", "tgcursor", "txtt", 0 };
159 const char * const known_math_font_packages[] = { "eulervm", "newtxmath", 0};
161 const char * const known_latex_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_paper_margins[] = { "lmargin", "tmargin", "rmargin",
168 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
170 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
171 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
174 /// commands that can start an \if...\else...\endif sequence
175 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
176 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
177 "ifsidecap", "ifupgreek", 0};
179 const char * const known_basic_colors[] = {"black", "blue", "brown", "cyan",
180 "darkgray", "gray", "green", "lightgray", "lime", "magenta", "orange", "olive",
181 "pink", "purple", "red", "teal", "violet", "white", "yellow", 0};
183 const char * const known_basic_color_codes[] = {"#000000", "#0000ff", "#964B00", "#00ffff",
184 "#a9a9a9", "#808080", "#00ff00", "#d3d3d3", "#bfff00", "#ff00ff", "#ff7f00", "#808000",
185 "#ffc0cb", "#800080", "#ff0000", "#008080", "#8f00ff", "#ffffff", "#ffff00", 0};
187 /// conditional commands with three arguments like \@ifundefined{}{}{}
188 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
192 * Known file extensions for TeX files as used by \\includeonly
194 char const * const known_tex_extensions[] = {"tex", 0};
196 /// packages that work only in xetex
197 /// polyglossia is handled separately
198 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
199 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
200 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
202 /// packages that are automatically skipped if loaded by LyX
203 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
204 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color",
205 "float", "fontspec", "framed", "graphicx", "hhline", "ifthen", "longtable",
206 "makeidx", "minted", "multirow", "nomencl", "pdfpages", "prettyref", "refstyle",
207 "rotating", "rotfloat", "splitidx", "setspace", "subscript", "tabularx","textcomp", "tipa",
208 "tipx", "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xcolor", "xltabular",
211 // codes used to remove packages that are loaded automatically by LyX.
212 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
213 const char package_beg_sep = '\001';
214 const char package_mid_sep = '\002';
215 const char package_end_sep = '\003';
218 // returns true if at least one of the options in what has been found
219 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
225 // the last language option is the document language (for babel and LyX)
226 // the last size option is the document font size
227 vector<string>::iterator it;
228 vector<string>::iterator position = opts.begin();
229 for (; *what; ++what) {
230 it = find(opts.begin(), opts.end(), *what);
231 if (it != opts.end()) {
232 if (it >= position) {
243 void delete_opt(vector<string> & opts, char const * const * what)
248 // remove found options from the list
249 // do this after handle_opt to avoid potential memory leaks
250 vector<string>::iterator it;
251 for (; *what; ++what) {
252 it = find(opts.begin(), opts.end(), *what);
253 if (it != opts.end())
260 * Split a package options string (keyval format) into a vector.
262 * authorformat=smallcaps,
264 * titleformat=colonsep,
265 * bibformat={tabular,ibidem,numbered}
267 vector<string> split_options(string const & input)
269 vector<string> options;
273 Token const & t = p.get_token();
274 if (t.asInput() == ",") {
275 options.push_back(trimSpaceAndEol(option));
277 } else if (t.asInput() == "=") {
280 if (p.next_token().asInput() == "{")
281 option += '{' + p.getArg('{', '}') + '}';
282 } else if (t.cat() != catSpace && t.cat() != catComment)
283 option += t.asInput();
287 options.push_back(trimSpaceAndEol(option));
294 * Retrieve a keyval option "name={value with=sign}" named \p name from
295 * \p options and return the value.
296 * The found option is also removed from \p options.
298 string process_keyval_opt(vector<string> & options, string name)
300 for (size_t i = 0; i < options.size(); ++i) {
301 vector<string> option;
302 split(options[i], option, '=');
303 if (option.size() < 2)
305 if (option[0] == name) {
306 options.erase(options.begin() + i);
307 option.erase(option.begin());
308 return join(option, "=");
314 } // anonymous namespace
318 * known polyglossia language names (including variants)
319 * FIXME: support spelling=old for german variants (german vs. ngerman LyX names etc)
321 const char * const Preamble::polyglossia_languages[] = {
322 "albanian", "american", "amharic", "ancient", "arabic", "armenian", "asturian", "australian",
323 "bahasai", "bahasam", "basque", "bengali", "brazil", "brazilian", "breton", "british", "bulgarian",
324 "catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
325 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
326 "galician", "greek", "monotonic", "hebrew", "hindi",
327 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean",
328 "lao", "latin", "latvian", "lithuanian", "lsorbian", "magyar", "malayalam", "marathi",
329 "austrian", "newzealand", "german", "norsk", "nynorsk", "occitan", "oldrussian",
330 "piedmontese", "polish", "polytonic", "portuges", "romanian", "romansh", "russian",
331 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovenian", "spanish", "swedish", "syriac",
332 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
333 "ukrainian", "urdu", "usorbian", "vietnamese", "welsh", 0};
334 // not yet supported by LyX: "korean", "nko"
337 * the same as polyglossia_languages with .lyx names
338 * please keep this in sync with polyglossia_languages line by line!
340 const char * const Preamble::coded_polyglossia_languages[] = {
341 "albanian", "american", "amharic", "ancientgreek", "arabic_arabi", "armenian", "asturian", "australian",
342 "bahasa", "bahasam", "basque", "bengali", "brazilian", "brazilian", "breton", "british", "bulgarian",
343 "catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
344 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
345 "galician", "greek", "greek", "hebrew", "hindi",
346 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean",
347 "lao", "latin", "latvian", "lithuanian", "lowersorbian", "magyar", "malayalam", "marathi",
348 "naustrian","newzealand", "ngerman", "norsk", "nynorsk", "occitan", "oldrussian",
349 "piedmontese", "polish", "polutonikogreek", "portuges", "romanian", "romansh", "russian",
350 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovene", "spanish", "swedish", "syriac",
351 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
352 "ukrainian", "urdu", "uppersorbian", "vietnamese", "welsh", 0};
353 // not yet supported by LyX: "korean-polyglossia", "nko"
356 bool Preamble::usePolyglossia() const
358 return h_use_non_tex_fonts && h_language_package == "default";
362 bool Preamble::indentParagraphs() const
364 return h_paragraph_separation == "indent";
368 bool Preamble::isPackageUsed(string const & package) const
370 return used_packages.find(package) != used_packages.end();
374 bool Preamble::isPackageAutoLoaded(string const & package) const
376 return auto_packages.find(package) != auto_packages.end();
380 vector<string> Preamble::getPackageOptions(string const & package) const
382 map<string, vector<string> >::const_iterator it = used_packages.find(package);
383 if (it != used_packages.end())
385 return vector<string>();
389 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
391 auto_packages.insert(package);
395 void Preamble::addModule(string const & module)
397 for (auto const & m : used_modules) {
401 used_modules.push_back(module);
405 void Preamble::suppressDate(bool suppress)
408 h_suppress_date = "true";
410 h_suppress_date = "false";
414 void Preamble::registerAuthor(std::string const & name, string const & initials)
416 Author author(from_utf8(name), empty_docstring(), from_utf8(initials));
417 author.setUsed(true);
418 authors_.record(author);
419 h_tracking_changes = "true";
420 h_output_changes = "true";
424 Author const & Preamble::getAuthor(std::string const & name) const
426 Author author(from_utf8(name), empty_docstring(), empty_docstring());
427 for (AuthorList::Authors::const_iterator it = authors_.begin();
428 it != authors_.end(); ++it)
431 static Author const dummy;
436 int Preamble::getSpecialTableColumnArguments(char c) const
438 map<char, int>::const_iterator it = special_columns_.find(c);
439 if (it == special_columns_.end())
445 void Preamble::add_package(string const & name, vector<string> & options)
447 // every package inherits the global options
448 if (used_packages.find(name) == used_packages.end())
449 used_packages[name] = split_options(h_options);
451 // Insert options passed via PassOptionsToPackage
452 for (auto const & p : extra_package_options_) {
453 if (p.first == name) {
454 vector<string> eo = getVectorFromString(p.second);
455 for (auto const & eoi : eo)
456 options.push_back(eoi);
460 vector<string> & v = used_packages[name];
461 v.insert(v.end(), options.begin(), options.end());
462 if (name == "jurabib") {
463 // Don't output the order argument (see the cite command
464 // handling code in text.cpp).
465 vector<string>::iterator end =
466 remove(options.begin(), options.end(), "natbiborder");
467 end = remove(options.begin(), end, "jurabiborder");
468 options.erase(end, options.end());
475 // Given is a string like "scaled=0.9" or "scale=0.9", return 0.9 * 100
476 bool scale_as_percentage(string const & scale, string & percentage)
478 if (contains(scale, '=')) {
479 string const value = support::split(scale, '=');
480 if (isStrDbl(value)) {
481 percentage = convert<string>(
482 static_cast<int>(100 * convert<double>(value)));
490 string remove_braces(string const & value)
494 if (value[0] == '{' && value[value.length()-1] == '}')
495 return value.substr(1, value.length()-2);
499 } // anonymous namespace
502 Preamble::Preamble() : one_language(true), explicit_babel(false),
503 title_layout_found(false), index_number(0), h_font_cjk_set(false)
507 h_biblio_style = "plain";
508 h_bibtex_command = "default";
509 h_cite_engine = "basic";
510 h_cite_engine_type = "default";
512 h_defskip = "medskip";
513 h_dynamic_quotes = false;
516 h_fontencoding = "default";
517 h_font_roman[0] = "default";
518 h_font_roman[1] = "default";
519 h_font_sans[0] = "default";
520 h_font_sans[1] = "default";
521 h_font_typewriter[0] = "default";
522 h_font_typewriter[1] = "default";
523 h_font_math[0] = "auto";
524 h_font_math[1] = "auto";
525 h_font_default_family = "default";
526 h_use_non_tex_fonts = false;
528 h_font_roman_osf = "false";
529 h_font_sans_osf = "false";
530 h_font_typewriter_osf = "false";
531 h_font_sf_scale[0] = "100";
532 h_font_sf_scale[1] = "100";
533 h_font_tt_scale[0] = "100";
534 h_font_tt_scale[1] = "100";
535 // h_font_roman_opts;
537 // h_font_typewriter_opts;
539 h_is_mathindent = "0";
540 h_math_numbering_side = "default";
541 h_graphics = "default";
542 h_default_output_format = "default";
543 h_html_be_strict = "false";
544 h_html_css_as_file = "0";
545 h_html_math_output = "0";
546 h_index[0] = "Index";
547 h_index_command = "default";
548 h_inputencoding = "auto-legacy";
549 h_justification = "true";
550 h_language = "english";
551 h_language_package = "none";
553 h_maintain_unincluded_children = "no";
557 h_output_changes = "false";
558 h_change_bars = "false";
560 //h_output_sync_macro
561 h_papercolumns = "1";
562 h_paperfontsize = "default";
563 h_paperorientation = "portrait";
564 h_paperpagestyle = "default";
566 h_papersize = "default";
567 h_paragraph_indentation = "default";
568 h_paragraph_separation = "indent";
573 h_pdf_bookmarks = "0";
574 h_pdf_bookmarksnumbered = "0";
575 h_pdf_bookmarksopen = "0";
576 h_pdf_bookmarksopenlevel = "1";
577 h_pdf_breaklinks = "0";
578 h_pdf_pdfborder = "0";
579 h_pdf_colorlinks = "0";
580 h_pdf_backref = "section";
581 h_pdf_pdfusetitle = "0";
583 //h_pdf_quoted_options;
584 h_quotes_style = "english";
586 h_shortcut[0] = "idx";
587 h_spacing = "single";
588 h_save_transient_properties = "true";
589 h_suppress_date = "false";
590 h_textclass = "article";
592 h_tracking_changes = "false";
593 h_use_bibtopic = "false";
594 h_use_dash_ligatures = "true";
595 h_use_indices = "false";
596 h_use_geometry = "false";
597 h_use_default_options = "false";
598 h_use_hyperref = "false";
599 h_use_microtype = "false";
600 h_use_lineno = "false";
601 h_use_refstyle = false;
602 h_use_minted = false;
603 h_use_packages["amsmath"] = "1";
604 h_use_packages["amssymb"] = "0";
605 h_use_packages["cancel"] = "0";
606 h_use_packages["esint"] = "1";
607 h_use_packages["mhchem"] = "0";
608 h_use_packages["mathdots"] = "0";
609 h_use_packages["mathtools"] = "0";
610 h_use_packages["stackrel"] = "0";
611 h_use_packages["stmaryrd"] = "0";
612 h_use_packages["undertilde"] = "0";
616 void Preamble::handle_hyperref(vector<string> & options)
618 // FIXME swallow inputencoding changes that might surround the
619 // hyperref setup if it was written by LyX
620 h_use_hyperref = "true";
621 // swallow "unicode=true", since LyX does always write that
622 vector<string>::iterator it =
623 find(options.begin(), options.end(), "unicode=true");
624 if (it != options.end())
626 it = find(options.begin(), options.end(), "pdfusetitle");
627 if (it != options.end()) {
628 h_pdf_pdfusetitle = "1";
631 string bookmarks = process_keyval_opt(options, "bookmarks");
632 if (bookmarks == "true")
633 h_pdf_bookmarks = "1";
634 else if (bookmarks == "false")
635 h_pdf_bookmarks = "0";
636 if (h_pdf_bookmarks == "1") {
637 string bookmarksnumbered =
638 process_keyval_opt(options, "bookmarksnumbered");
639 if (bookmarksnumbered == "true")
640 h_pdf_bookmarksnumbered = "1";
641 else if (bookmarksnumbered == "false")
642 h_pdf_bookmarksnumbered = "0";
643 string bookmarksopen =
644 process_keyval_opt(options, "bookmarksopen");
645 if (bookmarksopen == "true")
646 h_pdf_bookmarksopen = "1";
647 else if (bookmarksopen == "false")
648 h_pdf_bookmarksopen = "0";
649 if (h_pdf_bookmarksopen == "1") {
650 string bookmarksopenlevel =
651 process_keyval_opt(options, "bookmarksopenlevel");
652 if (!bookmarksopenlevel.empty())
653 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
656 string breaklinks = process_keyval_opt(options, "breaklinks");
657 if (breaklinks == "true")
658 h_pdf_breaklinks = "1";
659 else if (breaklinks == "false")
660 h_pdf_breaklinks = "0";
661 string pdfborder = process_keyval_opt(options, "pdfborder");
662 if (pdfborder == "{0 0 0}")
663 h_pdf_pdfborder = "1";
664 else if (pdfborder == "{0 0 1}")
665 h_pdf_pdfborder = "0";
666 string backref = process_keyval_opt(options, "backref");
667 if (!backref.empty())
668 h_pdf_backref = backref;
669 string colorlinks = process_keyval_opt(options, "colorlinks");
670 if (colorlinks == "true")
671 h_pdf_colorlinks = "1";
672 else if (colorlinks == "false")
673 h_pdf_colorlinks = "0";
674 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
675 if (!pdfpagemode.empty())
676 h_pdf_pagemode = pdfpagemode;
677 string pdftitle = process_keyval_opt(options, "pdftitle");
678 if (!pdftitle.empty()) {
679 h_pdf_title = remove_braces(pdftitle);
681 string pdfauthor = process_keyval_opt(options, "pdfauthor");
682 if (!pdfauthor.empty()) {
683 h_pdf_author = remove_braces(pdfauthor);
685 string pdfsubject = process_keyval_opt(options, "pdfsubject");
686 if (!pdfsubject.empty())
687 h_pdf_subject = remove_braces(pdfsubject);
688 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
689 if (!pdfkeywords.empty())
690 h_pdf_keywords = remove_braces(pdfkeywords);
691 if (!options.empty()) {
692 if (!h_pdf_quoted_options.empty())
693 h_pdf_quoted_options += ',';
694 h_pdf_quoted_options += join(options, ",");
700 void Preamble::handle_geometry(vector<string> & options)
702 h_use_geometry = "true";
703 vector<string>::iterator it;
705 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
706 h_paperorientation = "landscape";
710 // keyval version: "paper=letter" or "paper=letterpaper"
711 string paper = process_keyval_opt(options, "paper");
713 if (suffixIs(paper, "paper"))
714 paper = subst(paper, "paper", "");
715 // alternative version: "letterpaper"
716 handle_opt(options, known_latex_paper_sizes, paper);
717 if (suffixIs(paper, "paper"))
718 paper = subst(paper, "paper", "");
719 delete_opt(options, known_latex_paper_sizes);
723 char const * const * margin = known_paper_margins;
724 for (; *margin; ++margin) {
725 string value = process_keyval_opt(options, *margin);
726 if (!value.empty()) {
727 int k = margin - known_paper_margins;
728 string name = known_coded_paper_margins[k];
729 h_margins += '\\' + name + ' ' + value + '\n';
735 void Preamble::handle_package(Parser &p, string const & name,
736 string const & opts, bool in_lyx_preamble,
739 vector<string> options = split_options(opts);
740 add_package(name, options);
742 if (is_known(name, known_xetex_packages)) {
744 h_use_non_tex_fonts = true;
745 registerAutomaticallyLoadedPackage("fontspec");
746 if (h_inputencoding == "auto-legacy")
747 p.setEncoding("UTF-8");
750 // vector of all options for easier parsing and
752 vector<string> allopts = getVectorFromString(opts);
753 // this stores the potential extra options
760 // By default, we use the package name as LyX font name,
761 // so this only needs to be reset if these names differ
762 if (is_known(name, known_roman_font_packages))
763 h_font_roman[0] = name;
765 if (name == "ccfonts") {
766 for (auto const & opt : allopts) {
772 h_font_roman_opts = xopts;
776 if (name == "lmodern") {
777 for (auto const & opt : allopts) {
783 h_font_roman_opts = xopts;
787 if (name == "fourier") {
788 h_font_roman[0] = "utopia";
789 for (auto const & opt : allopts) {
791 h_font_roman_osf = "true";
794 if (opt == "expert") {
803 h_font_roman_opts = xopts;
807 if (name == "garamondx") {
808 for (auto const & opt : allopts) {
810 h_font_roman_osf = "true";
818 h_font_roman_opts = xopts;
822 if (name == "libertine") {
823 // this automatically invokes biolinum
824 h_font_sans[0] = "biolinum";
825 // as well as libertineMono
826 h_font_typewriter[0] = "libertine-mono";
827 for (auto const & opt : allopts) {
829 h_font_roman_osf = "true";
832 if (opt == "lining") {
833 h_font_roman_osf = "false";
841 h_font_roman_opts = xopts;
845 if (name == "libertineRoman" || name == "libertine-type1") {
846 h_font_roman[0] = "libertine";
847 // NOTE: contrary to libertine.sty, libertineRoman
848 // and libertine-type1 do not automatically invoke
849 // biolinum and libertineMono
850 if (opts == "lining")
851 h_font_roman_osf = "false";
852 else if (opts == "osf")
853 h_font_roman_osf = "true";
856 if (name == "MinionPro") {
857 h_font_roman[0] = "minionpro";
858 h_font_roman_osf = "true";
859 h_font_math[0] = "auto";
860 for (auto const & opt : allopts) {
862 h_font_roman_osf = "false";
865 if (opt == "onlytext") {
866 h_font_math[0] = "default";
874 h_font_roman_opts = xopts;
878 if (name == "mathdesign") {
879 for (auto const & opt : allopts) {
880 if (opt == "charter") {
881 h_font_roman[0] = "md-charter";
884 if (opt == "garamond") {
885 h_font_roman[0] = "md-garamond";
888 if (opt == "utopia") {
889 h_font_roman[0] = "md-utopia";
892 if (opt == "expert") {
894 h_font_roman_osf = "true";
900 else if (name == "mathpazo") {
901 h_font_roman[0] = "palatino";
902 for (auto const & opt : allopts) {
904 h_font_roman_osf = "true";
916 h_font_roman_opts = xopts;
920 else if (name == "mathptmx") {
921 h_font_roman[0] = "times";
922 for (auto const & opt : allopts) {
928 h_font_roman_opts = xopts;
932 if (name == "crimson")
933 h_font_roman[0] = "cochineal";
935 if (name == "cochineal") {
936 for (auto const & opt : allopts) {
937 if (opt == "osf" || opt == "oldstyle") {
938 h_font_roman_osf = "true";
941 if (opt == "proportional" || opt == "p")
948 h_font_roman_opts = xopts;
952 if (name == "CrimsonPro") {
953 h_font_roman_osf = "true";
954 for (auto const & opt : allopts) {
955 if (opt == "lf" || opt == "lining") {
956 h_font_roman_osf = "false";
959 if (opt == "proportional" || opt == "p")
961 if (opt == "medium") {
962 h_font_roman[0] = "CrimsonProMedium";
965 if (opt == "extralight") {
966 h_font_roman[0] = "CrimsonProExtraLight";
969 if (opt == "light") {
970 h_font_roman[0] = "CrimsonProLight";
978 h_font_roman_opts = xopts;
984 // font uses old-style figure
985 h_font_roman_osf = "true";
987 if (name == "paratype") {
988 // in this case all fonts are ParaType
989 h_font_roman[0] = "PTSerif-TLF";
990 h_font_sans[0] = "default";
991 h_font_typewriter[0] = "default";
994 if (name == "PTSerif")
995 h_font_roman[0] = "PTSerif-TLF";
997 if (name == "XCharter") {
998 h_font_roman[0] = "xcharter";
999 for (auto const & opt : allopts) {
1001 h_font_roman_osf = "true";
1009 h_font_roman_opts = xopts;
1013 if (name == "plex-serif") {
1014 h_font_roman[0] = "IBMPlexSerif";
1015 for (auto const & opt : allopts) {
1016 if (opt == "thin") {
1017 h_font_roman[0] = "IBMPlexSerifThin";
1020 if (opt == "extralight") {
1021 h_font_roman[0] = "IBMPlexSerifExtraLight";
1024 if (opt == "light") {
1025 h_font_roman[0] = "IBMPlexSerifLight";
1033 h_font_roman_opts = xopts;
1037 if (name == "noto-serif" || name == "noto") {
1044 bool extralight = false;
1046 bool medium = false;
1049 if (name == "noto") {
1054 // Since the options might apply to different shapes,
1055 // we need to parse all options first and then handle them.
1056 for (auto const & opt : allopts) {
1057 if (opt == "regular")
1067 if (opt == "thin") {
1071 if (opt == "extralight") {
1075 if (opt == "light") {
1079 if (opt == "medium") {
1090 if (opt == "nott") {
1098 if (prefixIs(opt, "scaled=")) {
1107 // handle options that might affect different shapes
1108 if (name == "noto-serif" || rm) {
1110 h_font_roman[0] = "NotoSerifThin";
1111 else if (extralight)
1112 h_font_roman[0] = "NotoSerifExtralight";
1114 h_font_roman[0] = "NotoSerifLight";
1116 h_font_roman[0] = "NotoSerifMedium";
1118 h_font_roman[0] = "NotoSerifRegular";
1120 h_font_roman_osf = "true";
1122 h_font_roman_opts = xopts;
1124 if (name == "noto" && sf) {
1126 h_font_sans[0] = "NotoSansThin";
1127 else if (extralight)
1128 h_font_sans[0] = "NotoSansExtralight";
1130 h_font_sans[0] = "NotoSansLight";
1132 h_font_sans[0] = "NotoSansMedium";
1134 h_font_sans[0] = "NotoSansRegular";
1136 h_font_sans_osf = "true";
1138 scale_as_percentage(scl, h_font_sf_scale[0]);
1140 h_font_sans_opts = xopts;
1142 if (name == "noto" && tt) {
1143 h_font_typewriter[0] = "NotoMonoRegular";
1145 h_font_typewriter_osf = "true";
1147 scale_as_percentage(scl, h_font_tt_scale[0]);
1149 h_font_typewriter_opts = xopts;
1153 if (name == "sourceserifpro") {
1154 h_font_roman[0] = "ADOBESourceSerifPro";
1155 for (auto const & opt : allopts) {
1157 h_font_roman_osf = "true";
1165 h_font_roman_opts = xopts;
1173 // By default, we use the package name as LyX font name,
1174 // so this only needs to be reset if these names differ.
1175 // Also, we handle the scaling option here generally.
1176 if (is_known(name, known_sans_font_packages)) {
1177 h_font_sans[0] = name;
1178 if (contains(opts, "scale")) {
1179 vector<string>::iterator it = allopts.begin();
1180 for (; it != allopts.end() ; ++it) {
1181 string const opt = *it;
1182 if (prefixIs(opt, "scaled=") || prefixIs(opt, "scale=")) {
1183 if (scale_as_percentage(opt, h_font_sf_scale[0])) {
1192 if (name == "biolinum" || name == "biolinum-type1") {
1193 h_font_sans[0] = "biolinum";
1194 for (auto const & opt : allopts) {
1195 if (prefixIs(opt, "osf")) {
1196 h_font_sans_osf = "true";
1204 h_font_sans_opts = xopts;
1208 if (name == "cantarell") {
1209 for (auto const & opt : allopts) {
1210 if (opt == "defaultsans")
1212 if (prefixIs(opt, "oldstyle")) {
1213 h_font_sans_osf = "true";
1221 h_font_sans_opts = xopts;
1225 if (name == "Chivo") {
1226 for (auto const & opt : allopts) {
1227 if (opt == "thin") {
1228 h_font_roman[0] = "ChivoThin";
1231 if (opt == "light") {
1232 h_font_roman[0] = "ChivoLight";
1235 if (opt == "regular") {
1236 h_font_roman[0] = "Chivo";
1239 if (opt == "medium") {
1240 h_font_roman[0] = "ChivoMedium";
1243 if (prefixIs(opt, "oldstyle")) {
1244 h_font_sans_osf = "true";
1252 h_font_sans_opts = xopts;
1256 if (name == "PTSans") {
1257 h_font_sans[0] = "PTSans-TLF";
1260 if (name == "FiraSans") {
1261 h_font_sans_osf = "true";
1262 for (auto const & opt : allopts) {
1263 if (opt == "book") {
1264 h_font_sans[0] = "FiraSansBook";
1267 if (opt == "thin") {
1270 if (opt == "extralight") {
1271 h_font_sans[0] = "FiraSansExtralight";
1274 if (opt == "light") {
1275 h_font_sans[0] = "FiraSansLight";
1278 if (opt == "ultralight") {
1279 h_font_sans[0] = "FiraSansUltralight";
1282 if (opt == "thin") {
1283 h_font_sans[0] = "FiraSansThin";
1286 if (opt == "lf" || opt == "lining") {
1287 h_font_sans_osf = "false";
1295 h_font_sans_opts = xopts;
1299 if (name == "plex-sans") {
1300 h_font_sans[0] = "IBMPlexSans";
1301 for (auto const & opt : allopts) {
1302 if (opt == "condensed") {
1303 h_font_sans[0] = "IBMPlexSansCondensed";
1306 if (opt == "thin") {
1307 h_font_sans[0] = "IBMPlexSansThin";
1310 if (opt == "extralight") {
1311 h_font_sans[0] = "IBMPlexSansExtraLight";
1314 if (opt == "light") {
1315 h_font_sans[0] = "IBMPlexSansLight";
1323 h_font_sans_opts = xopts;
1327 if (name == "noto-sans") {
1328 h_font_sans[0] = "NotoSansRegular";
1329 for (auto const & opt : allopts) {
1330 if (opt == "regular")
1332 if (opt == "medium") {
1333 h_font_sans[0] = "NotoSansMedium";
1336 if (opt == "thin") {
1337 h_font_sans[0] = "NotoSansThin";
1340 if (opt == "extralight") {
1341 h_font_sans[0] = "NotoSansExtralight";
1344 if (opt == "light") {
1345 h_font_sans[0] = "NotoSansLight";
1349 h_font_sans_osf = "true";
1357 h_font_sans_opts = xopts;
1361 if (name == "sourcesanspro") {
1362 h_font_sans[0] = "ADOBESourceSansPro";
1363 for (auto const & opt : allopts) {
1365 h_font_sans_osf = "true";
1373 h_font_sans_opts = xopts;
1381 // By default, we use the package name as LyX font name,
1382 // so this only needs to be reset if these names differ.
1383 // Also, we handle the scaling option here generally.
1384 if (is_known(name, known_typewriter_font_packages)) {
1385 h_font_typewriter[0] = name;
1386 if (contains(opts, "scale")) {
1387 vector<string>::iterator it = allopts.begin();
1388 for (; it != allopts.end() ; ++it) {
1389 string const opt = *it;
1390 if (prefixIs(opt, "scaled=") || prefixIs(opt, "scale=")) {
1391 if (scale_as_percentage(opt, h_font_tt_scale[0])) {
1400 if (name == "libertineMono" || name == "libertineMono-type1")
1401 h_font_typewriter[0] = "libertine-mono";
1403 if (name == "FiraMono") {
1404 h_font_typewriter_osf = "true";
1405 for (auto const & opt : allopts) {
1406 if (opt == "lf" || opt == "lining") {
1407 h_font_typewriter_osf = "false";
1415 h_font_typewriter_opts = xopts;
1419 if (name == "PTMono")
1420 h_font_typewriter[0] = "PTMono-TLF";
1422 if (name == "plex-mono") {
1423 h_font_typewriter[0] = "IBMPlexMono";
1424 for (auto const & opt : allopts) {
1425 if (opt == "thin") {
1426 h_font_typewriter[0] = "IBMPlexMonoThin";
1429 if (opt == "extralight") {
1430 h_font_typewriter[0] = "IBMPlexMonoExtraLight";
1433 if (opt == "light") {
1434 h_font_typewriter[0] = "IBMPlexMonoLight";
1442 h_font_typewriter_opts = xopts;
1446 if (name == "noto-mono") {
1447 h_font_typewriter[0] = "NotoMonoRegular";
1448 for (auto const & opt : allopts) {
1449 if (opt == "regular")
1456 h_font_typewriter_opts = xopts;
1460 if (name == "sourcecodepro") {
1461 h_font_typewriter[0] = "ADOBESourceCodePro";
1462 for (auto const & opt : allopts) {
1464 h_font_typewriter_osf = "true";
1472 h_font_typewriter_opts = xopts;
1480 // By default, we use the package name as LyX font name,
1481 // so this only needs to be reset if these names differ.
1482 if (is_known(name, known_math_font_packages))
1483 h_font_math[0] = name;
1485 if (name == "newtxmath") {
1487 h_font_math[0] = "newtxmath";
1488 else if (opts == "garamondx")
1489 h_font_math[0] = "garamondx-ntxm";
1490 else if (opts == "libertine")
1491 h_font_math[0] = "libertine-ntxm";
1492 else if (opts == "minion")
1493 h_font_math[0] = "minion-ntxm";
1494 else if (opts == "cochineal")
1495 h_font_math[0] = "cochineal-ntxm";
1498 if (name == "iwona")
1500 h_font_math[0] = "iwona-math";
1502 if (name == "kurier")
1504 h_font_math[0] = "kurier-math";
1506 // after the detection and handling of special cases, we can remove the
1507 // fonts, otherwise they would appear in the preamble, see bug #7856
1508 if (is_known(name, known_roman_font_packages) || is_known(name, known_sans_font_packages)
1509 || is_known(name, known_typewriter_font_packages) || is_known(name, known_math_font_packages))
1511 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
1512 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
1513 name == "esint" || name == "mhchem" || name == "mathdots" ||
1514 name == "mathtools" || name == "stackrel" ||
1515 name == "stmaryrd" || name == "undertilde") {
1516 h_use_packages[name] = "2";
1517 registerAutomaticallyLoadedPackage(name);
1520 else if (name == "babel") {
1521 h_language_package = "default";
1522 // One might think we would have to do nothing if babel is loaded
1523 // without any options to prevent pollution of the preamble with this
1524 // babel call in every roundtrip.
1525 // But the user could have defined babel-specific things afterwards. So
1526 // we need to keep it in the preamble to prevent cases like bug #7861.
1527 if (!opts.empty()) {
1528 // check if more than one option was used - used later for inputenc
1529 if (options.begin() != options.end() - 1)
1530 one_language = false;
1531 // babel takes the last language of the option of its \usepackage
1532 // call as document language. If there is no such language option, the
1533 // last language in the documentclass options is used.
1534 handle_opt(options, known_languages, h_language);
1535 // translate the babel name to a LyX name
1536 h_language = babel2lyx(h_language);
1537 if (h_language == "japanese") {
1538 // For Japanese, the encoding isn't indicated in the source
1539 // file, and there's really not much we can do. We could
1540 // 1) offer a list of possible encodings to choose from, or
1541 // 2) determine the encoding of the file by inspecting it.
1542 // For the time being, we leave the encoding alone so that
1543 // we don't get iconv errors when making a wrong guess, and
1544 // we will output a note at the top of the document
1545 // explaining what to do.
1546 Encoding const * const enc = encodings.fromIconvName(
1547 p.getEncoding(), Encoding::japanese, false);
1549 h_inputencoding = enc->name();
1550 is_nonCJKJapanese = true;
1551 // in this case babel can be removed from the preamble
1552 registerAutomaticallyLoadedPackage("babel");
1554 // If babel is called with options, LyX puts them by default into the
1555 // document class options. This works for most languages, except
1556 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
1557 // perhaps in future others.
1558 // Therefore keep the babel call as it is as the user might have
1560 string const babelcall = "\\usepackage[" + opts + "]{babel}\n";
1561 if (!contains(h_preamble.str(), babelcall))
1562 h_preamble << babelcall;
1564 delete_opt(options, known_languages);
1566 if (!contains(h_preamble.str(), "\\usepackage{babel}\n"))
1567 h_preamble << "\\usepackage{babel}\n";
1568 explicit_babel = true;
1572 else if (name == "polyglossia") {
1573 h_language_package = "default";
1574 h_default_output_format = "pdf4";
1575 h_use_non_tex_fonts = true;
1577 registerAutomaticallyLoadedPackage("xunicode");
1578 if (h_inputencoding == "auto-legacy")
1579 p.setEncoding("UTF-8");
1582 else if (name == "CJK") {
1583 // set the encoding to "auto-legacy" because it might be set to "auto-legacy-plain" by the babel handling
1584 // and this would not be correct for CJK
1585 if (h_inputencoding == "auto-legacy-plain")
1586 h_inputencoding = "auto-legacy";
1587 registerAutomaticallyLoadedPackage("CJK");
1590 else if (name == "CJKutf8") {
1591 h_inputencoding = "utf8-cjk";
1592 p.setEncoding("UTF-8");
1593 registerAutomaticallyLoadedPackage("CJKutf8");
1596 else if (name == "fontenc") {
1597 h_fontencoding = getStringFromVector(options, ",");
1601 else if (name == "inputenc" || name == "luainputenc") {
1602 // h_inputencoding is only set when there is not more than one
1603 // inputenc option because otherwise h_inputencoding must be
1604 // set to "auto-legacy" (the default encodings of the document's languages)
1605 // Therefore check that exactly one option is passed to inputenc.
1606 // It is also only set when there is not more than one babel
1608 if (!options.empty()) {
1609 string const encoding = options.back();
1610 Encoding const * const enc = encodings.fromLaTeXName(
1611 encoding, Encoding::inputenc, true);
1613 if (!detectEncoding)
1614 cerr << "Unknown encoding " << encoding
1615 << ". Ignoring." << std::endl;
1617 if (!enc->unsafe() && options.size() == 1 && one_language == true)
1618 h_inputencoding = enc->name();
1619 p.setEncoding(enc->iconvName());
1625 else if (name == "srcltx") {
1626 h_output_sync = "1";
1627 if (!opts.empty()) {
1628 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
1631 h_output_sync_macro = "\\usepackage{srcltx}";
1634 else if (is_known(name, known_old_language_packages)) {
1635 // known language packages from the times before babel
1636 // if they are found and not also babel, they will be used as
1637 // custom language package
1638 h_language_package = "\\usepackage{" + name + "}";
1641 else if (name == "lyxskak") {
1642 // ignore this and its options
1643 const char * const o[] = {"ps", "mover", 0};
1644 delete_opt(options, o);
1647 else if (is_known(name, known_lyx_packages) && options.empty()) {
1648 if (name == "splitidx")
1649 h_use_indices = "true";
1650 else if (name == "minted")
1651 h_use_minted = true;
1652 else if (name == "refstyle")
1653 h_use_refstyle = true;
1654 else if (name == "prettyref")
1655 h_use_refstyle = false;
1656 if (!in_lyx_preamble) {
1657 h_preamble << package_beg_sep << name
1658 << package_mid_sep << "\\usepackage{"
1660 if (p.next_token().cat() == catNewline ||
1661 (p.next_token().cat() == catSpace &&
1662 p.next_next_token().cat() == catNewline))
1664 h_preamble << package_end_sep;
1668 else if (name == "geometry")
1669 handle_geometry(options);
1671 else if (name == "subfig")
1672 ; // ignore this FIXME: Use the package separator mechanism instead
1674 else if (char const * const * where = is_known(name, known_languages))
1675 h_language = known_coded_languages[where - known_languages];
1677 else if (name == "natbib") {
1678 h_biblio_style = "plainnat";
1679 h_cite_engine = "natbib";
1680 h_cite_engine_type = "authoryear";
1681 vector<string>::iterator it =
1682 find(options.begin(), options.end(), "authoryear");
1683 if (it != options.end())
1686 it = find(options.begin(), options.end(), "numbers");
1687 if (it != options.end()) {
1688 h_cite_engine_type = "numerical";
1692 if (!options.empty())
1693 h_biblio_options = join(options, ",");
1696 else if (name == "biblatex") {
1697 h_biblio_style = "plainnat";
1698 h_cite_engine = "biblatex";
1699 h_cite_engine_type = "authoryear";
1701 vector<string>::iterator it =
1702 find(options.begin(), options.end(), "natbib");
1703 if (it != options.end()) {
1705 h_cite_engine = "biblatex-natbib";
1707 opt = process_keyval_opt(options, "natbib");
1709 h_cite_engine = "biblatex-natbib";
1711 opt = process_keyval_opt(options, "style");
1713 h_biblatex_citestyle = opt;
1714 h_biblatex_bibstyle = opt;
1716 opt = process_keyval_opt(options, "citestyle");
1718 h_biblatex_citestyle = opt;
1719 opt = process_keyval_opt(options, "bibstyle");
1721 h_biblatex_bibstyle = opt;
1723 opt = process_keyval_opt(options, "refsection");
1725 if (opt == "none" || opt == "part"
1726 || opt == "chapter" || opt == "section"
1727 || opt == "subsection")
1730 cerr << "Ignoring unknown refsection value '"
1733 opt = process_keyval_opt(options, "bibencoding");
1736 if (!options.empty()) {
1737 h_biblio_options = join(options, ",");
1742 else if (name == "jurabib") {
1743 h_biblio_style = "jurabib";
1744 h_cite_engine = "jurabib";
1745 h_cite_engine_type = "authoryear";
1746 if (!options.empty())
1747 h_biblio_options = join(options, ",");
1750 else if (name == "bibtopic")
1751 h_use_bibtopic = "true";
1753 else if (name == "chapterbib")
1754 h_multibib = "child";
1756 else if (name == "hyperref")
1757 handle_hyperref(options);
1759 else if (name == "algorithm2e") {
1760 // Load "algorithm2e" module
1761 addModule("algorithm2e");
1762 // Add the package options to the global document options
1763 if (!options.empty()) {
1764 if (h_options.empty())
1765 h_options = join(options, ",");
1767 h_options += ',' + join(options, ",");
1770 else if (name == "microtype") {
1771 //we internally support only microtype without params
1772 if (options.empty())
1773 h_use_microtype = "true";
1775 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1778 else if (name == "lineno") {
1779 h_use_lineno = "true";
1780 if (!options.empty()) {
1781 h_lineno_options = join(options, ",");
1786 else if (name == "changebar")
1787 h_output_changes = "true";
1789 else if (!in_lyx_preamble) {
1790 if (options.empty())
1791 h_preamble << "\\usepackage{" << name << '}';
1793 h_preamble << "\\usepackage[" << opts << "]{"
1797 if (p.next_token().cat() == catNewline ||
1798 (p.next_token().cat() == catSpace &&
1799 p.next_next_token().cat() == catNewline))
1803 // We need to do something with the options...
1804 if (!options.empty() && !detectEncoding)
1805 cerr << "Ignoring options '" << join(options, ",")
1806 << "' of package " << name << '.' << endl;
1808 // remove the whitespace
1813 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1816 Token t = p.get_token();
1817 if (t.cat() == catEscape &&
1818 is_known(t.cs(), known_if_commands))
1819 handle_if(p, in_lyx_preamble);
1821 if (!in_lyx_preamble)
1822 h_preamble << t.asInput();
1823 if (t.cat() == catEscape && t.cs() == "fi")
1830 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1832 if (contains(h_float_placement, "H"))
1833 registerAutomaticallyLoadedPackage("float");
1834 if (h_spacing != "single" && h_spacing != "default")
1835 registerAutomaticallyLoadedPackage("setspace");
1836 if (h_use_packages["amsmath"] == "2") {
1837 // amsbsy and amstext are already provided by amsmath
1838 registerAutomaticallyLoadedPackage("amsbsy");
1839 registerAutomaticallyLoadedPackage("amstext");
1842 // output the LyX file settings
1843 // Important: Keep the version formatting in sync with LyX and
1844 // lyx2lyx (bug 7951)
1845 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1846 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1847 << lyx_version_minor << '\n'
1848 << "\\lyxformat " << LYX_FORMAT << '\n'
1849 << "\\begin_document\n"
1850 << "\\begin_header\n"
1851 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1852 << "\\origin " << origin << "\n"
1853 << "\\textclass " << h_textclass << "\n";
1854 string const raw = subdoc ? empty_string() : h_preamble.str();
1856 os << "\\begin_preamble\n";
1857 for (string::size_type i = 0; i < raw.size(); ++i) {
1858 if (raw[i] == package_beg_sep) {
1859 // Here follows some package loading code that
1860 // must be skipped if the package is loaded
1862 string::size_type j = raw.find(package_mid_sep, i);
1863 if (j == string::npos)
1865 string::size_type k = raw.find(package_end_sep, j);
1866 if (k == string::npos)
1868 string const package = raw.substr(i + 1, j - i - 1);
1869 string const replacement = raw.substr(j + 1, k - j - 1);
1870 if (auto_packages.find(package) == auto_packages.end())
1876 os << "\n\\end_preamble\n";
1878 if (!h_options.empty())
1879 os << "\\options " << h_options << "\n";
1880 os << "\\use_default_options " << h_use_default_options << "\n";
1881 if (!used_modules.empty()) {
1882 os << "\\begin_modules\n";
1883 vector<string>::const_iterator const end = used_modules.end();
1884 vector<string>::const_iterator it = used_modules.begin();
1885 for (; it != end; ++it)
1887 os << "\\end_modules\n";
1889 if (!h_includeonlys.empty()) {
1890 os << "\\begin_includeonly\n";
1891 for (auto const & iofile : h_includeonlys)
1892 os << iofile << '\n';
1893 os << "\\end_includeonly\n";
1895 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1896 << "\\language " << h_language << "\n"
1897 << "\\language_package " << h_language_package << "\n"
1898 << "\\inputencoding " << h_inputencoding << "\n"
1899 << "\\fontencoding " << h_fontencoding << "\n"
1900 << "\\font_roman \"" << h_font_roman[0]
1901 << "\" \"" << h_font_roman[1] << "\"\n"
1902 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1903 << "\\font_typewriter \"" << h_font_typewriter[0]
1904 << "\" \"" << h_font_typewriter[1] << "\"\n"
1905 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1906 << "\\font_default_family " << h_font_default_family << "\n"
1907 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1908 << "\\font_sc " << h_font_sc << "\n"
1909 << "\\font_roman_osf " << h_font_roman_osf << "\n"
1910 << "\\font_sans_osf " << h_font_sans_osf << "\n"
1911 << "\\font_typewriter_osf " << h_font_typewriter_osf << "\n";
1912 if (!h_font_roman_opts.empty())
1913 os << "\\font_roman_opts \"" << h_font_roman_opts << "\"" << '\n';
1914 os << "\\font_sf_scale " << h_font_sf_scale[0]
1915 << ' ' << h_font_sf_scale[1] << '\n';
1916 if (!h_font_sans_opts.empty())
1917 os << "\\font_sans_opts \"" << h_font_sans_opts << "\"" << '\n';
1918 os << "\\font_tt_scale " << h_font_tt_scale[0]
1919 << ' ' << h_font_tt_scale[1] << '\n';
1920 if (!h_font_cjk.empty())
1921 os << "\\font_cjk " << h_font_cjk << '\n';
1922 if (!h_font_typewriter_opts.empty())
1923 os << "\\font_typewriter_opts \"" << h_font_typewriter_opts << "\"" << '\n';
1924 os << "\\use_microtype " << h_use_microtype << '\n'
1925 << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
1926 << "\\graphics " << h_graphics << '\n'
1927 << "\\default_output_format " << h_default_output_format << "\n"
1928 << "\\output_sync " << h_output_sync << "\n";
1929 if (h_output_sync == "1")
1930 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1931 os << "\\bibtex_command " << h_bibtex_command << "\n"
1932 << "\\index_command " << h_index_command << "\n";
1933 if (!h_float_placement.empty())
1934 os << "\\float_placement " << h_float_placement << "\n";
1935 os << "\\paperfontsize " << h_paperfontsize << "\n"
1936 << "\\spacing " << h_spacing << "\n"
1937 << "\\use_hyperref " << h_use_hyperref << '\n';
1938 if (h_use_hyperref == "true") {
1939 if (!h_pdf_title.empty())
1940 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1941 if (!h_pdf_author.empty())
1942 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1943 if (!h_pdf_subject.empty())
1944 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1945 if (!h_pdf_keywords.empty())
1946 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1947 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1948 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1949 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1950 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1951 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1952 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1953 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1954 "\\pdf_backref " << h_pdf_backref << "\n"
1955 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1956 if (!h_pdf_pagemode.empty())
1957 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1958 if (!h_pdf_quoted_options.empty())
1959 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1961 os << "\\papersize " << h_papersize << "\n"
1962 << "\\use_geometry " << h_use_geometry << '\n';
1963 for (map<string, string>::const_iterator it = h_use_packages.begin();
1964 it != h_use_packages.end(); ++it)
1965 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1966 os << "\\cite_engine " << h_cite_engine << '\n'
1967 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1968 << "\\biblio_style " << h_biblio_style << "\n"
1969 << "\\use_bibtopic " << h_use_bibtopic << "\n";
1970 if (!h_biblio_options.empty())
1971 os << "\\biblio_options " << h_biblio_options << "\n";
1972 if (!h_biblatex_bibstyle.empty())
1973 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
1974 if (!h_biblatex_citestyle.empty())
1975 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
1976 if (!h_multibib.empty())
1977 os << "\\multibib " << h_multibib << "\n";
1978 os << "\\use_indices " << h_use_indices << "\n"
1979 << "\\paperorientation " << h_paperorientation << '\n'
1980 << "\\suppress_date " << h_suppress_date << '\n'
1981 << "\\justification " << h_justification << '\n'
1982 << "\\use_refstyle " << h_use_refstyle << '\n'
1983 << "\\use_minted " << h_use_minted << '\n'
1984 << "\\use_lineno " << h_use_lineno << '\n';
1985 if (!h_lineno_options.empty())
1986 os << "\\lineno_options " << h_lineno_options << '\n';
1987 if (!h_fontcolor.empty())
1988 os << "\\fontcolor " << h_fontcolor << '\n';
1989 if (!h_notefontcolor.empty())
1990 os << "\\notefontcolor " << h_notefontcolor << '\n';
1991 if (!h_backgroundcolor.empty())
1992 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1993 if (!h_boxbgcolor.empty())
1994 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1995 if (index_number != 0)
1996 for (int i = 0; i < index_number; i++) {
1997 os << "\\index " << h_index[i] << '\n'
1998 << "\\shortcut " << h_shortcut[i] << '\n'
1999 << "\\color " << h_color << '\n'
2003 os << "\\index " << h_index[0] << '\n'
2004 << "\\shortcut " << h_shortcut[0] << '\n'
2005 << "\\color " << h_color << '\n'
2009 << "\\secnumdepth " << h_secnumdepth << "\n"
2010 << "\\tocdepth " << h_tocdepth << "\n"
2011 << "\\paragraph_separation " << h_paragraph_separation << "\n";
2012 if (h_paragraph_separation == "skip")
2013 os << "\\defskip " << h_defskip << "\n";
2015 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
2016 os << "\\is_math_indent " << h_is_mathindent << "\n";
2017 if (!h_mathindentation.empty())
2018 os << "\\math_indentation " << h_mathindentation << "\n";
2019 os << "\\math_numbering_side " << h_math_numbering_side << "\n";
2020 os << "\\quotes_style " << h_quotes_style << "\n"
2021 << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
2022 << "\\papercolumns " << h_papercolumns << "\n"
2023 << "\\papersides " << h_papersides << "\n"
2024 << "\\paperpagestyle " << h_paperpagestyle << "\n";
2025 if (!h_listings_params.empty())
2026 os << "\\listings_params " << h_listings_params << "\n";
2027 os << "\\tracking_changes " << h_tracking_changes << "\n"
2028 << "\\output_changes " << h_output_changes << "\n"
2029 << "\\change_bars " << h_change_bars << "\n"
2030 << "\\html_math_output " << h_html_math_output << "\n"
2031 << "\\html_css_as_file " << h_html_css_as_file << "\n"
2032 << "\\html_be_strict " << h_html_be_strict << "\n"
2034 << "\\end_header\n\n"
2035 << "\\begin_body\n";
2040 void Preamble::parse(Parser & p, string const & forceclass,
2041 TeX2LyXDocClass & tc)
2043 // initialize fixed types
2044 special_columns_['D'] = 3;
2045 parse(p, forceclass, false, tc);
2049 void Preamble::parse(Parser & p, string const & forceclass,
2050 bool detectEncoding, TeX2LyXDocClass & tc)
2052 bool is_full_document = false;
2053 bool is_lyx_file = false;
2054 bool in_lyx_preamble = false;
2056 // determine whether this is a full document or a fragment for inclusion
2058 Token const & t = p.get_token();
2060 if (t.cat() == catEscape && t.cs() == "documentclass") {
2061 is_full_document = true;
2067 if (detectEncoding && !is_full_document)
2070 while (is_full_document && p.good()) {
2071 if (detectEncoding && h_inputencoding != "auto-legacy" &&
2072 h_inputencoding != "auto-legacy-plain")
2075 Token const & t = p.get_token();
2078 if (!detectEncoding)
2079 cerr << "t: " << t << '\n';
2085 if (!in_lyx_preamble &&
2086 (t.cat() == catLetter ||
2087 t.cat() == catSuper ||
2088 t.cat() == catSub ||
2089 t.cat() == catOther ||
2090 t.cat() == catMath ||
2091 t.cat() == catActive ||
2092 t.cat() == catBegin ||
2093 t.cat() == catEnd ||
2094 t.cat() == catAlign ||
2095 t.cat() == catParameter)) {
2096 h_preamble << t.cs();
2100 if (!in_lyx_preamble &&
2101 (t.cat() == catSpace || t.cat() == catNewline)) {
2102 h_preamble << t.asInput();
2106 if (t.cat() == catComment) {
2107 static regex const islyxfile("%% LyX .* created this file");
2108 static regex const usercommands("User specified LaTeX commands");
2110 string const comment = t.asInput();
2112 // magically switch encoding default if it looks like XeLaTeX
2113 static string const magicXeLaTeX =
2114 "% This document must be compiled with XeLaTeX ";
2115 if (comment.size() > magicXeLaTeX.size()
2116 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
2117 && h_inputencoding == "auto-legacy") {
2118 if (!detectEncoding)
2119 cerr << "XeLaTeX comment found, switching to UTF8\n";
2120 h_inputencoding = "utf8";
2123 if (regex_search(comment, sub, islyxfile)) {
2125 in_lyx_preamble = true;
2126 } else if (is_lyx_file
2127 && regex_search(comment, sub, usercommands))
2128 in_lyx_preamble = false;
2129 else if (!in_lyx_preamble)
2130 h_preamble << t.asInput();
2134 if (t.cs() == "PassOptionsToPackage") {
2135 string const poptions = p.getArg('{', '}');
2136 string const package = p.verbatim_item();
2137 extra_package_options_.insert(make_pair(package, poptions));
2141 if (t.cs() == "pagestyle") {
2142 h_paperpagestyle = p.verbatim_item();
2146 if (t.cs() == "setdefaultlanguage") {
2148 // We don't yet care about non-language variant options
2149 // because LyX doesn't support this yet, see bug #8214
2151 string langopts = p.getOpt();
2152 // check if the option contains a variant, if yes, extract it
2153 string::size_type pos_var = langopts.find("variant");
2154 string::size_type i = langopts.find(',', pos_var);
2155 string::size_type k = langopts.find('=', pos_var);
2156 if (pos_var != string::npos){
2158 if (i == string::npos)
2159 variant = langopts.substr(k + 1, langopts.length() - k - 2);
2161 variant = langopts.substr(k + 1, i - k - 1);
2162 h_language = variant;
2166 h_language = p.verbatim_item();
2167 //finally translate the poyglossia name to a LyX name
2168 h_language = polyglossia2lyx(h_language);
2172 if (t.cs() == "setotherlanguage") {
2173 // We don't yet care about the option because LyX doesn't
2174 // support this yet, see bug #8214
2175 p.hasOpt() ? p.getOpt() : string();
2180 if (t.cs() == "setmainfont") {
2181 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2182 h_font_roman[1] = p.getArg('{', '}');
2183 if (!fontopts.empty()) {
2184 vector<string> opts = getVectorFromString(fontopts);
2186 for (auto const & opt : opts) {
2187 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2190 if (!fontopts.empty())
2194 h_font_roman_opts = fontopts;
2199 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
2200 // LyX currently only supports the scale option
2201 string scale, fontopts;
2203 fontopts = p.getArg('[', ']');
2204 if (!fontopts.empty()) {
2205 vector<string> opts = getVectorFromString(fontopts);
2207 for (auto const & opt : opts) {
2208 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2211 if (prefixIs(opt, "Scale=")) {
2212 scale_as_percentage(opt, scale);
2215 if (!fontopts.empty())
2221 if (t.cs() == "setsansfont") {
2223 h_font_sf_scale[1] = scale;
2224 h_font_sans[1] = p.getArg('{', '}');
2225 if (!fontopts.empty())
2226 h_font_sans_opts = fontopts;
2229 h_font_tt_scale[1] = scale;
2230 h_font_typewriter[1] = p.getArg('{', '}');
2231 if (!fontopts.empty())
2232 h_font_typewriter_opts = fontopts;
2237 if (t.cs() == "babelfont") {
2239 h_use_non_tex_fonts = true;
2240 h_language_package = "babel";
2241 if (h_inputencoding == "auto-legacy")
2242 p.setEncoding("UTF-8");
2243 // we don't care about the lang option
2244 string const lang = p.hasOpt() ? p.getArg('[', ']') : string();
2245 string const family = p.getArg('{', '}');
2246 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2247 string const fontname = p.getArg('{', '}');
2248 if (lang.empty() && family == "rm") {
2249 h_font_roman[1] = fontname;
2250 if (!fontopts.empty()) {
2251 vector<string> opts = getVectorFromString(fontopts);
2253 for (auto const & opt : opts) {
2254 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2257 if (!fontopts.empty())
2261 h_font_roman_opts = fontopts;
2264 } else if (lang.empty() && (family == "sf" || family == "tt")) {
2266 if (!fontopts.empty()) {
2267 vector<string> opts = getVectorFromString(fontopts);
2269 for (auto const & opt : opts) {
2270 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2273 if (prefixIs(opt, "Scale=")) {
2274 scale_as_percentage(opt, scale);
2277 if (!fontopts.empty())
2282 if (family == "sf") {
2284 h_font_sf_scale[1] = scale;
2285 h_font_sans[1] = fontname;
2286 if (!fontopts.empty())
2287 h_font_sans_opts = fontopts;
2290 h_font_tt_scale[1] = scale;
2291 h_font_typewriter[1] = fontname;
2292 if (!fontopts.empty())
2293 h_font_typewriter_opts = fontopts;
2297 // not rm, sf or tt or lang specific
2298 h_preamble << '\\' << t.cs();
2300 h_preamble << '[' << lang << ']';
2301 h_preamble << '{' << family << '}';
2302 if (!fontopts.empty())
2303 h_preamble << '[' << fontopts << ']';
2304 h_preamble << '{' << fontname << '}' << '\n';
2309 if (t.cs() == "date") {
2310 string argument = p.getArg('{', '}');
2311 if (argument.empty())
2312 h_suppress_date = "true";
2314 h_preamble << t.asInput() << '{' << argument << '}';
2318 if (t.cs() == "color") {
2319 string const space =
2320 (p.hasOpt() ? p.getOpt() : string());
2321 string argument = p.getArg('{', '}');
2322 // check the case that a standard color is used
2323 if (space.empty() && is_known(argument, known_basic_colors)) {
2324 h_fontcolor = rgbcolor2code(argument);
2325 registerAutomaticallyLoadedPackage("color");
2326 } else if (space.empty() && argument == "document_fontcolor")
2327 registerAutomaticallyLoadedPackage("color");
2328 // check the case that LyX's document_fontcolor is defined
2329 // but not used for \color
2331 h_preamble << t.asInput();
2333 h_preamble << space;
2334 h_preamble << '{' << argument << '}';
2335 // the color might already be set because \definecolor
2336 // is parsed before this
2342 if (t.cs() == "pagecolor") {
2343 string argument = p.getArg('{', '}');
2344 // check the case that a standard color is used
2345 if (is_known(argument, known_basic_colors)) {
2346 h_backgroundcolor = rgbcolor2code(argument);
2347 } else if (argument == "page_backgroundcolor")
2348 registerAutomaticallyLoadedPackage("color");
2349 // check the case that LyX's page_backgroundcolor is defined
2350 // but not used for \pagecolor
2352 h_preamble << t.asInput() << '{' << argument << '}';
2353 // the color might already be set because \definecolor
2354 // is parsed before this
2355 h_backgroundcolor = "";
2360 if (t.cs() == "makeatletter") {
2361 // LyX takes care of this
2362 p.setCatcode('@', catLetter);
2366 if (t.cs() == "makeatother") {
2367 // LyX takes care of this
2368 p.setCatcode('@', catOther);
2372 if (t.cs() == "makeindex") {
2373 // LyX will re-add this if a print index command is found
2378 if (t.cs() == "newindex") {
2379 string const indexname = p.getArg('[', ']');
2380 string const shortcut = p.verbatim_item();
2381 if (!indexname.empty())
2382 h_index[index_number] = indexname;
2384 h_index[index_number] = shortcut;
2385 h_shortcut[index_number] = shortcut;
2391 if (t.cs() == "addbibresource") {
2392 string const options = p.getArg('[', ']');
2393 string const arg = removeExtension(p.getArg('{', '}'));
2394 if (!options.empty()) {
2395 // check if the option contains a bibencoding, if yes, extract it
2396 string::size_type pos = options.find("bibencoding=");
2398 if (pos != string::npos) {
2399 string::size_type i = options.find(',', pos);
2400 if (i == string::npos)
2401 encoding = options.substr(pos + 1);
2403 encoding = options.substr(pos, i - pos);
2404 pos = encoding.find('=');
2405 if (pos == string::npos)
2408 encoding = encoding.substr(pos + 1);
2410 if (!encoding.empty())
2411 biblatex_encodings.push_back(normalize_filename(arg) + ' ' + encoding);
2413 biblatex_bibliographies.push_back(arg);
2417 if (t.cs() == "bibliography") {
2418 vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
2419 biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
2423 if (t.cs() == "RS@ifundefined") {
2424 string const name = p.verbatim_item();
2425 string const body1 = p.verbatim_item();
2426 string const body2 = p.verbatim_item();
2427 // only non-lyxspecific stuff
2428 if (in_lyx_preamble &&
2429 (name == "subsecref" || name == "thmref" || name == "lemref"))
2433 ss << '\\' << t.cs();
2434 ss << '{' << name << '}'
2435 << '{' << body1 << '}'
2436 << '{' << body2 << '}';
2437 h_preamble << ss.str();
2442 if (t.cs() == "AtBeginDocument") {
2443 string const name = p.verbatim_item();
2444 // only non-lyxspecific stuff
2445 if (in_lyx_preamble &&
2446 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
2447 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
2448 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
2449 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
2450 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
2451 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
2452 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
2453 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
2454 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
2455 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
2456 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
2457 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
2458 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
2459 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
2460 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
2464 ss << '\\' << t.cs();
2465 ss << '{' << name << '}';
2466 h_preamble << ss.str();
2471 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
2472 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
2473 || t.cs() == "providecommand" || t.cs() == "providecommandx"
2474 || t.cs() == "DeclareRobustCommand"
2475 || t.cs() == "DeclareRobustCommandx"
2476 || t.cs() == "ProvideTextCommandDefault"
2477 || t.cs() == "DeclareMathAccent") {
2479 if (p.next_token().character() == '*') {
2483 string const name = p.verbatim_item();
2484 string const opt1 = p.getFullOpt();
2485 string const opt2 = p.getFullOpt();
2486 string const body = p.verbatim_item();
2487 // store the in_lyx_preamble setting
2488 bool const was_in_lyx_preamble = in_lyx_preamble;
2490 if (name == "\\rmdefault")
2491 if (is_known(body, known_roman_font_packages)) {
2492 h_font_roman[0] = body;
2494 in_lyx_preamble = true;
2496 if (name == "\\sfdefault")
2497 if (is_known(body, known_sans_font_packages)) {
2498 h_font_sans[0] = body;
2500 in_lyx_preamble = true;
2502 if (name == "\\ttdefault")
2503 if (is_known(body, known_typewriter_font_packages)) {
2504 h_font_typewriter[0] = body;
2506 in_lyx_preamble = true;
2508 if (name == "\\familydefault") {
2509 string family = body;
2510 // remove leading "\"
2511 h_font_default_family = family.erase(0,1);
2513 in_lyx_preamble = true;
2516 // remove LyX-specific definitions that are re-added by LyX
2518 // \lyxline is an ancient command that is converted by tex2lyx into
2519 // a \rule therefore remove its preamble code
2520 if (name == "\\lyxdot" || name == "\\lyxarrow"
2521 || name == "\\lyxline" || name == "\\LyX") {
2523 in_lyx_preamble = true;
2526 // Add the command to the known commands
2527 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
2529 // only non-lyxspecific stuff
2530 if (!in_lyx_preamble) {
2532 ss << '\\' << t.cs();
2535 ss << '{' << name << '}' << opt1 << opt2
2536 << '{' << body << "}";
2537 if (prefixIs(t.cs(), "renew") || !contains(h_preamble.str(), ss.str()))
2538 h_preamble << ss.str();
2540 ostream & out = in_preamble ? h_preamble : os;
2541 out << "\\" << t.cs() << "{" << name << "}"
2542 << opts << "{" << body << "}";
2545 // restore the in_lyx_preamble setting
2546 in_lyx_preamble = was_in_lyx_preamble;
2550 if (t.cs() == "documentclass") {
2551 vector<string>::iterator it;
2552 vector<string> opts = split_options(p.getArg('[', ']'));
2553 // FIXME This does not work for classes that have a
2554 // different name in LyX than in LaTeX
2555 h_textclass = p.getArg('{', '}');
2557 // Force textclass if the user wanted it
2558 if (!forceclass.empty())
2559 h_textclass = forceclass;
2560 tc.setName(h_textclass);
2561 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2562 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2567 // Try those who are (most likely) known to all packages first
2568 handle_opt(opts, known_fontsizes, h_paperfontsize);
2569 delete_opt(opts, known_fontsizes);
2570 // delete "pt" at the end
2571 string::size_type i = h_paperfontsize.find("pt");
2572 if (i != string::npos)
2573 h_paperfontsize.erase(i);
2574 // Now those known specifically to the class
2575 vector<string> class_fsizes = getVectorFromString(tc.opt_fontsize(), "|");
2576 string const fsize_format = tc.fontsizeformat();
2577 for (auto const & fsize : class_fsizes) {
2578 string latexsize = subst(fsize_format, "$$s", fsize);
2579 vector<string>::iterator it = find(opts.begin(), opts.end(), latexsize);
2580 if (it != opts.end()) {
2581 h_paperfontsize = fsize;
2587 // The documentclass options are always parsed before the options
2588 // of the babel call so that a language cannot overwrite the babel
2590 handle_opt(opts, known_languages, h_language);
2591 delete_opt(opts, known_languages);
2594 if ((it = find(opts.begin(), opts.end(), "fleqn"))
2596 h_is_mathindent = "1";
2599 // formula numbering side
2600 if ((it = find(opts.begin(), opts.end(), "leqno"))
2602 h_math_numbering_side = "left";
2605 else if ((it = find(opts.begin(), opts.end(), "reqno"))
2607 h_math_numbering_side = "right";
2611 // paper orientation
2612 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
2613 h_paperorientation = "landscape";
2617 if ((it = find(opts.begin(), opts.end(), "oneside"))
2622 if ((it = find(opts.begin(), opts.end(), "twoside"))
2628 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
2630 h_papercolumns = "1";
2633 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
2635 h_papercolumns = "2";
2639 // some size options are known by the document class, other sizes
2640 // are handled by the \geometry command of the geometry package
2642 vector<string> class_psizes = getVectorFromString(tc.opt_pagesize(), "|");
2643 string const psize_format = tc.pagesizeformat();
2644 for (auto const & psize : class_psizes) {
2645 string latexsize = subst(psize_format, "$$s", psize);
2646 vector<string>::iterator it = find(opts.begin(), opts.end(), latexsize);
2647 if (it != opts.end()) {
2648 h_papersize = psize;
2652 if (psize_format == "$$spaper")
2654 // Also try with the default format since this is understood by
2656 latexsize = psize + "paper";
2657 it = find(opts.begin(), opts.end(), latexsize);
2658 if (it != opts.end()) {
2659 h_papersize = psize;
2664 // the remaining options
2665 h_options = join(opts, ",");
2669 if (t.cs() == "usepackage") {
2670 string const options = p.getArg('[', ']');
2671 string const name = p.getArg('{', '}');
2672 vector<string> vecnames;
2673 split(name, vecnames, ',');
2674 vector<string>::const_iterator it = vecnames.begin();
2675 vector<string>::const_iterator end = vecnames.end();
2676 for (; it != end; ++it)
2677 handle_package(p, trimSpaceAndEol(*it), options,
2678 in_lyx_preamble, detectEncoding);
2682 if (t.cs() == "inputencoding") {
2683 string const encoding = p.getArg('{','}');
2684 Encoding const * const enc = encodings.fromLaTeXName(
2685 encoding, Encoding::inputenc, true);
2687 if (!detectEncoding)
2688 cerr << "Unknown encoding " << encoding
2689 << ". Ignoring." << std::endl;
2692 h_inputencoding = enc->name();
2693 p.setEncoding(enc->iconvName());
2698 if (t.cs() == "newenvironment") {
2699 string const name = p.getArg('{', '}');
2700 string const opt1 = p.getFullOpt();
2701 string const opt2 = p.getFullOpt();
2702 string const beg = p.verbatim_item();
2703 string const end = p.verbatim_item();
2704 if (!in_lyx_preamble) {
2705 h_preamble << "\\newenvironment{" << name
2706 << '}' << opt1 << opt2 << '{'
2707 << beg << "}{" << end << '}';
2709 add_known_environment(name, opt1, !opt2.empty(),
2710 from_utf8(beg), from_utf8(end));
2714 if (t.cs() == "newtheorem") {
2716 if (p.next_token().character() == '*') {
2720 string const name = p.getArg('{', '}');
2721 string const opt1 = p.getFullOpt();
2722 string const opt2 = p.getFullOpt();
2723 string const body = p.verbatim_item();
2724 string const opt3 = p.getFullOpt();
2725 string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
2727 string const complete = cmd + "{" + name + '}' +
2728 opt1 + opt2 + '{' + body + '}' + opt3;
2730 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
2732 if (!in_lyx_preamble)
2733 h_preamble << complete;
2737 if (t.cs() == "def") {
2738 string name = p.get_token().cs();
2739 // In fact, name may be more than the name:
2740 // In the test case of bug 8116
2741 // name == "csname SF@gobble@opt \endcsname".
2742 // Therefore, we need to use asInput() instead of cs().
2743 while (p.next_token().cat() != catBegin)
2744 name += p.get_token().asInput();
2745 if (!in_lyx_preamble)
2746 h_preamble << "\\def\\" << name << '{'
2747 << p.verbatim_item() << "}";
2751 if (t.cs() == "newcolumntype") {
2752 string const name = p.getArg('{', '}');
2753 trimSpaceAndEol(name);
2755 string opts = p.getOpt();
2756 if (!opts.empty()) {
2757 istringstream is(string(opts, 1));
2760 special_columns_[name[0]] = nargs;
2761 h_preamble << "\\newcolumntype{" << name << "}";
2763 h_preamble << "[" << nargs << "]";
2764 h_preamble << "{" << p.verbatim_item() << "}";
2768 if (t.cs() == "setcounter") {
2769 string const name = p.getArg('{', '}');
2770 string const content = p.getArg('{', '}');
2771 if (name == "secnumdepth")
2772 h_secnumdepth = content;
2773 else if (name == "tocdepth")
2774 h_tocdepth = content;
2776 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
2780 if (t.cs() == "setlength") {
2781 string const name = p.verbatim_item();
2782 string const content = p.verbatim_item();
2783 // the paragraphs are only not indented when \parindent is set to zero
2784 if (name == "\\parindent" && content != "") {
2785 if (content[0] == '0')
2786 h_paragraph_separation = "skip";
2788 h_paragraph_indentation = translate_len(content);
2789 } else if (name == "\\parskip") {
2790 if (content == "\\smallskipamount")
2791 h_defskip = "smallskip";
2792 else if (content == "\\medskipamount")
2793 h_defskip = "medskip";
2794 else if (content == "\\bigskipamount")
2795 h_defskip = "bigskip";
2797 h_defskip = translate_len(content);
2798 } else if (name == "\\mathindent") {
2799 h_mathindentation = translate_len(content);
2801 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2805 if (t.cs() == "onehalfspacing") {
2806 h_spacing = "onehalf";
2810 if (t.cs() == "doublespacing") {
2811 h_spacing = "double";
2815 if (t.cs() == "setstretch") {
2816 h_spacing = "other " + p.verbatim_item();
2820 if (t.cs() == "synctex") {
2821 // the scheme is \synctex=value
2822 // where value can only be "1" or "-1"
2823 h_output_sync = "1";
2824 // there can be any character behind the value (e.g. a linebreak or a '\'
2825 // therefore we extract it char by char
2827 string value = p.get_token().asInput();
2829 value += p.get_token().asInput();
2830 h_output_sync_macro = "\\synctex=" + value;
2834 if (t.cs() == "begin") {
2835 string const name = p.getArg('{', '}');
2836 if (name == "document")
2838 h_preamble << "\\begin{" << name << "}";
2842 if (t.cs() == "geometry") {
2843 vector<string> opts = split_options(p.getArg('{', '}'));
2844 handle_geometry(opts);
2848 if (t.cs() == "definecolor") {
2849 string const color = p.getArg('{', '}');
2850 string const space = p.getArg('{', '}');
2851 string const value = p.getArg('{', '}');
2852 if (color == "document_fontcolor" && space == "rgb") {
2853 RGBColor c(RGBColorFromLaTeX(value));
2854 h_fontcolor = X11hexname(c);
2855 } else if (color == "note_fontcolor" && space == "rgb") {
2856 RGBColor c(RGBColorFromLaTeX(value));
2857 h_notefontcolor = X11hexname(c);
2858 } else if (color == "page_backgroundcolor" && space == "rgb") {
2859 RGBColor c(RGBColorFromLaTeX(value));
2860 h_backgroundcolor = X11hexname(c);
2861 } else if (color == "shadecolor" && space == "rgb") {
2862 RGBColor c(RGBColorFromLaTeX(value));
2863 h_boxbgcolor = X11hexname(c);
2865 h_preamble << "\\definecolor{" << color
2866 << "}{" << space << "}{" << value
2872 if (t.cs() == "bibliographystyle") {
2873 h_biblio_style = p.verbatim_item();
2877 if (t.cs() == "jurabibsetup") {
2878 // FIXME p.getArg('{', '}') is most probably wrong (it
2879 // does not handle nested braces).
2880 // Use p.verbatim_item() instead.
2881 vector<string> jurabibsetup =
2882 split_options(p.getArg('{', '}'));
2883 // add jurabibsetup to the jurabib package options
2884 add_package("jurabib", jurabibsetup);
2885 if (!jurabibsetup.empty()) {
2886 h_preamble << "\\jurabibsetup{"
2887 << join(jurabibsetup, ",") << '}';
2892 if (t.cs() == "hypersetup") {
2893 vector<string> hypersetup =
2894 split_options(p.verbatim_item());
2895 // add hypersetup to the hyperref package options
2896 handle_hyperref(hypersetup);
2897 if (!hypersetup.empty()) {
2898 h_preamble << "\\hypersetup{"
2899 << join(hypersetup, ",") << '}';
2904 if (t.cs() == "includeonly") {
2905 vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
2906 for (auto & iofile : includeonlys) {
2907 string filename(normalize_filename(iofile));
2908 string const path = getMasterFilePath(true);
2909 // We want to preserve relative/absolute filenames,
2910 // therefore path is only used for testing
2911 if (!makeAbsPath(filename, path).exists()) {
2912 // The file extension is probably missing.
2913 // Now try to find it out.
2914 string const tex_name =
2915 find_file(filename, path,
2916 known_tex_extensions);
2917 if (!tex_name.empty())
2918 filename = tex_name;
2921 if (makeAbsPath(filename, path).exists())
2922 fix_child_filename(filename);
2924 cerr << "Warning: Could not find included file '"
2925 << filename << "'." << endl;
2926 outname = changeExtension(filename, "lyx");
2927 h_includeonlys.push_back(outname);
2932 if (is_known(t.cs(), known_if_3arg_commands)) {
2933 // prevent misparsing of \usepackage if it is used
2934 // as an argument (see e.g. our own output of
2935 // \@ifundefined above)
2936 string const arg1 = p.verbatim_item();
2937 string const arg2 = p.verbatim_item();
2938 string const arg3 = p.verbatim_item();
2939 // test case \@ifundefined{date}{}{\date{}}
2940 if (t.cs() == "@ifundefined" && arg1 == "date" &&
2941 arg2.empty() && arg3 == "\\date{}") {
2942 h_suppress_date = "true";
2943 // older tex2lyx versions did output
2944 // \@ifundefined{definecolor}{\usepackage{color}}{}
2945 } else if (t.cs() == "@ifundefined" &&
2946 arg1 == "definecolor" &&
2947 arg2 == "\\usepackage{color}" &&
2949 if (!in_lyx_preamble)
2950 h_preamble << package_beg_sep
2953 << "\\@ifundefined{definecolor}{color}{}"
2956 //\@ifundefined{showcaptionsetup}{}{%
2957 // \PassOptionsToPackage{caption=false}{subfig}}
2958 // that LyX uses for subfloats
2959 } else if (t.cs() == "@ifundefined" &&
2960 arg1 == "showcaptionsetup" && arg2.empty()
2961 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
2963 } else if (!in_lyx_preamble) {
2964 h_preamble << t.asInput()
2965 << '{' << arg1 << '}'
2966 << '{' << arg2 << '}'
2967 << '{' << arg3 << '}';
2972 if (is_known(t.cs(), known_if_commands)) {
2973 // must not parse anything in conditional code, since
2974 // LyX would output the parsed contents unconditionally
2975 if (!in_lyx_preamble)
2976 h_preamble << t.asInput();
2977 handle_if(p, in_lyx_preamble);
2981 if (!t.cs().empty() && !in_lyx_preamble) {
2982 h_preamble << '\\' << t.cs();
2987 // remove the whitespace
2990 if (h_papersides.empty()) {
2993 h_papersides = ss.str();
2996 // If the CJK package is used we cannot set the document language from
2997 // the babel options. Instead, we guess which language is used most
2998 // and set this one.
2999 default_language = h_language;
3000 if (is_full_document &&
3001 (auto_packages.find("CJK") != auto_packages.end() ||
3002 auto_packages.find("CJKutf8") != auto_packages.end())) {
3004 h_language = guessLanguage(p, default_language);
3006 if (explicit_babel && h_language != default_language) {
3007 // We set the document language to a CJK language,
3008 // but babel is explicitly called in the user preamble
3009 // without options. LyX will not add the default
3010 // language to the document options if it is either
3011 // english, or no text is set as default language.
3012 // Therefore we need to add a language option explicitly.
3013 // FIXME: It would be better to remove all babel calls
3014 // from the user preamble, but this is difficult
3015 // without re-introducing bug 7861.
3016 if (h_options.empty())
3017 h_options = lyx2babel(default_language);
3019 h_options += ',' + lyx2babel(default_language);
3023 // Finally, set the quote style.
3024 // LyX knows the following quotes styles:
3025 // british, cjk, cjkangle, danish, english, french, german,
3026 // polish, russian, swedish and swiss
3027 // conversion list taken from
3028 // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
3029 // (quotes for kazakh are unknown)
3031 if (is_known(h_language, known_british_quotes_languages))
3032 h_quotes_style = "british";
3034 else if (is_known(h_language, known_cjk_quotes_languages))
3035 h_quotes_style = "cjk";
3037 else if (is_known(h_language, known_cjkangle_quotes_languages))
3038 h_quotes_style = "cjkangle";
3040 else if (is_known(h_language, known_danish_quotes_languages))
3041 h_quotes_style = "danish";
3043 else if (is_known(h_language, known_french_quotes_languages))
3044 h_quotes_style = "french";
3046 else if (is_known(h_language, known_german_quotes_languages))
3047 h_quotes_style = "german";
3049 else if (is_known(h_language, known_polish_quotes_languages))
3050 h_quotes_style = "polish";
3052 else if (is_known(h_language, known_russian_quotes_languages))
3053 h_quotes_style = "russian";
3055 else if (is_known(h_language, known_swedish_quotes_languages))
3056 h_quotes_style = "swedish";
3058 else if (is_known(h_language, known_swiss_quotes_languages))
3059 h_quotes_style = "swiss";
3061 else if (is_known(h_language, known_english_quotes_languages))
3062 h_quotes_style = "english";
3066 string Preamble::parseEncoding(Parser & p, string const & forceclass)
3068 TeX2LyXDocClass dummy;
3069 parse(p, forceclass, true, dummy);
3070 if (h_inputencoding != "auto-legacy" && h_inputencoding != "auto-legacy-plain")
3071 return h_inputencoding;
3076 string babel2lyx(string const & language)
3078 char const * const * where = is_known(language, known_languages);
3080 return known_coded_languages[where - known_languages];
3085 string lyx2babel(string const & language)
3087 char const * const * where = is_known(language, known_coded_languages);
3089 return known_languages[where - known_coded_languages];
3094 string Preamble::polyglossia2lyx(string const & language)
3096 char const * const * where = is_known(language, polyglossia_languages);
3098 return coded_polyglossia_languages[where - polyglossia_languages];
3103 string rgbcolor2code(string const & name)
3105 char const * const * where = is_known(name, known_basic_colors);
3107 // "red", "green" etc
3108 return known_basic_color_codes[where - known_basic_colors];
3110 // "255,0,0", "0,255,0" etc
3111 RGBColor c(RGBColorFromLaTeX(name));
3112 return X11hexname(c);