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
859 if (name == "paratype") {
860 // in this case all fonts are ParaType
861 h_font_roman[0] = "PTSerif-TLF";
862 h_font_sans[0] = "default";
863 h_font_typewriter[0] = "default";
866 if (name == "PTSerif")
867 h_font_roman[0] = "PTSerif-TLF";
869 if (name == "XCharter") {
870 h_font_roman[0] = "xcharter";
875 if (name == "plex-serif") {
877 h_font_roman[0] = "IBMPlexSerif";
878 else if (opts.find("thin") != string::npos)
879 h_font_roman[0] = "IBMPlexSerifThin";
880 else if (opts.find("extralight") != string::npos)
881 h_font_roman[0] = "IBMPlexSerifExtraLight";
882 else if (opts.find("light") != string::npos)
883 h_font_roman[0] = "IBMPlexSerifLight";
884 else if (opts.find("semibold") != string::npos)
885 h_font_roman[0] = "IBMPlexSerifSemibold";
886 vector<string> allopts = getVectorFromString(opts);
888 for (auto const & opt : allopts) {
891 if (opt == "extralight")
895 if (opt == "semibold")
902 h_font_roman_opts = xopts;
905 if (name == "noto-serif") {
906 h_font_roman[0] = "NotoSerifRegular";
908 if (opts.find("thin") != string::npos)
909 h_font_roman[0] = "NotoSerifThin";
910 else if (opts.find("medium") != string::npos)
911 h_font_roman[0] = "NotoSerifMedium";
912 else if (opts.find("extralight") != string::npos)
913 h_font_roman[0] = "NotoSerifExtralight";
914 else if (opts.find("light") != string::npos)
915 h_font_roman[0] = "NotoSerifLight";
917 vector<string> allopts = getVectorFromString(opts);
919 for (auto const & opt : allopts) {
920 if (opt == "regular")
924 if (opt == "extralight")
928 if (opt == "semibold")
935 h_font_roman_opts = xopts;
939 if (name == "sourceserifpro") {
940 h_font_roman[0] = "ADOBESourceSerifPro";
941 vector<string> allopts = getVectorFromString(opts);
943 for (auto const & opt : allopts) {
949 h_font_roman_opts = xopts;
954 if (is_known(name, known_sans_font_packages)) {
955 h_font_sans[0] = name;
956 if (options.size() >= 1) {
957 if (scale_as_percentage(opts, h_font_sf_scale[0]))
962 if (name == "biolinum" || name == "biolinum-type1") {
963 h_font_sans[0] = "biolinum";
964 // biolinum can have several options, e.g. [osf,scaled=0.97]
965 string::size_type pos = opts.find("osf");
966 if (pos != string::npos)
970 if (name == "PTSans") {
971 h_font_sans[0] = "PTSans-TLF";
972 if (options.size() >= 1) {
973 if (scale_as_percentage(opts, h_font_sf_scale[0]))
978 if (name == "plex-sans") {
979 if (opts.find("condensed") != string::npos)
980 h_font_sans[0] = "IBMPlexSansCondensed";
981 else if (opts.find("thin") != string::npos)
982 h_font_sans[0] = "IBMPlexSansThin";
983 else if (opts.find("extralight") != string::npos)
984 h_font_sans[0] = "IBMPlexSansExtraLight";
985 else if (opts.find("light") != string::npos)
986 h_font_sans[0] = "IBMPlexSansLight";
987 else if (opts.find("semibold") != string::npos)
988 h_font_sans[0] = "IBMPlexSansSemibold";
990 h_font_sans[0] = "IBMPlexSans";
991 vector<string> allopts = getVectorFromString(opts);
993 for (auto const & opt : allopts) {
996 if (opt == "extralight")
1000 if (opt == "semibold")
1002 if (prefixIs(opt, "scale=")) {
1003 scale_as_percentage(opt, h_font_sf_scale[0]);
1011 h_font_sans_opts = xopts;
1014 if (name == "noto-sans") {
1015 h_font_sans[0] = "NotoSansRegular";
1016 if (!opts.empty()) {
1017 if (opts.find("medium") != string::npos)
1018 h_font_sans[0] = "NotoSansMedium";
1019 else if (opts.find("thin") != string::npos)
1020 h_font_sans[0] = "NotoSansThin";
1021 else if (opts.find("extralight") != string::npos)
1022 h_font_sans[0] = "NotoSansExtralight";
1023 else if (opts.find("light") != string::npos)
1024 h_font_sans[0] = "NotoSansLight";
1026 vector<string> allopts = getVectorFromString(opts);
1028 for (auto const & opt : allopts) {
1029 if (opt == "regular")
1033 if (opt == "extralight")
1037 if (opt == "semibold")
1044 h_font_sans_opts = xopts;
1048 if (name == "sourcesanspro") {
1049 h_font_sans[0] = "ADOBESourceSansPro";
1050 vector<string> allopts = getVectorFromString(opts);
1052 for (auto const & opt : allopts) {
1053 if (prefixIs(opt, "scaled=")) {
1054 scale_as_percentage(opt, h_font_sf_scale[0]);
1062 h_font_sans_opts = xopts;
1067 if (is_known(name, known_typewriter_font_packages)) {
1068 // fourier can be set as roman font _only_
1069 // fourier as typewriter is handled in handling of \ttdefault
1070 if (name != "fourier") {
1071 h_font_typewriter[0] = name;
1072 if (options.size() >= 1) {
1073 if (scale_as_percentage(opts, h_font_tt_scale[0]))
1079 if (name == "libertineMono" || name == "libertineMono-type1")
1080 h_font_typewriter[0] = "libertine-mono";
1082 if (name == "PTMono") {
1083 h_font_typewriter[0] = "PTMono-TLF";
1084 if (options.size() >= 1) {
1085 if (scale_as_percentage(opts, h_font_tt_scale[0]))
1090 if (name == "plex-mono") {
1091 if (opts.find("thin") != string::npos)
1092 h_font_typewriter[0] = "IBMPlexMonoThin";
1093 else if (opts.find("extralight") != string::npos)
1094 h_font_typewriter[0] = "IBMPlexMonoExtraLight";
1095 else if (opts.find("light") != string::npos)
1096 h_font_typewriter[0] = "IBMPlexMonoLight";
1097 else if (opts.find("semibold") != string::npos)
1098 h_font_typewriter[0] = "IBMPlexMonoSemibold";
1100 h_font_typewriter[0] = "IBMPlexMono";
1101 vector<string> allopts = getVectorFromString(opts);
1103 for (auto const & opt : allopts) {
1106 if (opt == "extralight")
1110 if (opt == "semibold")
1112 if (prefixIs(opt, "scale=")) {
1113 scale_as_percentage(opt, h_font_tt_scale[0]);
1121 h_font_typewriter_opts = xopts;
1125 if (name == "noto-mono") {
1126 h_font_typewriter[0] = "NotoMonoRegular";
1127 vector<string> allopts = getVectorFromString(opts);
1129 for (auto const & opt : allopts) {
1130 if (opt == "regular")
1137 h_font_typewriter_opts = xopts;
1141 if (name == "sourcecodepro") {
1142 h_font_typewriter[0] = "ADOBESourceCodePro";
1143 vector<string> allopts = getVectorFromString(opts);
1145 for (auto const & opt : allopts) {
1146 if (prefixIs(opt, "scaled=")) {
1147 scale_as_percentage(opt, h_font_tt_scale[0]);
1155 h_font_typewriter_opts = xopts;
1159 // font uses old-style figure
1161 h_font_osf = "true";
1164 if (is_known(name, known_math_font_packages))
1165 h_font_math[0] = name;
1167 if (name == "newtxmath") {
1169 h_font_math[0] = "newtxmath";
1170 else if (opts == "garamondx")
1171 h_font_math[0] = "garamondx-ntxm";
1172 else if (opts == "libertine")
1173 h_font_math[0] = "libertine-ntxm";
1174 else if (opts == "minion")
1175 h_font_math[0] = "minion-ntxm";
1176 else if (opts == "cochineal")
1177 h_font_math[0] = "cochineal-ntxm";
1180 if (name == "iwona")
1182 h_font_math[0] = "iwona-math";
1184 if (name == "kurier")
1186 h_font_math[0] = "kurier-math";
1188 // after the detection and handling of special cases, we can remove the
1189 // fonts, otherwise they would appear in the preamble, see bug #7856
1190 if (is_known(name, known_roman_font_packages) || is_known(name, known_sans_font_packages)
1191 || is_known(name, known_typewriter_font_packages) || is_known(name, known_math_font_packages))
1193 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
1194 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
1195 name == "esint" || name == "mhchem" || name == "mathdots" ||
1196 name == "mathtools" || name == "stackrel" ||
1197 name == "stmaryrd" || name == "undertilde") {
1198 h_use_packages[name] = "2";
1199 registerAutomaticallyLoadedPackage(name);
1202 else if (name == "babel") {
1203 h_language_package = "default";
1204 // One might think we would have to do nothing if babel is loaded
1205 // without any options to prevent pollution of the preamble with this
1206 // babel call in every roundtrip.
1207 // But the user could have defined babel-specific things afterwards. So
1208 // we need to keep it in the preamble to prevent cases like bug #7861.
1209 if (!opts.empty()) {
1210 // check if more than one option was used - used later for inputenc
1211 if (options.begin() != options.end() - 1)
1212 one_language = false;
1213 // babel takes the last language of the option of its \usepackage
1214 // call as document language. If there is no such language option, the
1215 // last language in the documentclass options is used.
1216 handle_opt(options, known_languages, h_language);
1217 // translate the babel name to a LyX name
1218 h_language = babel2lyx(h_language);
1219 if (h_language == "japanese") {
1220 // For Japanese, the encoding isn't indicated in the source
1221 // file, and there's really not much we can do. We could
1222 // 1) offer a list of possible encodings to choose from, or
1223 // 2) determine the encoding of the file by inspecting it.
1224 // For the time being, we leave the encoding alone so that
1225 // we don't get iconv errors when making a wrong guess, and
1226 // we will output a note at the top of the document
1227 // explaining what to do.
1228 Encoding const * const enc = encodings.fromIconvName(
1229 p.getEncoding(), Encoding::japanese, false);
1231 h_inputencoding = enc->name();
1232 is_nonCJKJapanese = true;
1233 // in this case babel can be removed from the preamble
1234 registerAutomaticallyLoadedPackage("babel");
1236 // If babel is called with options, LyX puts them by default into the
1237 // document class options. This works for most languages, except
1238 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
1239 // perhaps in future others.
1240 // Therefore keep the babel call as it is as the user might have
1242 string const babelcall = "\\usepackage[" + opts + "]{babel}\n";
1243 if (!contains(h_preamble.str(), babelcall))
1244 h_preamble << babelcall;
1246 delete_opt(options, known_languages);
1248 if (!contains(h_preamble.str(), "\\usepackage{babel}\n"))
1249 h_preamble << "\\usepackage{babel}\n";
1250 explicit_babel = true;
1254 else if (name == "polyglossia") {
1255 h_language_package = "default";
1256 h_default_output_format = "pdf4";
1257 h_use_non_tex_fonts = true;
1259 registerAutomaticallyLoadedPackage("xunicode");
1260 if (h_inputencoding == "auto-legacy")
1261 p.setEncoding("UTF-8");
1264 else if (name == "CJK") {
1265 // set the encoding to "auto-legacy" because it might be set to "auto-legacy-plain" by the babel handling
1266 // and this would not be correct for CJK
1267 if (h_inputencoding == "auto-legacy-plain")
1268 h_inputencoding = "auto-legacy";
1269 registerAutomaticallyLoadedPackage("CJK");
1272 else if (name == "CJKutf8") {
1273 h_inputencoding = "utf8-cjk";
1274 p.setEncoding("UTF-8");
1275 registerAutomaticallyLoadedPackage("CJKutf8");
1278 else if (name == "fontenc") {
1279 h_fontencoding = getStringFromVector(options, ",");
1283 else if (name == "inputenc" || name == "luainputenc") {
1284 // h_inputencoding is only set when there is not more than one
1285 // inputenc option because otherwise h_inputencoding must be
1286 // set to "auto-legacy" (the default encodings of the document's languages)
1287 // Therefore check that exactly one option is passed to inputenc.
1288 // It is also only set when there is not more than one babel
1290 if (!options.empty()) {
1291 string const encoding = options.back();
1292 Encoding const * const enc = encodings.fromLaTeXName(
1293 encoding, Encoding::inputenc, true);
1295 if (!detectEncoding)
1296 cerr << "Unknown encoding " << encoding
1297 << ". Ignoring." << std::endl;
1299 if (!enc->unsafe() && options.size() == 1 && one_language == true)
1300 h_inputencoding = enc->name();
1301 p.setEncoding(enc->iconvName());
1307 else if (name == "srcltx") {
1308 h_output_sync = "1";
1309 if (!opts.empty()) {
1310 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
1313 h_output_sync_macro = "\\usepackage{srcltx}";
1316 else if (is_known(name, known_old_language_packages)) {
1317 // known language packages from the times before babel
1318 // if they are found and not also babel, they will be used as
1319 // custom language package
1320 h_language_package = "\\usepackage{" + name + "}";
1323 else if (name == "lyxskak") {
1324 // ignore this and its options
1325 const char * const o[] = {"ps", "mover", 0};
1326 delete_opt(options, o);
1329 else if (is_known(name, known_lyx_packages) && options.empty()) {
1330 if (name == "splitidx")
1331 h_use_indices = "true";
1332 else if (name == "minted")
1333 h_use_minted = true;
1334 else if (name == "refstyle")
1335 h_use_refstyle = true;
1336 else if (name == "prettyref")
1337 h_use_refstyle = false;
1338 if (!in_lyx_preamble) {
1339 h_preamble << package_beg_sep << name
1340 << package_mid_sep << "\\usepackage{"
1342 if (p.next_token().cat() == catNewline ||
1343 (p.next_token().cat() == catSpace &&
1344 p.next_next_token().cat() == catNewline))
1346 h_preamble << package_end_sep;
1350 else if (name == "geometry")
1351 handle_geometry(options);
1353 else if (name == "subfig")
1354 ; // ignore this FIXME: Use the package separator mechanism instead
1356 else if (char const * const * where = is_known(name, known_languages))
1357 h_language = known_coded_languages[where - known_languages];
1359 else if (name == "natbib") {
1360 h_biblio_style = "plainnat";
1361 h_cite_engine = "natbib";
1362 h_cite_engine_type = "authoryear";
1363 vector<string>::iterator it =
1364 find(options.begin(), options.end(), "authoryear");
1365 if (it != options.end())
1368 it = find(options.begin(), options.end(), "numbers");
1369 if (it != options.end()) {
1370 h_cite_engine_type = "numerical";
1374 if (!options.empty())
1375 h_biblio_options = join(options, ",");
1378 else if (name == "biblatex") {
1379 h_biblio_style = "plainnat";
1380 h_cite_engine = "biblatex";
1381 h_cite_engine_type = "authoryear";
1383 vector<string>::iterator it =
1384 find(options.begin(), options.end(), "natbib");
1385 if (it != options.end()) {
1387 h_cite_engine = "biblatex-natbib";
1389 opt = process_keyval_opt(options, "natbib");
1391 h_cite_engine = "biblatex-natbib";
1393 opt = process_keyval_opt(options, "style");
1395 h_biblatex_citestyle = opt;
1396 h_biblatex_bibstyle = opt;
1398 opt = process_keyval_opt(options, "citestyle");
1400 h_biblatex_citestyle = opt;
1401 opt = process_keyval_opt(options, "bibstyle");
1403 h_biblatex_bibstyle = opt;
1405 opt = process_keyval_opt(options, "refsection");
1407 if (opt == "none" || opt == "part"
1408 || opt == "chapter" || opt == "section"
1409 || opt == "subsection")
1412 cerr << "Ignoring unkown refesection value '"
1415 opt = process_keyval_opt(options, "bibencoding");
1418 if (!options.empty()) {
1419 h_biblio_options = join(options, ",");
1424 else if (name == "jurabib") {
1425 h_biblio_style = "jurabib";
1426 h_cite_engine = "jurabib";
1427 h_cite_engine_type = "authoryear";
1428 if (!options.empty())
1429 h_biblio_options = join(options, ",");
1432 else if (name == "bibtopic")
1433 h_use_bibtopic = "true";
1435 else if (name == "chapterbib")
1436 h_multibib = "child";
1438 else if (name == "hyperref")
1439 handle_hyperref(options);
1441 else if (name == "algorithm2e") {
1442 // Load "algorithm2e" module
1443 addModule("algorithm2e");
1444 // Add the package options to the global document options
1445 if (!options.empty()) {
1446 if (h_options.empty())
1447 h_options = join(options, ",");
1449 h_options += ',' + join(options, ",");
1452 else if (name == "microtype") {
1453 //we internally support only microtype without params
1454 if (options.empty())
1455 h_use_microtype = "true";
1457 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1460 else if (!in_lyx_preamble) {
1461 if (options.empty())
1462 h_preamble << "\\usepackage{" << name << '}';
1464 h_preamble << "\\usepackage[" << opts << "]{"
1468 if (p.next_token().cat() == catNewline ||
1469 (p.next_token().cat() == catSpace &&
1470 p.next_next_token().cat() == catNewline))
1474 // We need to do something with the options...
1475 if (!options.empty() && !detectEncoding)
1476 cerr << "Ignoring options '" << join(options, ",")
1477 << "' of package " << name << '.' << endl;
1479 // remove the whitespace
1484 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1487 Token t = p.get_token();
1488 if (t.cat() == catEscape &&
1489 is_known(t.cs(), known_if_commands))
1490 handle_if(p, in_lyx_preamble);
1492 if (!in_lyx_preamble)
1493 h_preamble << t.asInput();
1494 if (t.cat() == catEscape && t.cs() == "fi")
1501 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1503 if (contains(h_float_placement, "H"))
1504 registerAutomaticallyLoadedPackage("float");
1505 if (h_spacing != "single" && h_spacing != "default")
1506 registerAutomaticallyLoadedPackage("setspace");
1507 if (h_use_packages["amsmath"] == "2") {
1508 // amsbsy and amstext are already provided by amsmath
1509 registerAutomaticallyLoadedPackage("amsbsy");
1510 registerAutomaticallyLoadedPackage("amstext");
1513 // output the LyX file settings
1514 // Important: Keep the version formatting in sync with LyX and
1515 // lyx2lyx (bug 7951)
1516 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1517 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1518 << lyx_version_minor << '\n'
1519 << "\\lyxformat " << LYX_FORMAT << '\n'
1520 << "\\begin_document\n"
1521 << "\\begin_header\n"
1522 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1523 << "\\origin " << origin << "\n"
1524 << "\\textclass " << h_textclass << "\n";
1525 string const raw = subdoc ? empty_string() : h_preamble.str();
1527 os << "\\begin_preamble\n";
1528 for (string::size_type i = 0; i < raw.size(); ++i) {
1529 if (raw[i] == package_beg_sep) {
1530 // Here follows some package loading code that
1531 // must be skipped if the package is loaded
1533 string::size_type j = raw.find(package_mid_sep, i);
1534 if (j == string::npos)
1536 string::size_type k = raw.find(package_end_sep, j);
1537 if (k == string::npos)
1539 string const package = raw.substr(i + 1, j - i - 1);
1540 string const replacement = raw.substr(j + 1, k - j - 1);
1541 if (auto_packages.find(package) == auto_packages.end())
1547 os << "\n\\end_preamble\n";
1549 if (!h_options.empty())
1550 os << "\\options " << h_options << "\n";
1551 os << "\\use_default_options " << h_use_default_options << "\n";
1552 if (!used_modules.empty()) {
1553 os << "\\begin_modules\n";
1554 vector<string>::const_iterator const end = used_modules.end();
1555 vector<string>::const_iterator it = used_modules.begin();
1556 for (; it != end; ++it)
1558 os << "\\end_modules\n";
1560 if (!h_includeonlys.empty()) {
1561 os << "\\begin_includeonly\n";
1562 for (auto const & iofile : h_includeonlys)
1563 os << iofile << '\n';
1564 os << "\\end_includeonly\n";
1566 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1567 << "\\language " << h_language << "\n"
1568 << "\\language_package " << h_language_package << "\n"
1569 << "\\inputencoding " << h_inputencoding << "\n"
1570 << "\\fontencoding " << h_fontencoding << "\n"
1571 << "\\font_roman \"" << h_font_roman[0]
1572 << "\" \"" << h_font_roman[1] << "\"\n"
1573 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1574 << "\\font_typewriter \"" << h_font_typewriter[0]
1575 << "\" \"" << h_font_typewriter[1] << "\"\n"
1576 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1577 << "\\font_default_family " << h_font_default_family << "\n"
1578 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1579 << "\\font_sc " << h_font_sc << "\n"
1580 << "\\font_osf " << h_font_osf << "\n";
1581 if (!h_font_roman_opts.empty())
1582 os << "\\font_roman_opts \"" << h_font_roman_opts << "\"" << '\n';
1583 os << "\\font_sf_scale " << h_font_sf_scale[0]
1584 << ' ' << h_font_sf_scale[1] << '\n';
1585 if (!h_font_sans_opts.empty())
1586 os << "\\font_sans_opts \"" << h_font_sans_opts << "\"" << '\n';
1587 os << "\\font_tt_scale " << h_font_tt_scale[0]
1588 << ' ' << h_font_tt_scale[1] << '\n';
1589 if (!h_font_cjk.empty())
1590 os << "\\font_cjk " << h_font_cjk << '\n';
1591 if (!h_font_typewriter_opts.empty())
1592 os << "\\font_typewriter_opts \"" << h_font_typewriter_opts << "\"" << '\n';
1593 os << "\\use_microtype " << h_use_microtype << '\n'
1594 << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
1595 << "\\graphics " << h_graphics << '\n'
1596 << "\\default_output_format " << h_default_output_format << "\n"
1597 << "\\output_sync " << h_output_sync << "\n";
1598 if (h_output_sync == "1")
1599 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1600 os << "\\bibtex_command " << h_bibtex_command << "\n"
1601 << "\\index_command " << h_index_command << "\n";
1602 if (!h_float_placement.empty())
1603 os << "\\float_placement " << h_float_placement << "\n";
1604 os << "\\paperfontsize " << h_paperfontsize << "\n"
1605 << "\\spacing " << h_spacing << "\n"
1606 << "\\use_hyperref " << h_use_hyperref << '\n';
1607 if (h_use_hyperref == "true") {
1608 if (!h_pdf_title.empty())
1609 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1610 if (!h_pdf_author.empty())
1611 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1612 if (!h_pdf_subject.empty())
1613 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1614 if (!h_pdf_keywords.empty())
1615 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1616 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1617 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1618 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1619 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1620 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1621 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1622 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1623 "\\pdf_backref " << h_pdf_backref << "\n"
1624 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1625 if (!h_pdf_pagemode.empty())
1626 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1627 if (!h_pdf_quoted_options.empty())
1628 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1630 os << "\\papersize " << h_papersize << "\n"
1631 << "\\use_geometry " << h_use_geometry << '\n';
1632 for (map<string, string>::const_iterator it = h_use_packages.begin();
1633 it != h_use_packages.end(); ++it)
1634 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1635 os << "\\cite_engine " << h_cite_engine << '\n'
1636 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1637 << "\\biblio_style " << h_biblio_style << "\n"
1638 << "\\use_bibtopic " << h_use_bibtopic << "\n";
1639 if (!h_biblio_options.empty())
1640 os << "\\biblio_options " << h_biblio_options << "\n";
1641 if (!h_biblatex_bibstyle.empty())
1642 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
1643 if (!h_biblatex_citestyle.empty())
1644 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
1645 if (!h_multibib.empty())
1646 os << "\\multibib " << h_multibib << "\n";
1647 os << "\\use_indices " << h_use_indices << "\n"
1648 << "\\paperorientation " << h_paperorientation << '\n'
1649 << "\\suppress_date " << h_suppress_date << '\n'
1650 << "\\justification " << h_justification << '\n'
1651 << "\\use_refstyle " << h_use_refstyle << '\n'
1652 << "\\use_minted " << h_use_minted << '\n';
1653 if (!h_fontcolor.empty())
1654 os << "\\fontcolor " << h_fontcolor << '\n';
1655 if (!h_notefontcolor.empty())
1656 os << "\\notefontcolor " << h_notefontcolor << '\n';
1657 if (!h_backgroundcolor.empty())
1658 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1659 if (!h_boxbgcolor.empty())
1660 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1661 if (index_number != 0)
1662 for (int i = 0; i < index_number; i++) {
1663 os << "\\index " << h_index[i] << '\n'
1664 << "\\shortcut " << h_shortcut[i] << '\n'
1665 << "\\color " << h_color << '\n'
1669 os << "\\index " << h_index[0] << '\n'
1670 << "\\shortcut " << h_shortcut[0] << '\n'
1671 << "\\color " << h_color << '\n'
1675 << "\\secnumdepth " << h_secnumdepth << "\n"
1676 << "\\tocdepth " << h_tocdepth << "\n"
1677 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1678 if (h_paragraph_separation == "skip")
1679 os << "\\defskip " << h_defskip << "\n";
1681 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1682 os << "\\is_math_indent " << h_is_mathindent << "\n";
1683 if (!h_mathindentation.empty())
1684 os << "\\math_indentation " << h_mathindentation << "\n";
1685 os << "\\math_numbering_side " << h_math_numbering_side << "\n";
1686 os << "\\quotes_style " << h_quotes_style << "\n"
1687 << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
1688 << "\\papercolumns " << h_papercolumns << "\n"
1689 << "\\papersides " << h_papersides << "\n"
1690 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1691 if (!h_listings_params.empty())
1692 os << "\\listings_params " << h_listings_params << "\n";
1693 os << "\\tracking_changes " << h_tracking_changes << "\n"
1694 << "\\output_changes " << h_output_changes << "\n"
1695 << "\\html_math_output " << h_html_math_output << "\n"
1696 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1697 << "\\html_be_strict " << h_html_be_strict << "\n"
1699 << "\\end_header\n\n"
1700 << "\\begin_body\n";
1705 void Preamble::parse(Parser & p, string const & forceclass,
1706 TeX2LyXDocClass & tc)
1708 // initialize fixed types
1709 special_columns_['D'] = 3;
1710 parse(p, forceclass, false, tc);
1714 void Preamble::parse(Parser & p, string const & forceclass,
1715 bool detectEncoding, TeX2LyXDocClass & tc)
1717 bool is_full_document = false;
1718 bool is_lyx_file = false;
1719 bool in_lyx_preamble = false;
1721 // determine whether this is a full document or a fragment for inclusion
1723 Token const & t = p.get_token();
1725 if (t.cat() == catEscape && t.cs() == "documentclass") {
1726 is_full_document = true;
1732 if (detectEncoding && !is_full_document)
1735 while (is_full_document && p.good()) {
1736 if (detectEncoding && h_inputencoding != "auto-legacy" &&
1737 h_inputencoding != "auto-legacy-plain")
1740 Token const & t = p.get_token();
1743 if (!detectEncoding)
1744 cerr << "t: " << t << '\n';
1750 if (!in_lyx_preamble &&
1751 (t.cat() == catLetter ||
1752 t.cat() == catSuper ||
1753 t.cat() == catSub ||
1754 t.cat() == catOther ||
1755 t.cat() == catMath ||
1756 t.cat() == catActive ||
1757 t.cat() == catBegin ||
1758 t.cat() == catEnd ||
1759 t.cat() == catAlign ||
1760 t.cat() == catParameter)) {
1761 h_preamble << t.cs();
1765 if (!in_lyx_preamble &&
1766 (t.cat() == catSpace || t.cat() == catNewline)) {
1767 h_preamble << t.asInput();
1771 if (t.cat() == catComment) {
1772 static regex const islyxfile("%% LyX .* created this file");
1773 static regex const usercommands("User specified LaTeX commands");
1775 string const comment = t.asInput();
1777 // magically switch encoding default if it looks like XeLaTeX
1778 static string const magicXeLaTeX =
1779 "% This document must be compiled with XeLaTeX ";
1780 if (comment.size() > magicXeLaTeX.size()
1781 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1782 && h_inputencoding == "auto-legacy") {
1783 if (!detectEncoding)
1784 cerr << "XeLaTeX comment found, switching to UTF8\n";
1785 h_inputencoding = "utf8";
1788 if (regex_search(comment, sub, islyxfile)) {
1790 in_lyx_preamble = true;
1791 } else if (is_lyx_file
1792 && regex_search(comment, sub, usercommands))
1793 in_lyx_preamble = false;
1794 else if (!in_lyx_preamble)
1795 h_preamble << t.asInput();
1799 if (t.cs() == "PassOptionsToPackage") {
1800 string const poptions = p.getArg('{', '}');
1801 string const package = p.verbatim_item();
1802 extra_package_options_.insert(make_pair(package, poptions));
1806 if (t.cs() == "pagestyle") {
1807 h_paperpagestyle = p.verbatim_item();
1811 if (t.cs() == "setdefaultlanguage") {
1813 // We don't yet care about non-language variant options
1814 // because LyX doesn't support this yet, see bug #8214
1816 string langopts = p.getOpt();
1817 // check if the option contains a variant, if yes, extract it
1818 string::size_type pos_var = langopts.find("variant");
1819 string::size_type i = langopts.find(',', pos_var);
1820 string::size_type k = langopts.find('=', pos_var);
1821 if (pos_var != string::npos){
1823 if (i == string::npos)
1824 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1826 variant = langopts.substr(k + 1, i - k - 1);
1827 h_language = variant;
1831 h_language = p.verbatim_item();
1832 //finally translate the poyglossia name to a LyX name
1833 h_language = polyglossia2lyx(h_language);
1837 if (t.cs() == "setotherlanguage") {
1838 // We don't yet care about the option because LyX doesn't
1839 // support this yet, see bug #8214
1840 p.hasOpt() ? p.getOpt() : string();
1845 if (t.cs() == "setmainfont") {
1846 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
1847 h_font_roman[1] = p.getArg('{', '}');
1848 if (!fontopts.empty()) {
1849 vector<string> opts = getVectorFromString(fontopts);
1851 for (auto const & opt : opts) {
1852 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
1855 if (!fontopts.empty())
1859 h_font_roman_opts = fontopts;
1864 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1865 // LyX currently only supports the scale option
1866 string scale, fontopts;
1868 fontopts = 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 (prefixIs(opt, "Scale=")) {
1877 scale_as_percentage(opt, scale);
1880 if (!fontopts.empty())
1886 if (t.cs() == "setsansfont") {
1888 h_font_sf_scale[1] = scale;
1889 h_font_sans[1] = p.getArg('{', '}');
1890 if (!fontopts.empty())
1891 h_font_sans_opts = fontopts;
1894 h_font_tt_scale[1] = scale;
1895 h_font_typewriter[1] = p.getArg('{', '}');
1896 if (!fontopts.empty())
1897 h_font_typewriter_opts = fontopts;
1902 if (t.cs() == "babelfont") {
1904 h_use_non_tex_fonts = true;
1905 h_language_package = "babel";
1906 if (h_inputencoding == "auto-legacy")
1907 p.setEncoding("UTF-8");
1908 // we don't care about the lang option
1909 string const lang = p.hasOpt() ? p.getArg('[', ']') : string();
1910 string const family = p.getArg('{', '}');
1911 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
1912 string const fontname = p.getArg('{', '}');
1913 if (lang.empty() && family == "rm") {
1914 h_font_roman[1] = fontname;
1915 if (!fontopts.empty()) {
1916 vector<string> opts = getVectorFromString(fontopts);
1918 for (auto const & opt : opts) {
1919 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
1922 if (!fontopts.empty())
1926 h_font_roman_opts = fontopts;
1929 } else if (lang.empty() && (family == "sf" || family == "tt")) {
1930 // LyX currently only supports the scale option
1932 if (!fontopts.empty()) {
1933 vector<string> opts = getVectorFromString(fontopts);
1935 for (auto const & opt : opts) {
1936 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
1939 if (prefixIs(opt, "Scale=")) {
1940 scale_as_percentage(opt, scale);
1943 if (!fontopts.empty())
1948 if (family == "sf") {
1950 h_font_sf_scale[1] = scale;
1951 h_font_sans[1] = fontname;
1952 if (!fontopts.empty())
1953 h_font_sans_opts = fontopts;
1956 h_font_tt_scale[1] = scale;
1957 h_font_typewriter[1] = fontname;
1958 if (!fontopts.empty())
1959 h_font_typewriter_opts = fontopts;
1963 // not rm, sf or tt or lang specific
1964 h_preamble << '\\' << t.cs();
1966 h_preamble << '[' << lang << ']';
1967 h_preamble << '{' << family << '}';
1968 if (!fontopts.empty())
1969 h_preamble << '[' << fontopts << ']';
1970 h_preamble << '{' << fontname << '}' << '\n';
1975 if (t.cs() == "date") {
1976 string argument = p.getArg('{', '}');
1977 if (argument.empty())
1978 h_suppress_date = "true";
1980 h_preamble << t.asInput() << '{' << argument << '}';
1984 if (t.cs() == "color") {
1985 string const space =
1986 (p.hasOpt() ? p.getOpt() : string());
1987 string argument = p.getArg('{', '}');
1988 // check the case that a standard color is used
1989 if (space.empty() && is_known(argument, known_basic_colors)) {
1990 h_fontcolor = rgbcolor2code(argument);
1991 registerAutomaticallyLoadedPackage("color");
1992 } else if (space.empty() && argument == "document_fontcolor")
1993 registerAutomaticallyLoadedPackage("color");
1994 // check the case that LyX's document_fontcolor is defined
1995 // but not used for \color
1997 h_preamble << t.asInput();
1999 h_preamble << space;
2000 h_preamble << '{' << argument << '}';
2001 // the color might already be set because \definecolor
2002 // is parsed before this
2008 if (t.cs() == "pagecolor") {
2009 string argument = p.getArg('{', '}');
2010 // check the case that a standard color is used
2011 if (is_known(argument, known_basic_colors)) {
2012 h_backgroundcolor = rgbcolor2code(argument);
2013 } else if (argument == "page_backgroundcolor")
2014 registerAutomaticallyLoadedPackage("color");
2015 // check the case that LyX's page_backgroundcolor is defined
2016 // but not used for \pagecolor
2018 h_preamble << t.asInput() << '{' << argument << '}';
2019 // the color might already be set because \definecolor
2020 // is parsed before this
2021 h_backgroundcolor = "";
2026 if (t.cs() == "makeatletter") {
2027 // LyX takes care of this
2028 p.setCatcode('@', catLetter);
2032 if (t.cs() == "makeatother") {
2033 // LyX takes care of this
2034 p.setCatcode('@', catOther);
2038 if (t.cs() == "makeindex") {
2039 // LyX will re-add this if a print index command is found
2044 if (t.cs() == "newindex") {
2045 string const indexname = p.getArg('[', ']');
2046 string const shortcut = p.verbatim_item();
2047 if (!indexname.empty())
2048 h_index[index_number] = indexname;
2050 h_index[index_number] = shortcut;
2051 h_shortcut[index_number] = shortcut;
2057 if (t.cs() == "addbibresource") {
2058 string const options = p.getArg('[', ']');
2059 string const arg = removeExtension(p.getArg('{', '}'));
2060 if (!options.empty()) {
2061 // check if the option contains a bibencoding, if yes, extract it
2062 string::size_type pos = options.find("bibencoding=");
2064 if (pos != string::npos) {
2065 string::size_type i = options.find(',', pos);
2066 if (i == string::npos)
2067 encoding = options.substr(pos + 1);
2069 encoding = options.substr(pos, i - pos);
2070 pos = encoding.find('=');
2071 if (pos == string::npos)
2074 encoding = encoding.substr(pos + 1);
2076 if (!encoding.empty())
2077 biblatex_encodings.push_back(normalize_filename(arg) + ' ' + encoding);
2079 biblatex_bibliographies.push_back(arg);
2083 if (t.cs() == "bibliography") {
2084 vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
2085 biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
2089 if (t.cs() == "RS@ifundefined") {
2090 string const name = p.verbatim_item();
2091 string const body1 = p.verbatim_item();
2092 string const body2 = p.verbatim_item();
2093 // only non-lyxspecific stuff
2094 if (in_lyx_preamble &&
2095 (name == "subsecref" || name == "thmref" || name == "lemref"))
2099 ss << '\\' << t.cs();
2100 ss << '{' << name << '}'
2101 << '{' << body1 << '}'
2102 << '{' << body2 << '}';
2103 h_preamble << ss.str();
2108 if (t.cs() == "AtBeginDocument") {
2109 string const name = p.verbatim_item();
2110 // only non-lyxspecific stuff
2111 if (in_lyx_preamble &&
2112 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
2113 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
2114 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
2115 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
2116 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
2117 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
2118 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
2119 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
2120 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
2121 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
2122 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
2123 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
2124 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
2125 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
2126 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
2130 ss << '\\' << t.cs();
2131 ss << '{' << name << '}';
2132 h_preamble << ss.str();
2137 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
2138 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
2139 || t.cs() == "providecommand" || t.cs() == "providecommandx"
2140 || t.cs() == "DeclareRobustCommand"
2141 || t.cs() == "DeclareRobustCommandx"
2142 || t.cs() == "ProvideTextCommandDefault"
2143 || t.cs() == "DeclareMathAccent") {
2145 if (p.next_token().character() == '*') {
2149 string const name = p.verbatim_item();
2150 string const opt1 = p.getFullOpt();
2151 string const opt2 = p.getFullOpt();
2152 string const body = p.verbatim_item();
2153 // store the in_lyx_preamble setting
2154 bool const was_in_lyx_preamble = in_lyx_preamble;
2156 if (name == "\\rmdefault")
2157 if (is_known(body, known_roman_font_packages)) {
2158 h_font_roman[0] = body;
2160 in_lyx_preamble = true;
2162 if (name == "\\sfdefault")
2163 if (is_known(body, known_sans_font_packages)) {
2164 h_font_sans[0] = body;
2166 in_lyx_preamble = true;
2168 if (name == "\\ttdefault")
2169 if (is_known(body, known_typewriter_font_packages)) {
2170 h_font_typewriter[0] = body;
2172 in_lyx_preamble = true;
2174 if (name == "\\familydefault") {
2175 string family = body;
2176 // remove leading "\"
2177 h_font_default_family = family.erase(0,1);
2179 in_lyx_preamble = true;
2182 // remove LyX-specific definitions that are re-added by LyX
2184 // \lyxline is an ancient command that is converted by tex2lyx into
2185 // a \rule therefore remove its preamble code
2186 if (name == "\\lyxdot" || name == "\\lyxarrow"
2187 || name == "\\lyxline" || name == "\\LyX") {
2189 in_lyx_preamble = true;
2192 // Add the command to the known commands
2193 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
2195 // only non-lyxspecific stuff
2196 if (!in_lyx_preamble) {
2198 ss << '\\' << t.cs();
2201 ss << '{' << name << '}' << opt1 << opt2
2202 << '{' << body << "}";
2203 if (prefixIs(t.cs(), "renew") || !contains(h_preamble.str(), ss.str()))
2204 h_preamble << ss.str();
2206 ostream & out = in_preamble ? h_preamble : os;
2207 out << "\\" << t.cs() << "{" << name << "}"
2208 << opts << "{" << body << "}";
2211 // restore the in_lyx_preamble setting
2212 in_lyx_preamble = was_in_lyx_preamble;
2216 if (t.cs() == "documentclass") {
2217 vector<string>::iterator it;
2218 vector<string> opts = split_options(p.getArg('[', ']'));
2219 handle_opt(opts, known_fontsizes, h_paperfontsize);
2220 delete_opt(opts, known_fontsizes);
2221 // delete "pt" at the end
2222 string::size_type i = h_paperfontsize.find("pt");
2223 if (i != string::npos)
2224 h_paperfontsize.erase(i);
2225 // The documentclass options are always parsed before the options
2226 // of the babel call so that a language cannot overwrite the babel
2228 handle_opt(opts, known_languages, h_language);
2229 delete_opt(opts, known_languages);
2232 if ((it = find(opts.begin(), opts.end(), "fleqn"))
2234 h_is_mathindent = "1";
2237 // formula numbering side
2238 if ((it = find(opts.begin(), opts.end(), "leqno"))
2240 h_math_numbering_side = "left";
2243 else if ((it = find(opts.begin(), opts.end(), "reqno"))
2245 h_math_numbering_side = "right";
2249 // paper orientation
2250 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
2251 h_paperorientation = "landscape";
2255 if ((it = find(opts.begin(), opts.end(), "oneside"))
2260 if ((it = find(opts.begin(), opts.end(), "twoside"))
2266 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
2268 h_papercolumns = "1";
2271 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
2273 h_papercolumns = "2";
2277 // some size options are known to any document classes, other sizes
2278 // are handled by the \geometry command of the geometry package
2279 handle_opt(opts, known_class_paper_sizes, h_papersize);
2280 delete_opt(opts, known_class_paper_sizes);
2281 // the remaining options
2282 h_options = join(opts, ",");
2283 // FIXME This does not work for classes that have a
2284 // different name in LyX than in LaTeX
2285 h_textclass = p.getArg('{', '}');
2290 if (t.cs() == "usepackage") {
2291 string const options = p.getArg('[', ']');
2292 string const name = p.getArg('{', '}');
2293 vector<string> vecnames;
2294 split(name, vecnames, ',');
2295 vector<string>::const_iterator it = vecnames.begin();
2296 vector<string>::const_iterator end = vecnames.end();
2297 for (; it != end; ++it)
2298 handle_package(p, trimSpaceAndEol(*it), options,
2299 in_lyx_preamble, detectEncoding);
2303 if (t.cs() == "inputencoding") {
2304 string const encoding = p.getArg('{','}');
2305 Encoding const * const enc = encodings.fromLaTeXName(
2306 encoding, Encoding::inputenc, true);
2308 if (!detectEncoding)
2309 cerr << "Unknown encoding " << encoding
2310 << ". Ignoring." << std::endl;
2313 h_inputencoding = enc->name();
2314 p.setEncoding(enc->iconvName());
2319 if (t.cs() == "newenvironment") {
2320 string const name = p.getArg('{', '}');
2321 string const opt1 = p.getFullOpt();
2322 string const opt2 = p.getFullOpt();
2323 string const beg = p.verbatim_item();
2324 string const end = p.verbatim_item();
2325 if (!in_lyx_preamble) {
2326 h_preamble << "\\newenvironment{" << name
2327 << '}' << opt1 << opt2 << '{'
2328 << beg << "}{" << end << '}';
2330 add_known_environment(name, opt1, !opt2.empty(),
2331 from_utf8(beg), from_utf8(end));
2335 if (t.cs() == "newtheorem") {
2337 if (p.next_token().character() == '*') {
2341 string const name = p.getArg('{', '}');
2342 string const opt1 = p.getFullOpt();
2343 string const opt2 = p.getFullOpt();
2344 string const body = p.verbatim_item();
2345 string const opt3 = p.getFullOpt();
2346 string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
2348 string const complete = cmd + "{" + name + '}' +
2349 opt1 + opt2 + '{' + body + '}' + opt3;
2351 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
2353 if (!in_lyx_preamble)
2354 h_preamble << complete;
2358 if (t.cs() == "def") {
2359 string name = p.get_token().cs();
2360 // In fact, name may be more than the name:
2361 // In the test case of bug 8116
2362 // name == "csname SF@gobble@opt \endcsname".
2363 // Therefore, we need to use asInput() instead of cs().
2364 while (p.next_token().cat() != catBegin)
2365 name += p.get_token().asInput();
2366 if (!in_lyx_preamble)
2367 h_preamble << "\\def\\" << name << '{'
2368 << p.verbatim_item() << "}";
2372 if (t.cs() == "newcolumntype") {
2373 string const name = p.getArg('{', '}');
2374 trimSpaceAndEol(name);
2376 string opts = p.getOpt();
2377 if (!opts.empty()) {
2378 istringstream is(string(opts, 1));
2381 special_columns_[name[0]] = nargs;
2382 h_preamble << "\\newcolumntype{" << name << "}";
2384 h_preamble << "[" << nargs << "]";
2385 h_preamble << "{" << p.verbatim_item() << "}";
2389 if (t.cs() == "setcounter") {
2390 string const name = p.getArg('{', '}');
2391 string const content = p.getArg('{', '}');
2392 if (name == "secnumdepth")
2393 h_secnumdepth = content;
2394 else if (name == "tocdepth")
2395 h_tocdepth = content;
2397 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
2401 if (t.cs() == "setlength") {
2402 string const name = p.verbatim_item();
2403 string const content = p.verbatim_item();
2404 // the paragraphs are only not indented when \parindent is set to zero
2405 if (name == "\\parindent" && content != "") {
2406 if (content[0] == '0')
2407 h_paragraph_separation = "skip";
2409 h_paragraph_indentation = translate_len(content);
2410 } else if (name == "\\parskip") {
2411 if (content == "\\smallskipamount")
2412 h_defskip = "smallskip";
2413 else if (content == "\\medskipamount")
2414 h_defskip = "medskip";
2415 else if (content == "\\bigskipamount")
2416 h_defskip = "bigskip";
2418 h_defskip = translate_len(content);
2419 } else if (name == "\\mathindent") {
2420 h_mathindentation = translate_len(content);
2422 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2426 if (t.cs() == "onehalfspacing") {
2427 h_spacing = "onehalf";
2431 if (t.cs() == "doublespacing") {
2432 h_spacing = "double";
2436 if (t.cs() == "setstretch") {
2437 h_spacing = "other " + p.verbatim_item();
2441 if (t.cs() == "synctex") {
2442 // the scheme is \synctex=value
2443 // where value can only be "1" or "-1"
2444 h_output_sync = "1";
2445 // there can be any character behind the value (e.g. a linebreak or a '\'
2446 // therefore we extract it char by char
2448 string value = p.get_token().asInput();
2450 value += p.get_token().asInput();
2451 h_output_sync_macro = "\\synctex=" + value;
2455 if (t.cs() == "begin") {
2456 string const name = p.getArg('{', '}');
2457 if (name == "document")
2459 h_preamble << "\\begin{" << name << "}";
2463 if (t.cs() == "geometry") {
2464 vector<string> opts = split_options(p.getArg('{', '}'));
2465 handle_geometry(opts);
2469 if (t.cs() == "definecolor") {
2470 string const color = p.getArg('{', '}');
2471 string const space = p.getArg('{', '}');
2472 string const value = p.getArg('{', '}');
2473 if (color == "document_fontcolor" && space == "rgb") {
2474 RGBColor c(RGBColorFromLaTeX(value));
2475 h_fontcolor = X11hexname(c);
2476 } else if (color == "note_fontcolor" && space == "rgb") {
2477 RGBColor c(RGBColorFromLaTeX(value));
2478 h_notefontcolor = X11hexname(c);
2479 } else if (color == "page_backgroundcolor" && space == "rgb") {
2480 RGBColor c(RGBColorFromLaTeX(value));
2481 h_backgroundcolor = X11hexname(c);
2482 } else if (color == "shadecolor" && space == "rgb") {
2483 RGBColor c(RGBColorFromLaTeX(value));
2484 h_boxbgcolor = X11hexname(c);
2486 h_preamble << "\\definecolor{" << color
2487 << "}{" << space << "}{" << value
2493 if (t.cs() == "bibliographystyle") {
2494 h_biblio_style = p.verbatim_item();
2498 if (t.cs() == "jurabibsetup") {
2499 // FIXME p.getArg('{', '}') is most probably wrong (it
2500 // does not handle nested braces).
2501 // Use p.verbatim_item() instead.
2502 vector<string> jurabibsetup =
2503 split_options(p.getArg('{', '}'));
2504 // add jurabibsetup to the jurabib package options
2505 add_package("jurabib", jurabibsetup);
2506 if (!jurabibsetup.empty()) {
2507 h_preamble << "\\jurabibsetup{"
2508 << join(jurabibsetup, ",") << '}';
2513 if (t.cs() == "hypersetup") {
2514 vector<string> hypersetup =
2515 split_options(p.verbatim_item());
2516 // add hypersetup to the hyperref package options
2517 handle_hyperref(hypersetup);
2518 if (!hypersetup.empty()) {
2519 h_preamble << "\\hypersetup{"
2520 << join(hypersetup, ",") << '}';
2525 if (t.cs() == "includeonly") {
2526 vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
2527 for (auto & iofile : includeonlys) {
2528 string filename(normalize_filename(iofile));
2529 string const path = getMasterFilePath(true);
2530 // We want to preserve relative/absolute filenames,
2531 // therefore path is only used for testing
2532 if (!makeAbsPath(filename, path).exists()) {
2533 // The file extension is probably missing.
2534 // Now try to find it out.
2535 string const tex_name =
2536 find_file(filename, path,
2537 known_tex_extensions);
2538 if (!tex_name.empty())
2539 filename = tex_name;
2542 if (makeAbsPath(filename, path).exists())
2543 fix_child_filename(filename);
2545 cerr << "Warning: Could not find included file '"
2546 << filename << "'." << endl;
2547 outname = changeExtension(filename, "lyx");
2548 h_includeonlys.push_back(outname);
2553 if (is_known(t.cs(), known_if_3arg_commands)) {
2554 // prevent misparsing of \usepackage if it is used
2555 // as an argument (see e.g. our own output of
2556 // \@ifundefined above)
2557 string const arg1 = p.verbatim_item();
2558 string const arg2 = p.verbatim_item();
2559 string const arg3 = p.verbatim_item();
2560 // test case \@ifundefined{date}{}{\date{}}
2561 if (t.cs() == "@ifundefined" && arg1 == "date" &&
2562 arg2.empty() && arg3 == "\\date{}") {
2563 h_suppress_date = "true";
2564 // older tex2lyx versions did output
2565 // \@ifundefined{definecolor}{\usepackage{color}}{}
2566 } else if (t.cs() == "@ifundefined" &&
2567 arg1 == "definecolor" &&
2568 arg2 == "\\usepackage{color}" &&
2570 if (!in_lyx_preamble)
2571 h_preamble << package_beg_sep
2574 << "\\@ifundefined{definecolor}{color}{}"
2577 //\@ifundefined{showcaptionsetup}{}{%
2578 // \PassOptionsToPackage{caption=false}{subfig}}
2579 // that LyX uses for subfloats
2580 } else if (t.cs() == "@ifundefined" &&
2581 arg1 == "showcaptionsetup" && arg2.empty()
2582 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
2584 } else if (!in_lyx_preamble) {
2585 h_preamble << t.asInput()
2586 << '{' << arg1 << '}'
2587 << '{' << arg2 << '}'
2588 << '{' << arg3 << '}';
2593 if (is_known(t.cs(), known_if_commands)) {
2594 // must not parse anything in conditional code, since
2595 // LyX would output the parsed contents unconditionally
2596 if (!in_lyx_preamble)
2597 h_preamble << t.asInput();
2598 handle_if(p, in_lyx_preamble);
2602 if (!t.cs().empty() && !in_lyx_preamble) {
2603 h_preamble << '\\' << t.cs();
2608 // remove the whitespace
2611 // Force textclass if the user wanted it
2612 if (!forceclass.empty())
2613 h_textclass = forceclass;
2614 tc.setName(h_textclass);
2615 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2616 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2619 if (h_papersides.empty()) {
2622 h_papersides = ss.str();
2625 // If the CJK package is used we cannot set the document language from
2626 // the babel options. Instead, we guess which language is used most
2627 // and set this one.
2628 default_language = h_language;
2629 if (is_full_document &&
2630 (auto_packages.find("CJK") != auto_packages.end() ||
2631 auto_packages.find("CJKutf8") != auto_packages.end())) {
2633 h_language = guessLanguage(p, default_language);
2635 if (explicit_babel && h_language != default_language) {
2636 // We set the document language to a CJK language,
2637 // but babel is explicitly called in the user preamble
2638 // without options. LyX will not add the default
2639 // language to the document options if it is either
2640 // english, or no text is set as default language.
2641 // Therefore we need to add a language option explicitly.
2642 // FIXME: It would be better to remove all babel calls
2643 // from the user preamble, but this is difficult
2644 // without re-introducing bug 7861.
2645 if (h_options.empty())
2646 h_options = lyx2babel(default_language);
2648 h_options += ',' + lyx2babel(default_language);
2652 // Finally, set the quote style.
2653 // LyX knows the following quotes styles:
2654 // british, cjk, cjkangle, danish, english, french, german,
2655 // polish, russian, swedish and swiss
2656 // conversion list taken from
2657 // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
2658 // (quotes for kazakh are unknown)
2660 if (is_known(h_language, known_british_quotes_languages))
2661 h_quotes_style = "british";
2663 else if (is_known(h_language, known_cjk_quotes_languages))
2664 h_quotes_style = "cjk";
2666 else if (is_known(h_language, known_cjkangle_quotes_languages))
2667 h_quotes_style = "cjkangle";
2669 else if (is_known(h_language, known_danish_quotes_languages))
2670 h_quotes_style = "danish";
2672 else if (is_known(h_language, known_french_quotes_languages))
2673 h_quotes_style = "french";
2675 else if (is_known(h_language, known_german_quotes_languages))
2676 h_quotes_style = "german";
2678 else if (is_known(h_language, known_polish_quotes_languages))
2679 h_quotes_style = "polish";
2681 else if (is_known(h_language, known_russian_quotes_languages))
2682 h_quotes_style = "russian";
2684 else if (is_known(h_language, known_swedish_quotes_languages))
2685 h_quotes_style = "swedish";
2687 else if (is_known(h_language, known_swiss_quotes_languages))
2688 h_quotes_style = "swiss";
2690 else if (is_known(h_language, known_english_quotes_languages))
2691 h_quotes_style = "english";
2695 string Preamble::parseEncoding(Parser & p, string const & forceclass)
2697 TeX2LyXDocClass dummy;
2698 parse(p, forceclass, true, dummy);
2699 if (h_inputencoding != "auto-legacy" && h_inputencoding != "auto-legacy-plain")
2700 return h_inputencoding;
2705 string babel2lyx(string const & language)
2707 char const * const * where = is_known(language, known_languages);
2709 return known_coded_languages[where - known_languages];
2714 string lyx2babel(string const & language)
2716 char const * const * where = is_known(language, known_coded_languages);
2718 return known_languages[where - known_coded_languages];
2723 string Preamble::polyglossia2lyx(string const & language)
2725 char const * const * where = is_known(language, polyglossia_languages);
2727 return coded_polyglossia_languages[where - polyglossia_languages];
2732 string rgbcolor2code(string const & name)
2734 char const * const * where = is_known(name, known_basic_colors);
2736 // "red", "green" etc
2737 return known_basic_color_codes[where - known_basic_colors];
2739 // "255,0,0", "0,255,0" etc
2740 RGBColor c(RGBColorFromLaTeX(name));
2741 return X11hexname(c);