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() == "date") {
1720 string argument = p.getArg('{', '}');
1721 if (argument.empty())
1722 h_suppress_date = "true";
1724 h_preamble << t.asInput() << '{' << argument << '}';
1728 if (t.cs() == "color") {
1729 string const space =
1730 (p.hasOpt() ? p.getOpt() : string());
1731 string argument = p.getArg('{', '}');
1732 // check the case that a standard color is used
1733 if (space.empty() && is_known(argument, known_basic_colors)) {
1734 h_fontcolor = rgbcolor2code(argument);
1735 registerAutomaticallyLoadedPackage("color");
1736 } else if (space.empty() && argument == "document_fontcolor")
1737 registerAutomaticallyLoadedPackage("color");
1738 // check the case that LyX's document_fontcolor is defined
1739 // but not used for \color
1741 h_preamble << t.asInput();
1743 h_preamble << space;
1744 h_preamble << '{' << argument << '}';
1745 // the color might already be set because \definecolor
1746 // is parsed before this
1752 if (t.cs() == "pagecolor") {
1753 string argument = p.getArg('{', '}');
1754 // check the case that a standard color is used
1755 if (is_known(argument, known_basic_colors)) {
1756 h_backgroundcolor = rgbcolor2code(argument);
1757 } else if (argument == "page_backgroundcolor")
1758 registerAutomaticallyLoadedPackage("color");
1759 // check the case that LyX's page_backgroundcolor is defined
1760 // but not used for \pagecolor
1762 h_preamble << t.asInput() << '{' << argument << '}';
1763 // the color might already be set because \definecolor
1764 // is parsed before this
1765 h_backgroundcolor = "";
1770 if (t.cs() == "makeatletter") {
1771 // LyX takes care of this
1772 p.setCatcode('@', catLetter);
1776 if (t.cs() == "makeatother") {
1777 // LyX takes care of this
1778 p.setCatcode('@', catOther);
1782 if (t.cs() == "makeindex") {
1783 // LyX will re-add this if a print index command is found
1788 if (t.cs() == "newindex") {
1789 string const indexname = p.getArg('[', ']');
1790 string const shortcut = p.verbatim_item();
1791 if (!indexname.empty())
1792 h_index[index_number] = indexname;
1794 h_index[index_number] = shortcut;
1795 h_shortcut[index_number] = shortcut;
1801 if (t.cs() == "addbibresource") {
1802 string const options = p.getArg('[', ']');
1803 string const arg = removeExtension(p.getArg('{', '}'));
1804 if (!options.empty()) {
1805 // check if the option contains a bibencoding, if yes, extract it
1806 string::size_type pos = options.find("bibencoding=");
1808 if (pos != string::npos) {
1809 string::size_type i = options.find(',', pos);
1810 if (i == string::npos)
1811 encoding = options.substr(pos + 1);
1813 encoding = options.substr(pos, i - pos);
1814 pos = encoding.find('=');
1815 if (pos == string::npos)
1818 encoding = encoding.substr(pos + 1);
1820 if (!encoding.empty())
1821 biblatex_encodings.push_back(normalize_filename(arg) + ' ' + encoding);
1823 biblatex_bibliographies.push_back(arg);
1827 if (t.cs() == "bibliography") {
1828 vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
1829 biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
1833 if (t.cs() == "RS@ifundefined") {
1834 string const name = p.verbatim_item();
1835 string const body1 = p.verbatim_item();
1836 string const body2 = p.verbatim_item();
1837 // only non-lyxspecific stuff
1838 if (in_lyx_preamble &&
1839 (name == "subsecref" || name == "thmref" || name == "lemref"))
1843 ss << '\\' << t.cs();
1844 ss << '{' << name << '}'
1845 << '{' << body1 << '}'
1846 << '{' << body2 << '}';
1847 h_preamble << ss.str();
1852 if (t.cs() == "AtBeginDocument") {
1853 string const name = p.verbatim_item();
1854 // only non-lyxspecific stuff
1855 if (in_lyx_preamble &&
1856 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
1857 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
1858 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
1859 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
1860 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
1861 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
1862 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
1863 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
1864 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
1865 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
1866 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
1867 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
1868 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
1869 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
1870 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
1874 ss << '\\' << t.cs();
1875 ss << '{' << name << '}';
1876 h_preamble << ss.str();
1881 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1882 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1883 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1884 || t.cs() == "DeclareRobustCommand"
1885 || t.cs() == "DeclareRobustCommandx"
1886 || t.cs() == "ProvideTextCommandDefault"
1887 || t.cs() == "DeclareMathAccent") {
1889 if (p.next_token().character() == '*') {
1893 string const name = p.verbatim_item();
1894 string const opt1 = p.getFullOpt();
1895 string const opt2 = p.getFullOpt();
1896 string const body = p.verbatim_item();
1897 // store the in_lyx_preamble setting
1898 bool const was_in_lyx_preamble = in_lyx_preamble;
1900 if (name == "\\rmdefault")
1901 if (is_known(body, known_roman_font_packages)) {
1902 h_font_roman[0] = body;
1904 in_lyx_preamble = true;
1906 if (name == "\\sfdefault")
1907 if (is_known(body, known_sans_font_packages)) {
1908 h_font_sans[0] = body;
1910 in_lyx_preamble = true;
1912 if (name == "\\ttdefault")
1913 if (is_known(body, known_typewriter_font_packages)) {
1914 h_font_typewriter[0] = body;
1916 in_lyx_preamble = true;
1918 if (name == "\\familydefault") {
1919 string family = body;
1920 // remove leading "\"
1921 h_font_default_family = family.erase(0,1);
1923 in_lyx_preamble = true;
1926 // remove LyX-specific definitions that are re-added by LyX
1928 // \lyxline is an ancient command that is converted by tex2lyx into
1929 // a \rule therefore remove its preamble code
1930 if (name == "\\lyxdot" || name == "\\lyxarrow"
1931 || name == "\\lyxline" || name == "\\LyX") {
1933 in_lyx_preamble = true;
1936 // Add the command to the known commands
1937 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1939 // only non-lyxspecific stuff
1940 if (!in_lyx_preamble) {
1942 ss << '\\' << t.cs();
1945 ss << '{' << name << '}' << opt1 << opt2
1946 << '{' << body << "}";
1947 if (prefixIs(t.cs(), "renew") || !contains(h_preamble.str(), ss.str()))
1948 h_preamble << ss.str();
1950 ostream & out = in_preamble ? h_preamble : os;
1951 out << "\\" << t.cs() << "{" << name << "}"
1952 << opts << "{" << body << "}";
1955 // restore the in_lyx_preamble setting
1956 in_lyx_preamble = was_in_lyx_preamble;
1960 if (t.cs() == "documentclass") {
1961 vector<string>::iterator it;
1962 vector<string> opts = split_options(p.getArg('[', ']'));
1963 handle_opt(opts, known_fontsizes, h_paperfontsize);
1964 delete_opt(opts, known_fontsizes);
1965 // delete "pt" at the end
1966 string::size_type i = h_paperfontsize.find("pt");
1967 if (i != string::npos)
1968 h_paperfontsize.erase(i);
1969 // The documentclass options are always parsed before the options
1970 // of the babel call so that a language cannot overwrite the babel
1972 handle_opt(opts, known_languages, h_language);
1973 delete_opt(opts, known_languages);
1976 if ((it = find(opts.begin(), opts.end(), "fleqn"))
1978 h_is_mathindent = "1";
1981 // formula numbering side
1982 if ((it = find(opts.begin(), opts.end(), "leqno"))
1984 h_math_numbering_side = "left";
1987 else if ((it = find(opts.begin(), opts.end(), "reqno"))
1989 h_math_numbering_side = "right";
1993 // paper orientation
1994 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1995 h_paperorientation = "landscape";
1999 if ((it = find(opts.begin(), opts.end(), "oneside"))
2004 if ((it = find(opts.begin(), opts.end(), "twoside"))
2010 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
2012 h_papercolumns = "1";
2015 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
2017 h_papercolumns = "2";
2021 // some size options are known to any document classes, other sizes
2022 // are handled by the \geometry command of the geometry package
2023 handle_opt(opts, known_class_paper_sizes, h_papersize);
2024 delete_opt(opts, known_class_paper_sizes);
2025 // the remaining options
2026 h_options = join(opts, ",");
2027 // FIXME This does not work for classes that have a
2028 // different name in LyX than in LaTeX
2029 h_textclass = p.getArg('{', '}');
2034 if (t.cs() == "usepackage") {
2035 string const options = p.getArg('[', ']');
2036 string const name = p.getArg('{', '}');
2037 vector<string> vecnames;
2038 split(name, vecnames, ',');
2039 vector<string>::const_iterator it = vecnames.begin();
2040 vector<string>::const_iterator end = vecnames.end();
2041 for (; it != end; ++it)
2042 handle_package(p, trimSpaceAndEol(*it), options,
2043 in_lyx_preamble, detectEncoding);
2047 if (t.cs() == "inputencoding") {
2048 string const encoding = p.getArg('{','}');
2049 Encoding const * const enc = encodings.fromLaTeXName(
2050 encoding, Encoding::inputenc, true);
2052 if (!detectEncoding)
2053 cerr << "Unknown encoding " << encoding
2054 << ". Ignoring." << std::endl;
2057 h_inputencoding = enc->name();
2058 p.setEncoding(enc->iconvName());
2063 if (t.cs() == "newenvironment") {
2064 string const name = p.getArg('{', '}');
2065 string const opt1 = p.getFullOpt();
2066 string const opt2 = p.getFullOpt();
2067 string const beg = p.verbatim_item();
2068 string const end = p.verbatim_item();
2069 if (!in_lyx_preamble) {
2070 h_preamble << "\\newenvironment{" << name
2071 << '}' << opt1 << opt2 << '{'
2072 << beg << "}{" << end << '}';
2074 add_known_environment(name, opt1, !opt2.empty(),
2075 from_utf8(beg), from_utf8(end));
2079 if (t.cs() == "newtheorem") {
2081 if (p.next_token().character() == '*') {
2085 string const name = p.getArg('{', '}');
2086 string const opt1 = p.getFullOpt();
2087 string const opt2 = p.getFullOpt();
2088 string const body = p.verbatim_item();
2089 string const opt3 = p.getFullOpt();
2090 string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
2092 string const complete = cmd + "{" + name + '}' +
2093 opt1 + opt2 + '{' + body + '}' + opt3;
2095 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
2097 if (!in_lyx_preamble)
2098 h_preamble << complete;
2102 if (t.cs() == "def") {
2103 string name = p.get_token().cs();
2104 // In fact, name may be more than the name:
2105 // In the test case of bug 8116
2106 // name == "csname SF@gobble@opt \endcsname".
2107 // Therefore, we need to use asInput() instead of cs().
2108 while (p.next_token().cat() != catBegin)
2109 name += p.get_token().asInput();
2110 if (!in_lyx_preamble)
2111 h_preamble << "\\def\\" << name << '{'
2112 << p.verbatim_item() << "}";
2116 if (t.cs() == "newcolumntype") {
2117 string const name = p.getArg('{', '}');
2118 trimSpaceAndEol(name);
2120 string opts = p.getOpt();
2121 if (!opts.empty()) {
2122 istringstream is(string(opts, 1));
2125 special_columns_[name[0]] = nargs;
2126 h_preamble << "\\newcolumntype{" << name << "}";
2128 h_preamble << "[" << nargs << "]";
2129 h_preamble << "{" << p.verbatim_item() << "}";
2133 if (t.cs() == "setcounter") {
2134 string const name = p.getArg('{', '}');
2135 string const content = p.getArg('{', '}');
2136 if (name == "secnumdepth")
2137 h_secnumdepth = content;
2138 else if (name == "tocdepth")
2139 h_tocdepth = content;
2141 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
2145 if (t.cs() == "setlength") {
2146 string const name = p.verbatim_item();
2147 string const content = p.verbatim_item();
2148 // the paragraphs are only not indented when \parindent is set to zero
2149 if (name == "\\parindent" && content != "") {
2150 if (content[0] == '0')
2151 h_paragraph_separation = "skip";
2153 h_paragraph_indentation = translate_len(content);
2154 } else if (name == "\\parskip") {
2155 if (content == "\\smallskipamount")
2156 h_defskip = "smallskip";
2157 else if (content == "\\medskipamount")
2158 h_defskip = "medskip";
2159 else if (content == "\\bigskipamount")
2160 h_defskip = "bigskip";
2162 h_defskip = translate_len(content);
2163 } else if (name == "\\mathindent") {
2164 h_mathindentation = translate_len(content);
2166 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2170 if (t.cs() == "onehalfspacing") {
2171 h_spacing = "onehalf";
2175 if (t.cs() == "doublespacing") {
2176 h_spacing = "double";
2180 if (t.cs() == "setstretch") {
2181 h_spacing = "other " + p.verbatim_item();
2185 if (t.cs() == "synctex") {
2186 // the scheme is \synctex=value
2187 // where value can only be "1" or "-1"
2188 h_output_sync = "1";
2189 // there can be any character behind the value (e.g. a linebreak or a '\'
2190 // therefore we extract it char by char
2192 string value = p.get_token().asInput();
2194 value += p.get_token().asInput();
2195 h_output_sync_macro = "\\synctex=" + value;
2199 if (t.cs() == "begin") {
2200 string const name = p.getArg('{', '}');
2201 if (name == "document")
2203 h_preamble << "\\begin{" << name << "}";
2207 if (t.cs() == "geometry") {
2208 vector<string> opts = split_options(p.getArg('{', '}'));
2209 handle_geometry(opts);
2213 if (t.cs() == "definecolor") {
2214 string const color = p.getArg('{', '}');
2215 string const space = p.getArg('{', '}');
2216 string const value = p.getArg('{', '}');
2217 if (color == "document_fontcolor" && space == "rgb") {
2218 RGBColor c(RGBColorFromLaTeX(value));
2219 h_fontcolor = X11hexname(c);
2220 } else if (color == "note_fontcolor" && space == "rgb") {
2221 RGBColor c(RGBColorFromLaTeX(value));
2222 h_notefontcolor = X11hexname(c);
2223 } else if (color == "page_backgroundcolor" && space == "rgb") {
2224 RGBColor c(RGBColorFromLaTeX(value));
2225 h_backgroundcolor = X11hexname(c);
2226 } else if (color == "shadecolor" && space == "rgb") {
2227 RGBColor c(RGBColorFromLaTeX(value));
2228 h_boxbgcolor = X11hexname(c);
2230 h_preamble << "\\definecolor{" << color
2231 << "}{" << space << "}{" << value
2237 if (t.cs() == "bibliographystyle") {
2238 h_biblio_style = p.verbatim_item();
2242 if (t.cs() == "jurabibsetup") {
2243 // FIXME p.getArg('{', '}') is most probably wrong (it
2244 // does not handle nested braces).
2245 // Use p.verbatim_item() instead.
2246 vector<string> jurabibsetup =
2247 split_options(p.getArg('{', '}'));
2248 // add jurabibsetup to the jurabib package options
2249 add_package("jurabib", jurabibsetup);
2250 if (!jurabibsetup.empty()) {
2251 h_preamble << "\\jurabibsetup{"
2252 << join(jurabibsetup, ",") << '}';
2257 if (t.cs() == "hypersetup") {
2258 vector<string> hypersetup =
2259 split_options(p.verbatim_item());
2260 // add hypersetup to the hyperref package options
2261 handle_hyperref(hypersetup);
2262 if (!hypersetup.empty()) {
2263 h_preamble << "\\hypersetup{"
2264 << join(hypersetup, ",") << '}';
2269 if (t.cs() == "includeonly") {
2270 vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
2271 for (auto & iofile : includeonlys) {
2272 string filename(normalize_filename(iofile));
2273 string const path = getMasterFilePath(true);
2274 // We want to preserve relative/absolute filenames,
2275 // therefore path is only used for testing
2276 if (!makeAbsPath(filename, path).exists()) {
2277 // The file extension is probably missing.
2278 // Now try to find it out.
2279 string const tex_name =
2280 find_file(filename, path,
2281 known_tex_extensions);
2282 if (!tex_name.empty())
2283 filename = tex_name;
2286 if (makeAbsPath(filename, path).exists())
2287 fix_child_filename(filename);
2289 cerr << "Warning: Could not find included file '"
2290 << filename << "'." << endl;
2291 outname = changeExtension(filename, "lyx");
2292 h_includeonlys.push_back(outname);
2297 if (is_known(t.cs(), known_if_3arg_commands)) {
2298 // prevent misparsing of \usepackage if it is used
2299 // as an argument (see e.g. our own output of
2300 // \@ifundefined above)
2301 string const arg1 = p.verbatim_item();
2302 string const arg2 = p.verbatim_item();
2303 string const arg3 = p.verbatim_item();
2304 // test case \@ifundefined{date}{}{\date{}}
2305 if (t.cs() == "@ifundefined" && arg1 == "date" &&
2306 arg2.empty() && arg3 == "\\date{}") {
2307 h_suppress_date = "true";
2308 // older tex2lyx versions did output
2309 // \@ifundefined{definecolor}{\usepackage{color}}{}
2310 } else if (t.cs() == "@ifundefined" &&
2311 arg1 == "definecolor" &&
2312 arg2 == "\\usepackage{color}" &&
2314 if (!in_lyx_preamble)
2315 h_preamble << package_beg_sep
2318 << "\\@ifundefined{definecolor}{color}{}"
2321 //\@ifundefined{showcaptionsetup}{}{%
2322 // \PassOptionsToPackage{caption=false}{subfig}}
2323 // that LyX uses for subfloats
2324 } else if (t.cs() == "@ifundefined" &&
2325 arg1 == "showcaptionsetup" && arg2.empty()
2326 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
2328 } else if (!in_lyx_preamble) {
2329 h_preamble << t.asInput()
2330 << '{' << arg1 << '}'
2331 << '{' << arg2 << '}'
2332 << '{' << arg3 << '}';
2337 if (is_known(t.cs(), known_if_commands)) {
2338 // must not parse anything in conditional code, since
2339 // LyX would output the parsed contents unconditionally
2340 if (!in_lyx_preamble)
2341 h_preamble << t.asInput();
2342 handle_if(p, in_lyx_preamble);
2346 if (!t.cs().empty() && !in_lyx_preamble) {
2347 h_preamble << '\\' << t.cs();
2352 // remove the whitespace
2355 // Force textclass if the user wanted it
2356 if (!forceclass.empty())
2357 h_textclass = forceclass;
2358 tc.setName(h_textclass);
2359 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2360 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2363 if (h_papersides.empty()) {
2366 h_papersides = ss.str();
2369 // If the CJK package is used we cannot set the document language from
2370 // the babel options. Instead, we guess which language is used most
2371 // and set this one.
2372 default_language = h_language;
2373 if (is_full_document &&
2374 (auto_packages.find("CJK") != auto_packages.end() ||
2375 auto_packages.find("CJKutf8") != auto_packages.end())) {
2377 h_language = guessLanguage(p, default_language);
2379 if (explicit_babel && h_language != default_language) {
2380 // We set the document language to a CJK language,
2381 // but babel is explicitly called in the user preamble
2382 // without options. LyX will not add the default
2383 // language to the document options if it is either
2384 // english, or no text is set as default language.
2385 // Therefore we need to add a language option explicitly.
2386 // FIXME: It would be better to remove all babel calls
2387 // from the user preamble, but this is difficult
2388 // without re-introducing bug 7861.
2389 if (h_options.empty())
2390 h_options = lyx2babel(default_language);
2392 h_options += ',' + lyx2babel(default_language);
2396 // Finally, set the quote style.
2397 // LyX knows the following quotes styles:
2398 // british, cjk, cjkangle, danish, english, french, german,
2399 // polish, russian, swedish and swiss
2400 // conversion list taken from
2401 // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
2402 // (quotes for kazakh are unknown)
2404 if (is_known(h_language, known_british_quotes_languages))
2405 h_quotes_style = "british";
2407 else if (is_known(h_language, known_cjk_quotes_languages))
2408 h_quotes_style = "cjk";
2410 else if (is_known(h_language, known_cjkangle_quotes_languages))
2411 h_quotes_style = "cjkangle";
2413 else if (is_known(h_language, known_danish_quotes_languages))
2414 h_quotes_style = "danish";
2416 else if (is_known(h_language, known_french_quotes_languages))
2417 h_quotes_style = "french";
2419 else if (is_known(h_language, known_german_quotes_languages))
2420 h_quotes_style = "german";
2422 else if (is_known(h_language, known_polish_quotes_languages))
2423 h_quotes_style = "polish";
2425 else if (is_known(h_language, known_russian_quotes_languages))
2426 h_quotes_style = "russian";
2428 else if (is_known(h_language, known_swedish_quotes_languages))
2429 h_quotes_style = "swedish";
2431 else if (is_known(h_language, known_swiss_quotes_languages))
2432 h_quotes_style = "swiss";
2434 else if (is_known(h_language, known_english_quotes_languages))
2435 h_quotes_style = "english";
2439 string Preamble::parseEncoding(Parser & p, string const & forceclass)
2441 TeX2LyXDocClass dummy;
2442 parse(p, forceclass, true, dummy);
2443 if (h_inputencoding != "auto-legacy" && h_inputencoding != "auto-legacy-plain")
2444 return h_inputencoding;
2449 string babel2lyx(string const & language)
2451 char const * const * where = is_known(language, known_languages);
2453 return known_coded_languages[where - known_languages];
2458 string lyx2babel(string const & language)
2460 char const * const * where = is_known(language, known_coded_languages);
2462 return known_languages[where - known_coded_languages];
2467 string Preamble::polyglossia2lyx(string const & language)
2469 char const * const * where = is_known(language, polyglossia_languages);
2471 return coded_polyglossia_languages[where - polyglossia_languages];
2476 string rgbcolor2code(string const & name)
2478 char const * const * where = is_known(name, known_basic_colors);
2480 // "red", "green" etc
2481 return known_basic_color_codes[where - known_basic_colors];
2483 // "255,0,0", "0,255,0" etc
2484 RGBColor c(RGBColorFromLaTeX(name));
2485 return X11hexname(c);