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 danish quotes (.lyx names)
92 const char * const known_danish_quotes_languages[] = {"danish", 0};
94 /// languages with english quotes (.lyx names)
95 const char * const known_english_quotes_languages[] = {"american", "australian",
96 "bahasa", "bahasam", "brazilian", "canadian", "chinese-simplified", "english",
97 "esperanto", "hebrew", "irish", "korean", "newzealand", "portuguese", "scottish",
100 /// languages with french quotes (.lyx names)
101 const char * const known_french_quotes_languages[] = {"albanian",
102 "arabic_arabi", "arabic_arabtex", "asturian", "basque", "canadien", "catalan",
103 "french", "friulan", "galician", "greek", "italian", "norsk", "nynorsk",
104 "piedmontese", "polutonikogreek", "russian", "spanish", "spanish-mexico",
105 "turkish", "turkmen", "ukrainian", "vietnamese", 0};
107 /// languages with german quotes (.lyx names)
108 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
109 "czech", "german", "georgian", "icelandic", "lithuanian", "lowersorbian", "macedonian",
110 "naustrian", "ngerman", "romansh", "serbian", "serbian-latin", "slovak", "slovene",
113 /// languages with polish quotes (.lyx names)
114 const char * const known_polish_quotes_languages[] = {"afrikaans", "bosnian", "croatian",
115 "dutch", "estonian", "magyar", "polish", "romanian", 0};
117 /// languages with swedish quotes (.lyx names)
118 const char * const known_swedish_quotes_languages[] = {"finnish",
121 /// known language packages from the times before babel
122 const char * const known_old_language_packages[] = {"french", "frenchle",
123 "frenchpro", "german", "ngerman", "pmfrench", 0};
125 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
127 const char * const known_roman_fonts[] = { "ae", "beraserif", "bookman",
128 "ccfonts", "chancery", "charter", "cmr", "cochineal", "crimson", "fourier",
129 "garamondx", "libertine", "libertine-type1", "lmodern", "mathdesign", "mathpazo",
130 "mathptmx", "newcent", "NotoSerif-TLF", "tgbonum", "tgchorus", "tgpagella", "tgschola",
131 "tgtermes", "utopia", 0};
133 const char * const known_sans_fonts[] = { "avant", "berasans", "biolinum-type1",
134 "cmbr", "cmss", "helvet", "iwona", "iwonac", "iwonal", "iwonalc", "kurier",
135 "kurierc", "kurierl", "kurierlc", "lmss", "NotoSans-TLF", "tgadventor", "tgheros", 0};
137 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
138 "courier", "lmtt", "luximono", "fourier", "libertineMono-type1", "lmodern",
139 "mathpazo", "mathptmx", "newcent", "NotoMono-TLF", "tgcursor", "txtt", 0};
141 const char * const known_math_fonts[] = { "eulervm", "newtxmath", 0};
143 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
144 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
145 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
146 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
147 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
149 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
150 "executivepaper", "legalpaper", "letterpaper", 0};
152 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
153 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
155 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
156 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
159 /// commands that can start an \if...\else...\endif sequence
160 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
161 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
162 "ifsidecap", "ifupgreek", 0};
164 const char * const known_basic_colors[] = {"black", "blue", "brown", "cyan",
165 "darkgray", "gray", "green", "lightgray", "lime", "magenta", "orange", "olive",
166 "pink", "purple", "red", "teal", "violet", "white", "yellow", 0};
168 const char * const known_basic_color_codes[] = {"#000000", "#0000ff", "#964B00", "#00ffff",
169 "#a9a9a9", "#808080", "#00ff00", "#d3d3d3", "#bfff00", "#ff00ff", "#ff7f00", "#808000",
170 "#ffc0cb", "#800080", "#ff0000", "#008080", "#8f00ff", "#ffffff", "#ffff00", 0};
172 /// conditional commands with three arguments like \@ifundefined{}{}{}
173 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
177 * Known file extensions for TeX files as used by \\includeonly
179 char const * const known_tex_extensions[] = {"tex", 0};
181 /// packages that work only in xetex
182 /// polyglossia is handled separately
183 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
184 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
185 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
187 /// packages that are automatically skipped if loaded by LyX
188 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
189 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color",
190 "float", "fontspec", "framed", "graphicx", "hhline", "ifthen", "longtable",
191 "makeidx", "minted", "multirow", "nomencl", "pdfpages", "prettyref", "refstyle",
192 "rotating", "rotfloat", "splitidx", "setspace", "subscript", "textcomp", "tipa",
193 "tipx", "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xcolor",
196 // codes used to remove packages that are loaded automatically by LyX.
197 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
198 const char package_beg_sep = '\001';
199 const char package_mid_sep = '\002';
200 const char package_end_sep = '\003';
203 // returns true if at least one of the options in what has been found
204 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
210 // the last language option is the document language (for babel and LyX)
211 // the last size option is the document font size
212 vector<string>::iterator it;
213 vector<string>::iterator position = opts.begin();
214 for (; *what; ++what) {
215 it = find(opts.begin(), opts.end(), *what);
216 if (it != opts.end()) {
217 if (it >= position) {
228 void delete_opt(vector<string> & opts, char const * const * what)
233 // remove found options from the list
234 // do this after handle_opt to avoid potential memory leaks
235 vector<string>::iterator it;
236 for (; *what; ++what) {
237 it = find(opts.begin(), opts.end(), *what);
238 if (it != opts.end())
245 * Split a package options string (keyval format) into a vector.
247 * authorformat=smallcaps,
249 * titleformat=colonsep,
250 * bibformat={tabular,ibidem,numbered}
252 vector<string> split_options(string const & input)
254 vector<string> options;
258 Token const & t = p.get_token();
259 if (t.asInput() == ",") {
260 options.push_back(trimSpaceAndEol(option));
262 } else if (t.asInput() == "=") {
265 if (p.next_token().asInput() == "{")
266 option += '{' + p.getArg('{', '}') + '}';
267 } else if (t.cat() != catSpace && t.cat() != catComment)
268 option += t.asInput();
272 options.push_back(trimSpaceAndEol(option));
279 * Retrieve a keyval option "name={value with=sign}" named \p name from
280 * \p options and return the value.
281 * The found option is also removed from \p options.
283 string process_keyval_opt(vector<string> & options, string name)
285 for (size_t i = 0; i < options.size(); ++i) {
286 vector<string> option;
287 split(options[i], option, '=');
288 if (option.size() < 2)
290 if (option[0] == name) {
291 options.erase(options.begin() + i);
292 option.erase(option.begin());
293 return join(option, "=");
299 } // anonymous namespace
303 * known polyglossia language names (including variants)
304 * FIXME: support spelling=old for german variants (german vs. ngerman LyX names etc)
306 const char * const Preamble::polyglossia_languages[] = {
307 "albanian", "american", "amharic", "ancient", "arabic", "armenian", "asturian", "australian",
308 "bahasai", "bahasam", "basque", "bengali", "brazil", "brazilian", "breton", "british", "bulgarian",
309 "catalan", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
310 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
311 "galician", "greek", "monotonic", "hebrew", "hindi",
312 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer",
313 "lao", "latin", "latvian", "lithuanian", "lsorbian", "magyar", "malayalam", "marathi",
314 "austrian", "newzealand", "german", "norsk", "nynorsk", "occitan",
315 "piedmontese", "polish", "polytonic", "portuges", "romanian", "romansh", "russian",
316 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovenian", "spanish", "swedish", "syriac",
317 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
318 "ukrainian", "urdu", "usorbian", "vietnamese", "welsh", 0};
319 // not yet supported by LyX: "korean", "nko"
322 * the same as polyglossia_languages with .lyx names
323 * please keep this in sync with polyglossia_languages line by line!
325 const char * const Preamble::coded_polyglossia_languages[] = {
326 "albanian", "american", "amharic", "ancientgreek", "arabic_arabi", "armenian", "asturian", "australian",
327 "bahasa", "bahasam", "basque", "bengali", "brazilian", "brazilian", "breton", "british", "bulgarian",
328 "catalan", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
329 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
330 "galician", "greek", "greek", "hebrew", "hindi",
331 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer",
332 "lao", "latin", "latvian", "lithuanian", "lowersorbian", "magyar", "malayalam", "marathi",
333 "naustrian","newzealand", "ngerman", "norsk", "nynorsk", "occitan",
334 "piedmontese", "polish", "polutonikogreek", "portuges", "romanian", "romansh", "russian",
335 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovene", "spanish", "swedish", "syriac",
336 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
337 "ukrainian", "urdu", "uppersorbian", "vietnamese", "welsh", 0};
338 // not yet supported by LyX: "korean-polyglossia", "nko"
341 bool Preamble::usePolyglossia() const
343 return h_use_non_tex_fonts && h_language_package == "default";
347 bool Preamble::indentParagraphs() const
349 return h_paragraph_separation == "indent";
353 bool Preamble::isPackageUsed(string const & package) const
355 return used_packages.find(package) != used_packages.end();
359 vector<string> Preamble::getPackageOptions(string const & package) const
361 map<string, vector<string> >::const_iterator it = used_packages.find(package);
362 if (it != used_packages.end())
364 return vector<string>();
368 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
370 auto_packages.insert(package);
374 void Preamble::addModule(string const & module)
376 used_modules.push_back(module);
380 void Preamble::suppressDate(bool suppress)
383 h_suppress_date = "true";
385 h_suppress_date = "false";
389 void Preamble::registerAuthor(std::string const & name)
391 Author author(from_utf8(name), empty_docstring());
392 author.setUsed(true);
393 authors_.record(author);
394 h_tracking_changes = "true";
395 h_output_changes = "true";
399 Author const & Preamble::getAuthor(std::string const & name) const
401 Author author(from_utf8(name), empty_docstring());
402 for (AuthorList::Authors::const_iterator it = authors_.begin();
403 it != authors_.end(); ++it)
406 static Author const dummy;
411 int Preamble::getSpecialTableColumnArguments(char c) const
413 map<char, int>::const_iterator it = special_columns_.find(c);
414 if (it == special_columns_.end())
420 void Preamble::add_package(string const & name, vector<string> & options)
422 // every package inherits the global options
423 if (used_packages.find(name) == used_packages.end())
424 used_packages[name] = split_options(h_options);
426 // Insert options passed via PassOptionsToPackage
427 for (auto const & p : extra_package_options_) {
428 if (p.first == name) {
429 vector<string> eo = getVectorFromString(p.second);
430 for (auto const & eoi : eo)
431 options.push_back(eoi);
435 vector<string> & v = used_packages[name];
436 v.insert(v.end(), options.begin(), options.end());
437 if (name == "jurabib") {
438 // Don't output the order argument (see the cite command
439 // handling code in text.cpp).
440 vector<string>::iterator end =
441 remove(options.begin(), options.end(), "natbiborder");
442 end = remove(options.begin(), end, "jurabiborder");
443 options.erase(end, options.end());
450 // Given is a string like "scaled=0.9" or "Scale=0.9", return 0.9 * 100
451 bool scale_as_percentage(string const & scale, string & percentage)
453 string::size_type pos = scale.find('=');
454 if (pos != string::npos) {
455 string value = scale.substr(pos + 1);
456 if (isStrDbl(value)) {
457 percentage = convert<string>(
458 static_cast<int>(100 * convert<double>(value)));
466 string remove_braces(string const & value)
470 if (value[0] == '{' && value[value.length()-1] == '}')
471 return value.substr(1, value.length()-2);
475 } // anonymous namespace
478 Preamble::Preamble() : one_language(true), explicit_babel(false),
479 title_layout_found(false), index_number(0), h_font_cjk_set(false),
480 h_use_microtype("false")
484 h_biblio_style = "plain";
485 h_bibtex_command = "default";
486 h_cite_engine = "basic";
487 h_cite_engine_type = "default";
489 h_defskip = "medskip";
490 h_dynamic_quotes = false;
493 h_fontencoding = "default";
494 h_font_roman[0] = "default";
495 h_font_roman[1] = "default";
496 h_font_sans[0] = "default";
497 h_font_sans[1] = "default";
498 h_font_typewriter[0] = "default";
499 h_font_typewriter[1] = "default";
500 h_font_math[0] = "auto";
501 h_font_math[1] = "auto";
502 h_font_default_family = "default";
503 h_use_non_tex_fonts = false;
505 h_font_osf = "false";
506 h_font_sf_scale[0] = "100";
507 h_font_sf_scale[1] = "100";
508 h_font_tt_scale[0] = "100";
509 h_font_tt_scale[1] = "100";
511 h_is_mathindent = "0";
512 h_math_numbering_side = "default";
513 h_graphics = "default";
514 h_default_output_format = "default";
515 h_html_be_strict = "false";
516 h_html_css_as_file = "0";
517 h_html_math_output = "0";
518 h_index[0] = "Index";
519 h_index_command = "default";
520 h_inputencoding = "auto";
521 h_justification = "true";
522 h_language = "english";
523 h_language_package = "none";
525 h_maintain_unincluded_children = "false";
529 h_output_changes = "false";
531 //h_output_sync_macro
532 h_papercolumns = "1";
533 h_paperfontsize = "default";
534 h_paperorientation = "portrait";
535 h_paperpagestyle = "default";
537 h_papersize = "default";
538 h_paragraph_indentation = "default";
539 h_paragraph_separation = "indent";
544 h_pdf_bookmarks = "0";
545 h_pdf_bookmarksnumbered = "0";
546 h_pdf_bookmarksopen = "0";
547 h_pdf_bookmarksopenlevel = "1";
548 h_pdf_breaklinks = "0";
549 h_pdf_pdfborder = "0";
550 h_pdf_colorlinks = "0";
551 h_pdf_backref = "section";
552 h_pdf_pdfusetitle = "0";
554 //h_pdf_quoted_options;
555 h_quotes_style = "english";
557 h_shortcut[0] = "idx";
558 h_spacing = "single";
559 h_save_transient_properties = "true";
560 h_suppress_date = "false";
561 h_textclass = "article";
563 h_tracking_changes = "false";
564 h_use_bibtopic = "false";
565 h_use_dash_ligatures = "true";
566 h_use_indices = "false";
567 h_use_geometry = "false";
568 h_use_default_options = "false";
569 h_use_hyperref = "false";
570 h_use_microtype = "false";
571 h_use_refstyle = false;
572 h_use_minted = false;
573 h_use_packages["amsmath"] = "1";
574 h_use_packages["amssymb"] = "0";
575 h_use_packages["cancel"] = "0";
576 h_use_packages["esint"] = "1";
577 h_use_packages["mhchem"] = "0";
578 h_use_packages["mathdots"] = "0";
579 h_use_packages["mathtools"] = "0";
580 h_use_packages["stackrel"] = "0";
581 h_use_packages["stmaryrd"] = "0";
582 h_use_packages["undertilde"] = "0";
586 void Preamble::handle_hyperref(vector<string> & options)
588 // FIXME swallow inputencoding changes that might surround the
589 // hyperref setup if it was written by LyX
590 h_use_hyperref = "true";
591 // swallow "unicode=true", since LyX does always write that
592 vector<string>::iterator it =
593 find(options.begin(), options.end(), "unicode=true");
594 if (it != options.end())
596 it = find(options.begin(), options.end(), "pdfusetitle");
597 if (it != options.end()) {
598 h_pdf_pdfusetitle = "1";
601 string bookmarks = process_keyval_opt(options, "bookmarks");
602 if (bookmarks == "true")
603 h_pdf_bookmarks = "1";
604 else if (bookmarks == "false")
605 h_pdf_bookmarks = "0";
606 if (h_pdf_bookmarks == "1") {
607 string bookmarksnumbered =
608 process_keyval_opt(options, "bookmarksnumbered");
609 if (bookmarksnumbered == "true")
610 h_pdf_bookmarksnumbered = "1";
611 else if (bookmarksnumbered == "false")
612 h_pdf_bookmarksnumbered = "0";
613 string bookmarksopen =
614 process_keyval_opt(options, "bookmarksopen");
615 if (bookmarksopen == "true")
616 h_pdf_bookmarksopen = "1";
617 else if (bookmarksopen == "false")
618 h_pdf_bookmarksopen = "0";
619 if (h_pdf_bookmarksopen == "1") {
620 string bookmarksopenlevel =
621 process_keyval_opt(options, "bookmarksopenlevel");
622 if (!bookmarksopenlevel.empty())
623 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
626 string breaklinks = process_keyval_opt(options, "breaklinks");
627 if (breaklinks == "true")
628 h_pdf_breaklinks = "1";
629 else if (breaklinks == "false")
630 h_pdf_breaklinks = "0";
631 string pdfborder = process_keyval_opt(options, "pdfborder");
632 if (pdfborder == "{0 0 0}")
633 h_pdf_pdfborder = "1";
634 else if (pdfborder == "{0 0 1}")
635 h_pdf_pdfborder = "0";
636 string backref = process_keyval_opt(options, "backref");
637 if (!backref.empty())
638 h_pdf_backref = backref;
639 string colorlinks = process_keyval_opt(options, "colorlinks");
640 if (colorlinks == "true")
641 h_pdf_colorlinks = "1";
642 else if (colorlinks == "false")
643 h_pdf_colorlinks = "0";
644 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
645 if (!pdfpagemode.empty())
646 h_pdf_pagemode = pdfpagemode;
647 string pdftitle = process_keyval_opt(options, "pdftitle");
648 if (!pdftitle.empty()) {
649 h_pdf_title = remove_braces(pdftitle);
651 string pdfauthor = process_keyval_opt(options, "pdfauthor");
652 if (!pdfauthor.empty()) {
653 h_pdf_author = remove_braces(pdfauthor);
655 string pdfsubject = process_keyval_opt(options, "pdfsubject");
656 if (!pdfsubject.empty())
657 h_pdf_subject = remove_braces(pdfsubject);
658 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
659 if (!pdfkeywords.empty())
660 h_pdf_keywords = remove_braces(pdfkeywords);
661 if (!options.empty()) {
662 if (!h_pdf_quoted_options.empty())
663 h_pdf_quoted_options += ',';
664 h_pdf_quoted_options += join(options, ",");
670 void Preamble::handle_geometry(vector<string> & options)
672 h_use_geometry = "true";
673 vector<string>::iterator it;
675 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
676 h_paperorientation = "landscape";
680 // keyval version: "paper=letter"
681 string paper = process_keyval_opt(options, "paper");
683 h_papersize = paper + "paper";
684 // alternative version: "letterpaper"
685 handle_opt(options, known_paper_sizes, h_papersize);
686 delete_opt(options, known_paper_sizes);
688 char const * const * margin = known_paper_margins;
689 for (; *margin; ++margin) {
690 string value = process_keyval_opt(options, *margin);
691 if (!value.empty()) {
692 int k = margin - known_paper_margins;
693 string name = known_coded_paper_margins[k];
694 h_margins += '\\' + name + ' ' + value + '\n';
700 void Preamble::handle_package(Parser &p, string const & name,
701 string const & opts, bool in_lyx_preamble,
704 vector<string> options = split_options(opts);
705 add_package(name, options);
707 if (is_known(name, known_xetex_packages)) {
709 h_use_non_tex_fonts = true;
710 registerAutomaticallyLoadedPackage("fontspec");
711 if (h_inputencoding == "auto")
712 p.setEncoding("UTF-8");
716 if (is_known(name, known_roman_fonts))
717 h_font_roman[0] = name;
719 if (name == "fourier") {
720 h_font_roman[0] = "utopia";
721 // when font uses real small capitals
722 if (opts == "expert")
726 if (name == "garamondx") {
727 h_font_roman[0] = "garamondx";
732 if (name == "libertine") {
733 h_font_roman[0] = "libertine";
734 // this automatically invokes biolinum
735 h_font_sans[0] = "biolinum";
738 else if (opts == "lining")
739 h_font_osf = "false";
742 if (name == "libertine-type1") {
743 h_font_roman[0] = "libertine";
744 // NOTE: contrary to libertine.sty, libertine-type1
745 // does not automatically invoke biolinum
746 if (opts == "lining")
747 h_font_osf = "false";
748 else if (opts == "osf")
752 if (name == "mathdesign") {
753 if (opts.find("charter") != string::npos)
754 h_font_roman[0] = "md-charter";
755 if (opts.find("garamond") != string::npos)
756 h_font_roman[0] = "md-garamond";
757 if (opts.find("utopia") != string::npos)
758 h_font_roman[0] = "md-utopia";
759 if (opts.find("expert") != string::npos) {
765 else if (name == "mathpazo")
766 h_font_roman[0] = "palatino";
768 else if (name == "mathptmx")
769 h_font_roman[0] = "times";
771 if (name == "crimson")
772 h_font_roman[0] = "cochineal";
774 if (name == "cochineal") {
775 h_font_roman[0] = "cochineal";
776 // cochineal can have several options, e.g. [proportional,osf]
777 string::size_type pos = opts.find("osf");
778 if (pos != string::npos)
782 if (name == "noto") {
783 // noto can have several options
785 h_font_roman[0] = "NotoSerif-TLF";
786 string::size_type pos = opts.find("rm");
787 if (pos != string::npos)
788 h_font_roman[0] = "NotoSerif-TLF";
789 pos = opts.find("sf");
790 if (pos != string::npos)
791 h_font_sans[0] = "NotoSans-TLF";
792 pos = opts.find("nott");
793 if (pos != string::npos) {
794 h_font_roman[0] = "NotoSerif-TLF";
795 h_font_sans[0] = "NotoSans-TLF";
797 // noto as typewriter is handled in handling of \ttdefault
798 // special cases are handled in handling of \rmdefault and \sfdefault
802 if (is_known(name, known_sans_fonts)) {
803 h_font_sans[0] = name;
804 if (options.size() >= 1) {
805 if (scale_as_percentage(opts, h_font_sf_scale[0]))
810 if (name == "biolinum-type1") {
811 h_font_sans[0] = "biolinum";
812 // biolinum can have several options, e.g. [osf,scaled=0.97]
813 string::size_type pos = opts.find("osf");
814 if (pos != string::npos)
819 if (is_known(name, known_typewriter_fonts)) {
820 // fourier can be set as roman font _only_
821 // fourier as typewriter is handled in handling of \ttdefault
822 if (name != "fourier") {
823 h_font_typewriter[0] = name;
824 if (options.size() >= 1) {
825 if (scale_as_percentage(opts, h_font_tt_scale[0]))
831 if (name == "libertineMono-type1") {
832 h_font_typewriter[0] = "libertine-mono";
835 // font uses old-style figure
840 if (is_known(name, known_math_fonts))
841 h_font_math[0] = name;
843 if (name == "newtxmath") {
845 h_font_math[0] = "newtxmath";
846 else if (opts == "garamondx")
847 h_font_math[0] = "garamondx-ntxm";
848 else if (opts == "libertine")
849 h_font_math[0] = "libertine-ntxm";
850 else if (opts == "minion")
851 h_font_math[0] = "minion-ntxm";
852 else if (opts == "cochineal")
853 h_font_math[0] = "cochineal-ntxm";
858 h_font_math[0] = "iwona-math";
860 if (name == "kurier")
862 h_font_math[0] = "kurier-math";
864 // after the detection and handling of special cases, we can remove the
865 // fonts, otherwise they would appear in the preamble, see bug #7856
866 if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
867 || is_known(name, known_typewriter_fonts) || is_known(name, known_math_fonts))
869 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
870 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
871 name == "esint" || name == "mhchem" || name == "mathdots" ||
872 name == "mathtools" || name == "stackrel" ||
873 name == "stmaryrd" || name == "undertilde")
874 h_use_packages[name] = "2";
876 else if (name == "babel") {
877 h_language_package = "default";
878 // One might think we would have to do nothing if babel is loaded
879 // without any options to prevent pollution of the preamble with this
880 // babel call in every roundtrip.
881 // But the user could have defined babel-specific things afterwards. So
882 // we need to keep it in the preamble to prevent cases like bug #7861.
884 // check if more than one option was used - used later for inputenc
885 if (options.begin() != options.end() - 1)
886 one_language = false;
887 // babel takes the last language of the option of its \usepackage
888 // call as document language. If there is no such language option, the
889 // last language in the documentclass options is used.
890 handle_opt(options, known_languages, h_language);
891 // translate the babel name to a LyX name
892 h_language = babel2lyx(h_language);
893 if (h_language == "japanese") {
894 // For Japanese, the encoding isn't indicated in the source
895 // file, and there's really not much we can do. We could
896 // 1) offer a list of possible encodings to choose from, or
897 // 2) determine the encoding of the file by inspecting it.
898 // For the time being, we leave the encoding alone so that
899 // we don't get iconv errors when making a wrong guess, and
900 // we will output a note at the top of the document
901 // explaining what to do.
902 Encoding const * const enc = encodings.fromIconvName(
903 p.getEncoding(), Encoding::japanese, false);
905 h_inputencoding = enc->name();
906 is_nonCJKJapanese = true;
907 // in this case babel can be removed from the preamble
908 registerAutomaticallyLoadedPackage("babel");
910 // If babel is called with options, LyX puts them by default into the
911 // document class options. This works for most languages, except
912 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
913 // perhaps in future others.
914 // Therefore keep the babel call as it is as the user might have
916 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
918 delete_opt(options, known_languages);
920 h_preamble << "\\usepackage{babel}\n";
921 explicit_babel = true;
925 else if (name == "polyglossia") {
926 h_language_package = "default";
927 h_default_output_format = "pdf4";
928 h_use_non_tex_fonts = true;
930 registerAutomaticallyLoadedPackage("xunicode");
931 if (h_inputencoding == "auto")
932 p.setEncoding("UTF-8");
935 else if (name == "CJK") {
936 // set the encoding to "auto" because it might be set to "default" by the babel handling
937 // and this would not be correct for CJK
938 if (h_inputencoding == "default")
939 h_inputencoding = "auto";
940 registerAutomaticallyLoadedPackage("CJK");
943 else if (name == "CJKutf8") {
944 h_inputencoding = "utf8-cjk";
945 p.setEncoding("UTF-8");
946 registerAutomaticallyLoadedPackage("CJKutf8");
949 else if (name == "fontenc") {
950 h_fontencoding = getStringFromVector(options, ",");
951 /* We could do the following for better round trip support,
952 * but this makes the document less portable, so I skip it:
953 if (h_fontencoding == lyxrc.fontenc)
954 h_fontencoding = "global";
959 else if (name == "inputenc" || name == "luainputenc") {
960 // h_inputencoding is only set when there is not more than one
961 // inputenc option because otherwise h_inputencoding must be
962 // set to "auto" (the default encoding of the document language)
963 // Therefore check that exactly one option is passed to inputenc.
964 // It is also only set when there is not more than one babel
966 if (!options.empty()) {
967 string const encoding = options.back();
968 Encoding const * const enc = encodings.fromLaTeXName(
969 encoding, Encoding::inputenc, true);
972 cerr << "Unknown encoding " << encoding
973 << ". Ignoring." << std::endl;
975 if (!enc->unsafe() && options.size() == 1 && one_language == true)
976 h_inputencoding = enc->name();
977 p.setEncoding(enc->iconvName());
983 else if (name == "srcltx") {
986 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
989 h_output_sync_macro = "\\usepackage{srcltx}";
992 else if (is_known(name, known_old_language_packages)) {
993 // known language packages from the times before babel
994 // if they are found and not also babel, they will be used as
995 // custom language package
996 h_language_package = "\\usepackage{" + name + "}";
999 else if (name == "lyxskak") {
1000 // ignore this and its options
1001 const char * const o[] = {"ps", "mover", 0};
1002 delete_opt(options, o);
1005 else if (is_known(name, known_lyx_packages) && options.empty()) {
1006 if (name == "splitidx")
1007 h_use_indices = "true";
1008 else if (name == "minted")
1009 h_use_minted = true;
1010 else if (name == "refstyle")
1011 h_use_refstyle = true;
1012 else if (name == "prettyref")
1013 h_use_refstyle = false;
1014 if (!in_lyx_preamble) {
1015 h_preamble << package_beg_sep << name
1016 << package_mid_sep << "\\usepackage{"
1018 if (p.next_token().cat() == catNewline ||
1019 (p.next_token().cat() == catSpace &&
1020 p.next_next_token().cat() == catNewline))
1022 h_preamble << package_end_sep;
1026 else if (name == "geometry")
1027 handle_geometry(options);
1029 else if (name == "subfig")
1030 ; // ignore this FIXME: Use the package separator mechanism instead
1032 else if (char const * const * where = is_known(name, known_languages))
1033 h_language = known_coded_languages[where - known_languages];
1035 else if (name == "natbib") {
1036 h_biblio_style = "plainnat";
1037 h_cite_engine = "natbib";
1038 h_cite_engine_type = "authoryear";
1039 vector<string>::iterator it =
1040 find(options.begin(), options.end(), "authoryear");
1041 if (it != options.end())
1044 it = find(options.begin(), options.end(), "numbers");
1045 if (it != options.end()) {
1046 h_cite_engine_type = "numerical";
1050 if (!options.empty())
1051 h_biblio_options = join(options, ",");
1054 else if (name == "biblatex") {
1055 h_biblio_style = "plainnat";
1056 h_cite_engine = "biblatex";
1057 h_cite_engine_type = "authoryear";
1059 vector<string>::iterator it =
1060 find(options.begin(), options.end(), "natbib");
1061 if (it != options.end()) {
1063 h_cite_engine = "biblatex-natbib";
1065 opt = process_keyval_opt(options, "natbib");
1067 h_cite_engine = "biblatex-natbib";
1069 opt = process_keyval_opt(options, "style");
1071 h_biblatex_citestyle = opt;
1072 h_biblatex_bibstyle = opt;
1074 opt = process_keyval_opt(options, "citestyle");
1076 h_biblatex_citestyle = opt;
1077 opt = process_keyval_opt(options, "bibstyle");
1079 h_biblatex_bibstyle = opt;
1081 opt = process_keyval_opt(options, "refsection");
1083 if (opt == "none" || opt == "part"
1084 || opt == "chapter" || opt == "section"
1085 || opt == "subsection")
1088 cerr << "Ignoring unkown refesection value '"
1091 if (!options.empty()) {
1092 h_biblio_options = join(options, ",");
1097 else if (name == "jurabib") {
1098 h_biblio_style = "jurabib";
1099 h_cite_engine = "jurabib";
1100 h_cite_engine_type = "authoryear";
1101 if (!options.empty())
1102 h_biblio_options = join(options, ",");
1105 else if (name == "bibtopic")
1106 h_use_bibtopic = "true";
1108 else if (name == "chapterbib")
1109 h_multibib = "child";
1111 else if (name == "hyperref")
1112 handle_hyperref(options);
1114 else if (name == "algorithm2e") {
1115 // Load "algorithm2e" module
1116 addModule("algorithm2e");
1117 // Add the package options to the global document options
1118 if (!options.empty()) {
1119 if (h_options.empty())
1120 h_options = join(options, ",");
1122 h_options += ',' + join(options, ",");
1125 else if (name == "microtype") {
1126 //we internally support only microtype without params
1127 if (options.empty())
1128 h_use_microtype = "true";
1130 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1133 else if (!in_lyx_preamble) {
1134 if (options.empty())
1135 h_preamble << "\\usepackage{" << name << '}';
1137 h_preamble << "\\usepackage[" << opts << "]{"
1141 if (p.next_token().cat() == catNewline ||
1142 (p.next_token().cat() == catSpace &&
1143 p.next_next_token().cat() == catNewline))
1147 // We need to do something with the options...
1148 if (!options.empty() && !detectEncoding)
1149 cerr << "Ignoring options '" << join(options, ",")
1150 << "' of package " << name << '.' << endl;
1152 // remove the whitespace
1157 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1160 Token t = p.get_token();
1161 if (t.cat() == catEscape &&
1162 is_known(t.cs(), known_if_commands))
1163 handle_if(p, in_lyx_preamble);
1165 if (!in_lyx_preamble)
1166 h_preamble << t.asInput();
1167 if (t.cat() == catEscape && t.cs() == "fi")
1174 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1176 // set the quote language
1177 // LyX only knows the following quotes languages:
1178 // english, swedish, german, polish, french and danish
1179 // (quotes for "japanese" and "chinese-traditional" are missing because
1180 // they wouldn't be useful: https://www.lyx.org/trac/ticket/6383)
1181 // conversion list taken from
1182 // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
1183 // (quotes for kazakh and interlingua are unknown)
1185 if (is_known(h_language, known_danish_quotes_languages))
1186 h_quotes_style = "danish";
1188 else if (is_known(h_language, known_french_quotes_languages))
1189 h_quotes_style = "french";
1191 else if (is_known(h_language, known_german_quotes_languages))
1192 h_quotes_style = "german";
1194 else if (is_known(h_language, known_polish_quotes_languages))
1195 h_quotes_style = "polish";
1197 else if (is_known(h_language, known_swedish_quotes_languages))
1198 h_quotes_style = "swedish";
1200 else if (is_known(h_language, known_english_quotes_languages))
1201 h_quotes_style = "english";
1203 if (contains(h_float_placement, "H"))
1204 registerAutomaticallyLoadedPackage("float");
1205 if (h_spacing != "single" && h_spacing != "default")
1206 registerAutomaticallyLoadedPackage("setspace");
1207 if (h_use_packages["amsmath"] == "2") {
1208 // amsbsy and amstext are already provided by amsmath
1209 registerAutomaticallyLoadedPackage("amsbsy");
1210 registerAutomaticallyLoadedPackage("amstext");
1213 // output the LyX file settings
1214 // Important: Keep the version formatting in sync with LyX and
1215 // lyx2lyx (bug 7951)
1216 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1217 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1218 << lyx_version_minor << '\n'
1219 << "\\lyxformat " << LYX_FORMAT << '\n'
1220 << "\\begin_document\n"
1221 << "\\begin_header\n"
1222 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1223 << "\\origin " << origin << "\n"
1224 << "\\textclass " << h_textclass << "\n";
1225 string const raw = subdoc ? empty_string() : h_preamble.str();
1227 os << "\\begin_preamble\n";
1228 for (string::size_type i = 0; i < raw.size(); ++i) {
1229 if (raw[i] == package_beg_sep) {
1230 // Here follows some package loading code that
1231 // must be skipped if the package is loaded
1233 string::size_type j = raw.find(package_mid_sep, i);
1234 if (j == string::npos)
1236 string::size_type k = raw.find(package_end_sep, j);
1237 if (k == string::npos)
1239 string const package = raw.substr(i + 1, j - i - 1);
1240 string const replacement = raw.substr(j + 1, k - j - 1);
1241 if (auto_packages.find(package) == auto_packages.end())
1247 os << "\n\\end_preamble\n";
1249 if (!h_options.empty())
1250 os << "\\options " << h_options << "\n";
1251 os << "\\use_default_options " << h_use_default_options << "\n";
1252 if (!used_modules.empty()) {
1253 os << "\\begin_modules\n";
1254 vector<string>::const_iterator const end = used_modules.end();
1255 vector<string>::const_iterator it = used_modules.begin();
1256 for (; it != end; ++it)
1258 os << "\\end_modules\n";
1260 if (!h_includeonlys.empty()) {
1261 os << "\\begin_includeonly\n";
1262 for (auto const & iofile : h_includeonlys)
1263 os << iofile << '\n';
1264 os << "\\end_includeonly\n";
1266 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1267 << "\\language " << h_language << "\n"
1268 << "\\language_package " << h_language_package << "\n"
1269 << "\\inputencoding " << h_inputencoding << "\n"
1270 << "\\fontencoding " << h_fontencoding << "\n"
1271 << "\\font_roman \"" << h_font_roman[0]
1272 << "\" \"" << h_font_roman[1] << "\"\n"
1273 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1274 << "\\font_typewriter \"" << h_font_typewriter[0]
1275 << "\" \"" << h_font_typewriter[1] << "\"\n"
1276 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1277 << "\\font_default_family " << h_font_default_family << "\n"
1278 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1279 << "\\font_sc " << h_font_sc << "\n"
1280 << "\\font_osf " << h_font_osf << "\n"
1281 << "\\font_sf_scale " << h_font_sf_scale[0]
1282 << ' ' << h_font_sf_scale[1] << '\n'
1283 << "\\font_tt_scale " << h_font_tt_scale[0]
1284 << ' ' << h_font_tt_scale[1] << '\n';
1285 if (!h_font_cjk.empty())
1286 os << "\\font_cjk " << h_font_cjk << '\n';
1287 os << "\\use_microtype " << h_use_microtype << '\n'
1288 << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
1289 << "\\graphics " << h_graphics << '\n'
1290 << "\\default_output_format " << h_default_output_format << "\n"
1291 << "\\output_sync " << h_output_sync << "\n";
1292 if (h_output_sync == "1")
1293 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1294 os << "\\bibtex_command " << h_bibtex_command << "\n"
1295 << "\\index_command " << h_index_command << "\n";
1296 if (!h_float_placement.empty())
1297 os << "\\float_placement " << h_float_placement << "\n";
1298 os << "\\paperfontsize " << h_paperfontsize << "\n"
1299 << "\\spacing " << h_spacing << "\n"
1300 << "\\use_hyperref " << h_use_hyperref << '\n';
1301 if (h_use_hyperref == "true") {
1302 if (!h_pdf_title.empty())
1303 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1304 if (!h_pdf_author.empty())
1305 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1306 if (!h_pdf_subject.empty())
1307 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1308 if (!h_pdf_keywords.empty())
1309 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1310 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1311 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1312 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1313 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1314 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1315 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1316 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1317 "\\pdf_backref " << h_pdf_backref << "\n"
1318 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1319 if (!h_pdf_pagemode.empty())
1320 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1321 if (!h_pdf_quoted_options.empty())
1322 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1324 os << "\\papersize " << h_papersize << "\n"
1325 << "\\use_geometry " << h_use_geometry << '\n';
1326 for (map<string, string>::const_iterator it = h_use_packages.begin();
1327 it != h_use_packages.end(); ++it)
1328 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1329 os << "\\cite_engine " << h_cite_engine << '\n'
1330 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1331 << "\\biblio_style " << h_biblio_style << "\n"
1332 << "\\use_bibtopic " << h_use_bibtopic << "\n";
1333 if (!h_biblio_options.empty())
1334 os << "\\biblio_options " << h_biblio_options << "\n";
1335 if (!h_biblatex_bibstyle.empty())
1336 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
1337 if (!h_biblatex_citestyle.empty())
1338 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
1339 if (!h_multibib.empty())
1340 os << "\\multibib " << h_multibib << "\n";
1341 os << "\\use_indices " << h_use_indices << "\n"
1342 << "\\paperorientation " << h_paperorientation << '\n'
1343 << "\\suppress_date " << h_suppress_date << '\n'
1344 << "\\justification " << h_justification << '\n'
1345 << "\\use_refstyle " << h_use_refstyle << '\n'
1346 << "\\use_minted " << h_use_minted << '\n';
1347 if (!h_fontcolor.empty())
1348 os << "\\fontcolor " << h_fontcolor << '\n';
1349 if (!h_notefontcolor.empty())
1350 os << "\\notefontcolor " << h_notefontcolor << '\n';
1351 if (!h_backgroundcolor.empty())
1352 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1353 if (!h_boxbgcolor.empty())
1354 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1355 if (index_number != 0)
1356 for (int i = 0; i < index_number; i++) {
1357 os << "\\index " << h_index[i] << '\n'
1358 << "\\shortcut " << h_shortcut[i] << '\n'
1359 << "\\color " << h_color << '\n'
1363 os << "\\index " << h_index[0] << '\n'
1364 << "\\shortcut " << h_shortcut[0] << '\n'
1365 << "\\color " << h_color << '\n'
1369 << "\\secnumdepth " << h_secnumdepth << "\n"
1370 << "\\tocdepth " << h_tocdepth << "\n"
1371 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1372 if (h_paragraph_separation == "skip")
1373 os << "\\defskip " << h_defskip << "\n";
1375 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1376 os << "\\is_math_indent " << h_is_mathindent << "\n";
1377 if (!h_mathindentation.empty())
1378 os << "\\math_indentation " << h_mathindentation << "\n";
1379 os << "\\math_numbering_side " << h_math_numbering_side << "\n";
1380 os << "\\quotes_style " << h_quotes_style << "\n"
1381 << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
1382 << "\\papercolumns " << h_papercolumns << "\n"
1383 << "\\papersides " << h_papersides << "\n"
1384 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1385 if (!h_listings_params.empty())
1386 os << "\\listings_params " << h_listings_params << "\n";
1387 os << "\\tracking_changes " << h_tracking_changes << "\n"
1388 << "\\output_changes " << h_output_changes << "\n"
1389 << "\\html_math_output " << h_html_math_output << "\n"
1390 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1391 << "\\html_be_strict " << h_html_be_strict << "\n"
1393 << "\\end_header\n\n"
1394 << "\\begin_body\n";
1399 void Preamble::parse(Parser & p, string const & forceclass,
1400 TeX2LyXDocClass & tc)
1402 // initialize fixed types
1403 special_columns_['D'] = 3;
1404 parse(p, forceclass, false, tc);
1408 void Preamble::parse(Parser & p, string const & forceclass,
1409 bool detectEncoding, TeX2LyXDocClass & tc)
1411 bool is_full_document = false;
1412 bool is_lyx_file = false;
1413 bool in_lyx_preamble = false;
1415 // determine whether this is a full document or a fragment for inclusion
1417 Token const & t = p.get_token();
1419 if (t.cat() == catEscape && t.cs() == "documentclass") {
1420 is_full_document = true;
1426 if (detectEncoding && !is_full_document)
1429 while (is_full_document && p.good()) {
1430 if (detectEncoding && h_inputencoding != "auto" &&
1431 h_inputencoding != "default")
1434 Token const & t = p.get_token();
1437 if (!detectEncoding)
1438 cerr << "t: " << t << '\n';
1444 if (!in_lyx_preamble &&
1445 (t.cat() == catLetter ||
1446 t.cat() == catSuper ||
1447 t.cat() == catSub ||
1448 t.cat() == catOther ||
1449 t.cat() == catMath ||
1450 t.cat() == catActive ||
1451 t.cat() == catBegin ||
1452 t.cat() == catEnd ||
1453 t.cat() == catAlign ||
1454 t.cat() == catParameter)) {
1455 h_preamble << t.cs();
1459 if (!in_lyx_preamble &&
1460 (t.cat() == catSpace || t.cat() == catNewline)) {
1461 h_preamble << t.asInput();
1465 if (t.cat() == catComment) {
1466 static regex const islyxfile("%% LyX .* created this file");
1467 static regex const usercommands("User specified LaTeX commands");
1469 string const comment = t.asInput();
1471 // magically switch encoding default if it looks like XeLaTeX
1472 static string const magicXeLaTeX =
1473 "% This document must be compiled with XeLaTeX ";
1474 if (comment.size() > magicXeLaTeX.size()
1475 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1476 && h_inputencoding == "auto") {
1477 if (!detectEncoding)
1478 cerr << "XeLaTeX comment found, switching to UTF8\n";
1479 h_inputencoding = "utf8";
1482 if (regex_search(comment, sub, islyxfile)) {
1484 in_lyx_preamble = true;
1485 } else if (is_lyx_file
1486 && regex_search(comment, sub, usercommands))
1487 in_lyx_preamble = false;
1488 else if (!in_lyx_preamble)
1489 h_preamble << t.asInput();
1493 if (t.cs() == "PassOptionsToPackage") {
1494 string const poptions = p.getArg('{', '}');
1495 string const package = p.verbatim_item();
1496 extra_package_options_.insert(make_pair(package, poptions));
1500 if (t.cs() == "pagestyle") {
1501 h_paperpagestyle = p.verbatim_item();
1505 if (t.cs() == "setdefaultlanguage") {
1507 // We don't yet care about non-language variant options
1508 // because LyX doesn't support this yet, see bug #8214
1510 string langopts = p.getOpt();
1511 // check if the option contains a variant, if yes, extract it
1512 string::size_type pos_var = langopts.find("variant");
1513 string::size_type i = langopts.find(',', pos_var);
1514 string::size_type k = langopts.find('=', pos_var);
1515 if (pos_var != string::npos){
1517 if (i == string::npos)
1518 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1520 variant = langopts.substr(k + 1, i - k - 1);
1521 h_language = variant;
1525 h_language = p.verbatim_item();
1526 //finally translate the poyglossia name to a LyX name
1527 h_language = polyglossia2lyx(h_language);
1531 if (t.cs() == "setotherlanguage") {
1532 // We don't yet care about the option because LyX doesn't
1533 // support this yet, see bug #8214
1534 p.hasOpt() ? p.getOpt() : string();
1539 if (t.cs() == "setmainfont") {
1540 // we don't care about the option
1541 p.hasOpt() ? p.getOpt() : string();
1542 h_font_roman[1] = p.getArg('{', '}');
1546 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1547 // LyX currently only supports the scale option
1550 string fontopts = p.getArg('[', ']');
1551 // check if the option contains a scaling, if yes, extract it
1552 string::size_type pos = fontopts.find("Scale");
1553 if (pos != string::npos) {
1554 string::size_type i = fontopts.find(',', pos);
1555 if (i == string::npos)
1556 scale_as_percentage(fontopts.substr(pos + 1), scale);
1558 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1561 if (t.cs() == "setsansfont") {
1563 h_font_sf_scale[1] = scale;
1564 h_font_sans[1] = p.getArg('{', '}');
1567 h_font_tt_scale[1] = scale;
1568 h_font_typewriter[1] = p.getArg('{', '}');
1573 else if (t.cs() == "date") {
1574 string argument = p.getArg('{', '}');
1575 if (argument.empty())
1576 h_suppress_date = "true";
1578 h_preamble << t.asInput() << '{' << argument << '}';
1581 if (t.cs() == "color") {
1582 string const space =
1583 (p.hasOpt() ? p.getOpt() : string());
1584 string argument = p.getArg('{', '}');
1585 // check the case that a standard color is used
1586 if (space.empty() && is_known(argument, known_basic_colors)) {
1587 h_fontcolor = rgbcolor2code(argument);
1588 registerAutomaticallyLoadedPackage("color");
1589 } else if (space.empty() && argument == "document_fontcolor")
1590 registerAutomaticallyLoadedPackage("color");
1591 // check the case that LyX's document_fontcolor is defined
1592 // but not used for \color
1594 h_preamble << t.asInput();
1596 h_preamble << space;
1597 h_preamble << '{' << argument << '}';
1598 // the color might already be set because \definecolor
1599 // is parsed before this
1605 if (t.cs() == "pagecolor") {
1606 string argument = p.getArg('{', '}');
1607 // check the case that a standard color is used
1608 if (is_known(argument, known_basic_colors)) {
1609 h_backgroundcolor = rgbcolor2code(argument);
1610 } else if (argument == "page_backgroundcolor")
1611 registerAutomaticallyLoadedPackage("color");
1612 // check the case that LyX's page_backgroundcolor is defined
1613 // but not used for \pagecolor
1615 h_preamble << t.asInput() << '{' << argument << '}';
1616 // the color might already be set because \definecolor
1617 // is parsed before this
1618 h_backgroundcolor = "";
1623 if (t.cs() == "makeatletter") {
1624 // LyX takes care of this
1625 p.setCatcode('@', catLetter);
1629 if (t.cs() == "makeatother") {
1630 // LyX takes care of this
1631 p.setCatcode('@', catOther);
1635 if (t.cs() == "makeindex") {
1636 // LyX will re-add this if a print index command is found
1641 if (t.cs() == "newindex") {
1642 string const indexname = p.getArg('[', ']');
1643 string const shortcut = p.verbatim_item();
1644 if (!indexname.empty())
1645 h_index[index_number] = indexname;
1647 h_index[index_number] = shortcut;
1648 h_shortcut[index_number] = shortcut;
1654 if (t.cs() == "addbibresource") {
1655 biblatex_bibliographies.push_back(removeExtension(p.getArg('{', '}')));
1659 if (t.cs() == "bibliography") {
1660 vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
1661 biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
1665 if (t.cs() == "RS@ifundefined") {
1666 string const name = p.verbatim_item();
1667 string const body1 = p.verbatim_item();
1668 string const body2 = p.verbatim_item();
1669 // only non-lyxspecific stuff
1670 if (in_lyx_preamble &&
1671 (name == "subsecref" || name == "thmref" || name == "lemref"))
1675 ss << '\\' << t.cs();
1676 ss << '{' << name << '}'
1677 << '{' << body1 << '}'
1678 << '{' << body2 << '}';
1679 h_preamble << ss.str();
1684 if (t.cs() == "AtBeginDocument") {
1685 string const name = p.verbatim_item();
1686 // only non-lyxspecific stuff
1687 if (in_lyx_preamble &&
1688 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
1689 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
1690 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
1691 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
1692 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
1693 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
1694 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
1695 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
1696 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
1697 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
1698 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
1699 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
1700 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
1701 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
1702 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
1706 ss << '\\' << t.cs();
1707 ss << '{' << name << '}';
1708 h_preamble << ss.str();
1713 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1714 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1715 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1716 || t.cs() == "DeclareRobustCommand"
1717 || t.cs() == "DeclareRobustCommandx"
1718 || t.cs() == "ProvideTextCommandDefault"
1719 || t.cs() == "DeclareMathAccent") {
1721 if (p.next_token().character() == '*') {
1725 string const name = p.verbatim_item();
1726 string const opt1 = p.getFullOpt();
1727 string const opt2 = p.getFullOpt();
1728 string const body = p.verbatim_item();
1729 // store the in_lyx_preamble setting
1730 bool const was_in_lyx_preamble = in_lyx_preamble;
1732 if (name == "\\rmdefault")
1733 if (is_known(body, known_roman_fonts)) {
1734 h_font_roman[0] = body;
1736 in_lyx_preamble = true;
1738 if (name == "\\sfdefault")
1739 if (is_known(body, known_sans_fonts)) {
1740 h_font_sans[0] = body;
1742 in_lyx_preamble = true;
1744 if (name == "\\ttdefault")
1745 if (is_known(body, known_typewriter_fonts)) {
1746 h_font_typewriter[0] = body;
1748 in_lyx_preamble = true;
1750 if (name == "\\familydefault") {
1751 string family = body;
1752 // remove leading "\"
1753 h_font_default_family = family.erase(0,1);
1755 in_lyx_preamble = true;
1758 // remove LyX-specific definitions that are re-added by LyX
1760 // \lyxline is an ancient command that is converted by tex2lyx into
1761 // a \rule therefore remove its preamble code
1762 if (name == "\\lyxdot" || name == "\\lyxarrow"
1763 || name == "\\lyxline" || name == "\\LyX") {
1765 in_lyx_preamble = true;
1768 // Add the command to the known commands
1769 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1771 // only non-lyxspecific stuff
1772 if (!in_lyx_preamble) {
1774 ss << '\\' << t.cs();
1777 ss << '{' << name << '}' << opt1 << opt2
1778 << '{' << body << "}";
1779 h_preamble << ss.str();
1781 ostream & out = in_preamble ? h_preamble : os;
1782 out << "\\" << t.cs() << "{" << name << "}"
1783 << opts << "{" << body << "}";
1786 // restore the in_lyx_preamble setting
1787 in_lyx_preamble = was_in_lyx_preamble;
1791 if (t.cs() == "documentclass") {
1792 vector<string>::iterator it;
1793 vector<string> opts = split_options(p.getArg('[', ']'));
1794 handle_opt(opts, known_fontsizes, h_paperfontsize);
1795 delete_opt(opts, known_fontsizes);
1796 // delete "pt" at the end
1797 string::size_type i = h_paperfontsize.find("pt");
1798 if (i != string::npos)
1799 h_paperfontsize.erase(i);
1800 // The documentclass options are always parsed before the options
1801 // of the babel call so that a language cannot overwrite the babel
1803 handle_opt(opts, known_languages, h_language);
1804 delete_opt(opts, known_languages);
1807 if ((it = find(opts.begin(), opts.end(), "fleqn"))
1809 h_is_mathindent = "1";
1812 // formula numbering side
1813 if ((it = find(opts.begin(), opts.end(), "leqno"))
1815 h_math_numbering_side = "left";
1818 else if ((it = find(opts.begin(), opts.end(), "reqno"))
1820 h_math_numbering_side = "right";
1824 // paper orientation
1825 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1826 h_paperorientation = "landscape";
1830 if ((it = find(opts.begin(), opts.end(), "oneside"))
1835 if ((it = find(opts.begin(), opts.end(), "twoside"))
1841 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1843 h_papercolumns = "1";
1846 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1848 h_papercolumns = "2";
1852 // some size options are known to any document classes, other sizes
1853 // are handled by the \geometry command of the geometry package
1854 handle_opt(opts, known_class_paper_sizes, h_papersize);
1855 delete_opt(opts, known_class_paper_sizes);
1856 // the remaining options
1857 h_options = join(opts, ",");
1858 // FIXME This does not work for classes that have a
1859 // different name in LyX than in LaTeX
1860 h_textclass = p.getArg('{', '}');
1865 if (t.cs() == "usepackage") {
1866 string const options = p.getArg('[', ']');
1867 string const name = p.getArg('{', '}');
1868 vector<string> vecnames;
1869 split(name, vecnames, ',');
1870 vector<string>::const_iterator it = vecnames.begin();
1871 vector<string>::const_iterator end = vecnames.end();
1872 for (; it != end; ++it)
1873 handle_package(p, trimSpaceAndEol(*it), options,
1874 in_lyx_preamble, detectEncoding);
1878 if (t.cs() == "inputencoding") {
1879 string const encoding = p.getArg('{','}');
1880 Encoding const * const enc = encodings.fromLaTeXName(
1881 encoding, Encoding::inputenc, true);
1883 if (!detectEncoding)
1884 cerr << "Unknown encoding " << encoding
1885 << ". Ignoring." << std::endl;
1888 h_inputencoding = enc->name();
1889 p.setEncoding(enc->iconvName());
1894 if (t.cs() == "newenvironment") {
1895 string const name = p.getArg('{', '}');
1896 string const opt1 = p.getFullOpt();
1897 string const opt2 = p.getFullOpt();
1898 string const beg = p.verbatim_item();
1899 string const end = p.verbatim_item();
1900 if (!in_lyx_preamble) {
1901 h_preamble << "\\newenvironment{" << name
1902 << '}' << opt1 << opt2 << '{'
1903 << beg << "}{" << end << '}';
1905 add_known_environment(name, opt1, !opt2.empty(),
1906 from_utf8(beg), from_utf8(end));
1910 if (t.cs() == "newtheorem") {
1912 if (p.next_token().character() == '*') {
1916 string const name = p.getArg('{', '}');
1917 string const opt1 = p.getFullOpt();
1918 string const opt2 = p.getFullOpt();
1919 string const body = p.verbatim_item();
1920 string const opt3 = p.getFullOpt();
1921 string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
1923 string const complete = cmd + "{" + name + '}' +
1924 opt1 + opt2 + '{' + body + '}' + opt3;
1926 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
1928 if (!in_lyx_preamble)
1929 h_preamble << complete;
1933 if (t.cs() == "def") {
1934 string name = p.get_token().cs();
1935 // In fact, name may be more than the name:
1936 // In the test case of bug 8116
1937 // name == "csname SF@gobble@opt \endcsname".
1938 // Therefore, we need to use asInput() instead of cs().
1939 while (p.next_token().cat() != catBegin)
1940 name += p.get_token().asInput();
1941 if (!in_lyx_preamble)
1942 h_preamble << "\\def\\" << name << '{'
1943 << p.verbatim_item() << "}";
1947 if (t.cs() == "newcolumntype") {
1948 string const name = p.getArg('{', '}');
1949 trimSpaceAndEol(name);
1951 string opts = p.getOpt();
1952 if (!opts.empty()) {
1953 istringstream is(string(opts, 1));
1956 special_columns_[name[0]] = nargs;
1957 h_preamble << "\\newcolumntype{" << name << "}";
1959 h_preamble << "[" << nargs << "]";
1960 h_preamble << "{" << p.verbatim_item() << "}";
1964 if (t.cs() == "setcounter") {
1965 string const name = p.getArg('{', '}');
1966 string const content = p.getArg('{', '}');
1967 if (name == "secnumdepth")
1968 h_secnumdepth = content;
1969 else if (name == "tocdepth")
1970 h_tocdepth = content;
1972 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1976 if (t.cs() == "setlength") {
1977 string const name = p.verbatim_item();
1978 string const content = p.verbatim_item();
1979 // the paragraphs are only not indented when \parindent is set to zero
1980 if (name == "\\parindent" && content != "") {
1981 if (content[0] == '0')
1982 h_paragraph_separation = "skip";
1984 h_paragraph_indentation = translate_len(content);
1985 } else if (name == "\\parskip") {
1986 if (content == "\\smallskipamount")
1987 h_defskip = "smallskip";
1988 else if (content == "\\medskipamount")
1989 h_defskip = "medskip";
1990 else if (content == "\\bigskipamount")
1991 h_defskip = "bigskip";
1993 h_defskip = translate_len(content);
1994 } else if (name == "\\mathindent") {
1995 h_mathindentation = translate_len(content);
1997 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2001 if (t.cs() == "onehalfspacing") {
2002 h_spacing = "onehalf";
2006 if (t.cs() == "doublespacing") {
2007 h_spacing = "double";
2011 if (t.cs() == "setstretch") {
2012 h_spacing = "other " + p.verbatim_item();
2016 if (t.cs() == "synctex") {
2017 // the scheme is \synctex=value
2018 // where value can only be "1" or "-1"
2019 h_output_sync = "1";
2020 // there can be any character behind the value (e.g. a linebreak or a '\'
2021 // therefore we extract it char by char
2023 string value = p.get_token().asInput();
2025 value += p.get_token().asInput();
2026 h_output_sync_macro = "\\synctex=" + value;
2030 if (t.cs() == "begin") {
2031 string const name = p.getArg('{', '}');
2032 if (name == "document")
2034 h_preamble << "\\begin{" << name << "}";
2038 if (t.cs() == "geometry") {
2039 vector<string> opts = split_options(p.getArg('{', '}'));
2040 handle_geometry(opts);
2044 if (t.cs() == "definecolor") {
2045 string const color = p.getArg('{', '}');
2046 string const space = p.getArg('{', '}');
2047 string const value = p.getArg('{', '}');
2048 if (color == "document_fontcolor" && space == "rgb") {
2049 RGBColor c(RGBColorFromLaTeX(value));
2050 h_fontcolor = X11hexname(c);
2051 } else if (color == "note_fontcolor" && space == "rgb") {
2052 RGBColor c(RGBColorFromLaTeX(value));
2053 h_notefontcolor = X11hexname(c);
2054 } else if (color == "page_backgroundcolor" && space == "rgb") {
2055 RGBColor c(RGBColorFromLaTeX(value));
2056 h_backgroundcolor = X11hexname(c);
2057 } else if (color == "shadecolor" && space == "rgb") {
2058 RGBColor c(RGBColorFromLaTeX(value));
2059 h_boxbgcolor = X11hexname(c);
2061 h_preamble << "\\definecolor{" << color
2062 << "}{" << space << "}{" << value
2068 if (t.cs() == "bibliographystyle") {
2069 h_biblio_style = p.verbatim_item();
2073 if (t.cs() == "jurabibsetup") {
2074 // FIXME p.getArg('{', '}') is most probably wrong (it
2075 // does not handle nested braces).
2076 // Use p.verbatim_item() instead.
2077 vector<string> jurabibsetup =
2078 split_options(p.getArg('{', '}'));
2079 // add jurabibsetup to the jurabib package options
2080 add_package("jurabib", jurabibsetup);
2081 if (!jurabibsetup.empty()) {
2082 h_preamble << "\\jurabibsetup{"
2083 << join(jurabibsetup, ",") << '}';
2088 if (t.cs() == "hypersetup") {
2089 vector<string> hypersetup =
2090 split_options(p.verbatim_item());
2091 // add hypersetup to the hyperref package options
2092 handle_hyperref(hypersetup);
2093 if (!hypersetup.empty()) {
2094 h_preamble << "\\hypersetup{"
2095 << join(hypersetup, ",") << '}';
2100 if (t.cs() == "includeonly") {
2101 vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
2102 for (auto & iofile : includeonlys) {
2103 string filename(normalize_filename(iofile));
2104 string const path = getMasterFilePath(true);
2105 // We want to preserve relative/absolute filenames,
2106 // therefore path is only used for testing
2107 if (!makeAbsPath(filename, path).exists()) {
2108 // The file extension is probably missing.
2109 // Now try to find it out.
2110 string const tex_name =
2111 find_file(filename, path,
2112 known_tex_extensions);
2113 if (!tex_name.empty())
2114 filename = tex_name;
2117 if (makeAbsPath(filename, path).exists())
2118 fix_child_filename(filename);
2120 cerr << "Warning: Could not find included file '"
2121 << filename << "'." << endl;
2122 outname = changeExtension(filename, "lyx");
2123 h_includeonlys.push_back(outname);
2128 if (is_known(t.cs(), known_if_3arg_commands)) {
2129 // prevent misparsing of \usepackage if it is used
2130 // as an argument (see e.g. our own output of
2131 // \@ifundefined above)
2132 string const arg1 = p.verbatim_item();
2133 string const arg2 = p.verbatim_item();
2134 string const arg3 = p.verbatim_item();
2135 // test case \@ifundefined{date}{}{\date{}}
2136 if (t.cs() == "@ifundefined" && arg1 == "date" &&
2137 arg2.empty() && arg3 == "\\date{}") {
2138 h_suppress_date = "true";
2139 // older tex2lyx versions did output
2140 // \@ifundefined{definecolor}{\usepackage{color}}{}
2141 } else if (t.cs() == "@ifundefined" &&
2142 arg1 == "definecolor" &&
2143 arg2 == "\\usepackage{color}" &&
2145 if (!in_lyx_preamble)
2146 h_preamble << package_beg_sep
2149 << "\\@ifundefined{definecolor}{color}{}"
2152 //\@ifundefined{showcaptionsetup}{}{%
2153 // \PassOptionsToPackage{caption=false}{subfig}}
2154 // that LyX uses for subfloats
2155 } else if (t.cs() == "@ifundefined" &&
2156 arg1 == "showcaptionsetup" && arg2.empty()
2157 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
2159 } else if (!in_lyx_preamble) {
2160 h_preamble << t.asInput()
2161 << '{' << arg1 << '}'
2162 << '{' << arg2 << '}'
2163 << '{' << arg3 << '}';
2168 if (is_known(t.cs(), known_if_commands)) {
2169 // must not parse anything in conditional code, since
2170 // LyX would output the parsed contents unconditionally
2171 if (!in_lyx_preamble)
2172 h_preamble << t.asInput();
2173 handle_if(p, in_lyx_preamble);
2177 if (!t.cs().empty() && !in_lyx_preamble) {
2178 h_preamble << '\\' << t.cs();
2183 // remove the whitespace
2186 // Force textclass if the user wanted it
2187 if (!forceclass.empty())
2188 h_textclass = forceclass;
2189 tc.setName(h_textclass);
2190 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2191 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2194 if (h_papersides.empty()) {
2197 h_papersides = ss.str();
2200 // If the CJK package is used we cannot set the document language from
2201 // the babel options. Instead, we guess which language is used most
2202 // and set this one.
2203 default_language = h_language;
2204 if (is_full_document &&
2205 (auto_packages.find("CJK") != auto_packages.end() ||
2206 auto_packages.find("CJKutf8") != auto_packages.end())) {
2208 h_language = guessLanguage(p, default_language);
2210 if (explicit_babel && h_language != default_language) {
2211 // We set the document language to a CJK language,
2212 // but babel is explicitly called in the user preamble
2213 // without options. LyX will not add the default
2214 // language to the document options if it is either
2215 // english, or no text is set as default language.
2216 // Therefore we need to add a language option explicitly.
2217 // FIXME: It would be better to remove all babel calls
2218 // from the user preamble, but this is difficult
2219 // without re-introducing bug 7861.
2220 if (h_options.empty())
2221 h_options = lyx2babel(default_language);
2223 h_options += ',' + lyx2babel(default_language);
2229 string Preamble::parseEncoding(Parser & p, string const & forceclass)
2231 TeX2LyXDocClass dummy;
2232 parse(p, forceclass, true, dummy);
2233 if (h_inputencoding != "auto" && h_inputencoding != "default")
2234 return h_inputencoding;
2239 string babel2lyx(string const & language)
2241 char const * const * where = is_known(language, known_languages);
2243 return known_coded_languages[where - known_languages];
2248 string lyx2babel(string const & language)
2250 char const * const * where = is_known(language, known_coded_languages);
2252 return known_languages[where - known_coded_languages];
2257 string Preamble::polyglossia2lyx(string const & language)
2259 char const * const * where = is_known(language, polyglossia_languages);
2261 return coded_polyglossia_languages[where - polyglossia_languages];
2266 string rgbcolor2code(string const & name)
2268 char const * const * where = is_known(name, known_basic_colors);
2270 // "red", "green" etc
2271 return known_basic_color_codes[where - known_basic_colors];
2273 // "255,0,0", "0,255,0" etc
2274 RGBColor c(RGBColorFromLaTeX(name));
2275 return X11hexname(c);