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", "azerbaijani", "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", "azerbaijani", "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 british quotes (.lyx names)
92 const char * const known_british_quotes_languages[] = {"british", "welsh", 0};
94 /// languages with cjk quotes (.lyx names)
95 const char * const known_cjk_quotes_languages[] = {"chinese-traditional",
96 "japanese", "japanese-cjk", 0};
98 /// languages with cjk-angle quotes (.lyx names)
99 const char * const known_cjkangle_quotes_languages[] = {"korean", 0};
101 /// languages with danish quotes (.lyx names)
102 const char * const known_danish_quotes_languages[] = {"danish", 0};
104 /// languages with english quotes (.lyx names)
105 const char * const known_english_quotes_languages[] = {"american", "australian",
106 "bahasa", "bahasam", "bengali", "brazilian", "canadian", "chinese-simplified", "english",
107 "esperanto", "farsi", "interlingua", "irish", "newzealand", "scottish",
108 "thai", "turkish", "vietnamese", 0};
110 /// languages with french quotes (.lyx names)
111 const char * const known_french_quotes_languages[] = {"ancientgreek",
112 "arabic_arabi", "arabic_arabtex", "asturian", "belarusian", "breton",
113 "canadien", "catalan", "french", "friulan", "galician", "italian", "occitan",
114 "piedmontese", "portuguese", "spanish", "spanish-mexico", 0};
116 /// languages with german quotes (.lyx names)
117 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
118 "czech", "estonian", "georgian", "german", "icelandic", "latvian", "lithuanian",
119 "lowersorbian", "macedonian", "naustrian", "ngerman", "romansh", "slovak", "slovene",
122 /// languages with polish quotes (.lyx names)
123 const char * const known_polish_quotes_languages[] = {"afrikaans", "bosnian", "croatian",
124 "dutch", "magyar", "polish", "romanian", "serbian", "serbian-latin", 0};
126 /// languages with russian quotes (.lyx names)
127 const char * const known_russian_quotes_languages[] = {"azerbaijani", "oldrussian",
128 "russian", "ukrainian", 0};
130 /// languages with swedish quotes (.lyx names)
131 const char * const known_swedish_quotes_languages[] = {"finnish", "swedish", 0};
133 /// languages with swiss quotes (.lyx names)
134 const char * const known_swiss_quotes_languages[] = {"albanian",
135 "armenian", "basque", "churchslavonic", "german-ch", "german-ch-old",
136 "norsk", "nynorsk", "turkmen", "ukrainian", "vietnamese", 0};
138 /// known language packages from the times before babel
139 const char * const known_old_language_packages[] = {"french", "frenchle",
140 "frenchpro", "german", "ngerman", "pmfrench", 0};
142 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
144 const char * const known_roman_font_packages[] = { "ae", "beraserif", "bookman",
145 "ccfonts", "chancery", "charter", "cmr", "cochineal", "crimson", "DejaVuSerif", "DejaVuSerifCondensed", "fourier",
146 "garamondx", "libertine", "libertineRoman", "libertine-type1", "lmodern", "mathdesign", "mathpazo",
147 "mathptmx", "MinionPro", "newcent", "noto", "noto-serif", "PTSerif", "tgbonum", "tgchorus",
148 "tgpagella", "tgschola", "tgtermes", "utopia", "xcharter", 0 };
150 const char * const known_sans_font_packages[] = { "avant", "berasans", "biolinum",
151 "biolinum-type1", "cmbr", "cmss", "DejaVuSans", "DejaVuSansCondensed", "helvet", "iwona", "iwonac", "iwonal", "iwonalc",
152 "kurier", "kurierc", "kurierl", "kurierlc", "lmss", "noto", "noto-sans", "PTSans",
153 "tgadventor", "tgheros", "uop", 0 };
155 const char * const known_typewriter_font_packages[] = { "beramono", "cmtl", "cmtt",
156 "courier", "DejaVuSansMono", "lmtt", "luximono", "fourier", "libertineMono", "libertineMono-type1", "lmodern",
157 "mathpazo", "mathptmx", "newcent", "noto", "noto-mono", "PTMono", "tgcursor", "txtt", 0 };
159 const char * const known_math_font_packages[] = { "eulervm", "newtxmath", 0};
161 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
162 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
163 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
164 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
165 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
167 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
168 "executivepaper", "legalpaper", "letterpaper", 0};
170 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
171 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
173 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
174 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
177 /// commands that can start an \if...\else...\endif sequence
178 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
179 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
180 "ifsidecap", "ifupgreek", 0};
182 const char * const known_basic_colors[] = {"black", "blue", "brown", "cyan",
183 "darkgray", "gray", "green", "lightgray", "lime", "magenta", "orange", "olive",
184 "pink", "purple", "red", "teal", "violet", "white", "yellow", 0};
186 const char * const known_basic_color_codes[] = {"#000000", "#0000ff", "#964B00", "#00ffff",
187 "#a9a9a9", "#808080", "#00ff00", "#d3d3d3", "#bfff00", "#ff00ff", "#ff7f00", "#808000",
188 "#ffc0cb", "#800080", "#ff0000", "#008080", "#8f00ff", "#ffffff", "#ffff00", 0};
190 /// conditional commands with three arguments like \@ifundefined{}{}{}
191 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
195 * Known file extensions for TeX files as used by \\includeonly
197 char const * const known_tex_extensions[] = {"tex", 0};
199 /// packages that work only in xetex
200 /// polyglossia is handled separately
201 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
202 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
203 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
205 /// packages that are automatically skipped if loaded by LyX
206 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
207 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color",
208 "float", "fontspec", "framed", "graphicx", "hhline", "ifthen", "longtable",
209 "makeidx", "minted", "multirow", "nomencl", "pdfpages", "prettyref", "refstyle",
210 "rotating", "rotfloat", "splitidx", "setspace", "subscript", "tabularx","textcomp", "tipa",
211 "tipx", "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xcolor", "xltabular",
214 // codes used to remove packages that are loaded automatically by LyX.
215 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
216 const char package_beg_sep = '\001';
217 const char package_mid_sep = '\002';
218 const char package_end_sep = '\003';
221 // returns true if at least one of the options in what has been found
222 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
228 // the last language option is the document language (for babel and LyX)
229 // the last size option is the document font size
230 vector<string>::iterator it;
231 vector<string>::iterator position = opts.begin();
232 for (; *what; ++what) {
233 it = find(opts.begin(), opts.end(), *what);
234 if (it != opts.end()) {
235 if (it >= position) {
246 void delete_opt(vector<string> & opts, char const * const * what)
251 // remove found options from the list
252 // do this after handle_opt to avoid potential memory leaks
253 vector<string>::iterator it;
254 for (; *what; ++what) {
255 it = find(opts.begin(), opts.end(), *what);
256 if (it != opts.end())
263 * Split a package options string (keyval format) into a vector.
265 * authorformat=smallcaps,
267 * titleformat=colonsep,
268 * bibformat={tabular,ibidem,numbered}
270 vector<string> split_options(string const & input)
272 vector<string> options;
276 Token const & t = p.get_token();
277 if (t.asInput() == ",") {
278 options.push_back(trimSpaceAndEol(option));
280 } else if (t.asInput() == "=") {
283 if (p.next_token().asInput() == "{")
284 option += '{' + p.getArg('{', '}') + '}';
285 } else if (t.cat() != catSpace && t.cat() != catComment)
286 option += t.asInput();
290 options.push_back(trimSpaceAndEol(option));
297 * Retrieve a keyval option "name={value with=sign}" named \p name from
298 * \p options and return the value.
299 * The found option is also removed from \p options.
301 string process_keyval_opt(vector<string> & options, string name)
303 for (size_t i = 0; i < options.size(); ++i) {
304 vector<string> option;
305 split(options[i], option, '=');
306 if (option.size() < 2)
308 if (option[0] == name) {
309 options.erase(options.begin() + i);
310 option.erase(option.begin());
311 return join(option, "=");
317 } // anonymous namespace
321 * known polyglossia language names (including variants)
322 * FIXME: support spelling=old for german variants (german vs. ngerman LyX names etc)
324 const char * const Preamble::polyglossia_languages[] = {
325 "albanian", "american", "amharic", "ancient", "arabic", "armenian", "asturian", "australian",
326 "bahasai", "bahasam", "basque", "bengali", "brazil", "brazilian", "breton", "british", "bulgarian",
327 "catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
328 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
329 "galician", "greek", "monotonic", "hebrew", "hindi",
330 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean",
331 "lao", "latin", "latvian", "lithuanian", "lsorbian", "magyar", "malayalam", "marathi",
332 "austrian", "newzealand", "german", "norsk", "nynorsk", "occitan", "oldrussian",
333 "piedmontese", "polish", "polytonic", "portuges", "romanian", "romansh", "russian",
334 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovenian", "spanish", "swedish", "syriac",
335 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
336 "ukrainian", "urdu", "usorbian", "vietnamese", "welsh", 0};
337 // not yet supported by LyX: "korean", "nko"
340 * the same as polyglossia_languages with .lyx names
341 * please keep this in sync with polyglossia_languages line by line!
343 const char * const Preamble::coded_polyglossia_languages[] = {
344 "albanian", "american", "amharic", "ancientgreek", "arabic_arabi", "armenian", "asturian", "australian",
345 "bahasa", "bahasam", "basque", "bengali", "brazilian", "brazilian", "breton", "british", "bulgarian",
346 "catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
347 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
348 "galician", "greek", "greek", "hebrew", "hindi",
349 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean",
350 "lao", "latin", "latvian", "lithuanian", "lowersorbian", "magyar", "malayalam", "marathi",
351 "naustrian","newzealand", "ngerman", "norsk", "nynorsk", "occitan", "oldrussian",
352 "piedmontese", "polish", "polutonikogreek", "portuges", "romanian", "romansh", "russian",
353 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovene", "spanish", "swedish", "syriac",
354 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
355 "ukrainian", "urdu", "uppersorbian", "vietnamese", "welsh", 0};
356 // not yet supported by LyX: "korean-polyglossia", "nko"
359 bool Preamble::usePolyglossia() const
361 return h_use_non_tex_fonts && h_language_package == "default";
365 bool Preamble::indentParagraphs() const
367 return h_paragraph_separation == "indent";
371 bool Preamble::isPackageUsed(string const & package) const
373 return used_packages.find(package) != used_packages.end();
377 bool Preamble::isPackageAutoLoaded(string const & package) const
379 return auto_packages.find(package) != auto_packages.end();
383 vector<string> Preamble::getPackageOptions(string const & package) const
385 map<string, vector<string> >::const_iterator it = used_packages.find(package);
386 if (it != used_packages.end())
388 return vector<string>();
392 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
394 auto_packages.insert(package);
398 void Preamble::addModule(string const & module)
400 for (auto const & m : used_modules) {
404 used_modules.push_back(module);
408 void Preamble::suppressDate(bool suppress)
411 h_suppress_date = "true";
413 h_suppress_date = "false";
417 void Preamble::registerAuthor(std::string const & name)
419 Author author(from_utf8(name), empty_docstring());
420 author.setUsed(true);
421 authors_.record(author);
422 h_tracking_changes = "true";
423 h_output_changes = "true";
427 Author const & Preamble::getAuthor(std::string const & name) const
429 Author author(from_utf8(name), empty_docstring());
430 for (AuthorList::Authors::const_iterator it = authors_.begin();
431 it != authors_.end(); ++it)
434 static Author const dummy;
439 int Preamble::getSpecialTableColumnArguments(char c) const
441 map<char, int>::const_iterator it = special_columns_.find(c);
442 if (it == special_columns_.end())
448 void Preamble::add_package(string const & name, vector<string> & options)
450 // every package inherits the global options
451 if (used_packages.find(name) == used_packages.end())
452 used_packages[name] = split_options(h_options);
454 // Insert options passed via PassOptionsToPackage
455 for (auto const & p : extra_package_options_) {
456 if (p.first == name) {
457 vector<string> eo = getVectorFromString(p.second);
458 for (auto const & eoi : eo)
459 options.push_back(eoi);
463 vector<string> & v = used_packages[name];
464 v.insert(v.end(), options.begin(), options.end());
465 if (name == "jurabib") {
466 // Don't output the order argument (see the cite command
467 // handling code in text.cpp).
468 vector<string>::iterator end =
469 remove(options.begin(), options.end(), "natbiborder");
470 end = remove(options.begin(), end, "jurabiborder");
471 options.erase(end, options.end());
478 // Given is a string like "scaled=0.9" or "Scale=0.9", return 0.9 * 100
479 bool scale_as_percentage(string const & scale, string & percentage)
481 string::size_type pos = scale.find('=');
482 if (pos != string::npos) {
483 string value = scale.substr(pos + 1);
484 if (isStrDbl(value)) {
485 percentage = convert<string>(
486 static_cast<int>(100 * convert<double>(value)));
494 string remove_braces(string const & value)
498 if (value[0] == '{' && value[value.length()-1] == '}')
499 return value.substr(1, value.length()-2);
503 } // anonymous namespace
506 Preamble::Preamble() : one_language(true), explicit_babel(false),
507 title_layout_found(false), index_number(0), h_font_cjk_set(false),
508 h_use_microtype("false")
512 h_biblio_style = "plain";
513 h_bibtex_command = "default";
514 h_cite_engine = "basic";
515 h_cite_engine_type = "default";
517 h_defskip = "medskip";
518 h_dynamic_quotes = false;
521 h_fontencoding = "default";
522 h_font_roman[0] = "default";
523 h_font_roman[1] = "default";
524 h_font_sans[0] = "default";
525 h_font_sans[1] = "default";
526 h_font_typewriter[0] = "default";
527 h_font_typewriter[1] = "default";
528 h_font_math[0] = "auto";
529 h_font_math[1] = "auto";
530 h_font_default_family = "default";
531 h_use_non_tex_fonts = false;
533 h_font_osf = "false";
534 h_font_sf_scale[0] = "100";
535 h_font_sf_scale[1] = "100";
536 h_font_tt_scale[0] = "100";
537 h_font_tt_scale[1] = "100";
539 h_is_mathindent = "0";
540 h_math_numbering_side = "default";
541 h_graphics = "default";
542 h_default_output_format = "default";
543 h_html_be_strict = "false";
544 h_html_css_as_file = "0";
545 h_html_math_output = "0";
546 h_index[0] = "Index";
547 h_index_command = "default";
548 h_inputencoding = "auto-legacy";
549 h_justification = "true";
550 h_language = "english";
551 h_language_package = "none";
553 h_maintain_unincluded_children = "false";
557 h_output_changes = "false";
559 //h_output_sync_macro
560 h_papercolumns = "1";
561 h_paperfontsize = "default";
562 h_paperorientation = "portrait";
563 h_paperpagestyle = "default";
565 h_papersize = "default";
566 h_paragraph_indentation = "default";
567 h_paragraph_separation = "indent";
572 h_pdf_bookmarks = "0";
573 h_pdf_bookmarksnumbered = "0";
574 h_pdf_bookmarksopen = "0";
575 h_pdf_bookmarksopenlevel = "1";
576 h_pdf_breaklinks = "0";
577 h_pdf_pdfborder = "0";
578 h_pdf_colorlinks = "0";
579 h_pdf_backref = "section";
580 h_pdf_pdfusetitle = "0";
582 //h_pdf_quoted_options;
583 h_quotes_style = "english";
585 h_shortcut[0] = "idx";
586 h_spacing = "single";
587 h_save_transient_properties = "true";
588 h_suppress_date = "false";
589 h_textclass = "article";
591 h_tracking_changes = "false";
592 h_use_bibtopic = "false";
593 h_use_dash_ligatures = "true";
594 h_use_indices = "false";
595 h_use_geometry = "false";
596 h_use_default_options = "false";
597 h_use_hyperref = "false";
598 h_use_microtype = "false";
599 h_use_refstyle = false;
600 h_use_minted = false;
601 h_use_packages["amsmath"] = "1";
602 h_use_packages["amssymb"] = "0";
603 h_use_packages["cancel"] = "0";
604 h_use_packages["esint"] = "1";
605 h_use_packages["mhchem"] = "0";
606 h_use_packages["mathdots"] = "0";
607 h_use_packages["mathtools"] = "0";
608 h_use_packages["stackrel"] = "0";
609 h_use_packages["stmaryrd"] = "0";
610 h_use_packages["undertilde"] = "0";
614 void Preamble::handle_hyperref(vector<string> & options)
616 // FIXME swallow inputencoding changes that might surround the
617 // hyperref setup if it was written by LyX
618 h_use_hyperref = "true";
619 // swallow "unicode=true", since LyX does always write that
620 vector<string>::iterator it =
621 find(options.begin(), options.end(), "unicode=true");
622 if (it != options.end())
624 it = find(options.begin(), options.end(), "pdfusetitle");
625 if (it != options.end()) {
626 h_pdf_pdfusetitle = "1";
629 string bookmarks = process_keyval_opt(options, "bookmarks");
630 if (bookmarks == "true")
631 h_pdf_bookmarks = "1";
632 else if (bookmarks == "false")
633 h_pdf_bookmarks = "0";
634 if (h_pdf_bookmarks == "1") {
635 string bookmarksnumbered =
636 process_keyval_opt(options, "bookmarksnumbered");
637 if (bookmarksnumbered == "true")
638 h_pdf_bookmarksnumbered = "1";
639 else if (bookmarksnumbered == "false")
640 h_pdf_bookmarksnumbered = "0";
641 string bookmarksopen =
642 process_keyval_opt(options, "bookmarksopen");
643 if (bookmarksopen == "true")
644 h_pdf_bookmarksopen = "1";
645 else if (bookmarksopen == "false")
646 h_pdf_bookmarksopen = "0";
647 if (h_pdf_bookmarksopen == "1") {
648 string bookmarksopenlevel =
649 process_keyval_opt(options, "bookmarksopenlevel");
650 if (!bookmarksopenlevel.empty())
651 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
654 string breaklinks = process_keyval_opt(options, "breaklinks");
655 if (breaklinks == "true")
656 h_pdf_breaklinks = "1";
657 else if (breaklinks == "false")
658 h_pdf_breaklinks = "0";
659 string pdfborder = process_keyval_opt(options, "pdfborder");
660 if (pdfborder == "{0 0 0}")
661 h_pdf_pdfborder = "1";
662 else if (pdfborder == "{0 0 1}")
663 h_pdf_pdfborder = "0";
664 string backref = process_keyval_opt(options, "backref");
665 if (!backref.empty())
666 h_pdf_backref = backref;
667 string colorlinks = process_keyval_opt(options, "colorlinks");
668 if (colorlinks == "true")
669 h_pdf_colorlinks = "1";
670 else if (colorlinks == "false")
671 h_pdf_colorlinks = "0";
672 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
673 if (!pdfpagemode.empty())
674 h_pdf_pagemode = pdfpagemode;
675 string pdftitle = process_keyval_opt(options, "pdftitle");
676 if (!pdftitle.empty()) {
677 h_pdf_title = remove_braces(pdftitle);
679 string pdfauthor = process_keyval_opt(options, "pdfauthor");
680 if (!pdfauthor.empty()) {
681 h_pdf_author = remove_braces(pdfauthor);
683 string pdfsubject = process_keyval_opt(options, "pdfsubject");
684 if (!pdfsubject.empty())
685 h_pdf_subject = remove_braces(pdfsubject);
686 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
687 if (!pdfkeywords.empty())
688 h_pdf_keywords = remove_braces(pdfkeywords);
689 if (!options.empty()) {
690 if (!h_pdf_quoted_options.empty())
691 h_pdf_quoted_options += ',';
692 h_pdf_quoted_options += join(options, ",");
698 void Preamble::handle_geometry(vector<string> & options)
700 h_use_geometry = "true";
701 vector<string>::iterator it;
703 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
704 h_paperorientation = "landscape";
708 // keyval version: "paper=letter"
709 string paper = process_keyval_opt(options, "paper");
711 h_papersize = paper + "paper";
712 // alternative version: "letterpaper"
713 handle_opt(options, known_paper_sizes, h_papersize);
714 delete_opt(options, known_paper_sizes);
716 char const * const * margin = known_paper_margins;
717 for (; *margin; ++margin) {
718 string value = process_keyval_opt(options, *margin);
719 if (!value.empty()) {
720 int k = margin - known_paper_margins;
721 string name = known_coded_paper_margins[k];
722 h_margins += '\\' + name + ' ' + value + '\n';
728 void Preamble::handle_package(Parser &p, string const & name,
729 string const & opts, bool in_lyx_preamble,
732 vector<string> options = split_options(opts);
733 add_package(name, options);
735 if (is_known(name, known_xetex_packages)) {
737 h_use_non_tex_fonts = true;
738 registerAutomaticallyLoadedPackage("fontspec");
739 if (h_inputencoding == "auto-legacy")
740 p.setEncoding("UTF-8");
744 if (is_known(name, known_roman_font_packages))
745 h_font_roman[0] = name;
747 if (name == "fourier") {
748 h_font_roman[0] = "utopia";
749 // when font uses real small capitals
750 if (opts == "expert")
754 if (name == "garamondx") {
755 h_font_roman[0] = "garamondx";
760 if (name == "libertine") {
761 h_font_roman[0] = "libertine";
762 // this automatically invokes biolinum
763 h_font_sans[0] = "biolinum";
764 // as well as libertineMono
765 h_font_typewriter[0] = "libertine-mono";
768 else if (opts == "lining")
769 h_font_osf = "false";
772 if (name == "libertineRoman" || name == "libertine-type1") {
773 h_font_roman[0] = "libertine";
774 // NOTE: contrary to libertine.sty, libertineRoman
775 // and libertine-type1 do not automatically invoke
776 // biolinum and libertineMono
777 if (opts == "lining")
778 h_font_osf = "false";
779 else if (opts == "osf")
783 if (name == "MinionPro") {
784 h_font_roman[0] = "minionpro";
785 if (opts.find("lf") != string::npos)
786 h_font_osf = "false";
789 if (opts.find("onlytext") != string::npos)
790 h_font_math[0] = "default";
792 h_font_math[0] = "auto";
795 if (name == "mathdesign") {
796 if (opts.find("charter") != string::npos)
797 h_font_roman[0] = "md-charter";
798 if (opts.find("garamond") != string::npos)
799 h_font_roman[0] = "md-garamond";
800 if (opts.find("utopia") != string::npos)
801 h_font_roman[0] = "md-utopia";
802 if (opts.find("expert") != string::npos) {
808 else if (name == "mathpazo")
809 h_font_roman[0] = "palatino";
811 else if (name == "mathptmx")
812 h_font_roman[0] = "times";
814 if (name == "crimson")
815 h_font_roman[0] = "cochineal";
817 if (name == "cochineal") {
818 h_font_roman[0] = "cochineal";
819 // cochineal can have several options, e.g. [proportional,osf]
820 string::size_type pos = opts.find("osf");
821 if (pos != string::npos)
825 if (name == "noto") {
826 // noto can have several options
828 h_font_roman[0] = "NotoSerif-TLF";
829 string::size_type pos = opts.find("rm");
830 if (pos != string::npos)
831 h_font_roman[0] = "NotoSerif-TLF";
832 pos = opts.find("sf");
833 if (pos != string::npos)
834 h_font_sans[0] = "NotoSans-TLF";
835 pos = opts.find("nott");
836 if (pos != string::npos) {
837 h_font_roman[0] = "NotoSerif-TLF";
838 h_font_sans[0] = "NotoSans-TLF";
840 // noto as typewriter is handled in handling of \ttdefault
841 // special cases are handled in handling of \rmdefault and \sfdefault
844 if (name == "paratype") {
845 // in this case all fonts are ParaType
846 h_font_roman[0] = "PTSerif-TLF";
847 h_font_sans[0] = "default";
848 h_font_typewriter[0] = "default";
851 if (name == "PTSerif")
852 h_font_roman[0] = "PTSerif-TLF";
854 if (name == "XCharter") {
855 h_font_roman[0] = "xcharter";
860 if (name == "plex-serif") {
862 h_font_roman[0] = "IBMPlexSerif";
863 else if (opts.find("thin") != string::npos)
864 h_font_roman[0] = "IBMPlexSerifThin";
865 else if (opts.find("extralight") != string::npos)
866 h_font_roman[0] = "IBMPlexSerifExtraLight";
867 else if (opts.find("light") != string::npos)
868 h_font_roman[0] = "IBMPlexSerifLight";
869 else if (opts.find("semibold") != string::npos)
870 h_font_roman[0] = "IBMPlexSerifSemibold";
872 if (name == "noto-serif") {
873 h_font_roman[0] = "NotoSerifRegular";
875 if (opts.find("thin") != string::npos)
876 h_font_roman[0] = "NotoSerifThin";
877 else if (opts.find("medium") != string::npos)
878 h_font_roman[0] = "NotoSerifMedium";
879 else if (opts.find("extralight") != string::npos)
880 h_font_roman[0] = "NotoSerifExtralight";
881 else if (opts.find("light") != string::npos)
882 h_font_roman[0] = "NotoSerifLight";
887 if (is_known(name, known_sans_font_packages)) {
888 h_font_sans[0] = name;
889 if (options.size() >= 1) {
890 if (scale_as_percentage(opts, h_font_sf_scale[0]))
895 if (name == "biolinum" || name == "biolinum-type1") {
896 h_font_sans[0] = "biolinum";
897 // biolinum can have several options, e.g. [osf,scaled=0.97]
898 string::size_type pos = opts.find("osf");
899 if (pos != string::npos)
903 if (name == "PTSans") {
904 h_font_sans[0] = "PTSans-TLF";
905 if (options.size() >= 1) {
906 if (scale_as_percentage(opts, h_font_sf_scale[0]))
911 if (name == "plex-sans") {
912 if (opts.find("condensed") != string::npos)
913 h_font_sans[0] = "IBMPlexSansCondensed";
914 else if (opts.find("thin") != string::npos)
915 h_font_sans[0] = "IBMPlexSansThin";
916 else if (opts.find("extralight") != string::npos)
917 h_font_sans[0] = "IBMPlexSansExtraLight";
918 else if (opts.find("light") != string::npos)
919 h_font_sans[0] = "IBMPlexSansLight";
920 else if (opts.find("semibold") != string::npos)
921 h_font_sans[0] = "IBMPlexSansSemibold";
923 h_font_sans[0] = "IBMPlexSans";
924 // check if the option contains scaling, if yes, extract it
925 string::size_type pos = opts.find("scale");
926 if (pos != string::npos) {
928 string::size_type i = opts.find(',', pos);
929 if (i == string::npos)
930 scale_as_percentage(opts.substr(pos + 1), scale);
932 scale_as_percentage(opts.substr(pos, i - pos), scale);
934 h_font_sf_scale[1] = scale;
937 if (name == "noto-sans") {
938 h_font_sans[0] = "NotoSansRegular";
940 if (opts.find("medium") != string::npos)
941 h_font_sans[0] = "NotoSansMedium";
942 else if (opts.find("thin") != string::npos)
943 h_font_sans[0] = "NotoSansThin";
944 else if (opts.find("extralight") != string::npos)
945 h_font_sans[0] = "NotoSansExtralight";
946 else if (opts.find("light") != string::npos)
947 h_font_sans[0] = "NotoSansLight";
953 if (is_known(name, known_typewriter_font_packages)) {
954 // fourier can be set as roman font _only_
955 // fourier as typewriter is handled in handling of \ttdefault
956 if (name != "fourier") {
957 h_font_typewriter[0] = name;
958 if (options.size() >= 1) {
959 if (scale_as_percentage(opts, h_font_tt_scale[0]))
965 if (name == "libertineMono" || name == "libertineMono-type1")
966 h_font_typewriter[0] = "libertine-mono";
968 if (name == "PTMono") {
969 h_font_typewriter[0] = "PTMono-TLF";
970 if (options.size() >= 1) {
971 if (scale_as_percentage(opts, h_font_tt_scale[0]))
976 if (name == "plex-mono") {
977 if (opts.find("thin") != string::npos)
978 h_font_typewriter[0] = "IBMPlexMonoThin";
979 else if (opts.find("extralight") != string::npos)
980 h_font_typewriter[0] = "IBMPlexMonoExtraLight";
981 else if (opts.find("light") != string::npos)
982 h_font_typewriter[0] = "IBMPlexMonoLight";
983 else if (opts.find("semibold") != string::npos)
984 h_font_typewriter[0] = "IBMPlexMonoSemibold";
986 h_font_typewriter[0] = "IBMPlexMono";
987 // check if the option contains scaling, if yes, extract it
988 string::size_type pos = opts.find("scale");
989 if (pos != string::npos) {
991 string::size_type i = opts.find(',', pos);
992 if (i == string::npos)
993 scale_as_percentage(opts.substr(pos + 1), scale);
995 scale_as_percentage(opts.substr(pos, i - pos), scale);
997 h_font_tt_scale[1] = scale;
1001 if (name == "noto-mono") {
1002 h_font_typewriter[0] = "NotoMonoRegular";
1005 // font uses old-style figure
1007 h_font_osf = "true";
1010 if (is_known(name, known_math_font_packages))
1011 h_font_math[0] = name;
1013 if (name == "newtxmath") {
1015 h_font_math[0] = "newtxmath";
1016 else if (opts == "garamondx")
1017 h_font_math[0] = "garamondx-ntxm";
1018 else if (opts == "libertine")
1019 h_font_math[0] = "libertine-ntxm";
1020 else if (opts == "minion")
1021 h_font_math[0] = "minion-ntxm";
1022 else if (opts == "cochineal")
1023 h_font_math[0] = "cochineal-ntxm";
1026 if (name == "iwona")
1028 h_font_math[0] = "iwona-math";
1030 if (name == "kurier")
1032 h_font_math[0] = "kurier-math";
1034 // after the detection and handling of special cases, we can remove the
1035 // fonts, otherwise they would appear in the preamble, see bug #7856
1036 if (is_known(name, known_roman_font_packages) || is_known(name, known_sans_font_packages)
1037 || is_known(name, known_typewriter_font_packages) || is_known(name, known_math_font_packages))
1039 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
1040 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
1041 name == "esint" || name == "mhchem" || name == "mathdots" ||
1042 name == "mathtools" || name == "stackrel" ||
1043 name == "stmaryrd" || name == "undertilde") {
1044 h_use_packages[name] = "2";
1045 registerAutomaticallyLoadedPackage(name);
1048 else if (name == "babel") {
1049 h_language_package = "default";
1050 // One might think we would have to do nothing if babel is loaded
1051 // without any options to prevent pollution of the preamble with this
1052 // babel call in every roundtrip.
1053 // But the user could have defined babel-specific things afterwards. So
1054 // we need to keep it in the preamble to prevent cases like bug #7861.
1055 if (!opts.empty()) {
1056 // check if more than one option was used - used later for inputenc
1057 if (options.begin() != options.end() - 1)
1058 one_language = false;
1059 // babel takes the last language of the option of its \usepackage
1060 // call as document language. If there is no such language option, the
1061 // last language in the documentclass options is used.
1062 handle_opt(options, known_languages, h_language);
1063 // translate the babel name to a LyX name
1064 h_language = babel2lyx(h_language);
1065 if (h_language == "japanese") {
1066 // For Japanese, the encoding isn't indicated in the source
1067 // file, and there's really not much we can do. We could
1068 // 1) offer a list of possible encodings to choose from, or
1069 // 2) determine the encoding of the file by inspecting it.
1070 // For the time being, we leave the encoding alone so that
1071 // we don't get iconv errors when making a wrong guess, and
1072 // we will output a note at the top of the document
1073 // explaining what to do.
1074 Encoding const * const enc = encodings.fromIconvName(
1075 p.getEncoding(), Encoding::japanese, false);
1077 h_inputencoding = enc->name();
1078 is_nonCJKJapanese = true;
1079 // in this case babel can be removed from the preamble
1080 registerAutomaticallyLoadedPackage("babel");
1082 // If babel is called with options, LyX puts them by default into the
1083 // document class options. This works for most languages, except
1084 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
1085 // perhaps in future others.
1086 // Therefore keep the babel call as it is as the user might have
1088 string const babelcall = "\\usepackage[" + opts + "]{babel}\n";
1089 if (!contains(h_preamble.str(), babelcall))
1090 h_preamble << babelcall;
1092 delete_opt(options, known_languages);
1094 if (!contains(h_preamble.str(), "\\usepackage{babel}\n"))
1095 h_preamble << "\\usepackage{babel}\n";
1096 explicit_babel = true;
1100 else if (name == "polyglossia") {
1101 h_language_package = "default";
1102 h_default_output_format = "pdf4";
1103 h_use_non_tex_fonts = true;
1105 registerAutomaticallyLoadedPackage("xunicode");
1106 if (h_inputencoding == "auto-legacy")
1107 p.setEncoding("UTF-8");
1110 else if (name == "CJK") {
1111 // set the encoding to "auto-legacy" because it might be set to "auto-legacy-plain" by the babel handling
1112 // and this would not be correct for CJK
1113 if (h_inputencoding == "auto-legacy-plain")
1114 h_inputencoding = "auto-legacy";
1115 registerAutomaticallyLoadedPackage("CJK");
1118 else if (name == "CJKutf8") {
1119 h_inputencoding = "utf8-cjk";
1120 p.setEncoding("UTF-8");
1121 registerAutomaticallyLoadedPackage("CJKutf8");
1124 else if (name == "fontenc") {
1125 h_fontencoding = getStringFromVector(options, ",");
1129 else if (name == "inputenc" || name == "luainputenc") {
1130 // h_inputencoding is only set when there is not more than one
1131 // inputenc option because otherwise h_inputencoding must be
1132 // set to "auto-legacy" (the default encodings of the document's languages)
1133 // Therefore check that exactly one option is passed to inputenc.
1134 // It is also only set when there is not more than one babel
1136 if (!options.empty()) {
1137 string const encoding = options.back();
1138 Encoding const * const enc = encodings.fromLaTeXName(
1139 encoding, Encoding::inputenc, true);
1141 if (!detectEncoding)
1142 cerr << "Unknown encoding " << encoding
1143 << ". Ignoring." << std::endl;
1145 if (!enc->unsafe() && options.size() == 1 && one_language == true)
1146 h_inputencoding = enc->name();
1147 p.setEncoding(enc->iconvName());
1153 else if (name == "srcltx") {
1154 h_output_sync = "1";
1155 if (!opts.empty()) {
1156 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
1159 h_output_sync_macro = "\\usepackage{srcltx}";
1162 else if (is_known(name, known_old_language_packages)) {
1163 // known language packages from the times before babel
1164 // if they are found and not also babel, they will be used as
1165 // custom language package
1166 h_language_package = "\\usepackage{" + name + "}";
1169 else if (name == "lyxskak") {
1170 // ignore this and its options
1171 const char * const o[] = {"ps", "mover", 0};
1172 delete_opt(options, o);
1175 else if (is_known(name, known_lyx_packages) && options.empty()) {
1176 if (name == "splitidx")
1177 h_use_indices = "true";
1178 else if (name == "minted")
1179 h_use_minted = true;
1180 else if (name == "refstyle")
1181 h_use_refstyle = true;
1182 else if (name == "prettyref")
1183 h_use_refstyle = false;
1184 if (!in_lyx_preamble) {
1185 h_preamble << package_beg_sep << name
1186 << package_mid_sep << "\\usepackage{"
1188 if (p.next_token().cat() == catNewline ||
1189 (p.next_token().cat() == catSpace &&
1190 p.next_next_token().cat() == catNewline))
1192 h_preamble << package_end_sep;
1196 else if (name == "geometry")
1197 handle_geometry(options);
1199 else if (name == "subfig")
1200 ; // ignore this FIXME: Use the package separator mechanism instead
1202 else if (char const * const * where = is_known(name, known_languages))
1203 h_language = known_coded_languages[where - known_languages];
1205 else if (name == "natbib") {
1206 h_biblio_style = "plainnat";
1207 h_cite_engine = "natbib";
1208 h_cite_engine_type = "authoryear";
1209 vector<string>::iterator it =
1210 find(options.begin(), options.end(), "authoryear");
1211 if (it != options.end())
1214 it = find(options.begin(), options.end(), "numbers");
1215 if (it != options.end()) {
1216 h_cite_engine_type = "numerical";
1220 if (!options.empty())
1221 h_biblio_options = join(options, ",");
1224 else if (name == "biblatex") {
1225 h_biblio_style = "plainnat";
1226 h_cite_engine = "biblatex";
1227 h_cite_engine_type = "authoryear";
1229 vector<string>::iterator it =
1230 find(options.begin(), options.end(), "natbib");
1231 if (it != options.end()) {
1233 h_cite_engine = "biblatex-natbib";
1235 opt = process_keyval_opt(options, "natbib");
1237 h_cite_engine = "biblatex-natbib";
1239 opt = process_keyval_opt(options, "style");
1241 h_biblatex_citestyle = opt;
1242 h_biblatex_bibstyle = opt;
1244 opt = process_keyval_opt(options, "citestyle");
1246 h_biblatex_citestyle = opt;
1247 opt = process_keyval_opt(options, "bibstyle");
1249 h_biblatex_bibstyle = opt;
1251 opt = process_keyval_opt(options, "refsection");
1253 if (opt == "none" || opt == "part"
1254 || opt == "chapter" || opt == "section"
1255 || opt == "subsection")
1258 cerr << "Ignoring unkown refesection value '"
1261 opt = process_keyval_opt(options, "bibencoding");
1264 if (!options.empty()) {
1265 h_biblio_options = join(options, ",");
1270 else if (name == "jurabib") {
1271 h_biblio_style = "jurabib";
1272 h_cite_engine = "jurabib";
1273 h_cite_engine_type = "authoryear";
1274 if (!options.empty())
1275 h_biblio_options = join(options, ",");
1278 else if (name == "bibtopic")
1279 h_use_bibtopic = "true";
1281 else if (name == "chapterbib")
1282 h_multibib = "child";
1284 else if (name == "hyperref")
1285 handle_hyperref(options);
1287 else if (name == "algorithm2e") {
1288 // Load "algorithm2e" module
1289 addModule("algorithm2e");
1290 // Add the package options to the global document options
1291 if (!options.empty()) {
1292 if (h_options.empty())
1293 h_options = join(options, ",");
1295 h_options += ',' + join(options, ",");
1298 else if (name == "microtype") {
1299 //we internally support only microtype without params
1300 if (options.empty())
1301 h_use_microtype = "true";
1303 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1306 else if (!in_lyx_preamble) {
1307 if (options.empty())
1308 h_preamble << "\\usepackage{" << name << '}';
1310 h_preamble << "\\usepackage[" << opts << "]{"
1314 if (p.next_token().cat() == catNewline ||
1315 (p.next_token().cat() == catSpace &&
1316 p.next_next_token().cat() == catNewline))
1320 // We need to do something with the options...
1321 if (!options.empty() && !detectEncoding)
1322 cerr << "Ignoring options '" << join(options, ",")
1323 << "' of package " << name << '.' << endl;
1325 // remove the whitespace
1330 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1333 Token t = p.get_token();
1334 if (t.cat() == catEscape &&
1335 is_known(t.cs(), known_if_commands))
1336 handle_if(p, in_lyx_preamble);
1338 if (!in_lyx_preamble)
1339 h_preamble << t.asInput();
1340 if (t.cat() == catEscape && t.cs() == "fi")
1347 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1349 if (contains(h_float_placement, "H"))
1350 registerAutomaticallyLoadedPackage("float");
1351 if (h_spacing != "single" && h_spacing != "default")
1352 registerAutomaticallyLoadedPackage("setspace");
1353 if (h_use_packages["amsmath"] == "2") {
1354 // amsbsy and amstext are already provided by amsmath
1355 registerAutomaticallyLoadedPackage("amsbsy");
1356 registerAutomaticallyLoadedPackage("amstext");
1359 // output the LyX file settings
1360 // Important: Keep the version formatting in sync with LyX and
1361 // lyx2lyx (bug 7951)
1362 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1363 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1364 << lyx_version_minor << '\n'
1365 << "\\lyxformat " << LYX_FORMAT << '\n'
1366 << "\\begin_document\n"
1367 << "\\begin_header\n"
1368 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1369 << "\\origin " << origin << "\n"
1370 << "\\textclass " << h_textclass << "\n";
1371 string const raw = subdoc ? empty_string() : h_preamble.str();
1373 os << "\\begin_preamble\n";
1374 for (string::size_type i = 0; i < raw.size(); ++i) {
1375 if (raw[i] == package_beg_sep) {
1376 // Here follows some package loading code that
1377 // must be skipped if the package is loaded
1379 string::size_type j = raw.find(package_mid_sep, i);
1380 if (j == string::npos)
1382 string::size_type k = raw.find(package_end_sep, j);
1383 if (k == string::npos)
1385 string const package = raw.substr(i + 1, j - i - 1);
1386 string const replacement = raw.substr(j + 1, k - j - 1);
1387 if (auto_packages.find(package) == auto_packages.end())
1393 os << "\n\\end_preamble\n";
1395 if (!h_options.empty())
1396 os << "\\options " << h_options << "\n";
1397 os << "\\use_default_options " << h_use_default_options << "\n";
1398 if (!used_modules.empty()) {
1399 os << "\\begin_modules\n";
1400 vector<string>::const_iterator const end = used_modules.end();
1401 vector<string>::const_iterator it = used_modules.begin();
1402 for (; it != end; ++it)
1404 os << "\\end_modules\n";
1406 if (!h_includeonlys.empty()) {
1407 os << "\\begin_includeonly\n";
1408 for (auto const & iofile : h_includeonlys)
1409 os << iofile << '\n';
1410 os << "\\end_includeonly\n";
1412 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1413 << "\\language " << h_language << "\n"
1414 << "\\language_package " << h_language_package << "\n"
1415 << "\\inputencoding " << h_inputencoding << "\n"
1416 << "\\fontencoding " << h_fontencoding << "\n"
1417 << "\\font_roman \"" << h_font_roman[0]
1418 << "\" \"" << h_font_roman[1] << "\"\n"
1419 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1420 << "\\font_typewriter \"" << h_font_typewriter[0]
1421 << "\" \"" << h_font_typewriter[1] << "\"\n"
1422 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1423 << "\\font_default_family " << h_font_default_family << "\n"
1424 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1425 << "\\font_sc " << h_font_sc << "\n"
1426 << "\\font_osf " << h_font_osf << "\n"
1427 << "\\font_sf_scale " << h_font_sf_scale[0]
1428 << ' ' << h_font_sf_scale[1] << '\n'
1429 << "\\font_tt_scale " << h_font_tt_scale[0]
1430 << ' ' << h_font_tt_scale[1] << '\n';
1431 if (!h_font_cjk.empty())
1432 os << "\\font_cjk " << h_font_cjk << '\n';
1433 os << "\\use_microtype " << h_use_microtype << '\n'
1434 << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
1435 << "\\graphics " << h_graphics << '\n'
1436 << "\\default_output_format " << h_default_output_format << "\n"
1437 << "\\output_sync " << h_output_sync << "\n";
1438 if (h_output_sync == "1")
1439 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1440 os << "\\bibtex_command " << h_bibtex_command << "\n"
1441 << "\\index_command " << h_index_command << "\n";
1442 if (!h_float_placement.empty())
1443 os << "\\float_placement " << h_float_placement << "\n";
1444 os << "\\paperfontsize " << h_paperfontsize << "\n"
1445 << "\\spacing " << h_spacing << "\n"
1446 << "\\use_hyperref " << h_use_hyperref << '\n';
1447 if (h_use_hyperref == "true") {
1448 if (!h_pdf_title.empty())
1449 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1450 if (!h_pdf_author.empty())
1451 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1452 if (!h_pdf_subject.empty())
1453 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1454 if (!h_pdf_keywords.empty())
1455 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1456 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1457 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1458 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1459 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1460 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1461 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1462 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1463 "\\pdf_backref " << h_pdf_backref << "\n"
1464 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1465 if (!h_pdf_pagemode.empty())
1466 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1467 if (!h_pdf_quoted_options.empty())
1468 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1470 os << "\\papersize " << h_papersize << "\n"
1471 << "\\use_geometry " << h_use_geometry << '\n';
1472 for (map<string, string>::const_iterator it = h_use_packages.begin();
1473 it != h_use_packages.end(); ++it)
1474 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1475 os << "\\cite_engine " << h_cite_engine << '\n'
1476 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1477 << "\\biblio_style " << h_biblio_style << "\n"
1478 << "\\use_bibtopic " << h_use_bibtopic << "\n";
1479 if (!h_biblio_options.empty())
1480 os << "\\biblio_options " << h_biblio_options << "\n";
1481 if (!h_biblatex_bibstyle.empty())
1482 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
1483 if (!h_biblatex_citestyle.empty())
1484 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
1485 if (!h_multibib.empty())
1486 os << "\\multibib " << h_multibib << "\n";
1487 os << "\\use_indices " << h_use_indices << "\n"
1488 << "\\paperorientation " << h_paperorientation << '\n'
1489 << "\\suppress_date " << h_suppress_date << '\n'
1490 << "\\justification " << h_justification << '\n'
1491 << "\\use_refstyle " << h_use_refstyle << '\n'
1492 << "\\use_minted " << h_use_minted << '\n';
1493 if (!h_fontcolor.empty())
1494 os << "\\fontcolor " << h_fontcolor << '\n';
1495 if (!h_notefontcolor.empty())
1496 os << "\\notefontcolor " << h_notefontcolor << '\n';
1497 if (!h_backgroundcolor.empty())
1498 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1499 if (!h_boxbgcolor.empty())
1500 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1501 if (index_number != 0)
1502 for (int i = 0; i < index_number; i++) {
1503 os << "\\index " << h_index[i] << '\n'
1504 << "\\shortcut " << h_shortcut[i] << '\n'
1505 << "\\color " << h_color << '\n'
1509 os << "\\index " << h_index[0] << '\n'
1510 << "\\shortcut " << h_shortcut[0] << '\n'
1511 << "\\color " << h_color << '\n'
1515 << "\\secnumdepth " << h_secnumdepth << "\n"
1516 << "\\tocdepth " << h_tocdepth << "\n"
1517 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1518 if (h_paragraph_separation == "skip")
1519 os << "\\defskip " << h_defskip << "\n";
1521 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1522 os << "\\is_math_indent " << h_is_mathindent << "\n";
1523 if (!h_mathindentation.empty())
1524 os << "\\math_indentation " << h_mathindentation << "\n";
1525 os << "\\math_numbering_side " << h_math_numbering_side << "\n";
1526 os << "\\quotes_style " << h_quotes_style << "\n"
1527 << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
1528 << "\\papercolumns " << h_papercolumns << "\n"
1529 << "\\papersides " << h_papersides << "\n"
1530 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1531 if (!h_listings_params.empty())
1532 os << "\\listings_params " << h_listings_params << "\n";
1533 os << "\\tracking_changes " << h_tracking_changes << "\n"
1534 << "\\output_changes " << h_output_changes << "\n"
1535 << "\\html_math_output " << h_html_math_output << "\n"
1536 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1537 << "\\html_be_strict " << h_html_be_strict << "\n"
1539 << "\\end_header\n\n"
1540 << "\\begin_body\n";
1545 void Preamble::parse(Parser & p, string const & forceclass,
1546 TeX2LyXDocClass & tc)
1548 // initialize fixed types
1549 special_columns_['D'] = 3;
1550 parse(p, forceclass, false, tc);
1554 void Preamble::parse(Parser & p, string const & forceclass,
1555 bool detectEncoding, TeX2LyXDocClass & tc)
1557 bool is_full_document = false;
1558 bool is_lyx_file = false;
1559 bool in_lyx_preamble = false;
1561 // determine whether this is a full document or a fragment for inclusion
1563 Token const & t = p.get_token();
1565 if (t.cat() == catEscape && t.cs() == "documentclass") {
1566 is_full_document = true;
1572 if (detectEncoding && !is_full_document)
1575 while (is_full_document && p.good()) {
1576 if (detectEncoding && h_inputencoding != "auto-legacy" &&
1577 h_inputencoding != "auto-legacy-plain")
1580 Token const & t = p.get_token();
1583 if (!detectEncoding)
1584 cerr << "t: " << t << '\n';
1590 if (!in_lyx_preamble &&
1591 (t.cat() == catLetter ||
1592 t.cat() == catSuper ||
1593 t.cat() == catSub ||
1594 t.cat() == catOther ||
1595 t.cat() == catMath ||
1596 t.cat() == catActive ||
1597 t.cat() == catBegin ||
1598 t.cat() == catEnd ||
1599 t.cat() == catAlign ||
1600 t.cat() == catParameter)) {
1601 h_preamble << t.cs();
1605 if (!in_lyx_preamble &&
1606 (t.cat() == catSpace || t.cat() == catNewline)) {
1607 h_preamble << t.asInput();
1611 if (t.cat() == catComment) {
1612 static regex const islyxfile("%% LyX .* created this file");
1613 static regex const usercommands("User specified LaTeX commands");
1615 string const comment = t.asInput();
1617 // magically switch encoding default if it looks like XeLaTeX
1618 static string const magicXeLaTeX =
1619 "% This document must be compiled with XeLaTeX ";
1620 if (comment.size() > magicXeLaTeX.size()
1621 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1622 && h_inputencoding == "auto-legacy") {
1623 if (!detectEncoding)
1624 cerr << "XeLaTeX comment found, switching to UTF8\n";
1625 h_inputencoding = "utf8";
1628 if (regex_search(comment, sub, islyxfile)) {
1630 in_lyx_preamble = true;
1631 } else if (is_lyx_file
1632 && regex_search(comment, sub, usercommands))
1633 in_lyx_preamble = false;
1634 else if (!in_lyx_preamble)
1635 h_preamble << t.asInput();
1639 if (t.cs() == "PassOptionsToPackage") {
1640 string const poptions = p.getArg('{', '}');
1641 string const package = p.verbatim_item();
1642 extra_package_options_.insert(make_pair(package, poptions));
1646 if (t.cs() == "pagestyle") {
1647 h_paperpagestyle = p.verbatim_item();
1651 if (t.cs() == "setdefaultlanguage") {
1653 // We don't yet care about non-language variant options
1654 // because LyX doesn't support this yet, see bug #8214
1656 string langopts = p.getOpt();
1657 // check if the option contains a variant, if yes, extract it
1658 string::size_type pos_var = langopts.find("variant");
1659 string::size_type i = langopts.find(',', pos_var);
1660 string::size_type k = langopts.find('=', pos_var);
1661 if (pos_var != string::npos){
1663 if (i == string::npos)
1664 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1666 variant = langopts.substr(k + 1, i - k - 1);
1667 h_language = variant;
1671 h_language = p.verbatim_item();
1672 //finally translate the poyglossia name to a LyX name
1673 h_language = polyglossia2lyx(h_language);
1677 if (t.cs() == "setotherlanguage") {
1678 // We don't yet care about the option because LyX doesn't
1679 // support this yet, see bug #8214
1680 p.hasOpt() ? p.getOpt() : string();
1685 if (t.cs() == "setmainfont") {
1686 // we don't care about the option
1687 p.hasOpt() ? p.getOpt() : string();
1688 h_font_roman[1] = p.getArg('{', '}');
1692 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1693 // LyX currently only supports the scale option
1696 string fontopts = p.getArg('[', ']');
1697 // check if the option contains a scaling, if yes, extract it
1698 string::size_type pos = fontopts.find("Scale");
1699 if (pos != string::npos) {
1700 string::size_type i = fontopts.find(',', pos);
1701 if (i == string::npos)
1702 scale_as_percentage(fontopts.substr(pos + 1), scale);
1704 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1707 if (t.cs() == "setsansfont") {
1709 h_font_sf_scale[1] = scale;
1710 h_font_sans[1] = p.getArg('{', '}');
1713 h_font_tt_scale[1] = scale;
1714 h_font_typewriter[1] = p.getArg('{', '}');
1719 if (t.cs() == "babelfont") {
1721 h_use_non_tex_fonts = true;
1722 h_language_package = "babel";
1723 if (h_inputencoding == "auto-legacy")
1724 p.setEncoding("UTF-8");
1725 // we don't care about the lang option
1726 string const lang = p.hasOpt() ? p.getArg('[', ']') : string();
1727 string const family = p.getArg('{', '}');
1728 string const fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
1729 string const fontname = p.getArg('{', '}');
1730 if (lang.empty() && family == "rm") {
1731 h_font_roman[1] = fontname;
1733 } else if (lang.empty() && (family == "sf" || family == "tt")) {
1734 // LyX currently only supports the scale option
1736 if (!fontopts.empty()) {
1737 // check if the option contains a scaling, if yes, extract it
1738 string::size_type pos = fontopts.find("Scale");
1739 if (pos != string::npos) {
1740 string::size_type i = fontopts.find(',', pos);
1741 if (i == string::npos)
1742 scale_as_percentage(fontopts.substr(pos + 1), scale);
1744 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1747 if (family == "sf") {
1749 h_font_sf_scale[1] = scale;
1750 h_font_sans[1] = fontname;
1753 h_font_tt_scale[1] = scale;
1754 h_font_typewriter[1] = fontname;
1758 // not rm, sf or tt or lang specific
1759 h_preamble << '\\' << t.cs();
1761 h_preamble << '[' << lang << ']';
1762 h_preamble << '{' << family << '}';
1763 if (!fontopts.empty())
1764 h_preamble << '[' << fontopts << ']';
1765 h_preamble << '{' << fontname << '}' << '\n';
1770 if (t.cs() == "date") {
1771 string argument = p.getArg('{', '}');
1772 if (argument.empty())
1773 h_suppress_date = "true";
1775 h_preamble << t.asInput() << '{' << argument << '}';
1779 if (t.cs() == "color") {
1780 string const space =
1781 (p.hasOpt() ? p.getOpt() : string());
1782 string argument = p.getArg('{', '}');
1783 // check the case that a standard color is used
1784 if (space.empty() && is_known(argument, known_basic_colors)) {
1785 h_fontcolor = rgbcolor2code(argument);
1786 registerAutomaticallyLoadedPackage("color");
1787 } else if (space.empty() && argument == "document_fontcolor")
1788 registerAutomaticallyLoadedPackage("color");
1789 // check the case that LyX's document_fontcolor is defined
1790 // but not used for \color
1792 h_preamble << t.asInput();
1794 h_preamble << space;
1795 h_preamble << '{' << argument << '}';
1796 // the color might already be set because \definecolor
1797 // is parsed before this
1803 if (t.cs() == "pagecolor") {
1804 string argument = p.getArg('{', '}');
1805 // check the case that a standard color is used
1806 if (is_known(argument, known_basic_colors)) {
1807 h_backgroundcolor = rgbcolor2code(argument);
1808 } else if (argument == "page_backgroundcolor")
1809 registerAutomaticallyLoadedPackage("color");
1810 // check the case that LyX's page_backgroundcolor is defined
1811 // but not used for \pagecolor
1813 h_preamble << t.asInput() << '{' << argument << '}';
1814 // the color might already be set because \definecolor
1815 // is parsed before this
1816 h_backgroundcolor = "";
1821 if (t.cs() == "makeatletter") {
1822 // LyX takes care of this
1823 p.setCatcode('@', catLetter);
1827 if (t.cs() == "makeatother") {
1828 // LyX takes care of this
1829 p.setCatcode('@', catOther);
1833 if (t.cs() == "makeindex") {
1834 // LyX will re-add this if a print index command is found
1839 if (t.cs() == "newindex") {
1840 string const indexname = p.getArg('[', ']');
1841 string const shortcut = p.verbatim_item();
1842 if (!indexname.empty())
1843 h_index[index_number] = indexname;
1845 h_index[index_number] = shortcut;
1846 h_shortcut[index_number] = shortcut;
1852 if (t.cs() == "addbibresource") {
1853 string const options = p.getArg('[', ']');
1854 string const arg = removeExtension(p.getArg('{', '}'));
1855 if (!options.empty()) {
1856 // check if the option contains a bibencoding, if yes, extract it
1857 string::size_type pos = options.find("bibencoding=");
1859 if (pos != string::npos) {
1860 string::size_type i = options.find(',', pos);
1861 if (i == string::npos)
1862 encoding = options.substr(pos + 1);
1864 encoding = options.substr(pos, i - pos);
1865 pos = encoding.find('=');
1866 if (pos == string::npos)
1869 encoding = encoding.substr(pos + 1);
1871 if (!encoding.empty())
1872 biblatex_encodings.push_back(normalize_filename(arg) + ' ' + encoding);
1874 biblatex_bibliographies.push_back(arg);
1878 if (t.cs() == "bibliography") {
1879 vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
1880 biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
1884 if (t.cs() == "RS@ifundefined") {
1885 string const name = p.verbatim_item();
1886 string const body1 = p.verbatim_item();
1887 string const body2 = p.verbatim_item();
1888 // only non-lyxspecific stuff
1889 if (in_lyx_preamble &&
1890 (name == "subsecref" || name == "thmref" || name == "lemref"))
1894 ss << '\\' << t.cs();
1895 ss << '{' << name << '}'
1896 << '{' << body1 << '}'
1897 << '{' << body2 << '}';
1898 h_preamble << ss.str();
1903 if (t.cs() == "AtBeginDocument") {
1904 string const name = p.verbatim_item();
1905 // only non-lyxspecific stuff
1906 if (in_lyx_preamble &&
1907 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
1908 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
1909 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
1910 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
1911 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
1912 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
1913 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
1914 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
1915 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
1916 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
1917 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
1918 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
1919 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
1920 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
1921 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
1925 ss << '\\' << t.cs();
1926 ss << '{' << name << '}';
1927 h_preamble << ss.str();
1932 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1933 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1934 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1935 || t.cs() == "DeclareRobustCommand"
1936 || t.cs() == "DeclareRobustCommandx"
1937 || t.cs() == "ProvideTextCommandDefault"
1938 || t.cs() == "DeclareMathAccent") {
1940 if (p.next_token().character() == '*') {
1944 string const name = p.verbatim_item();
1945 string const opt1 = p.getFullOpt();
1946 string const opt2 = p.getFullOpt();
1947 string const body = p.verbatim_item();
1948 // store the in_lyx_preamble setting
1949 bool const was_in_lyx_preamble = in_lyx_preamble;
1951 if (name == "\\rmdefault")
1952 if (is_known(body, known_roman_font_packages)) {
1953 h_font_roman[0] = body;
1955 in_lyx_preamble = true;
1957 if (name == "\\sfdefault")
1958 if (is_known(body, known_sans_font_packages)) {
1959 h_font_sans[0] = body;
1961 in_lyx_preamble = true;
1963 if (name == "\\ttdefault")
1964 if (is_known(body, known_typewriter_font_packages)) {
1965 h_font_typewriter[0] = body;
1967 in_lyx_preamble = true;
1969 if (name == "\\familydefault") {
1970 string family = body;
1971 // remove leading "\"
1972 h_font_default_family = family.erase(0,1);
1974 in_lyx_preamble = true;
1977 // remove LyX-specific definitions that are re-added by LyX
1979 // \lyxline is an ancient command that is converted by tex2lyx into
1980 // a \rule therefore remove its preamble code
1981 if (name == "\\lyxdot" || name == "\\lyxarrow"
1982 || name == "\\lyxline" || name == "\\LyX") {
1984 in_lyx_preamble = true;
1987 // Add the command to the known commands
1988 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1990 // only non-lyxspecific stuff
1991 if (!in_lyx_preamble) {
1993 ss << '\\' << t.cs();
1996 ss << '{' << name << '}' << opt1 << opt2
1997 << '{' << body << "}";
1998 if (prefixIs(t.cs(), "renew") || !contains(h_preamble.str(), ss.str()))
1999 h_preamble << ss.str();
2001 ostream & out = in_preamble ? h_preamble : os;
2002 out << "\\" << t.cs() << "{" << name << "}"
2003 << opts << "{" << body << "}";
2006 // restore the in_lyx_preamble setting
2007 in_lyx_preamble = was_in_lyx_preamble;
2011 if (t.cs() == "documentclass") {
2012 vector<string>::iterator it;
2013 vector<string> opts = split_options(p.getArg('[', ']'));
2014 handle_opt(opts, known_fontsizes, h_paperfontsize);
2015 delete_opt(opts, known_fontsizes);
2016 // delete "pt" at the end
2017 string::size_type i = h_paperfontsize.find("pt");
2018 if (i != string::npos)
2019 h_paperfontsize.erase(i);
2020 // The documentclass options are always parsed before the options
2021 // of the babel call so that a language cannot overwrite the babel
2023 handle_opt(opts, known_languages, h_language);
2024 delete_opt(opts, known_languages);
2027 if ((it = find(opts.begin(), opts.end(), "fleqn"))
2029 h_is_mathindent = "1";
2032 // formula numbering side
2033 if ((it = find(opts.begin(), opts.end(), "leqno"))
2035 h_math_numbering_side = "left";
2038 else if ((it = find(opts.begin(), opts.end(), "reqno"))
2040 h_math_numbering_side = "right";
2044 // paper orientation
2045 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
2046 h_paperorientation = "landscape";
2050 if ((it = find(opts.begin(), opts.end(), "oneside"))
2055 if ((it = find(opts.begin(), opts.end(), "twoside"))
2061 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
2063 h_papercolumns = "1";
2066 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
2068 h_papercolumns = "2";
2072 // some size options are known to any document classes, other sizes
2073 // are handled by the \geometry command of the geometry package
2074 handle_opt(opts, known_class_paper_sizes, h_papersize);
2075 delete_opt(opts, known_class_paper_sizes);
2076 // the remaining options
2077 h_options = join(opts, ",");
2078 // FIXME This does not work for classes that have a
2079 // different name in LyX than in LaTeX
2080 h_textclass = p.getArg('{', '}');
2085 if (t.cs() == "usepackage") {
2086 string const options = p.getArg('[', ']');
2087 string const name = p.getArg('{', '}');
2088 vector<string> vecnames;
2089 split(name, vecnames, ',');
2090 vector<string>::const_iterator it = vecnames.begin();
2091 vector<string>::const_iterator end = vecnames.end();
2092 for (; it != end; ++it)
2093 handle_package(p, trimSpaceAndEol(*it), options,
2094 in_lyx_preamble, detectEncoding);
2098 if (t.cs() == "inputencoding") {
2099 string const encoding = p.getArg('{','}');
2100 Encoding const * const enc = encodings.fromLaTeXName(
2101 encoding, Encoding::inputenc, true);
2103 if (!detectEncoding)
2104 cerr << "Unknown encoding " << encoding
2105 << ". Ignoring." << std::endl;
2108 h_inputencoding = enc->name();
2109 p.setEncoding(enc->iconvName());
2114 if (t.cs() == "newenvironment") {
2115 string const name = p.getArg('{', '}');
2116 string const opt1 = p.getFullOpt();
2117 string const opt2 = p.getFullOpt();
2118 string const beg = p.verbatim_item();
2119 string const end = p.verbatim_item();
2120 if (!in_lyx_preamble) {
2121 h_preamble << "\\newenvironment{" << name
2122 << '}' << opt1 << opt2 << '{'
2123 << beg << "}{" << end << '}';
2125 add_known_environment(name, opt1, !opt2.empty(),
2126 from_utf8(beg), from_utf8(end));
2130 if (t.cs() == "newtheorem") {
2132 if (p.next_token().character() == '*') {
2136 string const name = p.getArg('{', '}');
2137 string const opt1 = p.getFullOpt();
2138 string const opt2 = p.getFullOpt();
2139 string const body = p.verbatim_item();
2140 string const opt3 = p.getFullOpt();
2141 string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
2143 string const complete = cmd + "{" + name + '}' +
2144 opt1 + opt2 + '{' + body + '}' + opt3;
2146 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
2148 if (!in_lyx_preamble)
2149 h_preamble << complete;
2153 if (t.cs() == "def") {
2154 string name = p.get_token().cs();
2155 // In fact, name may be more than the name:
2156 // In the test case of bug 8116
2157 // name == "csname SF@gobble@opt \endcsname".
2158 // Therefore, we need to use asInput() instead of cs().
2159 while (p.next_token().cat() != catBegin)
2160 name += p.get_token().asInput();
2161 if (!in_lyx_preamble)
2162 h_preamble << "\\def\\" << name << '{'
2163 << p.verbatim_item() << "}";
2167 if (t.cs() == "newcolumntype") {
2168 string const name = p.getArg('{', '}');
2169 trimSpaceAndEol(name);
2171 string opts = p.getOpt();
2172 if (!opts.empty()) {
2173 istringstream is(string(opts, 1));
2176 special_columns_[name[0]] = nargs;
2177 h_preamble << "\\newcolumntype{" << name << "}";
2179 h_preamble << "[" << nargs << "]";
2180 h_preamble << "{" << p.verbatim_item() << "}";
2184 if (t.cs() == "setcounter") {
2185 string const name = p.getArg('{', '}');
2186 string const content = p.getArg('{', '}');
2187 if (name == "secnumdepth")
2188 h_secnumdepth = content;
2189 else if (name == "tocdepth")
2190 h_tocdepth = content;
2192 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
2196 if (t.cs() == "setlength") {
2197 string const name = p.verbatim_item();
2198 string const content = p.verbatim_item();
2199 // the paragraphs are only not indented when \parindent is set to zero
2200 if (name == "\\parindent" && content != "") {
2201 if (content[0] == '0')
2202 h_paragraph_separation = "skip";
2204 h_paragraph_indentation = translate_len(content);
2205 } else if (name == "\\parskip") {
2206 if (content == "\\smallskipamount")
2207 h_defskip = "smallskip";
2208 else if (content == "\\medskipamount")
2209 h_defskip = "medskip";
2210 else if (content == "\\bigskipamount")
2211 h_defskip = "bigskip";
2213 h_defskip = translate_len(content);
2214 } else if (name == "\\mathindent") {
2215 h_mathindentation = translate_len(content);
2217 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2221 if (t.cs() == "onehalfspacing") {
2222 h_spacing = "onehalf";
2226 if (t.cs() == "doublespacing") {
2227 h_spacing = "double";
2231 if (t.cs() == "setstretch") {
2232 h_spacing = "other " + p.verbatim_item();
2236 if (t.cs() == "synctex") {
2237 // the scheme is \synctex=value
2238 // where value can only be "1" or "-1"
2239 h_output_sync = "1";
2240 // there can be any character behind the value (e.g. a linebreak or a '\'
2241 // therefore we extract it char by char
2243 string value = p.get_token().asInput();
2245 value += p.get_token().asInput();
2246 h_output_sync_macro = "\\synctex=" + value;
2250 if (t.cs() == "begin") {
2251 string const name = p.getArg('{', '}');
2252 if (name == "document")
2254 h_preamble << "\\begin{" << name << "}";
2258 if (t.cs() == "geometry") {
2259 vector<string> opts = split_options(p.getArg('{', '}'));
2260 handle_geometry(opts);
2264 if (t.cs() == "definecolor") {
2265 string const color = p.getArg('{', '}');
2266 string const space = p.getArg('{', '}');
2267 string const value = p.getArg('{', '}');
2268 if (color == "document_fontcolor" && space == "rgb") {
2269 RGBColor c(RGBColorFromLaTeX(value));
2270 h_fontcolor = X11hexname(c);
2271 } else if (color == "note_fontcolor" && space == "rgb") {
2272 RGBColor c(RGBColorFromLaTeX(value));
2273 h_notefontcolor = X11hexname(c);
2274 } else if (color == "page_backgroundcolor" && space == "rgb") {
2275 RGBColor c(RGBColorFromLaTeX(value));
2276 h_backgroundcolor = X11hexname(c);
2277 } else if (color == "shadecolor" && space == "rgb") {
2278 RGBColor c(RGBColorFromLaTeX(value));
2279 h_boxbgcolor = X11hexname(c);
2281 h_preamble << "\\definecolor{" << color
2282 << "}{" << space << "}{" << value
2288 if (t.cs() == "bibliographystyle") {
2289 h_biblio_style = p.verbatim_item();
2293 if (t.cs() == "jurabibsetup") {
2294 // FIXME p.getArg('{', '}') is most probably wrong (it
2295 // does not handle nested braces).
2296 // Use p.verbatim_item() instead.
2297 vector<string> jurabibsetup =
2298 split_options(p.getArg('{', '}'));
2299 // add jurabibsetup to the jurabib package options
2300 add_package("jurabib", jurabibsetup);
2301 if (!jurabibsetup.empty()) {
2302 h_preamble << "\\jurabibsetup{"
2303 << join(jurabibsetup, ",") << '}';
2308 if (t.cs() == "hypersetup") {
2309 vector<string> hypersetup =
2310 split_options(p.verbatim_item());
2311 // add hypersetup to the hyperref package options
2312 handle_hyperref(hypersetup);
2313 if (!hypersetup.empty()) {
2314 h_preamble << "\\hypersetup{"
2315 << join(hypersetup, ",") << '}';
2320 if (t.cs() == "includeonly") {
2321 vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
2322 for (auto & iofile : includeonlys) {
2323 string filename(normalize_filename(iofile));
2324 string const path = getMasterFilePath(true);
2325 // We want to preserve relative/absolute filenames,
2326 // therefore path is only used for testing
2327 if (!makeAbsPath(filename, path).exists()) {
2328 // The file extension is probably missing.
2329 // Now try to find it out.
2330 string const tex_name =
2331 find_file(filename, path,
2332 known_tex_extensions);
2333 if (!tex_name.empty())
2334 filename = tex_name;
2337 if (makeAbsPath(filename, path).exists())
2338 fix_child_filename(filename);
2340 cerr << "Warning: Could not find included file '"
2341 << filename << "'." << endl;
2342 outname = changeExtension(filename, "lyx");
2343 h_includeonlys.push_back(outname);
2348 if (is_known(t.cs(), known_if_3arg_commands)) {
2349 // prevent misparsing of \usepackage if it is used
2350 // as an argument (see e.g. our own output of
2351 // \@ifundefined above)
2352 string const arg1 = p.verbatim_item();
2353 string const arg2 = p.verbatim_item();
2354 string const arg3 = p.verbatim_item();
2355 // test case \@ifundefined{date}{}{\date{}}
2356 if (t.cs() == "@ifundefined" && arg1 == "date" &&
2357 arg2.empty() && arg3 == "\\date{}") {
2358 h_suppress_date = "true";
2359 // older tex2lyx versions did output
2360 // \@ifundefined{definecolor}{\usepackage{color}}{}
2361 } else if (t.cs() == "@ifundefined" &&
2362 arg1 == "definecolor" &&
2363 arg2 == "\\usepackage{color}" &&
2365 if (!in_lyx_preamble)
2366 h_preamble << package_beg_sep
2369 << "\\@ifundefined{definecolor}{color}{}"
2372 //\@ifundefined{showcaptionsetup}{}{%
2373 // \PassOptionsToPackage{caption=false}{subfig}}
2374 // that LyX uses for subfloats
2375 } else if (t.cs() == "@ifundefined" &&
2376 arg1 == "showcaptionsetup" && arg2.empty()
2377 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
2379 } else if (!in_lyx_preamble) {
2380 h_preamble << t.asInput()
2381 << '{' << arg1 << '}'
2382 << '{' << arg2 << '}'
2383 << '{' << arg3 << '}';
2388 if (is_known(t.cs(), known_if_commands)) {
2389 // must not parse anything in conditional code, since
2390 // LyX would output the parsed contents unconditionally
2391 if (!in_lyx_preamble)
2392 h_preamble << t.asInput();
2393 handle_if(p, in_lyx_preamble);
2397 if (!t.cs().empty() && !in_lyx_preamble) {
2398 h_preamble << '\\' << t.cs();
2403 // remove the whitespace
2406 // Force textclass if the user wanted it
2407 if (!forceclass.empty())
2408 h_textclass = forceclass;
2409 tc.setName(h_textclass);
2410 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2411 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2414 if (h_papersides.empty()) {
2417 h_papersides = ss.str();
2420 // If the CJK package is used we cannot set the document language from
2421 // the babel options. Instead, we guess which language is used most
2422 // and set this one.
2423 default_language = h_language;
2424 if (is_full_document &&
2425 (auto_packages.find("CJK") != auto_packages.end() ||
2426 auto_packages.find("CJKutf8") != auto_packages.end())) {
2428 h_language = guessLanguage(p, default_language);
2430 if (explicit_babel && h_language != default_language) {
2431 // We set the document language to a CJK language,
2432 // but babel is explicitly called in the user preamble
2433 // without options. LyX will not add the default
2434 // language to the document options if it is either
2435 // english, or no text is set as default language.
2436 // Therefore we need to add a language option explicitly.
2437 // FIXME: It would be better to remove all babel calls
2438 // from the user preamble, but this is difficult
2439 // without re-introducing bug 7861.
2440 if (h_options.empty())
2441 h_options = lyx2babel(default_language);
2443 h_options += ',' + lyx2babel(default_language);
2447 // Finally, set the quote style.
2448 // LyX knows the following quotes styles:
2449 // british, cjk, cjkangle, danish, english, french, german,
2450 // polish, russian, swedish and swiss
2451 // conversion list taken from
2452 // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
2453 // (quotes for kazakh are unknown)
2455 if (is_known(h_language, known_british_quotes_languages))
2456 h_quotes_style = "british";
2458 else if (is_known(h_language, known_cjk_quotes_languages))
2459 h_quotes_style = "cjk";
2461 else if (is_known(h_language, known_cjkangle_quotes_languages))
2462 h_quotes_style = "cjkangle";
2464 else if (is_known(h_language, known_danish_quotes_languages))
2465 h_quotes_style = "danish";
2467 else if (is_known(h_language, known_french_quotes_languages))
2468 h_quotes_style = "french";
2470 else if (is_known(h_language, known_german_quotes_languages))
2471 h_quotes_style = "german";
2473 else if (is_known(h_language, known_polish_quotes_languages))
2474 h_quotes_style = "polish";
2476 else if (is_known(h_language, known_russian_quotes_languages))
2477 h_quotes_style = "russian";
2479 else if (is_known(h_language, known_swedish_quotes_languages))
2480 h_quotes_style = "swedish";
2482 else if (is_known(h_language, known_swiss_quotes_languages))
2483 h_quotes_style = "swiss";
2485 else if (is_known(h_language, known_english_quotes_languages))
2486 h_quotes_style = "english";
2490 string Preamble::parseEncoding(Parser & p, string const & forceclass)
2492 TeX2LyXDocClass dummy;
2493 parse(p, forceclass, true, dummy);
2494 if (h_inputencoding != "auto-legacy" && h_inputencoding != "auto-legacy-plain")
2495 return h_inputencoding;
2500 string babel2lyx(string const & language)
2502 char const * const * where = is_known(language, known_languages);
2504 return known_coded_languages[where - known_languages];
2509 string lyx2babel(string const & language)
2511 char const * const * where = is_known(language, known_coded_languages);
2513 return known_languages[where - known_coded_languages];
2518 string Preamble::polyglossia2lyx(string const & language)
2520 char const * const * where = is_known(language, polyglossia_languages);
2522 return coded_polyglossia_languages[where - polyglossia_languages];
2527 string rgbcolor2code(string const & name)
2529 char const * const * where = is_known(name, known_basic_colors);
2531 // "red", "green" etc
2532 return known_basic_color_codes[where - known_basic_colors];
2534 // "255,0,0", "0,255,0" etc
2535 RGBColor c(RGBColorFromLaTeX(name));
2536 return X11hexname(c);