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",
176 /// packages that work only in xetex
177 /// polyglossia is handled separately
178 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
179 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
180 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
182 /// packages that are automatically skipped if loaded by LyX
183 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
184 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color",
185 "float", "fontspec", "framed", "graphicx", "hhline", "ifthen", "longtable",
186 "makeidx", "minted", "multirow", "nomencl", "pdfpages", "prettyref", "refstyle",
187 "rotating", "rotfloat", "splitidx", "setspace", "subscript", "textcomp", "tipa",
188 "tipx", "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xcolor",
191 // codes used to remove packages that are loaded automatically by LyX.
192 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
193 const char package_beg_sep = '\001';
194 const char package_mid_sep = '\002';
195 const char package_end_sep = '\003';
198 // returns true if at least one of the options in what has been found
199 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
205 // the last language option is the document language (for babel and LyX)
206 // the last size option is the document font size
207 vector<string>::iterator it;
208 vector<string>::iterator position = opts.begin();
209 for (; *what; ++what) {
210 it = find(opts.begin(), opts.end(), *what);
211 if (it != opts.end()) {
212 if (it >= position) {
223 void delete_opt(vector<string> & opts, char const * const * what)
228 // remove found options from the list
229 // do this after handle_opt to avoid potential memory leaks
230 vector<string>::iterator it;
231 for (; *what; ++what) {
232 it = find(opts.begin(), opts.end(), *what);
233 if (it != opts.end())
240 * Split a package options string (keyval format) into a vector.
242 * authorformat=smallcaps,
244 * titleformat=colonsep,
245 * bibformat={tabular,ibidem,numbered}
247 vector<string> split_options(string const & input)
249 vector<string> options;
253 Token const & t = p.get_token();
254 if (t.asInput() == ",") {
255 options.push_back(trimSpaceAndEol(option));
257 } else if (t.asInput() == "=") {
260 if (p.next_token().asInput() == "{")
261 option += '{' + p.getArg('{', '}') + '}';
262 } else if (t.cat() != catSpace && t.cat() != catComment)
263 option += t.asInput();
267 options.push_back(trimSpaceAndEol(option));
274 * Retrieve a keyval option "name={value with=sign}" named \p name from
275 * \p options and return the value.
276 * The found option is also removed from \p options.
278 string process_keyval_opt(vector<string> & options, string name)
280 for (size_t i = 0; i < options.size(); ++i) {
281 vector<string> option;
282 split(options[i], option, '=');
283 if (option.size() < 2)
285 if (option[0] == name) {
286 options.erase(options.begin() + i);
287 option.erase(option.begin());
288 return join(option, "=");
294 } // anonymous namespace
298 * known polyglossia language names (including variants)
299 * FIXME: support spelling=old for german variants (german vs. ngerman LyX names etc)
301 const char * const Preamble::polyglossia_languages[] = {
302 "albanian", "american", "amharic", "ancient", "arabic", "armenian", "asturian", "australian",
303 "bahasai", "bahasam", "basque", "bengali", "brazil", "brazilian", "breton", "british", "bulgarian",
304 "catalan", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
305 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
306 "galician", "greek", "monotonic", "hebrew", "hindi",
307 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer",
308 "lao", "latin", "latvian", "lithuanian", "lsorbian", "magyar", "malayalam", "marathi",
309 "austrian", "newzealand", "german", "norsk", "nynorsk", "occitan",
310 "piedmontese", "polish", "polytonic", "portuges", "romanian", "romansh", "russian",
311 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovenian", "spanish", "swedish", "syriac",
312 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
313 "ukrainian", "urdu", "usorbian", "vietnamese", "welsh", 0};
314 // not yet supported by LyX: "korean", "nko"
317 * the same as polyglossia_languages with .lyx names
318 * please keep this in sync with polyglossia_languages line by line!
320 const char * const Preamble::coded_polyglossia_languages[] = {
321 "albanian", "american", "amharic", "ancientgreek", "arabic_arabi", "armenian", "asturian", "australian",
322 "bahasa", "bahasam", "basque", "bengali", "brazilian", "brazilian", "breton", "british", "bulgarian",
323 "catalan", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
324 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
325 "galician", "greek", "greek", "hebrew", "hindi",
326 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer",
327 "lao", "latin", "latvian", "lithuanian", "lowersorbian", "magyar", "malayalam", "marathi",
328 "naustrian","newzealand", "ngerman", "norsk", "nynorsk", "occitan",
329 "piedmontese", "polish", "polutonikogreek", "portuges", "romanian", "romansh", "russian",
330 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovene", "spanish", "swedish", "syriac",
331 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
332 "ukrainian", "urdu", "uppersorbian", "vietnamese", "welsh", 0};
333 // not yet supported by LyX: "korean-polyglossia", "nko"
336 bool Preamble::usePolyglossia() const
338 return h_use_non_tex_fonts && h_language_package == "default";
342 bool Preamble::indentParagraphs() const
344 return h_paragraph_separation == "indent";
348 bool Preamble::isPackageUsed(string const & package) const
350 return used_packages.find(package) != used_packages.end();
354 vector<string> Preamble::getPackageOptions(string const & package) const
356 map<string, vector<string> >::const_iterator it = used_packages.find(package);
357 if (it != used_packages.end())
359 return vector<string>();
363 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
365 auto_packages.insert(package);
369 void Preamble::addModule(string const & module)
371 used_modules.push_back(module);
375 void Preamble::suppressDate(bool suppress)
378 h_suppress_date = "true";
380 h_suppress_date = "false";
384 void Preamble::registerAuthor(std::string const & name)
386 Author author(from_utf8(name), empty_docstring());
387 author.setUsed(true);
388 authors_.record(author);
389 h_tracking_changes = "true";
390 h_output_changes = "true";
394 Author const & Preamble::getAuthor(std::string const & name) const
396 Author author(from_utf8(name), empty_docstring());
397 for (AuthorList::Authors::const_iterator it = authors_.begin();
398 it != authors_.end(); ++it)
401 static Author const dummy;
406 int Preamble::getSpecialTableColumnArguments(char c) const
408 map<char, int>::const_iterator it = special_columns_.find(c);
409 if (it == special_columns_.end())
415 void Preamble::add_package(string const & name, vector<string> & options)
417 // every package inherits the global options
418 if (used_packages.find(name) == used_packages.end())
419 used_packages[name] = split_options(h_options);
421 // Insert options passed via PassOptionsToPackage
422 for (auto const & p : extra_package_options_) {
423 if (p.first == name) {
424 vector<string> eo = getVectorFromString(p.second);
425 for (auto const & eoi : eo)
426 options.push_back(eoi);
430 vector<string> & v = used_packages[name];
431 v.insert(v.end(), options.begin(), options.end());
432 if (name == "jurabib") {
433 // Don't output the order argument (see the cite command
434 // handling code in text.cpp).
435 vector<string>::iterator end =
436 remove(options.begin(), options.end(), "natbiborder");
437 end = remove(options.begin(), end, "jurabiborder");
438 options.erase(end, options.end());
445 // Given is a string like "scaled=0.9" or "Scale=0.9", return 0.9 * 100
446 bool scale_as_percentage(string const & scale, string & percentage)
448 string::size_type pos = scale.find('=');
449 if (pos != string::npos) {
450 string value = scale.substr(pos + 1);
451 if (isStrDbl(value)) {
452 percentage = convert<string>(
453 static_cast<int>(100 * convert<double>(value)));
461 string remove_braces(string const & value)
465 if (value[0] == '{' && value[value.length()-1] == '}')
466 return value.substr(1, value.length()-2);
470 } // anonymous namespace
473 Preamble::Preamble() : one_language(true), explicit_babel(false),
474 title_layout_found(false), index_number(0), h_font_cjk_set(false),
475 h_use_microtype("false")
479 h_biblio_style = "plain";
480 h_bibtex_command = "default";
481 h_cite_engine = "basic";
482 h_cite_engine_type = "default";
484 h_defskip = "medskip";
485 h_dynamic_quotes = false;
488 h_fontencoding = "default";
489 h_font_roman[0] = "default";
490 h_font_roman[1] = "default";
491 h_font_sans[0] = "default";
492 h_font_sans[1] = "default";
493 h_font_typewriter[0] = "default";
494 h_font_typewriter[1] = "default";
495 h_font_math[0] = "auto";
496 h_font_math[1] = "auto";
497 h_font_default_family = "default";
498 h_use_non_tex_fonts = false;
500 h_font_osf = "false";
501 h_font_sf_scale[0] = "100";
502 h_font_sf_scale[1] = "100";
503 h_font_tt_scale[0] = "100";
504 h_font_tt_scale[1] = "100";
506 h_is_mathindent = "0";
507 h_math_numbering_side = "default";
508 h_graphics = "default";
509 h_default_output_format = "default";
510 h_html_be_strict = "false";
511 h_html_css_as_file = "0";
512 h_html_math_output = "0";
513 h_index[0] = "Index";
514 h_index_command = "default";
515 h_inputencoding = "auto";
516 h_justification = "true";
517 h_language = "english";
518 h_language_package = "none";
520 h_maintain_unincluded_children = "false";
524 h_output_changes = "false";
526 //h_output_sync_macro
527 h_papercolumns = "1";
528 h_paperfontsize = "default";
529 h_paperorientation = "portrait";
530 h_paperpagestyle = "default";
532 h_papersize = "default";
533 h_paragraph_indentation = "default";
534 h_paragraph_separation = "indent";
539 h_pdf_bookmarks = "0";
540 h_pdf_bookmarksnumbered = "0";
541 h_pdf_bookmarksopen = "0";
542 h_pdf_bookmarksopenlevel = "1";
543 h_pdf_breaklinks = "0";
544 h_pdf_pdfborder = "0";
545 h_pdf_colorlinks = "0";
546 h_pdf_backref = "section";
547 h_pdf_pdfusetitle = "0";
549 //h_pdf_quoted_options;
550 h_quotes_style = "english";
552 h_shortcut[0] = "idx";
553 h_spacing = "single";
554 h_save_transient_properties = "true";
555 h_suppress_date = "false";
556 h_textclass = "article";
558 h_tracking_changes = "false";
559 h_use_bibtopic = "false";
560 h_use_dash_ligatures = "true";
561 h_use_indices = "false";
562 h_use_geometry = "false";
563 h_use_default_options = "false";
564 h_use_hyperref = "false";
565 h_use_microtype = "false";
566 h_use_refstyle = false;
567 h_use_minted = false;
568 h_use_packages["amsmath"] = "1";
569 h_use_packages["amssymb"] = "0";
570 h_use_packages["cancel"] = "0";
571 h_use_packages["esint"] = "1";
572 h_use_packages["mhchem"] = "0";
573 h_use_packages["mathdots"] = "0";
574 h_use_packages["mathtools"] = "0";
575 h_use_packages["stackrel"] = "0";
576 h_use_packages["stmaryrd"] = "0";
577 h_use_packages["undertilde"] = "0";
581 void Preamble::handle_hyperref(vector<string> & options)
583 // FIXME swallow inputencoding changes that might surround the
584 // hyperref setup if it was written by LyX
585 h_use_hyperref = "true";
586 // swallow "unicode=true", since LyX does always write that
587 vector<string>::iterator it =
588 find(options.begin(), options.end(), "unicode=true");
589 if (it != options.end())
591 it = find(options.begin(), options.end(), "pdfusetitle");
592 if (it != options.end()) {
593 h_pdf_pdfusetitle = "1";
596 string bookmarks = process_keyval_opt(options, "bookmarks");
597 if (bookmarks == "true")
598 h_pdf_bookmarks = "1";
599 else if (bookmarks == "false")
600 h_pdf_bookmarks = "0";
601 if (h_pdf_bookmarks == "1") {
602 string bookmarksnumbered =
603 process_keyval_opt(options, "bookmarksnumbered");
604 if (bookmarksnumbered == "true")
605 h_pdf_bookmarksnumbered = "1";
606 else if (bookmarksnumbered == "false")
607 h_pdf_bookmarksnumbered = "0";
608 string bookmarksopen =
609 process_keyval_opt(options, "bookmarksopen");
610 if (bookmarksopen == "true")
611 h_pdf_bookmarksopen = "1";
612 else if (bookmarksopen == "false")
613 h_pdf_bookmarksopen = "0";
614 if (h_pdf_bookmarksopen == "1") {
615 string bookmarksopenlevel =
616 process_keyval_opt(options, "bookmarksopenlevel");
617 if (!bookmarksopenlevel.empty())
618 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
621 string breaklinks = process_keyval_opt(options, "breaklinks");
622 if (breaklinks == "true")
623 h_pdf_breaklinks = "1";
624 else if (breaklinks == "false")
625 h_pdf_breaklinks = "0";
626 string pdfborder = process_keyval_opt(options, "pdfborder");
627 if (pdfborder == "{0 0 0}")
628 h_pdf_pdfborder = "1";
629 else if (pdfborder == "{0 0 1}")
630 h_pdf_pdfborder = "0";
631 string backref = process_keyval_opt(options, "backref");
632 if (!backref.empty())
633 h_pdf_backref = backref;
634 string colorlinks = process_keyval_opt(options, "colorlinks");
635 if (colorlinks == "true")
636 h_pdf_colorlinks = "1";
637 else if (colorlinks == "false")
638 h_pdf_colorlinks = "0";
639 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
640 if (!pdfpagemode.empty())
641 h_pdf_pagemode = pdfpagemode;
642 string pdftitle = process_keyval_opt(options, "pdftitle");
643 if (!pdftitle.empty()) {
644 h_pdf_title = remove_braces(pdftitle);
646 string pdfauthor = process_keyval_opt(options, "pdfauthor");
647 if (!pdfauthor.empty()) {
648 h_pdf_author = remove_braces(pdfauthor);
650 string pdfsubject = process_keyval_opt(options, "pdfsubject");
651 if (!pdfsubject.empty())
652 h_pdf_subject = remove_braces(pdfsubject);
653 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
654 if (!pdfkeywords.empty())
655 h_pdf_keywords = remove_braces(pdfkeywords);
656 if (!options.empty()) {
657 if (!h_pdf_quoted_options.empty())
658 h_pdf_quoted_options += ',';
659 h_pdf_quoted_options += join(options, ",");
665 void Preamble::handle_geometry(vector<string> & options)
667 h_use_geometry = "true";
668 vector<string>::iterator it;
670 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
671 h_paperorientation = "landscape";
675 // keyval version: "paper=letter"
676 string paper = process_keyval_opt(options, "paper");
678 h_papersize = paper + "paper";
679 // alternative version: "letterpaper"
680 handle_opt(options, known_paper_sizes, h_papersize);
681 delete_opt(options, known_paper_sizes);
683 char const * const * margin = known_paper_margins;
684 for (; *margin; ++margin) {
685 string value = process_keyval_opt(options, *margin);
686 if (!value.empty()) {
687 int k = margin - known_paper_margins;
688 string name = known_coded_paper_margins[k];
689 h_margins += '\\' + name + ' ' + value + '\n';
695 void Preamble::handle_package(Parser &p, string const & name,
696 string const & opts, bool in_lyx_preamble,
699 vector<string> options = split_options(opts);
700 add_package(name, options);
702 if (is_known(name, known_xetex_packages)) {
704 h_use_non_tex_fonts = true;
705 registerAutomaticallyLoadedPackage("fontspec");
706 if (h_inputencoding == "auto")
707 p.setEncoding("UTF-8");
711 if (is_known(name, known_roman_fonts))
712 h_font_roman[0] = name;
714 if (name == "fourier") {
715 h_font_roman[0] = "utopia";
716 // when font uses real small capitals
717 if (opts == "expert")
721 if (name == "garamondx") {
722 h_font_roman[0] = "garamondx";
727 if (name == "libertine") {
728 h_font_roman[0] = "libertine";
729 // this automatically invokes biolinum
730 h_font_sans[0] = "biolinum";
733 else if (opts == "lining")
734 h_font_osf = "false";
737 if (name == "libertine-type1") {
738 h_font_roman[0] = "libertine";
739 // NOTE: contrary to libertine.sty, libertine-type1
740 // does not automatically invoke biolinum
741 if (opts == "lining")
742 h_font_osf = "false";
743 else if (opts == "osf")
747 if (name == "mathdesign") {
748 if (opts.find("charter") != string::npos)
749 h_font_roman[0] = "md-charter";
750 if (opts.find("garamond") != string::npos)
751 h_font_roman[0] = "md-garamond";
752 if (opts.find("utopia") != string::npos)
753 h_font_roman[0] = "md-utopia";
754 if (opts.find("expert") != string::npos) {
760 else if (name == "mathpazo")
761 h_font_roman[0] = "palatino";
763 else if (name == "mathptmx")
764 h_font_roman[0] = "times";
766 if (name == "crimson")
767 h_font_roman[0] = "cochineal";
769 if (name == "cochineal") {
770 h_font_roman[0] = "cochineal";
771 // cochineal can have several options, e.g. [proportional,osf]
772 string::size_type pos = opts.find("osf");
773 if (pos != string::npos)
777 if (name == "noto") {
778 // noto can have several options
780 h_font_roman[0] = "NotoSerif-TLF";
781 string::size_type pos = opts.find("rm");
782 if (pos != string::npos)
783 h_font_roman[0] = "NotoSerif-TLF";
784 pos = opts.find("sf");
785 if (pos != string::npos)
786 h_font_sans[0] = "NotoSans-TLF";
787 pos = opts.find("nott");
788 if (pos != string::npos) {
789 h_font_roman[0] = "NotoSerif-TLF";
790 h_font_sans[0] = "NotoSans-TLF";
792 // noto as typewriter is handled in handling of \ttdefault
793 // special cases are handled in handling of \rmdefault and \sfdefault
797 if (is_known(name, known_sans_fonts)) {
798 h_font_sans[0] = name;
799 if (options.size() >= 1) {
800 if (scale_as_percentage(opts, h_font_sf_scale[0]))
805 if (name == "biolinum-type1") {
806 h_font_sans[0] = "biolinum";
807 // biolinum can have several options, e.g. [osf,scaled=0.97]
808 string::size_type pos = opts.find("osf");
809 if (pos != string::npos)
814 if (is_known(name, known_typewriter_fonts)) {
815 // fourier can be set as roman font _only_
816 // fourier as typewriter is handled in handling of \ttdefault
817 if (name != "fourier") {
818 h_font_typewriter[0] = name;
819 if (options.size() >= 1) {
820 if (scale_as_percentage(opts, h_font_tt_scale[0]))
826 if (name == "libertineMono-type1") {
827 h_font_typewriter[0] = "libertine-mono";
830 // font uses old-style figure
835 if (is_known(name, known_math_fonts))
836 h_font_math[0] = name;
838 if (name == "newtxmath") {
840 h_font_math[0] = "newtxmath";
841 else if (opts == "garamondx")
842 h_font_math[0] = "garamondx-ntxm";
843 else if (opts == "libertine")
844 h_font_math[0] = "libertine-ntxm";
845 else if (opts == "minion")
846 h_font_math[0] = "minion-ntxm";
847 else if (opts == "cochineal")
848 h_font_math[0] = "cochineal-ntxm";
853 h_font_math[0] = "iwona-math";
855 if (name == "kurier")
857 h_font_math[0] = "kurier-math";
859 // after the detection and handling of special cases, we can remove the
860 // fonts, otherwise they would appear in the preamble, see bug #7856
861 if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
862 || is_known(name, known_typewriter_fonts) || is_known(name, known_math_fonts))
864 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
865 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
866 name == "esint" || name == "mhchem" || name == "mathdots" ||
867 name == "mathtools" || name == "stackrel" ||
868 name == "stmaryrd" || name == "undertilde")
869 h_use_packages[name] = "2";
871 else if (name == "babel") {
872 h_language_package = "default";
873 // One might think we would have to do nothing if babel is loaded
874 // without any options to prevent pollution of the preamble with this
875 // babel call in every roundtrip.
876 // But the user could have defined babel-specific things afterwards. So
877 // we need to keep it in the preamble to prevent cases like bug #7861.
879 // check if more than one option was used - used later for inputenc
880 if (options.begin() != options.end() - 1)
881 one_language = false;
882 // babel takes the last language of the option of its \usepackage
883 // call as document language. If there is no such language option, the
884 // last language in the documentclass options is used.
885 handle_opt(options, known_languages, h_language);
886 // translate the babel name to a LyX name
887 h_language = babel2lyx(h_language);
888 if (h_language == "japanese") {
889 // For Japanese, the encoding isn't indicated in the source
890 // file, and there's really not much we can do. We could
891 // 1) offer a list of possible encodings to choose from, or
892 // 2) determine the encoding of the file by inspecting it.
893 // For the time being, we leave the encoding alone so that
894 // we don't get iconv errors when making a wrong guess, and
895 // we will output a note at the top of the document
896 // explaining what to do.
897 Encoding const * const enc = encodings.fromIconvName(
898 p.getEncoding(), Encoding::japanese, false);
900 h_inputencoding = enc->name();
901 is_nonCJKJapanese = true;
902 // in this case babel can be removed from the preamble
903 registerAutomaticallyLoadedPackage("babel");
905 // If babel is called with options, LyX puts them by default into the
906 // document class options. This works for most languages, except
907 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
908 // perhaps in future others.
909 // Therefore keep the babel call as it is as the user might have
911 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
913 delete_opt(options, known_languages);
915 h_preamble << "\\usepackage{babel}\n";
916 explicit_babel = true;
920 else if (name == "polyglossia") {
921 h_language_package = "default";
922 h_default_output_format = "pdf4";
923 h_use_non_tex_fonts = true;
925 registerAutomaticallyLoadedPackage("xunicode");
926 if (h_inputencoding == "auto")
927 p.setEncoding("UTF-8");
930 else if (name == "CJK") {
931 // set the encoding to "auto" because it might be set to "default" by the babel handling
932 // and this would not be correct for CJK
933 if (h_inputencoding == "default")
934 h_inputencoding = "auto";
935 registerAutomaticallyLoadedPackage("CJK");
938 else if (name == "CJKutf8") {
939 h_inputencoding = "utf8-cjk";
940 p.setEncoding("UTF-8");
941 registerAutomaticallyLoadedPackage("CJKutf8");
944 else if (name == "fontenc") {
945 h_fontencoding = getStringFromVector(options, ",");
946 /* We could do the following for better round trip support,
947 * but this makes the document less portable, so I skip it:
948 if (h_fontencoding == lyxrc.fontenc)
949 h_fontencoding = "global";
954 else if (name == "inputenc" || name == "luainputenc") {
955 // h_inputencoding is only set when there is not more than one
956 // inputenc option because otherwise h_inputencoding must be
957 // set to "auto" (the default encoding of the document language)
958 // Therefore check that exactly one option is passed to inputenc.
959 // It is also only set when there is not more than one babel
961 if (!options.empty()) {
962 string const encoding = options.back();
963 Encoding const * const enc = encodings.fromLaTeXName(
964 encoding, Encoding::inputenc, true);
967 cerr << "Unknown encoding " << encoding
968 << ". Ignoring." << std::endl;
970 if (!enc->unsafe() && options.size() == 1 && one_language == true)
971 h_inputencoding = enc->name();
972 p.setEncoding(enc->iconvName());
978 else if (name == "srcltx") {
981 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
984 h_output_sync_macro = "\\usepackage{srcltx}";
987 else if (is_known(name, known_old_language_packages)) {
988 // known language packages from the times before babel
989 // if they are found and not also babel, they will be used as
990 // custom language package
991 h_language_package = "\\usepackage{" + name + "}";
994 else if (name == "lyxskak") {
995 // ignore this and its options
996 const char * const o[] = {"ps", "mover", 0};
997 delete_opt(options, o);
1000 else if (is_known(name, known_lyx_packages) && options.empty()) {
1001 if (name == "splitidx")
1002 h_use_indices = "true";
1003 else if (name == "minted")
1004 h_use_minted = true;
1005 else if (name == "refstyle")
1006 h_use_refstyle = true;
1007 else if (name == "prettyref")
1008 h_use_refstyle = false;
1009 if (!in_lyx_preamble) {
1010 h_preamble << package_beg_sep << name
1011 << package_mid_sep << "\\usepackage{"
1013 if (p.next_token().cat() == catNewline ||
1014 (p.next_token().cat() == catSpace &&
1015 p.next_next_token().cat() == catNewline))
1017 h_preamble << package_end_sep;
1021 else if (name == "geometry")
1022 handle_geometry(options);
1024 else if (name == "subfig")
1025 ; // ignore this FIXME: Use the package separator mechanism instead
1027 else if (char const * const * where = is_known(name, known_languages))
1028 h_language = known_coded_languages[where - known_languages];
1030 else if (name == "natbib") {
1031 h_biblio_style = "plainnat";
1032 h_cite_engine = "natbib";
1033 h_cite_engine_type = "authoryear";
1034 vector<string>::iterator it =
1035 find(options.begin(), options.end(), "authoryear");
1036 if (it != options.end())
1039 it = find(options.begin(), options.end(), "numbers");
1040 if (it != options.end()) {
1041 h_cite_engine_type = "numerical";
1045 if (!options.empty())
1046 h_biblio_options = join(options, ",");
1049 else if (name == "biblatex") {
1050 h_biblio_style = "plainnat";
1051 h_cite_engine = "biblatex";
1052 h_cite_engine_type = "authoryear";
1054 vector<string>::iterator it =
1055 find(options.begin(), options.end(), "natbib");
1056 if (it != options.end()) {
1058 h_cite_engine = "biblatex-natbib";
1060 opt = process_keyval_opt(options, "natbib");
1062 h_cite_engine = "biblatex-natbib";
1064 opt = process_keyval_opt(options, "style");
1066 h_biblatex_citestyle = opt;
1067 h_biblatex_bibstyle = opt;
1069 opt = process_keyval_opt(options, "citestyle");
1071 h_biblatex_citestyle = opt;
1072 opt = process_keyval_opt(options, "bibstyle");
1074 h_biblatex_bibstyle = opt;
1076 if (!options.empty()) {
1077 h_biblio_options = join(options, ",");
1082 else if (name == "jurabib") {
1083 h_biblio_style = "jurabib";
1084 h_cite_engine = "jurabib";
1085 h_cite_engine_type = "authoryear";
1086 if (!options.empty())
1087 h_biblio_options = join(options, ",");
1090 else if (name == "bibtopic")
1091 h_use_bibtopic = "true";
1093 else if (name == "hyperref")
1094 handle_hyperref(options);
1096 else if (name == "algorithm2e") {
1097 // Load "algorithm2e" module
1098 addModule("algorithm2e");
1099 // Add the package options to the global document options
1100 if (!options.empty()) {
1101 if (h_options.empty())
1102 h_options = join(options, ",");
1104 h_options += ',' + join(options, ",");
1107 else if (name == "microtype") {
1108 //we internally support only microtype without params
1109 if (options.empty())
1110 h_use_microtype = "true";
1112 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1115 else if (!in_lyx_preamble) {
1116 if (options.empty())
1117 h_preamble << "\\usepackage{" << name << '}';
1119 h_preamble << "\\usepackage[" << opts << "]{"
1123 if (p.next_token().cat() == catNewline ||
1124 (p.next_token().cat() == catSpace &&
1125 p.next_next_token().cat() == catNewline))
1129 // We need to do something with the options...
1130 if (!options.empty() && !detectEncoding)
1131 cerr << "Ignoring options '" << join(options, ",")
1132 << "' of package " << name << '.' << endl;
1134 // remove the whitespace
1139 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1142 Token t = p.get_token();
1143 if (t.cat() == catEscape &&
1144 is_known(t.cs(), known_if_commands))
1145 handle_if(p, in_lyx_preamble);
1147 if (!in_lyx_preamble)
1148 h_preamble << t.asInput();
1149 if (t.cat() == catEscape && t.cs() == "fi")
1156 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1158 // set the quote language
1159 // LyX only knows the following quotes languages:
1160 // english, swedish, german, polish, french and danish
1161 // (quotes for "japanese" and "chinese-traditional" are missing because
1162 // they wouldn't be useful: https://www.lyx.org/trac/ticket/6383)
1163 // conversion list taken from
1164 // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
1165 // (quotes for kazakh and interlingua are unknown)
1167 if (is_known(h_language, known_danish_quotes_languages))
1168 h_quotes_style = "danish";
1170 else if (is_known(h_language, known_french_quotes_languages))
1171 h_quotes_style = "french";
1173 else if (is_known(h_language, known_german_quotes_languages))
1174 h_quotes_style = "german";
1176 else if (is_known(h_language, known_polish_quotes_languages))
1177 h_quotes_style = "polish";
1179 else if (is_known(h_language, known_swedish_quotes_languages))
1180 h_quotes_style = "swedish";
1182 else if (is_known(h_language, known_english_quotes_languages))
1183 h_quotes_style = "english";
1185 if (contains(h_float_placement, "H"))
1186 registerAutomaticallyLoadedPackage("float");
1187 if (h_spacing != "single" && h_spacing != "default")
1188 registerAutomaticallyLoadedPackage("setspace");
1189 if (h_use_packages["amsmath"] == "2") {
1190 // amsbsy and amstext are already provided by amsmath
1191 registerAutomaticallyLoadedPackage("amsbsy");
1192 registerAutomaticallyLoadedPackage("amstext");
1195 // output the LyX file settings
1196 // Important: Keep the version formatting in sync with LyX and
1197 // lyx2lyx (bug 7951)
1198 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1199 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1200 << lyx_version_minor << '\n'
1201 << "\\lyxformat " << LYX_FORMAT << '\n'
1202 << "\\begin_document\n"
1203 << "\\begin_header\n"
1204 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1205 << "\\origin " << origin << "\n"
1206 << "\\textclass " << h_textclass << "\n";
1207 string const raw = subdoc ? empty_string() : h_preamble.str();
1209 os << "\\begin_preamble\n";
1210 for (string::size_type i = 0; i < raw.size(); ++i) {
1211 if (raw[i] == package_beg_sep) {
1212 // Here follows some package loading code that
1213 // must be skipped if the package is loaded
1215 string::size_type j = raw.find(package_mid_sep, i);
1216 if (j == string::npos)
1218 string::size_type k = raw.find(package_end_sep, j);
1219 if (k == string::npos)
1221 string const package = raw.substr(i + 1, j - i - 1);
1222 string const replacement = raw.substr(j + 1, k - j - 1);
1223 if (auto_packages.find(package) == auto_packages.end())
1229 os << "\n\\end_preamble\n";
1231 if (!h_options.empty())
1232 os << "\\options " << h_options << "\n";
1233 os << "\\use_default_options " << h_use_default_options << "\n";
1234 if (!used_modules.empty()) {
1235 os << "\\begin_modules\n";
1236 vector<string>::const_iterator const end = used_modules.end();
1237 vector<string>::const_iterator it = used_modules.begin();
1238 for (; it != end; ++it)
1240 os << "\\end_modules\n";
1242 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1243 << "\\language " << h_language << "\n"
1244 << "\\language_package " << h_language_package << "\n"
1245 << "\\inputencoding " << h_inputencoding << "\n"
1246 << "\\fontencoding " << h_fontencoding << "\n"
1247 << "\\font_roman \"" << h_font_roman[0]
1248 << "\" \"" << h_font_roman[1] << "\"\n"
1249 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1250 << "\\font_typewriter \"" << h_font_typewriter[0]
1251 << "\" \"" << h_font_typewriter[1] << "\"\n"
1252 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1253 << "\\font_default_family " << h_font_default_family << "\n"
1254 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1255 << "\\font_sc " << h_font_sc << "\n"
1256 << "\\font_osf " << h_font_osf << "\n"
1257 << "\\font_sf_scale " << h_font_sf_scale[0]
1258 << ' ' << h_font_sf_scale[1] << '\n'
1259 << "\\font_tt_scale " << h_font_tt_scale[0]
1260 << ' ' << h_font_tt_scale[1] << '\n';
1261 if (!h_font_cjk.empty())
1262 os << "\\font_cjk " << h_font_cjk << '\n';
1263 os << "\\use_microtype " << h_use_microtype << '\n'
1264 << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
1265 << "\\graphics " << h_graphics << '\n'
1266 << "\\default_output_format " << h_default_output_format << "\n"
1267 << "\\output_sync " << h_output_sync << "\n";
1268 if (h_output_sync == "1")
1269 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1270 os << "\\bibtex_command " << h_bibtex_command << "\n"
1271 << "\\index_command " << h_index_command << "\n";
1272 if (!h_float_placement.empty())
1273 os << "\\float_placement " << h_float_placement << "\n";
1274 os << "\\paperfontsize " << h_paperfontsize << "\n"
1275 << "\\spacing " << h_spacing << "\n"
1276 << "\\use_hyperref " << h_use_hyperref << '\n';
1277 if (h_use_hyperref == "true") {
1278 if (!h_pdf_title.empty())
1279 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1280 if (!h_pdf_author.empty())
1281 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1282 if (!h_pdf_subject.empty())
1283 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1284 if (!h_pdf_keywords.empty())
1285 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1286 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1287 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1288 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1289 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1290 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1291 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1292 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1293 "\\pdf_backref " << h_pdf_backref << "\n"
1294 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1295 if (!h_pdf_pagemode.empty())
1296 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1297 if (!h_pdf_quoted_options.empty())
1298 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1300 os << "\\papersize " << h_papersize << "\n"
1301 << "\\use_geometry " << h_use_geometry << '\n';
1302 for (map<string, string>::const_iterator it = h_use_packages.begin();
1303 it != h_use_packages.end(); ++it)
1304 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1305 os << "\\cite_engine " << h_cite_engine << '\n'
1306 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1307 << "\\biblio_style " << h_biblio_style << "\n"
1308 << "\\use_bibtopic " << h_use_bibtopic << "\n";
1309 if (!h_biblio_options.empty())
1310 os << "\\biblio_options " << h_biblio_options << "\n";
1311 if (!h_biblatex_bibstyle.empty())
1312 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
1313 if (!h_biblatex_citestyle.empty())
1314 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
1315 os << "\\use_indices " << h_use_indices << "\n"
1316 << "\\paperorientation " << h_paperorientation << '\n'
1317 << "\\suppress_date " << h_suppress_date << '\n'
1318 << "\\justification " << h_justification << '\n'
1319 << "\\use_refstyle " << h_use_refstyle << '\n'
1320 << "\\use_minted " << h_use_minted << '\n';
1321 if (!h_fontcolor.empty())
1322 os << "\\fontcolor " << h_fontcolor << '\n';
1323 if (!h_notefontcolor.empty())
1324 os << "\\notefontcolor " << h_notefontcolor << '\n';
1325 if (!h_backgroundcolor.empty())
1326 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1327 if (!h_boxbgcolor.empty())
1328 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1329 if (index_number != 0)
1330 for (int i = 0; i < index_number; i++) {
1331 os << "\\index " << h_index[i] << '\n'
1332 << "\\shortcut " << h_shortcut[i] << '\n'
1333 << "\\color " << h_color << '\n'
1337 os << "\\index " << h_index[0] << '\n'
1338 << "\\shortcut " << h_shortcut[0] << '\n'
1339 << "\\color " << h_color << '\n'
1343 << "\\secnumdepth " << h_secnumdepth << "\n"
1344 << "\\tocdepth " << h_tocdepth << "\n"
1345 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1346 if (h_paragraph_separation == "skip")
1347 os << "\\defskip " << h_defskip << "\n";
1349 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1350 os << "\\is_math_indent " << h_is_mathindent << "\n";
1351 if (!h_mathindentation.empty())
1352 os << "\\math_indentation " << h_mathindentation << "\n";
1353 os << "\\math_numbering_side " << h_math_numbering_side << "\n";
1354 os << "\\quotes_style " << h_quotes_style << "\n"
1355 << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
1356 << "\\papercolumns " << h_papercolumns << "\n"
1357 << "\\papersides " << h_papersides << "\n"
1358 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1359 if (!h_listings_params.empty())
1360 os << "\\listings_params " << h_listings_params << "\n";
1361 os << "\\tracking_changes " << h_tracking_changes << "\n"
1362 << "\\output_changes " << h_output_changes << "\n"
1363 << "\\html_math_output " << h_html_math_output << "\n"
1364 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1365 << "\\html_be_strict " << h_html_be_strict << "\n"
1367 << "\\end_header\n\n"
1368 << "\\begin_body\n";
1373 void Preamble::parse(Parser & p, string const & forceclass,
1374 TeX2LyXDocClass & tc)
1376 // initialize fixed types
1377 special_columns_['D'] = 3;
1378 parse(p, forceclass, false, tc);
1382 void Preamble::parse(Parser & p, string const & forceclass,
1383 bool detectEncoding, TeX2LyXDocClass & tc)
1385 bool is_full_document = false;
1386 bool is_lyx_file = false;
1387 bool in_lyx_preamble = false;
1389 // determine whether this is a full document or a fragment for inclusion
1391 Token const & t = p.get_token();
1393 if (t.cat() == catEscape && t.cs() == "documentclass") {
1394 is_full_document = true;
1400 if (detectEncoding && !is_full_document)
1403 while (is_full_document && p.good()) {
1404 if (detectEncoding && h_inputencoding != "auto" &&
1405 h_inputencoding != "default")
1408 Token const & t = p.get_token();
1411 if (!detectEncoding)
1412 cerr << "t: " << t << '\n';
1418 if (!in_lyx_preamble &&
1419 (t.cat() == catLetter ||
1420 t.cat() == catSuper ||
1421 t.cat() == catSub ||
1422 t.cat() == catOther ||
1423 t.cat() == catMath ||
1424 t.cat() == catActive ||
1425 t.cat() == catBegin ||
1426 t.cat() == catEnd ||
1427 t.cat() == catAlign ||
1428 t.cat() == catParameter))
1429 h_preamble << t.cs();
1431 else if (!in_lyx_preamble &&
1432 (t.cat() == catSpace || t.cat() == catNewline))
1433 h_preamble << t.asInput();
1435 else if (t.cat() == catComment) {
1436 static regex const islyxfile("%% LyX .* created this file");
1437 static regex const usercommands("User specified LaTeX commands");
1439 string const comment = t.asInput();
1441 // magically switch encoding default if it looks like XeLaTeX
1442 static string const magicXeLaTeX =
1443 "% This document must be compiled with XeLaTeX ";
1444 if (comment.size() > magicXeLaTeX.size()
1445 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1446 && h_inputencoding == "auto") {
1447 if (!detectEncoding)
1448 cerr << "XeLaTeX comment found, switching to UTF8\n";
1449 h_inputencoding = "utf8";
1452 if (regex_search(comment, sub, islyxfile)) {
1454 in_lyx_preamble = true;
1455 } else if (is_lyx_file
1456 && regex_search(comment, sub, usercommands))
1457 in_lyx_preamble = false;
1458 else if (!in_lyx_preamble)
1459 h_preamble << t.asInput();
1462 else if (t.cs() == "PassOptionsToPackage") {
1463 string const poptions = p.getArg('{', '}');
1464 string const package = p.verbatim_item();
1465 extra_package_options_.insert(make_pair(package, poptions));
1468 else if (t.cs() == "pagestyle")
1469 h_paperpagestyle = p.verbatim_item();
1471 else if (t.cs() == "setdefaultlanguage") {
1473 // We don't yet care about non-language variant options
1474 // because LyX doesn't support this yet, see bug #8214
1476 string langopts = p.getOpt();
1477 // check if the option contains a variant, if yes, extract it
1478 string::size_type pos_var = langopts.find("variant");
1479 string::size_type i = langopts.find(',', pos_var);
1480 string::size_type k = langopts.find('=', pos_var);
1481 if (pos_var != string::npos){
1483 if (i == string::npos)
1484 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1486 variant = langopts.substr(k + 1, i - k - 1);
1487 h_language = variant;
1491 h_language = p.verbatim_item();
1492 //finally translate the poyglossia name to a LyX name
1493 h_language = polyglossia2lyx(h_language);
1496 else if (t.cs() == "setotherlanguage") {
1497 // We don't yet care about the option because LyX doesn't
1498 // support this yet, see bug #8214
1499 p.hasOpt() ? p.getOpt() : string();
1503 else if (t.cs() == "setmainfont") {
1504 // we don't care about the option
1505 p.hasOpt() ? p.getOpt() : string();
1506 h_font_roman[1] = p.getArg('{', '}');
1509 else if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1510 // LyX currently only supports the scale option
1513 string fontopts = p.getArg('[', ']');
1514 // check if the option contains a scaling, if yes, extract it
1515 string::size_type pos = fontopts.find("Scale");
1516 if (pos != string::npos) {
1517 string::size_type i = fontopts.find(',', pos);
1518 if (i == string::npos)
1519 scale_as_percentage(fontopts.substr(pos + 1), scale);
1521 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1524 if (t.cs() == "setsansfont") {
1526 h_font_sf_scale[1] = scale;
1527 h_font_sans[1] = p.getArg('{', '}');
1530 h_font_tt_scale[1] = scale;
1531 h_font_typewriter[1] = p.getArg('{', '}');
1535 else if (t.cs() == "date") {
1536 string argument = p.getArg('{', '}');
1537 if (argument.empty())
1538 h_suppress_date = "true";
1540 h_preamble << t.asInput() << '{' << argument << '}';
1543 else if (t.cs() == "color") {
1544 string const space =
1545 (p.hasOpt() ? p.getOpt() : string());
1546 string argument = p.getArg('{', '}');
1547 // check the case that a standard color is used
1548 if (space.empty() && is_known(argument, known_basic_colors)) {
1549 h_fontcolor = rgbcolor2code(argument);
1550 registerAutomaticallyLoadedPackage("color");
1551 } else if (space.empty() && argument == "document_fontcolor")
1552 registerAutomaticallyLoadedPackage("color");
1553 // check the case that LyX's document_fontcolor is defined
1554 // but not used for \color
1556 h_preamble << t.asInput();
1558 h_preamble << space;
1559 h_preamble << '{' << argument << '}';
1560 // the color might already be set because \definecolor
1561 // is parsed before this
1566 else if (t.cs() == "pagecolor") {
1567 string argument = p.getArg('{', '}');
1568 // check the case that a standard color is used
1569 if (is_known(argument, known_basic_colors)) {
1570 h_backgroundcolor = rgbcolor2code(argument);
1571 } else if (argument == "page_backgroundcolor")
1572 registerAutomaticallyLoadedPackage("color");
1573 // check the case that LyX's page_backgroundcolor is defined
1574 // but not used for \pagecolor
1576 h_preamble << t.asInput() << '{' << argument << '}';
1577 // the color might already be set because \definecolor
1578 // is parsed before this
1579 h_backgroundcolor = "";
1583 else if (t.cs() == "makeatletter") {
1584 // LyX takes care of this
1585 p.setCatcode('@', catLetter);
1588 else if (t.cs() == "makeatother") {
1589 // LyX takes care of this
1590 p.setCatcode('@', catOther);
1593 else if (t.cs() == "makeindex") {
1594 // LyX will re-add this if a print index command is found
1598 else if (t.cs() == "newindex") {
1599 string const indexname = p.getArg('[', ']');
1600 string const shortcut = p.verbatim_item();
1601 if (!indexname.empty())
1602 h_index[index_number] = indexname;
1604 h_index[index_number] = shortcut;
1605 h_shortcut[index_number] = shortcut;
1610 else if (t.cs() == "addbibresource")
1611 biblatex_bibliographies.push_back(removeExtension(p.getArg('{', '}')));
1613 else if (t.cs() == "bibliography") {
1614 vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
1615 biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
1618 else if (t.cs() == "RS@ifundefined") {
1619 string const name = p.verbatim_item();
1620 string const body1 = p.verbatim_item();
1621 string const body2 = p.verbatim_item();
1622 // only non-lyxspecific stuff
1623 if (in_lyx_preamble &&
1624 (name == "subsecref" || name == "thmref" || name == "lemref"))
1628 ss << '\\' << t.cs();
1629 ss << '{' << name << '}'
1630 << '{' << body1 << '}'
1631 << '{' << body2 << '}';
1632 h_preamble << ss.str();
1636 else if (t.cs() == "AtBeginDocument") {
1637 string const name = p.verbatim_item();
1638 // only non-lyxspecific stuff
1639 if (in_lyx_preamble &&
1640 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
1641 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
1642 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
1643 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
1644 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
1645 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
1646 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
1647 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
1648 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
1649 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
1650 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
1651 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
1652 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
1653 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
1654 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
1658 ss << '\\' << t.cs();
1659 ss << '{' << name << '}';
1660 h_preamble << ss.str();
1664 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1665 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1666 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1667 || t.cs() == "DeclareRobustCommand"
1668 || t.cs() == "DeclareRobustCommandx"
1669 || t.cs() == "ProvideTextCommandDefault"
1670 || t.cs() == "DeclareMathAccent") {
1672 if (p.next_token().character() == '*') {
1676 string const name = p.verbatim_item();
1677 string const opt1 = p.getFullOpt();
1678 string const opt2 = p.getFullOpt();
1679 string const body = p.verbatim_item();
1680 // store the in_lyx_preamble setting
1681 bool const was_in_lyx_preamble = in_lyx_preamble;
1683 if (name == "\\rmdefault")
1684 if (is_known(body, known_roman_fonts)) {
1685 h_font_roman[0] = body;
1687 in_lyx_preamble = true;
1689 if (name == "\\sfdefault")
1690 if (is_known(body, known_sans_fonts)) {
1691 h_font_sans[0] = body;
1693 in_lyx_preamble = true;
1695 if (name == "\\ttdefault")
1696 if (is_known(body, known_typewriter_fonts)) {
1697 h_font_typewriter[0] = body;
1699 in_lyx_preamble = true;
1701 if (name == "\\familydefault") {
1702 string family = body;
1703 // remove leading "\"
1704 h_font_default_family = family.erase(0,1);
1706 in_lyx_preamble = true;
1709 // remove LyX-specific definitions that are re-added by LyX
1711 // \lyxline is an ancient command that is converted by tex2lyx into
1712 // a \rule therefore remove its preamble code
1713 if (name == "\\lyxdot" || name == "\\lyxarrow"
1714 || name == "\\lyxline" || name == "\\LyX") {
1716 in_lyx_preamble = true;
1719 // Add the command to the known commands
1720 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1722 // only non-lyxspecific stuff
1723 if (!in_lyx_preamble) {
1725 ss << '\\' << t.cs();
1728 ss << '{' << name << '}' << opt1 << opt2
1729 << '{' << body << "}";
1730 h_preamble << ss.str();
1732 ostream & out = in_preamble ? h_preamble : os;
1733 out << "\\" << t.cs() << "{" << name << "}"
1734 << opts << "{" << body << "}";
1737 // restore the in_lyx_preamble setting
1738 in_lyx_preamble = was_in_lyx_preamble;
1741 else if (t.cs() == "documentclass") {
1742 vector<string>::iterator it;
1743 vector<string> opts = split_options(p.getArg('[', ']'));
1744 handle_opt(opts, known_fontsizes, h_paperfontsize);
1745 delete_opt(opts, known_fontsizes);
1746 // delete "pt" at the end
1747 string::size_type i = h_paperfontsize.find("pt");
1748 if (i != string::npos)
1749 h_paperfontsize.erase(i);
1750 // The documentclass options are always parsed before the options
1751 // of the babel call so that a language cannot overwrite the babel
1753 handle_opt(opts, known_languages, h_language);
1754 delete_opt(opts, known_languages);
1757 if ((it = find(opts.begin(), opts.end(), "fleqn"))
1759 h_is_mathindent = "1";
1762 // formula numbering side
1763 if ((it = find(opts.begin(), opts.end(), "leqno"))
1765 h_math_numbering_side = "left";
1768 else if ((it = find(opts.begin(), opts.end(), "reqno"))
1770 h_math_numbering_side = "right";
1774 // paper orientation
1775 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1776 h_paperorientation = "landscape";
1780 if ((it = find(opts.begin(), opts.end(), "oneside"))
1785 if ((it = find(opts.begin(), opts.end(), "twoside"))
1791 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1793 h_papercolumns = "1";
1796 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1798 h_papercolumns = "2";
1802 // some size options are known to any document classes, other sizes
1803 // are handled by the \geometry command of the geometry package
1804 handle_opt(opts, known_class_paper_sizes, h_papersize);
1805 delete_opt(opts, known_class_paper_sizes);
1806 // the remaining options
1807 h_options = join(opts, ",");
1808 // FIXME This does not work for classes that have a
1809 // different name in LyX than in LaTeX
1810 h_textclass = p.getArg('{', '}');
1814 else if (t.cs() == "usepackage") {
1815 string const options = p.getArg('[', ']');
1816 string const name = p.getArg('{', '}');
1817 vector<string> vecnames;
1818 split(name, vecnames, ',');
1819 vector<string>::const_iterator it = vecnames.begin();
1820 vector<string>::const_iterator end = vecnames.end();
1821 for (; it != end; ++it)
1822 handle_package(p, trimSpaceAndEol(*it), options,
1823 in_lyx_preamble, detectEncoding);
1826 else if (t.cs() == "inputencoding") {
1827 string const encoding = p.getArg('{','}');
1828 Encoding const * const enc = encodings.fromLaTeXName(
1829 encoding, Encoding::inputenc, true);
1831 if (!detectEncoding)
1832 cerr << "Unknown encoding " << encoding
1833 << ". Ignoring." << std::endl;
1836 h_inputencoding = enc->name();
1837 p.setEncoding(enc->iconvName());
1841 else if (t.cs() == "newenvironment") {
1842 string const name = p.getArg('{', '}');
1843 string const opt1 = p.getFullOpt();
1844 string const opt2 = p.getFullOpt();
1845 string const beg = p.verbatim_item();
1846 string const end = p.verbatim_item();
1847 if (!in_lyx_preamble) {
1848 h_preamble << "\\newenvironment{" << name
1849 << '}' << opt1 << opt2 << '{'
1850 << beg << "}{" << end << '}';
1852 add_known_environment(name, opt1, !opt2.empty(),
1853 from_utf8(beg), from_utf8(end));
1857 else if (t.cs() == "newtheorem") {
1859 if (p.next_token().character() == '*') {
1863 string const name = p.getArg('{', '}');
1864 string const opt1 = p.getFullOpt();
1865 string const opt2 = p.getFullOpt();
1866 string const body = p.verbatim_item();
1867 string const opt3 = p.getFullOpt();
1868 string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
1870 string const complete = cmd + "{" + name + '}' +
1871 opt1 + opt2 + '{' + body + '}' + opt3;
1873 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
1875 if (!in_lyx_preamble)
1876 h_preamble << complete;
1879 else if (t.cs() == "def") {
1880 string name = p.get_token().cs();
1881 // In fact, name may be more than the name:
1882 // In the test case of bug 8116
1883 // name == "csname SF@gobble@opt \endcsname".
1884 // Therefore, we need to use asInput() instead of cs().
1885 while (p.next_token().cat() != catBegin)
1886 name += p.get_token().asInput();
1887 if (!in_lyx_preamble)
1888 h_preamble << "\\def\\" << name << '{'
1889 << p.verbatim_item() << "}";
1892 else if (t.cs() == "newcolumntype") {
1893 string const name = p.getArg('{', '}');
1894 trimSpaceAndEol(name);
1896 string opts = p.getOpt();
1897 if (!opts.empty()) {
1898 istringstream is(string(opts, 1));
1901 special_columns_[name[0]] = nargs;
1902 h_preamble << "\\newcolumntype{" << name << "}";
1904 h_preamble << "[" << nargs << "]";
1905 h_preamble << "{" << p.verbatim_item() << "}";
1908 else if (t.cs() == "setcounter") {
1909 string const name = p.getArg('{', '}');
1910 string const content = p.getArg('{', '}');
1911 if (name == "secnumdepth")
1912 h_secnumdepth = content;
1913 else if (name == "tocdepth")
1914 h_tocdepth = content;
1916 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1919 else if (t.cs() == "setlength") {
1920 string const name = p.verbatim_item();
1921 string const content = p.verbatim_item();
1922 // the paragraphs are only not indented when \parindent is set to zero
1923 if (name == "\\parindent" && content != "") {
1924 if (content[0] == '0')
1925 h_paragraph_separation = "skip";
1927 h_paragraph_indentation = translate_len(content);
1928 } else if (name == "\\parskip") {
1929 if (content == "\\smallskipamount")
1930 h_defskip = "smallskip";
1931 else if (content == "\\medskipamount")
1932 h_defskip = "medskip";
1933 else if (content == "\\bigskipamount")
1934 h_defskip = "bigskip";
1936 h_defskip = translate_len(content);
1937 } else if (name == "\\mathindent") {
1938 h_mathindentation = translate_len(content);
1940 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1943 else if (t.cs() == "onehalfspacing")
1944 h_spacing = "onehalf";
1946 else if (t.cs() == "doublespacing")
1947 h_spacing = "double";
1949 else if (t.cs() == "setstretch")
1950 h_spacing = "other " + p.verbatim_item();
1952 else if (t.cs() == "synctex") {
1953 // the scheme is \synctex=value
1954 // where value can only be "1" or "-1"
1955 h_output_sync = "1";
1956 // there can be any character behind the value (e.g. a linebreak or a '\'
1957 // therefore we extract it char by char
1959 string value = p.get_token().asInput();
1961 value += p.get_token().asInput();
1962 h_output_sync_macro = "\\synctex=" + value;
1965 else if (t.cs() == "begin") {
1966 string const name = p.getArg('{', '}');
1967 if (name == "document")
1969 h_preamble << "\\begin{" << name << "}";
1972 else if (t.cs() == "geometry") {
1973 vector<string> opts = split_options(p.getArg('{', '}'));
1974 handle_geometry(opts);
1977 else if (t.cs() == "definecolor") {
1978 string const color = p.getArg('{', '}');
1979 string const space = p.getArg('{', '}');
1980 string const value = p.getArg('{', '}');
1981 if (color == "document_fontcolor" && space == "rgb") {
1982 RGBColor c(RGBColorFromLaTeX(value));
1983 h_fontcolor = X11hexname(c);
1984 } else if (color == "note_fontcolor" && space == "rgb") {
1985 RGBColor c(RGBColorFromLaTeX(value));
1986 h_notefontcolor = X11hexname(c);
1987 } else if (color == "page_backgroundcolor" && space == "rgb") {
1988 RGBColor c(RGBColorFromLaTeX(value));
1989 h_backgroundcolor = X11hexname(c);
1990 } else if (color == "shadecolor" && space == "rgb") {
1991 RGBColor c(RGBColorFromLaTeX(value));
1992 h_boxbgcolor = X11hexname(c);
1994 h_preamble << "\\definecolor{" << color
1995 << "}{" << space << "}{" << value
2000 else if (t.cs() == "bibliographystyle")
2001 h_biblio_style = p.verbatim_item();
2003 else if (t.cs() == "jurabibsetup") {
2004 // FIXME p.getArg('{', '}') is most probably wrong (it
2005 // does not handle nested braces).
2006 // Use p.verbatim_item() instead.
2007 vector<string> jurabibsetup =
2008 split_options(p.getArg('{', '}'));
2009 // add jurabibsetup to the jurabib package options
2010 add_package("jurabib", jurabibsetup);
2011 if (!jurabibsetup.empty()) {
2012 h_preamble << "\\jurabibsetup{"
2013 << join(jurabibsetup, ",") << '}';
2017 else if (t.cs() == "hypersetup") {
2018 vector<string> hypersetup =
2019 split_options(p.verbatim_item());
2020 // add hypersetup to the hyperref package options
2021 handle_hyperref(hypersetup);
2022 if (!hypersetup.empty()) {
2023 h_preamble << "\\hypersetup{"
2024 << join(hypersetup, ",") << '}';
2028 else if (is_known(t.cs(), known_if_3arg_commands)) {
2029 // prevent misparsing of \usepackage if it is used
2030 // as an argument (see e.g. our own output of
2031 // \@ifundefined above)
2032 string const arg1 = p.verbatim_item();
2033 string const arg2 = p.verbatim_item();
2034 string const arg3 = p.verbatim_item();
2035 // test case \@ifundefined{date}{}{\date{}}
2036 if (t.cs() == "@ifundefined" && arg1 == "date" &&
2037 arg2.empty() && arg3 == "\\date{}") {
2038 h_suppress_date = "true";
2039 // older tex2lyx versions did output
2040 // \@ifundefined{definecolor}{\usepackage{color}}{}
2041 } else if (t.cs() == "@ifundefined" &&
2042 arg1 == "definecolor" &&
2043 arg2 == "\\usepackage{color}" &&
2045 if (!in_lyx_preamble)
2046 h_preamble << package_beg_sep
2049 << "\\@ifundefined{definecolor}{color}{}"
2052 //\@ifundefined{showcaptionsetup}{}{%
2053 // \PassOptionsToPackage{caption=false}{subfig}}
2054 // that LyX uses for subfloats
2055 } else if (t.cs() == "@ifundefined" &&
2056 arg1 == "showcaptionsetup" && arg2.empty()
2057 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
2059 } else if (!in_lyx_preamble) {
2060 h_preamble << t.asInput()
2061 << '{' << arg1 << '}'
2062 << '{' << arg2 << '}'
2063 << '{' << arg3 << '}';
2067 else if (is_known(t.cs(), known_if_commands)) {
2068 // must not parse anything in conditional code, since
2069 // LyX would output the parsed contents unconditionally
2070 if (!in_lyx_preamble)
2071 h_preamble << t.asInput();
2072 handle_if(p, in_lyx_preamble);
2075 else if (!t.cs().empty() && !in_lyx_preamble)
2076 h_preamble << '\\' << t.cs();
2079 // remove the whitespace
2082 // Force textclass if the user wanted it
2083 if (!forceclass.empty())
2084 h_textclass = forceclass;
2085 tc.setName(h_textclass);
2086 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2087 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2090 if (h_papersides.empty()) {
2093 h_papersides = ss.str();
2096 // If the CJK package is used we cannot set the document language from
2097 // the babel options. Instead, we guess which language is used most
2098 // and set this one.
2099 default_language = h_language;
2100 if (is_full_document &&
2101 (auto_packages.find("CJK") != auto_packages.end() ||
2102 auto_packages.find("CJKutf8") != auto_packages.end())) {
2104 h_language = guessLanguage(p, default_language);
2106 if (explicit_babel && h_language != default_language) {
2107 // We set the document language to a CJK language,
2108 // but babel is explicitly called in the user preamble
2109 // without options. LyX will not add the default
2110 // language to the document options if it is either
2111 // english, or no text is set as default language.
2112 // Therefore we need to add a language option explicitly.
2113 // FIXME: It would be better to remove all babel calls
2114 // from the user preamble, but this is difficult
2115 // without re-introducing bug 7861.
2116 if (h_options.empty())
2117 h_options = lyx2babel(default_language);
2119 h_options += ',' + lyx2babel(default_language);
2125 string Preamble::parseEncoding(Parser & p, string const & forceclass)
2127 TeX2LyXDocClass dummy;
2128 parse(p, forceclass, true, dummy);
2129 if (h_inputencoding != "auto" && h_inputencoding != "default")
2130 return h_inputencoding;
2135 string babel2lyx(string const & language)
2137 char const * const * where = is_known(language, known_languages);
2139 return known_coded_languages[where - known_languages];
2144 string lyx2babel(string const & language)
2146 char const * const * where = is_known(language, known_coded_languages);
2148 return known_languages[where - known_coded_languages];
2153 string Preamble::polyglossia2lyx(string const & language)
2155 char const * const * where = is_known(language, polyglossia_languages);
2157 return coded_polyglossia_languages[where - polyglossia_languages];
2162 string rgbcolor2code(string const & name)
2164 char const * const * where = is_known(name, known_basic_colors);
2166 // "red", "green" etc
2167 return known_basic_color_codes[where - known_basic_colors];
2169 // "255,0,0", "0,255,0" etc
2170 RGBColor c(RGBColorFromLaTeX(name));
2171 return X11hexname(c);