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", "austrian", "bahasa", "bahasai", "bahasam",
61 "basque", "belarusian", "brazil", "brazilian", "breton", "british", "bulgarian",
62 "canadian", "canadien", "catalan", "croatian", "czech", "danish", "dutch",
63 "english", "esperanto", "estonian", "farsi", "finnish", "francais", "french",
64 "frenchb", "frenchle", "frenchpro", "galician", "german", "germanb", "greek",
65 "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", "austrian", "bahasa", "bahasa", "bahasam",
81 "basque", "belarusian", "brazilian", "brazilian", "breton", "british", "bulgarian",
82 "canadian", "canadien", "catalan", "croatian", "czech", "danish", "dutch",
83 "english", "esperanto", "estonian", "farsi", "finnish", "french", "french",
84 "french", "french", "french", "galician", "german", "german", "greek",
85 "hebrew", "magyar", "icelandic", "bahasa", "bahasa", "interlingua",
86 "irish", "italian", "kazakh", "latin", "latvian", "lithuanian", "lowersorbian",
87 "lowersorbian", "magyar", "bahasam", "bahasam", "mongolian", "naustrian", "english",
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", "bahasa",
97 "bahasam", "brazilian", "canadian", "chinese-simplified", "english",
98 "esperanto", "hebrew", "irish", "korean", "portuguese", "scottish", "thai", 0};
100 /// languages with french quotes (.lyx names)
101 const char * const known_french_quotes_languages[] = {"albanian",
102 "arabic_arabi", "arabic_arabtex", "basque", "canadien", "catalan", "french",
103 "galician", "greek", "italian", "norsk", "nynorsk", "polutonikogreek",
104 "russian", "spanish", "spanish-mexico", "turkish", "turkmen", "ukrainian",
107 /// languages with german quotes (.lyx names)
108 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
109 "czech", "german", "icelandic", "lithuanian", "lowersorbian", "naustrian",
110 "ngerman", "serbian", "serbian-latin", "slovak", "slovene", "uppersorbian", 0};
112 /// languages with polish quotes (.lyx names)
113 const char * const known_polish_quotes_languages[] = {"afrikaans", "croatian",
114 "dutch", "estonian", "magyar", "polish", "romanian", 0};
116 /// languages with swedish quotes (.lyx names)
117 const char * const known_swedish_quotes_languages[] = {"finnish",
120 /// known language packages from the times before babel
121 const char * const known_old_language_packages[] = {"french", "frenchle",
122 "frenchpro", "german", "ngerman", "pmfrench", 0};
124 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
126 const char * const known_roman_fonts[] = { "ae", "beraserif", "bookman",
127 "ccfonts", "chancery", "charter", "cmr", "fourier", "lmodern", "mathpazo",
128 "mathptmx", "newcent", "utopia", 0};
130 const char * const known_sans_fonts[] = { "avant", "berasans", "cmbr", "cmss",
131 "helvet", "lmss", 0};
133 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
134 "courier", "lmtt", "luximono", "fourier", "lmodern", "mathpazo", "mathptmx",
137 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
138 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
139 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
140 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
141 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
143 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
144 "executivepaper", "legalpaper", "letterpaper", 0};
146 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
147 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
149 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
150 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
153 /// commands that can start an \if...\else...\endif sequence
154 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
155 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
156 "ifsidecap", "ifupgreek", 0};
158 const char * const known_basic_colors[] = {"blue", "black", "cyan", "green",
159 "magenta", "red", "white", "yellow", 0};
161 const char * const known_basic_color_codes[] = {"#0000ff", "#000000", "#00ffff", "#00ff00",
162 "#ff00ff", "#ff0000", "#ffffff", "#ffff00", 0};
164 /// conditional commands with three arguments like \@ifundefined{}{}{}
165 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
168 // codes used to remove packages that are loaded automatically by LyX.
169 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
170 const char package_beg_sep = '\001';
171 const char package_mid_sep = '\002';
172 const char package_end_sep = '\003';
175 // returns true if at least one of the options in what has been found
176 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
182 // the last language option is the document language (for babel and LyX)
183 // the last size option is the document font size
184 vector<string>::iterator it;
185 vector<string>::iterator position = opts.begin();
186 for (; *what; ++what) {
187 it = find(opts.begin(), opts.end(), *what);
188 if (it != opts.end()) {
189 if (it >= position) {
200 void delete_opt(vector<string> & opts, char const * const * what)
205 // remove found options from the list
206 // do this after handle_opt to avoid potential memory leaks
207 vector<string>::iterator it;
208 for (; *what; ++what) {
209 it = find(opts.begin(), opts.end(), *what);
210 if (it != opts.end())
217 * Split a package options string (keyval format) into a vector.
219 * authorformat=smallcaps,
221 * titleformat=colonsep,
222 * bibformat={tabular,ibidem,numbered}
224 vector<string> split_options(string const & input)
226 vector<string> options;
230 Token const & t = p.get_token();
231 if (t.asInput() == ",") {
232 options.push_back(trimSpaceAndEol(option));
234 } else if (t.asInput() == "=") {
237 if (p.next_token().asInput() == "{")
238 option += '{' + p.getArg('{', '}') + '}';
239 } else if (t.cat() != catSpace)
240 option += t.asInput();
244 options.push_back(trimSpaceAndEol(option));
251 * Retrieve a keyval option "name={value with=sign}" named \p name from
252 * \p options and return the value.
253 * The found option is also removed from \p options.
255 string process_keyval_opt(vector<string> & options, string name)
257 for (size_t i = 0; i < options.size(); ++i) {
258 vector<string> option;
259 split(options[i], option, '=');
260 if (option.size() < 2)
262 if (option[0] == name) {
263 options.erase(options.begin() + i);
264 option.erase(option.begin());
265 return join(option, "=");
271 } // anonymous namespace
274 bool Preamble::indentParagraphs() const
276 return h_paragraph_separation == "indent";
280 bool Preamble::isPackageUsed(string const & package) const
282 return used_packages.find(package) != used_packages.end();
286 vector<string> Preamble::getPackageOptions(string const & package) const
288 map<string, vector<string> >::const_iterator it = used_packages.find(package);
289 if (it != used_packages.end())
291 return vector<string>();
295 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
297 auto_packages.insert(package);
301 void Preamble::addModule(string const & module)
303 used_modules.push_back(module);
307 void Preamble::suppressDate(bool suppress)
310 h_suppress_date = "true";
312 h_suppress_date = "false";
316 void Preamble::add_package(string const & name, vector<string> & options)
318 // every package inherits the global options
319 if (used_packages.find(name) == used_packages.end())
320 used_packages[name] = split_options(h_options);
322 vector<string> & v = used_packages[name];
323 v.insert(v.end(), options.begin(), options.end());
324 if (name == "jurabib") {
325 // Don't output the order argument (see the cite command
326 // handling code in text.cpp).
327 vector<string>::iterator end =
328 remove(options.begin(), options.end(), "natbiborder");
329 end = remove(options.begin(), end, "jurabiborder");
330 options.erase(end, options.end());
337 // Given is a string like "scaled=0.9", return 0.9 * 100
338 string const scale_as_percentage(string const & scale)
340 string::size_type pos = scale.find('=');
341 if (pos != string::npos) {
342 string value = scale.substr(pos + 1);
344 return convert<string>(100 * convert<double>(value));
346 // If the input string didn't match our expectations.
347 // return the default value "100"
352 string remove_braces(string const & value)
356 if (value[0] == '{' && value[value.length()-1] == '}')
357 return value.substr(1, value.length()-2);
361 } // anonymous namespace
364 Preamble::Preamble() : one_language(true)
368 h_cite_engine = "basic";
369 h_defskip = "medskip";
372 h_fontencoding = "default";
373 h_font_roman = "default";
374 h_font_sans = "default";
375 h_font_typewriter = "default";
376 h_font_default_family = "default";
378 h_font_osf = "false";
379 h_font_sf_scale = "100";
380 h_font_tt_scale = "100";
381 h_graphics = "default";
382 h_html_be_strict = "false";
383 h_html_css_as_file = "0";
384 h_html_math_output = "0";
385 h_inputencoding = "auto";
386 h_language = "english";
387 h_language_package = "none";
392 h_output_changes = "false";
393 h_papercolumns = "1";
394 h_paperfontsize = "default";
395 h_paperorientation = "portrait";
396 h_paperpagestyle = "default";
398 h_papersize = "default";
399 h_paragraph_indentation = "default";
400 h_paragraph_separation = "indent";
405 h_pdf_bookmarks = "1";
406 h_pdf_bookmarksnumbered = "0";
407 h_pdf_bookmarksopen = "0";
408 h_pdf_bookmarksopenlevel = "1";
409 h_pdf_breaklinks = "0";
410 h_pdf_pdfborder = "0";
411 h_pdf_colorlinks = "0";
412 h_pdf_backref = "section";
413 h_pdf_pdfusetitle = "1";
415 //h_pdf_quoted_options;
416 h_quotes_language = "english";
418 h_spacing = "single";
419 h_suppress_date = "false";
420 h_textclass = "article";
422 h_tracking_changes = "false";
423 h_use_bibtopic = "false";
424 h_use_geometry = "false";
426 h_use_default_options = "false";
428 h_use_hyperref = "0";
430 h_use_mathdots = "0";
431 h_use_refstyle = "0";
432 h_use_undertilde = "0";
436 void Preamble::handle_hyperref(vector<string> & options)
438 // FIXME swallow inputencoding changes that might surround the
439 // hyperref setup if it was written by LyX
440 h_use_hyperref = "1";
441 // swallow "unicode=true", since LyX does always write that
442 vector<string>::iterator it =
443 find(options.begin(), options.end(), "unicode=true");
444 if (it != options.end())
446 it = find(options.begin(), options.end(), "pdfusetitle");
447 if (it != options.end()) {
448 h_pdf_pdfusetitle = "1";
451 string bookmarks = process_keyval_opt(options, "bookmarks");
452 if (bookmarks == "true")
453 h_pdf_bookmarks = "1";
454 else if (bookmarks == "false")
455 h_pdf_bookmarks = "0";
456 if (h_pdf_bookmarks == "1") {
457 string bookmarksnumbered =
458 process_keyval_opt(options, "bookmarksnumbered");
459 if (bookmarksnumbered == "true")
460 h_pdf_bookmarksnumbered = "1";
461 else if (bookmarksnumbered == "false")
462 h_pdf_bookmarksnumbered = "0";
463 string bookmarksopen =
464 process_keyval_opt(options, "bookmarksopen");
465 if (bookmarksopen == "true")
466 h_pdf_bookmarksopen = "1";
467 else if (bookmarksopen == "false")
468 h_pdf_bookmarksopen = "0";
469 if (h_pdf_bookmarksopen == "1") {
470 string bookmarksopenlevel =
471 process_keyval_opt(options, "bookmarksopenlevel");
472 if (!bookmarksopenlevel.empty())
473 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
476 string breaklinks = process_keyval_opt(options, "breaklinks");
477 if (breaklinks == "true")
478 h_pdf_breaklinks = "1";
479 else if (breaklinks == "false")
480 h_pdf_breaklinks = "0";
481 string pdfborder = process_keyval_opt(options, "pdfborder");
482 if (pdfborder == "{0 0 0}")
483 h_pdf_pdfborder = "1";
484 else if (pdfborder == "{0 0 1}")
485 h_pdf_pdfborder = "0";
486 string backref = process_keyval_opt(options, "backref");
487 if (!backref.empty())
488 h_pdf_backref = backref;
489 string colorlinks = process_keyval_opt(options, "colorlinks");
490 if (colorlinks == "true")
491 h_pdf_colorlinks = "1";
492 else if (colorlinks == "false")
493 h_pdf_colorlinks = "0";
494 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
495 if (!pdfpagemode.empty())
496 h_pdf_pagemode = pdfpagemode;
497 string pdftitle = process_keyval_opt(options, "pdftitle");
498 if (!pdftitle.empty()) {
499 h_pdf_title = remove_braces(pdftitle);
501 string pdfauthor = process_keyval_opt(options, "pdfauthor");
502 if (!pdfauthor.empty()) {
503 h_pdf_author = remove_braces(pdfauthor);
505 string pdfsubject = process_keyval_opt(options, "pdfsubject");
506 if (!pdfsubject.empty())
507 h_pdf_subject = remove_braces(pdfsubject);
508 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
509 if (!pdfkeywords.empty())
510 h_pdf_keywords = remove_braces(pdfkeywords);
511 if (!options.empty()) {
512 if (!h_pdf_quoted_options.empty())
513 h_pdf_quoted_options += ',';
514 h_pdf_quoted_options += join(options, ",");
520 void Preamble::handle_package(Parser &p, string const & name,
521 string const & opts, bool in_lyx_preamble)
523 vector<string> options = split_options(opts);
524 add_package(name, options);
528 if (is_known(name, known_roman_fonts)) {
533 if (name == "fourier") {
534 h_font_roman = "utopia";
535 // when font uses real small capitals
536 if (opts == "expert")
540 if (name == "mathpazo")
541 h_font_roman = "palatino";
543 if (name == "mathptmx")
544 h_font_roman = "times";
547 if (is_known(name, known_sans_fonts)) {
551 h_font_sf_scale = scale_as_percentage(scale);
556 if (is_known(name, known_typewriter_fonts)) {
557 // fourier can be set as roman font _only_
558 // fourier as typewriter is handled in handling of \ttdefault
559 if (name != "fourier") {
560 h_font_typewriter = name;
563 h_font_tt_scale = scale_as_percentage(scale);
568 // font uses old-style figure
572 // after the detection and handling of special cases, we can remove the
573 // fonts, otherwise they would appear in the preamble, see bug #7856
574 if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
575 || is_known(name, known_typewriter_fonts))
578 else if (name == "amsmath" || name == "amssymb")
581 else if (name == "esint")
584 else if (name == "mhchem")
587 else if (name == "mathdots")
588 h_use_mathdots = "2";
590 else if (name == "undertilde")
591 h_use_undertilde = "2";
593 else if (name == "babel") {
594 h_language_package = "default";
595 // One might think we would have to do nothing if babel is loaded
596 // without any options to prevent pollution of the preamble with this
597 // babel call in every roundtrip.
598 // But the user could have defined babel-specific things afterwards. So
599 // we need to keep it in the preamble to prevent cases like bug #7861.
601 // check if more than one option was used - used later for inputenc
602 // in case inputenc is parsed before babel, set the encoding to auto
603 if (options.begin() != options.end() - 1)
604 one_language = false;
605 // babel takes the last language of the option of its \usepackage
606 // call as document language. If there is no such language option, the
607 // last language in the documentclass options is used.
608 handle_opt(options, known_languages, h_language);
609 // If babel is called with options, LyX puts them by default into the
610 // document class options. This works for most languages, except
611 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
612 // perhaps in future others.
613 // Therefore keep the babel call as it is as the user might have
615 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
616 delete_opt(options, known_languages);
619 h_preamble << "\\usepackage{babel}\n";
622 else if (name == "fontenc") {
623 h_fontencoding = getStringFromVector(options, ",");
624 /* We could do the following for better round trip support,
625 * but this makes the document less portable, so I skip it:
626 if (h_fontencoding == lyxrc.fontenc)
627 h_fontencoding = "global";
632 else if (name == "inputenc" || name == "luainputenc") {
633 // h_inputencoding is only set when there is not more than one
634 // inputenc option because otherwise h_inputencoding must be
635 // set to "auto" (the default encoding of the document language)
636 // Therefore check for the "," character.
637 // It is also only set when there is not more then one babel
638 // language option but this is handled in the routine for babel.
639 if (opts.find(",") == string::npos && one_language == true)
640 h_inputencoding = opts;
641 if (!options.empty())
642 p.setEncoding(options.back());
646 else if (is_known(name, known_old_language_packages)) {
647 // known language packages from the times before babel
648 // if they are found and not also babel, they will be used as
649 // cutom language package
650 h_language_package = "\\usepackage{" + name + "}";
653 else if (name == "makeidx")
656 else if (name == "prettyref")
659 else if (name == "varioref")
662 else if (name == "verbatim")
665 else if (name == "nomencl")
668 else if (name == "textcomp")
671 else if (name == "url")
674 else if (name == "color" || name == "subscript" || name == "ulem") {
675 if (!in_lyx_preamble)
676 h_preamble << package_beg_sep << name
677 << package_mid_sep << "\\usepackage{"
678 << name << '}' << package_end_sep;
681 else if (name == "graphicx")
684 else if (name == "setspace")
687 else if (name == "geometry")
688 ; // Ignore this, the geometry settings are made by the \geometry
689 // command. This command is handled below.
691 else if (name == "rotfloat")
694 else if (name == "wrapfig")
697 else if (name == "subfig")
700 else if (is_known(name, known_languages))
703 else if (name == "natbib") {
704 h_cite_engine = "natbib_authoryear";
705 vector<string>::iterator it =
706 find(options.begin(), options.end(), "authoryear");
707 if (it != options.end())
710 it = find(options.begin(), options.end(), "numbers");
711 if (it != options.end()) {
712 h_cite_engine = "natbib_numerical";
718 else if (name == "jurabib")
719 h_cite_engine = "jurabib";
721 else if (name == "hyperref")
722 handle_hyperref(options);
724 else if (!in_lyx_preamble) {
726 h_preamble << "\\usepackage{" << name << "}";
728 h_preamble << "\\usepackage[" << opts << "]{"
734 // We need to do something with the options...
735 if (!options.empty())
736 cerr << "Ignoring options '" << join(options, ",")
737 << "' of package " << name << '.' << endl;
739 // remove the whitespace
744 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
747 Token t = p.get_token();
748 if (t.cat() == catEscape &&
749 is_known(t.cs(), known_if_commands))
750 handle_if(p, in_lyx_preamble);
752 if (!in_lyx_preamble)
753 h_preamble << t.asInput();
754 if (t.cat() == catEscape && t.cs() == "fi")
761 bool Preamble::writeLyXHeader(ostream & os)
763 // translate from babel to LyX names
764 h_language = babel2lyx(h_language);
766 // set the quote language
767 // LyX only knows the following quotes languages:
768 // english, swedish, german, polish, french and danish
769 // (quotes for "japanese" and "chinese-traditional" are missing because
770 // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
771 // conversion list taken from
772 // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
773 // (quotes for kazakh and interlingua are unknown)
775 if (h_language == "danish")
776 h_quotes_language = "danish";
778 else if (is_known(h_language, known_french_quotes_languages))
779 h_quotes_language = "french";
781 else if (is_known(h_language, known_german_quotes_languages))
782 h_quotes_language = "german";
784 else if (is_known(h_language, known_polish_quotes_languages))
785 h_quotes_language = "polish";
787 else if (is_known(h_language, known_swedish_quotes_languages))
788 h_quotes_language = "swedish";
790 else if (is_known(h_language, known_english_quotes_languages))
791 h_quotes_language = "english";
793 // output the LyX file settings
794 os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
795 << "\\lyxformat " << LYX_FORMAT << '\n'
796 << "\\begin_document\n"
797 << "\\begin_header\n"
798 << "\\textclass " << h_textclass << "\n";
799 string const raw = h_preamble.str();
801 os << "\\begin_preamble\n";
802 for (string::size_type i = 0; i < raw.size(); ++i) {
803 if (raw[i] == package_beg_sep) {
804 // Here follows some package loading code that
805 // must be skipped if the package is loaded
807 string::size_type j = raw.find(package_mid_sep, i);
808 if (j == string::npos)
810 string::size_type k = raw.find(package_end_sep, j);
811 if (k == string::npos)
813 string const package = raw.substr(i + 1, j - i - 1);
814 string const replacement = raw.substr(j + 1, k - j - 1);
815 if (auto_packages.find(package) == auto_packages.end())
821 os << "\n\\end_preamble\n";
823 if (!h_options.empty())
824 os << "\\options " << h_options << "\n";
825 os << "\\use_default_options " << h_use_default_options << "\n";
826 if (!used_modules.empty()) {
827 os << "\\begin_modules\n";
828 vector<string>::const_iterator const end = used_modules.end();
829 vector<string>::const_iterator it = used_modules.begin();
830 for (; it != end; it++)
832 os << "\\end_modules\n";
834 os << "\\language " << h_language << "\n"
835 << "\\language_package " << h_language_package << "\n"
836 << "\\inputencoding " << h_inputencoding << "\n"
837 << "\\fontencoding " << h_fontencoding << "\n"
838 << "\\font_roman " << h_font_roman << "\n"
839 << "\\font_sans " << h_font_sans << "\n"
840 << "\\font_typewriter " << h_font_typewriter << "\n"
841 << "\\font_default_family " << h_font_default_family << "\n"
842 << "\\font_sc " << h_font_sc << "\n"
843 << "\\font_osf " << h_font_osf << "\n"
844 << "\\font_sf_scale " << h_font_sf_scale << "\n"
845 << "\\font_tt_scale " << h_font_tt_scale << "\n"
846 << "\\graphics " << h_graphics << "\n";
847 if (!h_float_placement.empty())
848 os << "\\float_placement " << h_float_placement << "\n";
849 os << "\\paperfontsize " << h_paperfontsize << "\n"
850 << "\\spacing " << h_spacing << "\n"
851 << "\\use_hyperref " << h_use_hyperref << '\n';
852 if (h_use_hyperref == "1") {
853 if (!h_pdf_title.empty())
854 os << "\\pdf_title \"" << h_pdf_title << "\"\n";
855 if (!h_pdf_author.empty())
856 os << "\\pdf_author \"" << h_pdf_author << "\"\n";
857 if (!h_pdf_subject.empty())
858 os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
859 if (!h_pdf_keywords.empty())
860 os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
861 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
862 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
863 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
864 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
865 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
866 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
867 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
868 "\\pdf_backref " << h_pdf_backref << "\n"
869 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
870 if (!h_pdf_pagemode.empty())
871 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
872 if (!h_pdf_quoted_options.empty())
873 os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
875 os << "\\papersize " << h_papersize << "\n"
876 << "\\use_geometry " << h_use_geometry << "\n"
877 << "\\use_amsmath " << h_use_amsmath << "\n"
878 << "\\use_esint " << h_use_esint << "\n"
879 << "\\use_mhchem " << h_use_mhchem << "\n"
880 << "\\use_mathdots " << h_use_mathdots << "\n"
881 << "\\use_undertilde " << h_use_undertilde << "\n"
882 << "\\cite_engine " << h_cite_engine << "\n"
883 << "\\use_bibtopic " << h_use_bibtopic << "\n"
884 << "\\paperorientation " << h_paperorientation << '\n'
885 << "\\suppress_date " << h_suppress_date << '\n'
886 << "\\use_refstyle " << h_use_refstyle << '\n';
887 if (!h_fontcolor.empty())
888 os << "\\fontcolor " << h_fontcolor << '\n';
889 if (!h_notefontcolor.empty())
890 os << "\\notefontcolor " << h_notefontcolor << '\n';
891 if (!h_backgroundcolor.empty())
892 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
893 if (!h_boxbgcolor.empty())
894 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
896 << "\\secnumdepth " << h_secnumdepth << "\n"
897 << "\\tocdepth " << h_tocdepth << "\n"
898 << "\\paragraph_separation " << h_paragraph_separation << "\n";
899 if (h_paragraph_separation == "skip")
900 os << "\\defskip " << h_defskip << "\n";
902 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
903 os << "\\quotes_language " << h_quotes_language << "\n"
904 << "\\papercolumns " << h_papercolumns << "\n"
905 << "\\papersides " << h_papersides << "\n"
906 << "\\paperpagestyle " << h_paperpagestyle << "\n";
907 if (!h_listings_params.empty())
908 os << "\\listings_params " << h_listings_params << "\n";
909 os << "\\tracking_changes " << h_tracking_changes << "\n"
910 << "\\output_changes " << h_output_changes << "\n"
911 << "\\html_math_output " << h_html_math_output << "\n"
912 << "\\html_css_as_file " << h_html_css_as_file << "\n"
913 << "\\html_be_strict " << h_html_be_strict << "\n"
914 << "\\end_header\n\n"
916 // clear preamble for subdocuments
922 void Preamble::parse(Parser & p, string const & forceclass,
923 TeX2LyXDocClass & tc)
925 // initialize fixed types
926 special_columns['D'] = 3;
927 bool is_full_document = false;
928 bool is_lyx_file = false;
929 bool in_lyx_preamble = false;
931 // determine whether this is a full document or a fragment for inclusion
933 Token const & t = p.get_token();
935 if (t.cat() == catEscape && t.cs() == "documentclass") {
936 is_full_document = true;
942 while (is_full_document && p.good()) {
943 Token const & t = p.get_token();
946 cerr << "t: " << t << "\n";
952 if (!in_lyx_preamble &&
953 (t.cat() == catLetter ||
954 t.cat() == catSuper ||
956 t.cat() == catOther ||
957 t.cat() == catMath ||
958 t.cat() == catActive ||
959 t.cat() == catBegin ||
961 t.cat() == catAlign ||
962 t.cat() == catParameter))
963 h_preamble << t.cs();
965 else if (!in_lyx_preamble &&
966 (t.cat() == catSpace || t.cat() == catNewline))
967 h_preamble << t.asInput();
969 else if (t.cat() == catComment) {
970 static regex const islyxfile("%% LyX .* created this file");
971 static regex const usercommands("User specified LaTeX commands");
973 string const comment = t.asInput();
975 // magically switch encoding default if it looks like XeLaTeX
976 static string const magicXeLaTeX =
977 "% This document must be compiled with XeLaTeX ";
978 if (comment.size() > magicXeLaTeX.size()
979 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
980 && h_inputencoding == "auto") {
981 cerr << "XeLaTeX comment found, switching to UTF8\n";
982 h_inputencoding = "utf8";
985 if (regex_search(comment, sub, islyxfile)) {
987 in_lyx_preamble = true;
988 } else if (is_lyx_file
989 && regex_search(comment, sub, usercommands))
990 in_lyx_preamble = false;
991 else if (!in_lyx_preamble)
992 h_preamble << t.asInput();
995 else if (t.cs() == "pagestyle")
996 h_paperpagestyle = p.verbatim_item();
998 else if (t.cs() == "date") {
999 string argument = p.getArg('{', '}');
1000 if (argument.empty())
1001 h_suppress_date = "true";
1003 h_preamble << t.asInput() << '{' << argument << '}';
1006 else if (t.cs() == "color") {
1007 string argument = p.getArg('{', '}');
1008 // check the case that a standard color is used
1009 if (is_known(argument, known_basic_colors)) {
1010 h_fontcolor = rgbcolor2code(argument);
1011 preamble.registerAutomaticallyLoadedPackage("color");
1012 } else if (argument == "document_fontcolor")
1013 preamble.registerAutomaticallyLoadedPackage("color");
1014 // check the case that LyX's document_fontcolor is defined
1015 // but not used for \color
1017 h_preamble << t.asInput() << '{' << argument << '}';
1018 // the color might already be set because \definecolor
1019 // is parsed before this
1024 else if (t.cs() == "pagecolor") {
1025 string argument = p.getArg('{', '}');
1026 // check the case that a standard color is used
1027 if (is_known(argument, known_basic_colors)) {
1028 h_backgroundcolor = rgbcolor2code(argument);
1029 } else if (argument == "page_backgroundcolor")
1030 preamble.registerAutomaticallyLoadedPackage("color");
1031 // check the case that LyX's page_backgroundcolor is defined
1032 // but not used for \pagecolor
1034 h_preamble << t.asInput() << '{' << argument << '}';
1035 // the color might already be set because \definecolor
1036 // is parsed before this
1037 h_backgroundcolor = "";
1041 else if (t.cs() == "makeatletter") {
1042 // LyX takes care of this
1043 p.setCatCode('@', catLetter);
1046 else if (t.cs() == "makeatother") {
1047 // LyX takes care of this
1048 p.setCatCode('@', catOther);
1051 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1052 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1053 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1054 || t.cs() == "DeclareRobustCommand"
1055 || t.cs() == "DeclareRobustCommandx"
1056 || t.cs() == "ProvideTextCommandDefault"
1057 || t.cs() == "DeclareMathAccent") {
1059 if (p.next_token().character() == '*') {
1063 string const name = p.verbatim_item();
1064 string const opt1 = p.getFullOpt();
1065 string const opt2 = p.getFullOpt();
1066 string const body = p.verbatim_item();
1068 if (name == "\\rmdefault")
1069 if (is_known(body, known_roman_fonts))
1070 h_font_roman = body;
1071 if (name == "\\sfdefault")
1072 if (is_known(body, known_sans_fonts))
1074 if (name == "\\ttdefault")
1075 if (is_known(body, known_typewriter_fonts))
1076 h_font_typewriter = body;
1077 if (name == "\\familydefault") {
1078 string family = body;
1079 // remove leading "\"
1080 h_font_default_family = family.erase(0,1);
1083 // Add the command to the known commands
1084 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1086 // only non-lyxspecific stuff
1087 if (!in_lyx_preamble) {
1089 ss << '\\' << t.cs();
1092 ss << '{' << name << '}' << opt1 << opt2
1093 << '{' << body << "}";
1094 h_preamble << ss.str();
1096 ostream & out = in_preamble ? h_preamble : os;
1097 out << "\\" << t.cs() << "{" << name << "}"
1098 << opts << "{" << body << "}";
1103 else if (t.cs() == "documentclass") {
1104 vector<string>::iterator it;
1105 vector<string> opts = split_options(p.getArg('[', ']'));
1106 handle_opt(opts, known_fontsizes, h_paperfontsize);
1107 delete_opt(opts, known_fontsizes);
1108 // delete "pt" at the end
1109 string::size_type i = h_paperfontsize.find("pt");
1110 if (i != string::npos)
1111 h_paperfontsize.erase(i);
1112 // The documentclass options are always parsed before the options
1113 // of the babel call so that a language cannot overwrite the babel
1115 handle_opt(opts, known_languages, h_language);
1116 delete_opt(opts, known_languages);
1118 // paper orientation
1119 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1120 h_paperorientation = "landscape";
1124 if ((it = find(opts.begin(), opts.end(), "oneside"))
1129 if ((it = find(opts.begin(), opts.end(), "twoside"))
1135 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1137 h_papercolumns = "1";
1140 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1142 h_papercolumns = "2";
1146 // some size options are know to any document classes, other sizes
1147 // are handled by the \geometry command of the geometry package
1148 handle_opt(opts, known_class_paper_sizes, h_papersize);
1149 delete_opt(opts, known_class_paper_sizes);
1150 // the remaining options
1151 h_options = join(opts, ",");
1152 // FIXME This does not work for classes that have a
1153 // different name in LyX than in LaTeX
1154 h_textclass = p.getArg('{', '}');
1157 else if (t.cs() == "usepackage") {
1158 string const options = p.getArg('[', ']');
1159 string const name = p.getArg('{', '}');
1160 vector<string> vecnames;
1161 split(name, vecnames, ',');
1162 vector<string>::const_iterator it = vecnames.begin();
1163 vector<string>::const_iterator end = vecnames.end();
1164 for (; it != end; ++it)
1165 handle_package(p, trimSpaceAndEol(*it), options,
1169 else if (t.cs() == "inputencoding") {
1170 string const encoding = p.getArg('{','}');
1171 h_inputencoding = encoding;
1172 p.setEncoding(encoding);
1175 else if (t.cs() == "newenvironment") {
1176 string const name = p.getArg('{', '}');
1177 string const opt1 = p.getFullOpt();
1178 string const opt2 = p.getFullOpt();
1179 string const beg = p.verbatim_item();
1180 string const end = p.verbatim_item();
1181 if (!in_lyx_preamble) {
1182 h_preamble << "\\newenvironment{" << name
1183 << '}' << opt1 << opt2 << '{'
1184 << beg << "}{" << end << '}';
1186 add_known_environment(name, opt1, !opt2.empty(),
1187 from_utf8(beg), from_utf8(end));
1191 else if (t.cs() == "def") {
1192 string name = p.get_token().cs();
1193 while (p.next_token().cat() != catBegin)
1194 name += p.get_token().cs();
1195 if (!in_lyx_preamble)
1196 h_preamble << "\\def\\" << name << '{'
1197 << p.verbatim_item() << "}";
1200 else if (t.cs() == "newcolumntype") {
1201 string const name = p.getArg('{', '}');
1202 trimSpaceAndEol(name);
1204 string opts = p.getOpt();
1205 if (!opts.empty()) {
1206 istringstream is(string(opts, 1));
1209 special_columns[name[0]] = nargs;
1210 h_preamble << "\\newcolumntype{" << name << "}";
1212 h_preamble << "[" << nargs << "]";
1213 h_preamble << "{" << p.verbatim_item() << "}";
1216 else if (t.cs() == "setcounter") {
1217 string const name = p.getArg('{', '}');
1218 string const content = p.getArg('{', '}');
1219 if (name == "secnumdepth")
1220 h_secnumdepth = content;
1221 else if (name == "tocdepth")
1222 h_tocdepth = content;
1224 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1227 else if (t.cs() == "setlength") {
1228 string const name = p.verbatim_item();
1229 string const content = p.verbatim_item();
1230 // the paragraphs are only not indented when \parindent is set to zero
1231 if (name == "\\parindent" && content != "") {
1232 if (content[0] == '0')
1233 h_paragraph_separation = "skip";
1235 h_paragraph_indentation = translate_len(content);
1236 } else if (name == "\\parskip") {
1237 if (content == "\\smallskipamount")
1238 h_defskip = "smallskip";
1239 else if (content == "\\medskipamount")
1240 h_defskip = "medskip";
1241 else if (content == "\\bigskipamount")
1242 h_defskip = "bigskip";
1244 h_defskip = content;
1246 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1249 else if (t.cs() == "onehalfspacing")
1250 h_spacing = "onehalf";
1252 else if (t.cs() == "doublespacing")
1253 h_spacing = "double";
1255 else if (t.cs() == "setstretch")
1256 h_spacing = "other " + p.verbatim_item();
1258 else if (t.cs() == "begin") {
1259 string const name = p.getArg('{', '}');
1260 if (name == "document")
1262 h_preamble << "\\begin{" << name << "}";
1265 else if (t.cs() == "geometry") {
1266 h_use_geometry = "true";
1267 vector<string> opts = split_options(p.getArg('{', '}'));
1268 vector<string>::iterator it;
1269 // paper orientation
1270 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1271 h_paperorientation = "landscape";
1275 handle_opt(opts, known_paper_sizes, h_papersize);
1276 delete_opt(opts, known_paper_sizes);
1278 char const * const * margin = known_paper_margins;
1280 for (; *margin; ++margin) {
1282 // search for the "=" in e.g. "lmargin=2cm" to get the value
1283 for(size_t i = 0; i != opts.size(); i++) {
1284 if (opts.at(i).find(*margin) != string::npos) {
1285 string::size_type pos = opts.at(i).find("=");
1286 string value = opts.at(i).substr(pos + 1);
1287 string name = known_coded_paper_margins[k];
1288 h_margins += "\\" + name + " " + value + "\n";
1294 else if (t.cs() == "definecolor") {
1295 string const color = p.getArg('{', '}');
1296 string const space = p.getArg('{', '}');
1297 string const value = p.getArg('{', '}');
1298 if (color == "document_fontcolor" && space == "rgb") {
1299 RGBColor c(RGBColorFromLaTeX(value));
1300 h_fontcolor = X11hexname(c);
1301 } else if (color == "note_fontcolor" && space == "rgb") {
1302 RGBColor c(RGBColorFromLaTeX(value));
1303 h_notefontcolor = X11hexname(c);
1304 } else if (color == "page_backgroundcolor" && space == "rgb") {
1305 RGBColor c(RGBColorFromLaTeX(value));
1306 h_backgroundcolor = X11hexname(c);
1307 } else if (color == "shadecolor" && space == "rgb") {
1308 RGBColor c(RGBColorFromLaTeX(value));
1309 h_boxbgcolor = X11hexname(c);
1311 h_preamble << "\\definecolor{" << color
1312 << "}{" << space << "}{" << value
1317 else if (t.cs() == "jurabibsetup") {
1318 // FIXME p.getArg('{', '}') is most probably wrong (it
1319 // does not handle nested braces).
1320 // Use p.verbatim_item() instead.
1321 vector<string> jurabibsetup =
1322 split_options(p.getArg('{', '}'));
1323 // add jurabibsetup to the jurabib package options
1324 add_package("jurabib", jurabibsetup);
1325 if (!jurabibsetup.empty()) {
1326 h_preamble << "\\jurabibsetup{"
1327 << join(jurabibsetup, ",") << '}';
1331 else if (t.cs() == "hypersetup") {
1332 vector<string> hypersetup =
1333 split_options(p.verbatim_item());
1334 // add hypersetup to the hyperref package options
1335 handle_hyperref(hypersetup);
1336 if (!hypersetup.empty()) {
1337 h_preamble << "\\hypersetup{"
1338 << join(hypersetup, ",") << '}';
1342 else if (is_known(t.cs(), known_if_3arg_commands)) {
1343 // prevent misparsing of \usepackage if it is used
1344 // as an argument (see e.g. our own output of
1345 // \@ifundefined above)
1346 string const arg1 = p.verbatim_item();
1347 string const arg2 = p.verbatim_item();
1348 string const arg3 = p.verbatim_item();
1349 // test case \@ifundefined{date}{}{\date{}}
1350 if (t.cs() == "@ifundefined" && arg1 == "date" &&
1351 arg2.empty() && arg3 == "\\date{}") {
1352 h_suppress_date = "true";
1353 // older tex2lyx versions did output
1354 // \@ifundefined{definecolor}{\usepackage{color}}{}
1355 } else if (t.cs() == "@ifundefined" &&
1356 arg1 == "definecolor" &&
1357 arg2 == "\\usepackage{color}" &&
1359 if (!in_lyx_preamble)
1360 h_preamble << package_beg_sep
1363 << "\\@ifundefined{definecolor}{color}{}"
1366 //\@ifundefined{showcaptionsetup}{}{%
1367 // \PassOptionsToPackage{caption=false}{subfig}}
1368 // that LyX uses for subfloats
1369 } else if (t.cs() == "@ifundefined" &&
1370 arg1 == "showcaptionsetup" && arg2.empty()
1371 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1373 } else if (!in_lyx_preamble) {
1374 h_preamble << t.asInput()
1375 << '{' << arg1 << '}'
1376 << '{' << arg2 << '}'
1377 << '{' << arg3 << '}';
1381 else if (is_known(t.cs(), known_if_commands)) {
1382 // must not parse anything in conditional code, since
1383 // LyX would output the parsed contents unconditionally
1384 if (!in_lyx_preamble)
1385 h_preamble << t.asInput();
1386 handle_if(p, in_lyx_preamble);
1389 else if (!t.cs().empty() && !in_lyx_preamble)
1390 h_preamble << '\\' << t.cs();
1393 // remove the whitespace
1396 // Force textclass if the user wanted it
1397 if (!forceclass.empty())
1398 h_textclass = forceclass;
1399 if (noweb_mode && !prefixIs(h_textclass, "literate-"))
1400 h_textclass.insert(0, "literate-");
1401 tc.setName(h_textclass);
1403 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1406 if (h_papersides.empty()) {
1409 h_papersides = ss.str();
1414 string babel2lyx(string const & language)
1416 char const * const * where = is_known(language, known_languages);
1418 return known_coded_languages[where - known_languages];
1423 string rgbcolor2code(string const & name)
1425 char const * const * where = is_known(name, known_basic_colors);
1427 // "red", "green" etc
1428 return known_basic_color_codes[where - known_basic_colors];
1430 // "255,0,0", "0,255,0" etc
1431 RGBColor c(RGBColorFromLaTeX(name));
1432 return X11hexname(c);