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.
19 #include "LayoutFile.h"
22 #include "TextClass.h"
24 #include "support/convert.h"
25 #include "support/FileName.h"
26 #include "support/filetools.h"
27 #include "support/lstrings.h"
29 #include "support/regex.h"
35 using namespace lyx::support;
40 // special columntypes
41 extern map<char, int> special_columns;
47 //add this to known_languages when updating to lyxformat 266:
48 // "armenian" (needs special handling since not supported by standard babel)
49 //add these to known_languages when updating to lyxformat 268:
50 //"chinese-simplified", "chinese-traditional", "japanese", "korean"
51 // Both changes require first that support for non-babel languages (CJK,
54 * known babel language names (including synonyms)
55 * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
56 * not yet supported by LyX: kurmanji
57 * please keep this in sync with known_coded_languages line by line!
59 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
60 "american", "arabic", "arabtex", "australian", "austrian", "bahasa", "bahasai",
61 "bahasam", "basque", "belarusian", "brazil", "brazilian", "breton", "british",
62 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
63 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "francais",
64 "french", "frenchb", "frenchle", "frenchpro", "galician", "german", "germanb",
65 "greek", "hebrew", "hungarian", "icelandic", "indon", "indonesian", "interlingua",
66 "irish", "italian", "kazakh", "latin", "latvian", "lithuanian", "lowersorbian",
67 "lsorbian", "magyar", "malay", "meyalu", "mongolian", "naustrian", "newzealand",
68 "ngerman", "ngermanb", "norsk", "nynorsk", "polutonikogreek", "polish",
69 "portuges", "portuguese", "romanian", "russian", "russianb", "samin",
70 "scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish",
71 "swedish", "thai", "turkish", "turkmen", "ukraineb", "ukrainian",
72 "uppersorbian", "UKenglish", "USenglish", "usorbian", "vietnam", "welsh",
76 * the same as known_languages with .lyx names
77 * please keep this in sync with known_languages line by line!
79 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
80 "american", "arabic_arabi", "arabic_arabtex", "australian", "austrian", "bahasa", "bahasa",
81 "bahasam", "basque", "belarusian", "brazilian", "brazilian", "breton", "british",
82 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
83 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "french",
84 "french", "french", "french", "french", "galician", "german", "german",
85 "greek", "hebrew", "magyar", "icelandic", "bahasa", "bahasa", "interlingua",
86 "irish", "italian", "kazakh", "latin", "latvian", "lithuanian", "lowersorbian",
87 "lowersorbian", "magyar", "bahasam", "bahasam", "mongolian", "naustrian", "newzealand",
88 "ngerman", "ngerman", "norsk", "nynorsk", "polutonikogreek", "polish",
89 "portuguese", "portuguese", "romanian", "russian", "russian", "samin",
90 "scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish",
91 "swedish", "thai", "turkish", "turkmen", "ukrainian", "ukrainian",
92 "uppersorbian", "uppersorbian", "english", "english", "vietnamese", "welsh",
95 /// languages with english quotes (.lyx names)
96 const char * const known_english_quotes_languages[] = {"american", "australian",
97 "bahasa", "bahasam", "brazilian", "canadian", "chinese-simplified", "english",
98 "esperanto", "hebrew", "irish", "korean", "newzealand", "portuguese", "scottish",
101 /// languages with french quotes (.lyx names)
102 const char * const known_french_quotes_languages[] = {"albanian",
103 "arabic_arabi", "arabic_arabtex", "basque", "canadien", "catalan", "french",
104 "galician", "greek", "italian", "norsk", "nynorsk", "polutonikogreek",
105 "russian", "spanish", "spanish-mexico", "turkish", "turkmen", "ukrainian",
108 /// languages with german quotes (.lyx names)
109 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
110 "czech", "german", "icelandic", "lithuanian", "lowersorbian", "naustrian",
111 "ngerman", "serbian", "serbian-latin", "slovak", "slovene", "uppersorbian", 0};
113 /// languages with polish quotes (.lyx names)
114 const char * const known_polish_quotes_languages[] = {"afrikaans", "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", "fourier", "lmodern", "mathpazo",
129 "mathptmx", "newcent", "utopia", 0};
131 const char * const known_sans_fonts[] = { "avant", "berasans", "cmbr", "cmss",
132 "helvet", "lmss", 0};
134 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
135 "courier", "lmtt", "luximono", "fourier", "lmodern", "mathpazo", "mathptmx",
138 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
139 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
140 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
141 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
142 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
144 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
145 "executivepaper", "legalpaper", "letterpaper", 0};
147 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
148 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
150 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
151 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
154 /// commands that can start an \if...\else...\endif sequence
155 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
156 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
157 "ifsidecap", "ifupgreek", 0};
159 const char * const known_basic_colors[] = {"blue", "black", "cyan", "green",
160 "magenta", "red", "white", "yellow", 0};
162 const char * const known_basic_color_codes[] = {"#0000ff", "#000000", "#00ffff", "#00ff00",
163 "#ff00ff", "#ff0000", "#ffffff", "#ffff00", 0};
165 /// conditional commands with three arguments like \@ifundefined{}{}{}
166 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
169 /// packages that work only in xetex
170 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
171 "fontbook", "fontwrap", "mathspec", "philokalia", "polyglossia", "unisugar",
172 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
174 /// packages that are automatically skipped if loaded by LyX
175 const char * const known_lyx_packages[] = {"array", "booktabs", "calc",
176 "color", "float", "graphicx", "hhline", "ifthen", "longtable", "makeidx",
177 "multirow", "nomencl", "pdfpages", "rotfloat", "splitidx", "setspace",
178 "subscript", "textcomp", "ulem", "url", "varioref", "verbatim", "wrapfig", 0};
180 // codes used to remove packages that are loaded automatically by LyX.
181 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
182 const char package_beg_sep = '\001';
183 const char package_mid_sep = '\002';
184 const char package_end_sep = '\003';
187 // returns true if at least one of the options in what has been found
188 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
194 // the last language option is the document language (for babel and LyX)
195 // the last size option is the document font size
196 vector<string>::iterator it;
197 vector<string>::iterator position = opts.begin();
198 for (; *what; ++what) {
199 it = find(opts.begin(), opts.end(), *what);
200 if (it != opts.end()) {
201 if (it >= position) {
212 void delete_opt(vector<string> & opts, char const * const * what)
217 // remove found options from the list
218 // do this after handle_opt to avoid potential memory leaks
219 vector<string>::iterator it;
220 for (; *what; ++what) {
221 it = find(opts.begin(), opts.end(), *what);
222 if (it != opts.end())
229 * Split a package options string (keyval format) into a vector.
231 * authorformat=smallcaps,
233 * titleformat=colonsep,
234 * bibformat={tabular,ibidem,numbered}
236 vector<string> split_options(string const & input)
238 vector<string> options;
242 Token const & t = p.get_token();
243 if (t.asInput() == ",") {
244 options.push_back(trimSpaceAndEol(option));
246 } else if (t.asInput() == "=") {
249 if (p.next_token().asInput() == "{")
250 option += '{' + p.getArg('{', '}') + '}';
251 } else if (t.cat() != catSpace)
252 option += t.asInput();
256 options.push_back(trimSpaceAndEol(option));
263 * Retrieve a keyval option "name={value with=sign}" named \p name from
264 * \p options and return the value.
265 * The found option is also removed from \p options.
267 string process_keyval_opt(vector<string> & options, string name)
269 for (size_t i = 0; i < options.size(); ++i) {
270 vector<string> option;
271 split(options[i], option, '=');
272 if (option.size() < 2)
274 if (option[0] == name) {
275 options.erase(options.begin() + i);
276 option.erase(option.begin());
277 return join(option, "=");
283 } // anonymous namespace
286 bool Preamble::indentParagraphs() const
288 return h_paragraph_separation == "indent";
292 bool Preamble::isPackageUsed(string const & package) const
294 return used_packages.find(package) != used_packages.end();
298 vector<string> Preamble::getPackageOptions(string const & package) const
300 map<string, vector<string> >::const_iterator it = used_packages.find(package);
301 if (it != used_packages.end())
303 return vector<string>();
307 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
309 auto_packages.insert(package);
313 void Preamble::addModule(string const & module)
315 used_modules.push_back(module);
319 void Preamble::suppressDate(bool suppress)
322 h_suppress_date = "true";
324 h_suppress_date = "false";
328 void Preamble::registerAuthor(std::string const & name)
330 Author author(from_utf8(name), empty_docstring());
331 author.setUsed(true);
332 authors_.record(author);
333 h_tracking_changes = "true";
334 h_output_changes = "true";
338 Author const & Preamble::getAuthor(std::string const & name) const
340 Author author(from_utf8(name), empty_docstring());
341 for (AuthorList::Authors::const_iterator it = authors_.begin();
342 it != authors_.end(); it++)
345 static Author const dummy;
350 void Preamble::add_package(string const & name, vector<string> & options)
352 // every package inherits the global options
353 if (used_packages.find(name) == used_packages.end())
354 used_packages[name] = split_options(h_options);
356 vector<string> & v = used_packages[name];
357 v.insert(v.end(), options.begin(), options.end());
358 if (name == "jurabib") {
359 // Don't output the order argument (see the cite command
360 // handling code in text.cpp).
361 vector<string>::iterator end =
362 remove(options.begin(), options.end(), "natbiborder");
363 end = remove(options.begin(), end, "jurabiborder");
364 options.erase(end, options.end());
371 // Given is a string like "scaled=0.9", return 0.9 * 100
372 string const scale_as_percentage(string const & scale)
374 string::size_type pos = scale.find('=');
375 if (pos != string::npos) {
376 string value = scale.substr(pos + 1);
378 return convert<string>(100 * convert<double>(value));
380 // If the input string didn't match our expectations.
381 // return the default value "100"
386 string remove_braces(string const & value)
390 if (value[0] == '{' && value[value.length()-1] == '}')
391 return value.substr(1, value.length()-2);
395 } // anonymous namespace
398 Preamble::Preamble() : one_language(true)
402 h_biblio_style = "plain";
403 h_cite_engine = "basic";
404 h_cite_engine_type = "numerical";
405 h_defskip = "medskip";
408 h_fontencoding = "default";
409 h_font_roman = "default";
410 h_font_sans = "default";
411 h_font_typewriter = "default";
412 h_font_default_family = "default";
413 h_use_non_tex_fonts = "false";
415 h_font_osf = "false";
416 h_font_sf_scale = "100";
417 h_font_tt_scale = "100";
418 h_graphics = "default";
419 h_html_be_strict = "false";
420 h_html_css_as_file = "0";
421 h_html_math_output = "0";
422 h_inputencoding = "auto";
423 h_justification = "true";
424 h_language = "english";
425 h_language_package = "none";
430 h_output_changes = "false";
431 h_papercolumns = "1";
432 h_paperfontsize = "default";
433 h_paperorientation = "portrait";
434 h_paperpagestyle = "default";
436 h_papersize = "default";
437 h_paragraph_indentation = "default";
438 h_paragraph_separation = "indent";
443 h_pdf_bookmarks = "1";
444 h_pdf_bookmarksnumbered = "0";
445 h_pdf_bookmarksopen = "0";
446 h_pdf_bookmarksopenlevel = "1";
447 h_pdf_breaklinks = "0";
448 h_pdf_pdfborder = "0";
449 h_pdf_colorlinks = "0";
450 h_pdf_backref = "section";
451 h_pdf_pdfusetitle = "1";
453 //h_pdf_quoted_options;
454 h_quotes_language = "english";
456 h_spacing = "single";
457 h_suppress_date = "false";
458 h_textclass = "article";
460 h_tracking_changes = "false";
461 h_use_bibtopic = "false";
462 h_use_indices = "false";
463 h_use_geometry = "false";
464 h_use_default_options = "false";
465 h_use_hyperref = "0";
466 h_use_refstyle = "0";
467 h_use_packages["amsmath"] = "1";
468 h_use_packages["esint"] = "1";
469 h_use_packages["mhchem"] = "0";
470 h_use_packages["mathdots"] = "0";
471 h_use_packages["mathtools"] = "0";
472 h_use_packages["undertilde"] = "0";
476 void Preamble::handle_hyperref(vector<string> & options)
478 // FIXME swallow inputencoding changes that might surround the
479 // hyperref setup if it was written by LyX
480 h_use_hyperref = "1";
481 // swallow "unicode=true", since LyX does always write that
482 vector<string>::iterator it =
483 find(options.begin(), options.end(), "unicode=true");
484 if (it != options.end())
486 it = find(options.begin(), options.end(), "pdfusetitle");
487 if (it != options.end()) {
488 h_pdf_pdfusetitle = "1";
491 string bookmarks = process_keyval_opt(options, "bookmarks");
492 if (bookmarks == "true")
493 h_pdf_bookmarks = "1";
494 else if (bookmarks == "false")
495 h_pdf_bookmarks = "0";
496 if (h_pdf_bookmarks == "1") {
497 string bookmarksnumbered =
498 process_keyval_opt(options, "bookmarksnumbered");
499 if (bookmarksnumbered == "true")
500 h_pdf_bookmarksnumbered = "1";
501 else if (bookmarksnumbered == "false")
502 h_pdf_bookmarksnumbered = "0";
503 string bookmarksopen =
504 process_keyval_opt(options, "bookmarksopen");
505 if (bookmarksopen == "true")
506 h_pdf_bookmarksopen = "1";
507 else if (bookmarksopen == "false")
508 h_pdf_bookmarksopen = "0";
509 if (h_pdf_bookmarksopen == "1") {
510 string bookmarksopenlevel =
511 process_keyval_opt(options, "bookmarksopenlevel");
512 if (!bookmarksopenlevel.empty())
513 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
516 string breaklinks = process_keyval_opt(options, "breaklinks");
517 if (breaklinks == "true")
518 h_pdf_breaklinks = "1";
519 else if (breaklinks == "false")
520 h_pdf_breaklinks = "0";
521 string pdfborder = process_keyval_opt(options, "pdfborder");
522 if (pdfborder == "{0 0 0}")
523 h_pdf_pdfborder = "1";
524 else if (pdfborder == "{0 0 1}")
525 h_pdf_pdfborder = "0";
526 string backref = process_keyval_opt(options, "backref");
527 if (!backref.empty())
528 h_pdf_backref = backref;
529 string colorlinks = process_keyval_opt(options, "colorlinks");
530 if (colorlinks == "true")
531 h_pdf_colorlinks = "1";
532 else if (colorlinks == "false")
533 h_pdf_colorlinks = "0";
534 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
535 if (!pdfpagemode.empty())
536 h_pdf_pagemode = pdfpagemode;
537 string pdftitle = process_keyval_opt(options, "pdftitle");
538 if (!pdftitle.empty()) {
539 h_pdf_title = remove_braces(pdftitle);
541 string pdfauthor = process_keyval_opt(options, "pdfauthor");
542 if (!pdfauthor.empty()) {
543 h_pdf_author = remove_braces(pdfauthor);
545 string pdfsubject = process_keyval_opt(options, "pdfsubject");
546 if (!pdfsubject.empty())
547 h_pdf_subject = remove_braces(pdfsubject);
548 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
549 if (!pdfkeywords.empty())
550 h_pdf_keywords = remove_braces(pdfkeywords);
551 if (!options.empty()) {
552 if (!h_pdf_quoted_options.empty())
553 h_pdf_quoted_options += ',';
554 h_pdf_quoted_options += join(options, ",");
560 void Preamble::handle_geometry(vector<string> & options)
562 h_use_geometry = "true";
563 vector<string>::iterator it;
565 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
566 h_paperorientation = "landscape";
570 // keyval version: "paper=letter"
571 string paper = process_keyval_opt(options, "paper");
573 h_papersize = paper + "paper";
574 // alternative version: "letterpaper"
575 handle_opt(options, known_paper_sizes, h_papersize);
576 delete_opt(options, known_paper_sizes);
578 char const * const * margin = known_paper_margins;
579 for (; *margin; ++margin) {
580 string value = process_keyval_opt(options, *margin);
581 if (!value.empty()) {
582 int k = margin - known_paper_margins;
583 string name = known_coded_paper_margins[k];
584 h_margins += '\\' + name + ' ' + value + '\n';
590 void Preamble::handle_package(Parser &p, string const & name,
591 string const & opts, bool in_lyx_preamble)
593 vector<string> options = split_options(opts);
594 add_package(name, options);
597 if (is_known(name, known_xetex_packages)) {
599 h_use_non_tex_fonts = "true";
600 if (h_inputencoding == "auto")
601 p.setEncoding("utf8");
605 if (is_known(name, known_roman_fonts)) {
610 if (name == "fourier") {
611 h_font_roman = "utopia";
612 // when font uses real small capitals
613 if (opts == "expert")
617 else if (name == "mathpazo")
618 h_font_roman = "palatino";
620 else if (name == "mathptmx")
621 h_font_roman = "times";
624 if (is_known(name, known_sans_fonts)) {
628 h_font_sf_scale = scale_as_percentage(scale);
633 if (is_known(name, known_typewriter_fonts)) {
634 // fourier can be set as roman font _only_
635 // fourier as typewriter is handled in handling of \ttdefault
636 if (name != "fourier") {
637 h_font_typewriter = name;
640 h_font_tt_scale = scale_as_percentage(scale);
645 // font uses old-style figure
649 // after the detection and handling of special cases, we can remove the
650 // fonts, otherwise they would appear in the preamble, see bug #7856
651 if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
652 || is_known(name, known_typewriter_fonts))
655 else if (name == "amsmath" || name == "amssymb")
656 h_use_packages["amsmath"] = "2";
658 else if (name == "esint" || name == "mhchem" || name == "mathdots" ||
659 name == "mathtools" || name == "undertilde")
660 h_use_packages[name] = "2";
662 else if (name == "babel") {
663 h_language_package = "default";
664 // One might think we would have to do nothing if babel is loaded
665 // without any options to prevent pollution of the preamble with this
666 // babel call in every roundtrip.
667 // But the user could have defined babel-specific things afterwards. So
668 // we need to keep it in the preamble to prevent cases like bug #7861.
670 // check if more than one option was used - used later for inputenc
671 if (options.begin() != options.end() - 1)
672 one_language = false;
673 // babel takes the last language of the option of its \usepackage
674 // call as document language. If there is no such language option, the
675 // last language in the documentclass options is used.
676 handle_opt(options, known_languages, h_language);
677 // If babel is called with options, LyX puts them by default into the
678 // document class options. This works for most languages, except
679 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
680 // perhaps in future others.
681 // Therefore keep the babel call as it is as the user might have
683 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
684 delete_opt(options, known_languages);
687 h_preamble << "\\usepackage{babel}\n";
690 else if (name == "fontenc") {
691 h_fontencoding = getStringFromVector(options, ",");
692 /* We could do the following for better round trip support,
693 * but this makes the document less portable, so I skip it:
694 if (h_fontencoding == lyxrc.fontenc)
695 h_fontencoding = "global";
700 else if (name == "inputenc" || name == "luainputenc") {
701 // h_inputencoding is only set when there is not more than one
702 // inputenc option because otherwise h_inputencoding must be
703 // set to "auto" (the default encoding of the document language)
704 // Therefore check for the "," character.
705 // It is also only set when there is not more than one babel
707 if (opts.find(",") == string::npos && one_language == true)
708 h_inputencoding = opts;
709 if (!options.empty())
710 p.setEncoding(options.back());
714 else if (is_known(name, known_old_language_packages)) {
715 // known language packages from the times before babel
716 // if they are found and not also babel, they will be used as
717 // custom language package
718 h_language_package = "\\usepackage{" + name + "}";
721 else if (name == "prettyref")
722 ; // ignore this FIXME: Use the package separator mechanism instead
724 else if (name == "lyxskak") {
725 // ignore this and its options
726 const char * const o[] = {"ps", "mover", 0};
727 delete_opt(options, o);
730 else if (is_known(name, known_lyx_packages) && options.empty()) {
731 if (!in_lyx_preamble)
732 h_preamble << package_beg_sep << name
733 << package_mid_sep << "\\usepackage{"
734 << name << "}\n" << package_end_sep;
737 else if (name == "geometry")
738 handle_geometry(options);
740 else if (name == "subfig")
741 ; // ignore this FIXME: Use the package separator mechanism instead
743 else if (is_known(name, known_languages))
746 else if (name == "natbib") {
747 h_biblio_style = "plainnat";
748 h_cite_engine = "natbib";
749 h_cite_engine_type = "authoryear";
750 vector<string>::iterator it =
751 find(options.begin(), options.end(), "authoryear");
752 if (it != options.end())
755 it = find(options.begin(), options.end(), "numbers");
756 if (it != options.end()) {
757 h_cite_engine_type = "numerical";
763 else if (name == "jurabib") {
764 h_biblio_style = "jurabib";
765 h_cite_engine = "jurabib";
766 h_cite_engine_type = "authoryear";
769 else if (name == "hyperref")
770 handle_hyperref(options);
772 else if (!in_lyx_preamble) {
774 h_preamble << "\\usepackage{" << name << "}";
776 h_preamble << "\\usepackage[" << opts << "]{"
782 // We need to do something with the options...
783 if (!options.empty())
784 cerr << "Ignoring options '" << join(options, ",")
785 << "' of package " << name << '.' << endl;
787 // remove the whitespace
792 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
795 Token t = p.get_token();
796 if (t.cat() == catEscape &&
797 is_known(t.cs(), known_if_commands))
798 handle_if(p, in_lyx_preamble);
800 if (!in_lyx_preamble)
801 h_preamble << t.asInput();
802 if (t.cat() == catEscape && t.cs() == "fi")
809 bool Preamble::writeLyXHeader(ostream & os, bool subdoc)
811 // translate from babel to LyX names
812 h_language = babel2lyx(h_language);
814 // set the quote language
815 // LyX only knows the following quotes languages:
816 // english, swedish, german, polish, french and danish
817 // (quotes for "japanese" and "chinese-traditional" are missing because
818 // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
819 // conversion list taken from
820 // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
821 // (quotes for kazakh and interlingua are unknown)
823 if (h_language == "danish")
824 h_quotes_language = "danish";
826 else if (is_known(h_language, known_french_quotes_languages))
827 h_quotes_language = "french";
829 else if (is_known(h_language, known_german_quotes_languages))
830 h_quotes_language = "german";
832 else if (is_known(h_language, known_polish_quotes_languages))
833 h_quotes_language = "polish";
835 else if (is_known(h_language, known_swedish_quotes_languages))
836 h_quotes_language = "swedish";
838 else if (is_known(h_language, known_english_quotes_languages))
839 h_quotes_language = "english";
841 if (contains(h_float_placement, "H"))
842 registerAutomaticallyLoadedPackage("float");
843 if (h_spacing != "single" && h_spacing != "default")
844 registerAutomaticallyLoadedPackage("setspace");
846 // output the LyX file settings
847 os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
848 << "\\lyxformat " << LYX_FORMAT << '\n'
849 << "\\begin_document\n"
850 << "\\begin_header\n"
851 << "\\textclass " << h_textclass << "\n";
852 string const raw = subdoc ? empty_string() : h_preamble.str();
854 os << "\\begin_preamble\n";
855 for (string::size_type i = 0; i < raw.size(); ++i) {
856 if (raw[i] == package_beg_sep) {
857 // Here follows some package loading code that
858 // must be skipped if the package is loaded
860 string::size_type j = raw.find(package_mid_sep, i);
861 if (j == string::npos)
863 string::size_type k = raw.find(package_end_sep, j);
864 if (k == string::npos)
866 string const package = raw.substr(i + 1, j - i - 1);
867 string const replacement = raw.substr(j + 1, k - j - 1);
868 if (auto_packages.find(package) == auto_packages.end())
874 os << "\n\\end_preamble\n";
876 if (!h_options.empty())
877 os << "\\options " << h_options << "\n";
878 os << "\\use_default_options " << h_use_default_options << "\n";
879 if (!used_modules.empty()) {
880 os << "\\begin_modules\n";
881 vector<string>::const_iterator const end = used_modules.end();
882 vector<string>::const_iterator it = used_modules.begin();
883 for (; it != end; it++)
885 os << "\\end_modules\n";
887 os << "\\language " << h_language << "\n"
888 << "\\language_package " << h_language_package << "\n"
889 << "\\inputencoding " << h_inputencoding << "\n"
890 << "\\fontencoding " << h_fontencoding << "\n"
891 << "\\font_roman " << h_font_roman << "\n"
892 << "\\font_sans " << h_font_sans << "\n"
893 << "\\font_typewriter " << h_font_typewriter << "\n"
894 << "\\font_default_family " << h_font_default_family << "\n"
895 << "\\use_non_tex_fonts " << h_use_non_tex_fonts << "\n"
896 << "\\font_sc " << h_font_sc << "\n"
897 << "\\font_osf " << h_font_osf << "\n"
898 << "\\font_sf_scale " << h_font_sf_scale << "\n"
899 << "\\font_tt_scale " << h_font_tt_scale << "\n"
900 << "\\graphics " << h_graphics << "\n";
901 if (!h_float_placement.empty())
902 os << "\\float_placement " << h_float_placement << "\n";
903 os << "\\paperfontsize " << h_paperfontsize << "\n"
904 << "\\spacing " << h_spacing << "\n"
905 << "\\use_hyperref " << h_use_hyperref << '\n';
906 if (h_use_hyperref == "1") {
907 if (!h_pdf_title.empty())
908 os << "\\pdf_title \"" << h_pdf_title << "\"\n";
909 if (!h_pdf_author.empty())
910 os << "\\pdf_author \"" << h_pdf_author << "\"\n";
911 if (!h_pdf_subject.empty())
912 os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
913 if (!h_pdf_keywords.empty())
914 os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
915 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
916 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
917 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
918 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
919 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
920 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
921 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
922 "\\pdf_backref " << h_pdf_backref << "\n"
923 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
924 if (!h_pdf_pagemode.empty())
925 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
926 if (!h_pdf_quoted_options.empty())
927 os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
929 os << "\\papersize " << h_papersize << "\n"
930 << "\\use_geometry " << h_use_geometry << '\n';
931 for (map<string, string>::const_iterator it = h_use_packages.begin();
932 it != h_use_packages.end(); it++)
933 os << "\\use_package " << it->first << ' ' << it->second << '\n';
934 os << "\\cite_engine " << h_cite_engine << '\n'
935 << "\\cite_engine_type " << h_cite_engine_type << '\n'
936 << "\\biblio_style " << h_biblio_style << "\n"
937 << "\\use_bibtopic " << h_use_bibtopic << "\n"
938 << "\\use_indices " << h_use_indices << "\n"
939 << "\\paperorientation " << h_paperorientation << '\n'
940 << "\\suppress_date " << h_suppress_date << '\n'
941 << "\\justification " << h_justification << '\n'
942 << "\\use_refstyle " << h_use_refstyle << '\n';
943 if (!h_fontcolor.empty())
944 os << "\\fontcolor " << h_fontcolor << '\n';
945 if (!h_notefontcolor.empty())
946 os << "\\notefontcolor " << h_notefontcolor << '\n';
947 if (!h_backgroundcolor.empty())
948 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
949 if (!h_boxbgcolor.empty())
950 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
952 << "\\secnumdepth " << h_secnumdepth << "\n"
953 << "\\tocdepth " << h_tocdepth << "\n"
954 << "\\paragraph_separation " << h_paragraph_separation << "\n";
955 if (h_paragraph_separation == "skip")
956 os << "\\defskip " << h_defskip << "\n";
958 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
959 os << "\\quotes_language " << h_quotes_language << "\n"
960 << "\\papercolumns " << h_papercolumns << "\n"
961 << "\\papersides " << h_papersides << "\n"
962 << "\\paperpagestyle " << h_paperpagestyle << "\n";
963 if (!h_listings_params.empty())
964 os << "\\listings_params " << h_listings_params << "\n";
965 os << "\\tracking_changes " << h_tracking_changes << "\n"
966 << "\\output_changes " << h_output_changes << "\n"
967 << "\\html_math_output " << h_html_math_output << "\n"
968 << "\\html_css_as_file " << h_html_css_as_file << "\n"
969 << "\\html_be_strict " << h_html_be_strict << "\n"
971 << "\\end_header\n\n"
977 void Preamble::parse(Parser & p, string const & forceclass,
978 TeX2LyXDocClass & tc)
980 // initialize fixed types
981 special_columns['D'] = 3;
982 bool is_full_document = false;
983 bool is_lyx_file = false;
984 bool in_lyx_preamble = false;
986 // determine whether this is a full document or a fragment for inclusion
988 Token const & t = p.get_token();
990 if (t.cat() == catEscape && t.cs() == "documentclass") {
991 is_full_document = true;
997 while (is_full_document && p.good()) {
998 Token const & t = p.get_token();
1001 cerr << "t: " << t << "\n";
1007 if (!in_lyx_preamble &&
1008 (t.cat() == catLetter ||
1009 t.cat() == catSuper ||
1010 t.cat() == catSub ||
1011 t.cat() == catOther ||
1012 t.cat() == catMath ||
1013 t.cat() == catActive ||
1014 t.cat() == catBegin ||
1015 t.cat() == catEnd ||
1016 t.cat() == catAlign ||
1017 t.cat() == catParameter))
1018 h_preamble << t.cs();
1020 else if (!in_lyx_preamble &&
1021 (t.cat() == catSpace || t.cat() == catNewline))
1022 h_preamble << t.asInput();
1024 else if (t.cat() == catComment) {
1025 static regex const islyxfile("%% LyX .* created this file");
1026 static regex const usercommands("User specified LaTeX commands");
1028 string const comment = t.asInput();
1030 // magically switch encoding default if it looks like XeLaTeX
1031 static string const magicXeLaTeX =
1032 "% This document must be compiled with XeLaTeX ";
1033 if (comment.size() > magicXeLaTeX.size()
1034 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1035 && h_inputencoding == "auto") {
1036 cerr << "XeLaTeX comment found, switching to UTF8\n";
1037 h_inputencoding = "utf8";
1040 if (regex_search(comment, sub, islyxfile)) {
1042 in_lyx_preamble = true;
1043 } else if (is_lyx_file
1044 && regex_search(comment, sub, usercommands))
1045 in_lyx_preamble = false;
1046 else if (!in_lyx_preamble)
1047 h_preamble << t.asInput();
1050 else if (t.cs() == "pagestyle")
1051 h_paperpagestyle = p.verbatim_item();
1053 else if (t.cs() == "date") {
1054 string argument = p.getArg('{', '}');
1055 if (argument.empty())
1056 h_suppress_date = "true";
1058 h_preamble << t.asInput() << '{' << argument << '}';
1061 else if (t.cs() == "color") {
1062 string const space =
1063 (p.hasOpt() ? p.getOpt() : string());
1064 string argument = p.getArg('{', '}');
1065 // check the case that a standard color is used
1066 if (space.empty() && is_known(argument, known_basic_colors)) {
1067 h_fontcolor = rgbcolor2code(argument);
1068 preamble.registerAutomaticallyLoadedPackage("color");
1069 } else if (space.empty() && argument == "document_fontcolor")
1070 preamble.registerAutomaticallyLoadedPackage("color");
1071 // check the case that LyX's document_fontcolor is defined
1072 // but not used for \color
1074 h_preamble << t.asInput();
1076 h_preamble << space;
1077 h_preamble << '{' << argument << '}';
1078 // the color might already be set because \definecolor
1079 // is parsed before this
1084 else if (t.cs() == "pagecolor") {
1085 string argument = p.getArg('{', '}');
1086 // check the case that a standard color is used
1087 if (is_known(argument, known_basic_colors)) {
1088 h_backgroundcolor = rgbcolor2code(argument);
1089 } else if (argument == "page_backgroundcolor")
1090 preamble.registerAutomaticallyLoadedPackage("color");
1091 // check the case that LyX's page_backgroundcolor is defined
1092 // but not used for \pagecolor
1094 h_preamble << t.asInput() << '{' << argument << '}';
1095 // the color might already be set because \definecolor
1096 // is parsed before this
1097 h_backgroundcolor = "";
1101 else if (t.cs() == "makeatletter") {
1102 // LyX takes care of this
1103 p.setCatCode('@', catLetter);
1106 else if (t.cs() == "makeatother") {
1107 // LyX takes care of this
1108 p.setCatCode('@', catOther);
1111 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1112 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1113 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1114 || t.cs() == "DeclareRobustCommand"
1115 || t.cs() == "DeclareRobustCommandx"
1116 || t.cs() == "ProvideTextCommandDefault"
1117 || t.cs() == "DeclareMathAccent") {
1119 if (p.next_token().character() == '*') {
1123 string const name = p.verbatim_item();
1124 string const opt1 = p.getFullOpt();
1125 string const opt2 = p.getFullOpt();
1126 string const body = p.verbatim_item();
1128 if (name == "\\rmdefault")
1129 if (is_known(body, known_roman_fonts))
1130 h_font_roman = body;
1131 if (name == "\\sfdefault")
1132 if (is_known(body, known_sans_fonts))
1134 if (name == "\\ttdefault")
1135 if (is_known(body, known_typewriter_fonts))
1136 h_font_typewriter = body;
1137 if (name == "\\familydefault") {
1138 string family = body;
1139 // remove leading "\"
1140 h_font_default_family = family.erase(0,1);
1143 // remove the lyxdot definition that is re-added by LyX
1145 if (name == "\\lyxdot")
1146 in_lyx_preamble = true;
1148 // Add the command to the known commands
1149 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1151 // only non-lyxspecific stuff
1152 if (!in_lyx_preamble) {
1154 ss << '\\' << t.cs();
1157 ss << '{' << name << '}' << opt1 << opt2
1158 << '{' << body << "}";
1159 h_preamble << ss.str();
1161 ostream & out = in_preamble ? h_preamble : os;
1162 out << "\\" << t.cs() << "{" << name << "}"
1163 << opts << "{" << body << "}";
1168 else if (t.cs() == "documentclass") {
1169 vector<string>::iterator it;
1170 vector<string> opts = split_options(p.getArg('[', ']'));
1171 handle_opt(opts, known_fontsizes, h_paperfontsize);
1172 delete_opt(opts, known_fontsizes);
1173 // delete "pt" at the end
1174 string::size_type i = h_paperfontsize.find("pt");
1175 if (i != string::npos)
1176 h_paperfontsize.erase(i);
1177 // The documentclass options are always parsed before the options
1178 // of the babel call so that a language cannot overwrite the babel
1180 handle_opt(opts, known_languages, h_language);
1181 delete_opt(opts, known_languages);
1183 // paper orientation
1184 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1185 h_paperorientation = "landscape";
1189 if ((it = find(opts.begin(), opts.end(), "oneside"))
1194 if ((it = find(opts.begin(), opts.end(), "twoside"))
1200 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1202 h_papercolumns = "1";
1205 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1207 h_papercolumns = "2";
1211 // some size options are known to any document classes, other sizes
1212 // are handled by the \geometry command of the geometry package
1213 handle_opt(opts, known_class_paper_sizes, h_papersize);
1214 delete_opt(opts, known_class_paper_sizes);
1215 // the remaining options
1216 h_options = join(opts, ",");
1217 // FIXME This does not work for classes that have a
1218 // different name in LyX than in LaTeX
1219 h_textclass = p.getArg('{', '}');
1222 else if (t.cs() == "usepackage") {
1223 string const options = p.getArg('[', ']');
1224 string const name = p.getArg('{', '}');
1225 vector<string> vecnames;
1226 split(name, vecnames, ',');
1227 vector<string>::const_iterator it = vecnames.begin();
1228 vector<string>::const_iterator end = vecnames.end();
1229 for (; it != end; ++it)
1230 handle_package(p, trimSpaceAndEol(*it), options,
1234 else if (t.cs() == "inputencoding") {
1235 string const encoding = p.getArg('{','}');
1236 h_inputencoding = encoding;
1237 p.setEncoding(encoding);
1240 else if (t.cs() == "newenvironment") {
1241 string const name = p.getArg('{', '}');
1242 string const opt1 = p.getFullOpt();
1243 string const opt2 = p.getFullOpt();
1244 string const beg = p.verbatim_item();
1245 string const end = p.verbatim_item();
1246 if (!in_lyx_preamble) {
1247 h_preamble << "\\newenvironment{" << name
1248 << '}' << opt1 << opt2 << '{'
1249 << beg << "}{" << end << '}';
1251 add_known_environment(name, opt1, !opt2.empty(),
1252 from_utf8(beg), from_utf8(end));
1256 else if (t.cs() == "def") {
1257 string name = p.get_token().cs();
1258 while (p.next_token().cat() != catBegin)
1259 name += p.get_token().cs();
1260 if (!in_lyx_preamble)
1261 h_preamble << "\\def\\" << name << '{'
1262 << p.verbatim_item() << "}";
1265 else if (t.cs() == "newcolumntype") {
1266 string const name = p.getArg('{', '}');
1267 trimSpaceAndEol(name);
1269 string opts = p.getOpt();
1270 if (!opts.empty()) {
1271 istringstream is(string(opts, 1));
1274 special_columns[name[0]] = nargs;
1275 h_preamble << "\\newcolumntype{" << name << "}";
1277 h_preamble << "[" << nargs << "]";
1278 h_preamble << "{" << p.verbatim_item() << "}";
1281 else if (t.cs() == "setcounter") {
1282 string const name = p.getArg('{', '}');
1283 string const content = p.getArg('{', '}');
1284 if (name == "secnumdepth")
1285 h_secnumdepth = content;
1286 else if (name == "tocdepth")
1287 h_tocdepth = content;
1289 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1292 else if (t.cs() == "setlength") {
1293 string const name = p.verbatim_item();
1294 string const content = p.verbatim_item();
1295 // the paragraphs are only not indented when \parindent is set to zero
1296 if (name == "\\parindent" && content != "") {
1297 if (content[0] == '0')
1298 h_paragraph_separation = "skip";
1300 h_paragraph_indentation = translate_len(content);
1301 } else if (name == "\\parskip") {
1302 if (content == "\\smallskipamount")
1303 h_defskip = "smallskip";
1304 else if (content == "\\medskipamount")
1305 h_defskip = "medskip";
1306 else if (content == "\\bigskipamount")
1307 h_defskip = "bigskip";
1309 h_defskip = content;
1311 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1314 else if (t.cs() == "onehalfspacing")
1315 h_spacing = "onehalf";
1317 else if (t.cs() == "doublespacing")
1318 h_spacing = "double";
1320 else if (t.cs() == "setstretch")
1321 h_spacing = "other " + p.verbatim_item();
1323 else if (t.cs() == "begin") {
1324 string const name = p.getArg('{', '}');
1325 if (name == "document")
1327 h_preamble << "\\begin{" << name << "}";
1330 else if (t.cs() == "geometry") {
1331 vector<string> opts = split_options(p.getArg('{', '}'));
1332 handle_geometry(opts);
1335 else if (t.cs() == "definecolor") {
1336 string const color = p.getArg('{', '}');
1337 string const space = p.getArg('{', '}');
1338 string const value = p.getArg('{', '}');
1339 if (color == "document_fontcolor" && space == "rgb") {
1340 RGBColor c(RGBColorFromLaTeX(value));
1341 h_fontcolor = X11hexname(c);
1342 } else if (color == "note_fontcolor" && space == "rgb") {
1343 RGBColor c(RGBColorFromLaTeX(value));
1344 h_notefontcolor = X11hexname(c);
1345 } else if (color == "page_backgroundcolor" && space == "rgb") {
1346 RGBColor c(RGBColorFromLaTeX(value));
1347 h_backgroundcolor = X11hexname(c);
1348 } else if (color == "shadecolor" && space == "rgb") {
1349 RGBColor c(RGBColorFromLaTeX(value));
1350 h_boxbgcolor = X11hexname(c);
1352 h_preamble << "\\definecolor{" << color
1353 << "}{" << space << "}{" << value
1358 else if (t.cs() == "bibliographystyle")
1359 h_biblio_style = p.verbatim_item();
1361 else if (t.cs() == "jurabibsetup") {
1362 // FIXME p.getArg('{', '}') is most probably wrong (it
1363 // does not handle nested braces).
1364 // Use p.verbatim_item() instead.
1365 vector<string> jurabibsetup =
1366 split_options(p.getArg('{', '}'));
1367 // add jurabibsetup to the jurabib package options
1368 add_package("jurabib", jurabibsetup);
1369 if (!jurabibsetup.empty()) {
1370 h_preamble << "\\jurabibsetup{"
1371 << join(jurabibsetup, ",") << '}';
1375 else if (t.cs() == "hypersetup") {
1376 vector<string> hypersetup =
1377 split_options(p.verbatim_item());
1378 // add hypersetup to the hyperref package options
1379 handle_hyperref(hypersetup);
1380 if (!hypersetup.empty()) {
1381 h_preamble << "\\hypersetup{"
1382 << join(hypersetup, ",") << '}';
1386 else if (is_known(t.cs(), known_if_3arg_commands)) {
1387 // prevent misparsing of \usepackage if it is used
1388 // as an argument (see e.g. our own output of
1389 // \@ifundefined above)
1390 string const arg1 = p.verbatim_item();
1391 string const arg2 = p.verbatim_item();
1392 string const arg3 = p.verbatim_item();
1393 // test case \@ifundefined{date}{}{\date{}}
1394 if (t.cs() == "@ifundefined" && arg1 == "date" &&
1395 arg2.empty() && arg3 == "\\date{}") {
1396 h_suppress_date = "true";
1397 // older tex2lyx versions did output
1398 // \@ifundefined{definecolor}{\usepackage{color}}{}
1399 } else if (t.cs() == "@ifundefined" &&
1400 arg1 == "definecolor" &&
1401 arg2 == "\\usepackage{color}" &&
1403 if (!in_lyx_preamble)
1404 h_preamble << package_beg_sep
1407 << "\\@ifundefined{definecolor}{color}{}"
1410 //\@ifundefined{showcaptionsetup}{}{%
1411 // \PassOptionsToPackage{caption=false}{subfig}}
1412 // that LyX uses for subfloats
1413 } else if (t.cs() == "@ifundefined" &&
1414 arg1 == "showcaptionsetup" && arg2.empty()
1415 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1417 } else if (!in_lyx_preamble) {
1418 h_preamble << t.asInput()
1419 << '{' << arg1 << '}'
1420 << '{' << arg2 << '}'
1421 << '{' << arg3 << '}';
1425 else if (is_known(t.cs(), known_if_commands)) {
1426 // must not parse anything in conditional code, since
1427 // LyX would output the parsed contents unconditionally
1428 if (!in_lyx_preamble)
1429 h_preamble << t.asInput();
1430 handle_if(p, in_lyx_preamble);
1433 else if (!t.cs().empty() && !in_lyx_preamble)
1434 h_preamble << '\\' << t.cs();
1437 // remove the whitespace
1440 // Force textclass if the user wanted it
1441 if (!forceclass.empty())
1442 h_textclass = forceclass;
1443 if (noweb_mode && !prefixIs(h_textclass, "literate-"))
1444 h_textclass.insert(0, "literate-");
1445 tc.setName(h_textclass);
1447 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1450 if (h_papersides.empty()) {
1453 h_papersides = ss.str();
1458 string babel2lyx(string const & language)
1460 char const * const * where = is_known(language, known_languages);
1462 return known_coded_languages[where - known_languages];
1467 string rgbcolor2code(string const & name)
1469 char const * const * where = is_known(name, known_basic_colors);
1471 // "red", "green" etc
1472 return known_basic_color_codes[where - known_basic_colors];
1474 // "255,0,0", "0,255,0" etc
1475 RGBColor c(RGBColorFromLaTeX(name));
1476 return X11hexname(c);