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)
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 opt = process_keyval_opt(options, "refsection");
1078 if (opt == "none" || opt == "part"
1079 || opt == "chapter" || opt == "section"
1080 || opt == "subsection")
1083 cerr << "Ignoring unkown refesection value '"
1086 if (!options.empty()) {
1087 h_biblio_options = join(options, ",");
1092 else if (name == "jurabib") {
1093 h_biblio_style = "jurabib";
1094 h_cite_engine = "jurabib";
1095 h_cite_engine_type = "authoryear";
1096 if (!options.empty())
1097 h_biblio_options = join(options, ",");
1100 else if (name == "bibtopic")
1101 h_use_bibtopic = "true";
1103 else if (name == "hyperref")
1104 handle_hyperref(options);
1106 else if (name == "algorithm2e") {
1107 // Load "algorithm2e" module
1108 addModule("algorithm2e");
1109 // Add the package options to the global document options
1110 if (!options.empty()) {
1111 if (h_options.empty())
1112 h_options = join(options, ",");
1114 h_options += ',' + join(options, ",");
1117 else if (name == "microtype") {
1118 //we internally support only microtype without params
1119 if (options.empty())
1120 h_use_microtype = "true";
1122 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1125 else if (!in_lyx_preamble) {
1126 if (options.empty())
1127 h_preamble << "\\usepackage{" << name << '}';
1129 h_preamble << "\\usepackage[" << opts << "]{"
1133 if (p.next_token().cat() == catNewline ||
1134 (p.next_token().cat() == catSpace &&
1135 p.next_next_token().cat() == catNewline))
1139 // We need to do something with the options...
1140 if (!options.empty() && !detectEncoding)
1141 cerr << "Ignoring options '" << join(options, ",")
1142 << "' of package " << name << '.' << endl;
1144 // remove the whitespace
1149 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1152 Token t = p.get_token();
1153 if (t.cat() == catEscape &&
1154 is_known(t.cs(), known_if_commands))
1155 handle_if(p, in_lyx_preamble);
1157 if (!in_lyx_preamble)
1158 h_preamble << t.asInput();
1159 if (t.cat() == catEscape && t.cs() == "fi")
1166 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1168 // set the quote language
1169 // LyX only knows the following quotes languages:
1170 // english, swedish, german, polish, french and danish
1171 // (quotes for "japanese" and "chinese-traditional" are missing because
1172 // they wouldn't be useful: https://www.lyx.org/trac/ticket/6383)
1173 // conversion list taken from
1174 // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
1175 // (quotes for kazakh and interlingua are unknown)
1177 if (is_known(h_language, known_danish_quotes_languages))
1178 h_quotes_style = "danish";
1180 else if (is_known(h_language, known_french_quotes_languages))
1181 h_quotes_style = "french";
1183 else if (is_known(h_language, known_german_quotes_languages))
1184 h_quotes_style = "german";
1186 else if (is_known(h_language, known_polish_quotes_languages))
1187 h_quotes_style = "polish";
1189 else if (is_known(h_language, known_swedish_quotes_languages))
1190 h_quotes_style = "swedish";
1192 else if (is_known(h_language, known_english_quotes_languages))
1193 h_quotes_style = "english";
1195 if (contains(h_float_placement, "H"))
1196 registerAutomaticallyLoadedPackage("float");
1197 if (h_spacing != "single" && h_spacing != "default")
1198 registerAutomaticallyLoadedPackage("setspace");
1199 if (h_use_packages["amsmath"] == "2") {
1200 // amsbsy and amstext are already provided by amsmath
1201 registerAutomaticallyLoadedPackage("amsbsy");
1202 registerAutomaticallyLoadedPackage("amstext");
1205 // output the LyX file settings
1206 // Important: Keep the version formatting in sync with LyX and
1207 // lyx2lyx (bug 7951)
1208 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1209 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1210 << lyx_version_minor << '\n'
1211 << "\\lyxformat " << LYX_FORMAT << '\n'
1212 << "\\begin_document\n"
1213 << "\\begin_header\n"
1214 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1215 << "\\origin " << origin << "\n"
1216 << "\\textclass " << h_textclass << "\n";
1217 string const raw = subdoc ? empty_string() : h_preamble.str();
1219 os << "\\begin_preamble\n";
1220 for (string::size_type i = 0; i < raw.size(); ++i) {
1221 if (raw[i] == package_beg_sep) {
1222 // Here follows some package loading code that
1223 // must be skipped if the package is loaded
1225 string::size_type j = raw.find(package_mid_sep, i);
1226 if (j == string::npos)
1228 string::size_type k = raw.find(package_end_sep, j);
1229 if (k == string::npos)
1231 string const package = raw.substr(i + 1, j - i - 1);
1232 string const replacement = raw.substr(j + 1, k - j - 1);
1233 if (auto_packages.find(package) == auto_packages.end())
1239 os << "\n\\end_preamble\n";
1241 if (!h_options.empty())
1242 os << "\\options " << h_options << "\n";
1243 os << "\\use_default_options " << h_use_default_options << "\n";
1244 if (!used_modules.empty()) {
1245 os << "\\begin_modules\n";
1246 vector<string>::const_iterator const end = used_modules.end();
1247 vector<string>::const_iterator it = used_modules.begin();
1248 for (; it != end; ++it)
1250 os << "\\end_modules\n";
1252 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1253 << "\\language " << h_language << "\n"
1254 << "\\language_package " << h_language_package << "\n"
1255 << "\\inputencoding " << h_inputencoding << "\n"
1256 << "\\fontencoding " << h_fontencoding << "\n"
1257 << "\\font_roman \"" << h_font_roman[0]
1258 << "\" \"" << h_font_roman[1] << "\"\n"
1259 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1260 << "\\font_typewriter \"" << h_font_typewriter[0]
1261 << "\" \"" << h_font_typewriter[1] << "\"\n"
1262 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1263 << "\\font_default_family " << h_font_default_family << "\n"
1264 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1265 << "\\font_sc " << h_font_sc << "\n"
1266 << "\\font_osf " << h_font_osf << "\n"
1267 << "\\font_sf_scale " << h_font_sf_scale[0]
1268 << ' ' << h_font_sf_scale[1] << '\n'
1269 << "\\font_tt_scale " << h_font_tt_scale[0]
1270 << ' ' << h_font_tt_scale[1] << '\n';
1271 if (!h_font_cjk.empty())
1272 os << "\\font_cjk " << h_font_cjk << '\n';
1273 os << "\\use_microtype " << h_use_microtype << '\n'
1274 << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
1275 << "\\graphics " << h_graphics << '\n'
1276 << "\\default_output_format " << h_default_output_format << "\n"
1277 << "\\output_sync " << h_output_sync << "\n";
1278 if (h_output_sync == "1")
1279 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1280 os << "\\bibtex_command " << h_bibtex_command << "\n"
1281 << "\\index_command " << h_index_command << "\n";
1282 if (!h_float_placement.empty())
1283 os << "\\float_placement " << h_float_placement << "\n";
1284 os << "\\paperfontsize " << h_paperfontsize << "\n"
1285 << "\\spacing " << h_spacing << "\n"
1286 << "\\use_hyperref " << h_use_hyperref << '\n';
1287 if (h_use_hyperref == "true") {
1288 if (!h_pdf_title.empty())
1289 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1290 if (!h_pdf_author.empty())
1291 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1292 if (!h_pdf_subject.empty())
1293 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1294 if (!h_pdf_keywords.empty())
1295 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1296 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1297 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1298 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1299 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1300 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1301 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1302 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1303 "\\pdf_backref " << h_pdf_backref << "\n"
1304 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1305 if (!h_pdf_pagemode.empty())
1306 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1307 if (!h_pdf_quoted_options.empty())
1308 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1310 os << "\\papersize " << h_papersize << "\n"
1311 << "\\use_geometry " << h_use_geometry << '\n';
1312 for (map<string, string>::const_iterator it = h_use_packages.begin();
1313 it != h_use_packages.end(); ++it)
1314 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1315 os << "\\cite_engine " << h_cite_engine << '\n'
1316 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1317 << "\\biblio_style " << h_biblio_style << "\n"
1318 << "\\use_bibtopic " << h_use_bibtopic << "\n";
1319 if (!h_biblio_options.empty())
1320 os << "\\biblio_options " << h_biblio_options << "\n";
1321 if (!h_biblatex_bibstyle.empty())
1322 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
1323 if (!h_biblatex_citestyle.empty())
1324 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
1325 if (!h_multibib.empty())
1326 os << "\\multibib " << h_multibib << "\n";
1327 os << "\\use_indices " << h_use_indices << "\n"
1328 << "\\paperorientation " << h_paperorientation << '\n'
1329 << "\\suppress_date " << h_suppress_date << '\n'
1330 << "\\justification " << h_justification << '\n'
1331 << "\\use_refstyle " << h_use_refstyle << '\n'
1332 << "\\use_minted " << h_use_minted << '\n';
1333 if (!h_fontcolor.empty())
1334 os << "\\fontcolor " << h_fontcolor << '\n';
1335 if (!h_notefontcolor.empty())
1336 os << "\\notefontcolor " << h_notefontcolor << '\n';
1337 if (!h_backgroundcolor.empty())
1338 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1339 if (!h_boxbgcolor.empty())
1340 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1341 if (index_number != 0)
1342 for (int i = 0; i < index_number; i++) {
1343 os << "\\index " << h_index[i] << '\n'
1344 << "\\shortcut " << h_shortcut[i] << '\n'
1345 << "\\color " << h_color << '\n'
1349 os << "\\index " << h_index[0] << '\n'
1350 << "\\shortcut " << h_shortcut[0] << '\n'
1351 << "\\color " << h_color << '\n'
1355 << "\\secnumdepth " << h_secnumdepth << "\n"
1356 << "\\tocdepth " << h_tocdepth << "\n"
1357 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1358 if (h_paragraph_separation == "skip")
1359 os << "\\defskip " << h_defskip << "\n";
1361 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1362 os << "\\is_math_indent " << h_is_mathindent << "\n";
1363 if (!h_mathindentation.empty())
1364 os << "\\math_indentation " << h_mathindentation << "\n";
1365 os << "\\math_numbering_side " << h_math_numbering_side << "\n";
1366 os << "\\quotes_style " << h_quotes_style << "\n"
1367 << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
1368 << "\\papercolumns " << h_papercolumns << "\n"
1369 << "\\papersides " << h_papersides << "\n"
1370 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1371 if (!h_listings_params.empty())
1372 os << "\\listings_params " << h_listings_params << "\n";
1373 os << "\\tracking_changes " << h_tracking_changes << "\n"
1374 << "\\output_changes " << h_output_changes << "\n"
1375 << "\\html_math_output " << h_html_math_output << "\n"
1376 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1377 << "\\html_be_strict " << h_html_be_strict << "\n"
1379 << "\\end_header\n\n"
1380 << "\\begin_body\n";
1385 void Preamble::parse(Parser & p, string const & forceclass,
1386 TeX2LyXDocClass & tc)
1388 // initialize fixed types
1389 special_columns_['D'] = 3;
1390 parse(p, forceclass, false, tc);
1394 void Preamble::parse(Parser & p, string const & forceclass,
1395 bool detectEncoding, TeX2LyXDocClass & tc)
1397 bool is_full_document = false;
1398 bool is_lyx_file = false;
1399 bool in_lyx_preamble = false;
1401 // determine whether this is a full document or a fragment for inclusion
1403 Token const & t = p.get_token();
1405 if (t.cat() == catEscape && t.cs() == "documentclass") {
1406 is_full_document = true;
1412 if (detectEncoding && !is_full_document)
1415 while (is_full_document && p.good()) {
1416 if (detectEncoding && h_inputencoding != "auto" &&
1417 h_inputencoding != "default")
1420 Token const & t = p.get_token();
1423 if (!detectEncoding)
1424 cerr << "t: " << t << '\n';
1430 if (!in_lyx_preamble &&
1431 (t.cat() == catLetter ||
1432 t.cat() == catSuper ||
1433 t.cat() == catSub ||
1434 t.cat() == catOther ||
1435 t.cat() == catMath ||
1436 t.cat() == catActive ||
1437 t.cat() == catBegin ||
1438 t.cat() == catEnd ||
1439 t.cat() == catAlign ||
1440 t.cat() == catParameter))
1441 h_preamble << t.cs();
1443 else if (!in_lyx_preamble &&
1444 (t.cat() == catSpace || t.cat() == catNewline))
1445 h_preamble << t.asInput();
1447 else if (t.cat() == catComment) {
1448 static regex const islyxfile("%% LyX .* created this file");
1449 static regex const usercommands("User specified LaTeX commands");
1451 string const comment = t.asInput();
1453 // magically switch encoding default if it looks like XeLaTeX
1454 static string const magicXeLaTeX =
1455 "% This document must be compiled with XeLaTeX ";
1456 if (comment.size() > magicXeLaTeX.size()
1457 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1458 && h_inputencoding == "auto") {
1459 if (!detectEncoding)
1460 cerr << "XeLaTeX comment found, switching to UTF8\n";
1461 h_inputencoding = "utf8";
1464 if (regex_search(comment, sub, islyxfile)) {
1466 in_lyx_preamble = true;
1467 } else if (is_lyx_file
1468 && regex_search(comment, sub, usercommands))
1469 in_lyx_preamble = false;
1470 else if (!in_lyx_preamble)
1471 h_preamble << t.asInput();
1474 else if (t.cs() == "PassOptionsToPackage") {
1475 string const poptions = p.getArg('{', '}');
1476 string const package = p.verbatim_item();
1477 extra_package_options_.insert(make_pair(package, poptions));
1480 else if (t.cs() == "pagestyle")
1481 h_paperpagestyle = p.verbatim_item();
1483 else if (t.cs() == "setdefaultlanguage") {
1485 // We don't yet care about non-language variant options
1486 // because LyX doesn't support this yet, see bug #8214
1488 string langopts = p.getOpt();
1489 // check if the option contains a variant, if yes, extract it
1490 string::size_type pos_var = langopts.find("variant");
1491 string::size_type i = langopts.find(',', pos_var);
1492 string::size_type k = langopts.find('=', pos_var);
1493 if (pos_var != string::npos){
1495 if (i == string::npos)
1496 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1498 variant = langopts.substr(k + 1, i - k - 1);
1499 h_language = variant;
1503 h_language = p.verbatim_item();
1504 //finally translate the poyglossia name to a LyX name
1505 h_language = polyglossia2lyx(h_language);
1508 else if (t.cs() == "setotherlanguage") {
1509 // We don't yet care about the option because LyX doesn't
1510 // support this yet, see bug #8214
1511 p.hasOpt() ? p.getOpt() : string();
1515 else if (t.cs() == "setmainfont") {
1516 // we don't care about the option
1517 p.hasOpt() ? p.getOpt() : string();
1518 h_font_roman[1] = p.getArg('{', '}');
1521 else if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1522 // LyX currently only supports the scale option
1525 string fontopts = p.getArg('[', ']');
1526 // check if the option contains a scaling, if yes, extract it
1527 string::size_type pos = fontopts.find("Scale");
1528 if (pos != string::npos) {
1529 string::size_type i = fontopts.find(',', pos);
1530 if (i == string::npos)
1531 scale_as_percentage(fontopts.substr(pos + 1), scale);
1533 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1536 if (t.cs() == "setsansfont") {
1538 h_font_sf_scale[1] = scale;
1539 h_font_sans[1] = p.getArg('{', '}');
1542 h_font_tt_scale[1] = scale;
1543 h_font_typewriter[1] = p.getArg('{', '}');
1547 else if (t.cs() == "date") {
1548 string argument = p.getArg('{', '}');
1549 if (argument.empty())
1550 h_suppress_date = "true";
1552 h_preamble << t.asInput() << '{' << argument << '}';
1555 else if (t.cs() == "color") {
1556 string const space =
1557 (p.hasOpt() ? p.getOpt() : string());
1558 string argument = p.getArg('{', '}');
1559 // check the case that a standard color is used
1560 if (space.empty() && is_known(argument, known_basic_colors)) {
1561 h_fontcolor = rgbcolor2code(argument);
1562 registerAutomaticallyLoadedPackage("color");
1563 } else if (space.empty() && argument == "document_fontcolor")
1564 registerAutomaticallyLoadedPackage("color");
1565 // check the case that LyX's document_fontcolor is defined
1566 // but not used for \color
1568 h_preamble << t.asInput();
1570 h_preamble << space;
1571 h_preamble << '{' << argument << '}';
1572 // the color might already be set because \definecolor
1573 // is parsed before this
1578 else if (t.cs() == "pagecolor") {
1579 string argument = p.getArg('{', '}');
1580 // check the case that a standard color is used
1581 if (is_known(argument, known_basic_colors)) {
1582 h_backgroundcolor = rgbcolor2code(argument);
1583 } else if (argument == "page_backgroundcolor")
1584 registerAutomaticallyLoadedPackage("color");
1585 // check the case that LyX's page_backgroundcolor is defined
1586 // but not used for \pagecolor
1588 h_preamble << t.asInput() << '{' << argument << '}';
1589 // the color might already be set because \definecolor
1590 // is parsed before this
1591 h_backgroundcolor = "";
1595 else if (t.cs() == "makeatletter") {
1596 // LyX takes care of this
1597 p.setCatcode('@', catLetter);
1600 else if (t.cs() == "makeatother") {
1601 // LyX takes care of this
1602 p.setCatcode('@', catOther);
1605 else if (t.cs() == "makeindex") {
1606 // LyX will re-add this if a print index command is found
1610 else if (t.cs() == "newindex") {
1611 string const indexname = p.getArg('[', ']');
1612 string const shortcut = p.verbatim_item();
1613 if (!indexname.empty())
1614 h_index[index_number] = indexname;
1616 h_index[index_number] = shortcut;
1617 h_shortcut[index_number] = shortcut;
1622 else if (t.cs() == "addbibresource")
1623 biblatex_bibliographies.push_back(removeExtension(p.getArg('{', '}')));
1625 else if (t.cs() == "bibliography") {
1626 vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
1627 biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
1630 else if (t.cs() == "RS@ifundefined") {
1631 string const name = p.verbatim_item();
1632 string const body1 = p.verbatim_item();
1633 string const body2 = p.verbatim_item();
1634 // only non-lyxspecific stuff
1635 if (in_lyx_preamble &&
1636 (name == "subsecref" || name == "thmref" || name == "lemref"))
1640 ss << '\\' << t.cs();
1641 ss << '{' << name << '}'
1642 << '{' << body1 << '}'
1643 << '{' << body2 << '}';
1644 h_preamble << ss.str();
1648 else if (t.cs() == "AtBeginDocument") {
1649 string const name = p.verbatim_item();
1650 // only non-lyxspecific stuff
1651 if (in_lyx_preamble &&
1652 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
1653 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
1654 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
1655 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
1656 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
1657 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
1658 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
1659 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
1660 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
1661 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
1662 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
1663 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
1664 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
1665 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
1666 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
1670 ss << '\\' << t.cs();
1671 ss << '{' << name << '}';
1672 h_preamble << ss.str();
1676 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1677 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1678 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1679 || t.cs() == "DeclareRobustCommand"
1680 || t.cs() == "DeclareRobustCommandx"
1681 || t.cs() == "ProvideTextCommandDefault"
1682 || t.cs() == "DeclareMathAccent") {
1684 if (p.next_token().character() == '*') {
1688 string const name = p.verbatim_item();
1689 string const opt1 = p.getFullOpt();
1690 string const opt2 = p.getFullOpt();
1691 string const body = p.verbatim_item();
1692 // store the in_lyx_preamble setting
1693 bool const was_in_lyx_preamble = in_lyx_preamble;
1695 if (name == "\\rmdefault")
1696 if (is_known(body, known_roman_fonts)) {
1697 h_font_roman[0] = body;
1699 in_lyx_preamble = true;
1701 if (name == "\\sfdefault")
1702 if (is_known(body, known_sans_fonts)) {
1703 h_font_sans[0] = body;
1705 in_lyx_preamble = true;
1707 if (name == "\\ttdefault")
1708 if (is_known(body, known_typewriter_fonts)) {
1709 h_font_typewriter[0] = body;
1711 in_lyx_preamble = true;
1713 if (name == "\\familydefault") {
1714 string family = body;
1715 // remove leading "\"
1716 h_font_default_family = family.erase(0,1);
1718 in_lyx_preamble = true;
1721 // remove LyX-specific definitions that are re-added by LyX
1723 // \lyxline is an ancient command that is converted by tex2lyx into
1724 // a \rule therefore remove its preamble code
1725 if (name == "\\lyxdot" || name == "\\lyxarrow"
1726 || name == "\\lyxline" || name == "\\LyX") {
1728 in_lyx_preamble = true;
1731 // Add the command to the known commands
1732 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1734 // only non-lyxspecific stuff
1735 if (!in_lyx_preamble) {
1737 ss << '\\' << t.cs();
1740 ss << '{' << name << '}' << opt1 << opt2
1741 << '{' << body << "}";
1742 h_preamble << ss.str();
1744 ostream & out = in_preamble ? h_preamble : os;
1745 out << "\\" << t.cs() << "{" << name << "}"
1746 << opts << "{" << body << "}";
1749 // restore the in_lyx_preamble setting
1750 in_lyx_preamble = was_in_lyx_preamble;
1753 else if (t.cs() == "documentclass") {
1754 vector<string>::iterator it;
1755 vector<string> opts = split_options(p.getArg('[', ']'));
1756 handle_opt(opts, known_fontsizes, h_paperfontsize);
1757 delete_opt(opts, known_fontsizes);
1758 // delete "pt" at the end
1759 string::size_type i = h_paperfontsize.find("pt");
1760 if (i != string::npos)
1761 h_paperfontsize.erase(i);
1762 // The documentclass options are always parsed before the options
1763 // of the babel call so that a language cannot overwrite the babel
1765 handle_opt(opts, known_languages, h_language);
1766 delete_opt(opts, known_languages);
1769 if ((it = find(opts.begin(), opts.end(), "fleqn"))
1771 h_is_mathindent = "1";
1774 // formula numbering side
1775 if ((it = find(opts.begin(), opts.end(), "leqno"))
1777 h_math_numbering_side = "left";
1780 else if ((it = find(opts.begin(), opts.end(), "reqno"))
1782 h_math_numbering_side = "right";
1786 // paper orientation
1787 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1788 h_paperorientation = "landscape";
1792 if ((it = find(opts.begin(), opts.end(), "oneside"))
1797 if ((it = find(opts.begin(), opts.end(), "twoside"))
1803 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1805 h_papercolumns = "1";
1808 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1810 h_papercolumns = "2";
1814 // some size options are known to any document classes, other sizes
1815 // are handled by the \geometry command of the geometry package
1816 handle_opt(opts, known_class_paper_sizes, h_papersize);
1817 delete_opt(opts, known_class_paper_sizes);
1818 // the remaining options
1819 h_options = join(opts, ",");
1820 // FIXME This does not work for classes that have a
1821 // different name in LyX than in LaTeX
1822 h_textclass = p.getArg('{', '}');
1826 else if (t.cs() == "usepackage") {
1827 string const options = p.getArg('[', ']');
1828 string const name = p.getArg('{', '}');
1829 vector<string> vecnames;
1830 split(name, vecnames, ',');
1831 vector<string>::const_iterator it = vecnames.begin();
1832 vector<string>::const_iterator end = vecnames.end();
1833 for (; it != end; ++it)
1834 handle_package(p, trimSpaceAndEol(*it), options,
1835 in_lyx_preamble, detectEncoding);
1838 else if (t.cs() == "inputencoding") {
1839 string const encoding = p.getArg('{','}');
1840 Encoding const * const enc = encodings.fromLaTeXName(
1841 encoding, Encoding::inputenc, true);
1843 if (!detectEncoding)
1844 cerr << "Unknown encoding " << encoding
1845 << ". Ignoring." << std::endl;
1848 h_inputencoding = enc->name();
1849 p.setEncoding(enc->iconvName());
1853 else if (t.cs() == "newenvironment") {
1854 string const name = p.getArg('{', '}');
1855 string const opt1 = p.getFullOpt();
1856 string const opt2 = p.getFullOpt();
1857 string const beg = p.verbatim_item();
1858 string const end = p.verbatim_item();
1859 if (!in_lyx_preamble) {
1860 h_preamble << "\\newenvironment{" << name
1861 << '}' << opt1 << opt2 << '{'
1862 << beg << "}{" << end << '}';
1864 add_known_environment(name, opt1, !opt2.empty(),
1865 from_utf8(beg), from_utf8(end));
1869 else if (t.cs() == "newtheorem") {
1871 if (p.next_token().character() == '*') {
1875 string const name = p.getArg('{', '}');
1876 string const opt1 = p.getFullOpt();
1877 string const opt2 = p.getFullOpt();
1878 string const body = p.verbatim_item();
1879 string const opt3 = p.getFullOpt();
1880 string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
1882 string const complete = cmd + "{" + name + '}' +
1883 opt1 + opt2 + '{' + body + '}' + opt3;
1885 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
1887 if (!in_lyx_preamble)
1888 h_preamble << complete;
1891 else if (t.cs() == "def") {
1892 string name = p.get_token().cs();
1893 // In fact, name may be more than the name:
1894 // In the test case of bug 8116
1895 // name == "csname SF@gobble@opt \endcsname".
1896 // Therefore, we need to use asInput() instead of cs().
1897 while (p.next_token().cat() != catBegin)
1898 name += p.get_token().asInput();
1899 if (!in_lyx_preamble)
1900 h_preamble << "\\def\\" << name << '{'
1901 << p.verbatim_item() << "}";
1904 else if (t.cs() == "newcolumntype") {
1905 string const name = p.getArg('{', '}');
1906 trimSpaceAndEol(name);
1908 string opts = p.getOpt();
1909 if (!opts.empty()) {
1910 istringstream is(string(opts, 1));
1913 special_columns_[name[0]] = nargs;
1914 h_preamble << "\\newcolumntype{" << name << "}";
1916 h_preamble << "[" << nargs << "]";
1917 h_preamble << "{" << p.verbatim_item() << "}";
1920 else if (t.cs() == "setcounter") {
1921 string const name = p.getArg('{', '}');
1922 string const content = p.getArg('{', '}');
1923 if (name == "secnumdepth")
1924 h_secnumdepth = content;
1925 else if (name == "tocdepth")
1926 h_tocdepth = content;
1928 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1931 else if (t.cs() == "setlength") {
1932 string const name = p.verbatim_item();
1933 string const content = p.verbatim_item();
1934 // the paragraphs are only not indented when \parindent is set to zero
1935 if (name == "\\parindent" && content != "") {
1936 if (content[0] == '0')
1937 h_paragraph_separation = "skip";
1939 h_paragraph_indentation = translate_len(content);
1940 } else if (name == "\\parskip") {
1941 if (content == "\\smallskipamount")
1942 h_defskip = "smallskip";
1943 else if (content == "\\medskipamount")
1944 h_defskip = "medskip";
1945 else if (content == "\\bigskipamount")
1946 h_defskip = "bigskip";
1948 h_defskip = translate_len(content);
1949 } else if (name == "\\mathindent") {
1950 h_mathindentation = translate_len(content);
1952 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1955 else if (t.cs() == "onehalfspacing")
1956 h_spacing = "onehalf";
1958 else if (t.cs() == "doublespacing")
1959 h_spacing = "double";
1961 else if (t.cs() == "setstretch")
1962 h_spacing = "other " + p.verbatim_item();
1964 else if (t.cs() == "synctex") {
1965 // the scheme is \synctex=value
1966 // where value can only be "1" or "-1"
1967 h_output_sync = "1";
1968 // there can be any character behind the value (e.g. a linebreak or a '\'
1969 // therefore we extract it char by char
1971 string value = p.get_token().asInput();
1973 value += p.get_token().asInput();
1974 h_output_sync_macro = "\\synctex=" + value;
1977 else if (t.cs() == "begin") {
1978 string const name = p.getArg('{', '}');
1979 if (name == "document")
1981 h_preamble << "\\begin{" << name << "}";
1984 else if (t.cs() == "geometry") {
1985 vector<string> opts = split_options(p.getArg('{', '}'));
1986 handle_geometry(opts);
1989 else if (t.cs() == "definecolor") {
1990 string const color = p.getArg('{', '}');
1991 string const space = p.getArg('{', '}');
1992 string const value = p.getArg('{', '}');
1993 if (color == "document_fontcolor" && space == "rgb") {
1994 RGBColor c(RGBColorFromLaTeX(value));
1995 h_fontcolor = X11hexname(c);
1996 } else if (color == "note_fontcolor" && space == "rgb") {
1997 RGBColor c(RGBColorFromLaTeX(value));
1998 h_notefontcolor = X11hexname(c);
1999 } else if (color == "page_backgroundcolor" && space == "rgb") {
2000 RGBColor c(RGBColorFromLaTeX(value));
2001 h_backgroundcolor = X11hexname(c);
2002 } else if (color == "shadecolor" && space == "rgb") {
2003 RGBColor c(RGBColorFromLaTeX(value));
2004 h_boxbgcolor = X11hexname(c);
2006 h_preamble << "\\definecolor{" << color
2007 << "}{" << space << "}{" << value
2012 else if (t.cs() == "bibliographystyle")
2013 h_biblio_style = p.verbatim_item();
2015 else if (t.cs() == "jurabibsetup") {
2016 // FIXME p.getArg('{', '}') is most probably wrong (it
2017 // does not handle nested braces).
2018 // Use p.verbatim_item() instead.
2019 vector<string> jurabibsetup =
2020 split_options(p.getArg('{', '}'));
2021 // add jurabibsetup to the jurabib package options
2022 add_package("jurabib", jurabibsetup);
2023 if (!jurabibsetup.empty()) {
2024 h_preamble << "\\jurabibsetup{"
2025 << join(jurabibsetup, ",") << '}';
2029 else if (t.cs() == "hypersetup") {
2030 vector<string> hypersetup =
2031 split_options(p.verbatim_item());
2032 // add hypersetup to the hyperref package options
2033 handle_hyperref(hypersetup);
2034 if (!hypersetup.empty()) {
2035 h_preamble << "\\hypersetup{"
2036 << join(hypersetup, ",") << '}';
2040 else if (is_known(t.cs(), known_if_3arg_commands)) {
2041 // prevent misparsing of \usepackage if it is used
2042 // as an argument (see e.g. our own output of
2043 // \@ifundefined above)
2044 string const arg1 = p.verbatim_item();
2045 string const arg2 = p.verbatim_item();
2046 string const arg3 = p.verbatim_item();
2047 // test case \@ifundefined{date}{}{\date{}}
2048 if (t.cs() == "@ifundefined" && arg1 == "date" &&
2049 arg2.empty() && arg3 == "\\date{}") {
2050 h_suppress_date = "true";
2051 // older tex2lyx versions did output
2052 // \@ifundefined{definecolor}{\usepackage{color}}{}
2053 } else if (t.cs() == "@ifundefined" &&
2054 arg1 == "definecolor" &&
2055 arg2 == "\\usepackage{color}" &&
2057 if (!in_lyx_preamble)
2058 h_preamble << package_beg_sep
2061 << "\\@ifundefined{definecolor}{color}{}"
2064 //\@ifundefined{showcaptionsetup}{}{%
2065 // \PassOptionsToPackage{caption=false}{subfig}}
2066 // that LyX uses for subfloats
2067 } else if (t.cs() == "@ifundefined" &&
2068 arg1 == "showcaptionsetup" && arg2.empty()
2069 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
2071 } else if (!in_lyx_preamble) {
2072 h_preamble << t.asInput()
2073 << '{' << arg1 << '}'
2074 << '{' << arg2 << '}'
2075 << '{' << arg3 << '}';
2079 else if (is_known(t.cs(), known_if_commands)) {
2080 // must not parse anything in conditional code, since
2081 // LyX would output the parsed contents unconditionally
2082 if (!in_lyx_preamble)
2083 h_preamble << t.asInput();
2084 handle_if(p, in_lyx_preamble);
2087 else if (!t.cs().empty() && !in_lyx_preamble)
2088 h_preamble << '\\' << t.cs();
2091 // remove the whitespace
2094 // Force textclass if the user wanted it
2095 if (!forceclass.empty())
2096 h_textclass = forceclass;
2097 tc.setName(h_textclass);
2098 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2099 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2102 if (h_papersides.empty()) {
2105 h_papersides = ss.str();
2108 // If the CJK package is used we cannot set the document language from
2109 // the babel options. Instead, we guess which language is used most
2110 // and set this one.
2111 default_language = h_language;
2112 if (is_full_document &&
2113 (auto_packages.find("CJK") != auto_packages.end() ||
2114 auto_packages.find("CJKutf8") != auto_packages.end())) {
2116 h_language = guessLanguage(p, default_language);
2118 if (explicit_babel && h_language != default_language) {
2119 // We set the document language to a CJK language,
2120 // but babel is explicitly called in the user preamble
2121 // without options. LyX will not add the default
2122 // language to the document options if it is either
2123 // english, or no text is set as default language.
2124 // Therefore we need to add a language option explicitly.
2125 // FIXME: It would be better to remove all babel calls
2126 // from the user preamble, but this is difficult
2127 // without re-introducing bug 7861.
2128 if (h_options.empty())
2129 h_options = lyx2babel(default_language);
2131 h_options += ',' + lyx2babel(default_language);
2137 string Preamble::parseEncoding(Parser & p, string const & forceclass)
2139 TeX2LyXDocClass dummy;
2140 parse(p, forceclass, true, dummy);
2141 if (h_inputencoding != "auto" && h_inputencoding != "default")
2142 return h_inputencoding;
2147 string babel2lyx(string const & language)
2149 char const * const * where = is_known(language, known_languages);
2151 return known_coded_languages[where - known_languages];
2156 string lyx2babel(string const & language)
2158 char const * const * where = is_known(language, known_coded_languages);
2160 return known_languages[where - known_coded_languages];
2165 string Preamble::polyglossia2lyx(string const & language)
2167 char const * const * where = is_known(language, polyglossia_languages);
2169 return coded_polyglossia_languages[where - polyglossia_languages];
2174 string rgbcolor2code(string const & name)
2176 char const * const * where = is_known(name, known_basic_colors);
2178 // "red", "green" etc
2179 return known_basic_color_codes[where - known_basic_colors];
2181 // "255,0,0", "0,255,0" etc
2182 RGBColor c(RGBColorFromLaTeX(name));
2183 return X11hexname(c);