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", "libertineRoman", "libertine-type1", "lmodern", "mathdesign", "mathpazo",
130 "mathptmx", "MinionPro", "newcent", "NotoSerif-TLF", "PTSerif-TLF", "tgbonum", "tgchorus",
131 "tgpagella", "tgschola", "tgtermes", "utopia", 0 };
133 const char * const known_sans_fonts[] = { "avant", "berasans", "biolinum",
134 "biolinum-type1", "cmbr", "cmss", "helvet", "iwona", "iwonac", "iwonal", "iwonalc",
135 "kurier", "kurierc", "kurierl", "kurierlc", "lmss", "NotoSans-TLF", "PTSans-TLF",
136 "tgadventor", "tgheros", "uop", 0 };
138 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
139 "courier", "lmtt", "luximono", "fourier", "libertineMono", "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";
737 // as well as libertineMono
738 h_font_typewriter[0] = "libertine-mono";
741 else if (opts == "lining")
742 h_font_osf = "false";
745 if (name == "libertineRoman" || name == "libertine-type1") {
746 h_font_roman[0] = "libertine";
747 // NOTE: contrary to libertine.sty, libertineRoman
748 // and libertine-type1 do not automatically invoke
749 // biolinum and libertineMono
750 if (opts == "lining")
751 h_font_osf = "false";
752 else if (opts == "osf")
756 if (name == "MinionPro") {
757 h_font_roman[0] = "minionpro";
758 if (opts.find("lf") != string::npos)
759 h_font_osf = "false";
762 if (opts.find("onlytext") != string::npos)
763 h_font_math[0] = "default";
765 h_font_math[0] = "auto";
768 if (name == "mathdesign") {
769 if (opts.find("charter") != string::npos)
770 h_font_roman[0] = "md-charter";
771 if (opts.find("garamond") != string::npos)
772 h_font_roman[0] = "md-garamond";
773 if (opts.find("utopia") != string::npos)
774 h_font_roman[0] = "md-utopia";
775 if (opts.find("expert") != string::npos) {
781 else if (name == "mathpazo")
782 h_font_roman[0] = "palatino";
784 else if (name == "mathptmx")
785 h_font_roman[0] = "times";
787 if (name == "crimson")
788 h_font_roman[0] = "cochineal";
790 if (name == "cochineal") {
791 h_font_roman[0] = "cochineal";
792 // cochineal can have several options, e.g. [proportional,osf]
793 string::size_type pos = opts.find("osf");
794 if (pos != string::npos)
798 if (name == "noto") {
799 // noto can have several options
801 h_font_roman[0] = "NotoSerif-TLF";
802 string::size_type pos = opts.find("rm");
803 if (pos != string::npos)
804 h_font_roman[0] = "NotoSerif-TLF";
805 pos = opts.find("sf");
806 if (pos != string::npos)
807 h_font_sans[0] = "NotoSans-TLF";
808 pos = opts.find("nott");
809 if (pos != string::npos) {
810 h_font_roman[0] = "NotoSerif-TLF";
811 h_font_sans[0] = "NotoSans-TLF";
813 // noto as typewriter is handled in handling of \ttdefault
814 // special cases are handled in handling of \rmdefault and \sfdefault
817 if (name == "paratype") {
818 // in this case all fonts are ParaType
819 h_font_roman[0] = "PTSerif-TLF";
820 h_font_sans[0] = "default";
821 h_font_typewriter[0] = "default";
824 if (name == "PTSerif")
825 h_font_roman[0] = "PTSerif-TLF";
828 if (is_known(name, known_sans_fonts)) {
829 h_font_sans[0] = name;
830 if (options.size() >= 1) {
831 if (scale_as_percentage(opts, h_font_sf_scale[0]))
836 if (name == "biolinum" || name == "biolinum-type1") {
837 h_font_sans[0] = "biolinum";
838 // biolinum can have several options, e.g. [osf,scaled=0.97]
839 string::size_type pos = opts.find("osf");
840 if (pos != string::npos)
844 if (name == "PTSans") {
845 h_font_sans[0] = "PTSans-TLF";
846 if (options.size() >= 1) {
847 if (scale_as_percentage(opts, h_font_sf_scale[0]))
853 if (is_known(name, known_typewriter_fonts)) {
854 // fourier can be set as roman font _only_
855 // fourier as typewriter is handled in handling of \ttdefault
856 if (name != "fourier") {
857 h_font_typewriter[0] = name;
858 if (options.size() >= 1) {
859 if (scale_as_percentage(opts, h_font_tt_scale[0]))
865 if (name == "libertineMono" || name == "libertineMono-type1")
866 h_font_typewriter[0] = "libertine-mono";
868 if (name == "PTMono") {
869 h_font_typewriter[0] = "PTMono-TLF";
870 if (options.size() >= 1) {
871 if (scale_as_percentage(opts, h_font_tt_scale[0]))
876 // font uses old-style figure
881 if (is_known(name, known_math_fonts))
882 h_font_math[0] = name;
884 if (name == "newtxmath") {
886 h_font_math[0] = "newtxmath";
887 else if (opts == "garamondx")
888 h_font_math[0] = "garamondx-ntxm";
889 else if (opts == "libertine")
890 h_font_math[0] = "libertine-ntxm";
891 else if (opts == "minion")
892 h_font_math[0] = "minion-ntxm";
893 else if (opts == "cochineal")
894 h_font_math[0] = "cochineal-ntxm";
899 h_font_math[0] = "iwona-math";
901 if (name == "kurier")
903 h_font_math[0] = "kurier-math";
905 // after the detection and handling of special cases, we can remove the
906 // fonts, otherwise they would appear in the preamble, see bug #7856
907 if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
908 || is_known(name, known_typewriter_fonts) || is_known(name, known_math_fonts))
910 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
911 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
912 name == "esint" || name == "mhchem" || name == "mathdots" ||
913 name == "mathtools" || name == "stackrel" ||
914 name == "stmaryrd" || name == "undertilde")
915 h_use_packages[name] = "2";
917 else if (name == "babel") {
918 h_language_package = "default";
919 // One might think we would have to do nothing if babel is loaded
920 // without any options to prevent pollution of the preamble with this
921 // babel call in every roundtrip.
922 // But the user could have defined babel-specific things afterwards. So
923 // we need to keep it in the preamble to prevent cases like bug #7861.
925 // check if more than one option was used - used later for inputenc
926 if (options.begin() != options.end() - 1)
927 one_language = false;
928 // babel takes the last language of the option of its \usepackage
929 // call as document language. If there is no such language option, the
930 // last language in the documentclass options is used.
931 handle_opt(options, known_languages, h_language);
932 // translate the babel name to a LyX name
933 h_language = babel2lyx(h_language);
934 if (h_language == "japanese") {
935 // For Japanese, the encoding isn't indicated in the source
936 // file, and there's really not much we can do. We could
937 // 1) offer a list of possible encodings to choose from, or
938 // 2) determine the encoding of the file by inspecting it.
939 // For the time being, we leave the encoding alone so that
940 // we don't get iconv errors when making a wrong guess, and
941 // we will output a note at the top of the document
942 // explaining what to do.
943 Encoding const * const enc = encodings.fromIconvName(
944 p.getEncoding(), Encoding::japanese, false);
946 h_inputencoding = enc->name();
947 is_nonCJKJapanese = true;
948 // in this case babel can be removed from the preamble
949 registerAutomaticallyLoadedPackage("babel");
951 // If babel is called with options, LyX puts them by default into the
952 // document class options. This works for most languages, except
953 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
954 // perhaps in future others.
955 // Therefore keep the babel call as it is as the user might have
957 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
959 delete_opt(options, known_languages);
961 h_preamble << "\\usepackage{babel}\n";
962 explicit_babel = true;
966 else if (name == "polyglossia") {
967 h_language_package = "default";
968 h_default_output_format = "pdf4";
969 h_use_non_tex_fonts = true;
971 registerAutomaticallyLoadedPackage("xunicode");
972 if (h_inputencoding == "auto")
973 p.setEncoding("UTF-8");
976 else if (name == "CJK") {
977 // set the encoding to "auto" because it might be set to "default" by the babel handling
978 // and this would not be correct for CJK
979 if (h_inputencoding == "default")
980 h_inputencoding = "auto";
981 registerAutomaticallyLoadedPackage("CJK");
984 else if (name == "CJKutf8") {
985 h_inputencoding = "utf8-cjk";
986 p.setEncoding("UTF-8");
987 registerAutomaticallyLoadedPackage("CJKutf8");
990 else if (name == "fontenc") {
991 h_fontencoding = getStringFromVector(options, ",");
992 /* We could do the following for better round trip support,
993 * but this makes the document less portable, so I skip it:
994 if (h_fontencoding == lyxrc.fontenc)
995 h_fontencoding = "global";
1000 else if (name == "inputenc" || name == "luainputenc") {
1001 // h_inputencoding is only set when there is not more than one
1002 // inputenc option because otherwise h_inputencoding must be
1003 // set to "auto" (the default encoding of the document language)
1004 // Therefore check that exactly one option is passed to inputenc.
1005 // It is also only set when there is not more than one babel
1007 if (!options.empty()) {
1008 string const encoding = options.back();
1009 Encoding const * const enc = encodings.fromLaTeXName(
1010 encoding, Encoding::inputenc, true);
1012 if (!detectEncoding)
1013 cerr << "Unknown encoding " << encoding
1014 << ". Ignoring." << std::endl;
1016 if (!enc->unsafe() && options.size() == 1 && one_language == true)
1017 h_inputencoding = enc->name();
1018 p.setEncoding(enc->iconvName());
1024 else if (name == "srcltx") {
1025 h_output_sync = "1";
1026 if (!opts.empty()) {
1027 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
1030 h_output_sync_macro = "\\usepackage{srcltx}";
1033 else if (is_known(name, known_old_language_packages)) {
1034 // known language packages from the times before babel
1035 // if they are found and not also babel, they will be used as
1036 // custom language package
1037 h_language_package = "\\usepackage{" + name + "}";
1040 else if (name == "lyxskak") {
1041 // ignore this and its options
1042 const char * const o[] = {"ps", "mover", 0};
1043 delete_opt(options, o);
1046 else if (is_known(name, known_lyx_packages) && options.empty()) {
1047 if (name == "splitidx")
1048 h_use_indices = "true";
1049 else if (name == "minted")
1050 h_use_minted = true;
1051 else if (name == "refstyle")
1052 h_use_refstyle = true;
1053 else if (name == "prettyref")
1054 h_use_refstyle = false;
1055 if (!in_lyx_preamble) {
1056 h_preamble << package_beg_sep << name
1057 << package_mid_sep << "\\usepackage{"
1059 if (p.next_token().cat() == catNewline ||
1060 (p.next_token().cat() == catSpace &&
1061 p.next_next_token().cat() == catNewline))
1063 h_preamble << package_end_sep;
1067 else if (name == "geometry")
1068 handle_geometry(options);
1070 else if (name == "subfig")
1071 ; // ignore this FIXME: Use the package separator mechanism instead
1073 else if (char const * const * where = is_known(name, known_languages))
1074 h_language = known_coded_languages[where - known_languages];
1076 else if (name == "natbib") {
1077 h_biblio_style = "plainnat";
1078 h_cite_engine = "natbib";
1079 h_cite_engine_type = "authoryear";
1080 vector<string>::iterator it =
1081 find(options.begin(), options.end(), "authoryear");
1082 if (it != options.end())
1085 it = find(options.begin(), options.end(), "numbers");
1086 if (it != options.end()) {
1087 h_cite_engine_type = "numerical";
1091 if (!options.empty())
1092 h_biblio_options = join(options, ",");
1095 else if (name == "biblatex") {
1096 h_biblio_style = "plainnat";
1097 h_cite_engine = "biblatex";
1098 h_cite_engine_type = "authoryear";
1100 vector<string>::iterator it =
1101 find(options.begin(), options.end(), "natbib");
1102 if (it != options.end()) {
1104 h_cite_engine = "biblatex-natbib";
1106 opt = process_keyval_opt(options, "natbib");
1108 h_cite_engine = "biblatex-natbib";
1110 opt = process_keyval_opt(options, "style");
1112 h_biblatex_citestyle = opt;
1113 h_biblatex_bibstyle = opt;
1115 opt = process_keyval_opt(options, "citestyle");
1117 h_biblatex_citestyle = opt;
1118 opt = process_keyval_opt(options, "bibstyle");
1120 h_biblatex_bibstyle = opt;
1122 opt = process_keyval_opt(options, "refsection");
1124 if (opt == "none" || opt == "part"
1125 || opt == "chapter" || opt == "section"
1126 || opt == "subsection")
1129 cerr << "Ignoring unkown refesection value '"
1132 if (!options.empty()) {
1133 h_biblio_options = join(options, ",");
1138 else if (name == "jurabib") {
1139 h_biblio_style = "jurabib";
1140 h_cite_engine = "jurabib";
1141 h_cite_engine_type = "authoryear";
1142 if (!options.empty())
1143 h_biblio_options = join(options, ",");
1146 else if (name == "bibtopic")
1147 h_use_bibtopic = "true";
1149 else if (name == "chapterbib")
1150 h_multibib = "child";
1152 else if (name == "hyperref")
1153 handle_hyperref(options);
1155 else if (name == "algorithm2e") {
1156 // Load "algorithm2e" module
1157 addModule("algorithm2e");
1158 // Add the package options to the global document options
1159 if (!options.empty()) {
1160 if (h_options.empty())
1161 h_options = join(options, ",");
1163 h_options += ',' + join(options, ",");
1166 else if (name == "microtype") {
1167 //we internally support only microtype without params
1168 if (options.empty())
1169 h_use_microtype = "true";
1171 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1174 else if (!in_lyx_preamble) {
1175 if (options.empty())
1176 h_preamble << "\\usepackage{" << name << '}';
1178 h_preamble << "\\usepackage[" << opts << "]{"
1182 if (p.next_token().cat() == catNewline ||
1183 (p.next_token().cat() == catSpace &&
1184 p.next_next_token().cat() == catNewline))
1188 // We need to do something with the options...
1189 if (!options.empty() && !detectEncoding)
1190 cerr << "Ignoring options '" << join(options, ",")
1191 << "' of package " << name << '.' << endl;
1193 // remove the whitespace
1198 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1201 Token t = p.get_token();
1202 if (t.cat() == catEscape &&
1203 is_known(t.cs(), known_if_commands))
1204 handle_if(p, in_lyx_preamble);
1206 if (!in_lyx_preamble)
1207 h_preamble << t.asInput();
1208 if (t.cat() == catEscape && t.cs() == "fi")
1215 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1217 // set the quote language
1218 // LyX only knows the following quotes languages:
1219 // english, swedish, german, polish, french and danish
1220 // (quotes for "japanese" and "chinese-traditional" are missing because
1221 // they wouldn't be useful: https://www.lyx.org/trac/ticket/6383)
1222 // conversion list taken from
1223 // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
1224 // (quotes for kazakh and interlingua are unknown)
1226 if (is_known(h_language, known_danish_quotes_languages))
1227 h_quotes_style = "danish";
1229 else if (is_known(h_language, known_french_quotes_languages))
1230 h_quotes_style = "french";
1232 else if (is_known(h_language, known_german_quotes_languages))
1233 h_quotes_style = "german";
1235 else if (is_known(h_language, known_polish_quotes_languages))
1236 h_quotes_style = "polish";
1238 else if (is_known(h_language, known_swedish_quotes_languages))
1239 h_quotes_style = "swedish";
1241 else if (is_known(h_language, known_english_quotes_languages))
1242 h_quotes_style = "english";
1244 if (contains(h_float_placement, "H"))
1245 registerAutomaticallyLoadedPackage("float");
1246 if (h_spacing != "single" && h_spacing != "default")
1247 registerAutomaticallyLoadedPackage("setspace");
1248 if (h_use_packages["amsmath"] == "2") {
1249 // amsbsy and amstext are already provided by amsmath
1250 registerAutomaticallyLoadedPackage("amsbsy");
1251 registerAutomaticallyLoadedPackage("amstext");
1254 // output the LyX file settings
1255 // Important: Keep the version formatting in sync with LyX and
1256 // lyx2lyx (bug 7951)
1257 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1258 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1259 << lyx_version_minor << '\n'
1260 << "\\lyxformat " << LYX_FORMAT << '\n'
1261 << "\\begin_document\n"
1262 << "\\begin_header\n"
1263 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1264 << "\\origin " << origin << "\n"
1265 << "\\textclass " << h_textclass << "\n";
1266 string const raw = subdoc ? empty_string() : h_preamble.str();
1268 os << "\\begin_preamble\n";
1269 for (string::size_type i = 0; i < raw.size(); ++i) {
1270 if (raw[i] == package_beg_sep) {
1271 // Here follows some package loading code that
1272 // must be skipped if the package is loaded
1274 string::size_type j = raw.find(package_mid_sep, i);
1275 if (j == string::npos)
1277 string::size_type k = raw.find(package_end_sep, j);
1278 if (k == string::npos)
1280 string const package = raw.substr(i + 1, j - i - 1);
1281 string const replacement = raw.substr(j + 1, k - j - 1);
1282 if (auto_packages.find(package) == auto_packages.end())
1288 os << "\n\\end_preamble\n";
1290 if (!h_options.empty())
1291 os << "\\options " << h_options << "\n";
1292 os << "\\use_default_options " << h_use_default_options << "\n";
1293 if (!used_modules.empty()) {
1294 os << "\\begin_modules\n";
1295 vector<string>::const_iterator const end = used_modules.end();
1296 vector<string>::const_iterator it = used_modules.begin();
1297 for (; it != end; ++it)
1299 os << "\\end_modules\n";
1301 if (!h_includeonlys.empty()) {
1302 os << "\\begin_includeonly\n";
1303 for (auto const & iofile : h_includeonlys)
1304 os << iofile << '\n';
1305 os << "\\end_includeonly\n";
1307 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1308 << "\\language " << h_language << "\n"
1309 << "\\language_package " << h_language_package << "\n"
1310 << "\\inputencoding " << h_inputencoding << "\n"
1311 << "\\fontencoding " << h_fontencoding << "\n"
1312 << "\\font_roman \"" << h_font_roman[0]
1313 << "\" \"" << h_font_roman[1] << "\"\n"
1314 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1315 << "\\font_typewriter \"" << h_font_typewriter[0]
1316 << "\" \"" << h_font_typewriter[1] << "\"\n"
1317 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1318 << "\\font_default_family " << h_font_default_family << "\n"
1319 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1320 << "\\font_sc " << h_font_sc << "\n"
1321 << "\\font_osf " << h_font_osf << "\n"
1322 << "\\font_sf_scale " << h_font_sf_scale[0]
1323 << ' ' << h_font_sf_scale[1] << '\n'
1324 << "\\font_tt_scale " << h_font_tt_scale[0]
1325 << ' ' << h_font_tt_scale[1] << '\n';
1326 if (!h_font_cjk.empty())
1327 os << "\\font_cjk " << h_font_cjk << '\n';
1328 os << "\\use_microtype " << h_use_microtype << '\n'
1329 << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
1330 << "\\graphics " << h_graphics << '\n'
1331 << "\\default_output_format " << h_default_output_format << "\n"
1332 << "\\output_sync " << h_output_sync << "\n";
1333 if (h_output_sync == "1")
1334 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1335 os << "\\bibtex_command " << h_bibtex_command << "\n"
1336 << "\\index_command " << h_index_command << "\n";
1337 if (!h_float_placement.empty())
1338 os << "\\float_placement " << h_float_placement << "\n";
1339 os << "\\paperfontsize " << h_paperfontsize << "\n"
1340 << "\\spacing " << h_spacing << "\n"
1341 << "\\use_hyperref " << h_use_hyperref << '\n';
1342 if (h_use_hyperref == "true") {
1343 if (!h_pdf_title.empty())
1344 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1345 if (!h_pdf_author.empty())
1346 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1347 if (!h_pdf_subject.empty())
1348 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1349 if (!h_pdf_keywords.empty())
1350 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1351 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1352 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1353 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1354 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1355 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1356 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1357 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1358 "\\pdf_backref " << h_pdf_backref << "\n"
1359 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1360 if (!h_pdf_pagemode.empty())
1361 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1362 if (!h_pdf_quoted_options.empty())
1363 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1365 os << "\\papersize " << h_papersize << "\n"
1366 << "\\use_geometry " << h_use_geometry << '\n';
1367 for (map<string, string>::const_iterator it = h_use_packages.begin();
1368 it != h_use_packages.end(); ++it)
1369 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1370 os << "\\cite_engine " << h_cite_engine << '\n'
1371 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1372 << "\\biblio_style " << h_biblio_style << "\n"
1373 << "\\use_bibtopic " << h_use_bibtopic << "\n";
1374 if (!h_biblio_options.empty())
1375 os << "\\biblio_options " << h_biblio_options << "\n";
1376 if (!h_biblatex_bibstyle.empty())
1377 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
1378 if (!h_biblatex_citestyle.empty())
1379 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
1380 if (!h_multibib.empty())
1381 os << "\\multibib " << h_multibib << "\n";
1382 os << "\\use_indices " << h_use_indices << "\n"
1383 << "\\paperorientation " << h_paperorientation << '\n'
1384 << "\\suppress_date " << h_suppress_date << '\n'
1385 << "\\justification " << h_justification << '\n'
1386 << "\\use_refstyle " << h_use_refstyle << '\n'
1387 << "\\use_minted " << h_use_minted << '\n';
1388 if (!h_fontcolor.empty())
1389 os << "\\fontcolor " << h_fontcolor << '\n';
1390 if (!h_notefontcolor.empty())
1391 os << "\\notefontcolor " << h_notefontcolor << '\n';
1392 if (!h_backgroundcolor.empty())
1393 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1394 if (!h_boxbgcolor.empty())
1395 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1396 if (index_number != 0)
1397 for (int i = 0; i < index_number; i++) {
1398 os << "\\index " << h_index[i] << '\n'
1399 << "\\shortcut " << h_shortcut[i] << '\n'
1400 << "\\color " << h_color << '\n'
1404 os << "\\index " << h_index[0] << '\n'
1405 << "\\shortcut " << h_shortcut[0] << '\n'
1406 << "\\color " << h_color << '\n'
1410 << "\\secnumdepth " << h_secnumdepth << "\n"
1411 << "\\tocdepth " << h_tocdepth << "\n"
1412 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1413 if (h_paragraph_separation == "skip")
1414 os << "\\defskip " << h_defskip << "\n";
1416 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1417 os << "\\is_math_indent " << h_is_mathindent << "\n";
1418 if (!h_mathindentation.empty())
1419 os << "\\math_indentation " << h_mathindentation << "\n";
1420 os << "\\math_numbering_side " << h_math_numbering_side << "\n";
1421 os << "\\quotes_style " << h_quotes_style << "\n"
1422 << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
1423 << "\\papercolumns " << h_papercolumns << "\n"
1424 << "\\papersides " << h_papersides << "\n"
1425 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1426 if (!h_listings_params.empty())
1427 os << "\\listings_params " << h_listings_params << "\n";
1428 os << "\\tracking_changes " << h_tracking_changes << "\n"
1429 << "\\output_changes " << h_output_changes << "\n"
1430 << "\\html_math_output " << h_html_math_output << "\n"
1431 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1432 << "\\html_be_strict " << h_html_be_strict << "\n"
1434 << "\\end_header\n\n"
1435 << "\\begin_body\n";
1440 void Preamble::parse(Parser & p, string const & forceclass,
1441 TeX2LyXDocClass & tc)
1443 // initialize fixed types
1444 special_columns_['D'] = 3;
1445 parse(p, forceclass, false, tc);
1449 void Preamble::parse(Parser & p, string const & forceclass,
1450 bool detectEncoding, TeX2LyXDocClass & tc)
1452 bool is_full_document = false;
1453 bool is_lyx_file = false;
1454 bool in_lyx_preamble = false;
1456 // determine whether this is a full document or a fragment for inclusion
1458 Token const & t = p.get_token();
1460 if (t.cat() == catEscape && t.cs() == "documentclass") {
1461 is_full_document = true;
1467 if (detectEncoding && !is_full_document)
1470 while (is_full_document && p.good()) {
1471 if (detectEncoding && h_inputencoding != "auto" &&
1472 h_inputencoding != "default")
1475 Token const & t = p.get_token();
1478 if (!detectEncoding)
1479 cerr << "t: " << t << '\n';
1485 if (!in_lyx_preamble &&
1486 (t.cat() == catLetter ||
1487 t.cat() == catSuper ||
1488 t.cat() == catSub ||
1489 t.cat() == catOther ||
1490 t.cat() == catMath ||
1491 t.cat() == catActive ||
1492 t.cat() == catBegin ||
1493 t.cat() == catEnd ||
1494 t.cat() == catAlign ||
1495 t.cat() == catParameter)) {
1496 h_preamble << t.cs();
1500 if (!in_lyx_preamble &&
1501 (t.cat() == catSpace || t.cat() == catNewline)) {
1502 h_preamble << t.asInput();
1506 if (t.cat() == catComment) {
1507 static regex const islyxfile("%% LyX .* created this file");
1508 static regex const usercommands("User specified LaTeX commands");
1510 string const comment = t.asInput();
1512 // magically switch encoding default if it looks like XeLaTeX
1513 static string const magicXeLaTeX =
1514 "% This document must be compiled with XeLaTeX ";
1515 if (comment.size() > magicXeLaTeX.size()
1516 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1517 && h_inputencoding == "auto") {
1518 if (!detectEncoding)
1519 cerr << "XeLaTeX comment found, switching to UTF8\n";
1520 h_inputencoding = "utf8";
1523 if (regex_search(comment, sub, islyxfile)) {
1525 in_lyx_preamble = true;
1526 } else if (is_lyx_file
1527 && regex_search(comment, sub, usercommands))
1528 in_lyx_preamble = false;
1529 else if (!in_lyx_preamble)
1530 h_preamble << t.asInput();
1534 if (t.cs() == "PassOptionsToPackage") {
1535 string const poptions = p.getArg('{', '}');
1536 string const package = p.verbatim_item();
1537 extra_package_options_.insert(make_pair(package, poptions));
1541 if (t.cs() == "pagestyle") {
1542 h_paperpagestyle = p.verbatim_item();
1546 if (t.cs() == "setdefaultlanguage") {
1548 // We don't yet care about non-language variant options
1549 // because LyX doesn't support this yet, see bug #8214
1551 string langopts = p.getOpt();
1552 // check if the option contains a variant, if yes, extract it
1553 string::size_type pos_var = langopts.find("variant");
1554 string::size_type i = langopts.find(',', pos_var);
1555 string::size_type k = langopts.find('=', pos_var);
1556 if (pos_var != string::npos){
1558 if (i == string::npos)
1559 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1561 variant = langopts.substr(k + 1, i - k - 1);
1562 h_language = variant;
1566 h_language = p.verbatim_item();
1567 //finally translate the poyglossia name to a LyX name
1568 h_language = polyglossia2lyx(h_language);
1572 if (t.cs() == "setotherlanguage") {
1573 // We don't yet care about the option because LyX doesn't
1574 // support this yet, see bug #8214
1575 p.hasOpt() ? p.getOpt() : string();
1580 if (t.cs() == "setmainfont") {
1581 // we don't care about the option
1582 p.hasOpt() ? p.getOpt() : string();
1583 h_font_roman[1] = p.getArg('{', '}');
1587 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1588 // LyX currently only supports the scale option
1591 string fontopts = p.getArg('[', ']');
1592 // check if the option contains a scaling, if yes, extract it
1593 string::size_type pos = fontopts.find("Scale");
1594 if (pos != string::npos) {
1595 string::size_type i = fontopts.find(',', pos);
1596 if (i == string::npos)
1597 scale_as_percentage(fontopts.substr(pos + 1), scale);
1599 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1602 if (t.cs() == "setsansfont") {
1604 h_font_sf_scale[1] = scale;
1605 h_font_sans[1] = p.getArg('{', '}');
1608 h_font_tt_scale[1] = scale;
1609 h_font_typewriter[1] = p.getArg('{', '}');
1614 else if (t.cs() == "date") {
1615 string argument = p.getArg('{', '}');
1616 if (argument.empty())
1617 h_suppress_date = "true";
1619 h_preamble << t.asInput() << '{' << argument << '}';
1622 if (t.cs() == "color") {
1623 string const space =
1624 (p.hasOpt() ? p.getOpt() : string());
1625 string argument = p.getArg('{', '}');
1626 // check the case that a standard color is used
1627 if (space.empty() && is_known(argument, known_basic_colors)) {
1628 h_fontcolor = rgbcolor2code(argument);
1629 registerAutomaticallyLoadedPackage("color");
1630 } else if (space.empty() && argument == "document_fontcolor")
1631 registerAutomaticallyLoadedPackage("color");
1632 // check the case that LyX's document_fontcolor is defined
1633 // but not used for \color
1635 h_preamble << t.asInput();
1637 h_preamble << space;
1638 h_preamble << '{' << argument << '}';
1639 // the color might already be set because \definecolor
1640 // is parsed before this
1646 if (t.cs() == "pagecolor") {
1647 string argument = p.getArg('{', '}');
1648 // check the case that a standard color is used
1649 if (is_known(argument, known_basic_colors)) {
1650 h_backgroundcolor = rgbcolor2code(argument);
1651 } else if (argument == "page_backgroundcolor")
1652 registerAutomaticallyLoadedPackage("color");
1653 // check the case that LyX's page_backgroundcolor is defined
1654 // but not used for \pagecolor
1656 h_preamble << t.asInput() << '{' << argument << '}';
1657 // the color might already be set because \definecolor
1658 // is parsed before this
1659 h_backgroundcolor = "";
1664 if (t.cs() == "makeatletter") {
1665 // LyX takes care of this
1666 p.setCatcode('@', catLetter);
1670 if (t.cs() == "makeatother") {
1671 // LyX takes care of this
1672 p.setCatcode('@', catOther);
1676 if (t.cs() == "makeindex") {
1677 // LyX will re-add this if a print index command is found
1682 if (t.cs() == "newindex") {
1683 string const indexname = p.getArg('[', ']');
1684 string const shortcut = p.verbatim_item();
1685 if (!indexname.empty())
1686 h_index[index_number] = indexname;
1688 h_index[index_number] = shortcut;
1689 h_shortcut[index_number] = shortcut;
1695 if (t.cs() == "addbibresource") {
1696 biblatex_bibliographies.push_back(removeExtension(p.getArg('{', '}')));
1700 if (t.cs() == "bibliography") {
1701 vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
1702 biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
1706 if (t.cs() == "RS@ifundefined") {
1707 string const name = p.verbatim_item();
1708 string const body1 = p.verbatim_item();
1709 string const body2 = p.verbatim_item();
1710 // only non-lyxspecific stuff
1711 if (in_lyx_preamble &&
1712 (name == "subsecref" || name == "thmref" || name == "lemref"))
1716 ss << '\\' << t.cs();
1717 ss << '{' << name << '}'
1718 << '{' << body1 << '}'
1719 << '{' << body2 << '}';
1720 h_preamble << ss.str();
1725 if (t.cs() == "AtBeginDocument") {
1726 string const name = p.verbatim_item();
1727 // only non-lyxspecific stuff
1728 if (in_lyx_preamble &&
1729 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
1730 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
1731 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
1732 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
1733 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
1734 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
1735 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
1736 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
1737 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
1738 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
1739 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
1740 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
1741 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
1742 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
1743 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
1747 ss << '\\' << t.cs();
1748 ss << '{' << name << '}';
1749 h_preamble << ss.str();
1754 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1755 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1756 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1757 || t.cs() == "DeclareRobustCommand"
1758 || t.cs() == "DeclareRobustCommandx"
1759 || t.cs() == "ProvideTextCommandDefault"
1760 || t.cs() == "DeclareMathAccent") {
1762 if (p.next_token().character() == '*') {
1766 string const name = p.verbatim_item();
1767 string const opt1 = p.getFullOpt();
1768 string const opt2 = p.getFullOpt();
1769 string const body = p.verbatim_item();
1770 // store the in_lyx_preamble setting
1771 bool const was_in_lyx_preamble = in_lyx_preamble;
1773 if (name == "\\rmdefault")
1774 if (is_known(body, known_roman_fonts)) {
1775 h_font_roman[0] = body;
1777 in_lyx_preamble = true;
1779 if (name == "\\sfdefault")
1780 if (is_known(body, known_sans_fonts)) {
1781 h_font_sans[0] = body;
1783 in_lyx_preamble = true;
1785 if (name == "\\ttdefault")
1786 if (is_known(body, known_typewriter_fonts)) {
1787 h_font_typewriter[0] = body;
1789 in_lyx_preamble = true;
1791 if (name == "\\familydefault") {
1792 string family = body;
1793 // remove leading "\"
1794 h_font_default_family = family.erase(0,1);
1796 in_lyx_preamble = true;
1799 // remove LyX-specific definitions that are re-added by LyX
1801 // \lyxline is an ancient command that is converted by tex2lyx into
1802 // a \rule therefore remove its preamble code
1803 if (name == "\\lyxdot" || name == "\\lyxarrow"
1804 || name == "\\lyxline" || name == "\\LyX") {
1806 in_lyx_preamble = true;
1809 // Add the command to the known commands
1810 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1812 // only non-lyxspecific stuff
1813 if (!in_lyx_preamble) {
1815 ss << '\\' << t.cs();
1818 ss << '{' << name << '}' << opt1 << opt2
1819 << '{' << body << "}";
1820 h_preamble << ss.str();
1822 ostream & out = in_preamble ? h_preamble : os;
1823 out << "\\" << t.cs() << "{" << name << "}"
1824 << opts << "{" << body << "}";
1827 // restore the in_lyx_preamble setting
1828 in_lyx_preamble = was_in_lyx_preamble;
1832 if (t.cs() == "documentclass") {
1833 vector<string>::iterator it;
1834 vector<string> opts = split_options(p.getArg('[', ']'));
1835 handle_opt(opts, known_fontsizes, h_paperfontsize);
1836 delete_opt(opts, known_fontsizes);
1837 // delete "pt" at the end
1838 string::size_type i = h_paperfontsize.find("pt");
1839 if (i != string::npos)
1840 h_paperfontsize.erase(i);
1841 // The documentclass options are always parsed before the options
1842 // of the babel call so that a language cannot overwrite the babel
1844 handle_opt(opts, known_languages, h_language);
1845 delete_opt(opts, known_languages);
1848 if ((it = find(opts.begin(), opts.end(), "fleqn"))
1850 h_is_mathindent = "1";
1853 // formula numbering side
1854 if ((it = find(opts.begin(), opts.end(), "leqno"))
1856 h_math_numbering_side = "left";
1859 else if ((it = find(opts.begin(), opts.end(), "reqno"))
1861 h_math_numbering_side = "right";
1865 // paper orientation
1866 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1867 h_paperorientation = "landscape";
1871 if ((it = find(opts.begin(), opts.end(), "oneside"))
1876 if ((it = find(opts.begin(), opts.end(), "twoside"))
1882 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1884 h_papercolumns = "1";
1887 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1889 h_papercolumns = "2";
1893 // some size options are known to any document classes, other sizes
1894 // are handled by the \geometry command of the geometry package
1895 handle_opt(opts, known_class_paper_sizes, h_papersize);
1896 delete_opt(opts, known_class_paper_sizes);
1897 // the remaining options
1898 h_options = join(opts, ",");
1899 // FIXME This does not work for classes that have a
1900 // different name in LyX than in LaTeX
1901 h_textclass = p.getArg('{', '}');
1906 if (t.cs() == "usepackage") {
1907 string const options = p.getArg('[', ']');
1908 string const name = p.getArg('{', '}');
1909 vector<string> vecnames;
1910 split(name, vecnames, ',');
1911 vector<string>::const_iterator it = vecnames.begin();
1912 vector<string>::const_iterator end = vecnames.end();
1913 for (; it != end; ++it)
1914 handle_package(p, trimSpaceAndEol(*it), options,
1915 in_lyx_preamble, detectEncoding);
1919 if (t.cs() == "inputencoding") {
1920 string const encoding = p.getArg('{','}');
1921 Encoding const * const enc = encodings.fromLaTeXName(
1922 encoding, Encoding::inputenc, true);
1924 if (!detectEncoding)
1925 cerr << "Unknown encoding " << encoding
1926 << ". Ignoring." << std::endl;
1929 h_inputencoding = enc->name();
1930 p.setEncoding(enc->iconvName());
1935 if (t.cs() == "newenvironment") {
1936 string const name = p.getArg('{', '}');
1937 string const opt1 = p.getFullOpt();
1938 string const opt2 = p.getFullOpt();
1939 string const beg = p.verbatim_item();
1940 string const end = p.verbatim_item();
1941 if (!in_lyx_preamble) {
1942 h_preamble << "\\newenvironment{" << name
1943 << '}' << opt1 << opt2 << '{'
1944 << beg << "}{" << end << '}';
1946 add_known_environment(name, opt1, !opt2.empty(),
1947 from_utf8(beg), from_utf8(end));
1951 if (t.cs() == "newtheorem") {
1953 if (p.next_token().character() == '*') {
1957 string const name = p.getArg('{', '}');
1958 string const opt1 = p.getFullOpt();
1959 string const opt2 = p.getFullOpt();
1960 string const body = p.verbatim_item();
1961 string const opt3 = p.getFullOpt();
1962 string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
1964 string const complete = cmd + "{" + name + '}' +
1965 opt1 + opt2 + '{' + body + '}' + opt3;
1967 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
1969 if (!in_lyx_preamble)
1970 h_preamble << complete;
1974 if (t.cs() == "def") {
1975 string name = p.get_token().cs();
1976 // In fact, name may be more than the name:
1977 // In the test case of bug 8116
1978 // name == "csname SF@gobble@opt \endcsname".
1979 // Therefore, we need to use asInput() instead of cs().
1980 while (p.next_token().cat() != catBegin)
1981 name += p.get_token().asInput();
1982 if (!in_lyx_preamble)
1983 h_preamble << "\\def\\" << name << '{'
1984 << p.verbatim_item() << "}";
1988 if (t.cs() == "newcolumntype") {
1989 string const name = p.getArg('{', '}');
1990 trimSpaceAndEol(name);
1992 string opts = p.getOpt();
1993 if (!opts.empty()) {
1994 istringstream is(string(opts, 1));
1997 special_columns_[name[0]] = nargs;
1998 h_preamble << "\\newcolumntype{" << name << "}";
2000 h_preamble << "[" << nargs << "]";
2001 h_preamble << "{" << p.verbatim_item() << "}";
2005 if (t.cs() == "setcounter") {
2006 string const name = p.getArg('{', '}');
2007 string const content = p.getArg('{', '}');
2008 if (name == "secnumdepth")
2009 h_secnumdepth = content;
2010 else if (name == "tocdepth")
2011 h_tocdepth = content;
2013 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
2017 if (t.cs() == "setlength") {
2018 string const name = p.verbatim_item();
2019 string const content = p.verbatim_item();
2020 // the paragraphs are only not indented when \parindent is set to zero
2021 if (name == "\\parindent" && content != "") {
2022 if (content[0] == '0')
2023 h_paragraph_separation = "skip";
2025 h_paragraph_indentation = translate_len(content);
2026 } else if (name == "\\parskip") {
2027 if (content == "\\smallskipamount")
2028 h_defskip = "smallskip";
2029 else if (content == "\\medskipamount")
2030 h_defskip = "medskip";
2031 else if (content == "\\bigskipamount")
2032 h_defskip = "bigskip";
2034 h_defskip = translate_len(content);
2035 } else if (name == "\\mathindent") {
2036 h_mathindentation = translate_len(content);
2038 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2042 if (t.cs() == "onehalfspacing") {
2043 h_spacing = "onehalf";
2047 if (t.cs() == "doublespacing") {
2048 h_spacing = "double";
2052 if (t.cs() == "setstretch") {
2053 h_spacing = "other " + p.verbatim_item();
2057 if (t.cs() == "synctex") {
2058 // the scheme is \synctex=value
2059 // where value can only be "1" or "-1"
2060 h_output_sync = "1";
2061 // there can be any character behind the value (e.g. a linebreak or a '\'
2062 // therefore we extract it char by char
2064 string value = p.get_token().asInput();
2066 value += p.get_token().asInput();
2067 h_output_sync_macro = "\\synctex=" + value;
2071 if (t.cs() == "begin") {
2072 string const name = p.getArg('{', '}');
2073 if (name == "document")
2075 h_preamble << "\\begin{" << name << "}";
2079 if (t.cs() == "geometry") {
2080 vector<string> opts = split_options(p.getArg('{', '}'));
2081 handle_geometry(opts);
2085 if (t.cs() == "definecolor") {
2086 string const color = p.getArg('{', '}');
2087 string const space = p.getArg('{', '}');
2088 string const value = p.getArg('{', '}');
2089 if (color == "document_fontcolor" && space == "rgb") {
2090 RGBColor c(RGBColorFromLaTeX(value));
2091 h_fontcolor = X11hexname(c);
2092 } else if (color == "note_fontcolor" && space == "rgb") {
2093 RGBColor c(RGBColorFromLaTeX(value));
2094 h_notefontcolor = X11hexname(c);
2095 } else if (color == "page_backgroundcolor" && space == "rgb") {
2096 RGBColor c(RGBColorFromLaTeX(value));
2097 h_backgroundcolor = X11hexname(c);
2098 } else if (color == "shadecolor" && space == "rgb") {
2099 RGBColor c(RGBColorFromLaTeX(value));
2100 h_boxbgcolor = X11hexname(c);
2102 h_preamble << "\\definecolor{" << color
2103 << "}{" << space << "}{" << value
2109 if (t.cs() == "bibliographystyle") {
2110 h_biblio_style = p.verbatim_item();
2114 if (t.cs() == "jurabibsetup") {
2115 // FIXME p.getArg('{', '}') is most probably wrong (it
2116 // does not handle nested braces).
2117 // Use p.verbatim_item() instead.
2118 vector<string> jurabibsetup =
2119 split_options(p.getArg('{', '}'));
2120 // add jurabibsetup to the jurabib package options
2121 add_package("jurabib", jurabibsetup);
2122 if (!jurabibsetup.empty()) {
2123 h_preamble << "\\jurabibsetup{"
2124 << join(jurabibsetup, ",") << '}';
2129 if (t.cs() == "hypersetup") {
2130 vector<string> hypersetup =
2131 split_options(p.verbatim_item());
2132 // add hypersetup to the hyperref package options
2133 handle_hyperref(hypersetup);
2134 if (!hypersetup.empty()) {
2135 h_preamble << "\\hypersetup{"
2136 << join(hypersetup, ",") << '}';
2141 if (t.cs() == "includeonly") {
2142 vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
2143 for (auto & iofile : includeonlys) {
2144 string filename(normalize_filename(iofile));
2145 string const path = getMasterFilePath(true);
2146 // We want to preserve relative/absolute filenames,
2147 // therefore path is only used for testing
2148 if (!makeAbsPath(filename, path).exists()) {
2149 // The file extension is probably missing.
2150 // Now try to find it out.
2151 string const tex_name =
2152 find_file(filename, path,
2153 known_tex_extensions);
2154 if (!tex_name.empty())
2155 filename = tex_name;
2158 if (makeAbsPath(filename, path).exists())
2159 fix_child_filename(filename);
2161 cerr << "Warning: Could not find included file '"
2162 << filename << "'." << endl;
2163 outname = changeExtension(filename, "lyx");
2164 h_includeonlys.push_back(outname);
2169 if (is_known(t.cs(), known_if_3arg_commands)) {
2170 // prevent misparsing of \usepackage if it is used
2171 // as an argument (see e.g. our own output of
2172 // \@ifundefined above)
2173 string const arg1 = p.verbatim_item();
2174 string const arg2 = p.verbatim_item();
2175 string const arg3 = p.verbatim_item();
2176 // test case \@ifundefined{date}{}{\date{}}
2177 if (t.cs() == "@ifundefined" && arg1 == "date" &&
2178 arg2.empty() && arg3 == "\\date{}") {
2179 h_suppress_date = "true";
2180 // older tex2lyx versions did output
2181 // \@ifundefined{definecolor}{\usepackage{color}}{}
2182 } else if (t.cs() == "@ifundefined" &&
2183 arg1 == "definecolor" &&
2184 arg2 == "\\usepackage{color}" &&
2186 if (!in_lyx_preamble)
2187 h_preamble << package_beg_sep
2190 << "\\@ifundefined{definecolor}{color}{}"
2193 //\@ifundefined{showcaptionsetup}{}{%
2194 // \PassOptionsToPackage{caption=false}{subfig}}
2195 // that LyX uses for subfloats
2196 } else if (t.cs() == "@ifundefined" &&
2197 arg1 == "showcaptionsetup" && arg2.empty()
2198 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
2200 } else if (!in_lyx_preamble) {
2201 h_preamble << t.asInput()
2202 << '{' << arg1 << '}'
2203 << '{' << arg2 << '}'
2204 << '{' << arg3 << '}';
2209 if (is_known(t.cs(), known_if_commands)) {
2210 // must not parse anything in conditional code, since
2211 // LyX would output the parsed contents unconditionally
2212 if (!in_lyx_preamble)
2213 h_preamble << t.asInput();
2214 handle_if(p, in_lyx_preamble);
2218 if (!t.cs().empty() && !in_lyx_preamble) {
2219 h_preamble << '\\' << t.cs();
2224 // remove the whitespace
2227 // Force textclass if the user wanted it
2228 if (!forceclass.empty())
2229 h_textclass = forceclass;
2230 tc.setName(h_textclass);
2231 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2232 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2235 if (h_papersides.empty()) {
2238 h_papersides = ss.str();
2241 // If the CJK package is used we cannot set the document language from
2242 // the babel options. Instead, we guess which language is used most
2243 // and set this one.
2244 default_language = h_language;
2245 if (is_full_document &&
2246 (auto_packages.find("CJK") != auto_packages.end() ||
2247 auto_packages.find("CJKutf8") != auto_packages.end())) {
2249 h_language = guessLanguage(p, default_language);
2251 if (explicit_babel && h_language != default_language) {
2252 // We set the document language to a CJK language,
2253 // but babel is explicitly called in the user preamble
2254 // without options. LyX will not add the default
2255 // language to the document options if it is either
2256 // english, or no text is set as default language.
2257 // Therefore we need to add a language option explicitly.
2258 // FIXME: It would be better to remove all babel calls
2259 // from the user preamble, but this is difficult
2260 // without re-introducing bug 7861.
2261 if (h_options.empty())
2262 h_options = lyx2babel(default_language);
2264 h_options += ',' + lyx2babel(default_language);
2270 string Preamble::parseEncoding(Parser & p, string const & forceclass)
2272 TeX2LyXDocClass dummy;
2273 parse(p, forceclass, true, dummy);
2274 if (h_inputencoding != "auto" && h_inputencoding != "default")
2275 return h_inputencoding;
2280 string babel2lyx(string const & language)
2282 char const * const * where = is_known(language, known_languages);
2284 return known_coded_languages[where - known_languages];
2289 string lyx2babel(string const & language)
2291 char const * const * where = is_known(language, known_coded_languages);
2293 return known_languages[where - known_coded_languages];
2298 string Preamble::polyglossia2lyx(string const & language)
2300 char const * const * where = is_known(language, polyglossia_languages);
2302 return coded_polyglossia_languages[where - polyglossia_languages];
2307 string rgbcolor2code(string const & name)
2309 char const * const * where = is_known(name, known_basic_colors);
2311 // "red", "green" etc
2312 return known_basic_color_codes[where - known_basic_colors];
2314 // "255,0,0", "0,255,0" etc
2315 RGBColor c(RGBColorFromLaTeX(name));
2316 return X11hexname(c);