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", "multirow", "nomencl", "pdfpages", "prettyref", "refstyle", "rotating",
187 "rotfloat", "splitidx", "setspace", "subscript", "textcomp", "tipa", "tipx",
188 "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xcolor", "xunicode", 0};
190 // codes used to remove packages that are loaded automatically by LyX.
191 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
192 const char package_beg_sep = '\001';
193 const char package_mid_sep = '\002';
194 const char package_end_sep = '\003';
197 // returns true if at least one of the options in what has been found
198 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
204 // the last language option is the document language (for babel and LyX)
205 // the last size option is the document font size
206 vector<string>::iterator it;
207 vector<string>::iterator position = opts.begin();
208 for (; *what; ++what) {
209 it = find(opts.begin(), opts.end(), *what);
210 if (it != opts.end()) {
211 if (it >= position) {
222 void delete_opt(vector<string> & opts, char const * const * what)
227 // remove found options from the list
228 // do this after handle_opt to avoid potential memory leaks
229 vector<string>::iterator it;
230 for (; *what; ++what) {
231 it = find(opts.begin(), opts.end(), *what);
232 if (it != opts.end())
239 * Split a package options string (keyval format) into a vector.
241 * authorformat=smallcaps,
243 * titleformat=colonsep,
244 * bibformat={tabular,ibidem,numbered}
246 vector<string> split_options(string const & input)
248 vector<string> options;
252 Token const & t = p.get_token();
253 if (t.asInput() == ",") {
254 options.push_back(trimSpaceAndEol(option));
256 } else if (t.asInput() == "=") {
259 if (p.next_token().asInput() == "{")
260 option += '{' + p.getArg('{', '}') + '}';
261 } else if (t.cat() != catSpace)
262 option += t.asInput();
266 options.push_back(trimSpaceAndEol(option));
273 * Retrieve a keyval option "name={value with=sign}" named \p name from
274 * \p options and return the value.
275 * The found option is also removed from \p options.
277 string process_keyval_opt(vector<string> & options, string name)
279 for (size_t i = 0; i < options.size(); ++i) {
280 vector<string> option;
281 split(options[i], option, '=');
282 if (option.size() < 2)
284 if (option[0] == name) {
285 options.erase(options.begin() + i);
286 option.erase(option.begin());
287 return join(option, "=");
293 } // anonymous namespace
297 * known polyglossia language names (including variants)
298 * FIXME: support spelling=old for german variants (german vs. ngerman LyX names etc)
300 const char * const Preamble::polyglossia_languages[] = {
301 "albanian", "american", "amharic", "ancient", "arabic", "armenian", "asturian", "australian",
302 "bahasai", "bahasam", "basque", "bengali", "brazil", "brazilian", "breton", "british", "bulgarian",
303 "catalan", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
304 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
305 "galician", "greek", "monotonic", "hebrew", "hindi",
306 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer",
307 "lao", "latin", "latvian", "lithuanian", "lsorbian", "magyar", "malayalam", "marathi",
308 "austrian", "newzealand", "german", "norsk", "nynorsk", "occitan",
309 "piedmontese", "polish", "polytonic", "portuges", "romanian", "romansh", "russian",
310 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovenian", "spanish", "swedish", "syriac",
311 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
312 "ukrainian", "urdu", "usorbian", "vietnamese", "welsh", 0};
313 // not yet supported by LyX: "korean", "nko"
316 * the same as polyglossia_languages with .lyx names
317 * please keep this in sync with polyglossia_languages line by line!
319 const char * const Preamble::coded_polyglossia_languages[] = {
320 "albanian", "american", "amharic", "ancientgreek", "arabic_arabi", "armenian", "asturian", "australian",
321 "bahasa", "bahasam", "basque", "bengali", "brazilian", "brazilian", "breton", "british", "bulgarian",
322 "catalan", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
323 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
324 "galician", "greek", "greek", "hebrew", "hindi",
325 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer",
326 "lao", "latin", "latvian", "lithuanian", "lowersorbian", "magyar", "malayalam", "marathi",
327 "naustrian","newzealand", "ngerman", "norsk", "nynorsk", "occitan",
328 "piedmontese", "polish", "polutonikogreek", "portuges", "romanian", "romansh", "russian",
329 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovene", "spanish", "swedish", "syriac",
330 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
331 "ukrainian", "urdu", "uppersorbian", "vietnamese", "welsh", 0};
332 // not yet supported by LyX: "korean-polyglossia", "nko"
335 bool Preamble::usePolyglossia() const
337 return h_use_non_tex_fonts && h_language_package == "default";
341 bool Preamble::indentParagraphs() const
343 return h_paragraph_separation == "indent";
347 bool Preamble::isPackageUsed(string const & package) const
349 return used_packages.find(package) != used_packages.end();
353 vector<string> Preamble::getPackageOptions(string const & package) const
355 map<string, vector<string> >::const_iterator it = used_packages.find(package);
356 if (it != used_packages.end())
358 return vector<string>();
362 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
364 auto_packages.insert(package);
368 void Preamble::addModule(string const & module)
370 used_modules.push_back(module);
374 void Preamble::suppressDate(bool suppress)
377 h_suppress_date = "true";
379 h_suppress_date = "false";
383 void Preamble::registerAuthor(std::string const & name)
385 Author author(from_utf8(name), empty_docstring());
386 author.setUsed(true);
387 authors_.record(author);
388 h_tracking_changes = "true";
389 h_output_changes = "true";
393 Author const & Preamble::getAuthor(std::string const & name) const
395 Author author(from_utf8(name), empty_docstring());
396 for (AuthorList::Authors::const_iterator it = authors_.begin();
397 it != authors_.end(); ++it)
400 static Author const dummy;
405 int Preamble::getSpecialTableColumnArguments(char c) const
407 map<char, int>::const_iterator it = special_columns_.find(c);
408 if (it == special_columns_.end())
414 void Preamble::add_package(string const & name, vector<string> & options)
416 // every package inherits the global options
417 if (used_packages.find(name) == used_packages.end())
418 used_packages[name] = split_options(h_options);
420 vector<string> & v = used_packages[name];
421 v.insert(v.end(), options.begin(), options.end());
422 if (name == "jurabib") {
423 // Don't output the order argument (see the cite command
424 // handling code in text.cpp).
425 vector<string>::iterator end =
426 remove(options.begin(), options.end(), "natbiborder");
427 end = remove(options.begin(), end, "jurabiborder");
428 options.erase(end, options.end());
435 // Given is a string like "scaled=0.9" or "Scale=0.9", return 0.9 * 100
436 bool scale_as_percentage(string const & scale, string & percentage)
438 string::size_type pos = scale.find('=');
439 if (pos != string::npos) {
440 string value = scale.substr(pos + 1);
441 if (isStrDbl(value)) {
442 percentage = convert<string>(
443 static_cast<int>(100 * convert<double>(value)));
451 string remove_braces(string const & value)
455 if (value[0] == '{' && value[value.length()-1] == '}')
456 return value.substr(1, value.length()-2);
460 } // anonymous namespace
463 Preamble::Preamble() : one_language(true), explicit_babel(false),
464 title_layout_found(false), index_number(0), h_font_cjk_set(false),
465 h_use_microtype(false)
469 h_biblio_style = "plain";
470 h_bibtex_command = "default";
471 h_cite_engine = "basic";
472 h_cite_engine_type = "default";
474 h_defskip = "medskip";
477 h_fontencoding = "default";
478 h_font_roman[0] = "default";
479 h_font_roman[1] = "default";
480 h_font_sans[0] = "default";
481 h_font_sans[1] = "default";
482 h_font_typewriter[0] = "default";
483 h_font_typewriter[1] = "default";
484 h_font_math[0] = "auto";
485 h_font_math[1] = "auto";
486 h_font_default_family = "default";
487 h_use_non_tex_fonts = false;
489 h_font_osf = "false";
490 h_font_sf_scale[0] = "100";
491 h_font_sf_scale[1] = "100";
492 h_font_tt_scale[0] = "100";
493 h_font_tt_scale[1] = "100";
495 h_is_formulaindent = "0";
496 h_graphics = "default";
497 h_default_output_format = "default";
498 h_html_be_strict = "false";
499 h_html_css_as_file = "0";
500 h_html_math_output = "0";
501 h_index[0] = "Index";
502 h_index_command = "default";
503 h_inputencoding = "auto";
504 h_justification = "true";
505 h_language = "english";
506 h_language_package = "none";
508 h_maintain_unincluded_children = "false";
512 h_output_changes = "false";
514 //h_output_sync_macro
515 h_papercolumns = "1";
516 h_paperfontsize = "default";
517 h_paperorientation = "portrait";
518 h_paperpagestyle = "default";
520 h_papersize = "default";
521 h_paragraph_indentation = "default";
522 h_paragraph_separation = "indent";
527 h_pdf_bookmarks = "0";
528 h_pdf_bookmarksnumbered = "0";
529 h_pdf_bookmarksopen = "0";
530 h_pdf_bookmarksopenlevel = "1";
531 h_pdf_breaklinks = "0";
532 h_pdf_pdfborder = "0";
533 h_pdf_colorlinks = "0";
534 h_pdf_backref = "section";
535 h_pdf_pdfusetitle = "0";
537 //h_pdf_quoted_options;
538 h_quotes_style = "english";
540 h_shortcut[0] = "idx";
541 h_spacing = "single";
542 h_save_transient_properties = "true";
543 h_suppress_date = "false";
544 h_textclass = "article";
546 h_tracking_changes = "false";
547 h_use_bibtopic = "false";
548 h_use_indices = "false";
549 h_use_geometry = "false";
550 h_use_default_options = "false";
551 h_use_hyperref = "false";
552 h_use_microtype = false;
553 h_use_refstyle = false;
554 h_use_packages["amsmath"] = "1";
555 h_use_packages["amssymb"] = "0";
556 h_use_packages["cancel"] = "0";
557 h_use_packages["esint"] = "1";
558 h_use_packages["mhchem"] = "0";
559 h_use_packages["mathdots"] = "0";
560 h_use_packages["mathtools"] = "0";
561 h_use_packages["stackrel"] = "0";
562 h_use_packages["stmaryrd"] = "0";
563 h_use_packages["undertilde"] = "0";
567 void Preamble::handle_hyperref(vector<string> & options)
569 // FIXME swallow inputencoding changes that might surround the
570 // hyperref setup if it was written by LyX
571 h_use_hyperref = "true";
572 // swallow "unicode=true", since LyX does always write that
573 vector<string>::iterator it =
574 find(options.begin(), options.end(), "unicode=true");
575 if (it != options.end())
577 it = find(options.begin(), options.end(), "pdfusetitle");
578 if (it != options.end()) {
579 h_pdf_pdfusetitle = "1";
582 string bookmarks = process_keyval_opt(options, "bookmarks");
583 if (bookmarks == "true")
584 h_pdf_bookmarks = "1";
585 else if (bookmarks == "false")
586 h_pdf_bookmarks = "0";
587 if (h_pdf_bookmarks == "1") {
588 string bookmarksnumbered =
589 process_keyval_opt(options, "bookmarksnumbered");
590 if (bookmarksnumbered == "true")
591 h_pdf_bookmarksnumbered = "1";
592 else if (bookmarksnumbered == "false")
593 h_pdf_bookmarksnumbered = "0";
594 string bookmarksopen =
595 process_keyval_opt(options, "bookmarksopen");
596 if (bookmarksopen == "true")
597 h_pdf_bookmarksopen = "1";
598 else if (bookmarksopen == "false")
599 h_pdf_bookmarksopen = "0";
600 if (h_pdf_bookmarksopen == "1") {
601 string bookmarksopenlevel =
602 process_keyval_opt(options, "bookmarksopenlevel");
603 if (!bookmarksopenlevel.empty())
604 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
607 string breaklinks = process_keyval_opt(options, "breaklinks");
608 if (breaklinks == "true")
609 h_pdf_breaklinks = "1";
610 else if (breaklinks == "false")
611 h_pdf_breaklinks = "0";
612 string pdfborder = process_keyval_opt(options, "pdfborder");
613 if (pdfborder == "{0 0 0}")
614 h_pdf_pdfborder = "1";
615 else if (pdfborder == "{0 0 1}")
616 h_pdf_pdfborder = "0";
617 string backref = process_keyval_opt(options, "backref");
618 if (!backref.empty())
619 h_pdf_backref = backref;
620 string colorlinks = process_keyval_opt(options, "colorlinks");
621 if (colorlinks == "true")
622 h_pdf_colorlinks = "1";
623 else if (colorlinks == "false")
624 h_pdf_colorlinks = "0";
625 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
626 if (!pdfpagemode.empty())
627 h_pdf_pagemode = pdfpagemode;
628 string pdftitle = process_keyval_opt(options, "pdftitle");
629 if (!pdftitle.empty()) {
630 h_pdf_title = remove_braces(pdftitle);
632 string pdfauthor = process_keyval_opt(options, "pdfauthor");
633 if (!pdfauthor.empty()) {
634 h_pdf_author = remove_braces(pdfauthor);
636 string pdfsubject = process_keyval_opt(options, "pdfsubject");
637 if (!pdfsubject.empty())
638 h_pdf_subject = remove_braces(pdfsubject);
639 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
640 if (!pdfkeywords.empty())
641 h_pdf_keywords = remove_braces(pdfkeywords);
642 if (!options.empty()) {
643 if (!h_pdf_quoted_options.empty())
644 h_pdf_quoted_options += ',';
645 h_pdf_quoted_options += join(options, ",");
651 void Preamble::handle_geometry(vector<string> & options)
653 h_use_geometry = "true";
654 vector<string>::iterator it;
656 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
657 h_paperorientation = "landscape";
661 // keyval version: "paper=letter"
662 string paper = process_keyval_opt(options, "paper");
664 h_papersize = paper + "paper";
665 // alternative version: "letterpaper"
666 handle_opt(options, known_paper_sizes, h_papersize);
667 delete_opt(options, known_paper_sizes);
669 char const * const * margin = known_paper_margins;
670 for (; *margin; ++margin) {
671 string value = process_keyval_opt(options, *margin);
672 if (!value.empty()) {
673 int k = margin - known_paper_margins;
674 string name = known_coded_paper_margins[k];
675 h_margins += '\\' + name + ' ' + value + '\n';
681 void Preamble::handle_package(Parser &p, string const & name,
682 string const & opts, bool in_lyx_preamble,
685 vector<string> options = split_options(opts);
686 add_package(name, options);
687 char const * const * where = 0;
689 if (is_known(name, known_xetex_packages)) {
691 h_use_non_tex_fonts = true;
692 registerAutomaticallyLoadedPackage("fontspec");
693 if (h_inputencoding == "auto")
694 p.setEncoding("UTF-8");
698 if (is_known(name, known_roman_fonts))
699 h_font_roman[0] = name;
701 if (name == "fourier") {
702 h_font_roman[0] = "utopia";
703 // when font uses real small capitals
704 if (opts == "expert")
708 if (name == "garamondx") {
709 h_font_roman[0] = "garamondx";
714 if (name == "libertine") {
715 h_font_roman[0] = "libertine";
716 // this automatically invokes biolinum
717 h_font_sans[0] = "biolinum";
720 else if (opts == "lining")
721 h_font_osf = "false";
724 if (name == "libertine-type1") {
725 h_font_roman[0] = "libertine";
726 // NOTE: contrary to libertine.sty, libertine-type1
727 // does not automatically invoke biolinum
728 if (opts == "lining")
729 h_font_osf = "false";
730 else if (opts == "osf")
734 if (name == "mathdesign") {
735 if (opts.find("charter") != string::npos)
736 h_font_roman[0] = "md-charter";
737 if (opts.find("garamond") != string::npos)
738 h_font_roman[0] = "md-garamond";
739 if (opts.find("utopia") != string::npos)
740 h_font_roman[0] = "md-utopia";
741 if (opts.find("expert") != string::npos) {
747 else if (name == "mathpazo")
748 h_font_roman[0] = "palatino";
750 else if (name == "mathptmx")
751 h_font_roman[0] = "times";
753 if (name == "crimson")
754 h_font_roman[0] = "cochineal";
756 if (name == "cochineal") {
757 h_font_roman[0] = "cochineal";
758 // cochineal can have several options, e.g. [proportional,osf]
759 string::size_type pos = opts.find("osf");
760 if (pos != string::npos)
764 if (name == "noto") {
765 // noto can have several options
767 h_font_roman[0] = "NotoSerif-TLF";
768 string::size_type pos = opts.find("rm");
769 if (pos != string::npos)
770 h_font_roman[0] = "NotoSerif-TLF";
771 pos = opts.find("sf");
772 if (pos != string::npos)
773 h_font_sans[0] = "NotoSans-TLF";
774 pos = opts.find("nott");
775 if (pos != string::npos) {
776 h_font_roman[0] = "NotoSerif-TLF";
777 h_font_sans[0] = "NotoSans-TLF";
779 // noto as typewriter is handled in handling of \ttdefault
780 // special cases are handled in handling of \rmdefault and \sfdefault
784 if (is_known(name, known_sans_fonts)) {
785 h_font_sans[0] = name;
786 if (options.size() >= 1) {
787 if (scale_as_percentage(opts, h_font_sf_scale[0]))
792 if (name == "biolinum-type1") {
793 h_font_sans[0] = "biolinum";
794 // biolinum can have several options, e.g. [osf,scaled=0.97]
795 string::size_type pos = opts.find("osf");
796 if (pos != string::npos)
801 if (is_known(name, known_typewriter_fonts)) {
802 // fourier can be set as roman font _only_
803 // fourier as typewriter is handled in handling of \ttdefault
804 if (name != "fourier") {
805 h_font_typewriter[0] = name;
806 if (options.size() >= 1) {
807 if (scale_as_percentage(opts, h_font_tt_scale[0]))
813 if (name == "libertineMono-type1") {
814 h_font_typewriter[0] = "libertine-mono";
817 // font uses old-style figure
822 if (is_known(name, known_math_fonts))
823 h_font_math[0] = name;
825 if (name == "newtxmath") {
827 h_font_math[0] = "newtxmath";
828 else if (opts == "garamondx")
829 h_font_math[0] = "garamondx-ntxm";
830 else if (opts == "libertine")
831 h_font_math[0] = "libertine-ntxm";
832 else if (opts == "minion")
833 h_font_math[0] = "minion-ntxm";
834 else if (opts == "cochineal")
835 h_font_math[0] = "cochineal-ntxm";
840 h_font_math[0] = "iwona-math";
842 if (name == "kurier")
844 h_font_math[0] = "kurier-math";
846 // after the detection and handling of special cases, we can remove the
847 // fonts, otherwise they would appear in the preamble, see bug #7856
848 if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
849 || is_known(name, known_typewriter_fonts) || is_known(name, known_math_fonts))
851 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
852 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
853 name == "esint" || name == "mhchem" || name == "mathdots" ||
854 name == "mathtools" || name == "stackrel" ||
855 name == "stmaryrd" || name == "undertilde")
856 h_use_packages[name] = "2";
858 else if (name == "babel") {
859 h_language_package = "default";
860 // One might think we would have to do nothing if babel is loaded
861 // without any options to prevent pollution of the preamble with this
862 // babel call in every roundtrip.
863 // But the user could have defined babel-specific things afterwards. So
864 // we need to keep it in the preamble to prevent cases like bug #7861.
866 // check if more than one option was used - used later for inputenc
867 if (options.begin() != options.end() - 1)
868 one_language = false;
869 // babel takes the last language of the option of its \usepackage
870 // call as document language. If there is no such language option, the
871 // last language in the documentclass options is used.
872 handle_opt(options, known_languages, h_language);
873 // translate the babel name to a LyX name
874 h_language = babel2lyx(h_language);
875 if (h_language == "japanese") {
876 // For Japanese, the encoding isn't indicated in the source
877 // file, and there's really not much we can do. We could
878 // 1) offer a list of possible encodings to choose from, or
879 // 2) determine the encoding of the file by inspecting it.
880 // For the time being, we leave the encoding alone so that
881 // we don't get iconv errors when making a wrong guess, and
882 // we will output a note at the top of the document
883 // explaining what to do.
884 Encoding const * const enc = encodings.fromIconvName(
885 p.getEncoding(), Encoding::japanese, false);
887 h_inputencoding = enc->name();
888 is_nonCJKJapanese = true;
889 // in this case babel can be removed from the preamble
890 registerAutomaticallyLoadedPackage("babel");
892 // If babel is called with options, LyX puts them by default into the
893 // document class options. This works for most languages, except
894 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
895 // perhaps in future others.
896 // Therefore keep the babel call as it is as the user might have
898 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
900 delete_opt(options, known_languages);
902 h_preamble << "\\usepackage{babel}\n";
903 explicit_babel = true;
907 else if (name == "polyglossia") {
908 h_language_package = "default";
909 h_default_output_format = "pdf4";
910 h_use_non_tex_fonts = true;
912 registerAutomaticallyLoadedPackage("xunicode");
913 if (h_inputencoding == "auto")
914 p.setEncoding("UTF-8");
917 else if (name == "CJK") {
918 // set the encoding to "auto" because it might be set to "default" by the babel handling
919 // and this would not be correct for CJK
920 if (h_inputencoding == "default")
921 h_inputencoding = "auto";
922 registerAutomaticallyLoadedPackage("CJK");
925 else if (name == "CJKutf8") {
926 h_inputencoding = "utf8-cjk";
927 p.setEncoding("UTF-8");
928 registerAutomaticallyLoadedPackage("CJKutf8");
931 else if (name == "fontenc") {
932 h_fontencoding = getStringFromVector(options, ",");
933 /* We could do the following for better round trip support,
934 * but this makes the document less portable, so I skip it:
935 if (h_fontencoding == lyxrc.fontenc)
936 h_fontencoding = "global";
941 else if (name == "inputenc" || name == "luainputenc") {
942 // h_inputencoding is only set when there is not more than one
943 // inputenc option because otherwise h_inputencoding must be
944 // set to "auto" (the default encoding of the document language)
945 // Therefore check that exactly one option is passed to inputenc.
946 // It is also only set when there is not more than one babel
948 if (!options.empty()) {
949 string const encoding = options.back();
950 Encoding const * const enc = encodings.fromLaTeXName(
951 encoding, Encoding::inputenc, true);
954 cerr << "Unknown encoding " << encoding
955 << ". Ignoring." << std::endl;
957 if (!enc->unsafe() && options.size() == 1 && one_language == true)
958 h_inputencoding = enc->name();
959 p.setEncoding(enc->iconvName());
965 else if (name == "srcltx") {
968 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
971 h_output_sync_macro = "\\usepackage{srcltx}";
974 else if (is_known(name, known_old_language_packages)) {
975 // known language packages from the times before babel
976 // if they are found and not also babel, they will be used as
977 // custom language package
978 h_language_package = "\\usepackage{" + name + "}";
981 else if (name == "lyxskak") {
982 // ignore this and its options
983 const char * const o[] = {"ps", "mover", 0};
984 delete_opt(options, o);
987 else if (is_known(name, known_lyx_packages) && options.empty()) {
988 if (name == "splitidx")
989 h_use_indices = "true";
990 if (name == "refstyle")
991 h_use_refstyle = true;
992 else if (name == "prettyref")
993 h_use_refstyle = false;
994 if (!in_lyx_preamble) {
995 h_preamble << package_beg_sep << name
996 << package_mid_sep << "\\usepackage{"
998 if (p.next_token().cat() == catNewline ||
999 (p.next_token().cat() == catSpace &&
1000 p.next_next_token().cat() == catNewline))
1002 h_preamble << package_end_sep;
1006 else if (name == "geometry")
1007 handle_geometry(options);
1009 else if (name == "subfig")
1010 ; // ignore this FIXME: Use the package separator mechanism instead
1012 else if ((where = is_known(name, known_languages)))
1013 h_language = known_coded_languages[where - known_languages];
1015 else if (name == "natbib") {
1016 h_biblio_style = "plainnat";
1017 h_cite_engine = "natbib";
1018 h_cite_engine_type = "authoryear";
1019 vector<string>::iterator it =
1020 find(options.begin(), options.end(), "authoryear");
1021 if (it != options.end())
1024 it = find(options.begin(), options.end(), "numbers");
1025 if (it != options.end()) {
1026 h_cite_engine_type = "numerical";
1032 else if (name == "jurabib") {
1033 h_biblio_style = "jurabib";
1034 h_cite_engine = "jurabib";
1035 h_cite_engine_type = "authoryear";
1038 else if (name == "bibtopic")
1039 h_use_bibtopic = "true";
1041 else if (name == "hyperref")
1042 handle_hyperref(options);
1044 else if (name == "algorithm2e") {
1045 // Load "algorithm2e" module
1046 addModule("algorithm2e");
1047 // Add the package options to the global document options
1048 if (!options.empty()) {
1049 if (h_options.empty())
1050 h_options = join(options, ",");
1052 h_options += ',' + join(options, ",");
1055 else if (name == "microtype") {
1056 //we internally support only microtype without params
1057 if (options.empty())
1058 h_use_microtype = true;
1060 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1063 else if (!in_lyx_preamble) {
1064 if (options.empty())
1065 h_preamble << "\\usepackage{" << name << '}';
1067 h_preamble << "\\usepackage[" << opts << "]{"
1071 if (p.next_token().cat() == catNewline ||
1072 (p.next_token().cat() == catSpace &&
1073 p.next_next_token().cat() == catNewline))
1077 // We need to do something with the options...
1078 if (!options.empty() && !detectEncoding)
1079 cerr << "Ignoring options '" << join(options, ",")
1080 << "' of package " << name << '.' << endl;
1082 // remove the whitespace
1087 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1090 Token t = p.get_token();
1091 if (t.cat() == catEscape &&
1092 is_known(t.cs(), known_if_commands))
1093 handle_if(p, in_lyx_preamble);
1095 if (!in_lyx_preamble)
1096 h_preamble << t.asInput();
1097 if (t.cat() == catEscape && t.cs() == "fi")
1104 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1106 // set the quote language
1107 // LyX only knows the following quotes languages:
1108 // english, swedish, german, polish, french and danish
1109 // (quotes for "japanese" and "chinese-traditional" are missing because
1110 // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
1111 // conversion list taken from
1112 // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
1113 // (quotes for kazakh and interlingua are unknown)
1115 if (is_known(h_language, known_danish_quotes_languages))
1116 h_quotes_style = "danish";
1118 else if (is_known(h_language, known_french_quotes_languages))
1119 h_quotes_style = "french";
1121 else if (is_known(h_language, known_german_quotes_languages))
1122 h_quotes_style = "german";
1124 else if (is_known(h_language, known_polish_quotes_languages))
1125 h_quotes_style = "polish";
1127 else if (is_known(h_language, known_swedish_quotes_languages))
1128 h_quotes_style = "swedish";
1130 else if (is_known(h_language, known_english_quotes_languages))
1131 h_quotes_style = "english";
1133 if (contains(h_float_placement, "H"))
1134 registerAutomaticallyLoadedPackage("float");
1135 if (h_spacing != "single" && h_spacing != "default")
1136 registerAutomaticallyLoadedPackage("setspace");
1137 if (h_use_packages["amsmath"] == "2") {
1138 // amsbsy and amstext are already provided by amsmath
1139 registerAutomaticallyLoadedPackage("amsbsy");
1140 registerAutomaticallyLoadedPackage("amstext");
1143 // output the LyX file settings
1144 // Important: Keep the version formatting in sync with LyX and
1145 // lyx2lyx (bug 7951)
1146 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1147 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1148 << lyx_version_minor << '\n'
1149 << "\\lyxformat " << LYX_FORMAT << '\n'
1150 << "\\begin_document\n"
1151 << "\\begin_header\n"
1152 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1153 << "\\origin " << origin << "\n"
1154 << "\\textclass " << h_textclass << "\n";
1155 string const raw = subdoc ? empty_string() : h_preamble.str();
1157 os << "\\begin_preamble\n";
1158 for (string::size_type i = 0; i < raw.size(); ++i) {
1159 if (raw[i] == package_beg_sep) {
1160 // Here follows some package loading code that
1161 // must be skipped if the package is loaded
1163 string::size_type j = raw.find(package_mid_sep, i);
1164 if (j == string::npos)
1166 string::size_type k = raw.find(package_end_sep, j);
1167 if (k == string::npos)
1169 string const package = raw.substr(i + 1, j - i - 1);
1170 string const replacement = raw.substr(j + 1, k - j - 1);
1171 if (auto_packages.find(package) == auto_packages.end())
1177 os << "\n\\end_preamble\n";
1179 if (!h_options.empty())
1180 os << "\\options " << h_options << "\n";
1181 os << "\\use_default_options " << h_use_default_options << "\n";
1182 if (!used_modules.empty()) {
1183 os << "\\begin_modules\n";
1184 vector<string>::const_iterator const end = used_modules.end();
1185 vector<string>::const_iterator it = used_modules.begin();
1186 for (; it != end; ++it)
1188 os << "\\end_modules\n";
1190 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1191 << "\\language " << h_language << "\n"
1192 << "\\language_package " << h_language_package << "\n"
1193 << "\\inputencoding " << h_inputencoding << "\n"
1194 << "\\fontencoding " << h_fontencoding << "\n"
1195 << "\\font_roman \"" << h_font_roman[0]
1196 << "\" \"" << h_font_roman[1] << "\"\n"
1197 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1198 << "\\font_typewriter \"" << h_font_typewriter[0]
1199 << "\" \"" << h_font_typewriter[1] << "\"\n"
1200 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1201 << "\\font_default_family " << h_font_default_family << "\n"
1202 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1203 << "\\font_sc " << h_font_sc << "\n"
1204 << "\\font_osf " << h_font_osf << "\n"
1205 << "\\font_sf_scale " << h_font_sf_scale[0]
1206 << ' ' << h_font_sf_scale[1] << '\n'
1207 << "\\font_tt_scale " << h_font_tt_scale[0]
1208 << ' ' << h_font_tt_scale[1] << '\n';
1209 if (!h_font_cjk.empty())
1210 os << "\\font_cjk " << h_font_cjk << '\n';
1211 os << "\\use_microtype " << h_use_microtype << '\n'
1212 << "\\graphics " << h_graphics << '\n'
1213 << "\\default_output_format " << h_default_output_format << "\n"
1214 << "\\output_sync " << h_output_sync << "\n";
1215 if (h_output_sync == "1")
1216 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1217 os << "\\bibtex_command " << h_bibtex_command << "\n"
1218 << "\\index_command " << h_index_command << "\n";
1219 if (!h_float_placement.empty())
1220 os << "\\float_placement " << h_float_placement << "\n";
1221 os << "\\paperfontsize " << h_paperfontsize << "\n"
1222 << "\\spacing " << h_spacing << "\n"
1223 << "\\use_hyperref " << h_use_hyperref << '\n';
1224 if (h_use_hyperref == "true") {
1225 if (!h_pdf_title.empty())
1226 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1227 if (!h_pdf_author.empty())
1228 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1229 if (!h_pdf_subject.empty())
1230 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1231 if (!h_pdf_keywords.empty())
1232 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1233 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1234 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1235 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1236 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1237 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1238 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1239 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1240 "\\pdf_backref " << h_pdf_backref << "\n"
1241 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1242 if (!h_pdf_pagemode.empty())
1243 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1244 if (!h_pdf_quoted_options.empty())
1245 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1247 os << "\\papersize " << h_papersize << "\n"
1248 << "\\use_geometry " << h_use_geometry << '\n';
1249 for (map<string, string>::const_iterator it = h_use_packages.begin();
1250 it != h_use_packages.end(); ++it)
1251 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1252 os << "\\cite_engine " << h_cite_engine << '\n'
1253 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1254 << "\\biblio_style " << h_biblio_style << "\n"
1255 << "\\use_bibtopic " << h_use_bibtopic << "\n"
1256 << "\\use_indices " << h_use_indices << "\n"
1257 << "\\paperorientation " << h_paperorientation << '\n'
1258 << "\\suppress_date " << h_suppress_date << '\n'
1259 << "\\justification " << h_justification << '\n'
1260 << "\\use_refstyle " << h_use_refstyle << '\n';
1261 if (!h_fontcolor.empty())
1262 os << "\\fontcolor " << h_fontcolor << '\n';
1263 if (!h_notefontcolor.empty())
1264 os << "\\notefontcolor " << h_notefontcolor << '\n';
1265 if (!h_backgroundcolor.empty())
1266 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1267 if (!h_boxbgcolor.empty())
1268 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1269 if (index_number != 0)
1270 for (int i = 0; i < index_number; i++) {
1271 os << "\\index " << h_index[i] << '\n'
1272 << "\\shortcut " << h_shortcut[i] << '\n'
1273 << "\\color " << h_color << '\n'
1277 os << "\\index " << h_index[0] << '\n'
1278 << "\\shortcut " << h_shortcut[0] << '\n'
1279 << "\\color " << h_color << '\n'
1283 << "\\secnumdepth " << h_secnumdepth << "\n"
1284 << "\\tocdepth " << h_tocdepth << "\n"
1285 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1286 if (h_paragraph_separation == "skip")
1287 os << "\\defskip " << h_defskip << "\n";
1289 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1290 os << "\\is_formula_indent " << h_is_formulaindent << "\n"
1291 << "\\formula_indentation " << h_formulaindentation << "\n"
1292 << "\\quotes_style " << h_quotes_style << "\n"
1293 << "\\papercolumns " << h_papercolumns << "\n"
1294 << "\\papersides " << h_papersides << "\n"
1295 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1296 if (!h_listings_params.empty())
1297 os << "\\listings_params " << h_listings_params << "\n";
1298 os << "\\tracking_changes " << h_tracking_changes << "\n"
1299 << "\\output_changes " << h_output_changes << "\n"
1300 << "\\html_math_output " << h_html_math_output << "\n"
1301 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1302 << "\\html_be_strict " << h_html_be_strict << "\n"
1304 << "\\end_header\n\n"
1305 << "\\begin_body\n";
1310 void Preamble::parse(Parser & p, string const & forceclass,
1311 TeX2LyXDocClass & tc)
1313 // initialize fixed types
1314 special_columns_['D'] = 3;
1315 parse(p, forceclass, false, tc);
1319 void Preamble::parse(Parser & p, string const & forceclass,
1320 bool detectEncoding, TeX2LyXDocClass & tc)
1322 bool is_full_document = false;
1323 bool is_lyx_file = false;
1324 bool in_lyx_preamble = false;
1326 // determine whether this is a full document or a fragment for inclusion
1328 Token const & t = p.get_token();
1330 if (t.cat() == catEscape && t.cs() == "documentclass") {
1331 is_full_document = true;
1337 if (detectEncoding && !is_full_document)
1340 while (is_full_document && p.good()) {
1341 if (detectEncoding && h_inputencoding != "auto" &&
1342 h_inputencoding != "default")
1345 Token const & t = p.get_token();
1348 if (!detectEncoding)
1349 cerr << "t: " << t << '\n';
1355 if (!in_lyx_preamble &&
1356 (t.cat() == catLetter ||
1357 t.cat() == catSuper ||
1358 t.cat() == catSub ||
1359 t.cat() == catOther ||
1360 t.cat() == catMath ||
1361 t.cat() == catActive ||
1362 t.cat() == catBegin ||
1363 t.cat() == catEnd ||
1364 t.cat() == catAlign ||
1365 t.cat() == catParameter))
1366 h_preamble << t.cs();
1368 else if (!in_lyx_preamble &&
1369 (t.cat() == catSpace || t.cat() == catNewline))
1370 h_preamble << t.asInput();
1372 else if (t.cat() == catComment) {
1373 static regex const islyxfile("%% LyX .* created this file");
1374 static regex const usercommands("User specified LaTeX commands");
1376 string const comment = t.asInput();
1378 // magically switch encoding default if it looks like XeLaTeX
1379 static string const magicXeLaTeX =
1380 "% This document must be compiled with XeLaTeX ";
1381 if (comment.size() > magicXeLaTeX.size()
1382 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1383 && h_inputencoding == "auto") {
1384 if (!detectEncoding)
1385 cerr << "XeLaTeX comment found, switching to UTF8\n";
1386 h_inputencoding = "utf8";
1389 if (regex_search(comment, sub, islyxfile)) {
1391 in_lyx_preamble = true;
1392 } else if (is_lyx_file
1393 && regex_search(comment, sub, usercommands))
1394 in_lyx_preamble = false;
1395 else if (!in_lyx_preamble)
1396 h_preamble << t.asInput();
1399 else if (t.cs() == "pagestyle")
1400 h_paperpagestyle = p.verbatim_item();
1402 else if (t.cs() == "setdefaultlanguage") {
1404 // We don't yet care about non-language variant options
1405 // because LyX doesn't support this yet, see bug #8214
1407 string langopts = p.getOpt();
1408 // check if the option contains a variant, if yes, extract it
1409 string::size_type pos_var = langopts.find("variant");
1410 string::size_type i = langopts.find(',', pos_var);
1411 string::size_type k = langopts.find('=', pos_var);
1412 if (pos_var != string::npos){
1414 if (i == string::npos)
1415 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1417 variant = langopts.substr(k + 1, i - k - 1);
1418 h_language = variant;
1422 h_language = p.verbatim_item();
1423 //finally translate the poyglossia name to a LyX name
1424 h_language = polyglossia2lyx(h_language);
1427 else if (t.cs() == "setotherlanguage") {
1428 // We don't yet care about the option because LyX doesn't
1429 // support this yet, see bug #8214
1430 p.hasOpt() ? p.getOpt() : string();
1434 else if (t.cs() == "setmainfont") {
1435 // we don't care about the option
1436 p.hasOpt() ? p.getOpt() : string();
1437 h_font_roman[1] = p.getArg('{', '}');
1440 else if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1441 // LyX currently only supports the scale option
1444 string fontopts = p.getArg('[', ']');
1445 // check if the option contains a scaling, if yes, extract it
1446 string::size_type pos = fontopts.find("Scale");
1447 if (pos != string::npos) {
1448 string::size_type i = fontopts.find(',', pos);
1449 if (i == string::npos)
1450 scale_as_percentage(fontopts.substr(pos + 1), scale);
1452 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1455 if (t.cs() == "setsansfont") {
1457 h_font_sf_scale[1] = scale;
1458 h_font_sans[1] = p.getArg('{', '}');
1461 h_font_tt_scale[1] = scale;
1462 h_font_typewriter[1] = p.getArg('{', '}');
1466 else if (t.cs() == "date") {
1467 string argument = p.getArg('{', '}');
1468 if (argument.empty())
1469 h_suppress_date = "true";
1471 h_preamble << t.asInput() << '{' << argument << '}';
1474 else if (t.cs() == "color") {
1475 string const space =
1476 (p.hasOpt() ? p.getOpt() : string());
1477 string argument = p.getArg('{', '}');
1478 // check the case that a standard color is used
1479 if (space.empty() && is_known(argument, known_basic_colors)) {
1480 h_fontcolor = rgbcolor2code(argument);
1481 registerAutomaticallyLoadedPackage("color");
1482 } else if (space.empty() && argument == "document_fontcolor")
1483 registerAutomaticallyLoadedPackage("color");
1484 // check the case that LyX's document_fontcolor is defined
1485 // but not used for \color
1487 h_preamble << t.asInput();
1489 h_preamble << space;
1490 h_preamble << '{' << argument << '}';
1491 // the color might already be set because \definecolor
1492 // is parsed before this
1497 else if (t.cs() == "pagecolor") {
1498 string argument = p.getArg('{', '}');
1499 // check the case that a standard color is used
1500 if (is_known(argument, known_basic_colors)) {
1501 h_backgroundcolor = rgbcolor2code(argument);
1502 } else if (argument == "page_backgroundcolor")
1503 registerAutomaticallyLoadedPackage("color");
1504 // check the case that LyX's page_backgroundcolor is defined
1505 // but not used for \pagecolor
1507 h_preamble << t.asInput() << '{' << argument << '}';
1508 // the color might already be set because \definecolor
1509 // is parsed before this
1510 h_backgroundcolor = "";
1514 else if (t.cs() == "makeatletter") {
1515 // LyX takes care of this
1516 p.setCatcode('@', catLetter);
1519 else if (t.cs() == "makeatother") {
1520 // LyX takes care of this
1521 p.setCatcode('@', catOther);
1524 else if (t.cs() == "makeindex") {
1525 // LyX will re-add this if a print index command is found
1529 else if (t.cs() == "newindex") {
1530 string const indexname = p.getArg('[', ']');
1531 string const shortcut = p.verbatim_item();
1532 if (!indexname.empty())
1533 h_index[index_number] = indexname;
1535 h_index[index_number] = shortcut;
1536 h_shortcut[index_number] = shortcut;
1541 else if (t.cs() == "RS@ifundefined") {
1542 string const name = p.verbatim_item();
1543 string const body1 = p.verbatim_item();
1544 string const body2 = p.verbatim_item();
1545 // only non-lyxspecific stuff
1546 if (in_lyx_preamble &&
1547 (name == "subsecref" || name == "thmref" || name == "lemref"))
1551 ss << '\\' << t.cs();
1552 ss << '{' << name << '}'
1553 << '{' << body1 << '}'
1554 << '{' << body2 << '}';
1555 h_preamble << ss.str();
1559 else if (t.cs() == "AtBeginDocument") {
1560 string const name = p.verbatim_item();
1561 // only non-lyxspecific stuff
1562 if (in_lyx_preamble &&
1563 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
1564 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
1565 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
1566 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
1567 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
1568 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
1569 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
1570 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
1571 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
1572 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
1573 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
1574 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
1575 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
1576 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
1577 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
1581 ss << '\\' << t.cs();
1582 ss << '{' << name << '}';
1583 h_preamble << ss.str();
1587 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1588 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1589 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1590 || t.cs() == "DeclareRobustCommand"
1591 || t.cs() == "DeclareRobustCommandx"
1592 || t.cs() == "ProvideTextCommandDefault"
1593 || t.cs() == "DeclareMathAccent") {
1595 if (p.next_token().character() == '*') {
1599 string const name = p.verbatim_item();
1600 string const opt1 = p.getFullOpt();
1601 string const opt2 = p.getFullOpt();
1602 string const body = p.verbatim_item();
1603 // store the in_lyx_preamble setting
1604 bool const was_in_lyx_preamble = in_lyx_preamble;
1606 if (name == "\\rmdefault")
1607 if (is_known(body, known_roman_fonts)) {
1608 h_font_roman[0] = body;
1610 in_lyx_preamble = true;
1612 if (name == "\\sfdefault")
1613 if (is_known(body, known_sans_fonts)) {
1614 h_font_sans[0] = body;
1616 in_lyx_preamble = true;
1618 if (name == "\\ttdefault")
1619 if (is_known(body, known_typewriter_fonts)) {
1620 h_font_typewriter[0] = body;
1622 in_lyx_preamble = true;
1624 if (name == "\\familydefault") {
1625 string family = body;
1626 // remove leading "\"
1627 h_font_default_family = family.erase(0,1);
1629 in_lyx_preamble = true;
1632 // remove LyX-specific definitions that are re-added by LyX
1634 // \lyxline is an ancient command that is converted by tex2lyx into
1635 // a \rule therefore remove its preamble code
1636 if (name == "\\lyxdot" || name == "\\lyxarrow"
1637 || name == "\\lyxline" || name == "\\LyX") {
1639 in_lyx_preamble = true;
1642 // Add the command to the known commands
1643 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1645 // only non-lyxspecific stuff
1646 if (!in_lyx_preamble) {
1648 ss << '\\' << t.cs();
1651 ss << '{' << name << '}' << opt1 << opt2
1652 << '{' << body << "}";
1653 h_preamble << ss.str();
1655 ostream & out = in_preamble ? h_preamble : os;
1656 out << "\\" << t.cs() << "{" << name << "}"
1657 << opts << "{" << body << "}";
1660 // restore the in_lyx_preamble setting
1661 in_lyx_preamble = was_in_lyx_preamble;
1664 else if (t.cs() == "documentclass") {
1665 vector<string>::iterator it;
1666 vector<string> opts = split_options(p.getArg('[', ']'));
1667 handle_opt(opts, known_fontsizes, h_paperfontsize);
1668 delete_opt(opts, known_fontsizes);
1669 // delete "pt" at the end
1670 string::size_type i = h_paperfontsize.find("pt");
1671 if (i != string::npos)
1672 h_paperfontsize.erase(i);
1673 // The documentclass options are always parsed before the options
1674 // of the babel call so that a language cannot overwrite the babel
1676 handle_opt(opts, known_languages, h_language);
1677 delete_opt(opts, known_languages);
1679 // formula indentation
1680 if ((it = find(opts.begin(), opts.end(), "fleqn"))
1682 h_is_formulaindent = "1";
1685 // paper orientation
1686 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1687 h_paperorientation = "landscape";
1691 if ((it = find(opts.begin(), opts.end(), "oneside"))
1696 if ((it = find(opts.begin(), opts.end(), "twoside"))
1702 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1704 h_papercolumns = "1";
1707 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1709 h_papercolumns = "2";
1713 // some size options are known to any document classes, other sizes
1714 // are handled by the \geometry command of the geometry package
1715 handle_opt(opts, known_class_paper_sizes, h_papersize);
1716 delete_opt(opts, known_class_paper_sizes);
1717 // the remaining options
1718 h_options = join(opts, ",");
1719 // FIXME This does not work for classes that have a
1720 // different name in LyX than in LaTeX
1721 h_textclass = p.getArg('{', '}');
1725 else if (t.cs() == "usepackage") {
1726 string const options = p.getArg('[', ']');
1727 string const name = p.getArg('{', '}');
1728 vector<string> vecnames;
1729 split(name, vecnames, ',');
1730 vector<string>::const_iterator it = vecnames.begin();
1731 vector<string>::const_iterator end = vecnames.end();
1732 for (; it != end; ++it)
1733 handle_package(p, trimSpaceAndEol(*it), options,
1734 in_lyx_preamble, detectEncoding);
1737 else if (t.cs() == "inputencoding") {
1738 string const encoding = p.getArg('{','}');
1739 Encoding const * const enc = encodings.fromLaTeXName(
1740 encoding, Encoding::inputenc, true);
1742 if (!detectEncoding)
1743 cerr << "Unknown encoding " << encoding
1744 << ". Ignoring." << std::endl;
1747 h_inputencoding = enc->name();
1748 p.setEncoding(enc->iconvName());
1752 else if (t.cs() == "newenvironment") {
1753 string const name = p.getArg('{', '}');
1754 string const opt1 = p.getFullOpt();
1755 string const opt2 = p.getFullOpt();
1756 string const beg = p.verbatim_item();
1757 string const end = p.verbatim_item();
1758 if (!in_lyx_preamble) {
1759 h_preamble << "\\newenvironment{" << name
1760 << '}' << opt1 << opt2 << '{'
1761 << beg << "}{" << end << '}';
1763 add_known_environment(name, opt1, !opt2.empty(),
1764 from_utf8(beg), from_utf8(end));
1768 else if (t.cs() == "newtheorem") {
1769 string const name = p.getArg('{', '}');
1770 string const opt1 = p.getFullOpt();
1771 string const opt2 = p.getFullOpt();
1772 string const body = p.verbatim_item();
1773 string const opt3 = p.getFullOpt();
1775 string const complete = "\\newtheorem{" + name + '}' +
1776 opt1 + opt2 + '{' + body + '}' + opt3;
1778 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
1780 if (!in_lyx_preamble)
1781 h_preamble << complete;
1784 else if (t.cs() == "def") {
1785 string name = p.get_token().cs();
1786 // In fact, name may be more than the name:
1787 // In the test case of bug 8116
1788 // name == "csname SF@gobble@opt \endcsname".
1789 // Therefore, we need to use asInput() instead of cs().
1790 while (p.next_token().cat() != catBegin)
1791 name += p.get_token().asInput();
1792 if (!in_lyx_preamble)
1793 h_preamble << "\\def\\" << name << '{'
1794 << p.verbatim_item() << "}";
1797 else if (t.cs() == "newcolumntype") {
1798 string const name = p.getArg('{', '}');
1799 trimSpaceAndEol(name);
1801 string opts = p.getOpt();
1802 if (!opts.empty()) {
1803 istringstream is(string(opts, 1));
1806 special_columns_[name[0]] = nargs;
1807 h_preamble << "\\newcolumntype{" << name << "}";
1809 h_preamble << "[" << nargs << "]";
1810 h_preamble << "{" << p.verbatim_item() << "}";
1813 else if (t.cs() == "setcounter") {
1814 string const name = p.getArg('{', '}');
1815 string const content = p.getArg('{', '}');
1816 if (name == "secnumdepth")
1817 h_secnumdepth = content;
1818 else if (name == "tocdepth")
1819 h_tocdepth = content;
1821 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1824 else if (t.cs() == "setlength") {
1825 string const name = p.verbatim_item();
1826 string const content = p.verbatim_item();
1827 // the paragraphs are only not indented when \parindent is set to zero
1828 if (name == "\\parindent" && content != "") {
1829 if (content[0] == '0')
1830 h_paragraph_separation = "skip";
1832 h_paragraph_indentation = translate_len(content);
1833 } else if (name == "\\parskip") {
1834 if (content == "\\smallskipamount")
1835 h_defskip = "smallskip";
1836 else if (content == "\\medskipamount")
1837 h_defskip = "medskip";
1838 else if (content == "\\bigskipamount")
1839 h_defskip = "bigskip";
1841 h_defskip = translate_len(content);
1842 } else if (name == "\\mathindent") {
1843 h_formulaindentation = translate_len(content);
1845 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1848 else if (t.cs() == "onehalfspacing")
1849 h_spacing = "onehalf";
1851 else if (t.cs() == "doublespacing")
1852 h_spacing = "double";
1854 else if (t.cs() == "setstretch")
1855 h_spacing = "other " + p.verbatim_item();
1857 else if (t.cs() == "synctex") {
1858 // the scheme is \synctex=value
1859 // where value can only be "1" or "-1"
1860 h_output_sync = "1";
1861 // there can be any character behind the value (e.g. a linebreak or a '\'
1862 // therefore we extract it char by char
1864 string value = p.get_token().asInput();
1866 value += p.get_token().asInput();
1867 h_output_sync_macro = "\\synctex=" + value;
1870 else if (t.cs() == "begin") {
1871 string const name = p.getArg('{', '}');
1872 if (name == "document")
1874 h_preamble << "\\begin{" << name << "}";
1877 else if (t.cs() == "geometry") {
1878 vector<string> opts = split_options(p.getArg('{', '}'));
1879 handle_geometry(opts);
1882 else if (t.cs() == "definecolor") {
1883 string const color = p.getArg('{', '}');
1884 string const space = p.getArg('{', '}');
1885 string const value = p.getArg('{', '}');
1886 if (color == "document_fontcolor" && space == "rgb") {
1887 RGBColor c(RGBColorFromLaTeX(value));
1888 h_fontcolor = X11hexname(c);
1889 } else if (color == "note_fontcolor" && space == "rgb") {
1890 RGBColor c(RGBColorFromLaTeX(value));
1891 h_notefontcolor = X11hexname(c);
1892 } else if (color == "page_backgroundcolor" && space == "rgb") {
1893 RGBColor c(RGBColorFromLaTeX(value));
1894 h_backgroundcolor = X11hexname(c);
1895 } else if (color == "shadecolor" && space == "rgb") {
1896 RGBColor c(RGBColorFromLaTeX(value));
1897 h_boxbgcolor = X11hexname(c);
1899 h_preamble << "\\definecolor{" << color
1900 << "}{" << space << "}{" << value
1905 else if (t.cs() == "bibliographystyle")
1906 h_biblio_style = p.verbatim_item();
1908 else if (t.cs() == "jurabibsetup") {
1909 // FIXME p.getArg('{', '}') is most probably wrong (it
1910 // does not handle nested braces).
1911 // Use p.verbatim_item() instead.
1912 vector<string> jurabibsetup =
1913 split_options(p.getArg('{', '}'));
1914 // add jurabibsetup to the jurabib package options
1915 add_package("jurabib", jurabibsetup);
1916 if (!jurabibsetup.empty()) {
1917 h_preamble << "\\jurabibsetup{"
1918 << join(jurabibsetup, ",") << '}';
1922 else if (t.cs() == "hypersetup") {
1923 vector<string> hypersetup =
1924 split_options(p.verbatim_item());
1925 // add hypersetup to the hyperref package options
1926 handle_hyperref(hypersetup);
1927 if (!hypersetup.empty()) {
1928 h_preamble << "\\hypersetup{"
1929 << join(hypersetup, ",") << '}';
1933 else if (is_known(t.cs(), known_if_3arg_commands)) {
1934 // prevent misparsing of \usepackage if it is used
1935 // as an argument (see e.g. our own output of
1936 // \@ifundefined above)
1937 string const arg1 = p.verbatim_item();
1938 string const arg2 = p.verbatim_item();
1939 string const arg3 = p.verbatim_item();
1940 // test case \@ifundefined{date}{}{\date{}}
1941 if (t.cs() == "@ifundefined" && arg1 == "date" &&
1942 arg2.empty() && arg3 == "\\date{}") {
1943 h_suppress_date = "true";
1944 // older tex2lyx versions did output
1945 // \@ifundefined{definecolor}{\usepackage{color}}{}
1946 } else if (t.cs() == "@ifundefined" &&
1947 arg1 == "definecolor" &&
1948 arg2 == "\\usepackage{color}" &&
1950 if (!in_lyx_preamble)
1951 h_preamble << package_beg_sep
1954 << "\\@ifundefined{definecolor}{color}{}"
1957 //\@ifundefined{showcaptionsetup}{}{%
1958 // \PassOptionsToPackage{caption=false}{subfig}}
1959 // that LyX uses for subfloats
1960 } else if (t.cs() == "@ifundefined" &&
1961 arg1 == "showcaptionsetup" && arg2.empty()
1962 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1964 } else if (!in_lyx_preamble) {
1965 h_preamble << t.asInput()
1966 << '{' << arg1 << '}'
1967 << '{' << arg2 << '}'
1968 << '{' << arg3 << '}';
1972 else if (is_known(t.cs(), known_if_commands)) {
1973 // must not parse anything in conditional code, since
1974 // LyX would output the parsed contents unconditionally
1975 if (!in_lyx_preamble)
1976 h_preamble << t.asInput();
1977 handle_if(p, in_lyx_preamble);
1980 else if (!t.cs().empty() && !in_lyx_preamble)
1981 h_preamble << '\\' << t.cs();
1984 // remove the whitespace
1987 // Force textclass if the user wanted it
1988 if (!forceclass.empty())
1989 h_textclass = forceclass;
1990 tc.setName(h_textclass);
1992 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1995 if (h_papersides.empty()) {
1998 h_papersides = ss.str();
2001 // If the CJK package is used we cannot set the document language from
2002 // the babel options. Instead, we guess which language is used most
2003 // and set this one.
2004 default_language = h_language;
2005 if (is_full_document &&
2006 (auto_packages.find("CJK") != auto_packages.end() ||
2007 auto_packages.find("CJKutf8") != auto_packages.end())) {
2009 h_language = guessLanguage(p, default_language);
2011 if (explicit_babel && h_language != default_language) {
2012 // We set the document language to a CJK language,
2013 // but babel is explicitly called in the user preamble
2014 // without options. LyX will not add the default
2015 // language to the document options if it is either
2016 // english, or no text is set as default language.
2017 // Therefore we need to add a language option explicitly.
2018 // FIXME: It would be better to remove all babel calls
2019 // from the user preamble, but this is difficult
2020 // without re-introducing bug 7861.
2021 if (h_options.empty())
2022 h_options = lyx2babel(default_language);
2024 h_options += ',' + lyx2babel(default_language);
2030 string Preamble::parseEncoding(Parser & p, string const & forceclass)
2032 TeX2LyXDocClass dummy;
2033 parse(p, forceclass, true, dummy);
2034 if (h_inputencoding != "auto" && h_inputencoding != "default")
2035 return h_inputencoding;
2040 string babel2lyx(string const & language)
2042 char const * const * where = is_known(language, known_languages);
2044 return known_coded_languages[where - known_languages];
2049 string lyx2babel(string const & language)
2051 char const * const * where = is_known(language, known_coded_languages);
2053 return known_languages[where - known_coded_languages];
2058 string Preamble::polyglossia2lyx(string const & language)
2060 char const * const * where = is_known(language, polyglossia_languages);
2062 return coded_polyglossia_languages[where - polyglossia_languages];
2067 string rgbcolor2code(string const & name)
2069 char const * const * where = is_known(name, known_basic_colors);
2071 // "red", "green" etc
2072 return known_basic_color_codes[where - known_basic_colors];
2074 // "255,0,0", "0,255,0" etc
2075 RGBColor c(RGBColorFromLaTeX(name));
2076 return X11hexname(c);