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();
1457 else if (!in_lyx_preamble &&
1458 (t.cat() == catSpace || t.cat() == catNewline))
1459 h_preamble << t.asInput();
1461 else if (t.cat() == catComment) {
1462 static regex const islyxfile("%% LyX .* created this file");
1463 static regex const usercommands("User specified LaTeX commands");
1465 string const comment = t.asInput();
1467 // magically switch encoding default if it looks like XeLaTeX
1468 static string const magicXeLaTeX =
1469 "% This document must be compiled with XeLaTeX ";
1470 if (comment.size() > magicXeLaTeX.size()
1471 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1472 && h_inputencoding == "auto") {
1473 if (!detectEncoding)
1474 cerr << "XeLaTeX comment found, switching to UTF8\n";
1475 h_inputencoding = "utf8";
1478 if (regex_search(comment, sub, islyxfile)) {
1480 in_lyx_preamble = true;
1481 } else if (is_lyx_file
1482 && regex_search(comment, sub, usercommands))
1483 in_lyx_preamble = false;
1484 else if (!in_lyx_preamble)
1485 h_preamble << t.asInput();
1488 else if (t.cs() == "PassOptionsToPackage") {
1489 string const poptions = p.getArg('{', '}');
1490 string const package = p.verbatim_item();
1491 extra_package_options_.insert(make_pair(package, poptions));
1494 else if (t.cs() == "pagestyle")
1495 h_paperpagestyle = p.verbatim_item();
1497 else if (t.cs() == "setdefaultlanguage") {
1499 // We don't yet care about non-language variant options
1500 // because LyX doesn't support this yet, see bug #8214
1502 string langopts = p.getOpt();
1503 // check if the option contains a variant, if yes, extract it
1504 string::size_type pos_var = langopts.find("variant");
1505 string::size_type i = langopts.find(',', pos_var);
1506 string::size_type k = langopts.find('=', pos_var);
1507 if (pos_var != string::npos){
1509 if (i == string::npos)
1510 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1512 variant = langopts.substr(k + 1, i - k - 1);
1513 h_language = variant;
1517 h_language = p.verbatim_item();
1518 //finally translate the poyglossia name to a LyX name
1519 h_language = polyglossia2lyx(h_language);
1522 else if (t.cs() == "setotherlanguage") {
1523 // We don't yet care about the option because LyX doesn't
1524 // support this yet, see bug #8214
1525 p.hasOpt() ? p.getOpt() : string();
1529 else if (t.cs() == "setmainfont") {
1530 // we don't care about the option
1531 p.hasOpt() ? p.getOpt() : string();
1532 h_font_roman[1] = p.getArg('{', '}');
1535 else if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1536 // LyX currently only supports the scale option
1539 string fontopts = p.getArg('[', ']');
1540 // check if the option contains a scaling, if yes, extract it
1541 string::size_type pos = fontopts.find("Scale");
1542 if (pos != string::npos) {
1543 string::size_type i = fontopts.find(',', pos);
1544 if (i == string::npos)
1545 scale_as_percentage(fontopts.substr(pos + 1), scale);
1547 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1550 if (t.cs() == "setsansfont") {
1552 h_font_sf_scale[1] = scale;
1553 h_font_sans[1] = p.getArg('{', '}');
1556 h_font_tt_scale[1] = scale;
1557 h_font_typewriter[1] = p.getArg('{', '}');
1561 else if (t.cs() == "date") {
1562 string argument = p.getArg('{', '}');
1563 if (argument.empty())
1564 h_suppress_date = "true";
1566 h_preamble << t.asInput() << '{' << argument << '}';
1569 else if (t.cs() == "color") {
1570 string const space =
1571 (p.hasOpt() ? p.getOpt() : string());
1572 string argument = p.getArg('{', '}');
1573 // check the case that a standard color is used
1574 if (space.empty() && is_known(argument, known_basic_colors)) {
1575 h_fontcolor = rgbcolor2code(argument);
1576 registerAutomaticallyLoadedPackage("color");
1577 } else if (space.empty() && argument == "document_fontcolor")
1578 registerAutomaticallyLoadedPackage("color");
1579 // check the case that LyX's document_fontcolor is defined
1580 // but not used for \color
1582 h_preamble << t.asInput();
1584 h_preamble << space;
1585 h_preamble << '{' << argument << '}';
1586 // the color might already be set because \definecolor
1587 // is parsed before this
1592 else if (t.cs() == "pagecolor") {
1593 string argument = p.getArg('{', '}');
1594 // check the case that a standard color is used
1595 if (is_known(argument, known_basic_colors)) {
1596 h_backgroundcolor = rgbcolor2code(argument);
1597 } else if (argument == "page_backgroundcolor")
1598 registerAutomaticallyLoadedPackage("color");
1599 // check the case that LyX's page_backgroundcolor is defined
1600 // but not used for \pagecolor
1602 h_preamble << t.asInput() << '{' << argument << '}';
1603 // the color might already be set because \definecolor
1604 // is parsed before this
1605 h_backgroundcolor = "";
1609 else if (t.cs() == "makeatletter") {
1610 // LyX takes care of this
1611 p.setCatcode('@', catLetter);
1614 else if (t.cs() == "makeatother") {
1615 // LyX takes care of this
1616 p.setCatcode('@', catOther);
1619 else if (t.cs() == "makeindex") {
1620 // LyX will re-add this if a print index command is found
1624 else if (t.cs() == "newindex") {
1625 string const indexname = p.getArg('[', ']');
1626 string const shortcut = p.verbatim_item();
1627 if (!indexname.empty())
1628 h_index[index_number] = indexname;
1630 h_index[index_number] = shortcut;
1631 h_shortcut[index_number] = shortcut;
1636 else if (t.cs() == "addbibresource")
1637 biblatex_bibliographies.push_back(removeExtension(p.getArg('{', '}')));
1639 else if (t.cs() == "bibliography") {
1640 vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
1641 biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
1644 else if (t.cs() == "RS@ifundefined") {
1645 string const name = p.verbatim_item();
1646 string const body1 = p.verbatim_item();
1647 string const body2 = p.verbatim_item();
1648 // only non-lyxspecific stuff
1649 if (in_lyx_preamble &&
1650 (name == "subsecref" || name == "thmref" || name == "lemref"))
1654 ss << '\\' << t.cs();
1655 ss << '{' << name << '}'
1656 << '{' << body1 << '}'
1657 << '{' << body2 << '}';
1658 h_preamble << ss.str();
1662 else if (t.cs() == "AtBeginDocument") {
1663 string const name = p.verbatim_item();
1664 // only non-lyxspecific stuff
1665 if (in_lyx_preamble &&
1666 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
1667 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
1668 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
1669 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
1670 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
1671 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
1672 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
1673 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
1674 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
1675 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
1676 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
1677 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
1678 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
1679 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
1680 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
1684 ss << '\\' << t.cs();
1685 ss << '{' << name << '}';
1686 h_preamble << ss.str();
1690 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1691 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1692 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1693 || t.cs() == "DeclareRobustCommand"
1694 || t.cs() == "DeclareRobustCommandx"
1695 || t.cs() == "ProvideTextCommandDefault"
1696 || t.cs() == "DeclareMathAccent") {
1698 if (p.next_token().character() == '*') {
1702 string const name = p.verbatim_item();
1703 string const opt1 = p.getFullOpt();
1704 string const opt2 = p.getFullOpt();
1705 string const body = p.verbatim_item();
1706 // store the in_lyx_preamble setting
1707 bool const was_in_lyx_preamble = in_lyx_preamble;
1709 if (name == "\\rmdefault")
1710 if (is_known(body, known_roman_fonts)) {
1711 h_font_roman[0] = body;
1713 in_lyx_preamble = true;
1715 if (name == "\\sfdefault")
1716 if (is_known(body, known_sans_fonts)) {
1717 h_font_sans[0] = body;
1719 in_lyx_preamble = true;
1721 if (name == "\\ttdefault")
1722 if (is_known(body, known_typewriter_fonts)) {
1723 h_font_typewriter[0] = body;
1725 in_lyx_preamble = true;
1727 if (name == "\\familydefault") {
1728 string family = body;
1729 // remove leading "\"
1730 h_font_default_family = family.erase(0,1);
1732 in_lyx_preamble = true;
1735 // remove LyX-specific definitions that are re-added by LyX
1737 // \lyxline is an ancient command that is converted by tex2lyx into
1738 // a \rule therefore remove its preamble code
1739 if (name == "\\lyxdot" || name == "\\lyxarrow"
1740 || name == "\\lyxline" || name == "\\LyX") {
1742 in_lyx_preamble = true;
1745 // Add the command to the known commands
1746 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1748 // only non-lyxspecific stuff
1749 if (!in_lyx_preamble) {
1751 ss << '\\' << t.cs();
1754 ss << '{' << name << '}' << opt1 << opt2
1755 << '{' << body << "}";
1756 h_preamble << ss.str();
1758 ostream & out = in_preamble ? h_preamble : os;
1759 out << "\\" << t.cs() << "{" << name << "}"
1760 << opts << "{" << body << "}";
1763 // restore the in_lyx_preamble setting
1764 in_lyx_preamble = was_in_lyx_preamble;
1767 else if (t.cs() == "documentclass") {
1768 vector<string>::iterator it;
1769 vector<string> opts = split_options(p.getArg('[', ']'));
1770 handle_opt(opts, known_fontsizes, h_paperfontsize);
1771 delete_opt(opts, known_fontsizes);
1772 // delete "pt" at the end
1773 string::size_type i = h_paperfontsize.find("pt");
1774 if (i != string::npos)
1775 h_paperfontsize.erase(i);
1776 // The documentclass options are always parsed before the options
1777 // of the babel call so that a language cannot overwrite the babel
1779 handle_opt(opts, known_languages, h_language);
1780 delete_opt(opts, known_languages);
1783 if ((it = find(opts.begin(), opts.end(), "fleqn"))
1785 h_is_mathindent = "1";
1788 // formula numbering side
1789 if ((it = find(opts.begin(), opts.end(), "leqno"))
1791 h_math_numbering_side = "left";
1794 else if ((it = find(opts.begin(), opts.end(), "reqno"))
1796 h_math_numbering_side = "right";
1800 // paper orientation
1801 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1802 h_paperorientation = "landscape";
1806 if ((it = find(opts.begin(), opts.end(), "oneside"))
1811 if ((it = find(opts.begin(), opts.end(), "twoside"))
1817 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1819 h_papercolumns = "1";
1822 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1824 h_papercolumns = "2";
1828 // some size options are known to any document classes, other sizes
1829 // are handled by the \geometry command of the geometry package
1830 handle_opt(opts, known_class_paper_sizes, h_papersize);
1831 delete_opt(opts, known_class_paper_sizes);
1832 // the remaining options
1833 h_options = join(opts, ",");
1834 // FIXME This does not work for classes that have a
1835 // different name in LyX than in LaTeX
1836 h_textclass = p.getArg('{', '}');
1840 else if (t.cs() == "usepackage") {
1841 string const options = p.getArg('[', ']');
1842 string const name = p.getArg('{', '}');
1843 vector<string> vecnames;
1844 split(name, vecnames, ',');
1845 vector<string>::const_iterator it = vecnames.begin();
1846 vector<string>::const_iterator end = vecnames.end();
1847 for (; it != end; ++it)
1848 handle_package(p, trimSpaceAndEol(*it), options,
1849 in_lyx_preamble, detectEncoding);
1852 else if (t.cs() == "inputencoding") {
1853 string const encoding = p.getArg('{','}');
1854 Encoding const * const enc = encodings.fromLaTeXName(
1855 encoding, Encoding::inputenc, true);
1857 if (!detectEncoding)
1858 cerr << "Unknown encoding " << encoding
1859 << ". Ignoring." << std::endl;
1862 h_inputencoding = enc->name();
1863 p.setEncoding(enc->iconvName());
1867 else if (t.cs() == "newenvironment") {
1868 string const name = p.getArg('{', '}');
1869 string const opt1 = p.getFullOpt();
1870 string const opt2 = p.getFullOpt();
1871 string const beg = p.verbatim_item();
1872 string const end = p.verbatim_item();
1873 if (!in_lyx_preamble) {
1874 h_preamble << "\\newenvironment{" << name
1875 << '}' << opt1 << opt2 << '{'
1876 << beg << "}{" << end << '}';
1878 add_known_environment(name, opt1, !opt2.empty(),
1879 from_utf8(beg), from_utf8(end));
1883 else if (t.cs() == "newtheorem") {
1885 if (p.next_token().character() == '*') {
1889 string const name = p.getArg('{', '}');
1890 string const opt1 = p.getFullOpt();
1891 string const opt2 = p.getFullOpt();
1892 string const body = p.verbatim_item();
1893 string const opt3 = p.getFullOpt();
1894 string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
1896 string const complete = cmd + "{" + name + '}' +
1897 opt1 + opt2 + '{' + body + '}' + opt3;
1899 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
1901 if (!in_lyx_preamble)
1902 h_preamble << complete;
1905 else if (t.cs() == "def") {
1906 string name = p.get_token().cs();
1907 // In fact, name may be more than the name:
1908 // In the test case of bug 8116
1909 // name == "csname SF@gobble@opt \endcsname".
1910 // Therefore, we need to use asInput() instead of cs().
1911 while (p.next_token().cat() != catBegin)
1912 name += p.get_token().asInput();
1913 if (!in_lyx_preamble)
1914 h_preamble << "\\def\\" << name << '{'
1915 << p.verbatim_item() << "}";
1918 else if (t.cs() == "newcolumntype") {
1919 string const name = p.getArg('{', '}');
1920 trimSpaceAndEol(name);
1922 string opts = p.getOpt();
1923 if (!opts.empty()) {
1924 istringstream is(string(opts, 1));
1927 special_columns_[name[0]] = nargs;
1928 h_preamble << "\\newcolumntype{" << name << "}";
1930 h_preamble << "[" << nargs << "]";
1931 h_preamble << "{" << p.verbatim_item() << "}";
1934 else if (t.cs() == "setcounter") {
1935 string const name = p.getArg('{', '}');
1936 string const content = p.getArg('{', '}');
1937 if (name == "secnumdepth")
1938 h_secnumdepth = content;
1939 else if (name == "tocdepth")
1940 h_tocdepth = content;
1942 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1945 else if (t.cs() == "setlength") {
1946 string const name = p.verbatim_item();
1947 string const content = p.verbatim_item();
1948 // the paragraphs are only not indented when \parindent is set to zero
1949 if (name == "\\parindent" && content != "") {
1950 if (content[0] == '0')
1951 h_paragraph_separation = "skip";
1953 h_paragraph_indentation = translate_len(content);
1954 } else if (name == "\\parskip") {
1955 if (content == "\\smallskipamount")
1956 h_defskip = "smallskip";
1957 else if (content == "\\medskipamount")
1958 h_defskip = "medskip";
1959 else if (content == "\\bigskipamount")
1960 h_defskip = "bigskip";
1962 h_defskip = translate_len(content);
1963 } else if (name == "\\mathindent") {
1964 h_mathindentation = translate_len(content);
1966 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1969 else if (t.cs() == "onehalfspacing")
1970 h_spacing = "onehalf";
1972 else if (t.cs() == "doublespacing")
1973 h_spacing = "double";
1975 else if (t.cs() == "setstretch")
1976 h_spacing = "other " + p.verbatim_item();
1978 else if (t.cs() == "synctex") {
1979 // the scheme is \synctex=value
1980 // where value can only be "1" or "-1"
1981 h_output_sync = "1";
1982 // there can be any character behind the value (e.g. a linebreak or a '\'
1983 // therefore we extract it char by char
1985 string value = p.get_token().asInput();
1987 value += p.get_token().asInput();
1988 h_output_sync_macro = "\\synctex=" + value;
1991 else if (t.cs() == "begin") {
1992 string const name = p.getArg('{', '}');
1993 if (name == "document")
1995 h_preamble << "\\begin{" << name << "}";
1998 else if (t.cs() == "geometry") {
1999 vector<string> opts = split_options(p.getArg('{', '}'));
2000 handle_geometry(opts);
2003 else if (t.cs() == "definecolor") {
2004 string const color = p.getArg('{', '}');
2005 string const space = p.getArg('{', '}');
2006 string const value = p.getArg('{', '}');
2007 if (color == "document_fontcolor" && space == "rgb") {
2008 RGBColor c(RGBColorFromLaTeX(value));
2009 h_fontcolor = X11hexname(c);
2010 } else if (color == "note_fontcolor" && space == "rgb") {
2011 RGBColor c(RGBColorFromLaTeX(value));
2012 h_notefontcolor = X11hexname(c);
2013 } else if (color == "page_backgroundcolor" && space == "rgb") {
2014 RGBColor c(RGBColorFromLaTeX(value));
2015 h_backgroundcolor = X11hexname(c);
2016 } else if (color == "shadecolor" && space == "rgb") {
2017 RGBColor c(RGBColorFromLaTeX(value));
2018 h_boxbgcolor = X11hexname(c);
2020 h_preamble << "\\definecolor{" << color
2021 << "}{" << space << "}{" << value
2026 else if (t.cs() == "bibliographystyle")
2027 h_biblio_style = p.verbatim_item();
2029 else if (t.cs() == "jurabibsetup") {
2030 // FIXME p.getArg('{', '}') is most probably wrong (it
2031 // does not handle nested braces).
2032 // Use p.verbatim_item() instead.
2033 vector<string> jurabibsetup =
2034 split_options(p.getArg('{', '}'));
2035 // add jurabibsetup to the jurabib package options
2036 add_package("jurabib", jurabibsetup);
2037 if (!jurabibsetup.empty()) {
2038 h_preamble << "\\jurabibsetup{"
2039 << join(jurabibsetup, ",") << '}';
2043 else if (t.cs() == "hypersetup") {
2044 vector<string> hypersetup =
2045 split_options(p.verbatim_item());
2046 // add hypersetup to the hyperref package options
2047 handle_hyperref(hypersetup);
2048 if (!hypersetup.empty()) {
2049 h_preamble << "\\hypersetup{"
2050 << join(hypersetup, ",") << '}';
2054 else if (t.cs() == "includeonly") {
2055 vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
2056 for (auto & iofile : includeonlys) {
2057 string filename(normalize_filename(iofile));
2058 string const path = getMasterFilePath(true);
2059 // We want to preserve relative/absolute filenames,
2060 // therefore path is only used for testing
2061 if (!makeAbsPath(filename, path).exists()) {
2062 // The file extension is probably missing.
2063 // Now try to find it out.
2064 string const tex_name =
2065 find_file(filename, path,
2066 known_tex_extensions);
2067 if (!tex_name.empty())
2068 filename = tex_name;
2071 if (makeAbsPath(filename, path).exists())
2072 fix_child_filename(filename);
2074 cerr << "Warning: Could not find included file '"
2075 << filename << "'." << endl;
2076 outname = changeExtension(filename, "lyx");
2077 h_includeonlys.push_back(outname);
2081 else if (is_known(t.cs(), known_if_3arg_commands)) {
2082 // prevent misparsing of \usepackage if it is used
2083 // as an argument (see e.g. our own output of
2084 // \@ifundefined above)
2085 string const arg1 = p.verbatim_item();
2086 string const arg2 = p.verbatim_item();
2087 string const arg3 = p.verbatim_item();
2088 // test case \@ifundefined{date}{}{\date{}}
2089 if (t.cs() == "@ifundefined" && arg1 == "date" &&
2090 arg2.empty() && arg3 == "\\date{}") {
2091 h_suppress_date = "true";
2092 // older tex2lyx versions did output
2093 // \@ifundefined{definecolor}{\usepackage{color}}{}
2094 } else if (t.cs() == "@ifundefined" &&
2095 arg1 == "definecolor" &&
2096 arg2 == "\\usepackage{color}" &&
2098 if (!in_lyx_preamble)
2099 h_preamble << package_beg_sep
2102 << "\\@ifundefined{definecolor}{color}{}"
2105 //\@ifundefined{showcaptionsetup}{}{%
2106 // \PassOptionsToPackage{caption=false}{subfig}}
2107 // that LyX uses for subfloats
2108 } else if (t.cs() == "@ifundefined" &&
2109 arg1 == "showcaptionsetup" && arg2.empty()
2110 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
2112 } else if (!in_lyx_preamble) {
2113 h_preamble << t.asInput()
2114 << '{' << arg1 << '}'
2115 << '{' << arg2 << '}'
2116 << '{' << arg3 << '}';
2120 else if (is_known(t.cs(), known_if_commands)) {
2121 // must not parse anything in conditional code, since
2122 // LyX would output the parsed contents unconditionally
2123 if (!in_lyx_preamble)
2124 h_preamble << t.asInput();
2125 handle_if(p, in_lyx_preamble);
2128 else if (!t.cs().empty() && !in_lyx_preamble)
2129 h_preamble << '\\' << t.cs();
2132 // remove the whitespace
2135 // Force textclass if the user wanted it
2136 if (!forceclass.empty())
2137 h_textclass = forceclass;
2138 tc.setName(h_textclass);
2139 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2140 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2143 if (h_papersides.empty()) {
2146 h_papersides = ss.str();
2149 // If the CJK package is used we cannot set the document language from
2150 // the babel options. Instead, we guess which language is used most
2151 // and set this one.
2152 default_language = h_language;
2153 if (is_full_document &&
2154 (auto_packages.find("CJK") != auto_packages.end() ||
2155 auto_packages.find("CJKutf8") != auto_packages.end())) {
2157 h_language = guessLanguage(p, default_language);
2159 if (explicit_babel && h_language != default_language) {
2160 // We set the document language to a CJK language,
2161 // but babel is explicitly called in the user preamble
2162 // without options. LyX will not add the default
2163 // language to the document options if it is either
2164 // english, or no text is set as default language.
2165 // Therefore we need to add a language option explicitly.
2166 // FIXME: It would be better to remove all babel calls
2167 // from the user preamble, but this is difficult
2168 // without re-introducing bug 7861.
2169 if (h_options.empty())
2170 h_options = lyx2babel(default_language);
2172 h_options += ',' + lyx2babel(default_language);
2178 string Preamble::parseEncoding(Parser & p, string const & forceclass)
2180 TeX2LyXDocClass dummy;
2181 parse(p, forceclass, true, dummy);
2182 if (h_inputencoding != "auto" && h_inputencoding != "default")
2183 return h_inputencoding;
2188 string babel2lyx(string const & language)
2190 char const * const * where = is_known(language, known_languages);
2192 return known_coded_languages[where - known_languages];
2197 string lyx2babel(string const & language)
2199 char const * const * where = is_known(language, known_coded_languages);
2201 return known_languages[where - known_coded_languages];
2206 string Preamble::polyglossia2lyx(string const & language)
2208 char const * const * where = is_known(language, polyglossia_languages);
2210 return coded_polyglossia_languages[where - polyglossia_languages];
2215 string rgbcolor2code(string const & name)
2217 char const * const * where = is_known(name, known_basic_colors);
2219 // "red", "green" etc
2220 return known_basic_color_codes[where - known_basic_colors];
2222 // "255,0,0", "0,255,0" etc
2223 RGBColor c(RGBColorFromLaTeX(name));
2224 return X11hexname(c);