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 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 == "chapterbib")
1104 h_multibib = "child";
1106 else if (name == "hyperref")
1107 handle_hyperref(options);
1109 else if (name == "algorithm2e") {
1110 // Load "algorithm2e" module
1111 addModule("algorithm2e");
1112 // Add the package options to the global document options
1113 if (!options.empty()) {
1114 if (h_options.empty())
1115 h_options = join(options, ",");
1117 h_options += ',' + join(options, ",");
1120 else if (name == "microtype") {
1121 //we internally support only microtype without params
1122 if (options.empty())
1123 h_use_microtype = "true";
1125 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1128 else if (!in_lyx_preamble) {
1129 if (options.empty())
1130 h_preamble << "\\usepackage{" << name << '}';
1132 h_preamble << "\\usepackage[" << opts << "]{"
1136 if (p.next_token().cat() == catNewline ||
1137 (p.next_token().cat() == catSpace &&
1138 p.next_next_token().cat() == catNewline))
1142 // We need to do something with the options...
1143 if (!options.empty() && !detectEncoding)
1144 cerr << "Ignoring options '" << join(options, ",")
1145 << "' of package " << name << '.' << endl;
1147 // remove the whitespace
1152 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1155 Token t = p.get_token();
1156 if (t.cat() == catEscape &&
1157 is_known(t.cs(), known_if_commands))
1158 handle_if(p, in_lyx_preamble);
1160 if (!in_lyx_preamble)
1161 h_preamble << t.asInput();
1162 if (t.cat() == catEscape && t.cs() == "fi")
1169 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1171 // set the quote language
1172 // LyX only knows the following quotes languages:
1173 // english, swedish, german, polish, french and danish
1174 // (quotes for "japanese" and "chinese-traditional" are missing because
1175 // they wouldn't be useful: https://www.lyx.org/trac/ticket/6383)
1176 // conversion list taken from
1177 // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
1178 // (quotes for kazakh and interlingua are unknown)
1180 if (is_known(h_language, known_danish_quotes_languages))
1181 h_quotes_style = "danish";
1183 else if (is_known(h_language, known_french_quotes_languages))
1184 h_quotes_style = "french";
1186 else if (is_known(h_language, known_german_quotes_languages))
1187 h_quotes_style = "german";
1189 else if (is_known(h_language, known_polish_quotes_languages))
1190 h_quotes_style = "polish";
1192 else if (is_known(h_language, known_swedish_quotes_languages))
1193 h_quotes_style = "swedish";
1195 else if (is_known(h_language, known_english_quotes_languages))
1196 h_quotes_style = "english";
1198 if (contains(h_float_placement, "H"))
1199 registerAutomaticallyLoadedPackage("float");
1200 if (h_spacing != "single" && h_spacing != "default")
1201 registerAutomaticallyLoadedPackage("setspace");
1202 if (h_use_packages["amsmath"] == "2") {
1203 // amsbsy and amstext are already provided by amsmath
1204 registerAutomaticallyLoadedPackage("amsbsy");
1205 registerAutomaticallyLoadedPackage("amstext");
1208 // output the LyX file settings
1209 // Important: Keep the version formatting in sync with LyX and
1210 // lyx2lyx (bug 7951)
1211 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1212 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1213 << lyx_version_minor << '\n'
1214 << "\\lyxformat " << LYX_FORMAT << '\n'
1215 << "\\begin_document\n"
1216 << "\\begin_header\n"
1217 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1218 << "\\origin " << origin << "\n"
1219 << "\\textclass " << h_textclass << "\n";
1220 string const raw = subdoc ? empty_string() : h_preamble.str();
1222 os << "\\begin_preamble\n";
1223 for (string::size_type i = 0; i < raw.size(); ++i) {
1224 if (raw[i] == package_beg_sep) {
1225 // Here follows some package loading code that
1226 // must be skipped if the package is loaded
1228 string::size_type j = raw.find(package_mid_sep, i);
1229 if (j == string::npos)
1231 string::size_type k = raw.find(package_end_sep, j);
1232 if (k == string::npos)
1234 string const package = raw.substr(i + 1, j - i - 1);
1235 string const replacement = raw.substr(j + 1, k - j - 1);
1236 if (auto_packages.find(package) == auto_packages.end())
1242 os << "\n\\end_preamble\n";
1244 if (!h_options.empty())
1245 os << "\\options " << h_options << "\n";
1246 os << "\\use_default_options " << h_use_default_options << "\n";
1247 if (!used_modules.empty()) {
1248 os << "\\begin_modules\n";
1249 vector<string>::const_iterator const end = used_modules.end();
1250 vector<string>::const_iterator it = used_modules.begin();
1251 for (; it != end; ++it)
1253 os << "\\end_modules\n";
1255 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1256 << "\\language " << h_language << "\n"
1257 << "\\language_package " << h_language_package << "\n"
1258 << "\\inputencoding " << h_inputencoding << "\n"
1259 << "\\fontencoding " << h_fontencoding << "\n"
1260 << "\\font_roman \"" << h_font_roman[0]
1261 << "\" \"" << h_font_roman[1] << "\"\n"
1262 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1263 << "\\font_typewriter \"" << h_font_typewriter[0]
1264 << "\" \"" << h_font_typewriter[1] << "\"\n"
1265 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1266 << "\\font_default_family " << h_font_default_family << "\n"
1267 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1268 << "\\font_sc " << h_font_sc << "\n"
1269 << "\\font_osf " << h_font_osf << "\n"
1270 << "\\font_sf_scale " << h_font_sf_scale[0]
1271 << ' ' << h_font_sf_scale[1] << '\n'
1272 << "\\font_tt_scale " << h_font_tt_scale[0]
1273 << ' ' << h_font_tt_scale[1] << '\n';
1274 if (!h_font_cjk.empty())
1275 os << "\\font_cjk " << h_font_cjk << '\n';
1276 os << "\\use_microtype " << h_use_microtype << '\n'
1277 << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
1278 << "\\graphics " << h_graphics << '\n'
1279 << "\\default_output_format " << h_default_output_format << "\n"
1280 << "\\output_sync " << h_output_sync << "\n";
1281 if (h_output_sync == "1")
1282 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1283 os << "\\bibtex_command " << h_bibtex_command << "\n"
1284 << "\\index_command " << h_index_command << "\n";
1285 if (!h_float_placement.empty())
1286 os << "\\float_placement " << h_float_placement << "\n";
1287 os << "\\paperfontsize " << h_paperfontsize << "\n"
1288 << "\\spacing " << h_spacing << "\n"
1289 << "\\use_hyperref " << h_use_hyperref << '\n';
1290 if (h_use_hyperref == "true") {
1291 if (!h_pdf_title.empty())
1292 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1293 if (!h_pdf_author.empty())
1294 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1295 if (!h_pdf_subject.empty())
1296 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1297 if (!h_pdf_keywords.empty())
1298 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1299 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1300 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1301 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1302 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1303 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1304 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1305 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1306 "\\pdf_backref " << h_pdf_backref << "\n"
1307 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1308 if (!h_pdf_pagemode.empty())
1309 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1310 if (!h_pdf_quoted_options.empty())
1311 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1313 os << "\\papersize " << h_papersize << "\n"
1314 << "\\use_geometry " << h_use_geometry << '\n';
1315 for (map<string, string>::const_iterator it = h_use_packages.begin();
1316 it != h_use_packages.end(); ++it)
1317 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1318 os << "\\cite_engine " << h_cite_engine << '\n'
1319 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1320 << "\\biblio_style " << h_biblio_style << "\n"
1321 << "\\use_bibtopic " << h_use_bibtopic << "\n";
1322 if (!h_biblio_options.empty())
1323 os << "\\biblio_options " << h_biblio_options << "\n";
1324 if (!h_biblatex_bibstyle.empty())
1325 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
1326 if (!h_biblatex_citestyle.empty())
1327 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
1328 if (!h_multibib.empty())
1329 os << "\\multibib " << h_multibib << "\n";
1330 os << "\\use_indices " << h_use_indices << "\n"
1331 << "\\paperorientation " << h_paperorientation << '\n'
1332 << "\\suppress_date " << h_suppress_date << '\n'
1333 << "\\justification " << h_justification << '\n'
1334 << "\\use_refstyle " << h_use_refstyle << '\n'
1335 << "\\use_minted " << h_use_minted << '\n';
1336 if (!h_fontcolor.empty())
1337 os << "\\fontcolor " << h_fontcolor << '\n';
1338 if (!h_notefontcolor.empty())
1339 os << "\\notefontcolor " << h_notefontcolor << '\n';
1340 if (!h_backgroundcolor.empty())
1341 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1342 if (!h_boxbgcolor.empty())
1343 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1344 if (index_number != 0)
1345 for (int i = 0; i < index_number; i++) {
1346 os << "\\index " << h_index[i] << '\n'
1347 << "\\shortcut " << h_shortcut[i] << '\n'
1348 << "\\color " << h_color << '\n'
1352 os << "\\index " << h_index[0] << '\n'
1353 << "\\shortcut " << h_shortcut[0] << '\n'
1354 << "\\color " << h_color << '\n'
1358 << "\\secnumdepth " << h_secnumdepth << "\n"
1359 << "\\tocdepth " << h_tocdepth << "\n"
1360 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1361 if (h_paragraph_separation == "skip")
1362 os << "\\defskip " << h_defskip << "\n";
1364 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1365 os << "\\is_math_indent " << h_is_mathindent << "\n";
1366 if (!h_mathindentation.empty())
1367 os << "\\math_indentation " << h_mathindentation << "\n";
1368 os << "\\math_numbering_side " << h_math_numbering_side << "\n";
1369 os << "\\quotes_style " << h_quotes_style << "\n"
1370 << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
1371 << "\\papercolumns " << h_papercolumns << "\n"
1372 << "\\papersides " << h_papersides << "\n"
1373 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1374 if (!h_listings_params.empty())
1375 os << "\\listings_params " << h_listings_params << "\n";
1376 os << "\\tracking_changes " << h_tracking_changes << "\n"
1377 << "\\output_changes " << h_output_changes << "\n"
1378 << "\\html_math_output " << h_html_math_output << "\n"
1379 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1380 << "\\html_be_strict " << h_html_be_strict << "\n"
1382 << "\\end_header\n\n"
1383 << "\\begin_body\n";
1388 void Preamble::parse(Parser & p, string const & forceclass,
1389 TeX2LyXDocClass & tc)
1391 // initialize fixed types
1392 special_columns_['D'] = 3;
1393 parse(p, forceclass, false, tc);
1397 void Preamble::parse(Parser & p, string const & forceclass,
1398 bool detectEncoding, TeX2LyXDocClass & tc)
1400 bool is_full_document = false;
1401 bool is_lyx_file = false;
1402 bool in_lyx_preamble = false;
1404 // determine whether this is a full document or a fragment for inclusion
1406 Token const & t = p.get_token();
1408 if (t.cat() == catEscape && t.cs() == "documentclass") {
1409 is_full_document = true;
1415 if (detectEncoding && !is_full_document)
1418 while (is_full_document && p.good()) {
1419 if (detectEncoding && h_inputencoding != "auto" &&
1420 h_inputencoding != "default")
1423 Token const & t = p.get_token();
1426 if (!detectEncoding)
1427 cerr << "t: " << t << '\n';
1433 if (!in_lyx_preamble &&
1434 (t.cat() == catLetter ||
1435 t.cat() == catSuper ||
1436 t.cat() == catSub ||
1437 t.cat() == catOther ||
1438 t.cat() == catMath ||
1439 t.cat() == catActive ||
1440 t.cat() == catBegin ||
1441 t.cat() == catEnd ||
1442 t.cat() == catAlign ||
1443 t.cat() == catParameter))
1444 h_preamble << t.cs();
1446 else if (!in_lyx_preamble &&
1447 (t.cat() == catSpace || t.cat() == catNewline))
1448 h_preamble << t.asInput();
1450 else if (t.cat() == catComment) {
1451 static regex const islyxfile("%% LyX .* created this file");
1452 static regex const usercommands("User specified LaTeX commands");
1454 string const comment = t.asInput();
1456 // magically switch encoding default if it looks like XeLaTeX
1457 static string const magicXeLaTeX =
1458 "% This document must be compiled with XeLaTeX ";
1459 if (comment.size() > magicXeLaTeX.size()
1460 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1461 && h_inputencoding == "auto") {
1462 if (!detectEncoding)
1463 cerr << "XeLaTeX comment found, switching to UTF8\n";
1464 h_inputencoding = "utf8";
1467 if (regex_search(comment, sub, islyxfile)) {
1469 in_lyx_preamble = true;
1470 } else if (is_lyx_file
1471 && regex_search(comment, sub, usercommands))
1472 in_lyx_preamble = false;
1473 else if (!in_lyx_preamble)
1474 h_preamble << t.asInput();
1477 else if (t.cs() == "PassOptionsToPackage") {
1478 string const poptions = p.getArg('{', '}');
1479 string const package = p.verbatim_item();
1480 extra_package_options_.insert(make_pair(package, poptions));
1483 else if (t.cs() == "pagestyle")
1484 h_paperpagestyle = p.verbatim_item();
1486 else if (t.cs() == "setdefaultlanguage") {
1488 // We don't yet care about non-language variant options
1489 // because LyX doesn't support this yet, see bug #8214
1491 string langopts = p.getOpt();
1492 // check if the option contains a variant, if yes, extract it
1493 string::size_type pos_var = langopts.find("variant");
1494 string::size_type i = langopts.find(',', pos_var);
1495 string::size_type k = langopts.find('=', pos_var);
1496 if (pos_var != string::npos){
1498 if (i == string::npos)
1499 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1501 variant = langopts.substr(k + 1, i - k - 1);
1502 h_language = variant;
1506 h_language = p.verbatim_item();
1507 //finally translate the poyglossia name to a LyX name
1508 h_language = polyglossia2lyx(h_language);
1511 else if (t.cs() == "setotherlanguage") {
1512 // We don't yet care about the option because LyX doesn't
1513 // support this yet, see bug #8214
1514 p.hasOpt() ? p.getOpt() : string();
1518 else if (t.cs() == "setmainfont") {
1519 // we don't care about the option
1520 p.hasOpt() ? p.getOpt() : string();
1521 h_font_roman[1] = p.getArg('{', '}');
1524 else if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1525 // LyX currently only supports the scale option
1528 string fontopts = p.getArg('[', ']');
1529 // check if the option contains a scaling, if yes, extract it
1530 string::size_type pos = fontopts.find("Scale");
1531 if (pos != string::npos) {
1532 string::size_type i = fontopts.find(',', pos);
1533 if (i == string::npos)
1534 scale_as_percentage(fontopts.substr(pos + 1), scale);
1536 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1539 if (t.cs() == "setsansfont") {
1541 h_font_sf_scale[1] = scale;
1542 h_font_sans[1] = p.getArg('{', '}');
1545 h_font_tt_scale[1] = scale;
1546 h_font_typewriter[1] = p.getArg('{', '}');
1550 else if (t.cs() == "date") {
1551 string argument = p.getArg('{', '}');
1552 if (argument.empty())
1553 h_suppress_date = "true";
1555 h_preamble << t.asInput() << '{' << argument << '}';
1558 else if (t.cs() == "color") {
1559 string const space =
1560 (p.hasOpt() ? p.getOpt() : string());
1561 string argument = p.getArg('{', '}');
1562 // check the case that a standard color is used
1563 if (space.empty() && is_known(argument, known_basic_colors)) {
1564 h_fontcolor = rgbcolor2code(argument);
1565 registerAutomaticallyLoadedPackage("color");
1566 } else if (space.empty() && argument == "document_fontcolor")
1567 registerAutomaticallyLoadedPackage("color");
1568 // check the case that LyX's document_fontcolor is defined
1569 // but not used for \color
1571 h_preamble << t.asInput();
1573 h_preamble << space;
1574 h_preamble << '{' << argument << '}';
1575 // the color might already be set because \definecolor
1576 // is parsed before this
1581 else if (t.cs() == "pagecolor") {
1582 string argument = p.getArg('{', '}');
1583 // check the case that a standard color is used
1584 if (is_known(argument, known_basic_colors)) {
1585 h_backgroundcolor = rgbcolor2code(argument);
1586 } else if (argument == "page_backgroundcolor")
1587 registerAutomaticallyLoadedPackage("color");
1588 // check the case that LyX's page_backgroundcolor is defined
1589 // but not used for \pagecolor
1591 h_preamble << t.asInput() << '{' << argument << '}';
1592 // the color might already be set because \definecolor
1593 // is parsed before this
1594 h_backgroundcolor = "";
1598 else if (t.cs() == "makeatletter") {
1599 // LyX takes care of this
1600 p.setCatcode('@', catLetter);
1603 else if (t.cs() == "makeatother") {
1604 // LyX takes care of this
1605 p.setCatcode('@', catOther);
1608 else if (t.cs() == "makeindex") {
1609 // LyX will re-add this if a print index command is found
1613 else if (t.cs() == "newindex") {
1614 string const indexname = p.getArg('[', ']');
1615 string const shortcut = p.verbatim_item();
1616 if (!indexname.empty())
1617 h_index[index_number] = indexname;
1619 h_index[index_number] = shortcut;
1620 h_shortcut[index_number] = shortcut;
1625 else if (t.cs() == "addbibresource")
1626 biblatex_bibliographies.push_back(removeExtension(p.getArg('{', '}')));
1628 else if (t.cs() == "bibliography") {
1629 vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
1630 biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
1633 else if (t.cs() == "RS@ifundefined") {
1634 string const name = p.verbatim_item();
1635 string const body1 = p.verbatim_item();
1636 string const body2 = p.verbatim_item();
1637 // only non-lyxspecific stuff
1638 if (in_lyx_preamble &&
1639 (name == "subsecref" || name == "thmref" || name == "lemref"))
1643 ss << '\\' << t.cs();
1644 ss << '{' << name << '}'
1645 << '{' << body1 << '}'
1646 << '{' << body2 << '}';
1647 h_preamble << ss.str();
1651 else if (t.cs() == "AtBeginDocument") {
1652 string const name = p.verbatim_item();
1653 // only non-lyxspecific stuff
1654 if (in_lyx_preamble &&
1655 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
1656 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
1657 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
1658 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
1659 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
1660 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
1661 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
1662 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
1663 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
1664 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
1665 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
1666 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
1667 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
1668 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
1669 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
1673 ss << '\\' << t.cs();
1674 ss << '{' << name << '}';
1675 h_preamble << ss.str();
1679 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1680 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1681 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1682 || t.cs() == "DeclareRobustCommand"
1683 || t.cs() == "DeclareRobustCommandx"
1684 || t.cs() == "ProvideTextCommandDefault"
1685 || t.cs() == "DeclareMathAccent") {
1687 if (p.next_token().character() == '*') {
1691 string const name = p.verbatim_item();
1692 string const opt1 = p.getFullOpt();
1693 string const opt2 = p.getFullOpt();
1694 string const body = p.verbatim_item();
1695 // store the in_lyx_preamble setting
1696 bool const was_in_lyx_preamble = in_lyx_preamble;
1698 if (name == "\\rmdefault")
1699 if (is_known(body, known_roman_fonts)) {
1700 h_font_roman[0] = body;
1702 in_lyx_preamble = true;
1704 if (name == "\\sfdefault")
1705 if (is_known(body, known_sans_fonts)) {
1706 h_font_sans[0] = body;
1708 in_lyx_preamble = true;
1710 if (name == "\\ttdefault")
1711 if (is_known(body, known_typewriter_fonts)) {
1712 h_font_typewriter[0] = body;
1714 in_lyx_preamble = true;
1716 if (name == "\\familydefault") {
1717 string family = body;
1718 // remove leading "\"
1719 h_font_default_family = family.erase(0,1);
1721 in_lyx_preamble = true;
1724 // remove LyX-specific definitions that are re-added by LyX
1726 // \lyxline is an ancient command that is converted by tex2lyx into
1727 // a \rule therefore remove its preamble code
1728 if (name == "\\lyxdot" || name == "\\lyxarrow"
1729 || name == "\\lyxline" || name == "\\LyX") {
1731 in_lyx_preamble = true;
1734 // Add the command to the known commands
1735 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1737 // only non-lyxspecific stuff
1738 if (!in_lyx_preamble) {
1740 ss << '\\' << t.cs();
1743 ss << '{' << name << '}' << opt1 << opt2
1744 << '{' << body << "}";
1745 h_preamble << ss.str();
1747 ostream & out = in_preamble ? h_preamble : os;
1748 out << "\\" << t.cs() << "{" << name << "}"
1749 << opts << "{" << body << "}";
1752 // restore the in_lyx_preamble setting
1753 in_lyx_preamble = was_in_lyx_preamble;
1756 else if (t.cs() == "documentclass") {
1757 vector<string>::iterator it;
1758 vector<string> opts = split_options(p.getArg('[', ']'));
1759 handle_opt(opts, known_fontsizes, h_paperfontsize);
1760 delete_opt(opts, known_fontsizes);
1761 // delete "pt" at the end
1762 string::size_type i = h_paperfontsize.find("pt");
1763 if (i != string::npos)
1764 h_paperfontsize.erase(i);
1765 // The documentclass options are always parsed before the options
1766 // of the babel call so that a language cannot overwrite the babel
1768 handle_opt(opts, known_languages, h_language);
1769 delete_opt(opts, known_languages);
1772 if ((it = find(opts.begin(), opts.end(), "fleqn"))
1774 h_is_mathindent = "1";
1777 // formula numbering side
1778 if ((it = find(opts.begin(), opts.end(), "leqno"))
1780 h_math_numbering_side = "left";
1783 else if ((it = find(opts.begin(), opts.end(), "reqno"))
1785 h_math_numbering_side = "right";
1789 // paper orientation
1790 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1791 h_paperorientation = "landscape";
1795 if ((it = find(opts.begin(), opts.end(), "oneside"))
1800 if ((it = find(opts.begin(), opts.end(), "twoside"))
1806 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1808 h_papercolumns = "1";
1811 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1813 h_papercolumns = "2";
1817 // some size options are known to any document classes, other sizes
1818 // are handled by the \geometry command of the geometry package
1819 handle_opt(opts, known_class_paper_sizes, h_papersize);
1820 delete_opt(opts, known_class_paper_sizes);
1821 // the remaining options
1822 h_options = join(opts, ",");
1823 // FIXME This does not work for classes that have a
1824 // different name in LyX than in LaTeX
1825 h_textclass = p.getArg('{', '}');
1829 else if (t.cs() == "usepackage") {
1830 string const options = p.getArg('[', ']');
1831 string const name = p.getArg('{', '}');
1832 vector<string> vecnames;
1833 split(name, vecnames, ',');
1834 vector<string>::const_iterator it = vecnames.begin();
1835 vector<string>::const_iterator end = vecnames.end();
1836 for (; it != end; ++it)
1837 handle_package(p, trimSpaceAndEol(*it), options,
1838 in_lyx_preamble, detectEncoding);
1841 else if (t.cs() == "inputencoding") {
1842 string const encoding = p.getArg('{','}');
1843 Encoding const * const enc = encodings.fromLaTeXName(
1844 encoding, Encoding::inputenc, true);
1846 if (!detectEncoding)
1847 cerr << "Unknown encoding " << encoding
1848 << ". Ignoring." << std::endl;
1851 h_inputencoding = enc->name();
1852 p.setEncoding(enc->iconvName());
1856 else if (t.cs() == "newenvironment") {
1857 string const name = p.getArg('{', '}');
1858 string const opt1 = p.getFullOpt();
1859 string const opt2 = p.getFullOpt();
1860 string const beg = p.verbatim_item();
1861 string const end = p.verbatim_item();
1862 if (!in_lyx_preamble) {
1863 h_preamble << "\\newenvironment{" << name
1864 << '}' << opt1 << opt2 << '{'
1865 << beg << "}{" << end << '}';
1867 add_known_environment(name, opt1, !opt2.empty(),
1868 from_utf8(beg), from_utf8(end));
1872 else if (t.cs() == "newtheorem") {
1874 if (p.next_token().character() == '*') {
1878 string const name = p.getArg('{', '}');
1879 string const opt1 = p.getFullOpt();
1880 string const opt2 = p.getFullOpt();
1881 string const body = p.verbatim_item();
1882 string const opt3 = p.getFullOpt();
1883 string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
1885 string const complete = cmd + "{" + name + '}' +
1886 opt1 + opt2 + '{' + body + '}' + opt3;
1888 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
1890 if (!in_lyx_preamble)
1891 h_preamble << complete;
1894 else if (t.cs() == "def") {
1895 string name = p.get_token().cs();
1896 // In fact, name may be more than the name:
1897 // In the test case of bug 8116
1898 // name == "csname SF@gobble@opt \endcsname".
1899 // Therefore, we need to use asInput() instead of cs().
1900 while (p.next_token().cat() != catBegin)
1901 name += p.get_token().asInput();
1902 if (!in_lyx_preamble)
1903 h_preamble << "\\def\\" << name << '{'
1904 << p.verbatim_item() << "}";
1907 else if (t.cs() == "newcolumntype") {
1908 string const name = p.getArg('{', '}');
1909 trimSpaceAndEol(name);
1911 string opts = p.getOpt();
1912 if (!opts.empty()) {
1913 istringstream is(string(opts, 1));
1916 special_columns_[name[0]] = nargs;
1917 h_preamble << "\\newcolumntype{" << name << "}";
1919 h_preamble << "[" << nargs << "]";
1920 h_preamble << "{" << p.verbatim_item() << "}";
1923 else if (t.cs() == "setcounter") {
1924 string const name = p.getArg('{', '}');
1925 string const content = p.getArg('{', '}');
1926 if (name == "secnumdepth")
1927 h_secnumdepth = content;
1928 else if (name == "tocdepth")
1929 h_tocdepth = content;
1931 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1934 else if (t.cs() == "setlength") {
1935 string const name = p.verbatim_item();
1936 string const content = p.verbatim_item();
1937 // the paragraphs are only not indented when \parindent is set to zero
1938 if (name == "\\parindent" && content != "") {
1939 if (content[0] == '0')
1940 h_paragraph_separation = "skip";
1942 h_paragraph_indentation = translate_len(content);
1943 } else if (name == "\\parskip") {
1944 if (content == "\\smallskipamount")
1945 h_defskip = "smallskip";
1946 else if (content == "\\medskipamount")
1947 h_defskip = "medskip";
1948 else if (content == "\\bigskipamount")
1949 h_defskip = "bigskip";
1951 h_defskip = translate_len(content);
1952 } else if (name == "\\mathindent") {
1953 h_mathindentation = translate_len(content);
1955 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1958 else if (t.cs() == "onehalfspacing")
1959 h_spacing = "onehalf";
1961 else if (t.cs() == "doublespacing")
1962 h_spacing = "double";
1964 else if (t.cs() == "setstretch")
1965 h_spacing = "other " + p.verbatim_item();
1967 else if (t.cs() == "synctex") {
1968 // the scheme is \synctex=value
1969 // where value can only be "1" or "-1"
1970 h_output_sync = "1";
1971 // there can be any character behind the value (e.g. a linebreak or a '\'
1972 // therefore we extract it char by char
1974 string value = p.get_token().asInput();
1976 value += p.get_token().asInput();
1977 h_output_sync_macro = "\\synctex=" + value;
1980 else if (t.cs() == "begin") {
1981 string const name = p.getArg('{', '}');
1982 if (name == "document")
1984 h_preamble << "\\begin{" << name << "}";
1987 else if (t.cs() == "geometry") {
1988 vector<string> opts = split_options(p.getArg('{', '}'));
1989 handle_geometry(opts);
1992 else if (t.cs() == "definecolor") {
1993 string const color = p.getArg('{', '}');
1994 string const space = p.getArg('{', '}');
1995 string const value = p.getArg('{', '}');
1996 if (color == "document_fontcolor" && space == "rgb") {
1997 RGBColor c(RGBColorFromLaTeX(value));
1998 h_fontcolor = X11hexname(c);
1999 } else if (color == "note_fontcolor" && space == "rgb") {
2000 RGBColor c(RGBColorFromLaTeX(value));
2001 h_notefontcolor = X11hexname(c);
2002 } else if (color == "page_backgroundcolor" && space == "rgb") {
2003 RGBColor c(RGBColorFromLaTeX(value));
2004 h_backgroundcolor = X11hexname(c);
2005 } else if (color == "shadecolor" && space == "rgb") {
2006 RGBColor c(RGBColorFromLaTeX(value));
2007 h_boxbgcolor = X11hexname(c);
2009 h_preamble << "\\definecolor{" << color
2010 << "}{" << space << "}{" << value
2015 else if (t.cs() == "bibliographystyle")
2016 h_biblio_style = p.verbatim_item();
2018 else if (t.cs() == "jurabibsetup") {
2019 // FIXME p.getArg('{', '}') is most probably wrong (it
2020 // does not handle nested braces).
2021 // Use p.verbatim_item() instead.
2022 vector<string> jurabibsetup =
2023 split_options(p.getArg('{', '}'));
2024 // add jurabibsetup to the jurabib package options
2025 add_package("jurabib", jurabibsetup);
2026 if (!jurabibsetup.empty()) {
2027 h_preamble << "\\jurabibsetup{"
2028 << join(jurabibsetup, ",") << '}';
2032 else if (t.cs() == "hypersetup") {
2033 vector<string> hypersetup =
2034 split_options(p.verbatim_item());
2035 // add hypersetup to the hyperref package options
2036 handle_hyperref(hypersetup);
2037 if (!hypersetup.empty()) {
2038 h_preamble << "\\hypersetup{"
2039 << join(hypersetup, ",") << '}';
2043 else if (is_known(t.cs(), known_if_3arg_commands)) {
2044 // prevent misparsing of \usepackage if it is used
2045 // as an argument (see e.g. our own output of
2046 // \@ifundefined above)
2047 string const arg1 = p.verbatim_item();
2048 string const arg2 = p.verbatim_item();
2049 string const arg3 = p.verbatim_item();
2050 // test case \@ifundefined{date}{}{\date{}}
2051 if (t.cs() == "@ifundefined" && arg1 == "date" &&
2052 arg2.empty() && arg3 == "\\date{}") {
2053 h_suppress_date = "true";
2054 // older tex2lyx versions did output
2055 // \@ifundefined{definecolor}{\usepackage{color}}{}
2056 } else if (t.cs() == "@ifundefined" &&
2057 arg1 == "definecolor" &&
2058 arg2 == "\\usepackage{color}" &&
2060 if (!in_lyx_preamble)
2061 h_preamble << package_beg_sep
2064 << "\\@ifundefined{definecolor}{color}{}"
2067 //\@ifundefined{showcaptionsetup}{}{%
2068 // \PassOptionsToPackage{caption=false}{subfig}}
2069 // that LyX uses for subfloats
2070 } else if (t.cs() == "@ifundefined" &&
2071 arg1 == "showcaptionsetup" && arg2.empty()
2072 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
2074 } else if (!in_lyx_preamble) {
2075 h_preamble << t.asInput()
2076 << '{' << arg1 << '}'
2077 << '{' << arg2 << '}'
2078 << '{' << arg3 << '}';
2082 else if (is_known(t.cs(), known_if_commands)) {
2083 // must not parse anything in conditional code, since
2084 // LyX would output the parsed contents unconditionally
2085 if (!in_lyx_preamble)
2086 h_preamble << t.asInput();
2087 handle_if(p, in_lyx_preamble);
2090 else if (!t.cs().empty() && !in_lyx_preamble)
2091 h_preamble << '\\' << t.cs();
2094 // remove the whitespace
2097 // Force textclass if the user wanted it
2098 if (!forceclass.empty())
2099 h_textclass = forceclass;
2100 tc.setName(h_textclass);
2101 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2102 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2105 if (h_papersides.empty()) {
2108 h_papersides = ss.str();
2111 // If the CJK package is used we cannot set the document language from
2112 // the babel options. Instead, we guess which language is used most
2113 // and set this one.
2114 default_language = h_language;
2115 if (is_full_document &&
2116 (auto_packages.find("CJK") != auto_packages.end() ||
2117 auto_packages.find("CJKutf8") != auto_packages.end())) {
2119 h_language = guessLanguage(p, default_language);
2121 if (explicit_babel && h_language != default_language) {
2122 // We set the document language to a CJK language,
2123 // but babel is explicitly called in the user preamble
2124 // without options. LyX will not add the default
2125 // language to the document options if it is either
2126 // english, or no text is set as default language.
2127 // Therefore we need to add a language option explicitly.
2128 // FIXME: It would be better to remove all babel calls
2129 // from the user preamble, but this is difficult
2130 // without re-introducing bug 7861.
2131 if (h_options.empty())
2132 h_options = lyx2babel(default_language);
2134 h_options += ',' + lyx2babel(default_language);
2140 string Preamble::parseEncoding(Parser & p, string const & forceclass)
2142 TeX2LyXDocClass dummy;
2143 parse(p, forceclass, true, dummy);
2144 if (h_inputencoding != "auto" && h_inputencoding != "default")
2145 return h_inputencoding;
2150 string babel2lyx(string const & language)
2152 char const * const * where = is_known(language, known_languages);
2154 return known_coded_languages[where - known_languages];
2159 string lyx2babel(string const & language)
2161 char const * const * where = is_known(language, known_coded_languages);
2163 return known_languages[where - known_coded_languages];
2168 string Preamble::polyglossia2lyx(string const & language)
2170 char const * const * where = is_known(language, polyglossia_languages);
2172 return coded_polyglossia_languages[where - polyglossia_languages];
2177 string rgbcolor2code(string const & name)
2179 char const * const * where = is_known(name, known_basic_colors);
2181 // "red", "green" etc
2182 return known_basic_color_codes[where - known_basic_colors];
2184 // "255,0,0", "0,255,0" etc
2185 RGBColor c(RGBColorFromLaTeX(name));
2186 return X11hexname(c);