3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
9 * Full author contact details are available in file CREDITS.
20 #include "LayoutFile.h"
23 #include "TextClass.h"
26 #include "support/convert.h"
27 #include "support/FileName.h"
28 #include "support/filetools.h"
29 #include "support/lstrings.h"
31 #include "support/regex.h"
37 using namespace lyx::support;
46 // CJK languages are handled in text.cpp, polyglossia languages are listed
49 * known babel language names (including synonyms)
50 * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
51 * please keep this in sync with known_coded_languages line by line!
53 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
54 "american", "arabic", "arabtex", "australian", "austrian", "bahasa", "bahasai",
55 "bahasam", "basque", "belarusian", "bosnian", "brazil", "brazilian", "breton", "british",
56 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
57 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "francais",
58 "french", "frenchb", "frenchle", "frenchpro", "friulan", "galician", "german", "germanb",
59 "georgian", "greek", "hebrew", "hungarian", "icelandic", "indon", "indonesian",
60 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
61 "latvian", "lithuanian", "lowersorbian", "lsorbian", "macedonian", "magyar", "malay", "meyalu",
62 "mongolian", "naustrian", "newzealand", "ngerman", "ngermanb", "norsk", "nswissgerman",
63 "nynorsk", "piedmontese", "polutonikogreek", "polish", "portuges", "portuguese",
64 "romanian", "romansh", "russian", "russianb", "samin", "scottish", "serbian", "serbian-latin",
65 "slovak", "slovene", "spanish", "swedish", "swissgerman", "thai", "turkish", "turkmen",
66 "ukraineb", "ukrainian", "uppersorbian", "UKenglish", "USenglish", "usorbian",
71 * the same as known_languages with .lyx names
72 * please keep this in sync with known_languages line by line!
74 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
75 "american", "arabic_arabi", "arabic_arabtex", "australian", "austrian", "bahasa", "bahasa",
76 "bahasam", "basque", "belarusian", "bosnian", "brazilian", "brazilian", "breton", "british",
77 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
78 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "french",
79 "french", "french", "french", "french", "friulan", "galician", "german", "german",
80 "georgian", "greek", "hebrew", "magyar", "icelandic", "bahasa", "bahasa",
81 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
82 "latvian", "lithuanian", "lowersorbian", "lowersorbian", "macedonian", "magyar", "bahasam", "bahasam",
83 "mongolian", "naustrian", "newzealand", "ngerman", "ngerman", "norsk", "german-ch",
84 "nynorsk", "piedmontese", "polutonikogreek", "polish", "portuguese", "portuguese",
85 "romanian", "romansh", "russian", "russian", "samin", "scottish", "serbian", "serbian-latin",
86 "slovak", "slovene", "spanish", "swedish", "german-ch-old", "thai", "turkish", "turkmen",
87 "ukrainian", "ukrainian", "uppersorbian", "english", "english", "uppersorbian",
88 "vietnamese", "welsh",
91 /// languages with danish quotes (.lyx names)
92 const char * const known_danish_quotes_languages[] = {"danish", 0};
94 /// languages with english quotes (.lyx names)
95 const char * const known_english_quotes_languages[] = {"american", "australian",
96 "bahasa", "bahasam", "brazilian", "canadian", "chinese-simplified", "english",
97 "esperanto", "hebrew", "irish", "korean", "newzealand", "portuguese", "scottish",
100 /// languages with french quotes (.lyx names)
101 const char * const known_french_quotes_languages[] = {"albanian",
102 "arabic_arabi", "arabic_arabtex", "asturian", "basque", "canadien", "catalan",
103 "french", "friulan", "galician", "greek", "italian", "norsk", "nynorsk",
104 "piedmontese", "polutonikogreek", "russian", "spanish", "spanish-mexico",
105 "turkish", "turkmen", "ukrainian", "vietnamese", 0};
107 /// languages with german quotes (.lyx names)
108 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
109 "czech", "german", "georgian", "icelandic", "lithuanian", "lowersorbian", "macedonian",
110 "naustrian", "ngerman", "romansh", "serbian", "serbian-latin", "slovak", "slovene",
113 /// languages with polish quotes (.lyx names)
114 const char * const known_polish_quotes_languages[] = {"afrikaans", "bosnian", "croatian",
115 "dutch", "estonian", "magyar", "polish", "romanian", 0};
117 /// languages with swedish quotes (.lyx names)
118 const char * const known_swedish_quotes_languages[] = {"finnish",
121 /// known language packages from the times before babel
122 const char * const known_old_language_packages[] = {"french", "frenchle",
123 "frenchpro", "german", "ngerman", "pmfrench", 0};
125 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
127 const char * const known_roman_fonts[] = { "ae", "beraserif", "bookman",
128 "ccfonts", "chancery", "charter", "cmr", "cochineal", "crimson", "fourier",
129 "garamondx", "libertine", "libertine-type1", "lmodern", "mathdesign", "mathpazo",
130 "mathptmx", "newcent", "NotoSerif-TLF", "tgbonum", "tgchorus", "tgpagella", "tgschola",
131 "tgtermes", "utopia", 0};
133 const char * const known_sans_fonts[] = { "avant", "berasans", "biolinum-type1",
134 "cmbr", "cmss", "helvet", "iwona", "iwonac", "iwonal", "iwonalc", "kurier",
135 "kurierc", "kurierl", "kurierlc", "lmss", "NotoSans-TLF", "tgadventor", "tgheros", 0};
137 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
138 "courier", "lmtt", "luximono", "fourier", "libertineMono-type1", "lmodern",
139 "mathpazo", "mathptmx", "newcent", "NotoMono-TLF", "tgcursor", "txtt", 0};
141 const char * const known_math_fonts[] = { "eulervm", "newtxmath", 0};
143 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
144 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
145 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
146 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
147 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
149 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
150 "executivepaper", "legalpaper", "letterpaper", 0};
152 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
153 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
155 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
156 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
159 /// commands that can start an \if...\else...\endif sequence
160 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
161 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
162 "ifsidecap", "ifupgreek", 0};
164 const char * const known_basic_colors[] = {"black", "blue", "brown", "cyan",
165 "darkgray", "gray", "green", "lightgray", "lime", "magenta", "orange", "olive",
166 "pink", "purple", "red", "teal", "violet", "white", "yellow", 0};
168 const char * const known_basic_color_codes[] = {"#000000", "#0000ff", "#964B00", "#00ffff",
169 "#a9a9a9", "#808080", "#00ff00", "#d3d3d3", "#bfff00", "#ff00ff", "#ff7f00", "#808000",
170 "#ffc0cb", "#800080", "#ff0000", "#008080", "#8f00ff", "#ffffff", "#ffff00", 0};
172 /// conditional commands with three arguments like \@ifundefined{}{}{}
173 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
176 /// packages that work only in xetex
177 /// polyglossia is handled separately
178 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
179 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
180 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
182 /// packages that are automatically skipped if loaded by LyX
183 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
184 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color",
185 "float", "fontspec", "framed", "graphicx", "hhline", "ifthen", "longtable",
186 "makeidx", "minted", "multirow", "nomencl", "pdfpages", "prettyref", "refstyle",
187 "rotating", "rotfloat", "splitidx", "setspace", "subscript", "textcomp", "tipa",
188 "tipx", "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xcolor",
191 // codes used to remove packages that are loaded automatically by LyX.
192 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
193 const char package_beg_sep = '\001';
194 const char package_mid_sep = '\002';
195 const char package_end_sep = '\003';
198 // returns true if at least one of the options in what has been found
199 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
205 // the last language option is the document language (for babel and LyX)
206 // the last size option is the document font size
207 vector<string>::iterator it;
208 vector<string>::iterator position = opts.begin();
209 for (; *what; ++what) {
210 it = find(opts.begin(), opts.end(), *what);
211 if (it != opts.end()) {
212 if (it >= position) {
223 void delete_opt(vector<string> & opts, char const * const * what)
228 // remove found options from the list
229 // do this after handle_opt to avoid potential memory leaks
230 vector<string>::iterator it;
231 for (; *what; ++what) {
232 it = find(opts.begin(), opts.end(), *what);
233 if (it != opts.end())
240 * Split a package options string (keyval format) into a vector.
242 * authorformat=smallcaps,
244 * titleformat=colonsep,
245 * bibformat={tabular,ibidem,numbered}
247 vector<string> split_options(string const & input)
249 vector<string> options;
253 Token const & t = p.get_token();
254 if (t.asInput() == ",") {
255 options.push_back(trimSpaceAndEol(option));
257 } else if (t.asInput() == "=") {
260 if (p.next_token().asInput() == "{")
261 option += '{' + p.getArg('{', '}') + '}';
262 } else if (t.cat() != catSpace)
263 option += t.asInput();
267 options.push_back(trimSpaceAndEol(option));
274 * Retrieve a keyval option "name={value with=sign}" named \p name from
275 * \p options and return the value.
276 * The found option is also removed from \p options.
278 string process_keyval_opt(vector<string> & options, string name)
280 for (size_t i = 0; i < options.size(); ++i) {
281 vector<string> option;
282 split(options[i], option, '=');
283 if (option.size() < 2)
285 if (option[0] == name) {
286 options.erase(options.begin() + i);
287 option.erase(option.begin());
288 return join(option, "=");
294 } // anonymous namespace
298 * known polyglossia language names (including variants)
299 * FIXME: support spelling=old for german variants (german vs. ngerman LyX names etc)
301 const char * const Preamble::polyglossia_languages[] = {
302 "albanian", "american", "amharic", "ancient", "arabic", "armenian", "asturian", "australian",
303 "bahasai", "bahasam", "basque", "bengali", "brazil", "brazilian", "breton", "british", "bulgarian",
304 "catalan", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
305 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
306 "galician", "greek", "monotonic", "hebrew", "hindi",
307 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer",
308 "lao", "latin", "latvian", "lithuanian", "lsorbian", "magyar", "malayalam", "marathi",
309 "austrian", "newzealand", "german", "norsk", "nynorsk", "occitan",
310 "piedmontese", "polish", "polytonic", "portuges", "romanian", "romansh", "russian",
311 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovenian", "spanish", "swedish", "syriac",
312 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
313 "ukrainian", "urdu", "usorbian", "vietnamese", "welsh", 0};
314 // not yet supported by LyX: "korean", "nko"
317 * the same as polyglossia_languages with .lyx names
318 * please keep this in sync with polyglossia_languages line by line!
320 const char * const Preamble::coded_polyglossia_languages[] = {
321 "albanian", "american", "amharic", "ancientgreek", "arabic_arabi", "armenian", "asturian", "australian",
322 "bahasa", "bahasam", "basque", "bengali", "brazilian", "brazilian", "breton", "british", "bulgarian",
323 "catalan", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
324 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
325 "galician", "greek", "greek", "hebrew", "hindi",
326 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer",
327 "lao", "latin", "latvian", "lithuanian", "lowersorbian", "magyar", "malayalam", "marathi",
328 "naustrian","newzealand", "ngerman", "norsk", "nynorsk", "occitan",
329 "piedmontese", "polish", "polutonikogreek", "portuges", "romanian", "romansh", "russian",
330 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovene", "spanish", "swedish", "syriac",
331 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
332 "ukrainian", "urdu", "uppersorbian", "vietnamese", "welsh", 0};
333 // not yet supported by LyX: "korean-polyglossia", "nko"
336 bool Preamble::usePolyglossia() const
338 return h_use_non_tex_fonts && h_language_package == "default";
342 bool Preamble::indentParagraphs() const
344 return h_paragraph_separation == "indent";
348 bool Preamble::isPackageUsed(string const & package) const
350 return used_packages.find(package) != used_packages.end();
354 vector<string> Preamble::getPackageOptions(string const & package) const
356 map<string, vector<string> >::const_iterator it = used_packages.find(package);
357 if (it != used_packages.end())
359 return vector<string>();
363 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
365 auto_packages.insert(package);
369 void Preamble::addModule(string const & module)
371 used_modules.push_back(module);
375 void Preamble::suppressDate(bool suppress)
378 h_suppress_date = "true";
380 h_suppress_date = "false";
384 void Preamble::registerAuthor(std::string const & name)
386 Author author(from_utf8(name), empty_docstring());
387 author.setUsed(true);
388 authors_.record(author);
389 h_tracking_changes = "true";
390 h_output_changes = "true";
394 Author const & Preamble::getAuthor(std::string const & name) const
396 Author author(from_utf8(name), empty_docstring());
397 for (AuthorList::Authors::const_iterator it = authors_.begin();
398 it != authors_.end(); ++it)
401 static Author const dummy;
406 int Preamble::getSpecialTableColumnArguments(char c) const
408 map<char, int>::const_iterator it = special_columns_.find(c);
409 if (it == special_columns_.end())
415 void Preamble::add_package(string const & name, vector<string> & options)
417 // every package inherits the global options
418 if (used_packages.find(name) == used_packages.end())
419 used_packages[name] = split_options(h_options);
421 vector<string> & v = used_packages[name];
422 v.insert(v.end(), options.begin(), options.end());
423 if (name == "jurabib") {
424 // Don't output the order argument (see the cite command
425 // handling code in text.cpp).
426 vector<string>::iterator end =
427 remove(options.begin(), options.end(), "natbiborder");
428 end = remove(options.begin(), end, "jurabiborder");
429 options.erase(end, options.end());
436 // Given is a string like "scaled=0.9" or "Scale=0.9", return 0.9 * 100
437 bool scale_as_percentage(string const & scale, string & percentage)
439 string::size_type pos = scale.find('=');
440 if (pos != string::npos) {
441 string value = scale.substr(pos + 1);
442 if (isStrDbl(value)) {
443 percentage = convert<string>(
444 static_cast<int>(100 * convert<double>(value)));
452 string remove_braces(string const & value)
456 if (value[0] == '{' && value[value.length()-1] == '}')
457 return value.substr(1, value.length()-2);
461 } // anonymous namespace
464 Preamble::Preamble() : one_language(true), explicit_babel(false),
465 title_layout_found(false), index_number(0), h_font_cjk_set(false),
466 h_use_microtype("false")
470 h_biblio_style = "plain";
471 h_bibtex_command = "default";
472 h_cite_engine = "basic";
473 h_cite_engine_type = "default";
475 h_defskip = "medskip";
476 h_dynamic_quotes = false;
479 h_fontencoding = "default";
480 h_font_roman[0] = "default";
481 h_font_roman[1] = "default";
482 h_font_sans[0] = "default";
483 h_font_sans[1] = "default";
484 h_font_typewriter[0] = "default";
485 h_font_typewriter[1] = "default";
486 h_font_math[0] = "auto";
487 h_font_math[1] = "auto";
488 h_font_default_family = "default";
489 h_use_non_tex_fonts = false;
491 h_font_osf = "false";
492 h_font_sf_scale[0] = "100";
493 h_font_sf_scale[1] = "100";
494 h_font_tt_scale[0] = "100";
495 h_font_tt_scale[1] = "100";
497 h_is_mathindent = "0";
498 h_math_numbering_side = "default";
499 h_graphics = "default";
500 h_default_output_format = "default";
501 h_html_be_strict = "false";
502 h_html_css_as_file = "0";
503 h_html_math_output = "0";
504 h_index[0] = "Index";
505 h_index_command = "default";
506 h_inputencoding = "auto";
507 h_justification = "true";
508 h_language = "english";
509 h_language_package = "none";
511 h_maintain_unincluded_children = "false";
515 h_output_changes = "false";
517 //h_output_sync_macro
518 h_papercolumns = "1";
519 h_paperfontsize = "default";
520 h_paperorientation = "portrait";
521 h_paperpagestyle = "default";
523 h_papersize = "default";
524 h_paragraph_indentation = "default";
525 h_paragraph_separation = "indent";
530 h_pdf_bookmarks = "0";
531 h_pdf_bookmarksnumbered = "0";
532 h_pdf_bookmarksopen = "0";
533 h_pdf_bookmarksopenlevel = "1";
534 h_pdf_breaklinks = "0";
535 h_pdf_pdfborder = "0";
536 h_pdf_colorlinks = "0";
537 h_pdf_backref = "section";
538 h_pdf_pdfusetitle = "0";
540 //h_pdf_quoted_options;
541 h_quotes_style = "english";
543 h_shortcut[0] = "idx";
544 h_spacing = "single";
545 h_save_transient_properties = "true";
546 h_suppress_date = "false";
547 h_textclass = "article";
549 h_tracking_changes = "false";
550 h_use_bibtopic = "false";
551 h_use_dash_ligatures = "true";
552 h_use_indices = "false";
553 h_use_geometry = "false";
554 h_use_default_options = "false";
555 h_use_hyperref = "false";
556 h_use_microtype = "false";
557 h_use_refstyle = false;
558 h_use_minted = false;
559 h_use_packages["amsmath"] = "1";
560 h_use_packages["amssymb"] = "0";
561 h_use_packages["cancel"] = "0";
562 h_use_packages["esint"] = "1";
563 h_use_packages["mhchem"] = "0";
564 h_use_packages["mathdots"] = "0";
565 h_use_packages["mathtools"] = "0";
566 h_use_packages["stackrel"] = "0";
567 h_use_packages["stmaryrd"] = "0";
568 h_use_packages["undertilde"] = "0";
572 void Preamble::handle_hyperref(vector<string> & options)
574 // FIXME swallow inputencoding changes that might surround the
575 // hyperref setup if it was written by LyX
576 h_use_hyperref = "true";
577 // swallow "unicode=true", since LyX does always write that
578 vector<string>::iterator it =
579 find(options.begin(), options.end(), "unicode=true");
580 if (it != options.end())
582 it = find(options.begin(), options.end(), "pdfusetitle");
583 if (it != options.end()) {
584 h_pdf_pdfusetitle = "1";
587 string bookmarks = process_keyval_opt(options, "bookmarks");
588 if (bookmarks == "true")
589 h_pdf_bookmarks = "1";
590 else if (bookmarks == "false")
591 h_pdf_bookmarks = "0";
592 if (h_pdf_bookmarks == "1") {
593 string bookmarksnumbered =
594 process_keyval_opt(options, "bookmarksnumbered");
595 if (bookmarksnumbered == "true")
596 h_pdf_bookmarksnumbered = "1";
597 else if (bookmarksnumbered == "false")
598 h_pdf_bookmarksnumbered = "0";
599 string bookmarksopen =
600 process_keyval_opt(options, "bookmarksopen");
601 if (bookmarksopen == "true")
602 h_pdf_bookmarksopen = "1";
603 else if (bookmarksopen == "false")
604 h_pdf_bookmarksopen = "0";
605 if (h_pdf_bookmarksopen == "1") {
606 string bookmarksopenlevel =
607 process_keyval_opt(options, "bookmarksopenlevel");
608 if (!bookmarksopenlevel.empty())
609 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
612 string breaklinks = process_keyval_opt(options, "breaklinks");
613 if (breaklinks == "true")
614 h_pdf_breaklinks = "1";
615 else if (breaklinks == "false")
616 h_pdf_breaklinks = "0";
617 string pdfborder = process_keyval_opt(options, "pdfborder");
618 if (pdfborder == "{0 0 0}")
619 h_pdf_pdfborder = "1";
620 else if (pdfborder == "{0 0 1}")
621 h_pdf_pdfborder = "0";
622 string backref = process_keyval_opt(options, "backref");
623 if (!backref.empty())
624 h_pdf_backref = backref;
625 string colorlinks = process_keyval_opt(options, "colorlinks");
626 if (colorlinks == "true")
627 h_pdf_colorlinks = "1";
628 else if (colorlinks == "false")
629 h_pdf_colorlinks = "0";
630 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
631 if (!pdfpagemode.empty())
632 h_pdf_pagemode = pdfpagemode;
633 string pdftitle = process_keyval_opt(options, "pdftitle");
634 if (!pdftitle.empty()) {
635 h_pdf_title = remove_braces(pdftitle);
637 string pdfauthor = process_keyval_opt(options, "pdfauthor");
638 if (!pdfauthor.empty()) {
639 h_pdf_author = remove_braces(pdfauthor);
641 string pdfsubject = process_keyval_opt(options, "pdfsubject");
642 if (!pdfsubject.empty())
643 h_pdf_subject = remove_braces(pdfsubject);
644 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
645 if (!pdfkeywords.empty())
646 h_pdf_keywords = remove_braces(pdfkeywords);
647 if (!options.empty()) {
648 if (!h_pdf_quoted_options.empty())
649 h_pdf_quoted_options += ',';
650 h_pdf_quoted_options += join(options, ",");
656 void Preamble::handle_geometry(vector<string> & options)
658 h_use_geometry = "true";
659 vector<string>::iterator it;
661 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
662 h_paperorientation = "landscape";
666 // keyval version: "paper=letter"
667 string paper = process_keyval_opt(options, "paper");
669 h_papersize = paper + "paper";
670 // alternative version: "letterpaper"
671 handle_opt(options, known_paper_sizes, h_papersize);
672 delete_opt(options, known_paper_sizes);
674 char const * const * margin = known_paper_margins;
675 for (; *margin; ++margin) {
676 string value = process_keyval_opt(options, *margin);
677 if (!value.empty()) {
678 int k = margin - known_paper_margins;
679 string name = known_coded_paper_margins[k];
680 h_margins += '\\' + name + ' ' + value + '\n';
686 void Preamble::handle_package(Parser &p, string const & name,
687 string const & opts, bool in_lyx_preamble,
690 vector<string> options = split_options(opts);
691 add_package(name, options);
693 if (is_known(name, known_xetex_packages)) {
695 h_use_non_tex_fonts = true;
696 registerAutomaticallyLoadedPackage("fontspec");
697 if (h_inputencoding == "auto")
698 p.setEncoding("UTF-8");
702 if (is_known(name, known_roman_fonts))
703 h_font_roman[0] = name;
705 if (name == "fourier") {
706 h_font_roman[0] = "utopia";
707 // when font uses real small capitals
708 if (opts == "expert")
712 if (name == "garamondx") {
713 h_font_roman[0] = "garamondx";
718 if (name == "libertine") {
719 h_font_roman[0] = "libertine";
720 // this automatically invokes biolinum
721 h_font_sans[0] = "biolinum";
724 else if (opts == "lining")
725 h_font_osf = "false";
728 if (name == "libertine-type1") {
729 h_font_roman[0] = "libertine";
730 // NOTE: contrary to libertine.sty, libertine-type1
731 // does not automatically invoke biolinum
732 if (opts == "lining")
733 h_font_osf = "false";
734 else if (opts == "osf")
738 if (name == "mathdesign") {
739 if (opts.find("charter") != string::npos)
740 h_font_roman[0] = "md-charter";
741 if (opts.find("garamond") != string::npos)
742 h_font_roman[0] = "md-garamond";
743 if (opts.find("utopia") != string::npos)
744 h_font_roman[0] = "md-utopia";
745 if (opts.find("expert") != string::npos) {
751 else if (name == "mathpazo")
752 h_font_roman[0] = "palatino";
754 else if (name == "mathptmx")
755 h_font_roman[0] = "times";
757 if (name == "crimson")
758 h_font_roman[0] = "cochineal";
760 if (name == "cochineal") {
761 h_font_roman[0] = "cochineal";
762 // cochineal can have several options, e.g. [proportional,osf]
763 string::size_type pos = opts.find("osf");
764 if (pos != string::npos)
768 if (name == "noto") {
769 // noto can have several options
771 h_font_roman[0] = "NotoSerif-TLF";
772 string::size_type pos = opts.find("rm");
773 if (pos != string::npos)
774 h_font_roman[0] = "NotoSerif-TLF";
775 pos = opts.find("sf");
776 if (pos != string::npos)
777 h_font_sans[0] = "NotoSans-TLF";
778 pos = opts.find("nott");
779 if (pos != string::npos) {
780 h_font_roman[0] = "NotoSerif-TLF";
781 h_font_sans[0] = "NotoSans-TLF";
783 // noto as typewriter is handled in handling of \ttdefault
784 // special cases are handled in handling of \rmdefault and \sfdefault
788 if (is_known(name, known_sans_fonts)) {
789 h_font_sans[0] = name;
790 if (options.size() >= 1) {
791 if (scale_as_percentage(opts, h_font_sf_scale[0]))
796 if (name == "biolinum-type1") {
797 h_font_sans[0] = "biolinum";
798 // biolinum can have several options, e.g. [osf,scaled=0.97]
799 string::size_type pos = opts.find("osf");
800 if (pos != string::npos)
805 if (is_known(name, known_typewriter_fonts)) {
806 // fourier can be set as roman font _only_
807 // fourier as typewriter is handled in handling of \ttdefault
808 if (name != "fourier") {
809 h_font_typewriter[0] = name;
810 if (options.size() >= 1) {
811 if (scale_as_percentage(opts, h_font_tt_scale[0]))
817 if (name == "libertineMono-type1") {
818 h_font_typewriter[0] = "libertine-mono";
821 // font uses old-style figure
826 if (is_known(name, known_math_fonts))
827 h_font_math[0] = name;
829 if (name == "newtxmath") {
831 h_font_math[0] = "newtxmath";
832 else if (opts == "garamondx")
833 h_font_math[0] = "garamondx-ntxm";
834 else if (opts == "libertine")
835 h_font_math[0] = "libertine-ntxm";
836 else if (opts == "minion")
837 h_font_math[0] = "minion-ntxm";
838 else if (opts == "cochineal")
839 h_font_math[0] = "cochineal-ntxm";
844 h_font_math[0] = "iwona-math";
846 if (name == "kurier")
848 h_font_math[0] = "kurier-math";
850 // after the detection and handling of special cases, we can remove the
851 // fonts, otherwise they would appear in the preamble, see bug #7856
852 if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
853 || is_known(name, known_typewriter_fonts) || is_known(name, known_math_fonts))
855 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
856 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
857 name == "esint" || name == "mhchem" || name == "mathdots" ||
858 name == "mathtools" || name == "stackrel" ||
859 name == "stmaryrd" || name == "undertilde")
860 h_use_packages[name] = "2";
862 else if (name == "babel") {
863 h_language_package = "default";
864 // One might think we would have to do nothing if babel is loaded
865 // without any options to prevent pollution of the preamble with this
866 // babel call in every roundtrip.
867 // But the user could have defined babel-specific things afterwards. So
868 // we need to keep it in the preamble to prevent cases like bug #7861.
870 // check if more than one option was used - used later for inputenc
871 if (options.begin() != options.end() - 1)
872 one_language = false;
873 // babel takes the last language of the option of its \usepackage
874 // call as document language. If there is no such language option, the
875 // last language in the documentclass options is used.
876 handle_opt(options, known_languages, h_language);
877 // translate the babel name to a LyX name
878 h_language = babel2lyx(h_language);
879 if (h_language == "japanese") {
880 // For Japanese, the encoding isn't indicated in the source
881 // file, and there's really not much we can do. We could
882 // 1) offer a list of possible encodings to choose from, or
883 // 2) determine the encoding of the file by inspecting it.
884 // For the time being, we leave the encoding alone so that
885 // we don't get iconv errors when making a wrong guess, and
886 // we will output a note at the top of the document
887 // explaining what to do.
888 Encoding const * const enc = encodings.fromIconvName(
889 p.getEncoding(), Encoding::japanese, false);
891 h_inputencoding = enc->name();
892 is_nonCJKJapanese = true;
893 // in this case babel can be removed from the preamble
894 registerAutomaticallyLoadedPackage("babel");
896 // If babel is called with options, LyX puts them by default into the
897 // document class options. This works for most languages, except
898 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
899 // perhaps in future others.
900 // Therefore keep the babel call as it is as the user might have
902 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
904 delete_opt(options, known_languages);
906 h_preamble << "\\usepackage{babel}\n";
907 explicit_babel = true;
911 else if (name == "polyglossia") {
912 h_language_package = "default";
913 h_default_output_format = "pdf4";
914 h_use_non_tex_fonts = true;
916 registerAutomaticallyLoadedPackage("xunicode");
917 if (h_inputencoding == "auto")
918 p.setEncoding("UTF-8");
921 else if (name == "CJK") {
922 // set the encoding to "auto" because it might be set to "default" by the babel handling
923 // and this would not be correct for CJK
924 if (h_inputencoding == "default")
925 h_inputencoding = "auto";
926 registerAutomaticallyLoadedPackage("CJK");
929 else if (name == "CJKutf8") {
930 h_inputencoding = "utf8-cjk";
931 p.setEncoding("UTF-8");
932 registerAutomaticallyLoadedPackage("CJKutf8");
935 else if (name == "fontenc") {
936 h_fontencoding = getStringFromVector(options, ",");
937 /* We could do the following for better round trip support,
938 * but this makes the document less portable, so I skip it:
939 if (h_fontencoding == lyxrc.fontenc)
940 h_fontencoding = "global";
945 else if (name == "inputenc" || name == "luainputenc") {
946 // h_inputencoding is only set when there is not more than one
947 // inputenc option because otherwise h_inputencoding must be
948 // set to "auto" (the default encoding of the document language)
949 // Therefore check that exactly one option is passed to inputenc.
950 // It is also only set when there is not more than one babel
952 if (!options.empty()) {
953 string const encoding = options.back();
954 Encoding const * const enc = encodings.fromLaTeXName(
955 encoding, Encoding::inputenc, true);
958 cerr << "Unknown encoding " << encoding
959 << ". Ignoring." << std::endl;
961 if (!enc->unsafe() && options.size() == 1 && one_language == true)
962 h_inputencoding = enc->name();
963 p.setEncoding(enc->iconvName());
969 else if (name == "srcltx") {
972 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
975 h_output_sync_macro = "\\usepackage{srcltx}";
978 else if (is_known(name, known_old_language_packages)) {
979 // known language packages from the times before babel
980 // if they are found and not also babel, they will be used as
981 // custom language package
982 h_language_package = "\\usepackage{" + name + "}";
985 else if (name == "lyxskak") {
986 // ignore this and its options
987 const char * const o[] = {"ps", "mover", 0};
988 delete_opt(options, o);
991 else if (is_known(name, known_lyx_packages) && options.empty()) {
992 if (name == "splitidx")
993 h_use_indices = "true";
994 else if (name == "minted")
996 else if (name == "refstyle")
997 h_use_refstyle = true;
998 else if (name == "prettyref")
999 h_use_refstyle = false;
1000 if (!in_lyx_preamble) {
1001 h_preamble << package_beg_sep << name
1002 << package_mid_sep << "\\usepackage{"
1004 if (p.next_token().cat() == catNewline ||
1005 (p.next_token().cat() == catSpace &&
1006 p.next_next_token().cat() == catNewline))
1008 h_preamble << package_end_sep;
1012 else if (name == "geometry")
1013 handle_geometry(options);
1015 else if (name == "subfig")
1016 ; // ignore this FIXME: Use the package separator mechanism instead
1018 else if (char const * const * where = is_known(name, known_languages))
1019 h_language = known_coded_languages[where - known_languages];
1021 else if (name == "natbib") {
1022 h_biblio_style = "plainnat";
1023 h_cite_engine = "natbib";
1024 h_cite_engine_type = "authoryear";
1025 vector<string>::iterator it =
1026 find(options.begin(), options.end(), "authoryear");
1027 if (it != options.end())
1030 it = find(options.begin(), options.end(), "numbers");
1031 if (it != options.end()) {
1032 h_cite_engine_type = "numerical";
1038 else if (name == "jurabib") {
1039 h_biblio_style = "jurabib";
1040 h_cite_engine = "jurabib";
1041 h_cite_engine_type = "authoryear";
1044 else if (name == "bibtopic")
1045 h_use_bibtopic = "true";
1047 else if (name == "hyperref")
1048 handle_hyperref(options);
1050 else if (name == "algorithm2e") {
1051 // Load "algorithm2e" module
1052 addModule("algorithm2e");
1053 // Add the package options to the global document options
1054 if (!options.empty()) {
1055 if (h_options.empty())
1056 h_options = join(options, ",");
1058 h_options += ',' + join(options, ",");
1061 else if (name == "microtype") {
1062 //we internally support only microtype without params
1063 if (options.empty())
1064 h_use_microtype = "true";
1066 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1069 else if (!in_lyx_preamble) {
1070 if (options.empty())
1071 h_preamble << "\\usepackage{" << name << '}';
1073 h_preamble << "\\usepackage[" << opts << "]{"
1077 if (p.next_token().cat() == catNewline ||
1078 (p.next_token().cat() == catSpace &&
1079 p.next_next_token().cat() == catNewline))
1083 // We need to do something with the options...
1084 if (!options.empty() && !detectEncoding)
1085 cerr << "Ignoring options '" << join(options, ",")
1086 << "' of package " << name << '.' << endl;
1088 // remove the whitespace
1093 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1096 Token t = p.get_token();
1097 if (t.cat() == catEscape &&
1098 is_known(t.cs(), known_if_commands))
1099 handle_if(p, in_lyx_preamble);
1101 if (!in_lyx_preamble)
1102 h_preamble << t.asInput();
1103 if (t.cat() == catEscape && t.cs() == "fi")
1110 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1112 // set the quote language
1113 // LyX only knows the following quotes languages:
1114 // english, swedish, german, polish, french and danish
1115 // (quotes for "japanese" and "chinese-traditional" are missing because
1116 // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
1117 // conversion list taken from
1118 // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
1119 // (quotes for kazakh and interlingua are unknown)
1121 if (is_known(h_language, known_danish_quotes_languages))
1122 h_quotes_style = "danish";
1124 else if (is_known(h_language, known_french_quotes_languages))
1125 h_quotes_style = "french";
1127 else if (is_known(h_language, known_german_quotes_languages))
1128 h_quotes_style = "german";
1130 else if (is_known(h_language, known_polish_quotes_languages))
1131 h_quotes_style = "polish";
1133 else if (is_known(h_language, known_swedish_quotes_languages))
1134 h_quotes_style = "swedish";
1136 else if (is_known(h_language, known_english_quotes_languages))
1137 h_quotes_style = "english";
1139 if (contains(h_float_placement, "H"))
1140 registerAutomaticallyLoadedPackage("float");
1141 if (h_spacing != "single" && h_spacing != "default")
1142 registerAutomaticallyLoadedPackage("setspace");
1143 if (h_use_packages["amsmath"] == "2") {
1144 // amsbsy and amstext are already provided by amsmath
1145 registerAutomaticallyLoadedPackage("amsbsy");
1146 registerAutomaticallyLoadedPackage("amstext");
1149 // output the LyX file settings
1150 // Important: Keep the version formatting in sync with LyX and
1151 // lyx2lyx (bug 7951)
1152 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1153 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1154 << lyx_version_minor << '\n'
1155 << "\\lyxformat " << LYX_FORMAT << '\n'
1156 << "\\begin_document\n"
1157 << "\\begin_header\n"
1158 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1159 << "\\origin " << origin << "\n"
1160 << "\\textclass " << h_textclass << "\n";
1161 string const raw = subdoc ? empty_string() : h_preamble.str();
1163 os << "\\begin_preamble\n";
1164 for (string::size_type i = 0; i < raw.size(); ++i) {
1165 if (raw[i] == package_beg_sep) {
1166 // Here follows some package loading code that
1167 // must be skipped if the package is loaded
1169 string::size_type j = raw.find(package_mid_sep, i);
1170 if (j == string::npos)
1172 string::size_type k = raw.find(package_end_sep, j);
1173 if (k == string::npos)
1175 string const package = raw.substr(i + 1, j - i - 1);
1176 string const replacement = raw.substr(j + 1, k - j - 1);
1177 if (auto_packages.find(package) == auto_packages.end())
1183 os << "\n\\end_preamble\n";
1185 if (!h_options.empty())
1186 os << "\\options " << h_options << "\n";
1187 os << "\\use_default_options " << h_use_default_options << "\n";
1188 if (!used_modules.empty()) {
1189 os << "\\begin_modules\n";
1190 vector<string>::const_iterator const end = used_modules.end();
1191 vector<string>::const_iterator it = used_modules.begin();
1192 for (; it != end; ++it)
1194 os << "\\end_modules\n";
1196 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1197 << "\\language " << h_language << "\n"
1198 << "\\language_package " << h_language_package << "\n"
1199 << "\\inputencoding " << h_inputencoding << "\n"
1200 << "\\fontencoding " << h_fontencoding << "\n"
1201 << "\\font_roman \"" << h_font_roman[0]
1202 << "\" \"" << h_font_roman[1] << "\"\n"
1203 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1204 << "\\font_typewriter \"" << h_font_typewriter[0]
1205 << "\" \"" << h_font_typewriter[1] << "\"\n"
1206 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1207 << "\\font_default_family " << h_font_default_family << "\n"
1208 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1209 << "\\font_sc " << h_font_sc << "\n"
1210 << "\\font_osf " << h_font_osf << "\n"
1211 << "\\font_sf_scale " << h_font_sf_scale[0]
1212 << ' ' << h_font_sf_scale[1] << '\n'
1213 << "\\font_tt_scale " << h_font_tt_scale[0]
1214 << ' ' << h_font_tt_scale[1] << '\n';
1215 if (!h_font_cjk.empty())
1216 os << "\\font_cjk " << h_font_cjk << '\n';
1217 os << "\\use_microtype " << h_use_microtype << '\n'
1218 << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
1219 << "\\graphics " << h_graphics << '\n'
1220 << "\\default_output_format " << h_default_output_format << "\n"
1221 << "\\output_sync " << h_output_sync << "\n";
1222 if (h_output_sync == "1")
1223 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1224 os << "\\bibtex_command " << h_bibtex_command << "\n"
1225 << "\\index_command " << h_index_command << "\n";
1226 if (!h_float_placement.empty())
1227 os << "\\float_placement " << h_float_placement << "\n";
1228 os << "\\paperfontsize " << h_paperfontsize << "\n"
1229 << "\\spacing " << h_spacing << "\n"
1230 << "\\use_hyperref " << h_use_hyperref << '\n';
1231 if (h_use_hyperref == "true") {
1232 if (!h_pdf_title.empty())
1233 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1234 if (!h_pdf_author.empty())
1235 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1236 if (!h_pdf_subject.empty())
1237 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1238 if (!h_pdf_keywords.empty())
1239 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1240 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1241 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1242 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1243 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1244 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1245 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1246 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1247 "\\pdf_backref " << h_pdf_backref << "\n"
1248 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1249 if (!h_pdf_pagemode.empty())
1250 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1251 if (!h_pdf_quoted_options.empty())
1252 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1254 os << "\\papersize " << h_papersize << "\n"
1255 << "\\use_geometry " << h_use_geometry << '\n';
1256 for (map<string, string>::const_iterator it = h_use_packages.begin();
1257 it != h_use_packages.end(); ++it)
1258 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1259 os << "\\cite_engine " << h_cite_engine << '\n'
1260 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1261 << "\\biblio_style " << h_biblio_style << "\n"
1262 << "\\use_bibtopic " << h_use_bibtopic << "\n"
1263 << "\\use_indices " << h_use_indices << "\n"
1264 << "\\paperorientation " << h_paperorientation << '\n'
1265 << "\\suppress_date " << h_suppress_date << '\n'
1266 << "\\justification " << h_justification << '\n'
1267 << "\\use_refstyle " << h_use_refstyle << '\n'
1268 << "\\use_minted " << h_use_minted << '\n';
1269 if (!h_fontcolor.empty())
1270 os << "\\fontcolor " << h_fontcolor << '\n';
1271 if (!h_notefontcolor.empty())
1272 os << "\\notefontcolor " << h_notefontcolor << '\n';
1273 if (!h_backgroundcolor.empty())
1274 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1275 if (!h_boxbgcolor.empty())
1276 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1277 if (index_number != 0)
1278 for (int i = 0; i < index_number; i++) {
1279 os << "\\index " << h_index[i] << '\n'
1280 << "\\shortcut " << h_shortcut[i] << '\n'
1281 << "\\color " << h_color << '\n'
1285 os << "\\index " << h_index[0] << '\n'
1286 << "\\shortcut " << h_shortcut[0] << '\n'
1287 << "\\color " << h_color << '\n'
1291 << "\\secnumdepth " << h_secnumdepth << "\n"
1292 << "\\tocdepth " << h_tocdepth << "\n"
1293 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1294 if (h_paragraph_separation == "skip")
1295 os << "\\defskip " << h_defskip << "\n";
1297 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1298 os << "\\is_math_indent " << h_is_mathindent << "\n";
1299 if (!h_mathindentation.empty())
1300 os << "\\math_indentation " << h_mathindentation << "\n";
1301 os << "\\math_numbering_side " << h_math_numbering_side << "\n";
1302 os << "\\quotes_style " << h_quotes_style << "\n"
1303 << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
1304 << "\\papercolumns " << h_papercolumns << "\n"
1305 << "\\papersides " << h_papersides << "\n"
1306 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1307 if (!h_listings_params.empty())
1308 os << "\\listings_params " << h_listings_params << "\n";
1309 os << "\\tracking_changes " << h_tracking_changes << "\n"
1310 << "\\output_changes " << h_output_changes << "\n"
1311 << "\\html_math_output " << h_html_math_output << "\n"
1312 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1313 << "\\html_be_strict " << h_html_be_strict << "\n"
1315 << "\\end_header\n\n"
1316 << "\\begin_body\n";
1321 void Preamble::parse(Parser & p, string const & forceclass,
1322 TeX2LyXDocClass & tc)
1324 // initialize fixed types
1325 special_columns_['D'] = 3;
1326 parse(p, forceclass, false, tc);
1330 void Preamble::parse(Parser & p, string const & forceclass,
1331 bool detectEncoding, TeX2LyXDocClass & tc)
1333 bool is_full_document = false;
1334 bool is_lyx_file = false;
1335 bool in_lyx_preamble = false;
1337 // determine whether this is a full document or a fragment for inclusion
1339 Token const & t = p.get_token();
1341 if (t.cat() == catEscape && t.cs() == "documentclass") {
1342 is_full_document = true;
1348 if (detectEncoding && !is_full_document)
1351 while (is_full_document && p.good()) {
1352 if (detectEncoding && h_inputencoding != "auto" &&
1353 h_inputencoding != "default")
1356 Token const & t = p.get_token();
1359 if (!detectEncoding)
1360 cerr << "t: " << t << '\n';
1366 if (!in_lyx_preamble &&
1367 (t.cat() == catLetter ||
1368 t.cat() == catSuper ||
1369 t.cat() == catSub ||
1370 t.cat() == catOther ||
1371 t.cat() == catMath ||
1372 t.cat() == catActive ||
1373 t.cat() == catBegin ||
1374 t.cat() == catEnd ||
1375 t.cat() == catAlign ||
1376 t.cat() == catParameter))
1377 h_preamble << t.cs();
1379 else if (!in_lyx_preamble &&
1380 (t.cat() == catSpace || t.cat() == catNewline))
1381 h_preamble << t.asInput();
1383 else if (t.cat() == catComment) {
1384 static regex const islyxfile("%% LyX .* created this file");
1385 static regex const usercommands("User specified LaTeX commands");
1387 string const comment = t.asInput();
1389 // magically switch encoding default if it looks like XeLaTeX
1390 static string const magicXeLaTeX =
1391 "% This document must be compiled with XeLaTeX ";
1392 if (comment.size() > magicXeLaTeX.size()
1393 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1394 && h_inputencoding == "auto") {
1395 if (!detectEncoding)
1396 cerr << "XeLaTeX comment found, switching to UTF8\n";
1397 h_inputencoding = "utf8";
1400 if (regex_search(comment, sub, islyxfile)) {
1402 in_lyx_preamble = true;
1403 } else if (is_lyx_file
1404 && regex_search(comment, sub, usercommands))
1405 in_lyx_preamble = false;
1406 else if (!in_lyx_preamble)
1407 h_preamble << t.asInput();
1410 else if (t.cs() == "pagestyle")
1411 h_paperpagestyle = p.verbatim_item();
1413 else if (t.cs() == "setdefaultlanguage") {
1415 // We don't yet care about non-language variant options
1416 // because LyX doesn't support this yet, see bug #8214
1418 string langopts = p.getOpt();
1419 // check if the option contains a variant, if yes, extract it
1420 string::size_type pos_var = langopts.find("variant");
1421 string::size_type i = langopts.find(',', pos_var);
1422 string::size_type k = langopts.find('=', pos_var);
1423 if (pos_var != string::npos){
1425 if (i == string::npos)
1426 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1428 variant = langopts.substr(k + 1, i - k - 1);
1429 h_language = variant;
1433 h_language = p.verbatim_item();
1434 //finally translate the poyglossia name to a LyX name
1435 h_language = polyglossia2lyx(h_language);
1438 else if (t.cs() == "setotherlanguage") {
1439 // We don't yet care about the option because LyX doesn't
1440 // support this yet, see bug #8214
1441 p.hasOpt() ? p.getOpt() : string();
1445 else if (t.cs() == "setmainfont") {
1446 // we don't care about the option
1447 p.hasOpt() ? p.getOpt() : string();
1448 h_font_roman[1] = p.getArg('{', '}');
1451 else if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1452 // LyX currently only supports the scale option
1455 string fontopts = p.getArg('[', ']');
1456 // check if the option contains a scaling, if yes, extract it
1457 string::size_type pos = fontopts.find("Scale");
1458 if (pos != string::npos) {
1459 string::size_type i = fontopts.find(',', pos);
1460 if (i == string::npos)
1461 scale_as_percentage(fontopts.substr(pos + 1), scale);
1463 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1466 if (t.cs() == "setsansfont") {
1468 h_font_sf_scale[1] = scale;
1469 h_font_sans[1] = p.getArg('{', '}');
1472 h_font_tt_scale[1] = scale;
1473 h_font_typewriter[1] = p.getArg('{', '}');
1477 else if (t.cs() == "date") {
1478 string argument = p.getArg('{', '}');
1479 if (argument.empty())
1480 h_suppress_date = "true";
1482 h_preamble << t.asInput() << '{' << argument << '}';
1485 else if (t.cs() == "color") {
1486 string const space =
1487 (p.hasOpt() ? p.getOpt() : string());
1488 string argument = p.getArg('{', '}');
1489 // check the case that a standard color is used
1490 if (space.empty() && is_known(argument, known_basic_colors)) {
1491 h_fontcolor = rgbcolor2code(argument);
1492 registerAutomaticallyLoadedPackage("color");
1493 } else if (space.empty() && argument == "document_fontcolor")
1494 registerAutomaticallyLoadedPackage("color");
1495 // check the case that LyX's document_fontcolor is defined
1496 // but not used for \color
1498 h_preamble << t.asInput();
1500 h_preamble << space;
1501 h_preamble << '{' << argument << '}';
1502 // the color might already be set because \definecolor
1503 // is parsed before this
1508 else if (t.cs() == "pagecolor") {
1509 string argument = p.getArg('{', '}');
1510 // check the case that a standard color is used
1511 if (is_known(argument, known_basic_colors)) {
1512 h_backgroundcolor = rgbcolor2code(argument);
1513 } else if (argument == "page_backgroundcolor")
1514 registerAutomaticallyLoadedPackage("color");
1515 // check the case that LyX's page_backgroundcolor is defined
1516 // but not used for \pagecolor
1518 h_preamble << t.asInput() << '{' << argument << '}';
1519 // the color might already be set because \definecolor
1520 // is parsed before this
1521 h_backgroundcolor = "";
1525 else if (t.cs() == "makeatletter") {
1526 // LyX takes care of this
1527 p.setCatcode('@', catLetter);
1530 else if (t.cs() == "makeatother") {
1531 // LyX takes care of this
1532 p.setCatcode('@', catOther);
1535 else if (t.cs() == "makeindex") {
1536 // LyX will re-add this if a print index command is found
1540 else if (t.cs() == "newindex") {
1541 string const indexname = p.getArg('[', ']');
1542 string const shortcut = p.verbatim_item();
1543 if (!indexname.empty())
1544 h_index[index_number] = indexname;
1546 h_index[index_number] = shortcut;
1547 h_shortcut[index_number] = shortcut;
1552 else if (t.cs() == "RS@ifundefined") {
1553 string const name = p.verbatim_item();
1554 string const body1 = p.verbatim_item();
1555 string const body2 = p.verbatim_item();
1556 // only non-lyxspecific stuff
1557 if (in_lyx_preamble &&
1558 (name == "subsecref" || name == "thmref" || name == "lemref"))
1562 ss << '\\' << t.cs();
1563 ss << '{' << name << '}'
1564 << '{' << body1 << '}'
1565 << '{' << body2 << '}';
1566 h_preamble << ss.str();
1570 else if (t.cs() == "AtBeginDocument") {
1571 string const name = p.verbatim_item();
1572 // only non-lyxspecific stuff
1573 if (in_lyx_preamble &&
1574 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
1575 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
1576 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
1577 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
1578 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
1579 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
1580 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
1581 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
1582 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
1583 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
1584 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
1585 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
1586 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
1587 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
1588 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
1592 ss << '\\' << t.cs();
1593 ss << '{' << name << '}';
1594 h_preamble << ss.str();
1598 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1599 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1600 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1601 || t.cs() == "DeclareRobustCommand"
1602 || t.cs() == "DeclareRobustCommandx"
1603 || t.cs() == "ProvideTextCommandDefault"
1604 || t.cs() == "DeclareMathAccent") {
1606 if (p.next_token().character() == '*') {
1610 string const name = p.verbatim_item();
1611 string const opt1 = p.getFullOpt();
1612 string const opt2 = p.getFullOpt();
1613 string const body = p.verbatim_item();
1614 // store the in_lyx_preamble setting
1615 bool const was_in_lyx_preamble = in_lyx_preamble;
1617 if (name == "\\rmdefault")
1618 if (is_known(body, known_roman_fonts)) {
1619 h_font_roman[0] = body;
1621 in_lyx_preamble = true;
1623 if (name == "\\sfdefault")
1624 if (is_known(body, known_sans_fonts)) {
1625 h_font_sans[0] = body;
1627 in_lyx_preamble = true;
1629 if (name == "\\ttdefault")
1630 if (is_known(body, known_typewriter_fonts)) {
1631 h_font_typewriter[0] = body;
1633 in_lyx_preamble = true;
1635 if (name == "\\familydefault") {
1636 string family = body;
1637 // remove leading "\"
1638 h_font_default_family = family.erase(0,1);
1640 in_lyx_preamble = true;
1643 // remove LyX-specific definitions that are re-added by LyX
1645 // \lyxline is an ancient command that is converted by tex2lyx into
1646 // a \rule therefore remove its preamble code
1647 if (name == "\\lyxdot" || name == "\\lyxarrow"
1648 || name == "\\lyxline" || name == "\\LyX") {
1650 in_lyx_preamble = true;
1653 // Add the command to the known commands
1654 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1656 // only non-lyxspecific stuff
1657 if (!in_lyx_preamble) {
1659 ss << '\\' << t.cs();
1662 ss << '{' << name << '}' << opt1 << opt2
1663 << '{' << body << "}";
1664 h_preamble << ss.str();
1666 ostream & out = in_preamble ? h_preamble : os;
1667 out << "\\" << t.cs() << "{" << name << "}"
1668 << opts << "{" << body << "}";
1671 // restore the in_lyx_preamble setting
1672 in_lyx_preamble = was_in_lyx_preamble;
1675 else if (t.cs() == "documentclass") {
1676 vector<string>::iterator it;
1677 vector<string> opts = split_options(p.getArg('[', ']'));
1678 handle_opt(opts, known_fontsizes, h_paperfontsize);
1679 delete_opt(opts, known_fontsizes);
1680 // delete "pt" at the end
1681 string::size_type i = h_paperfontsize.find("pt");
1682 if (i != string::npos)
1683 h_paperfontsize.erase(i);
1684 // The documentclass options are always parsed before the options
1685 // of the babel call so that a language cannot overwrite the babel
1687 handle_opt(opts, known_languages, h_language);
1688 delete_opt(opts, known_languages);
1691 if ((it = find(opts.begin(), opts.end(), "fleqn"))
1693 h_is_mathindent = "1";
1696 // formula numbering side
1697 if ((it = find(opts.begin(), opts.end(), "leqno"))
1699 h_math_numbering_side = "left";
1702 else if ((it = find(opts.begin(), opts.end(), "reqno"))
1704 h_math_numbering_side = "right";
1708 // paper orientation
1709 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1710 h_paperorientation = "landscape";
1714 if ((it = find(opts.begin(), opts.end(), "oneside"))
1719 if ((it = find(opts.begin(), opts.end(), "twoside"))
1725 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1727 h_papercolumns = "1";
1730 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1732 h_papercolumns = "2";
1736 // some size options are known to any document classes, other sizes
1737 // are handled by the \geometry command of the geometry package
1738 handle_opt(opts, known_class_paper_sizes, h_papersize);
1739 delete_opt(opts, known_class_paper_sizes);
1740 // the remaining options
1741 h_options = join(opts, ",");
1742 // FIXME This does not work for classes that have a
1743 // different name in LyX than in LaTeX
1744 h_textclass = p.getArg('{', '}');
1748 else if (t.cs() == "usepackage") {
1749 string const options = p.getArg('[', ']');
1750 string const name = p.getArg('{', '}');
1751 vector<string> vecnames;
1752 split(name, vecnames, ',');
1753 vector<string>::const_iterator it = vecnames.begin();
1754 vector<string>::const_iterator end = vecnames.end();
1755 for (; it != end; ++it)
1756 handle_package(p, trimSpaceAndEol(*it), options,
1757 in_lyx_preamble, detectEncoding);
1760 else if (t.cs() == "inputencoding") {
1761 string const encoding = p.getArg('{','}');
1762 Encoding const * const enc = encodings.fromLaTeXName(
1763 encoding, Encoding::inputenc, true);
1765 if (!detectEncoding)
1766 cerr << "Unknown encoding " << encoding
1767 << ". Ignoring." << std::endl;
1770 h_inputencoding = enc->name();
1771 p.setEncoding(enc->iconvName());
1775 else if (t.cs() == "newenvironment") {
1776 string const name = p.getArg('{', '}');
1777 string const opt1 = p.getFullOpt();
1778 string const opt2 = p.getFullOpt();
1779 string const beg = p.verbatim_item();
1780 string const end = p.verbatim_item();
1781 if (!in_lyx_preamble) {
1782 h_preamble << "\\newenvironment{" << name
1783 << '}' << opt1 << opt2 << '{'
1784 << beg << "}{" << end << '}';
1786 add_known_environment(name, opt1, !opt2.empty(),
1787 from_utf8(beg), from_utf8(end));
1791 else if (t.cs() == "newtheorem") {
1793 if (p.next_token().character() == '*') {
1797 string const name = p.getArg('{', '}');
1798 string const opt1 = p.getFullOpt();
1799 string const opt2 = p.getFullOpt();
1800 string const body = p.verbatim_item();
1801 string const opt3 = p.getFullOpt();
1802 string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
1804 string const complete = cmd + "{" + name + '}' +
1805 opt1 + opt2 + '{' + body + '}' + opt3;
1807 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
1809 if (!in_lyx_preamble)
1810 h_preamble << complete;
1813 else if (t.cs() == "def") {
1814 string name = p.get_token().cs();
1815 // In fact, name may be more than the name:
1816 // In the test case of bug 8116
1817 // name == "csname SF@gobble@opt \endcsname".
1818 // Therefore, we need to use asInput() instead of cs().
1819 while (p.next_token().cat() != catBegin)
1820 name += p.get_token().asInput();
1821 if (!in_lyx_preamble)
1822 h_preamble << "\\def\\" << name << '{'
1823 << p.verbatim_item() << "}";
1826 else if (t.cs() == "newcolumntype") {
1827 string const name = p.getArg('{', '}');
1828 trimSpaceAndEol(name);
1830 string opts = p.getOpt();
1831 if (!opts.empty()) {
1832 istringstream is(string(opts, 1));
1835 special_columns_[name[0]] = nargs;
1836 h_preamble << "\\newcolumntype{" << name << "}";
1838 h_preamble << "[" << nargs << "]";
1839 h_preamble << "{" << p.verbatim_item() << "}";
1842 else if (t.cs() == "setcounter") {
1843 string const name = p.getArg('{', '}');
1844 string const content = p.getArg('{', '}');
1845 if (name == "secnumdepth")
1846 h_secnumdepth = content;
1847 else if (name == "tocdepth")
1848 h_tocdepth = content;
1850 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1853 else if (t.cs() == "setlength") {
1854 string const name = p.verbatim_item();
1855 string const content = p.verbatim_item();
1856 // the paragraphs are only not indented when \parindent is set to zero
1857 if (name == "\\parindent" && content != "") {
1858 if (content[0] == '0')
1859 h_paragraph_separation = "skip";
1861 h_paragraph_indentation = translate_len(content);
1862 } else if (name == "\\parskip") {
1863 if (content == "\\smallskipamount")
1864 h_defskip = "smallskip";
1865 else if (content == "\\medskipamount")
1866 h_defskip = "medskip";
1867 else if (content == "\\bigskipamount")
1868 h_defskip = "bigskip";
1870 h_defskip = translate_len(content);
1871 } else if (name == "\\mathindent") {
1872 h_mathindentation = translate_len(content);
1874 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1877 else if (t.cs() == "onehalfspacing")
1878 h_spacing = "onehalf";
1880 else if (t.cs() == "doublespacing")
1881 h_spacing = "double";
1883 else if (t.cs() == "setstretch")
1884 h_spacing = "other " + p.verbatim_item();
1886 else if (t.cs() == "synctex") {
1887 // the scheme is \synctex=value
1888 // where value can only be "1" or "-1"
1889 h_output_sync = "1";
1890 // there can be any character behind the value (e.g. a linebreak or a '\'
1891 // therefore we extract it char by char
1893 string value = p.get_token().asInput();
1895 value += p.get_token().asInput();
1896 h_output_sync_macro = "\\synctex=" + value;
1899 else if (t.cs() == "begin") {
1900 string const name = p.getArg('{', '}');
1901 if (name == "document")
1903 h_preamble << "\\begin{" << name << "}";
1906 else if (t.cs() == "geometry") {
1907 vector<string> opts = split_options(p.getArg('{', '}'));
1908 handle_geometry(opts);
1911 else if (t.cs() == "definecolor") {
1912 string const color = p.getArg('{', '}');
1913 string const space = p.getArg('{', '}');
1914 string const value = p.getArg('{', '}');
1915 if (color == "document_fontcolor" && space == "rgb") {
1916 RGBColor c(RGBColorFromLaTeX(value));
1917 h_fontcolor = X11hexname(c);
1918 } else if (color == "note_fontcolor" && space == "rgb") {
1919 RGBColor c(RGBColorFromLaTeX(value));
1920 h_notefontcolor = X11hexname(c);
1921 } else if (color == "page_backgroundcolor" && space == "rgb") {
1922 RGBColor c(RGBColorFromLaTeX(value));
1923 h_backgroundcolor = X11hexname(c);
1924 } else if (color == "shadecolor" && space == "rgb") {
1925 RGBColor c(RGBColorFromLaTeX(value));
1926 h_boxbgcolor = X11hexname(c);
1928 h_preamble << "\\definecolor{" << color
1929 << "}{" << space << "}{" << value
1934 else if (t.cs() == "bibliographystyle")
1935 h_biblio_style = p.verbatim_item();
1937 else if (t.cs() == "jurabibsetup") {
1938 // FIXME p.getArg('{', '}') is most probably wrong (it
1939 // does not handle nested braces).
1940 // Use p.verbatim_item() instead.
1941 vector<string> jurabibsetup =
1942 split_options(p.getArg('{', '}'));
1943 // add jurabibsetup to the jurabib package options
1944 add_package("jurabib", jurabibsetup);
1945 if (!jurabibsetup.empty()) {
1946 h_preamble << "\\jurabibsetup{"
1947 << join(jurabibsetup, ",") << '}';
1951 else if (t.cs() == "hypersetup") {
1952 vector<string> hypersetup =
1953 split_options(p.verbatim_item());
1954 // add hypersetup to the hyperref package options
1955 handle_hyperref(hypersetup);
1956 if (!hypersetup.empty()) {
1957 h_preamble << "\\hypersetup{"
1958 << join(hypersetup, ",") << '}';
1962 else if (is_known(t.cs(), known_if_3arg_commands)) {
1963 // prevent misparsing of \usepackage if it is used
1964 // as an argument (see e.g. our own output of
1965 // \@ifundefined above)
1966 string const arg1 = p.verbatim_item();
1967 string const arg2 = p.verbatim_item();
1968 string const arg3 = p.verbatim_item();
1969 // test case \@ifundefined{date}{}{\date{}}
1970 if (t.cs() == "@ifundefined" && arg1 == "date" &&
1971 arg2.empty() && arg3 == "\\date{}") {
1972 h_suppress_date = "true";
1973 // older tex2lyx versions did output
1974 // \@ifundefined{definecolor}{\usepackage{color}}{}
1975 } else if (t.cs() == "@ifundefined" &&
1976 arg1 == "definecolor" &&
1977 arg2 == "\\usepackage{color}" &&
1979 if (!in_lyx_preamble)
1980 h_preamble << package_beg_sep
1983 << "\\@ifundefined{definecolor}{color}{}"
1986 //\@ifundefined{showcaptionsetup}{}{%
1987 // \PassOptionsToPackage{caption=false}{subfig}}
1988 // that LyX uses for subfloats
1989 } else if (t.cs() == "@ifundefined" &&
1990 arg1 == "showcaptionsetup" && arg2.empty()
1991 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1993 } else if (!in_lyx_preamble) {
1994 h_preamble << t.asInput()
1995 << '{' << arg1 << '}'
1996 << '{' << arg2 << '}'
1997 << '{' << arg3 << '}';
2001 else if (is_known(t.cs(), known_if_commands)) {
2002 // must not parse anything in conditional code, since
2003 // LyX would output the parsed contents unconditionally
2004 if (!in_lyx_preamble)
2005 h_preamble << t.asInput();
2006 handle_if(p, in_lyx_preamble);
2009 else if (!t.cs().empty() && !in_lyx_preamble)
2010 h_preamble << '\\' << t.cs();
2013 // remove the whitespace
2016 // Force textclass if the user wanted it
2017 if (!forceclass.empty())
2018 h_textclass = forceclass;
2019 tc.setName(h_textclass);
2020 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2021 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2024 if (h_papersides.empty()) {
2027 h_papersides = ss.str();
2030 // If the CJK package is used we cannot set the document language from
2031 // the babel options. Instead, we guess which language is used most
2032 // and set this one.
2033 default_language = h_language;
2034 if (is_full_document &&
2035 (auto_packages.find("CJK") != auto_packages.end() ||
2036 auto_packages.find("CJKutf8") != auto_packages.end())) {
2038 h_language = guessLanguage(p, default_language);
2040 if (explicit_babel && h_language != default_language) {
2041 // We set the document language to a CJK language,
2042 // but babel is explicitly called in the user preamble
2043 // without options. LyX will not add the default
2044 // language to the document options if it is either
2045 // english, or no text is set as default language.
2046 // Therefore we need to add a language option explicitly.
2047 // FIXME: It would be better to remove all babel calls
2048 // from the user preamble, but this is difficult
2049 // without re-introducing bug 7861.
2050 if (h_options.empty())
2051 h_options = lyx2babel(default_language);
2053 h_options += ',' + lyx2babel(default_language);
2059 string Preamble::parseEncoding(Parser & p, string const & forceclass)
2061 TeX2LyXDocClass dummy;
2062 parse(p, forceclass, true, dummy);
2063 if (h_inputencoding != "auto" && h_inputencoding != "default")
2064 return h_inputencoding;
2069 string babel2lyx(string const & language)
2071 char const * const * where = is_known(language, known_languages);
2073 return known_coded_languages[where - known_languages];
2078 string lyx2babel(string const & language)
2080 char const * const * where = is_known(language, known_coded_languages);
2082 return known_languages[where - known_coded_languages];
2087 string Preamble::polyglossia2lyx(string const & language)
2089 char const * const * where = is_known(language, polyglossia_languages);
2091 return coded_polyglossia_languages[where - polyglossia_languages];
2096 string rgbcolor2code(string const & name)
2098 char const * const * where = is_known(name, known_basic_colors);
2100 // "red", "green" etc
2101 return known_basic_color_codes[where - known_basic_colors];
2103 // "255,0,0", "0,255,0" etc
2104 RGBColor c(RGBColorFromLaTeX(name));
2105 return X11hexname(c);