3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
9 * Full author contact details are available in file CREDITS.
20 #include "LayoutFile.h"
23 #include "TextClass.h"
26 #include "support/convert.h"
27 #include "support/FileName.h"
28 #include "support/filetools.h"
29 #include "support/lstrings.h"
31 #include "support/regex.h"
37 using namespace lyx::support;
46 // CJK languages are handled in text.cpp, polyglossia languages are listed
49 * known babel language names (including synonyms)
50 * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
51 * please keep this in sync with known_coded_languages line by line!
53 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
54 "american", "arabic", "arabtex", "australian", "austrian", "bahasa", "bahasai",
55 "bahasam", "basque", "belarusian", "bosnian", "brazil", "brazilian", "breton", "british",
56 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
57 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "francais",
58 "french", "frenchb", "frenchle", "frenchpro", "friulan", "galician", "german", "germanb",
59 "georgian", "greek", "hebrew", "hungarian", "icelandic", "indon", "indonesian",
60 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
61 "latvian", "lithuanian", "lowersorbian", "lsorbian", "macedonian", "magyar", "malay", "meyalu",
62 "mongolian", "naustrian", "newzealand", "ngerman", "ngermanb", "norsk", "nswissgerman",
63 "nynorsk", "piedmontese", "polutonikogreek", "polish", "portuges", "portuguese",
64 "romanian", "romansh", "russian", "russianb", "samin", "scottish", "serbian", "serbian-latin",
65 "slovak", "slovene", "spanish", "swedish", "swissgerman", "thai", "turkish", "turkmen",
66 "ukraineb", "ukrainian", "uppersorbian", "UKenglish", "USenglish", "usorbian",
71 * the same as known_languages with .lyx names
72 * please keep this in sync with known_languages line by line!
74 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
75 "american", "arabic_arabi", "arabic_arabtex", "australian", "austrian", "bahasa", "bahasa",
76 "bahasam", "basque", "belarusian", "bosnian", "brazilian", "brazilian", "breton", "british",
77 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
78 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "french",
79 "french", "french", "french", "french", "friulan", "galician", "german", "german",
80 "georgian", "greek", "hebrew", "magyar", "icelandic", "bahasa", "bahasa",
81 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
82 "latvian", "lithuanian", "lowersorbian", "lowersorbian", "macedonian", "magyar", "bahasam", "bahasam",
83 "mongolian", "naustrian", "newzealand", "ngerman", "ngerman", "norsk", "german-ch",
84 "nynorsk", "piedmontese", "polutonikogreek", "polish", "portuguese", "portuguese",
85 "romanian", "romansh", "russian", "russian", "samin", "scottish", "serbian", "serbian-latin",
86 "slovak", "slovene", "spanish", "swedish", "german-ch-old", "thai", "turkish", "turkmen",
87 "ukrainian", "ukrainian", "uppersorbian", "english", "english", "uppersorbian",
88 "vietnamese", "welsh",
91 /// languages with danish quotes (.lyx names)
92 const char * const known_danish_quotes_languages[] = {"danish", 0};
94 /// languages with english quotes (.lyx names)
95 const char * const known_english_quotes_languages[] = {"american", "australian",
96 "bahasa", "bahasam", "brazilian", "canadian", "chinese-simplified", "english",
97 "esperanto", "hebrew", "irish", "korean", "newzealand", "portuguese", "scottish",
100 /// languages with french quotes (.lyx names)
101 const char * const known_french_quotes_languages[] = {"albanian",
102 "arabic_arabi", "arabic_arabtex", "asturian", "basque", "canadien", "catalan",
103 "french", "friulan", "galician", "greek", "italian", "norsk", "nynorsk",
104 "piedmontese", "polutonikogreek", "russian", "spanish", "spanish-mexico",
105 "turkish", "turkmen", "ukrainian", "vietnamese", 0};
107 /// languages with german quotes (.lyx names)
108 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
109 "czech", "german", "georgian", "icelandic", "lithuanian", "lowersorbian", "macedonian",
110 "naustrian", "ngerman", "romansh", "serbian", "serbian-latin", "slovak", "slovene",
113 /// languages with polish quotes (.lyx names)
114 const char * const known_polish_quotes_languages[] = {"afrikaans", "bosnian", "croatian",
115 "dutch", "estonian", "magyar", "polish", "romanian", 0};
117 /// languages with swedish quotes (.lyx names)
118 const char * const known_swedish_quotes_languages[] = {"finnish",
121 /// known language packages from the times before babel
122 const char * const known_old_language_packages[] = {"french", "frenchle",
123 "frenchpro", "german", "ngerman", "pmfrench", 0};
125 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
127 const char * const known_roman_fonts[] = { "ae", "beraserif", "bookman",
128 "ccfonts", "chancery", "charter", "cmr", "cochineal", "crimson", "fourier",
129 "garamondx", "libertine", "libertine-type1", "lmodern", "mathdesign", "mathpazo",
130 "mathptmx", "newcent", "NotoSerif-TLF", "tgbonum", "tgchorus", "tgpagella", "tgschola",
131 "tgtermes", "utopia", 0};
133 const char * const known_sans_fonts[] = { "avant", "berasans", "biolinum-type1",
134 "cmbr", "cmss", "helvet", "iwona", "iwonac", "iwonal", "iwonalc", "kurier",
135 "kurierc", "kurierl", "kurierlc", "lmss", "NotoSans-TLF", "tgadventor", "tgheros", 0};
137 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
138 "courier", "lmtt", "luximono", "fourier", "libertineMono-type1", "lmodern",
139 "mathpazo", "mathptmx", "newcent", "NotoMono-TLF", "tgcursor", "txtt", 0};
141 const char * const known_math_fonts[] = { "eulervm", "newtxmath", 0};
143 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
144 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
145 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
146 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
147 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
149 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
150 "executivepaper", "legalpaper", "letterpaper", 0};
152 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
153 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
155 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
156 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
159 /// commands that can start an \if...\else...\endif sequence
160 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
161 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
162 "ifsidecap", "ifupgreek", 0};
164 const char * const known_basic_colors[] = {"black", "blue", "brown", "cyan",
165 "darkgray", "gray", "green", "lightgray", "lime", "magenta", "orange", "olive",
166 "pink", "purple", "red", "teal", "violet", "white", "yellow", 0};
168 const char * const known_basic_color_codes[] = {"#000000", "#0000ff", "#964B00", "#00ffff",
169 "#a9a9a9", "#808080", "#00ff00", "#d3d3d3", "#bfff00", "#ff00ff", "#ff7f00", "#808000",
170 "#ffc0cb", "#800080", "#ff0000", "#008080", "#8f00ff", "#ffffff", "#ffff00", 0};
172 /// conditional commands with three arguments like \@ifundefined{}{}{}
173 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
176 /// packages that work only in xetex
177 /// polyglossia is handled separately
178 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
179 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
180 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
182 /// packages that are automatically skipped if loaded by LyX
183 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
184 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color",
185 "float", "fontspec", "framed", "graphicx", "hhline", "ifthen", "longtable",
186 "makeidx", "minted", "multirow", "nomencl", "pdfpages", "prettyref", "refstyle",
187 "rotating", "rotfloat", "splitidx", "setspace", "subscript", "textcomp", "tipa",
188 "tipx", "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xcolor",
191 // codes used to remove packages that are loaded automatically by LyX.
192 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
193 const char package_beg_sep = '\001';
194 const char package_mid_sep = '\002';
195 const char package_end_sep = '\003';
198 // returns true if at least one of the options in what has been found
199 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
205 // the last language option is the document language (for babel and LyX)
206 // the last size option is the document font size
207 vector<string>::iterator it;
208 vector<string>::iterator position = opts.begin();
209 for (; *what; ++what) {
210 it = find(opts.begin(), opts.end(), *what);
211 if (it != opts.end()) {
212 if (it >= position) {
223 void delete_opt(vector<string> & opts, char const * const * what)
228 // remove found options from the list
229 // do this after handle_opt to avoid potential memory leaks
230 vector<string>::iterator it;
231 for (; *what; ++what) {
232 it = find(opts.begin(), opts.end(), *what);
233 if (it != opts.end())
240 * Split a package options string (keyval format) into a vector.
242 * authorformat=smallcaps,
244 * titleformat=colonsep,
245 * bibformat={tabular,ibidem,numbered}
247 vector<string> split_options(string const & input)
249 vector<string> options;
253 Token const & t = p.get_token();
254 if (t.asInput() == ",") {
255 options.push_back(trimSpaceAndEol(option));
257 } else if (t.asInput() == "=") {
260 if (p.next_token().asInput() == "{")
261 option += '{' + p.getArg('{', '}') + '}';
262 } else if (t.cat() != catSpace && t.cat() != catComment)
263 option += t.asInput();
267 options.push_back(trimSpaceAndEol(option));
274 * Retrieve a keyval option "name={value with=sign}" named \p name from
275 * \p options and return the value.
276 * The found option is also removed from \p options.
278 string process_keyval_opt(vector<string> & options, string name)
280 for (size_t i = 0; i < options.size(); ++i) {
281 vector<string> option;
282 split(options[i], option, '=');
283 if (option.size() < 2)
285 if (option[0] == name) {
286 options.erase(options.begin() + i);
287 option.erase(option.begin());
288 return join(option, "=");
294 } // anonymous namespace
298 * known polyglossia language names (including variants)
299 * FIXME: support spelling=old for german variants (german vs. ngerman LyX names etc)
301 const char * const Preamble::polyglossia_languages[] = {
302 "albanian", "american", "amharic", "ancient", "arabic", "armenian", "asturian", "australian",
303 "bahasai", "bahasam", "basque", "bengali", "brazil", "brazilian", "breton", "british", "bulgarian",
304 "catalan", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
305 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
306 "galician", "greek", "monotonic", "hebrew", "hindi",
307 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer",
308 "lao", "latin", "latvian", "lithuanian", "lsorbian", "magyar", "malayalam", "marathi",
309 "austrian", "newzealand", "german", "norsk", "nynorsk", "occitan",
310 "piedmontese", "polish", "polytonic", "portuges", "romanian", "romansh", "russian",
311 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovenian", "spanish", "swedish", "syriac",
312 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
313 "ukrainian", "urdu", "usorbian", "vietnamese", "welsh", 0};
314 // not yet supported by LyX: "korean", "nko"
317 * the same as polyglossia_languages with .lyx names
318 * please keep this in sync with polyglossia_languages line by line!
320 const char * const Preamble::coded_polyglossia_languages[] = {
321 "albanian", "american", "amharic", "ancientgreek", "arabic_arabi", "armenian", "asturian", "australian",
322 "bahasa", "bahasam", "basque", "bengali", "brazilian", "brazilian", "breton", "british", "bulgarian",
323 "catalan", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
324 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
325 "galician", "greek", "greek", "hebrew", "hindi",
326 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer",
327 "lao", "latin", "latvian", "lithuanian", "lowersorbian", "magyar", "malayalam", "marathi",
328 "naustrian","newzealand", "ngerman", "norsk", "nynorsk", "occitan",
329 "piedmontese", "polish", "polutonikogreek", "portuges", "romanian", "romansh", "russian",
330 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovene", "spanish", "swedish", "syriac",
331 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
332 "ukrainian", "urdu", "uppersorbian", "vietnamese", "welsh", 0};
333 // not yet supported by LyX: "korean-polyglossia", "nko"
336 bool Preamble::usePolyglossia() const
338 return h_use_non_tex_fonts && h_language_package == "default";
342 bool Preamble::indentParagraphs() const
344 return h_paragraph_separation == "indent";
348 bool Preamble::isPackageUsed(string const & package) const
350 return used_packages.find(package) != used_packages.end();
354 vector<string> Preamble::getPackageOptions(string const & package) const
356 map<string, vector<string> >::const_iterator it = used_packages.find(package);
357 if (it != used_packages.end())
359 return vector<string>();
363 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
365 auto_packages.insert(package);
369 void Preamble::addModule(string const & module)
371 used_modules.push_back(module);
375 void Preamble::suppressDate(bool suppress)
378 h_suppress_date = "true";
380 h_suppress_date = "false";
384 void Preamble::registerAuthor(std::string const & name)
386 Author author(from_utf8(name), empty_docstring());
387 author.setUsed(true);
388 authors_.record(author);
389 h_tracking_changes = "true";
390 h_output_changes = "true";
394 Author const & Preamble::getAuthor(std::string const & name) const
396 Author author(from_utf8(name), empty_docstring());
397 for (AuthorList::Authors::const_iterator it = authors_.begin();
398 it != authors_.end(); ++it)
401 static Author const dummy;
406 int Preamble::getSpecialTableColumnArguments(char c) const
408 map<char, int>::const_iterator it = special_columns_.find(c);
409 if (it == special_columns_.end())
415 void Preamble::add_package(string const & name, vector<string> & options)
417 // every package inherits the global options
418 if (used_packages.find(name) == used_packages.end())
419 used_packages[name] = split_options(h_options);
421 // Insert options passed via PassOptionsToPackage
422 for (auto const & p : extra_package_options_) {
423 if (p.first == name) {
424 vector<string> eo = getVectorFromString(p.second);
425 for (auto const & eoi : eo)
426 options.push_back(eoi);
430 vector<string> & v = used_packages[name];
431 v.insert(v.end(), options.begin(), options.end());
432 if (name == "jurabib") {
433 // Don't output the order argument (see the cite command
434 // handling code in text.cpp).
435 vector<string>::iterator end =
436 remove(options.begin(), options.end(), "natbiborder");
437 end = remove(options.begin(), end, "jurabiborder");
438 options.erase(end, options.end());
445 // Given is a string like "scaled=0.9" or "Scale=0.9", return 0.9 * 100
446 bool scale_as_percentage(string const & scale, string & percentage)
448 string::size_type pos = scale.find('=');
449 if (pos != string::npos) {
450 string value = scale.substr(pos + 1);
451 if (isStrDbl(value)) {
452 percentage = convert<string>(
453 static_cast<int>(100 * convert<double>(value)));
461 string remove_braces(string const & value)
465 if (value[0] == '{' && value[value.length()-1] == '}')
466 return value.substr(1, value.length()-2);
470 } // anonymous namespace
473 Preamble::Preamble() : one_language(true), explicit_babel(false),
474 title_layout_found(false), index_number(0), h_font_cjk_set(false),
475 h_use_microtype("false")
479 h_biblio_style = "plain";
480 h_bibtex_command = "default";
481 h_cite_engine = "basic";
482 h_cite_engine_type = "default";
484 h_defskip = "medskip";
485 h_dynamic_quotes = false;
488 h_fontencoding = "default";
489 h_font_roman[0] = "default";
490 h_font_roman[1] = "default";
491 h_font_sans[0] = "default";
492 h_font_sans[1] = "default";
493 h_font_typewriter[0] = "default";
494 h_font_typewriter[1] = "default";
495 h_font_math[0] = "auto";
496 h_font_math[1] = "auto";
497 h_font_default_family = "default";
498 h_use_non_tex_fonts = false;
500 h_font_osf = "false";
501 h_font_sf_scale[0] = "100";
502 h_font_sf_scale[1] = "100";
503 h_font_tt_scale[0] = "100";
504 h_font_tt_scale[1] = "100";
506 h_is_mathindent = "0";
507 h_math_numbering_side = "default";
508 h_graphics = "default";
509 h_default_output_format = "default";
510 h_html_be_strict = "false";
511 h_html_css_as_file = "0";
512 h_html_math_output = "0";
513 h_index[0] = "Index";
514 h_index_command = "default";
515 h_inputencoding = "auto";
516 h_justification = "true";
517 h_language = "english";
518 h_language_package = "none";
520 h_maintain_unincluded_children = "false";
524 h_output_changes = "false";
526 //h_output_sync_macro
527 h_papercolumns = "1";
528 h_paperfontsize = "default";
529 h_paperorientation = "portrait";
530 h_paperpagestyle = "default";
532 h_papersize = "default";
533 h_paragraph_indentation = "default";
534 h_paragraph_separation = "indent";
539 h_pdf_bookmarks = "0";
540 h_pdf_bookmarksnumbered = "0";
541 h_pdf_bookmarksopen = "0";
542 h_pdf_bookmarksopenlevel = "1";
543 h_pdf_breaklinks = "0";
544 h_pdf_pdfborder = "0";
545 h_pdf_colorlinks = "0";
546 h_pdf_backref = "section";
547 h_pdf_pdfusetitle = "0";
549 //h_pdf_quoted_options;
550 h_quotes_style = "english";
552 h_shortcut[0] = "idx";
553 h_spacing = "single";
554 h_save_transient_properties = "true";
555 h_suppress_date = "false";
556 h_textclass = "article";
558 h_tracking_changes = "false";
559 h_use_bibtopic = "false";
560 h_use_dash_ligatures = "true";
561 h_use_indices = "false";
562 h_use_geometry = "false";
563 h_use_default_options = "false";
564 h_use_hyperref = "false";
565 h_use_microtype = "false";
566 h_use_refstyle = false;
567 h_use_minted = false;
568 h_use_packages["amsmath"] = "1";
569 h_use_packages["amssymb"] = "0";
570 h_use_packages["cancel"] = "0";
571 h_use_packages["esint"] = "1";
572 h_use_packages["mhchem"] = "0";
573 h_use_packages["mathdots"] = "0";
574 h_use_packages["mathtools"] = "0";
575 h_use_packages["stackrel"] = "0";
576 h_use_packages["stmaryrd"] = "0";
577 h_use_packages["undertilde"] = "0";
581 void Preamble::handle_hyperref(vector<string> & options)
583 // FIXME swallow inputencoding changes that might surround the
584 // hyperref setup if it was written by LyX
585 h_use_hyperref = "true";
586 // swallow "unicode=true", since LyX does always write that
587 vector<string>::iterator it =
588 find(options.begin(), options.end(), "unicode=true");
589 if (it != options.end())
591 it = find(options.begin(), options.end(), "pdfusetitle");
592 if (it != options.end()) {
593 h_pdf_pdfusetitle = "1";
596 string bookmarks = process_keyval_opt(options, "bookmarks");
597 if (bookmarks == "true")
598 h_pdf_bookmarks = "1";
599 else if (bookmarks == "false")
600 h_pdf_bookmarks = "0";
601 if (h_pdf_bookmarks == "1") {
602 string bookmarksnumbered =
603 process_keyval_opt(options, "bookmarksnumbered");
604 if (bookmarksnumbered == "true")
605 h_pdf_bookmarksnumbered = "1";
606 else if (bookmarksnumbered == "false")
607 h_pdf_bookmarksnumbered = "0";
608 string bookmarksopen =
609 process_keyval_opt(options, "bookmarksopen");
610 if (bookmarksopen == "true")
611 h_pdf_bookmarksopen = "1";
612 else if (bookmarksopen == "false")
613 h_pdf_bookmarksopen = "0";
614 if (h_pdf_bookmarksopen == "1") {
615 string bookmarksopenlevel =
616 process_keyval_opt(options, "bookmarksopenlevel");
617 if (!bookmarksopenlevel.empty())
618 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
621 string breaklinks = process_keyval_opt(options, "breaklinks");
622 if (breaklinks == "true")
623 h_pdf_breaklinks = "1";
624 else if (breaklinks == "false")
625 h_pdf_breaklinks = "0";
626 string pdfborder = process_keyval_opt(options, "pdfborder");
627 if (pdfborder == "{0 0 0}")
628 h_pdf_pdfborder = "1";
629 else if (pdfborder == "{0 0 1}")
630 h_pdf_pdfborder = "0";
631 string backref = process_keyval_opt(options, "backref");
632 if (!backref.empty())
633 h_pdf_backref = backref;
634 string colorlinks = process_keyval_opt(options, "colorlinks");
635 if (colorlinks == "true")
636 h_pdf_colorlinks = "1";
637 else if (colorlinks == "false")
638 h_pdf_colorlinks = "0";
639 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
640 if (!pdfpagemode.empty())
641 h_pdf_pagemode = pdfpagemode;
642 string pdftitle = process_keyval_opt(options, "pdftitle");
643 if (!pdftitle.empty()) {
644 h_pdf_title = remove_braces(pdftitle);
646 string pdfauthor = process_keyval_opt(options, "pdfauthor");
647 if (!pdfauthor.empty()) {
648 h_pdf_author = remove_braces(pdfauthor);
650 string pdfsubject = process_keyval_opt(options, "pdfsubject");
651 if (!pdfsubject.empty())
652 h_pdf_subject = remove_braces(pdfsubject);
653 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
654 if (!pdfkeywords.empty())
655 h_pdf_keywords = remove_braces(pdfkeywords);
656 if (!options.empty()) {
657 if (!h_pdf_quoted_options.empty())
658 h_pdf_quoted_options += ',';
659 h_pdf_quoted_options += join(options, ",");
665 void Preamble::handle_geometry(vector<string> & options)
667 h_use_geometry = "true";
668 vector<string>::iterator it;
670 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
671 h_paperorientation = "landscape";
675 // keyval version: "paper=letter"
676 string paper = process_keyval_opt(options, "paper");
678 h_papersize = paper + "paper";
679 // alternative version: "letterpaper"
680 handle_opt(options, known_paper_sizes, h_papersize);
681 delete_opt(options, known_paper_sizes);
683 char const * const * margin = known_paper_margins;
684 for (; *margin; ++margin) {
685 string value = process_keyval_opt(options, *margin);
686 if (!value.empty()) {
687 int k = margin - known_paper_margins;
688 string name = known_coded_paper_margins[k];
689 h_margins += '\\' + name + ' ' + value + '\n';
695 void Preamble::handle_package(Parser &p, string const & name,
696 string const & opts, bool in_lyx_preamble,
699 vector<string> options = split_options(opts);
700 add_package(name, options);
702 if (is_known(name, known_xetex_packages)) {
704 h_use_non_tex_fonts = true;
705 registerAutomaticallyLoadedPackage("fontspec");
706 if (h_inputencoding == "auto")
707 p.setEncoding("UTF-8");
711 if (is_known(name, known_roman_fonts))
712 h_font_roman[0] = name;
714 if (name == "fourier") {
715 h_font_roman[0] = "utopia";
716 // when font uses real small capitals
717 if (opts == "expert")
721 if (name == "garamondx") {
722 h_font_roman[0] = "garamondx";
727 if (name == "libertine") {
728 h_font_roman[0] = "libertine";
729 // this automatically invokes biolinum
730 h_font_sans[0] = "biolinum";
733 else if (opts == "lining")
734 h_font_osf = "false";
737 if (name == "libertine-type1") {
738 h_font_roman[0] = "libertine";
739 // NOTE: contrary to libertine.sty, libertine-type1
740 // does not automatically invoke biolinum
741 if (opts == "lining")
742 h_font_osf = "false";
743 else if (opts == "osf")
747 if (name == "mathdesign") {
748 if (opts.find("charter") != string::npos)
749 h_font_roman[0] = "md-charter";
750 if (opts.find("garamond") != string::npos)
751 h_font_roman[0] = "md-garamond";
752 if (opts.find("utopia") != string::npos)
753 h_font_roman[0] = "md-utopia";
754 if (opts.find("expert") != string::npos) {
760 else if (name == "mathpazo")
761 h_font_roman[0] = "palatino";
763 else if (name == "mathptmx")
764 h_font_roman[0] = "times";
766 if (name == "crimson")
767 h_font_roman[0] = "cochineal";
769 if (name == "cochineal") {
770 h_font_roman[0] = "cochineal";
771 // cochineal can have several options, e.g. [proportional,osf]
772 string::size_type pos = opts.find("osf");
773 if (pos != string::npos)
777 if (name == "noto") {
778 // noto can have several options
780 h_font_roman[0] = "NotoSerif-TLF";
781 string::size_type pos = opts.find("rm");
782 if (pos != string::npos)
783 h_font_roman[0] = "NotoSerif-TLF";
784 pos = opts.find("sf");
785 if (pos != string::npos)
786 h_font_sans[0] = "NotoSans-TLF";
787 pos = opts.find("nott");
788 if (pos != string::npos) {
789 h_font_roman[0] = "NotoSerif-TLF";
790 h_font_sans[0] = "NotoSans-TLF";
792 // noto as typewriter is handled in handling of \ttdefault
793 // special cases are handled in handling of \rmdefault and \sfdefault
797 if (is_known(name, known_sans_fonts)) {
798 h_font_sans[0] = name;
799 if (options.size() >= 1) {
800 if (scale_as_percentage(opts, h_font_sf_scale[0]))
805 if (name == "biolinum-type1") {
806 h_font_sans[0] = "biolinum";
807 // biolinum can have several options, e.g. [osf,scaled=0.97]
808 string::size_type pos = opts.find("osf");
809 if (pos != string::npos)
814 if (is_known(name, known_typewriter_fonts)) {
815 // fourier can be set as roman font _only_
816 // fourier as typewriter is handled in handling of \ttdefault
817 if (name != "fourier") {
818 h_font_typewriter[0] = name;
819 if (options.size() >= 1) {
820 if (scale_as_percentage(opts, h_font_tt_scale[0]))
826 if (name == "libertineMono-type1") {
827 h_font_typewriter[0] = "libertine-mono";
830 // font uses old-style figure
835 if (is_known(name, known_math_fonts))
836 h_font_math[0] = name;
838 if (name == "newtxmath") {
840 h_font_math[0] = "newtxmath";
841 else if (opts == "garamondx")
842 h_font_math[0] = "garamondx-ntxm";
843 else if (opts == "libertine")
844 h_font_math[0] = "libertine-ntxm";
845 else if (opts == "minion")
846 h_font_math[0] = "minion-ntxm";
847 else if (opts == "cochineal")
848 h_font_math[0] = "cochineal-ntxm";
853 h_font_math[0] = "iwona-math";
855 if (name == "kurier")
857 h_font_math[0] = "kurier-math";
859 // after the detection and handling of special cases, we can remove the
860 // fonts, otherwise they would appear in the preamble, see bug #7856
861 if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
862 || is_known(name, known_typewriter_fonts) || is_known(name, known_math_fonts))
864 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
865 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
866 name == "esint" || name == "mhchem" || name == "mathdots" ||
867 name == "mathtools" || name == "stackrel" ||
868 name == "stmaryrd" || name == "undertilde")
869 h_use_packages[name] = "2";
871 else if (name == "babel") {
872 h_language_package = "default";
873 // One might think we would have to do nothing if babel is loaded
874 // without any options to prevent pollution of the preamble with this
875 // babel call in every roundtrip.
876 // But the user could have defined babel-specific things afterwards. So
877 // we need to keep it in the preamble to prevent cases like bug #7861.
879 // check if more than one option was used - used later for inputenc
880 if (options.begin() != options.end() - 1)
881 one_language = false;
882 // babel takes the last language of the option of its \usepackage
883 // call as document language. If there is no such language option, the
884 // last language in the documentclass options is used.
885 handle_opt(options, known_languages, h_language);
886 // translate the babel name to a LyX name
887 h_language = babel2lyx(h_language);
888 if (h_language == "japanese") {
889 // For Japanese, the encoding isn't indicated in the source
890 // file, and there's really not much we can do. We could
891 // 1) offer a list of possible encodings to choose from, or
892 // 2) determine the encoding of the file by inspecting it.
893 // For the time being, we leave the encoding alone so that
894 // we don't get iconv errors when making a wrong guess, and
895 // we will output a note at the top of the document
896 // explaining what to do.
897 Encoding const * const enc = encodings.fromIconvName(
898 p.getEncoding(), Encoding::japanese, false);
900 h_inputencoding = enc->name();
901 is_nonCJKJapanese = true;
902 // in this case babel can be removed from the preamble
903 registerAutomaticallyLoadedPackage("babel");
905 // If babel is called with options, LyX puts them by default into the
906 // document class options. This works for most languages, except
907 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
908 // perhaps in future others.
909 // Therefore keep the babel call as it is as the user might have
911 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
913 delete_opt(options, known_languages);
915 h_preamble << "\\usepackage{babel}\n";
916 explicit_babel = true;
920 else if (name == "polyglossia") {
921 h_language_package = "default";
922 h_default_output_format = "pdf4";
923 h_use_non_tex_fonts = true;
925 registerAutomaticallyLoadedPackage("xunicode");
926 if (h_inputencoding == "auto")
927 p.setEncoding("UTF-8");
930 else if (name == "CJK") {
931 // set the encoding to "auto" because it might be set to "default" by the babel handling
932 // and this would not be correct for CJK
933 if (h_inputencoding == "default")
934 h_inputencoding = "auto";
935 registerAutomaticallyLoadedPackage("CJK");
938 else if (name == "CJKutf8") {
939 h_inputencoding = "utf8-cjk";
940 p.setEncoding("UTF-8");
941 registerAutomaticallyLoadedPackage("CJKutf8");
944 else if (name == "fontenc") {
945 h_fontencoding = getStringFromVector(options, ",");
946 /* We could do the following for better round trip support,
947 * but this makes the document less portable, so I skip it:
948 if (h_fontencoding == lyxrc.fontenc)
949 h_fontencoding = "global";
954 else if (name == "inputenc" || name == "luainputenc") {
955 // h_inputencoding is only set when there is not more than one
956 // inputenc option because otherwise h_inputencoding must be
957 // set to "auto" (the default encoding of the document language)
958 // Therefore check that exactly one option is passed to inputenc.
959 // It is also only set when there is not more than one babel
961 if (!options.empty()) {
962 string const encoding = options.back();
963 Encoding const * const enc = encodings.fromLaTeXName(
964 encoding, Encoding::inputenc, true);
967 cerr << "Unknown encoding " << encoding
968 << ". Ignoring." << std::endl;
970 if (!enc->unsafe() && options.size() == 1 && one_language == true)
971 h_inputencoding = enc->name();
972 p.setEncoding(enc->iconvName());
978 else if (name == "srcltx") {
981 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
984 h_output_sync_macro = "\\usepackage{srcltx}";
987 else if (is_known(name, known_old_language_packages)) {
988 // known language packages from the times before babel
989 // if they are found and not also babel, they will be used as
990 // custom language package
991 h_language_package = "\\usepackage{" + name + "}";
994 else if (name == "lyxskak") {
995 // ignore this and its options
996 const char * const o[] = {"ps", "mover", 0};
997 delete_opt(options, o);
1000 else if (is_known(name, known_lyx_packages) && options.empty()) {
1001 if (name == "splitidx")
1002 h_use_indices = "true";
1003 else if (name == "minted")
1004 h_use_minted = true;
1005 else if (name == "refstyle")
1006 h_use_refstyle = true;
1007 else if (name == "prettyref")
1008 h_use_refstyle = false;
1009 if (!in_lyx_preamble) {
1010 h_preamble << package_beg_sep << name
1011 << package_mid_sep << "\\usepackage{"
1013 if (p.next_token().cat() == catNewline ||
1014 (p.next_token().cat() == catSpace &&
1015 p.next_next_token().cat() == catNewline))
1017 h_preamble << package_end_sep;
1021 else if (name == "geometry")
1022 handle_geometry(options);
1024 else if (name == "subfig")
1025 ; // ignore this FIXME: Use the package separator mechanism instead
1027 else if (char const * const * where = is_known(name, known_languages))
1028 h_language = known_coded_languages[where - known_languages];
1030 else if (name == "natbib") {
1031 h_biblio_style = "plainnat";
1032 h_cite_engine = "natbib";
1033 h_cite_engine_type = "authoryear";
1034 vector<string>::iterator it =
1035 find(options.begin(), options.end(), "authoryear");
1036 if (it != options.end())
1039 it = find(options.begin(), options.end(), "numbers");
1040 if (it != options.end()) {
1041 h_cite_engine_type = "numerical";
1045 if (!options.empty())
1046 h_biblio_options = join(options, ",");
1049 else if (name == "jurabib") {
1050 h_biblio_style = "jurabib";
1051 h_cite_engine = "jurabib";
1052 h_cite_engine_type = "authoryear";
1055 else if (name == "bibtopic")
1056 h_use_bibtopic = "true";
1058 else if (name == "hyperref")
1059 handle_hyperref(options);
1061 else if (name == "algorithm2e") {
1062 // Load "algorithm2e" module
1063 addModule("algorithm2e");
1064 // Add the package options to the global document options
1065 if (!options.empty()) {
1066 if (h_options.empty())
1067 h_options = join(options, ",");
1069 h_options += ',' + join(options, ",");
1072 else if (name == "microtype") {
1073 //we internally support only microtype without params
1074 if (options.empty())
1075 h_use_microtype = "true";
1077 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1080 else if (!in_lyx_preamble) {
1081 if (options.empty())
1082 h_preamble << "\\usepackage{" << name << '}';
1084 h_preamble << "\\usepackage[" << opts << "]{"
1088 if (p.next_token().cat() == catNewline ||
1089 (p.next_token().cat() == catSpace &&
1090 p.next_next_token().cat() == catNewline))
1094 // We need to do something with the options...
1095 if (!options.empty() && !detectEncoding)
1096 cerr << "Ignoring options '" << join(options, ",")
1097 << "' of package " << name << '.' << endl;
1099 // remove the whitespace
1104 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1107 Token t = p.get_token();
1108 if (t.cat() == catEscape &&
1109 is_known(t.cs(), known_if_commands))
1110 handle_if(p, in_lyx_preamble);
1112 if (!in_lyx_preamble)
1113 h_preamble << t.asInput();
1114 if (t.cat() == catEscape && t.cs() == "fi")
1121 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1123 // set the quote language
1124 // LyX only knows the following quotes languages:
1125 // english, swedish, german, polish, french and danish
1126 // (quotes for "japanese" and "chinese-traditional" are missing because
1127 // they wouldn't be useful: https://www.lyx.org/trac/ticket/6383)
1128 // conversion list taken from
1129 // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
1130 // (quotes for kazakh and interlingua are unknown)
1132 if (is_known(h_language, known_danish_quotes_languages))
1133 h_quotes_style = "danish";
1135 else if (is_known(h_language, known_french_quotes_languages))
1136 h_quotes_style = "french";
1138 else if (is_known(h_language, known_german_quotes_languages))
1139 h_quotes_style = "german";
1141 else if (is_known(h_language, known_polish_quotes_languages))
1142 h_quotes_style = "polish";
1144 else if (is_known(h_language, known_swedish_quotes_languages))
1145 h_quotes_style = "swedish";
1147 else if (is_known(h_language, known_english_quotes_languages))
1148 h_quotes_style = "english";
1150 if (contains(h_float_placement, "H"))
1151 registerAutomaticallyLoadedPackage("float");
1152 if (h_spacing != "single" && h_spacing != "default")
1153 registerAutomaticallyLoadedPackage("setspace");
1154 if (h_use_packages["amsmath"] == "2") {
1155 // amsbsy and amstext are already provided by amsmath
1156 registerAutomaticallyLoadedPackage("amsbsy");
1157 registerAutomaticallyLoadedPackage("amstext");
1160 // output the LyX file settings
1161 // Important: Keep the version formatting in sync with LyX and
1162 // lyx2lyx (bug 7951)
1163 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1164 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1165 << lyx_version_minor << '\n'
1166 << "\\lyxformat " << LYX_FORMAT << '\n'
1167 << "\\begin_document\n"
1168 << "\\begin_header\n"
1169 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1170 << "\\origin " << origin << "\n"
1171 << "\\textclass " << h_textclass << "\n";
1172 string const raw = subdoc ? empty_string() : h_preamble.str();
1174 os << "\\begin_preamble\n";
1175 for (string::size_type i = 0; i < raw.size(); ++i) {
1176 if (raw[i] == package_beg_sep) {
1177 // Here follows some package loading code that
1178 // must be skipped if the package is loaded
1180 string::size_type j = raw.find(package_mid_sep, i);
1181 if (j == string::npos)
1183 string::size_type k = raw.find(package_end_sep, j);
1184 if (k == string::npos)
1186 string const package = raw.substr(i + 1, j - i - 1);
1187 string const replacement = raw.substr(j + 1, k - j - 1);
1188 if (auto_packages.find(package) == auto_packages.end())
1194 os << "\n\\end_preamble\n";
1196 if (!h_options.empty())
1197 os << "\\options " << h_options << "\n";
1198 os << "\\use_default_options " << h_use_default_options << "\n";
1199 if (!used_modules.empty()) {
1200 os << "\\begin_modules\n";
1201 vector<string>::const_iterator const end = used_modules.end();
1202 vector<string>::const_iterator it = used_modules.begin();
1203 for (; it != end; ++it)
1205 os << "\\end_modules\n";
1207 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1208 << "\\language " << h_language << "\n"
1209 << "\\language_package " << h_language_package << "\n"
1210 << "\\inputencoding " << h_inputencoding << "\n"
1211 << "\\fontencoding " << h_fontencoding << "\n"
1212 << "\\font_roman \"" << h_font_roman[0]
1213 << "\" \"" << h_font_roman[1] << "\"\n"
1214 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1215 << "\\font_typewriter \"" << h_font_typewriter[0]
1216 << "\" \"" << h_font_typewriter[1] << "\"\n"
1217 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1218 << "\\font_default_family " << h_font_default_family << "\n"
1219 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1220 << "\\font_sc " << h_font_sc << "\n"
1221 << "\\font_osf " << h_font_osf << "\n"
1222 << "\\font_sf_scale " << h_font_sf_scale[0]
1223 << ' ' << h_font_sf_scale[1] << '\n'
1224 << "\\font_tt_scale " << h_font_tt_scale[0]
1225 << ' ' << h_font_tt_scale[1] << '\n';
1226 if (!h_font_cjk.empty())
1227 os << "\\font_cjk " << h_font_cjk << '\n';
1228 os << "\\use_microtype " << h_use_microtype << '\n'
1229 << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
1230 << "\\graphics " << h_graphics << '\n'
1231 << "\\default_output_format " << h_default_output_format << "\n"
1232 << "\\output_sync " << h_output_sync << "\n";
1233 if (h_output_sync == "1")
1234 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1235 os << "\\bibtex_command " << h_bibtex_command << "\n"
1236 << "\\index_command " << h_index_command << "\n";
1237 if (!h_float_placement.empty())
1238 os << "\\float_placement " << h_float_placement << "\n";
1239 os << "\\paperfontsize " << h_paperfontsize << "\n"
1240 << "\\spacing " << h_spacing << "\n"
1241 << "\\use_hyperref " << h_use_hyperref << '\n';
1242 if (h_use_hyperref == "true") {
1243 if (!h_pdf_title.empty())
1244 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1245 if (!h_pdf_author.empty())
1246 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1247 if (!h_pdf_subject.empty())
1248 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1249 if (!h_pdf_keywords.empty())
1250 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1251 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1252 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1253 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1254 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1255 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1256 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1257 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1258 "\\pdf_backref " << h_pdf_backref << "\n"
1259 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1260 if (!h_pdf_pagemode.empty())
1261 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1262 if (!h_pdf_quoted_options.empty())
1263 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1265 os << "\\papersize " << h_papersize << "\n"
1266 << "\\use_geometry " << h_use_geometry << '\n';
1267 for (map<string, string>::const_iterator it = h_use_packages.begin();
1268 it != h_use_packages.end(); ++it)
1269 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1270 os << "\\cite_engine " << h_cite_engine << '\n'
1271 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1272 << "\\biblio_style " << h_biblio_style << "\n"
1273 << "\\use_bibtopic " << h_use_bibtopic << "\n"
1274 << "\\use_indices " << h_use_indices << "\n"
1275 << "\\paperorientation " << h_paperorientation << '\n'
1276 << "\\suppress_date " << h_suppress_date << '\n'
1277 << "\\justification " << h_justification << '\n'
1278 << "\\use_refstyle " << h_use_refstyle << '\n'
1279 << "\\use_minted " << h_use_minted << '\n';
1280 if (!h_fontcolor.empty())
1281 os << "\\fontcolor " << h_fontcolor << '\n';
1282 if (!h_notefontcolor.empty())
1283 os << "\\notefontcolor " << h_notefontcolor << '\n';
1284 if (!h_backgroundcolor.empty())
1285 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1286 if (!h_boxbgcolor.empty())
1287 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1288 if (index_number != 0)
1289 for (int i = 0; i < index_number; i++) {
1290 os << "\\index " << h_index[i] << '\n'
1291 << "\\shortcut " << h_shortcut[i] << '\n'
1292 << "\\color " << h_color << '\n'
1296 os << "\\index " << h_index[0] << '\n'
1297 << "\\shortcut " << h_shortcut[0] << '\n'
1298 << "\\color " << h_color << '\n'
1302 << "\\secnumdepth " << h_secnumdepth << "\n"
1303 << "\\tocdepth " << h_tocdepth << "\n"
1304 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1305 if (h_paragraph_separation == "skip")
1306 os << "\\defskip " << h_defskip << "\n";
1308 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1309 os << "\\is_math_indent " << h_is_mathindent << "\n";
1310 if (!h_mathindentation.empty())
1311 os << "\\math_indentation " << h_mathindentation << "\n";
1312 os << "\\math_numbering_side " << h_math_numbering_side << "\n";
1313 os << "\\quotes_style " << h_quotes_style << "\n"
1314 << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
1315 << "\\papercolumns " << h_papercolumns << "\n"
1316 << "\\papersides " << h_papersides << "\n"
1317 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1318 if (!h_listings_params.empty())
1319 os << "\\listings_params " << h_listings_params << "\n";
1320 os << "\\tracking_changes " << h_tracking_changes << "\n"
1321 << "\\output_changes " << h_output_changes << "\n"
1322 << "\\html_math_output " << h_html_math_output << "\n"
1323 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1324 << "\\html_be_strict " << h_html_be_strict << "\n"
1326 << "\\end_header\n\n"
1327 << "\\begin_body\n";
1332 void Preamble::parse(Parser & p, string const & forceclass,
1333 TeX2LyXDocClass & tc)
1335 // initialize fixed types
1336 special_columns_['D'] = 3;
1337 parse(p, forceclass, false, tc);
1341 void Preamble::parse(Parser & p, string const & forceclass,
1342 bool detectEncoding, TeX2LyXDocClass & tc)
1344 bool is_full_document = false;
1345 bool is_lyx_file = false;
1346 bool in_lyx_preamble = false;
1348 // determine whether this is a full document or a fragment for inclusion
1350 Token const & t = p.get_token();
1352 if (t.cat() == catEscape && t.cs() == "documentclass") {
1353 is_full_document = true;
1359 if (detectEncoding && !is_full_document)
1362 while (is_full_document && p.good()) {
1363 if (detectEncoding && h_inputencoding != "auto" &&
1364 h_inputencoding != "default")
1367 Token const & t = p.get_token();
1370 if (!detectEncoding)
1371 cerr << "t: " << t << '\n';
1377 if (!in_lyx_preamble &&
1378 (t.cat() == catLetter ||
1379 t.cat() == catSuper ||
1380 t.cat() == catSub ||
1381 t.cat() == catOther ||
1382 t.cat() == catMath ||
1383 t.cat() == catActive ||
1384 t.cat() == catBegin ||
1385 t.cat() == catEnd ||
1386 t.cat() == catAlign ||
1387 t.cat() == catParameter))
1388 h_preamble << t.cs();
1390 else if (!in_lyx_preamble &&
1391 (t.cat() == catSpace || t.cat() == catNewline))
1392 h_preamble << t.asInput();
1394 else if (t.cat() == catComment) {
1395 static regex const islyxfile("%% LyX .* created this file");
1396 static regex const usercommands("User specified LaTeX commands");
1398 string const comment = t.asInput();
1400 // magically switch encoding default if it looks like XeLaTeX
1401 static string const magicXeLaTeX =
1402 "% This document must be compiled with XeLaTeX ";
1403 if (comment.size() > magicXeLaTeX.size()
1404 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1405 && h_inputencoding == "auto") {
1406 if (!detectEncoding)
1407 cerr << "XeLaTeX comment found, switching to UTF8\n";
1408 h_inputencoding = "utf8";
1411 if (regex_search(comment, sub, islyxfile)) {
1413 in_lyx_preamble = true;
1414 } else if (is_lyx_file
1415 && regex_search(comment, sub, usercommands))
1416 in_lyx_preamble = false;
1417 else if (!in_lyx_preamble)
1418 h_preamble << t.asInput();
1421 else if (t.cs() == "PassOptionsToPackage") {
1422 string const poptions = p.getArg('{', '}');
1423 string const package = p.verbatim_item();
1424 extra_package_options_.insert(make_pair(package, poptions));
1427 else if (t.cs() == "pagestyle")
1428 h_paperpagestyle = p.verbatim_item();
1430 else if (t.cs() == "setdefaultlanguage") {
1432 // We don't yet care about non-language variant options
1433 // because LyX doesn't support this yet, see bug #8214
1435 string langopts = p.getOpt();
1436 // check if the option contains a variant, if yes, extract it
1437 string::size_type pos_var = langopts.find("variant");
1438 string::size_type i = langopts.find(',', pos_var);
1439 string::size_type k = langopts.find('=', pos_var);
1440 if (pos_var != string::npos){
1442 if (i == string::npos)
1443 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1445 variant = langopts.substr(k + 1, i - k - 1);
1446 h_language = variant;
1450 h_language = p.verbatim_item();
1451 //finally translate the poyglossia name to a LyX name
1452 h_language = polyglossia2lyx(h_language);
1455 else if (t.cs() == "setotherlanguage") {
1456 // We don't yet care about the option because LyX doesn't
1457 // support this yet, see bug #8214
1458 p.hasOpt() ? p.getOpt() : string();
1462 else if (t.cs() == "setmainfont") {
1463 // we don't care about the option
1464 p.hasOpt() ? p.getOpt() : string();
1465 h_font_roman[1] = p.getArg('{', '}');
1468 else if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1469 // LyX currently only supports the scale option
1472 string fontopts = p.getArg('[', ']');
1473 // check if the option contains a scaling, if yes, extract it
1474 string::size_type pos = fontopts.find("Scale");
1475 if (pos != string::npos) {
1476 string::size_type i = fontopts.find(',', pos);
1477 if (i == string::npos)
1478 scale_as_percentage(fontopts.substr(pos + 1), scale);
1480 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1483 if (t.cs() == "setsansfont") {
1485 h_font_sf_scale[1] = scale;
1486 h_font_sans[1] = p.getArg('{', '}');
1489 h_font_tt_scale[1] = scale;
1490 h_font_typewriter[1] = p.getArg('{', '}');
1494 else if (t.cs() == "date") {
1495 string argument = p.getArg('{', '}');
1496 if (argument.empty())
1497 h_suppress_date = "true";
1499 h_preamble << t.asInput() << '{' << argument << '}';
1502 else if (t.cs() == "color") {
1503 string const space =
1504 (p.hasOpt() ? p.getOpt() : string());
1505 string argument = p.getArg('{', '}');
1506 // check the case that a standard color is used
1507 if (space.empty() && is_known(argument, known_basic_colors)) {
1508 h_fontcolor = rgbcolor2code(argument);
1509 registerAutomaticallyLoadedPackage("color");
1510 } else if (space.empty() && argument == "document_fontcolor")
1511 registerAutomaticallyLoadedPackage("color");
1512 // check the case that LyX's document_fontcolor is defined
1513 // but not used for \color
1515 h_preamble << t.asInput();
1517 h_preamble << space;
1518 h_preamble << '{' << argument << '}';
1519 // the color might already be set because \definecolor
1520 // is parsed before this
1525 else if (t.cs() == "pagecolor") {
1526 string argument = p.getArg('{', '}');
1527 // check the case that a standard color is used
1528 if (is_known(argument, known_basic_colors)) {
1529 h_backgroundcolor = rgbcolor2code(argument);
1530 } else if (argument == "page_backgroundcolor")
1531 registerAutomaticallyLoadedPackage("color");
1532 // check the case that LyX's page_backgroundcolor is defined
1533 // but not used for \pagecolor
1535 h_preamble << t.asInput() << '{' << argument << '}';
1536 // the color might already be set because \definecolor
1537 // is parsed before this
1538 h_backgroundcolor = "";
1542 else if (t.cs() == "makeatletter") {
1543 // LyX takes care of this
1544 p.setCatcode('@', catLetter);
1547 else if (t.cs() == "makeatother") {
1548 // LyX takes care of this
1549 p.setCatcode('@', catOther);
1552 else if (t.cs() == "makeindex") {
1553 // LyX will re-add this if a print index command is found
1557 else if (t.cs() == "newindex") {
1558 string const indexname = p.getArg('[', ']');
1559 string const shortcut = p.verbatim_item();
1560 if (!indexname.empty())
1561 h_index[index_number] = indexname;
1563 h_index[index_number] = shortcut;
1564 h_shortcut[index_number] = shortcut;
1569 else if (t.cs() == "RS@ifundefined") {
1570 string const name = p.verbatim_item();
1571 string const body1 = p.verbatim_item();
1572 string const body2 = p.verbatim_item();
1573 // only non-lyxspecific stuff
1574 if (in_lyx_preamble &&
1575 (name == "subsecref" || name == "thmref" || name == "lemref"))
1579 ss << '\\' << t.cs();
1580 ss << '{' << name << '}'
1581 << '{' << body1 << '}'
1582 << '{' << body2 << '}';
1583 h_preamble << ss.str();
1587 else if (t.cs() == "AtBeginDocument") {
1588 string const name = p.verbatim_item();
1589 // only non-lyxspecific stuff
1590 if (in_lyx_preamble &&
1591 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
1592 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
1593 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
1594 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
1595 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
1596 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
1597 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
1598 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
1599 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
1600 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
1601 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
1602 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
1603 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
1604 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
1605 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
1609 ss << '\\' << t.cs();
1610 ss << '{' << name << '}';
1611 h_preamble << ss.str();
1615 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1616 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1617 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1618 || t.cs() == "DeclareRobustCommand"
1619 || t.cs() == "DeclareRobustCommandx"
1620 || t.cs() == "ProvideTextCommandDefault"
1621 || t.cs() == "DeclareMathAccent") {
1623 if (p.next_token().character() == '*') {
1627 string const name = p.verbatim_item();
1628 string const opt1 = p.getFullOpt();
1629 string const opt2 = p.getFullOpt();
1630 string const body = p.verbatim_item();
1631 // store the in_lyx_preamble setting
1632 bool const was_in_lyx_preamble = in_lyx_preamble;
1634 if (name == "\\rmdefault")
1635 if (is_known(body, known_roman_fonts)) {
1636 h_font_roman[0] = body;
1638 in_lyx_preamble = true;
1640 if (name == "\\sfdefault")
1641 if (is_known(body, known_sans_fonts)) {
1642 h_font_sans[0] = body;
1644 in_lyx_preamble = true;
1646 if (name == "\\ttdefault")
1647 if (is_known(body, known_typewriter_fonts)) {
1648 h_font_typewriter[0] = body;
1650 in_lyx_preamble = true;
1652 if (name == "\\familydefault") {
1653 string family = body;
1654 // remove leading "\"
1655 h_font_default_family = family.erase(0,1);
1657 in_lyx_preamble = true;
1660 // remove LyX-specific definitions that are re-added by LyX
1662 // \lyxline is an ancient command that is converted by tex2lyx into
1663 // a \rule therefore remove its preamble code
1664 if (name == "\\lyxdot" || name == "\\lyxarrow"
1665 || name == "\\lyxline" || name == "\\LyX") {
1667 in_lyx_preamble = true;
1670 // Add the command to the known commands
1671 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1673 // only non-lyxspecific stuff
1674 if (!in_lyx_preamble) {
1676 ss << '\\' << t.cs();
1679 ss << '{' << name << '}' << opt1 << opt2
1680 << '{' << body << "}";
1681 h_preamble << ss.str();
1683 ostream & out = in_preamble ? h_preamble : os;
1684 out << "\\" << t.cs() << "{" << name << "}"
1685 << opts << "{" << body << "}";
1688 // restore the in_lyx_preamble setting
1689 in_lyx_preamble = was_in_lyx_preamble;
1692 else if (t.cs() == "documentclass") {
1693 vector<string>::iterator it;
1694 vector<string> opts = split_options(p.getArg('[', ']'));
1695 handle_opt(opts, known_fontsizes, h_paperfontsize);
1696 delete_opt(opts, known_fontsizes);
1697 // delete "pt" at the end
1698 string::size_type i = h_paperfontsize.find("pt");
1699 if (i != string::npos)
1700 h_paperfontsize.erase(i);
1701 // The documentclass options are always parsed before the options
1702 // of the babel call so that a language cannot overwrite the babel
1704 handle_opt(opts, known_languages, h_language);
1705 delete_opt(opts, known_languages);
1708 if ((it = find(opts.begin(), opts.end(), "fleqn"))
1710 h_is_mathindent = "1";
1713 // formula numbering side
1714 if ((it = find(opts.begin(), opts.end(), "leqno"))
1716 h_math_numbering_side = "left";
1719 else if ((it = find(opts.begin(), opts.end(), "reqno"))
1721 h_math_numbering_side = "right";
1725 // paper orientation
1726 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1727 h_paperorientation = "landscape";
1731 if ((it = find(opts.begin(), opts.end(), "oneside"))
1736 if ((it = find(opts.begin(), opts.end(), "twoside"))
1742 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1744 h_papercolumns = "1";
1747 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1749 h_papercolumns = "2";
1753 // some size options are known to any document classes, other sizes
1754 // are handled by the \geometry command of the geometry package
1755 handle_opt(opts, known_class_paper_sizes, h_papersize);
1756 delete_opt(opts, known_class_paper_sizes);
1757 // the remaining options
1758 h_options = join(opts, ",");
1759 // FIXME This does not work for classes that have a
1760 // different name in LyX than in LaTeX
1761 h_textclass = p.getArg('{', '}');
1765 else if (t.cs() == "usepackage") {
1766 string const options = p.getArg('[', ']');
1767 string const name = p.getArg('{', '}');
1768 vector<string> vecnames;
1769 split(name, vecnames, ',');
1770 vector<string>::const_iterator it = vecnames.begin();
1771 vector<string>::const_iterator end = vecnames.end();
1772 for (; it != end; ++it)
1773 handle_package(p, trimSpaceAndEol(*it), options,
1774 in_lyx_preamble, detectEncoding);
1777 else if (t.cs() == "inputencoding") {
1778 string const encoding = p.getArg('{','}');
1779 Encoding const * const enc = encodings.fromLaTeXName(
1780 encoding, Encoding::inputenc, true);
1782 if (!detectEncoding)
1783 cerr << "Unknown encoding " << encoding
1784 << ". Ignoring." << std::endl;
1787 h_inputencoding = enc->name();
1788 p.setEncoding(enc->iconvName());
1792 else if (t.cs() == "newenvironment") {
1793 string const name = p.getArg('{', '}');
1794 string const opt1 = p.getFullOpt();
1795 string const opt2 = p.getFullOpt();
1796 string const beg = p.verbatim_item();
1797 string const end = p.verbatim_item();
1798 if (!in_lyx_preamble) {
1799 h_preamble << "\\newenvironment{" << name
1800 << '}' << opt1 << opt2 << '{'
1801 << beg << "}{" << end << '}';
1803 add_known_environment(name, opt1, !opt2.empty(),
1804 from_utf8(beg), from_utf8(end));
1808 else if (t.cs() == "newtheorem") {
1810 if (p.next_token().character() == '*') {
1814 string const name = p.getArg('{', '}');
1815 string const opt1 = p.getFullOpt();
1816 string const opt2 = p.getFullOpt();
1817 string const body = p.verbatim_item();
1818 string const opt3 = p.getFullOpt();
1819 string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
1821 string const complete = cmd + "{" + name + '}' +
1822 opt1 + opt2 + '{' + body + '}' + opt3;
1824 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
1826 if (!in_lyx_preamble)
1827 h_preamble << complete;
1830 else if (t.cs() == "def") {
1831 string name = p.get_token().cs();
1832 // In fact, name may be more than the name:
1833 // In the test case of bug 8116
1834 // name == "csname SF@gobble@opt \endcsname".
1835 // Therefore, we need to use asInput() instead of cs().
1836 while (p.next_token().cat() != catBegin)
1837 name += p.get_token().asInput();
1838 if (!in_lyx_preamble)
1839 h_preamble << "\\def\\" << name << '{'
1840 << p.verbatim_item() << "}";
1843 else if (t.cs() == "newcolumntype") {
1844 string const name = p.getArg('{', '}');
1845 trimSpaceAndEol(name);
1847 string opts = p.getOpt();
1848 if (!opts.empty()) {
1849 istringstream is(string(opts, 1));
1852 special_columns_[name[0]] = nargs;
1853 h_preamble << "\\newcolumntype{" << name << "}";
1855 h_preamble << "[" << nargs << "]";
1856 h_preamble << "{" << p.verbatim_item() << "}";
1859 else if (t.cs() == "setcounter") {
1860 string const name = p.getArg('{', '}');
1861 string const content = p.getArg('{', '}');
1862 if (name == "secnumdepth")
1863 h_secnumdepth = content;
1864 else if (name == "tocdepth")
1865 h_tocdepth = content;
1867 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1870 else if (t.cs() == "setlength") {
1871 string const name = p.verbatim_item();
1872 string const content = p.verbatim_item();
1873 // the paragraphs are only not indented when \parindent is set to zero
1874 if (name == "\\parindent" && content != "") {
1875 if (content[0] == '0')
1876 h_paragraph_separation = "skip";
1878 h_paragraph_indentation = translate_len(content);
1879 } else if (name == "\\parskip") {
1880 if (content == "\\smallskipamount")
1881 h_defskip = "smallskip";
1882 else if (content == "\\medskipamount")
1883 h_defskip = "medskip";
1884 else if (content == "\\bigskipamount")
1885 h_defskip = "bigskip";
1887 h_defskip = translate_len(content);
1888 } else if (name == "\\mathindent") {
1889 h_mathindentation = translate_len(content);
1891 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1894 else if (t.cs() == "onehalfspacing")
1895 h_spacing = "onehalf";
1897 else if (t.cs() == "doublespacing")
1898 h_spacing = "double";
1900 else if (t.cs() == "setstretch")
1901 h_spacing = "other " + p.verbatim_item();
1903 else if (t.cs() == "synctex") {
1904 // the scheme is \synctex=value
1905 // where value can only be "1" or "-1"
1906 h_output_sync = "1";
1907 // there can be any character behind the value (e.g. a linebreak or a '\'
1908 // therefore we extract it char by char
1910 string value = p.get_token().asInput();
1912 value += p.get_token().asInput();
1913 h_output_sync_macro = "\\synctex=" + value;
1916 else if (t.cs() == "begin") {
1917 string const name = p.getArg('{', '}');
1918 if (name == "document")
1920 h_preamble << "\\begin{" << name << "}";
1923 else if (t.cs() == "geometry") {
1924 vector<string> opts = split_options(p.getArg('{', '}'));
1925 handle_geometry(opts);
1928 else if (t.cs() == "definecolor") {
1929 string const color = p.getArg('{', '}');
1930 string const space = p.getArg('{', '}');
1931 string const value = p.getArg('{', '}');
1932 if (color == "document_fontcolor" && space == "rgb") {
1933 RGBColor c(RGBColorFromLaTeX(value));
1934 h_fontcolor = X11hexname(c);
1935 } else if (color == "note_fontcolor" && space == "rgb") {
1936 RGBColor c(RGBColorFromLaTeX(value));
1937 h_notefontcolor = X11hexname(c);
1938 } else if (color == "page_backgroundcolor" && space == "rgb") {
1939 RGBColor c(RGBColorFromLaTeX(value));
1940 h_backgroundcolor = X11hexname(c);
1941 } else if (color == "shadecolor" && space == "rgb") {
1942 RGBColor c(RGBColorFromLaTeX(value));
1943 h_boxbgcolor = X11hexname(c);
1945 h_preamble << "\\definecolor{" << color
1946 << "}{" << space << "}{" << value
1951 else if (t.cs() == "bibliographystyle")
1952 h_biblio_style = p.verbatim_item();
1954 else if (t.cs() == "jurabibsetup") {
1955 // FIXME p.getArg('{', '}') is most probably wrong (it
1956 // does not handle nested braces).
1957 // Use p.verbatim_item() instead.
1958 vector<string> jurabibsetup =
1959 split_options(p.getArg('{', '}'));
1960 // add jurabibsetup to the jurabib package options
1961 add_package("jurabib", jurabibsetup);
1962 if (!jurabibsetup.empty()) {
1963 h_preamble << "\\jurabibsetup{"
1964 << join(jurabibsetup, ",") << '}';
1968 else if (t.cs() == "hypersetup") {
1969 vector<string> hypersetup =
1970 split_options(p.verbatim_item());
1971 // add hypersetup to the hyperref package options
1972 handle_hyperref(hypersetup);
1973 if (!hypersetup.empty()) {
1974 h_preamble << "\\hypersetup{"
1975 << join(hypersetup, ",") << '}';
1979 else if (is_known(t.cs(), known_if_3arg_commands)) {
1980 // prevent misparsing of \usepackage if it is used
1981 // as an argument (see e.g. our own output of
1982 // \@ifundefined above)
1983 string const arg1 = p.verbatim_item();
1984 string const arg2 = p.verbatim_item();
1985 string const arg3 = p.verbatim_item();
1986 // test case \@ifundefined{date}{}{\date{}}
1987 if (t.cs() == "@ifundefined" && arg1 == "date" &&
1988 arg2.empty() && arg3 == "\\date{}") {
1989 h_suppress_date = "true";
1990 // older tex2lyx versions did output
1991 // \@ifundefined{definecolor}{\usepackage{color}}{}
1992 } else if (t.cs() == "@ifundefined" &&
1993 arg1 == "definecolor" &&
1994 arg2 == "\\usepackage{color}" &&
1996 if (!in_lyx_preamble)
1997 h_preamble << package_beg_sep
2000 << "\\@ifundefined{definecolor}{color}{}"
2003 //\@ifundefined{showcaptionsetup}{}{%
2004 // \PassOptionsToPackage{caption=false}{subfig}}
2005 // that LyX uses for subfloats
2006 } else if (t.cs() == "@ifundefined" &&
2007 arg1 == "showcaptionsetup" && arg2.empty()
2008 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
2010 } else if (!in_lyx_preamble) {
2011 h_preamble << t.asInput()
2012 << '{' << arg1 << '}'
2013 << '{' << arg2 << '}'
2014 << '{' << arg3 << '}';
2018 else if (is_known(t.cs(), known_if_commands)) {
2019 // must not parse anything in conditional code, since
2020 // LyX would output the parsed contents unconditionally
2021 if (!in_lyx_preamble)
2022 h_preamble << t.asInput();
2023 handle_if(p, in_lyx_preamble);
2026 else if (!t.cs().empty() && !in_lyx_preamble)
2027 h_preamble << '\\' << t.cs();
2030 // remove the whitespace
2033 // Force textclass if the user wanted it
2034 if (!forceclass.empty())
2035 h_textclass = forceclass;
2036 tc.setName(h_textclass);
2037 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2038 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2041 if (h_papersides.empty()) {
2044 h_papersides = ss.str();
2047 // If the CJK package is used we cannot set the document language from
2048 // the babel options. Instead, we guess which language is used most
2049 // and set this one.
2050 default_language = h_language;
2051 if (is_full_document &&
2052 (auto_packages.find("CJK") != auto_packages.end() ||
2053 auto_packages.find("CJKutf8") != auto_packages.end())) {
2055 h_language = guessLanguage(p, default_language);
2057 if (explicit_babel && h_language != default_language) {
2058 // We set the document language to a CJK language,
2059 // but babel is explicitly called in the user preamble
2060 // without options. LyX will not add the default
2061 // language to the document options if it is either
2062 // english, or no text is set as default language.
2063 // Therefore we need to add a language option explicitly.
2064 // FIXME: It would be better to remove all babel calls
2065 // from the user preamble, but this is difficult
2066 // without re-introducing bug 7861.
2067 if (h_options.empty())
2068 h_options = lyx2babel(default_language);
2070 h_options += ',' + lyx2babel(default_language);
2076 string Preamble::parseEncoding(Parser & p, string const & forceclass)
2078 TeX2LyXDocClass dummy;
2079 parse(p, forceclass, true, dummy);
2080 if (h_inputencoding != "auto" && h_inputencoding != "default")
2081 return h_inputencoding;
2086 string babel2lyx(string const & language)
2088 char const * const * where = is_known(language, known_languages);
2090 return known_coded_languages[where - known_languages];
2095 string lyx2babel(string const & language)
2097 char const * const * where = is_known(language, known_coded_languages);
2099 return known_languages[where - known_coded_languages];
2104 string Preamble::polyglossia2lyx(string const & language)
2106 char const * const * where = is_known(language, polyglossia_languages);
2108 return coded_polyglossia_languages[where - polyglossia_languages];
2113 string rgbcolor2code(string const & name)
2115 char const * const * where = is_known(name, known_basic_colors);
2117 // "red", "green" etc
2118 return known_basic_color_codes[where - known_basic_colors];
2120 // "255,0,0", "0,255,0" etc
2121 RGBColor c(RGBColorFromLaTeX(name));
2122 return X11hexname(c);