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_osf = "false";
533 h_font_sf_scale[0] = "100";
534 h_font_sf_scale[1] = "100";
535 h_font_tt_scale[0] = "100";
536 h_font_tt_scale[1] = "100";
537 // h_font_roman_opts;
539 // h_font_typewriter_opts;
541 h_is_mathindent = "0";
542 h_math_numbering_side = "default";
543 h_graphics = "default";
544 h_default_output_format = "default";
545 h_html_be_strict = "false";
546 h_html_css_as_file = "0";
547 h_html_math_output = "0";
548 h_index[0] = "Index";
549 h_index_command = "default";
550 h_inputencoding = "auto-legacy";
551 h_justification = "true";
552 h_language = "english";
553 h_language_package = "none";
555 h_maintain_unincluded_children = "false";
559 h_output_changes = "false";
561 //h_output_sync_macro
562 h_papercolumns = "1";
563 h_paperfontsize = "default";
564 h_paperorientation = "portrait";
565 h_paperpagestyle = "default";
567 h_papersize = "default";
568 h_paragraph_indentation = "default";
569 h_paragraph_separation = "indent";
574 h_pdf_bookmarks = "0";
575 h_pdf_bookmarksnumbered = "0";
576 h_pdf_bookmarksopen = "0";
577 h_pdf_bookmarksopenlevel = "1";
578 h_pdf_breaklinks = "0";
579 h_pdf_pdfborder = "0";
580 h_pdf_colorlinks = "0";
581 h_pdf_backref = "section";
582 h_pdf_pdfusetitle = "0";
584 //h_pdf_quoted_options;
585 h_quotes_style = "english";
587 h_shortcut[0] = "idx";
588 h_spacing = "single";
589 h_save_transient_properties = "true";
590 h_suppress_date = "false";
591 h_textclass = "article";
593 h_tracking_changes = "false";
594 h_use_bibtopic = "false";
595 h_use_dash_ligatures = "true";
596 h_use_indices = "false";
597 h_use_geometry = "false";
598 h_use_default_options = "false";
599 h_use_hyperref = "false";
600 h_use_microtype = "false";
601 h_use_lineno = "false";
602 h_use_refstyle = false;
603 h_use_minted = false;
604 h_use_packages["amsmath"] = "1";
605 h_use_packages["amssymb"] = "0";
606 h_use_packages["cancel"] = "0";
607 h_use_packages["esint"] = "1";
608 h_use_packages["mhchem"] = "0";
609 h_use_packages["mathdots"] = "0";
610 h_use_packages["mathtools"] = "0";
611 h_use_packages["stackrel"] = "0";
612 h_use_packages["stmaryrd"] = "0";
613 h_use_packages["undertilde"] = "0";
617 void Preamble::handle_hyperref(vector<string> & options)
619 // FIXME swallow inputencoding changes that might surround the
620 // hyperref setup if it was written by LyX
621 h_use_hyperref = "true";
622 // swallow "unicode=true", since LyX does always write that
623 vector<string>::iterator it =
624 find(options.begin(), options.end(), "unicode=true");
625 if (it != options.end())
627 it = find(options.begin(), options.end(), "pdfusetitle");
628 if (it != options.end()) {
629 h_pdf_pdfusetitle = "1";
632 string bookmarks = process_keyval_opt(options, "bookmarks");
633 if (bookmarks == "true")
634 h_pdf_bookmarks = "1";
635 else if (bookmarks == "false")
636 h_pdf_bookmarks = "0";
637 if (h_pdf_bookmarks == "1") {
638 string bookmarksnumbered =
639 process_keyval_opt(options, "bookmarksnumbered");
640 if (bookmarksnumbered == "true")
641 h_pdf_bookmarksnumbered = "1";
642 else if (bookmarksnumbered == "false")
643 h_pdf_bookmarksnumbered = "0";
644 string bookmarksopen =
645 process_keyval_opt(options, "bookmarksopen");
646 if (bookmarksopen == "true")
647 h_pdf_bookmarksopen = "1";
648 else if (bookmarksopen == "false")
649 h_pdf_bookmarksopen = "0";
650 if (h_pdf_bookmarksopen == "1") {
651 string bookmarksopenlevel =
652 process_keyval_opt(options, "bookmarksopenlevel");
653 if (!bookmarksopenlevel.empty())
654 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
657 string breaklinks = process_keyval_opt(options, "breaklinks");
658 if (breaklinks == "true")
659 h_pdf_breaklinks = "1";
660 else if (breaklinks == "false")
661 h_pdf_breaklinks = "0";
662 string pdfborder = process_keyval_opt(options, "pdfborder");
663 if (pdfborder == "{0 0 0}")
664 h_pdf_pdfborder = "1";
665 else if (pdfborder == "{0 0 1}")
666 h_pdf_pdfborder = "0";
667 string backref = process_keyval_opt(options, "backref");
668 if (!backref.empty())
669 h_pdf_backref = backref;
670 string colorlinks = process_keyval_opt(options, "colorlinks");
671 if (colorlinks == "true")
672 h_pdf_colorlinks = "1";
673 else if (colorlinks == "false")
674 h_pdf_colorlinks = "0";
675 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
676 if (!pdfpagemode.empty())
677 h_pdf_pagemode = pdfpagemode;
678 string pdftitle = process_keyval_opt(options, "pdftitle");
679 if (!pdftitle.empty()) {
680 h_pdf_title = remove_braces(pdftitle);
682 string pdfauthor = process_keyval_opt(options, "pdfauthor");
683 if (!pdfauthor.empty()) {
684 h_pdf_author = remove_braces(pdfauthor);
686 string pdfsubject = process_keyval_opt(options, "pdfsubject");
687 if (!pdfsubject.empty())
688 h_pdf_subject = remove_braces(pdfsubject);
689 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
690 if (!pdfkeywords.empty())
691 h_pdf_keywords = remove_braces(pdfkeywords);
692 if (!options.empty()) {
693 if (!h_pdf_quoted_options.empty())
694 h_pdf_quoted_options += ',';
695 h_pdf_quoted_options += join(options, ",");
701 void Preamble::handle_geometry(vector<string> & options)
703 h_use_geometry = "true";
704 vector<string>::iterator it;
706 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
707 h_paperorientation = "landscape";
711 // keyval version: "paper=letter"
712 string paper = process_keyval_opt(options, "paper");
714 h_papersize = paper + "paper";
715 // alternative version: "letterpaper"
716 handle_opt(options, known_paper_sizes, h_papersize);
717 delete_opt(options, known_paper_sizes);
719 char const * const * margin = known_paper_margins;
720 for (; *margin; ++margin) {
721 string value = process_keyval_opt(options, *margin);
722 if (!value.empty()) {
723 int k = margin - known_paper_margins;
724 string name = known_coded_paper_margins[k];
725 h_margins += '\\' + name + ' ' + value + '\n';
731 void Preamble::handle_package(Parser &p, string const & name,
732 string const & opts, bool in_lyx_preamble,
735 vector<string> options = split_options(opts);
736 add_package(name, options);
738 if (is_known(name, known_xetex_packages)) {
740 h_use_non_tex_fonts = true;
741 registerAutomaticallyLoadedPackage("fontspec");
742 if (h_inputencoding == "auto-legacy")
743 p.setEncoding("UTF-8");
747 if (is_known(name, known_roman_font_packages))
748 h_font_roman[0] = name;
750 if (name == "fourier") {
751 h_font_roman[0] = "utopia";
752 // when font uses real small capitals
753 if (opts == "expert")
757 if (name == "garamondx") {
758 h_font_roman[0] = "garamondx";
763 if (name == "libertine") {
764 h_font_roman[0] = "libertine";
765 // this automatically invokes biolinum
766 h_font_sans[0] = "biolinum";
767 // as well as libertineMono
768 h_font_typewriter[0] = "libertine-mono";
771 else if (opts == "lining")
772 h_font_osf = "false";
775 if (name == "libertineRoman" || name == "libertine-type1") {
776 h_font_roman[0] = "libertine";
777 // NOTE: contrary to libertine.sty, libertineRoman
778 // and libertine-type1 do not automatically invoke
779 // biolinum and libertineMono
780 if (opts == "lining")
781 h_font_osf = "false";
782 else if (opts == "osf")
786 if (name == "MinionPro") {
787 h_font_roman[0] = "minionpro";
788 vector<string> allopts = getVectorFromString(opts);
791 h_font_math[0] = "auto";
792 for (auto const & opt : allopts) {
794 h_font_osf = "false";
797 if (opt == "onlytext") {
798 h_font_math[0] = "default";
806 h_font_roman_opts = xopts;
810 if (name == "mathdesign") {
811 if (opts.find("charter") != string::npos)
812 h_font_roman[0] = "md-charter";
813 if (opts.find("garamond") != string::npos)
814 h_font_roman[0] = "md-garamond";
815 if (opts.find("utopia") != string::npos)
816 h_font_roman[0] = "md-utopia";
817 if (opts.find("expert") != string::npos) {
823 else if (name == "mathpazo")
824 h_font_roman[0] = "palatino";
826 else if (name == "mathptmx")
827 h_font_roman[0] = "times";
829 if (name == "crimson")
830 h_font_roman[0] = "cochineal";
832 if (name == "cochineal") {
833 h_font_roman[0] = "cochineal";
834 // cochineal can have several options, e.g. [proportional,osf]
835 string::size_type pos = opts.find("osf");
836 if (pos != string::npos)
840 if (name == "noto") {
841 // noto can have several options
843 h_font_roman[0] = "NotoSerif-TLF";
844 string::size_type pos = opts.find("rm");
845 if (pos != string::npos)
846 h_font_roman[0] = "NotoSerif-TLF";
847 pos = opts.find("sf");
848 if (pos != string::npos)
849 h_font_sans[0] = "NotoSans-TLF";
850 pos = opts.find("nott");
851 if (pos != string::npos) {
852 h_font_roman[0] = "NotoSerif-TLF";
853 h_font_sans[0] = "NotoSans-TLF";
855 // noto as typewriter is handled in handling of \ttdefault
856 // special cases are handled in handling of \rmdefault and \sfdefault
857 vector<string> allopts = getVectorFromString(opts);
859 for (auto const & opt : allopts) {
876 if (name == "paratype") {
877 // in this case all fonts are ParaType
878 h_font_roman[0] = "PTSerif-TLF";
879 h_font_sans[0] = "default";
880 h_font_typewriter[0] = "default";
883 if (name == "PTSerif")
884 h_font_roman[0] = "PTSerif-TLF";
886 if (name == "XCharter") {
887 h_font_roman[0] = "xcharter";
892 if (name == "plex-serif") {
894 h_font_roman[0] = "IBMPlexSerif";
895 else if (opts.find("thin") != string::npos)
896 h_font_roman[0] = "IBMPlexSerifThin";
897 else if (opts.find("extralight") != string::npos)
898 h_font_roman[0] = "IBMPlexSerifExtraLight";
899 else if (opts.find("light") != string::npos)
900 h_font_roman[0] = "IBMPlexSerifLight";
901 else if (opts.find("semibold") != string::npos)
902 h_font_roman[0] = "IBMPlexSerifSemibold";
903 vector<string> allopts = getVectorFromString(opts);
905 for (auto const & opt : allopts) {
908 if (opt == "extralight")
912 if (opt == "semibold")
919 h_font_roman_opts = xopts;
922 if (name == "noto-serif") {
923 h_font_roman[0] = "NotoSerifRegular";
925 if (opts.find("thin") != string::npos)
926 h_font_roman[0] = "NotoSerifThin";
927 else if (opts.find("medium") != string::npos)
928 h_font_roman[0] = "NotoSerifMedium";
929 else if (opts.find("extralight") != string::npos)
930 h_font_roman[0] = "NotoSerifExtralight";
931 else if (opts.find("light") != string::npos)
932 h_font_roman[0] = "NotoSerifLight";
934 vector<string> allopts = getVectorFromString(opts);
936 for (auto const & opt : allopts) {
937 if (opt == "regular")
941 if (opt == "extralight")
945 if (opt == "semibold")
952 h_font_roman_opts = xopts;
956 if (name == "sourceserifpro") {
957 h_font_roman[0] = "ADOBESourceSerifPro";
958 vector<string> allopts = getVectorFromString(opts);
960 for (auto const & opt : allopts) {
970 h_font_roman_opts = xopts;
975 if (is_known(name, known_sans_font_packages)) {
976 h_font_sans[0] = name;
977 if (options.size() >= 1) {
978 if (scale_as_percentage(opts, h_font_sf_scale[0]))
983 if (name == "biolinum" || name == "biolinum-type1") {
984 h_font_sans[0] = "biolinum";
985 // biolinum can have several options, e.g. [osf,scaled=0.97]
986 string::size_type pos = opts.find("osf");
987 if (pos != string::npos)
991 if (name == "PTSans") {
992 h_font_sans[0] = "PTSans-TLF";
993 if (options.size() >= 1) {
994 if (scale_as_percentage(opts, h_font_sf_scale[0]))
999 if (name == "plex-sans") {
1000 if (opts.find("condensed") != string::npos)
1001 h_font_sans[0] = "IBMPlexSansCondensed";
1002 else if (opts.find("thin") != string::npos)
1003 h_font_sans[0] = "IBMPlexSansThin";
1004 else if (opts.find("extralight") != string::npos)
1005 h_font_sans[0] = "IBMPlexSansExtraLight";
1006 else if (opts.find("light") != string::npos)
1007 h_font_sans[0] = "IBMPlexSansLight";
1008 else if (opts.find("semibold") != string::npos)
1009 h_font_sans[0] = "IBMPlexSansSemibold";
1011 h_font_sans[0] = "IBMPlexSans";
1012 vector<string> allopts = getVectorFromString(opts);
1014 for (auto const & opt : allopts) {
1017 if (opt == "extralight")
1021 if (opt == "semibold")
1023 if (prefixIs(opt, "scale=")) {
1024 scale_as_percentage(opt, h_font_sf_scale[0]);
1032 h_font_sans_opts = xopts;
1035 if (name == "noto-sans") {
1036 h_font_sans[0] = "NotoSansRegular";
1037 if (!opts.empty()) {
1038 if (opts.find("medium") != string::npos)
1039 h_font_sans[0] = "NotoSansMedium";
1040 else if (opts.find("thin") != string::npos)
1041 h_font_sans[0] = "NotoSansThin";
1042 else if (opts.find("extralight") != string::npos)
1043 h_font_sans[0] = "NotoSansExtralight";
1044 else if (opts.find("light") != string::npos)
1045 h_font_sans[0] = "NotoSansLight";
1047 vector<string> allopts = getVectorFromString(opts);
1049 for (auto const & opt : allopts) {
1050 if (opt == "regular")
1054 if (opt == "extralight")
1058 if (opt == "semibold")
1065 h_font_sans_opts = xopts;
1069 if (name == "sourcesanspro") {
1070 h_font_sans[0] = "ADOBESourceSansPro";
1071 vector<string> allopts = getVectorFromString(opts);
1073 for (auto const & opt : allopts) {
1074 if (prefixIs(opt, "scaled=")) {
1075 scale_as_percentage(opt, h_font_sf_scale[0]);
1079 h_font_osf = "true";
1087 h_font_sans_opts = xopts;
1092 if (is_known(name, known_typewriter_font_packages)) {
1093 // fourier can be set as roman font _only_
1094 // fourier as typewriter is handled in handling of \ttdefault
1095 if (name != "fourier") {
1096 h_font_typewriter[0] = name;
1097 if (options.size() >= 1) {
1098 if (scale_as_percentage(opts, h_font_tt_scale[0]))
1104 if (name == "libertineMono" || name == "libertineMono-type1")
1105 h_font_typewriter[0] = "libertine-mono";
1107 if (name == "PTMono") {
1108 h_font_typewriter[0] = "PTMono-TLF";
1109 if (options.size() >= 1) {
1110 if (scale_as_percentage(opts, h_font_tt_scale[0]))
1115 if (name == "plex-mono") {
1116 if (opts.find("thin") != string::npos)
1117 h_font_typewriter[0] = "IBMPlexMonoThin";
1118 else if (opts.find("extralight") != string::npos)
1119 h_font_typewriter[0] = "IBMPlexMonoExtraLight";
1120 else if (opts.find("light") != string::npos)
1121 h_font_typewriter[0] = "IBMPlexMonoLight";
1122 else if (opts.find("semibold") != string::npos)
1123 h_font_typewriter[0] = "IBMPlexMonoSemibold";
1125 h_font_typewriter[0] = "IBMPlexMono";
1126 vector<string> allopts = getVectorFromString(opts);
1128 for (auto const & opt : allopts) {
1131 if (opt == "extralight")
1135 if (opt == "semibold")
1137 if (prefixIs(opt, "scale=")) {
1138 scale_as_percentage(opt, h_font_tt_scale[0]);
1146 h_font_typewriter_opts = xopts;
1150 if (name == "noto-mono") {
1151 h_font_typewriter[0] = "NotoMonoRegular";
1152 vector<string> allopts = getVectorFromString(opts);
1154 for (auto const & opt : allopts) {
1155 if (opt == "regular")
1162 h_font_typewriter_opts = xopts;
1166 if (name == "sourcecodepro") {
1167 h_font_typewriter[0] = "ADOBESourceCodePro";
1168 vector<string> allopts = getVectorFromString(opts);
1170 for (auto const & opt : allopts) {
1171 if (prefixIs(opt, "scaled=")) {
1172 scale_as_percentage(opt, h_font_tt_scale[0]);
1176 h_font_osf = "true";
1184 h_font_typewriter_opts = xopts;
1188 // font uses old-style figure
1190 h_font_osf = "true";
1193 if (is_known(name, known_math_font_packages))
1194 h_font_math[0] = name;
1196 if (name == "newtxmath") {
1198 h_font_math[0] = "newtxmath";
1199 else if (opts == "garamondx")
1200 h_font_math[0] = "garamondx-ntxm";
1201 else if (opts == "libertine")
1202 h_font_math[0] = "libertine-ntxm";
1203 else if (opts == "minion")
1204 h_font_math[0] = "minion-ntxm";
1205 else if (opts == "cochineal")
1206 h_font_math[0] = "cochineal-ntxm";
1209 if (name == "iwona")
1211 h_font_math[0] = "iwona-math";
1213 if (name == "kurier")
1215 h_font_math[0] = "kurier-math";
1217 // after the detection and handling of special cases, we can remove the
1218 // fonts, otherwise they would appear in the preamble, see bug #7856
1219 if (is_known(name, known_roman_font_packages) || is_known(name, known_sans_font_packages)
1220 || is_known(name, known_typewriter_font_packages) || is_known(name, known_math_font_packages))
1222 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
1223 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
1224 name == "esint" || name == "mhchem" || name == "mathdots" ||
1225 name == "mathtools" || name == "stackrel" ||
1226 name == "stmaryrd" || name == "undertilde") {
1227 h_use_packages[name] = "2";
1228 registerAutomaticallyLoadedPackage(name);
1231 else if (name == "babel") {
1232 h_language_package = "default";
1233 // One might think we would have to do nothing if babel is loaded
1234 // without any options to prevent pollution of the preamble with this
1235 // babel call in every roundtrip.
1236 // But the user could have defined babel-specific things afterwards. So
1237 // we need to keep it in the preamble to prevent cases like bug #7861.
1238 if (!opts.empty()) {
1239 // check if more than one option was used - used later for inputenc
1240 if (options.begin() != options.end() - 1)
1241 one_language = false;
1242 // babel takes the last language of the option of its \usepackage
1243 // call as document language. If there is no such language option, the
1244 // last language in the documentclass options is used.
1245 handle_opt(options, known_languages, h_language);
1246 // translate the babel name to a LyX name
1247 h_language = babel2lyx(h_language);
1248 if (h_language == "japanese") {
1249 // For Japanese, the encoding isn't indicated in the source
1250 // file, and there's really not much we can do. We could
1251 // 1) offer a list of possible encodings to choose from, or
1252 // 2) determine the encoding of the file by inspecting it.
1253 // For the time being, we leave the encoding alone so that
1254 // we don't get iconv errors when making a wrong guess, and
1255 // we will output a note at the top of the document
1256 // explaining what to do.
1257 Encoding const * const enc = encodings.fromIconvName(
1258 p.getEncoding(), Encoding::japanese, false);
1260 h_inputencoding = enc->name();
1261 is_nonCJKJapanese = true;
1262 // in this case babel can be removed from the preamble
1263 registerAutomaticallyLoadedPackage("babel");
1265 // If babel is called with options, LyX puts them by default into the
1266 // document class options. This works for most languages, except
1267 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
1268 // perhaps in future others.
1269 // Therefore keep the babel call as it is as the user might have
1271 string const babelcall = "\\usepackage[" + opts + "]{babel}\n";
1272 if (!contains(h_preamble.str(), babelcall))
1273 h_preamble << babelcall;
1275 delete_opt(options, known_languages);
1277 if (!contains(h_preamble.str(), "\\usepackage{babel}\n"))
1278 h_preamble << "\\usepackage{babel}\n";
1279 explicit_babel = true;
1283 else if (name == "polyglossia") {
1284 h_language_package = "default";
1285 h_default_output_format = "pdf4";
1286 h_use_non_tex_fonts = true;
1288 registerAutomaticallyLoadedPackage("xunicode");
1289 if (h_inputencoding == "auto-legacy")
1290 p.setEncoding("UTF-8");
1293 else if (name == "CJK") {
1294 // set the encoding to "auto-legacy" because it might be set to "auto-legacy-plain" by the babel handling
1295 // and this would not be correct for CJK
1296 if (h_inputencoding == "auto-legacy-plain")
1297 h_inputencoding = "auto-legacy";
1298 registerAutomaticallyLoadedPackage("CJK");
1301 else if (name == "CJKutf8") {
1302 h_inputencoding = "utf8-cjk";
1303 p.setEncoding("UTF-8");
1304 registerAutomaticallyLoadedPackage("CJKutf8");
1307 else if (name == "fontenc") {
1308 h_fontencoding = getStringFromVector(options, ",");
1312 else if (name == "inputenc" || name == "luainputenc") {
1313 // h_inputencoding is only set when there is not more than one
1314 // inputenc option because otherwise h_inputencoding must be
1315 // set to "auto-legacy" (the default encodings of the document's languages)
1316 // Therefore check that exactly one option is passed to inputenc.
1317 // It is also only set when there is not more than one babel
1319 if (!options.empty()) {
1320 string const encoding = options.back();
1321 Encoding const * const enc = encodings.fromLaTeXName(
1322 encoding, Encoding::inputenc, true);
1324 if (!detectEncoding)
1325 cerr << "Unknown encoding " << encoding
1326 << ". Ignoring." << std::endl;
1328 if (!enc->unsafe() && options.size() == 1 && one_language == true)
1329 h_inputencoding = enc->name();
1330 p.setEncoding(enc->iconvName());
1336 else if (name == "srcltx") {
1337 h_output_sync = "1";
1338 if (!opts.empty()) {
1339 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
1342 h_output_sync_macro = "\\usepackage{srcltx}";
1345 else if (is_known(name, known_old_language_packages)) {
1346 // known language packages from the times before babel
1347 // if they are found and not also babel, they will be used as
1348 // custom language package
1349 h_language_package = "\\usepackage{" + name + "}";
1352 else if (name == "lyxskak") {
1353 // ignore this and its options
1354 const char * const o[] = {"ps", "mover", 0};
1355 delete_opt(options, o);
1358 else if (is_known(name, known_lyx_packages) && options.empty()) {
1359 if (name == "splitidx")
1360 h_use_indices = "true";
1361 else if (name == "minted")
1362 h_use_minted = true;
1363 else if (name == "refstyle")
1364 h_use_refstyle = true;
1365 else if (name == "prettyref")
1366 h_use_refstyle = false;
1367 if (!in_lyx_preamble) {
1368 h_preamble << package_beg_sep << name
1369 << package_mid_sep << "\\usepackage{"
1371 if (p.next_token().cat() == catNewline ||
1372 (p.next_token().cat() == catSpace &&
1373 p.next_next_token().cat() == catNewline))
1375 h_preamble << package_end_sep;
1379 else if (name == "geometry")
1380 handle_geometry(options);
1382 else if (name == "subfig")
1383 ; // ignore this FIXME: Use the package separator mechanism instead
1385 else if (char const * const * where = is_known(name, known_languages))
1386 h_language = known_coded_languages[where - known_languages];
1388 else if (name == "natbib") {
1389 h_biblio_style = "plainnat";
1390 h_cite_engine = "natbib";
1391 h_cite_engine_type = "authoryear";
1392 vector<string>::iterator it =
1393 find(options.begin(), options.end(), "authoryear");
1394 if (it != options.end())
1397 it = find(options.begin(), options.end(), "numbers");
1398 if (it != options.end()) {
1399 h_cite_engine_type = "numerical";
1403 if (!options.empty())
1404 h_biblio_options = join(options, ",");
1407 else if (name == "biblatex") {
1408 h_biblio_style = "plainnat";
1409 h_cite_engine = "biblatex";
1410 h_cite_engine_type = "authoryear";
1412 vector<string>::iterator it =
1413 find(options.begin(), options.end(), "natbib");
1414 if (it != options.end()) {
1416 h_cite_engine = "biblatex-natbib";
1418 opt = process_keyval_opt(options, "natbib");
1420 h_cite_engine = "biblatex-natbib";
1422 opt = process_keyval_opt(options, "style");
1424 h_biblatex_citestyle = opt;
1425 h_biblatex_bibstyle = opt;
1427 opt = process_keyval_opt(options, "citestyle");
1429 h_biblatex_citestyle = opt;
1430 opt = process_keyval_opt(options, "bibstyle");
1432 h_biblatex_bibstyle = opt;
1434 opt = process_keyval_opt(options, "refsection");
1436 if (opt == "none" || opt == "part"
1437 || opt == "chapter" || opt == "section"
1438 || opt == "subsection")
1441 cerr << "Ignoring unkown refesection value '"
1444 opt = process_keyval_opt(options, "bibencoding");
1447 if (!options.empty()) {
1448 h_biblio_options = join(options, ",");
1453 else if (name == "jurabib") {
1454 h_biblio_style = "jurabib";
1455 h_cite_engine = "jurabib";
1456 h_cite_engine_type = "authoryear";
1457 if (!options.empty())
1458 h_biblio_options = join(options, ",");
1461 else if (name == "bibtopic")
1462 h_use_bibtopic = "true";
1464 else if (name == "chapterbib")
1465 h_multibib = "child";
1467 else if (name == "hyperref")
1468 handle_hyperref(options);
1470 else if (name == "algorithm2e") {
1471 // Load "algorithm2e" module
1472 addModule("algorithm2e");
1473 // Add the package options to the global document options
1474 if (!options.empty()) {
1475 if (h_options.empty())
1476 h_options = join(options, ",");
1478 h_options += ',' + join(options, ",");
1481 else if (name == "microtype") {
1482 //we internally support only microtype without params
1483 if (options.empty())
1484 h_use_microtype = "true";
1486 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1489 else if (name == "lineno") {
1490 h_use_lineno = "true";
1491 if (!options.empty()) {
1492 h_lineno_options = join(options, ",");
1497 else if (!in_lyx_preamble) {
1498 if (options.empty())
1499 h_preamble << "\\usepackage{" << name << '}';
1501 h_preamble << "\\usepackage[" << opts << "]{"
1505 if (p.next_token().cat() == catNewline ||
1506 (p.next_token().cat() == catSpace &&
1507 p.next_next_token().cat() == catNewline))
1511 // We need to do something with the options...
1512 if (!options.empty() && !detectEncoding)
1513 cerr << "Ignoring options '" << join(options, ",")
1514 << "' of package " << name << '.' << endl;
1516 // remove the whitespace
1521 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1524 Token t = p.get_token();
1525 if (t.cat() == catEscape &&
1526 is_known(t.cs(), known_if_commands))
1527 handle_if(p, in_lyx_preamble);
1529 if (!in_lyx_preamble)
1530 h_preamble << t.asInput();
1531 if (t.cat() == catEscape && t.cs() == "fi")
1538 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1540 if (contains(h_float_placement, "H"))
1541 registerAutomaticallyLoadedPackage("float");
1542 if (h_spacing != "single" && h_spacing != "default")
1543 registerAutomaticallyLoadedPackage("setspace");
1544 if (h_use_packages["amsmath"] == "2") {
1545 // amsbsy and amstext are already provided by amsmath
1546 registerAutomaticallyLoadedPackage("amsbsy");
1547 registerAutomaticallyLoadedPackage("amstext");
1550 // output the LyX file settings
1551 // Important: Keep the version formatting in sync with LyX and
1552 // lyx2lyx (bug 7951)
1553 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1554 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1555 << lyx_version_minor << '\n'
1556 << "\\lyxformat " << LYX_FORMAT << '\n'
1557 << "\\begin_document\n"
1558 << "\\begin_header\n"
1559 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1560 << "\\origin " << origin << "\n"
1561 << "\\textclass " << h_textclass << "\n";
1562 string const raw = subdoc ? empty_string() : h_preamble.str();
1564 os << "\\begin_preamble\n";
1565 for (string::size_type i = 0; i < raw.size(); ++i) {
1566 if (raw[i] == package_beg_sep) {
1567 // Here follows some package loading code that
1568 // must be skipped if the package is loaded
1570 string::size_type j = raw.find(package_mid_sep, i);
1571 if (j == string::npos)
1573 string::size_type k = raw.find(package_end_sep, j);
1574 if (k == string::npos)
1576 string const package = raw.substr(i + 1, j - i - 1);
1577 string const replacement = raw.substr(j + 1, k - j - 1);
1578 if (auto_packages.find(package) == auto_packages.end())
1584 os << "\n\\end_preamble\n";
1586 if (!h_options.empty())
1587 os << "\\options " << h_options << "\n";
1588 os << "\\use_default_options " << h_use_default_options << "\n";
1589 if (!used_modules.empty()) {
1590 os << "\\begin_modules\n";
1591 vector<string>::const_iterator const end = used_modules.end();
1592 vector<string>::const_iterator it = used_modules.begin();
1593 for (; it != end; ++it)
1595 os << "\\end_modules\n";
1597 if (!h_includeonlys.empty()) {
1598 os << "\\begin_includeonly\n";
1599 for (auto const & iofile : h_includeonlys)
1600 os << iofile << '\n';
1601 os << "\\end_includeonly\n";
1603 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1604 << "\\language " << h_language << "\n"
1605 << "\\language_package " << h_language_package << "\n"
1606 << "\\inputencoding " << h_inputencoding << "\n"
1607 << "\\fontencoding " << h_fontencoding << "\n"
1608 << "\\font_roman \"" << h_font_roman[0]
1609 << "\" \"" << h_font_roman[1] << "\"\n"
1610 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1611 << "\\font_typewriter \"" << h_font_typewriter[0]
1612 << "\" \"" << h_font_typewriter[1] << "\"\n"
1613 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1614 << "\\font_default_family " << h_font_default_family << "\n"
1615 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1616 << "\\font_sc " << h_font_sc << "\n"
1617 << "\\font_osf " << h_font_osf << "\n";
1618 if (!h_font_roman_opts.empty())
1619 os << "\\font_roman_opts \"" << h_font_roman_opts << "\"" << '\n';
1620 os << "\\font_sf_scale " << h_font_sf_scale[0]
1621 << ' ' << h_font_sf_scale[1] << '\n';
1622 if (!h_font_sans_opts.empty())
1623 os << "\\font_sans_opts \"" << h_font_sans_opts << "\"" << '\n';
1624 os << "\\font_tt_scale " << h_font_tt_scale[0]
1625 << ' ' << h_font_tt_scale[1] << '\n';
1626 if (!h_font_cjk.empty())
1627 os << "\\font_cjk " << h_font_cjk << '\n';
1628 if (!h_font_typewriter_opts.empty())
1629 os << "\\font_typewriter_opts \"" << h_font_typewriter_opts << "\"" << '\n';
1630 os << "\\use_microtype " << h_use_microtype << '\n'
1631 << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
1632 << "\\graphics " << h_graphics << '\n'
1633 << "\\default_output_format " << h_default_output_format << "\n"
1634 << "\\output_sync " << h_output_sync << "\n";
1635 if (h_output_sync == "1")
1636 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1637 os << "\\bibtex_command " << h_bibtex_command << "\n"
1638 << "\\index_command " << h_index_command << "\n";
1639 if (!h_float_placement.empty())
1640 os << "\\float_placement " << h_float_placement << "\n";
1641 os << "\\paperfontsize " << h_paperfontsize << "\n"
1642 << "\\spacing " << h_spacing << "\n"
1643 << "\\use_hyperref " << h_use_hyperref << '\n';
1644 if (h_use_hyperref == "true") {
1645 if (!h_pdf_title.empty())
1646 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1647 if (!h_pdf_author.empty())
1648 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1649 if (!h_pdf_subject.empty())
1650 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1651 if (!h_pdf_keywords.empty())
1652 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1653 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1654 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1655 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1656 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1657 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1658 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1659 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1660 "\\pdf_backref " << h_pdf_backref << "\n"
1661 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1662 if (!h_pdf_pagemode.empty())
1663 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1664 if (!h_pdf_quoted_options.empty())
1665 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1667 os << "\\papersize " << h_papersize << "\n"
1668 << "\\use_geometry " << h_use_geometry << '\n';
1669 for (map<string, string>::const_iterator it = h_use_packages.begin();
1670 it != h_use_packages.end(); ++it)
1671 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1672 os << "\\cite_engine " << h_cite_engine << '\n'
1673 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1674 << "\\biblio_style " << h_biblio_style << "\n"
1675 << "\\use_bibtopic " << h_use_bibtopic << "\n";
1676 if (!h_biblio_options.empty())
1677 os << "\\biblio_options " << h_biblio_options << "\n";
1678 if (!h_biblatex_bibstyle.empty())
1679 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
1680 if (!h_biblatex_citestyle.empty())
1681 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
1682 if (!h_multibib.empty())
1683 os << "\\multibib " << h_multibib << "\n";
1684 os << "\\use_indices " << h_use_indices << "\n"
1685 << "\\paperorientation " << h_paperorientation << '\n'
1686 << "\\suppress_date " << h_suppress_date << '\n'
1687 << "\\justification " << h_justification << '\n'
1688 << "\\use_refstyle " << h_use_refstyle << '\n'
1689 << "\\use_minted " << h_use_minted << '\n'
1690 << "\\use_lineno " << h_use_lineno << '\n';
1691 if (!h_lineno_options.empty())
1692 os << "\\lineno_options " << h_lineno_options << '\n';
1693 if (!h_fontcolor.empty())
1694 os << "\\fontcolor " << h_fontcolor << '\n';
1695 if (!h_notefontcolor.empty())
1696 os << "\\notefontcolor " << h_notefontcolor << '\n';
1697 if (!h_backgroundcolor.empty())
1698 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1699 if (!h_boxbgcolor.empty())
1700 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1701 if (index_number != 0)
1702 for (int i = 0; i < index_number; i++) {
1703 os << "\\index " << h_index[i] << '\n'
1704 << "\\shortcut " << h_shortcut[i] << '\n'
1705 << "\\color " << h_color << '\n'
1709 os << "\\index " << h_index[0] << '\n'
1710 << "\\shortcut " << h_shortcut[0] << '\n'
1711 << "\\color " << h_color << '\n'
1715 << "\\secnumdepth " << h_secnumdepth << "\n"
1716 << "\\tocdepth " << h_tocdepth << "\n"
1717 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1718 if (h_paragraph_separation == "skip")
1719 os << "\\defskip " << h_defskip << "\n";
1721 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1722 os << "\\is_math_indent " << h_is_mathindent << "\n";
1723 if (!h_mathindentation.empty())
1724 os << "\\math_indentation " << h_mathindentation << "\n";
1725 os << "\\math_numbering_side " << h_math_numbering_side << "\n";
1726 os << "\\quotes_style " << h_quotes_style << "\n"
1727 << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
1728 << "\\papercolumns " << h_papercolumns << "\n"
1729 << "\\papersides " << h_papersides << "\n"
1730 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1731 if (!h_listings_params.empty())
1732 os << "\\listings_params " << h_listings_params << "\n";
1733 os << "\\tracking_changes " << h_tracking_changes << "\n"
1734 << "\\output_changes " << h_output_changes << "\n"
1735 << "\\html_math_output " << h_html_math_output << "\n"
1736 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1737 << "\\html_be_strict " << h_html_be_strict << "\n"
1739 << "\\end_header\n\n"
1740 << "\\begin_body\n";
1745 void Preamble::parse(Parser & p, string const & forceclass,
1746 TeX2LyXDocClass & tc)
1748 // initialize fixed types
1749 special_columns_['D'] = 3;
1750 parse(p, forceclass, false, tc);
1754 void Preamble::parse(Parser & p, string const & forceclass,
1755 bool detectEncoding, TeX2LyXDocClass & tc)
1757 bool is_full_document = false;
1758 bool is_lyx_file = false;
1759 bool in_lyx_preamble = false;
1761 // determine whether this is a full document or a fragment for inclusion
1763 Token const & t = p.get_token();
1765 if (t.cat() == catEscape && t.cs() == "documentclass") {
1766 is_full_document = true;
1772 if (detectEncoding && !is_full_document)
1775 while (is_full_document && p.good()) {
1776 if (detectEncoding && h_inputencoding != "auto-legacy" &&
1777 h_inputencoding != "auto-legacy-plain")
1780 Token const & t = p.get_token();
1783 if (!detectEncoding)
1784 cerr << "t: " << t << '\n';
1790 if (!in_lyx_preamble &&
1791 (t.cat() == catLetter ||
1792 t.cat() == catSuper ||
1793 t.cat() == catSub ||
1794 t.cat() == catOther ||
1795 t.cat() == catMath ||
1796 t.cat() == catActive ||
1797 t.cat() == catBegin ||
1798 t.cat() == catEnd ||
1799 t.cat() == catAlign ||
1800 t.cat() == catParameter)) {
1801 h_preamble << t.cs();
1805 if (!in_lyx_preamble &&
1806 (t.cat() == catSpace || t.cat() == catNewline)) {
1807 h_preamble << t.asInput();
1811 if (t.cat() == catComment) {
1812 static regex const islyxfile("%% LyX .* created this file");
1813 static regex const usercommands("User specified LaTeX commands");
1815 string const comment = t.asInput();
1817 // magically switch encoding default if it looks like XeLaTeX
1818 static string const magicXeLaTeX =
1819 "% This document must be compiled with XeLaTeX ";
1820 if (comment.size() > magicXeLaTeX.size()
1821 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1822 && h_inputencoding == "auto-legacy") {
1823 if (!detectEncoding)
1824 cerr << "XeLaTeX comment found, switching to UTF8\n";
1825 h_inputencoding = "utf8";
1828 if (regex_search(comment, sub, islyxfile)) {
1830 in_lyx_preamble = true;
1831 } else if (is_lyx_file
1832 && regex_search(comment, sub, usercommands))
1833 in_lyx_preamble = false;
1834 else if (!in_lyx_preamble)
1835 h_preamble << t.asInput();
1839 if (t.cs() == "PassOptionsToPackage") {
1840 string const poptions = p.getArg('{', '}');
1841 string const package = p.verbatim_item();
1842 extra_package_options_.insert(make_pair(package, poptions));
1846 if (t.cs() == "pagestyle") {
1847 h_paperpagestyle = p.verbatim_item();
1851 if (t.cs() == "setdefaultlanguage") {
1853 // We don't yet care about non-language variant options
1854 // because LyX doesn't support this yet, see bug #8214
1856 string langopts = p.getOpt();
1857 // check if the option contains a variant, if yes, extract it
1858 string::size_type pos_var = langopts.find("variant");
1859 string::size_type i = langopts.find(',', pos_var);
1860 string::size_type k = langopts.find('=', pos_var);
1861 if (pos_var != string::npos){
1863 if (i == string::npos)
1864 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1866 variant = langopts.substr(k + 1, i - k - 1);
1867 h_language = variant;
1871 h_language = p.verbatim_item();
1872 //finally translate the poyglossia name to a LyX name
1873 h_language = polyglossia2lyx(h_language);
1877 if (t.cs() == "setotherlanguage") {
1878 // We don't yet care about the option because LyX doesn't
1879 // support this yet, see bug #8214
1880 p.hasOpt() ? p.getOpt() : string();
1885 if (t.cs() == "setmainfont") {
1886 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
1887 h_font_roman[1] = p.getArg('{', '}');
1888 if (!fontopts.empty()) {
1889 vector<string> opts = getVectorFromString(fontopts);
1891 for (auto const & opt : opts) {
1892 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
1895 if (!fontopts.empty())
1899 h_font_roman_opts = fontopts;
1904 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1905 // LyX currently only supports the scale option
1906 string scale, fontopts;
1908 fontopts = p.getArg('[', ']');
1909 if (!fontopts.empty()) {
1910 vector<string> opts = getVectorFromString(fontopts);
1912 for (auto const & opt : opts) {
1913 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
1916 if (prefixIs(opt, "Scale=")) {
1917 scale_as_percentage(opt, scale);
1920 if (!fontopts.empty())
1926 if (t.cs() == "setsansfont") {
1928 h_font_sf_scale[1] = scale;
1929 h_font_sans[1] = p.getArg('{', '}');
1930 if (!fontopts.empty())
1931 h_font_sans_opts = fontopts;
1934 h_font_tt_scale[1] = scale;
1935 h_font_typewriter[1] = p.getArg('{', '}');
1936 if (!fontopts.empty())
1937 h_font_typewriter_opts = fontopts;
1942 if (t.cs() == "babelfont") {
1944 h_use_non_tex_fonts = true;
1945 h_language_package = "babel";
1946 if (h_inputencoding == "auto-legacy")
1947 p.setEncoding("UTF-8");
1948 // we don't care about the lang option
1949 string const lang = p.hasOpt() ? p.getArg('[', ']') : string();
1950 string const family = p.getArg('{', '}');
1951 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
1952 string const fontname = p.getArg('{', '}');
1953 if (lang.empty() && family == "rm") {
1954 h_font_roman[1] = fontname;
1955 if (!fontopts.empty()) {
1956 vector<string> opts = getVectorFromString(fontopts);
1958 for (auto const & opt : opts) {
1959 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
1962 if (!fontopts.empty())
1966 h_font_roman_opts = fontopts;
1969 } else if (lang.empty() && (family == "sf" || family == "tt")) {
1970 // LyX currently only supports the scale option
1972 if (!fontopts.empty()) {
1973 vector<string> opts = getVectorFromString(fontopts);
1975 for (auto const & opt : opts) {
1976 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
1979 if (prefixIs(opt, "Scale=")) {
1980 scale_as_percentage(opt, scale);
1983 if (!fontopts.empty())
1988 if (family == "sf") {
1990 h_font_sf_scale[1] = scale;
1991 h_font_sans[1] = fontname;
1992 if (!fontopts.empty())
1993 h_font_sans_opts = fontopts;
1996 h_font_tt_scale[1] = scale;
1997 h_font_typewriter[1] = fontname;
1998 if (!fontopts.empty())
1999 h_font_typewriter_opts = fontopts;
2003 // not rm, sf or tt or lang specific
2004 h_preamble << '\\' << t.cs();
2006 h_preamble << '[' << lang << ']';
2007 h_preamble << '{' << family << '}';
2008 if (!fontopts.empty())
2009 h_preamble << '[' << fontopts << ']';
2010 h_preamble << '{' << fontname << '}' << '\n';
2015 if (t.cs() == "date") {
2016 string argument = p.getArg('{', '}');
2017 if (argument.empty())
2018 h_suppress_date = "true";
2020 h_preamble << t.asInput() << '{' << argument << '}';
2024 if (t.cs() == "color") {
2025 string const space =
2026 (p.hasOpt() ? p.getOpt() : string());
2027 string argument = p.getArg('{', '}');
2028 // check the case that a standard color is used
2029 if (space.empty() && is_known(argument, known_basic_colors)) {
2030 h_fontcolor = rgbcolor2code(argument);
2031 registerAutomaticallyLoadedPackage("color");
2032 } else if (space.empty() && argument == "document_fontcolor")
2033 registerAutomaticallyLoadedPackage("color");
2034 // check the case that LyX's document_fontcolor is defined
2035 // but not used for \color
2037 h_preamble << t.asInput();
2039 h_preamble << space;
2040 h_preamble << '{' << argument << '}';
2041 // the color might already be set because \definecolor
2042 // is parsed before this
2048 if (t.cs() == "pagecolor") {
2049 string argument = p.getArg('{', '}');
2050 // check the case that a standard color is used
2051 if (is_known(argument, known_basic_colors)) {
2052 h_backgroundcolor = rgbcolor2code(argument);
2053 } else if (argument == "page_backgroundcolor")
2054 registerAutomaticallyLoadedPackage("color");
2055 // check the case that LyX's page_backgroundcolor is defined
2056 // but not used for \pagecolor
2058 h_preamble << t.asInput() << '{' << argument << '}';
2059 // the color might already be set because \definecolor
2060 // is parsed before this
2061 h_backgroundcolor = "";
2066 if (t.cs() == "makeatletter") {
2067 // LyX takes care of this
2068 p.setCatcode('@', catLetter);
2072 if (t.cs() == "makeatother") {
2073 // LyX takes care of this
2074 p.setCatcode('@', catOther);
2078 if (t.cs() == "makeindex") {
2079 // LyX will re-add this if a print index command is found
2084 if (t.cs() == "newindex") {
2085 string const indexname = p.getArg('[', ']');
2086 string const shortcut = p.verbatim_item();
2087 if (!indexname.empty())
2088 h_index[index_number] = indexname;
2090 h_index[index_number] = shortcut;
2091 h_shortcut[index_number] = shortcut;
2097 if (t.cs() == "addbibresource") {
2098 string const options = p.getArg('[', ']');
2099 string const arg = removeExtension(p.getArg('{', '}'));
2100 if (!options.empty()) {
2101 // check if the option contains a bibencoding, if yes, extract it
2102 string::size_type pos = options.find("bibencoding=");
2104 if (pos != string::npos) {
2105 string::size_type i = options.find(',', pos);
2106 if (i == string::npos)
2107 encoding = options.substr(pos + 1);
2109 encoding = options.substr(pos, i - pos);
2110 pos = encoding.find('=');
2111 if (pos == string::npos)
2114 encoding = encoding.substr(pos + 1);
2116 if (!encoding.empty())
2117 biblatex_encodings.push_back(normalize_filename(arg) + ' ' + encoding);
2119 biblatex_bibliographies.push_back(arg);
2123 if (t.cs() == "bibliography") {
2124 vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
2125 biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
2129 if (t.cs() == "RS@ifundefined") {
2130 string const name = p.verbatim_item();
2131 string const body1 = p.verbatim_item();
2132 string const body2 = p.verbatim_item();
2133 // only non-lyxspecific stuff
2134 if (in_lyx_preamble &&
2135 (name == "subsecref" || name == "thmref" || name == "lemref"))
2139 ss << '\\' << t.cs();
2140 ss << '{' << name << '}'
2141 << '{' << body1 << '}'
2142 << '{' << body2 << '}';
2143 h_preamble << ss.str();
2148 if (t.cs() == "AtBeginDocument") {
2149 string const name = p.verbatim_item();
2150 // only non-lyxspecific stuff
2151 if (in_lyx_preamble &&
2152 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
2153 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
2154 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
2155 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
2156 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
2157 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
2158 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
2159 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
2160 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
2161 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
2162 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
2163 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
2164 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
2165 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
2166 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
2170 ss << '\\' << t.cs();
2171 ss << '{' << name << '}';
2172 h_preamble << ss.str();
2177 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
2178 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
2179 || t.cs() == "providecommand" || t.cs() == "providecommandx"
2180 || t.cs() == "DeclareRobustCommand"
2181 || t.cs() == "DeclareRobustCommandx"
2182 || t.cs() == "ProvideTextCommandDefault"
2183 || t.cs() == "DeclareMathAccent") {
2185 if (p.next_token().character() == '*') {
2189 string const name = p.verbatim_item();
2190 string const opt1 = p.getFullOpt();
2191 string const opt2 = p.getFullOpt();
2192 string const body = p.verbatim_item();
2193 // store the in_lyx_preamble setting
2194 bool const was_in_lyx_preamble = in_lyx_preamble;
2196 if (name == "\\rmdefault")
2197 if (is_known(body, known_roman_font_packages)) {
2198 h_font_roman[0] = body;
2200 in_lyx_preamble = true;
2202 if (name == "\\sfdefault")
2203 if (is_known(body, known_sans_font_packages)) {
2204 h_font_sans[0] = body;
2206 in_lyx_preamble = true;
2208 if (name == "\\ttdefault")
2209 if (is_known(body, known_typewriter_font_packages)) {
2210 h_font_typewriter[0] = body;
2212 in_lyx_preamble = true;
2214 if (name == "\\familydefault") {
2215 string family = body;
2216 // remove leading "\"
2217 h_font_default_family = family.erase(0,1);
2219 in_lyx_preamble = true;
2222 // remove LyX-specific definitions that are re-added by LyX
2224 // \lyxline is an ancient command that is converted by tex2lyx into
2225 // a \rule therefore remove its preamble code
2226 if (name == "\\lyxdot" || name == "\\lyxarrow"
2227 || name == "\\lyxline" || name == "\\LyX") {
2229 in_lyx_preamble = true;
2232 // Add the command to the known commands
2233 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
2235 // only non-lyxspecific stuff
2236 if (!in_lyx_preamble) {
2238 ss << '\\' << t.cs();
2241 ss << '{' << name << '}' << opt1 << opt2
2242 << '{' << body << "}";
2243 if (prefixIs(t.cs(), "renew") || !contains(h_preamble.str(), ss.str()))
2244 h_preamble << ss.str();
2246 ostream & out = in_preamble ? h_preamble : os;
2247 out << "\\" << t.cs() << "{" << name << "}"
2248 << opts << "{" << body << "}";
2251 // restore the in_lyx_preamble setting
2252 in_lyx_preamble = was_in_lyx_preamble;
2256 if (t.cs() == "documentclass") {
2257 vector<string>::iterator it;
2258 vector<string> opts = split_options(p.getArg('[', ']'));
2259 handle_opt(opts, known_fontsizes, h_paperfontsize);
2260 delete_opt(opts, known_fontsizes);
2261 // delete "pt" at the end
2262 string::size_type i = h_paperfontsize.find("pt");
2263 if (i != string::npos)
2264 h_paperfontsize.erase(i);
2265 // The documentclass options are always parsed before the options
2266 // of the babel call so that a language cannot overwrite the babel
2268 handle_opt(opts, known_languages, h_language);
2269 delete_opt(opts, known_languages);
2272 if ((it = find(opts.begin(), opts.end(), "fleqn"))
2274 h_is_mathindent = "1";
2277 // formula numbering side
2278 if ((it = find(opts.begin(), opts.end(), "leqno"))
2280 h_math_numbering_side = "left";
2283 else if ((it = find(opts.begin(), opts.end(), "reqno"))
2285 h_math_numbering_side = "right";
2289 // paper orientation
2290 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
2291 h_paperorientation = "landscape";
2295 if ((it = find(opts.begin(), opts.end(), "oneside"))
2300 if ((it = find(opts.begin(), opts.end(), "twoside"))
2306 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
2308 h_papercolumns = "1";
2311 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
2313 h_papercolumns = "2";
2317 // some size options are known to any document classes, other sizes
2318 // are handled by the \geometry command of the geometry package
2319 handle_opt(opts, known_class_paper_sizes, h_papersize);
2320 delete_opt(opts, known_class_paper_sizes);
2321 // the remaining options
2322 h_options = join(opts, ",");
2323 // FIXME This does not work for classes that have a
2324 // different name in LyX than in LaTeX
2325 h_textclass = p.getArg('{', '}');
2330 if (t.cs() == "usepackage") {
2331 string const options = p.getArg('[', ']');
2332 string const name = p.getArg('{', '}');
2333 vector<string> vecnames;
2334 split(name, vecnames, ',');
2335 vector<string>::const_iterator it = vecnames.begin();
2336 vector<string>::const_iterator end = vecnames.end();
2337 for (; it != end; ++it)
2338 handle_package(p, trimSpaceAndEol(*it), options,
2339 in_lyx_preamble, detectEncoding);
2343 if (t.cs() == "inputencoding") {
2344 string const encoding = p.getArg('{','}');
2345 Encoding const * const enc = encodings.fromLaTeXName(
2346 encoding, Encoding::inputenc, true);
2348 if (!detectEncoding)
2349 cerr << "Unknown encoding " << encoding
2350 << ". Ignoring." << std::endl;
2353 h_inputencoding = enc->name();
2354 p.setEncoding(enc->iconvName());
2359 if (t.cs() == "newenvironment") {
2360 string const name = p.getArg('{', '}');
2361 string const opt1 = p.getFullOpt();
2362 string const opt2 = p.getFullOpt();
2363 string const beg = p.verbatim_item();
2364 string const end = p.verbatim_item();
2365 if (!in_lyx_preamble) {
2366 h_preamble << "\\newenvironment{" << name
2367 << '}' << opt1 << opt2 << '{'
2368 << beg << "}{" << end << '}';
2370 add_known_environment(name, opt1, !opt2.empty(),
2371 from_utf8(beg), from_utf8(end));
2375 if (t.cs() == "newtheorem") {
2377 if (p.next_token().character() == '*') {
2381 string const name = p.getArg('{', '}');
2382 string const opt1 = p.getFullOpt();
2383 string const opt2 = p.getFullOpt();
2384 string const body = p.verbatim_item();
2385 string const opt3 = p.getFullOpt();
2386 string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
2388 string const complete = cmd + "{" + name + '}' +
2389 opt1 + opt2 + '{' + body + '}' + opt3;
2391 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
2393 if (!in_lyx_preamble)
2394 h_preamble << complete;
2398 if (t.cs() == "def") {
2399 string name = p.get_token().cs();
2400 // In fact, name may be more than the name:
2401 // In the test case of bug 8116
2402 // name == "csname SF@gobble@opt \endcsname".
2403 // Therefore, we need to use asInput() instead of cs().
2404 while (p.next_token().cat() != catBegin)
2405 name += p.get_token().asInput();
2406 if (!in_lyx_preamble)
2407 h_preamble << "\\def\\" << name << '{'
2408 << p.verbatim_item() << "}";
2412 if (t.cs() == "newcolumntype") {
2413 string const name = p.getArg('{', '}');
2414 trimSpaceAndEol(name);
2416 string opts = p.getOpt();
2417 if (!opts.empty()) {
2418 istringstream is(string(opts, 1));
2421 special_columns_[name[0]] = nargs;
2422 h_preamble << "\\newcolumntype{" << name << "}";
2424 h_preamble << "[" << nargs << "]";
2425 h_preamble << "{" << p.verbatim_item() << "}";
2429 if (t.cs() == "setcounter") {
2430 string const name = p.getArg('{', '}');
2431 string const content = p.getArg('{', '}');
2432 if (name == "secnumdepth")
2433 h_secnumdepth = content;
2434 else if (name == "tocdepth")
2435 h_tocdepth = content;
2437 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
2441 if (t.cs() == "setlength") {
2442 string const name = p.verbatim_item();
2443 string const content = p.verbatim_item();
2444 // the paragraphs are only not indented when \parindent is set to zero
2445 if (name == "\\parindent" && content != "") {
2446 if (content[0] == '0')
2447 h_paragraph_separation = "skip";
2449 h_paragraph_indentation = translate_len(content);
2450 } else if (name == "\\parskip") {
2451 if (content == "\\smallskipamount")
2452 h_defskip = "smallskip";
2453 else if (content == "\\medskipamount")
2454 h_defskip = "medskip";
2455 else if (content == "\\bigskipamount")
2456 h_defskip = "bigskip";
2458 h_defskip = translate_len(content);
2459 } else if (name == "\\mathindent") {
2460 h_mathindentation = translate_len(content);
2462 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2466 if (t.cs() == "onehalfspacing") {
2467 h_spacing = "onehalf";
2471 if (t.cs() == "doublespacing") {
2472 h_spacing = "double";
2476 if (t.cs() == "setstretch") {
2477 h_spacing = "other " + p.verbatim_item();
2481 if (t.cs() == "synctex") {
2482 // the scheme is \synctex=value
2483 // where value can only be "1" or "-1"
2484 h_output_sync = "1";
2485 // there can be any character behind the value (e.g. a linebreak or a '\'
2486 // therefore we extract it char by char
2488 string value = p.get_token().asInput();
2490 value += p.get_token().asInput();
2491 h_output_sync_macro = "\\synctex=" + value;
2495 if (t.cs() == "begin") {
2496 string const name = p.getArg('{', '}');
2497 if (name == "document")
2499 h_preamble << "\\begin{" << name << "}";
2503 if (t.cs() == "geometry") {
2504 vector<string> opts = split_options(p.getArg('{', '}'));
2505 handle_geometry(opts);
2509 if (t.cs() == "definecolor") {
2510 string const color = p.getArg('{', '}');
2511 string const space = p.getArg('{', '}');
2512 string const value = p.getArg('{', '}');
2513 if (color == "document_fontcolor" && space == "rgb") {
2514 RGBColor c(RGBColorFromLaTeX(value));
2515 h_fontcolor = X11hexname(c);
2516 } else if (color == "note_fontcolor" && space == "rgb") {
2517 RGBColor c(RGBColorFromLaTeX(value));
2518 h_notefontcolor = X11hexname(c);
2519 } else if (color == "page_backgroundcolor" && space == "rgb") {
2520 RGBColor c(RGBColorFromLaTeX(value));
2521 h_backgroundcolor = X11hexname(c);
2522 } else if (color == "shadecolor" && space == "rgb") {
2523 RGBColor c(RGBColorFromLaTeX(value));
2524 h_boxbgcolor = X11hexname(c);
2526 h_preamble << "\\definecolor{" << color
2527 << "}{" << space << "}{" << value
2533 if (t.cs() == "bibliographystyle") {
2534 h_biblio_style = p.verbatim_item();
2538 if (t.cs() == "jurabibsetup") {
2539 // FIXME p.getArg('{', '}') is most probably wrong (it
2540 // does not handle nested braces).
2541 // Use p.verbatim_item() instead.
2542 vector<string> jurabibsetup =
2543 split_options(p.getArg('{', '}'));
2544 // add jurabibsetup to the jurabib package options
2545 add_package("jurabib", jurabibsetup);
2546 if (!jurabibsetup.empty()) {
2547 h_preamble << "\\jurabibsetup{"
2548 << join(jurabibsetup, ",") << '}';
2553 if (t.cs() == "hypersetup") {
2554 vector<string> hypersetup =
2555 split_options(p.verbatim_item());
2556 // add hypersetup to the hyperref package options
2557 handle_hyperref(hypersetup);
2558 if (!hypersetup.empty()) {
2559 h_preamble << "\\hypersetup{"
2560 << join(hypersetup, ",") << '}';
2565 if (t.cs() == "includeonly") {
2566 vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
2567 for (auto & iofile : includeonlys) {
2568 string filename(normalize_filename(iofile));
2569 string const path = getMasterFilePath(true);
2570 // We want to preserve relative/absolute filenames,
2571 // therefore path is only used for testing
2572 if (!makeAbsPath(filename, path).exists()) {
2573 // The file extension is probably missing.
2574 // Now try to find it out.
2575 string const tex_name =
2576 find_file(filename, path,
2577 known_tex_extensions);
2578 if (!tex_name.empty())
2579 filename = tex_name;
2582 if (makeAbsPath(filename, path).exists())
2583 fix_child_filename(filename);
2585 cerr << "Warning: Could not find included file '"
2586 << filename << "'." << endl;
2587 outname = changeExtension(filename, "lyx");
2588 h_includeonlys.push_back(outname);
2593 if (is_known(t.cs(), known_if_3arg_commands)) {
2594 // prevent misparsing of \usepackage if it is used
2595 // as an argument (see e.g. our own output of
2596 // \@ifundefined above)
2597 string const arg1 = p.verbatim_item();
2598 string const arg2 = p.verbatim_item();
2599 string const arg3 = p.verbatim_item();
2600 // test case \@ifundefined{date}{}{\date{}}
2601 if (t.cs() == "@ifundefined" && arg1 == "date" &&
2602 arg2.empty() && arg3 == "\\date{}") {
2603 h_suppress_date = "true";
2604 // older tex2lyx versions did output
2605 // \@ifundefined{definecolor}{\usepackage{color}}{}
2606 } else if (t.cs() == "@ifundefined" &&
2607 arg1 == "definecolor" &&
2608 arg2 == "\\usepackage{color}" &&
2610 if (!in_lyx_preamble)
2611 h_preamble << package_beg_sep
2614 << "\\@ifundefined{definecolor}{color}{}"
2617 //\@ifundefined{showcaptionsetup}{}{%
2618 // \PassOptionsToPackage{caption=false}{subfig}}
2619 // that LyX uses for subfloats
2620 } else if (t.cs() == "@ifundefined" &&
2621 arg1 == "showcaptionsetup" && arg2.empty()
2622 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
2624 } else if (!in_lyx_preamble) {
2625 h_preamble << t.asInput()
2626 << '{' << arg1 << '}'
2627 << '{' << arg2 << '}'
2628 << '{' << arg3 << '}';
2633 if (is_known(t.cs(), known_if_commands)) {
2634 // must not parse anything in conditional code, since
2635 // LyX would output the parsed contents unconditionally
2636 if (!in_lyx_preamble)
2637 h_preamble << t.asInput();
2638 handle_if(p, in_lyx_preamble);
2642 if (!t.cs().empty() && !in_lyx_preamble) {
2643 h_preamble << '\\' << t.cs();
2648 // remove the whitespace
2651 // Force textclass if the user wanted it
2652 if (!forceclass.empty())
2653 h_textclass = forceclass;
2654 tc.setName(h_textclass);
2655 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2656 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2659 if (h_papersides.empty()) {
2662 h_papersides = ss.str();
2665 // If the CJK package is used we cannot set the document language from
2666 // the babel options. Instead, we guess which language is used most
2667 // and set this one.
2668 default_language = h_language;
2669 if (is_full_document &&
2670 (auto_packages.find("CJK") != auto_packages.end() ||
2671 auto_packages.find("CJKutf8") != auto_packages.end())) {
2673 h_language = guessLanguage(p, default_language);
2675 if (explicit_babel && h_language != default_language) {
2676 // We set the document language to a CJK language,
2677 // but babel is explicitly called in the user preamble
2678 // without options. LyX will not add the default
2679 // language to the document options if it is either
2680 // english, or no text is set as default language.
2681 // Therefore we need to add a language option explicitly.
2682 // FIXME: It would be better to remove all babel calls
2683 // from the user preamble, but this is difficult
2684 // without re-introducing bug 7861.
2685 if (h_options.empty())
2686 h_options = lyx2babel(default_language);
2688 h_options += ',' + lyx2babel(default_language);
2692 // Finally, set the quote style.
2693 // LyX knows the following quotes styles:
2694 // british, cjk, cjkangle, danish, english, french, german,
2695 // polish, russian, swedish and swiss
2696 // conversion list taken from
2697 // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
2698 // (quotes for kazakh are unknown)
2700 if (is_known(h_language, known_british_quotes_languages))
2701 h_quotes_style = "british";
2703 else if (is_known(h_language, known_cjk_quotes_languages))
2704 h_quotes_style = "cjk";
2706 else if (is_known(h_language, known_cjkangle_quotes_languages))
2707 h_quotes_style = "cjkangle";
2709 else if (is_known(h_language, known_danish_quotes_languages))
2710 h_quotes_style = "danish";
2712 else if (is_known(h_language, known_french_quotes_languages))
2713 h_quotes_style = "french";
2715 else if (is_known(h_language, known_german_quotes_languages))
2716 h_quotes_style = "german";
2718 else if (is_known(h_language, known_polish_quotes_languages))
2719 h_quotes_style = "polish";
2721 else if (is_known(h_language, known_russian_quotes_languages))
2722 h_quotes_style = "russian";
2724 else if (is_known(h_language, known_swedish_quotes_languages))
2725 h_quotes_style = "swedish";
2727 else if (is_known(h_language, known_swiss_quotes_languages))
2728 h_quotes_style = "swiss";
2730 else if (is_known(h_language, known_english_quotes_languages))
2731 h_quotes_style = "english";
2735 string Preamble::parseEncoding(Parser & p, string const & forceclass)
2737 TeX2LyXDocClass dummy;
2738 parse(p, forceclass, true, dummy);
2739 if (h_inputencoding != "auto-legacy" && h_inputencoding != "auto-legacy-plain")
2740 return h_inputencoding;
2745 string babel2lyx(string const & language)
2747 char const * const * where = is_known(language, known_languages);
2749 return known_coded_languages[where - known_languages];
2754 string lyx2babel(string const & language)
2756 char const * const * where = is_known(language, known_coded_languages);
2758 return known_languages[where - known_coded_languages];
2763 string Preamble::polyglossia2lyx(string const & language)
2765 char const * const * where = is_known(language, polyglossia_languages);
2767 return coded_polyglossia_languages[where - polyglossia_languages];
2772 string rgbcolor2code(string const & name)
2774 char const * const * where = is_known(name, known_basic_colors);
2776 // "red", "green" etc
2777 return known_basic_color_codes[where - known_basic_colors];
2779 // "255,0,0", "0,255,0" etc
2780 RGBColor c(RGBColorFromLaTeX(name));
2781 return X11hexname(c);