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";
475 h_dynamic_quotes = false;
478 h_fontencoding = "default";
479 h_font_roman[0] = "default";
480 h_font_roman[1] = "default";
481 h_font_sans[0] = "default";
482 h_font_sans[1] = "default";
483 h_font_typewriter[0] = "default";
484 h_font_typewriter[1] = "default";
485 h_font_math[0] = "auto";
486 h_font_math[1] = "auto";
487 h_font_default_family = "default";
488 h_use_non_tex_fonts = false;
490 h_font_osf = "false";
491 h_font_sf_scale[0] = "100";
492 h_font_sf_scale[1] = "100";
493 h_font_tt_scale[0] = "100";
494 h_font_tt_scale[1] = "100";
496 h_is_mathindent = "0";
497 h_graphics = "default";
498 h_default_output_format = "default";
499 h_html_be_strict = "false";
500 h_html_css_as_file = "0";
501 h_html_math_output = "0";
502 h_index[0] = "Index";
503 h_index_command = "default";
504 h_inputencoding = "auto";
505 h_justification = "true";
506 h_language = "english";
507 h_language_package = "none";
509 h_maintain_unincluded_children = "false";
513 h_output_changes = "false";
515 //h_output_sync_macro
516 h_papercolumns = "1";
517 h_paperfontsize = "default";
518 h_paperorientation = "portrait";
519 h_paperpagestyle = "default";
521 h_papersize = "default";
522 h_paragraph_indentation = "default";
523 h_paragraph_separation = "indent";
528 h_pdf_bookmarks = "0";
529 h_pdf_bookmarksnumbered = "0";
530 h_pdf_bookmarksopen = "0";
531 h_pdf_bookmarksopenlevel = "1";
532 h_pdf_breaklinks = "0";
533 h_pdf_pdfborder = "0";
534 h_pdf_colorlinks = "0";
535 h_pdf_backref = "section";
536 h_pdf_pdfusetitle = "0";
538 //h_pdf_quoted_options;
539 h_quotes_style = "english";
541 h_shortcut[0] = "idx";
542 h_spacing = "single";
543 h_save_transient_properties = "true";
544 h_suppress_date = "false";
545 h_textclass = "article";
547 h_tracking_changes = "false";
548 h_use_bibtopic = "false";
549 h_use_dash_ligatures = "true";
550 h_use_indices = "false";
551 h_use_geometry = "false";
552 h_use_default_options = "false";
553 h_use_hyperref = "false";
554 h_use_microtype = "false";
555 h_use_refstyle = false;
556 h_use_packages["amsmath"] = "1";
557 h_use_packages["amssymb"] = "0";
558 h_use_packages["cancel"] = "0";
559 h_use_packages["esint"] = "1";
560 h_use_packages["mhchem"] = "0";
561 h_use_packages["mathdots"] = "0";
562 h_use_packages["mathtools"] = "0";
563 h_use_packages["stackrel"] = "0";
564 h_use_packages["stmaryrd"] = "0";
565 h_use_packages["undertilde"] = "0";
569 void Preamble::handle_hyperref(vector<string> & options)
571 // FIXME swallow inputencoding changes that might surround the
572 // hyperref setup if it was written by LyX
573 h_use_hyperref = "true";
574 // swallow "unicode=true", since LyX does always write that
575 vector<string>::iterator it =
576 find(options.begin(), options.end(), "unicode=true");
577 if (it != options.end())
579 it = find(options.begin(), options.end(), "pdfusetitle");
580 if (it != options.end()) {
581 h_pdf_pdfusetitle = "1";
584 string bookmarks = process_keyval_opt(options, "bookmarks");
585 if (bookmarks == "true")
586 h_pdf_bookmarks = "1";
587 else if (bookmarks == "false")
588 h_pdf_bookmarks = "0";
589 if (h_pdf_bookmarks == "1") {
590 string bookmarksnumbered =
591 process_keyval_opt(options, "bookmarksnumbered");
592 if (bookmarksnumbered == "true")
593 h_pdf_bookmarksnumbered = "1";
594 else if (bookmarksnumbered == "false")
595 h_pdf_bookmarksnumbered = "0";
596 string bookmarksopen =
597 process_keyval_opt(options, "bookmarksopen");
598 if (bookmarksopen == "true")
599 h_pdf_bookmarksopen = "1";
600 else if (bookmarksopen == "false")
601 h_pdf_bookmarksopen = "0";
602 if (h_pdf_bookmarksopen == "1") {
603 string bookmarksopenlevel =
604 process_keyval_opt(options, "bookmarksopenlevel");
605 if (!bookmarksopenlevel.empty())
606 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
609 string breaklinks = process_keyval_opt(options, "breaklinks");
610 if (breaklinks == "true")
611 h_pdf_breaklinks = "1";
612 else if (breaklinks == "false")
613 h_pdf_breaklinks = "0";
614 string pdfborder = process_keyval_opt(options, "pdfborder");
615 if (pdfborder == "{0 0 0}")
616 h_pdf_pdfborder = "1";
617 else if (pdfborder == "{0 0 1}")
618 h_pdf_pdfborder = "0";
619 string backref = process_keyval_opt(options, "backref");
620 if (!backref.empty())
621 h_pdf_backref = backref;
622 string colorlinks = process_keyval_opt(options, "colorlinks");
623 if (colorlinks == "true")
624 h_pdf_colorlinks = "1";
625 else if (colorlinks == "false")
626 h_pdf_colorlinks = "0";
627 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
628 if (!pdfpagemode.empty())
629 h_pdf_pagemode = pdfpagemode;
630 string pdftitle = process_keyval_opt(options, "pdftitle");
631 if (!pdftitle.empty()) {
632 h_pdf_title = remove_braces(pdftitle);
634 string pdfauthor = process_keyval_opt(options, "pdfauthor");
635 if (!pdfauthor.empty()) {
636 h_pdf_author = remove_braces(pdfauthor);
638 string pdfsubject = process_keyval_opt(options, "pdfsubject");
639 if (!pdfsubject.empty())
640 h_pdf_subject = remove_braces(pdfsubject);
641 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
642 if (!pdfkeywords.empty())
643 h_pdf_keywords = remove_braces(pdfkeywords);
644 if (!options.empty()) {
645 if (!h_pdf_quoted_options.empty())
646 h_pdf_quoted_options += ',';
647 h_pdf_quoted_options += join(options, ",");
653 void Preamble::handle_geometry(vector<string> & options)
655 h_use_geometry = "true";
656 vector<string>::iterator it;
658 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
659 h_paperorientation = "landscape";
663 // keyval version: "paper=letter"
664 string paper = process_keyval_opt(options, "paper");
666 h_papersize = paper + "paper";
667 // alternative version: "letterpaper"
668 handle_opt(options, known_paper_sizes, h_papersize);
669 delete_opt(options, known_paper_sizes);
671 char const * const * margin = known_paper_margins;
672 for (; *margin; ++margin) {
673 string value = process_keyval_opt(options, *margin);
674 if (!value.empty()) {
675 int k = margin - known_paper_margins;
676 string name = known_coded_paper_margins[k];
677 h_margins += '\\' + name + ' ' + value + '\n';
683 void Preamble::handle_package(Parser &p, string const & name,
684 string const & opts, bool in_lyx_preamble,
687 vector<string> options = split_options(opts);
688 add_package(name, options);
689 char const * const * where = 0;
691 if (is_known(name, known_xetex_packages)) {
693 h_use_non_tex_fonts = true;
694 registerAutomaticallyLoadedPackage("fontspec");
695 if (h_inputencoding == "auto")
696 p.setEncoding("UTF-8");
700 if (is_known(name, known_roman_fonts))
701 h_font_roman[0] = name;
703 if (name == "fourier") {
704 h_font_roman[0] = "utopia";
705 // when font uses real small capitals
706 if (opts == "expert")
710 if (name == "garamondx") {
711 h_font_roman[0] = "garamondx";
716 if (name == "libertine") {
717 h_font_roman[0] = "libertine";
718 // this automatically invokes biolinum
719 h_font_sans[0] = "biolinum";
722 else if (opts == "lining")
723 h_font_osf = "false";
726 if (name == "libertine-type1") {
727 h_font_roman[0] = "libertine";
728 // NOTE: contrary to libertine.sty, libertine-type1
729 // does not automatically invoke biolinum
730 if (opts == "lining")
731 h_font_osf = "false";
732 else if (opts == "osf")
736 if (name == "mathdesign") {
737 if (opts.find("charter") != string::npos)
738 h_font_roman[0] = "md-charter";
739 if (opts.find("garamond") != string::npos)
740 h_font_roman[0] = "md-garamond";
741 if (opts.find("utopia") != string::npos)
742 h_font_roman[0] = "md-utopia";
743 if (opts.find("expert") != string::npos) {
749 else if (name == "mathpazo")
750 h_font_roman[0] = "palatino";
752 else if (name == "mathptmx")
753 h_font_roman[0] = "times";
755 if (name == "crimson")
756 h_font_roman[0] = "cochineal";
758 if (name == "cochineal") {
759 h_font_roman[0] = "cochineal";
760 // cochineal can have several options, e.g. [proportional,osf]
761 string::size_type pos = opts.find("osf");
762 if (pos != string::npos)
766 if (name == "noto") {
767 // noto can have several options
769 h_font_roman[0] = "NotoSerif-TLF";
770 string::size_type pos = opts.find("rm");
771 if (pos != string::npos)
772 h_font_roman[0] = "NotoSerif-TLF";
773 pos = opts.find("sf");
774 if (pos != string::npos)
775 h_font_sans[0] = "NotoSans-TLF";
776 pos = opts.find("nott");
777 if (pos != string::npos) {
778 h_font_roman[0] = "NotoSerif-TLF";
779 h_font_sans[0] = "NotoSans-TLF";
781 // noto as typewriter is handled in handling of \ttdefault
782 // special cases are handled in handling of \rmdefault and \sfdefault
786 if (is_known(name, known_sans_fonts)) {
787 h_font_sans[0] = name;
788 if (options.size() >= 1) {
789 if (scale_as_percentage(opts, h_font_sf_scale[0]))
794 if (name == "biolinum-type1") {
795 h_font_sans[0] = "biolinum";
796 // biolinum can have several options, e.g. [osf,scaled=0.97]
797 string::size_type pos = opts.find("osf");
798 if (pos != string::npos)
803 if (is_known(name, known_typewriter_fonts)) {
804 // fourier can be set as roman font _only_
805 // fourier as typewriter is handled in handling of \ttdefault
806 if (name != "fourier") {
807 h_font_typewriter[0] = name;
808 if (options.size() >= 1) {
809 if (scale_as_percentage(opts, h_font_tt_scale[0]))
815 if (name == "libertineMono-type1") {
816 h_font_typewriter[0] = "libertine-mono";
819 // font uses old-style figure
824 if (is_known(name, known_math_fonts))
825 h_font_math[0] = name;
827 if (name == "newtxmath") {
829 h_font_math[0] = "newtxmath";
830 else if (opts == "garamondx")
831 h_font_math[0] = "garamondx-ntxm";
832 else if (opts == "libertine")
833 h_font_math[0] = "libertine-ntxm";
834 else if (opts == "minion")
835 h_font_math[0] = "minion-ntxm";
836 else if (opts == "cochineal")
837 h_font_math[0] = "cochineal-ntxm";
842 h_font_math[0] = "iwona-math";
844 if (name == "kurier")
846 h_font_math[0] = "kurier-math";
848 // after the detection and handling of special cases, we can remove the
849 // fonts, otherwise they would appear in the preamble, see bug #7856
850 if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
851 || is_known(name, known_typewriter_fonts) || is_known(name, known_math_fonts))
853 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
854 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
855 name == "esint" || name == "mhchem" || name == "mathdots" ||
856 name == "mathtools" || name == "stackrel" ||
857 name == "stmaryrd" || name == "undertilde")
858 h_use_packages[name] = "2";
860 else if (name == "babel") {
861 h_language_package = "default";
862 // One might think we would have to do nothing if babel is loaded
863 // without any options to prevent pollution of the preamble with this
864 // babel call in every roundtrip.
865 // But the user could have defined babel-specific things afterwards. So
866 // we need to keep it in the preamble to prevent cases like bug #7861.
868 // check if more than one option was used - used later for inputenc
869 if (options.begin() != options.end() - 1)
870 one_language = false;
871 // babel takes the last language of the option of its \usepackage
872 // call as document language. If there is no such language option, the
873 // last language in the documentclass options is used.
874 handle_opt(options, known_languages, h_language);
875 // translate the babel name to a LyX name
876 h_language = babel2lyx(h_language);
877 if (h_language == "japanese") {
878 // For Japanese, the encoding isn't indicated in the source
879 // file, and there's really not much we can do. We could
880 // 1) offer a list of possible encodings to choose from, or
881 // 2) determine the encoding of the file by inspecting it.
882 // For the time being, we leave the encoding alone so that
883 // we don't get iconv errors when making a wrong guess, and
884 // we will output a note at the top of the document
885 // explaining what to do.
886 Encoding const * const enc = encodings.fromIconvName(
887 p.getEncoding(), Encoding::japanese, false);
889 h_inputencoding = enc->name();
890 is_nonCJKJapanese = true;
891 // in this case babel can be removed from the preamble
892 registerAutomaticallyLoadedPackage("babel");
894 // If babel is called with options, LyX puts them by default into the
895 // document class options. This works for most languages, except
896 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
897 // perhaps in future others.
898 // Therefore keep the babel call as it is as the user might have
900 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
902 delete_opt(options, known_languages);
904 h_preamble << "\\usepackage{babel}\n";
905 explicit_babel = true;
909 else if (name == "polyglossia") {
910 h_language_package = "default";
911 h_default_output_format = "pdf4";
912 h_use_non_tex_fonts = true;
914 registerAutomaticallyLoadedPackage("xunicode");
915 if (h_inputencoding == "auto")
916 p.setEncoding("UTF-8");
919 else if (name == "CJK") {
920 // set the encoding to "auto" because it might be set to "default" by the babel handling
921 // and this would not be correct for CJK
922 if (h_inputencoding == "default")
923 h_inputencoding = "auto";
924 registerAutomaticallyLoadedPackage("CJK");
927 else if (name == "CJKutf8") {
928 h_inputencoding = "utf8-cjk";
929 p.setEncoding("UTF-8");
930 registerAutomaticallyLoadedPackage("CJKutf8");
933 else if (name == "fontenc") {
934 h_fontencoding = getStringFromVector(options, ",");
935 /* We could do the following for better round trip support,
936 * but this makes the document less portable, so I skip it:
937 if (h_fontencoding == lyxrc.fontenc)
938 h_fontencoding = "global";
943 else if (name == "inputenc" || name == "luainputenc") {
944 // h_inputencoding is only set when there is not more than one
945 // inputenc option because otherwise h_inputencoding must be
946 // set to "auto" (the default encoding of the document language)
947 // Therefore check that exactly one option is passed to inputenc.
948 // It is also only set when there is not more than one babel
950 if (!options.empty()) {
951 string const encoding = options.back();
952 Encoding const * const enc = encodings.fromLaTeXName(
953 encoding, Encoding::inputenc, true);
956 cerr << "Unknown encoding " << encoding
957 << ". Ignoring." << std::endl;
959 if (!enc->unsafe() && options.size() == 1 && one_language == true)
960 h_inputencoding = enc->name();
961 p.setEncoding(enc->iconvName());
967 else if (name == "srcltx") {
970 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
973 h_output_sync_macro = "\\usepackage{srcltx}";
976 else if (is_known(name, known_old_language_packages)) {
977 // known language packages from the times before babel
978 // if they are found and not also babel, they will be used as
979 // custom language package
980 h_language_package = "\\usepackage{" + name + "}";
983 else if (name == "lyxskak") {
984 // ignore this and its options
985 const char * const o[] = {"ps", "mover", 0};
986 delete_opt(options, o);
989 else if (is_known(name, known_lyx_packages) && options.empty()) {
990 if (name == "splitidx")
991 h_use_indices = "true";
992 if (name == "refstyle")
993 h_use_refstyle = true;
994 else if (name == "prettyref")
995 h_use_refstyle = false;
996 if (!in_lyx_preamble) {
997 h_preamble << package_beg_sep << name
998 << package_mid_sep << "\\usepackage{"
1000 if (p.next_token().cat() == catNewline ||
1001 (p.next_token().cat() == catSpace &&
1002 p.next_next_token().cat() == catNewline))
1004 h_preamble << package_end_sep;
1008 else if (name == "geometry")
1009 handle_geometry(options);
1011 else if (name == "subfig")
1012 ; // ignore this FIXME: Use the package separator mechanism instead
1014 else if ((where = is_known(name, known_languages)))
1015 h_language = known_coded_languages[where - known_languages];
1017 else if (name == "natbib") {
1018 h_biblio_style = "plainnat";
1019 h_cite_engine = "natbib";
1020 h_cite_engine_type = "authoryear";
1021 vector<string>::iterator it =
1022 find(options.begin(), options.end(), "authoryear");
1023 if (it != options.end())
1026 it = find(options.begin(), options.end(), "numbers");
1027 if (it != options.end()) {
1028 h_cite_engine_type = "numerical";
1034 else if (name == "jurabib") {
1035 h_biblio_style = "jurabib";
1036 h_cite_engine = "jurabib";
1037 h_cite_engine_type = "authoryear";
1040 else if (name == "bibtopic")
1041 h_use_bibtopic = "true";
1043 else if (name == "hyperref")
1044 handle_hyperref(options);
1046 else if (name == "algorithm2e") {
1047 // Load "algorithm2e" module
1048 addModule("algorithm2e");
1049 // Add the package options to the global document options
1050 if (!options.empty()) {
1051 if (h_options.empty())
1052 h_options = join(options, ",");
1054 h_options += ',' + join(options, ",");
1057 else if (name == "microtype") {
1058 //we internally support only microtype without params
1059 if (options.empty())
1060 h_use_microtype = "true";
1062 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1065 else if (!in_lyx_preamble) {
1066 if (options.empty())
1067 h_preamble << "\\usepackage{" << name << '}';
1069 h_preamble << "\\usepackage[" << opts << "]{"
1073 if (p.next_token().cat() == catNewline ||
1074 (p.next_token().cat() == catSpace &&
1075 p.next_next_token().cat() == catNewline))
1079 // We need to do something with the options...
1080 if (!options.empty() && !detectEncoding)
1081 cerr << "Ignoring options '" << join(options, ",")
1082 << "' of package " << name << '.' << endl;
1084 // remove the whitespace
1089 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1092 Token t = p.get_token();
1093 if (t.cat() == catEscape &&
1094 is_known(t.cs(), known_if_commands))
1095 handle_if(p, in_lyx_preamble);
1097 if (!in_lyx_preamble)
1098 h_preamble << t.asInput();
1099 if (t.cat() == catEscape && t.cs() == "fi")
1106 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1108 // set the quote language
1109 // LyX only knows the following quotes languages:
1110 // english, swedish, german, polish, french and danish
1111 // (quotes for "japanese" and "chinese-traditional" are missing because
1112 // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
1113 // conversion list taken from
1114 // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
1115 // (quotes for kazakh and interlingua are unknown)
1117 if (is_known(h_language, known_danish_quotes_languages))
1118 h_quotes_style = "danish";
1120 else if (is_known(h_language, known_french_quotes_languages))
1121 h_quotes_style = "french";
1123 else if (is_known(h_language, known_german_quotes_languages))
1124 h_quotes_style = "german";
1126 else if (is_known(h_language, known_polish_quotes_languages))
1127 h_quotes_style = "polish";
1129 else if (is_known(h_language, known_swedish_quotes_languages))
1130 h_quotes_style = "swedish";
1132 else if (is_known(h_language, known_english_quotes_languages))
1133 h_quotes_style = "english";
1135 if (contains(h_float_placement, "H"))
1136 registerAutomaticallyLoadedPackage("float");
1137 if (h_spacing != "single" && h_spacing != "default")
1138 registerAutomaticallyLoadedPackage("setspace");
1139 if (h_use_packages["amsmath"] == "2") {
1140 // amsbsy and amstext are already provided by amsmath
1141 registerAutomaticallyLoadedPackage("amsbsy");
1142 registerAutomaticallyLoadedPackage("amstext");
1145 // output the LyX file settings
1146 // Important: Keep the version formatting in sync with LyX and
1147 // lyx2lyx (bug 7951)
1148 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1149 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1150 << lyx_version_minor << '\n'
1151 << "\\lyxformat " << LYX_FORMAT << '\n'
1152 << "\\begin_document\n"
1153 << "\\begin_header\n"
1154 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1155 << "\\origin " << origin << "\n"
1156 << "\\textclass " << h_textclass << "\n";
1157 string const raw = subdoc ? empty_string() : h_preamble.str();
1159 os << "\\begin_preamble\n";
1160 for (string::size_type i = 0; i < raw.size(); ++i) {
1161 if (raw[i] == package_beg_sep) {
1162 // Here follows some package loading code that
1163 // must be skipped if the package is loaded
1165 string::size_type j = raw.find(package_mid_sep, i);
1166 if (j == string::npos)
1168 string::size_type k = raw.find(package_end_sep, j);
1169 if (k == string::npos)
1171 string const package = raw.substr(i + 1, j - i - 1);
1172 string const replacement = raw.substr(j + 1, k - j - 1);
1173 if (auto_packages.find(package) == auto_packages.end())
1179 os << "\n\\end_preamble\n";
1181 if (!h_options.empty())
1182 os << "\\options " << h_options << "\n";
1183 os << "\\use_default_options " << h_use_default_options << "\n";
1184 if (!used_modules.empty()) {
1185 os << "\\begin_modules\n";
1186 vector<string>::const_iterator const end = used_modules.end();
1187 vector<string>::const_iterator it = used_modules.begin();
1188 for (; it != end; ++it)
1190 os << "\\end_modules\n";
1192 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1193 << "\\language " << h_language << "\n"
1194 << "\\language_package " << h_language_package << "\n"
1195 << "\\inputencoding " << h_inputencoding << "\n"
1196 << "\\fontencoding " << h_fontencoding << "\n"
1197 << "\\font_roman \"" << h_font_roman[0]
1198 << "\" \"" << h_font_roman[1] << "\"\n"
1199 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1200 << "\\font_typewriter \"" << h_font_typewriter[0]
1201 << "\" \"" << h_font_typewriter[1] << "\"\n"
1202 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1203 << "\\font_default_family " << h_font_default_family << "\n"
1204 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1205 << "\\font_sc " << h_font_sc << "\n"
1206 << "\\font_osf " << h_font_osf << "\n"
1207 << "\\font_sf_scale " << h_font_sf_scale[0]
1208 << ' ' << h_font_sf_scale[1] << '\n'
1209 << "\\font_tt_scale " << h_font_tt_scale[0]
1210 << ' ' << h_font_tt_scale[1] << '\n';
1211 if (!h_font_cjk.empty())
1212 os << "\\font_cjk " << h_font_cjk << '\n';
1213 os << "\\use_microtype " << h_use_microtype << '\n'
1214 << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
1215 << "\\graphics " << h_graphics << '\n'
1216 << "\\default_output_format " << h_default_output_format << "\n"
1217 << "\\output_sync " << h_output_sync << "\n";
1218 if (h_output_sync == "1")
1219 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1220 os << "\\bibtex_command " << h_bibtex_command << "\n"
1221 << "\\index_command " << h_index_command << "\n";
1222 if (!h_float_placement.empty())
1223 os << "\\float_placement " << h_float_placement << "\n";
1224 os << "\\paperfontsize " << h_paperfontsize << "\n"
1225 << "\\spacing " << h_spacing << "\n"
1226 << "\\use_hyperref " << h_use_hyperref << '\n';
1227 if (h_use_hyperref == "true") {
1228 if (!h_pdf_title.empty())
1229 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1230 if (!h_pdf_author.empty())
1231 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1232 if (!h_pdf_subject.empty())
1233 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1234 if (!h_pdf_keywords.empty())
1235 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1236 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1237 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1238 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1239 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1240 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1241 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1242 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1243 "\\pdf_backref " << h_pdf_backref << "\n"
1244 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1245 if (!h_pdf_pagemode.empty())
1246 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1247 if (!h_pdf_quoted_options.empty())
1248 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1250 os << "\\papersize " << h_papersize << "\n"
1251 << "\\use_geometry " << h_use_geometry << '\n';
1252 for (map<string, string>::const_iterator it = h_use_packages.begin();
1253 it != h_use_packages.end(); ++it)
1254 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1255 os << "\\cite_engine " << h_cite_engine << '\n'
1256 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1257 << "\\biblio_style " << h_biblio_style << "\n"
1258 << "\\use_bibtopic " << h_use_bibtopic << "\n"
1259 << "\\use_indices " << h_use_indices << "\n"
1260 << "\\paperorientation " << h_paperorientation << '\n'
1261 << "\\suppress_date " << h_suppress_date << '\n'
1262 << "\\justification " << h_justification << '\n'
1263 << "\\use_refstyle " << h_use_refstyle << '\n';
1264 if (!h_fontcolor.empty())
1265 os << "\\fontcolor " << h_fontcolor << '\n';
1266 if (!h_notefontcolor.empty())
1267 os << "\\notefontcolor " << h_notefontcolor << '\n';
1268 if (!h_backgroundcolor.empty())
1269 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1270 if (!h_boxbgcolor.empty())
1271 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1272 if (index_number != 0)
1273 for (int i = 0; i < index_number; i++) {
1274 os << "\\index " << h_index[i] << '\n'
1275 << "\\shortcut " << h_shortcut[i] << '\n'
1276 << "\\color " << h_color << '\n'
1280 os << "\\index " << h_index[0] << '\n'
1281 << "\\shortcut " << h_shortcut[0] << '\n'
1282 << "\\color " << h_color << '\n'
1286 << "\\secnumdepth " << h_secnumdepth << "\n"
1287 << "\\tocdepth " << h_tocdepth << "\n"
1288 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1289 if (h_paragraph_separation == "skip")
1290 os << "\\defskip " << h_defskip << "\n";
1292 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1293 os << "\\is_math_indent " << h_is_mathindent << "\n";
1294 if (!h_mathindentation.empty())
1295 os << "\\math_indentation " << h_mathindentation << "\n";
1296 os << "\\quotes_style " << h_quotes_style << "\n"
1297 << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
1298 << "\\papercolumns " << h_papercolumns << "\n"
1299 << "\\papersides " << h_papersides << "\n"
1300 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1301 if (!h_listings_params.empty())
1302 os << "\\listings_params " << h_listings_params << "\n";
1303 os << "\\tracking_changes " << h_tracking_changes << "\n"
1304 << "\\output_changes " << h_output_changes << "\n"
1305 << "\\html_math_output " << h_html_math_output << "\n"
1306 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1307 << "\\html_be_strict " << h_html_be_strict << "\n"
1309 << "\\end_header\n\n"
1310 << "\\begin_body\n";
1315 void Preamble::parse(Parser & p, string const & forceclass,
1316 TeX2LyXDocClass & tc)
1318 // initialize fixed types
1319 special_columns_['D'] = 3;
1320 parse(p, forceclass, false, tc);
1324 void Preamble::parse(Parser & p, string const & forceclass,
1325 bool detectEncoding, TeX2LyXDocClass & tc)
1327 bool is_full_document = false;
1328 bool is_lyx_file = false;
1329 bool in_lyx_preamble = false;
1331 // determine whether this is a full document or a fragment for inclusion
1333 Token const & t = p.get_token();
1335 if (t.cat() == catEscape && t.cs() == "documentclass") {
1336 is_full_document = true;
1342 if (detectEncoding && !is_full_document)
1345 while (is_full_document && p.good()) {
1346 if (detectEncoding && h_inputencoding != "auto" &&
1347 h_inputencoding != "default")
1350 Token const & t = p.get_token();
1353 if (!detectEncoding)
1354 cerr << "t: " << t << '\n';
1360 if (!in_lyx_preamble &&
1361 (t.cat() == catLetter ||
1362 t.cat() == catSuper ||
1363 t.cat() == catSub ||
1364 t.cat() == catOther ||
1365 t.cat() == catMath ||
1366 t.cat() == catActive ||
1367 t.cat() == catBegin ||
1368 t.cat() == catEnd ||
1369 t.cat() == catAlign ||
1370 t.cat() == catParameter))
1371 h_preamble << t.cs();
1373 else if (!in_lyx_preamble &&
1374 (t.cat() == catSpace || t.cat() == catNewline))
1375 h_preamble << t.asInput();
1377 else if (t.cat() == catComment) {
1378 static regex const islyxfile("%% LyX .* created this file");
1379 static regex const usercommands("User specified LaTeX commands");
1381 string const comment = t.asInput();
1383 // magically switch encoding default if it looks like XeLaTeX
1384 static string const magicXeLaTeX =
1385 "% This document must be compiled with XeLaTeX ";
1386 if (comment.size() > magicXeLaTeX.size()
1387 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1388 && h_inputencoding == "auto") {
1389 if (!detectEncoding)
1390 cerr << "XeLaTeX comment found, switching to UTF8\n";
1391 h_inputencoding = "utf8";
1394 if (regex_search(comment, sub, islyxfile)) {
1396 in_lyx_preamble = true;
1397 } else if (is_lyx_file
1398 && regex_search(comment, sub, usercommands))
1399 in_lyx_preamble = false;
1400 else if (!in_lyx_preamble)
1401 h_preamble << t.asInput();
1404 else if (t.cs() == "pagestyle")
1405 h_paperpagestyle = p.verbatim_item();
1407 else if (t.cs() == "setdefaultlanguage") {
1409 // We don't yet care about non-language variant options
1410 // because LyX doesn't support this yet, see bug #8214
1412 string langopts = p.getOpt();
1413 // check if the option contains a variant, if yes, extract it
1414 string::size_type pos_var = langopts.find("variant");
1415 string::size_type i = langopts.find(',', pos_var);
1416 string::size_type k = langopts.find('=', pos_var);
1417 if (pos_var != string::npos){
1419 if (i == string::npos)
1420 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1422 variant = langopts.substr(k + 1, i - k - 1);
1423 h_language = variant;
1427 h_language = p.verbatim_item();
1428 //finally translate the poyglossia name to a LyX name
1429 h_language = polyglossia2lyx(h_language);
1432 else if (t.cs() == "setotherlanguage") {
1433 // We don't yet care about the option because LyX doesn't
1434 // support this yet, see bug #8214
1435 p.hasOpt() ? p.getOpt() : string();
1439 else if (t.cs() == "setmainfont") {
1440 // we don't care about the option
1441 p.hasOpt() ? p.getOpt() : string();
1442 h_font_roman[1] = p.getArg('{', '}');
1445 else if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1446 // LyX currently only supports the scale option
1449 string fontopts = p.getArg('[', ']');
1450 // check if the option contains a scaling, if yes, extract it
1451 string::size_type pos = fontopts.find("Scale");
1452 if (pos != string::npos) {
1453 string::size_type i = fontopts.find(',', pos);
1454 if (i == string::npos)
1455 scale_as_percentage(fontopts.substr(pos + 1), scale);
1457 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1460 if (t.cs() == "setsansfont") {
1462 h_font_sf_scale[1] = scale;
1463 h_font_sans[1] = p.getArg('{', '}');
1466 h_font_tt_scale[1] = scale;
1467 h_font_typewriter[1] = p.getArg('{', '}');
1471 else if (t.cs() == "date") {
1472 string argument = p.getArg('{', '}');
1473 if (argument.empty())
1474 h_suppress_date = "true";
1476 h_preamble << t.asInput() << '{' << argument << '}';
1479 else if (t.cs() == "color") {
1480 string const space =
1481 (p.hasOpt() ? p.getOpt() : string());
1482 string argument = p.getArg('{', '}');
1483 // check the case that a standard color is used
1484 if (space.empty() && is_known(argument, known_basic_colors)) {
1485 h_fontcolor = rgbcolor2code(argument);
1486 registerAutomaticallyLoadedPackage("color");
1487 } else if (space.empty() && argument == "document_fontcolor")
1488 registerAutomaticallyLoadedPackage("color");
1489 // check the case that LyX's document_fontcolor is defined
1490 // but not used for \color
1492 h_preamble << t.asInput();
1494 h_preamble << space;
1495 h_preamble << '{' << argument << '}';
1496 // the color might already be set because \definecolor
1497 // is parsed before this
1502 else if (t.cs() == "pagecolor") {
1503 string argument = p.getArg('{', '}');
1504 // check the case that a standard color is used
1505 if (is_known(argument, known_basic_colors)) {
1506 h_backgroundcolor = rgbcolor2code(argument);
1507 } else if (argument == "page_backgroundcolor")
1508 registerAutomaticallyLoadedPackage("color");
1509 // check the case that LyX's page_backgroundcolor is defined
1510 // but not used for \pagecolor
1512 h_preamble << t.asInput() << '{' << argument << '}';
1513 // the color might already be set because \definecolor
1514 // is parsed before this
1515 h_backgroundcolor = "";
1519 else if (t.cs() == "makeatletter") {
1520 // LyX takes care of this
1521 p.setCatcode('@', catLetter);
1524 else if (t.cs() == "makeatother") {
1525 // LyX takes care of this
1526 p.setCatcode('@', catOther);
1529 else if (t.cs() == "makeindex") {
1530 // LyX will re-add this if a print index command is found
1534 else if (t.cs() == "newindex") {
1535 string const indexname = p.getArg('[', ']');
1536 string const shortcut = p.verbatim_item();
1537 if (!indexname.empty())
1538 h_index[index_number] = indexname;
1540 h_index[index_number] = shortcut;
1541 h_shortcut[index_number] = shortcut;
1546 else if (t.cs() == "RS@ifundefined") {
1547 string const name = p.verbatim_item();
1548 string const body1 = p.verbatim_item();
1549 string const body2 = p.verbatim_item();
1550 // only non-lyxspecific stuff
1551 if (in_lyx_preamble &&
1552 (name == "subsecref" || name == "thmref" || name == "lemref"))
1556 ss << '\\' << t.cs();
1557 ss << '{' << name << '}'
1558 << '{' << body1 << '}'
1559 << '{' << body2 << '}';
1560 h_preamble << ss.str();
1564 else if (t.cs() == "AtBeginDocument") {
1565 string const name = p.verbatim_item();
1566 // only non-lyxspecific stuff
1567 if (in_lyx_preamble &&
1568 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
1569 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
1570 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
1571 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
1572 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
1573 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
1574 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
1575 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
1576 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
1577 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
1578 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
1579 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
1580 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
1581 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
1582 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
1586 ss << '\\' << t.cs();
1587 ss << '{' << name << '}';
1588 h_preamble << ss.str();
1592 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1593 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1594 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1595 || t.cs() == "DeclareRobustCommand"
1596 || t.cs() == "DeclareRobustCommandx"
1597 || t.cs() == "ProvideTextCommandDefault"
1598 || t.cs() == "DeclareMathAccent") {
1600 if (p.next_token().character() == '*') {
1604 string const name = p.verbatim_item();
1605 string const opt1 = p.getFullOpt();
1606 string const opt2 = p.getFullOpt();
1607 string const body = p.verbatim_item();
1608 // store the in_lyx_preamble setting
1609 bool const was_in_lyx_preamble = in_lyx_preamble;
1611 if (name == "\\rmdefault")
1612 if (is_known(body, known_roman_fonts)) {
1613 h_font_roman[0] = body;
1615 in_lyx_preamble = true;
1617 if (name == "\\sfdefault")
1618 if (is_known(body, known_sans_fonts)) {
1619 h_font_sans[0] = body;
1621 in_lyx_preamble = true;
1623 if (name == "\\ttdefault")
1624 if (is_known(body, known_typewriter_fonts)) {
1625 h_font_typewriter[0] = body;
1627 in_lyx_preamble = true;
1629 if (name == "\\familydefault") {
1630 string family = body;
1631 // remove leading "\"
1632 h_font_default_family = family.erase(0,1);
1634 in_lyx_preamble = true;
1637 // remove LyX-specific definitions that are re-added by LyX
1639 // \lyxline is an ancient command that is converted by tex2lyx into
1640 // a \rule therefore remove its preamble code
1641 if (name == "\\lyxdot" || name == "\\lyxarrow"
1642 || name == "\\lyxline" || name == "\\LyX") {
1644 in_lyx_preamble = true;
1647 // Add the command to the known commands
1648 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1650 // only non-lyxspecific stuff
1651 if (!in_lyx_preamble) {
1653 ss << '\\' << t.cs();
1656 ss << '{' << name << '}' << opt1 << opt2
1657 << '{' << body << "}";
1658 h_preamble << ss.str();
1660 ostream & out = in_preamble ? h_preamble : os;
1661 out << "\\" << t.cs() << "{" << name << "}"
1662 << opts << "{" << body << "}";
1665 // restore the in_lyx_preamble setting
1666 in_lyx_preamble = was_in_lyx_preamble;
1669 else if (t.cs() == "documentclass") {
1670 vector<string>::iterator it;
1671 vector<string> opts = split_options(p.getArg('[', ']'));
1672 handle_opt(opts, known_fontsizes, h_paperfontsize);
1673 delete_opt(opts, known_fontsizes);
1674 // delete "pt" at the end
1675 string::size_type i = h_paperfontsize.find("pt");
1676 if (i != string::npos)
1677 h_paperfontsize.erase(i);
1678 // The documentclass options are always parsed before the options
1679 // of the babel call so that a language cannot overwrite the babel
1681 handle_opt(opts, known_languages, h_language);
1682 delete_opt(opts, known_languages);
1685 if ((it = find(opts.begin(), opts.end(), "fleqn"))
1687 h_is_mathindent = "1";
1690 // paper orientation
1691 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1692 h_paperorientation = "landscape";
1696 if ((it = find(opts.begin(), opts.end(), "oneside"))
1701 if ((it = find(opts.begin(), opts.end(), "twoside"))
1707 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1709 h_papercolumns = "1";
1712 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1714 h_papercolumns = "2";
1718 // some size options are known to any document classes, other sizes
1719 // are handled by the \geometry command of the geometry package
1720 handle_opt(opts, known_class_paper_sizes, h_papersize);
1721 delete_opt(opts, known_class_paper_sizes);
1722 // the remaining options
1723 h_options = join(opts, ",");
1724 // FIXME This does not work for classes that have a
1725 // different name in LyX than in LaTeX
1726 h_textclass = p.getArg('{', '}');
1730 else if (t.cs() == "usepackage") {
1731 string const options = p.getArg('[', ']');
1732 string const name = p.getArg('{', '}');
1733 vector<string> vecnames;
1734 split(name, vecnames, ',');
1735 vector<string>::const_iterator it = vecnames.begin();
1736 vector<string>::const_iterator end = vecnames.end();
1737 for (; it != end; ++it)
1738 handle_package(p, trimSpaceAndEol(*it), options,
1739 in_lyx_preamble, detectEncoding);
1742 else if (t.cs() == "inputencoding") {
1743 string const encoding = p.getArg('{','}');
1744 Encoding const * const enc = encodings.fromLaTeXName(
1745 encoding, Encoding::inputenc, true);
1747 if (!detectEncoding)
1748 cerr << "Unknown encoding " << encoding
1749 << ". Ignoring." << std::endl;
1752 h_inputencoding = enc->name();
1753 p.setEncoding(enc->iconvName());
1757 else if (t.cs() == "newenvironment") {
1758 string const name = p.getArg('{', '}');
1759 string const opt1 = p.getFullOpt();
1760 string const opt2 = p.getFullOpt();
1761 string const beg = p.verbatim_item();
1762 string const end = p.verbatim_item();
1763 if (!in_lyx_preamble) {
1764 h_preamble << "\\newenvironment{" << name
1765 << '}' << opt1 << opt2 << '{'
1766 << beg << "}{" << end << '}';
1768 add_known_environment(name, opt1, !opt2.empty(),
1769 from_utf8(beg), from_utf8(end));
1773 else if (t.cs() == "newtheorem") {
1774 string const name = p.getArg('{', '}');
1775 string const opt1 = p.getFullOpt();
1776 string const opt2 = p.getFullOpt();
1777 string const body = p.verbatim_item();
1778 string const opt3 = p.getFullOpt();
1780 string const complete = "\\newtheorem{" + name + '}' +
1781 opt1 + opt2 + '{' + body + '}' + opt3;
1783 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
1785 if (!in_lyx_preamble)
1786 h_preamble << complete;
1789 else if (t.cs() == "def") {
1790 string name = p.get_token().cs();
1791 // In fact, name may be more than the name:
1792 // In the test case of bug 8116
1793 // name == "csname SF@gobble@opt \endcsname".
1794 // Therefore, we need to use asInput() instead of cs().
1795 while (p.next_token().cat() != catBegin)
1796 name += p.get_token().asInput();
1797 if (!in_lyx_preamble)
1798 h_preamble << "\\def\\" << name << '{'
1799 << p.verbatim_item() << "}";
1802 else if (t.cs() == "newcolumntype") {
1803 string const name = p.getArg('{', '}');
1804 trimSpaceAndEol(name);
1806 string opts = p.getOpt();
1807 if (!opts.empty()) {
1808 istringstream is(string(opts, 1));
1811 special_columns_[name[0]] = nargs;
1812 h_preamble << "\\newcolumntype{" << name << "}";
1814 h_preamble << "[" << nargs << "]";
1815 h_preamble << "{" << p.verbatim_item() << "}";
1818 else if (t.cs() == "setcounter") {
1819 string const name = p.getArg('{', '}');
1820 string const content = p.getArg('{', '}');
1821 if (name == "secnumdepth")
1822 h_secnumdepth = content;
1823 else if (name == "tocdepth")
1824 h_tocdepth = content;
1826 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1829 else if (t.cs() == "setlength") {
1830 string const name = p.verbatim_item();
1831 string const content = p.verbatim_item();
1832 // the paragraphs are only not indented when \parindent is set to zero
1833 if (name == "\\parindent" && content != "") {
1834 if (content[0] == '0')
1835 h_paragraph_separation = "skip";
1837 h_paragraph_indentation = translate_len(content);
1838 } else if (name == "\\parskip") {
1839 if (content == "\\smallskipamount")
1840 h_defskip = "smallskip";
1841 else if (content == "\\medskipamount")
1842 h_defskip = "medskip";
1843 else if (content == "\\bigskipamount")
1844 h_defskip = "bigskip";
1846 h_defskip = translate_len(content);
1847 } else if (name == "\\mathindent") {
1848 h_mathindentation = translate_len(content);
1850 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1853 else if (t.cs() == "onehalfspacing")
1854 h_spacing = "onehalf";
1856 else if (t.cs() == "doublespacing")
1857 h_spacing = "double";
1859 else if (t.cs() == "setstretch")
1860 h_spacing = "other " + p.verbatim_item();
1862 else if (t.cs() == "synctex") {
1863 // the scheme is \synctex=value
1864 // where value can only be "1" or "-1"
1865 h_output_sync = "1";
1866 // there can be any character behind the value (e.g. a linebreak or a '\'
1867 // therefore we extract it char by char
1869 string value = p.get_token().asInput();
1871 value += p.get_token().asInput();
1872 h_output_sync_macro = "\\synctex=" + value;
1875 else if (t.cs() == "begin") {
1876 string const name = p.getArg('{', '}');
1877 if (name == "document")
1879 h_preamble << "\\begin{" << name << "}";
1882 else if (t.cs() == "geometry") {
1883 vector<string> opts = split_options(p.getArg('{', '}'));
1884 handle_geometry(opts);
1887 else if (t.cs() == "definecolor") {
1888 string const color = p.getArg('{', '}');
1889 string const space = p.getArg('{', '}');
1890 string const value = p.getArg('{', '}');
1891 if (color == "document_fontcolor" && space == "rgb") {
1892 RGBColor c(RGBColorFromLaTeX(value));
1893 h_fontcolor = X11hexname(c);
1894 } else if (color == "note_fontcolor" && space == "rgb") {
1895 RGBColor c(RGBColorFromLaTeX(value));
1896 h_notefontcolor = X11hexname(c);
1897 } else if (color == "page_backgroundcolor" && space == "rgb") {
1898 RGBColor c(RGBColorFromLaTeX(value));
1899 h_backgroundcolor = X11hexname(c);
1900 } else if (color == "shadecolor" && space == "rgb") {
1901 RGBColor c(RGBColorFromLaTeX(value));
1902 h_boxbgcolor = X11hexname(c);
1904 h_preamble << "\\definecolor{" << color
1905 << "}{" << space << "}{" << value
1910 else if (t.cs() == "bibliographystyle")
1911 h_biblio_style = p.verbatim_item();
1913 else if (t.cs() == "jurabibsetup") {
1914 // FIXME p.getArg('{', '}') is most probably wrong (it
1915 // does not handle nested braces).
1916 // Use p.verbatim_item() instead.
1917 vector<string> jurabibsetup =
1918 split_options(p.getArg('{', '}'));
1919 // add jurabibsetup to the jurabib package options
1920 add_package("jurabib", jurabibsetup);
1921 if (!jurabibsetup.empty()) {
1922 h_preamble << "\\jurabibsetup{"
1923 << join(jurabibsetup, ",") << '}';
1927 else if (t.cs() == "hypersetup") {
1928 vector<string> hypersetup =
1929 split_options(p.verbatim_item());
1930 // add hypersetup to the hyperref package options
1931 handle_hyperref(hypersetup);
1932 if (!hypersetup.empty()) {
1933 h_preamble << "\\hypersetup{"
1934 << join(hypersetup, ",") << '}';
1938 else if (is_known(t.cs(), known_if_3arg_commands)) {
1939 // prevent misparsing of \usepackage if it is used
1940 // as an argument (see e.g. our own output of
1941 // \@ifundefined above)
1942 string const arg1 = p.verbatim_item();
1943 string const arg2 = p.verbatim_item();
1944 string const arg3 = p.verbatim_item();
1945 // test case \@ifundefined{date}{}{\date{}}
1946 if (t.cs() == "@ifundefined" && arg1 == "date" &&
1947 arg2.empty() && arg3 == "\\date{}") {
1948 h_suppress_date = "true";
1949 // older tex2lyx versions did output
1950 // \@ifundefined{definecolor}{\usepackage{color}}{}
1951 } else if (t.cs() == "@ifundefined" &&
1952 arg1 == "definecolor" &&
1953 arg2 == "\\usepackage{color}" &&
1955 if (!in_lyx_preamble)
1956 h_preamble << package_beg_sep
1959 << "\\@ifundefined{definecolor}{color}{}"
1962 //\@ifundefined{showcaptionsetup}{}{%
1963 // \PassOptionsToPackage{caption=false}{subfig}}
1964 // that LyX uses for subfloats
1965 } else if (t.cs() == "@ifundefined" &&
1966 arg1 == "showcaptionsetup" && arg2.empty()
1967 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1969 } else if (!in_lyx_preamble) {
1970 h_preamble << t.asInput()
1971 << '{' << arg1 << '}'
1972 << '{' << arg2 << '}'
1973 << '{' << arg3 << '}';
1977 else if (is_known(t.cs(), known_if_commands)) {
1978 // must not parse anything in conditional code, since
1979 // LyX would output the parsed contents unconditionally
1980 if (!in_lyx_preamble)
1981 h_preamble << t.asInput();
1982 handle_if(p, in_lyx_preamble);
1985 else if (!t.cs().empty() && !in_lyx_preamble)
1986 h_preamble << '\\' << t.cs();
1989 // remove the whitespace
1992 // Force textclass if the user wanted it
1993 if (!forceclass.empty())
1994 h_textclass = forceclass;
1995 tc.setName(h_textclass);
1997 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2000 if (h_papersides.empty()) {
2003 h_papersides = ss.str();
2006 // If the CJK package is used we cannot set the document language from
2007 // the babel options. Instead, we guess which language is used most
2008 // and set this one.
2009 default_language = h_language;
2010 if (is_full_document &&
2011 (auto_packages.find("CJK") != auto_packages.end() ||
2012 auto_packages.find("CJKutf8") != auto_packages.end())) {
2014 h_language = guessLanguage(p, default_language);
2016 if (explicit_babel && h_language != default_language) {
2017 // We set the document language to a CJK language,
2018 // but babel is explicitly called in the user preamble
2019 // without options. LyX will not add the default
2020 // language to the document options if it is either
2021 // english, or no text is set as default language.
2022 // Therefore we need to add a language option explicitly.
2023 // FIXME: It would be better to remove all babel calls
2024 // from the user preamble, but this is difficult
2025 // without re-introducing bug 7861.
2026 if (h_options.empty())
2027 h_options = lyx2babel(default_language);
2029 h_options += ',' + lyx2babel(default_language);
2035 string Preamble::parseEncoding(Parser & p, string const & forceclass)
2037 TeX2LyXDocClass dummy;
2038 parse(p, forceclass, true, dummy);
2039 if (h_inputencoding != "auto" && h_inputencoding != "default")
2040 return h_inputencoding;
2045 string babel2lyx(string const & language)
2047 char const * const * where = is_known(language, known_languages);
2049 return known_coded_languages[where - known_languages];
2054 string lyx2babel(string const & language)
2056 char const * const * where = is_known(language, known_coded_languages);
2058 return known_languages[where - known_coded_languages];
2063 string Preamble::polyglossia2lyx(string const & language)
2065 char const * const * where = is_known(language, polyglossia_languages);
2067 return coded_polyglossia_languages[where - polyglossia_languages];
2072 string rgbcolor2code(string const & name)
2074 char const * const * where = is_known(name, known_basic_colors);
2076 // "red", "green" etc
2077 return known_basic_color_codes[where - known_basic_colors];
2079 // "255,0,0", "0,255,0" etc
2080 RGBColor c(RGBColorFromLaTeX(name));
2081 return X11hexname(c);