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", "PTSerif-TLF", "tgbonum", "tgchorus",
131 "tgpagella", "tgschola", "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", "PTSans-TLF", "tgadventor",
138 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
139 "courier", "lmtt", "luximono", "fourier", "libertineMono-type1", "lmodern",
140 "mathpazo", "mathptmx", "newcent", "NotoMono-TLF", "PTMono-TLF", "tgcursor", "txtt", 0 };
142 const char * const known_math_fonts[] = { "eulervm", "newtxmath", 0};
144 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
145 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
146 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
147 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
148 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
150 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
151 "executivepaper", "legalpaper", "letterpaper", 0};
153 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
154 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
156 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
157 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
160 /// commands that can start an \if...\else...\endif sequence
161 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
162 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
163 "ifsidecap", "ifupgreek", 0};
165 const char * const known_basic_colors[] = {"black", "blue", "brown", "cyan",
166 "darkgray", "gray", "green", "lightgray", "lime", "magenta", "orange", "olive",
167 "pink", "purple", "red", "teal", "violet", "white", "yellow", 0};
169 const char * const known_basic_color_codes[] = {"#000000", "#0000ff", "#964B00", "#00ffff",
170 "#a9a9a9", "#808080", "#00ff00", "#d3d3d3", "#bfff00", "#ff00ff", "#ff7f00", "#808000",
171 "#ffc0cb", "#800080", "#ff0000", "#008080", "#8f00ff", "#ffffff", "#ffff00", 0};
173 /// conditional commands with three arguments like \@ifundefined{}{}{}
174 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
178 * Known file extensions for TeX files as used by \\includeonly
180 char const * const known_tex_extensions[] = {"tex", 0};
182 /// packages that work only in xetex
183 /// polyglossia is handled separately
184 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
185 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
186 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
188 /// packages that are automatically skipped if loaded by LyX
189 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
190 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color",
191 "float", "fontspec", "framed", "graphicx", "hhline", "ifthen", "longtable",
192 "makeidx", "minted", "multirow", "nomencl", "pdfpages", "prettyref", "refstyle",
193 "rotating", "rotfloat", "splitidx", "setspace", "subscript", "textcomp", "tipa",
194 "tipx", "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xcolor",
197 // codes used to remove packages that are loaded automatically by LyX.
198 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
199 const char package_beg_sep = '\001';
200 const char package_mid_sep = '\002';
201 const char package_end_sep = '\003';
204 // returns true if at least one of the options in what has been found
205 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
211 // the last language option is the document language (for babel and LyX)
212 // the last size option is the document font size
213 vector<string>::iterator it;
214 vector<string>::iterator position = opts.begin();
215 for (; *what; ++what) {
216 it = find(opts.begin(), opts.end(), *what);
217 if (it != opts.end()) {
218 if (it >= position) {
229 void delete_opt(vector<string> & opts, char const * const * what)
234 // remove found options from the list
235 // do this after handle_opt to avoid potential memory leaks
236 vector<string>::iterator it;
237 for (; *what; ++what) {
238 it = find(opts.begin(), opts.end(), *what);
239 if (it != opts.end())
246 * Split a package options string (keyval format) into a vector.
248 * authorformat=smallcaps,
250 * titleformat=colonsep,
251 * bibformat={tabular,ibidem,numbered}
253 vector<string> split_options(string const & input)
255 vector<string> options;
259 Token const & t = p.get_token();
260 if (t.asInput() == ",") {
261 options.push_back(trimSpaceAndEol(option));
263 } else if (t.asInput() == "=") {
266 if (p.next_token().asInput() == "{")
267 option += '{' + p.getArg('{', '}') + '}';
268 } else if (t.cat() != catSpace && t.cat() != catComment)
269 option += t.asInput();
273 options.push_back(trimSpaceAndEol(option));
280 * Retrieve a keyval option "name={value with=sign}" named \p name from
281 * \p options and return the value.
282 * The found option is also removed from \p options.
284 string process_keyval_opt(vector<string> & options, string name)
286 for (size_t i = 0; i < options.size(); ++i) {
287 vector<string> option;
288 split(options[i], option, '=');
289 if (option.size() < 2)
291 if (option[0] == name) {
292 options.erase(options.begin() + i);
293 option.erase(option.begin());
294 return join(option, "=");
300 } // anonymous namespace
304 * known polyglossia language names (including variants)
305 * FIXME: support spelling=old for german variants (german vs. ngerman LyX names etc)
307 const char * const Preamble::polyglossia_languages[] = {
308 "albanian", "american", "amharic", "ancient", "arabic", "armenian", "asturian", "australian",
309 "bahasai", "bahasam", "basque", "bengali", "brazil", "brazilian", "breton", "british", "bulgarian",
310 "catalan", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
311 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
312 "galician", "greek", "monotonic", "hebrew", "hindi",
313 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer",
314 "lao", "latin", "latvian", "lithuanian", "lsorbian", "magyar", "malayalam", "marathi",
315 "austrian", "newzealand", "german", "norsk", "nynorsk", "occitan",
316 "piedmontese", "polish", "polytonic", "portuges", "romanian", "romansh", "russian",
317 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovenian", "spanish", "swedish", "syriac",
318 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
319 "ukrainian", "urdu", "usorbian", "vietnamese", "welsh", 0};
320 // not yet supported by LyX: "korean", "nko"
323 * the same as polyglossia_languages with .lyx names
324 * please keep this in sync with polyglossia_languages line by line!
326 const char * const Preamble::coded_polyglossia_languages[] = {
327 "albanian", "american", "amharic", "ancientgreek", "arabic_arabi", "armenian", "asturian", "australian",
328 "bahasa", "bahasam", "basque", "bengali", "brazilian", "brazilian", "breton", "british", "bulgarian",
329 "catalan", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
330 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
331 "galician", "greek", "greek", "hebrew", "hindi",
332 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer",
333 "lao", "latin", "latvian", "lithuanian", "lowersorbian", "magyar", "malayalam", "marathi",
334 "naustrian","newzealand", "ngerman", "norsk", "nynorsk", "occitan",
335 "piedmontese", "polish", "polutonikogreek", "portuges", "romanian", "romansh", "russian",
336 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovene", "spanish", "swedish", "syriac",
337 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
338 "ukrainian", "urdu", "uppersorbian", "vietnamese", "welsh", 0};
339 // not yet supported by LyX: "korean-polyglossia", "nko"
342 bool Preamble::usePolyglossia() const
344 return h_use_non_tex_fonts && h_language_package == "default";
348 bool Preamble::indentParagraphs() const
350 return h_paragraph_separation == "indent";
354 bool Preamble::isPackageUsed(string const & package) const
356 return used_packages.find(package) != used_packages.end();
360 vector<string> Preamble::getPackageOptions(string const & package) const
362 map<string, vector<string> >::const_iterator it = used_packages.find(package);
363 if (it != used_packages.end())
365 return vector<string>();
369 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
371 auto_packages.insert(package);
375 void Preamble::addModule(string const & module)
377 used_modules.push_back(module);
381 void Preamble::suppressDate(bool suppress)
384 h_suppress_date = "true";
386 h_suppress_date = "false";
390 void Preamble::registerAuthor(std::string const & name)
392 Author author(from_utf8(name), empty_docstring());
393 author.setUsed(true);
394 authors_.record(author);
395 h_tracking_changes = "true";
396 h_output_changes = "true";
400 Author const & Preamble::getAuthor(std::string const & name) const
402 Author author(from_utf8(name), empty_docstring());
403 for (AuthorList::Authors::const_iterator it = authors_.begin();
404 it != authors_.end(); ++it)
407 static Author const dummy;
412 int Preamble::getSpecialTableColumnArguments(char c) const
414 map<char, int>::const_iterator it = special_columns_.find(c);
415 if (it == special_columns_.end())
421 void Preamble::add_package(string const & name, vector<string> & options)
423 // every package inherits the global options
424 if (used_packages.find(name) == used_packages.end())
425 used_packages[name] = split_options(h_options);
427 // Insert options passed via PassOptionsToPackage
428 for (auto const & p : extra_package_options_) {
429 if (p.first == name) {
430 vector<string> eo = getVectorFromString(p.second);
431 for (auto const & eoi : eo)
432 options.push_back(eoi);
436 vector<string> & v = used_packages[name];
437 v.insert(v.end(), options.begin(), options.end());
438 if (name == "jurabib") {
439 // Don't output the order argument (see the cite command
440 // handling code in text.cpp).
441 vector<string>::iterator end =
442 remove(options.begin(), options.end(), "natbiborder");
443 end = remove(options.begin(), end, "jurabiborder");
444 options.erase(end, options.end());
451 // Given is a string like "scaled=0.9" or "Scale=0.9", return 0.9 * 100
452 bool scale_as_percentage(string const & scale, string & percentage)
454 string::size_type pos = scale.find('=');
455 if (pos != string::npos) {
456 string value = scale.substr(pos + 1);
457 if (isStrDbl(value)) {
458 percentage = convert<string>(
459 static_cast<int>(100 * convert<double>(value)));
467 string remove_braces(string const & value)
471 if (value[0] == '{' && value[value.length()-1] == '}')
472 return value.substr(1, value.length()-2);
476 } // anonymous namespace
479 Preamble::Preamble() : one_language(true), explicit_babel(false),
480 title_layout_found(false), index_number(0), h_font_cjk_set(false),
481 h_use_microtype("false")
485 h_biblio_style = "plain";
486 h_bibtex_command = "default";
487 h_cite_engine = "basic";
488 h_cite_engine_type = "default";
490 h_defskip = "medskip";
491 h_dynamic_quotes = false;
494 h_fontencoding = "default";
495 h_font_roman[0] = "default";
496 h_font_roman[1] = "default";
497 h_font_sans[0] = "default";
498 h_font_sans[1] = "default";
499 h_font_typewriter[0] = "default";
500 h_font_typewriter[1] = "default";
501 h_font_math[0] = "auto";
502 h_font_math[1] = "auto";
503 h_font_default_family = "default";
504 h_use_non_tex_fonts = false;
506 h_font_osf = "false";
507 h_font_sf_scale[0] = "100";
508 h_font_sf_scale[1] = "100";
509 h_font_tt_scale[0] = "100";
510 h_font_tt_scale[1] = "100";
512 h_is_mathindent = "0";
513 h_math_numbering_side = "default";
514 h_graphics = "default";
515 h_default_output_format = "default";
516 h_html_be_strict = "false";
517 h_html_css_as_file = "0";
518 h_html_math_output = "0";
519 h_index[0] = "Index";
520 h_index_command = "default";
521 h_inputencoding = "auto";
522 h_justification = "true";
523 h_language = "english";
524 h_language_package = "none";
526 h_maintain_unincluded_children = "false";
530 h_output_changes = "false";
532 //h_output_sync_macro
533 h_papercolumns = "1";
534 h_paperfontsize = "default";
535 h_paperorientation = "portrait";
536 h_paperpagestyle = "default";
538 h_papersize = "default";
539 h_paragraph_indentation = "default";
540 h_paragraph_separation = "indent";
545 h_pdf_bookmarks = "0";
546 h_pdf_bookmarksnumbered = "0";
547 h_pdf_bookmarksopen = "0";
548 h_pdf_bookmarksopenlevel = "1";
549 h_pdf_breaklinks = "0";
550 h_pdf_pdfborder = "0";
551 h_pdf_colorlinks = "0";
552 h_pdf_backref = "section";
553 h_pdf_pdfusetitle = "0";
555 //h_pdf_quoted_options;
556 h_quotes_style = "english";
558 h_shortcut[0] = "idx";
559 h_spacing = "single";
560 h_save_transient_properties = "true";
561 h_suppress_date = "false";
562 h_textclass = "article";
564 h_tracking_changes = "false";
565 h_use_bibtopic = "false";
566 h_use_dash_ligatures = "true";
567 h_use_indices = "false";
568 h_use_geometry = "false";
569 h_use_default_options = "false";
570 h_use_hyperref = "false";
571 h_use_microtype = "false";
572 h_use_refstyle = false;
573 h_use_minted = false;
574 h_use_packages["amsmath"] = "1";
575 h_use_packages["amssymb"] = "0";
576 h_use_packages["cancel"] = "0";
577 h_use_packages["esint"] = "1";
578 h_use_packages["mhchem"] = "0";
579 h_use_packages["mathdots"] = "0";
580 h_use_packages["mathtools"] = "0";
581 h_use_packages["stackrel"] = "0";
582 h_use_packages["stmaryrd"] = "0";
583 h_use_packages["undertilde"] = "0";
587 void Preamble::handle_hyperref(vector<string> & options)
589 // FIXME swallow inputencoding changes that might surround the
590 // hyperref setup if it was written by LyX
591 h_use_hyperref = "true";
592 // swallow "unicode=true", since LyX does always write that
593 vector<string>::iterator it =
594 find(options.begin(), options.end(), "unicode=true");
595 if (it != options.end())
597 it = find(options.begin(), options.end(), "pdfusetitle");
598 if (it != options.end()) {
599 h_pdf_pdfusetitle = "1";
602 string bookmarks = process_keyval_opt(options, "bookmarks");
603 if (bookmarks == "true")
604 h_pdf_bookmarks = "1";
605 else if (bookmarks == "false")
606 h_pdf_bookmarks = "0";
607 if (h_pdf_bookmarks == "1") {
608 string bookmarksnumbered =
609 process_keyval_opt(options, "bookmarksnumbered");
610 if (bookmarksnumbered == "true")
611 h_pdf_bookmarksnumbered = "1";
612 else if (bookmarksnumbered == "false")
613 h_pdf_bookmarksnumbered = "0";
614 string bookmarksopen =
615 process_keyval_opt(options, "bookmarksopen");
616 if (bookmarksopen == "true")
617 h_pdf_bookmarksopen = "1";
618 else if (bookmarksopen == "false")
619 h_pdf_bookmarksopen = "0";
620 if (h_pdf_bookmarksopen == "1") {
621 string bookmarksopenlevel =
622 process_keyval_opt(options, "bookmarksopenlevel");
623 if (!bookmarksopenlevel.empty())
624 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
627 string breaklinks = process_keyval_opt(options, "breaklinks");
628 if (breaklinks == "true")
629 h_pdf_breaklinks = "1";
630 else if (breaklinks == "false")
631 h_pdf_breaklinks = "0";
632 string pdfborder = process_keyval_opt(options, "pdfborder");
633 if (pdfborder == "{0 0 0}")
634 h_pdf_pdfborder = "1";
635 else if (pdfborder == "{0 0 1}")
636 h_pdf_pdfborder = "0";
637 string backref = process_keyval_opt(options, "backref");
638 if (!backref.empty())
639 h_pdf_backref = backref;
640 string colorlinks = process_keyval_opt(options, "colorlinks");
641 if (colorlinks == "true")
642 h_pdf_colorlinks = "1";
643 else if (colorlinks == "false")
644 h_pdf_colorlinks = "0";
645 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
646 if (!pdfpagemode.empty())
647 h_pdf_pagemode = pdfpagemode;
648 string pdftitle = process_keyval_opt(options, "pdftitle");
649 if (!pdftitle.empty()) {
650 h_pdf_title = remove_braces(pdftitle);
652 string pdfauthor = process_keyval_opt(options, "pdfauthor");
653 if (!pdfauthor.empty()) {
654 h_pdf_author = remove_braces(pdfauthor);
656 string pdfsubject = process_keyval_opt(options, "pdfsubject");
657 if (!pdfsubject.empty())
658 h_pdf_subject = remove_braces(pdfsubject);
659 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
660 if (!pdfkeywords.empty())
661 h_pdf_keywords = remove_braces(pdfkeywords);
662 if (!options.empty()) {
663 if (!h_pdf_quoted_options.empty())
664 h_pdf_quoted_options += ',';
665 h_pdf_quoted_options += join(options, ",");
671 void Preamble::handle_geometry(vector<string> & options)
673 h_use_geometry = "true";
674 vector<string>::iterator it;
676 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
677 h_paperorientation = "landscape";
681 // keyval version: "paper=letter"
682 string paper = process_keyval_opt(options, "paper");
684 h_papersize = paper + "paper";
685 // alternative version: "letterpaper"
686 handle_opt(options, known_paper_sizes, h_papersize);
687 delete_opt(options, known_paper_sizes);
689 char const * const * margin = known_paper_margins;
690 for (; *margin; ++margin) {
691 string value = process_keyval_opt(options, *margin);
692 if (!value.empty()) {
693 int k = margin - known_paper_margins;
694 string name = known_coded_paper_margins[k];
695 h_margins += '\\' + name + ' ' + value + '\n';
701 void Preamble::handle_package(Parser &p, string const & name,
702 string const & opts, bool in_lyx_preamble,
705 vector<string> options = split_options(opts);
706 add_package(name, options);
708 if (is_known(name, known_xetex_packages)) {
710 h_use_non_tex_fonts = true;
711 registerAutomaticallyLoadedPackage("fontspec");
712 if (h_inputencoding == "auto")
713 p.setEncoding("UTF-8");
717 if (is_known(name, known_roman_fonts))
718 h_font_roman[0] = name;
720 if (name == "fourier") {
721 h_font_roman[0] = "utopia";
722 // when font uses real small capitals
723 if (opts == "expert")
727 if (name == "garamondx") {
728 h_font_roman[0] = "garamondx";
733 if (name == "libertine") {
734 h_font_roman[0] = "libertine";
735 // this automatically invokes biolinum
736 h_font_sans[0] = "biolinum";
739 else if (opts == "lining")
740 h_font_osf = "false";
743 if (name == "libertine-type1") {
744 h_font_roman[0] = "libertine";
745 // NOTE: contrary to libertine.sty, libertine-type1
746 // does not automatically invoke biolinum
747 if (opts == "lining")
748 h_font_osf = "false";
749 else if (opts == "osf")
753 if (name == "mathdesign") {
754 if (opts.find("charter") != string::npos)
755 h_font_roman[0] = "md-charter";
756 if (opts.find("garamond") != string::npos)
757 h_font_roman[0] = "md-garamond";
758 if (opts.find("utopia") != string::npos)
759 h_font_roman[0] = "md-utopia";
760 if (opts.find("expert") != string::npos) {
766 else if (name == "mathpazo")
767 h_font_roman[0] = "palatino";
769 else if (name == "mathptmx")
770 h_font_roman[0] = "times";
772 if (name == "crimson")
773 h_font_roman[0] = "cochineal";
775 if (name == "cochineal") {
776 h_font_roman[0] = "cochineal";
777 // cochineal can have several options, e.g. [proportional,osf]
778 string::size_type pos = opts.find("osf");
779 if (pos != string::npos)
783 if (name == "noto") {
784 // noto can have several options
786 h_font_roman[0] = "NotoSerif-TLF";
787 string::size_type pos = opts.find("rm");
788 if (pos != string::npos)
789 h_font_roman[0] = "NotoSerif-TLF";
790 pos = opts.find("sf");
791 if (pos != string::npos)
792 h_font_sans[0] = "NotoSans-TLF";
793 pos = opts.find("nott");
794 if (pos != string::npos) {
795 h_font_roman[0] = "NotoSerif-TLF";
796 h_font_sans[0] = "NotoSans-TLF";
798 // noto as typewriter is handled in handling of \ttdefault
799 // special cases are handled in handling of \rmdefault and \sfdefault
802 if (name == "paratype") {
803 // in this case all fonts are ParaType
804 h_font_roman[0] = "PTSerif-TLF";
805 h_font_sans[0] = "default";
806 h_font_typewriter[0] = "default";
809 if (name == "PTSerif")
810 h_font_roman[0] = "PTSerif-TLF";
813 if (is_known(name, known_sans_fonts)) {
814 h_font_sans[0] = name;
815 if (options.size() >= 1) {
816 if (scale_as_percentage(opts, h_font_sf_scale[0]))
821 if (name == "biolinum-type1") {
822 h_font_sans[0] = "biolinum";
823 // biolinum can have several options, e.g. [osf,scaled=0.97]
824 string::size_type pos = opts.find("osf");
825 if (pos != string::npos)
829 if (name == "PTSans") {
830 h_font_sans[0] = "PTSans-TLF";
831 if (options.size() >= 1) {
832 if (scale_as_percentage(opts, h_font_sf_scale[0]))
838 if (is_known(name, known_typewriter_fonts)) {
839 // fourier can be set as roman font _only_
840 // fourier as typewriter is handled in handling of \ttdefault
841 if (name != "fourier") {
842 h_font_typewriter[0] = name;
843 if (options.size() >= 1) {
844 if (scale_as_percentage(opts, h_font_tt_scale[0]))
850 if (name == "libertineMono-type1")
851 h_font_typewriter[0] = "libertine-mono";
853 if (name == "PTMono") {
854 h_font_typewriter[0] = "PTMono-TLF";
855 if (options.size() >= 1) {
856 if (scale_as_percentage(opts, h_font_tt_scale[0]))
861 // font uses old-style figure
866 if (is_known(name, known_math_fonts))
867 h_font_math[0] = name;
869 if (name == "newtxmath") {
871 h_font_math[0] = "newtxmath";
872 else if (opts == "garamondx")
873 h_font_math[0] = "garamondx-ntxm";
874 else if (opts == "libertine")
875 h_font_math[0] = "libertine-ntxm";
876 else if (opts == "minion")
877 h_font_math[0] = "minion-ntxm";
878 else if (opts == "cochineal")
879 h_font_math[0] = "cochineal-ntxm";
884 h_font_math[0] = "iwona-math";
886 if (name == "kurier")
888 h_font_math[0] = "kurier-math";
890 // after the detection and handling of special cases, we can remove the
891 // fonts, otherwise they would appear in the preamble, see bug #7856
892 if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
893 || is_known(name, known_typewriter_fonts) || is_known(name, known_math_fonts))
895 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
896 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
897 name == "esint" || name == "mhchem" || name == "mathdots" ||
898 name == "mathtools" || name == "stackrel" ||
899 name == "stmaryrd" || name == "undertilde")
900 h_use_packages[name] = "2";
902 else if (name == "babel") {
903 h_language_package = "default";
904 // One might think we would have to do nothing if babel is loaded
905 // without any options to prevent pollution of the preamble with this
906 // babel call in every roundtrip.
907 // But the user could have defined babel-specific things afterwards. So
908 // we need to keep it in the preamble to prevent cases like bug #7861.
910 // check if more than one option was used - used later for inputenc
911 if (options.begin() != options.end() - 1)
912 one_language = false;
913 // babel takes the last language of the option of its \usepackage
914 // call as document language. If there is no such language option, the
915 // last language in the documentclass options is used.
916 handle_opt(options, known_languages, h_language);
917 // translate the babel name to a LyX name
918 h_language = babel2lyx(h_language);
919 if (h_language == "japanese") {
920 // For Japanese, the encoding isn't indicated in the source
921 // file, and there's really not much we can do. We could
922 // 1) offer a list of possible encodings to choose from, or
923 // 2) determine the encoding of the file by inspecting it.
924 // For the time being, we leave the encoding alone so that
925 // we don't get iconv errors when making a wrong guess, and
926 // we will output a note at the top of the document
927 // explaining what to do.
928 Encoding const * const enc = encodings.fromIconvName(
929 p.getEncoding(), Encoding::japanese, false);
931 h_inputencoding = enc->name();
932 is_nonCJKJapanese = true;
933 // in this case babel can be removed from the preamble
934 registerAutomaticallyLoadedPackage("babel");
936 // If babel is called with options, LyX puts them by default into the
937 // document class options. This works for most languages, except
938 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
939 // perhaps in future others.
940 // Therefore keep the babel call as it is as the user might have
942 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
944 delete_opt(options, known_languages);
946 h_preamble << "\\usepackage{babel}\n";
947 explicit_babel = true;
951 else if (name == "polyglossia") {
952 h_language_package = "default";
953 h_default_output_format = "pdf4";
954 h_use_non_tex_fonts = true;
956 registerAutomaticallyLoadedPackage("xunicode");
957 if (h_inputencoding == "auto")
958 p.setEncoding("UTF-8");
961 else if (name == "CJK") {
962 // set the encoding to "auto" because it might be set to "default" by the babel handling
963 // and this would not be correct for CJK
964 if (h_inputencoding == "default")
965 h_inputencoding = "auto";
966 registerAutomaticallyLoadedPackage("CJK");
969 else if (name == "CJKutf8") {
970 h_inputencoding = "utf8-cjk";
971 p.setEncoding("UTF-8");
972 registerAutomaticallyLoadedPackage("CJKutf8");
975 else if (name == "fontenc") {
976 h_fontencoding = getStringFromVector(options, ",");
977 /* We could do the following for better round trip support,
978 * but this makes the document less portable, so I skip it:
979 if (h_fontencoding == lyxrc.fontenc)
980 h_fontencoding = "global";
985 else if (name == "inputenc" || name == "luainputenc") {
986 // h_inputencoding is only set when there is not more than one
987 // inputenc option because otherwise h_inputencoding must be
988 // set to "auto" (the default encoding of the document language)
989 // Therefore check that exactly one option is passed to inputenc.
990 // It is also only set when there is not more than one babel
992 if (!options.empty()) {
993 string const encoding = options.back();
994 Encoding const * const enc = encodings.fromLaTeXName(
995 encoding, Encoding::inputenc, true);
998 cerr << "Unknown encoding " << encoding
999 << ". Ignoring." << std::endl;
1001 if (!enc->unsafe() && options.size() == 1 && one_language == true)
1002 h_inputencoding = enc->name();
1003 p.setEncoding(enc->iconvName());
1009 else if (name == "srcltx") {
1010 h_output_sync = "1";
1011 if (!opts.empty()) {
1012 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
1015 h_output_sync_macro = "\\usepackage{srcltx}";
1018 else if (is_known(name, known_old_language_packages)) {
1019 // known language packages from the times before babel
1020 // if they are found and not also babel, they will be used as
1021 // custom language package
1022 h_language_package = "\\usepackage{" + name + "}";
1025 else if (name == "lyxskak") {
1026 // ignore this and its options
1027 const char * const o[] = {"ps", "mover", 0};
1028 delete_opt(options, o);
1031 else if (is_known(name, known_lyx_packages) && options.empty()) {
1032 if (name == "splitidx")
1033 h_use_indices = "true";
1034 else if (name == "minted")
1035 h_use_minted = true;
1036 else if (name == "refstyle")
1037 h_use_refstyle = true;
1038 else if (name == "prettyref")
1039 h_use_refstyle = false;
1040 if (!in_lyx_preamble) {
1041 h_preamble << package_beg_sep << name
1042 << package_mid_sep << "\\usepackage{"
1044 if (p.next_token().cat() == catNewline ||
1045 (p.next_token().cat() == catSpace &&
1046 p.next_next_token().cat() == catNewline))
1048 h_preamble << package_end_sep;
1052 else if (name == "geometry")
1053 handle_geometry(options);
1055 else if (name == "subfig")
1056 ; // ignore this FIXME: Use the package separator mechanism instead
1058 else if (char const * const * where = is_known(name, known_languages))
1059 h_language = known_coded_languages[where - known_languages];
1061 else if (name == "natbib") {
1062 h_biblio_style = "plainnat";
1063 h_cite_engine = "natbib";
1064 h_cite_engine_type = "authoryear";
1065 vector<string>::iterator it =
1066 find(options.begin(), options.end(), "authoryear");
1067 if (it != options.end())
1070 it = find(options.begin(), options.end(), "numbers");
1071 if (it != options.end()) {
1072 h_cite_engine_type = "numerical";
1076 if (!options.empty())
1077 h_biblio_options = join(options, ",");
1080 else if (name == "biblatex") {
1081 h_biblio_style = "plainnat";
1082 h_cite_engine = "biblatex";
1083 h_cite_engine_type = "authoryear";
1085 vector<string>::iterator it =
1086 find(options.begin(), options.end(), "natbib");
1087 if (it != options.end()) {
1089 h_cite_engine = "biblatex-natbib";
1091 opt = process_keyval_opt(options, "natbib");
1093 h_cite_engine = "biblatex-natbib";
1095 opt = process_keyval_opt(options, "style");
1097 h_biblatex_citestyle = opt;
1098 h_biblatex_bibstyle = opt;
1100 opt = process_keyval_opt(options, "citestyle");
1102 h_biblatex_citestyle = opt;
1103 opt = process_keyval_opt(options, "bibstyle");
1105 h_biblatex_bibstyle = opt;
1107 opt = process_keyval_opt(options, "refsection");
1109 if (opt == "none" || opt == "part"
1110 || opt == "chapter" || opt == "section"
1111 || opt == "subsection")
1114 cerr << "Ignoring unkown refesection value '"
1117 if (!options.empty()) {
1118 h_biblio_options = join(options, ",");
1123 else if (name == "jurabib") {
1124 h_biblio_style = "jurabib";
1125 h_cite_engine = "jurabib";
1126 h_cite_engine_type = "authoryear";
1127 if (!options.empty())
1128 h_biblio_options = join(options, ",");
1131 else if (name == "bibtopic")
1132 h_use_bibtopic = "true";
1134 else if (name == "chapterbib")
1135 h_multibib = "child";
1137 else if (name == "hyperref")
1138 handle_hyperref(options);
1140 else if (name == "algorithm2e") {
1141 // Load "algorithm2e" module
1142 addModule("algorithm2e");
1143 // Add the package options to the global document options
1144 if (!options.empty()) {
1145 if (h_options.empty())
1146 h_options = join(options, ",");
1148 h_options += ',' + join(options, ",");
1151 else if (name == "microtype") {
1152 //we internally support only microtype without params
1153 if (options.empty())
1154 h_use_microtype = "true";
1156 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1159 else if (!in_lyx_preamble) {
1160 if (options.empty())
1161 h_preamble << "\\usepackage{" << name << '}';
1163 h_preamble << "\\usepackage[" << opts << "]{"
1167 if (p.next_token().cat() == catNewline ||
1168 (p.next_token().cat() == catSpace &&
1169 p.next_next_token().cat() == catNewline))
1173 // We need to do something with the options...
1174 if (!options.empty() && !detectEncoding)
1175 cerr << "Ignoring options '" << join(options, ",")
1176 << "' of package " << name << '.' << endl;
1178 // remove the whitespace
1183 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1186 Token t = p.get_token();
1187 if (t.cat() == catEscape &&
1188 is_known(t.cs(), known_if_commands))
1189 handle_if(p, in_lyx_preamble);
1191 if (!in_lyx_preamble)
1192 h_preamble << t.asInput();
1193 if (t.cat() == catEscape && t.cs() == "fi")
1200 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1202 // set the quote language
1203 // LyX only knows the following quotes languages:
1204 // english, swedish, german, polish, french and danish
1205 // (quotes for "japanese" and "chinese-traditional" are missing because
1206 // they wouldn't be useful: https://www.lyx.org/trac/ticket/6383)
1207 // conversion list taken from
1208 // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
1209 // (quotes for kazakh and interlingua are unknown)
1211 if (is_known(h_language, known_danish_quotes_languages))
1212 h_quotes_style = "danish";
1214 else if (is_known(h_language, known_french_quotes_languages))
1215 h_quotes_style = "french";
1217 else if (is_known(h_language, known_german_quotes_languages))
1218 h_quotes_style = "german";
1220 else if (is_known(h_language, known_polish_quotes_languages))
1221 h_quotes_style = "polish";
1223 else if (is_known(h_language, known_swedish_quotes_languages))
1224 h_quotes_style = "swedish";
1226 else if (is_known(h_language, known_english_quotes_languages))
1227 h_quotes_style = "english";
1229 if (contains(h_float_placement, "H"))
1230 registerAutomaticallyLoadedPackage("float");
1231 if (h_spacing != "single" && h_spacing != "default")
1232 registerAutomaticallyLoadedPackage("setspace");
1233 if (h_use_packages["amsmath"] == "2") {
1234 // amsbsy and amstext are already provided by amsmath
1235 registerAutomaticallyLoadedPackage("amsbsy");
1236 registerAutomaticallyLoadedPackage("amstext");
1239 // output the LyX file settings
1240 // Important: Keep the version formatting in sync with LyX and
1241 // lyx2lyx (bug 7951)
1242 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1243 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1244 << lyx_version_minor << '\n'
1245 << "\\lyxformat " << LYX_FORMAT << '\n'
1246 << "\\begin_document\n"
1247 << "\\begin_header\n"
1248 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1249 << "\\origin " << origin << "\n"
1250 << "\\textclass " << h_textclass << "\n";
1251 string const raw = subdoc ? empty_string() : h_preamble.str();
1253 os << "\\begin_preamble\n";
1254 for (string::size_type i = 0; i < raw.size(); ++i) {
1255 if (raw[i] == package_beg_sep) {
1256 // Here follows some package loading code that
1257 // must be skipped if the package is loaded
1259 string::size_type j = raw.find(package_mid_sep, i);
1260 if (j == string::npos)
1262 string::size_type k = raw.find(package_end_sep, j);
1263 if (k == string::npos)
1265 string const package = raw.substr(i + 1, j - i - 1);
1266 string const replacement = raw.substr(j + 1, k - j - 1);
1267 if (auto_packages.find(package) == auto_packages.end())
1273 os << "\n\\end_preamble\n";
1275 if (!h_options.empty())
1276 os << "\\options " << h_options << "\n";
1277 os << "\\use_default_options " << h_use_default_options << "\n";
1278 if (!used_modules.empty()) {
1279 os << "\\begin_modules\n";
1280 vector<string>::const_iterator const end = used_modules.end();
1281 vector<string>::const_iterator it = used_modules.begin();
1282 for (; it != end; ++it)
1284 os << "\\end_modules\n";
1286 if (!h_includeonlys.empty()) {
1287 os << "\\begin_includeonly\n";
1288 for (auto const & iofile : h_includeonlys)
1289 os << iofile << '\n';
1290 os << "\\end_includeonly\n";
1292 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1293 << "\\language " << h_language << "\n"
1294 << "\\language_package " << h_language_package << "\n"
1295 << "\\inputencoding " << h_inputencoding << "\n"
1296 << "\\fontencoding " << h_fontencoding << "\n"
1297 << "\\font_roman \"" << h_font_roman[0]
1298 << "\" \"" << h_font_roman[1] << "\"\n"
1299 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1300 << "\\font_typewriter \"" << h_font_typewriter[0]
1301 << "\" \"" << h_font_typewriter[1] << "\"\n"
1302 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1303 << "\\font_default_family " << h_font_default_family << "\n"
1304 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1305 << "\\font_sc " << h_font_sc << "\n"
1306 << "\\font_osf " << h_font_osf << "\n"
1307 << "\\font_sf_scale " << h_font_sf_scale[0]
1308 << ' ' << h_font_sf_scale[1] << '\n'
1309 << "\\font_tt_scale " << h_font_tt_scale[0]
1310 << ' ' << h_font_tt_scale[1] << '\n';
1311 if (!h_font_cjk.empty())
1312 os << "\\font_cjk " << h_font_cjk << '\n';
1313 os << "\\use_microtype " << h_use_microtype << '\n'
1314 << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
1315 << "\\graphics " << h_graphics << '\n'
1316 << "\\default_output_format " << h_default_output_format << "\n"
1317 << "\\output_sync " << h_output_sync << "\n";
1318 if (h_output_sync == "1")
1319 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1320 os << "\\bibtex_command " << h_bibtex_command << "\n"
1321 << "\\index_command " << h_index_command << "\n";
1322 if (!h_float_placement.empty())
1323 os << "\\float_placement " << h_float_placement << "\n";
1324 os << "\\paperfontsize " << h_paperfontsize << "\n"
1325 << "\\spacing " << h_spacing << "\n"
1326 << "\\use_hyperref " << h_use_hyperref << '\n';
1327 if (h_use_hyperref == "true") {
1328 if (!h_pdf_title.empty())
1329 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1330 if (!h_pdf_author.empty())
1331 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1332 if (!h_pdf_subject.empty())
1333 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1334 if (!h_pdf_keywords.empty())
1335 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1336 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1337 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1338 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1339 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1340 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1341 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1342 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1343 "\\pdf_backref " << h_pdf_backref << "\n"
1344 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1345 if (!h_pdf_pagemode.empty())
1346 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1347 if (!h_pdf_quoted_options.empty())
1348 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1350 os << "\\papersize " << h_papersize << "\n"
1351 << "\\use_geometry " << h_use_geometry << '\n';
1352 for (map<string, string>::const_iterator it = h_use_packages.begin();
1353 it != h_use_packages.end(); ++it)
1354 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1355 os << "\\cite_engine " << h_cite_engine << '\n'
1356 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1357 << "\\biblio_style " << h_biblio_style << "\n"
1358 << "\\use_bibtopic " << h_use_bibtopic << "\n";
1359 if (!h_biblio_options.empty())
1360 os << "\\biblio_options " << h_biblio_options << "\n";
1361 if (!h_biblatex_bibstyle.empty())
1362 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
1363 if (!h_biblatex_citestyle.empty())
1364 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
1365 if (!h_multibib.empty())
1366 os << "\\multibib " << h_multibib << "\n";
1367 os << "\\use_indices " << h_use_indices << "\n"
1368 << "\\paperorientation " << h_paperorientation << '\n'
1369 << "\\suppress_date " << h_suppress_date << '\n'
1370 << "\\justification " << h_justification << '\n'
1371 << "\\use_refstyle " << h_use_refstyle << '\n'
1372 << "\\use_minted " << h_use_minted << '\n';
1373 if (!h_fontcolor.empty())
1374 os << "\\fontcolor " << h_fontcolor << '\n';
1375 if (!h_notefontcolor.empty())
1376 os << "\\notefontcolor " << h_notefontcolor << '\n';
1377 if (!h_backgroundcolor.empty())
1378 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1379 if (!h_boxbgcolor.empty())
1380 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1381 if (index_number != 0)
1382 for (int i = 0; i < index_number; i++) {
1383 os << "\\index " << h_index[i] << '\n'
1384 << "\\shortcut " << h_shortcut[i] << '\n'
1385 << "\\color " << h_color << '\n'
1389 os << "\\index " << h_index[0] << '\n'
1390 << "\\shortcut " << h_shortcut[0] << '\n'
1391 << "\\color " << h_color << '\n'
1395 << "\\secnumdepth " << h_secnumdepth << "\n"
1396 << "\\tocdepth " << h_tocdepth << "\n"
1397 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1398 if (h_paragraph_separation == "skip")
1399 os << "\\defskip " << h_defskip << "\n";
1401 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1402 os << "\\is_math_indent " << h_is_mathindent << "\n";
1403 if (!h_mathindentation.empty())
1404 os << "\\math_indentation " << h_mathindentation << "\n";
1405 os << "\\math_numbering_side " << h_math_numbering_side << "\n";
1406 os << "\\quotes_style " << h_quotes_style << "\n"
1407 << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
1408 << "\\papercolumns " << h_papercolumns << "\n"
1409 << "\\papersides " << h_papersides << "\n"
1410 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1411 if (!h_listings_params.empty())
1412 os << "\\listings_params " << h_listings_params << "\n";
1413 os << "\\tracking_changes " << h_tracking_changes << "\n"
1414 << "\\output_changes " << h_output_changes << "\n"
1415 << "\\html_math_output " << h_html_math_output << "\n"
1416 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1417 << "\\html_be_strict " << h_html_be_strict << "\n"
1419 << "\\end_header\n\n"
1420 << "\\begin_body\n";
1425 void Preamble::parse(Parser & p, string const & forceclass,
1426 TeX2LyXDocClass & tc)
1428 // initialize fixed types
1429 special_columns_['D'] = 3;
1430 parse(p, forceclass, false, tc);
1434 void Preamble::parse(Parser & p, string const & forceclass,
1435 bool detectEncoding, TeX2LyXDocClass & tc)
1437 bool is_full_document = false;
1438 bool is_lyx_file = false;
1439 bool in_lyx_preamble = false;
1441 // determine whether this is a full document or a fragment for inclusion
1443 Token const & t = p.get_token();
1445 if (t.cat() == catEscape && t.cs() == "documentclass") {
1446 is_full_document = true;
1452 if (detectEncoding && !is_full_document)
1455 while (is_full_document && p.good()) {
1456 if (detectEncoding && h_inputencoding != "auto" &&
1457 h_inputencoding != "default")
1460 Token const & t = p.get_token();
1463 if (!detectEncoding)
1464 cerr << "t: " << t << '\n';
1470 if (!in_lyx_preamble &&
1471 (t.cat() == catLetter ||
1472 t.cat() == catSuper ||
1473 t.cat() == catSub ||
1474 t.cat() == catOther ||
1475 t.cat() == catMath ||
1476 t.cat() == catActive ||
1477 t.cat() == catBegin ||
1478 t.cat() == catEnd ||
1479 t.cat() == catAlign ||
1480 t.cat() == catParameter)) {
1481 h_preamble << t.cs();
1485 if (!in_lyx_preamble &&
1486 (t.cat() == catSpace || t.cat() == catNewline)) {
1487 h_preamble << t.asInput();
1491 if (t.cat() == catComment) {
1492 static regex const islyxfile("%% LyX .* created this file");
1493 static regex const usercommands("User specified LaTeX commands");
1495 string const comment = t.asInput();
1497 // magically switch encoding default if it looks like XeLaTeX
1498 static string const magicXeLaTeX =
1499 "% This document must be compiled with XeLaTeX ";
1500 if (comment.size() > magicXeLaTeX.size()
1501 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1502 && h_inputencoding == "auto") {
1503 if (!detectEncoding)
1504 cerr << "XeLaTeX comment found, switching to UTF8\n";
1505 h_inputencoding = "utf8";
1508 if (regex_search(comment, sub, islyxfile)) {
1510 in_lyx_preamble = true;
1511 } else if (is_lyx_file
1512 && regex_search(comment, sub, usercommands))
1513 in_lyx_preamble = false;
1514 else if (!in_lyx_preamble)
1515 h_preamble << t.asInput();
1519 if (t.cs() == "PassOptionsToPackage") {
1520 string const poptions = p.getArg('{', '}');
1521 string const package = p.verbatim_item();
1522 extra_package_options_.insert(make_pair(package, poptions));
1526 if (t.cs() == "pagestyle") {
1527 h_paperpagestyle = p.verbatim_item();
1531 if (t.cs() == "setdefaultlanguage") {
1533 // We don't yet care about non-language variant options
1534 // because LyX doesn't support this yet, see bug #8214
1536 string langopts = p.getOpt();
1537 // check if the option contains a variant, if yes, extract it
1538 string::size_type pos_var = langopts.find("variant");
1539 string::size_type i = langopts.find(',', pos_var);
1540 string::size_type k = langopts.find('=', pos_var);
1541 if (pos_var != string::npos){
1543 if (i == string::npos)
1544 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1546 variant = langopts.substr(k + 1, i - k - 1);
1547 h_language = variant;
1551 h_language = p.verbatim_item();
1552 //finally translate the poyglossia name to a LyX name
1553 h_language = polyglossia2lyx(h_language);
1557 if (t.cs() == "setotherlanguage") {
1558 // We don't yet care about the option because LyX doesn't
1559 // support this yet, see bug #8214
1560 p.hasOpt() ? p.getOpt() : string();
1565 if (t.cs() == "setmainfont") {
1566 // we don't care about the option
1567 p.hasOpt() ? p.getOpt() : string();
1568 h_font_roman[1] = p.getArg('{', '}');
1572 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1573 // LyX currently only supports the scale option
1576 string fontopts = p.getArg('[', ']');
1577 // check if the option contains a scaling, if yes, extract it
1578 string::size_type pos = fontopts.find("Scale");
1579 if (pos != string::npos) {
1580 string::size_type i = fontopts.find(',', pos);
1581 if (i == string::npos)
1582 scale_as_percentage(fontopts.substr(pos + 1), scale);
1584 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1587 if (t.cs() == "setsansfont") {
1589 h_font_sf_scale[1] = scale;
1590 h_font_sans[1] = p.getArg('{', '}');
1593 h_font_tt_scale[1] = scale;
1594 h_font_typewriter[1] = p.getArg('{', '}');
1599 else if (t.cs() == "date") {
1600 string argument = p.getArg('{', '}');
1601 if (argument.empty())
1602 h_suppress_date = "true";
1604 h_preamble << t.asInput() << '{' << argument << '}';
1607 if (t.cs() == "color") {
1608 string const space =
1609 (p.hasOpt() ? p.getOpt() : string());
1610 string argument = p.getArg('{', '}');
1611 // check the case that a standard color is used
1612 if (space.empty() && is_known(argument, known_basic_colors)) {
1613 h_fontcolor = rgbcolor2code(argument);
1614 registerAutomaticallyLoadedPackage("color");
1615 } else if (space.empty() && argument == "document_fontcolor")
1616 registerAutomaticallyLoadedPackage("color");
1617 // check the case that LyX's document_fontcolor is defined
1618 // but not used for \color
1620 h_preamble << t.asInput();
1622 h_preamble << space;
1623 h_preamble << '{' << argument << '}';
1624 // the color might already be set because \definecolor
1625 // is parsed before this
1631 if (t.cs() == "pagecolor") {
1632 string argument = p.getArg('{', '}');
1633 // check the case that a standard color is used
1634 if (is_known(argument, known_basic_colors)) {
1635 h_backgroundcolor = rgbcolor2code(argument);
1636 } else if (argument == "page_backgroundcolor")
1637 registerAutomaticallyLoadedPackage("color");
1638 // check the case that LyX's page_backgroundcolor is defined
1639 // but not used for \pagecolor
1641 h_preamble << t.asInput() << '{' << argument << '}';
1642 // the color might already be set because \definecolor
1643 // is parsed before this
1644 h_backgroundcolor = "";
1649 if (t.cs() == "makeatletter") {
1650 // LyX takes care of this
1651 p.setCatcode('@', catLetter);
1655 if (t.cs() == "makeatother") {
1656 // LyX takes care of this
1657 p.setCatcode('@', catOther);
1661 if (t.cs() == "makeindex") {
1662 // LyX will re-add this if a print index command is found
1667 if (t.cs() == "newindex") {
1668 string const indexname = p.getArg('[', ']');
1669 string const shortcut = p.verbatim_item();
1670 if (!indexname.empty())
1671 h_index[index_number] = indexname;
1673 h_index[index_number] = shortcut;
1674 h_shortcut[index_number] = shortcut;
1680 if (t.cs() == "addbibresource") {
1681 biblatex_bibliographies.push_back(removeExtension(p.getArg('{', '}')));
1685 if (t.cs() == "bibliography") {
1686 vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
1687 biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
1691 if (t.cs() == "RS@ifundefined") {
1692 string const name = p.verbatim_item();
1693 string const body1 = p.verbatim_item();
1694 string const body2 = p.verbatim_item();
1695 // only non-lyxspecific stuff
1696 if (in_lyx_preamble &&
1697 (name == "subsecref" || name == "thmref" || name == "lemref"))
1701 ss << '\\' << t.cs();
1702 ss << '{' << name << '}'
1703 << '{' << body1 << '}'
1704 << '{' << body2 << '}';
1705 h_preamble << ss.str();
1710 if (t.cs() == "AtBeginDocument") {
1711 string const name = p.verbatim_item();
1712 // only non-lyxspecific stuff
1713 if (in_lyx_preamble &&
1714 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
1715 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
1716 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
1717 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
1718 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
1719 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
1720 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
1721 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
1722 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
1723 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
1724 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
1725 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
1726 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
1727 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
1728 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
1732 ss << '\\' << t.cs();
1733 ss << '{' << name << '}';
1734 h_preamble << ss.str();
1739 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1740 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1741 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1742 || t.cs() == "DeclareRobustCommand"
1743 || t.cs() == "DeclareRobustCommandx"
1744 || t.cs() == "ProvideTextCommandDefault"
1745 || t.cs() == "DeclareMathAccent") {
1747 if (p.next_token().character() == '*') {
1751 string const name = p.verbatim_item();
1752 string const opt1 = p.getFullOpt();
1753 string const opt2 = p.getFullOpt();
1754 string const body = p.verbatim_item();
1755 // store the in_lyx_preamble setting
1756 bool const was_in_lyx_preamble = in_lyx_preamble;
1758 if (name == "\\rmdefault")
1759 if (is_known(body, known_roman_fonts)) {
1760 h_font_roman[0] = body;
1762 in_lyx_preamble = true;
1764 if (name == "\\sfdefault")
1765 if (is_known(body, known_sans_fonts)) {
1766 h_font_sans[0] = body;
1768 in_lyx_preamble = true;
1770 if (name == "\\ttdefault")
1771 if (is_known(body, known_typewriter_fonts)) {
1772 h_font_typewriter[0] = body;
1774 in_lyx_preamble = true;
1776 if (name == "\\familydefault") {
1777 string family = body;
1778 // remove leading "\"
1779 h_font_default_family = family.erase(0,1);
1781 in_lyx_preamble = true;
1784 // remove LyX-specific definitions that are re-added by LyX
1786 // \lyxline is an ancient command that is converted by tex2lyx into
1787 // a \rule therefore remove its preamble code
1788 if (name == "\\lyxdot" || name == "\\lyxarrow"
1789 || name == "\\lyxline" || name == "\\LyX") {
1791 in_lyx_preamble = true;
1794 // Add the command to the known commands
1795 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1797 // only non-lyxspecific stuff
1798 if (!in_lyx_preamble) {
1800 ss << '\\' << t.cs();
1803 ss << '{' << name << '}' << opt1 << opt2
1804 << '{' << body << "}";
1805 h_preamble << ss.str();
1807 ostream & out = in_preamble ? h_preamble : os;
1808 out << "\\" << t.cs() << "{" << name << "}"
1809 << opts << "{" << body << "}";
1812 // restore the in_lyx_preamble setting
1813 in_lyx_preamble = was_in_lyx_preamble;
1817 if (t.cs() == "documentclass") {
1818 vector<string>::iterator it;
1819 vector<string> opts = split_options(p.getArg('[', ']'));
1820 handle_opt(opts, known_fontsizes, h_paperfontsize);
1821 delete_opt(opts, known_fontsizes);
1822 // delete "pt" at the end
1823 string::size_type i = h_paperfontsize.find("pt");
1824 if (i != string::npos)
1825 h_paperfontsize.erase(i);
1826 // The documentclass options are always parsed before the options
1827 // of the babel call so that a language cannot overwrite the babel
1829 handle_opt(opts, known_languages, h_language);
1830 delete_opt(opts, known_languages);
1833 if ((it = find(opts.begin(), opts.end(), "fleqn"))
1835 h_is_mathindent = "1";
1838 // formula numbering side
1839 if ((it = find(opts.begin(), opts.end(), "leqno"))
1841 h_math_numbering_side = "left";
1844 else if ((it = find(opts.begin(), opts.end(), "reqno"))
1846 h_math_numbering_side = "right";
1850 // paper orientation
1851 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1852 h_paperorientation = "landscape";
1856 if ((it = find(opts.begin(), opts.end(), "oneside"))
1861 if ((it = find(opts.begin(), opts.end(), "twoside"))
1867 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1869 h_papercolumns = "1";
1872 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1874 h_papercolumns = "2";
1878 // some size options are known to any document classes, other sizes
1879 // are handled by the \geometry command of the geometry package
1880 handle_opt(opts, known_class_paper_sizes, h_papersize);
1881 delete_opt(opts, known_class_paper_sizes);
1882 // the remaining options
1883 h_options = join(opts, ",");
1884 // FIXME This does not work for classes that have a
1885 // different name in LyX than in LaTeX
1886 h_textclass = p.getArg('{', '}');
1891 if (t.cs() == "usepackage") {
1892 string const options = p.getArg('[', ']');
1893 string const name = p.getArg('{', '}');
1894 vector<string> vecnames;
1895 split(name, vecnames, ',');
1896 vector<string>::const_iterator it = vecnames.begin();
1897 vector<string>::const_iterator end = vecnames.end();
1898 for (; it != end; ++it)
1899 handle_package(p, trimSpaceAndEol(*it), options,
1900 in_lyx_preamble, detectEncoding);
1904 if (t.cs() == "inputencoding") {
1905 string const encoding = p.getArg('{','}');
1906 Encoding const * const enc = encodings.fromLaTeXName(
1907 encoding, Encoding::inputenc, true);
1909 if (!detectEncoding)
1910 cerr << "Unknown encoding " << encoding
1911 << ". Ignoring." << std::endl;
1914 h_inputencoding = enc->name();
1915 p.setEncoding(enc->iconvName());
1920 if (t.cs() == "newenvironment") {
1921 string const name = p.getArg('{', '}');
1922 string const opt1 = p.getFullOpt();
1923 string const opt2 = p.getFullOpt();
1924 string const beg = p.verbatim_item();
1925 string const end = p.verbatim_item();
1926 if (!in_lyx_preamble) {
1927 h_preamble << "\\newenvironment{" << name
1928 << '}' << opt1 << opt2 << '{'
1929 << beg << "}{" << end << '}';
1931 add_known_environment(name, opt1, !opt2.empty(),
1932 from_utf8(beg), from_utf8(end));
1936 if (t.cs() == "newtheorem") {
1938 if (p.next_token().character() == '*') {
1942 string const name = p.getArg('{', '}');
1943 string const opt1 = p.getFullOpt();
1944 string const opt2 = p.getFullOpt();
1945 string const body = p.verbatim_item();
1946 string const opt3 = p.getFullOpt();
1947 string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
1949 string const complete = cmd + "{" + name + '}' +
1950 opt1 + opt2 + '{' + body + '}' + opt3;
1952 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
1954 if (!in_lyx_preamble)
1955 h_preamble << complete;
1959 if (t.cs() == "def") {
1960 string name = p.get_token().cs();
1961 // In fact, name may be more than the name:
1962 // In the test case of bug 8116
1963 // name == "csname SF@gobble@opt \endcsname".
1964 // Therefore, we need to use asInput() instead of cs().
1965 while (p.next_token().cat() != catBegin)
1966 name += p.get_token().asInput();
1967 if (!in_lyx_preamble)
1968 h_preamble << "\\def\\" << name << '{'
1969 << p.verbatim_item() << "}";
1973 if (t.cs() == "newcolumntype") {
1974 string const name = p.getArg('{', '}');
1975 trimSpaceAndEol(name);
1977 string opts = p.getOpt();
1978 if (!opts.empty()) {
1979 istringstream is(string(opts, 1));
1982 special_columns_[name[0]] = nargs;
1983 h_preamble << "\\newcolumntype{" << name << "}";
1985 h_preamble << "[" << nargs << "]";
1986 h_preamble << "{" << p.verbatim_item() << "}";
1990 if (t.cs() == "setcounter") {
1991 string const name = p.getArg('{', '}');
1992 string const content = p.getArg('{', '}');
1993 if (name == "secnumdepth")
1994 h_secnumdepth = content;
1995 else if (name == "tocdepth")
1996 h_tocdepth = content;
1998 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
2002 if (t.cs() == "setlength") {
2003 string const name = p.verbatim_item();
2004 string const content = p.verbatim_item();
2005 // the paragraphs are only not indented when \parindent is set to zero
2006 if (name == "\\parindent" && content != "") {
2007 if (content[0] == '0')
2008 h_paragraph_separation = "skip";
2010 h_paragraph_indentation = translate_len(content);
2011 } else if (name == "\\parskip") {
2012 if (content == "\\smallskipamount")
2013 h_defskip = "smallskip";
2014 else if (content == "\\medskipamount")
2015 h_defskip = "medskip";
2016 else if (content == "\\bigskipamount")
2017 h_defskip = "bigskip";
2019 h_defskip = translate_len(content);
2020 } else if (name == "\\mathindent") {
2021 h_mathindentation = translate_len(content);
2023 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2027 if (t.cs() == "onehalfspacing") {
2028 h_spacing = "onehalf";
2032 if (t.cs() == "doublespacing") {
2033 h_spacing = "double";
2037 if (t.cs() == "setstretch") {
2038 h_spacing = "other " + p.verbatim_item();
2042 if (t.cs() == "synctex") {
2043 // the scheme is \synctex=value
2044 // where value can only be "1" or "-1"
2045 h_output_sync = "1";
2046 // there can be any character behind the value (e.g. a linebreak or a '\'
2047 // therefore we extract it char by char
2049 string value = p.get_token().asInput();
2051 value += p.get_token().asInput();
2052 h_output_sync_macro = "\\synctex=" + value;
2056 if (t.cs() == "begin") {
2057 string const name = p.getArg('{', '}');
2058 if (name == "document")
2060 h_preamble << "\\begin{" << name << "}";
2064 if (t.cs() == "geometry") {
2065 vector<string> opts = split_options(p.getArg('{', '}'));
2066 handle_geometry(opts);
2070 if (t.cs() == "definecolor") {
2071 string const color = p.getArg('{', '}');
2072 string const space = p.getArg('{', '}');
2073 string const value = p.getArg('{', '}');
2074 if (color == "document_fontcolor" && space == "rgb") {
2075 RGBColor c(RGBColorFromLaTeX(value));
2076 h_fontcolor = X11hexname(c);
2077 } else if (color == "note_fontcolor" && space == "rgb") {
2078 RGBColor c(RGBColorFromLaTeX(value));
2079 h_notefontcolor = X11hexname(c);
2080 } else if (color == "page_backgroundcolor" && space == "rgb") {
2081 RGBColor c(RGBColorFromLaTeX(value));
2082 h_backgroundcolor = X11hexname(c);
2083 } else if (color == "shadecolor" && space == "rgb") {
2084 RGBColor c(RGBColorFromLaTeX(value));
2085 h_boxbgcolor = X11hexname(c);
2087 h_preamble << "\\definecolor{" << color
2088 << "}{" << space << "}{" << value
2094 if (t.cs() == "bibliographystyle") {
2095 h_biblio_style = p.verbatim_item();
2099 if (t.cs() == "jurabibsetup") {
2100 // FIXME p.getArg('{', '}') is most probably wrong (it
2101 // does not handle nested braces).
2102 // Use p.verbatim_item() instead.
2103 vector<string> jurabibsetup =
2104 split_options(p.getArg('{', '}'));
2105 // add jurabibsetup to the jurabib package options
2106 add_package("jurabib", jurabibsetup);
2107 if (!jurabibsetup.empty()) {
2108 h_preamble << "\\jurabibsetup{"
2109 << join(jurabibsetup, ",") << '}';
2114 if (t.cs() == "hypersetup") {
2115 vector<string> hypersetup =
2116 split_options(p.verbatim_item());
2117 // add hypersetup to the hyperref package options
2118 handle_hyperref(hypersetup);
2119 if (!hypersetup.empty()) {
2120 h_preamble << "\\hypersetup{"
2121 << join(hypersetup, ",") << '}';
2126 if (t.cs() == "includeonly") {
2127 vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
2128 for (auto & iofile : includeonlys) {
2129 string filename(normalize_filename(iofile));
2130 string const path = getMasterFilePath(true);
2131 // We want to preserve relative/absolute filenames,
2132 // therefore path is only used for testing
2133 if (!makeAbsPath(filename, path).exists()) {
2134 // The file extension is probably missing.
2135 // Now try to find it out.
2136 string const tex_name =
2137 find_file(filename, path,
2138 known_tex_extensions);
2139 if (!tex_name.empty())
2140 filename = tex_name;
2143 if (makeAbsPath(filename, path).exists())
2144 fix_child_filename(filename);
2146 cerr << "Warning: Could not find included file '"
2147 << filename << "'." << endl;
2148 outname = changeExtension(filename, "lyx");
2149 h_includeonlys.push_back(outname);
2154 if (is_known(t.cs(), known_if_3arg_commands)) {
2155 // prevent misparsing of \usepackage if it is used
2156 // as an argument (see e.g. our own output of
2157 // \@ifundefined above)
2158 string const arg1 = p.verbatim_item();
2159 string const arg2 = p.verbatim_item();
2160 string const arg3 = p.verbatim_item();
2161 // test case \@ifundefined{date}{}{\date{}}
2162 if (t.cs() == "@ifundefined" && arg1 == "date" &&
2163 arg2.empty() && arg3 == "\\date{}") {
2164 h_suppress_date = "true";
2165 // older tex2lyx versions did output
2166 // \@ifundefined{definecolor}{\usepackage{color}}{}
2167 } else if (t.cs() == "@ifundefined" &&
2168 arg1 == "definecolor" &&
2169 arg2 == "\\usepackage{color}" &&
2171 if (!in_lyx_preamble)
2172 h_preamble << package_beg_sep
2175 << "\\@ifundefined{definecolor}{color}{}"
2178 //\@ifundefined{showcaptionsetup}{}{%
2179 // \PassOptionsToPackage{caption=false}{subfig}}
2180 // that LyX uses for subfloats
2181 } else if (t.cs() == "@ifundefined" &&
2182 arg1 == "showcaptionsetup" && arg2.empty()
2183 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
2185 } else if (!in_lyx_preamble) {
2186 h_preamble << t.asInput()
2187 << '{' << arg1 << '}'
2188 << '{' << arg2 << '}'
2189 << '{' << arg3 << '}';
2194 if (is_known(t.cs(), known_if_commands)) {
2195 // must not parse anything in conditional code, since
2196 // LyX would output the parsed contents unconditionally
2197 if (!in_lyx_preamble)
2198 h_preamble << t.asInput();
2199 handle_if(p, in_lyx_preamble);
2203 if (!t.cs().empty() && !in_lyx_preamble) {
2204 h_preamble << '\\' << t.cs();
2209 // remove the whitespace
2212 // Force textclass if the user wanted it
2213 if (!forceclass.empty())
2214 h_textclass = forceclass;
2215 tc.setName(h_textclass);
2216 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2217 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2220 if (h_papersides.empty()) {
2223 h_papersides = ss.str();
2226 // If the CJK package is used we cannot set the document language from
2227 // the babel options. Instead, we guess which language is used most
2228 // and set this one.
2229 default_language = h_language;
2230 if (is_full_document &&
2231 (auto_packages.find("CJK") != auto_packages.end() ||
2232 auto_packages.find("CJKutf8") != auto_packages.end())) {
2234 h_language = guessLanguage(p, default_language);
2236 if (explicit_babel && h_language != default_language) {
2237 // We set the document language to a CJK language,
2238 // but babel is explicitly called in the user preamble
2239 // without options. LyX will not add the default
2240 // language to the document options if it is either
2241 // english, or no text is set as default language.
2242 // Therefore we need to add a language option explicitly.
2243 // FIXME: It would be better to remove all babel calls
2244 // from the user preamble, but this is difficult
2245 // without re-introducing bug 7861.
2246 if (h_options.empty())
2247 h_options = lyx2babel(default_language);
2249 h_options += ',' + lyx2babel(default_language);
2255 string Preamble::parseEncoding(Parser & p, string const & forceclass)
2257 TeX2LyXDocClass dummy;
2258 parse(p, forceclass, true, dummy);
2259 if (h_inputencoding != "auto" && h_inputencoding != "default")
2260 return h_inputencoding;
2265 string babel2lyx(string const & language)
2267 char const * const * where = is_known(language, known_languages);
2269 return known_coded_languages[where - known_languages];
2274 string lyx2babel(string const & language)
2276 char const * const * where = is_known(language, known_coded_languages);
2278 return known_languages[where - known_coded_languages];
2283 string Preamble::polyglossia2lyx(string const & language)
2285 char const * const * where = is_known(language, polyglossia_languages);
2287 return coded_polyglossia_languages[where - polyglossia_languages];
2292 string rgbcolor2code(string const & name)
2294 char const * const * where = is_known(name, known_basic_colors);
2296 // "red", "green" etc
2297 return known_basic_color_codes[where - known_basic_colors];
2299 // "255,0,0", "0,255,0" etc
2300 RGBColor c(RGBColorFromLaTeX(name));
2301 return X11hexname(c);