3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
9 * Full author contact details are available in file CREDITS.
20 #include "LayoutFile.h"
23 #include "TextClass.h"
26 #include "support/convert.h"
27 #include "support/FileName.h"
28 #include "support/filetools.h"
29 #include "support/lstrings.h"
31 #include "support/regex.h"
37 using namespace lyx::support;
46 // CJK languages are handled in text.cpp, polyglossia languages are listed
49 * known babel language names (including synonyms)
50 * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
51 * please keep this in sync with known_coded_languages line by line!
53 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
54 "american", "arabic", "arabtex", "australian", "austrian", "azerbaijani", "bahasa", "bahasai",
55 "bahasam", "basque", "belarusian", "bosnian", "brazil", "brazilian", "breton", "british",
56 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
57 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "francais",
58 "french", "frenchb", "frenchle", "frenchpro", "friulan", "galician", "german", "germanb",
59 "georgian", "greek", "hebrew", "hungarian", "icelandic", "indon", "indonesian",
60 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
61 "latvian", "lithuanian", "lowersorbian", "lsorbian", "macedonian", "magyar", "malay", "meyalu",
62 "mongolian", "naustrian", "newzealand", "ngerman", "ngermanb", "norsk", "nswissgerman",
63 "nynorsk", "piedmontese", "polutonikogreek", "polish", "portuges", "portuguese",
64 "romanian", "romansh", "russian", "russianb", "samin", "scottish", "serbian", "serbian-latin",
65 "slovak", "slovene", "spanish", "swedish", "swissgerman", "thai", "turkish", "turkmen",
66 "ukraineb", "ukrainian", "uppersorbian", "UKenglish", "USenglish", "usorbian",
71 * the same as known_languages with .lyx names
72 * please keep this in sync with known_languages line by line!
74 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
75 "american", "arabic_arabi", "arabic_arabtex", "australian", "austrian", "azerbaijani", "bahasa", "bahasa",
76 "bahasam", "basque", "belarusian", "bosnian", "brazilian", "brazilian", "breton", "british",
77 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
78 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "french",
79 "french", "french", "french", "french", "friulan", "galician", "german", "german",
80 "georgian", "greek", "hebrew", "magyar", "icelandic", "bahasa", "bahasa",
81 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
82 "latvian", "lithuanian", "lowersorbian", "lowersorbian", "macedonian", "magyar", "bahasam", "bahasam",
83 "mongolian", "naustrian", "newzealand", "ngerman", "ngerman", "norsk", "german-ch",
84 "nynorsk", "piedmontese", "polutonikogreek", "polish", "portuguese", "portuguese",
85 "romanian", "romansh", "russian", "russian", "samin", "scottish", "serbian", "serbian-latin",
86 "slovak", "slovene", "spanish", "swedish", "german-ch-old", "thai", "turkish", "turkmen",
87 "ukrainian", "ukrainian", "uppersorbian", "english", "english", "uppersorbian",
88 "vietnamese", "welsh",
91 /// languages with british quotes (.lyx names)
92 const char * const known_british_quotes_languages[] = {"british", "welsh", 0};
94 /// languages with cjk quotes (.lyx names)
95 const char * const known_cjk_quotes_languages[] = {"chinese-traditional",
96 "japanese", "japanese-cjk", 0};
98 /// languages with cjk-angle quotes (.lyx names)
99 const char * const known_cjkangle_quotes_languages[] = {"korean", 0};
101 /// languages with danish quotes (.lyx names)
102 const char * const known_danish_quotes_languages[] = {"danish", 0};
104 /// languages with english quotes (.lyx names)
105 const char * const known_english_quotes_languages[] = {"american", "australian",
106 "bahasa", "bahasam", "bengali", "brazilian", "canadian", "chinese-simplified", "english",
107 "esperanto", "farsi", "interlingua", "irish", "newzealand", "scottish",
108 "thai", "turkish", "vietnamese", 0};
110 /// languages with french quotes (.lyx names)
111 const char * const known_french_quotes_languages[] = {"ancientgreek",
112 "arabic_arabi", "arabic_arabtex", "asturian", "belarusian", "breton",
113 "canadien", "catalan", "french", "friulan", "galician", "italian", "occitan",
114 "piedmontese", "portuguese", "spanish", "spanish-mexico", 0};
116 /// languages with german quotes (.lyx names)
117 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
118 "czech", "estonian", "georgian", "german", "icelandic", "latvian", "lithuanian",
119 "lowersorbian", "macedonian", "naustrian", "ngerman", "romansh", "slovak", "slovene",
122 /// languages with polish quotes (.lyx names)
123 const char * const known_polish_quotes_languages[] = {"afrikaans", "bosnian", "croatian",
124 "dutch", "magyar", "polish", "romanian", "serbian", "serbian-latin", 0};
126 /// languages with russian quotes (.lyx names)
127 const char * const known_russian_quotes_languages[] = {"azerbaijani", "oldrussian",
128 "russian", "ukrainian", 0};
130 /// languages with swedish quotes (.lyx names)
131 const char * const known_swedish_quotes_languages[] = {"finnish", "swedish", 0};
133 /// languages with swiss quotes (.lyx names)
134 const char * const known_swiss_quotes_languages[] = {"albanian",
135 "armenian", "basque", "churchslavonic", "german-ch", "german-ch-old",
136 "norsk", "nynorsk", "turkmen", "ukrainian", "vietnamese", 0};
138 /// known language packages from the times before babel
139 const char * const known_old_language_packages[] = {"french", "frenchle",
140 "frenchpro", "german", "ngerman", "pmfrench", 0};
142 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
144 const char * const known_roman_font_packages[] = { "ae", "beraserif", "bookman",
145 "ccfonts", "chancery", "charter", "cmr", "cochineal", "crimson", "DejaVuSerif", "DejaVuSerifCondensed", "fourier",
146 "garamondx", "libertine", "libertineRoman", "libertine-type1", "lmodern", "mathdesign", "mathpazo",
147 "mathptmx", "MinionPro", "newcent", "noto", "noto-serif", "PTSerif", "tgbonum", "tgchorus",
148 "tgpagella", "tgschola", "tgtermes", "utopia", "xcharter", 0 };
150 const char * const known_sans_font_packages[] = { "avant", "berasans", "biolinum",
151 "biolinum-type1", "cmbr", "cmss", "DejaVuSans", "DejaVuSansCondensed", "helvet", "iwona", "iwonac", "iwonal", "iwonalc",
152 "kurier", "kurierc", "kurierl", "kurierlc", "lmss", "noto", "noto-sans", "PTSans",
153 "tgadventor", "tgheros", "uop", 0 };
155 const char * const known_typewriter_font_packages[] = { "beramono", "cmtl", "cmtt",
156 "courier", "DejaVuSansMono", "lmtt", "luximono", "fourier", "libertineMono", "libertineMono-type1", "lmodern",
157 "mathpazo", "mathptmx", "newcent", "noto", "noto-mono", "PTMono", "tgcursor", "txtt", 0 };
159 const char * const known_math_font_packages[] = { "eulervm", "newtxmath", 0};
161 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
162 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
163 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
164 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
165 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
167 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
168 "executivepaper", "legalpaper", "letterpaper", 0};
170 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
171 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
173 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
174 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
177 /// commands that can start an \if...\else...\endif sequence
178 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
179 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
180 "ifsidecap", "ifupgreek", 0};
182 const char * const known_basic_colors[] = {"black", "blue", "brown", "cyan",
183 "darkgray", "gray", "green", "lightgray", "lime", "magenta", "orange", "olive",
184 "pink", "purple", "red", "teal", "violet", "white", "yellow", 0};
186 const char * const known_basic_color_codes[] = {"#000000", "#0000ff", "#964B00", "#00ffff",
187 "#a9a9a9", "#808080", "#00ff00", "#d3d3d3", "#bfff00", "#ff00ff", "#ff7f00", "#808000",
188 "#ffc0cb", "#800080", "#ff0000", "#008080", "#8f00ff", "#ffffff", "#ffff00", 0};
190 /// conditional commands with three arguments like \@ifundefined{}{}{}
191 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
195 * Known file extensions for TeX files as used by \\includeonly
197 char const * const known_tex_extensions[] = {"tex", 0};
199 /// packages that work only in xetex
200 /// polyglossia is handled separately
201 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
202 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
203 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
205 /// packages that are automatically skipped if loaded by LyX
206 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
207 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color",
208 "float", "fontspec", "framed", "graphicx", "hhline", "ifthen", "longtable",
209 "makeidx", "minted", "multirow", "nomencl", "pdfpages", "prettyref", "refstyle",
210 "rotating", "rotfloat", "splitidx", "setspace", "subscript", "tabularx","textcomp", "tipa",
211 "tipx", "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xcolor", "xltabular",
214 // codes used to remove packages that are loaded automatically by LyX.
215 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
216 const char package_beg_sep = '\001';
217 const char package_mid_sep = '\002';
218 const char package_end_sep = '\003';
221 // returns true if at least one of the options in what has been found
222 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
228 // the last language option is the document language (for babel and LyX)
229 // the last size option is the document font size
230 vector<string>::iterator it;
231 vector<string>::iterator position = opts.begin();
232 for (; *what; ++what) {
233 it = find(opts.begin(), opts.end(), *what);
234 if (it != opts.end()) {
235 if (it >= position) {
246 void delete_opt(vector<string> & opts, char const * const * what)
251 // remove found options from the list
252 // do this after handle_opt to avoid potential memory leaks
253 vector<string>::iterator it;
254 for (; *what; ++what) {
255 it = find(opts.begin(), opts.end(), *what);
256 if (it != opts.end())
263 * Split a package options string (keyval format) into a vector.
265 * authorformat=smallcaps,
267 * titleformat=colonsep,
268 * bibformat={tabular,ibidem,numbered}
270 vector<string> split_options(string const & input)
272 vector<string> options;
276 Token const & t = p.get_token();
277 if (t.asInput() == ",") {
278 options.push_back(trimSpaceAndEol(option));
280 } else if (t.asInput() == "=") {
283 if (p.next_token().asInput() == "{")
284 option += '{' + p.getArg('{', '}') + '}';
285 } else if (t.cat() != catSpace && t.cat() != catComment)
286 option += t.asInput();
290 options.push_back(trimSpaceAndEol(option));
297 * Retrieve a keyval option "name={value with=sign}" named \p name from
298 * \p options and return the value.
299 * The found option is also removed from \p options.
301 string process_keyval_opt(vector<string> & options, string name)
303 for (size_t i = 0; i < options.size(); ++i) {
304 vector<string> option;
305 split(options[i], option, '=');
306 if (option.size() < 2)
308 if (option[0] == name) {
309 options.erase(options.begin() + i);
310 option.erase(option.begin());
311 return join(option, "=");
317 } // anonymous namespace
321 * known polyglossia language names (including variants)
322 * FIXME: support spelling=old for german variants (german vs. ngerman LyX names etc)
324 const char * const Preamble::polyglossia_languages[] = {
325 "albanian", "american", "amharic", "ancient", "arabic", "armenian", "asturian", "australian",
326 "bahasai", "bahasam", "basque", "bengali", "brazil", "brazilian", "breton", "british", "bulgarian",
327 "catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
328 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
329 "galician", "greek", "monotonic", "hebrew", "hindi",
330 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean",
331 "lao", "latin", "latvian", "lithuanian", "lsorbian", "magyar", "malayalam", "marathi",
332 "austrian", "newzealand", "german", "norsk", "nynorsk", "occitan", "oldrussian",
333 "piedmontese", "polish", "polytonic", "portuges", "romanian", "romansh", "russian",
334 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovenian", "spanish", "swedish", "syriac",
335 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
336 "ukrainian", "urdu", "usorbian", "vietnamese", "welsh", 0};
337 // not yet supported by LyX: "korean", "nko"
340 * the same as polyglossia_languages with .lyx names
341 * please keep this in sync with polyglossia_languages line by line!
343 const char * const Preamble::coded_polyglossia_languages[] = {
344 "albanian", "american", "amharic", "ancientgreek", "arabic_arabi", "armenian", "asturian", "australian",
345 "bahasa", "bahasam", "basque", "bengali", "brazilian", "brazilian", "breton", "british", "bulgarian",
346 "catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
347 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
348 "galician", "greek", "greek", "hebrew", "hindi",
349 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean",
350 "lao", "latin", "latvian", "lithuanian", "lowersorbian", "magyar", "malayalam", "marathi",
351 "naustrian","newzealand", "ngerman", "norsk", "nynorsk", "occitan", "oldrussian",
352 "piedmontese", "polish", "polutonikogreek", "portuges", "romanian", "romansh", "russian",
353 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovene", "spanish", "swedish", "syriac",
354 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
355 "ukrainian", "urdu", "uppersorbian", "vietnamese", "welsh", 0};
356 // not yet supported by LyX: "korean-polyglossia", "nko"
359 bool Preamble::usePolyglossia() const
361 return h_use_non_tex_fonts && h_language_package == "default";
365 bool Preamble::indentParagraphs() const
367 return h_paragraph_separation == "indent";
371 bool Preamble::isPackageUsed(string const & package) const
373 return used_packages.find(package) != used_packages.end();
377 bool Preamble::isPackageAutoLoaded(string const & package) const
379 return auto_packages.find(package) != auto_packages.end();
383 vector<string> Preamble::getPackageOptions(string const & package) const
385 map<string, vector<string> >::const_iterator it = used_packages.find(package);
386 if (it != used_packages.end())
388 return vector<string>();
392 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
394 auto_packages.insert(package);
398 void Preamble::addModule(string const & module)
400 for (auto const & m : used_modules) {
404 used_modules.push_back(module);
408 void Preamble::suppressDate(bool suppress)
411 h_suppress_date = "true";
413 h_suppress_date = "false";
417 void Preamble::registerAuthor(std::string const & name)
419 Author author(from_utf8(name), empty_docstring());
420 author.setUsed(true);
421 authors_.record(author);
422 h_tracking_changes = "true";
423 h_output_changes = "true";
427 Author const & Preamble::getAuthor(std::string const & name) const
429 Author author(from_utf8(name), empty_docstring());
430 for (AuthorList::Authors::const_iterator it = authors_.begin();
431 it != authors_.end(); ++it)
434 static Author const dummy;
439 int Preamble::getSpecialTableColumnArguments(char c) const
441 map<char, int>::const_iterator it = special_columns_.find(c);
442 if (it == special_columns_.end())
448 void Preamble::add_package(string const & name, vector<string> & options)
450 // every package inherits the global options
451 if (used_packages.find(name) == used_packages.end())
452 used_packages[name] = split_options(h_options);
454 // Insert options passed via PassOptionsToPackage
455 for (auto const & p : extra_package_options_) {
456 if (p.first == name) {
457 vector<string> eo = getVectorFromString(p.second);
458 for (auto const & eoi : eo)
459 options.push_back(eoi);
463 vector<string> & v = used_packages[name];
464 v.insert(v.end(), options.begin(), options.end());
465 if (name == "jurabib") {
466 // Don't output the order argument (see the cite command
467 // handling code in text.cpp).
468 vector<string>::iterator end =
469 remove(options.begin(), options.end(), "natbiborder");
470 end = remove(options.begin(), end, "jurabiborder");
471 options.erase(end, options.end());
478 // Given is a string like "scaled=0.9" or "Scale=0.9", return 0.9 * 100
479 bool scale_as_percentage(string const & scale, string & percentage)
481 string::size_type pos = scale.find('=');
482 if (pos != string::npos) {
483 string value = scale.substr(pos + 1);
484 if (isStrDbl(value)) {
485 percentage = convert<string>(
486 static_cast<int>(100 * convert<double>(value)));
494 string remove_braces(string const & value)
498 if (value[0] == '{' && value[value.length()-1] == '}')
499 return value.substr(1, value.length()-2);
503 } // anonymous namespace
506 Preamble::Preamble() : one_language(true), explicit_babel(false),
507 title_layout_found(false), index_number(0), h_font_cjk_set(false)
511 h_biblio_style = "plain";
512 h_bibtex_command = "default";
513 h_cite_engine = "basic";
514 h_cite_engine_type = "default";
516 h_defskip = "medskip";
517 h_dynamic_quotes = false;
520 h_fontencoding = "default";
521 h_font_roman[0] = "default";
522 h_font_roman[1] = "default";
523 h_font_sans[0] = "default";
524 h_font_sans[1] = "default";
525 h_font_typewriter[0] = "default";
526 h_font_typewriter[1] = "default";
527 h_font_math[0] = "auto";
528 h_font_math[1] = "auto";
529 h_font_default_family = "default";
530 h_use_non_tex_fonts = false;
532 h_font_roman_osf = "false";
533 h_font_sans_osf = "false";
534 h_font_typewriter_osf = "false";
535 h_font_sf_scale[0] = "100";
536 h_font_sf_scale[1] = "100";
537 h_font_tt_scale[0] = "100";
538 h_font_tt_scale[1] = "100";
539 // h_font_roman_opts;
541 // h_font_typewriter_opts;
543 h_is_mathindent = "0";
544 h_math_numbering_side = "default";
545 h_graphics = "default";
546 h_default_output_format = "default";
547 h_html_be_strict = "false";
548 h_html_css_as_file = "0";
549 h_html_math_output = "0";
550 h_index[0] = "Index";
551 h_index_command = "default";
552 h_inputencoding = "auto-legacy";
553 h_justification = "true";
554 h_language = "english";
555 h_language_package = "none";
557 h_maintain_unincluded_children = "false";
561 h_output_changes = "false";
563 //h_output_sync_macro
564 h_papercolumns = "1";
565 h_paperfontsize = "default";
566 h_paperorientation = "portrait";
567 h_paperpagestyle = "default";
569 h_papersize = "default";
570 h_paragraph_indentation = "default";
571 h_paragraph_separation = "indent";
576 h_pdf_bookmarks = "0";
577 h_pdf_bookmarksnumbered = "0";
578 h_pdf_bookmarksopen = "0";
579 h_pdf_bookmarksopenlevel = "1";
580 h_pdf_breaklinks = "0";
581 h_pdf_pdfborder = "0";
582 h_pdf_colorlinks = "0";
583 h_pdf_backref = "section";
584 h_pdf_pdfusetitle = "0";
586 //h_pdf_quoted_options;
587 h_quotes_style = "english";
589 h_shortcut[0] = "idx";
590 h_spacing = "single";
591 h_save_transient_properties = "true";
592 h_suppress_date = "false";
593 h_textclass = "article";
595 h_tracking_changes = "false";
596 h_use_bibtopic = "false";
597 h_use_dash_ligatures = "true";
598 h_use_indices = "false";
599 h_use_geometry = "false";
600 h_use_default_options = "false";
601 h_use_hyperref = "false";
602 h_use_microtype = "false";
603 h_use_lineno = "false";
604 h_use_refstyle = false;
605 h_use_minted = false;
606 h_use_packages["amsmath"] = "1";
607 h_use_packages["amssymb"] = "0";
608 h_use_packages["cancel"] = "0";
609 h_use_packages["esint"] = "1";
610 h_use_packages["mhchem"] = "0";
611 h_use_packages["mathdots"] = "0";
612 h_use_packages["mathtools"] = "0";
613 h_use_packages["stackrel"] = "0";
614 h_use_packages["stmaryrd"] = "0";
615 h_use_packages["undertilde"] = "0";
619 void Preamble::handle_hyperref(vector<string> & options)
621 // FIXME swallow inputencoding changes that might surround the
622 // hyperref setup if it was written by LyX
623 h_use_hyperref = "true";
624 // swallow "unicode=true", since LyX does always write that
625 vector<string>::iterator it =
626 find(options.begin(), options.end(), "unicode=true");
627 if (it != options.end())
629 it = find(options.begin(), options.end(), "pdfusetitle");
630 if (it != options.end()) {
631 h_pdf_pdfusetitle = "1";
634 string bookmarks = process_keyval_opt(options, "bookmarks");
635 if (bookmarks == "true")
636 h_pdf_bookmarks = "1";
637 else if (bookmarks == "false")
638 h_pdf_bookmarks = "0";
639 if (h_pdf_bookmarks == "1") {
640 string bookmarksnumbered =
641 process_keyval_opt(options, "bookmarksnumbered");
642 if (bookmarksnumbered == "true")
643 h_pdf_bookmarksnumbered = "1";
644 else if (bookmarksnumbered == "false")
645 h_pdf_bookmarksnumbered = "0";
646 string bookmarksopen =
647 process_keyval_opt(options, "bookmarksopen");
648 if (bookmarksopen == "true")
649 h_pdf_bookmarksopen = "1";
650 else if (bookmarksopen == "false")
651 h_pdf_bookmarksopen = "0";
652 if (h_pdf_bookmarksopen == "1") {
653 string bookmarksopenlevel =
654 process_keyval_opt(options, "bookmarksopenlevel");
655 if (!bookmarksopenlevel.empty())
656 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
659 string breaklinks = process_keyval_opt(options, "breaklinks");
660 if (breaklinks == "true")
661 h_pdf_breaklinks = "1";
662 else if (breaklinks == "false")
663 h_pdf_breaklinks = "0";
664 string pdfborder = process_keyval_opt(options, "pdfborder");
665 if (pdfborder == "{0 0 0}")
666 h_pdf_pdfborder = "1";
667 else if (pdfborder == "{0 0 1}")
668 h_pdf_pdfborder = "0";
669 string backref = process_keyval_opt(options, "backref");
670 if (!backref.empty())
671 h_pdf_backref = backref;
672 string colorlinks = process_keyval_opt(options, "colorlinks");
673 if (colorlinks == "true")
674 h_pdf_colorlinks = "1";
675 else if (colorlinks == "false")
676 h_pdf_colorlinks = "0";
677 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
678 if (!pdfpagemode.empty())
679 h_pdf_pagemode = pdfpagemode;
680 string pdftitle = process_keyval_opt(options, "pdftitle");
681 if (!pdftitle.empty()) {
682 h_pdf_title = remove_braces(pdftitle);
684 string pdfauthor = process_keyval_opt(options, "pdfauthor");
685 if (!pdfauthor.empty()) {
686 h_pdf_author = remove_braces(pdfauthor);
688 string pdfsubject = process_keyval_opt(options, "pdfsubject");
689 if (!pdfsubject.empty())
690 h_pdf_subject = remove_braces(pdfsubject);
691 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
692 if (!pdfkeywords.empty())
693 h_pdf_keywords = remove_braces(pdfkeywords);
694 if (!options.empty()) {
695 if (!h_pdf_quoted_options.empty())
696 h_pdf_quoted_options += ',';
697 h_pdf_quoted_options += join(options, ",");
703 void Preamble::handle_geometry(vector<string> & options)
705 h_use_geometry = "true";
706 vector<string>::iterator it;
708 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
709 h_paperorientation = "landscape";
713 // keyval version: "paper=letter"
714 string paper = process_keyval_opt(options, "paper");
716 h_papersize = paper + "paper";
717 // alternative version: "letterpaper"
718 handle_opt(options, known_paper_sizes, h_papersize);
719 delete_opt(options, known_paper_sizes);
721 char const * const * margin = known_paper_margins;
722 for (; *margin; ++margin) {
723 string value = process_keyval_opt(options, *margin);
724 if (!value.empty()) {
725 int k = margin - known_paper_margins;
726 string name = known_coded_paper_margins[k];
727 h_margins += '\\' + name + ' ' + value + '\n';
733 void Preamble::handle_package(Parser &p, string const & name,
734 string const & opts, bool in_lyx_preamble,
737 vector<string> options = split_options(opts);
738 add_package(name, options);
740 if (is_known(name, known_xetex_packages)) {
742 h_use_non_tex_fonts = true;
743 registerAutomaticallyLoadedPackage("fontspec");
744 if (h_inputencoding == "auto-legacy")
745 p.setEncoding("UTF-8");
749 if (is_known(name, known_roman_font_packages))
750 h_font_roman[0] = name;
752 vector<string> allopts = getVectorFromString(opts);
755 if (name == "ccfonts") {
756 for (auto const & opt : allopts) {
762 h_font_roman_opts = xopts;
766 if (name == "lmodern") {
767 for (auto const & opt : allopts) {
773 h_font_roman_opts = xopts;
777 if (name == "fourier") {
778 h_font_roman[0] = "utopia";
779 // when font uses real small capitals
780 for (auto const & opt : allopts) {
782 h_font_roman_osf = "true";
785 if (opt == "expert") {
794 h_font_roman_opts = xopts;
798 if (name == "garamondx") {
799 h_font_roman[0] = "garamondx";
800 for (auto const & opt : allopts) {
802 h_font_roman_osf = "true";
810 h_font_roman_opts = xopts;
814 if (name == "libertine") {
815 h_font_roman[0] = "libertine";
816 // this automatically invokes biolinum
817 h_font_sans[0] = "biolinum";
818 // as well as libertineMono
819 h_font_typewriter[0] = "libertine-mono";
820 for (auto const & opt : allopts) {
822 h_font_roman_osf = "true";
825 if (opt == "lining") {
826 h_font_roman_osf = "false";
834 h_font_roman_opts = xopts;
838 if (name == "libertineRoman" || name == "libertine-type1") {
839 h_font_roman[0] = "libertine";
840 // NOTE: contrary to libertine.sty, libertineRoman
841 // and libertine-type1 do not automatically invoke
842 // biolinum and libertineMono
843 if (opts == "lining")
844 h_font_roman_osf = "false";
845 else if (opts == "osf")
846 h_font_roman_osf = "true";
849 if (name == "MinionPro") {
850 h_font_roman[0] = "minionpro";
851 h_font_roman_osf = "true";
852 h_font_math[0] = "auto";
853 for (auto const & opt : allopts) {
855 h_font_roman_osf = "false";
858 if (opt == "onlytext") {
859 h_font_math[0] = "default";
867 h_font_roman_opts = xopts;
871 if (name == "mathdesign") {
872 if (opts.find("charter") != string::npos)
873 h_font_roman[0] = "md-charter";
874 if (opts.find("garamond") != string::npos)
875 h_font_roman[0] = "md-garamond";
876 if (opts.find("utopia") != string::npos)
877 h_font_roman[0] = "md-utopia";
878 if (opts.find("expert") != string::npos) {
880 h_font_roman_osf = "true";
884 else if (name == "mathpazo") {
885 h_font_roman[0] = "palatino";
886 for (auto const & opt : allopts) {
888 h_font_roman_osf = "true";
900 h_font_roman_opts = xopts;
904 else if (name == "mathptmx") {
905 h_font_roman[0] = "times";
906 for (auto const & opt : allopts) {
912 h_font_roman_opts = xopts;
916 if (name == "crimson")
917 h_font_roman[0] = "cochineal";
919 if (name == "cochineal") {
920 h_font_roman[0] = "cochineal";
921 // cochineal can have several options, e.g. [proportional,osf]
922 for (auto const & opt : allopts) {
924 h_font_roman_osf = "true";
927 if (opt == "proportional")
934 h_font_roman_opts = xopts;
938 if (name == "noto") {
939 // noto can have several options
941 h_font_roman[0] = "NotoSerif-TLF";
942 string::size_type pos = opts.find("rm");
943 if (pos != string::npos)
944 h_font_roman[0] = "NotoSerif-TLF";
945 pos = opts.find("sf");
946 if (pos != string::npos)
947 h_font_sans[0] = "NotoSans-TLF";
948 pos = opts.find("nott");
949 if (pos != string::npos) {
950 h_font_roman[0] = "NotoSerif-TLF";
951 h_font_sans[0] = "NotoSans-TLF";
953 // noto as typewriter is handled in handling of \ttdefault
954 // special cases are handled in handling of \rmdefault and \sfdefault
955 for (auto const & opt : allopts) {
963 h_font_roman_osf = "true";
972 if (name == "paratype") {
973 // in this case all fonts are ParaType
974 h_font_roman[0] = "PTSerif-TLF";
975 h_font_sans[0] = "default";
976 h_font_typewriter[0] = "default";
979 if (name == "PTSerif")
980 h_font_roman[0] = "PTSerif-TLF";
982 if (name == "XCharter") {
983 h_font_roman[0] = "xcharter";
984 for (auto const & opt : allopts) {
986 h_font_roman_osf = "true";
994 h_font_roman_opts = xopts;
998 if (name == "plex-serif") {
1000 h_font_roman[0] = "IBMPlexSerif";
1001 else if (opts.find("thin") != string::npos)
1002 h_font_roman[0] = "IBMPlexSerifThin";
1003 else if (opts.find("extralight") != string::npos)
1004 h_font_roman[0] = "IBMPlexSerifExtraLight";
1005 else if (opts.find("light") != string::npos)
1006 h_font_roman[0] = "IBMPlexSerifLight";
1007 else if (opts.find("semibold") != string::npos)
1008 h_font_roman[0] = "IBMPlexSerifSemibold";
1009 for (auto const & opt : allopts) {
1012 if (opt == "extralight")
1016 if (opt == "semibold")
1023 h_font_roman_opts = xopts;
1026 if (name == "noto-serif") {
1027 h_font_roman[0] = "NotoSerifRegular";
1028 if (!opts.empty()) {
1029 if (opts.find("thin") != string::npos)
1030 h_font_roman[0] = "NotoSerifThin";
1031 else if (opts.find("medium") != string::npos)
1032 h_font_roman[0] = "NotoSerifMedium";
1033 else if (opts.find("extralight") != string::npos)
1034 h_font_roman[0] = "NotoSerifExtralight";
1035 else if (opts.find("light") != string::npos)
1036 h_font_roman[0] = "NotoSerifLight";
1038 for (auto const & opt : allopts) {
1039 if (opt == "regular")
1043 if (opt == "extralight")
1047 if (opt == "semibold")
1054 h_font_roman_opts = xopts;
1058 if (name == "sourceserifpro") {
1059 h_font_roman[0] = "ADOBESourceSerifPro";
1060 for (auto const & opt : allopts) {
1062 h_font_roman_osf = "true";
1070 h_font_roman_opts = xopts;
1075 if (is_known(name, known_sans_font_packages)) {
1076 h_font_sans[0] = name;
1077 if (options.size() >= 1) {
1078 if (scale_as_percentage(opts, h_font_sf_scale[0]))
1083 if (name == "biolinum" || name == "biolinum-type1") {
1084 h_font_sans[0] = "biolinum";
1085 // biolinum can have several options, e.g. [osf,scaled=0.97]
1086 string::size_type pos = opts.find("osf");
1087 if (pos != string::npos)
1088 h_font_sans_osf = "true";
1091 if (name == "PTSans") {
1092 h_font_sans[0] = "PTSans-TLF";
1093 if (options.size() >= 1) {
1094 if (scale_as_percentage(opts, h_font_sf_scale[0]))
1099 if (name == "plex-sans") {
1100 if (opts.find("condensed") != string::npos)
1101 h_font_sans[0] = "IBMPlexSansCondensed";
1102 else if (opts.find("thin") != string::npos)
1103 h_font_sans[0] = "IBMPlexSansThin";
1104 else if (opts.find("extralight") != string::npos)
1105 h_font_sans[0] = "IBMPlexSansExtraLight";
1106 else if (opts.find("light") != string::npos)
1107 h_font_sans[0] = "IBMPlexSansLight";
1108 else if (opts.find("semibold") != string::npos)
1109 h_font_sans[0] = "IBMPlexSansSemibold";
1111 h_font_sans[0] = "IBMPlexSans";
1112 for (auto const & opt : allopts) {
1115 if (opt == "extralight")
1119 if (opt == "semibold")
1121 if (prefixIs(opt, "scale=")) {
1122 scale_as_percentage(opt, h_font_sf_scale[0]);
1130 h_font_sans_opts = xopts;
1133 if (name == "noto-sans") {
1134 h_font_sans[0] = "NotoSansRegular";
1135 if (!opts.empty()) {
1136 if (opts.find("medium") != string::npos)
1137 h_font_sans[0] = "NotoSansMedium";
1138 else if (opts.find("thin") != string::npos)
1139 h_font_sans[0] = "NotoSansThin";
1140 else if (opts.find("extralight") != string::npos)
1141 h_font_sans[0] = "NotoSansExtralight";
1142 else if (opts.find("light") != string::npos)
1143 h_font_sans[0] = "NotoSansLight";
1145 for (auto const & opt : allopts) {
1146 if (opt == "regular")
1150 if (opt == "extralight")
1154 if (opt == "semibold")
1157 h_font_sans_osf = "true";
1165 h_font_sans_opts = xopts;
1169 if (name == "sourcesanspro") {
1170 h_font_sans[0] = "ADOBESourceSansPro";
1171 for (auto const & opt : allopts) {
1172 if (prefixIs(opt, "scaled=")) {
1173 scale_as_percentage(opt, h_font_sf_scale[0]);
1177 h_font_sans_osf = "true";
1185 h_font_sans_opts = xopts;
1190 if (is_known(name, known_typewriter_font_packages)) {
1191 // fourier can be set as roman font _only_
1192 // fourier as typewriter is handled in handling of \ttdefault
1193 if (name != "fourier") {
1194 h_font_typewriter[0] = name;
1195 if (options.size() >= 1) {
1196 if (scale_as_percentage(opts, h_font_tt_scale[0]))
1202 if (name == "libertineMono" || name == "libertineMono-type1")
1203 h_font_typewriter[0] = "libertine-mono";
1205 if (name == "PTMono") {
1206 h_font_typewriter[0] = "PTMono-TLF";
1207 if (options.size() >= 1) {
1208 if (scale_as_percentage(opts, h_font_tt_scale[0]))
1213 if (name == "plex-mono") {
1214 if (opts.find("thin") != string::npos)
1215 h_font_typewriter[0] = "IBMPlexMonoThin";
1216 else if (opts.find("extralight") != string::npos)
1217 h_font_typewriter[0] = "IBMPlexMonoExtraLight";
1218 else if (opts.find("light") != string::npos)
1219 h_font_typewriter[0] = "IBMPlexMonoLight";
1220 else if (opts.find("semibold") != string::npos)
1221 h_font_typewriter[0] = "IBMPlexMonoSemibold";
1223 h_font_typewriter[0] = "IBMPlexMono";
1224 for (auto const & opt : allopts) {
1227 if (opt == "extralight")
1231 if (opt == "semibold")
1233 if (prefixIs(opt, "scale=")) {
1234 scale_as_percentage(opt, h_font_tt_scale[0]);
1242 h_font_typewriter_opts = xopts;
1246 if (name == "noto-mono") {
1247 h_font_typewriter[0] = "NotoMonoRegular";
1248 for (auto const & opt : allopts) {
1249 if (opt == "regular")
1256 h_font_typewriter_opts = xopts;
1260 if (name == "sourcecodepro") {
1261 h_font_typewriter[0] = "ADOBESourceCodePro";
1262 for (auto const & opt : allopts) {
1263 if (prefixIs(opt, "scaled=")) {
1264 scale_as_percentage(opt, h_font_tt_scale[0]);
1268 h_font_typewriter_osf = "true";
1276 h_font_typewriter_opts = xopts;
1280 // font uses old-style figure
1282 h_font_roman_osf = "true";
1285 if (is_known(name, known_math_font_packages))
1286 h_font_math[0] = name;
1288 if (name == "newtxmath") {
1290 h_font_math[0] = "newtxmath";
1291 else if (opts == "garamondx")
1292 h_font_math[0] = "garamondx-ntxm";
1293 else if (opts == "libertine")
1294 h_font_math[0] = "libertine-ntxm";
1295 else if (opts == "minion")
1296 h_font_math[0] = "minion-ntxm";
1297 else if (opts == "cochineal")
1298 h_font_math[0] = "cochineal-ntxm";
1301 if (name == "iwona")
1303 h_font_math[0] = "iwona-math";
1305 if (name == "kurier")
1307 h_font_math[0] = "kurier-math";
1309 // after the detection and handling of special cases, we can remove the
1310 // fonts, otherwise they would appear in the preamble, see bug #7856
1311 if (is_known(name, known_roman_font_packages) || is_known(name, known_sans_font_packages)
1312 || is_known(name, known_typewriter_font_packages) || is_known(name, known_math_font_packages))
1314 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
1315 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
1316 name == "esint" || name == "mhchem" || name == "mathdots" ||
1317 name == "mathtools" || name == "stackrel" ||
1318 name == "stmaryrd" || name == "undertilde") {
1319 h_use_packages[name] = "2";
1320 registerAutomaticallyLoadedPackage(name);
1323 else if (name == "babel") {
1324 h_language_package = "default";
1325 // One might think we would have to do nothing if babel is loaded
1326 // without any options to prevent pollution of the preamble with this
1327 // babel call in every roundtrip.
1328 // But the user could have defined babel-specific things afterwards. So
1329 // we need to keep it in the preamble to prevent cases like bug #7861.
1330 if (!opts.empty()) {
1331 // check if more than one option was used - used later for inputenc
1332 if (options.begin() != options.end() - 1)
1333 one_language = false;
1334 // babel takes the last language of the option of its \usepackage
1335 // call as document language. If there is no such language option, the
1336 // last language in the documentclass options is used.
1337 handle_opt(options, known_languages, h_language);
1338 // translate the babel name to a LyX name
1339 h_language = babel2lyx(h_language);
1340 if (h_language == "japanese") {
1341 // For Japanese, the encoding isn't indicated in the source
1342 // file, and there's really not much we can do. We could
1343 // 1) offer a list of possible encodings to choose from, or
1344 // 2) determine the encoding of the file by inspecting it.
1345 // For the time being, we leave the encoding alone so that
1346 // we don't get iconv errors when making a wrong guess, and
1347 // we will output a note at the top of the document
1348 // explaining what to do.
1349 Encoding const * const enc = encodings.fromIconvName(
1350 p.getEncoding(), Encoding::japanese, false);
1352 h_inputencoding = enc->name();
1353 is_nonCJKJapanese = true;
1354 // in this case babel can be removed from the preamble
1355 registerAutomaticallyLoadedPackage("babel");
1357 // If babel is called with options, LyX puts them by default into the
1358 // document class options. This works for most languages, except
1359 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
1360 // perhaps in future others.
1361 // Therefore keep the babel call as it is as the user might have
1363 string const babelcall = "\\usepackage[" + opts + "]{babel}\n";
1364 if (!contains(h_preamble.str(), babelcall))
1365 h_preamble << babelcall;
1367 delete_opt(options, known_languages);
1369 if (!contains(h_preamble.str(), "\\usepackage{babel}\n"))
1370 h_preamble << "\\usepackage{babel}\n";
1371 explicit_babel = true;
1375 else if (name == "polyglossia") {
1376 h_language_package = "default";
1377 h_default_output_format = "pdf4";
1378 h_use_non_tex_fonts = true;
1380 registerAutomaticallyLoadedPackage("xunicode");
1381 if (h_inputencoding == "auto-legacy")
1382 p.setEncoding("UTF-8");
1385 else if (name == "CJK") {
1386 // set the encoding to "auto-legacy" because it might be set to "auto-legacy-plain" by the babel handling
1387 // and this would not be correct for CJK
1388 if (h_inputencoding == "auto-legacy-plain")
1389 h_inputencoding = "auto-legacy";
1390 registerAutomaticallyLoadedPackage("CJK");
1393 else if (name == "CJKutf8") {
1394 h_inputencoding = "utf8-cjk";
1395 p.setEncoding("UTF-8");
1396 registerAutomaticallyLoadedPackage("CJKutf8");
1399 else if (name == "fontenc") {
1400 h_fontencoding = getStringFromVector(options, ",");
1404 else if (name == "inputenc" || name == "luainputenc") {
1405 // h_inputencoding is only set when there is not more than one
1406 // inputenc option because otherwise h_inputencoding must be
1407 // set to "auto-legacy" (the default encodings of the document's languages)
1408 // Therefore check that exactly one option is passed to inputenc.
1409 // It is also only set when there is not more than one babel
1411 if (!options.empty()) {
1412 string const encoding = options.back();
1413 Encoding const * const enc = encodings.fromLaTeXName(
1414 encoding, Encoding::inputenc, true);
1416 if (!detectEncoding)
1417 cerr << "Unknown encoding " << encoding
1418 << ". Ignoring." << std::endl;
1420 if (!enc->unsafe() && options.size() == 1 && one_language == true)
1421 h_inputencoding = enc->name();
1422 p.setEncoding(enc->iconvName());
1428 else if (name == "srcltx") {
1429 h_output_sync = "1";
1430 if (!opts.empty()) {
1431 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
1434 h_output_sync_macro = "\\usepackage{srcltx}";
1437 else if (is_known(name, known_old_language_packages)) {
1438 // known language packages from the times before babel
1439 // if they are found and not also babel, they will be used as
1440 // custom language package
1441 h_language_package = "\\usepackage{" + name + "}";
1444 else if (name == "lyxskak") {
1445 // ignore this and its options
1446 const char * const o[] = {"ps", "mover", 0};
1447 delete_opt(options, o);
1450 else if (is_known(name, known_lyx_packages) && options.empty()) {
1451 if (name == "splitidx")
1452 h_use_indices = "true";
1453 else if (name == "minted")
1454 h_use_minted = true;
1455 else if (name == "refstyle")
1456 h_use_refstyle = true;
1457 else if (name == "prettyref")
1458 h_use_refstyle = false;
1459 if (!in_lyx_preamble) {
1460 h_preamble << package_beg_sep << name
1461 << package_mid_sep << "\\usepackage{"
1463 if (p.next_token().cat() == catNewline ||
1464 (p.next_token().cat() == catSpace &&
1465 p.next_next_token().cat() == catNewline))
1467 h_preamble << package_end_sep;
1471 else if (name == "geometry")
1472 handle_geometry(options);
1474 else if (name == "subfig")
1475 ; // ignore this FIXME: Use the package separator mechanism instead
1477 else if (char const * const * where = is_known(name, known_languages))
1478 h_language = known_coded_languages[where - known_languages];
1480 else if (name == "natbib") {
1481 h_biblio_style = "plainnat";
1482 h_cite_engine = "natbib";
1483 h_cite_engine_type = "authoryear";
1484 vector<string>::iterator it =
1485 find(options.begin(), options.end(), "authoryear");
1486 if (it != options.end())
1489 it = find(options.begin(), options.end(), "numbers");
1490 if (it != options.end()) {
1491 h_cite_engine_type = "numerical";
1495 if (!options.empty())
1496 h_biblio_options = join(options, ",");
1499 else if (name == "biblatex") {
1500 h_biblio_style = "plainnat";
1501 h_cite_engine = "biblatex";
1502 h_cite_engine_type = "authoryear";
1504 vector<string>::iterator it =
1505 find(options.begin(), options.end(), "natbib");
1506 if (it != options.end()) {
1508 h_cite_engine = "biblatex-natbib";
1510 opt = process_keyval_opt(options, "natbib");
1512 h_cite_engine = "biblatex-natbib";
1514 opt = process_keyval_opt(options, "style");
1516 h_biblatex_citestyle = opt;
1517 h_biblatex_bibstyle = opt;
1519 opt = process_keyval_opt(options, "citestyle");
1521 h_biblatex_citestyle = opt;
1522 opt = process_keyval_opt(options, "bibstyle");
1524 h_biblatex_bibstyle = opt;
1526 opt = process_keyval_opt(options, "refsection");
1528 if (opt == "none" || opt == "part"
1529 || opt == "chapter" || opt == "section"
1530 || opt == "subsection")
1533 cerr << "Ignoring unkown refesection value '"
1536 opt = process_keyval_opt(options, "bibencoding");
1539 if (!options.empty()) {
1540 h_biblio_options = join(options, ",");
1545 else if (name == "jurabib") {
1546 h_biblio_style = "jurabib";
1547 h_cite_engine = "jurabib";
1548 h_cite_engine_type = "authoryear";
1549 if (!options.empty())
1550 h_biblio_options = join(options, ",");
1553 else if (name == "bibtopic")
1554 h_use_bibtopic = "true";
1556 else if (name == "chapterbib")
1557 h_multibib = "child";
1559 else if (name == "hyperref")
1560 handle_hyperref(options);
1562 else if (name == "algorithm2e") {
1563 // Load "algorithm2e" module
1564 addModule("algorithm2e");
1565 // Add the package options to the global document options
1566 if (!options.empty()) {
1567 if (h_options.empty())
1568 h_options = join(options, ",");
1570 h_options += ',' + join(options, ",");
1573 else if (name == "microtype") {
1574 //we internally support only microtype without params
1575 if (options.empty())
1576 h_use_microtype = "true";
1578 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1581 else if (name == "lineno") {
1582 h_use_lineno = "true";
1583 if (!options.empty()) {
1584 h_lineno_options = join(options, ",");
1589 else if (!in_lyx_preamble) {
1590 if (options.empty())
1591 h_preamble << "\\usepackage{" << name << '}';
1593 h_preamble << "\\usepackage[" << opts << "]{"
1597 if (p.next_token().cat() == catNewline ||
1598 (p.next_token().cat() == catSpace &&
1599 p.next_next_token().cat() == catNewline))
1603 // We need to do something with the options...
1604 if (!options.empty() && !detectEncoding)
1605 cerr << "Ignoring options '" << join(options, ",")
1606 << "' of package " << name << '.' << endl;
1608 // remove the whitespace
1613 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1616 Token t = p.get_token();
1617 if (t.cat() == catEscape &&
1618 is_known(t.cs(), known_if_commands))
1619 handle_if(p, in_lyx_preamble);
1621 if (!in_lyx_preamble)
1622 h_preamble << t.asInput();
1623 if (t.cat() == catEscape && t.cs() == "fi")
1630 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1632 if (contains(h_float_placement, "H"))
1633 registerAutomaticallyLoadedPackage("float");
1634 if (h_spacing != "single" && h_spacing != "default")
1635 registerAutomaticallyLoadedPackage("setspace");
1636 if (h_use_packages["amsmath"] == "2") {
1637 // amsbsy and amstext are already provided by amsmath
1638 registerAutomaticallyLoadedPackage("amsbsy");
1639 registerAutomaticallyLoadedPackage("amstext");
1642 // output the LyX file settings
1643 // Important: Keep the version formatting in sync with LyX and
1644 // lyx2lyx (bug 7951)
1645 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1646 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1647 << lyx_version_minor << '\n'
1648 << "\\lyxformat " << LYX_FORMAT << '\n'
1649 << "\\begin_document\n"
1650 << "\\begin_header\n"
1651 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1652 << "\\origin " << origin << "\n"
1653 << "\\textclass " << h_textclass << "\n";
1654 string const raw = subdoc ? empty_string() : h_preamble.str();
1656 os << "\\begin_preamble\n";
1657 for (string::size_type i = 0; i < raw.size(); ++i) {
1658 if (raw[i] == package_beg_sep) {
1659 // Here follows some package loading code that
1660 // must be skipped if the package is loaded
1662 string::size_type j = raw.find(package_mid_sep, i);
1663 if (j == string::npos)
1665 string::size_type k = raw.find(package_end_sep, j);
1666 if (k == string::npos)
1668 string const package = raw.substr(i + 1, j - i - 1);
1669 string const replacement = raw.substr(j + 1, k - j - 1);
1670 if (auto_packages.find(package) == auto_packages.end())
1676 os << "\n\\end_preamble\n";
1678 if (!h_options.empty())
1679 os << "\\options " << h_options << "\n";
1680 os << "\\use_default_options " << h_use_default_options << "\n";
1681 if (!used_modules.empty()) {
1682 os << "\\begin_modules\n";
1683 vector<string>::const_iterator const end = used_modules.end();
1684 vector<string>::const_iterator it = used_modules.begin();
1685 for (; it != end; ++it)
1687 os << "\\end_modules\n";
1689 if (!h_includeonlys.empty()) {
1690 os << "\\begin_includeonly\n";
1691 for (auto const & iofile : h_includeonlys)
1692 os << iofile << '\n';
1693 os << "\\end_includeonly\n";
1695 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1696 << "\\language " << h_language << "\n"
1697 << "\\language_package " << h_language_package << "\n"
1698 << "\\inputencoding " << h_inputencoding << "\n"
1699 << "\\fontencoding " << h_fontencoding << "\n"
1700 << "\\font_roman \"" << h_font_roman[0]
1701 << "\" \"" << h_font_roman[1] << "\"\n"
1702 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1703 << "\\font_typewriter \"" << h_font_typewriter[0]
1704 << "\" \"" << h_font_typewriter[1] << "\"\n"
1705 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1706 << "\\font_default_family " << h_font_default_family << "\n"
1707 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1708 << "\\font_sc " << h_font_sc << "\n"
1709 << "\\font_roman_osf " << h_font_roman_osf << "\n"
1710 << "\\font_sans_osf " << h_font_sans_osf << "\n"
1711 << "\\font_typewriter_osf " << h_font_typewriter_osf << "\n";
1712 if (!h_font_roman_opts.empty())
1713 os << "\\font_roman_opts \"" << h_font_roman_opts << "\"" << '\n';
1714 os << "\\font_sf_scale " << h_font_sf_scale[0]
1715 << ' ' << h_font_sf_scale[1] << '\n';
1716 if (!h_font_sans_opts.empty())
1717 os << "\\font_sans_opts \"" << h_font_sans_opts << "\"" << '\n';
1718 os << "\\font_tt_scale " << h_font_tt_scale[0]
1719 << ' ' << h_font_tt_scale[1] << '\n';
1720 if (!h_font_cjk.empty())
1721 os << "\\font_cjk " << h_font_cjk << '\n';
1722 if (!h_font_typewriter_opts.empty())
1723 os << "\\font_typewriter_opts \"" << h_font_typewriter_opts << "\"" << '\n';
1724 os << "\\use_microtype " << h_use_microtype << '\n'
1725 << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
1726 << "\\graphics " << h_graphics << '\n'
1727 << "\\default_output_format " << h_default_output_format << "\n"
1728 << "\\output_sync " << h_output_sync << "\n";
1729 if (h_output_sync == "1")
1730 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1731 os << "\\bibtex_command " << h_bibtex_command << "\n"
1732 << "\\index_command " << h_index_command << "\n";
1733 if (!h_float_placement.empty())
1734 os << "\\float_placement " << h_float_placement << "\n";
1735 os << "\\paperfontsize " << h_paperfontsize << "\n"
1736 << "\\spacing " << h_spacing << "\n"
1737 << "\\use_hyperref " << h_use_hyperref << '\n';
1738 if (h_use_hyperref == "true") {
1739 if (!h_pdf_title.empty())
1740 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1741 if (!h_pdf_author.empty())
1742 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1743 if (!h_pdf_subject.empty())
1744 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1745 if (!h_pdf_keywords.empty())
1746 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1747 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1748 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1749 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1750 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1751 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1752 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1753 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1754 "\\pdf_backref " << h_pdf_backref << "\n"
1755 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1756 if (!h_pdf_pagemode.empty())
1757 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1758 if (!h_pdf_quoted_options.empty())
1759 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1761 os << "\\papersize " << h_papersize << "\n"
1762 << "\\use_geometry " << h_use_geometry << '\n';
1763 for (map<string, string>::const_iterator it = h_use_packages.begin();
1764 it != h_use_packages.end(); ++it)
1765 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1766 os << "\\cite_engine " << h_cite_engine << '\n'
1767 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1768 << "\\biblio_style " << h_biblio_style << "\n"
1769 << "\\use_bibtopic " << h_use_bibtopic << "\n";
1770 if (!h_biblio_options.empty())
1771 os << "\\biblio_options " << h_biblio_options << "\n";
1772 if (!h_biblatex_bibstyle.empty())
1773 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
1774 if (!h_biblatex_citestyle.empty())
1775 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
1776 if (!h_multibib.empty())
1777 os << "\\multibib " << h_multibib << "\n";
1778 os << "\\use_indices " << h_use_indices << "\n"
1779 << "\\paperorientation " << h_paperorientation << '\n'
1780 << "\\suppress_date " << h_suppress_date << '\n'
1781 << "\\justification " << h_justification << '\n'
1782 << "\\use_refstyle " << h_use_refstyle << '\n'
1783 << "\\use_minted " << h_use_minted << '\n'
1784 << "\\use_lineno " << h_use_lineno << '\n';
1785 if (!h_lineno_options.empty())
1786 os << "\\lineno_options " << h_lineno_options << '\n';
1787 if (!h_fontcolor.empty())
1788 os << "\\fontcolor " << h_fontcolor << '\n';
1789 if (!h_notefontcolor.empty())
1790 os << "\\notefontcolor " << h_notefontcolor << '\n';
1791 if (!h_backgroundcolor.empty())
1792 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1793 if (!h_boxbgcolor.empty())
1794 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1795 if (index_number != 0)
1796 for (int i = 0; i < index_number; i++) {
1797 os << "\\index " << h_index[i] << '\n'
1798 << "\\shortcut " << h_shortcut[i] << '\n'
1799 << "\\color " << h_color << '\n'
1803 os << "\\index " << h_index[0] << '\n'
1804 << "\\shortcut " << h_shortcut[0] << '\n'
1805 << "\\color " << h_color << '\n'
1809 << "\\secnumdepth " << h_secnumdepth << "\n"
1810 << "\\tocdepth " << h_tocdepth << "\n"
1811 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1812 if (h_paragraph_separation == "skip")
1813 os << "\\defskip " << h_defskip << "\n";
1815 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1816 os << "\\is_math_indent " << h_is_mathindent << "\n";
1817 if (!h_mathindentation.empty())
1818 os << "\\math_indentation " << h_mathindentation << "\n";
1819 os << "\\math_numbering_side " << h_math_numbering_side << "\n";
1820 os << "\\quotes_style " << h_quotes_style << "\n"
1821 << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
1822 << "\\papercolumns " << h_papercolumns << "\n"
1823 << "\\papersides " << h_papersides << "\n"
1824 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1825 if (!h_listings_params.empty())
1826 os << "\\listings_params " << h_listings_params << "\n";
1827 os << "\\tracking_changes " << h_tracking_changes << "\n"
1828 << "\\output_changes " << h_output_changes << "\n"
1829 << "\\html_math_output " << h_html_math_output << "\n"
1830 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1831 << "\\html_be_strict " << h_html_be_strict << "\n"
1833 << "\\end_header\n\n"
1834 << "\\begin_body\n";
1839 void Preamble::parse(Parser & p, string const & forceclass,
1840 TeX2LyXDocClass & tc)
1842 // initialize fixed types
1843 special_columns_['D'] = 3;
1844 parse(p, forceclass, false, tc);
1848 void Preamble::parse(Parser & p, string const & forceclass,
1849 bool detectEncoding, TeX2LyXDocClass & tc)
1851 bool is_full_document = false;
1852 bool is_lyx_file = false;
1853 bool in_lyx_preamble = false;
1855 // determine whether this is a full document or a fragment for inclusion
1857 Token const & t = p.get_token();
1859 if (t.cat() == catEscape && t.cs() == "documentclass") {
1860 is_full_document = true;
1866 if (detectEncoding && !is_full_document)
1869 while (is_full_document && p.good()) {
1870 if (detectEncoding && h_inputencoding != "auto-legacy" &&
1871 h_inputencoding != "auto-legacy-plain")
1874 Token const & t = p.get_token();
1877 if (!detectEncoding)
1878 cerr << "t: " << t << '\n';
1884 if (!in_lyx_preamble &&
1885 (t.cat() == catLetter ||
1886 t.cat() == catSuper ||
1887 t.cat() == catSub ||
1888 t.cat() == catOther ||
1889 t.cat() == catMath ||
1890 t.cat() == catActive ||
1891 t.cat() == catBegin ||
1892 t.cat() == catEnd ||
1893 t.cat() == catAlign ||
1894 t.cat() == catParameter)) {
1895 h_preamble << t.cs();
1899 if (!in_lyx_preamble &&
1900 (t.cat() == catSpace || t.cat() == catNewline)) {
1901 h_preamble << t.asInput();
1905 if (t.cat() == catComment) {
1906 static regex const islyxfile("%% LyX .* created this file");
1907 static regex const usercommands("User specified LaTeX commands");
1909 string const comment = t.asInput();
1911 // magically switch encoding default if it looks like XeLaTeX
1912 static string const magicXeLaTeX =
1913 "% This document must be compiled with XeLaTeX ";
1914 if (comment.size() > magicXeLaTeX.size()
1915 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1916 && h_inputencoding == "auto-legacy") {
1917 if (!detectEncoding)
1918 cerr << "XeLaTeX comment found, switching to UTF8\n";
1919 h_inputencoding = "utf8";
1922 if (regex_search(comment, sub, islyxfile)) {
1924 in_lyx_preamble = true;
1925 } else if (is_lyx_file
1926 && regex_search(comment, sub, usercommands))
1927 in_lyx_preamble = false;
1928 else if (!in_lyx_preamble)
1929 h_preamble << t.asInput();
1933 if (t.cs() == "PassOptionsToPackage") {
1934 string const poptions = p.getArg('{', '}');
1935 string const package = p.verbatim_item();
1936 extra_package_options_.insert(make_pair(package, poptions));
1940 if (t.cs() == "pagestyle") {
1941 h_paperpagestyle = p.verbatim_item();
1945 if (t.cs() == "setdefaultlanguage") {
1947 // We don't yet care about non-language variant options
1948 // because LyX doesn't support this yet, see bug #8214
1950 string langopts = p.getOpt();
1951 // check if the option contains a variant, if yes, extract it
1952 string::size_type pos_var = langopts.find("variant");
1953 string::size_type i = langopts.find(',', pos_var);
1954 string::size_type k = langopts.find('=', pos_var);
1955 if (pos_var != string::npos){
1957 if (i == string::npos)
1958 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1960 variant = langopts.substr(k + 1, i - k - 1);
1961 h_language = variant;
1965 h_language = p.verbatim_item();
1966 //finally translate the poyglossia name to a LyX name
1967 h_language = polyglossia2lyx(h_language);
1971 if (t.cs() == "setotherlanguage") {
1972 // We don't yet care about the option because LyX doesn't
1973 // support this yet, see bug #8214
1974 p.hasOpt() ? p.getOpt() : string();
1979 if (t.cs() == "setmainfont") {
1980 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
1981 h_font_roman[1] = p.getArg('{', '}');
1982 if (!fontopts.empty()) {
1983 vector<string> opts = getVectorFromString(fontopts);
1985 for (auto const & opt : opts) {
1986 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
1989 if (!fontopts.empty())
1993 h_font_roman_opts = fontopts;
1998 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1999 // LyX currently only supports the scale option
2000 string scale, fontopts;
2002 fontopts = p.getArg('[', ']');
2003 if (!fontopts.empty()) {
2004 vector<string> opts = getVectorFromString(fontopts);
2006 for (auto const & opt : opts) {
2007 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2010 if (prefixIs(opt, "Scale=")) {
2011 scale_as_percentage(opt, scale);
2014 if (!fontopts.empty())
2020 if (t.cs() == "setsansfont") {
2022 h_font_sf_scale[1] = scale;
2023 h_font_sans[1] = p.getArg('{', '}');
2024 if (!fontopts.empty())
2025 h_font_sans_opts = fontopts;
2028 h_font_tt_scale[1] = scale;
2029 h_font_typewriter[1] = p.getArg('{', '}');
2030 if (!fontopts.empty())
2031 h_font_typewriter_opts = fontopts;
2036 if (t.cs() == "babelfont") {
2038 h_use_non_tex_fonts = true;
2039 h_language_package = "babel";
2040 if (h_inputencoding == "auto-legacy")
2041 p.setEncoding("UTF-8");
2042 // we don't care about the lang option
2043 string const lang = p.hasOpt() ? p.getArg('[', ']') : string();
2044 string const family = p.getArg('{', '}');
2045 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2046 string const fontname = p.getArg('{', '}');
2047 if (lang.empty() && family == "rm") {
2048 h_font_roman[1] = fontname;
2049 if (!fontopts.empty()) {
2050 vector<string> opts = getVectorFromString(fontopts);
2052 for (auto const & opt : opts) {
2053 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2056 if (!fontopts.empty())
2060 h_font_roman_opts = fontopts;
2063 } else if (lang.empty() && (family == "sf" || family == "tt")) {
2064 // LyX currently only supports the scale option
2066 if (!fontopts.empty()) {
2067 vector<string> opts = getVectorFromString(fontopts);
2069 for (auto const & opt : opts) {
2070 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2073 if (prefixIs(opt, "Scale=")) {
2074 scale_as_percentage(opt, scale);
2077 if (!fontopts.empty())
2082 if (family == "sf") {
2084 h_font_sf_scale[1] = scale;
2085 h_font_sans[1] = fontname;
2086 if (!fontopts.empty())
2087 h_font_sans_opts = fontopts;
2090 h_font_tt_scale[1] = scale;
2091 h_font_typewriter[1] = fontname;
2092 if (!fontopts.empty())
2093 h_font_typewriter_opts = fontopts;
2097 // not rm, sf or tt or lang specific
2098 h_preamble << '\\' << t.cs();
2100 h_preamble << '[' << lang << ']';
2101 h_preamble << '{' << family << '}';
2102 if (!fontopts.empty())
2103 h_preamble << '[' << fontopts << ']';
2104 h_preamble << '{' << fontname << '}' << '\n';
2109 if (t.cs() == "date") {
2110 string argument = p.getArg('{', '}');
2111 if (argument.empty())
2112 h_suppress_date = "true";
2114 h_preamble << t.asInput() << '{' << argument << '}';
2118 if (t.cs() == "color") {
2119 string const space =
2120 (p.hasOpt() ? p.getOpt() : string());
2121 string argument = p.getArg('{', '}');
2122 // check the case that a standard color is used
2123 if (space.empty() && is_known(argument, known_basic_colors)) {
2124 h_fontcolor = rgbcolor2code(argument);
2125 registerAutomaticallyLoadedPackage("color");
2126 } else if (space.empty() && argument == "document_fontcolor")
2127 registerAutomaticallyLoadedPackage("color");
2128 // check the case that LyX's document_fontcolor is defined
2129 // but not used for \color
2131 h_preamble << t.asInput();
2133 h_preamble << space;
2134 h_preamble << '{' << argument << '}';
2135 // the color might already be set because \definecolor
2136 // is parsed before this
2142 if (t.cs() == "pagecolor") {
2143 string argument = p.getArg('{', '}');
2144 // check the case that a standard color is used
2145 if (is_known(argument, known_basic_colors)) {
2146 h_backgroundcolor = rgbcolor2code(argument);
2147 } else if (argument == "page_backgroundcolor")
2148 registerAutomaticallyLoadedPackage("color");
2149 // check the case that LyX's page_backgroundcolor is defined
2150 // but not used for \pagecolor
2152 h_preamble << t.asInput() << '{' << argument << '}';
2153 // the color might already be set because \definecolor
2154 // is parsed before this
2155 h_backgroundcolor = "";
2160 if (t.cs() == "makeatletter") {
2161 // LyX takes care of this
2162 p.setCatcode('@', catLetter);
2166 if (t.cs() == "makeatother") {
2167 // LyX takes care of this
2168 p.setCatcode('@', catOther);
2172 if (t.cs() == "makeindex") {
2173 // LyX will re-add this if a print index command is found
2178 if (t.cs() == "newindex") {
2179 string const indexname = p.getArg('[', ']');
2180 string const shortcut = p.verbatim_item();
2181 if (!indexname.empty())
2182 h_index[index_number] = indexname;
2184 h_index[index_number] = shortcut;
2185 h_shortcut[index_number] = shortcut;
2191 if (t.cs() == "addbibresource") {
2192 string const options = p.getArg('[', ']');
2193 string const arg = removeExtension(p.getArg('{', '}'));
2194 if (!options.empty()) {
2195 // check if the option contains a bibencoding, if yes, extract it
2196 string::size_type pos = options.find("bibencoding=");
2198 if (pos != string::npos) {
2199 string::size_type i = options.find(',', pos);
2200 if (i == string::npos)
2201 encoding = options.substr(pos + 1);
2203 encoding = options.substr(pos, i - pos);
2204 pos = encoding.find('=');
2205 if (pos == string::npos)
2208 encoding = encoding.substr(pos + 1);
2210 if (!encoding.empty())
2211 biblatex_encodings.push_back(normalize_filename(arg) + ' ' + encoding);
2213 biblatex_bibliographies.push_back(arg);
2217 if (t.cs() == "bibliography") {
2218 vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
2219 biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
2223 if (t.cs() == "RS@ifundefined") {
2224 string const name = p.verbatim_item();
2225 string const body1 = p.verbatim_item();
2226 string const body2 = p.verbatim_item();
2227 // only non-lyxspecific stuff
2228 if (in_lyx_preamble &&
2229 (name == "subsecref" || name == "thmref" || name == "lemref"))
2233 ss << '\\' << t.cs();
2234 ss << '{' << name << '}'
2235 << '{' << body1 << '}'
2236 << '{' << body2 << '}';
2237 h_preamble << ss.str();
2242 if (t.cs() == "AtBeginDocument") {
2243 string const name = p.verbatim_item();
2244 // only non-lyxspecific stuff
2245 if (in_lyx_preamble &&
2246 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
2247 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
2248 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
2249 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
2250 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
2251 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
2252 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
2253 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
2254 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
2255 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
2256 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
2257 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
2258 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
2259 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
2260 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
2264 ss << '\\' << t.cs();
2265 ss << '{' << name << '}';
2266 h_preamble << ss.str();
2271 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
2272 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
2273 || t.cs() == "providecommand" || t.cs() == "providecommandx"
2274 || t.cs() == "DeclareRobustCommand"
2275 || t.cs() == "DeclareRobustCommandx"
2276 || t.cs() == "ProvideTextCommandDefault"
2277 || t.cs() == "DeclareMathAccent") {
2279 if (p.next_token().character() == '*') {
2283 string const name = p.verbatim_item();
2284 string const opt1 = p.getFullOpt();
2285 string const opt2 = p.getFullOpt();
2286 string const body = p.verbatim_item();
2287 // store the in_lyx_preamble setting
2288 bool const was_in_lyx_preamble = in_lyx_preamble;
2290 if (name == "\\rmdefault")
2291 if (is_known(body, known_roman_font_packages)) {
2292 h_font_roman[0] = body;
2294 in_lyx_preamble = true;
2296 if (name == "\\sfdefault")
2297 if (is_known(body, known_sans_font_packages)) {
2298 h_font_sans[0] = body;
2300 in_lyx_preamble = true;
2302 if (name == "\\ttdefault")
2303 if (is_known(body, known_typewriter_font_packages)) {
2304 h_font_typewriter[0] = body;
2306 in_lyx_preamble = true;
2308 if (name == "\\familydefault") {
2309 string family = body;
2310 // remove leading "\"
2311 h_font_default_family = family.erase(0,1);
2313 in_lyx_preamble = true;
2316 // remove LyX-specific definitions that are re-added by LyX
2318 // \lyxline is an ancient command that is converted by tex2lyx into
2319 // a \rule therefore remove its preamble code
2320 if (name == "\\lyxdot" || name == "\\lyxarrow"
2321 || name == "\\lyxline" || name == "\\LyX") {
2323 in_lyx_preamble = true;
2326 // Add the command to the known commands
2327 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
2329 // only non-lyxspecific stuff
2330 if (!in_lyx_preamble) {
2332 ss << '\\' << t.cs();
2335 ss << '{' << name << '}' << opt1 << opt2
2336 << '{' << body << "}";
2337 if (prefixIs(t.cs(), "renew") || !contains(h_preamble.str(), ss.str()))
2338 h_preamble << ss.str();
2340 ostream & out = in_preamble ? h_preamble : os;
2341 out << "\\" << t.cs() << "{" << name << "}"
2342 << opts << "{" << body << "}";
2345 // restore the in_lyx_preamble setting
2346 in_lyx_preamble = was_in_lyx_preamble;
2350 if (t.cs() == "documentclass") {
2351 vector<string>::iterator it;
2352 vector<string> opts = split_options(p.getArg('[', ']'));
2353 handle_opt(opts, known_fontsizes, h_paperfontsize);
2354 delete_opt(opts, known_fontsizes);
2355 // delete "pt" at the end
2356 string::size_type i = h_paperfontsize.find("pt");
2357 if (i != string::npos)
2358 h_paperfontsize.erase(i);
2359 // The documentclass options are always parsed before the options
2360 // of the babel call so that a language cannot overwrite the babel
2362 handle_opt(opts, known_languages, h_language);
2363 delete_opt(opts, known_languages);
2366 if ((it = find(opts.begin(), opts.end(), "fleqn"))
2368 h_is_mathindent = "1";
2371 // formula numbering side
2372 if ((it = find(opts.begin(), opts.end(), "leqno"))
2374 h_math_numbering_side = "left";
2377 else if ((it = find(opts.begin(), opts.end(), "reqno"))
2379 h_math_numbering_side = "right";
2383 // paper orientation
2384 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
2385 h_paperorientation = "landscape";
2389 if ((it = find(opts.begin(), opts.end(), "oneside"))
2394 if ((it = find(opts.begin(), opts.end(), "twoside"))
2400 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
2402 h_papercolumns = "1";
2405 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
2407 h_papercolumns = "2";
2411 // some size options are known to any document classes, other sizes
2412 // are handled by the \geometry command of the geometry package
2413 handle_opt(opts, known_class_paper_sizes, h_papersize);
2414 delete_opt(opts, known_class_paper_sizes);
2415 // the remaining options
2416 h_options = join(opts, ",");
2417 // FIXME This does not work for classes that have a
2418 // different name in LyX than in LaTeX
2419 h_textclass = p.getArg('{', '}');
2424 if (t.cs() == "usepackage") {
2425 string const options = p.getArg('[', ']');
2426 string const name = p.getArg('{', '}');
2427 vector<string> vecnames;
2428 split(name, vecnames, ',');
2429 vector<string>::const_iterator it = vecnames.begin();
2430 vector<string>::const_iterator end = vecnames.end();
2431 for (; it != end; ++it)
2432 handle_package(p, trimSpaceAndEol(*it), options,
2433 in_lyx_preamble, detectEncoding);
2437 if (t.cs() == "inputencoding") {
2438 string const encoding = p.getArg('{','}');
2439 Encoding const * const enc = encodings.fromLaTeXName(
2440 encoding, Encoding::inputenc, true);
2442 if (!detectEncoding)
2443 cerr << "Unknown encoding " << encoding
2444 << ". Ignoring." << std::endl;
2447 h_inputencoding = enc->name();
2448 p.setEncoding(enc->iconvName());
2453 if (t.cs() == "newenvironment") {
2454 string const name = p.getArg('{', '}');
2455 string const opt1 = p.getFullOpt();
2456 string const opt2 = p.getFullOpt();
2457 string const beg = p.verbatim_item();
2458 string const end = p.verbatim_item();
2459 if (!in_lyx_preamble) {
2460 h_preamble << "\\newenvironment{" << name
2461 << '}' << opt1 << opt2 << '{'
2462 << beg << "}{" << end << '}';
2464 add_known_environment(name, opt1, !opt2.empty(),
2465 from_utf8(beg), from_utf8(end));
2469 if (t.cs() == "newtheorem") {
2471 if (p.next_token().character() == '*') {
2475 string const name = p.getArg('{', '}');
2476 string const opt1 = p.getFullOpt();
2477 string const opt2 = p.getFullOpt();
2478 string const body = p.verbatim_item();
2479 string const opt3 = p.getFullOpt();
2480 string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
2482 string const complete = cmd + "{" + name + '}' +
2483 opt1 + opt2 + '{' + body + '}' + opt3;
2485 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
2487 if (!in_lyx_preamble)
2488 h_preamble << complete;
2492 if (t.cs() == "def") {
2493 string name = p.get_token().cs();
2494 // In fact, name may be more than the name:
2495 // In the test case of bug 8116
2496 // name == "csname SF@gobble@opt \endcsname".
2497 // Therefore, we need to use asInput() instead of cs().
2498 while (p.next_token().cat() != catBegin)
2499 name += p.get_token().asInput();
2500 if (!in_lyx_preamble)
2501 h_preamble << "\\def\\" << name << '{'
2502 << p.verbatim_item() << "}";
2506 if (t.cs() == "newcolumntype") {
2507 string const name = p.getArg('{', '}');
2508 trimSpaceAndEol(name);
2510 string opts = p.getOpt();
2511 if (!opts.empty()) {
2512 istringstream is(string(opts, 1));
2515 special_columns_[name[0]] = nargs;
2516 h_preamble << "\\newcolumntype{" << name << "}";
2518 h_preamble << "[" << nargs << "]";
2519 h_preamble << "{" << p.verbatim_item() << "}";
2523 if (t.cs() == "setcounter") {
2524 string const name = p.getArg('{', '}');
2525 string const content = p.getArg('{', '}');
2526 if (name == "secnumdepth")
2527 h_secnumdepth = content;
2528 else if (name == "tocdepth")
2529 h_tocdepth = content;
2531 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
2535 if (t.cs() == "setlength") {
2536 string const name = p.verbatim_item();
2537 string const content = p.verbatim_item();
2538 // the paragraphs are only not indented when \parindent is set to zero
2539 if (name == "\\parindent" && content != "") {
2540 if (content[0] == '0')
2541 h_paragraph_separation = "skip";
2543 h_paragraph_indentation = translate_len(content);
2544 } else if (name == "\\parskip") {
2545 if (content == "\\smallskipamount")
2546 h_defskip = "smallskip";
2547 else if (content == "\\medskipamount")
2548 h_defskip = "medskip";
2549 else if (content == "\\bigskipamount")
2550 h_defskip = "bigskip";
2552 h_defskip = translate_len(content);
2553 } else if (name == "\\mathindent") {
2554 h_mathindentation = translate_len(content);
2556 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2560 if (t.cs() == "onehalfspacing") {
2561 h_spacing = "onehalf";
2565 if (t.cs() == "doublespacing") {
2566 h_spacing = "double";
2570 if (t.cs() == "setstretch") {
2571 h_spacing = "other " + p.verbatim_item();
2575 if (t.cs() == "synctex") {
2576 // the scheme is \synctex=value
2577 // where value can only be "1" or "-1"
2578 h_output_sync = "1";
2579 // there can be any character behind the value (e.g. a linebreak or a '\'
2580 // therefore we extract it char by char
2582 string value = p.get_token().asInput();
2584 value += p.get_token().asInput();
2585 h_output_sync_macro = "\\synctex=" + value;
2589 if (t.cs() == "begin") {
2590 string const name = p.getArg('{', '}');
2591 if (name == "document")
2593 h_preamble << "\\begin{" << name << "}";
2597 if (t.cs() == "geometry") {
2598 vector<string> opts = split_options(p.getArg('{', '}'));
2599 handle_geometry(opts);
2603 if (t.cs() == "definecolor") {
2604 string const color = p.getArg('{', '}');
2605 string const space = p.getArg('{', '}');
2606 string const value = p.getArg('{', '}');
2607 if (color == "document_fontcolor" && space == "rgb") {
2608 RGBColor c(RGBColorFromLaTeX(value));
2609 h_fontcolor = X11hexname(c);
2610 } else if (color == "note_fontcolor" && space == "rgb") {
2611 RGBColor c(RGBColorFromLaTeX(value));
2612 h_notefontcolor = X11hexname(c);
2613 } else if (color == "page_backgroundcolor" && space == "rgb") {
2614 RGBColor c(RGBColorFromLaTeX(value));
2615 h_backgroundcolor = X11hexname(c);
2616 } else if (color == "shadecolor" && space == "rgb") {
2617 RGBColor c(RGBColorFromLaTeX(value));
2618 h_boxbgcolor = X11hexname(c);
2620 h_preamble << "\\definecolor{" << color
2621 << "}{" << space << "}{" << value
2627 if (t.cs() == "bibliographystyle") {
2628 h_biblio_style = p.verbatim_item();
2632 if (t.cs() == "jurabibsetup") {
2633 // FIXME p.getArg('{', '}') is most probably wrong (it
2634 // does not handle nested braces).
2635 // Use p.verbatim_item() instead.
2636 vector<string> jurabibsetup =
2637 split_options(p.getArg('{', '}'));
2638 // add jurabibsetup to the jurabib package options
2639 add_package("jurabib", jurabibsetup);
2640 if (!jurabibsetup.empty()) {
2641 h_preamble << "\\jurabibsetup{"
2642 << join(jurabibsetup, ",") << '}';
2647 if (t.cs() == "hypersetup") {
2648 vector<string> hypersetup =
2649 split_options(p.verbatim_item());
2650 // add hypersetup to the hyperref package options
2651 handle_hyperref(hypersetup);
2652 if (!hypersetup.empty()) {
2653 h_preamble << "\\hypersetup{"
2654 << join(hypersetup, ",") << '}';
2659 if (t.cs() == "includeonly") {
2660 vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
2661 for (auto & iofile : includeonlys) {
2662 string filename(normalize_filename(iofile));
2663 string const path = getMasterFilePath(true);
2664 // We want to preserve relative/absolute filenames,
2665 // therefore path is only used for testing
2666 if (!makeAbsPath(filename, path).exists()) {
2667 // The file extension is probably missing.
2668 // Now try to find it out.
2669 string const tex_name =
2670 find_file(filename, path,
2671 known_tex_extensions);
2672 if (!tex_name.empty())
2673 filename = tex_name;
2676 if (makeAbsPath(filename, path).exists())
2677 fix_child_filename(filename);
2679 cerr << "Warning: Could not find included file '"
2680 << filename << "'." << endl;
2681 outname = changeExtension(filename, "lyx");
2682 h_includeonlys.push_back(outname);
2687 if (is_known(t.cs(), known_if_3arg_commands)) {
2688 // prevent misparsing of \usepackage if it is used
2689 // as an argument (see e.g. our own output of
2690 // \@ifundefined above)
2691 string const arg1 = p.verbatim_item();
2692 string const arg2 = p.verbatim_item();
2693 string const arg3 = p.verbatim_item();
2694 // test case \@ifundefined{date}{}{\date{}}
2695 if (t.cs() == "@ifundefined" && arg1 == "date" &&
2696 arg2.empty() && arg3 == "\\date{}") {
2697 h_suppress_date = "true";
2698 // older tex2lyx versions did output
2699 // \@ifundefined{definecolor}{\usepackage{color}}{}
2700 } else if (t.cs() == "@ifundefined" &&
2701 arg1 == "definecolor" &&
2702 arg2 == "\\usepackage{color}" &&
2704 if (!in_lyx_preamble)
2705 h_preamble << package_beg_sep
2708 << "\\@ifundefined{definecolor}{color}{}"
2711 //\@ifundefined{showcaptionsetup}{}{%
2712 // \PassOptionsToPackage{caption=false}{subfig}}
2713 // that LyX uses for subfloats
2714 } else if (t.cs() == "@ifundefined" &&
2715 arg1 == "showcaptionsetup" && arg2.empty()
2716 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
2718 } else if (!in_lyx_preamble) {
2719 h_preamble << t.asInput()
2720 << '{' << arg1 << '}'
2721 << '{' << arg2 << '}'
2722 << '{' << arg3 << '}';
2727 if (is_known(t.cs(), known_if_commands)) {
2728 // must not parse anything in conditional code, since
2729 // LyX would output the parsed contents unconditionally
2730 if (!in_lyx_preamble)
2731 h_preamble << t.asInput();
2732 handle_if(p, in_lyx_preamble);
2736 if (!t.cs().empty() && !in_lyx_preamble) {
2737 h_preamble << '\\' << t.cs();
2742 // remove the whitespace
2745 // Force textclass if the user wanted it
2746 if (!forceclass.empty())
2747 h_textclass = forceclass;
2748 tc.setName(h_textclass);
2749 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2750 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2753 if (h_papersides.empty()) {
2756 h_papersides = ss.str();
2759 // If the CJK package is used we cannot set the document language from
2760 // the babel options. Instead, we guess which language is used most
2761 // and set this one.
2762 default_language = h_language;
2763 if (is_full_document &&
2764 (auto_packages.find("CJK") != auto_packages.end() ||
2765 auto_packages.find("CJKutf8") != auto_packages.end())) {
2767 h_language = guessLanguage(p, default_language);
2769 if (explicit_babel && h_language != default_language) {
2770 // We set the document language to a CJK language,
2771 // but babel is explicitly called in the user preamble
2772 // without options. LyX will not add the default
2773 // language to the document options if it is either
2774 // english, or no text is set as default language.
2775 // Therefore we need to add a language option explicitly.
2776 // FIXME: It would be better to remove all babel calls
2777 // from the user preamble, but this is difficult
2778 // without re-introducing bug 7861.
2779 if (h_options.empty())
2780 h_options = lyx2babel(default_language);
2782 h_options += ',' + lyx2babel(default_language);
2786 // Finally, set the quote style.
2787 // LyX knows the following quotes styles:
2788 // british, cjk, cjkangle, danish, english, french, german,
2789 // polish, russian, swedish and swiss
2790 // conversion list taken from
2791 // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
2792 // (quotes for kazakh are unknown)
2794 if (is_known(h_language, known_british_quotes_languages))
2795 h_quotes_style = "british";
2797 else if (is_known(h_language, known_cjk_quotes_languages))
2798 h_quotes_style = "cjk";
2800 else if (is_known(h_language, known_cjkangle_quotes_languages))
2801 h_quotes_style = "cjkangle";
2803 else if (is_known(h_language, known_danish_quotes_languages))
2804 h_quotes_style = "danish";
2806 else if (is_known(h_language, known_french_quotes_languages))
2807 h_quotes_style = "french";
2809 else if (is_known(h_language, known_german_quotes_languages))
2810 h_quotes_style = "german";
2812 else if (is_known(h_language, known_polish_quotes_languages))
2813 h_quotes_style = "polish";
2815 else if (is_known(h_language, known_russian_quotes_languages))
2816 h_quotes_style = "russian";
2818 else if (is_known(h_language, known_swedish_quotes_languages))
2819 h_quotes_style = "swedish";
2821 else if (is_known(h_language, known_swiss_quotes_languages))
2822 h_quotes_style = "swiss";
2824 else if (is_known(h_language, known_english_quotes_languages))
2825 h_quotes_style = "english";
2829 string Preamble::parseEncoding(Parser & p, string const & forceclass)
2831 TeX2LyXDocClass dummy;
2832 parse(p, forceclass, true, dummy);
2833 if (h_inputencoding != "auto-legacy" && h_inputencoding != "auto-legacy-plain")
2834 return h_inputencoding;
2839 string babel2lyx(string const & language)
2841 char const * const * where = is_known(language, known_languages);
2843 return known_coded_languages[where - known_languages];
2848 string lyx2babel(string const & language)
2850 char const * const * where = is_known(language, known_coded_languages);
2852 return known_languages[where - known_coded_languages];
2857 string Preamble::polyglossia2lyx(string const & language)
2859 char const * const * where = is_known(language, polyglossia_languages);
2861 return coded_polyglossia_languages[where - polyglossia_languages];
2866 string rgbcolor2code(string const & name)
2868 char const * const * where = is_known(name, known_basic_colors);
2870 // "red", "green" etc
2871 return known_basic_color_codes[where - known_basic_colors];
2873 // "255,0,0", "0,255,0" etc
2874 RGBColor c(RGBColorFromLaTeX(name));
2875 return X11hexname(c);