3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
9 * Full author contact details are available in file CREDITS.
20 #include "LayoutFile.h"
23 #include "TextClass.h"
26 #include "support/convert.h"
27 #include "support/FileName.h"
28 #include "support/filetools.h"
29 #include "support/lstrings.h"
31 #include "support/regex.h"
37 using namespace lyx::support;
46 // CJK languages are handled in text.cpp, polyglossia languages are listed
49 * known babel language names (including synonyms)
50 * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
51 * please keep this in sync with known_coded_languages line by line!
53 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
54 "american", "arabic", "arabtex", "australian", "austrian", "azerbaijani", "bahasa", "bahasai",
55 "bahasam", "basque", "belarusian", "bosnian", "brazil", "brazilian", "breton", "british",
56 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
57 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "francais",
58 "french", "frenchb", "frenchle", "frenchpro", "friulan", "galician", "german", "germanb",
59 "georgian", "greek", "hebrew", "hungarian", "icelandic", "indon", "indonesian",
60 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
61 "latvian", "lithuanian", "lowersorbian", "lsorbian", "macedonian", "magyar", "malay", "meyalu",
62 "mongolian", "naustrian", "newzealand", "ngerman", "ngermanb", "norsk", "nswissgerman",
63 "nynorsk", "piedmontese", "polutonikogreek", "polish", "portuges", "portuguese",
64 "romanian", "romansh", "russian", "russianb", "samin", "scottish", "serbian", "serbian-latin",
65 "slovak", "slovene", "spanish", "swedish", "swissgerman", "thai", "turkish", "turkmen",
66 "ukraineb", "ukrainian", "uppersorbian", "UKenglish", "USenglish", "usorbian",
71 * the same as known_languages with .lyx names
72 * please keep this in sync with known_languages line by line!
74 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
75 "american", "arabic_arabi", "arabic_arabtex", "australian", "austrian", "azerbaijani", "bahasa", "bahasa",
76 "bahasam", "basque", "belarusian", "bosnian", "brazilian", "brazilian", "breton", "british",
77 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
78 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "french",
79 "french", "french", "french", "french", "friulan", "galician", "german", "german",
80 "georgian", "greek", "hebrew", "magyar", "icelandic", "bahasa", "bahasa",
81 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
82 "latvian", "lithuanian", "lowersorbian", "lowersorbian", "macedonian", "magyar", "bahasam", "bahasam",
83 "mongolian", "naustrian", "newzealand", "ngerman", "ngerman", "norsk", "german-ch",
84 "nynorsk", "piedmontese", "polutonikogreek", "polish", "portuguese", "portuguese",
85 "romanian", "romansh", "russian", "russian", "samin", "scottish", "serbian", "serbian-latin",
86 "slovak", "slovene", "spanish", "swedish", "german-ch-old", "thai", "turkish", "turkmen",
87 "ukrainian", "ukrainian", "uppersorbian", "english", "english", "uppersorbian",
88 "vietnamese", "welsh",
91 /// languages with british quotes (.lyx names)
92 const char * const known_british_quotes_languages[] = {"british", "welsh", 0};
94 /// languages with cjk quotes (.lyx names)
95 const char * const known_cjk_quotes_languages[] = {"chinese-traditional",
96 "japanese", "japanese-cjk", 0};
98 /// languages with cjk-angle quotes (.lyx names)
99 const char * const known_cjkangle_quotes_languages[] = {"korean", 0};
101 /// languages with danish quotes (.lyx names)
102 const char * const known_danish_quotes_languages[] = {"danish", 0};
104 /// languages with english quotes (.lyx names)
105 const char * const known_english_quotes_languages[] = {"american", "australian",
106 "bahasa", "bahasam", "bengali", "brazilian", "canadian", "chinese-simplified", "english",
107 "esperanto", "farsi", "interlingua", "irish", "newzealand", "scottish",
108 "thai", "turkish", "vietnamese", 0};
110 /// languages with french quotes (.lyx names)
111 const char * const known_french_quotes_languages[] = {"ancientgreek",
112 "arabic_arabi", "arabic_arabtex", "asturian", "belarusian", "breton",
113 "canadien", "catalan", "french", "friulan", "galician", "italian", "occitan",
114 "piedmontese", "portuguese", "spanish", "spanish-mexico", 0};
116 /// languages with german quotes (.lyx names)
117 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
118 "czech", "estonian", "georgian", "german", "icelandic", "latvian", "lithuanian",
119 "lowersorbian", "macedonian", "naustrian", "ngerman", "romansh", "slovak", "slovene",
122 /// languages with polish quotes (.lyx names)
123 const char * const known_polish_quotes_languages[] = {"afrikaans", "bosnian", "croatian",
124 "dutch", "magyar", "polish", "romanian", "serbian", "serbian-latin", 0};
126 /// languages with russian quotes (.lyx names)
127 const char * const known_russian_quotes_languages[] = {"azerbaijani", "oldrussian",
128 "russian", "ukrainian", 0};
130 /// languages with swedish quotes (.lyx names)
131 const char * const known_swedish_quotes_languages[] = {"finnish", "swedish", 0};
133 /// languages with swiss quotes (.lyx names)
134 const char * const known_swiss_quotes_languages[] = {"albanian",
135 "armenian", "basque", "churchslavonic", "german-ch", "german-ch-old",
136 "norsk", "nynorsk", "turkmen", "ukrainian", "vietnamese", 0};
138 /// known language packages from the times before babel
139 const char * const known_old_language_packages[] = {"french", "frenchle",
140 "frenchpro", "german", "ngerman", "pmfrench", 0};
142 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
144 const char * const known_roman_font_packages[] = { "ae", "beraserif", "bookman",
145 "ccfonts", "chancery", "charter", "cmr", "cochineal", "crimson", "DejaVuSerif", "DejaVuSerifCondensed", "fourier",
146 "garamondx", "libertine", "libertineRoman", "libertine-type1", "lmodern", "mathdesign", "mathpazo",
147 "mathptmx", "MinionPro", "newcent", "noto", "noto-serif", "PTSerif", "tgbonum", "tgchorus",
148 "tgpagella", "tgschola", "tgtermes", "utopia", "xcharter", 0 };
150 const char * const known_sans_font_packages[] = { "avant", "berasans", "biolinum",
151 "biolinum-type1", "cmbr", "cmss", "DejaVuSans", "DejaVuSansCondensed", "helvet", "iwona", "iwonac", "iwonal", "iwonalc",
152 "kurier", "kurierc", "kurierl", "kurierlc", "lmss", "noto", "noto-sans", "PTSans",
153 "tgadventor", "tgheros", "uop", 0 };
155 const char * const known_typewriter_font_packages[] = { "beramono", "cmtl", "cmtt",
156 "courier", "DejaVuSansMono", "lmtt", "luximono", "fourier", "libertineMono", "libertineMono-type1", "lmodern",
157 "mathpazo", "mathptmx", "newcent", "noto", "noto-mono", "PTMono", "tgcursor", "txtt", 0 };
159 const char * const known_math_font_packages[] = { "eulervm", "newtxmath", 0};
161 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
162 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
163 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
164 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
165 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
167 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
168 "executivepaper", "legalpaper", "letterpaper", 0};
170 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
171 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
173 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
174 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
177 /// commands that can start an \if...\else...\endif sequence
178 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
179 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
180 "ifsidecap", "ifupgreek", 0};
182 const char * const known_basic_colors[] = {"black", "blue", "brown", "cyan",
183 "darkgray", "gray", "green", "lightgray", "lime", "magenta", "orange", "olive",
184 "pink", "purple", "red", "teal", "violet", "white", "yellow", 0};
186 const char * const known_basic_color_codes[] = {"#000000", "#0000ff", "#964B00", "#00ffff",
187 "#a9a9a9", "#808080", "#00ff00", "#d3d3d3", "#bfff00", "#ff00ff", "#ff7f00", "#808000",
188 "#ffc0cb", "#800080", "#ff0000", "#008080", "#8f00ff", "#ffffff", "#ffff00", 0};
190 /// conditional commands with three arguments like \@ifundefined{}{}{}
191 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
195 * Known file extensions for TeX files as used by \\includeonly
197 char const * const known_tex_extensions[] = {"tex", 0};
199 /// packages that work only in xetex
200 /// polyglossia is handled separately
201 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
202 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
203 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
205 /// packages that are automatically skipped if loaded by LyX
206 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
207 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color",
208 "float", "fontspec", "framed", "graphicx", "hhline", "ifthen", "longtable",
209 "makeidx", "minted", "multirow", "nomencl", "pdfpages", "prettyref", "refstyle",
210 "rotating", "rotfloat", "splitidx", "setspace", "subscript", "tabularx","textcomp", "tipa",
211 "tipx", "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xcolor", "xltabular",
214 // codes used to remove packages that are loaded automatically by LyX.
215 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
216 const char package_beg_sep = '\001';
217 const char package_mid_sep = '\002';
218 const char package_end_sep = '\003';
221 // returns true if at least one of the options in what has been found
222 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
228 // the last language option is the document language (for babel and LyX)
229 // the last size option is the document font size
230 vector<string>::iterator it;
231 vector<string>::iterator position = opts.begin();
232 for (; *what; ++what) {
233 it = find(opts.begin(), opts.end(), *what);
234 if (it != opts.end()) {
235 if (it >= position) {
246 void delete_opt(vector<string> & opts, char const * const * what)
251 // remove found options from the list
252 // do this after handle_opt to avoid potential memory leaks
253 vector<string>::iterator it;
254 for (; *what; ++what) {
255 it = find(opts.begin(), opts.end(), *what);
256 if (it != opts.end())
263 * Split a package options string (keyval format) into a vector.
265 * authorformat=smallcaps,
267 * titleformat=colonsep,
268 * bibformat={tabular,ibidem,numbered}
270 vector<string> split_options(string const & input)
272 vector<string> options;
276 Token const & t = p.get_token();
277 if (t.asInput() == ",") {
278 options.push_back(trimSpaceAndEol(option));
280 } else if (t.asInput() == "=") {
283 if (p.next_token().asInput() == "{")
284 option += '{' + p.getArg('{', '}') + '}';
285 } else if (t.cat() != catSpace && t.cat() != catComment)
286 option += t.asInput();
290 options.push_back(trimSpaceAndEol(option));
297 * Retrieve a keyval option "name={value with=sign}" named \p name from
298 * \p options and return the value.
299 * The found option is also removed from \p options.
301 string process_keyval_opt(vector<string> & options, string name)
303 for (size_t i = 0; i < options.size(); ++i) {
304 vector<string> option;
305 split(options[i], option, '=');
306 if (option.size() < 2)
308 if (option[0] == name) {
309 options.erase(options.begin() + i);
310 option.erase(option.begin());
311 return join(option, "=");
317 } // anonymous namespace
321 * known polyglossia language names (including variants)
322 * FIXME: support spelling=old for german variants (german vs. ngerman LyX names etc)
324 const char * const Preamble::polyglossia_languages[] = {
325 "albanian", "american", "amharic", "ancient", "arabic", "armenian", "asturian", "australian",
326 "bahasai", "bahasam", "basque", "bengali", "brazil", "brazilian", "breton", "british", "bulgarian",
327 "catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
328 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
329 "galician", "greek", "monotonic", "hebrew", "hindi",
330 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean",
331 "lao", "latin", "latvian", "lithuanian", "lsorbian", "magyar", "malayalam", "marathi",
332 "austrian", "newzealand", "german", "norsk", "nynorsk", "occitan", "oldrussian",
333 "piedmontese", "polish", "polytonic", "portuges", "romanian", "romansh", "russian",
334 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovenian", "spanish", "swedish", "syriac",
335 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
336 "ukrainian", "urdu", "usorbian", "vietnamese", "welsh", 0};
337 // not yet supported by LyX: "korean", "nko"
340 * the same as polyglossia_languages with .lyx names
341 * please keep this in sync with polyglossia_languages line by line!
343 const char * const Preamble::coded_polyglossia_languages[] = {
344 "albanian", "american", "amharic", "ancientgreek", "arabic_arabi", "armenian", "asturian", "australian",
345 "bahasa", "bahasam", "basque", "bengali", "brazilian", "brazilian", "breton", "british", "bulgarian",
346 "catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
347 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
348 "galician", "greek", "greek", "hebrew", "hindi",
349 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean",
350 "lao", "latin", "latvian", "lithuanian", "lowersorbian", "magyar", "malayalam", "marathi",
351 "naustrian","newzealand", "ngerman", "norsk", "nynorsk", "occitan", "oldrussian",
352 "piedmontese", "polish", "polutonikogreek", "portuges", "romanian", "romansh", "russian",
353 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovene", "spanish", "swedish", "syriac",
354 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
355 "ukrainian", "urdu", "uppersorbian", "vietnamese", "welsh", 0};
356 // not yet supported by LyX: "korean-polyglossia", "nko"
359 bool Preamble::usePolyglossia() const
361 return h_use_non_tex_fonts && h_language_package == "default";
365 bool Preamble::indentParagraphs() const
367 return h_paragraph_separation == "indent";
371 bool Preamble::isPackageUsed(string const & package) const
373 return used_packages.find(package) != used_packages.end();
377 bool Preamble::isPackageAutoLoaded(string const & package) const
379 return auto_packages.find(package) != auto_packages.end();
383 vector<string> Preamble::getPackageOptions(string const & package) const
385 map<string, vector<string> >::const_iterator it = used_packages.find(package);
386 if (it != used_packages.end())
388 return vector<string>();
392 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
394 auto_packages.insert(package);
398 void Preamble::addModule(string const & module)
400 for (auto const & m : used_modules) {
404 used_modules.push_back(module);
408 void Preamble::suppressDate(bool suppress)
411 h_suppress_date = "true";
413 h_suppress_date = "false";
417 void Preamble::registerAuthor(std::string const & name)
419 Author author(from_utf8(name), empty_docstring());
420 author.setUsed(true);
421 authors_.record(author);
422 h_tracking_changes = "true";
423 h_output_changes = "true";
427 Author const & Preamble::getAuthor(std::string const & name) const
429 Author author(from_utf8(name), empty_docstring());
430 for (AuthorList::Authors::const_iterator it = authors_.begin();
431 it != authors_.end(); ++it)
434 static Author const dummy;
439 int Preamble::getSpecialTableColumnArguments(char c) const
441 map<char, int>::const_iterator it = special_columns_.find(c);
442 if (it == special_columns_.end())
448 void Preamble::add_package(string const & name, vector<string> & options)
450 // every package inherits the global options
451 if (used_packages.find(name) == used_packages.end())
452 used_packages[name] = split_options(h_options);
454 // Insert options passed via PassOptionsToPackage
455 for (auto const & p : extra_package_options_) {
456 if (p.first == name) {
457 vector<string> eo = getVectorFromString(p.second);
458 for (auto const & eoi : eo)
459 options.push_back(eoi);
463 vector<string> & v = used_packages[name];
464 v.insert(v.end(), options.begin(), options.end());
465 if (name == "jurabib") {
466 // Don't output the order argument (see the cite command
467 // handling code in text.cpp).
468 vector<string>::iterator end =
469 remove(options.begin(), options.end(), "natbiborder");
470 end = remove(options.begin(), end, "jurabiborder");
471 options.erase(end, options.end());
478 // Given is a string like "scaled=0.9" or "Scale=0.9", return 0.9 * 100
479 bool scale_as_percentage(string const & scale, string & percentage)
481 string::size_type pos = scale.find('=');
482 if (pos != string::npos) {
483 string value = scale.substr(pos + 1);
484 if (isStrDbl(value)) {
485 percentage = convert<string>(
486 static_cast<int>(100 * convert<double>(value)));
494 string remove_braces(string const & value)
498 if (value[0] == '{' && value[value.length()-1] == '}')
499 return value.substr(1, value.length()-2);
503 } // anonymous namespace
506 Preamble::Preamble() : one_language(true), explicit_babel(false),
507 title_layout_found(false), index_number(0), h_font_cjk_set(false),
508 h_use_microtype("false")
512 h_biblio_style = "plain";
513 h_bibtex_command = "default";
514 h_cite_engine = "basic";
515 h_cite_engine_type = "default";
517 h_defskip = "medskip";
518 h_dynamic_quotes = false;
521 h_fontencoding = "default";
522 h_font_roman[0] = "default";
523 h_font_roman[1] = "default";
524 h_font_sans[0] = "default";
525 h_font_sans[1] = "default";
526 h_font_typewriter[0] = "default";
527 h_font_typewriter[1] = "default";
528 h_font_math[0] = "auto";
529 h_font_math[1] = "auto";
530 h_font_default_family = "default";
531 h_use_non_tex_fonts = false;
533 h_font_osf = "false";
534 h_font_sf_scale[0] = "100";
535 h_font_sf_scale[1] = "100";
536 h_font_tt_scale[0] = "100";
537 h_font_tt_scale[1] = "100";
538 // h_font_roman_opts;
540 // h_font_typewriter_opts;
542 h_is_mathindent = "0";
543 h_math_numbering_side = "default";
544 h_graphics = "default";
545 h_default_output_format = "default";
546 h_html_be_strict = "false";
547 h_html_css_as_file = "0";
548 h_html_math_output = "0";
549 h_index[0] = "Index";
550 h_index_command = "default";
551 h_inputencoding = "auto-legacy";
552 h_justification = "true";
553 h_language = "english";
554 h_language_package = "none";
556 h_maintain_unincluded_children = "false";
560 h_output_changes = "false";
562 //h_output_sync_macro
563 h_papercolumns = "1";
564 h_paperfontsize = "default";
565 h_paperorientation = "portrait";
566 h_paperpagestyle = "default";
568 h_papersize = "default";
569 h_paragraph_indentation = "default";
570 h_paragraph_separation = "indent";
575 h_pdf_bookmarks = "0";
576 h_pdf_bookmarksnumbered = "0";
577 h_pdf_bookmarksopen = "0";
578 h_pdf_bookmarksopenlevel = "1";
579 h_pdf_breaklinks = "0";
580 h_pdf_pdfborder = "0";
581 h_pdf_colorlinks = "0";
582 h_pdf_backref = "section";
583 h_pdf_pdfusetitle = "0";
585 //h_pdf_quoted_options;
586 h_quotes_style = "english";
588 h_shortcut[0] = "idx";
589 h_spacing = "single";
590 h_save_transient_properties = "true";
591 h_suppress_date = "false";
592 h_textclass = "article";
594 h_tracking_changes = "false";
595 h_use_bibtopic = "false";
596 h_use_dash_ligatures = "true";
597 h_use_indices = "false";
598 h_use_geometry = "false";
599 h_use_default_options = "false";
600 h_use_hyperref = "false";
601 h_use_microtype = "false";
602 h_use_refstyle = false;
603 h_use_minted = false;
604 h_use_packages["amsmath"] = "1";
605 h_use_packages["amssymb"] = "0";
606 h_use_packages["cancel"] = "0";
607 h_use_packages["esint"] = "1";
608 h_use_packages["mhchem"] = "0";
609 h_use_packages["mathdots"] = "0";
610 h_use_packages["mathtools"] = "0";
611 h_use_packages["stackrel"] = "0";
612 h_use_packages["stmaryrd"] = "0";
613 h_use_packages["undertilde"] = "0";
617 void Preamble::handle_hyperref(vector<string> & options)
619 // FIXME swallow inputencoding changes that might surround the
620 // hyperref setup if it was written by LyX
621 h_use_hyperref = "true";
622 // swallow "unicode=true", since LyX does always write that
623 vector<string>::iterator it =
624 find(options.begin(), options.end(), "unicode=true");
625 if (it != options.end())
627 it = find(options.begin(), options.end(), "pdfusetitle");
628 if (it != options.end()) {
629 h_pdf_pdfusetitle = "1";
632 string bookmarks = process_keyval_opt(options, "bookmarks");
633 if (bookmarks == "true")
634 h_pdf_bookmarks = "1";
635 else if (bookmarks == "false")
636 h_pdf_bookmarks = "0";
637 if (h_pdf_bookmarks == "1") {
638 string bookmarksnumbered =
639 process_keyval_opt(options, "bookmarksnumbered");
640 if (bookmarksnumbered == "true")
641 h_pdf_bookmarksnumbered = "1";
642 else if (bookmarksnumbered == "false")
643 h_pdf_bookmarksnumbered = "0";
644 string bookmarksopen =
645 process_keyval_opt(options, "bookmarksopen");
646 if (bookmarksopen == "true")
647 h_pdf_bookmarksopen = "1";
648 else if (bookmarksopen == "false")
649 h_pdf_bookmarksopen = "0";
650 if (h_pdf_bookmarksopen == "1") {
651 string bookmarksopenlevel =
652 process_keyval_opt(options, "bookmarksopenlevel");
653 if (!bookmarksopenlevel.empty())
654 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
657 string breaklinks = process_keyval_opt(options, "breaklinks");
658 if (breaklinks == "true")
659 h_pdf_breaklinks = "1";
660 else if (breaklinks == "false")
661 h_pdf_breaklinks = "0";
662 string pdfborder = process_keyval_opt(options, "pdfborder");
663 if (pdfborder == "{0 0 0}")
664 h_pdf_pdfborder = "1";
665 else if (pdfborder == "{0 0 1}")
666 h_pdf_pdfborder = "0";
667 string backref = process_keyval_opt(options, "backref");
668 if (!backref.empty())
669 h_pdf_backref = backref;
670 string colorlinks = process_keyval_opt(options, "colorlinks");
671 if (colorlinks == "true")
672 h_pdf_colorlinks = "1";
673 else if (colorlinks == "false")
674 h_pdf_colorlinks = "0";
675 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
676 if (!pdfpagemode.empty())
677 h_pdf_pagemode = pdfpagemode;
678 string pdftitle = process_keyval_opt(options, "pdftitle");
679 if (!pdftitle.empty()) {
680 h_pdf_title = remove_braces(pdftitle);
682 string pdfauthor = process_keyval_opt(options, "pdfauthor");
683 if (!pdfauthor.empty()) {
684 h_pdf_author = remove_braces(pdfauthor);
686 string pdfsubject = process_keyval_opt(options, "pdfsubject");
687 if (!pdfsubject.empty())
688 h_pdf_subject = remove_braces(pdfsubject);
689 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
690 if (!pdfkeywords.empty())
691 h_pdf_keywords = remove_braces(pdfkeywords);
692 if (!options.empty()) {
693 if (!h_pdf_quoted_options.empty())
694 h_pdf_quoted_options += ',';
695 h_pdf_quoted_options += join(options, ",");
701 void Preamble::handle_geometry(vector<string> & options)
703 h_use_geometry = "true";
704 vector<string>::iterator it;
706 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
707 h_paperorientation = "landscape";
711 // keyval version: "paper=letter"
712 string paper = process_keyval_opt(options, "paper");
714 h_papersize = paper + "paper";
715 // alternative version: "letterpaper"
716 handle_opt(options, known_paper_sizes, h_papersize);
717 delete_opt(options, known_paper_sizes);
719 char const * const * margin = known_paper_margins;
720 for (; *margin; ++margin) {
721 string value = process_keyval_opt(options, *margin);
722 if (!value.empty()) {
723 int k = margin - known_paper_margins;
724 string name = known_coded_paper_margins[k];
725 h_margins += '\\' + name + ' ' + value + '\n';
731 void Preamble::handle_package(Parser &p, string const & name,
732 string const & opts, bool in_lyx_preamble,
735 vector<string> options = split_options(opts);
736 add_package(name, options);
738 if (is_known(name, known_xetex_packages)) {
740 h_use_non_tex_fonts = true;
741 registerAutomaticallyLoadedPackage("fontspec");
742 if (h_inputencoding == "auto-legacy")
743 p.setEncoding("UTF-8");
747 if (is_known(name, known_roman_font_packages))
748 h_font_roman[0] = name;
750 if (name == "fourier") {
751 h_font_roman[0] = "utopia";
752 // when font uses real small capitals
753 if (opts == "expert")
757 if (name == "garamondx") {
758 h_font_roman[0] = "garamondx";
763 if (name == "libertine") {
764 h_font_roman[0] = "libertine";
765 // this automatically invokes biolinum
766 h_font_sans[0] = "biolinum";
767 // as well as libertineMono
768 h_font_typewriter[0] = "libertine-mono";
771 else if (opts == "lining")
772 h_font_osf = "false";
775 if (name == "libertineRoman" || name == "libertine-type1") {
776 h_font_roman[0] = "libertine";
777 // NOTE: contrary to libertine.sty, libertineRoman
778 // and libertine-type1 do not automatically invoke
779 // biolinum and libertineMono
780 if (opts == "lining")
781 h_font_osf = "false";
782 else if (opts == "osf")
786 if (name == "MinionPro") {
787 h_font_roman[0] = "minionpro";
788 vector<string> allopts = getVectorFromString(opts);
791 h_font_math[0] = "auto";
792 for (auto const & opt : allopts) {
794 h_font_osf = "false";
797 if (opt == "onlytext") {
798 h_font_math[0] = "default";
806 h_font_roman_opts = xopts;
810 if (name == "mathdesign") {
811 if (opts.find("charter") != string::npos)
812 h_font_roman[0] = "md-charter";
813 if (opts.find("garamond") != string::npos)
814 h_font_roman[0] = "md-garamond";
815 if (opts.find("utopia") != string::npos)
816 h_font_roman[0] = "md-utopia";
817 if (opts.find("expert") != string::npos) {
823 else if (name == "mathpazo")
824 h_font_roman[0] = "palatino";
826 else if (name == "mathptmx")
827 h_font_roman[0] = "times";
829 if (name == "crimson")
830 h_font_roman[0] = "cochineal";
832 if (name == "cochineal") {
833 h_font_roman[0] = "cochineal";
834 // cochineal can have several options, e.g. [proportional,osf]
835 string::size_type pos = opts.find("osf");
836 if (pos != string::npos)
840 if (name == "noto") {
841 // noto can have several options
843 h_font_roman[0] = "NotoSerif-TLF";
844 string::size_type pos = opts.find("rm");
845 if (pos != string::npos)
846 h_font_roman[0] = "NotoSerif-TLF";
847 pos = opts.find("sf");
848 if (pos != string::npos)
849 h_font_sans[0] = "NotoSans-TLF";
850 pos = opts.find("nott");
851 if (pos != string::npos) {
852 h_font_roman[0] = "NotoSerif-TLF";
853 h_font_sans[0] = "NotoSans-TLF";
855 // noto as typewriter is handled in handling of \ttdefault
856 // special cases are handled in handling of \rmdefault and \sfdefault
857 vector<string> allopts = getVectorFromString(opts);
859 for (auto const & opt : allopts) {
876 if (name == "paratype") {
877 // in this case all fonts are ParaType
878 h_font_roman[0] = "PTSerif-TLF";
879 h_font_sans[0] = "default";
880 h_font_typewriter[0] = "default";
883 if (name == "PTSerif")
884 h_font_roman[0] = "PTSerif-TLF";
886 if (name == "XCharter") {
887 h_font_roman[0] = "xcharter";
892 if (name == "plex-serif") {
894 h_font_roman[0] = "IBMPlexSerif";
895 else if (opts.find("thin") != string::npos)
896 h_font_roman[0] = "IBMPlexSerifThin";
897 else if (opts.find("extralight") != string::npos)
898 h_font_roman[0] = "IBMPlexSerifExtraLight";
899 else if (opts.find("light") != string::npos)
900 h_font_roman[0] = "IBMPlexSerifLight";
901 else if (opts.find("semibold") != string::npos)
902 h_font_roman[0] = "IBMPlexSerifSemibold";
903 vector<string> allopts = getVectorFromString(opts);
905 for (auto const & opt : allopts) {
908 if (opt == "extralight")
912 if (opt == "semibold")
919 h_font_roman_opts = xopts;
922 if (name == "noto-serif") {
923 h_font_roman[0] = "NotoSerifRegular";
925 if (opts.find("thin") != string::npos)
926 h_font_roman[0] = "NotoSerifThin";
927 else if (opts.find("medium") != string::npos)
928 h_font_roman[0] = "NotoSerifMedium";
929 else if (opts.find("extralight") != string::npos)
930 h_font_roman[0] = "NotoSerifExtralight";
931 else if (opts.find("light") != string::npos)
932 h_font_roman[0] = "NotoSerifLight";
934 vector<string> allopts = getVectorFromString(opts);
936 for (auto const & opt : allopts) {
937 if (opt == "regular")
941 if (opt == "extralight")
945 if (opt == "semibold")
952 h_font_roman_opts = xopts;
956 if (name == "sourceserifpro") {
957 h_font_roman[0] = "ADOBESourceSerifPro";
958 vector<string> allopts = getVectorFromString(opts);
960 for (auto const & opt : allopts) {
970 h_font_roman_opts = xopts;
975 if (is_known(name, known_sans_font_packages)) {
976 h_font_sans[0] = name;
977 if (options.size() >= 1) {
978 if (scale_as_percentage(opts, h_font_sf_scale[0]))
983 if (name == "biolinum" || name == "biolinum-type1") {
984 h_font_sans[0] = "biolinum";
985 // biolinum can have several options, e.g. [osf,scaled=0.97]
986 string::size_type pos = opts.find("osf");
987 if (pos != string::npos)
991 if (name == "PTSans") {
992 h_font_sans[0] = "PTSans-TLF";
993 if (options.size() >= 1) {
994 if (scale_as_percentage(opts, h_font_sf_scale[0]))
999 if (name == "plex-sans") {
1000 if (opts.find("condensed") != string::npos)
1001 h_font_sans[0] = "IBMPlexSansCondensed";
1002 else if (opts.find("thin") != string::npos)
1003 h_font_sans[0] = "IBMPlexSansThin";
1004 else if (opts.find("extralight") != string::npos)
1005 h_font_sans[0] = "IBMPlexSansExtraLight";
1006 else if (opts.find("light") != string::npos)
1007 h_font_sans[0] = "IBMPlexSansLight";
1008 else if (opts.find("semibold") != string::npos)
1009 h_font_sans[0] = "IBMPlexSansSemibold";
1011 h_font_sans[0] = "IBMPlexSans";
1012 vector<string> allopts = getVectorFromString(opts);
1014 for (auto const & opt : allopts) {
1017 if (opt == "extralight")
1021 if (opt == "semibold")
1023 if (prefixIs(opt, "scale=")) {
1024 scale_as_percentage(opt, h_font_sf_scale[0]);
1032 h_font_sans_opts = xopts;
1035 if (name == "noto-sans") {
1036 h_font_sans[0] = "NotoSansRegular";
1037 if (!opts.empty()) {
1038 if (opts.find("medium") != string::npos)
1039 h_font_sans[0] = "NotoSansMedium";
1040 else if (opts.find("thin") != string::npos)
1041 h_font_sans[0] = "NotoSansThin";
1042 else if (opts.find("extralight") != string::npos)
1043 h_font_sans[0] = "NotoSansExtralight";
1044 else if (opts.find("light") != string::npos)
1045 h_font_sans[0] = "NotoSansLight";
1047 vector<string> allopts = getVectorFromString(opts);
1049 for (auto const & opt : allopts) {
1050 if (opt == "regular")
1054 if (opt == "extralight")
1058 if (opt == "semibold")
1065 h_font_sans_opts = xopts;
1069 if (name == "sourcesanspro") {
1070 h_font_sans[0] = "ADOBESourceSansPro";
1071 vector<string> allopts = getVectorFromString(opts);
1073 for (auto const & opt : allopts) {
1074 if (prefixIs(opt, "scaled=")) {
1075 scale_as_percentage(opt, h_font_sf_scale[0]);
1079 h_font_osf = "true";
1087 h_font_sans_opts = xopts;
1092 if (is_known(name, known_typewriter_font_packages)) {
1093 // fourier can be set as roman font _only_
1094 // fourier as typewriter is handled in handling of \ttdefault
1095 if (name != "fourier") {
1096 h_font_typewriter[0] = name;
1097 if (options.size() >= 1) {
1098 if (scale_as_percentage(opts, h_font_tt_scale[0]))
1104 if (name == "libertineMono" || name == "libertineMono-type1")
1105 h_font_typewriter[0] = "libertine-mono";
1107 if (name == "PTMono") {
1108 h_font_typewriter[0] = "PTMono-TLF";
1109 if (options.size() >= 1) {
1110 if (scale_as_percentage(opts, h_font_tt_scale[0]))
1115 if (name == "plex-mono") {
1116 if (opts.find("thin") != string::npos)
1117 h_font_typewriter[0] = "IBMPlexMonoThin";
1118 else if (opts.find("extralight") != string::npos)
1119 h_font_typewriter[0] = "IBMPlexMonoExtraLight";
1120 else if (opts.find("light") != string::npos)
1121 h_font_typewriter[0] = "IBMPlexMonoLight";
1122 else if (opts.find("semibold") != string::npos)
1123 h_font_typewriter[0] = "IBMPlexMonoSemibold";
1125 h_font_typewriter[0] = "IBMPlexMono";
1126 vector<string> allopts = getVectorFromString(opts);
1128 for (auto const & opt : allopts) {
1131 if (opt == "extralight")
1135 if (opt == "semibold")
1137 if (prefixIs(opt, "scale=")) {
1138 scale_as_percentage(opt, h_font_tt_scale[0]);
1146 h_font_typewriter_opts = xopts;
1150 if (name == "noto-mono") {
1151 h_font_typewriter[0] = "NotoMonoRegular";
1152 vector<string> allopts = getVectorFromString(opts);
1154 for (auto const & opt : allopts) {
1155 if (opt == "regular")
1162 h_font_typewriter_opts = xopts;
1166 if (name == "sourcecodepro") {
1167 h_font_typewriter[0] = "ADOBESourceCodePro";
1168 vector<string> allopts = getVectorFromString(opts);
1170 for (auto const & opt : allopts) {
1171 if (prefixIs(opt, "scaled=")) {
1172 scale_as_percentage(opt, h_font_tt_scale[0]);
1176 h_font_osf = "true";
1184 h_font_typewriter_opts = xopts;
1188 // font uses old-style figure
1190 h_font_osf = "true";
1193 if (is_known(name, known_math_font_packages))
1194 h_font_math[0] = name;
1196 if (name == "newtxmath") {
1198 h_font_math[0] = "newtxmath";
1199 else if (opts == "garamondx")
1200 h_font_math[0] = "garamondx-ntxm";
1201 else if (opts == "libertine")
1202 h_font_math[0] = "libertine-ntxm";
1203 else if (opts == "minion")
1204 h_font_math[0] = "minion-ntxm";
1205 else if (opts == "cochineal")
1206 h_font_math[0] = "cochineal-ntxm";
1209 if (name == "iwona")
1211 h_font_math[0] = "iwona-math";
1213 if (name == "kurier")
1215 h_font_math[0] = "kurier-math";
1217 // after the detection and handling of special cases, we can remove the
1218 // fonts, otherwise they would appear in the preamble, see bug #7856
1219 if (is_known(name, known_roman_font_packages) || is_known(name, known_sans_font_packages)
1220 || is_known(name, known_typewriter_font_packages) || is_known(name, known_math_font_packages))
1222 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
1223 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
1224 name == "esint" || name == "mhchem" || name == "mathdots" ||
1225 name == "mathtools" || name == "stackrel" ||
1226 name == "stmaryrd" || name == "undertilde") {
1227 h_use_packages[name] = "2";
1228 registerAutomaticallyLoadedPackage(name);
1231 else if (name == "babel") {
1232 h_language_package = "default";
1233 // One might think we would have to do nothing if babel is loaded
1234 // without any options to prevent pollution of the preamble with this
1235 // babel call in every roundtrip.
1236 // But the user could have defined babel-specific things afterwards. So
1237 // we need to keep it in the preamble to prevent cases like bug #7861.
1238 if (!opts.empty()) {
1239 // check if more than one option was used - used later for inputenc
1240 if (options.begin() != options.end() - 1)
1241 one_language = false;
1242 // babel takes the last language of the option of its \usepackage
1243 // call as document language. If there is no such language option, the
1244 // last language in the documentclass options is used.
1245 handle_opt(options, known_languages, h_language);
1246 // translate the babel name to a LyX name
1247 h_language = babel2lyx(h_language);
1248 if (h_language == "japanese") {
1249 // For Japanese, the encoding isn't indicated in the source
1250 // file, and there's really not much we can do. We could
1251 // 1) offer a list of possible encodings to choose from, or
1252 // 2) determine the encoding of the file by inspecting it.
1253 // For the time being, we leave the encoding alone so that
1254 // we don't get iconv errors when making a wrong guess, and
1255 // we will output a note at the top of the document
1256 // explaining what to do.
1257 Encoding const * const enc = encodings.fromIconvName(
1258 p.getEncoding(), Encoding::japanese, false);
1260 h_inputencoding = enc->name();
1261 is_nonCJKJapanese = true;
1262 // in this case babel can be removed from the preamble
1263 registerAutomaticallyLoadedPackage("babel");
1265 // If babel is called with options, LyX puts them by default into the
1266 // document class options. This works for most languages, except
1267 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
1268 // perhaps in future others.
1269 // Therefore keep the babel call as it is as the user might have
1271 string const babelcall = "\\usepackage[" + opts + "]{babel}\n";
1272 if (!contains(h_preamble.str(), babelcall))
1273 h_preamble << babelcall;
1275 delete_opt(options, known_languages);
1277 if (!contains(h_preamble.str(), "\\usepackage{babel}\n"))
1278 h_preamble << "\\usepackage{babel}\n";
1279 explicit_babel = true;
1283 else if (name == "polyglossia") {
1284 h_language_package = "default";
1285 h_default_output_format = "pdf4";
1286 h_use_non_tex_fonts = true;
1288 registerAutomaticallyLoadedPackage("xunicode");
1289 if (h_inputencoding == "auto-legacy")
1290 p.setEncoding("UTF-8");
1293 else if (name == "CJK") {
1294 // set the encoding to "auto-legacy" because it might be set to "auto-legacy-plain" by the babel handling
1295 // and this would not be correct for CJK
1296 if (h_inputencoding == "auto-legacy-plain")
1297 h_inputencoding = "auto-legacy";
1298 registerAutomaticallyLoadedPackage("CJK");
1301 else if (name == "CJKutf8") {
1302 h_inputencoding = "utf8-cjk";
1303 p.setEncoding("UTF-8");
1304 registerAutomaticallyLoadedPackage("CJKutf8");
1307 else if (name == "fontenc") {
1308 h_fontencoding = getStringFromVector(options, ",");
1312 else if (name == "inputenc" || name == "luainputenc") {
1313 // h_inputencoding is only set when there is not more than one
1314 // inputenc option because otherwise h_inputencoding must be
1315 // set to "auto-legacy" (the default encodings of the document's languages)
1316 // Therefore check that exactly one option is passed to inputenc.
1317 // It is also only set when there is not more than one babel
1319 if (!options.empty()) {
1320 string const encoding = options.back();
1321 Encoding const * const enc = encodings.fromLaTeXName(
1322 encoding, Encoding::inputenc, true);
1324 if (!detectEncoding)
1325 cerr << "Unknown encoding " << encoding
1326 << ". Ignoring." << std::endl;
1328 if (!enc->unsafe() && options.size() == 1 && one_language == true)
1329 h_inputencoding = enc->name();
1330 p.setEncoding(enc->iconvName());
1336 else if (name == "srcltx") {
1337 h_output_sync = "1";
1338 if (!opts.empty()) {
1339 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
1342 h_output_sync_macro = "\\usepackage{srcltx}";
1345 else if (is_known(name, known_old_language_packages)) {
1346 // known language packages from the times before babel
1347 // if they are found and not also babel, they will be used as
1348 // custom language package
1349 h_language_package = "\\usepackage{" + name + "}";
1352 else if (name == "lyxskak") {
1353 // ignore this and its options
1354 const char * const o[] = {"ps", "mover", 0};
1355 delete_opt(options, o);
1358 else if (is_known(name, known_lyx_packages) && options.empty()) {
1359 if (name == "splitidx")
1360 h_use_indices = "true";
1361 else if (name == "minted")
1362 h_use_minted = true;
1363 else if (name == "refstyle")
1364 h_use_refstyle = true;
1365 else if (name == "prettyref")
1366 h_use_refstyle = false;
1367 if (!in_lyx_preamble) {
1368 h_preamble << package_beg_sep << name
1369 << package_mid_sep << "\\usepackage{"
1371 if (p.next_token().cat() == catNewline ||
1372 (p.next_token().cat() == catSpace &&
1373 p.next_next_token().cat() == catNewline))
1375 h_preamble << package_end_sep;
1379 else if (name == "geometry")
1380 handle_geometry(options);
1382 else if (name == "subfig")
1383 ; // ignore this FIXME: Use the package separator mechanism instead
1385 else if (char const * const * where = is_known(name, known_languages))
1386 h_language = known_coded_languages[where - known_languages];
1388 else if (name == "natbib") {
1389 h_biblio_style = "plainnat";
1390 h_cite_engine = "natbib";
1391 h_cite_engine_type = "authoryear";
1392 vector<string>::iterator it =
1393 find(options.begin(), options.end(), "authoryear");
1394 if (it != options.end())
1397 it = find(options.begin(), options.end(), "numbers");
1398 if (it != options.end()) {
1399 h_cite_engine_type = "numerical";
1403 if (!options.empty())
1404 h_biblio_options = join(options, ",");
1407 else if (name == "biblatex") {
1408 h_biblio_style = "plainnat";
1409 h_cite_engine = "biblatex";
1410 h_cite_engine_type = "authoryear";
1412 vector<string>::iterator it =
1413 find(options.begin(), options.end(), "natbib");
1414 if (it != options.end()) {
1416 h_cite_engine = "biblatex-natbib";
1418 opt = process_keyval_opt(options, "natbib");
1420 h_cite_engine = "biblatex-natbib";
1422 opt = process_keyval_opt(options, "style");
1424 h_biblatex_citestyle = opt;
1425 h_biblatex_bibstyle = opt;
1427 opt = process_keyval_opt(options, "citestyle");
1429 h_biblatex_citestyle = opt;
1430 opt = process_keyval_opt(options, "bibstyle");
1432 h_biblatex_bibstyle = opt;
1434 opt = process_keyval_opt(options, "refsection");
1436 if (opt == "none" || opt == "part"
1437 || opt == "chapter" || opt == "section"
1438 || opt == "subsection")
1441 cerr << "Ignoring unkown refesection value '"
1444 opt = process_keyval_opt(options, "bibencoding");
1447 if (!options.empty()) {
1448 h_biblio_options = join(options, ",");
1453 else if (name == "jurabib") {
1454 h_biblio_style = "jurabib";
1455 h_cite_engine = "jurabib";
1456 h_cite_engine_type = "authoryear";
1457 if (!options.empty())
1458 h_biblio_options = join(options, ",");
1461 else if (name == "bibtopic")
1462 h_use_bibtopic = "true";
1464 else if (name == "chapterbib")
1465 h_multibib = "child";
1467 else if (name == "hyperref")
1468 handle_hyperref(options);
1470 else if (name == "algorithm2e") {
1471 // Load "algorithm2e" module
1472 addModule("algorithm2e");
1473 // Add the package options to the global document options
1474 if (!options.empty()) {
1475 if (h_options.empty())
1476 h_options = join(options, ",");
1478 h_options += ',' + join(options, ",");
1481 else if (name == "microtype") {
1482 //we internally support only microtype without params
1483 if (options.empty())
1484 h_use_microtype = "true";
1486 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1489 else if (!in_lyx_preamble) {
1490 if (options.empty())
1491 h_preamble << "\\usepackage{" << name << '}';
1493 h_preamble << "\\usepackage[" << opts << "]{"
1497 if (p.next_token().cat() == catNewline ||
1498 (p.next_token().cat() == catSpace &&
1499 p.next_next_token().cat() == catNewline))
1503 // We need to do something with the options...
1504 if (!options.empty() && !detectEncoding)
1505 cerr << "Ignoring options '" << join(options, ",")
1506 << "' of package " << name << '.' << endl;
1508 // remove the whitespace
1513 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1516 Token t = p.get_token();
1517 if (t.cat() == catEscape &&
1518 is_known(t.cs(), known_if_commands))
1519 handle_if(p, in_lyx_preamble);
1521 if (!in_lyx_preamble)
1522 h_preamble << t.asInput();
1523 if (t.cat() == catEscape && t.cs() == "fi")
1530 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1532 if (contains(h_float_placement, "H"))
1533 registerAutomaticallyLoadedPackage("float");
1534 if (h_spacing != "single" && h_spacing != "default")
1535 registerAutomaticallyLoadedPackage("setspace");
1536 if (h_use_packages["amsmath"] == "2") {
1537 // amsbsy and amstext are already provided by amsmath
1538 registerAutomaticallyLoadedPackage("amsbsy");
1539 registerAutomaticallyLoadedPackage("amstext");
1542 // output the LyX file settings
1543 // Important: Keep the version formatting in sync with LyX and
1544 // lyx2lyx (bug 7951)
1545 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1546 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1547 << lyx_version_minor << '\n'
1548 << "\\lyxformat " << LYX_FORMAT << '\n'
1549 << "\\begin_document\n"
1550 << "\\begin_header\n"
1551 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1552 << "\\origin " << origin << "\n"
1553 << "\\textclass " << h_textclass << "\n";
1554 string const raw = subdoc ? empty_string() : h_preamble.str();
1556 os << "\\begin_preamble\n";
1557 for (string::size_type i = 0; i < raw.size(); ++i) {
1558 if (raw[i] == package_beg_sep) {
1559 // Here follows some package loading code that
1560 // must be skipped if the package is loaded
1562 string::size_type j = raw.find(package_mid_sep, i);
1563 if (j == string::npos)
1565 string::size_type k = raw.find(package_end_sep, j);
1566 if (k == string::npos)
1568 string const package = raw.substr(i + 1, j - i - 1);
1569 string const replacement = raw.substr(j + 1, k - j - 1);
1570 if (auto_packages.find(package) == auto_packages.end())
1576 os << "\n\\end_preamble\n";
1578 if (!h_options.empty())
1579 os << "\\options " << h_options << "\n";
1580 os << "\\use_default_options " << h_use_default_options << "\n";
1581 if (!used_modules.empty()) {
1582 os << "\\begin_modules\n";
1583 vector<string>::const_iterator const end = used_modules.end();
1584 vector<string>::const_iterator it = used_modules.begin();
1585 for (; it != end; ++it)
1587 os << "\\end_modules\n";
1589 if (!h_includeonlys.empty()) {
1590 os << "\\begin_includeonly\n";
1591 for (auto const & iofile : h_includeonlys)
1592 os << iofile << '\n';
1593 os << "\\end_includeonly\n";
1595 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1596 << "\\language " << h_language << "\n"
1597 << "\\language_package " << h_language_package << "\n"
1598 << "\\inputencoding " << h_inputencoding << "\n"
1599 << "\\fontencoding " << h_fontencoding << "\n"
1600 << "\\font_roman \"" << h_font_roman[0]
1601 << "\" \"" << h_font_roman[1] << "\"\n"
1602 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1603 << "\\font_typewriter \"" << h_font_typewriter[0]
1604 << "\" \"" << h_font_typewriter[1] << "\"\n"
1605 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1606 << "\\font_default_family " << h_font_default_family << "\n"
1607 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1608 << "\\font_sc " << h_font_sc << "\n"
1609 << "\\font_osf " << h_font_osf << "\n";
1610 if (!h_font_roman_opts.empty())
1611 os << "\\font_roman_opts \"" << h_font_roman_opts << "\"" << '\n';
1612 os << "\\font_sf_scale " << h_font_sf_scale[0]
1613 << ' ' << h_font_sf_scale[1] << '\n';
1614 if (!h_font_sans_opts.empty())
1615 os << "\\font_sans_opts \"" << h_font_sans_opts << "\"" << '\n';
1616 os << "\\font_tt_scale " << h_font_tt_scale[0]
1617 << ' ' << h_font_tt_scale[1] << '\n';
1618 if (!h_font_cjk.empty())
1619 os << "\\font_cjk " << h_font_cjk << '\n';
1620 if (!h_font_typewriter_opts.empty())
1621 os << "\\font_typewriter_opts \"" << h_font_typewriter_opts << "\"" << '\n';
1622 os << "\\use_microtype " << h_use_microtype << '\n'
1623 << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
1624 << "\\graphics " << h_graphics << '\n'
1625 << "\\default_output_format " << h_default_output_format << "\n"
1626 << "\\output_sync " << h_output_sync << "\n";
1627 if (h_output_sync == "1")
1628 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1629 os << "\\bibtex_command " << h_bibtex_command << "\n"
1630 << "\\index_command " << h_index_command << "\n";
1631 if (!h_float_placement.empty())
1632 os << "\\float_placement " << h_float_placement << "\n";
1633 os << "\\paperfontsize " << h_paperfontsize << "\n"
1634 << "\\spacing " << h_spacing << "\n"
1635 << "\\use_hyperref " << h_use_hyperref << '\n';
1636 if (h_use_hyperref == "true") {
1637 if (!h_pdf_title.empty())
1638 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1639 if (!h_pdf_author.empty())
1640 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1641 if (!h_pdf_subject.empty())
1642 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1643 if (!h_pdf_keywords.empty())
1644 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1645 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1646 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1647 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1648 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1649 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1650 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1651 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1652 "\\pdf_backref " << h_pdf_backref << "\n"
1653 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1654 if (!h_pdf_pagemode.empty())
1655 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1656 if (!h_pdf_quoted_options.empty())
1657 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1659 os << "\\papersize " << h_papersize << "\n"
1660 << "\\use_geometry " << h_use_geometry << '\n';
1661 for (map<string, string>::const_iterator it = h_use_packages.begin();
1662 it != h_use_packages.end(); ++it)
1663 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1664 os << "\\cite_engine " << h_cite_engine << '\n'
1665 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1666 << "\\biblio_style " << h_biblio_style << "\n"
1667 << "\\use_bibtopic " << h_use_bibtopic << "\n";
1668 if (!h_biblio_options.empty())
1669 os << "\\biblio_options " << h_biblio_options << "\n";
1670 if (!h_biblatex_bibstyle.empty())
1671 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
1672 if (!h_biblatex_citestyle.empty())
1673 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
1674 if (!h_multibib.empty())
1675 os << "\\multibib " << h_multibib << "\n";
1676 os << "\\use_indices " << h_use_indices << "\n"
1677 << "\\paperorientation " << h_paperorientation << '\n'
1678 << "\\suppress_date " << h_suppress_date << '\n'
1679 << "\\justification " << h_justification << '\n'
1680 << "\\use_refstyle " << h_use_refstyle << '\n'
1681 << "\\use_minted " << h_use_minted << '\n';
1682 if (!h_fontcolor.empty())
1683 os << "\\fontcolor " << h_fontcolor << '\n';
1684 if (!h_notefontcolor.empty())
1685 os << "\\notefontcolor " << h_notefontcolor << '\n';
1686 if (!h_backgroundcolor.empty())
1687 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1688 if (!h_boxbgcolor.empty())
1689 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1690 if (index_number != 0)
1691 for (int i = 0; i < index_number; i++) {
1692 os << "\\index " << h_index[i] << '\n'
1693 << "\\shortcut " << h_shortcut[i] << '\n'
1694 << "\\color " << h_color << '\n'
1698 os << "\\index " << h_index[0] << '\n'
1699 << "\\shortcut " << h_shortcut[0] << '\n'
1700 << "\\color " << h_color << '\n'
1704 << "\\secnumdepth " << h_secnumdepth << "\n"
1705 << "\\tocdepth " << h_tocdepth << "\n"
1706 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1707 if (h_paragraph_separation == "skip")
1708 os << "\\defskip " << h_defskip << "\n";
1710 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1711 os << "\\is_math_indent " << h_is_mathindent << "\n";
1712 if (!h_mathindentation.empty())
1713 os << "\\math_indentation " << h_mathindentation << "\n";
1714 os << "\\math_numbering_side " << h_math_numbering_side << "\n";
1715 os << "\\quotes_style " << h_quotes_style << "\n"
1716 << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
1717 << "\\papercolumns " << h_papercolumns << "\n"
1718 << "\\papersides " << h_papersides << "\n"
1719 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1720 if (!h_listings_params.empty())
1721 os << "\\listings_params " << h_listings_params << "\n";
1722 os << "\\tracking_changes " << h_tracking_changes << "\n"
1723 << "\\output_changes " << h_output_changes << "\n"
1724 << "\\html_math_output " << h_html_math_output << "\n"
1725 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1726 << "\\html_be_strict " << h_html_be_strict << "\n"
1728 << "\\end_header\n\n"
1729 << "\\begin_body\n";
1734 void Preamble::parse(Parser & p, string const & forceclass,
1735 TeX2LyXDocClass & tc)
1737 // initialize fixed types
1738 special_columns_['D'] = 3;
1739 parse(p, forceclass, false, tc);
1743 void Preamble::parse(Parser & p, string const & forceclass,
1744 bool detectEncoding, TeX2LyXDocClass & tc)
1746 bool is_full_document = false;
1747 bool is_lyx_file = false;
1748 bool in_lyx_preamble = false;
1750 // determine whether this is a full document or a fragment for inclusion
1752 Token const & t = p.get_token();
1754 if (t.cat() == catEscape && t.cs() == "documentclass") {
1755 is_full_document = true;
1761 if (detectEncoding && !is_full_document)
1764 while (is_full_document && p.good()) {
1765 if (detectEncoding && h_inputencoding != "auto-legacy" &&
1766 h_inputencoding != "auto-legacy-plain")
1769 Token const & t = p.get_token();
1772 if (!detectEncoding)
1773 cerr << "t: " << t << '\n';
1779 if (!in_lyx_preamble &&
1780 (t.cat() == catLetter ||
1781 t.cat() == catSuper ||
1782 t.cat() == catSub ||
1783 t.cat() == catOther ||
1784 t.cat() == catMath ||
1785 t.cat() == catActive ||
1786 t.cat() == catBegin ||
1787 t.cat() == catEnd ||
1788 t.cat() == catAlign ||
1789 t.cat() == catParameter)) {
1790 h_preamble << t.cs();
1794 if (!in_lyx_preamble &&
1795 (t.cat() == catSpace || t.cat() == catNewline)) {
1796 h_preamble << t.asInput();
1800 if (t.cat() == catComment) {
1801 static regex const islyxfile("%% LyX .* created this file");
1802 static regex const usercommands("User specified LaTeX commands");
1804 string const comment = t.asInput();
1806 // magically switch encoding default if it looks like XeLaTeX
1807 static string const magicXeLaTeX =
1808 "% This document must be compiled with XeLaTeX ";
1809 if (comment.size() > magicXeLaTeX.size()
1810 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1811 && h_inputencoding == "auto-legacy") {
1812 if (!detectEncoding)
1813 cerr << "XeLaTeX comment found, switching to UTF8\n";
1814 h_inputencoding = "utf8";
1817 if (regex_search(comment, sub, islyxfile)) {
1819 in_lyx_preamble = true;
1820 } else if (is_lyx_file
1821 && regex_search(comment, sub, usercommands))
1822 in_lyx_preamble = false;
1823 else if (!in_lyx_preamble)
1824 h_preamble << t.asInput();
1828 if (t.cs() == "PassOptionsToPackage") {
1829 string const poptions = p.getArg('{', '}');
1830 string const package = p.verbatim_item();
1831 extra_package_options_.insert(make_pair(package, poptions));
1835 if (t.cs() == "pagestyle") {
1836 h_paperpagestyle = p.verbatim_item();
1840 if (t.cs() == "setdefaultlanguage") {
1842 // We don't yet care about non-language variant options
1843 // because LyX doesn't support this yet, see bug #8214
1845 string langopts = p.getOpt();
1846 // check if the option contains a variant, if yes, extract it
1847 string::size_type pos_var = langopts.find("variant");
1848 string::size_type i = langopts.find(',', pos_var);
1849 string::size_type k = langopts.find('=', pos_var);
1850 if (pos_var != string::npos){
1852 if (i == string::npos)
1853 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1855 variant = langopts.substr(k + 1, i - k - 1);
1856 h_language = variant;
1860 h_language = p.verbatim_item();
1861 //finally translate the poyglossia name to a LyX name
1862 h_language = polyglossia2lyx(h_language);
1866 if (t.cs() == "setotherlanguage") {
1867 // We don't yet care about the option because LyX doesn't
1868 // support this yet, see bug #8214
1869 p.hasOpt() ? p.getOpt() : string();
1874 if (t.cs() == "setmainfont") {
1875 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
1876 h_font_roman[1] = p.getArg('{', '}');
1877 if (!fontopts.empty()) {
1878 vector<string> opts = getVectorFromString(fontopts);
1880 for (auto const & opt : opts) {
1881 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
1884 if (!fontopts.empty())
1888 h_font_roman_opts = fontopts;
1893 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1894 // LyX currently only supports the scale option
1895 string scale, fontopts;
1897 fontopts = p.getArg('[', ']');
1898 if (!fontopts.empty()) {
1899 vector<string> opts = getVectorFromString(fontopts);
1901 for (auto const & opt : opts) {
1902 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
1905 if (prefixIs(opt, "Scale=")) {
1906 scale_as_percentage(opt, scale);
1909 if (!fontopts.empty())
1915 if (t.cs() == "setsansfont") {
1917 h_font_sf_scale[1] = scale;
1918 h_font_sans[1] = p.getArg('{', '}');
1919 if (!fontopts.empty())
1920 h_font_sans_opts = fontopts;
1923 h_font_tt_scale[1] = scale;
1924 h_font_typewriter[1] = p.getArg('{', '}');
1925 if (!fontopts.empty())
1926 h_font_typewriter_opts = fontopts;
1931 if (t.cs() == "babelfont") {
1933 h_use_non_tex_fonts = true;
1934 h_language_package = "babel";
1935 if (h_inputencoding == "auto-legacy")
1936 p.setEncoding("UTF-8");
1937 // we don't care about the lang option
1938 string const lang = p.hasOpt() ? p.getArg('[', ']') : string();
1939 string const family = p.getArg('{', '}');
1940 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
1941 string const fontname = p.getArg('{', '}');
1942 if (lang.empty() && family == "rm") {
1943 h_font_roman[1] = fontname;
1944 if (!fontopts.empty()) {
1945 vector<string> opts = getVectorFromString(fontopts);
1947 for (auto const & opt : opts) {
1948 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
1951 if (!fontopts.empty())
1955 h_font_roman_opts = fontopts;
1958 } else if (lang.empty() && (family == "sf" || family == "tt")) {
1959 // LyX currently only supports the scale option
1961 if (!fontopts.empty()) {
1962 vector<string> opts = getVectorFromString(fontopts);
1964 for (auto const & opt : opts) {
1965 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
1968 if (prefixIs(opt, "Scale=")) {
1969 scale_as_percentage(opt, scale);
1972 if (!fontopts.empty())
1977 if (family == "sf") {
1979 h_font_sf_scale[1] = scale;
1980 h_font_sans[1] = fontname;
1981 if (!fontopts.empty())
1982 h_font_sans_opts = fontopts;
1985 h_font_tt_scale[1] = scale;
1986 h_font_typewriter[1] = fontname;
1987 if (!fontopts.empty())
1988 h_font_typewriter_opts = fontopts;
1992 // not rm, sf or tt or lang specific
1993 h_preamble << '\\' << t.cs();
1995 h_preamble << '[' << lang << ']';
1996 h_preamble << '{' << family << '}';
1997 if (!fontopts.empty())
1998 h_preamble << '[' << fontopts << ']';
1999 h_preamble << '{' << fontname << '}' << '\n';
2004 if (t.cs() == "date") {
2005 string argument = p.getArg('{', '}');
2006 if (argument.empty())
2007 h_suppress_date = "true";
2009 h_preamble << t.asInput() << '{' << argument << '}';
2013 if (t.cs() == "color") {
2014 string const space =
2015 (p.hasOpt() ? p.getOpt() : string());
2016 string argument = p.getArg('{', '}');
2017 // check the case that a standard color is used
2018 if (space.empty() && is_known(argument, known_basic_colors)) {
2019 h_fontcolor = rgbcolor2code(argument);
2020 registerAutomaticallyLoadedPackage("color");
2021 } else if (space.empty() && argument == "document_fontcolor")
2022 registerAutomaticallyLoadedPackage("color");
2023 // check the case that LyX's document_fontcolor is defined
2024 // but not used for \color
2026 h_preamble << t.asInput();
2028 h_preamble << space;
2029 h_preamble << '{' << argument << '}';
2030 // the color might already be set because \definecolor
2031 // is parsed before this
2037 if (t.cs() == "pagecolor") {
2038 string argument = p.getArg('{', '}');
2039 // check the case that a standard color is used
2040 if (is_known(argument, known_basic_colors)) {
2041 h_backgroundcolor = rgbcolor2code(argument);
2042 } else if (argument == "page_backgroundcolor")
2043 registerAutomaticallyLoadedPackage("color");
2044 // check the case that LyX's page_backgroundcolor is defined
2045 // but not used for \pagecolor
2047 h_preamble << t.asInput() << '{' << argument << '}';
2048 // the color might already be set because \definecolor
2049 // is parsed before this
2050 h_backgroundcolor = "";
2055 if (t.cs() == "makeatletter") {
2056 // LyX takes care of this
2057 p.setCatcode('@', catLetter);
2061 if (t.cs() == "makeatother") {
2062 // LyX takes care of this
2063 p.setCatcode('@', catOther);
2067 if (t.cs() == "makeindex") {
2068 // LyX will re-add this if a print index command is found
2073 if (t.cs() == "newindex") {
2074 string const indexname = p.getArg('[', ']');
2075 string const shortcut = p.verbatim_item();
2076 if (!indexname.empty())
2077 h_index[index_number] = indexname;
2079 h_index[index_number] = shortcut;
2080 h_shortcut[index_number] = shortcut;
2086 if (t.cs() == "addbibresource") {
2087 string const options = p.getArg('[', ']');
2088 string const arg = removeExtension(p.getArg('{', '}'));
2089 if (!options.empty()) {
2090 // check if the option contains a bibencoding, if yes, extract it
2091 string::size_type pos = options.find("bibencoding=");
2093 if (pos != string::npos) {
2094 string::size_type i = options.find(',', pos);
2095 if (i == string::npos)
2096 encoding = options.substr(pos + 1);
2098 encoding = options.substr(pos, i - pos);
2099 pos = encoding.find('=');
2100 if (pos == string::npos)
2103 encoding = encoding.substr(pos + 1);
2105 if (!encoding.empty())
2106 biblatex_encodings.push_back(normalize_filename(arg) + ' ' + encoding);
2108 biblatex_bibliographies.push_back(arg);
2112 if (t.cs() == "bibliography") {
2113 vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
2114 biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
2118 if (t.cs() == "RS@ifundefined") {
2119 string const name = p.verbatim_item();
2120 string const body1 = p.verbatim_item();
2121 string const body2 = p.verbatim_item();
2122 // only non-lyxspecific stuff
2123 if (in_lyx_preamble &&
2124 (name == "subsecref" || name == "thmref" || name == "lemref"))
2128 ss << '\\' << t.cs();
2129 ss << '{' << name << '}'
2130 << '{' << body1 << '}'
2131 << '{' << body2 << '}';
2132 h_preamble << ss.str();
2137 if (t.cs() == "AtBeginDocument") {
2138 string const name = p.verbatim_item();
2139 // only non-lyxspecific stuff
2140 if (in_lyx_preamble &&
2141 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
2142 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
2143 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
2144 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
2145 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
2146 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
2147 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
2148 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
2149 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
2150 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
2151 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
2152 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
2153 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
2154 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
2155 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
2159 ss << '\\' << t.cs();
2160 ss << '{' << name << '}';
2161 h_preamble << ss.str();
2166 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
2167 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
2168 || t.cs() == "providecommand" || t.cs() == "providecommandx"
2169 || t.cs() == "DeclareRobustCommand"
2170 || t.cs() == "DeclareRobustCommandx"
2171 || t.cs() == "ProvideTextCommandDefault"
2172 || t.cs() == "DeclareMathAccent") {
2174 if (p.next_token().character() == '*') {
2178 string const name = p.verbatim_item();
2179 string const opt1 = p.getFullOpt();
2180 string const opt2 = p.getFullOpt();
2181 string const body = p.verbatim_item();
2182 // store the in_lyx_preamble setting
2183 bool const was_in_lyx_preamble = in_lyx_preamble;
2185 if (name == "\\rmdefault")
2186 if (is_known(body, known_roman_font_packages)) {
2187 h_font_roman[0] = body;
2189 in_lyx_preamble = true;
2191 if (name == "\\sfdefault")
2192 if (is_known(body, known_sans_font_packages)) {
2193 h_font_sans[0] = body;
2195 in_lyx_preamble = true;
2197 if (name == "\\ttdefault")
2198 if (is_known(body, known_typewriter_font_packages)) {
2199 h_font_typewriter[0] = body;
2201 in_lyx_preamble = true;
2203 if (name == "\\familydefault") {
2204 string family = body;
2205 // remove leading "\"
2206 h_font_default_family = family.erase(0,1);
2208 in_lyx_preamble = true;
2211 // remove LyX-specific definitions that are re-added by LyX
2213 // \lyxline is an ancient command that is converted by tex2lyx into
2214 // a \rule therefore remove its preamble code
2215 if (name == "\\lyxdot" || name == "\\lyxarrow"
2216 || name == "\\lyxline" || name == "\\LyX") {
2218 in_lyx_preamble = true;
2221 // Add the command to the known commands
2222 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
2224 // only non-lyxspecific stuff
2225 if (!in_lyx_preamble) {
2227 ss << '\\' << t.cs();
2230 ss << '{' << name << '}' << opt1 << opt2
2231 << '{' << body << "}";
2232 if (prefixIs(t.cs(), "renew") || !contains(h_preamble.str(), ss.str()))
2233 h_preamble << ss.str();
2235 ostream & out = in_preamble ? h_preamble : os;
2236 out << "\\" << t.cs() << "{" << name << "}"
2237 << opts << "{" << body << "}";
2240 // restore the in_lyx_preamble setting
2241 in_lyx_preamble = was_in_lyx_preamble;
2245 if (t.cs() == "documentclass") {
2246 vector<string>::iterator it;
2247 vector<string> opts = split_options(p.getArg('[', ']'));
2248 handle_opt(opts, known_fontsizes, h_paperfontsize);
2249 delete_opt(opts, known_fontsizes);
2250 // delete "pt" at the end
2251 string::size_type i = h_paperfontsize.find("pt");
2252 if (i != string::npos)
2253 h_paperfontsize.erase(i);
2254 // The documentclass options are always parsed before the options
2255 // of the babel call so that a language cannot overwrite the babel
2257 handle_opt(opts, known_languages, h_language);
2258 delete_opt(opts, known_languages);
2261 if ((it = find(opts.begin(), opts.end(), "fleqn"))
2263 h_is_mathindent = "1";
2266 // formula numbering side
2267 if ((it = find(opts.begin(), opts.end(), "leqno"))
2269 h_math_numbering_side = "left";
2272 else if ((it = find(opts.begin(), opts.end(), "reqno"))
2274 h_math_numbering_side = "right";
2278 // paper orientation
2279 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
2280 h_paperorientation = "landscape";
2284 if ((it = find(opts.begin(), opts.end(), "oneside"))
2289 if ((it = find(opts.begin(), opts.end(), "twoside"))
2295 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
2297 h_papercolumns = "1";
2300 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
2302 h_papercolumns = "2";
2306 // some size options are known to any document classes, other sizes
2307 // are handled by the \geometry command of the geometry package
2308 handle_opt(opts, known_class_paper_sizes, h_papersize);
2309 delete_opt(opts, known_class_paper_sizes);
2310 // the remaining options
2311 h_options = join(opts, ",");
2312 // FIXME This does not work for classes that have a
2313 // different name in LyX than in LaTeX
2314 h_textclass = p.getArg('{', '}');
2319 if (t.cs() == "usepackage") {
2320 string const options = p.getArg('[', ']');
2321 string const name = p.getArg('{', '}');
2322 vector<string> vecnames;
2323 split(name, vecnames, ',');
2324 vector<string>::const_iterator it = vecnames.begin();
2325 vector<string>::const_iterator end = vecnames.end();
2326 for (; it != end; ++it)
2327 handle_package(p, trimSpaceAndEol(*it), options,
2328 in_lyx_preamble, detectEncoding);
2332 if (t.cs() == "inputencoding") {
2333 string const encoding = p.getArg('{','}');
2334 Encoding const * const enc = encodings.fromLaTeXName(
2335 encoding, Encoding::inputenc, true);
2337 if (!detectEncoding)
2338 cerr << "Unknown encoding " << encoding
2339 << ". Ignoring." << std::endl;
2342 h_inputencoding = enc->name();
2343 p.setEncoding(enc->iconvName());
2348 if (t.cs() == "newenvironment") {
2349 string const name = p.getArg('{', '}');
2350 string const opt1 = p.getFullOpt();
2351 string const opt2 = p.getFullOpt();
2352 string const beg = p.verbatim_item();
2353 string const end = p.verbatim_item();
2354 if (!in_lyx_preamble) {
2355 h_preamble << "\\newenvironment{" << name
2356 << '}' << opt1 << opt2 << '{'
2357 << beg << "}{" << end << '}';
2359 add_known_environment(name, opt1, !opt2.empty(),
2360 from_utf8(beg), from_utf8(end));
2364 if (t.cs() == "newtheorem") {
2366 if (p.next_token().character() == '*') {
2370 string const name = p.getArg('{', '}');
2371 string const opt1 = p.getFullOpt();
2372 string const opt2 = p.getFullOpt();
2373 string const body = p.verbatim_item();
2374 string const opt3 = p.getFullOpt();
2375 string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
2377 string const complete = cmd + "{" + name + '}' +
2378 opt1 + opt2 + '{' + body + '}' + opt3;
2380 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
2382 if (!in_lyx_preamble)
2383 h_preamble << complete;
2387 if (t.cs() == "def") {
2388 string name = p.get_token().cs();
2389 // In fact, name may be more than the name:
2390 // In the test case of bug 8116
2391 // name == "csname SF@gobble@opt \endcsname".
2392 // Therefore, we need to use asInput() instead of cs().
2393 while (p.next_token().cat() != catBegin)
2394 name += p.get_token().asInput();
2395 if (!in_lyx_preamble)
2396 h_preamble << "\\def\\" << name << '{'
2397 << p.verbatim_item() << "}";
2401 if (t.cs() == "newcolumntype") {
2402 string const name = p.getArg('{', '}');
2403 trimSpaceAndEol(name);
2405 string opts = p.getOpt();
2406 if (!opts.empty()) {
2407 istringstream is(string(opts, 1));
2410 special_columns_[name[0]] = nargs;
2411 h_preamble << "\\newcolumntype{" << name << "}";
2413 h_preamble << "[" << nargs << "]";
2414 h_preamble << "{" << p.verbatim_item() << "}";
2418 if (t.cs() == "setcounter") {
2419 string const name = p.getArg('{', '}');
2420 string const content = p.getArg('{', '}');
2421 if (name == "secnumdepth")
2422 h_secnumdepth = content;
2423 else if (name == "tocdepth")
2424 h_tocdepth = content;
2426 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
2430 if (t.cs() == "setlength") {
2431 string const name = p.verbatim_item();
2432 string const content = p.verbatim_item();
2433 // the paragraphs are only not indented when \parindent is set to zero
2434 if (name == "\\parindent" && content != "") {
2435 if (content[0] == '0')
2436 h_paragraph_separation = "skip";
2438 h_paragraph_indentation = translate_len(content);
2439 } else if (name == "\\parskip") {
2440 if (content == "\\smallskipamount")
2441 h_defskip = "smallskip";
2442 else if (content == "\\medskipamount")
2443 h_defskip = "medskip";
2444 else if (content == "\\bigskipamount")
2445 h_defskip = "bigskip";
2447 h_defskip = translate_len(content);
2448 } else if (name == "\\mathindent") {
2449 h_mathindentation = translate_len(content);
2451 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2455 if (t.cs() == "onehalfspacing") {
2456 h_spacing = "onehalf";
2460 if (t.cs() == "doublespacing") {
2461 h_spacing = "double";
2465 if (t.cs() == "setstretch") {
2466 h_spacing = "other " + p.verbatim_item();
2470 if (t.cs() == "synctex") {
2471 // the scheme is \synctex=value
2472 // where value can only be "1" or "-1"
2473 h_output_sync = "1";
2474 // there can be any character behind the value (e.g. a linebreak or a '\'
2475 // therefore we extract it char by char
2477 string value = p.get_token().asInput();
2479 value += p.get_token().asInput();
2480 h_output_sync_macro = "\\synctex=" + value;
2484 if (t.cs() == "begin") {
2485 string const name = p.getArg('{', '}');
2486 if (name == "document")
2488 h_preamble << "\\begin{" << name << "}";
2492 if (t.cs() == "geometry") {
2493 vector<string> opts = split_options(p.getArg('{', '}'));
2494 handle_geometry(opts);
2498 if (t.cs() == "definecolor") {
2499 string const color = p.getArg('{', '}');
2500 string const space = p.getArg('{', '}');
2501 string const value = p.getArg('{', '}');
2502 if (color == "document_fontcolor" && space == "rgb") {
2503 RGBColor c(RGBColorFromLaTeX(value));
2504 h_fontcolor = X11hexname(c);
2505 } else if (color == "note_fontcolor" && space == "rgb") {
2506 RGBColor c(RGBColorFromLaTeX(value));
2507 h_notefontcolor = X11hexname(c);
2508 } else if (color == "page_backgroundcolor" && space == "rgb") {
2509 RGBColor c(RGBColorFromLaTeX(value));
2510 h_backgroundcolor = X11hexname(c);
2511 } else if (color == "shadecolor" && space == "rgb") {
2512 RGBColor c(RGBColorFromLaTeX(value));
2513 h_boxbgcolor = X11hexname(c);
2515 h_preamble << "\\definecolor{" << color
2516 << "}{" << space << "}{" << value
2522 if (t.cs() == "bibliographystyle") {
2523 h_biblio_style = p.verbatim_item();
2527 if (t.cs() == "jurabibsetup") {
2528 // FIXME p.getArg('{', '}') is most probably wrong (it
2529 // does not handle nested braces).
2530 // Use p.verbatim_item() instead.
2531 vector<string> jurabibsetup =
2532 split_options(p.getArg('{', '}'));
2533 // add jurabibsetup to the jurabib package options
2534 add_package("jurabib", jurabibsetup);
2535 if (!jurabibsetup.empty()) {
2536 h_preamble << "\\jurabibsetup{"
2537 << join(jurabibsetup, ",") << '}';
2542 if (t.cs() == "hypersetup") {
2543 vector<string> hypersetup =
2544 split_options(p.verbatim_item());
2545 // add hypersetup to the hyperref package options
2546 handle_hyperref(hypersetup);
2547 if (!hypersetup.empty()) {
2548 h_preamble << "\\hypersetup{"
2549 << join(hypersetup, ",") << '}';
2554 if (t.cs() == "includeonly") {
2555 vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
2556 for (auto & iofile : includeonlys) {
2557 string filename(normalize_filename(iofile));
2558 string const path = getMasterFilePath(true);
2559 // We want to preserve relative/absolute filenames,
2560 // therefore path is only used for testing
2561 if (!makeAbsPath(filename, path).exists()) {
2562 // The file extension is probably missing.
2563 // Now try to find it out.
2564 string const tex_name =
2565 find_file(filename, path,
2566 known_tex_extensions);
2567 if (!tex_name.empty())
2568 filename = tex_name;
2571 if (makeAbsPath(filename, path).exists())
2572 fix_child_filename(filename);
2574 cerr << "Warning: Could not find included file '"
2575 << filename << "'." << endl;
2576 outname = changeExtension(filename, "lyx");
2577 h_includeonlys.push_back(outname);
2582 if (is_known(t.cs(), known_if_3arg_commands)) {
2583 // prevent misparsing of \usepackage if it is used
2584 // as an argument (see e.g. our own output of
2585 // \@ifundefined above)
2586 string const arg1 = p.verbatim_item();
2587 string const arg2 = p.verbatim_item();
2588 string const arg3 = p.verbatim_item();
2589 // test case \@ifundefined{date}{}{\date{}}
2590 if (t.cs() == "@ifundefined" && arg1 == "date" &&
2591 arg2.empty() && arg3 == "\\date{}") {
2592 h_suppress_date = "true";
2593 // older tex2lyx versions did output
2594 // \@ifundefined{definecolor}{\usepackage{color}}{}
2595 } else if (t.cs() == "@ifundefined" &&
2596 arg1 == "definecolor" &&
2597 arg2 == "\\usepackage{color}" &&
2599 if (!in_lyx_preamble)
2600 h_preamble << package_beg_sep
2603 << "\\@ifundefined{definecolor}{color}{}"
2606 //\@ifundefined{showcaptionsetup}{}{%
2607 // \PassOptionsToPackage{caption=false}{subfig}}
2608 // that LyX uses for subfloats
2609 } else if (t.cs() == "@ifundefined" &&
2610 arg1 == "showcaptionsetup" && arg2.empty()
2611 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
2613 } else if (!in_lyx_preamble) {
2614 h_preamble << t.asInput()
2615 << '{' << arg1 << '}'
2616 << '{' << arg2 << '}'
2617 << '{' << arg3 << '}';
2622 if (is_known(t.cs(), known_if_commands)) {
2623 // must not parse anything in conditional code, since
2624 // LyX would output the parsed contents unconditionally
2625 if (!in_lyx_preamble)
2626 h_preamble << t.asInput();
2627 handle_if(p, in_lyx_preamble);
2631 if (!t.cs().empty() && !in_lyx_preamble) {
2632 h_preamble << '\\' << t.cs();
2637 // remove the whitespace
2640 // Force textclass if the user wanted it
2641 if (!forceclass.empty())
2642 h_textclass = forceclass;
2643 tc.setName(h_textclass);
2644 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2645 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2648 if (h_papersides.empty()) {
2651 h_papersides = ss.str();
2654 // If the CJK package is used we cannot set the document language from
2655 // the babel options. Instead, we guess which language is used most
2656 // and set this one.
2657 default_language = h_language;
2658 if (is_full_document &&
2659 (auto_packages.find("CJK") != auto_packages.end() ||
2660 auto_packages.find("CJKutf8") != auto_packages.end())) {
2662 h_language = guessLanguage(p, default_language);
2664 if (explicit_babel && h_language != default_language) {
2665 // We set the document language to a CJK language,
2666 // but babel is explicitly called in the user preamble
2667 // without options. LyX will not add the default
2668 // language to the document options if it is either
2669 // english, or no text is set as default language.
2670 // Therefore we need to add a language option explicitly.
2671 // FIXME: It would be better to remove all babel calls
2672 // from the user preamble, but this is difficult
2673 // without re-introducing bug 7861.
2674 if (h_options.empty())
2675 h_options = lyx2babel(default_language);
2677 h_options += ',' + lyx2babel(default_language);
2681 // Finally, set the quote style.
2682 // LyX knows the following quotes styles:
2683 // british, cjk, cjkangle, danish, english, french, german,
2684 // polish, russian, swedish and swiss
2685 // conversion list taken from
2686 // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
2687 // (quotes for kazakh are unknown)
2689 if (is_known(h_language, known_british_quotes_languages))
2690 h_quotes_style = "british";
2692 else if (is_known(h_language, known_cjk_quotes_languages))
2693 h_quotes_style = "cjk";
2695 else if (is_known(h_language, known_cjkangle_quotes_languages))
2696 h_quotes_style = "cjkangle";
2698 else if (is_known(h_language, known_danish_quotes_languages))
2699 h_quotes_style = "danish";
2701 else if (is_known(h_language, known_french_quotes_languages))
2702 h_quotes_style = "french";
2704 else if (is_known(h_language, known_german_quotes_languages))
2705 h_quotes_style = "german";
2707 else if (is_known(h_language, known_polish_quotes_languages))
2708 h_quotes_style = "polish";
2710 else if (is_known(h_language, known_russian_quotes_languages))
2711 h_quotes_style = "russian";
2713 else if (is_known(h_language, known_swedish_quotes_languages))
2714 h_quotes_style = "swedish";
2716 else if (is_known(h_language, known_swiss_quotes_languages))
2717 h_quotes_style = "swiss";
2719 else if (is_known(h_language, known_english_quotes_languages))
2720 h_quotes_style = "english";
2724 string Preamble::parseEncoding(Parser & p, string const & forceclass)
2726 TeX2LyXDocClass dummy;
2727 parse(p, forceclass, true, dummy);
2728 if (h_inputencoding != "auto-legacy" && h_inputencoding != "auto-legacy-plain")
2729 return h_inputencoding;
2734 string babel2lyx(string const & language)
2736 char const * const * where = is_known(language, known_languages);
2738 return known_coded_languages[where - known_languages];
2743 string lyx2babel(string const & language)
2745 char const * const * where = is_known(language, known_coded_languages);
2747 return known_languages[where - known_coded_languages];
2752 string Preamble::polyglossia2lyx(string const & language)
2754 char const * const * where = is_known(language, polyglossia_languages);
2756 return coded_polyglossia_languages[where - polyglossia_languages];
2761 string rgbcolor2code(string const & name)
2763 char const * const * where = is_known(name, known_basic_colors);
2765 // "red", "green" etc
2766 return known_basic_color_codes[where - known_basic_colors];
2768 // "255,0,0", "0,255,0" etc
2769 RGBColor c(RGBColorFromLaTeX(name));
2770 return X11hexname(c);