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_graphics = "default";
496 h_default_output_format = "default";
497 h_html_be_strict = "false";
498 h_html_css_as_file = "0";
499 h_html_math_output = "0";
500 h_index[0] = "Index";
501 h_index_command = "default";
502 h_inputencoding = "auto";
503 h_justification = "true";
504 h_language = "english";
505 h_language_package = "none";
507 h_maintain_unincluded_children = "false";
511 h_output_changes = "false";
513 //h_output_sync_macro
514 h_papercolumns = "1";
515 h_paperfontsize = "default";
516 h_paperorientation = "portrait";
517 h_paperpagestyle = "default";
519 h_papersize = "default";
520 h_paragraph_indentation = "default";
521 h_paragraph_separation = "indent";
526 h_pdf_bookmarks = "0";
527 h_pdf_bookmarksnumbered = "0";
528 h_pdf_bookmarksopen = "0";
529 h_pdf_bookmarksopenlevel = "1";
530 h_pdf_breaklinks = "0";
531 h_pdf_pdfborder = "0";
532 h_pdf_colorlinks = "0";
533 h_pdf_backref = "section";
534 h_pdf_pdfusetitle = "0";
536 //h_pdf_quoted_options;
537 h_quotes_style = "english";
539 h_shortcut[0] = "idx";
540 h_spacing = "single";
541 h_save_transient_properties = "true";
542 h_suppress_date = "false";
543 h_textclass = "article";
545 h_tracking_changes = "false";
546 h_use_bibtopic = "false";
547 h_use_indices = "false";
548 h_use_geometry = "false";
549 h_use_default_options = "false";
550 h_use_hyperref = "false";
551 h_use_microtype = false;
552 h_use_refstyle = false;
553 h_use_packages["amsmath"] = "1";
554 h_use_packages["amssymb"] = "0";
555 h_use_packages["cancel"] = "0";
556 h_use_packages["esint"] = "1";
557 h_use_packages["mhchem"] = "0";
558 h_use_packages["mathdots"] = "0";
559 h_use_packages["mathtools"] = "0";
560 h_use_packages["stackrel"] = "0";
561 h_use_packages["stmaryrd"] = "0";
562 h_use_packages["undertilde"] = "0";
566 void Preamble::handle_hyperref(vector<string> & options)
568 // FIXME swallow inputencoding changes that might surround the
569 // hyperref setup if it was written by LyX
570 h_use_hyperref = "true";
571 // swallow "unicode=true", since LyX does always write that
572 vector<string>::iterator it =
573 find(options.begin(), options.end(), "unicode=true");
574 if (it != options.end())
576 it = find(options.begin(), options.end(), "pdfusetitle");
577 if (it != options.end()) {
578 h_pdf_pdfusetitle = "1";
581 string bookmarks = process_keyval_opt(options, "bookmarks");
582 if (bookmarks == "true")
583 h_pdf_bookmarks = "1";
584 else if (bookmarks == "false")
585 h_pdf_bookmarks = "0";
586 if (h_pdf_bookmarks == "1") {
587 string bookmarksnumbered =
588 process_keyval_opt(options, "bookmarksnumbered");
589 if (bookmarksnumbered == "true")
590 h_pdf_bookmarksnumbered = "1";
591 else if (bookmarksnumbered == "false")
592 h_pdf_bookmarksnumbered = "0";
593 string bookmarksopen =
594 process_keyval_opt(options, "bookmarksopen");
595 if (bookmarksopen == "true")
596 h_pdf_bookmarksopen = "1";
597 else if (bookmarksopen == "false")
598 h_pdf_bookmarksopen = "0";
599 if (h_pdf_bookmarksopen == "1") {
600 string bookmarksopenlevel =
601 process_keyval_opt(options, "bookmarksopenlevel");
602 if (!bookmarksopenlevel.empty())
603 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
606 string breaklinks = process_keyval_opt(options, "breaklinks");
607 if (breaklinks == "true")
608 h_pdf_breaklinks = "1";
609 else if (breaklinks == "false")
610 h_pdf_breaklinks = "0";
611 string pdfborder = process_keyval_opt(options, "pdfborder");
612 if (pdfborder == "{0 0 0}")
613 h_pdf_pdfborder = "1";
614 else if (pdfborder == "{0 0 1}")
615 h_pdf_pdfborder = "0";
616 string backref = process_keyval_opt(options, "backref");
617 if (!backref.empty())
618 h_pdf_backref = backref;
619 string colorlinks = process_keyval_opt(options, "colorlinks");
620 if (colorlinks == "true")
621 h_pdf_colorlinks = "1";
622 else if (colorlinks == "false")
623 h_pdf_colorlinks = "0";
624 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
625 if (!pdfpagemode.empty())
626 h_pdf_pagemode = pdfpagemode;
627 string pdftitle = process_keyval_opt(options, "pdftitle");
628 if (!pdftitle.empty()) {
629 h_pdf_title = remove_braces(pdftitle);
631 string pdfauthor = process_keyval_opt(options, "pdfauthor");
632 if (!pdfauthor.empty()) {
633 h_pdf_author = remove_braces(pdfauthor);
635 string pdfsubject = process_keyval_opt(options, "pdfsubject");
636 if (!pdfsubject.empty())
637 h_pdf_subject = remove_braces(pdfsubject);
638 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
639 if (!pdfkeywords.empty())
640 h_pdf_keywords = remove_braces(pdfkeywords);
641 if (!options.empty()) {
642 if (!h_pdf_quoted_options.empty())
643 h_pdf_quoted_options += ',';
644 h_pdf_quoted_options += join(options, ",");
650 void Preamble::handle_geometry(vector<string> & options)
652 h_use_geometry = "true";
653 vector<string>::iterator it;
655 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
656 h_paperorientation = "landscape";
660 // keyval version: "paper=letter"
661 string paper = process_keyval_opt(options, "paper");
663 h_papersize = paper + "paper";
664 // alternative version: "letterpaper"
665 handle_opt(options, known_paper_sizes, h_papersize);
666 delete_opt(options, known_paper_sizes);
668 char const * const * margin = known_paper_margins;
669 for (; *margin; ++margin) {
670 string value = process_keyval_opt(options, *margin);
671 if (!value.empty()) {
672 int k = margin - known_paper_margins;
673 string name = known_coded_paper_margins[k];
674 h_margins += '\\' + name + ' ' + value + '\n';
680 void Preamble::handle_package(Parser &p, string const & name,
681 string const & opts, bool in_lyx_preamble,
684 vector<string> options = split_options(opts);
685 add_package(name, options);
686 char const * const * where = 0;
688 if (is_known(name, known_xetex_packages)) {
690 h_use_non_tex_fonts = true;
691 registerAutomaticallyLoadedPackage("fontspec");
692 if (h_inputencoding == "auto")
693 p.setEncoding("UTF-8");
697 if (is_known(name, known_roman_fonts))
698 h_font_roman[0] = name;
700 if (name == "fourier") {
701 h_font_roman[0] = "utopia";
702 // when font uses real small capitals
703 if (opts == "expert")
707 if (name == "garamondx") {
708 h_font_roman[0] = "garamondx";
713 if (name == "libertine") {
714 h_font_roman[0] = "libertine";
715 // this automatically invokes biolinum
716 h_font_sans[0] = "biolinum";
719 else if (opts == "lining")
720 h_font_osf = "false";
723 if (name == "libertine-type1") {
724 h_font_roman[0] = "libertine";
725 // NOTE: contrary to libertine.sty, libertine-type1
726 // does not automatically invoke biolinum
727 if (opts == "lining")
728 h_font_osf = "false";
729 else if (opts == "osf")
733 if (name == "mathdesign") {
734 if (opts.find("charter") != string::npos)
735 h_font_roman[0] = "md-charter";
736 if (opts.find("garamond") != string::npos)
737 h_font_roman[0] = "md-garamond";
738 if (opts.find("utopia") != string::npos)
739 h_font_roman[0] = "md-utopia";
740 if (opts.find("expert") != string::npos) {
746 else if (name == "mathpazo")
747 h_font_roman[0] = "palatino";
749 else if (name == "mathptmx")
750 h_font_roman[0] = "times";
752 if (name == "crimson")
753 h_font_roman[0] = "cochineal";
755 if (name == "cochineal") {
756 h_font_roman[0] = "cochineal";
757 // cochineal can have several options, e.g. [proportional,osf]
758 string::size_type pos = opts.find("osf");
759 if (pos != string::npos)
763 if (name == "noto") {
764 // noto can have several options
766 h_font_roman[0] = "NotoSerif-TLF";
767 string::size_type pos = opts.find("rm");
768 if (pos != string::npos)
769 h_font_roman[0] = "NotoSerif-TLF";
770 pos = opts.find("sf");
771 if (pos != string::npos)
772 h_font_sans[0] = "NotoSans-TLF";
773 pos = opts.find("nott");
774 if (pos != string::npos) {
775 h_font_roman[0] = "NotoSerif-TLF";
776 h_font_sans[0] = "NotoSans-TLF";
778 // noto as typewriter is handled in handling of \ttdefault
779 // special cases are handled in handling of \rmdefault and \sfdefault
783 if (is_known(name, known_sans_fonts)) {
784 h_font_sans[0] = name;
785 if (options.size() >= 1) {
786 if (scale_as_percentage(opts, h_font_sf_scale[0]))
791 if (name == "biolinum-type1") {
792 h_font_sans[0] = "biolinum";
793 // biolinum can have several options, e.g. [osf,scaled=0.97]
794 string::size_type pos = opts.find("osf");
795 if (pos != string::npos)
800 if (is_known(name, known_typewriter_fonts)) {
801 // fourier can be set as roman font _only_
802 // fourier as typewriter is handled in handling of \ttdefault
803 if (name != "fourier") {
804 h_font_typewriter[0] = name;
805 if (options.size() >= 1) {
806 if (scale_as_percentage(opts, h_font_tt_scale[0]))
812 if (name == "libertineMono-type1") {
813 h_font_typewriter[0] = "libertine-mono";
816 // font uses old-style figure
821 if (is_known(name, known_math_fonts))
822 h_font_math[0] = name;
824 if (name == "newtxmath") {
826 h_font_math[0] = "newtxmath";
827 else if (opts == "garamondx")
828 h_font_math[0] = "garamondx-ntxm";
829 else if (opts == "libertine")
830 h_font_math[0] = "libertine-ntxm";
831 else if (opts == "minion")
832 h_font_math[0] = "minion-ntxm";
833 else if (opts == "cochineal")
834 h_font_math[0] = "cochineal-ntxm";
839 h_font_math[0] = "iwona-math";
841 if (name == "kurier")
843 h_font_math[0] = "kurier-math";
845 // after the detection and handling of special cases, we can remove the
846 // fonts, otherwise they would appear in the preamble, see bug #7856
847 if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
848 || is_known(name, known_typewriter_fonts) || is_known(name, known_math_fonts))
850 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
851 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
852 name == "esint" || name == "mhchem" || name == "mathdots" ||
853 name == "mathtools" || name == "stackrel" ||
854 name == "stmaryrd" || name == "undertilde")
855 h_use_packages[name] = "2";
857 else if (name == "babel") {
858 h_language_package = "default";
859 // One might think we would have to do nothing if babel is loaded
860 // without any options to prevent pollution of the preamble with this
861 // babel call in every roundtrip.
862 // But the user could have defined babel-specific things afterwards. So
863 // we need to keep it in the preamble to prevent cases like bug #7861.
865 // check if more than one option was used - used later for inputenc
866 if (options.begin() != options.end() - 1)
867 one_language = false;
868 // babel takes the last language of the option of its \usepackage
869 // call as document language. If there is no such language option, the
870 // last language in the documentclass options is used.
871 handle_opt(options, known_languages, h_language);
872 // translate the babel name to a LyX name
873 h_language = babel2lyx(h_language);
874 if (h_language == "japanese") {
875 // For Japanese, the encoding isn't indicated in the source
876 // file, and there's really not much we can do. We could
877 // 1) offer a list of possible encodings to choose from, or
878 // 2) determine the encoding of the file by inspecting it.
879 // For the time being, we leave the encoding alone so that
880 // we don't get iconv errors when making a wrong guess, and
881 // we will output a note at the top of the document
882 // explaining what to do.
883 Encoding const * const enc = encodings.fromIconvName(
884 p.getEncoding(), Encoding::japanese, false);
886 h_inputencoding = enc->name();
887 is_nonCJKJapanese = true;
888 // in this case babel can be removed from the preamble
889 registerAutomaticallyLoadedPackage("babel");
891 // If babel is called with options, LyX puts them by default into the
892 // document class options. This works for most languages, except
893 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
894 // perhaps in future others.
895 // Therefore keep the babel call as it is as the user might have
897 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
899 delete_opt(options, known_languages);
901 h_preamble << "\\usepackage{babel}\n";
902 explicit_babel = true;
906 else if (name == "polyglossia") {
907 h_language_package = "default";
908 h_default_output_format = "pdf4";
909 h_use_non_tex_fonts = true;
911 registerAutomaticallyLoadedPackage("xunicode");
912 if (h_inputencoding == "auto")
913 p.setEncoding("UTF-8");
916 else if (name == "CJK") {
917 // set the encoding to "auto" because it might be set to "default" by the babel handling
918 // and this would not be correct for CJK
919 if (h_inputencoding == "default")
920 h_inputencoding = "auto";
921 registerAutomaticallyLoadedPackage("CJK");
924 else if (name == "CJKutf8") {
925 h_inputencoding = "utf8-cjk";
926 p.setEncoding("UTF-8");
927 registerAutomaticallyLoadedPackage("CJKutf8");
930 else if (name == "fontenc") {
931 h_fontencoding = getStringFromVector(options, ",");
932 /* We could do the following for better round trip support,
933 * but this makes the document less portable, so I skip it:
934 if (h_fontencoding == lyxrc.fontenc)
935 h_fontencoding = "global";
940 else if (name == "inputenc" || name == "luainputenc") {
941 // h_inputencoding is only set when there is not more than one
942 // inputenc option because otherwise h_inputencoding must be
943 // set to "auto" (the default encoding of the document language)
944 // Therefore check that exactly one option is passed to inputenc.
945 // It is also only set when there is not more than one babel
947 if (!options.empty()) {
948 string const encoding = options.back();
949 Encoding const * const enc = encodings.fromLaTeXName(
950 encoding, Encoding::inputenc, true);
953 cerr << "Unknown encoding " << encoding
954 << ". Ignoring." << std::endl;
956 if (!enc->unsafe() && options.size() == 1 && one_language == true)
957 h_inputencoding = enc->name();
958 p.setEncoding(enc->iconvName());
964 else if (name == "srcltx") {
967 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
970 h_output_sync_macro = "\\usepackage{srcltx}";
973 else if (is_known(name, known_old_language_packages)) {
974 // known language packages from the times before babel
975 // if they are found and not also babel, they will be used as
976 // custom language package
977 h_language_package = "\\usepackage{" + name + "}";
980 else if (name == "lyxskak") {
981 // ignore this and its options
982 const char * const o[] = {"ps", "mover", 0};
983 delete_opt(options, o);
986 else if (is_known(name, known_lyx_packages) && options.empty()) {
987 if (name == "splitidx")
988 h_use_indices = "true";
989 if (name == "refstyle")
990 h_use_refstyle = true;
991 else if (name == "prettyref")
992 h_use_refstyle = false;
993 if (!in_lyx_preamble) {
994 h_preamble << package_beg_sep << name
995 << package_mid_sep << "\\usepackage{"
997 if (p.next_token().cat() == catNewline ||
998 (p.next_token().cat() == catSpace &&
999 p.next_next_token().cat() == catNewline))
1001 h_preamble << package_end_sep;
1005 else if (name == "geometry")
1006 handle_geometry(options);
1008 else if (name == "subfig")
1009 ; // ignore this FIXME: Use the package separator mechanism instead
1011 else if ((where = is_known(name, known_languages)))
1012 h_language = known_coded_languages[where - known_languages];
1014 else if (name == "natbib") {
1015 h_biblio_style = "plainnat";
1016 h_cite_engine = "natbib";
1017 h_cite_engine_type = "authoryear";
1018 vector<string>::iterator it =
1019 find(options.begin(), options.end(), "authoryear");
1020 if (it != options.end())
1023 it = find(options.begin(), options.end(), "numbers");
1024 if (it != options.end()) {
1025 h_cite_engine_type = "numerical";
1031 else if (name == "jurabib") {
1032 h_biblio_style = "jurabib";
1033 h_cite_engine = "jurabib";
1034 h_cite_engine_type = "authoryear";
1037 else if (name == "bibtopic")
1038 h_use_bibtopic = "true";
1040 else if (name == "hyperref")
1041 handle_hyperref(options);
1043 else if (name == "algorithm2e") {
1044 // Load "algorithm2e" module
1045 addModule("algorithm2e");
1046 // Add the package options to the global document options
1047 if (!options.empty()) {
1048 if (h_options.empty())
1049 h_options = join(options, ",");
1051 h_options += ',' + join(options, ",");
1054 else if (name == "microtype") {
1055 //we internally support only microtype without params
1056 if (options.empty())
1057 h_use_microtype = true;
1059 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1062 else if (!in_lyx_preamble) {
1063 if (options.empty())
1064 h_preamble << "\\usepackage{" << name << '}';
1066 h_preamble << "\\usepackage[" << opts << "]{"
1070 if (p.next_token().cat() == catNewline ||
1071 (p.next_token().cat() == catSpace &&
1072 p.next_next_token().cat() == catNewline))
1076 // We need to do something with the options...
1077 if (!options.empty() && !detectEncoding)
1078 cerr << "Ignoring options '" << join(options, ",")
1079 << "' of package " << name << '.' << endl;
1081 // remove the whitespace
1086 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1089 Token t = p.get_token();
1090 if (t.cat() == catEscape &&
1091 is_known(t.cs(), known_if_commands))
1092 handle_if(p, in_lyx_preamble);
1094 if (!in_lyx_preamble)
1095 h_preamble << t.asInput();
1096 if (t.cat() == catEscape && t.cs() == "fi")
1103 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1105 // set the quote language
1106 // LyX only knows the following quotes languages:
1107 // english, swedish, german, polish, french and danish
1108 // (quotes for "japanese" and "chinese-traditional" are missing because
1109 // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
1110 // conversion list taken from
1111 // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
1112 // (quotes for kazakh and interlingua are unknown)
1114 if (is_known(h_language, known_danish_quotes_languages))
1115 h_quotes_style = "danish";
1117 else if (is_known(h_language, known_french_quotes_languages))
1118 h_quotes_style = "french";
1120 else if (is_known(h_language, known_german_quotes_languages))
1121 h_quotes_style = "german";
1123 else if (is_known(h_language, known_polish_quotes_languages))
1124 h_quotes_style = "polish";
1126 else if (is_known(h_language, known_swedish_quotes_languages))
1127 h_quotes_style = "swedish";
1129 else if (is_known(h_language, known_english_quotes_languages))
1130 h_quotes_style = "english";
1132 if (contains(h_float_placement, "H"))
1133 registerAutomaticallyLoadedPackage("float");
1134 if (h_spacing != "single" && h_spacing != "default")
1135 registerAutomaticallyLoadedPackage("setspace");
1136 if (h_use_packages["amsmath"] == "2") {
1137 // amsbsy and amstext are already provided by amsmath
1138 registerAutomaticallyLoadedPackage("amsbsy");
1139 registerAutomaticallyLoadedPackage("amstext");
1142 // output the LyX file settings
1143 // Important: Keep the version formatting in sync with LyX and
1144 // lyx2lyx (bug 7951)
1145 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1146 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1147 << lyx_version_minor << '\n'
1148 << "\\lyxformat " << LYX_FORMAT << '\n'
1149 << "\\begin_document\n"
1150 << "\\begin_header\n"
1151 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1152 << "\\origin " << origin << "\n"
1153 << "\\textclass " << h_textclass << "\n";
1154 string const raw = subdoc ? empty_string() : h_preamble.str();
1156 os << "\\begin_preamble\n";
1157 for (string::size_type i = 0; i < raw.size(); ++i) {
1158 if (raw[i] == package_beg_sep) {
1159 // Here follows some package loading code that
1160 // must be skipped if the package is loaded
1162 string::size_type j = raw.find(package_mid_sep, i);
1163 if (j == string::npos)
1165 string::size_type k = raw.find(package_end_sep, j);
1166 if (k == string::npos)
1168 string const package = raw.substr(i + 1, j - i - 1);
1169 string const replacement = raw.substr(j + 1, k - j - 1);
1170 if (auto_packages.find(package) == auto_packages.end())
1176 os << "\n\\end_preamble\n";
1178 if (!h_options.empty())
1179 os << "\\options " << h_options << "\n";
1180 os << "\\use_default_options " << h_use_default_options << "\n";
1181 if (!used_modules.empty()) {
1182 os << "\\begin_modules\n";
1183 vector<string>::const_iterator const end = used_modules.end();
1184 vector<string>::const_iterator it = used_modules.begin();
1185 for (; it != end; ++it)
1187 os << "\\end_modules\n";
1189 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1190 << "\\language " << h_language << "\n"
1191 << "\\language_package " << h_language_package << "\n"
1192 << "\\inputencoding " << h_inputencoding << "\n"
1193 << "\\fontencoding " << h_fontencoding << "\n"
1194 << "\\font_roman \"" << h_font_roman[0]
1195 << "\" \"" << h_font_roman[1] << "\"\n"
1196 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1197 << "\\font_typewriter \"" << h_font_typewriter[0]
1198 << "\" \"" << h_font_typewriter[1] << "\"\n"
1199 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1200 << "\\font_default_family " << h_font_default_family << "\n"
1201 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1202 << "\\font_sc " << h_font_sc << "\n"
1203 << "\\font_osf " << h_font_osf << "\n"
1204 << "\\font_sf_scale " << h_font_sf_scale[0]
1205 << ' ' << h_font_sf_scale[1] << '\n'
1206 << "\\font_tt_scale " << h_font_tt_scale[0]
1207 << ' ' << h_font_tt_scale[1] << '\n';
1208 if (!h_font_cjk.empty())
1209 os << "\\font_cjk " << h_font_cjk << '\n';
1210 os << "\\use_microtype " << h_use_microtype << '\n'
1211 << "\\graphics " << h_graphics << '\n'
1212 << "\\default_output_format " << h_default_output_format << "\n"
1213 << "\\output_sync " << h_output_sync << "\n";
1214 if (h_output_sync == "1")
1215 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1216 os << "\\bibtex_command " << h_bibtex_command << "\n"
1217 << "\\index_command " << h_index_command << "\n";
1218 if (!h_float_placement.empty())
1219 os << "\\float_placement " << h_float_placement << "\n";
1220 os << "\\paperfontsize " << h_paperfontsize << "\n"
1221 << "\\spacing " << h_spacing << "\n"
1222 << "\\use_hyperref " << h_use_hyperref << '\n';
1223 if (h_use_hyperref == "true") {
1224 if (!h_pdf_title.empty())
1225 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1226 if (!h_pdf_author.empty())
1227 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1228 if (!h_pdf_subject.empty())
1229 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1230 if (!h_pdf_keywords.empty())
1231 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1232 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1233 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1234 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1235 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1236 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1237 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1238 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1239 "\\pdf_backref " << h_pdf_backref << "\n"
1240 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1241 if (!h_pdf_pagemode.empty())
1242 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1243 if (!h_pdf_quoted_options.empty())
1244 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1246 os << "\\papersize " << h_papersize << "\n"
1247 << "\\use_geometry " << h_use_geometry << '\n';
1248 for (map<string, string>::const_iterator it = h_use_packages.begin();
1249 it != h_use_packages.end(); ++it)
1250 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1251 os << "\\cite_engine " << h_cite_engine << '\n'
1252 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1253 << "\\biblio_style " << h_biblio_style << "\n"
1254 << "\\use_bibtopic " << h_use_bibtopic << "\n"
1255 << "\\use_indices " << h_use_indices << "\n"
1256 << "\\paperorientation " << h_paperorientation << '\n'
1257 << "\\suppress_date " << h_suppress_date << '\n'
1258 << "\\justification " << h_justification << '\n'
1259 << "\\use_refstyle " << h_use_refstyle << '\n';
1260 if (!h_fontcolor.empty())
1261 os << "\\fontcolor " << h_fontcolor << '\n';
1262 if (!h_notefontcolor.empty())
1263 os << "\\notefontcolor " << h_notefontcolor << '\n';
1264 if (!h_backgroundcolor.empty())
1265 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1266 if (!h_boxbgcolor.empty())
1267 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1268 if (index_number != 0)
1269 for (int i = 0; i < index_number; i++) {
1270 os << "\\index " << h_index[i] << '\n'
1271 << "\\shortcut " << h_shortcut[i] << '\n'
1272 << "\\color " << h_color << '\n'
1276 os << "\\index " << h_index[0] << '\n'
1277 << "\\shortcut " << h_shortcut[0] << '\n'
1278 << "\\color " << h_color << '\n'
1282 << "\\secnumdepth " << h_secnumdepth << "\n"
1283 << "\\tocdepth " << h_tocdepth << "\n"
1284 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1285 if (h_paragraph_separation == "skip")
1286 os << "\\defskip " << h_defskip << "\n";
1288 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1289 os << "\\quotes_style " << h_quotes_style << "\n"
1290 << "\\papercolumns " << h_papercolumns << "\n"
1291 << "\\papersides " << h_papersides << "\n"
1292 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1293 if (!h_listings_params.empty())
1294 os << "\\listings_params " << h_listings_params << "\n";
1295 os << "\\tracking_changes " << h_tracking_changes << "\n"
1296 << "\\output_changes " << h_output_changes << "\n"
1297 << "\\html_math_output " << h_html_math_output << "\n"
1298 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1299 << "\\html_be_strict " << h_html_be_strict << "\n"
1301 << "\\end_header\n\n"
1302 << "\\begin_body\n";
1307 void Preamble::parse(Parser & p, string const & forceclass,
1308 TeX2LyXDocClass & tc)
1310 // initialize fixed types
1311 special_columns_['D'] = 3;
1312 parse(p, forceclass, false, tc);
1316 void Preamble::parse(Parser & p, string const & forceclass,
1317 bool detectEncoding, TeX2LyXDocClass & tc)
1319 bool is_full_document = false;
1320 bool is_lyx_file = false;
1321 bool in_lyx_preamble = false;
1323 // determine whether this is a full document or a fragment for inclusion
1325 Token const & t = p.get_token();
1327 if (t.cat() == catEscape && t.cs() == "documentclass") {
1328 is_full_document = true;
1334 if (detectEncoding && !is_full_document)
1337 while (is_full_document && p.good()) {
1338 if (detectEncoding && h_inputencoding != "auto" &&
1339 h_inputencoding != "default")
1342 Token const & t = p.get_token();
1345 if (!detectEncoding)
1346 cerr << "t: " << t << '\n';
1352 if (!in_lyx_preamble &&
1353 (t.cat() == catLetter ||
1354 t.cat() == catSuper ||
1355 t.cat() == catSub ||
1356 t.cat() == catOther ||
1357 t.cat() == catMath ||
1358 t.cat() == catActive ||
1359 t.cat() == catBegin ||
1360 t.cat() == catEnd ||
1361 t.cat() == catAlign ||
1362 t.cat() == catParameter))
1363 h_preamble << t.cs();
1365 else if (!in_lyx_preamble &&
1366 (t.cat() == catSpace || t.cat() == catNewline))
1367 h_preamble << t.asInput();
1369 else if (t.cat() == catComment) {
1370 static regex const islyxfile("%% LyX .* created this file");
1371 static regex const usercommands("User specified LaTeX commands");
1373 string const comment = t.asInput();
1375 // magically switch encoding default if it looks like XeLaTeX
1376 static string const magicXeLaTeX =
1377 "% This document must be compiled with XeLaTeX ";
1378 if (comment.size() > magicXeLaTeX.size()
1379 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1380 && h_inputencoding == "auto") {
1381 if (!detectEncoding)
1382 cerr << "XeLaTeX comment found, switching to UTF8\n";
1383 h_inputencoding = "utf8";
1386 if (regex_search(comment, sub, islyxfile)) {
1388 in_lyx_preamble = true;
1389 } else if (is_lyx_file
1390 && regex_search(comment, sub, usercommands))
1391 in_lyx_preamble = false;
1392 else if (!in_lyx_preamble)
1393 h_preamble << t.asInput();
1396 else if (t.cs() == "pagestyle")
1397 h_paperpagestyle = p.verbatim_item();
1399 else if (t.cs() == "setdefaultlanguage") {
1401 // We don't yet care about non-language variant options
1402 // because LyX doesn't support this yet, see bug #8214
1404 string langopts = p.getOpt();
1405 // check if the option contains a variant, if yes, extract it
1406 string::size_type pos_var = langopts.find("variant");
1407 string::size_type i = langopts.find(',', pos_var);
1408 string::size_type k = langopts.find('=', pos_var);
1409 if (pos_var != string::npos){
1411 if (i == string::npos)
1412 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1414 variant = langopts.substr(k + 1, i - k - 1);
1415 h_language = variant;
1419 h_language = p.verbatim_item();
1420 //finally translate the poyglossia name to a LyX name
1421 h_language = polyglossia2lyx(h_language);
1424 else if (t.cs() == "setotherlanguage") {
1425 // We don't yet care about the option because LyX doesn't
1426 // support this yet, see bug #8214
1427 p.hasOpt() ? p.getOpt() : string();
1431 else if (t.cs() == "setmainfont") {
1432 // we don't care about the option
1433 p.hasOpt() ? p.getOpt() : string();
1434 h_font_roman[1] = p.getArg('{', '}');
1437 else if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1438 // LyX currently only supports the scale option
1441 string fontopts = p.getArg('[', ']');
1442 // check if the option contains a scaling, if yes, extract it
1443 string::size_type pos = fontopts.find("Scale");
1444 if (pos != string::npos) {
1445 string::size_type i = fontopts.find(',', pos);
1446 if (i == string::npos)
1447 scale_as_percentage(fontopts.substr(pos + 1), scale);
1449 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1452 if (t.cs() == "setsansfont") {
1454 h_font_sf_scale[1] = scale;
1455 h_font_sans[1] = p.getArg('{', '}');
1458 h_font_tt_scale[1] = scale;
1459 h_font_typewriter[1] = p.getArg('{', '}');
1463 else if (t.cs() == "date") {
1464 string argument = p.getArg('{', '}');
1465 if (argument.empty())
1466 h_suppress_date = "true";
1468 h_preamble << t.asInput() << '{' << argument << '}';
1471 else if (t.cs() == "color") {
1472 string const space =
1473 (p.hasOpt() ? p.getOpt() : string());
1474 string argument = p.getArg('{', '}');
1475 // check the case that a standard color is used
1476 if (space.empty() && is_known(argument, known_basic_colors)) {
1477 h_fontcolor = rgbcolor2code(argument);
1478 registerAutomaticallyLoadedPackage("color");
1479 } else if (space.empty() && argument == "document_fontcolor")
1480 registerAutomaticallyLoadedPackage("color");
1481 // check the case that LyX's document_fontcolor is defined
1482 // but not used for \color
1484 h_preamble << t.asInput();
1486 h_preamble << space;
1487 h_preamble << '{' << argument << '}';
1488 // the color might already be set because \definecolor
1489 // is parsed before this
1494 else if (t.cs() == "pagecolor") {
1495 string argument = p.getArg('{', '}');
1496 // check the case that a standard color is used
1497 if (is_known(argument, known_basic_colors)) {
1498 h_backgroundcolor = rgbcolor2code(argument);
1499 } else if (argument == "page_backgroundcolor")
1500 registerAutomaticallyLoadedPackage("color");
1501 // check the case that LyX's page_backgroundcolor is defined
1502 // but not used for \pagecolor
1504 h_preamble << t.asInput() << '{' << argument << '}';
1505 // the color might already be set because \definecolor
1506 // is parsed before this
1507 h_backgroundcolor = "";
1511 else if (t.cs() == "makeatletter") {
1512 // LyX takes care of this
1513 p.setCatcode('@', catLetter);
1516 else if (t.cs() == "makeatother") {
1517 // LyX takes care of this
1518 p.setCatcode('@', catOther);
1521 else if (t.cs() == "makeindex") {
1522 // LyX will re-add this if a print index command is found
1526 else if (t.cs() == "newindex") {
1527 string const indexname = p.getArg('[', ']');
1528 string const shortcut = p.verbatim_item();
1529 if (!indexname.empty())
1530 h_index[index_number] = indexname;
1532 h_index[index_number] = shortcut;
1533 h_shortcut[index_number] = shortcut;
1538 else if (t.cs() == "RS@ifundefined") {
1539 string const name = p.verbatim_item();
1540 string const body1 = p.verbatim_item();
1541 string const body2 = p.verbatim_item();
1542 // only non-lyxspecific stuff
1543 if (in_lyx_preamble &&
1544 (name == "subsecref" || name == "thmref" || name == "lemref"))
1548 ss << '\\' << t.cs();
1549 ss << '{' << name << '}'
1550 << '{' << body1 << '}'
1551 << '{' << body2 << '}';
1552 h_preamble << ss.str();
1556 else if (t.cs() == "AtBeginDocument") {
1557 string const name = p.verbatim_item();
1558 // only non-lyxspecific stuff
1559 if (in_lyx_preamble &&
1560 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
1561 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
1562 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
1563 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
1564 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
1565 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
1566 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
1567 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
1568 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
1569 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
1570 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
1571 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
1572 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
1573 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
1574 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
1578 ss << '\\' << t.cs();
1579 ss << '{' << name << '}';
1580 h_preamble << ss.str();
1584 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1585 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1586 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1587 || t.cs() == "DeclareRobustCommand"
1588 || t.cs() == "DeclareRobustCommandx"
1589 || t.cs() == "ProvideTextCommandDefault"
1590 || t.cs() == "DeclareMathAccent") {
1592 if (p.next_token().character() == '*') {
1596 string const name = p.verbatim_item();
1597 string const opt1 = p.getFullOpt();
1598 string const opt2 = p.getFullOpt();
1599 string const body = p.verbatim_item();
1600 // store the in_lyx_preamble setting
1601 bool const was_in_lyx_preamble = in_lyx_preamble;
1603 if (name == "\\rmdefault")
1604 if (is_known(body, known_roman_fonts)) {
1605 h_font_roman[0] = body;
1607 in_lyx_preamble = true;
1609 if (name == "\\sfdefault")
1610 if (is_known(body, known_sans_fonts)) {
1611 h_font_sans[0] = body;
1613 in_lyx_preamble = true;
1615 if (name == "\\ttdefault")
1616 if (is_known(body, known_typewriter_fonts)) {
1617 h_font_typewriter[0] = body;
1619 in_lyx_preamble = true;
1621 if (name == "\\familydefault") {
1622 string family = body;
1623 // remove leading "\"
1624 h_font_default_family = family.erase(0,1);
1626 in_lyx_preamble = true;
1629 // remove LyX-specific definitions that are re-added by LyX
1631 // \lyxline is an ancient command that is converted by tex2lyx into
1632 // a \rule therefore remove its preamble code
1633 if (name == "\\lyxdot" || name == "\\lyxarrow"
1634 || name == "\\lyxline" || name == "\\LyX") {
1636 in_lyx_preamble = true;
1639 // Add the command to the known commands
1640 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1642 // only non-lyxspecific stuff
1643 if (!in_lyx_preamble) {
1645 ss << '\\' << t.cs();
1648 ss << '{' << name << '}' << opt1 << opt2
1649 << '{' << body << "}";
1650 h_preamble << ss.str();
1652 ostream & out = in_preamble ? h_preamble : os;
1653 out << "\\" << t.cs() << "{" << name << "}"
1654 << opts << "{" << body << "}";
1657 // restore the in_lyx_preamble setting
1658 in_lyx_preamble = was_in_lyx_preamble;
1661 else if (t.cs() == "documentclass") {
1662 vector<string>::iterator it;
1663 vector<string> opts = split_options(p.getArg('[', ']'));
1664 handle_opt(opts, known_fontsizes, h_paperfontsize);
1665 delete_opt(opts, known_fontsizes);
1666 // delete "pt" at the end
1667 string::size_type i = h_paperfontsize.find("pt");
1668 if (i != string::npos)
1669 h_paperfontsize.erase(i);
1670 // The documentclass options are always parsed before the options
1671 // of the babel call so that a language cannot overwrite the babel
1673 handle_opt(opts, known_languages, h_language);
1674 delete_opt(opts, known_languages);
1676 // paper orientation
1677 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1678 h_paperorientation = "landscape";
1682 if ((it = find(opts.begin(), opts.end(), "oneside"))
1687 if ((it = find(opts.begin(), opts.end(), "twoside"))
1693 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1695 h_papercolumns = "1";
1698 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1700 h_papercolumns = "2";
1704 // some size options are known to any document classes, other sizes
1705 // are handled by the \geometry command of the geometry package
1706 handle_opt(opts, known_class_paper_sizes, h_papersize);
1707 delete_opt(opts, known_class_paper_sizes);
1708 // the remaining options
1709 h_options = join(opts, ",");
1710 // FIXME This does not work for classes that have a
1711 // different name in LyX than in LaTeX
1712 h_textclass = p.getArg('{', '}');
1716 else if (t.cs() == "usepackage") {
1717 string const options = p.getArg('[', ']');
1718 string const name = p.getArg('{', '}');
1719 vector<string> vecnames;
1720 split(name, vecnames, ',');
1721 vector<string>::const_iterator it = vecnames.begin();
1722 vector<string>::const_iterator end = vecnames.end();
1723 for (; it != end; ++it)
1724 handle_package(p, trimSpaceAndEol(*it), options,
1725 in_lyx_preamble, detectEncoding);
1728 else if (t.cs() == "inputencoding") {
1729 string const encoding = p.getArg('{','}');
1730 Encoding const * const enc = encodings.fromLaTeXName(
1731 encoding, Encoding::inputenc, true);
1733 if (!detectEncoding)
1734 cerr << "Unknown encoding " << encoding
1735 << ". Ignoring." << std::endl;
1738 h_inputencoding = enc->name();
1739 p.setEncoding(enc->iconvName());
1743 else if (t.cs() == "newenvironment") {
1744 string const name = p.getArg('{', '}');
1745 string const opt1 = p.getFullOpt();
1746 string const opt2 = p.getFullOpt();
1747 string const beg = p.verbatim_item();
1748 string const end = p.verbatim_item();
1749 if (!in_lyx_preamble) {
1750 h_preamble << "\\newenvironment{" << name
1751 << '}' << opt1 << opt2 << '{'
1752 << beg << "}{" << end << '}';
1754 add_known_environment(name, opt1, !opt2.empty(),
1755 from_utf8(beg), from_utf8(end));
1759 else if (t.cs() == "newtheorem") {
1760 string const name = p.getArg('{', '}');
1761 string const opt1 = p.getFullOpt();
1762 string const opt2 = p.getFullOpt();
1763 string const body = p.verbatim_item();
1764 string const opt3 = p.getFullOpt();
1766 string const complete = "\\newtheorem{" + name + '}' +
1767 opt1 + opt2 + '{' + body + '}' + opt3;
1769 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
1771 if (!in_lyx_preamble)
1772 h_preamble << complete;
1775 else if (t.cs() == "def") {
1776 string name = p.get_token().cs();
1777 // In fact, name may be more than the name:
1778 // In the test case of bug 8116
1779 // name == "csname SF@gobble@opt \endcsname".
1780 // Therefore, we need to use asInput() instead of cs().
1781 while (p.next_token().cat() != catBegin)
1782 name += p.get_token().asInput();
1783 if (!in_lyx_preamble)
1784 h_preamble << "\\def\\" << name << '{'
1785 << p.verbatim_item() << "}";
1788 else if (t.cs() == "newcolumntype") {
1789 string const name = p.getArg('{', '}');
1790 trimSpaceAndEol(name);
1792 string opts = p.getOpt();
1793 if (!opts.empty()) {
1794 istringstream is(string(opts, 1));
1797 special_columns_[name[0]] = nargs;
1798 h_preamble << "\\newcolumntype{" << name << "}";
1800 h_preamble << "[" << nargs << "]";
1801 h_preamble << "{" << p.verbatim_item() << "}";
1804 else if (t.cs() == "setcounter") {
1805 string const name = p.getArg('{', '}');
1806 string const content = p.getArg('{', '}');
1807 if (name == "secnumdepth")
1808 h_secnumdepth = content;
1809 else if (name == "tocdepth")
1810 h_tocdepth = content;
1812 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1815 else if (t.cs() == "setlength") {
1816 string const name = p.verbatim_item();
1817 string const content = p.verbatim_item();
1818 // the paragraphs are only not indented when \parindent is set to zero
1819 if (name == "\\parindent" && content != "") {
1820 if (content[0] == '0')
1821 h_paragraph_separation = "skip";
1823 h_paragraph_indentation = translate_len(content);
1824 } else if (name == "\\parskip") {
1825 if (content == "\\smallskipamount")
1826 h_defskip = "smallskip";
1827 else if (content == "\\medskipamount")
1828 h_defskip = "medskip";
1829 else if (content == "\\bigskipamount")
1830 h_defskip = "bigskip";
1832 h_defskip = translate_len(content);
1834 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1837 else if (t.cs() == "onehalfspacing")
1838 h_spacing = "onehalf";
1840 else if (t.cs() == "doublespacing")
1841 h_spacing = "double";
1843 else if (t.cs() == "setstretch")
1844 h_spacing = "other " + p.verbatim_item();
1846 else if (t.cs() == "synctex") {
1847 // the scheme is \synctex=value
1848 // where value can only be "1" or "-1"
1849 h_output_sync = "1";
1850 // there can be any character behind the value (e.g. a linebreak or a '\'
1851 // therefore we extract it char by char
1853 string value = p.get_token().asInput();
1855 value += p.get_token().asInput();
1856 h_output_sync_macro = "\\synctex=" + value;
1859 else if (t.cs() == "begin") {
1860 string const name = p.getArg('{', '}');
1861 if (name == "document")
1863 h_preamble << "\\begin{" << name << "}";
1866 else if (t.cs() == "geometry") {
1867 vector<string> opts = split_options(p.getArg('{', '}'));
1868 handle_geometry(opts);
1871 else if (t.cs() == "definecolor") {
1872 string const color = p.getArg('{', '}');
1873 string const space = p.getArg('{', '}');
1874 string const value = p.getArg('{', '}');
1875 if (color == "document_fontcolor" && space == "rgb") {
1876 RGBColor c(RGBColorFromLaTeX(value));
1877 h_fontcolor = X11hexname(c);
1878 } else if (color == "note_fontcolor" && space == "rgb") {
1879 RGBColor c(RGBColorFromLaTeX(value));
1880 h_notefontcolor = X11hexname(c);
1881 } else if (color == "page_backgroundcolor" && space == "rgb") {
1882 RGBColor c(RGBColorFromLaTeX(value));
1883 h_backgroundcolor = X11hexname(c);
1884 } else if (color == "shadecolor" && space == "rgb") {
1885 RGBColor c(RGBColorFromLaTeX(value));
1886 h_boxbgcolor = X11hexname(c);
1888 h_preamble << "\\definecolor{" << color
1889 << "}{" << space << "}{" << value
1894 else if (t.cs() == "bibliographystyle")
1895 h_biblio_style = p.verbatim_item();
1897 else if (t.cs() == "jurabibsetup") {
1898 // FIXME p.getArg('{', '}') is most probably wrong (it
1899 // does not handle nested braces).
1900 // Use p.verbatim_item() instead.
1901 vector<string> jurabibsetup =
1902 split_options(p.getArg('{', '}'));
1903 // add jurabibsetup to the jurabib package options
1904 add_package("jurabib", jurabibsetup);
1905 if (!jurabibsetup.empty()) {
1906 h_preamble << "\\jurabibsetup{"
1907 << join(jurabibsetup, ",") << '}';
1911 else if (t.cs() == "hypersetup") {
1912 vector<string> hypersetup =
1913 split_options(p.verbatim_item());
1914 // add hypersetup to the hyperref package options
1915 handle_hyperref(hypersetup);
1916 if (!hypersetup.empty()) {
1917 h_preamble << "\\hypersetup{"
1918 << join(hypersetup, ",") << '}';
1922 else if (is_known(t.cs(), known_if_3arg_commands)) {
1923 // prevent misparsing of \usepackage if it is used
1924 // as an argument (see e.g. our own output of
1925 // \@ifundefined above)
1926 string const arg1 = p.verbatim_item();
1927 string const arg2 = p.verbatim_item();
1928 string const arg3 = p.verbatim_item();
1929 // test case \@ifundefined{date}{}{\date{}}
1930 if (t.cs() == "@ifundefined" && arg1 == "date" &&
1931 arg2.empty() && arg3 == "\\date{}") {
1932 h_suppress_date = "true";
1933 // older tex2lyx versions did output
1934 // \@ifundefined{definecolor}{\usepackage{color}}{}
1935 } else if (t.cs() == "@ifundefined" &&
1936 arg1 == "definecolor" &&
1937 arg2 == "\\usepackage{color}" &&
1939 if (!in_lyx_preamble)
1940 h_preamble << package_beg_sep
1943 << "\\@ifundefined{definecolor}{color}{}"
1946 //\@ifundefined{showcaptionsetup}{}{%
1947 // \PassOptionsToPackage{caption=false}{subfig}}
1948 // that LyX uses for subfloats
1949 } else if (t.cs() == "@ifundefined" &&
1950 arg1 == "showcaptionsetup" && arg2.empty()
1951 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1953 } else if (!in_lyx_preamble) {
1954 h_preamble << t.asInput()
1955 << '{' << arg1 << '}'
1956 << '{' << arg2 << '}'
1957 << '{' << arg3 << '}';
1961 else if (is_known(t.cs(), known_if_commands)) {
1962 // must not parse anything in conditional code, since
1963 // LyX would output the parsed contents unconditionally
1964 if (!in_lyx_preamble)
1965 h_preamble << t.asInput();
1966 handle_if(p, in_lyx_preamble);
1969 else if (!t.cs().empty() && !in_lyx_preamble)
1970 h_preamble << '\\' << t.cs();
1973 // remove the whitespace
1976 // Force textclass if the user wanted it
1977 if (!forceclass.empty())
1978 h_textclass = forceclass;
1979 tc.setName(h_textclass);
1981 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1984 if (h_papersides.empty()) {
1987 h_papersides = ss.str();
1990 // If the CJK package is used we cannot set the document language from
1991 // the babel options. Instead, we guess which language is used most
1992 // and set this one.
1993 default_language = h_language;
1994 if (is_full_document &&
1995 (auto_packages.find("CJK") != auto_packages.end() ||
1996 auto_packages.find("CJKutf8") != auto_packages.end())) {
1998 h_language = guessLanguage(p, default_language);
2000 if (explicit_babel && h_language != default_language) {
2001 // We set the document language to a CJK language,
2002 // but babel is explicitly called in the user preamble
2003 // without options. LyX will not add the default
2004 // language to the document options if it is either
2005 // english, or no text is set as default language.
2006 // Therefore we need to add a language option explicitly.
2007 // FIXME: It would be better to remove all babel calls
2008 // from the user preamble, but this is difficult
2009 // without re-introducing bug 7861.
2010 if (h_options.empty())
2011 h_options = lyx2babel(default_language);
2013 h_options += ',' + lyx2babel(default_language);
2019 string Preamble::parseEncoding(Parser & p, string const & forceclass)
2021 TeX2LyXDocClass dummy;
2022 parse(p, forceclass, true, dummy);
2023 if (h_inputencoding != "auto" && h_inputencoding != "default")
2024 return h_inputencoding;
2029 string babel2lyx(string const & language)
2031 char const * const * where = is_known(language, known_languages);
2033 return known_coded_languages[where - known_languages];
2038 string lyx2babel(string const & language)
2040 char const * const * where = is_known(language, known_coded_languages);
2042 return known_languages[where - known_coded_languages];
2047 string Preamble::polyglossia2lyx(string const & language)
2049 char const * const * where = is_known(language, polyglossia_languages);
2051 return coded_polyglossia_languages[where - polyglossia_languages];
2056 string rgbcolor2code(string const & name)
2058 char const * const * where = is_known(name, known_basic_colors);
2060 // "red", "green" etc
2061 return known_basic_color_codes[where - known_basic_colors];
2063 // "255,0,0", "0,255,0" etc
2064 RGBColor c(RGBColorFromLaTeX(name));
2065 return X11hexname(c);