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)
416 Author author(from_utf8(name), empty_docstring());
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());
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 = "false";
557 h_output_changes = "false";
559 //h_output_sync_macro
560 h_papercolumns = "1";
561 h_paperfontsize = "default";
562 h_paperorientation = "portrait";
563 h_paperpagestyle = "default";
565 h_papersize = "default";
566 h_paragraph_indentation = "default";
567 h_paragraph_separation = "indent";
572 h_pdf_bookmarks = "0";
573 h_pdf_bookmarksnumbered = "0";
574 h_pdf_bookmarksopen = "0";
575 h_pdf_bookmarksopenlevel = "1";
576 h_pdf_breaklinks = "0";
577 h_pdf_pdfborder = "0";
578 h_pdf_colorlinks = "0";
579 h_pdf_backref = "section";
580 h_pdf_pdfusetitle = "0";
582 //h_pdf_quoted_options;
583 h_quotes_style = "english";
585 h_shortcut[0] = "idx";
586 h_spacing = "single";
587 h_save_transient_properties = "true";
588 h_suppress_date = "false";
589 h_textclass = "article";
591 h_tracking_changes = "false";
592 h_use_bibtopic = "false";
593 h_use_dash_ligatures = "true";
594 h_use_indices = "false";
595 h_use_geometry = "false";
596 h_use_default_options = "false";
597 h_use_hyperref = "false";
598 h_use_microtype = "false";
599 h_use_lineno = "false";
600 h_use_refstyle = false;
601 h_use_minted = false;
602 h_use_packages["amsmath"] = "1";
603 h_use_packages["amssymb"] = "0";
604 h_use_packages["cancel"] = "0";
605 h_use_packages["esint"] = "1";
606 h_use_packages["mhchem"] = "0";
607 h_use_packages["mathdots"] = "0";
608 h_use_packages["mathtools"] = "0";
609 h_use_packages["stackrel"] = "0";
610 h_use_packages["stmaryrd"] = "0";
611 h_use_packages["undertilde"] = "0";
615 void Preamble::handle_hyperref(vector<string> & options)
617 // FIXME swallow inputencoding changes that might surround the
618 // hyperref setup if it was written by LyX
619 h_use_hyperref = "true";
620 // swallow "unicode=true", since LyX does always write that
621 vector<string>::iterator it =
622 find(options.begin(), options.end(), "unicode=true");
623 if (it != options.end())
625 it = find(options.begin(), options.end(), "pdfusetitle");
626 if (it != options.end()) {
627 h_pdf_pdfusetitle = "1";
630 string bookmarks = process_keyval_opt(options, "bookmarks");
631 if (bookmarks == "true")
632 h_pdf_bookmarks = "1";
633 else if (bookmarks == "false")
634 h_pdf_bookmarks = "0";
635 if (h_pdf_bookmarks == "1") {
636 string bookmarksnumbered =
637 process_keyval_opt(options, "bookmarksnumbered");
638 if (bookmarksnumbered == "true")
639 h_pdf_bookmarksnumbered = "1";
640 else if (bookmarksnumbered == "false")
641 h_pdf_bookmarksnumbered = "0";
642 string bookmarksopen =
643 process_keyval_opt(options, "bookmarksopen");
644 if (bookmarksopen == "true")
645 h_pdf_bookmarksopen = "1";
646 else if (bookmarksopen == "false")
647 h_pdf_bookmarksopen = "0";
648 if (h_pdf_bookmarksopen == "1") {
649 string bookmarksopenlevel =
650 process_keyval_opt(options, "bookmarksopenlevel");
651 if (!bookmarksopenlevel.empty())
652 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
655 string breaklinks = process_keyval_opt(options, "breaklinks");
656 if (breaklinks == "true")
657 h_pdf_breaklinks = "1";
658 else if (breaklinks == "false")
659 h_pdf_breaklinks = "0";
660 string pdfborder = process_keyval_opt(options, "pdfborder");
661 if (pdfborder == "{0 0 0}")
662 h_pdf_pdfborder = "1";
663 else if (pdfborder == "{0 0 1}")
664 h_pdf_pdfborder = "0";
665 string backref = process_keyval_opt(options, "backref");
666 if (!backref.empty())
667 h_pdf_backref = backref;
668 string colorlinks = process_keyval_opt(options, "colorlinks");
669 if (colorlinks == "true")
670 h_pdf_colorlinks = "1";
671 else if (colorlinks == "false")
672 h_pdf_colorlinks = "0";
673 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
674 if (!pdfpagemode.empty())
675 h_pdf_pagemode = pdfpagemode;
676 string pdftitle = process_keyval_opt(options, "pdftitle");
677 if (!pdftitle.empty()) {
678 h_pdf_title = remove_braces(pdftitle);
680 string pdfauthor = process_keyval_opt(options, "pdfauthor");
681 if (!pdfauthor.empty()) {
682 h_pdf_author = remove_braces(pdfauthor);
684 string pdfsubject = process_keyval_opt(options, "pdfsubject");
685 if (!pdfsubject.empty())
686 h_pdf_subject = remove_braces(pdfsubject);
687 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
688 if (!pdfkeywords.empty())
689 h_pdf_keywords = remove_braces(pdfkeywords);
690 if (!options.empty()) {
691 if (!h_pdf_quoted_options.empty())
692 h_pdf_quoted_options += ',';
693 h_pdf_quoted_options += join(options, ",");
699 void Preamble::handle_geometry(vector<string> & options)
701 h_use_geometry = "true";
702 vector<string>::iterator it;
704 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
705 h_paperorientation = "landscape";
709 // keyval version: "paper=letter" or "paper=letterpaper"
710 string paper = process_keyval_opt(options, "paper");
712 if (suffixIs(paper, "paper"))
713 paper = subst(paper, "paper", "");
714 // alternative version: "letterpaper"
715 handle_opt(options, known_latex_paper_sizes, paper);
716 if (suffixIs(paper, "paper"))
717 paper = subst(paper, "paper", "");
718 delete_opt(options, known_latex_paper_sizes);
722 char const * const * margin = known_paper_margins;
723 for (; *margin; ++margin) {
724 string value = process_keyval_opt(options, *margin);
725 if (!value.empty()) {
726 int k = margin - known_paper_margins;
727 string name = known_coded_paper_margins[k];
728 h_margins += '\\' + name + ' ' + value + '\n';
734 void Preamble::handle_package(Parser &p, string const & name,
735 string const & opts, bool in_lyx_preamble,
738 vector<string> options = split_options(opts);
739 add_package(name, options);
741 if (is_known(name, known_xetex_packages)) {
743 h_use_non_tex_fonts = true;
744 registerAutomaticallyLoadedPackage("fontspec");
745 if (h_inputencoding == "auto-legacy")
746 p.setEncoding("UTF-8");
749 // vector of all options for easier parsing and
751 vector<string> allopts = getVectorFromString(opts);
752 // this stores the potential extra options
759 // By default, we use the package name as LyX font name,
760 // so this only needs to be reset if these names differ
761 if (is_known(name, known_roman_font_packages))
762 h_font_roman[0] = name;
764 if (name == "ccfonts") {
765 for (auto const & opt : allopts) {
771 h_font_roman_opts = xopts;
775 if (name == "lmodern") {
776 for (auto const & opt : allopts) {
782 h_font_roman_opts = xopts;
786 if (name == "fourier") {
787 h_font_roman[0] = "utopia";
788 for (auto const & opt : allopts) {
790 h_font_roman_osf = "true";
793 if (opt == "expert") {
802 h_font_roman_opts = xopts;
806 if (name == "garamondx") {
807 for (auto const & opt : allopts) {
809 h_font_roman_osf = "true";
817 h_font_roman_opts = xopts;
821 if (name == "libertine") {
822 // this automatically invokes biolinum
823 h_font_sans[0] = "biolinum";
824 // as well as libertineMono
825 h_font_typewriter[0] = "libertine-mono";
826 for (auto const & opt : allopts) {
828 h_font_roman_osf = "true";
831 if (opt == "lining") {
832 h_font_roman_osf = "false";
840 h_font_roman_opts = xopts;
844 if (name == "libertineRoman" || name == "libertine-type1") {
845 h_font_roman[0] = "libertine";
846 // NOTE: contrary to libertine.sty, libertineRoman
847 // and libertine-type1 do not automatically invoke
848 // biolinum and libertineMono
849 if (opts == "lining")
850 h_font_roman_osf = "false";
851 else if (opts == "osf")
852 h_font_roman_osf = "true";
855 if (name == "MinionPro") {
856 h_font_roman[0] = "minionpro";
857 h_font_roman_osf = "true";
858 h_font_math[0] = "auto";
859 for (auto const & opt : allopts) {
861 h_font_roman_osf = "false";
864 if (opt == "onlytext") {
865 h_font_math[0] = "default";
873 h_font_roman_opts = xopts;
877 if (name == "mathdesign") {
878 for (auto const & opt : allopts) {
879 if (opt == "charter") {
880 h_font_roman[0] = "md-charter";
883 if (opt == "garamond") {
884 h_font_roman[0] = "md-garamond";
887 if (opt == "utopia") {
888 h_font_roman[0] = "md-utopia";
891 if (opt == "expert") {
893 h_font_roman_osf = "true";
899 else if (name == "mathpazo") {
900 h_font_roman[0] = "palatino";
901 for (auto const & opt : allopts) {
903 h_font_roman_osf = "true";
915 h_font_roman_opts = xopts;
919 else if (name == "mathptmx") {
920 h_font_roman[0] = "times";
921 for (auto const & opt : allopts) {
927 h_font_roman_opts = xopts;
931 if (name == "crimson")
932 h_font_roman[0] = "cochineal";
934 if (name == "cochineal") {
935 for (auto const & opt : allopts) {
936 if (opt == "osf" || opt == "oldstyle") {
937 h_font_roman_osf = "true";
940 if (opt == "proportional" || opt == "p")
947 h_font_roman_opts = xopts;
951 if (name == "CrimsonPro") {
952 h_font_roman_osf = "true";
953 for (auto const & opt : allopts) {
954 if (opt == "lf" || opt == "lining") {
955 h_font_roman_osf = "false";
958 if (opt == "proportional" || opt == "p")
960 if (opt == "medium") {
961 h_font_roman[0] = "CrimsonProMedium";
964 if (opt == "extralight") {
965 h_font_roman[0] = "CrimsonProExtraLight";
968 if (opt == "light") {
969 h_font_roman[0] = "CrimsonProLight";
977 h_font_roman_opts = xopts;
983 // font uses old-style figure
984 h_font_roman_osf = "true";
986 if (name == "paratype") {
987 // in this case all fonts are ParaType
988 h_font_roman[0] = "PTSerif-TLF";
989 h_font_sans[0] = "default";
990 h_font_typewriter[0] = "default";
993 if (name == "PTSerif")
994 h_font_roman[0] = "PTSerif-TLF";
996 if (name == "XCharter") {
997 h_font_roman[0] = "xcharter";
998 for (auto const & opt : allopts) {
1000 h_font_roman_osf = "true";
1008 h_font_roman_opts = xopts;
1012 if (name == "plex-serif") {
1013 h_font_roman[0] = "IBMPlexSerif";
1014 for (auto const & opt : allopts) {
1015 if (opt == "thin") {
1016 h_font_roman[0] = "IBMPlexSerifThin";
1019 if (opt == "extralight") {
1020 h_font_roman[0] = "IBMPlexSerifExtraLight";
1023 if (opt == "light") {
1024 h_font_roman[0] = "IBMPlexSerifLight";
1032 h_font_roman_opts = xopts;
1036 if (name == "noto-serif" || name == "noto") {
1043 bool extralight = false;
1045 bool medium = false;
1048 if (name == "noto") {
1053 // Since the options might apply to different shapes,
1054 // we need to parse all options first and then handle them.
1055 for (auto const & opt : allopts) {
1056 if (opt == "regular")
1066 if (opt == "thin") {
1070 if (opt == "extralight") {
1074 if (opt == "light") {
1078 if (opt == "medium") {
1089 if (opt == "nott") {
1097 if (prefixIs(opt, "scaled=")) {
1106 // handle options that might affect different shapes
1107 if (name == "noto-serif" || rm) {
1109 h_font_roman[0] = "NotoSerifThin";
1110 else if (extralight)
1111 h_font_roman[0] = "NotoSerifExtralight";
1113 h_font_roman[0] = "NotoSerifLight";
1115 h_font_roman[0] = "NotoSerifMedium";
1117 h_font_roman[0] = "NotoSerifRegular";
1119 h_font_roman_osf = "true";
1121 h_font_roman_opts = xopts;
1123 if (name == "noto" && sf) {
1125 h_font_sans[0] = "NotoSansThin";
1126 else if (extralight)
1127 h_font_sans[0] = "NotoSansExtralight";
1129 h_font_sans[0] = "NotoSansLight";
1131 h_font_sans[0] = "NotoSansMedium";
1133 h_font_sans[0] = "NotoSansRegular";
1135 h_font_sans_osf = "true";
1137 scale_as_percentage(scl, h_font_sf_scale[0]);
1139 h_font_sans_opts = xopts;
1141 if (name == "noto" && tt) {
1142 h_font_typewriter[0] = "NotoMonoRegular";
1144 h_font_typewriter_osf = "true";
1146 scale_as_percentage(scl, h_font_tt_scale[0]);
1148 h_font_typewriter_opts = xopts;
1152 if (name == "sourceserifpro") {
1153 h_font_roman[0] = "ADOBESourceSerifPro";
1154 for (auto const & opt : allopts) {
1156 h_font_roman_osf = "true";
1164 h_font_roman_opts = xopts;
1172 // By default, we use the package name as LyX font name,
1173 // so this only needs to be reset if these names differ.
1174 // Also, we handle the scaling option here generally.
1175 if (is_known(name, known_sans_font_packages)) {
1176 h_font_sans[0] = name;
1177 if (contains(opts, "scale")) {
1178 vector<string>::iterator it = allopts.begin();
1179 for (; it != allopts.end() ; ++it) {
1180 string const opt = *it;
1181 if (prefixIs(opt, "scaled=") || prefixIs(opt, "scale=")) {
1182 if (scale_as_percentage(opt, h_font_sf_scale[0])) {
1191 if (name == "biolinum" || name == "biolinum-type1") {
1192 h_font_sans[0] = "biolinum";
1193 for (auto const & opt : allopts) {
1194 if (prefixIs(opt, "osf")) {
1195 h_font_sans_osf = "true";
1203 h_font_sans_opts = xopts;
1207 if (name == "cantarell") {
1208 for (auto const & opt : allopts) {
1209 if (opt == "defaultsans")
1211 if (prefixIs(opt, "oldstyle")) {
1212 h_font_sans_osf = "true";
1220 h_font_sans_opts = xopts;
1224 if (name == "Chivo") {
1225 for (auto const & opt : allopts) {
1226 if (opt == "thin") {
1227 h_font_roman[0] = "ChivoThin";
1230 if (opt == "light") {
1231 h_font_roman[0] = "ChivoLight";
1234 if (opt == "regular") {
1235 h_font_roman[0] = "Chivo";
1238 if (opt == "medium") {
1239 h_font_roman[0] = "ChivoMedium";
1242 if (prefixIs(opt, "oldstyle")) {
1243 h_font_sans_osf = "true";
1251 h_font_sans_opts = xopts;
1255 if (name == "PTSans") {
1256 h_font_sans[0] = "PTSans-TLF";
1259 if (name == "FiraSans") {
1260 h_font_sans_osf = "true";
1261 for (auto const & opt : allopts) {
1262 if (opt == "book") {
1263 h_font_sans[0] = "FiraSansBook";
1266 if (opt == "thin") {
1269 if (opt == "extralight") {
1270 h_font_sans[0] = "FiraSansExtralight";
1273 if (opt == "light") {
1274 h_font_sans[0] = "FiraSansLight";
1277 if (opt == "ultralight") {
1278 h_font_sans[0] = "FiraSansUltralight";
1281 if (opt == "thin") {
1282 h_font_sans[0] = "FiraSansThin";
1285 if (opt == "lf" || opt == "lining") {
1286 h_font_sans_osf = "false";
1294 h_font_sans_opts = xopts;
1298 if (name == "plex-sans") {
1299 h_font_sans[0] = "IBMPlexSans";
1300 for (auto const & opt : allopts) {
1301 if (opt == "condensed") {
1302 h_font_sans[0] = "IBMPlexSansCondensed";
1305 if (opt == "thin") {
1306 h_font_sans[0] = "IBMPlexSansThin";
1309 if (opt == "extralight") {
1310 h_font_sans[0] = "IBMPlexSansExtraLight";
1313 if (opt == "light") {
1314 h_font_sans[0] = "IBMPlexSansLight";
1322 h_font_sans_opts = xopts;
1326 if (name == "noto-sans") {
1327 h_font_sans[0] = "NotoSansRegular";
1328 for (auto const & opt : allopts) {
1329 if (opt == "regular")
1331 if (opt == "medium") {
1332 h_font_sans[0] = "NotoSansMedium";
1335 if (opt == "thin") {
1336 h_font_sans[0] = "NotoSansThin";
1339 if (opt == "extralight") {
1340 h_font_sans[0] = "NotoSansExtralight";
1343 if (opt == "light") {
1344 h_font_sans[0] = "NotoSansLight";
1348 h_font_sans_osf = "true";
1356 h_font_sans_opts = xopts;
1360 if (name == "sourcesanspro") {
1361 h_font_sans[0] = "ADOBESourceSansPro";
1362 for (auto const & opt : allopts) {
1364 h_font_sans_osf = "true";
1372 h_font_sans_opts = xopts;
1380 // By default, we use the package name as LyX font name,
1381 // so this only needs to be reset if these names differ.
1382 // Also, we handle the scaling option here generally.
1383 if (is_known(name, known_typewriter_font_packages)) {
1384 h_font_typewriter[0] = name;
1385 if (contains(opts, "scale")) {
1386 vector<string>::iterator it = allopts.begin();
1387 for (; it != allopts.end() ; ++it) {
1388 string const opt = *it;
1389 if (prefixIs(opt, "scaled=") || prefixIs(opt, "scale=")) {
1390 if (scale_as_percentage(opt, h_font_tt_scale[0])) {
1399 if (name == "libertineMono" || name == "libertineMono-type1")
1400 h_font_typewriter[0] = "libertine-mono";
1402 if (name == "FiraMono") {
1403 h_font_typewriter_osf = "true";
1404 for (auto const & opt : allopts) {
1405 if (opt == "lf" || opt == "lining") {
1406 h_font_typewriter_osf = "false";
1414 h_font_typewriter_opts = xopts;
1418 if (name == "PTMono")
1419 h_font_typewriter[0] = "PTMono-TLF";
1421 if (name == "plex-mono") {
1422 h_font_typewriter[0] = "IBMPlexMono";
1423 for (auto const & opt : allopts) {
1424 if (opt == "thin") {
1425 h_font_typewriter[0] = "IBMPlexMonoThin";
1428 if (opt == "extralight") {
1429 h_font_typewriter[0] = "IBMPlexMonoExtraLight";
1432 if (opt == "light") {
1433 h_font_typewriter[0] = "IBMPlexMonoLight";
1441 h_font_typewriter_opts = xopts;
1445 if (name == "noto-mono") {
1446 h_font_typewriter[0] = "NotoMonoRegular";
1447 for (auto const & opt : allopts) {
1448 if (opt == "regular")
1455 h_font_typewriter_opts = xopts;
1459 if (name == "sourcecodepro") {
1460 h_font_typewriter[0] = "ADOBESourceCodePro";
1461 for (auto const & opt : allopts) {
1463 h_font_typewriter_osf = "true";
1471 h_font_typewriter_opts = xopts;
1479 // By default, we use the package name as LyX font name,
1480 // so this only needs to be reset if these names differ.
1481 if (is_known(name, known_math_font_packages))
1482 h_font_math[0] = name;
1484 if (name == "newtxmath") {
1486 h_font_math[0] = "newtxmath";
1487 else if (opts == "garamondx")
1488 h_font_math[0] = "garamondx-ntxm";
1489 else if (opts == "libertine")
1490 h_font_math[0] = "libertine-ntxm";
1491 else if (opts == "minion")
1492 h_font_math[0] = "minion-ntxm";
1493 else if (opts == "cochineal")
1494 h_font_math[0] = "cochineal-ntxm";
1497 if (name == "iwona")
1499 h_font_math[0] = "iwona-math";
1501 if (name == "kurier")
1503 h_font_math[0] = "kurier-math";
1505 // after the detection and handling of special cases, we can remove the
1506 // fonts, otherwise they would appear in the preamble, see bug #7856
1507 if (is_known(name, known_roman_font_packages) || is_known(name, known_sans_font_packages)
1508 || is_known(name, known_typewriter_font_packages) || is_known(name, known_math_font_packages))
1510 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
1511 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
1512 name == "esint" || name == "mhchem" || name == "mathdots" ||
1513 name == "mathtools" || name == "stackrel" ||
1514 name == "stmaryrd" || name == "undertilde") {
1515 h_use_packages[name] = "2";
1516 registerAutomaticallyLoadedPackage(name);
1519 else if (name == "babel") {
1520 h_language_package = "default";
1521 // One might think we would have to do nothing if babel is loaded
1522 // without any options to prevent pollution of the preamble with this
1523 // babel call in every roundtrip.
1524 // But the user could have defined babel-specific things afterwards. So
1525 // we need to keep it in the preamble to prevent cases like bug #7861.
1526 if (!opts.empty()) {
1527 // check if more than one option was used - used later for inputenc
1528 if (options.begin() != options.end() - 1)
1529 one_language = false;
1530 // babel takes the last language of the option of its \usepackage
1531 // call as document language. If there is no such language option, the
1532 // last language in the documentclass options is used.
1533 handle_opt(options, known_languages, h_language);
1534 // translate the babel name to a LyX name
1535 h_language = babel2lyx(h_language);
1536 if (h_language == "japanese") {
1537 // For Japanese, the encoding isn't indicated in the source
1538 // file, and there's really not much we can do. We could
1539 // 1) offer a list of possible encodings to choose from, or
1540 // 2) determine the encoding of the file by inspecting it.
1541 // For the time being, we leave the encoding alone so that
1542 // we don't get iconv errors when making a wrong guess, and
1543 // we will output a note at the top of the document
1544 // explaining what to do.
1545 Encoding const * const enc = encodings.fromIconvName(
1546 p.getEncoding(), Encoding::japanese, false);
1548 h_inputencoding = enc->name();
1549 is_nonCJKJapanese = true;
1550 // in this case babel can be removed from the preamble
1551 registerAutomaticallyLoadedPackage("babel");
1553 // If babel is called with options, LyX puts them by default into the
1554 // document class options. This works for most languages, except
1555 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
1556 // perhaps in future others.
1557 // Therefore keep the babel call as it is as the user might have
1559 string const babelcall = "\\usepackage[" + opts + "]{babel}\n";
1560 if (!contains(h_preamble.str(), babelcall))
1561 h_preamble << babelcall;
1563 delete_opt(options, known_languages);
1565 if (!contains(h_preamble.str(), "\\usepackage{babel}\n"))
1566 h_preamble << "\\usepackage{babel}\n";
1567 explicit_babel = true;
1571 else if (name == "polyglossia") {
1572 h_language_package = "default";
1573 h_default_output_format = "pdf4";
1574 h_use_non_tex_fonts = true;
1576 registerAutomaticallyLoadedPackage("xunicode");
1577 if (h_inputencoding == "auto-legacy")
1578 p.setEncoding("UTF-8");
1581 else if (name == "CJK") {
1582 // set the encoding to "auto-legacy" because it might be set to "auto-legacy-plain" by the babel handling
1583 // and this would not be correct for CJK
1584 if (h_inputencoding == "auto-legacy-plain")
1585 h_inputencoding = "auto-legacy";
1586 registerAutomaticallyLoadedPackage("CJK");
1589 else if (name == "CJKutf8") {
1590 h_inputencoding = "utf8-cjk";
1591 p.setEncoding("UTF-8");
1592 registerAutomaticallyLoadedPackage("CJKutf8");
1595 else if (name == "fontenc") {
1596 h_fontencoding = getStringFromVector(options, ",");
1600 else if (name == "inputenc" || name == "luainputenc") {
1601 // h_inputencoding is only set when there is not more than one
1602 // inputenc option because otherwise h_inputencoding must be
1603 // set to "auto-legacy" (the default encodings of the document's languages)
1604 // Therefore check that exactly one option is passed to inputenc.
1605 // It is also only set when there is not more than one babel
1607 if (!options.empty()) {
1608 string const encoding = options.back();
1609 Encoding const * const enc = encodings.fromLaTeXName(
1610 encoding, Encoding::inputenc, true);
1612 if (!detectEncoding)
1613 cerr << "Unknown encoding " << encoding
1614 << ". Ignoring." << std::endl;
1616 if (!enc->unsafe() && options.size() == 1 && one_language == true)
1617 h_inputencoding = enc->name();
1618 p.setEncoding(enc->iconvName());
1624 else if (name == "srcltx") {
1625 h_output_sync = "1";
1626 if (!opts.empty()) {
1627 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
1630 h_output_sync_macro = "\\usepackage{srcltx}";
1633 else if (is_known(name, known_old_language_packages)) {
1634 // known language packages from the times before babel
1635 // if they are found and not also babel, they will be used as
1636 // custom language package
1637 h_language_package = "\\usepackage{" + name + "}";
1640 else if (name == "lyxskak") {
1641 // ignore this and its options
1642 const char * const o[] = {"ps", "mover", 0};
1643 delete_opt(options, o);
1646 else if (is_known(name, known_lyx_packages) && options.empty()) {
1647 if (name == "splitidx")
1648 h_use_indices = "true";
1649 else if (name == "minted")
1650 h_use_minted = true;
1651 else if (name == "refstyle")
1652 h_use_refstyle = true;
1653 else if (name == "prettyref")
1654 h_use_refstyle = false;
1655 if (!in_lyx_preamble) {
1656 h_preamble << package_beg_sep << name
1657 << package_mid_sep << "\\usepackage{"
1659 if (p.next_token().cat() == catNewline ||
1660 (p.next_token().cat() == catSpace &&
1661 p.next_next_token().cat() == catNewline))
1663 h_preamble << package_end_sep;
1667 else if (name == "geometry")
1668 handle_geometry(options);
1670 else if (name == "subfig")
1671 ; // ignore this FIXME: Use the package separator mechanism instead
1673 else if (char const * const * where = is_known(name, known_languages))
1674 h_language = known_coded_languages[where - known_languages];
1676 else if (name == "natbib") {
1677 h_biblio_style = "plainnat";
1678 h_cite_engine = "natbib";
1679 h_cite_engine_type = "authoryear";
1680 vector<string>::iterator it =
1681 find(options.begin(), options.end(), "authoryear");
1682 if (it != options.end())
1685 it = find(options.begin(), options.end(), "numbers");
1686 if (it != options.end()) {
1687 h_cite_engine_type = "numerical";
1691 if (!options.empty())
1692 h_biblio_options = join(options, ",");
1695 else if (name == "biblatex") {
1696 h_biblio_style = "plainnat";
1697 h_cite_engine = "biblatex";
1698 h_cite_engine_type = "authoryear";
1700 vector<string>::iterator it =
1701 find(options.begin(), options.end(), "natbib");
1702 if (it != options.end()) {
1704 h_cite_engine = "biblatex-natbib";
1706 opt = process_keyval_opt(options, "natbib");
1708 h_cite_engine = "biblatex-natbib";
1710 opt = process_keyval_opt(options, "style");
1712 h_biblatex_citestyle = opt;
1713 h_biblatex_bibstyle = opt;
1715 opt = process_keyval_opt(options, "citestyle");
1717 h_biblatex_citestyle = opt;
1718 opt = process_keyval_opt(options, "bibstyle");
1720 h_biblatex_bibstyle = opt;
1722 opt = process_keyval_opt(options, "refsection");
1724 if (opt == "none" || opt == "part"
1725 || opt == "chapter" || opt == "section"
1726 || opt == "subsection")
1729 cerr << "Ignoring unkown refesection value '"
1732 opt = process_keyval_opt(options, "bibencoding");
1735 if (!options.empty()) {
1736 h_biblio_options = join(options, ",");
1741 else if (name == "jurabib") {
1742 h_biblio_style = "jurabib";
1743 h_cite_engine = "jurabib";
1744 h_cite_engine_type = "authoryear";
1745 if (!options.empty())
1746 h_biblio_options = join(options, ",");
1749 else if (name == "bibtopic")
1750 h_use_bibtopic = "true";
1752 else if (name == "chapterbib")
1753 h_multibib = "child";
1755 else if (name == "hyperref")
1756 handle_hyperref(options);
1758 else if (name == "algorithm2e") {
1759 // Load "algorithm2e" module
1760 addModule("algorithm2e");
1761 // Add the package options to the global document options
1762 if (!options.empty()) {
1763 if (h_options.empty())
1764 h_options = join(options, ",");
1766 h_options += ',' + join(options, ",");
1769 else if (name == "microtype") {
1770 //we internally support only microtype without params
1771 if (options.empty())
1772 h_use_microtype = "true";
1774 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1777 else if (name == "lineno") {
1778 h_use_lineno = "true";
1779 if (!options.empty()) {
1780 h_lineno_options = join(options, ",");
1785 else if (!in_lyx_preamble) {
1786 if (options.empty())
1787 h_preamble << "\\usepackage{" << name << '}';
1789 h_preamble << "\\usepackage[" << opts << "]{"
1793 if (p.next_token().cat() == catNewline ||
1794 (p.next_token().cat() == catSpace &&
1795 p.next_next_token().cat() == catNewline))
1799 // We need to do something with the options...
1800 if (!options.empty() && !detectEncoding)
1801 cerr << "Ignoring options '" << join(options, ",")
1802 << "' of package " << name << '.' << endl;
1804 // remove the whitespace
1809 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1812 Token t = p.get_token();
1813 if (t.cat() == catEscape &&
1814 is_known(t.cs(), known_if_commands))
1815 handle_if(p, in_lyx_preamble);
1817 if (!in_lyx_preamble)
1818 h_preamble << t.asInput();
1819 if (t.cat() == catEscape && t.cs() == "fi")
1826 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1828 if (contains(h_float_placement, "H"))
1829 registerAutomaticallyLoadedPackage("float");
1830 if (h_spacing != "single" && h_spacing != "default")
1831 registerAutomaticallyLoadedPackage("setspace");
1832 if (h_use_packages["amsmath"] == "2") {
1833 // amsbsy and amstext are already provided by amsmath
1834 registerAutomaticallyLoadedPackage("amsbsy");
1835 registerAutomaticallyLoadedPackage("amstext");
1838 // output the LyX file settings
1839 // Important: Keep the version formatting in sync with LyX and
1840 // lyx2lyx (bug 7951)
1841 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1842 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1843 << lyx_version_minor << '\n'
1844 << "\\lyxformat " << LYX_FORMAT << '\n'
1845 << "\\begin_document\n"
1846 << "\\begin_header\n"
1847 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1848 << "\\origin " << origin << "\n"
1849 << "\\textclass " << h_textclass << "\n";
1850 string const raw = subdoc ? empty_string() : h_preamble.str();
1852 os << "\\begin_preamble\n";
1853 for (string::size_type i = 0; i < raw.size(); ++i) {
1854 if (raw[i] == package_beg_sep) {
1855 // Here follows some package loading code that
1856 // must be skipped if the package is loaded
1858 string::size_type j = raw.find(package_mid_sep, i);
1859 if (j == string::npos)
1861 string::size_type k = raw.find(package_end_sep, j);
1862 if (k == string::npos)
1864 string const package = raw.substr(i + 1, j - i - 1);
1865 string const replacement = raw.substr(j + 1, k - j - 1);
1866 if (auto_packages.find(package) == auto_packages.end())
1872 os << "\n\\end_preamble\n";
1874 if (!h_options.empty())
1875 os << "\\options " << h_options << "\n";
1876 os << "\\use_default_options " << h_use_default_options << "\n";
1877 if (!used_modules.empty()) {
1878 os << "\\begin_modules\n";
1879 vector<string>::const_iterator const end = used_modules.end();
1880 vector<string>::const_iterator it = used_modules.begin();
1881 for (; it != end; ++it)
1883 os << "\\end_modules\n";
1885 if (!h_includeonlys.empty()) {
1886 os << "\\begin_includeonly\n";
1887 for (auto const & iofile : h_includeonlys)
1888 os << iofile << '\n';
1889 os << "\\end_includeonly\n";
1891 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1892 << "\\language " << h_language << "\n"
1893 << "\\language_package " << h_language_package << "\n"
1894 << "\\inputencoding " << h_inputencoding << "\n"
1895 << "\\fontencoding " << h_fontencoding << "\n"
1896 << "\\font_roman \"" << h_font_roman[0]
1897 << "\" \"" << h_font_roman[1] << "\"\n"
1898 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1899 << "\\font_typewriter \"" << h_font_typewriter[0]
1900 << "\" \"" << h_font_typewriter[1] << "\"\n"
1901 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1902 << "\\font_default_family " << h_font_default_family << "\n"
1903 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1904 << "\\font_sc " << h_font_sc << "\n"
1905 << "\\font_roman_osf " << h_font_roman_osf << "\n"
1906 << "\\font_sans_osf " << h_font_sans_osf << "\n"
1907 << "\\font_typewriter_osf " << h_font_typewriter_osf << "\n";
1908 if (!h_font_roman_opts.empty())
1909 os << "\\font_roman_opts \"" << h_font_roman_opts << "\"" << '\n';
1910 os << "\\font_sf_scale " << h_font_sf_scale[0]
1911 << ' ' << h_font_sf_scale[1] << '\n';
1912 if (!h_font_sans_opts.empty())
1913 os << "\\font_sans_opts \"" << h_font_sans_opts << "\"" << '\n';
1914 os << "\\font_tt_scale " << h_font_tt_scale[0]
1915 << ' ' << h_font_tt_scale[1] << '\n';
1916 if (!h_font_cjk.empty())
1917 os << "\\font_cjk " << h_font_cjk << '\n';
1918 if (!h_font_typewriter_opts.empty())
1919 os << "\\font_typewriter_opts \"" << h_font_typewriter_opts << "\"" << '\n';
1920 os << "\\use_microtype " << h_use_microtype << '\n'
1921 << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
1922 << "\\graphics " << h_graphics << '\n'
1923 << "\\default_output_format " << h_default_output_format << "\n"
1924 << "\\output_sync " << h_output_sync << "\n";
1925 if (h_output_sync == "1")
1926 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1927 os << "\\bibtex_command " << h_bibtex_command << "\n"
1928 << "\\index_command " << h_index_command << "\n";
1929 if (!h_float_placement.empty())
1930 os << "\\float_placement " << h_float_placement << "\n";
1931 os << "\\paperfontsize " << h_paperfontsize << "\n"
1932 << "\\spacing " << h_spacing << "\n"
1933 << "\\use_hyperref " << h_use_hyperref << '\n';
1934 if (h_use_hyperref == "true") {
1935 if (!h_pdf_title.empty())
1936 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1937 if (!h_pdf_author.empty())
1938 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1939 if (!h_pdf_subject.empty())
1940 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1941 if (!h_pdf_keywords.empty())
1942 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1943 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1944 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1945 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1946 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1947 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1948 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1949 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1950 "\\pdf_backref " << h_pdf_backref << "\n"
1951 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1952 if (!h_pdf_pagemode.empty())
1953 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1954 if (!h_pdf_quoted_options.empty())
1955 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1957 os << "\\papersize " << h_papersize << "\n"
1958 << "\\use_geometry " << h_use_geometry << '\n';
1959 for (map<string, string>::const_iterator it = h_use_packages.begin();
1960 it != h_use_packages.end(); ++it)
1961 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1962 os << "\\cite_engine " << h_cite_engine << '\n'
1963 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1964 << "\\biblio_style " << h_biblio_style << "\n"
1965 << "\\use_bibtopic " << h_use_bibtopic << "\n";
1966 if (!h_biblio_options.empty())
1967 os << "\\biblio_options " << h_biblio_options << "\n";
1968 if (!h_biblatex_bibstyle.empty())
1969 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
1970 if (!h_biblatex_citestyle.empty())
1971 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
1972 if (!h_multibib.empty())
1973 os << "\\multibib " << h_multibib << "\n";
1974 os << "\\use_indices " << h_use_indices << "\n"
1975 << "\\paperorientation " << h_paperorientation << '\n'
1976 << "\\suppress_date " << h_suppress_date << '\n'
1977 << "\\justification " << h_justification << '\n'
1978 << "\\use_refstyle " << h_use_refstyle << '\n'
1979 << "\\use_minted " << h_use_minted << '\n'
1980 << "\\use_lineno " << h_use_lineno << '\n';
1981 if (!h_lineno_options.empty())
1982 os << "\\lineno_options " << h_lineno_options << '\n';
1983 if (!h_fontcolor.empty())
1984 os << "\\fontcolor " << h_fontcolor << '\n';
1985 if (!h_notefontcolor.empty())
1986 os << "\\notefontcolor " << h_notefontcolor << '\n';
1987 if (!h_backgroundcolor.empty())
1988 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1989 if (!h_boxbgcolor.empty())
1990 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1991 if (index_number != 0)
1992 for (int i = 0; i < index_number; i++) {
1993 os << "\\index " << h_index[i] << '\n'
1994 << "\\shortcut " << h_shortcut[i] << '\n'
1995 << "\\color " << h_color << '\n'
1999 os << "\\index " << h_index[0] << '\n'
2000 << "\\shortcut " << h_shortcut[0] << '\n'
2001 << "\\color " << h_color << '\n'
2005 << "\\secnumdepth " << h_secnumdepth << "\n"
2006 << "\\tocdepth " << h_tocdepth << "\n"
2007 << "\\paragraph_separation " << h_paragraph_separation << "\n";
2008 if (h_paragraph_separation == "skip")
2009 os << "\\defskip " << h_defskip << "\n";
2011 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
2012 os << "\\is_math_indent " << h_is_mathindent << "\n";
2013 if (!h_mathindentation.empty())
2014 os << "\\math_indentation " << h_mathindentation << "\n";
2015 os << "\\math_numbering_side " << h_math_numbering_side << "\n";
2016 os << "\\quotes_style " << h_quotes_style << "\n"
2017 << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
2018 << "\\papercolumns " << h_papercolumns << "\n"
2019 << "\\papersides " << h_papersides << "\n"
2020 << "\\paperpagestyle " << h_paperpagestyle << "\n";
2021 if (!h_listings_params.empty())
2022 os << "\\listings_params " << h_listings_params << "\n";
2023 os << "\\tracking_changes " << h_tracking_changes << "\n"
2024 << "\\output_changes " << h_output_changes << "\n"
2025 << "\\html_math_output " << h_html_math_output << "\n"
2026 << "\\html_css_as_file " << h_html_css_as_file << "\n"
2027 << "\\html_be_strict " << h_html_be_strict << "\n"
2029 << "\\end_header\n\n"
2030 << "\\begin_body\n";
2035 void Preamble::parse(Parser & p, string const & forceclass,
2036 TeX2LyXDocClass & tc)
2038 // initialize fixed types
2039 special_columns_['D'] = 3;
2040 parse(p, forceclass, false, tc);
2044 void Preamble::parse(Parser & p, string const & forceclass,
2045 bool detectEncoding, TeX2LyXDocClass & tc)
2047 bool is_full_document = false;
2048 bool is_lyx_file = false;
2049 bool in_lyx_preamble = false;
2051 // determine whether this is a full document or a fragment for inclusion
2053 Token const & t = p.get_token();
2055 if (t.cat() == catEscape && t.cs() == "documentclass") {
2056 is_full_document = true;
2062 if (detectEncoding && !is_full_document)
2065 while (is_full_document && p.good()) {
2066 if (detectEncoding && h_inputencoding != "auto-legacy" &&
2067 h_inputencoding != "auto-legacy-plain")
2070 Token const & t = p.get_token();
2073 if (!detectEncoding)
2074 cerr << "t: " << t << '\n';
2080 if (!in_lyx_preamble &&
2081 (t.cat() == catLetter ||
2082 t.cat() == catSuper ||
2083 t.cat() == catSub ||
2084 t.cat() == catOther ||
2085 t.cat() == catMath ||
2086 t.cat() == catActive ||
2087 t.cat() == catBegin ||
2088 t.cat() == catEnd ||
2089 t.cat() == catAlign ||
2090 t.cat() == catParameter)) {
2091 h_preamble << t.cs();
2095 if (!in_lyx_preamble &&
2096 (t.cat() == catSpace || t.cat() == catNewline)) {
2097 h_preamble << t.asInput();
2101 if (t.cat() == catComment) {
2102 static regex const islyxfile("%% LyX .* created this file");
2103 static regex const usercommands("User specified LaTeX commands");
2105 string const comment = t.asInput();
2107 // magically switch encoding default if it looks like XeLaTeX
2108 static string const magicXeLaTeX =
2109 "% This document must be compiled with XeLaTeX ";
2110 if (comment.size() > magicXeLaTeX.size()
2111 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
2112 && h_inputencoding == "auto-legacy") {
2113 if (!detectEncoding)
2114 cerr << "XeLaTeX comment found, switching to UTF8\n";
2115 h_inputencoding = "utf8";
2118 if (regex_search(comment, sub, islyxfile)) {
2120 in_lyx_preamble = true;
2121 } else if (is_lyx_file
2122 && regex_search(comment, sub, usercommands))
2123 in_lyx_preamble = false;
2124 else if (!in_lyx_preamble)
2125 h_preamble << t.asInput();
2129 if (t.cs() == "PassOptionsToPackage") {
2130 string const poptions = p.getArg('{', '}');
2131 string const package = p.verbatim_item();
2132 extra_package_options_.insert(make_pair(package, poptions));
2136 if (t.cs() == "pagestyle") {
2137 h_paperpagestyle = p.verbatim_item();
2141 if (t.cs() == "setdefaultlanguage") {
2143 // We don't yet care about non-language variant options
2144 // because LyX doesn't support this yet, see bug #8214
2146 string langopts = p.getOpt();
2147 // check if the option contains a variant, if yes, extract it
2148 string::size_type pos_var = langopts.find("variant");
2149 string::size_type i = langopts.find(',', pos_var);
2150 string::size_type k = langopts.find('=', pos_var);
2151 if (pos_var != string::npos){
2153 if (i == string::npos)
2154 variant = langopts.substr(k + 1, langopts.length() - k - 2);
2156 variant = langopts.substr(k + 1, i - k - 1);
2157 h_language = variant;
2161 h_language = p.verbatim_item();
2162 //finally translate the poyglossia name to a LyX name
2163 h_language = polyglossia2lyx(h_language);
2167 if (t.cs() == "setotherlanguage") {
2168 // We don't yet care about the option because LyX doesn't
2169 // support this yet, see bug #8214
2170 p.hasOpt() ? p.getOpt() : string();
2175 if (t.cs() == "setmainfont") {
2176 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2177 h_font_roman[1] = p.getArg('{', '}');
2178 if (!fontopts.empty()) {
2179 vector<string> opts = getVectorFromString(fontopts);
2181 for (auto const & opt : opts) {
2182 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2185 if (!fontopts.empty())
2189 h_font_roman_opts = fontopts;
2194 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
2195 // LyX currently only supports the scale option
2196 string scale, fontopts;
2198 fontopts = p.getArg('[', ']');
2199 if (!fontopts.empty()) {
2200 vector<string> opts = getVectorFromString(fontopts);
2202 for (auto const & opt : opts) {
2203 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2206 if (prefixIs(opt, "Scale=")) {
2207 scale_as_percentage(opt, scale);
2210 if (!fontopts.empty())
2216 if (t.cs() == "setsansfont") {
2218 h_font_sf_scale[1] = scale;
2219 h_font_sans[1] = p.getArg('{', '}');
2220 if (!fontopts.empty())
2221 h_font_sans_opts = fontopts;
2224 h_font_tt_scale[1] = scale;
2225 h_font_typewriter[1] = p.getArg('{', '}');
2226 if (!fontopts.empty())
2227 h_font_typewriter_opts = fontopts;
2232 if (t.cs() == "babelfont") {
2234 h_use_non_tex_fonts = true;
2235 h_language_package = "babel";
2236 if (h_inputencoding == "auto-legacy")
2237 p.setEncoding("UTF-8");
2238 // we don't care about the lang option
2239 string const lang = p.hasOpt() ? p.getArg('[', ']') : string();
2240 string const family = p.getArg('{', '}');
2241 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2242 string const fontname = p.getArg('{', '}');
2243 if (lang.empty() && family == "rm") {
2244 h_font_roman[1] = fontname;
2245 if (!fontopts.empty()) {
2246 vector<string> opts = getVectorFromString(fontopts);
2248 for (auto const & opt : opts) {
2249 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2252 if (!fontopts.empty())
2256 h_font_roman_opts = fontopts;
2259 } else if (lang.empty() && (family == "sf" || family == "tt")) {
2261 if (!fontopts.empty()) {
2262 vector<string> opts = getVectorFromString(fontopts);
2264 for (auto const & opt : opts) {
2265 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2268 if (prefixIs(opt, "Scale=")) {
2269 scale_as_percentage(opt, scale);
2272 if (!fontopts.empty())
2277 if (family == "sf") {
2279 h_font_sf_scale[1] = scale;
2280 h_font_sans[1] = fontname;
2281 if (!fontopts.empty())
2282 h_font_sans_opts = fontopts;
2285 h_font_tt_scale[1] = scale;
2286 h_font_typewriter[1] = fontname;
2287 if (!fontopts.empty())
2288 h_font_typewriter_opts = fontopts;
2292 // not rm, sf or tt or lang specific
2293 h_preamble << '\\' << t.cs();
2295 h_preamble << '[' << lang << ']';
2296 h_preamble << '{' << family << '}';
2297 if (!fontopts.empty())
2298 h_preamble << '[' << fontopts << ']';
2299 h_preamble << '{' << fontname << '}' << '\n';
2304 if (t.cs() == "date") {
2305 string argument = p.getArg('{', '}');
2306 if (argument.empty())
2307 h_suppress_date = "true";
2309 h_preamble << t.asInput() << '{' << argument << '}';
2313 if (t.cs() == "color") {
2314 string const space =
2315 (p.hasOpt() ? p.getOpt() : string());
2316 string argument = p.getArg('{', '}');
2317 // check the case that a standard color is used
2318 if (space.empty() && is_known(argument, known_basic_colors)) {
2319 h_fontcolor = rgbcolor2code(argument);
2320 registerAutomaticallyLoadedPackage("color");
2321 } else if (space.empty() && argument == "document_fontcolor")
2322 registerAutomaticallyLoadedPackage("color");
2323 // check the case that LyX's document_fontcolor is defined
2324 // but not used for \color
2326 h_preamble << t.asInput();
2328 h_preamble << space;
2329 h_preamble << '{' << argument << '}';
2330 // the color might already be set because \definecolor
2331 // is parsed before this
2337 if (t.cs() == "pagecolor") {
2338 string argument = p.getArg('{', '}');
2339 // check the case that a standard color is used
2340 if (is_known(argument, known_basic_colors)) {
2341 h_backgroundcolor = rgbcolor2code(argument);
2342 } else if (argument == "page_backgroundcolor")
2343 registerAutomaticallyLoadedPackage("color");
2344 // check the case that LyX's page_backgroundcolor is defined
2345 // but not used for \pagecolor
2347 h_preamble << t.asInput() << '{' << argument << '}';
2348 // the color might already be set because \definecolor
2349 // is parsed before this
2350 h_backgroundcolor = "";
2355 if (t.cs() == "makeatletter") {
2356 // LyX takes care of this
2357 p.setCatcode('@', catLetter);
2361 if (t.cs() == "makeatother") {
2362 // LyX takes care of this
2363 p.setCatcode('@', catOther);
2367 if (t.cs() == "makeindex") {
2368 // LyX will re-add this if a print index command is found
2373 if (t.cs() == "newindex") {
2374 string const indexname = p.getArg('[', ']');
2375 string const shortcut = p.verbatim_item();
2376 if (!indexname.empty())
2377 h_index[index_number] = indexname;
2379 h_index[index_number] = shortcut;
2380 h_shortcut[index_number] = shortcut;
2386 if (t.cs() == "addbibresource") {
2387 string const options = p.getArg('[', ']');
2388 string const arg = removeExtension(p.getArg('{', '}'));
2389 if (!options.empty()) {
2390 // check if the option contains a bibencoding, if yes, extract it
2391 string::size_type pos = options.find("bibencoding=");
2393 if (pos != string::npos) {
2394 string::size_type i = options.find(',', pos);
2395 if (i == string::npos)
2396 encoding = options.substr(pos + 1);
2398 encoding = options.substr(pos, i - pos);
2399 pos = encoding.find('=');
2400 if (pos == string::npos)
2403 encoding = encoding.substr(pos + 1);
2405 if (!encoding.empty())
2406 biblatex_encodings.push_back(normalize_filename(arg) + ' ' + encoding);
2408 biblatex_bibliographies.push_back(arg);
2412 if (t.cs() == "bibliography") {
2413 vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
2414 biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
2418 if (t.cs() == "RS@ifundefined") {
2419 string const name = p.verbatim_item();
2420 string const body1 = p.verbatim_item();
2421 string const body2 = p.verbatim_item();
2422 // only non-lyxspecific stuff
2423 if (in_lyx_preamble &&
2424 (name == "subsecref" || name == "thmref" || name == "lemref"))
2428 ss << '\\' << t.cs();
2429 ss << '{' << name << '}'
2430 << '{' << body1 << '}'
2431 << '{' << body2 << '}';
2432 h_preamble << ss.str();
2437 if (t.cs() == "AtBeginDocument") {
2438 string const name = p.verbatim_item();
2439 // only non-lyxspecific stuff
2440 if (in_lyx_preamble &&
2441 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
2442 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
2443 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
2444 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
2445 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
2446 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
2447 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
2448 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
2449 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
2450 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
2451 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
2452 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
2453 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
2454 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
2455 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
2459 ss << '\\' << t.cs();
2460 ss << '{' << name << '}';
2461 h_preamble << ss.str();
2466 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
2467 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
2468 || t.cs() == "providecommand" || t.cs() == "providecommandx"
2469 || t.cs() == "DeclareRobustCommand"
2470 || t.cs() == "DeclareRobustCommandx"
2471 || t.cs() == "ProvideTextCommandDefault"
2472 || t.cs() == "DeclareMathAccent") {
2474 if (p.next_token().character() == '*') {
2478 string const name = p.verbatim_item();
2479 string const opt1 = p.getFullOpt();
2480 string const opt2 = p.getFullOpt();
2481 string const body = p.verbatim_item();
2482 // store the in_lyx_preamble setting
2483 bool const was_in_lyx_preamble = in_lyx_preamble;
2485 if (name == "\\rmdefault")
2486 if (is_known(body, known_roman_font_packages)) {
2487 h_font_roman[0] = body;
2489 in_lyx_preamble = true;
2491 if (name == "\\sfdefault")
2492 if (is_known(body, known_sans_font_packages)) {
2493 h_font_sans[0] = body;
2495 in_lyx_preamble = true;
2497 if (name == "\\ttdefault")
2498 if (is_known(body, known_typewriter_font_packages)) {
2499 h_font_typewriter[0] = body;
2501 in_lyx_preamble = true;
2503 if (name == "\\familydefault") {
2504 string family = body;
2505 // remove leading "\"
2506 h_font_default_family = family.erase(0,1);
2508 in_lyx_preamble = true;
2511 // remove LyX-specific definitions that are re-added by LyX
2513 // \lyxline is an ancient command that is converted by tex2lyx into
2514 // a \rule therefore remove its preamble code
2515 if (name == "\\lyxdot" || name == "\\lyxarrow"
2516 || name == "\\lyxline" || name == "\\LyX") {
2518 in_lyx_preamble = true;
2521 // Add the command to the known commands
2522 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
2524 // only non-lyxspecific stuff
2525 if (!in_lyx_preamble) {
2527 ss << '\\' << t.cs();
2530 ss << '{' << name << '}' << opt1 << opt2
2531 << '{' << body << "}";
2532 if (prefixIs(t.cs(), "renew") || !contains(h_preamble.str(), ss.str()))
2533 h_preamble << ss.str();
2535 ostream & out = in_preamble ? h_preamble : os;
2536 out << "\\" << t.cs() << "{" << name << "}"
2537 << opts << "{" << body << "}";
2540 // restore the in_lyx_preamble setting
2541 in_lyx_preamble = was_in_lyx_preamble;
2545 if (t.cs() == "documentclass") {
2546 vector<string>::iterator it;
2547 vector<string> opts = split_options(p.getArg('[', ']'));
2548 // FIXME This does not work for classes that have a
2549 // different name in LyX than in LaTeX
2550 h_textclass = p.getArg('{', '}');
2552 // Force textclass if the user wanted it
2553 if (!forceclass.empty())
2554 h_textclass = forceclass;
2555 tc.setName(h_textclass);
2556 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2557 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2562 // Try those who are (most likely) known to all packages first
2563 handle_opt(opts, known_fontsizes, h_paperfontsize);
2564 delete_opt(opts, known_fontsizes);
2565 // delete "pt" at the end
2566 string::size_type i = h_paperfontsize.find("pt");
2567 if (i != string::npos)
2568 h_paperfontsize.erase(i);
2569 // Now those known specifically to the class
2571 vector<string> class_fsizes = getVectorFromString(tc.opt_fontsize(), "|");
2572 string const fsize_format = tc.fontsizeformat();
2573 for (auto const fsize : class_fsizes) {
2574 string latexsize = subst(fsize_format, "$$s", fsize);
2575 vector<string>::iterator it = find(opts.begin(), opts.end(), latexsize);
2576 if (it != opts.end()) {
2577 h_paperfontsize = fsize;
2583 // The documentclass options are always parsed before the options
2584 // of the babel call so that a language cannot overwrite the babel
2586 handle_opt(opts, known_languages, h_language);
2587 delete_opt(opts, known_languages);
2590 if ((it = find(opts.begin(), opts.end(), "fleqn"))
2592 h_is_mathindent = "1";
2595 // formula numbering side
2596 if ((it = find(opts.begin(), opts.end(), "leqno"))
2598 h_math_numbering_side = "left";
2601 else if ((it = find(opts.begin(), opts.end(), "reqno"))
2603 h_math_numbering_side = "right";
2607 // paper orientation
2608 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
2609 h_paperorientation = "landscape";
2613 if ((it = find(opts.begin(), opts.end(), "oneside"))
2618 if ((it = find(opts.begin(), opts.end(), "twoside"))
2624 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
2626 h_papercolumns = "1";
2629 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
2631 h_papercolumns = "2";
2635 // some size options are known by the document class, other sizes
2636 // are handled by the \geometry command of the geometry package
2638 vector<string> class_psizes = getVectorFromString(tc.opt_pagesize(), "|");
2639 string const psize_format = tc.pagesizeformat();
2640 for (auto const psize : class_psizes) {
2641 string latexsize = subst(psize_format, "$$s", psize);
2642 vector<string>::iterator it = find(opts.begin(), opts.end(), latexsize);
2643 if (it != opts.end()) {
2644 h_papersize = psize;
2648 if (psize_format == "$$spaper")
2650 // Also try with the default format since this is understood by
2652 latexsize = psize + "paper";
2653 it = find(opts.begin(), opts.end(), latexsize);
2654 if (it != opts.end()) {
2655 h_papersize = psize;
2660 // the remaining options
2661 h_options = join(opts, ",");
2665 if (t.cs() == "usepackage") {
2666 string const options = p.getArg('[', ']');
2667 string const name = p.getArg('{', '}');
2668 vector<string> vecnames;
2669 split(name, vecnames, ',');
2670 vector<string>::const_iterator it = vecnames.begin();
2671 vector<string>::const_iterator end = vecnames.end();
2672 for (; it != end; ++it)
2673 handle_package(p, trimSpaceAndEol(*it), options,
2674 in_lyx_preamble, detectEncoding);
2678 if (t.cs() == "inputencoding") {
2679 string const encoding = p.getArg('{','}');
2680 Encoding const * const enc = encodings.fromLaTeXName(
2681 encoding, Encoding::inputenc, true);
2683 if (!detectEncoding)
2684 cerr << "Unknown encoding " << encoding
2685 << ". Ignoring." << std::endl;
2688 h_inputencoding = enc->name();
2689 p.setEncoding(enc->iconvName());
2694 if (t.cs() == "newenvironment") {
2695 string const name = p.getArg('{', '}');
2696 string const opt1 = p.getFullOpt();
2697 string const opt2 = p.getFullOpt();
2698 string const beg = p.verbatim_item();
2699 string const end = p.verbatim_item();
2700 if (!in_lyx_preamble) {
2701 h_preamble << "\\newenvironment{" << name
2702 << '}' << opt1 << opt2 << '{'
2703 << beg << "}{" << end << '}';
2705 add_known_environment(name, opt1, !opt2.empty(),
2706 from_utf8(beg), from_utf8(end));
2710 if (t.cs() == "newtheorem") {
2712 if (p.next_token().character() == '*') {
2716 string const name = p.getArg('{', '}');
2717 string const opt1 = p.getFullOpt();
2718 string const opt2 = p.getFullOpt();
2719 string const body = p.verbatim_item();
2720 string const opt3 = p.getFullOpt();
2721 string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
2723 string const complete = cmd + "{" + name + '}' +
2724 opt1 + opt2 + '{' + body + '}' + opt3;
2726 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
2728 if (!in_lyx_preamble)
2729 h_preamble << complete;
2733 if (t.cs() == "def") {
2734 string name = p.get_token().cs();
2735 // In fact, name may be more than the name:
2736 // In the test case of bug 8116
2737 // name == "csname SF@gobble@opt \endcsname".
2738 // Therefore, we need to use asInput() instead of cs().
2739 while (p.next_token().cat() != catBegin)
2740 name += p.get_token().asInput();
2741 if (!in_lyx_preamble)
2742 h_preamble << "\\def\\" << name << '{'
2743 << p.verbatim_item() << "}";
2747 if (t.cs() == "newcolumntype") {
2748 string const name = p.getArg('{', '}');
2749 trimSpaceAndEol(name);
2751 string opts = p.getOpt();
2752 if (!opts.empty()) {
2753 istringstream is(string(opts, 1));
2756 special_columns_[name[0]] = nargs;
2757 h_preamble << "\\newcolumntype{" << name << "}";
2759 h_preamble << "[" << nargs << "]";
2760 h_preamble << "{" << p.verbatim_item() << "}";
2764 if (t.cs() == "setcounter") {
2765 string const name = p.getArg('{', '}');
2766 string const content = p.getArg('{', '}');
2767 if (name == "secnumdepth")
2768 h_secnumdepth = content;
2769 else if (name == "tocdepth")
2770 h_tocdepth = content;
2772 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
2776 if (t.cs() == "setlength") {
2777 string const name = p.verbatim_item();
2778 string const content = p.verbatim_item();
2779 // the paragraphs are only not indented when \parindent is set to zero
2780 if (name == "\\parindent" && content != "") {
2781 if (content[0] == '0')
2782 h_paragraph_separation = "skip";
2784 h_paragraph_indentation = translate_len(content);
2785 } else if (name == "\\parskip") {
2786 if (content == "\\smallskipamount")
2787 h_defskip = "smallskip";
2788 else if (content == "\\medskipamount")
2789 h_defskip = "medskip";
2790 else if (content == "\\bigskipamount")
2791 h_defskip = "bigskip";
2793 h_defskip = translate_len(content);
2794 } else if (name == "\\mathindent") {
2795 h_mathindentation = translate_len(content);
2797 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2801 if (t.cs() == "onehalfspacing") {
2802 h_spacing = "onehalf";
2806 if (t.cs() == "doublespacing") {
2807 h_spacing = "double";
2811 if (t.cs() == "setstretch") {
2812 h_spacing = "other " + p.verbatim_item();
2816 if (t.cs() == "synctex") {
2817 // the scheme is \synctex=value
2818 // where value can only be "1" or "-1"
2819 h_output_sync = "1";
2820 // there can be any character behind the value (e.g. a linebreak or a '\'
2821 // therefore we extract it char by char
2823 string value = p.get_token().asInput();
2825 value += p.get_token().asInput();
2826 h_output_sync_macro = "\\synctex=" + value;
2830 if (t.cs() == "begin") {
2831 string const name = p.getArg('{', '}');
2832 if (name == "document")
2834 h_preamble << "\\begin{" << name << "}";
2838 if (t.cs() == "geometry") {
2839 vector<string> opts = split_options(p.getArg('{', '}'));
2840 handle_geometry(opts);
2844 if (t.cs() == "definecolor") {
2845 string const color = p.getArg('{', '}');
2846 string const space = p.getArg('{', '}');
2847 string const value = p.getArg('{', '}');
2848 if (color == "document_fontcolor" && space == "rgb") {
2849 RGBColor c(RGBColorFromLaTeX(value));
2850 h_fontcolor = X11hexname(c);
2851 } else if (color == "note_fontcolor" && space == "rgb") {
2852 RGBColor c(RGBColorFromLaTeX(value));
2853 h_notefontcolor = X11hexname(c);
2854 } else if (color == "page_backgroundcolor" && space == "rgb") {
2855 RGBColor c(RGBColorFromLaTeX(value));
2856 h_backgroundcolor = X11hexname(c);
2857 } else if (color == "shadecolor" && space == "rgb") {
2858 RGBColor c(RGBColorFromLaTeX(value));
2859 h_boxbgcolor = X11hexname(c);
2861 h_preamble << "\\definecolor{" << color
2862 << "}{" << space << "}{" << value
2868 if (t.cs() == "bibliographystyle") {
2869 h_biblio_style = p.verbatim_item();
2873 if (t.cs() == "jurabibsetup") {
2874 // FIXME p.getArg('{', '}') is most probably wrong (it
2875 // does not handle nested braces).
2876 // Use p.verbatim_item() instead.
2877 vector<string> jurabibsetup =
2878 split_options(p.getArg('{', '}'));
2879 // add jurabibsetup to the jurabib package options
2880 add_package("jurabib", jurabibsetup);
2881 if (!jurabibsetup.empty()) {
2882 h_preamble << "\\jurabibsetup{"
2883 << join(jurabibsetup, ",") << '}';
2888 if (t.cs() == "hypersetup") {
2889 vector<string> hypersetup =
2890 split_options(p.verbatim_item());
2891 // add hypersetup to the hyperref package options
2892 handle_hyperref(hypersetup);
2893 if (!hypersetup.empty()) {
2894 h_preamble << "\\hypersetup{"
2895 << join(hypersetup, ",") << '}';
2900 if (t.cs() == "includeonly") {
2901 vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
2902 for (auto & iofile : includeonlys) {
2903 string filename(normalize_filename(iofile));
2904 string const path = getMasterFilePath(true);
2905 // We want to preserve relative/absolute filenames,
2906 // therefore path is only used for testing
2907 if (!makeAbsPath(filename, path).exists()) {
2908 // The file extension is probably missing.
2909 // Now try to find it out.
2910 string const tex_name =
2911 find_file(filename, path,
2912 known_tex_extensions);
2913 if (!tex_name.empty())
2914 filename = tex_name;
2917 if (makeAbsPath(filename, path).exists())
2918 fix_child_filename(filename);
2920 cerr << "Warning: Could not find included file '"
2921 << filename << "'." << endl;
2922 outname = changeExtension(filename, "lyx");
2923 h_includeonlys.push_back(outname);
2928 if (is_known(t.cs(), known_if_3arg_commands)) {
2929 // prevent misparsing of \usepackage if it is used
2930 // as an argument (see e.g. our own output of
2931 // \@ifundefined above)
2932 string const arg1 = p.verbatim_item();
2933 string const arg2 = p.verbatim_item();
2934 string const arg3 = p.verbatim_item();
2935 // test case \@ifundefined{date}{}{\date{}}
2936 if (t.cs() == "@ifundefined" && arg1 == "date" &&
2937 arg2.empty() && arg3 == "\\date{}") {
2938 h_suppress_date = "true";
2939 // older tex2lyx versions did output
2940 // \@ifundefined{definecolor}{\usepackage{color}}{}
2941 } else if (t.cs() == "@ifundefined" &&
2942 arg1 == "definecolor" &&
2943 arg2 == "\\usepackage{color}" &&
2945 if (!in_lyx_preamble)
2946 h_preamble << package_beg_sep
2949 << "\\@ifundefined{definecolor}{color}{}"
2952 //\@ifundefined{showcaptionsetup}{}{%
2953 // \PassOptionsToPackage{caption=false}{subfig}}
2954 // that LyX uses for subfloats
2955 } else if (t.cs() == "@ifundefined" &&
2956 arg1 == "showcaptionsetup" && arg2.empty()
2957 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
2959 } else if (!in_lyx_preamble) {
2960 h_preamble << t.asInput()
2961 << '{' << arg1 << '}'
2962 << '{' << arg2 << '}'
2963 << '{' << arg3 << '}';
2968 if (is_known(t.cs(), known_if_commands)) {
2969 // must not parse anything in conditional code, since
2970 // LyX would output the parsed contents unconditionally
2971 if (!in_lyx_preamble)
2972 h_preamble << t.asInput();
2973 handle_if(p, in_lyx_preamble);
2977 if (!t.cs().empty() && !in_lyx_preamble) {
2978 h_preamble << '\\' << t.cs();
2983 // remove the whitespace
2986 if (h_papersides.empty()) {
2989 h_papersides = ss.str();
2992 // If the CJK package is used we cannot set the document language from
2993 // the babel options. Instead, we guess which language is used most
2994 // and set this one.
2995 default_language = h_language;
2996 if (is_full_document &&
2997 (auto_packages.find("CJK") != auto_packages.end() ||
2998 auto_packages.find("CJKutf8") != auto_packages.end())) {
3000 h_language = guessLanguage(p, default_language);
3002 if (explicit_babel && h_language != default_language) {
3003 // We set the document language to a CJK language,
3004 // but babel is explicitly called in the user preamble
3005 // without options. LyX will not add the default
3006 // language to the document options if it is either
3007 // english, or no text is set as default language.
3008 // Therefore we need to add a language option explicitly.
3009 // FIXME: It would be better to remove all babel calls
3010 // from the user preamble, but this is difficult
3011 // without re-introducing bug 7861.
3012 if (h_options.empty())
3013 h_options = lyx2babel(default_language);
3015 h_options += ',' + lyx2babel(default_language);
3019 // Finally, set the quote style.
3020 // LyX knows the following quotes styles:
3021 // british, cjk, cjkangle, danish, english, french, german,
3022 // polish, russian, swedish and swiss
3023 // conversion list taken from
3024 // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
3025 // (quotes for kazakh are unknown)
3027 if (is_known(h_language, known_british_quotes_languages))
3028 h_quotes_style = "british";
3030 else if (is_known(h_language, known_cjk_quotes_languages))
3031 h_quotes_style = "cjk";
3033 else if (is_known(h_language, known_cjkangle_quotes_languages))
3034 h_quotes_style = "cjkangle";
3036 else if (is_known(h_language, known_danish_quotes_languages))
3037 h_quotes_style = "danish";
3039 else if (is_known(h_language, known_french_quotes_languages))
3040 h_quotes_style = "french";
3042 else if (is_known(h_language, known_german_quotes_languages))
3043 h_quotes_style = "german";
3045 else if (is_known(h_language, known_polish_quotes_languages))
3046 h_quotes_style = "polish";
3048 else if (is_known(h_language, known_russian_quotes_languages))
3049 h_quotes_style = "russian";
3051 else if (is_known(h_language, known_swedish_quotes_languages))
3052 h_quotes_style = "swedish";
3054 else if (is_known(h_language, known_swiss_quotes_languages))
3055 h_quotes_style = "swiss";
3057 else if (is_known(h_language, known_english_quotes_languages))
3058 h_quotes_style = "english";
3062 string Preamble::parseEncoding(Parser & p, string const & forceclass)
3064 TeX2LyXDocClass dummy;
3065 parse(p, forceclass, true, dummy);
3066 if (h_inputencoding != "auto-legacy" && h_inputencoding != "auto-legacy-plain")
3067 return h_inputencoding;
3072 string babel2lyx(string const & language)
3074 char const * const * where = is_known(language, known_languages);
3076 return known_coded_languages[where - known_languages];
3081 string lyx2babel(string const & language)
3083 char const * const * where = is_known(language, known_coded_languages);
3085 return known_languages[where - known_coded_languages];
3090 string Preamble::polyglossia2lyx(string const & language)
3092 char const * const * where = is_known(language, polyglossia_languages);
3094 return coded_polyglossia_languages[where - polyglossia_languages];
3099 string rgbcolor2code(string const & name)
3101 char const * const * where = is_known(name, known_basic_colors);
3103 // "red", "green" etc
3104 return known_basic_color_codes[where - known_basic_colors];
3106 // "255,0,0", "0,255,0" etc
3107 RGBColor c(RGBColorFromLaTeX(name));
3108 return X11hexname(c);