3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
9 * Full author contact details are available in file CREDITS.
20 #include "LayoutFile.h"
23 #include "TextClass.h"
26 #include "support/convert.h"
27 #include "support/FileName.h"
28 #include "support/filetools.h"
29 #include "support/lstrings.h"
31 #include "support/regex.h"
37 using namespace lyx::support;
46 // CJK languages are handled in text.cpp, polyglossia languages are listed
49 * known babel language names (including synonyms)
50 * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
51 * please keep this in sync with known_coded_languages line by line!
53 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
54 "american", "arabic", "arabtex", "australian", "austrian", "azerbaijani", "bahasa", "bahasai",
55 "bahasam", "basque", "belarusian", "bosnian", "brazil", "brazilian", "breton", "british",
56 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
57 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "francais",
58 "french", "frenchb", "frenchle", "frenchpro", "friulan", "galician", "german", "germanb",
59 "georgian", "greek", "hebrew", "hungarian", "icelandic", "indon", "indonesian",
60 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
61 "latvian", "lithuanian", "lowersorbian", "lsorbian", "macedonian", "magyar", "malay", "meyalu",
62 "mongolian", "naustrian", "newzealand", "ngerman", "ngermanb", "norsk", "nswissgerman",
63 "nynorsk", "piedmontese", "polutonikogreek", "polish", "portuges", "portuguese",
64 "romanian", "romansh", "russian", "russianb", "samin", "scottish", "serbian", "serbian-latin",
65 "slovak", "slovene", "spanish", "swedish", "swissgerman", "thai", "turkish", "turkmen",
66 "ukraineb", "ukrainian", "uppersorbian", "UKenglish", "USenglish", "usorbian",
71 * the same as known_languages with .lyx names
72 * please keep this in sync with known_languages line by line!
74 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
75 "american", "arabic_arabi", "arabic_arabtex", "australian", "austrian", "azerbaijani", "bahasa", "bahasa",
76 "bahasam", "basque", "belarusian", "bosnian", "brazilian", "brazilian", "breton", "british",
77 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
78 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "french",
79 "french", "french", "french", "french", "friulan", "galician", "german", "german",
80 "georgian", "greek", "hebrew", "magyar", "icelandic", "bahasa", "bahasa",
81 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
82 "latvian", "lithuanian", "lowersorbian", "lowersorbian", "macedonian", "magyar", "bahasam", "bahasam",
83 "mongolian", "naustrian", "newzealand", "ngerman", "ngerman", "norsk", "german-ch",
84 "nynorsk", "piedmontese", "polutonikogreek", "polish", "portuguese", "portuguese",
85 "romanian", "romansh", "russian", "russian", "samin", "scottish", "serbian", "serbian-latin",
86 "slovak", "slovene", "spanish", "swedish", "german-ch-old", "thai", "turkish", "turkmen",
87 "ukrainian", "ukrainian", "uppersorbian", "english", "english", "uppersorbian",
88 "vietnamese", "welsh",
91 /// languages with british quotes (.lyx names)
92 const char * const known_british_quotes_languages[] = {"british", "welsh", 0};
94 /// languages with cjk quotes (.lyx names)
95 const char * const known_cjk_quotes_languages[] = {"chinese-traditional",
96 "japanese", "japanese-cjk", 0};
98 /// languages with cjk-angle quotes (.lyx names)
99 const char * const known_cjkangle_quotes_languages[] = {"korean", 0};
101 /// languages with danish quotes (.lyx names)
102 const char * const known_danish_quotes_languages[] = {"danish", 0};
104 /// languages with english quotes (.lyx names)
105 const char * const known_english_quotes_languages[] = {"american", "australian",
106 "bahasa", "bahasam", "bengali", "brazilian", "canadian", "chinese-simplified", "english",
107 "esperanto", "farsi", "interlingua", "irish", "newzealand", "scottish",
108 "thai", "turkish", "vietnamese", 0};
110 /// languages with french quotes (.lyx names)
111 const char * const known_french_quotes_languages[] = {"ancientgreek",
112 "arabic_arabi", "arabic_arabtex", "asturian", "belarusian", "breton",
113 "canadien", "catalan", "french", "friulan", "galician", "italian", "occitan",
114 "piedmontese", "portuguese", "spanish", "spanish-mexico", 0};
116 /// languages with german quotes (.lyx names)
117 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
118 "czech", "estonian", "georgian", "german", "icelandic", "latvian", "lithuanian",
119 "lowersorbian", "macedonian", "naustrian", "ngerman", "romansh", "slovak", "slovene",
122 /// languages with polish quotes (.lyx names)
123 const char * const known_polish_quotes_languages[] = {"afrikaans", "bosnian", "croatian",
124 "dutch", "magyar", "polish", "romanian", "serbian", "serbian-latin", 0};
126 /// languages with russian quotes (.lyx names)
127 const char * const known_russian_quotes_languages[] = {"azerbaijani", "oldrussian",
128 "russian", "ukrainian", 0};
130 /// languages with swedish quotes (.lyx names)
131 const char * const known_swedish_quotes_languages[] = {"finnish", "swedish", 0};
133 /// languages with swiss quotes (.lyx names)
134 const char * const known_swiss_quotes_languages[] = {"albanian",
135 "armenian", "basque", "churchslavonic", "german-ch", "german-ch-old",
136 "norsk", "nynorsk", "turkmen", "ukrainian", "vietnamese", 0};
138 /// known language packages from the times before babel
139 const char * const known_old_language_packages[] = {"french", "frenchle",
140 "frenchpro", "german", "ngerman", "pmfrench", 0};
142 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
144 const char * const known_roman_font_packages[] = { "ae", "beraserif", "bookman",
145 "ccfonts", "chancery", "charter", "cmr", "cochineal", "crimson", "DejaVuSerif", "DejaVuSerifCondensed", "fourier",
146 "garamondx", "libertine", "libertineRoman", "libertine-type1", "lmodern", "mathdesign", "mathpazo",
147 "mathptmx", "MinionPro", "newcent", "noto", "noto-serif", "PTSerif", "tgbonum", "tgchorus",
148 "tgpagella", "tgschola", "tgtermes", "utopia", "xcharter", 0 };
150 const char * const known_sans_font_packages[] = { "avant", "berasans", "biolinum",
151 "biolinum-type1", "cmbr", "cmss", "DejaVuSans", "DejaVuSansCondensed", "helvet", "iwona", "iwonac", "iwonal", "iwonalc",
152 "kurier", "kurierc", "kurierl", "kurierlc", "lmss", "noto", "noto-sans", "PTSans",
153 "tgadventor", "tgheros", "uop", 0 };
155 const char * const known_typewriter_font_packages[] = { "beramono", "cmtl", "cmtt",
156 "courier", "DejaVuSansMono", "lmtt", "luximono", "fourier", "libertineMono", "libertineMono-type1", "lmodern",
157 "mathpazo", "mathptmx", "newcent", "noto", "noto-mono", "PTMono", "tgcursor", "txtt", 0 };
159 const char * const known_math_font_packages[] = { "eulervm", "newtxmath", 0};
161 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
162 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
163 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
164 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
165 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
167 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
168 "executivepaper", "legalpaper", "letterpaper", 0};
170 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
171 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
173 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
174 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
177 /// commands that can start an \if...\else...\endif sequence
178 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
179 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
180 "ifsidecap", "ifupgreek", 0};
182 const char * const known_basic_colors[] = {"black", "blue", "brown", "cyan",
183 "darkgray", "gray", "green", "lightgray", "lime", "magenta", "orange", "olive",
184 "pink", "purple", "red", "teal", "violet", "white", "yellow", 0};
186 const char * const known_basic_color_codes[] = {"#000000", "#0000ff", "#964B00", "#00ffff",
187 "#a9a9a9", "#808080", "#00ff00", "#d3d3d3", "#bfff00", "#ff00ff", "#ff7f00", "#808000",
188 "#ffc0cb", "#800080", "#ff0000", "#008080", "#8f00ff", "#ffffff", "#ffff00", 0};
190 /// conditional commands with three arguments like \@ifundefined{}{}{}
191 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
195 * Known file extensions for TeX files as used by \\includeonly
197 char const * const known_tex_extensions[] = {"tex", 0};
199 /// packages that work only in xetex
200 /// polyglossia is handled separately
201 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
202 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
203 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
205 /// packages that are automatically skipped if loaded by LyX
206 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
207 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color",
208 "float", "fontspec", "framed", "graphicx", "hhline", "ifthen", "longtable",
209 "makeidx", "minted", "multirow", "nomencl", "pdfpages", "prettyref", "refstyle",
210 "rotating", "rotfloat", "splitidx", "setspace", "subscript", "tabularx","textcomp", "tipa",
211 "tipx", "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xcolor", "xltabular",
214 // codes used to remove packages that are loaded automatically by LyX.
215 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
216 const char package_beg_sep = '\001';
217 const char package_mid_sep = '\002';
218 const char package_end_sep = '\003';
221 // returns true if at least one of the options in what has been found
222 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
228 // the last language option is the document language (for babel and LyX)
229 // the last size option is the document font size
230 vector<string>::iterator it;
231 vector<string>::iterator position = opts.begin();
232 for (; *what; ++what) {
233 it = find(opts.begin(), opts.end(), *what);
234 if (it != opts.end()) {
235 if (it >= position) {
246 void delete_opt(vector<string> & opts, char const * const * what)
251 // remove found options from the list
252 // do this after handle_opt to avoid potential memory leaks
253 vector<string>::iterator it;
254 for (; *what; ++what) {
255 it = find(opts.begin(), opts.end(), *what);
256 if (it != opts.end())
263 * Split a package options string (keyval format) into a vector.
265 * authorformat=smallcaps,
267 * titleformat=colonsep,
268 * bibformat={tabular,ibidem,numbered}
270 vector<string> split_options(string const & input)
272 vector<string> options;
276 Token const & t = p.get_token();
277 if (t.asInput() == ",") {
278 options.push_back(trimSpaceAndEol(option));
280 } else if (t.asInput() == "=") {
283 if (p.next_token().asInput() == "{")
284 option += '{' + p.getArg('{', '}') + '}';
285 } else if (t.cat() != catSpace && t.cat() != catComment)
286 option += t.asInput();
290 options.push_back(trimSpaceAndEol(option));
297 * Retrieve a keyval option "name={value with=sign}" named \p name from
298 * \p options and return the value.
299 * The found option is also removed from \p options.
301 string process_keyval_opt(vector<string> & options, string name)
303 for (size_t i = 0; i < options.size(); ++i) {
304 vector<string> option;
305 split(options[i], option, '=');
306 if (option.size() < 2)
308 if (option[0] == name) {
309 options.erase(options.begin() + i);
310 option.erase(option.begin());
311 return join(option, "=");
317 } // anonymous namespace
321 * known polyglossia language names (including variants)
322 * FIXME: support spelling=old for german variants (german vs. ngerman LyX names etc)
324 const char * const Preamble::polyglossia_languages[] = {
325 "albanian", "american", "amharic", "ancient", "arabic", "armenian", "asturian", "australian",
326 "bahasai", "bahasam", "basque", "bengali", "brazil", "brazilian", "breton", "british", "bulgarian",
327 "catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
328 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
329 "galician", "greek", "monotonic", "hebrew", "hindi",
330 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean",
331 "lao", "latin", "latvian", "lithuanian", "lsorbian", "magyar", "malayalam", "marathi",
332 "austrian", "newzealand", "german", "norsk", "nynorsk", "occitan", "oldrussian",
333 "piedmontese", "polish", "polytonic", "portuges", "romanian", "romansh", "russian",
334 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovenian", "spanish", "swedish", "syriac",
335 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
336 "ukrainian", "urdu", "usorbian", "vietnamese", "welsh", 0};
337 // not yet supported by LyX: "korean", "nko"
340 * the same as polyglossia_languages with .lyx names
341 * please keep this in sync with polyglossia_languages line by line!
343 const char * const Preamble::coded_polyglossia_languages[] = {
344 "albanian", "american", "amharic", "ancientgreek", "arabic_arabi", "armenian", "asturian", "australian",
345 "bahasa", "bahasam", "basque", "bengali", "brazilian", "brazilian", "breton", "british", "bulgarian",
346 "catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
347 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
348 "galician", "greek", "greek", "hebrew", "hindi",
349 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean",
350 "lao", "latin", "latvian", "lithuanian", "lowersorbian", "magyar", "malayalam", "marathi",
351 "naustrian","newzealand", "ngerman", "norsk", "nynorsk", "occitan", "oldrussian",
352 "piedmontese", "polish", "polutonikogreek", "portuges", "romanian", "romansh", "russian",
353 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovene", "spanish", "swedish", "syriac",
354 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
355 "ukrainian", "urdu", "uppersorbian", "vietnamese", "welsh", 0};
356 // not yet supported by LyX: "korean-polyglossia", "nko"
359 bool Preamble::usePolyglossia() const
361 return h_use_non_tex_fonts && h_language_package == "default";
365 bool Preamble::indentParagraphs() const
367 return h_paragraph_separation == "indent";
371 bool Preamble::isPackageUsed(string const & package) const
373 return used_packages.find(package) != used_packages.end();
377 bool Preamble::isPackageAutoLoaded(string const & package) const
379 return auto_packages.find(package) != auto_packages.end();
383 vector<string> Preamble::getPackageOptions(string const & package) const
385 map<string, vector<string> >::const_iterator it = used_packages.find(package);
386 if (it != used_packages.end())
388 return vector<string>();
392 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
394 auto_packages.insert(package);
398 void Preamble::addModule(string const & module)
400 for (auto const & m : used_modules) {
404 used_modules.push_back(module);
408 void Preamble::suppressDate(bool suppress)
411 h_suppress_date = "true";
413 h_suppress_date = "false";
417 void Preamble::registerAuthor(std::string const & name)
419 Author author(from_utf8(name), empty_docstring());
420 author.setUsed(true);
421 authors_.record(author);
422 h_tracking_changes = "true";
423 h_output_changes = "true";
427 Author const & Preamble::getAuthor(std::string const & name) const
429 Author author(from_utf8(name), empty_docstring());
430 for (AuthorList::Authors::const_iterator it = authors_.begin();
431 it != authors_.end(); ++it)
434 static Author const dummy;
439 int Preamble::getSpecialTableColumnArguments(char c) const
441 map<char, int>::const_iterator it = special_columns_.find(c);
442 if (it == special_columns_.end())
448 void Preamble::add_package(string const & name, vector<string> & options)
450 // every package inherits the global options
451 if (used_packages.find(name) == used_packages.end())
452 used_packages[name] = split_options(h_options);
454 // Insert options passed via PassOptionsToPackage
455 for (auto const & p : extra_package_options_) {
456 if (p.first == name) {
457 vector<string> eo = getVectorFromString(p.second);
458 for (auto const & eoi : eo)
459 options.push_back(eoi);
463 vector<string> & v = used_packages[name];
464 v.insert(v.end(), options.begin(), options.end());
465 if (name == "jurabib") {
466 // Don't output the order argument (see the cite command
467 // handling code in text.cpp).
468 vector<string>::iterator end =
469 remove(options.begin(), options.end(), "natbiborder");
470 end = remove(options.begin(), end, "jurabiborder");
471 options.erase(end, options.end());
478 // Given is a string like "scaled=0.9" or "Scale=0.9", return 0.9 * 100
479 bool scale_as_percentage(string const & scale, string & percentage)
481 string::size_type pos = scale.find('=');
482 if (pos != string::npos) {
483 string value = scale.substr(pos + 1);
484 if (isStrDbl(value)) {
485 percentage = convert<string>(
486 static_cast<int>(100 * convert<double>(value)));
494 string remove_braces(string const & value)
498 if (value[0] == '{' && value[value.length()-1] == '}')
499 return value.substr(1, value.length()-2);
503 } // anonymous namespace
506 Preamble::Preamble() : one_language(true), explicit_babel(false),
507 title_layout_found(false), index_number(0), h_font_cjk_set(false),
508 h_use_microtype("false")
512 h_biblio_style = "plain";
513 h_bibtex_command = "default";
514 h_cite_engine = "basic";
515 h_cite_engine_type = "default";
517 h_defskip = "medskip";
518 h_dynamic_quotes = false;
521 h_fontencoding = "default";
522 h_font_roman[0] = "default";
523 h_font_roman[1] = "default";
524 h_font_sans[0] = "default";
525 h_font_sans[1] = "default";
526 h_font_typewriter[0] = "default";
527 h_font_typewriter[1] = "default";
528 h_font_math[0] = "auto";
529 h_font_math[1] = "auto";
530 h_font_default_family = "default";
531 h_use_non_tex_fonts = false;
533 h_font_osf = "false";
534 h_font_sf_scale[0] = "100";
535 h_font_sf_scale[1] = "100";
536 h_font_tt_scale[0] = "100";
537 h_font_tt_scale[1] = "100";
538 // h_font_roman_opts;
540 // h_font_typewriter_opts;
542 h_is_mathindent = "0";
543 h_math_numbering_side = "default";
544 h_graphics = "default";
545 h_default_output_format = "default";
546 h_html_be_strict = "false";
547 h_html_css_as_file = "0";
548 h_html_math_output = "0";
549 h_index[0] = "Index";
550 h_index_command = "default";
551 h_inputencoding = "auto-legacy";
552 h_justification = "true";
553 h_language = "english";
554 h_language_package = "none";
556 h_maintain_unincluded_children = "false";
560 h_output_changes = "false";
562 //h_output_sync_macro
563 h_papercolumns = "1";
564 h_paperfontsize = "default";
565 h_paperorientation = "portrait";
566 h_paperpagestyle = "default";
568 h_papersize = "default";
569 h_paragraph_indentation = "default";
570 h_paragraph_separation = "indent";
575 h_pdf_bookmarks = "0";
576 h_pdf_bookmarksnumbered = "0";
577 h_pdf_bookmarksopen = "0";
578 h_pdf_bookmarksopenlevel = "1";
579 h_pdf_breaklinks = "0";
580 h_pdf_pdfborder = "0";
581 h_pdf_colorlinks = "0";
582 h_pdf_backref = "section";
583 h_pdf_pdfusetitle = "0";
585 //h_pdf_quoted_options;
586 h_quotes_style = "english";
588 h_shortcut[0] = "idx";
589 h_spacing = "single";
590 h_save_transient_properties = "true";
591 h_suppress_date = "false";
592 h_textclass = "article";
594 h_tracking_changes = "false";
595 h_use_bibtopic = "false";
596 h_use_dash_ligatures = "true";
597 h_use_indices = "false";
598 h_use_geometry = "false";
599 h_use_default_options = "false";
600 h_use_hyperref = "false";
601 h_use_microtype = "false";
602 h_use_refstyle = false;
603 h_use_minted = false;
604 h_use_packages["amsmath"] = "1";
605 h_use_packages["amssymb"] = "0";
606 h_use_packages["cancel"] = "0";
607 h_use_packages["esint"] = "1";
608 h_use_packages["mhchem"] = "0";
609 h_use_packages["mathdots"] = "0";
610 h_use_packages["mathtools"] = "0";
611 h_use_packages["stackrel"] = "0";
612 h_use_packages["stmaryrd"] = "0";
613 h_use_packages["undertilde"] = "0";
617 void Preamble::handle_hyperref(vector<string> & options)
619 // FIXME swallow inputencoding changes that might surround the
620 // hyperref setup if it was written by LyX
621 h_use_hyperref = "true";
622 // swallow "unicode=true", since LyX does always write that
623 vector<string>::iterator it =
624 find(options.begin(), options.end(), "unicode=true");
625 if (it != options.end())
627 it = find(options.begin(), options.end(), "pdfusetitle");
628 if (it != options.end()) {
629 h_pdf_pdfusetitle = "1";
632 string bookmarks = process_keyval_opt(options, "bookmarks");
633 if (bookmarks == "true")
634 h_pdf_bookmarks = "1";
635 else if (bookmarks == "false")
636 h_pdf_bookmarks = "0";
637 if (h_pdf_bookmarks == "1") {
638 string bookmarksnumbered =
639 process_keyval_opt(options, "bookmarksnumbered");
640 if (bookmarksnumbered == "true")
641 h_pdf_bookmarksnumbered = "1";
642 else if (bookmarksnumbered == "false")
643 h_pdf_bookmarksnumbered = "0";
644 string bookmarksopen =
645 process_keyval_opt(options, "bookmarksopen");
646 if (bookmarksopen == "true")
647 h_pdf_bookmarksopen = "1";
648 else if (bookmarksopen == "false")
649 h_pdf_bookmarksopen = "0";
650 if (h_pdf_bookmarksopen == "1") {
651 string bookmarksopenlevel =
652 process_keyval_opt(options, "bookmarksopenlevel");
653 if (!bookmarksopenlevel.empty())
654 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
657 string breaklinks = process_keyval_opt(options, "breaklinks");
658 if (breaklinks == "true")
659 h_pdf_breaklinks = "1";
660 else if (breaklinks == "false")
661 h_pdf_breaklinks = "0";
662 string pdfborder = process_keyval_opt(options, "pdfborder");
663 if (pdfborder == "{0 0 0}")
664 h_pdf_pdfborder = "1";
665 else if (pdfborder == "{0 0 1}")
666 h_pdf_pdfborder = "0";
667 string backref = process_keyval_opt(options, "backref");
668 if (!backref.empty())
669 h_pdf_backref = backref;
670 string colorlinks = process_keyval_opt(options, "colorlinks");
671 if (colorlinks == "true")
672 h_pdf_colorlinks = "1";
673 else if (colorlinks == "false")
674 h_pdf_colorlinks = "0";
675 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
676 if (!pdfpagemode.empty())
677 h_pdf_pagemode = pdfpagemode;
678 string pdftitle = process_keyval_opt(options, "pdftitle");
679 if (!pdftitle.empty()) {
680 h_pdf_title = remove_braces(pdftitle);
682 string pdfauthor = process_keyval_opt(options, "pdfauthor");
683 if (!pdfauthor.empty()) {
684 h_pdf_author = remove_braces(pdfauthor);
686 string pdfsubject = process_keyval_opt(options, "pdfsubject");
687 if (!pdfsubject.empty())
688 h_pdf_subject = remove_braces(pdfsubject);
689 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
690 if (!pdfkeywords.empty())
691 h_pdf_keywords = remove_braces(pdfkeywords);
692 if (!options.empty()) {
693 if (!h_pdf_quoted_options.empty())
694 h_pdf_quoted_options += ',';
695 h_pdf_quoted_options += join(options, ",");
701 void Preamble::handle_geometry(vector<string> & options)
703 h_use_geometry = "true";
704 vector<string>::iterator it;
706 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
707 h_paperorientation = "landscape";
711 // keyval version: "paper=letter"
712 string paper = process_keyval_opt(options, "paper");
714 h_papersize = paper + "paper";
715 // alternative version: "letterpaper"
716 handle_opt(options, known_paper_sizes, h_papersize);
717 delete_opt(options, known_paper_sizes);
719 char const * const * margin = known_paper_margins;
720 for (; *margin; ++margin) {
721 string value = process_keyval_opt(options, *margin);
722 if (!value.empty()) {
723 int k = margin - known_paper_margins;
724 string name = known_coded_paper_margins[k];
725 h_margins += '\\' + name + ' ' + value + '\n';
731 void Preamble::handle_package(Parser &p, string const & name,
732 string const & opts, bool in_lyx_preamble,
735 vector<string> options = split_options(opts);
736 add_package(name, options);
738 if (is_known(name, known_xetex_packages)) {
740 h_use_non_tex_fonts = true;
741 registerAutomaticallyLoadedPackage("fontspec");
742 if (h_inputencoding == "auto-legacy")
743 p.setEncoding("UTF-8");
747 if (is_known(name, known_roman_font_packages))
748 h_font_roman[0] = name;
750 if (name == "fourier") {
751 h_font_roman[0] = "utopia";
752 // when font uses real small capitals
753 if (opts == "expert")
757 if (name == "garamondx") {
758 h_font_roman[0] = "garamondx";
763 if (name == "libertine") {
764 h_font_roman[0] = "libertine";
765 // this automatically invokes biolinum
766 h_font_sans[0] = "biolinum";
767 // as well as libertineMono
768 h_font_typewriter[0] = "libertine-mono";
771 else if (opts == "lining")
772 h_font_osf = "false";
775 if (name == "libertineRoman" || name == "libertine-type1") {
776 h_font_roman[0] = "libertine";
777 // NOTE: contrary to libertine.sty, libertineRoman
778 // and libertine-type1 do not automatically invoke
779 // biolinum and libertineMono
780 if (opts == "lining")
781 h_font_osf = "false";
782 else if (opts == "osf")
786 if (name == "MinionPro") {
787 h_font_roman[0] = "minionpro";
788 vector<string> allopts = getVectorFromString(opts);
791 h_font_math[0] = "auto";
792 for (auto const & opt : allopts) {
794 h_font_osf = "false";
797 if (opt == "onlytext") {
798 h_font_math[0] = "default";
806 h_font_roman_opts = xopts;
810 if (name == "mathdesign") {
811 if (opts.find("charter") != string::npos)
812 h_font_roman[0] = "md-charter";
813 if (opts.find("garamond") != string::npos)
814 h_font_roman[0] = "md-garamond";
815 if (opts.find("utopia") != string::npos)
816 h_font_roman[0] = "md-utopia";
817 if (opts.find("expert") != string::npos) {
823 else if (name == "mathpazo")
824 h_font_roman[0] = "palatino";
826 else if (name == "mathptmx")
827 h_font_roman[0] = "times";
829 if (name == "crimson")
830 h_font_roman[0] = "cochineal";
832 if (name == "cochineal") {
833 h_font_roman[0] = "cochineal";
834 // cochineal can have several options, e.g. [proportional,osf]
835 string::size_type pos = opts.find("osf");
836 if (pos != string::npos)
840 if (name == "noto") {
841 // noto can have several options
843 h_font_roman[0] = "NotoSerif-TLF";
844 string::size_type pos = opts.find("rm");
845 if (pos != string::npos)
846 h_font_roman[0] = "NotoSerif-TLF";
847 pos = opts.find("sf");
848 if (pos != string::npos)
849 h_font_sans[0] = "NotoSans-TLF";
850 pos = opts.find("nott");
851 if (pos != string::npos) {
852 h_font_roman[0] = "NotoSerif-TLF";
853 h_font_sans[0] = "NotoSans-TLF";
855 // noto as typewriter is handled in handling of \ttdefault
856 // special cases are handled in handling of \rmdefault and \sfdefault
859 if (name == "paratype") {
860 // in this case all fonts are ParaType
861 h_font_roman[0] = "PTSerif-TLF";
862 h_font_sans[0] = "default";
863 h_font_typewriter[0] = "default";
866 if (name == "PTSerif")
867 h_font_roman[0] = "PTSerif-TLF";
869 if (name == "XCharter") {
870 h_font_roman[0] = "xcharter";
875 if (name == "plex-serif") {
877 h_font_roman[0] = "IBMPlexSerif";
878 else if (opts.find("thin") != string::npos)
879 h_font_roman[0] = "IBMPlexSerifThin";
880 else if (opts.find("extralight") != string::npos)
881 h_font_roman[0] = "IBMPlexSerifExtraLight";
882 else if (opts.find("light") != string::npos)
883 h_font_roman[0] = "IBMPlexSerifLight";
884 else if (opts.find("semibold") != string::npos)
885 h_font_roman[0] = "IBMPlexSerifSemibold";
887 if (name == "noto-serif") {
888 h_font_roman[0] = "NotoSerifRegular";
890 if (opts.find("thin") != string::npos)
891 h_font_roman[0] = "NotoSerifThin";
892 else if (opts.find("medium") != string::npos)
893 h_font_roman[0] = "NotoSerifMedium";
894 else if (opts.find("extralight") != string::npos)
895 h_font_roman[0] = "NotoSerifExtralight";
896 else if (opts.find("light") != string::npos)
897 h_font_roman[0] = "NotoSerifLight";
902 if (is_known(name, known_sans_font_packages)) {
903 h_font_sans[0] = name;
904 if (options.size() >= 1) {
905 if (scale_as_percentage(opts, h_font_sf_scale[0]))
910 if (name == "biolinum" || name == "biolinum-type1") {
911 h_font_sans[0] = "biolinum";
912 // biolinum can have several options, e.g. [osf,scaled=0.97]
913 string::size_type pos = opts.find("osf");
914 if (pos != string::npos)
918 if (name == "PTSans") {
919 h_font_sans[0] = "PTSans-TLF";
920 if (options.size() >= 1) {
921 if (scale_as_percentage(opts, h_font_sf_scale[0]))
926 if (name == "plex-sans") {
927 if (opts.find("condensed") != string::npos)
928 h_font_sans[0] = "IBMPlexSansCondensed";
929 else if (opts.find("thin") != string::npos)
930 h_font_sans[0] = "IBMPlexSansThin";
931 else if (opts.find("extralight") != string::npos)
932 h_font_sans[0] = "IBMPlexSansExtraLight";
933 else if (opts.find("light") != string::npos)
934 h_font_sans[0] = "IBMPlexSansLight";
935 else if (opts.find("semibold") != string::npos)
936 h_font_sans[0] = "IBMPlexSansSemibold";
938 h_font_sans[0] = "IBMPlexSans";
939 // check if the option contains scaling, if yes, extract it
940 string::size_type pos = opts.find("scale");
941 if (pos != string::npos) {
943 string::size_type i = opts.find(',', pos);
944 if (i == string::npos)
945 scale_as_percentage(opts.substr(pos + 1), scale);
947 scale_as_percentage(opts.substr(pos, i - pos), scale);
949 h_font_sf_scale[1] = scale;
952 if (name == "noto-sans") {
953 h_font_sans[0] = "NotoSansRegular";
955 if (opts.find("medium") != string::npos)
956 h_font_sans[0] = "NotoSansMedium";
957 else if (opts.find("thin") != string::npos)
958 h_font_sans[0] = "NotoSansThin";
959 else if (opts.find("extralight") != string::npos)
960 h_font_sans[0] = "NotoSansExtralight";
961 else if (opts.find("light") != string::npos)
962 h_font_sans[0] = "NotoSansLight";
968 if (is_known(name, known_typewriter_font_packages)) {
969 // fourier can be set as roman font _only_
970 // fourier as typewriter is handled in handling of \ttdefault
971 if (name != "fourier") {
972 h_font_typewriter[0] = name;
973 if (options.size() >= 1) {
974 if (scale_as_percentage(opts, h_font_tt_scale[0]))
980 if (name == "libertineMono" || name == "libertineMono-type1")
981 h_font_typewriter[0] = "libertine-mono";
983 if (name == "PTMono") {
984 h_font_typewriter[0] = "PTMono-TLF";
985 if (options.size() >= 1) {
986 if (scale_as_percentage(opts, h_font_tt_scale[0]))
991 if (name == "plex-mono") {
992 if (opts.find("thin") != string::npos)
993 h_font_typewriter[0] = "IBMPlexMonoThin";
994 else if (opts.find("extralight") != string::npos)
995 h_font_typewriter[0] = "IBMPlexMonoExtraLight";
996 else if (opts.find("light") != string::npos)
997 h_font_typewriter[0] = "IBMPlexMonoLight";
998 else if (opts.find("semibold") != string::npos)
999 h_font_typewriter[0] = "IBMPlexMonoSemibold";
1001 h_font_typewriter[0] = "IBMPlexMono";
1002 // check if the option contains scaling, if yes, extract it
1003 string::size_type pos = opts.find("scale");
1004 if (pos != string::npos) {
1006 string::size_type i = opts.find(',', pos);
1007 if (i == string::npos)
1008 scale_as_percentage(opts.substr(pos + 1), scale);
1010 scale_as_percentage(opts.substr(pos, i - pos), scale);
1012 h_font_tt_scale[1] = scale;
1016 if (name == "noto-mono") {
1017 h_font_typewriter[0] = "NotoMonoRegular";
1020 // font uses old-style figure
1022 h_font_osf = "true";
1025 if (is_known(name, known_math_font_packages))
1026 h_font_math[0] = name;
1028 if (name == "newtxmath") {
1030 h_font_math[0] = "newtxmath";
1031 else if (opts == "garamondx")
1032 h_font_math[0] = "garamondx-ntxm";
1033 else if (opts == "libertine")
1034 h_font_math[0] = "libertine-ntxm";
1035 else if (opts == "minion")
1036 h_font_math[0] = "minion-ntxm";
1037 else if (opts == "cochineal")
1038 h_font_math[0] = "cochineal-ntxm";
1041 if (name == "iwona")
1043 h_font_math[0] = "iwona-math";
1045 if (name == "kurier")
1047 h_font_math[0] = "kurier-math";
1049 // after the detection and handling of special cases, we can remove the
1050 // fonts, otherwise they would appear in the preamble, see bug #7856
1051 if (is_known(name, known_roman_font_packages) || is_known(name, known_sans_font_packages)
1052 || is_known(name, known_typewriter_font_packages) || is_known(name, known_math_font_packages))
1054 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
1055 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
1056 name == "esint" || name == "mhchem" || name == "mathdots" ||
1057 name == "mathtools" || name == "stackrel" ||
1058 name == "stmaryrd" || name == "undertilde") {
1059 h_use_packages[name] = "2";
1060 registerAutomaticallyLoadedPackage(name);
1063 else if (name == "babel") {
1064 h_language_package = "default";
1065 // One might think we would have to do nothing if babel is loaded
1066 // without any options to prevent pollution of the preamble with this
1067 // babel call in every roundtrip.
1068 // But the user could have defined babel-specific things afterwards. So
1069 // we need to keep it in the preamble to prevent cases like bug #7861.
1070 if (!opts.empty()) {
1071 // check if more than one option was used - used later for inputenc
1072 if (options.begin() != options.end() - 1)
1073 one_language = false;
1074 // babel takes the last language of the option of its \usepackage
1075 // call as document language. If there is no such language option, the
1076 // last language in the documentclass options is used.
1077 handle_opt(options, known_languages, h_language);
1078 // translate the babel name to a LyX name
1079 h_language = babel2lyx(h_language);
1080 if (h_language == "japanese") {
1081 // For Japanese, the encoding isn't indicated in the source
1082 // file, and there's really not much we can do. We could
1083 // 1) offer a list of possible encodings to choose from, or
1084 // 2) determine the encoding of the file by inspecting it.
1085 // For the time being, we leave the encoding alone so that
1086 // we don't get iconv errors when making a wrong guess, and
1087 // we will output a note at the top of the document
1088 // explaining what to do.
1089 Encoding const * const enc = encodings.fromIconvName(
1090 p.getEncoding(), Encoding::japanese, false);
1092 h_inputencoding = enc->name();
1093 is_nonCJKJapanese = true;
1094 // in this case babel can be removed from the preamble
1095 registerAutomaticallyLoadedPackage("babel");
1097 // If babel is called with options, LyX puts them by default into the
1098 // document class options. This works for most languages, except
1099 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
1100 // perhaps in future others.
1101 // Therefore keep the babel call as it is as the user might have
1103 string const babelcall = "\\usepackage[" + opts + "]{babel}\n";
1104 if (!contains(h_preamble.str(), babelcall))
1105 h_preamble << babelcall;
1107 delete_opt(options, known_languages);
1109 if (!contains(h_preamble.str(), "\\usepackage{babel}\n"))
1110 h_preamble << "\\usepackage{babel}\n";
1111 explicit_babel = true;
1115 else if (name == "polyglossia") {
1116 h_language_package = "default";
1117 h_default_output_format = "pdf4";
1118 h_use_non_tex_fonts = true;
1120 registerAutomaticallyLoadedPackage("xunicode");
1121 if (h_inputencoding == "auto-legacy")
1122 p.setEncoding("UTF-8");
1125 else if (name == "CJK") {
1126 // set the encoding to "auto-legacy" because it might be set to "auto-legacy-plain" by the babel handling
1127 // and this would not be correct for CJK
1128 if (h_inputencoding == "auto-legacy-plain")
1129 h_inputencoding = "auto-legacy";
1130 registerAutomaticallyLoadedPackage("CJK");
1133 else if (name == "CJKutf8") {
1134 h_inputencoding = "utf8-cjk";
1135 p.setEncoding("UTF-8");
1136 registerAutomaticallyLoadedPackage("CJKutf8");
1139 else if (name == "fontenc") {
1140 h_fontencoding = getStringFromVector(options, ",");
1144 else if (name == "inputenc" || name == "luainputenc") {
1145 // h_inputencoding is only set when there is not more than one
1146 // inputenc option because otherwise h_inputencoding must be
1147 // set to "auto-legacy" (the default encodings of the document's languages)
1148 // Therefore check that exactly one option is passed to inputenc.
1149 // It is also only set when there is not more than one babel
1151 if (!options.empty()) {
1152 string const encoding = options.back();
1153 Encoding const * const enc = encodings.fromLaTeXName(
1154 encoding, Encoding::inputenc, true);
1156 if (!detectEncoding)
1157 cerr << "Unknown encoding " << encoding
1158 << ". Ignoring." << std::endl;
1160 if (!enc->unsafe() && options.size() == 1 && one_language == true)
1161 h_inputencoding = enc->name();
1162 p.setEncoding(enc->iconvName());
1168 else if (name == "srcltx") {
1169 h_output_sync = "1";
1170 if (!opts.empty()) {
1171 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
1174 h_output_sync_macro = "\\usepackage{srcltx}";
1177 else if (is_known(name, known_old_language_packages)) {
1178 // known language packages from the times before babel
1179 // if they are found and not also babel, they will be used as
1180 // custom language package
1181 h_language_package = "\\usepackage{" + name + "}";
1184 else if (name == "lyxskak") {
1185 // ignore this and its options
1186 const char * const o[] = {"ps", "mover", 0};
1187 delete_opt(options, o);
1190 else if (is_known(name, known_lyx_packages) && options.empty()) {
1191 if (name == "splitidx")
1192 h_use_indices = "true";
1193 else if (name == "minted")
1194 h_use_minted = true;
1195 else if (name == "refstyle")
1196 h_use_refstyle = true;
1197 else if (name == "prettyref")
1198 h_use_refstyle = false;
1199 if (!in_lyx_preamble) {
1200 h_preamble << package_beg_sep << name
1201 << package_mid_sep << "\\usepackage{"
1203 if (p.next_token().cat() == catNewline ||
1204 (p.next_token().cat() == catSpace &&
1205 p.next_next_token().cat() == catNewline))
1207 h_preamble << package_end_sep;
1211 else if (name == "geometry")
1212 handle_geometry(options);
1214 else if (name == "subfig")
1215 ; // ignore this FIXME: Use the package separator mechanism instead
1217 else if (char const * const * where = is_known(name, known_languages))
1218 h_language = known_coded_languages[where - known_languages];
1220 else if (name == "natbib") {
1221 h_biblio_style = "plainnat";
1222 h_cite_engine = "natbib";
1223 h_cite_engine_type = "authoryear";
1224 vector<string>::iterator it =
1225 find(options.begin(), options.end(), "authoryear");
1226 if (it != options.end())
1229 it = find(options.begin(), options.end(), "numbers");
1230 if (it != options.end()) {
1231 h_cite_engine_type = "numerical";
1235 if (!options.empty())
1236 h_biblio_options = join(options, ",");
1239 else if (name == "biblatex") {
1240 h_biblio_style = "plainnat";
1241 h_cite_engine = "biblatex";
1242 h_cite_engine_type = "authoryear";
1244 vector<string>::iterator it =
1245 find(options.begin(), options.end(), "natbib");
1246 if (it != options.end()) {
1248 h_cite_engine = "biblatex-natbib";
1250 opt = process_keyval_opt(options, "natbib");
1252 h_cite_engine = "biblatex-natbib";
1254 opt = process_keyval_opt(options, "style");
1256 h_biblatex_citestyle = opt;
1257 h_biblatex_bibstyle = opt;
1259 opt = process_keyval_opt(options, "citestyle");
1261 h_biblatex_citestyle = opt;
1262 opt = process_keyval_opt(options, "bibstyle");
1264 h_biblatex_bibstyle = opt;
1266 opt = process_keyval_opt(options, "refsection");
1268 if (opt == "none" || opt == "part"
1269 || opt == "chapter" || opt == "section"
1270 || opt == "subsection")
1273 cerr << "Ignoring unkown refesection value '"
1276 opt = process_keyval_opt(options, "bibencoding");
1279 if (!options.empty()) {
1280 h_biblio_options = join(options, ",");
1285 else if (name == "jurabib") {
1286 h_biblio_style = "jurabib";
1287 h_cite_engine = "jurabib";
1288 h_cite_engine_type = "authoryear";
1289 if (!options.empty())
1290 h_biblio_options = join(options, ",");
1293 else if (name == "bibtopic")
1294 h_use_bibtopic = "true";
1296 else if (name == "chapterbib")
1297 h_multibib = "child";
1299 else if (name == "hyperref")
1300 handle_hyperref(options);
1302 else if (name == "algorithm2e") {
1303 // Load "algorithm2e" module
1304 addModule("algorithm2e");
1305 // Add the package options to the global document options
1306 if (!options.empty()) {
1307 if (h_options.empty())
1308 h_options = join(options, ",");
1310 h_options += ',' + join(options, ",");
1313 else if (name == "microtype") {
1314 //we internally support only microtype without params
1315 if (options.empty())
1316 h_use_microtype = "true";
1318 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1321 else if (!in_lyx_preamble) {
1322 if (options.empty())
1323 h_preamble << "\\usepackage{" << name << '}';
1325 h_preamble << "\\usepackage[" << opts << "]{"
1329 if (p.next_token().cat() == catNewline ||
1330 (p.next_token().cat() == catSpace &&
1331 p.next_next_token().cat() == catNewline))
1335 // We need to do something with the options...
1336 if (!options.empty() && !detectEncoding)
1337 cerr << "Ignoring options '" << join(options, ",")
1338 << "' of package " << name << '.' << endl;
1340 // remove the whitespace
1345 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1348 Token t = p.get_token();
1349 if (t.cat() == catEscape &&
1350 is_known(t.cs(), known_if_commands))
1351 handle_if(p, in_lyx_preamble);
1353 if (!in_lyx_preamble)
1354 h_preamble << t.asInput();
1355 if (t.cat() == catEscape && t.cs() == "fi")
1362 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1364 if (contains(h_float_placement, "H"))
1365 registerAutomaticallyLoadedPackage("float");
1366 if (h_spacing != "single" && h_spacing != "default")
1367 registerAutomaticallyLoadedPackage("setspace");
1368 if (h_use_packages["amsmath"] == "2") {
1369 // amsbsy and amstext are already provided by amsmath
1370 registerAutomaticallyLoadedPackage("amsbsy");
1371 registerAutomaticallyLoadedPackage("amstext");
1374 // output the LyX file settings
1375 // Important: Keep the version formatting in sync with LyX and
1376 // lyx2lyx (bug 7951)
1377 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1378 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1379 << lyx_version_minor << '\n'
1380 << "\\lyxformat " << LYX_FORMAT << '\n'
1381 << "\\begin_document\n"
1382 << "\\begin_header\n"
1383 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1384 << "\\origin " << origin << "\n"
1385 << "\\textclass " << h_textclass << "\n";
1386 string const raw = subdoc ? empty_string() : h_preamble.str();
1388 os << "\\begin_preamble\n";
1389 for (string::size_type i = 0; i < raw.size(); ++i) {
1390 if (raw[i] == package_beg_sep) {
1391 // Here follows some package loading code that
1392 // must be skipped if the package is loaded
1394 string::size_type j = raw.find(package_mid_sep, i);
1395 if (j == string::npos)
1397 string::size_type k = raw.find(package_end_sep, j);
1398 if (k == string::npos)
1400 string const package = raw.substr(i + 1, j - i - 1);
1401 string const replacement = raw.substr(j + 1, k - j - 1);
1402 if (auto_packages.find(package) == auto_packages.end())
1408 os << "\n\\end_preamble\n";
1410 if (!h_options.empty())
1411 os << "\\options " << h_options << "\n";
1412 os << "\\use_default_options " << h_use_default_options << "\n";
1413 if (!used_modules.empty()) {
1414 os << "\\begin_modules\n";
1415 vector<string>::const_iterator const end = used_modules.end();
1416 vector<string>::const_iterator it = used_modules.begin();
1417 for (; it != end; ++it)
1419 os << "\\end_modules\n";
1421 if (!h_includeonlys.empty()) {
1422 os << "\\begin_includeonly\n";
1423 for (auto const & iofile : h_includeonlys)
1424 os << iofile << '\n';
1425 os << "\\end_includeonly\n";
1427 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1428 << "\\language " << h_language << "\n"
1429 << "\\language_package " << h_language_package << "\n"
1430 << "\\inputencoding " << h_inputencoding << "\n"
1431 << "\\fontencoding " << h_fontencoding << "\n"
1432 << "\\font_roman \"" << h_font_roman[0]
1433 << "\" \"" << h_font_roman[1] << "\"\n"
1434 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1435 << "\\font_typewriter \"" << h_font_typewriter[0]
1436 << "\" \"" << h_font_typewriter[1] << "\"\n"
1437 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1438 << "\\font_default_family " << h_font_default_family << "\n"
1439 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1440 << "\\font_sc " << h_font_sc << "\n"
1441 << "\\font_osf " << h_font_osf << "\n";
1442 if (!h_font_roman_opts.empty())
1443 os << "\\font_roman_opts \"" << h_font_roman_opts << "\"" << '\n';
1444 os << "\\font_sf_scale " << h_font_sf_scale[0]
1445 << ' ' << h_font_sf_scale[1] << '\n';
1446 if (!h_font_sans_opts.empty())
1447 os << "\\font_sans_opts \"" << h_font_sans_opts << "\"" << '\n';
1448 os << "\\font_tt_scale " << h_font_tt_scale[0]
1449 << ' ' << h_font_tt_scale[1] << '\n';
1450 if (!h_font_cjk.empty())
1451 os << "\\font_cjk " << h_font_cjk << '\n';
1452 if (!h_font_typewriter_opts.empty())
1453 os << "\\font_typewriter_opts \"" << h_font_typewriter_opts << "\"" << '\n';
1454 os << "\\use_microtype " << h_use_microtype << '\n'
1455 << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
1456 << "\\graphics " << h_graphics << '\n'
1457 << "\\default_output_format " << h_default_output_format << "\n"
1458 << "\\output_sync " << h_output_sync << "\n";
1459 if (h_output_sync == "1")
1460 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1461 os << "\\bibtex_command " << h_bibtex_command << "\n"
1462 << "\\index_command " << h_index_command << "\n";
1463 if (!h_float_placement.empty())
1464 os << "\\float_placement " << h_float_placement << "\n";
1465 os << "\\paperfontsize " << h_paperfontsize << "\n"
1466 << "\\spacing " << h_spacing << "\n"
1467 << "\\use_hyperref " << h_use_hyperref << '\n';
1468 if (h_use_hyperref == "true") {
1469 if (!h_pdf_title.empty())
1470 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1471 if (!h_pdf_author.empty())
1472 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1473 if (!h_pdf_subject.empty())
1474 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1475 if (!h_pdf_keywords.empty())
1476 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1477 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1478 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1479 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1480 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1481 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1482 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1483 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1484 "\\pdf_backref " << h_pdf_backref << "\n"
1485 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1486 if (!h_pdf_pagemode.empty())
1487 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1488 if (!h_pdf_quoted_options.empty())
1489 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1491 os << "\\papersize " << h_papersize << "\n"
1492 << "\\use_geometry " << h_use_geometry << '\n';
1493 for (map<string, string>::const_iterator it = h_use_packages.begin();
1494 it != h_use_packages.end(); ++it)
1495 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1496 os << "\\cite_engine " << h_cite_engine << '\n'
1497 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1498 << "\\biblio_style " << h_biblio_style << "\n"
1499 << "\\use_bibtopic " << h_use_bibtopic << "\n";
1500 if (!h_biblio_options.empty())
1501 os << "\\biblio_options " << h_biblio_options << "\n";
1502 if (!h_biblatex_bibstyle.empty())
1503 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
1504 if (!h_biblatex_citestyle.empty())
1505 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
1506 if (!h_multibib.empty())
1507 os << "\\multibib " << h_multibib << "\n";
1508 os << "\\use_indices " << h_use_indices << "\n"
1509 << "\\paperorientation " << h_paperorientation << '\n'
1510 << "\\suppress_date " << h_suppress_date << '\n'
1511 << "\\justification " << h_justification << '\n'
1512 << "\\use_refstyle " << h_use_refstyle << '\n'
1513 << "\\use_minted " << h_use_minted << '\n';
1514 if (!h_fontcolor.empty())
1515 os << "\\fontcolor " << h_fontcolor << '\n';
1516 if (!h_notefontcolor.empty())
1517 os << "\\notefontcolor " << h_notefontcolor << '\n';
1518 if (!h_backgroundcolor.empty())
1519 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1520 if (!h_boxbgcolor.empty())
1521 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1522 if (index_number != 0)
1523 for (int i = 0; i < index_number; i++) {
1524 os << "\\index " << h_index[i] << '\n'
1525 << "\\shortcut " << h_shortcut[i] << '\n'
1526 << "\\color " << h_color << '\n'
1530 os << "\\index " << h_index[0] << '\n'
1531 << "\\shortcut " << h_shortcut[0] << '\n'
1532 << "\\color " << h_color << '\n'
1536 << "\\secnumdepth " << h_secnumdepth << "\n"
1537 << "\\tocdepth " << h_tocdepth << "\n"
1538 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1539 if (h_paragraph_separation == "skip")
1540 os << "\\defskip " << h_defskip << "\n";
1542 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1543 os << "\\is_math_indent " << h_is_mathindent << "\n";
1544 if (!h_mathindentation.empty())
1545 os << "\\math_indentation " << h_mathindentation << "\n";
1546 os << "\\math_numbering_side " << h_math_numbering_side << "\n";
1547 os << "\\quotes_style " << h_quotes_style << "\n"
1548 << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
1549 << "\\papercolumns " << h_papercolumns << "\n"
1550 << "\\papersides " << h_papersides << "\n"
1551 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1552 if (!h_listings_params.empty())
1553 os << "\\listings_params " << h_listings_params << "\n";
1554 os << "\\tracking_changes " << h_tracking_changes << "\n"
1555 << "\\output_changes " << h_output_changes << "\n"
1556 << "\\html_math_output " << h_html_math_output << "\n"
1557 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1558 << "\\html_be_strict " << h_html_be_strict << "\n"
1560 << "\\end_header\n\n"
1561 << "\\begin_body\n";
1566 void Preamble::parse(Parser & p, string const & forceclass,
1567 TeX2LyXDocClass & tc)
1569 // initialize fixed types
1570 special_columns_['D'] = 3;
1571 parse(p, forceclass, false, tc);
1575 void Preamble::parse(Parser & p, string const & forceclass,
1576 bool detectEncoding, TeX2LyXDocClass & tc)
1578 bool is_full_document = false;
1579 bool is_lyx_file = false;
1580 bool in_lyx_preamble = false;
1582 // determine whether this is a full document or a fragment for inclusion
1584 Token const & t = p.get_token();
1586 if (t.cat() == catEscape && t.cs() == "documentclass") {
1587 is_full_document = true;
1593 if (detectEncoding && !is_full_document)
1596 while (is_full_document && p.good()) {
1597 if (detectEncoding && h_inputencoding != "auto-legacy" &&
1598 h_inputencoding != "auto-legacy-plain")
1601 Token const & t = p.get_token();
1604 if (!detectEncoding)
1605 cerr << "t: " << t << '\n';
1611 if (!in_lyx_preamble &&
1612 (t.cat() == catLetter ||
1613 t.cat() == catSuper ||
1614 t.cat() == catSub ||
1615 t.cat() == catOther ||
1616 t.cat() == catMath ||
1617 t.cat() == catActive ||
1618 t.cat() == catBegin ||
1619 t.cat() == catEnd ||
1620 t.cat() == catAlign ||
1621 t.cat() == catParameter)) {
1622 h_preamble << t.cs();
1626 if (!in_lyx_preamble &&
1627 (t.cat() == catSpace || t.cat() == catNewline)) {
1628 h_preamble << t.asInput();
1632 if (t.cat() == catComment) {
1633 static regex const islyxfile("%% LyX .* created this file");
1634 static regex const usercommands("User specified LaTeX commands");
1636 string const comment = t.asInput();
1638 // magically switch encoding default if it looks like XeLaTeX
1639 static string const magicXeLaTeX =
1640 "% This document must be compiled with XeLaTeX ";
1641 if (comment.size() > magicXeLaTeX.size()
1642 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1643 && h_inputencoding == "auto-legacy") {
1644 if (!detectEncoding)
1645 cerr << "XeLaTeX comment found, switching to UTF8\n";
1646 h_inputencoding = "utf8";
1649 if (regex_search(comment, sub, islyxfile)) {
1651 in_lyx_preamble = true;
1652 } else if (is_lyx_file
1653 && regex_search(comment, sub, usercommands))
1654 in_lyx_preamble = false;
1655 else if (!in_lyx_preamble)
1656 h_preamble << t.asInput();
1660 if (t.cs() == "PassOptionsToPackage") {
1661 string const poptions = p.getArg('{', '}');
1662 string const package = p.verbatim_item();
1663 extra_package_options_.insert(make_pair(package, poptions));
1667 if (t.cs() == "pagestyle") {
1668 h_paperpagestyle = p.verbatim_item();
1672 if (t.cs() == "setdefaultlanguage") {
1674 // We don't yet care about non-language variant options
1675 // because LyX doesn't support this yet, see bug #8214
1677 string langopts = p.getOpt();
1678 // check if the option contains a variant, if yes, extract it
1679 string::size_type pos_var = langopts.find("variant");
1680 string::size_type i = langopts.find(',', pos_var);
1681 string::size_type k = langopts.find('=', pos_var);
1682 if (pos_var != string::npos){
1684 if (i == string::npos)
1685 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1687 variant = langopts.substr(k + 1, i - k - 1);
1688 h_language = variant;
1692 h_language = p.verbatim_item();
1693 //finally translate the poyglossia name to a LyX name
1694 h_language = polyglossia2lyx(h_language);
1698 if (t.cs() == "setotherlanguage") {
1699 // We don't yet care about the option because LyX doesn't
1700 // support this yet, see bug #8214
1701 p.hasOpt() ? p.getOpt() : string();
1706 if (t.cs() == "setmainfont") {
1707 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
1708 h_font_roman[1] = p.getArg('{', '}');
1709 if (!fontopts.empty()) {
1710 vector<string> opts = getVectorFromString(fontopts);
1712 for (auto const & opt : opts) {
1713 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
1716 if (!fontopts.empty())
1720 h_font_roman_opts = fontopts;
1725 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1726 // LyX currently only supports the scale option
1727 string scale, fontopts;
1729 fontopts = p.getArg('[', ']');
1730 if (!fontopts.empty()) {
1731 vector<string> opts = getVectorFromString(fontopts);
1733 for (auto const & opt : opts) {
1734 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
1737 if (prefixIs(opt, "Scale=")) {
1738 scale_as_percentage(opt, scale);
1741 if (!fontopts.empty())
1747 if (t.cs() == "setsansfont") {
1749 h_font_sf_scale[1] = scale;
1750 h_font_sans[1] = p.getArg('{', '}');
1751 if (!fontopts.empty())
1752 h_font_sans_opts = fontopts;
1755 h_font_tt_scale[1] = scale;
1756 h_font_typewriter[1] = p.getArg('{', '}');
1757 if (!fontopts.empty())
1758 h_font_typewriter_opts = fontopts;
1763 if (t.cs() == "babelfont") {
1765 h_use_non_tex_fonts = true;
1766 h_language_package = "babel";
1767 if (h_inputencoding == "auto-legacy")
1768 p.setEncoding("UTF-8");
1769 // we don't care about the lang option
1770 string const lang = p.hasOpt() ? p.getArg('[', ']') : string();
1771 string const family = p.getArg('{', '}');
1772 string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
1773 string const fontname = p.getArg('{', '}');
1774 if (lang.empty() && family == "rm") {
1775 h_font_roman[1] = fontname;
1776 if (!fontopts.empty()) {
1777 vector<string> opts = getVectorFromString(fontopts);
1779 for (auto const & opt : opts) {
1780 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
1783 if (!fontopts.empty())
1787 h_font_roman_opts = fontopts;
1790 } else if (lang.empty() && (family == "sf" || family == "tt")) {
1791 // LyX currently only supports the scale option
1793 if (!fontopts.empty()) {
1794 vector<string> opts = getVectorFromString(fontopts);
1796 for (auto const & opt : opts) {
1797 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
1800 if (prefixIs(opt, "Scale=")) {
1801 scale_as_percentage(opt, scale);
1804 if (!fontopts.empty())
1809 if (family == "sf") {
1811 h_font_sf_scale[1] = scale;
1812 h_font_sans[1] = fontname;
1813 if (!fontopts.empty())
1814 h_font_sans_opts = fontopts;
1817 h_font_tt_scale[1] = scale;
1818 h_font_typewriter[1] = fontname;
1819 if (!fontopts.empty())
1820 h_font_typewriter_opts = fontopts;
1824 // not rm, sf or tt or lang specific
1825 h_preamble << '\\' << t.cs();
1827 h_preamble << '[' << lang << ']';
1828 h_preamble << '{' << family << '}';
1829 if (!fontopts.empty())
1830 h_preamble << '[' << fontopts << ']';
1831 h_preamble << '{' << fontname << '}' << '\n';
1836 if (t.cs() == "date") {
1837 string argument = p.getArg('{', '}');
1838 if (argument.empty())
1839 h_suppress_date = "true";
1841 h_preamble << t.asInput() << '{' << argument << '}';
1845 if (t.cs() == "color") {
1846 string const space =
1847 (p.hasOpt() ? p.getOpt() : string());
1848 string argument = p.getArg('{', '}');
1849 // check the case that a standard color is used
1850 if (space.empty() && is_known(argument, known_basic_colors)) {
1851 h_fontcolor = rgbcolor2code(argument);
1852 registerAutomaticallyLoadedPackage("color");
1853 } else if (space.empty() && argument == "document_fontcolor")
1854 registerAutomaticallyLoadedPackage("color");
1855 // check the case that LyX's document_fontcolor is defined
1856 // but not used for \color
1858 h_preamble << t.asInput();
1860 h_preamble << space;
1861 h_preamble << '{' << argument << '}';
1862 // the color might already be set because \definecolor
1863 // is parsed before this
1869 if (t.cs() == "pagecolor") {
1870 string argument = p.getArg('{', '}');
1871 // check the case that a standard color is used
1872 if (is_known(argument, known_basic_colors)) {
1873 h_backgroundcolor = rgbcolor2code(argument);
1874 } else if (argument == "page_backgroundcolor")
1875 registerAutomaticallyLoadedPackage("color");
1876 // check the case that LyX's page_backgroundcolor is defined
1877 // but not used for \pagecolor
1879 h_preamble << t.asInput() << '{' << argument << '}';
1880 // the color might already be set because \definecolor
1881 // is parsed before this
1882 h_backgroundcolor = "";
1887 if (t.cs() == "makeatletter") {
1888 // LyX takes care of this
1889 p.setCatcode('@', catLetter);
1893 if (t.cs() == "makeatother") {
1894 // LyX takes care of this
1895 p.setCatcode('@', catOther);
1899 if (t.cs() == "makeindex") {
1900 // LyX will re-add this if a print index command is found
1905 if (t.cs() == "newindex") {
1906 string const indexname = p.getArg('[', ']');
1907 string const shortcut = p.verbatim_item();
1908 if (!indexname.empty())
1909 h_index[index_number] = indexname;
1911 h_index[index_number] = shortcut;
1912 h_shortcut[index_number] = shortcut;
1918 if (t.cs() == "addbibresource") {
1919 string const options = p.getArg('[', ']');
1920 string const arg = removeExtension(p.getArg('{', '}'));
1921 if (!options.empty()) {
1922 // check if the option contains a bibencoding, if yes, extract it
1923 string::size_type pos = options.find("bibencoding=");
1925 if (pos != string::npos) {
1926 string::size_type i = options.find(',', pos);
1927 if (i == string::npos)
1928 encoding = options.substr(pos + 1);
1930 encoding = options.substr(pos, i - pos);
1931 pos = encoding.find('=');
1932 if (pos == string::npos)
1935 encoding = encoding.substr(pos + 1);
1937 if (!encoding.empty())
1938 biblatex_encodings.push_back(normalize_filename(arg) + ' ' + encoding);
1940 biblatex_bibliographies.push_back(arg);
1944 if (t.cs() == "bibliography") {
1945 vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
1946 biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
1950 if (t.cs() == "RS@ifundefined") {
1951 string const name = p.verbatim_item();
1952 string const body1 = p.verbatim_item();
1953 string const body2 = p.verbatim_item();
1954 // only non-lyxspecific stuff
1955 if (in_lyx_preamble &&
1956 (name == "subsecref" || name == "thmref" || name == "lemref"))
1960 ss << '\\' << t.cs();
1961 ss << '{' << name << '}'
1962 << '{' << body1 << '}'
1963 << '{' << body2 << '}';
1964 h_preamble << ss.str();
1969 if (t.cs() == "AtBeginDocument") {
1970 string const name = p.verbatim_item();
1971 // only non-lyxspecific stuff
1972 if (in_lyx_preamble &&
1973 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
1974 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
1975 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
1976 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
1977 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
1978 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
1979 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
1980 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
1981 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
1982 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
1983 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
1984 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
1985 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
1986 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
1987 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
1991 ss << '\\' << t.cs();
1992 ss << '{' << name << '}';
1993 h_preamble << ss.str();
1998 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1999 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
2000 || t.cs() == "providecommand" || t.cs() == "providecommandx"
2001 || t.cs() == "DeclareRobustCommand"
2002 || t.cs() == "DeclareRobustCommandx"
2003 || t.cs() == "ProvideTextCommandDefault"
2004 || t.cs() == "DeclareMathAccent") {
2006 if (p.next_token().character() == '*') {
2010 string const name = p.verbatim_item();
2011 string const opt1 = p.getFullOpt();
2012 string const opt2 = p.getFullOpt();
2013 string const body = p.verbatim_item();
2014 // store the in_lyx_preamble setting
2015 bool const was_in_lyx_preamble = in_lyx_preamble;
2017 if (name == "\\rmdefault")
2018 if (is_known(body, known_roman_font_packages)) {
2019 h_font_roman[0] = body;
2021 in_lyx_preamble = true;
2023 if (name == "\\sfdefault")
2024 if (is_known(body, known_sans_font_packages)) {
2025 h_font_sans[0] = body;
2027 in_lyx_preamble = true;
2029 if (name == "\\ttdefault")
2030 if (is_known(body, known_typewriter_font_packages)) {
2031 h_font_typewriter[0] = body;
2033 in_lyx_preamble = true;
2035 if (name == "\\familydefault") {
2036 string family = body;
2037 // remove leading "\"
2038 h_font_default_family = family.erase(0,1);
2040 in_lyx_preamble = true;
2043 // remove LyX-specific definitions that are re-added by LyX
2045 // \lyxline is an ancient command that is converted by tex2lyx into
2046 // a \rule therefore remove its preamble code
2047 if (name == "\\lyxdot" || name == "\\lyxarrow"
2048 || name == "\\lyxline" || name == "\\LyX") {
2050 in_lyx_preamble = true;
2053 // Add the command to the known commands
2054 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
2056 // only non-lyxspecific stuff
2057 if (!in_lyx_preamble) {
2059 ss << '\\' << t.cs();
2062 ss << '{' << name << '}' << opt1 << opt2
2063 << '{' << body << "}";
2064 if (prefixIs(t.cs(), "renew") || !contains(h_preamble.str(), ss.str()))
2065 h_preamble << ss.str();
2067 ostream & out = in_preamble ? h_preamble : os;
2068 out << "\\" << t.cs() << "{" << name << "}"
2069 << opts << "{" << body << "}";
2072 // restore the in_lyx_preamble setting
2073 in_lyx_preamble = was_in_lyx_preamble;
2077 if (t.cs() == "documentclass") {
2078 vector<string>::iterator it;
2079 vector<string> opts = split_options(p.getArg('[', ']'));
2080 handle_opt(opts, known_fontsizes, h_paperfontsize);
2081 delete_opt(opts, known_fontsizes);
2082 // delete "pt" at the end
2083 string::size_type i = h_paperfontsize.find("pt");
2084 if (i != string::npos)
2085 h_paperfontsize.erase(i);
2086 // The documentclass options are always parsed before the options
2087 // of the babel call so that a language cannot overwrite the babel
2089 handle_opt(opts, known_languages, h_language);
2090 delete_opt(opts, known_languages);
2093 if ((it = find(opts.begin(), opts.end(), "fleqn"))
2095 h_is_mathindent = "1";
2098 // formula numbering side
2099 if ((it = find(opts.begin(), opts.end(), "leqno"))
2101 h_math_numbering_side = "left";
2104 else if ((it = find(opts.begin(), opts.end(), "reqno"))
2106 h_math_numbering_side = "right";
2110 // paper orientation
2111 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
2112 h_paperorientation = "landscape";
2116 if ((it = find(opts.begin(), opts.end(), "oneside"))
2121 if ((it = find(opts.begin(), opts.end(), "twoside"))
2127 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
2129 h_papercolumns = "1";
2132 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
2134 h_papercolumns = "2";
2138 // some size options are known to any document classes, other sizes
2139 // are handled by the \geometry command of the geometry package
2140 handle_opt(opts, known_class_paper_sizes, h_papersize);
2141 delete_opt(opts, known_class_paper_sizes);
2142 // the remaining options
2143 h_options = join(opts, ",");
2144 // FIXME This does not work for classes that have a
2145 // different name in LyX than in LaTeX
2146 h_textclass = p.getArg('{', '}');
2151 if (t.cs() == "usepackage") {
2152 string const options = p.getArg('[', ']');
2153 string const name = p.getArg('{', '}');
2154 vector<string> vecnames;
2155 split(name, vecnames, ',');
2156 vector<string>::const_iterator it = vecnames.begin();
2157 vector<string>::const_iterator end = vecnames.end();
2158 for (; it != end; ++it)
2159 handle_package(p, trimSpaceAndEol(*it), options,
2160 in_lyx_preamble, detectEncoding);
2164 if (t.cs() == "inputencoding") {
2165 string const encoding = p.getArg('{','}');
2166 Encoding const * const enc = encodings.fromLaTeXName(
2167 encoding, Encoding::inputenc, true);
2169 if (!detectEncoding)
2170 cerr << "Unknown encoding " << encoding
2171 << ". Ignoring." << std::endl;
2174 h_inputencoding = enc->name();
2175 p.setEncoding(enc->iconvName());
2180 if (t.cs() == "newenvironment") {
2181 string const name = p.getArg('{', '}');
2182 string const opt1 = p.getFullOpt();
2183 string const opt2 = p.getFullOpt();
2184 string const beg = p.verbatim_item();
2185 string const end = p.verbatim_item();
2186 if (!in_lyx_preamble) {
2187 h_preamble << "\\newenvironment{" << name
2188 << '}' << opt1 << opt2 << '{'
2189 << beg << "}{" << end << '}';
2191 add_known_environment(name, opt1, !opt2.empty(),
2192 from_utf8(beg), from_utf8(end));
2196 if (t.cs() == "newtheorem") {
2198 if (p.next_token().character() == '*') {
2202 string const name = p.getArg('{', '}');
2203 string const opt1 = p.getFullOpt();
2204 string const opt2 = p.getFullOpt();
2205 string const body = p.verbatim_item();
2206 string const opt3 = p.getFullOpt();
2207 string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
2209 string const complete = cmd + "{" + name + '}' +
2210 opt1 + opt2 + '{' + body + '}' + opt3;
2212 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
2214 if (!in_lyx_preamble)
2215 h_preamble << complete;
2219 if (t.cs() == "def") {
2220 string name = p.get_token().cs();
2221 // In fact, name may be more than the name:
2222 // In the test case of bug 8116
2223 // name == "csname SF@gobble@opt \endcsname".
2224 // Therefore, we need to use asInput() instead of cs().
2225 while (p.next_token().cat() != catBegin)
2226 name += p.get_token().asInput();
2227 if (!in_lyx_preamble)
2228 h_preamble << "\\def\\" << name << '{'
2229 << p.verbatim_item() << "}";
2233 if (t.cs() == "newcolumntype") {
2234 string const name = p.getArg('{', '}');
2235 trimSpaceAndEol(name);
2237 string opts = p.getOpt();
2238 if (!opts.empty()) {
2239 istringstream is(string(opts, 1));
2242 special_columns_[name[0]] = nargs;
2243 h_preamble << "\\newcolumntype{" << name << "}";
2245 h_preamble << "[" << nargs << "]";
2246 h_preamble << "{" << p.verbatim_item() << "}";
2250 if (t.cs() == "setcounter") {
2251 string const name = p.getArg('{', '}');
2252 string const content = p.getArg('{', '}');
2253 if (name == "secnumdepth")
2254 h_secnumdepth = content;
2255 else if (name == "tocdepth")
2256 h_tocdepth = content;
2258 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
2262 if (t.cs() == "setlength") {
2263 string const name = p.verbatim_item();
2264 string const content = p.verbatim_item();
2265 // the paragraphs are only not indented when \parindent is set to zero
2266 if (name == "\\parindent" && content != "") {
2267 if (content[0] == '0')
2268 h_paragraph_separation = "skip";
2270 h_paragraph_indentation = translate_len(content);
2271 } else if (name == "\\parskip") {
2272 if (content == "\\smallskipamount")
2273 h_defskip = "smallskip";
2274 else if (content == "\\medskipamount")
2275 h_defskip = "medskip";
2276 else if (content == "\\bigskipamount")
2277 h_defskip = "bigskip";
2279 h_defskip = translate_len(content);
2280 } else if (name == "\\mathindent") {
2281 h_mathindentation = translate_len(content);
2283 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2287 if (t.cs() == "onehalfspacing") {
2288 h_spacing = "onehalf";
2292 if (t.cs() == "doublespacing") {
2293 h_spacing = "double";
2297 if (t.cs() == "setstretch") {
2298 h_spacing = "other " + p.verbatim_item();
2302 if (t.cs() == "synctex") {
2303 // the scheme is \synctex=value
2304 // where value can only be "1" or "-1"
2305 h_output_sync = "1";
2306 // there can be any character behind the value (e.g. a linebreak or a '\'
2307 // therefore we extract it char by char
2309 string value = p.get_token().asInput();
2311 value += p.get_token().asInput();
2312 h_output_sync_macro = "\\synctex=" + value;
2316 if (t.cs() == "begin") {
2317 string const name = p.getArg('{', '}');
2318 if (name == "document")
2320 h_preamble << "\\begin{" << name << "}";
2324 if (t.cs() == "geometry") {
2325 vector<string> opts = split_options(p.getArg('{', '}'));
2326 handle_geometry(opts);
2330 if (t.cs() == "definecolor") {
2331 string const color = p.getArg('{', '}');
2332 string const space = p.getArg('{', '}');
2333 string const value = p.getArg('{', '}');
2334 if (color == "document_fontcolor" && space == "rgb") {
2335 RGBColor c(RGBColorFromLaTeX(value));
2336 h_fontcolor = X11hexname(c);
2337 } else if (color == "note_fontcolor" && space == "rgb") {
2338 RGBColor c(RGBColorFromLaTeX(value));
2339 h_notefontcolor = X11hexname(c);
2340 } else if (color == "page_backgroundcolor" && space == "rgb") {
2341 RGBColor c(RGBColorFromLaTeX(value));
2342 h_backgroundcolor = X11hexname(c);
2343 } else if (color == "shadecolor" && space == "rgb") {
2344 RGBColor c(RGBColorFromLaTeX(value));
2345 h_boxbgcolor = X11hexname(c);
2347 h_preamble << "\\definecolor{" << color
2348 << "}{" << space << "}{" << value
2354 if (t.cs() == "bibliographystyle") {
2355 h_biblio_style = p.verbatim_item();
2359 if (t.cs() == "jurabibsetup") {
2360 // FIXME p.getArg('{', '}') is most probably wrong (it
2361 // does not handle nested braces).
2362 // Use p.verbatim_item() instead.
2363 vector<string> jurabibsetup =
2364 split_options(p.getArg('{', '}'));
2365 // add jurabibsetup to the jurabib package options
2366 add_package("jurabib", jurabibsetup);
2367 if (!jurabibsetup.empty()) {
2368 h_preamble << "\\jurabibsetup{"
2369 << join(jurabibsetup, ",") << '}';
2374 if (t.cs() == "hypersetup") {
2375 vector<string> hypersetup =
2376 split_options(p.verbatim_item());
2377 // add hypersetup to the hyperref package options
2378 handle_hyperref(hypersetup);
2379 if (!hypersetup.empty()) {
2380 h_preamble << "\\hypersetup{"
2381 << join(hypersetup, ",") << '}';
2386 if (t.cs() == "includeonly") {
2387 vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
2388 for (auto & iofile : includeonlys) {
2389 string filename(normalize_filename(iofile));
2390 string const path = getMasterFilePath(true);
2391 // We want to preserve relative/absolute filenames,
2392 // therefore path is only used for testing
2393 if (!makeAbsPath(filename, path).exists()) {
2394 // The file extension is probably missing.
2395 // Now try to find it out.
2396 string const tex_name =
2397 find_file(filename, path,
2398 known_tex_extensions);
2399 if (!tex_name.empty())
2400 filename = tex_name;
2403 if (makeAbsPath(filename, path).exists())
2404 fix_child_filename(filename);
2406 cerr << "Warning: Could not find included file '"
2407 << filename << "'." << endl;
2408 outname = changeExtension(filename, "lyx");
2409 h_includeonlys.push_back(outname);
2414 if (is_known(t.cs(), known_if_3arg_commands)) {
2415 // prevent misparsing of \usepackage if it is used
2416 // as an argument (see e.g. our own output of
2417 // \@ifundefined above)
2418 string const arg1 = p.verbatim_item();
2419 string const arg2 = p.verbatim_item();
2420 string const arg3 = p.verbatim_item();
2421 // test case \@ifundefined{date}{}{\date{}}
2422 if (t.cs() == "@ifundefined" && arg1 == "date" &&
2423 arg2.empty() && arg3 == "\\date{}") {
2424 h_suppress_date = "true";
2425 // older tex2lyx versions did output
2426 // \@ifundefined{definecolor}{\usepackage{color}}{}
2427 } else if (t.cs() == "@ifundefined" &&
2428 arg1 == "definecolor" &&
2429 arg2 == "\\usepackage{color}" &&
2431 if (!in_lyx_preamble)
2432 h_preamble << package_beg_sep
2435 << "\\@ifundefined{definecolor}{color}{}"
2438 //\@ifundefined{showcaptionsetup}{}{%
2439 // \PassOptionsToPackage{caption=false}{subfig}}
2440 // that LyX uses for subfloats
2441 } else if (t.cs() == "@ifundefined" &&
2442 arg1 == "showcaptionsetup" && arg2.empty()
2443 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
2445 } else if (!in_lyx_preamble) {
2446 h_preamble << t.asInput()
2447 << '{' << arg1 << '}'
2448 << '{' << arg2 << '}'
2449 << '{' << arg3 << '}';
2454 if (is_known(t.cs(), known_if_commands)) {
2455 // must not parse anything in conditional code, since
2456 // LyX would output the parsed contents unconditionally
2457 if (!in_lyx_preamble)
2458 h_preamble << t.asInput();
2459 handle_if(p, in_lyx_preamble);
2463 if (!t.cs().empty() && !in_lyx_preamble) {
2464 h_preamble << '\\' << t.cs();
2469 // remove the whitespace
2472 // Force textclass if the user wanted it
2473 if (!forceclass.empty())
2474 h_textclass = forceclass;
2475 tc.setName(h_textclass);
2476 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2477 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2480 if (h_papersides.empty()) {
2483 h_papersides = ss.str();
2486 // If the CJK package is used we cannot set the document language from
2487 // the babel options. Instead, we guess which language is used most
2488 // and set this one.
2489 default_language = h_language;
2490 if (is_full_document &&
2491 (auto_packages.find("CJK") != auto_packages.end() ||
2492 auto_packages.find("CJKutf8") != auto_packages.end())) {
2494 h_language = guessLanguage(p, default_language);
2496 if (explicit_babel && h_language != default_language) {
2497 // We set the document language to a CJK language,
2498 // but babel is explicitly called in the user preamble
2499 // without options. LyX will not add the default
2500 // language to the document options if it is either
2501 // english, or no text is set as default language.
2502 // Therefore we need to add a language option explicitly.
2503 // FIXME: It would be better to remove all babel calls
2504 // from the user preamble, but this is difficult
2505 // without re-introducing bug 7861.
2506 if (h_options.empty())
2507 h_options = lyx2babel(default_language);
2509 h_options += ',' + lyx2babel(default_language);
2513 // Finally, set the quote style.
2514 // LyX knows the following quotes styles:
2515 // british, cjk, cjkangle, danish, english, french, german,
2516 // polish, russian, swedish and swiss
2517 // conversion list taken from
2518 // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
2519 // (quotes for kazakh are unknown)
2521 if (is_known(h_language, known_british_quotes_languages))
2522 h_quotes_style = "british";
2524 else if (is_known(h_language, known_cjk_quotes_languages))
2525 h_quotes_style = "cjk";
2527 else if (is_known(h_language, known_cjkangle_quotes_languages))
2528 h_quotes_style = "cjkangle";
2530 else if (is_known(h_language, known_danish_quotes_languages))
2531 h_quotes_style = "danish";
2533 else if (is_known(h_language, known_french_quotes_languages))
2534 h_quotes_style = "french";
2536 else if (is_known(h_language, known_german_quotes_languages))
2537 h_quotes_style = "german";
2539 else if (is_known(h_language, known_polish_quotes_languages))
2540 h_quotes_style = "polish";
2542 else if (is_known(h_language, known_russian_quotes_languages))
2543 h_quotes_style = "russian";
2545 else if (is_known(h_language, known_swedish_quotes_languages))
2546 h_quotes_style = "swedish";
2548 else if (is_known(h_language, known_swiss_quotes_languages))
2549 h_quotes_style = "swiss";
2551 else if (is_known(h_language, known_english_quotes_languages))
2552 h_quotes_style = "english";
2556 string Preamble::parseEncoding(Parser & p, string const & forceclass)
2558 TeX2LyXDocClass dummy;
2559 parse(p, forceclass, true, dummy);
2560 if (h_inputencoding != "auto-legacy" && h_inputencoding != "auto-legacy-plain")
2561 return h_inputencoding;
2566 string babel2lyx(string const & language)
2568 char const * const * where = is_known(language, known_languages);
2570 return known_coded_languages[where - known_languages];
2575 string lyx2babel(string const & language)
2577 char const * const * where = is_known(language, known_coded_languages);
2579 return known_languages[where - known_coded_languages];
2584 string Preamble::polyglossia2lyx(string const & language)
2586 char const * const * where = is_known(language, polyglossia_languages);
2588 return coded_polyglossia_languages[where - polyglossia_languages];
2593 string rgbcolor2code(string const & name)
2595 char const * const * where = is_known(name, known_basic_colors);
2597 // "red", "green" etc
2598 return known_basic_color_codes[where - known_basic_colors];
2600 // "255,0,0", "0,255,0" etc
2601 RGBColor c(RGBColorFromLaTeX(name));
2602 return X11hexname(c);