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),
508 h_use_microtype("false")
512 h_biblio_style = "plain";
513 h_bibtex_command = "default";
514 h_cite_engine = "basic";
515 h_cite_engine_type = "default";
517 h_defskip = "medskip";
518 h_dynamic_quotes = false;
521 h_fontencoding = "default";
522 h_font_roman[0] = "default";
523 h_font_roman[1] = "default";
524 h_font_sans[0] = "default";
525 h_font_sans[1] = "default";
526 h_font_typewriter[0] = "default";
527 h_font_typewriter[1] = "default";
528 h_font_math[0] = "auto";
529 h_font_math[1] = "auto";
530 h_font_default_family = "default";
531 h_use_non_tex_fonts = false;
533 h_font_osf = "false";
534 h_font_sf_scale[0] = "100";
535 h_font_sf_scale[1] = "100";
536 h_font_tt_scale[0] = "100";
537 h_font_tt_scale[1] = "100";
538 // h_font_roman_opts;
540 // h_font_typewriter_opts;
542 h_is_mathindent = "0";
543 h_math_numbering_side = "default";
544 h_graphics = "default";
545 h_default_output_format = "default";
546 h_html_be_strict = "false";
547 h_html_css_as_file = "0";
548 h_html_math_output = "0";
549 h_index[0] = "Index";
550 h_index_command = "default";
551 h_inputencoding = "auto-legacy";
552 h_justification = "true";
553 h_language = "english";
554 h_language_package = "none";
556 h_maintain_unincluded_children = "false";
560 h_output_changes = "false";
562 //h_output_sync_macro
563 h_papercolumns = "1";
564 h_paperfontsize = "default";
565 h_paperorientation = "portrait";
566 h_paperpagestyle = "default";
568 h_papersize = "default";
569 h_paragraph_indentation = "default";
570 h_paragraph_separation = "indent";
575 h_pdf_bookmarks = "0";
576 h_pdf_bookmarksnumbered = "0";
577 h_pdf_bookmarksopen = "0";
578 h_pdf_bookmarksopenlevel = "1";
579 h_pdf_breaklinks = "0";
580 h_pdf_pdfborder = "0";
581 h_pdf_colorlinks = "0";
582 h_pdf_backref = "section";
583 h_pdf_pdfusetitle = "0";
585 //h_pdf_quoted_options;
586 h_quotes_style = "english";
588 h_shortcut[0] = "idx";
589 h_spacing = "single";
590 h_save_transient_properties = "true";
591 h_suppress_date = "false";
592 h_textclass = "article";
594 h_tracking_changes = "false";
595 h_use_bibtopic = "false";
596 h_use_dash_ligatures = "true";
597 h_use_indices = "false";
598 h_use_geometry = "false";
599 h_use_default_options = "false";
600 h_use_hyperref = "false";
601 h_use_microtype = "false";
602 h_use_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]);
1083 h_font_sans_opts = xopts;
1088 if (is_known(name, known_typewriter_font_packages)) {
1089 // fourier can be set as roman font _only_
1090 // fourier as typewriter is handled in handling of \ttdefault
1091 if (name != "fourier") {
1092 h_font_typewriter[0] = name;
1093 if (options.size() >= 1) {
1094 if (scale_as_percentage(opts, h_font_tt_scale[0]))
1100 if (name == "libertineMono" || name == "libertineMono-type1")
1101 h_font_typewriter[0] = "libertine-mono";
1103 if (name == "PTMono") {
1104 h_font_typewriter[0] = "PTMono-TLF";
1105 if (options.size() >= 1) {
1106 if (scale_as_percentage(opts, h_font_tt_scale[0]))
1111 if (name == "plex-mono") {
1112 if (opts.find("thin") != string::npos)
1113 h_font_typewriter[0] = "IBMPlexMonoThin";
1114 else if (opts.find("extralight") != string::npos)
1115 h_font_typewriter[0] = "IBMPlexMonoExtraLight";
1116 else if (opts.find("light") != string::npos)
1117 h_font_typewriter[0] = "IBMPlexMonoLight";
1118 else if (opts.find("semibold") != string::npos)
1119 h_font_typewriter[0] = "IBMPlexMonoSemibold";
1121 h_font_typewriter[0] = "IBMPlexMono";
1122 vector<string> allopts = getVectorFromString(opts);
1124 for (auto const & opt : allopts) {
1127 if (opt == "extralight")
1131 if (opt == "semibold")
1133 if (prefixIs(opt, "scale=")) {
1134 scale_as_percentage(opt, h_font_tt_scale[0]);
1142 h_font_typewriter_opts = xopts;
1146 if (name == "noto-mono") {
1147 h_font_typewriter[0] = "NotoMonoRegular";
1148 vector<string> allopts = getVectorFromString(opts);
1150 for (auto const & opt : allopts) {
1151 if (opt == "regular")
1158 h_font_typewriter_opts = xopts;
1162 if (name == "sourcecodepro") {
1163 h_font_typewriter[0] = "ADOBESourceCodePro";
1164 vector<string> allopts = getVectorFromString(opts);
1166 for (auto const & opt : allopts) {
1167 if (prefixIs(opt, "scaled=")) {
1168 scale_as_percentage(opt, h_font_tt_scale[0]);
1176 h_font_typewriter_opts = xopts;
1180 // font uses old-style figure
1182 h_font_osf = "true";
1185 if (is_known(name, known_math_font_packages))
1186 h_font_math[0] = name;
1188 if (name == "newtxmath") {
1190 h_font_math[0] = "newtxmath";
1191 else if (opts == "garamondx")
1192 h_font_math[0] = "garamondx-ntxm";
1193 else if (opts == "libertine")
1194 h_font_math[0] = "libertine-ntxm";
1195 else if (opts == "minion")
1196 h_font_math[0] = "minion-ntxm";
1197 else if (opts == "cochineal")
1198 h_font_math[0] = "cochineal-ntxm";
1201 if (name == "iwona")
1203 h_font_math[0] = "iwona-math";
1205 if (name == "kurier")
1207 h_font_math[0] = "kurier-math";
1209 // after the detection and handling of special cases, we can remove the
1210 // fonts, otherwise they would appear in the preamble, see bug #7856
1211 if (is_known(name, known_roman_font_packages) || is_known(name, known_sans_font_packages)
1212 || is_known(name, known_typewriter_font_packages) || is_known(name, known_math_font_packages))
1214 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
1215 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
1216 name == "esint" || name == "mhchem" || name == "mathdots" ||
1217 name == "mathtools" || name == "stackrel" ||
1218 name == "stmaryrd" || name == "undertilde") {
1219 h_use_packages[name] = "2";
1220 registerAutomaticallyLoadedPackage(name);
1223 else if (name == "babel") {
1224 h_language_package = "default";
1225 // One might think we would have to do nothing if babel is loaded
1226 // without any options to prevent pollution of the preamble with this
1227 // babel call in every roundtrip.
1228 // But the user could have defined babel-specific things afterwards. So
1229 // we need to keep it in the preamble to prevent cases like bug #7861.
1230 if (!opts.empty()) {
1231 // check if more than one option was used - used later for inputenc
1232 if (options.begin() != options.end() - 1)
1233 one_language = false;
1234 // babel takes the last language of the option of its \usepackage
1235 // call as document language. If there is no such language option, the
1236 // last language in the documentclass options is used.
1237 handle_opt(options, known_languages, h_language);
1238 // translate the babel name to a LyX name
1239 h_language = babel2lyx(h_language);
1240 if (h_language == "japanese") {
1241 // For Japanese, the encoding isn't indicated in the source
1242 // file, and there's really not much we can do. We could
1243 // 1) offer a list of possible encodings to choose from, or
1244 // 2) determine the encoding of the file by inspecting it.
1245 // For the time being, we leave the encoding alone so that
1246 // we don't get iconv errors when making a wrong guess, and
1247 // we will output a note at the top of the document
1248 // explaining what to do.
1249 Encoding const * const enc = encodings.fromIconvName(
1250 p.getEncoding(), Encoding::japanese, false);
1252 h_inputencoding = enc->name();
1253 is_nonCJKJapanese = true;
1254 // in this case babel can be removed from the preamble
1255 registerAutomaticallyLoadedPackage("babel");
1257 // If babel is called with options, LyX puts them by default into the
1258 // document class options. This works for most languages, except
1259 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
1260 // perhaps in future others.
1261 // Therefore keep the babel call as it is as the user might have
1263 string const babelcall = "\\usepackage[" + opts + "]{babel}\n";
1264 if (!contains(h_preamble.str(), babelcall))
1265 h_preamble << babelcall;
1267 delete_opt(options, known_languages);
1269 if (!contains(h_preamble.str(), "\\usepackage{babel}\n"))
1270 h_preamble << "\\usepackage{babel}\n";
1271 explicit_babel = true;
1275 else if (name == "polyglossia") {
1276 h_language_package = "default";
1277 h_default_output_format = "pdf4";
1278 h_use_non_tex_fonts = true;
1280 registerAutomaticallyLoadedPackage("xunicode");
1281 if (h_inputencoding == "auto-legacy")
1282 p.setEncoding("UTF-8");
1285 else if (name == "CJK") {
1286 // set the encoding to "auto-legacy" because it might be set to "auto-legacy-plain" by the babel handling
1287 // and this would not be correct for CJK
1288 if (h_inputencoding == "auto-legacy-plain")
1289 h_inputencoding = "auto-legacy";
1290 registerAutomaticallyLoadedPackage("CJK");
1293 else if (name == "CJKutf8") {
1294 h_inputencoding = "utf8-cjk";
1295 p.setEncoding("UTF-8");
1296 registerAutomaticallyLoadedPackage("CJKutf8");
1299 else if (name == "fontenc") {
1300 h_fontencoding = getStringFromVector(options, ",");
1304 else if (name == "inputenc" || name == "luainputenc") {
1305 // h_inputencoding is only set when there is not more than one
1306 // inputenc option because otherwise h_inputencoding must be
1307 // set to "auto-legacy" (the default encodings of the document's languages)
1308 // Therefore check that exactly one option is passed to inputenc.
1309 // It is also only set when there is not more than one babel
1311 if (!options.empty()) {
1312 string const encoding = options.back();
1313 Encoding const * const enc = encodings.fromLaTeXName(
1314 encoding, Encoding::inputenc, true);
1316 if (!detectEncoding)
1317 cerr << "Unknown encoding " << encoding
1318 << ". Ignoring." << std::endl;
1320 if (!enc->unsafe() && options.size() == 1 && one_language == true)
1321 h_inputencoding = enc->name();
1322 p.setEncoding(enc->iconvName());
1328 else if (name == "srcltx") {
1329 h_output_sync = "1";
1330 if (!opts.empty()) {
1331 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
1334 h_output_sync_macro = "\\usepackage{srcltx}";
1337 else if (is_known(name, known_old_language_packages)) {
1338 // known language packages from the times before babel
1339 // if they are found and not also babel, they will be used as
1340 // custom language package
1341 h_language_package = "\\usepackage{" + name + "}";
1344 else if (name == "lyxskak") {
1345 // ignore this and its options
1346 const char * const o[] = {"ps", "mover", 0};
1347 delete_opt(options, o);
1350 else if (is_known(name, known_lyx_packages) && options.empty()) {
1351 if (name == "splitidx")
1352 h_use_indices = "true";
1353 else if (name == "minted")
1354 h_use_minted = true;
1355 else if (name == "refstyle")
1356 h_use_refstyle = true;
1357 else if (name == "prettyref")
1358 h_use_refstyle = false;
1359 if (!in_lyx_preamble) {
1360 h_preamble << package_beg_sep << name
1361 << package_mid_sep << "\\usepackage{"
1363 if (p.next_token().cat() == catNewline ||
1364 (p.next_token().cat() == catSpace &&
1365 p.next_next_token().cat() == catNewline))
1367 h_preamble << package_end_sep;
1371 else if (name == "geometry")
1372 handle_geometry(options);
1374 else if (name == "subfig")
1375 ; // ignore this FIXME: Use the package separator mechanism instead
1377 else if (char const * const * where = is_known(name, known_languages))
1378 h_language = known_coded_languages[where - known_languages];
1380 else if (name == "natbib") {
1381 h_biblio_style = "plainnat";
1382 h_cite_engine = "natbib";
1383 h_cite_engine_type = "authoryear";
1384 vector<string>::iterator it =
1385 find(options.begin(), options.end(), "authoryear");
1386 if (it != options.end())
1389 it = find(options.begin(), options.end(), "numbers");
1390 if (it != options.end()) {
1391 h_cite_engine_type = "numerical";
1395 if (!options.empty())
1396 h_biblio_options = join(options, ",");
1399 else if (name == "biblatex") {
1400 h_biblio_style = "plainnat";
1401 h_cite_engine = "biblatex";
1402 h_cite_engine_type = "authoryear";
1404 vector<string>::iterator it =
1405 find(options.begin(), options.end(), "natbib");
1406 if (it != options.end()) {
1408 h_cite_engine = "biblatex-natbib";
1410 opt = process_keyval_opt(options, "natbib");
1412 h_cite_engine = "biblatex-natbib";
1414 opt = process_keyval_opt(options, "style");
1416 h_biblatex_citestyle = opt;
1417 h_biblatex_bibstyle = opt;
1419 opt = process_keyval_opt(options, "citestyle");
1421 h_biblatex_citestyle = opt;
1422 opt = process_keyval_opt(options, "bibstyle");
1424 h_biblatex_bibstyle = opt;
1426 opt = process_keyval_opt(options, "refsection");
1428 if (opt == "none" || opt == "part"
1429 || opt == "chapter" || opt == "section"
1430 || opt == "subsection")
1433 cerr << "Ignoring unkown refesection value '"
1436 opt = process_keyval_opt(options, "bibencoding");
1439 if (!options.empty()) {
1440 h_biblio_options = join(options, ",");
1445 else if (name == "jurabib") {
1446 h_biblio_style = "jurabib";
1447 h_cite_engine = "jurabib";
1448 h_cite_engine_type = "authoryear";
1449 if (!options.empty())
1450 h_biblio_options = join(options, ",");
1453 else if (name == "bibtopic")
1454 h_use_bibtopic = "true";
1456 else if (name == "chapterbib")
1457 h_multibib = "child";
1459 else if (name == "hyperref")
1460 handle_hyperref(options);
1462 else if (name == "algorithm2e") {
1463 // Load "algorithm2e" module
1464 addModule("algorithm2e");
1465 // Add the package options to the global document options
1466 if (!options.empty()) {
1467 if (h_options.empty())
1468 h_options = join(options, ",");
1470 h_options += ',' + join(options, ",");
1473 else if (name == "microtype") {
1474 //we internally support only microtype without params
1475 if (options.empty())
1476 h_use_microtype = "true";
1478 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1481 else if (!in_lyx_preamble) {
1482 if (options.empty())
1483 h_preamble << "\\usepackage{" << name << '}';
1485 h_preamble << "\\usepackage[" << opts << "]{"
1489 if (p.next_token().cat() == catNewline ||
1490 (p.next_token().cat() == catSpace &&
1491 p.next_next_token().cat() == catNewline))
1495 // We need to do something with the options...
1496 if (!options.empty() && !detectEncoding)
1497 cerr << "Ignoring options '" << join(options, ",")
1498 << "' of package " << name << '.' << endl;
1500 // remove the whitespace
1505 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1508 Token t = p.get_token();
1509 if (t.cat() == catEscape &&
1510 is_known(t.cs(), known_if_commands))
1511 handle_if(p, in_lyx_preamble);
1513 if (!in_lyx_preamble)
1514 h_preamble << t.asInput();
1515 if (t.cat() == catEscape && t.cs() == "fi")
1522 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1524 if (contains(h_float_placement, "H"))
1525 registerAutomaticallyLoadedPackage("float");
1526 if (h_spacing != "single" && h_spacing != "default")
1527 registerAutomaticallyLoadedPackage("setspace");
1528 if (h_use_packages["amsmath"] == "2") {
1529 // amsbsy and amstext are already provided by amsmath
1530 registerAutomaticallyLoadedPackage("amsbsy");
1531 registerAutomaticallyLoadedPackage("amstext");
1534 // output the LyX file settings
1535 // Important: Keep the version formatting in sync with LyX and
1536 // lyx2lyx (bug 7951)
1537 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1538 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1539 << lyx_version_minor << '\n'
1540 << "\\lyxformat " << LYX_FORMAT << '\n'
1541 << "\\begin_document\n"
1542 << "\\begin_header\n"
1543 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1544 << "\\origin " << origin << "\n"
1545 << "\\textclass " << h_textclass << "\n";
1546 string const raw = subdoc ? empty_string() : h_preamble.str();
1548 os << "\\begin_preamble\n";
1549 for (string::size_type i = 0; i < raw.size(); ++i) {
1550 if (raw[i] == package_beg_sep) {
1551 // Here follows some package loading code that
1552 // must be skipped if the package is loaded
1554 string::size_type j = raw.find(package_mid_sep, i);
1555 if (j == string::npos)
1557 string::size_type k = raw.find(package_end_sep, j);
1558 if (k == string::npos)
1560 string const package = raw.substr(i + 1, j - i - 1);
1561 string const replacement = raw.substr(j + 1, k - j - 1);
1562 if (auto_packages.find(package) == auto_packages.end())
1568 os << "\n\\end_preamble\n";
1570 if (!h_options.empty())
1571 os << "\\options " << h_options << "\n";
1572 os << "\\use_default_options " << h_use_default_options << "\n";
1573 if (!used_modules.empty()) {
1574 os << "\\begin_modules\n";
1575 vector<string>::const_iterator const end = used_modules.end();
1576 vector<string>::const_iterator it = used_modules.begin();
1577 for (; it != end; ++it)
1579 os << "\\end_modules\n";
1581 if (!h_includeonlys.empty()) {
1582 os << "\\begin_includeonly\n";
1583 for (auto const & iofile : h_includeonlys)
1584 os << iofile << '\n';
1585 os << "\\end_includeonly\n";
1587 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1588 << "\\language " << h_language << "\n"
1589 << "\\language_package " << h_language_package << "\n"
1590 << "\\inputencoding " << h_inputencoding << "\n"
1591 << "\\fontencoding " << h_fontencoding << "\n"
1592 << "\\font_roman \"" << h_font_roman[0]
1593 << "\" \"" << h_font_roman[1] << "\"\n"
1594 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1595 << "\\font_typewriter \"" << h_font_typewriter[0]
1596 << "\" \"" << h_font_typewriter[1] << "\"\n"
1597 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1598 << "\\font_default_family " << h_font_default_family << "\n"
1599 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1600 << "\\font_sc " << h_font_sc << "\n"
1601 << "\\font_osf " << h_font_osf << "\n";
1602 if (!h_font_roman_opts.empty())
1603 os << "\\font_roman_opts \"" << h_font_roman_opts << "\"" << '\n';
1604 os << "\\font_sf_scale " << h_font_sf_scale[0]
1605 << ' ' << h_font_sf_scale[1] << '\n';
1606 if (!h_font_sans_opts.empty())
1607 os << "\\font_sans_opts \"" << h_font_sans_opts << "\"" << '\n';
1608 os << "\\font_tt_scale " << h_font_tt_scale[0]
1609 << ' ' << h_font_tt_scale[1] << '\n';
1610 if (!h_font_cjk.empty())
1611 os << "\\font_cjk " << h_font_cjk << '\n';
1612 if (!h_font_typewriter_opts.empty())
1613 os << "\\font_typewriter_opts \"" << h_font_typewriter_opts << "\"" << '\n';
1614 os << "\\use_microtype " << h_use_microtype << '\n'
1615 << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
1616 << "\\graphics " << h_graphics << '\n'
1617 << "\\default_output_format " << h_default_output_format << "\n"
1618 << "\\output_sync " << h_output_sync << "\n";
1619 if (h_output_sync == "1")
1620 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1621 os << "\\bibtex_command " << h_bibtex_command << "\n"
1622 << "\\index_command " << h_index_command << "\n";
1623 if (!h_float_placement.empty())
1624 os << "\\float_placement " << h_float_placement << "\n";
1625 os << "\\paperfontsize " << h_paperfontsize << "\n"
1626 << "\\spacing " << h_spacing << "\n"
1627 << "\\use_hyperref " << h_use_hyperref << '\n';
1628 if (h_use_hyperref == "true") {
1629 if (!h_pdf_title.empty())
1630 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1631 if (!h_pdf_author.empty())
1632 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1633 if (!h_pdf_subject.empty())
1634 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1635 if (!h_pdf_keywords.empty())
1636 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1637 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1638 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1639 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1640 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1641 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1642 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1643 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1644 "\\pdf_backref " << h_pdf_backref << "\n"
1645 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1646 if (!h_pdf_pagemode.empty())
1647 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1648 if (!h_pdf_quoted_options.empty())
1649 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1651 os << "\\papersize " << h_papersize << "\n"
1652 << "\\use_geometry " << h_use_geometry << '\n';
1653 for (map<string, string>::const_iterator it = h_use_packages.begin();
1654 it != h_use_packages.end(); ++it)
1655 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1656 os << "\\cite_engine " << h_cite_engine << '\n'
1657 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1658 << "\\biblio_style " << h_biblio_style << "\n"
1659 << "\\use_bibtopic " << h_use_bibtopic << "\n";
1660 if (!h_biblio_options.empty())
1661 os << "\\biblio_options " << h_biblio_options << "\n";
1662 if (!h_biblatex_bibstyle.empty())
1663 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
1664 if (!h_biblatex_citestyle.empty())
1665 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
1666 if (!h_multibib.empty())
1667 os << "\\multibib " << h_multibib << "\n";
1668 os << "\\use_indices " << h_use_indices << "\n"
1669 << "\\paperorientation " << h_paperorientation << '\n'
1670 << "\\suppress_date " << h_suppress_date << '\n'
1671 << "\\justification " << h_justification << '\n'
1672 << "\\use_refstyle " << h_use_refstyle << '\n'
1673 << "\\use_minted " << h_use_minted << '\n';
1674 if (!h_fontcolor.empty())
1675 os << "\\fontcolor " << h_fontcolor << '\n';
1676 if (!h_notefontcolor.empty())
1677 os << "\\notefontcolor " << h_notefontcolor << '\n';
1678 if (!h_backgroundcolor.empty())
1679 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1680 if (!h_boxbgcolor.empty())
1681 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1682 if (index_number != 0)
1683 for (int i = 0; i < index_number; i++) {
1684 os << "\\index " << h_index[i] << '\n'
1685 << "\\shortcut " << h_shortcut[i] << '\n'
1686 << "\\color " << h_color << '\n'
1690 os << "\\index " << h_index[0] << '\n'
1691 << "\\shortcut " << h_shortcut[0] << '\n'
1692 << "\\color " << h_color << '\n'
1696 << "\\secnumdepth " << h_secnumdepth << "\n"
1697 << "\\tocdepth " << h_tocdepth << "\n"
1698 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1699 if (h_paragraph_separation == "skip")
1700 os << "\\defskip " << h_defskip << "\n";
1702 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1703 os << "\\is_math_indent " << h_is_mathindent << "\n";
1704 if (!h_mathindentation.empty())
1705 os << "\\math_indentation " << h_mathindentation << "\n";
1706 os << "\\math_numbering_side " << h_math_numbering_side << "\n";
1707 os << "\\quotes_style " << h_quotes_style << "\n"
1708 << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
1709 << "\\papercolumns " << h_papercolumns << "\n"
1710 << "\\papersides " << h_papersides << "\n"
1711 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1712 if (!h_listings_params.empty())
1713 os << "\\listings_params " << h_listings_params << "\n";
1714 os << "\\tracking_changes " << h_tracking_changes << "\n"
1715 << "\\output_changes " << h_output_changes << "\n"
1716 << "\\html_math_output " << h_html_math_output << "\n"
1717 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1718 << "\\html_be_strict " << h_html_be_strict << "\n"
1720 << "\\end_header\n\n"
1721 << "\\begin_body\n";
1726 void Preamble::parse(Parser & p, string const & forceclass,
1727 TeX2LyXDocClass & tc)
1729 // initialize fixed types
1730 special_columns_['D'] = 3;
1731 parse(p, forceclass, false, tc);
1735 void Preamble::parse(Parser & p, string const & forceclass,
1736 bool detectEncoding, TeX2LyXDocClass & tc)
1738 bool is_full_document = false;
1739 bool is_lyx_file = false;
1740 bool in_lyx_preamble = false;
1742 // determine whether this is a full document or a fragment for inclusion
1744 Token const & t = p.get_token();
1746 if (t.cat() == catEscape && t.cs() == "documentclass") {
1747 is_full_document = true;
1753 if (detectEncoding && !is_full_document)
1756 while (is_full_document && p.good()) {
1757 if (detectEncoding && h_inputencoding != "auto-legacy" &&
1758 h_inputencoding != "auto-legacy-plain")
1761 Token const & t = p.get_token();
1764 if (!detectEncoding)
1765 cerr << "t: " << t << '\n';
1771 if (!in_lyx_preamble &&
1772 (t.cat() == catLetter ||
1773 t.cat() == catSuper ||
1774 t.cat() == catSub ||
1775 t.cat() == catOther ||
1776 t.cat() == catMath ||
1777 t.cat() == catActive ||
1778 t.cat() == catBegin ||
1779 t.cat() == catEnd ||
1780 t.cat() == catAlign ||
1781 t.cat() == catParameter)) {
1782 h_preamble << t.cs();
1786 if (!in_lyx_preamble &&
1787 (t.cat() == catSpace || t.cat() == catNewline)) {
1788 h_preamble << t.asInput();
1792 if (t.cat() == catComment) {
1793 static regex const islyxfile("%% LyX .* created this file");
1794 static regex const usercommands("User specified LaTeX commands");
1796 string const comment = t.asInput();
1798 // magically switch encoding default if it looks like XeLaTeX
1799 static string const magicXeLaTeX =
1800 "% This document must be compiled with XeLaTeX ";
1801 if (comment.size() > magicXeLaTeX.size()
1802 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1803 && h_inputencoding == "auto-legacy") {
1804 if (!detectEncoding)
1805 cerr << "XeLaTeX comment found, switching to UTF8\n";
1806 h_inputencoding = "utf8";
1809 if (regex_search(comment, sub, islyxfile)) {
1811 in_lyx_preamble = true;
1812 } else if (is_lyx_file
1813 && regex_search(comment, sub, usercommands))
1814 in_lyx_preamble = false;
1815 else if (!in_lyx_preamble)
1816 h_preamble << t.asInput();
1820 if (t.cs() == "PassOptionsToPackage") {
1821 string const poptions = p.getArg('{', '}');
1822 string const package = p.verbatim_item();
1823 extra_package_options_.insert(make_pair(package, poptions));
1827 if (t.cs() == "pagestyle") {
1828 h_paperpagestyle = p.verbatim_item();
1832 if (t.cs() == "setdefaultlanguage") {
1834 // We don't yet care about non-language variant options
1835 // because LyX doesn't support this yet, see bug #8214
1837 string langopts = p.getOpt();
1838 // check if the option contains a variant, if yes, extract it
1839 string::size_type pos_var = langopts.find("variant");
1840 string::size_type i = langopts.find(',', pos_var);
1841 string::size_type k = langopts.find('=', pos_var);
1842 if (pos_var != string::npos){
1844 if (i == string::npos)
1845 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1847 variant = langopts.substr(k + 1, i - k - 1);
1848 h_language = variant;
1852 h_language = p.verbatim_item();
1853 //finally translate the poyglossia name to a LyX name
1854 h_language = polyglossia2lyx(h_language);
1858 if (t.cs() == "setotherlanguage") {
1859 // We don't yet care about the option because LyX doesn't
1860 // support this yet, see bug #8214
1861 p.hasOpt() ? p.getOpt() : string();
1866 if (t.cs() == "setmainfont") {
1867 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
1868 h_font_roman[1] = p.getArg('{', '}');
1869 if (!fontopts.empty()) {
1870 vector<string> opts = getVectorFromString(fontopts);
1872 for (auto const & opt : opts) {
1873 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
1876 if (!fontopts.empty())
1880 h_font_roman_opts = fontopts;
1885 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1886 // LyX currently only supports the scale option
1887 string scale, fontopts;
1889 fontopts = p.getArg('[', ']');
1890 if (!fontopts.empty()) {
1891 vector<string> opts = getVectorFromString(fontopts);
1893 for (auto const & opt : opts) {
1894 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
1897 if (prefixIs(opt, "Scale=")) {
1898 scale_as_percentage(opt, scale);
1901 if (!fontopts.empty())
1907 if (t.cs() == "setsansfont") {
1909 h_font_sf_scale[1] = scale;
1910 h_font_sans[1] = p.getArg('{', '}');
1911 if (!fontopts.empty())
1912 h_font_sans_opts = fontopts;
1915 h_font_tt_scale[1] = scale;
1916 h_font_typewriter[1] = p.getArg('{', '}');
1917 if (!fontopts.empty())
1918 h_font_typewriter_opts = fontopts;
1923 if (t.cs() == "babelfont") {
1925 h_use_non_tex_fonts = true;
1926 h_language_package = "babel";
1927 if (h_inputencoding == "auto-legacy")
1928 p.setEncoding("UTF-8");
1929 // we don't care about the lang option
1930 string const lang = p.hasOpt() ? p.getArg('[', ']') : string();
1931 string const family = p.getArg('{', '}');
1932 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
1933 string const fontname = p.getArg('{', '}');
1934 if (lang.empty() && family == "rm") {
1935 h_font_roman[1] = fontname;
1936 if (!fontopts.empty()) {
1937 vector<string> opts = getVectorFromString(fontopts);
1939 for (auto const & opt : opts) {
1940 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
1943 if (!fontopts.empty())
1947 h_font_roman_opts = fontopts;
1950 } else if (lang.empty() && (family == "sf" || family == "tt")) {
1951 // LyX currently only supports the scale option
1953 if (!fontopts.empty()) {
1954 vector<string> opts = getVectorFromString(fontopts);
1956 for (auto const & opt : opts) {
1957 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
1960 if (prefixIs(opt, "Scale=")) {
1961 scale_as_percentage(opt, scale);
1964 if (!fontopts.empty())
1969 if (family == "sf") {
1971 h_font_sf_scale[1] = scale;
1972 h_font_sans[1] = fontname;
1973 if (!fontopts.empty())
1974 h_font_sans_opts = fontopts;
1977 h_font_tt_scale[1] = scale;
1978 h_font_typewriter[1] = fontname;
1979 if (!fontopts.empty())
1980 h_font_typewriter_opts = fontopts;
1984 // not rm, sf or tt or lang specific
1985 h_preamble << '\\' << t.cs();
1987 h_preamble << '[' << lang << ']';
1988 h_preamble << '{' << family << '}';
1989 if (!fontopts.empty())
1990 h_preamble << '[' << fontopts << ']';
1991 h_preamble << '{' << fontname << '}' << '\n';
1996 if (t.cs() == "date") {
1997 string argument = p.getArg('{', '}');
1998 if (argument.empty())
1999 h_suppress_date = "true";
2001 h_preamble << t.asInput() << '{' << argument << '}';
2005 if (t.cs() == "color") {
2006 string const space =
2007 (p.hasOpt() ? p.getOpt() : string());
2008 string argument = p.getArg('{', '}');
2009 // check the case that a standard color is used
2010 if (space.empty() && is_known(argument, known_basic_colors)) {
2011 h_fontcolor = rgbcolor2code(argument);
2012 registerAutomaticallyLoadedPackage("color");
2013 } else if (space.empty() && argument == "document_fontcolor")
2014 registerAutomaticallyLoadedPackage("color");
2015 // check the case that LyX's document_fontcolor is defined
2016 // but not used for \color
2018 h_preamble << t.asInput();
2020 h_preamble << space;
2021 h_preamble << '{' << argument << '}';
2022 // the color might already be set because \definecolor
2023 // is parsed before this
2029 if (t.cs() == "pagecolor") {
2030 string argument = p.getArg('{', '}');
2031 // check the case that a standard color is used
2032 if (is_known(argument, known_basic_colors)) {
2033 h_backgroundcolor = rgbcolor2code(argument);
2034 } else if (argument == "page_backgroundcolor")
2035 registerAutomaticallyLoadedPackage("color");
2036 // check the case that LyX's page_backgroundcolor is defined
2037 // but not used for \pagecolor
2039 h_preamble << t.asInput() << '{' << argument << '}';
2040 // the color might already be set because \definecolor
2041 // is parsed before this
2042 h_backgroundcolor = "";
2047 if (t.cs() == "makeatletter") {
2048 // LyX takes care of this
2049 p.setCatcode('@', catLetter);
2053 if (t.cs() == "makeatother") {
2054 // LyX takes care of this
2055 p.setCatcode('@', catOther);
2059 if (t.cs() == "makeindex") {
2060 // LyX will re-add this if a print index command is found
2065 if (t.cs() == "newindex") {
2066 string const indexname = p.getArg('[', ']');
2067 string const shortcut = p.verbatim_item();
2068 if (!indexname.empty())
2069 h_index[index_number] = indexname;
2071 h_index[index_number] = shortcut;
2072 h_shortcut[index_number] = shortcut;
2078 if (t.cs() == "addbibresource") {
2079 string const options = p.getArg('[', ']');
2080 string const arg = removeExtension(p.getArg('{', '}'));
2081 if (!options.empty()) {
2082 // check if the option contains a bibencoding, if yes, extract it
2083 string::size_type pos = options.find("bibencoding=");
2085 if (pos != string::npos) {
2086 string::size_type i = options.find(',', pos);
2087 if (i == string::npos)
2088 encoding = options.substr(pos + 1);
2090 encoding = options.substr(pos, i - pos);
2091 pos = encoding.find('=');
2092 if (pos == string::npos)
2095 encoding = encoding.substr(pos + 1);
2097 if (!encoding.empty())
2098 biblatex_encodings.push_back(normalize_filename(arg) + ' ' + encoding);
2100 biblatex_bibliographies.push_back(arg);
2104 if (t.cs() == "bibliography") {
2105 vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
2106 biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
2110 if (t.cs() == "RS@ifundefined") {
2111 string const name = p.verbatim_item();
2112 string const body1 = p.verbatim_item();
2113 string const body2 = p.verbatim_item();
2114 // only non-lyxspecific stuff
2115 if (in_lyx_preamble &&
2116 (name == "subsecref" || name == "thmref" || name == "lemref"))
2120 ss << '\\' << t.cs();
2121 ss << '{' << name << '}'
2122 << '{' << body1 << '}'
2123 << '{' << body2 << '}';
2124 h_preamble << ss.str();
2129 if (t.cs() == "AtBeginDocument") {
2130 string const name = p.verbatim_item();
2131 // only non-lyxspecific stuff
2132 if (in_lyx_preamble &&
2133 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
2134 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
2135 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
2136 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
2137 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
2138 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
2139 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
2140 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
2141 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
2142 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
2143 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
2144 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
2145 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
2146 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
2147 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
2151 ss << '\\' << t.cs();
2152 ss << '{' << name << '}';
2153 h_preamble << ss.str();
2158 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
2159 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
2160 || t.cs() == "providecommand" || t.cs() == "providecommandx"
2161 || t.cs() == "DeclareRobustCommand"
2162 || t.cs() == "DeclareRobustCommandx"
2163 || t.cs() == "ProvideTextCommandDefault"
2164 || t.cs() == "DeclareMathAccent") {
2166 if (p.next_token().character() == '*') {
2170 string const name = p.verbatim_item();
2171 string const opt1 = p.getFullOpt();
2172 string const opt2 = p.getFullOpt();
2173 string const body = p.verbatim_item();
2174 // store the in_lyx_preamble setting
2175 bool const was_in_lyx_preamble = in_lyx_preamble;
2177 if (name == "\\rmdefault")
2178 if (is_known(body, known_roman_font_packages)) {
2179 h_font_roman[0] = body;
2181 in_lyx_preamble = true;
2183 if (name == "\\sfdefault")
2184 if (is_known(body, known_sans_font_packages)) {
2185 h_font_sans[0] = body;
2187 in_lyx_preamble = true;
2189 if (name == "\\ttdefault")
2190 if (is_known(body, known_typewriter_font_packages)) {
2191 h_font_typewriter[0] = body;
2193 in_lyx_preamble = true;
2195 if (name == "\\familydefault") {
2196 string family = body;
2197 // remove leading "\"
2198 h_font_default_family = family.erase(0,1);
2200 in_lyx_preamble = true;
2203 // remove LyX-specific definitions that are re-added by LyX
2205 // \lyxline is an ancient command that is converted by tex2lyx into
2206 // a \rule therefore remove its preamble code
2207 if (name == "\\lyxdot" || name == "\\lyxarrow"
2208 || name == "\\lyxline" || name == "\\LyX") {
2210 in_lyx_preamble = true;
2213 // Add the command to the known commands
2214 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
2216 // only non-lyxspecific stuff
2217 if (!in_lyx_preamble) {
2219 ss << '\\' << t.cs();
2222 ss << '{' << name << '}' << opt1 << opt2
2223 << '{' << body << "}";
2224 if (prefixIs(t.cs(), "renew") || !contains(h_preamble.str(), ss.str()))
2225 h_preamble << ss.str();
2227 ostream & out = in_preamble ? h_preamble : os;
2228 out << "\\" << t.cs() << "{" << name << "}"
2229 << opts << "{" << body << "}";
2232 // restore the in_lyx_preamble setting
2233 in_lyx_preamble = was_in_lyx_preamble;
2237 if (t.cs() == "documentclass") {
2238 vector<string>::iterator it;
2239 vector<string> opts = split_options(p.getArg('[', ']'));
2240 handle_opt(opts, known_fontsizes, h_paperfontsize);
2241 delete_opt(opts, known_fontsizes);
2242 // delete "pt" at the end
2243 string::size_type i = h_paperfontsize.find("pt");
2244 if (i != string::npos)
2245 h_paperfontsize.erase(i);
2246 // The documentclass options are always parsed before the options
2247 // of the babel call so that a language cannot overwrite the babel
2249 handle_opt(opts, known_languages, h_language);
2250 delete_opt(opts, known_languages);
2253 if ((it = find(opts.begin(), opts.end(), "fleqn"))
2255 h_is_mathindent = "1";
2258 // formula numbering side
2259 if ((it = find(opts.begin(), opts.end(), "leqno"))
2261 h_math_numbering_side = "left";
2264 else if ((it = find(opts.begin(), opts.end(), "reqno"))
2266 h_math_numbering_side = "right";
2270 // paper orientation
2271 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
2272 h_paperorientation = "landscape";
2276 if ((it = find(opts.begin(), opts.end(), "oneside"))
2281 if ((it = find(opts.begin(), opts.end(), "twoside"))
2287 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
2289 h_papercolumns = "1";
2292 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
2294 h_papercolumns = "2";
2298 // some size options are known to any document classes, other sizes
2299 // are handled by the \geometry command of the geometry package
2300 handle_opt(opts, known_class_paper_sizes, h_papersize);
2301 delete_opt(opts, known_class_paper_sizes);
2302 // the remaining options
2303 h_options = join(opts, ",");
2304 // FIXME This does not work for classes that have a
2305 // different name in LyX than in LaTeX
2306 h_textclass = p.getArg('{', '}');
2311 if (t.cs() == "usepackage") {
2312 string const options = p.getArg('[', ']');
2313 string const name = p.getArg('{', '}');
2314 vector<string> vecnames;
2315 split(name, vecnames, ',');
2316 vector<string>::const_iterator it = vecnames.begin();
2317 vector<string>::const_iterator end = vecnames.end();
2318 for (; it != end; ++it)
2319 handle_package(p, trimSpaceAndEol(*it), options,
2320 in_lyx_preamble, detectEncoding);
2324 if (t.cs() == "inputencoding") {
2325 string const encoding = p.getArg('{','}');
2326 Encoding const * const enc = encodings.fromLaTeXName(
2327 encoding, Encoding::inputenc, true);
2329 if (!detectEncoding)
2330 cerr << "Unknown encoding " << encoding
2331 << ". Ignoring." << std::endl;
2334 h_inputencoding = enc->name();
2335 p.setEncoding(enc->iconvName());
2340 if (t.cs() == "newenvironment") {
2341 string const name = p.getArg('{', '}');
2342 string const opt1 = p.getFullOpt();
2343 string const opt2 = p.getFullOpt();
2344 string const beg = p.verbatim_item();
2345 string const end = p.verbatim_item();
2346 if (!in_lyx_preamble) {
2347 h_preamble << "\\newenvironment{" << name
2348 << '}' << opt1 << opt2 << '{'
2349 << beg << "}{" << end << '}';
2351 add_known_environment(name, opt1, !opt2.empty(),
2352 from_utf8(beg), from_utf8(end));
2356 if (t.cs() == "newtheorem") {
2358 if (p.next_token().character() == '*') {
2362 string const name = p.getArg('{', '}');
2363 string const opt1 = p.getFullOpt();
2364 string const opt2 = p.getFullOpt();
2365 string const body = p.verbatim_item();
2366 string const opt3 = p.getFullOpt();
2367 string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
2369 string const complete = cmd + "{" + name + '}' +
2370 opt1 + opt2 + '{' + body + '}' + opt3;
2372 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
2374 if (!in_lyx_preamble)
2375 h_preamble << complete;
2379 if (t.cs() == "def") {
2380 string name = p.get_token().cs();
2381 // In fact, name may be more than the name:
2382 // In the test case of bug 8116
2383 // name == "csname SF@gobble@opt \endcsname".
2384 // Therefore, we need to use asInput() instead of cs().
2385 while (p.next_token().cat() != catBegin)
2386 name += p.get_token().asInput();
2387 if (!in_lyx_preamble)
2388 h_preamble << "\\def\\" << name << '{'
2389 << p.verbatim_item() << "}";
2393 if (t.cs() == "newcolumntype") {
2394 string const name = p.getArg('{', '}');
2395 trimSpaceAndEol(name);
2397 string opts = p.getOpt();
2398 if (!opts.empty()) {
2399 istringstream is(string(opts, 1));
2402 special_columns_[name[0]] = nargs;
2403 h_preamble << "\\newcolumntype{" << name << "}";
2405 h_preamble << "[" << nargs << "]";
2406 h_preamble << "{" << p.verbatim_item() << "}";
2410 if (t.cs() == "setcounter") {
2411 string const name = p.getArg('{', '}');
2412 string const content = p.getArg('{', '}');
2413 if (name == "secnumdepth")
2414 h_secnumdepth = content;
2415 else if (name == "tocdepth")
2416 h_tocdepth = content;
2418 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
2422 if (t.cs() == "setlength") {
2423 string const name = p.verbatim_item();
2424 string const content = p.verbatim_item();
2425 // the paragraphs are only not indented when \parindent is set to zero
2426 if (name == "\\parindent" && content != "") {
2427 if (content[0] == '0')
2428 h_paragraph_separation = "skip";
2430 h_paragraph_indentation = translate_len(content);
2431 } else if (name == "\\parskip") {
2432 if (content == "\\smallskipamount")
2433 h_defskip = "smallskip";
2434 else if (content == "\\medskipamount")
2435 h_defskip = "medskip";
2436 else if (content == "\\bigskipamount")
2437 h_defskip = "bigskip";
2439 h_defskip = translate_len(content);
2440 } else if (name == "\\mathindent") {
2441 h_mathindentation = translate_len(content);
2443 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2447 if (t.cs() == "onehalfspacing") {
2448 h_spacing = "onehalf";
2452 if (t.cs() == "doublespacing") {
2453 h_spacing = "double";
2457 if (t.cs() == "setstretch") {
2458 h_spacing = "other " + p.verbatim_item();
2462 if (t.cs() == "synctex") {
2463 // the scheme is \synctex=value
2464 // where value can only be "1" or "-1"
2465 h_output_sync = "1";
2466 // there can be any character behind the value (e.g. a linebreak or a '\'
2467 // therefore we extract it char by char
2469 string value = p.get_token().asInput();
2471 value += p.get_token().asInput();
2472 h_output_sync_macro = "\\synctex=" + value;
2476 if (t.cs() == "begin") {
2477 string const name = p.getArg('{', '}');
2478 if (name == "document")
2480 h_preamble << "\\begin{" << name << "}";
2484 if (t.cs() == "geometry") {
2485 vector<string> opts = split_options(p.getArg('{', '}'));
2486 handle_geometry(opts);
2490 if (t.cs() == "definecolor") {
2491 string const color = p.getArg('{', '}');
2492 string const space = p.getArg('{', '}');
2493 string const value = p.getArg('{', '}');
2494 if (color == "document_fontcolor" && space == "rgb") {
2495 RGBColor c(RGBColorFromLaTeX(value));
2496 h_fontcolor = X11hexname(c);
2497 } else if (color == "note_fontcolor" && space == "rgb") {
2498 RGBColor c(RGBColorFromLaTeX(value));
2499 h_notefontcolor = X11hexname(c);
2500 } else if (color == "page_backgroundcolor" && space == "rgb") {
2501 RGBColor c(RGBColorFromLaTeX(value));
2502 h_backgroundcolor = X11hexname(c);
2503 } else if (color == "shadecolor" && space == "rgb") {
2504 RGBColor c(RGBColorFromLaTeX(value));
2505 h_boxbgcolor = X11hexname(c);
2507 h_preamble << "\\definecolor{" << color
2508 << "}{" << space << "}{" << value
2514 if (t.cs() == "bibliographystyle") {
2515 h_biblio_style = p.verbatim_item();
2519 if (t.cs() == "jurabibsetup") {
2520 // FIXME p.getArg('{', '}') is most probably wrong (it
2521 // does not handle nested braces).
2522 // Use p.verbatim_item() instead.
2523 vector<string> jurabibsetup =
2524 split_options(p.getArg('{', '}'));
2525 // add jurabibsetup to the jurabib package options
2526 add_package("jurabib", jurabibsetup);
2527 if (!jurabibsetup.empty()) {
2528 h_preamble << "\\jurabibsetup{"
2529 << join(jurabibsetup, ",") << '}';
2534 if (t.cs() == "hypersetup") {
2535 vector<string> hypersetup =
2536 split_options(p.verbatim_item());
2537 // add hypersetup to the hyperref package options
2538 handle_hyperref(hypersetup);
2539 if (!hypersetup.empty()) {
2540 h_preamble << "\\hypersetup{"
2541 << join(hypersetup, ",") << '}';
2546 if (t.cs() == "includeonly") {
2547 vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
2548 for (auto & iofile : includeonlys) {
2549 string filename(normalize_filename(iofile));
2550 string const path = getMasterFilePath(true);
2551 // We want to preserve relative/absolute filenames,
2552 // therefore path is only used for testing
2553 if (!makeAbsPath(filename, path).exists()) {
2554 // The file extension is probably missing.
2555 // Now try to find it out.
2556 string const tex_name =
2557 find_file(filename, path,
2558 known_tex_extensions);
2559 if (!tex_name.empty())
2560 filename = tex_name;
2563 if (makeAbsPath(filename, path).exists())
2564 fix_child_filename(filename);
2566 cerr << "Warning: Could not find included file '"
2567 << filename << "'." << endl;
2568 outname = changeExtension(filename, "lyx");
2569 h_includeonlys.push_back(outname);
2574 if (is_known(t.cs(), known_if_3arg_commands)) {
2575 // prevent misparsing of \usepackage if it is used
2576 // as an argument (see e.g. our own output of
2577 // \@ifundefined above)
2578 string const arg1 = p.verbatim_item();
2579 string const arg2 = p.verbatim_item();
2580 string const arg3 = p.verbatim_item();
2581 // test case \@ifundefined{date}{}{\date{}}
2582 if (t.cs() == "@ifundefined" && arg1 == "date" &&
2583 arg2.empty() && arg3 == "\\date{}") {
2584 h_suppress_date = "true";
2585 // older tex2lyx versions did output
2586 // \@ifundefined{definecolor}{\usepackage{color}}{}
2587 } else if (t.cs() == "@ifundefined" &&
2588 arg1 == "definecolor" &&
2589 arg2 == "\\usepackage{color}" &&
2591 if (!in_lyx_preamble)
2592 h_preamble << package_beg_sep
2595 << "\\@ifundefined{definecolor}{color}{}"
2598 //\@ifundefined{showcaptionsetup}{}{%
2599 // \PassOptionsToPackage{caption=false}{subfig}}
2600 // that LyX uses for subfloats
2601 } else if (t.cs() == "@ifundefined" &&
2602 arg1 == "showcaptionsetup" && arg2.empty()
2603 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
2605 } else if (!in_lyx_preamble) {
2606 h_preamble << t.asInput()
2607 << '{' << arg1 << '}'
2608 << '{' << arg2 << '}'
2609 << '{' << arg3 << '}';
2614 if (is_known(t.cs(), known_if_commands)) {
2615 // must not parse anything in conditional code, since
2616 // LyX would output the parsed contents unconditionally
2617 if (!in_lyx_preamble)
2618 h_preamble << t.asInput();
2619 handle_if(p, in_lyx_preamble);
2623 if (!t.cs().empty() && !in_lyx_preamble) {
2624 h_preamble << '\\' << t.cs();
2629 // remove the whitespace
2632 // Force textclass if the user wanted it
2633 if (!forceclass.empty())
2634 h_textclass = forceclass;
2635 tc.setName(h_textclass);
2636 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2637 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2640 if (h_papersides.empty()) {
2643 h_papersides = ss.str();
2646 // If the CJK package is used we cannot set the document language from
2647 // the babel options. Instead, we guess which language is used most
2648 // and set this one.
2649 default_language = h_language;
2650 if (is_full_document &&
2651 (auto_packages.find("CJK") != auto_packages.end() ||
2652 auto_packages.find("CJKutf8") != auto_packages.end())) {
2654 h_language = guessLanguage(p, default_language);
2656 if (explicit_babel && h_language != default_language) {
2657 // We set the document language to a CJK language,
2658 // but babel is explicitly called in the user preamble
2659 // without options. LyX will not add the default
2660 // language to the document options if it is either
2661 // english, or no text is set as default language.
2662 // Therefore we need to add a language option explicitly.
2663 // FIXME: It would be better to remove all babel calls
2664 // from the user preamble, but this is difficult
2665 // without re-introducing bug 7861.
2666 if (h_options.empty())
2667 h_options = lyx2babel(default_language);
2669 h_options += ',' + lyx2babel(default_language);
2673 // Finally, set the quote style.
2674 // LyX knows the following quotes styles:
2675 // british, cjk, cjkangle, danish, english, french, german,
2676 // polish, russian, swedish and swiss
2677 // conversion list taken from
2678 // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
2679 // (quotes for kazakh are unknown)
2681 if (is_known(h_language, known_british_quotes_languages))
2682 h_quotes_style = "british";
2684 else if (is_known(h_language, known_cjk_quotes_languages))
2685 h_quotes_style = "cjk";
2687 else if (is_known(h_language, known_cjkangle_quotes_languages))
2688 h_quotes_style = "cjkangle";
2690 else if (is_known(h_language, known_danish_quotes_languages))
2691 h_quotes_style = "danish";
2693 else if (is_known(h_language, known_french_quotes_languages))
2694 h_quotes_style = "french";
2696 else if (is_known(h_language, known_german_quotes_languages))
2697 h_quotes_style = "german";
2699 else if (is_known(h_language, known_polish_quotes_languages))
2700 h_quotes_style = "polish";
2702 else if (is_known(h_language, known_russian_quotes_languages))
2703 h_quotes_style = "russian";
2705 else if (is_known(h_language, known_swedish_quotes_languages))
2706 h_quotes_style = "swedish";
2708 else if (is_known(h_language, known_swiss_quotes_languages))
2709 h_quotes_style = "swiss";
2711 else if (is_known(h_language, known_english_quotes_languages))
2712 h_quotes_style = "english";
2716 string Preamble::parseEncoding(Parser & p, string const & forceclass)
2718 TeX2LyXDocClass dummy;
2719 parse(p, forceclass, true, dummy);
2720 if (h_inputencoding != "auto-legacy" && h_inputencoding != "auto-legacy-plain")
2721 return h_inputencoding;
2726 string babel2lyx(string const & language)
2728 char const * const * where = is_known(language, known_languages);
2730 return known_coded_languages[where - known_languages];
2735 string lyx2babel(string const & language)
2737 char const * const * where = is_known(language, known_coded_languages);
2739 return known_languages[where - known_coded_languages];
2744 string Preamble::polyglossia2lyx(string const & language)
2746 char const * const * where = is_known(language, polyglossia_languages);
2748 return coded_polyglossia_languages[where - polyglossia_languages];
2753 string rgbcolor2code(string const & name)
2755 char const * const * where = is_known(name, known_basic_colors);
2757 // "red", "green" etc
2758 return known_basic_color_codes[where - known_basic_colors];
2760 // "255,0,0", "0,255,0" etc
2761 RGBColor c(RGBColorFromLaTeX(name));
2762 return X11hexname(c);