3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
9 * Full author contact details are available in file CREDITS.
20 #include "LayoutFile.h"
23 #include "TextClass.h"
26 #include "support/convert.h"
27 #include "support/FileName.h"
28 #include "support/filetools.h"
29 #include "support/lstrings.h"
31 #include "support/regex.h"
37 using namespace lyx::support;
46 // CJK languages are handled in text.cpp, polyglossia languages are listed
49 * known babel language names (including synonyms)
50 * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
51 * please keep this in sync with known_coded_languages line by line!
53 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
54 "american", "arabic", "arabtex", "australian", "austrian", "azerbaijani", "bahasa", "bahasai",
55 "bahasam", "basque", "belarusian", "bosnian", "brazil", "brazilian", "breton", "british",
56 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
57 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "francais",
58 "french", "frenchb", "frenchle", "frenchpro", "friulan", "galician", "german", "germanb",
59 "georgian", "greek", "hebrew", "hungarian", "icelandic", "indon", "indonesian",
60 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
61 "latvian", "lithuanian", "lowersorbian", "lsorbian", "macedonian", "magyar", "malay", "meyalu",
62 "mongolian", "naustrian", "newzealand", "ngerman", "ngermanb", "norsk", "nswissgerman",
63 "nynorsk", "piedmontese", "polutonikogreek", "polish", "portuges", "portuguese",
64 "romanian", "romansh", "russian", "russianb", "samin", "scottish", "serbian", "serbian-latin",
65 "slovak", "slovene", "spanish", "swedish", "swissgerman", "thai", "turkish", "turkmen",
66 "ukraineb", "ukrainian", "uppersorbian", "UKenglish", "USenglish", "usorbian",
71 * the same as known_languages with .lyx names
72 * please keep this in sync with known_languages line by line!
74 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
75 "american", "arabic_arabi", "arabic_arabtex", "australian", "austrian", "azerbaijani", "bahasa", "bahasa",
76 "bahasam", "basque", "belarusian", "bosnian", "brazilian", "brazilian", "breton", "british",
77 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
78 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "french",
79 "french", "french", "french", "french", "friulan", "galician", "german", "german",
80 "georgian", "greek", "hebrew", "magyar", "icelandic", "bahasa", "bahasa",
81 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
82 "latvian", "lithuanian", "lowersorbian", "lowersorbian", "macedonian", "magyar", "bahasam", "bahasam",
83 "mongolian", "naustrian", "newzealand", "ngerman", "ngerman", "norsk", "german-ch",
84 "nynorsk", "piedmontese", "polutonikogreek", "polish", "portuguese", "portuguese",
85 "romanian", "romansh", "russian", "russian", "samin", "scottish", "serbian", "serbian-latin",
86 "slovak", "slovene", "spanish", "swedish", "german-ch-old", "thai", "turkish", "turkmen",
87 "ukrainian", "ukrainian", "uppersorbian", "english", "english", "uppersorbian",
88 "vietnamese", "welsh",
91 /// languages with british quotes (.lyx names)
92 const char * const known_british_quotes_languages[] = {"british", "welsh", 0};
94 /// languages with cjk quotes (.lyx names)
95 const char * const known_cjk_quotes_languages[] = {"chinese-traditional",
96 "japanese", "japanese-cjk", 0};
98 /// languages with cjk-angle quotes (.lyx names)
99 const char * const known_cjkangle_quotes_languages[] = {"korean", 0};
101 /// languages with danish quotes (.lyx names)
102 const char * const known_danish_quotes_languages[] = {"danish", 0};
104 /// languages with english quotes (.lyx names)
105 const char * const known_english_quotes_languages[] = {"american", "australian",
106 "bahasa", "bahasam", "bengali", "brazilian", "canadian", "chinese-simplified", "english",
107 "esperanto", "farsi", "interlingua", "irish", "newzealand", "scottish",
108 "thai", "turkish", "vietnamese", 0};
110 /// languages with french quotes (.lyx names)
111 const char * const known_french_quotes_languages[] = {"ancientgreek",
112 "arabic_arabi", "arabic_arabtex", "asturian", "belarusian", "breton",
113 "canadien", "catalan", "french", "friulan", "galician", "italian", "occitan",
114 "piedmontese", "portuguese", "spanish", "spanish-mexico", 0};
116 /// languages with german quotes (.lyx names)
117 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
118 "czech", "estonian", "georgian", "german", "icelandic", "latvian", "lithuanian",
119 "lowersorbian", "macedonian", "naustrian", "ngerman", "romansh", "slovak", "slovene",
122 /// languages with polish quotes (.lyx names)
123 const char * const known_polish_quotes_languages[] = {"afrikaans", "bosnian", "croatian",
124 "dutch", "magyar", "polish", "romanian", "serbian", "serbian-latin", 0};
126 /// languages with russian quotes (.lyx names)
127 const char * const known_russian_quotes_languages[] = {"azerbaijani", "oldrussian",
128 "russian", "ukrainian", 0};
130 /// languages with swedish quotes (.lyx names)
131 const char * const known_swedish_quotes_languages[] = {"finnish", "swedish", 0};
133 /// languages with swiss quotes (.lyx names)
134 const char * const known_swiss_quotes_languages[] = {"albanian",
135 "armenian", "basque", "churchslavonic", "german-ch", "german-ch-old",
136 "norsk", "nynorsk", "turkmen", "ukrainian", "vietnamese", 0};
138 /// known language packages from the times before babel
139 const char * const known_old_language_packages[] = {"french", "frenchle",
140 "frenchpro", "german", "ngerman", "pmfrench", 0};
142 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
144 const char * const known_roman_font_packages[] = { "ae", "beraserif", "bookman",
145 "ccfonts", "chancery", "charter", "cmr", "cochineal", "crimson", "DejaVuSerif", "DejaVuSerifCondensed", "fourier",
146 "garamondx", "libertine", "libertineRoman", "libertine-type1", "lmodern", "mathdesign", "mathpazo",
147 "mathptmx", "MinionPro", "newcent", "noto", "noto-serif", "PTSerif", "tgbonum", "tgchorus",
148 "tgpagella", "tgschola", "tgtermes", "utopia", "xcharter", 0 };
150 const char * const known_sans_font_packages[] = { "avant", "berasans", "biolinum",
151 "biolinum-type1", "cmbr", "cmss", "DejaVuSans", "DejaVuSansCondensed", "helvet", "iwona", "iwonac", "iwonal", "iwonalc",
152 "kurier", "kurierc", "kurierl", "kurierlc", "lmss", "noto", "noto-sans", "PTSans",
153 "tgadventor", "tgheros", "uop", 0 };
155 const char * const known_typewriter_font_packages[] = { "beramono", "cmtl", "cmtt",
156 "courier", "DejaVuSansMono", "lmtt", "luximono", "fourier", "libertineMono", "libertineMono-type1", "lmodern",
157 "mathpazo", "mathptmx", "newcent", "noto", "noto-mono", "PTMono", "tgcursor", "txtt", 0 };
159 const char * const known_math_font_packages[] = { "eulervm", "newtxmath", 0};
161 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
162 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
163 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
164 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
165 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
167 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
168 "executivepaper", "legalpaper", "letterpaper", 0};
170 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
171 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
173 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
174 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
177 /// commands that can start an \if...\else...\endif sequence
178 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
179 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
180 "ifsidecap", "ifupgreek", 0};
182 const char * const known_basic_colors[] = {"black", "blue", "brown", "cyan",
183 "darkgray", "gray", "green", "lightgray", "lime", "magenta", "orange", "olive",
184 "pink", "purple", "red", "teal", "violet", "white", "yellow", 0};
186 const char * const known_basic_color_codes[] = {"#000000", "#0000ff", "#964B00", "#00ffff",
187 "#a9a9a9", "#808080", "#00ff00", "#d3d3d3", "#bfff00", "#ff00ff", "#ff7f00", "#808000",
188 "#ffc0cb", "#800080", "#ff0000", "#008080", "#8f00ff", "#ffffff", "#ffff00", 0};
190 /// conditional commands with three arguments like \@ifundefined{}{}{}
191 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
195 * Known file extensions for TeX files as used by \\includeonly
197 char const * const known_tex_extensions[] = {"tex", 0};
199 /// packages that work only in xetex
200 /// polyglossia is handled separately
201 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
202 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
203 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
205 /// packages that are automatically skipped if loaded by LyX
206 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
207 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color",
208 "float", "fontspec", "framed", "graphicx", "hhline", "ifthen", "longtable",
209 "makeidx", "minted", "multirow", "nomencl", "pdfpages", "prettyref", "refstyle",
210 "rotating", "rotfloat", "splitidx", "setspace", "subscript", "tabularx","textcomp", "tipa",
211 "tipx", "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xcolor", "xltabular",
214 // codes used to remove packages that are loaded automatically by LyX.
215 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
216 const char package_beg_sep = '\001';
217 const char package_mid_sep = '\002';
218 const char package_end_sep = '\003';
221 // returns true if at least one of the options in what has been found
222 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
228 // the last language option is the document language (for babel and LyX)
229 // the last size option is the document font size
230 vector<string>::iterator it;
231 vector<string>::iterator position = opts.begin();
232 for (; *what; ++what) {
233 it = find(opts.begin(), opts.end(), *what);
234 if (it != opts.end()) {
235 if (it >= position) {
246 void delete_opt(vector<string> & opts, char const * const * what)
251 // remove found options from the list
252 // do this after handle_opt to avoid potential memory leaks
253 vector<string>::iterator it;
254 for (; *what; ++what) {
255 it = find(opts.begin(), opts.end(), *what);
256 if (it != opts.end())
263 * Split a package options string (keyval format) into a vector.
265 * authorformat=smallcaps,
267 * titleformat=colonsep,
268 * bibformat={tabular,ibidem,numbered}
270 vector<string> split_options(string const & input)
272 vector<string> options;
276 Token const & t = p.get_token();
277 if (t.asInput() == ",") {
278 options.push_back(trimSpaceAndEol(option));
280 } else if (t.asInput() == "=") {
283 if (p.next_token().asInput() == "{")
284 option += '{' + p.getArg('{', '}') + '}';
285 } else if (t.cat() != catSpace && t.cat() != catComment)
286 option += t.asInput();
290 options.push_back(trimSpaceAndEol(option));
297 * Retrieve a keyval option "name={value with=sign}" named \p name from
298 * \p options and return the value.
299 * The found option is also removed from \p options.
301 string process_keyval_opt(vector<string> & options, string name)
303 for (size_t i = 0; i < options.size(); ++i) {
304 vector<string> option;
305 split(options[i], option, '=');
306 if (option.size() < 2)
308 if (option[0] == name) {
309 options.erase(options.begin() + i);
310 option.erase(option.begin());
311 return join(option, "=");
317 } // anonymous namespace
321 * known polyglossia language names (including variants)
322 * FIXME: support spelling=old for german variants (german vs. ngerman LyX names etc)
324 const char * const Preamble::polyglossia_languages[] = {
325 "albanian", "american", "amharic", "ancient", "arabic", "armenian", "asturian", "australian",
326 "bahasai", "bahasam", "basque", "bengali", "brazil", "brazilian", "breton", "british", "bulgarian",
327 "catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
328 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
329 "galician", "greek", "monotonic", "hebrew", "hindi",
330 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean",
331 "lao", "latin", "latvian", "lithuanian", "lsorbian", "magyar", "malayalam", "marathi",
332 "austrian", "newzealand", "german", "norsk", "nynorsk", "occitan", "oldrussian",
333 "piedmontese", "polish", "polytonic", "portuges", "romanian", "romansh", "russian",
334 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovenian", "spanish", "swedish", "syriac",
335 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
336 "ukrainian", "urdu", "usorbian", "vietnamese", "welsh", 0};
337 // not yet supported by LyX: "korean", "nko"
340 * the same as polyglossia_languages with .lyx names
341 * please keep this in sync with polyglossia_languages line by line!
343 const char * const Preamble::coded_polyglossia_languages[] = {
344 "albanian", "american", "amharic", "ancientgreek", "arabic_arabi", "armenian", "asturian", "australian",
345 "bahasa", "bahasam", "basque", "bengali", "brazilian", "brazilian", "breton", "british", "bulgarian",
346 "catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
347 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
348 "galician", "greek", "greek", "hebrew", "hindi",
349 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean",
350 "lao", "latin", "latvian", "lithuanian", "lowersorbian", "magyar", "malayalam", "marathi",
351 "naustrian","newzealand", "ngerman", "norsk", "nynorsk", "occitan", "oldrussian",
352 "piedmontese", "polish", "polutonikogreek", "portuges", "romanian", "romansh", "russian",
353 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovene", "spanish", "swedish", "syriac",
354 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
355 "ukrainian", "urdu", "uppersorbian", "vietnamese", "welsh", 0};
356 // not yet supported by LyX: "korean-polyglossia", "nko"
359 bool Preamble::usePolyglossia() const
361 return h_use_non_tex_fonts && h_language_package == "default";
365 bool Preamble::indentParagraphs() const
367 return h_paragraph_separation == "indent";
371 bool Preamble::isPackageUsed(string const & package) const
373 return used_packages.find(package) != used_packages.end();
377 bool Preamble::isPackageAutoLoaded(string const & package) const
379 return auto_packages.find(package) != auto_packages.end();
383 vector<string> Preamble::getPackageOptions(string const & package) const
385 map<string, vector<string> >::const_iterator it = used_packages.find(package);
386 if (it != used_packages.end())
388 return vector<string>();
392 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
394 auto_packages.insert(package);
398 void Preamble::addModule(string const & module)
400 for (auto const & m : used_modules) {
404 used_modules.push_back(module);
408 void Preamble::suppressDate(bool suppress)
411 h_suppress_date = "true";
413 h_suppress_date = "false";
417 void Preamble::registerAuthor(std::string const & name)
419 Author author(from_utf8(name), empty_docstring());
420 author.setUsed(true);
421 authors_.record(author);
422 h_tracking_changes = "true";
423 h_output_changes = "true";
427 Author const & Preamble::getAuthor(std::string const & name) const
429 Author author(from_utf8(name), empty_docstring());
430 for (AuthorList::Authors::const_iterator it = authors_.begin();
431 it != authors_.end(); ++it)
434 static Author const dummy;
439 int Preamble::getSpecialTableColumnArguments(char c) const
441 map<char, int>::const_iterator it = special_columns_.find(c);
442 if (it == special_columns_.end())
448 void Preamble::add_package(string const & name, vector<string> & options)
450 // every package inherits the global options
451 if (used_packages.find(name) == used_packages.end())
452 used_packages[name] = split_options(h_options);
454 // Insert options passed via PassOptionsToPackage
455 for (auto const & p : extra_package_options_) {
456 if (p.first == name) {
457 vector<string> eo = getVectorFromString(p.second);
458 for (auto const & eoi : eo)
459 options.push_back(eoi);
463 vector<string> & v = used_packages[name];
464 v.insert(v.end(), options.begin(), options.end());
465 if (name == "jurabib") {
466 // Don't output the order argument (see the cite command
467 // handling code in text.cpp).
468 vector<string>::iterator end =
469 remove(options.begin(), options.end(), "natbiborder");
470 end = remove(options.begin(), end, "jurabiborder");
471 options.erase(end, options.end());
478 // Given is a string like "scaled=0.9" or "Scale=0.9", return 0.9 * 100
479 bool scale_as_percentage(string const & scale, string & percentage)
481 string::size_type pos = scale.find('=');
482 if (pos != string::npos) {
483 string value = scale.substr(pos + 1);
484 if (isStrDbl(value)) {
485 percentage = convert<string>(
486 static_cast<int>(100 * convert<double>(value)));
494 string remove_braces(string const & value)
498 if (value[0] == '{' && value[value.length()-1] == '}')
499 return value.substr(1, value.length()-2);
503 } // anonymous namespace
506 Preamble::Preamble() : one_language(true), explicit_babel(false),
507 title_layout_found(false), index_number(0), h_font_cjk_set(false)
511 h_biblio_style = "plain";
512 h_bibtex_command = "default";
513 h_cite_engine = "basic";
514 h_cite_engine_type = "default";
516 h_defskip = "medskip";
517 h_dynamic_quotes = false;
520 h_fontencoding = "default";
521 h_font_roman[0] = "default";
522 h_font_roman[1] = "default";
523 h_font_sans[0] = "default";
524 h_font_sans[1] = "default";
525 h_font_typewriter[0] = "default";
526 h_font_typewriter[1] = "default";
527 h_font_math[0] = "auto";
528 h_font_math[1] = "auto";
529 h_font_default_family = "default";
530 h_use_non_tex_fonts = false;
532 h_font_roman_osf = "false";
533 h_font_sans_osf = "false";
534 h_font_typewriter_osf = "false";
535 h_font_sf_scale[0] = "100";
536 h_font_sf_scale[1] = "100";
537 h_font_tt_scale[0] = "100";
538 h_font_tt_scale[1] = "100";
539 // h_font_roman_opts;
541 // h_font_typewriter_opts;
543 h_is_mathindent = "0";
544 h_math_numbering_side = "default";
545 h_graphics = "default";
546 h_default_output_format = "default";
547 h_html_be_strict = "false";
548 h_html_css_as_file = "0";
549 h_html_math_output = "0";
550 h_index[0] = "Index";
551 h_index_command = "default";
552 h_inputencoding = "auto-legacy";
553 h_justification = "true";
554 h_language = "english";
555 h_language_package = "none";
557 h_maintain_unincluded_children = "false";
561 h_output_changes = "false";
563 //h_output_sync_macro
564 h_papercolumns = "1";
565 h_paperfontsize = "default";
566 h_paperorientation = "portrait";
567 h_paperpagestyle = "default";
569 h_papersize = "default";
570 h_paragraph_indentation = "default";
571 h_paragraph_separation = "indent";
576 h_pdf_bookmarks = "0";
577 h_pdf_bookmarksnumbered = "0";
578 h_pdf_bookmarksopen = "0";
579 h_pdf_bookmarksopenlevel = "1";
580 h_pdf_breaklinks = "0";
581 h_pdf_pdfborder = "0";
582 h_pdf_colorlinks = "0";
583 h_pdf_backref = "section";
584 h_pdf_pdfusetitle = "0";
586 //h_pdf_quoted_options;
587 h_quotes_style = "english";
589 h_shortcut[0] = "idx";
590 h_spacing = "single";
591 h_save_transient_properties = "true";
592 h_suppress_date = "false";
593 h_textclass = "article";
595 h_tracking_changes = "false";
596 h_use_bibtopic = "false";
597 h_use_dash_ligatures = "true";
598 h_use_indices = "false";
599 h_use_geometry = "false";
600 h_use_default_options = "false";
601 h_use_hyperref = "false";
602 h_use_microtype = "false";
603 h_use_lineno = "false";
604 h_use_refstyle = false;
605 h_use_minted = false;
606 h_use_packages["amsmath"] = "1";
607 h_use_packages["amssymb"] = "0";
608 h_use_packages["cancel"] = "0";
609 h_use_packages["esint"] = "1";
610 h_use_packages["mhchem"] = "0";
611 h_use_packages["mathdots"] = "0";
612 h_use_packages["mathtools"] = "0";
613 h_use_packages["stackrel"] = "0";
614 h_use_packages["stmaryrd"] = "0";
615 h_use_packages["undertilde"] = "0";
619 void Preamble::handle_hyperref(vector<string> & options)
621 // FIXME swallow inputencoding changes that might surround the
622 // hyperref setup if it was written by LyX
623 h_use_hyperref = "true";
624 // swallow "unicode=true", since LyX does always write that
625 vector<string>::iterator it =
626 find(options.begin(), options.end(), "unicode=true");
627 if (it != options.end())
629 it = find(options.begin(), options.end(), "pdfusetitle");
630 if (it != options.end()) {
631 h_pdf_pdfusetitle = "1";
634 string bookmarks = process_keyval_opt(options, "bookmarks");
635 if (bookmarks == "true")
636 h_pdf_bookmarks = "1";
637 else if (bookmarks == "false")
638 h_pdf_bookmarks = "0";
639 if (h_pdf_bookmarks == "1") {
640 string bookmarksnumbered =
641 process_keyval_opt(options, "bookmarksnumbered");
642 if (bookmarksnumbered == "true")
643 h_pdf_bookmarksnumbered = "1";
644 else if (bookmarksnumbered == "false")
645 h_pdf_bookmarksnumbered = "0";
646 string bookmarksopen =
647 process_keyval_opt(options, "bookmarksopen");
648 if (bookmarksopen == "true")
649 h_pdf_bookmarksopen = "1";
650 else if (bookmarksopen == "false")
651 h_pdf_bookmarksopen = "0";
652 if (h_pdf_bookmarksopen == "1") {
653 string bookmarksopenlevel =
654 process_keyval_opt(options, "bookmarksopenlevel");
655 if (!bookmarksopenlevel.empty())
656 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
659 string breaklinks = process_keyval_opt(options, "breaklinks");
660 if (breaklinks == "true")
661 h_pdf_breaklinks = "1";
662 else if (breaklinks == "false")
663 h_pdf_breaklinks = "0";
664 string pdfborder = process_keyval_opt(options, "pdfborder");
665 if (pdfborder == "{0 0 0}")
666 h_pdf_pdfborder = "1";
667 else if (pdfborder == "{0 0 1}")
668 h_pdf_pdfborder = "0";
669 string backref = process_keyval_opt(options, "backref");
670 if (!backref.empty())
671 h_pdf_backref = backref;
672 string colorlinks = process_keyval_opt(options, "colorlinks");
673 if (colorlinks == "true")
674 h_pdf_colorlinks = "1";
675 else if (colorlinks == "false")
676 h_pdf_colorlinks = "0";
677 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
678 if (!pdfpagemode.empty())
679 h_pdf_pagemode = pdfpagemode;
680 string pdftitle = process_keyval_opt(options, "pdftitle");
681 if (!pdftitle.empty()) {
682 h_pdf_title = remove_braces(pdftitle);
684 string pdfauthor = process_keyval_opt(options, "pdfauthor");
685 if (!pdfauthor.empty()) {
686 h_pdf_author = remove_braces(pdfauthor);
688 string pdfsubject = process_keyval_opt(options, "pdfsubject");
689 if (!pdfsubject.empty())
690 h_pdf_subject = remove_braces(pdfsubject);
691 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
692 if (!pdfkeywords.empty())
693 h_pdf_keywords = remove_braces(pdfkeywords);
694 if (!options.empty()) {
695 if (!h_pdf_quoted_options.empty())
696 h_pdf_quoted_options += ',';
697 h_pdf_quoted_options += join(options, ",");
703 void Preamble::handle_geometry(vector<string> & options)
705 h_use_geometry = "true";
706 vector<string>::iterator it;
708 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
709 h_paperorientation = "landscape";
713 // keyval version: "paper=letter"
714 string paper = process_keyval_opt(options, "paper");
716 h_papersize = paper + "paper";
717 // alternative version: "letterpaper"
718 handle_opt(options, known_paper_sizes, h_papersize);
719 delete_opt(options, known_paper_sizes);
721 char const * const * margin = known_paper_margins;
722 for (; *margin; ++margin) {
723 string value = process_keyval_opt(options, *margin);
724 if (!value.empty()) {
725 int k = margin - known_paper_margins;
726 string name = known_coded_paper_margins[k];
727 h_margins += '\\' + name + ' ' + value + '\n';
733 void Preamble::handle_package(Parser &p, string const & name,
734 string const & opts, bool in_lyx_preamble,
737 vector<string> options = split_options(opts);
738 add_package(name, options);
740 if (is_known(name, known_xetex_packages)) {
742 h_use_non_tex_fonts = true;
743 registerAutomaticallyLoadedPackage("fontspec");
744 if (h_inputencoding == "auto-legacy")
745 p.setEncoding("UTF-8");
749 if (is_known(name, known_roman_font_packages))
750 h_font_roman[0] = name;
752 if (name == "fourier") {
753 h_font_roman[0] = "utopia";
754 // when font uses real small capitals
755 if (opts == "expert")
759 if (name == "garamondx") {
760 h_font_roman[0] = "garamondx";
762 h_font_roman_osf = "true";
765 if (name == "libertine") {
766 h_font_roman[0] = "libertine";
767 // this automatically invokes biolinum
768 h_font_sans[0] = "biolinum";
769 // as well as libertineMono
770 h_font_typewriter[0] = "libertine-mono";
772 h_font_roman_osf = "true";
773 else if (opts == "lining")
774 h_font_roman_osf = "false";
777 if (name == "libertineRoman" || name == "libertine-type1") {
778 h_font_roman[0] = "libertine";
779 // NOTE: contrary to libertine.sty, libertineRoman
780 // and libertine-type1 do not automatically invoke
781 // biolinum and libertineMono
782 if (opts == "lining")
783 h_font_roman_osf = "false";
784 else if (opts == "osf")
785 h_font_roman_osf = "true";
788 if (name == "MinionPro") {
789 h_font_roman[0] = "minionpro";
790 vector<string> allopts = getVectorFromString(opts);
792 h_font_roman_osf = "true";
793 h_font_math[0] = "auto";
794 for (auto const & opt : allopts) {
796 h_font_roman_osf = "false";
799 if (opt == "onlytext") {
800 h_font_math[0] = "default";
808 h_font_roman_opts = xopts;
812 if (name == "mathdesign") {
813 if (opts.find("charter") != string::npos)
814 h_font_roman[0] = "md-charter";
815 if (opts.find("garamond") != string::npos)
816 h_font_roman[0] = "md-garamond";
817 if (opts.find("utopia") != string::npos)
818 h_font_roman[0] = "md-utopia";
819 if (opts.find("expert") != string::npos) {
821 h_font_roman_osf = "true";
825 else if (name == "mathpazo")
826 h_font_roman[0] = "palatino";
828 else if (name == "mathptmx")
829 h_font_roman[0] = "times";
831 if (name == "crimson")
832 h_font_roman[0] = "cochineal";
834 if (name == "cochineal") {
835 h_font_roman[0] = "cochineal";
836 // cochineal can have several options, e.g. [proportional,osf]
837 string::size_type pos = opts.find("osf");
838 if (pos != string::npos)
839 h_font_roman_osf = "true";
842 if (name == "noto") {
843 // noto can have several options
845 h_font_roman[0] = "NotoSerif-TLF";
846 string::size_type pos = opts.find("rm");
847 if (pos != string::npos)
848 h_font_roman[0] = "NotoSerif-TLF";
849 pos = opts.find("sf");
850 if (pos != string::npos)
851 h_font_sans[0] = "NotoSans-TLF";
852 pos = opts.find("nott");
853 if (pos != string::npos) {
854 h_font_roman[0] = "NotoSerif-TLF";
855 h_font_sans[0] = "NotoSans-TLF";
857 // noto as typewriter is handled in handling of \ttdefault
858 // special cases are handled in handling of \rmdefault and \sfdefault
859 vector<string> allopts = getVectorFromString(opts);
861 for (auto const & opt : allopts) {
869 h_font_roman_osf = "true";
878 if (name == "paratype") {
879 // in this case all fonts are ParaType
880 h_font_roman[0] = "PTSerif-TLF";
881 h_font_sans[0] = "default";
882 h_font_typewriter[0] = "default";
885 if (name == "PTSerif")
886 h_font_roman[0] = "PTSerif-TLF";
888 if (name == "XCharter") {
889 h_font_roman[0] = "xcharter";
891 h_font_roman_osf = "true";
894 if (name == "plex-serif") {
896 h_font_roman[0] = "IBMPlexSerif";
897 else if (opts.find("thin") != string::npos)
898 h_font_roman[0] = "IBMPlexSerifThin";
899 else if (opts.find("extralight") != string::npos)
900 h_font_roman[0] = "IBMPlexSerifExtraLight";
901 else if (opts.find("light") != string::npos)
902 h_font_roman[0] = "IBMPlexSerifLight";
903 else if (opts.find("semibold") != string::npos)
904 h_font_roman[0] = "IBMPlexSerifSemibold";
905 vector<string> allopts = getVectorFromString(opts);
907 for (auto const & opt : allopts) {
910 if (opt == "extralight")
914 if (opt == "semibold")
921 h_font_roman_opts = xopts;
924 if (name == "noto-serif") {
925 h_font_roman[0] = "NotoSerifRegular";
927 if (opts.find("thin") != string::npos)
928 h_font_roman[0] = "NotoSerifThin";
929 else if (opts.find("medium") != string::npos)
930 h_font_roman[0] = "NotoSerifMedium";
931 else if (opts.find("extralight") != string::npos)
932 h_font_roman[0] = "NotoSerifExtralight";
933 else if (opts.find("light") != string::npos)
934 h_font_roman[0] = "NotoSerifLight";
936 vector<string> allopts = getVectorFromString(opts);
938 for (auto const & opt : allopts) {
939 if (opt == "regular")
943 if (opt == "extralight")
947 if (opt == "semibold")
954 h_font_roman_opts = xopts;
958 if (name == "sourceserifpro") {
959 h_font_roman[0] = "ADOBESourceSerifPro";
960 vector<string> allopts = getVectorFromString(opts);
962 for (auto const & opt : allopts) {
964 h_font_roman_osf = "true";
972 h_font_roman_opts = xopts;
977 if (is_known(name, known_sans_font_packages)) {
978 h_font_sans[0] = name;
979 if (options.size() >= 1) {
980 if (scale_as_percentage(opts, h_font_sf_scale[0]))
985 if (name == "biolinum" || name == "biolinum-type1") {
986 h_font_sans[0] = "biolinum";
987 // biolinum can have several options, e.g. [osf,scaled=0.97]
988 string::size_type pos = opts.find("osf");
989 if (pos != string::npos)
990 h_font_sans_osf = "true";
993 if (name == "PTSans") {
994 h_font_sans[0] = "PTSans-TLF";
995 if (options.size() >= 1) {
996 if (scale_as_percentage(opts, h_font_sf_scale[0]))
1001 if (name == "plex-sans") {
1002 if (opts.find("condensed") != string::npos)
1003 h_font_sans[0] = "IBMPlexSansCondensed";
1004 else if (opts.find("thin") != string::npos)
1005 h_font_sans[0] = "IBMPlexSansThin";
1006 else if (opts.find("extralight") != string::npos)
1007 h_font_sans[0] = "IBMPlexSansExtraLight";
1008 else if (opts.find("light") != string::npos)
1009 h_font_sans[0] = "IBMPlexSansLight";
1010 else if (opts.find("semibold") != string::npos)
1011 h_font_sans[0] = "IBMPlexSansSemibold";
1013 h_font_sans[0] = "IBMPlexSans";
1014 vector<string> allopts = getVectorFromString(opts);
1016 for (auto const & opt : allopts) {
1019 if (opt == "extralight")
1023 if (opt == "semibold")
1025 if (prefixIs(opt, "scale=")) {
1026 scale_as_percentage(opt, h_font_sf_scale[0]);
1034 h_font_sans_opts = xopts;
1037 if (name == "noto-sans") {
1038 h_font_sans[0] = "NotoSansRegular";
1039 if (!opts.empty()) {
1040 if (opts.find("medium") != string::npos)
1041 h_font_sans[0] = "NotoSansMedium";
1042 else if (opts.find("thin") != string::npos)
1043 h_font_sans[0] = "NotoSansThin";
1044 else if (opts.find("extralight") != string::npos)
1045 h_font_sans[0] = "NotoSansExtralight";
1046 else if (opts.find("light") != string::npos)
1047 h_font_sans[0] = "NotoSansLight";
1049 vector<string> allopts = getVectorFromString(opts);
1051 for (auto const & opt : allopts) {
1052 if (opt == "regular")
1056 if (opt == "extralight")
1060 if (opt == "semibold")
1063 h_font_sans_osf = "true";
1071 h_font_sans_opts = xopts;
1075 if (name == "sourcesanspro") {
1076 h_font_sans[0] = "ADOBESourceSansPro";
1077 vector<string> allopts = getVectorFromString(opts);
1079 for (auto const & opt : allopts) {
1080 if (prefixIs(opt, "scaled=")) {
1081 scale_as_percentage(opt, h_font_sf_scale[0]);
1085 h_font_sans_osf = "true";
1093 h_font_sans_opts = xopts;
1098 if (is_known(name, known_typewriter_font_packages)) {
1099 // fourier can be set as roman font _only_
1100 // fourier as typewriter is handled in handling of \ttdefault
1101 if (name != "fourier") {
1102 h_font_typewriter[0] = name;
1103 if (options.size() >= 1) {
1104 if (scale_as_percentage(opts, h_font_tt_scale[0]))
1110 if (name == "libertineMono" || name == "libertineMono-type1")
1111 h_font_typewriter[0] = "libertine-mono";
1113 if (name == "PTMono") {
1114 h_font_typewriter[0] = "PTMono-TLF";
1115 if (options.size() >= 1) {
1116 if (scale_as_percentage(opts, h_font_tt_scale[0]))
1121 if (name == "plex-mono") {
1122 if (opts.find("thin") != string::npos)
1123 h_font_typewriter[0] = "IBMPlexMonoThin";
1124 else if (opts.find("extralight") != string::npos)
1125 h_font_typewriter[0] = "IBMPlexMonoExtraLight";
1126 else if (opts.find("light") != string::npos)
1127 h_font_typewriter[0] = "IBMPlexMonoLight";
1128 else if (opts.find("semibold") != string::npos)
1129 h_font_typewriter[0] = "IBMPlexMonoSemibold";
1131 h_font_typewriter[0] = "IBMPlexMono";
1132 vector<string> allopts = getVectorFromString(opts);
1134 for (auto const & opt : allopts) {
1137 if (opt == "extralight")
1141 if (opt == "semibold")
1143 if (prefixIs(opt, "scale=")) {
1144 scale_as_percentage(opt, h_font_tt_scale[0]);
1152 h_font_typewriter_opts = xopts;
1156 if (name == "noto-mono") {
1157 h_font_typewriter[0] = "NotoMonoRegular";
1158 vector<string> allopts = getVectorFromString(opts);
1160 for (auto const & opt : allopts) {
1161 if (opt == "regular")
1168 h_font_typewriter_opts = xopts;
1172 if (name == "sourcecodepro") {
1173 h_font_typewriter[0] = "ADOBESourceCodePro";
1174 vector<string> allopts = getVectorFromString(opts);
1176 for (auto const & opt : allopts) {
1177 if (prefixIs(opt, "scaled=")) {
1178 scale_as_percentage(opt, h_font_tt_scale[0]);
1182 h_font_typewriter_osf = "true";
1190 h_font_typewriter_opts = xopts;
1194 // font uses old-style figure
1196 h_font_roman_osf = "true";
1199 if (is_known(name, known_math_font_packages))
1200 h_font_math[0] = name;
1202 if (name == "newtxmath") {
1204 h_font_math[0] = "newtxmath";
1205 else if (opts == "garamondx")
1206 h_font_math[0] = "garamondx-ntxm";
1207 else if (opts == "libertine")
1208 h_font_math[0] = "libertine-ntxm";
1209 else if (opts == "minion")
1210 h_font_math[0] = "minion-ntxm";
1211 else if (opts == "cochineal")
1212 h_font_math[0] = "cochineal-ntxm";
1215 if (name == "iwona")
1217 h_font_math[0] = "iwona-math";
1219 if (name == "kurier")
1221 h_font_math[0] = "kurier-math";
1223 // after the detection and handling of special cases, we can remove the
1224 // fonts, otherwise they would appear in the preamble, see bug #7856
1225 if (is_known(name, known_roman_font_packages) || is_known(name, known_sans_font_packages)
1226 || is_known(name, known_typewriter_font_packages) || is_known(name, known_math_font_packages))
1228 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
1229 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
1230 name == "esint" || name == "mhchem" || name == "mathdots" ||
1231 name == "mathtools" || name == "stackrel" ||
1232 name == "stmaryrd" || name == "undertilde") {
1233 h_use_packages[name] = "2";
1234 registerAutomaticallyLoadedPackage(name);
1237 else if (name == "babel") {
1238 h_language_package = "default";
1239 // One might think we would have to do nothing if babel is loaded
1240 // without any options to prevent pollution of the preamble with this
1241 // babel call in every roundtrip.
1242 // But the user could have defined babel-specific things afterwards. So
1243 // we need to keep it in the preamble to prevent cases like bug #7861.
1244 if (!opts.empty()) {
1245 // check if more than one option was used - used later for inputenc
1246 if (options.begin() != options.end() - 1)
1247 one_language = false;
1248 // babel takes the last language of the option of its \usepackage
1249 // call as document language. If there is no such language option, the
1250 // last language in the documentclass options is used.
1251 handle_opt(options, known_languages, h_language);
1252 // translate the babel name to a LyX name
1253 h_language = babel2lyx(h_language);
1254 if (h_language == "japanese") {
1255 // For Japanese, the encoding isn't indicated in the source
1256 // file, and there's really not much we can do. We could
1257 // 1) offer a list of possible encodings to choose from, or
1258 // 2) determine the encoding of the file by inspecting it.
1259 // For the time being, we leave the encoding alone so that
1260 // we don't get iconv errors when making a wrong guess, and
1261 // we will output a note at the top of the document
1262 // explaining what to do.
1263 Encoding const * const enc = encodings.fromIconvName(
1264 p.getEncoding(), Encoding::japanese, false);
1266 h_inputencoding = enc->name();
1267 is_nonCJKJapanese = true;
1268 // in this case babel can be removed from the preamble
1269 registerAutomaticallyLoadedPackage("babel");
1271 // If babel is called with options, LyX puts them by default into the
1272 // document class options. This works for most languages, except
1273 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
1274 // perhaps in future others.
1275 // Therefore keep the babel call as it is as the user might have
1277 string const babelcall = "\\usepackage[" + opts + "]{babel}\n";
1278 if (!contains(h_preamble.str(), babelcall))
1279 h_preamble << babelcall;
1281 delete_opt(options, known_languages);
1283 if (!contains(h_preamble.str(), "\\usepackage{babel}\n"))
1284 h_preamble << "\\usepackage{babel}\n";
1285 explicit_babel = true;
1289 else if (name == "polyglossia") {
1290 h_language_package = "default";
1291 h_default_output_format = "pdf4";
1292 h_use_non_tex_fonts = true;
1294 registerAutomaticallyLoadedPackage("xunicode");
1295 if (h_inputencoding == "auto-legacy")
1296 p.setEncoding("UTF-8");
1299 else if (name == "CJK") {
1300 // set the encoding to "auto-legacy" because it might be set to "auto-legacy-plain" by the babel handling
1301 // and this would not be correct for CJK
1302 if (h_inputencoding == "auto-legacy-plain")
1303 h_inputencoding = "auto-legacy";
1304 registerAutomaticallyLoadedPackage("CJK");
1307 else if (name == "CJKutf8") {
1308 h_inputencoding = "utf8-cjk";
1309 p.setEncoding("UTF-8");
1310 registerAutomaticallyLoadedPackage("CJKutf8");
1313 else if (name == "fontenc") {
1314 h_fontencoding = getStringFromVector(options, ",");
1318 else if (name == "inputenc" || name == "luainputenc") {
1319 // h_inputencoding is only set when there is not more than one
1320 // inputenc option because otherwise h_inputencoding must be
1321 // set to "auto-legacy" (the default encodings of the document's languages)
1322 // Therefore check that exactly one option is passed to inputenc.
1323 // It is also only set when there is not more than one babel
1325 if (!options.empty()) {
1326 string const encoding = options.back();
1327 Encoding const * const enc = encodings.fromLaTeXName(
1328 encoding, Encoding::inputenc, true);
1330 if (!detectEncoding)
1331 cerr << "Unknown encoding " << encoding
1332 << ". Ignoring." << std::endl;
1334 if (!enc->unsafe() && options.size() == 1 && one_language == true)
1335 h_inputencoding = enc->name();
1336 p.setEncoding(enc->iconvName());
1342 else if (name == "srcltx") {
1343 h_output_sync = "1";
1344 if (!opts.empty()) {
1345 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
1348 h_output_sync_macro = "\\usepackage{srcltx}";
1351 else if (is_known(name, known_old_language_packages)) {
1352 // known language packages from the times before babel
1353 // if they are found and not also babel, they will be used as
1354 // custom language package
1355 h_language_package = "\\usepackage{" + name + "}";
1358 else if (name == "lyxskak") {
1359 // ignore this and its options
1360 const char * const o[] = {"ps", "mover", 0};
1361 delete_opt(options, o);
1364 else if (is_known(name, known_lyx_packages) && options.empty()) {
1365 if (name == "splitidx")
1366 h_use_indices = "true";
1367 else if (name == "minted")
1368 h_use_minted = true;
1369 else if (name == "refstyle")
1370 h_use_refstyle = true;
1371 else if (name == "prettyref")
1372 h_use_refstyle = false;
1373 if (!in_lyx_preamble) {
1374 h_preamble << package_beg_sep << name
1375 << package_mid_sep << "\\usepackage{"
1377 if (p.next_token().cat() == catNewline ||
1378 (p.next_token().cat() == catSpace &&
1379 p.next_next_token().cat() == catNewline))
1381 h_preamble << package_end_sep;
1385 else if (name == "geometry")
1386 handle_geometry(options);
1388 else if (name == "subfig")
1389 ; // ignore this FIXME: Use the package separator mechanism instead
1391 else if (char const * const * where = is_known(name, known_languages))
1392 h_language = known_coded_languages[where - known_languages];
1394 else if (name == "natbib") {
1395 h_biblio_style = "plainnat";
1396 h_cite_engine = "natbib";
1397 h_cite_engine_type = "authoryear";
1398 vector<string>::iterator it =
1399 find(options.begin(), options.end(), "authoryear");
1400 if (it != options.end())
1403 it = find(options.begin(), options.end(), "numbers");
1404 if (it != options.end()) {
1405 h_cite_engine_type = "numerical";
1409 if (!options.empty())
1410 h_biblio_options = join(options, ",");
1413 else if (name == "biblatex") {
1414 h_biblio_style = "plainnat";
1415 h_cite_engine = "biblatex";
1416 h_cite_engine_type = "authoryear";
1418 vector<string>::iterator it =
1419 find(options.begin(), options.end(), "natbib");
1420 if (it != options.end()) {
1422 h_cite_engine = "biblatex-natbib";
1424 opt = process_keyval_opt(options, "natbib");
1426 h_cite_engine = "biblatex-natbib";
1428 opt = process_keyval_opt(options, "style");
1430 h_biblatex_citestyle = opt;
1431 h_biblatex_bibstyle = opt;
1433 opt = process_keyval_opt(options, "citestyle");
1435 h_biblatex_citestyle = opt;
1436 opt = process_keyval_opt(options, "bibstyle");
1438 h_biblatex_bibstyle = opt;
1440 opt = process_keyval_opt(options, "refsection");
1442 if (opt == "none" || opt == "part"
1443 || opt == "chapter" || opt == "section"
1444 || opt == "subsection")
1447 cerr << "Ignoring unkown refesection value '"
1450 opt = process_keyval_opt(options, "bibencoding");
1453 if (!options.empty()) {
1454 h_biblio_options = join(options, ",");
1459 else if (name == "jurabib") {
1460 h_biblio_style = "jurabib";
1461 h_cite_engine = "jurabib";
1462 h_cite_engine_type = "authoryear";
1463 if (!options.empty())
1464 h_biblio_options = join(options, ",");
1467 else if (name == "bibtopic")
1468 h_use_bibtopic = "true";
1470 else if (name == "chapterbib")
1471 h_multibib = "child";
1473 else if (name == "hyperref")
1474 handle_hyperref(options);
1476 else if (name == "algorithm2e") {
1477 // Load "algorithm2e" module
1478 addModule("algorithm2e");
1479 // Add the package options to the global document options
1480 if (!options.empty()) {
1481 if (h_options.empty())
1482 h_options = join(options, ",");
1484 h_options += ',' + join(options, ",");
1487 else if (name == "microtype") {
1488 //we internally support only microtype without params
1489 if (options.empty())
1490 h_use_microtype = "true";
1492 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1495 else if (name == "lineno") {
1496 h_use_lineno = "true";
1497 if (!options.empty()) {
1498 h_lineno_options = join(options, ",");
1503 else if (!in_lyx_preamble) {
1504 if (options.empty())
1505 h_preamble << "\\usepackage{" << name << '}';
1507 h_preamble << "\\usepackage[" << opts << "]{"
1511 if (p.next_token().cat() == catNewline ||
1512 (p.next_token().cat() == catSpace &&
1513 p.next_next_token().cat() == catNewline))
1517 // We need to do something with the options...
1518 if (!options.empty() && !detectEncoding)
1519 cerr << "Ignoring options '" << join(options, ",")
1520 << "' of package " << name << '.' << endl;
1522 // remove the whitespace
1527 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1530 Token t = p.get_token();
1531 if (t.cat() == catEscape &&
1532 is_known(t.cs(), known_if_commands))
1533 handle_if(p, in_lyx_preamble);
1535 if (!in_lyx_preamble)
1536 h_preamble << t.asInput();
1537 if (t.cat() == catEscape && t.cs() == "fi")
1544 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1546 if (contains(h_float_placement, "H"))
1547 registerAutomaticallyLoadedPackage("float");
1548 if (h_spacing != "single" && h_spacing != "default")
1549 registerAutomaticallyLoadedPackage("setspace");
1550 if (h_use_packages["amsmath"] == "2") {
1551 // amsbsy and amstext are already provided by amsmath
1552 registerAutomaticallyLoadedPackage("amsbsy");
1553 registerAutomaticallyLoadedPackage("amstext");
1556 // output the LyX file settings
1557 // Important: Keep the version formatting in sync with LyX and
1558 // lyx2lyx (bug 7951)
1559 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1560 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1561 << lyx_version_minor << '\n'
1562 << "\\lyxformat " << LYX_FORMAT << '\n'
1563 << "\\begin_document\n"
1564 << "\\begin_header\n"
1565 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1566 << "\\origin " << origin << "\n"
1567 << "\\textclass " << h_textclass << "\n";
1568 string const raw = subdoc ? empty_string() : h_preamble.str();
1570 os << "\\begin_preamble\n";
1571 for (string::size_type i = 0; i < raw.size(); ++i) {
1572 if (raw[i] == package_beg_sep) {
1573 // Here follows some package loading code that
1574 // must be skipped if the package is loaded
1576 string::size_type j = raw.find(package_mid_sep, i);
1577 if (j == string::npos)
1579 string::size_type k = raw.find(package_end_sep, j);
1580 if (k == string::npos)
1582 string const package = raw.substr(i + 1, j - i - 1);
1583 string const replacement = raw.substr(j + 1, k - j - 1);
1584 if (auto_packages.find(package) == auto_packages.end())
1590 os << "\n\\end_preamble\n";
1592 if (!h_options.empty())
1593 os << "\\options " << h_options << "\n";
1594 os << "\\use_default_options " << h_use_default_options << "\n";
1595 if (!used_modules.empty()) {
1596 os << "\\begin_modules\n";
1597 vector<string>::const_iterator const end = used_modules.end();
1598 vector<string>::const_iterator it = used_modules.begin();
1599 for (; it != end; ++it)
1601 os << "\\end_modules\n";
1603 if (!h_includeonlys.empty()) {
1604 os << "\\begin_includeonly\n";
1605 for (auto const & iofile : h_includeonlys)
1606 os << iofile << '\n';
1607 os << "\\end_includeonly\n";
1609 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1610 << "\\language " << h_language << "\n"
1611 << "\\language_package " << h_language_package << "\n"
1612 << "\\inputencoding " << h_inputencoding << "\n"
1613 << "\\fontencoding " << h_fontencoding << "\n"
1614 << "\\font_roman \"" << h_font_roman[0]
1615 << "\" \"" << h_font_roman[1] << "\"\n"
1616 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1617 << "\\font_typewriter \"" << h_font_typewriter[0]
1618 << "\" \"" << h_font_typewriter[1] << "\"\n"
1619 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1620 << "\\font_default_family " << h_font_default_family << "\n"
1621 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1622 << "\\font_sc " << h_font_sc << "\n"
1623 << "\\font_roman_osf " << h_font_roman_osf << "\n"
1624 << "\\font_sans_osf " << h_font_sans_osf << "\n"
1625 << "\\font_typewriter_osf " << h_font_typewriter_osf << "\n";
1626 if (!h_font_roman_opts.empty())
1627 os << "\\font_roman_opts \"" << h_font_roman_opts << "\"" << '\n';
1628 os << "\\font_sf_scale " << h_font_sf_scale[0]
1629 << ' ' << h_font_sf_scale[1] << '\n';
1630 if (!h_font_sans_opts.empty())
1631 os << "\\font_sans_opts \"" << h_font_sans_opts << "\"" << '\n';
1632 os << "\\font_tt_scale " << h_font_tt_scale[0]
1633 << ' ' << h_font_tt_scale[1] << '\n';
1634 if (!h_font_cjk.empty())
1635 os << "\\font_cjk " << h_font_cjk << '\n';
1636 if (!h_font_typewriter_opts.empty())
1637 os << "\\font_typewriter_opts \"" << h_font_typewriter_opts << "\"" << '\n';
1638 os << "\\use_microtype " << h_use_microtype << '\n'
1639 << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
1640 << "\\graphics " << h_graphics << '\n'
1641 << "\\default_output_format " << h_default_output_format << "\n"
1642 << "\\output_sync " << h_output_sync << "\n";
1643 if (h_output_sync == "1")
1644 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1645 os << "\\bibtex_command " << h_bibtex_command << "\n"
1646 << "\\index_command " << h_index_command << "\n";
1647 if (!h_float_placement.empty())
1648 os << "\\float_placement " << h_float_placement << "\n";
1649 os << "\\paperfontsize " << h_paperfontsize << "\n"
1650 << "\\spacing " << h_spacing << "\n"
1651 << "\\use_hyperref " << h_use_hyperref << '\n';
1652 if (h_use_hyperref == "true") {
1653 if (!h_pdf_title.empty())
1654 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1655 if (!h_pdf_author.empty())
1656 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1657 if (!h_pdf_subject.empty())
1658 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1659 if (!h_pdf_keywords.empty())
1660 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1661 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1662 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1663 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1664 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1665 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1666 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1667 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1668 "\\pdf_backref " << h_pdf_backref << "\n"
1669 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1670 if (!h_pdf_pagemode.empty())
1671 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1672 if (!h_pdf_quoted_options.empty())
1673 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1675 os << "\\papersize " << h_papersize << "\n"
1676 << "\\use_geometry " << h_use_geometry << '\n';
1677 for (map<string, string>::const_iterator it = h_use_packages.begin();
1678 it != h_use_packages.end(); ++it)
1679 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1680 os << "\\cite_engine " << h_cite_engine << '\n'
1681 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1682 << "\\biblio_style " << h_biblio_style << "\n"
1683 << "\\use_bibtopic " << h_use_bibtopic << "\n";
1684 if (!h_biblio_options.empty())
1685 os << "\\biblio_options " << h_biblio_options << "\n";
1686 if (!h_biblatex_bibstyle.empty())
1687 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
1688 if (!h_biblatex_citestyle.empty())
1689 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
1690 if (!h_multibib.empty())
1691 os << "\\multibib " << h_multibib << "\n";
1692 os << "\\use_indices " << h_use_indices << "\n"
1693 << "\\paperorientation " << h_paperorientation << '\n'
1694 << "\\suppress_date " << h_suppress_date << '\n'
1695 << "\\justification " << h_justification << '\n'
1696 << "\\use_refstyle " << h_use_refstyle << '\n'
1697 << "\\use_minted " << h_use_minted << '\n'
1698 << "\\use_lineno " << h_use_lineno << '\n';
1699 if (!h_lineno_options.empty())
1700 os << "\\lineno_options " << h_lineno_options << '\n';
1701 if (!h_fontcolor.empty())
1702 os << "\\fontcolor " << h_fontcolor << '\n';
1703 if (!h_notefontcolor.empty())
1704 os << "\\notefontcolor " << h_notefontcolor << '\n';
1705 if (!h_backgroundcolor.empty())
1706 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1707 if (!h_boxbgcolor.empty())
1708 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1709 if (index_number != 0)
1710 for (int i = 0; i < index_number; i++) {
1711 os << "\\index " << h_index[i] << '\n'
1712 << "\\shortcut " << h_shortcut[i] << '\n'
1713 << "\\color " << h_color << '\n'
1717 os << "\\index " << h_index[0] << '\n'
1718 << "\\shortcut " << h_shortcut[0] << '\n'
1719 << "\\color " << h_color << '\n'
1723 << "\\secnumdepth " << h_secnumdepth << "\n"
1724 << "\\tocdepth " << h_tocdepth << "\n"
1725 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1726 if (h_paragraph_separation == "skip")
1727 os << "\\defskip " << h_defskip << "\n";
1729 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1730 os << "\\is_math_indent " << h_is_mathindent << "\n";
1731 if (!h_mathindentation.empty())
1732 os << "\\math_indentation " << h_mathindentation << "\n";
1733 os << "\\math_numbering_side " << h_math_numbering_side << "\n";
1734 os << "\\quotes_style " << h_quotes_style << "\n"
1735 << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
1736 << "\\papercolumns " << h_papercolumns << "\n"
1737 << "\\papersides " << h_papersides << "\n"
1738 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1739 if (!h_listings_params.empty())
1740 os << "\\listings_params " << h_listings_params << "\n";
1741 os << "\\tracking_changes " << h_tracking_changes << "\n"
1742 << "\\output_changes " << h_output_changes << "\n"
1743 << "\\html_math_output " << h_html_math_output << "\n"
1744 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1745 << "\\html_be_strict " << h_html_be_strict << "\n"
1747 << "\\end_header\n\n"
1748 << "\\begin_body\n";
1753 void Preamble::parse(Parser & p, string const & forceclass,
1754 TeX2LyXDocClass & tc)
1756 // initialize fixed types
1757 special_columns_['D'] = 3;
1758 parse(p, forceclass, false, tc);
1762 void Preamble::parse(Parser & p, string const & forceclass,
1763 bool detectEncoding, TeX2LyXDocClass & tc)
1765 bool is_full_document = false;
1766 bool is_lyx_file = false;
1767 bool in_lyx_preamble = false;
1769 // determine whether this is a full document or a fragment for inclusion
1771 Token const & t = p.get_token();
1773 if (t.cat() == catEscape && t.cs() == "documentclass") {
1774 is_full_document = true;
1780 if (detectEncoding && !is_full_document)
1783 while (is_full_document && p.good()) {
1784 if (detectEncoding && h_inputencoding != "auto-legacy" &&
1785 h_inputencoding != "auto-legacy-plain")
1788 Token const & t = p.get_token();
1791 if (!detectEncoding)
1792 cerr << "t: " << t << '\n';
1798 if (!in_lyx_preamble &&
1799 (t.cat() == catLetter ||
1800 t.cat() == catSuper ||
1801 t.cat() == catSub ||
1802 t.cat() == catOther ||
1803 t.cat() == catMath ||
1804 t.cat() == catActive ||
1805 t.cat() == catBegin ||
1806 t.cat() == catEnd ||
1807 t.cat() == catAlign ||
1808 t.cat() == catParameter)) {
1809 h_preamble << t.cs();
1813 if (!in_lyx_preamble &&
1814 (t.cat() == catSpace || t.cat() == catNewline)) {
1815 h_preamble << t.asInput();
1819 if (t.cat() == catComment) {
1820 static regex const islyxfile("%% LyX .* created this file");
1821 static regex const usercommands("User specified LaTeX commands");
1823 string const comment = t.asInput();
1825 // magically switch encoding default if it looks like XeLaTeX
1826 static string const magicXeLaTeX =
1827 "% This document must be compiled with XeLaTeX ";
1828 if (comment.size() > magicXeLaTeX.size()
1829 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1830 && h_inputencoding == "auto-legacy") {
1831 if (!detectEncoding)
1832 cerr << "XeLaTeX comment found, switching to UTF8\n";
1833 h_inputencoding = "utf8";
1836 if (regex_search(comment, sub, islyxfile)) {
1838 in_lyx_preamble = true;
1839 } else if (is_lyx_file
1840 && regex_search(comment, sub, usercommands))
1841 in_lyx_preamble = false;
1842 else if (!in_lyx_preamble)
1843 h_preamble << t.asInput();
1847 if (t.cs() == "PassOptionsToPackage") {
1848 string const poptions = p.getArg('{', '}');
1849 string const package = p.verbatim_item();
1850 extra_package_options_.insert(make_pair(package, poptions));
1854 if (t.cs() == "pagestyle") {
1855 h_paperpagestyle = p.verbatim_item();
1859 if (t.cs() == "setdefaultlanguage") {
1861 // We don't yet care about non-language variant options
1862 // because LyX doesn't support this yet, see bug #8214
1864 string langopts = p.getOpt();
1865 // check if the option contains a variant, if yes, extract it
1866 string::size_type pos_var = langopts.find("variant");
1867 string::size_type i = langopts.find(',', pos_var);
1868 string::size_type k = langopts.find('=', pos_var);
1869 if (pos_var != string::npos){
1871 if (i == string::npos)
1872 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1874 variant = langopts.substr(k + 1, i - k - 1);
1875 h_language = variant;
1879 h_language = p.verbatim_item();
1880 //finally translate the poyglossia name to a LyX name
1881 h_language = polyglossia2lyx(h_language);
1885 if (t.cs() == "setotherlanguage") {
1886 // We don't yet care about the option because LyX doesn't
1887 // support this yet, see bug #8214
1888 p.hasOpt() ? p.getOpt() : string();
1893 if (t.cs() == "setmainfont") {
1894 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
1895 h_font_roman[1] = p.getArg('{', '}');
1896 if (!fontopts.empty()) {
1897 vector<string> opts = getVectorFromString(fontopts);
1899 for (auto const & opt : opts) {
1900 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
1903 if (!fontopts.empty())
1907 h_font_roman_opts = fontopts;
1912 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1913 // LyX currently only supports the scale option
1914 string scale, fontopts;
1916 fontopts = p.getArg('[', ']');
1917 if (!fontopts.empty()) {
1918 vector<string> opts = getVectorFromString(fontopts);
1920 for (auto const & opt : opts) {
1921 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
1924 if (prefixIs(opt, "Scale=")) {
1925 scale_as_percentage(opt, scale);
1928 if (!fontopts.empty())
1934 if (t.cs() == "setsansfont") {
1936 h_font_sf_scale[1] = scale;
1937 h_font_sans[1] = p.getArg('{', '}');
1938 if (!fontopts.empty())
1939 h_font_sans_opts = fontopts;
1942 h_font_tt_scale[1] = scale;
1943 h_font_typewriter[1] = p.getArg('{', '}');
1944 if (!fontopts.empty())
1945 h_font_typewriter_opts = fontopts;
1950 if (t.cs() == "babelfont") {
1952 h_use_non_tex_fonts = true;
1953 h_language_package = "babel";
1954 if (h_inputencoding == "auto-legacy")
1955 p.setEncoding("UTF-8");
1956 // we don't care about the lang option
1957 string const lang = p.hasOpt() ? p.getArg('[', ']') : string();
1958 string const family = p.getArg('{', '}');
1959 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
1960 string const fontname = p.getArg('{', '}');
1961 if (lang.empty() && family == "rm") {
1962 h_font_roman[1] = fontname;
1963 if (!fontopts.empty()) {
1964 vector<string> opts = getVectorFromString(fontopts);
1966 for (auto const & opt : opts) {
1967 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
1970 if (!fontopts.empty())
1974 h_font_roman_opts = fontopts;
1977 } else if (lang.empty() && (family == "sf" || family == "tt")) {
1978 // LyX currently only supports the scale option
1980 if (!fontopts.empty()) {
1981 vector<string> opts = getVectorFromString(fontopts);
1983 for (auto const & opt : opts) {
1984 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
1987 if (prefixIs(opt, "Scale=")) {
1988 scale_as_percentage(opt, scale);
1991 if (!fontopts.empty())
1996 if (family == "sf") {
1998 h_font_sf_scale[1] = scale;
1999 h_font_sans[1] = fontname;
2000 if (!fontopts.empty())
2001 h_font_sans_opts = fontopts;
2004 h_font_tt_scale[1] = scale;
2005 h_font_typewriter[1] = fontname;
2006 if (!fontopts.empty())
2007 h_font_typewriter_opts = fontopts;
2011 // not rm, sf or tt or lang specific
2012 h_preamble << '\\' << t.cs();
2014 h_preamble << '[' << lang << ']';
2015 h_preamble << '{' << family << '}';
2016 if (!fontopts.empty())
2017 h_preamble << '[' << fontopts << ']';
2018 h_preamble << '{' << fontname << '}' << '\n';
2023 if (t.cs() == "date") {
2024 string argument = p.getArg('{', '}');
2025 if (argument.empty())
2026 h_suppress_date = "true";
2028 h_preamble << t.asInput() << '{' << argument << '}';
2032 if (t.cs() == "color") {
2033 string const space =
2034 (p.hasOpt() ? p.getOpt() : string());
2035 string argument = p.getArg('{', '}');
2036 // check the case that a standard color is used
2037 if (space.empty() && is_known(argument, known_basic_colors)) {
2038 h_fontcolor = rgbcolor2code(argument);
2039 registerAutomaticallyLoadedPackage("color");
2040 } else if (space.empty() && argument == "document_fontcolor")
2041 registerAutomaticallyLoadedPackage("color");
2042 // check the case that LyX's document_fontcolor is defined
2043 // but not used for \color
2045 h_preamble << t.asInput();
2047 h_preamble << space;
2048 h_preamble << '{' << argument << '}';
2049 // the color might already be set because \definecolor
2050 // is parsed before this
2056 if (t.cs() == "pagecolor") {
2057 string argument = p.getArg('{', '}');
2058 // check the case that a standard color is used
2059 if (is_known(argument, known_basic_colors)) {
2060 h_backgroundcolor = rgbcolor2code(argument);
2061 } else if (argument == "page_backgroundcolor")
2062 registerAutomaticallyLoadedPackage("color");
2063 // check the case that LyX's page_backgroundcolor is defined
2064 // but not used for \pagecolor
2066 h_preamble << t.asInput() << '{' << argument << '}';
2067 // the color might already be set because \definecolor
2068 // is parsed before this
2069 h_backgroundcolor = "";
2074 if (t.cs() == "makeatletter") {
2075 // LyX takes care of this
2076 p.setCatcode('@', catLetter);
2080 if (t.cs() == "makeatother") {
2081 // LyX takes care of this
2082 p.setCatcode('@', catOther);
2086 if (t.cs() == "makeindex") {
2087 // LyX will re-add this if a print index command is found
2092 if (t.cs() == "newindex") {
2093 string const indexname = p.getArg('[', ']');
2094 string const shortcut = p.verbatim_item();
2095 if (!indexname.empty())
2096 h_index[index_number] = indexname;
2098 h_index[index_number] = shortcut;
2099 h_shortcut[index_number] = shortcut;
2105 if (t.cs() == "addbibresource") {
2106 string const options = p.getArg('[', ']');
2107 string const arg = removeExtension(p.getArg('{', '}'));
2108 if (!options.empty()) {
2109 // check if the option contains a bibencoding, if yes, extract it
2110 string::size_type pos = options.find("bibencoding=");
2112 if (pos != string::npos) {
2113 string::size_type i = options.find(',', pos);
2114 if (i == string::npos)
2115 encoding = options.substr(pos + 1);
2117 encoding = options.substr(pos, i - pos);
2118 pos = encoding.find('=');
2119 if (pos == string::npos)
2122 encoding = encoding.substr(pos + 1);
2124 if (!encoding.empty())
2125 biblatex_encodings.push_back(normalize_filename(arg) + ' ' + encoding);
2127 biblatex_bibliographies.push_back(arg);
2131 if (t.cs() == "bibliography") {
2132 vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
2133 biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
2137 if (t.cs() == "RS@ifundefined") {
2138 string const name = p.verbatim_item();
2139 string const body1 = p.verbatim_item();
2140 string const body2 = p.verbatim_item();
2141 // only non-lyxspecific stuff
2142 if (in_lyx_preamble &&
2143 (name == "subsecref" || name == "thmref" || name == "lemref"))
2147 ss << '\\' << t.cs();
2148 ss << '{' << name << '}'
2149 << '{' << body1 << '}'
2150 << '{' << body2 << '}';
2151 h_preamble << ss.str();
2156 if (t.cs() == "AtBeginDocument") {
2157 string const name = p.verbatim_item();
2158 // only non-lyxspecific stuff
2159 if (in_lyx_preamble &&
2160 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
2161 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
2162 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
2163 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
2164 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
2165 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
2166 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
2167 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
2168 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
2169 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
2170 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
2171 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
2172 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
2173 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
2174 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
2178 ss << '\\' << t.cs();
2179 ss << '{' << name << '}';
2180 h_preamble << ss.str();
2185 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
2186 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
2187 || t.cs() == "providecommand" || t.cs() == "providecommandx"
2188 || t.cs() == "DeclareRobustCommand"
2189 || t.cs() == "DeclareRobustCommandx"
2190 || t.cs() == "ProvideTextCommandDefault"
2191 || t.cs() == "DeclareMathAccent") {
2193 if (p.next_token().character() == '*') {
2197 string const name = p.verbatim_item();
2198 string const opt1 = p.getFullOpt();
2199 string const opt2 = p.getFullOpt();
2200 string const body = p.verbatim_item();
2201 // store the in_lyx_preamble setting
2202 bool const was_in_lyx_preamble = in_lyx_preamble;
2204 if (name == "\\rmdefault")
2205 if (is_known(body, known_roman_font_packages)) {
2206 h_font_roman[0] = body;
2208 in_lyx_preamble = true;
2210 if (name == "\\sfdefault")
2211 if (is_known(body, known_sans_font_packages)) {
2212 h_font_sans[0] = body;
2214 in_lyx_preamble = true;
2216 if (name == "\\ttdefault")
2217 if (is_known(body, known_typewriter_font_packages)) {
2218 h_font_typewriter[0] = body;
2220 in_lyx_preamble = true;
2222 if (name == "\\familydefault") {
2223 string family = body;
2224 // remove leading "\"
2225 h_font_default_family = family.erase(0,1);
2227 in_lyx_preamble = true;
2230 // remove LyX-specific definitions that are re-added by LyX
2232 // \lyxline is an ancient command that is converted by tex2lyx into
2233 // a \rule therefore remove its preamble code
2234 if (name == "\\lyxdot" || name == "\\lyxarrow"
2235 || name == "\\lyxline" || name == "\\LyX") {
2237 in_lyx_preamble = true;
2240 // Add the command to the known commands
2241 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
2243 // only non-lyxspecific stuff
2244 if (!in_lyx_preamble) {
2246 ss << '\\' << t.cs();
2249 ss << '{' << name << '}' << opt1 << opt2
2250 << '{' << body << "}";
2251 if (prefixIs(t.cs(), "renew") || !contains(h_preamble.str(), ss.str()))
2252 h_preamble << ss.str();
2254 ostream & out = in_preamble ? h_preamble : os;
2255 out << "\\" << t.cs() << "{" << name << "}"
2256 << opts << "{" << body << "}";
2259 // restore the in_lyx_preamble setting
2260 in_lyx_preamble = was_in_lyx_preamble;
2264 if (t.cs() == "documentclass") {
2265 vector<string>::iterator it;
2266 vector<string> opts = split_options(p.getArg('[', ']'));
2267 handle_opt(opts, known_fontsizes, h_paperfontsize);
2268 delete_opt(opts, known_fontsizes);
2269 // delete "pt" at the end
2270 string::size_type i = h_paperfontsize.find("pt");
2271 if (i != string::npos)
2272 h_paperfontsize.erase(i);
2273 // The documentclass options are always parsed before the options
2274 // of the babel call so that a language cannot overwrite the babel
2276 handle_opt(opts, known_languages, h_language);
2277 delete_opt(opts, known_languages);
2280 if ((it = find(opts.begin(), opts.end(), "fleqn"))
2282 h_is_mathindent = "1";
2285 // formula numbering side
2286 if ((it = find(opts.begin(), opts.end(), "leqno"))
2288 h_math_numbering_side = "left";
2291 else if ((it = find(opts.begin(), opts.end(), "reqno"))
2293 h_math_numbering_side = "right";
2297 // paper orientation
2298 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
2299 h_paperorientation = "landscape";
2303 if ((it = find(opts.begin(), opts.end(), "oneside"))
2308 if ((it = find(opts.begin(), opts.end(), "twoside"))
2314 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
2316 h_papercolumns = "1";
2319 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
2321 h_papercolumns = "2";
2325 // some size options are known to any document classes, other sizes
2326 // are handled by the \geometry command of the geometry package
2327 handle_opt(opts, known_class_paper_sizes, h_papersize);
2328 delete_opt(opts, known_class_paper_sizes);
2329 // the remaining options
2330 h_options = join(opts, ",");
2331 // FIXME This does not work for classes that have a
2332 // different name in LyX than in LaTeX
2333 h_textclass = p.getArg('{', '}');
2338 if (t.cs() == "usepackage") {
2339 string const options = p.getArg('[', ']');
2340 string const name = p.getArg('{', '}');
2341 vector<string> vecnames;
2342 split(name, vecnames, ',');
2343 vector<string>::const_iterator it = vecnames.begin();
2344 vector<string>::const_iterator end = vecnames.end();
2345 for (; it != end; ++it)
2346 handle_package(p, trimSpaceAndEol(*it), options,
2347 in_lyx_preamble, detectEncoding);
2351 if (t.cs() == "inputencoding") {
2352 string const encoding = p.getArg('{','}');
2353 Encoding const * const enc = encodings.fromLaTeXName(
2354 encoding, Encoding::inputenc, true);
2356 if (!detectEncoding)
2357 cerr << "Unknown encoding " << encoding
2358 << ". Ignoring." << std::endl;
2361 h_inputencoding = enc->name();
2362 p.setEncoding(enc->iconvName());
2367 if (t.cs() == "newenvironment") {
2368 string const name = p.getArg('{', '}');
2369 string const opt1 = p.getFullOpt();
2370 string const opt2 = p.getFullOpt();
2371 string const beg = p.verbatim_item();
2372 string const end = p.verbatim_item();
2373 if (!in_lyx_preamble) {
2374 h_preamble << "\\newenvironment{" << name
2375 << '}' << opt1 << opt2 << '{'
2376 << beg << "}{" << end << '}';
2378 add_known_environment(name, opt1, !opt2.empty(),
2379 from_utf8(beg), from_utf8(end));
2383 if (t.cs() == "newtheorem") {
2385 if (p.next_token().character() == '*') {
2389 string const name = p.getArg('{', '}');
2390 string const opt1 = p.getFullOpt();
2391 string const opt2 = p.getFullOpt();
2392 string const body = p.verbatim_item();
2393 string const opt3 = p.getFullOpt();
2394 string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
2396 string const complete = cmd + "{" + name + '}' +
2397 opt1 + opt2 + '{' + body + '}' + opt3;
2399 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
2401 if (!in_lyx_preamble)
2402 h_preamble << complete;
2406 if (t.cs() == "def") {
2407 string name = p.get_token().cs();
2408 // In fact, name may be more than the name:
2409 // In the test case of bug 8116
2410 // name == "csname SF@gobble@opt \endcsname".
2411 // Therefore, we need to use asInput() instead of cs().
2412 while (p.next_token().cat() != catBegin)
2413 name += p.get_token().asInput();
2414 if (!in_lyx_preamble)
2415 h_preamble << "\\def\\" << name << '{'
2416 << p.verbatim_item() << "}";
2420 if (t.cs() == "newcolumntype") {
2421 string const name = p.getArg('{', '}');
2422 trimSpaceAndEol(name);
2424 string opts = p.getOpt();
2425 if (!opts.empty()) {
2426 istringstream is(string(opts, 1));
2429 special_columns_[name[0]] = nargs;
2430 h_preamble << "\\newcolumntype{" << name << "}";
2432 h_preamble << "[" << nargs << "]";
2433 h_preamble << "{" << p.verbatim_item() << "}";
2437 if (t.cs() == "setcounter") {
2438 string const name = p.getArg('{', '}');
2439 string const content = p.getArg('{', '}');
2440 if (name == "secnumdepth")
2441 h_secnumdepth = content;
2442 else if (name == "tocdepth")
2443 h_tocdepth = content;
2445 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
2449 if (t.cs() == "setlength") {
2450 string const name = p.verbatim_item();
2451 string const content = p.verbatim_item();
2452 // the paragraphs are only not indented when \parindent is set to zero
2453 if (name == "\\parindent" && content != "") {
2454 if (content[0] == '0')
2455 h_paragraph_separation = "skip";
2457 h_paragraph_indentation = translate_len(content);
2458 } else if (name == "\\parskip") {
2459 if (content == "\\smallskipamount")
2460 h_defskip = "smallskip";
2461 else if (content == "\\medskipamount")
2462 h_defskip = "medskip";
2463 else if (content == "\\bigskipamount")
2464 h_defskip = "bigskip";
2466 h_defskip = translate_len(content);
2467 } else if (name == "\\mathindent") {
2468 h_mathindentation = translate_len(content);
2470 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2474 if (t.cs() == "onehalfspacing") {
2475 h_spacing = "onehalf";
2479 if (t.cs() == "doublespacing") {
2480 h_spacing = "double";
2484 if (t.cs() == "setstretch") {
2485 h_spacing = "other " + p.verbatim_item();
2489 if (t.cs() == "synctex") {
2490 // the scheme is \synctex=value
2491 // where value can only be "1" or "-1"
2492 h_output_sync = "1";
2493 // there can be any character behind the value (e.g. a linebreak or a '\'
2494 // therefore we extract it char by char
2496 string value = p.get_token().asInput();
2498 value += p.get_token().asInput();
2499 h_output_sync_macro = "\\synctex=" + value;
2503 if (t.cs() == "begin") {
2504 string const name = p.getArg('{', '}');
2505 if (name == "document")
2507 h_preamble << "\\begin{" << name << "}";
2511 if (t.cs() == "geometry") {
2512 vector<string> opts = split_options(p.getArg('{', '}'));
2513 handle_geometry(opts);
2517 if (t.cs() == "definecolor") {
2518 string const color = p.getArg('{', '}');
2519 string const space = p.getArg('{', '}');
2520 string const value = p.getArg('{', '}');
2521 if (color == "document_fontcolor" && space == "rgb") {
2522 RGBColor c(RGBColorFromLaTeX(value));
2523 h_fontcolor = X11hexname(c);
2524 } else if (color == "note_fontcolor" && space == "rgb") {
2525 RGBColor c(RGBColorFromLaTeX(value));
2526 h_notefontcolor = X11hexname(c);
2527 } else if (color == "page_backgroundcolor" && space == "rgb") {
2528 RGBColor c(RGBColorFromLaTeX(value));
2529 h_backgroundcolor = X11hexname(c);
2530 } else if (color == "shadecolor" && space == "rgb") {
2531 RGBColor c(RGBColorFromLaTeX(value));
2532 h_boxbgcolor = X11hexname(c);
2534 h_preamble << "\\definecolor{" << color
2535 << "}{" << space << "}{" << value
2541 if (t.cs() == "bibliographystyle") {
2542 h_biblio_style = p.verbatim_item();
2546 if (t.cs() == "jurabibsetup") {
2547 // FIXME p.getArg('{', '}') is most probably wrong (it
2548 // does not handle nested braces).
2549 // Use p.verbatim_item() instead.
2550 vector<string> jurabibsetup =
2551 split_options(p.getArg('{', '}'));
2552 // add jurabibsetup to the jurabib package options
2553 add_package("jurabib", jurabibsetup);
2554 if (!jurabibsetup.empty()) {
2555 h_preamble << "\\jurabibsetup{"
2556 << join(jurabibsetup, ",") << '}';
2561 if (t.cs() == "hypersetup") {
2562 vector<string> hypersetup =
2563 split_options(p.verbatim_item());
2564 // add hypersetup to the hyperref package options
2565 handle_hyperref(hypersetup);
2566 if (!hypersetup.empty()) {
2567 h_preamble << "\\hypersetup{"
2568 << join(hypersetup, ",") << '}';
2573 if (t.cs() == "includeonly") {
2574 vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
2575 for (auto & iofile : includeonlys) {
2576 string filename(normalize_filename(iofile));
2577 string const path = getMasterFilePath(true);
2578 // We want to preserve relative/absolute filenames,
2579 // therefore path is only used for testing
2580 if (!makeAbsPath(filename, path).exists()) {
2581 // The file extension is probably missing.
2582 // Now try to find it out.
2583 string const tex_name =
2584 find_file(filename, path,
2585 known_tex_extensions);
2586 if (!tex_name.empty())
2587 filename = tex_name;
2590 if (makeAbsPath(filename, path).exists())
2591 fix_child_filename(filename);
2593 cerr << "Warning: Could not find included file '"
2594 << filename << "'." << endl;
2595 outname = changeExtension(filename, "lyx");
2596 h_includeonlys.push_back(outname);
2601 if (is_known(t.cs(), known_if_3arg_commands)) {
2602 // prevent misparsing of \usepackage if it is used
2603 // as an argument (see e.g. our own output of
2604 // \@ifundefined above)
2605 string const arg1 = p.verbatim_item();
2606 string const arg2 = p.verbatim_item();
2607 string const arg3 = p.verbatim_item();
2608 // test case \@ifundefined{date}{}{\date{}}
2609 if (t.cs() == "@ifundefined" && arg1 == "date" &&
2610 arg2.empty() && arg3 == "\\date{}") {
2611 h_suppress_date = "true";
2612 // older tex2lyx versions did output
2613 // \@ifundefined{definecolor}{\usepackage{color}}{}
2614 } else if (t.cs() == "@ifundefined" &&
2615 arg1 == "definecolor" &&
2616 arg2 == "\\usepackage{color}" &&
2618 if (!in_lyx_preamble)
2619 h_preamble << package_beg_sep
2622 << "\\@ifundefined{definecolor}{color}{}"
2625 //\@ifundefined{showcaptionsetup}{}{%
2626 // \PassOptionsToPackage{caption=false}{subfig}}
2627 // that LyX uses for subfloats
2628 } else if (t.cs() == "@ifundefined" &&
2629 arg1 == "showcaptionsetup" && arg2.empty()
2630 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
2632 } else if (!in_lyx_preamble) {
2633 h_preamble << t.asInput()
2634 << '{' << arg1 << '}'
2635 << '{' << arg2 << '}'
2636 << '{' << arg3 << '}';
2641 if (is_known(t.cs(), known_if_commands)) {
2642 // must not parse anything in conditional code, since
2643 // LyX would output the parsed contents unconditionally
2644 if (!in_lyx_preamble)
2645 h_preamble << t.asInput();
2646 handle_if(p, in_lyx_preamble);
2650 if (!t.cs().empty() && !in_lyx_preamble) {
2651 h_preamble << '\\' << t.cs();
2656 // remove the whitespace
2659 // Force textclass if the user wanted it
2660 if (!forceclass.empty())
2661 h_textclass = forceclass;
2662 tc.setName(h_textclass);
2663 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2664 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2667 if (h_papersides.empty()) {
2670 h_papersides = ss.str();
2673 // If the CJK package is used we cannot set the document language from
2674 // the babel options. Instead, we guess which language is used most
2675 // and set this one.
2676 default_language = h_language;
2677 if (is_full_document &&
2678 (auto_packages.find("CJK") != auto_packages.end() ||
2679 auto_packages.find("CJKutf8") != auto_packages.end())) {
2681 h_language = guessLanguage(p, default_language);
2683 if (explicit_babel && h_language != default_language) {
2684 // We set the document language to a CJK language,
2685 // but babel is explicitly called in the user preamble
2686 // without options. LyX will not add the default
2687 // language to the document options if it is either
2688 // english, or no text is set as default language.
2689 // Therefore we need to add a language option explicitly.
2690 // FIXME: It would be better to remove all babel calls
2691 // from the user preamble, but this is difficult
2692 // without re-introducing bug 7861.
2693 if (h_options.empty())
2694 h_options = lyx2babel(default_language);
2696 h_options += ',' + lyx2babel(default_language);
2700 // Finally, set the quote style.
2701 // LyX knows the following quotes styles:
2702 // british, cjk, cjkangle, danish, english, french, german,
2703 // polish, russian, swedish and swiss
2704 // conversion list taken from
2705 // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
2706 // (quotes for kazakh are unknown)
2708 if (is_known(h_language, known_british_quotes_languages))
2709 h_quotes_style = "british";
2711 else if (is_known(h_language, known_cjk_quotes_languages))
2712 h_quotes_style = "cjk";
2714 else if (is_known(h_language, known_cjkangle_quotes_languages))
2715 h_quotes_style = "cjkangle";
2717 else if (is_known(h_language, known_danish_quotes_languages))
2718 h_quotes_style = "danish";
2720 else if (is_known(h_language, known_french_quotes_languages))
2721 h_quotes_style = "french";
2723 else if (is_known(h_language, known_german_quotes_languages))
2724 h_quotes_style = "german";
2726 else if (is_known(h_language, known_polish_quotes_languages))
2727 h_quotes_style = "polish";
2729 else if (is_known(h_language, known_russian_quotes_languages))
2730 h_quotes_style = "russian";
2732 else if (is_known(h_language, known_swedish_quotes_languages))
2733 h_quotes_style = "swedish";
2735 else if (is_known(h_language, known_swiss_quotes_languages))
2736 h_quotes_style = "swiss";
2738 else if (is_known(h_language, known_english_quotes_languages))
2739 h_quotes_style = "english";
2743 string Preamble::parseEncoding(Parser & p, string const & forceclass)
2745 TeX2LyXDocClass dummy;
2746 parse(p, forceclass, true, dummy);
2747 if (h_inputencoding != "auto-legacy" && h_inputencoding != "auto-legacy-plain")
2748 return h_inputencoding;
2753 string babel2lyx(string const & language)
2755 char const * const * where = is_known(language, known_languages);
2757 return known_coded_languages[where - known_languages];
2762 string lyx2babel(string const & language)
2764 char const * const * where = is_known(language, known_coded_languages);
2766 return known_languages[where - known_coded_languages];
2771 string Preamble::polyglossia2lyx(string const & language)
2773 char const * const * where = is_known(language, polyglossia_languages);
2775 return coded_polyglossia_languages[where - polyglossia_languages];
2780 string rgbcolor2code(string const & name)
2782 char const * const * where = is_known(name, known_basic_colors);
2784 // "red", "green" etc
2785 return known_basic_color_codes[where - known_basic_colors];
2787 // "255,0,0", "0,255,0" etc
2788 RGBColor c(RGBColorFromLaTeX(name));
2789 return X11hexname(c);