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", "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", "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", "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[] = {"russian", "ukrainian", 0};
129 /// languages with swedish quotes (.lyx names)
130 const char * const known_swedish_quotes_languages[] = {"finnish", "swedish", 0};
132 /// languages with swiss quotes (.lyx names)
133 const char * const known_swiss_quotes_languages[] = {"albanian",
134 "armenian", "basque", "german-ch", "german-ch-old",
135 "norsk", "nynorsk", "turkmen", "ukrainian", "vietnamese", 0};
137 /// known language packages from the times before babel
138 const char * const known_old_language_packages[] = {"french", "frenchle",
139 "frenchpro", "german", "ngerman", "pmfrench", 0};
141 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
143 const char * const known_roman_fonts[] = { "ae", "beraserif", "bookman",
144 "ccfonts", "chancery", "charter", "cmr", "cochineal", "crimson", "fourier",
145 "garamondx", "libertine", "libertineRoman", "libertine-type1", "lmodern", "mathdesign",
146 "mathpazo", "mathptmx", "MinionPro", "newcent", "NotoSerif-TLF", "tgbonum", "tgchorus",
147 "tgpagella", "tgschola", "tgtermes", "utopia", 0 };
149 const char * const known_sans_fonts[] = { "avant", "berasans", "biolinum",
150 "biolinum-type1", "cmbr", "cmss", "helvet", "iwona", "iwonac", "iwonal", "iwonalc",
151 "kurier", "kurierc", "kurierl", "kurierlc", "lmss", "NotoSans-TLF",
152 "tgadventor", "tgheros", "uop", 0 };
154 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
155 "courier", "lmtt", "luximono", "fourier", "libertineMono", "libertineMono-type1", "lmodern",
156 "mathpazo", "mathptmx", "newcent", "NotoMono-TLF", "tgcursor", "txtt", 0 };
158 const char * const known_math_fonts[] = { "eulervm", "newtxmath", 0};
160 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
161 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
162 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
163 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
164 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
166 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
167 "executivepaper", "legalpaper", "letterpaper", 0};
169 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
170 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
172 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
173 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
176 /// commands that can start an \if...\else...\endif sequence
177 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
178 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
179 "ifsidecap", "ifupgreek", 0};
181 const char * const known_basic_colors[] = {"black", "blue", "brown", "cyan",
182 "darkgray", "gray", "green", "lightgray", "lime", "magenta", "orange", "olive",
183 "pink", "purple", "red", "teal", "violet", "white", "yellow", 0};
185 const char * const known_basic_color_codes[] = {"#000000", "#0000ff", "#964B00", "#00ffff",
186 "#a9a9a9", "#808080", "#00ff00", "#d3d3d3", "#bfff00", "#ff00ff", "#ff7f00", "#808000",
187 "#ffc0cb", "#800080", "#ff0000", "#008080", "#8f00ff", "#ffffff", "#ffff00", 0};
189 /// conditional commands with three arguments like \@ifundefined{}{}{}
190 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
194 * Known file extensions for TeX files as used by \\includeonly
196 char const * const known_tex_extensions[] = {"tex", 0};
198 /// packages that work only in xetex
199 /// polyglossia is handled separately
200 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
201 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
202 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
204 /// packages that are automatically skipped if loaded by LyX
205 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
206 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color",
207 "float", "fontspec", "framed", "graphicx", "hhline", "ifthen", "longtable",
208 "makeidx", "minted", "multirow", "nomencl", "pdfpages", "prettyref", "refstyle",
209 "rotating", "rotfloat", "splitidx", "setspace", "subscript", "textcomp", "tipa",
210 "tipx", "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xcolor",
213 // codes used to remove packages that are loaded automatically by LyX.
214 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
215 const char package_beg_sep = '\001';
216 const char package_mid_sep = '\002';
217 const char package_end_sep = '\003';
220 // returns true if at least one of the options in what has been found
221 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
227 // the last language option is the document language (for babel and LyX)
228 // the last size option is the document font size
229 vector<string>::iterator it;
230 vector<string>::iterator position = opts.begin();
231 for (; *what; ++what) {
232 it = find(opts.begin(), opts.end(), *what);
233 if (it != opts.end()) {
234 if (it >= position) {
245 void delete_opt(vector<string> & opts, char const * const * what)
250 // remove found options from the list
251 // do this after handle_opt to avoid potential memory leaks
252 vector<string>::iterator it;
253 for (; *what; ++what) {
254 it = find(opts.begin(), opts.end(), *what);
255 if (it != opts.end())
262 * Split a package options string (keyval format) into a vector.
264 * authorformat=smallcaps,
266 * titleformat=colonsep,
267 * bibformat={tabular,ibidem,numbered}
269 vector<string> split_options(string const & input)
271 vector<string> options;
275 Token const & t = p.get_token();
276 if (t.asInput() == ",") {
277 options.push_back(trimSpaceAndEol(option));
279 } else if (t.asInput() == "=") {
282 if (p.next_token().asInput() == "{")
283 option += '{' + p.getArg('{', '}') + '}';
284 } else if (t.cat() != catSpace && t.cat() != catComment)
285 option += t.asInput();
289 options.push_back(trimSpaceAndEol(option));
296 * Retrieve a keyval option "name={value with=sign}" named \p name from
297 * \p options and return the value.
298 * The found option is also removed from \p options.
300 string process_keyval_opt(vector<string> & options, string name)
302 for (size_t i = 0; i < options.size(); ++i) {
303 vector<string> option;
304 split(options[i], option, '=');
305 if (option.size() < 2)
307 if (option[0] == name) {
308 options.erase(options.begin() + i);
309 option.erase(option.begin());
310 return join(option, "=");
316 } // anonymous namespace
320 * known polyglossia language names (including variants)
321 * FIXME: support spelling=old for german variants (german vs. ngerman LyX names etc)
323 const char * const Preamble::polyglossia_languages[] = {
324 "albanian", "american", "amharic", "ancient", "arabic", "armenian", "asturian", "australian",
325 "bahasai", "bahasam", "basque", "bengali", "brazil", "brazilian", "breton", "british", "bulgarian",
326 "catalan", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
327 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
328 "galician", "greek", "monotonic", "hebrew", "hindi",
329 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer",
330 "lao", "latin", "latvian", "lithuanian", "lsorbian", "magyar", "malayalam", "marathi",
331 "austrian", "newzealand", "german", "norsk", "nynorsk", "occitan",
332 "piedmontese", "polish", "polytonic", "portuges", "romanian", "romansh", "russian",
333 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovenian", "spanish", "swedish", "syriac",
334 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
335 "ukrainian", "urdu", "usorbian", "vietnamese", "welsh", 0};
336 // not yet supported by LyX: "korean", "nko"
339 * the same as polyglossia_languages with .lyx names
340 * please keep this in sync with polyglossia_languages line by line!
342 const char * const Preamble::coded_polyglossia_languages[] = {
343 "albanian", "american", "amharic", "ancientgreek", "arabic_arabi", "armenian", "asturian", "australian",
344 "bahasa", "bahasam", "basque", "bengali", "brazilian", "brazilian", "breton", "british", "bulgarian",
345 "catalan", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
346 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
347 "galician", "greek", "greek", "hebrew", "hindi",
348 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer",
349 "lao", "latin", "latvian", "lithuanian", "lowersorbian", "magyar", "malayalam", "marathi",
350 "naustrian","newzealand", "ngerman", "norsk", "nynorsk", "occitan",
351 "piedmontese", "polish", "polutonikogreek", "portuges", "romanian", "romansh", "russian",
352 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovene", "spanish", "swedish", "syriac",
353 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
354 "ukrainian", "urdu", "uppersorbian", "vietnamese", "welsh", 0};
355 // not yet supported by LyX: "korean-polyglossia", "nko"
358 bool Preamble::usePolyglossia() const
360 return h_use_non_tex_fonts && h_language_package == "default";
364 bool Preamble::indentParagraphs() const
366 return h_paragraph_separation == "indent";
370 bool Preamble::isPackageUsed(string const & package) const
372 return used_packages.find(package) != used_packages.end();
376 vector<string> Preamble::getPackageOptions(string const & package) const
378 map<string, vector<string> >::const_iterator it = used_packages.find(package);
379 if (it != used_packages.end())
381 return vector<string>();
385 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
387 auto_packages.insert(package);
391 void Preamble::addModule(string const & module)
393 used_modules.push_back(module);
397 void Preamble::suppressDate(bool suppress)
400 h_suppress_date = "true";
402 h_suppress_date = "false";
406 void Preamble::registerAuthor(std::string const & name)
408 Author author(from_utf8(name), empty_docstring());
409 author.setUsed(true);
410 authors_.record(author);
411 h_tracking_changes = "true";
412 h_output_changes = "true";
416 Author const & Preamble::getAuthor(std::string const & name) const
418 Author author(from_utf8(name), empty_docstring());
419 for (AuthorList::Authors::const_iterator it = authors_.begin();
420 it != authors_.end(); ++it)
423 static Author const dummy;
428 int Preamble::getSpecialTableColumnArguments(char c) const
430 map<char, int>::const_iterator it = special_columns_.find(c);
431 if (it == special_columns_.end())
437 void Preamble::add_package(string const & name, vector<string> & options)
439 // every package inherits the global options
440 if (used_packages.find(name) == used_packages.end())
441 used_packages[name] = split_options(h_options);
443 // Insert options passed via PassOptionsToPackage
444 for (auto const & p : extra_package_options_) {
445 if (p.first == name) {
446 vector<string> eo = getVectorFromString(p.second);
447 for (auto const & eoi : eo)
448 options.push_back(eoi);
452 vector<string> & v = used_packages[name];
453 v.insert(v.end(), options.begin(), options.end());
454 if (name == "jurabib") {
455 // Don't output the order argument (see the cite command
456 // handling code in text.cpp).
457 vector<string>::iterator end =
458 remove(options.begin(), options.end(), "natbiborder");
459 end = remove(options.begin(), end, "jurabiborder");
460 options.erase(end, options.end());
467 // Given is a string like "scaled=0.9" or "Scale=0.9", return 0.9 * 100
468 bool scale_as_percentage(string const & scale, string & percentage)
470 string::size_type pos = scale.find('=');
471 if (pos != string::npos) {
472 string value = scale.substr(pos + 1);
473 if (isStrDbl(value)) {
474 percentage = convert<string>(
475 static_cast<int>(100 * convert<double>(value)));
483 string remove_braces(string const & value)
487 if (value[0] == '{' && value[value.length()-1] == '}')
488 return value.substr(1, value.length()-2);
492 } // anonymous namespace
495 Preamble::Preamble() : one_language(true), explicit_babel(false),
496 title_layout_found(false), index_number(0), h_font_cjk_set(false),
497 h_use_microtype("false")
501 h_biblio_style = "plain";
502 h_bibtex_command = "default";
503 h_cite_engine = "basic";
504 h_cite_engine_type = "default";
506 h_defskip = "medskip";
507 h_dynamic_quotes = false;
510 h_fontencoding = "default";
511 h_font_roman[0] = "default";
512 h_font_roman[1] = "default";
513 h_font_sans[0] = "default";
514 h_font_sans[1] = "default";
515 h_font_typewriter[0] = "default";
516 h_font_typewriter[1] = "default";
517 h_font_math[0] = "auto";
518 h_font_math[1] = "auto";
519 h_font_default_family = "default";
520 h_use_non_tex_fonts = false;
522 h_font_osf = "false";
523 h_font_sf_scale[0] = "100";
524 h_font_sf_scale[1] = "100";
525 h_font_tt_scale[0] = "100";
526 h_font_tt_scale[1] = "100";
528 h_is_mathindent = "0";
529 h_math_numbering_side = "default";
530 h_graphics = "default";
531 h_default_output_format = "default";
532 h_html_be_strict = "false";
533 h_html_css_as_file = "0";
534 h_html_math_output = "0";
535 h_index[0] = "Index";
536 h_index_command = "default";
537 h_inputencoding = "auto";
538 h_justification = "true";
539 h_language = "english";
540 h_language_package = "none";
542 h_maintain_unincluded_children = "false";
546 h_output_changes = "false";
548 //h_output_sync_macro
549 h_papercolumns = "1";
550 h_paperfontsize = "default";
551 h_paperorientation = "portrait";
552 h_paperpagestyle = "default";
554 h_papersize = "default";
555 h_paragraph_indentation = "default";
556 h_paragraph_separation = "indent";
561 h_pdf_bookmarks = "0";
562 h_pdf_bookmarksnumbered = "0";
563 h_pdf_bookmarksopen = "0";
564 h_pdf_bookmarksopenlevel = "1";
565 h_pdf_breaklinks = "0";
566 h_pdf_pdfborder = "0";
567 h_pdf_colorlinks = "0";
568 h_pdf_backref = "section";
569 h_pdf_pdfusetitle = "0";
571 //h_pdf_quoted_options;
572 h_quotes_style = "english";
574 h_shortcut[0] = "idx";
575 h_spacing = "single";
576 h_save_transient_properties = "true";
577 h_suppress_date = "false";
578 h_textclass = "article";
580 h_tracking_changes = "false";
581 h_use_bibtopic = "false";
582 h_use_dash_ligatures = "true";
583 h_use_indices = "false";
584 h_use_geometry = "false";
585 h_use_default_options = "false";
586 h_use_hyperref = "false";
587 h_use_microtype = "false";
588 h_use_refstyle = false;
589 h_use_minted = false;
590 h_use_packages["amsmath"] = "1";
591 h_use_packages["amssymb"] = "0";
592 h_use_packages["cancel"] = "0";
593 h_use_packages["esint"] = "1";
594 h_use_packages["mhchem"] = "0";
595 h_use_packages["mathdots"] = "0";
596 h_use_packages["mathtools"] = "0";
597 h_use_packages["stackrel"] = "0";
598 h_use_packages["stmaryrd"] = "0";
599 h_use_packages["undertilde"] = "0";
603 void Preamble::handle_hyperref(vector<string> & options)
605 // FIXME swallow inputencoding changes that might surround the
606 // hyperref setup if it was written by LyX
607 h_use_hyperref = "true";
608 // swallow "unicode=true", since LyX does always write that
609 vector<string>::iterator it =
610 find(options.begin(), options.end(), "unicode=true");
611 if (it != options.end())
613 it = find(options.begin(), options.end(), "pdfusetitle");
614 if (it != options.end()) {
615 h_pdf_pdfusetitle = "1";
618 string bookmarks = process_keyval_opt(options, "bookmarks");
619 if (bookmarks == "true")
620 h_pdf_bookmarks = "1";
621 else if (bookmarks == "false")
622 h_pdf_bookmarks = "0";
623 if (h_pdf_bookmarks == "1") {
624 string bookmarksnumbered =
625 process_keyval_opt(options, "bookmarksnumbered");
626 if (bookmarksnumbered == "true")
627 h_pdf_bookmarksnumbered = "1";
628 else if (bookmarksnumbered == "false")
629 h_pdf_bookmarksnumbered = "0";
630 string bookmarksopen =
631 process_keyval_opt(options, "bookmarksopen");
632 if (bookmarksopen == "true")
633 h_pdf_bookmarksopen = "1";
634 else if (bookmarksopen == "false")
635 h_pdf_bookmarksopen = "0";
636 if (h_pdf_bookmarksopen == "1") {
637 string bookmarksopenlevel =
638 process_keyval_opt(options, "bookmarksopenlevel");
639 if (!bookmarksopenlevel.empty())
640 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
643 string breaklinks = process_keyval_opt(options, "breaklinks");
644 if (breaklinks == "true")
645 h_pdf_breaklinks = "1";
646 else if (breaklinks == "false")
647 h_pdf_breaklinks = "0";
648 string pdfborder = process_keyval_opt(options, "pdfborder");
649 if (pdfborder == "{0 0 0}")
650 h_pdf_pdfborder = "1";
651 else if (pdfborder == "{0 0 1}")
652 h_pdf_pdfborder = "0";
653 string backref = process_keyval_opt(options, "backref");
654 if (!backref.empty())
655 h_pdf_backref = backref;
656 string colorlinks = process_keyval_opt(options, "colorlinks");
657 if (colorlinks == "true")
658 h_pdf_colorlinks = "1";
659 else if (colorlinks == "false")
660 h_pdf_colorlinks = "0";
661 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
662 if (!pdfpagemode.empty())
663 h_pdf_pagemode = pdfpagemode;
664 string pdftitle = process_keyval_opt(options, "pdftitle");
665 if (!pdftitle.empty()) {
666 h_pdf_title = remove_braces(pdftitle);
668 string pdfauthor = process_keyval_opt(options, "pdfauthor");
669 if (!pdfauthor.empty()) {
670 h_pdf_author = remove_braces(pdfauthor);
672 string pdfsubject = process_keyval_opt(options, "pdfsubject");
673 if (!pdfsubject.empty())
674 h_pdf_subject = remove_braces(pdfsubject);
675 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
676 if (!pdfkeywords.empty())
677 h_pdf_keywords = remove_braces(pdfkeywords);
678 if (!options.empty()) {
679 if (!h_pdf_quoted_options.empty())
680 h_pdf_quoted_options += ',';
681 h_pdf_quoted_options += join(options, ",");
687 void Preamble::handle_geometry(vector<string> & options)
689 h_use_geometry = "true";
690 vector<string>::iterator it;
692 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
693 h_paperorientation = "landscape";
697 // keyval version: "paper=letter"
698 string paper = process_keyval_opt(options, "paper");
700 h_papersize = paper + "paper";
701 // alternative version: "letterpaper"
702 handle_opt(options, known_paper_sizes, h_papersize);
703 delete_opt(options, known_paper_sizes);
705 char const * const * margin = known_paper_margins;
706 for (; *margin; ++margin) {
707 string value = process_keyval_opt(options, *margin);
708 if (!value.empty()) {
709 int k = margin - known_paper_margins;
710 string name = known_coded_paper_margins[k];
711 h_margins += '\\' + name + ' ' + value + '\n';
717 void Preamble::handle_package(Parser &p, string const & name,
718 string const & opts, bool in_lyx_preamble,
721 vector<string> options = split_options(opts);
722 add_package(name, options);
724 if (is_known(name, known_xetex_packages)) {
726 h_use_non_tex_fonts = true;
727 registerAutomaticallyLoadedPackage("fontspec");
728 if (h_inputencoding == "auto")
729 p.setEncoding("UTF-8");
733 if (is_known(name, known_roman_fonts))
734 h_font_roman[0] = name;
736 if (name == "fourier") {
737 h_font_roman[0] = "utopia";
738 // when font uses real small capitals
739 if (opts == "expert")
743 if (name == "garamondx") {
744 h_font_roman[0] = "garamondx";
749 if (name == "libertine") {
750 h_font_roman[0] = "libertine";
751 // this automatically invokes biolinum
752 h_font_sans[0] = "biolinum";
753 // as well as libertineMono
754 h_font_typewriter[0] = "libertine-mono";
757 else if (opts == "lining")
758 h_font_osf = "false";
761 if (name == "libertineRoman" || name == "libertine-type1") {
762 h_font_roman[0] = "libertine";
763 // NOTE: contrary to libertine.sty, libertineRoman
764 // and libertine-type1 do not automatically invoke
765 // biolinum and libertineMono
766 if (opts == "lining")
767 h_font_osf = "false";
768 else if (opts == "osf")
772 if (name == "MinionPro") {
773 h_font_roman[0] = "minionpro";
774 if (opts.find("lf") != string::npos)
775 h_font_osf = "false";
778 if (opts.find("onlytext") != string::npos)
779 h_font_math[0] = "default";
781 h_font_math[0] = "auto";
784 if (name == "mathdesign") {
785 if (opts.find("charter") != string::npos)
786 h_font_roman[0] = "md-charter";
787 if (opts.find("garamond") != string::npos)
788 h_font_roman[0] = "md-garamond";
789 if (opts.find("utopia") != string::npos)
790 h_font_roman[0] = "md-utopia";
791 if (opts.find("expert") != string::npos) {
797 else if (name == "mathpazo")
798 h_font_roman[0] = "palatino";
800 else if (name == "mathptmx")
801 h_font_roman[0] = "times";
803 if (name == "crimson")
804 h_font_roman[0] = "cochineal";
806 if (name == "cochineal") {
807 h_font_roman[0] = "cochineal";
808 // cochineal can have several options, e.g. [proportional,osf]
809 string::size_type pos = opts.find("osf");
810 if (pos != string::npos)
814 if (name == "noto") {
815 // noto can have several options
817 h_font_roman[0] = "NotoSerif-TLF";
818 string::size_type pos = opts.find("rm");
819 if (pos != string::npos)
820 h_font_roman[0] = "NotoSerif-TLF";
821 pos = opts.find("sf");
822 if (pos != string::npos)
823 h_font_sans[0] = "NotoSans-TLF";
824 pos = opts.find("nott");
825 if (pos != string::npos) {
826 h_font_roman[0] = "NotoSerif-TLF";
827 h_font_sans[0] = "NotoSans-TLF";
829 // noto as typewriter is handled in handling of \ttdefault
830 // special cases are handled in handling of \rmdefault and \sfdefault
834 if (is_known(name, known_sans_fonts)) {
835 h_font_sans[0] = name;
836 if (options.size() >= 1) {
837 if (scale_as_percentage(opts, h_font_sf_scale[0]))
842 if (name == "biolinum" || name == "biolinum-type1") {
843 h_font_sans[0] = "biolinum";
844 // biolinum can have several options, e.g. [osf,scaled=0.97]
845 string::size_type pos = opts.find("osf");
846 if (pos != string::npos)
851 if (is_known(name, known_typewriter_fonts)) {
852 // fourier can be set as roman font _only_
853 // fourier as typewriter is handled in handling of \ttdefault
854 if (name != "fourier") {
855 h_font_typewriter[0] = name;
856 if (options.size() >= 1) {
857 if (scale_as_percentage(opts, h_font_tt_scale[0]))
863 if (name == "libertineMono" || name == "libertineMono-type1")
864 h_font_typewriter[0] = "libertine-mono";
866 // font uses old-style figure
871 if (is_known(name, known_math_fonts))
872 h_font_math[0] = name;
874 if (name == "newtxmath") {
876 h_font_math[0] = "newtxmath";
877 else if (opts == "garamondx")
878 h_font_math[0] = "garamondx-ntxm";
879 else if (opts == "libertine")
880 h_font_math[0] = "libertine-ntxm";
881 else if (opts == "minion")
882 h_font_math[0] = "minion-ntxm";
883 else if (opts == "cochineal")
884 h_font_math[0] = "cochineal-ntxm";
889 h_font_math[0] = "iwona-math";
891 if (name == "kurier")
893 h_font_math[0] = "kurier-math";
895 // after the detection and handling of special cases, we can remove the
896 // fonts, otherwise they would appear in the preamble, see bug #7856
897 if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
898 || is_known(name, known_typewriter_fonts) || is_known(name, known_math_fonts))
900 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
901 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
902 name == "esint" || name == "mhchem" || name == "mathdots" ||
903 name == "mathtools" || name == "stackrel" ||
904 name == "stmaryrd" || name == "undertilde")
905 h_use_packages[name] = "2";
907 else if (name == "babel") {
908 h_language_package = "default";
909 // One might think we would have to do nothing if babel is loaded
910 // without any options to prevent pollution of the preamble with this
911 // babel call in every roundtrip.
912 // But the user could have defined babel-specific things afterwards. So
913 // we need to keep it in the preamble to prevent cases like bug #7861.
915 // check if more than one option was used - used later for inputenc
916 if (options.begin() != options.end() - 1)
917 one_language = false;
918 // babel takes the last language of the option of its \usepackage
919 // call as document language. If there is no such language option, the
920 // last language in the documentclass options is used.
921 handle_opt(options, known_languages, h_language);
922 // translate the babel name to a LyX name
923 h_language = babel2lyx(h_language);
924 if (h_language == "japanese") {
925 // For Japanese, the encoding isn't indicated in the source
926 // file, and there's really not much we can do. We could
927 // 1) offer a list of possible encodings to choose from, or
928 // 2) determine the encoding of the file by inspecting it.
929 // For the time being, we leave the encoding alone so that
930 // we don't get iconv errors when making a wrong guess, and
931 // we will output a note at the top of the document
932 // explaining what to do.
933 Encoding const * const enc = encodings.fromIconvName(
934 p.getEncoding(), Encoding::japanese, false);
936 h_inputencoding = enc->name();
937 is_nonCJKJapanese = true;
938 // in this case babel can be removed from the preamble
939 registerAutomaticallyLoadedPackage("babel");
941 // If babel is called with options, LyX puts them by default into the
942 // document class options. This works for most languages, except
943 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
944 // perhaps in future others.
945 // Therefore keep the babel call as it is as the user might have
947 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
949 delete_opt(options, known_languages);
951 h_preamble << "\\usepackage{babel}\n";
952 explicit_babel = true;
956 else if (name == "polyglossia") {
957 h_language_package = "default";
958 h_default_output_format = "pdf4";
959 h_use_non_tex_fonts = true;
961 registerAutomaticallyLoadedPackage("xunicode");
962 if (h_inputencoding == "auto")
963 p.setEncoding("UTF-8");
966 else if (name == "CJK") {
967 // set the encoding to "auto" because it might be set to "default" by the babel handling
968 // and this would not be correct for CJK
969 if (h_inputencoding == "default")
970 h_inputencoding = "auto";
971 registerAutomaticallyLoadedPackage("CJK");
974 else if (name == "CJKutf8") {
975 h_inputencoding = "utf8-cjk";
976 p.setEncoding("UTF-8");
977 registerAutomaticallyLoadedPackage("CJKutf8");
980 else if (name == "fontenc") {
981 h_fontencoding = getStringFromVector(options, ",");
982 /* We could do the following for better round trip support,
983 * but this makes the document less portable, so I skip it:
984 if (h_fontencoding == lyxrc.fontenc)
985 h_fontencoding = "global";
990 else if (name == "inputenc" || name == "luainputenc") {
991 // h_inputencoding is only set when there is not more than one
992 // inputenc option because otherwise h_inputencoding must be
993 // set to "auto" (the default encoding of the document language)
994 // Therefore check that exactly one option is passed to inputenc.
995 // It is also only set when there is not more than one babel
997 if (!options.empty()) {
998 string const encoding = options.back();
999 Encoding const * const enc = encodings.fromLaTeXName(
1000 encoding, Encoding::inputenc, true);
1002 if (!detectEncoding)
1003 cerr << "Unknown encoding " << encoding
1004 << ". Ignoring." << std::endl;
1006 if (!enc->unsafe() && options.size() == 1 && one_language == true)
1007 h_inputencoding = enc->name();
1008 p.setEncoding(enc->iconvName());
1014 else if (name == "srcltx") {
1015 h_output_sync = "1";
1016 if (!opts.empty()) {
1017 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
1020 h_output_sync_macro = "\\usepackage{srcltx}";
1023 else if (is_known(name, known_old_language_packages)) {
1024 // known language packages from the times before babel
1025 // if they are found and not also babel, they will be used as
1026 // custom language package
1027 h_language_package = "\\usepackage{" + name + "}";
1030 else if (name == "lyxskak") {
1031 // ignore this and its options
1032 const char * const o[] = {"ps", "mover", 0};
1033 delete_opt(options, o);
1036 else if (is_known(name, known_lyx_packages) && options.empty()) {
1037 if (name == "splitidx")
1038 h_use_indices = "true";
1039 else if (name == "minted")
1040 h_use_minted = true;
1041 else if (name == "refstyle")
1042 h_use_refstyle = true;
1043 else if (name == "prettyref")
1044 h_use_refstyle = false;
1045 if (!in_lyx_preamble) {
1046 h_preamble << package_beg_sep << name
1047 << package_mid_sep << "\\usepackage{"
1049 if (p.next_token().cat() == catNewline ||
1050 (p.next_token().cat() == catSpace &&
1051 p.next_next_token().cat() == catNewline))
1053 h_preamble << package_end_sep;
1057 else if (name == "geometry")
1058 handle_geometry(options);
1060 else if (name == "subfig")
1061 ; // ignore this FIXME: Use the package separator mechanism instead
1063 else if (char const * const * where = is_known(name, known_languages))
1064 h_language = known_coded_languages[where - known_languages];
1066 else if (name == "natbib") {
1067 h_biblio_style = "plainnat";
1068 h_cite_engine = "natbib";
1069 h_cite_engine_type = "authoryear";
1070 vector<string>::iterator it =
1071 find(options.begin(), options.end(), "authoryear");
1072 if (it != options.end())
1075 it = find(options.begin(), options.end(), "numbers");
1076 if (it != options.end()) {
1077 h_cite_engine_type = "numerical";
1081 if (!options.empty())
1082 h_biblio_options = join(options, ",");
1085 else if (name == "biblatex") {
1086 h_biblio_style = "plainnat";
1087 h_cite_engine = "biblatex";
1088 h_cite_engine_type = "authoryear";
1090 vector<string>::iterator it =
1091 find(options.begin(), options.end(), "natbib");
1092 if (it != options.end()) {
1094 h_cite_engine = "biblatex-natbib";
1096 opt = process_keyval_opt(options, "natbib");
1098 h_cite_engine = "biblatex-natbib";
1100 opt = process_keyval_opt(options, "style");
1102 h_biblatex_citestyle = opt;
1103 h_biblatex_bibstyle = opt;
1105 opt = process_keyval_opt(options, "citestyle");
1107 h_biblatex_citestyle = opt;
1108 opt = process_keyval_opt(options, "bibstyle");
1110 h_biblatex_bibstyle = opt;
1112 opt = process_keyval_opt(options, "refsection");
1114 if (opt == "none" || opt == "part"
1115 || opt == "chapter" || opt == "section"
1116 || opt == "subsection")
1119 cerr << "Ignoring unkown refesection value '"
1122 if (!options.empty()) {
1123 h_biblio_options = join(options, ",");
1128 else if (name == "jurabib") {
1129 h_biblio_style = "jurabib";
1130 h_cite_engine = "jurabib";
1131 h_cite_engine_type = "authoryear";
1132 if (!options.empty())
1133 h_biblio_options = join(options, ",");
1136 else if (name == "bibtopic")
1137 h_use_bibtopic = "true";
1139 else if (name == "chapterbib")
1140 h_multibib = "child";
1142 else if (name == "hyperref")
1143 handle_hyperref(options);
1145 else if (name == "algorithm2e") {
1146 // Load "algorithm2e" module
1147 addModule("algorithm2e");
1148 // Add the package options to the global document options
1149 if (!options.empty()) {
1150 if (h_options.empty())
1151 h_options = join(options, ",");
1153 h_options += ',' + join(options, ",");
1156 else if (name == "microtype") {
1157 //we internally support only microtype without params
1158 if (options.empty())
1159 h_use_microtype = "true";
1161 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1164 else if (!in_lyx_preamble) {
1165 if (options.empty())
1166 h_preamble << "\\usepackage{" << name << '}';
1168 h_preamble << "\\usepackage[" << opts << "]{"
1172 if (p.next_token().cat() == catNewline ||
1173 (p.next_token().cat() == catSpace &&
1174 p.next_next_token().cat() == catNewline))
1178 // We need to do something with the options...
1179 if (!options.empty() && !detectEncoding)
1180 cerr << "Ignoring options '" << join(options, ",")
1181 << "' of package " << name << '.' << endl;
1183 // remove the whitespace
1188 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1191 Token t = p.get_token();
1192 if (t.cat() == catEscape &&
1193 is_known(t.cs(), known_if_commands))
1194 handle_if(p, in_lyx_preamble);
1196 if (!in_lyx_preamble)
1197 h_preamble << t.asInput();
1198 if (t.cat() == catEscape && t.cs() == "fi")
1205 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1207 if (contains(h_float_placement, "H"))
1208 registerAutomaticallyLoadedPackage("float");
1209 if (h_spacing != "single" && h_spacing != "default")
1210 registerAutomaticallyLoadedPackage("setspace");
1211 if (h_use_packages["amsmath"] == "2") {
1212 // amsbsy and amstext are already provided by amsmath
1213 registerAutomaticallyLoadedPackage("amsbsy");
1214 registerAutomaticallyLoadedPackage("amstext");
1217 // output the LyX file settings
1218 // Important: Keep the version formatting in sync with LyX and
1219 // lyx2lyx (bug 7951)
1220 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1221 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1222 << lyx_version_minor << '\n'
1223 << "\\lyxformat " << LYX_FORMAT << '\n'
1224 << "\\begin_document\n"
1225 << "\\begin_header\n"
1226 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1227 << "\\origin " << origin << "\n"
1228 << "\\textclass " << h_textclass << "\n";
1229 string const raw = subdoc ? empty_string() : h_preamble.str();
1231 os << "\\begin_preamble\n";
1232 for (string::size_type i = 0; i < raw.size(); ++i) {
1233 if (raw[i] == package_beg_sep) {
1234 // Here follows some package loading code that
1235 // must be skipped if the package is loaded
1237 string::size_type j = raw.find(package_mid_sep, i);
1238 if (j == string::npos)
1240 string::size_type k = raw.find(package_end_sep, j);
1241 if (k == string::npos)
1243 string const package = raw.substr(i + 1, j - i - 1);
1244 string const replacement = raw.substr(j + 1, k - j - 1);
1245 if (auto_packages.find(package) == auto_packages.end())
1251 os << "\n\\end_preamble\n";
1253 if (!h_options.empty())
1254 os << "\\options " << h_options << "\n";
1255 os << "\\use_default_options " << h_use_default_options << "\n";
1256 if (!used_modules.empty()) {
1257 os << "\\begin_modules\n";
1258 vector<string>::const_iterator const end = used_modules.end();
1259 vector<string>::const_iterator it = used_modules.begin();
1260 for (; it != end; ++it)
1262 os << "\\end_modules\n";
1264 if (!h_includeonlys.empty()) {
1265 os << "\\begin_includeonly\n";
1266 for (auto const & iofile : h_includeonlys)
1267 os << iofile << '\n';
1268 os << "\\end_includeonly\n";
1270 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1271 << "\\language " << h_language << "\n"
1272 << "\\language_package " << h_language_package << "\n"
1273 << "\\inputencoding " << h_inputencoding << "\n"
1274 << "\\fontencoding " << h_fontencoding << "\n"
1275 << "\\font_roman \"" << h_font_roman[0]
1276 << "\" \"" << h_font_roman[1] << "\"\n"
1277 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1278 << "\\font_typewriter \"" << h_font_typewriter[0]
1279 << "\" \"" << h_font_typewriter[1] << "\"\n"
1280 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1281 << "\\font_default_family " << h_font_default_family << "\n"
1282 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1283 << "\\font_sc " << h_font_sc << "\n"
1284 << "\\font_osf " << h_font_osf << "\n"
1285 << "\\font_sf_scale " << h_font_sf_scale[0]
1286 << ' ' << h_font_sf_scale[1] << '\n'
1287 << "\\font_tt_scale " << h_font_tt_scale[0]
1288 << ' ' << h_font_tt_scale[1] << '\n';
1289 if (!h_font_cjk.empty())
1290 os << "\\font_cjk " << h_font_cjk << '\n';
1291 os << "\\use_microtype " << h_use_microtype << '\n'
1292 << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
1293 << "\\graphics " << h_graphics << '\n'
1294 << "\\default_output_format " << h_default_output_format << "\n"
1295 << "\\output_sync " << h_output_sync << "\n";
1296 if (h_output_sync == "1")
1297 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1298 os << "\\bibtex_command " << h_bibtex_command << "\n"
1299 << "\\index_command " << h_index_command << "\n";
1300 if (!h_float_placement.empty())
1301 os << "\\float_placement " << h_float_placement << "\n";
1302 os << "\\paperfontsize " << h_paperfontsize << "\n"
1303 << "\\spacing " << h_spacing << "\n"
1304 << "\\use_hyperref " << h_use_hyperref << '\n';
1305 if (h_use_hyperref == "true") {
1306 if (!h_pdf_title.empty())
1307 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1308 if (!h_pdf_author.empty())
1309 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1310 if (!h_pdf_subject.empty())
1311 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1312 if (!h_pdf_keywords.empty())
1313 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1314 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1315 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1316 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1317 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1318 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1319 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1320 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1321 "\\pdf_backref " << h_pdf_backref << "\n"
1322 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1323 if (!h_pdf_pagemode.empty())
1324 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1325 if (!h_pdf_quoted_options.empty())
1326 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1328 os << "\\papersize " << h_papersize << "\n"
1329 << "\\use_geometry " << h_use_geometry << '\n';
1330 for (map<string, string>::const_iterator it = h_use_packages.begin();
1331 it != h_use_packages.end(); ++it)
1332 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1333 os << "\\cite_engine " << h_cite_engine << '\n'
1334 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1335 << "\\biblio_style " << h_biblio_style << "\n"
1336 << "\\use_bibtopic " << h_use_bibtopic << "\n";
1337 if (!h_biblio_options.empty())
1338 os << "\\biblio_options " << h_biblio_options << "\n";
1339 if (!h_biblatex_bibstyle.empty())
1340 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
1341 if (!h_biblatex_citestyle.empty())
1342 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
1343 if (!h_multibib.empty())
1344 os << "\\multibib " << h_multibib << "\n";
1345 os << "\\use_indices " << h_use_indices << "\n"
1346 << "\\paperorientation " << h_paperorientation << '\n'
1347 << "\\suppress_date " << h_suppress_date << '\n'
1348 << "\\justification " << h_justification << '\n'
1349 << "\\use_refstyle " << h_use_refstyle << '\n'
1350 << "\\use_minted " << h_use_minted << '\n';
1351 if (!h_fontcolor.empty())
1352 os << "\\fontcolor " << h_fontcolor << '\n';
1353 if (!h_notefontcolor.empty())
1354 os << "\\notefontcolor " << h_notefontcolor << '\n';
1355 if (!h_backgroundcolor.empty())
1356 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1357 if (!h_boxbgcolor.empty())
1358 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1359 if (index_number != 0)
1360 for (int i = 0; i < index_number; i++) {
1361 os << "\\index " << h_index[i] << '\n'
1362 << "\\shortcut " << h_shortcut[i] << '\n'
1363 << "\\color " << h_color << '\n'
1367 os << "\\index " << h_index[0] << '\n'
1368 << "\\shortcut " << h_shortcut[0] << '\n'
1369 << "\\color " << h_color << '\n'
1373 << "\\secnumdepth " << h_secnumdepth << "\n"
1374 << "\\tocdepth " << h_tocdepth << "\n"
1375 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1376 if (h_paragraph_separation == "skip")
1377 os << "\\defskip " << h_defskip << "\n";
1379 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1380 os << "\\is_math_indent " << h_is_mathindent << "\n";
1381 if (!h_mathindentation.empty())
1382 os << "\\math_indentation " << h_mathindentation << "\n";
1383 os << "\\math_numbering_side " << h_math_numbering_side << "\n";
1384 os << "\\quotes_style " << h_quotes_style << "\n"
1385 << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
1386 << "\\papercolumns " << h_papercolumns << "\n"
1387 << "\\papersides " << h_papersides << "\n"
1388 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1389 if (!h_listings_params.empty())
1390 os << "\\listings_params " << h_listings_params << "\n";
1391 os << "\\tracking_changes " << h_tracking_changes << "\n"
1392 << "\\output_changes " << h_output_changes << "\n"
1393 << "\\html_math_output " << h_html_math_output << "\n"
1394 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1395 << "\\html_be_strict " << h_html_be_strict << "\n"
1397 << "\\end_header\n\n"
1398 << "\\begin_body\n";
1403 void Preamble::parse(Parser & p, string const & forceclass,
1404 TeX2LyXDocClass & tc)
1406 // initialize fixed types
1407 special_columns_['D'] = 3;
1408 parse(p, forceclass, false, tc);
1412 void Preamble::parse(Parser & p, string const & forceclass,
1413 bool detectEncoding, TeX2LyXDocClass & tc)
1415 bool is_full_document = false;
1416 bool is_lyx_file = false;
1417 bool in_lyx_preamble = false;
1419 // determine whether this is a full document or a fragment for inclusion
1421 Token const & t = p.get_token();
1423 if (t.cat() == catEscape && t.cs() == "documentclass") {
1424 is_full_document = true;
1430 if (detectEncoding && !is_full_document)
1433 while (is_full_document && p.good()) {
1434 if (detectEncoding && h_inputencoding != "auto" &&
1435 h_inputencoding != "default")
1438 Token const & t = p.get_token();
1441 if (!detectEncoding)
1442 cerr << "t: " << t << '\n';
1448 if (!in_lyx_preamble &&
1449 (t.cat() == catLetter ||
1450 t.cat() == catSuper ||
1451 t.cat() == catSub ||
1452 t.cat() == catOther ||
1453 t.cat() == catMath ||
1454 t.cat() == catActive ||
1455 t.cat() == catBegin ||
1456 t.cat() == catEnd ||
1457 t.cat() == catAlign ||
1458 t.cat() == catParameter)) {
1459 h_preamble << t.cs();
1463 if (!in_lyx_preamble &&
1464 (t.cat() == catSpace || t.cat() == catNewline)) {
1465 h_preamble << t.asInput();
1469 if (t.cat() == catComment) {
1470 static regex const islyxfile("%% LyX .* created this file");
1471 static regex const usercommands("User specified LaTeX commands");
1473 string const comment = t.asInput();
1475 // magically switch encoding default if it looks like XeLaTeX
1476 static string const magicXeLaTeX =
1477 "% This document must be compiled with XeLaTeX ";
1478 if (comment.size() > magicXeLaTeX.size()
1479 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1480 && h_inputencoding == "auto") {
1481 if (!detectEncoding)
1482 cerr << "XeLaTeX comment found, switching to UTF8\n";
1483 h_inputencoding = "utf8";
1486 if (regex_search(comment, sub, islyxfile)) {
1488 in_lyx_preamble = true;
1489 } else if (is_lyx_file
1490 && regex_search(comment, sub, usercommands))
1491 in_lyx_preamble = false;
1492 else if (!in_lyx_preamble)
1493 h_preamble << t.asInput();
1497 if (t.cs() == "PassOptionsToPackage") {
1498 string const poptions = p.getArg('{', '}');
1499 string const package = p.verbatim_item();
1500 extra_package_options_.insert(make_pair(package, poptions));
1504 if (t.cs() == "pagestyle") {
1505 h_paperpagestyle = p.verbatim_item();
1509 if (t.cs() == "setdefaultlanguage") {
1511 // We don't yet care about non-language variant options
1512 // because LyX doesn't support this yet, see bug #8214
1514 string langopts = p.getOpt();
1515 // check if the option contains a variant, if yes, extract it
1516 string::size_type pos_var = langopts.find("variant");
1517 string::size_type i = langopts.find(',', pos_var);
1518 string::size_type k = langopts.find('=', pos_var);
1519 if (pos_var != string::npos){
1521 if (i == string::npos)
1522 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1524 variant = langopts.substr(k + 1, i - k - 1);
1525 h_language = variant;
1529 h_language = p.verbatim_item();
1530 //finally translate the poyglossia name to a LyX name
1531 h_language = polyglossia2lyx(h_language);
1535 if (t.cs() == "setotherlanguage") {
1536 // We don't yet care about the option because LyX doesn't
1537 // support this yet, see bug #8214
1538 p.hasOpt() ? p.getOpt() : string();
1543 if (t.cs() == "setmainfont") {
1544 // we don't care about the option
1545 p.hasOpt() ? p.getOpt() : string();
1546 h_font_roman[1] = p.getArg('{', '}');
1550 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1551 // LyX currently only supports the scale option
1554 string fontopts = p.getArg('[', ']');
1555 // check if the option contains a scaling, if yes, extract it
1556 string::size_type pos = fontopts.find("Scale");
1557 if (pos != string::npos) {
1558 string::size_type i = fontopts.find(',', pos);
1559 if (i == string::npos)
1560 scale_as_percentage(fontopts.substr(pos + 1), scale);
1562 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1565 if (t.cs() == "setsansfont") {
1567 h_font_sf_scale[1] = scale;
1568 h_font_sans[1] = p.getArg('{', '}');
1571 h_font_tt_scale[1] = scale;
1572 h_font_typewriter[1] = p.getArg('{', '}');
1577 if (t.cs() == "date") {
1578 string argument = p.getArg('{', '}');
1579 if (argument.empty())
1580 h_suppress_date = "true";
1582 h_preamble << t.asInput() << '{' << argument << '}';
1586 if (t.cs() == "color") {
1587 string const space =
1588 (p.hasOpt() ? p.getOpt() : string());
1589 string argument = p.getArg('{', '}');
1590 // check the case that a standard color is used
1591 if (space.empty() && is_known(argument, known_basic_colors)) {
1592 h_fontcolor = rgbcolor2code(argument);
1593 registerAutomaticallyLoadedPackage("color");
1594 } else if (space.empty() && argument == "document_fontcolor")
1595 registerAutomaticallyLoadedPackage("color");
1596 // check the case that LyX's document_fontcolor is defined
1597 // but not used for \color
1599 h_preamble << t.asInput();
1601 h_preamble << space;
1602 h_preamble << '{' << argument << '}';
1603 // the color might already be set because \definecolor
1604 // is parsed before this
1610 if (t.cs() == "pagecolor") {
1611 string argument = p.getArg('{', '}');
1612 // check the case that a standard color is used
1613 if (is_known(argument, known_basic_colors)) {
1614 h_backgroundcolor = rgbcolor2code(argument);
1615 } else if (argument == "page_backgroundcolor")
1616 registerAutomaticallyLoadedPackage("color");
1617 // check the case that LyX's page_backgroundcolor is defined
1618 // but not used for \pagecolor
1620 h_preamble << t.asInput() << '{' << argument << '}';
1621 // the color might already be set because \definecolor
1622 // is parsed before this
1623 h_backgroundcolor = "";
1628 if (t.cs() == "makeatletter") {
1629 // LyX takes care of this
1630 p.setCatcode('@', catLetter);
1634 if (t.cs() == "makeatother") {
1635 // LyX takes care of this
1636 p.setCatcode('@', catOther);
1640 if (t.cs() == "makeindex") {
1641 // LyX will re-add this if a print index command is found
1646 if (t.cs() == "newindex") {
1647 string const indexname = p.getArg('[', ']');
1648 string const shortcut = p.verbatim_item();
1649 if (!indexname.empty())
1650 h_index[index_number] = indexname;
1652 h_index[index_number] = shortcut;
1653 h_shortcut[index_number] = shortcut;
1659 if (t.cs() == "addbibresource") {
1660 biblatex_bibliographies.push_back(removeExtension(p.getArg('{', '}')));
1664 if (t.cs() == "bibliography") {
1665 vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
1666 biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
1670 if (t.cs() == "RS@ifundefined") {
1671 string const name = p.verbatim_item();
1672 string const body1 = p.verbatim_item();
1673 string const body2 = p.verbatim_item();
1674 // only non-lyxspecific stuff
1675 if (in_lyx_preamble &&
1676 (name == "subsecref" || name == "thmref" || name == "lemref"))
1680 ss << '\\' << t.cs();
1681 ss << '{' << name << '}'
1682 << '{' << body1 << '}'
1683 << '{' << body2 << '}';
1684 h_preamble << ss.str();
1689 if (t.cs() == "AtBeginDocument") {
1690 string const name = p.verbatim_item();
1691 // only non-lyxspecific stuff
1692 if (in_lyx_preamble &&
1693 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
1694 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
1695 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
1696 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
1697 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
1698 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
1699 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
1700 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
1701 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
1702 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
1703 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
1704 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
1705 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
1706 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
1707 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
1711 ss << '\\' << t.cs();
1712 ss << '{' << name << '}';
1713 h_preamble << ss.str();
1718 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1719 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1720 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1721 || t.cs() == "DeclareRobustCommand"
1722 || t.cs() == "DeclareRobustCommandx"
1723 || t.cs() == "ProvideTextCommandDefault"
1724 || t.cs() == "DeclareMathAccent") {
1726 if (p.next_token().character() == '*') {
1730 string const name = p.verbatim_item();
1731 string const opt1 = p.getFullOpt();
1732 string const opt2 = p.getFullOpt();
1733 string const body = p.verbatim_item();
1734 // store the in_lyx_preamble setting
1735 bool const was_in_lyx_preamble = in_lyx_preamble;
1737 if (name == "\\rmdefault")
1738 if (is_known(body, known_roman_fonts)) {
1739 h_font_roman[0] = body;
1741 in_lyx_preamble = true;
1743 if (name == "\\sfdefault")
1744 if (is_known(body, known_sans_fonts)) {
1745 h_font_sans[0] = body;
1747 in_lyx_preamble = true;
1749 if (name == "\\ttdefault")
1750 if (is_known(body, known_typewriter_fonts)) {
1751 h_font_typewriter[0] = body;
1753 in_lyx_preamble = true;
1755 if (name == "\\familydefault") {
1756 string family = body;
1757 // remove leading "\"
1758 h_font_default_family = family.erase(0,1);
1760 in_lyx_preamble = true;
1763 // remove LyX-specific definitions that are re-added by LyX
1765 // \lyxline is an ancient command that is converted by tex2lyx into
1766 // a \rule therefore remove its preamble code
1767 if (name == "\\lyxdot" || name == "\\lyxarrow"
1768 || name == "\\lyxline" || name == "\\LyX") {
1770 in_lyx_preamble = true;
1773 // Add the command to the known commands
1774 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1776 // only non-lyxspecific stuff
1777 if (!in_lyx_preamble) {
1779 ss << '\\' << t.cs();
1782 ss << '{' << name << '}' << opt1 << opt2
1783 << '{' << body << "}";
1784 h_preamble << ss.str();
1786 ostream & out = in_preamble ? h_preamble : os;
1787 out << "\\" << t.cs() << "{" << name << "}"
1788 << opts << "{" << body << "}";
1791 // restore the in_lyx_preamble setting
1792 in_lyx_preamble = was_in_lyx_preamble;
1796 if (t.cs() == "documentclass") {
1797 vector<string>::iterator it;
1798 vector<string> opts = split_options(p.getArg('[', ']'));
1799 handle_opt(opts, known_fontsizes, h_paperfontsize);
1800 delete_opt(opts, known_fontsizes);
1801 // delete "pt" at the end
1802 string::size_type i = h_paperfontsize.find("pt");
1803 if (i != string::npos)
1804 h_paperfontsize.erase(i);
1805 // The documentclass options are always parsed before the options
1806 // of the babel call so that a language cannot overwrite the babel
1808 handle_opt(opts, known_languages, h_language);
1809 delete_opt(opts, known_languages);
1812 if ((it = find(opts.begin(), opts.end(), "fleqn"))
1814 h_is_mathindent = "1";
1817 // formula numbering side
1818 if ((it = find(opts.begin(), opts.end(), "leqno"))
1820 h_math_numbering_side = "left";
1823 else if ((it = find(opts.begin(), opts.end(), "reqno"))
1825 h_math_numbering_side = "right";
1829 // paper orientation
1830 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1831 h_paperorientation = "landscape";
1835 if ((it = find(opts.begin(), opts.end(), "oneside"))
1840 if ((it = find(opts.begin(), opts.end(), "twoside"))
1846 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1848 h_papercolumns = "1";
1851 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1853 h_papercolumns = "2";
1857 // some size options are known to any document classes, other sizes
1858 // are handled by the \geometry command of the geometry package
1859 handle_opt(opts, known_class_paper_sizes, h_papersize);
1860 delete_opt(opts, known_class_paper_sizes);
1861 // the remaining options
1862 h_options = join(opts, ",");
1863 // FIXME This does not work for classes that have a
1864 // different name in LyX than in LaTeX
1865 h_textclass = p.getArg('{', '}');
1870 if (t.cs() == "usepackage") {
1871 string const options = p.getArg('[', ']');
1872 string const name = p.getArg('{', '}');
1873 vector<string> vecnames;
1874 split(name, vecnames, ',');
1875 vector<string>::const_iterator it = vecnames.begin();
1876 vector<string>::const_iterator end = vecnames.end();
1877 for (; it != end; ++it)
1878 handle_package(p, trimSpaceAndEol(*it), options,
1879 in_lyx_preamble, detectEncoding);
1883 if (t.cs() == "inputencoding") {
1884 string const encoding = p.getArg('{','}');
1885 Encoding const * const enc = encodings.fromLaTeXName(
1886 encoding, Encoding::inputenc, true);
1888 if (!detectEncoding)
1889 cerr << "Unknown encoding " << encoding
1890 << ". Ignoring." << std::endl;
1893 h_inputencoding = enc->name();
1894 p.setEncoding(enc->iconvName());
1899 if (t.cs() == "newenvironment") {
1900 string const name = p.getArg('{', '}');
1901 string const opt1 = p.getFullOpt();
1902 string const opt2 = p.getFullOpt();
1903 string const beg = p.verbatim_item();
1904 string const end = p.verbatim_item();
1905 if (!in_lyx_preamble) {
1906 h_preamble << "\\newenvironment{" << name
1907 << '}' << opt1 << opt2 << '{'
1908 << beg << "}{" << end << '}';
1910 add_known_environment(name, opt1, !opt2.empty(),
1911 from_utf8(beg), from_utf8(end));
1915 if (t.cs() == "newtheorem") {
1917 if (p.next_token().character() == '*') {
1921 string const name = p.getArg('{', '}');
1922 string const opt1 = p.getFullOpt();
1923 string const opt2 = p.getFullOpt();
1924 string const body = p.verbatim_item();
1925 string const opt3 = p.getFullOpt();
1926 string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
1928 string const complete = cmd + "{" + name + '}' +
1929 opt1 + opt2 + '{' + body + '}' + opt3;
1931 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
1933 if (!in_lyx_preamble)
1934 h_preamble << complete;
1938 if (t.cs() == "def") {
1939 string name = p.get_token().cs();
1940 // In fact, name may be more than the name:
1941 // In the test case of bug 8116
1942 // name == "csname SF@gobble@opt \endcsname".
1943 // Therefore, we need to use asInput() instead of cs().
1944 while (p.next_token().cat() != catBegin)
1945 name += p.get_token().asInput();
1946 if (!in_lyx_preamble)
1947 h_preamble << "\\def\\" << name << '{'
1948 << p.verbatim_item() << "}";
1952 if (t.cs() == "newcolumntype") {
1953 string const name = p.getArg('{', '}');
1954 trimSpaceAndEol(name);
1956 string opts = p.getOpt();
1957 if (!opts.empty()) {
1958 istringstream is(string(opts, 1));
1961 special_columns_[name[0]] = nargs;
1962 h_preamble << "\\newcolumntype{" << name << "}";
1964 h_preamble << "[" << nargs << "]";
1965 h_preamble << "{" << p.verbatim_item() << "}";
1969 if (t.cs() == "setcounter") {
1970 string const name = p.getArg('{', '}');
1971 string const content = p.getArg('{', '}');
1972 if (name == "secnumdepth")
1973 h_secnumdepth = content;
1974 else if (name == "tocdepth")
1975 h_tocdepth = content;
1977 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1981 if (t.cs() == "setlength") {
1982 string const name = p.verbatim_item();
1983 string const content = p.verbatim_item();
1984 // the paragraphs are only not indented when \parindent is set to zero
1985 if (name == "\\parindent" && content != "") {
1986 if (content[0] == '0')
1987 h_paragraph_separation = "skip";
1989 h_paragraph_indentation = translate_len(content);
1990 } else if (name == "\\parskip") {
1991 if (content == "\\smallskipamount")
1992 h_defskip = "smallskip";
1993 else if (content == "\\medskipamount")
1994 h_defskip = "medskip";
1995 else if (content == "\\bigskipamount")
1996 h_defskip = "bigskip";
1998 h_defskip = translate_len(content);
1999 } else if (name == "\\mathindent") {
2000 h_mathindentation = translate_len(content);
2002 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2006 if (t.cs() == "onehalfspacing") {
2007 h_spacing = "onehalf";
2011 if (t.cs() == "doublespacing") {
2012 h_spacing = "double";
2016 if (t.cs() == "setstretch") {
2017 h_spacing = "other " + p.verbatim_item();
2021 if (t.cs() == "synctex") {
2022 // the scheme is \synctex=value
2023 // where value can only be "1" or "-1"
2024 h_output_sync = "1";
2025 // there can be any character behind the value (e.g. a linebreak or a '\'
2026 // therefore we extract it char by char
2028 string value = p.get_token().asInput();
2030 value += p.get_token().asInput();
2031 h_output_sync_macro = "\\synctex=" + value;
2035 if (t.cs() == "begin") {
2036 string const name = p.getArg('{', '}');
2037 if (name == "document")
2039 h_preamble << "\\begin{" << name << "}";
2043 if (t.cs() == "geometry") {
2044 vector<string> opts = split_options(p.getArg('{', '}'));
2045 handle_geometry(opts);
2049 if (t.cs() == "definecolor") {
2050 string const color = p.getArg('{', '}');
2051 string const space = p.getArg('{', '}');
2052 string const value = p.getArg('{', '}');
2053 if (color == "document_fontcolor" && space == "rgb") {
2054 RGBColor c(RGBColorFromLaTeX(value));
2055 h_fontcolor = X11hexname(c);
2056 } else if (color == "note_fontcolor" && space == "rgb") {
2057 RGBColor c(RGBColorFromLaTeX(value));
2058 h_notefontcolor = X11hexname(c);
2059 } else if (color == "page_backgroundcolor" && space == "rgb") {
2060 RGBColor c(RGBColorFromLaTeX(value));
2061 h_backgroundcolor = X11hexname(c);
2062 } else if (color == "shadecolor" && space == "rgb") {
2063 RGBColor c(RGBColorFromLaTeX(value));
2064 h_boxbgcolor = X11hexname(c);
2066 h_preamble << "\\definecolor{" << color
2067 << "}{" << space << "}{" << value
2073 if (t.cs() == "bibliographystyle") {
2074 h_biblio_style = p.verbatim_item();
2078 if (t.cs() == "jurabibsetup") {
2079 // FIXME p.getArg('{', '}') is most probably wrong (it
2080 // does not handle nested braces).
2081 // Use p.verbatim_item() instead.
2082 vector<string> jurabibsetup =
2083 split_options(p.getArg('{', '}'));
2084 // add jurabibsetup to the jurabib package options
2085 add_package("jurabib", jurabibsetup);
2086 if (!jurabibsetup.empty()) {
2087 h_preamble << "\\jurabibsetup{"
2088 << join(jurabibsetup, ",") << '}';
2093 if (t.cs() == "hypersetup") {
2094 vector<string> hypersetup =
2095 split_options(p.verbatim_item());
2096 // add hypersetup to the hyperref package options
2097 handle_hyperref(hypersetup);
2098 if (!hypersetup.empty()) {
2099 h_preamble << "\\hypersetup{"
2100 << join(hypersetup, ",") << '}';
2105 if (t.cs() == "includeonly") {
2106 vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
2107 for (auto & iofile : includeonlys) {
2108 string filename(normalize_filename(iofile));
2109 string const path = getMasterFilePath(true);
2110 // We want to preserve relative/absolute filenames,
2111 // therefore path is only used for testing
2112 if (!makeAbsPath(filename, path).exists()) {
2113 // The file extension is probably missing.
2114 // Now try to find it out.
2115 string const tex_name =
2116 find_file(filename, path,
2117 known_tex_extensions);
2118 if (!tex_name.empty())
2119 filename = tex_name;
2122 if (makeAbsPath(filename, path).exists())
2123 fix_child_filename(filename);
2125 cerr << "Warning: Could not find included file '"
2126 << filename << "'." << endl;
2127 outname = changeExtension(filename, "lyx");
2128 h_includeonlys.push_back(outname);
2133 if (is_known(t.cs(), known_if_3arg_commands)) {
2134 // prevent misparsing of \usepackage if it is used
2135 // as an argument (see e.g. our own output of
2136 // \@ifundefined above)
2137 string const arg1 = p.verbatim_item();
2138 string const arg2 = p.verbatim_item();
2139 string const arg3 = p.verbatim_item();
2140 // test case \@ifundefined{date}{}{\date{}}
2141 if (t.cs() == "@ifundefined" && arg1 == "date" &&
2142 arg2.empty() && arg3 == "\\date{}") {
2143 h_suppress_date = "true";
2144 // older tex2lyx versions did output
2145 // \@ifundefined{definecolor}{\usepackage{color}}{}
2146 } else if (t.cs() == "@ifundefined" &&
2147 arg1 == "definecolor" &&
2148 arg2 == "\\usepackage{color}" &&
2150 if (!in_lyx_preamble)
2151 h_preamble << package_beg_sep
2154 << "\\@ifundefined{definecolor}{color}{}"
2157 //\@ifundefined{showcaptionsetup}{}{%
2158 // \PassOptionsToPackage{caption=false}{subfig}}
2159 // that LyX uses for subfloats
2160 } else if (t.cs() == "@ifundefined" &&
2161 arg1 == "showcaptionsetup" && arg2.empty()
2162 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
2164 } else if (!in_lyx_preamble) {
2165 h_preamble << t.asInput()
2166 << '{' << arg1 << '}'
2167 << '{' << arg2 << '}'
2168 << '{' << arg3 << '}';
2173 if (is_known(t.cs(), known_if_commands)) {
2174 // must not parse anything in conditional code, since
2175 // LyX would output the parsed contents unconditionally
2176 if (!in_lyx_preamble)
2177 h_preamble << t.asInput();
2178 handle_if(p, in_lyx_preamble);
2182 if (!t.cs().empty() && !in_lyx_preamble) {
2183 h_preamble << '\\' << t.cs();
2188 // remove the whitespace
2191 // Force textclass if the user wanted it
2192 if (!forceclass.empty())
2193 h_textclass = forceclass;
2194 tc.setName(h_textclass);
2195 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2196 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2199 if (h_papersides.empty()) {
2202 h_papersides = ss.str();
2205 // If the CJK package is used we cannot set the document language from
2206 // the babel options. Instead, we guess which language is used most
2207 // and set this one.
2208 default_language = h_language;
2209 if (is_full_document &&
2210 (auto_packages.find("CJK") != auto_packages.end() ||
2211 auto_packages.find("CJKutf8") != auto_packages.end())) {
2213 h_language = guessLanguage(p, default_language);
2215 if (explicit_babel && h_language != default_language) {
2216 // We set the document language to a CJK language,
2217 // but babel is explicitly called in the user preamble
2218 // without options. LyX will not add the default
2219 // language to the document options if it is either
2220 // english, or no text is set as default language.
2221 // Therefore we need to add a language option explicitly.
2222 // FIXME: It would be better to remove all babel calls
2223 // from the user preamble, but this is difficult
2224 // without re-introducing bug 7861.
2225 if (h_options.empty())
2226 h_options = lyx2babel(default_language);
2228 h_options += ',' + lyx2babel(default_language);
2232 // Finally, set the quote style.
2233 // LyX knows the following quotes styles:
2234 // british, cjk, cjkangle, danish, english, french, german,
2235 // polish, russian, swedish and swiss
2236 // conversion list taken from
2237 // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
2238 // (quotes for kazakh are unknown)
2240 if (is_known(h_language, known_british_quotes_languages))
2241 h_quotes_style = "british";
2243 else if (is_known(h_language, known_cjk_quotes_languages))
2244 h_quotes_style = "cjk";
2246 else if (is_known(h_language, known_cjkangle_quotes_languages))
2247 h_quotes_style = "cjkangle";
2249 else if (is_known(h_language, known_danish_quotes_languages))
2250 h_quotes_style = "danish";
2252 else if (is_known(h_language, known_french_quotes_languages))
2253 h_quotes_style = "french";
2255 else if (is_known(h_language, known_german_quotes_languages))
2256 h_quotes_style = "german";
2258 else if (is_known(h_language, known_polish_quotes_languages))
2259 h_quotes_style = "polish";
2261 else if (is_known(h_language, known_russian_quotes_languages))
2262 h_quotes_style = "russian";
2264 else if (is_known(h_language, known_swedish_quotes_languages))
2265 h_quotes_style = "swedish";
2267 else if (is_known(h_language, known_swiss_quotes_languages))
2268 h_quotes_style = "swiss";
2270 else if (is_known(h_language, known_english_quotes_languages))
2271 h_quotes_style = "english";
2275 string Preamble::parseEncoding(Parser & p, string const & forceclass)
2277 TeX2LyXDocClass dummy;
2278 parse(p, forceclass, true, dummy);
2279 if (h_inputencoding != "auto" && h_inputencoding != "default")
2280 return h_inputencoding;
2285 string babel2lyx(string const & language)
2287 char const * const * where = is_known(language, known_languages);
2289 return known_coded_languages[where - known_languages];
2294 string lyx2babel(string const & language)
2296 char const * const * where = is_known(language, known_coded_languages);
2298 return known_languages[where - known_coded_languages];
2303 string Preamble::polyglossia2lyx(string const & language)
2305 char const * const * where = is_known(language, polyglossia_languages);
2307 return coded_polyglossia_languages[where - polyglossia_languages];
2312 string rgbcolor2code(string const & name)
2314 char const * const * where = is_known(name, known_basic_colors);
2316 // "red", "green" etc
2317 return known_basic_color_codes[where - known_basic_colors];
2319 // "255,0,0", "0,255,0" etc
2320 RGBColor c(RGBColorFromLaTeX(name));
2321 return X11hexname(c);