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 == "subscript")
677 else if (name == "color") {
678 if (!in_lyx_preamble)
679 h_preamble << package_beg_sep << name
680 << package_mid_sep << "\\usepackage{"
681 << name << '}' << package_end_sep;
684 else if (name == "graphicx")
687 else if (name == "setspace")
691 // do not ignore as long as we don't support all commands (e.g. \xout is missing)
692 // and as long as we don't support change tracking
693 else if (name == "ulem")
697 else if (name == "geometry")
698 ; // Ignore this, the geometry settings are made by the \geometry
699 // command. This command is handled below.
701 else if (name == "rotfloat")
704 else if (name == "wrapfig")
707 else if (name == "subfig")
710 else if (is_known(name, known_languages))
713 else if (name == "natbib") {
714 h_cite_engine = "natbib_authoryear";
715 vector<string>::iterator it =
716 find(options.begin(), options.end(), "authoryear");
717 if (it != options.end())
720 it = find(options.begin(), options.end(), "numbers");
721 if (it != options.end()) {
722 h_cite_engine = "natbib_numerical";
728 else if (name == "jurabib")
729 h_cite_engine = "jurabib";
731 else if (name == "hyperref")
732 handle_hyperref(options);
734 else if (!in_lyx_preamble) {
736 h_preamble << "\\usepackage{" << name << "}";
738 h_preamble << "\\usepackage[" << opts << "]{"
744 // We need to do something with the options...
745 if (!options.empty())
746 cerr << "Ignoring options '" << join(options, ",")
747 << "' of package " << name << '.' << endl;
749 // remove the whitespace
754 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
757 Token t = p.get_token();
758 if (t.cat() == catEscape &&
759 is_known(t.cs(), known_if_commands))
760 handle_if(p, in_lyx_preamble);
762 if (!in_lyx_preamble)
763 h_preamble << t.asInput();
764 if (t.cat() == catEscape && t.cs() == "fi")
771 bool Preamble::writeLyXHeader(ostream & os)
773 // translate from babel to LyX names
774 h_language = babel2lyx(h_language);
776 // set the quote language
777 // LyX only knows the following quotes languages:
778 // english, swedish, german, polish, french and danish
779 // (quotes for "japanese" and "chinese-traditional" are missing because
780 // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
781 // conversion list taken from
782 // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
783 // (quotes for kazakh and interlingua are unknown)
785 if (h_language == "danish")
786 h_quotes_language = "danish";
788 else if (is_known(h_language, known_french_quotes_languages))
789 h_quotes_language = "french";
791 else if (is_known(h_language, known_german_quotes_languages))
792 h_quotes_language = "german";
794 else if (is_known(h_language, known_polish_quotes_languages))
795 h_quotes_language = "polish";
797 else if (is_known(h_language, known_swedish_quotes_languages))
798 h_quotes_language = "swedish";
800 else if (is_known(h_language, known_english_quotes_languages))
801 h_quotes_language = "english";
803 // output the LyX file settings
804 os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
805 << "\\lyxformat " << LYX_FORMAT << '\n'
806 << "\\begin_document\n"
807 << "\\begin_header\n"
808 << "\\textclass " << h_textclass << "\n";
809 string const raw = h_preamble.str();
811 os << "\\begin_preamble\n";
812 for (string::size_type i = 0; i < raw.size(); ++i) {
813 if (raw[i] == package_beg_sep) {
814 // Here follows some package loading code that
815 // must be skipped if the package is loaded
817 string::size_type j = raw.find(package_mid_sep, i);
818 if (j == string::npos)
820 string::size_type k = raw.find(package_end_sep, j);
821 if (k == string::npos)
823 string const package = raw.substr(i + 1, j - i - 1);
824 string const replacement = raw.substr(j + 1, k - j - 1);
825 if (auto_packages.find(package) == auto_packages.end())
831 os << "\n\\end_preamble\n";
833 if (!h_options.empty())
834 os << "\\options " << h_options << "\n";
835 os << "\\use_default_options " << h_use_default_options << "\n";
836 if (!used_modules.empty()) {
837 os << "\\begin_modules\n";
838 vector<string>::const_iterator const end = used_modules.end();
839 vector<string>::const_iterator it = used_modules.begin();
840 for (; it != end; it++)
842 os << "\\end_modules\n";
844 os << "\\language " << h_language << "\n"
845 << "\\language_package " << h_language_package << "\n"
846 << "\\inputencoding " << h_inputencoding << "\n"
847 << "\\fontencoding " << h_fontencoding << "\n"
848 << "\\font_roman " << h_font_roman << "\n"
849 << "\\font_sans " << h_font_sans << "\n"
850 << "\\font_typewriter " << h_font_typewriter << "\n"
851 << "\\font_default_family " << h_font_default_family << "\n"
852 << "\\font_sc " << h_font_sc << "\n"
853 << "\\font_osf " << h_font_osf << "\n"
854 << "\\font_sf_scale " << h_font_sf_scale << "\n"
855 << "\\font_tt_scale " << h_font_tt_scale << "\n"
856 << "\\graphics " << h_graphics << "\n";
857 if (!h_float_placement.empty())
858 os << "\\float_placement " << h_float_placement << "\n";
859 os << "\\paperfontsize " << h_paperfontsize << "\n"
860 << "\\spacing " << h_spacing << "\n"
861 << "\\use_hyperref " << h_use_hyperref << '\n';
862 if (h_use_hyperref == "1") {
863 if (!h_pdf_title.empty())
864 os << "\\pdf_title \"" << h_pdf_title << "\"\n";
865 if (!h_pdf_author.empty())
866 os << "\\pdf_author \"" << h_pdf_author << "\"\n";
867 if (!h_pdf_subject.empty())
868 os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
869 if (!h_pdf_keywords.empty())
870 os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
871 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
872 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
873 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
874 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
875 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
876 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
877 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
878 "\\pdf_backref " << h_pdf_backref << "\n"
879 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
880 if (!h_pdf_pagemode.empty())
881 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
882 if (!h_pdf_quoted_options.empty())
883 os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
885 os << "\\papersize " << h_papersize << "\n"
886 << "\\use_geometry " << h_use_geometry << "\n"
887 << "\\use_amsmath " << h_use_amsmath << "\n"
888 << "\\use_esint " << h_use_esint << "\n"
889 << "\\use_mhchem " << h_use_mhchem << "\n"
890 << "\\use_mathdots " << h_use_mathdots << "\n"
891 << "\\use_undertilde " << h_use_undertilde << "\n"
892 << "\\cite_engine " << h_cite_engine << "\n"
893 << "\\use_bibtopic " << h_use_bibtopic << "\n"
894 << "\\paperorientation " << h_paperorientation << '\n'
895 << "\\suppress_date " << h_suppress_date << '\n'
896 << "\\use_refstyle " << h_use_refstyle << '\n';
897 if (!h_fontcolor.empty())
898 os << "\\fontcolor " << h_fontcolor << '\n';
899 if (!h_notefontcolor.empty())
900 os << "\\notefontcolor " << h_notefontcolor << '\n';
901 if (!h_backgroundcolor.empty())
902 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
903 if (!h_boxbgcolor.empty())
904 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
906 << "\\secnumdepth " << h_secnumdepth << "\n"
907 << "\\tocdepth " << h_tocdepth << "\n"
908 << "\\paragraph_separation " << h_paragraph_separation << "\n";
909 if (h_paragraph_separation == "skip")
910 os << "\\defskip " << h_defskip << "\n";
912 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
913 os << "\\quotes_language " << h_quotes_language << "\n"
914 << "\\papercolumns " << h_papercolumns << "\n"
915 << "\\papersides " << h_papersides << "\n"
916 << "\\paperpagestyle " << h_paperpagestyle << "\n";
917 if (!h_listings_params.empty())
918 os << "\\listings_params " << h_listings_params << "\n";
919 os << "\\tracking_changes " << h_tracking_changes << "\n"
920 << "\\output_changes " << h_output_changes << "\n"
921 << "\\html_math_output " << h_html_math_output << "\n"
922 << "\\html_css_as_file " << h_html_css_as_file << "\n"
923 << "\\html_be_strict " << h_html_be_strict << "\n"
924 << "\\end_header\n\n"
926 // clear preamble for subdocuments
932 void Preamble::parse(Parser & p, string const & forceclass,
933 TeX2LyXDocClass & tc)
935 // initialize fixed types
936 special_columns['D'] = 3;
937 bool is_full_document = false;
938 bool is_lyx_file = false;
939 bool in_lyx_preamble = false;
941 // determine whether this is a full document or a fragment for inclusion
943 Token const & t = p.get_token();
945 if (t.cat() == catEscape && t.cs() == "documentclass") {
946 is_full_document = true;
952 while (is_full_document && p.good()) {
953 Token const & t = p.get_token();
956 cerr << "t: " << t << "\n";
962 if (!in_lyx_preamble &&
963 (t.cat() == catLetter ||
964 t.cat() == catSuper ||
966 t.cat() == catOther ||
967 t.cat() == catMath ||
968 t.cat() == catActive ||
969 t.cat() == catBegin ||
971 t.cat() == catAlign ||
972 t.cat() == catParameter))
973 h_preamble << t.cs();
975 else if (!in_lyx_preamble &&
976 (t.cat() == catSpace || t.cat() == catNewline))
977 h_preamble << t.asInput();
979 else if (t.cat() == catComment) {
980 static regex const islyxfile("%% LyX .* created this file");
981 static regex const usercommands("User specified LaTeX commands");
983 string const comment = t.asInput();
985 // magically switch encoding default if it looks like XeLaTeX
986 static string const magicXeLaTeX =
987 "% This document must be compiled with XeLaTeX ";
988 if (comment.size() > magicXeLaTeX.size()
989 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
990 && h_inputencoding == "auto") {
991 cerr << "XeLaTeX comment found, switching to UTF8\n";
992 h_inputencoding = "utf8";
995 if (regex_search(comment, sub, islyxfile)) {
997 in_lyx_preamble = true;
998 } else if (is_lyx_file
999 && regex_search(comment, sub, usercommands))
1000 in_lyx_preamble = false;
1001 else if (!in_lyx_preamble)
1002 h_preamble << t.asInput();
1005 else if (t.cs() == "pagestyle")
1006 h_paperpagestyle = p.verbatim_item();
1008 else if (t.cs() == "date") {
1009 string argument = p.getArg('{', '}');
1010 if (argument.empty())
1011 h_suppress_date = "true";
1013 h_preamble << t.asInput() << '{' << argument << '}';
1016 else if (t.cs() == "color") {
1017 string argument = p.getArg('{', '}');
1018 // check the case that a standard color is used
1019 if (is_known(argument, known_basic_colors)) {
1020 h_fontcolor = rgbcolor2code(argument);
1021 preamble.registerAutomaticallyLoadedPackage("color");
1022 } else if (argument == "document_fontcolor")
1023 preamble.registerAutomaticallyLoadedPackage("color");
1024 // check the case that LyX's document_fontcolor is defined
1025 // but not used for \color
1027 h_preamble << t.asInput() << '{' << argument << '}';
1028 // the color might already be set because \definecolor
1029 // is parsed before this
1034 else if (t.cs() == "pagecolor") {
1035 string argument = p.getArg('{', '}');
1036 // check the case that a standard color is used
1037 if (is_known(argument, known_basic_colors)) {
1038 h_backgroundcolor = rgbcolor2code(argument);
1039 } else if (argument == "page_backgroundcolor")
1040 preamble.registerAutomaticallyLoadedPackage("color");
1041 // check the case that LyX's page_backgroundcolor is defined
1042 // but not used for \pagecolor
1044 h_preamble << t.asInput() << '{' << argument << '}';
1045 // the color might already be set because \definecolor
1046 // is parsed before this
1047 h_backgroundcolor = "";
1051 else if (t.cs() == "makeatletter") {
1052 // LyX takes care of this
1053 p.setCatCode('@', catLetter);
1056 else if (t.cs() == "makeatother") {
1057 // LyX takes care of this
1058 p.setCatCode('@', catOther);
1061 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1062 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1063 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1064 || t.cs() == "DeclareRobustCommand"
1065 || t.cs() == "DeclareRobustCommandx"
1066 || t.cs() == "ProvideTextCommandDefault"
1067 || t.cs() == "DeclareMathAccent") {
1069 if (p.next_token().character() == '*') {
1073 string const name = p.verbatim_item();
1074 string const opt1 = p.getFullOpt();
1075 string const opt2 = p.getFullOpt();
1076 string const body = p.verbatim_item();
1078 if (name == "\\rmdefault")
1079 if (is_known(body, known_roman_fonts))
1080 h_font_roman = body;
1081 if (name == "\\sfdefault")
1082 if (is_known(body, known_sans_fonts))
1084 if (name == "\\ttdefault")
1085 if (is_known(body, known_typewriter_fonts))
1086 h_font_typewriter = body;
1087 if (name == "\\familydefault") {
1088 string family = body;
1089 // remove leading "\"
1090 h_font_default_family = family.erase(0,1);
1093 // Add the command to the known commands
1094 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1096 // only non-lyxspecific stuff
1097 if (!in_lyx_preamble) {
1099 ss << '\\' << t.cs();
1102 ss << '{' << name << '}' << opt1 << opt2
1103 << '{' << body << "}";
1104 h_preamble << ss.str();
1106 ostream & out = in_preamble ? h_preamble : os;
1107 out << "\\" << t.cs() << "{" << name << "}"
1108 << opts << "{" << body << "}";
1113 else if (t.cs() == "documentclass") {
1114 vector<string>::iterator it;
1115 vector<string> opts = split_options(p.getArg('[', ']'));
1116 handle_opt(opts, known_fontsizes, h_paperfontsize);
1117 delete_opt(opts, known_fontsizes);
1118 // delete "pt" at the end
1119 string::size_type i = h_paperfontsize.find("pt");
1120 if (i != string::npos)
1121 h_paperfontsize.erase(i);
1122 // The documentclass options are always parsed before the options
1123 // of the babel call so that a language cannot overwrite the babel
1125 handle_opt(opts, known_languages, h_language);
1126 delete_opt(opts, known_languages);
1128 // paper orientation
1129 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1130 h_paperorientation = "landscape";
1134 if ((it = find(opts.begin(), opts.end(), "oneside"))
1139 if ((it = find(opts.begin(), opts.end(), "twoside"))
1145 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1147 h_papercolumns = "1";
1150 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1152 h_papercolumns = "2";
1156 // some size options are know to any document classes, other sizes
1157 // are handled by the \geometry command of the geometry package
1158 handle_opt(opts, known_class_paper_sizes, h_papersize);
1159 delete_opt(opts, known_class_paper_sizes);
1160 // the remaining options
1161 h_options = join(opts, ",");
1162 // FIXME This does not work for classes that have a
1163 // different name in LyX than in LaTeX
1164 h_textclass = p.getArg('{', '}');
1167 else if (t.cs() == "usepackage") {
1168 string const options = p.getArg('[', ']');
1169 string const name = p.getArg('{', '}');
1170 vector<string> vecnames;
1171 split(name, vecnames, ',');
1172 vector<string>::const_iterator it = vecnames.begin();
1173 vector<string>::const_iterator end = vecnames.end();
1174 for (; it != end; ++it)
1175 handle_package(p, trimSpaceAndEol(*it), options,
1179 else if (t.cs() == "inputencoding") {
1180 string const encoding = p.getArg('{','}');
1181 h_inputencoding = encoding;
1182 p.setEncoding(encoding);
1185 else if (t.cs() == "newenvironment") {
1186 string const name = p.getArg('{', '}');
1187 string const opt1 = p.getFullOpt();
1188 string const opt2 = p.getFullOpt();
1189 string const beg = p.verbatim_item();
1190 string const end = p.verbatim_item();
1191 if (!in_lyx_preamble) {
1192 h_preamble << "\\newenvironment{" << name
1193 << '}' << opt1 << opt2 << '{'
1194 << beg << "}{" << end << '}';
1196 add_known_environment(name, opt1, !opt2.empty(),
1197 from_utf8(beg), from_utf8(end));
1201 else if (t.cs() == "def") {
1202 string name = p.get_token().cs();
1203 while (p.next_token().cat() != catBegin)
1204 name += p.get_token().cs();
1205 if (!in_lyx_preamble)
1206 h_preamble << "\\def\\" << name << '{'
1207 << p.verbatim_item() << "}";
1210 else if (t.cs() == "newcolumntype") {
1211 string const name = p.getArg('{', '}');
1212 trimSpaceAndEol(name);
1214 string opts = p.getOpt();
1215 if (!opts.empty()) {
1216 istringstream is(string(opts, 1));
1219 special_columns[name[0]] = nargs;
1220 h_preamble << "\\newcolumntype{" << name << "}";
1222 h_preamble << "[" << nargs << "]";
1223 h_preamble << "{" << p.verbatim_item() << "}";
1226 else if (t.cs() == "setcounter") {
1227 string const name = p.getArg('{', '}');
1228 string const content = p.getArg('{', '}');
1229 if (name == "secnumdepth")
1230 h_secnumdepth = content;
1231 else if (name == "tocdepth")
1232 h_tocdepth = content;
1234 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1237 else if (t.cs() == "setlength") {
1238 string const name = p.verbatim_item();
1239 string const content = p.verbatim_item();
1240 // the paragraphs are only not indented when \parindent is set to zero
1241 if (name == "\\parindent" && content != "") {
1242 if (content[0] == '0')
1243 h_paragraph_separation = "skip";
1245 h_paragraph_indentation = translate_len(content);
1246 } else if (name == "\\parskip") {
1247 if (content == "\\smallskipamount")
1248 h_defskip = "smallskip";
1249 else if (content == "\\medskipamount")
1250 h_defskip = "medskip";
1251 else if (content == "\\bigskipamount")
1252 h_defskip = "bigskip";
1254 h_defskip = content;
1256 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1259 else if (t.cs() == "onehalfspacing")
1260 h_spacing = "onehalf";
1262 else if (t.cs() == "doublespacing")
1263 h_spacing = "double";
1265 else if (t.cs() == "setstretch")
1266 h_spacing = "other " + p.verbatim_item();
1268 else if (t.cs() == "begin") {
1269 string const name = p.getArg('{', '}');
1270 if (name == "document")
1272 h_preamble << "\\begin{" << name << "}";
1275 else if (t.cs() == "geometry") {
1276 h_use_geometry = "true";
1277 vector<string> opts = split_options(p.getArg('{', '}'));
1278 vector<string>::iterator it;
1279 // paper orientation
1280 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1281 h_paperorientation = "landscape";
1285 handle_opt(opts, known_paper_sizes, h_papersize);
1286 delete_opt(opts, known_paper_sizes);
1288 char const * const * margin = known_paper_margins;
1290 for (; *margin; ++margin) {
1292 // search for the "=" in e.g. "lmargin=2cm" to get the value
1293 for(size_t i = 0; i != opts.size(); i++) {
1294 if (opts.at(i).find(*margin) != string::npos) {
1295 string::size_type pos = opts.at(i).find("=");
1296 string value = opts.at(i).substr(pos + 1);
1297 string name = known_coded_paper_margins[k];
1298 h_margins += "\\" + name + " " + value + "\n";
1304 else if (t.cs() == "definecolor") {
1305 string const color = p.getArg('{', '}');
1306 string const space = p.getArg('{', '}');
1307 string const value = p.getArg('{', '}');
1308 if (color == "document_fontcolor" && space == "rgb") {
1309 RGBColor c(RGBColorFromLaTeX(value));
1310 h_fontcolor = X11hexname(c);
1311 } else if (color == "note_fontcolor" && space == "rgb") {
1312 RGBColor c(RGBColorFromLaTeX(value));
1313 h_notefontcolor = X11hexname(c);
1314 } else if (color == "page_backgroundcolor" && space == "rgb") {
1315 RGBColor c(RGBColorFromLaTeX(value));
1316 h_backgroundcolor = X11hexname(c);
1317 } else if (color == "shadecolor" && space == "rgb") {
1318 RGBColor c(RGBColorFromLaTeX(value));
1319 h_boxbgcolor = X11hexname(c);
1321 h_preamble << "\\definecolor{" << color
1322 << "}{" << space << "}{" << value
1327 else if (t.cs() == "jurabibsetup") {
1328 // FIXME p.getArg('{', '}') is most probably wrong (it
1329 // does not handle nested braces).
1330 // Use p.verbatim_item() instead.
1331 vector<string> jurabibsetup =
1332 split_options(p.getArg('{', '}'));
1333 // add jurabibsetup to the jurabib package options
1334 add_package("jurabib", jurabibsetup);
1335 if (!jurabibsetup.empty()) {
1336 h_preamble << "\\jurabibsetup{"
1337 << join(jurabibsetup, ",") << '}';
1341 else if (t.cs() == "hypersetup") {
1342 vector<string> hypersetup =
1343 split_options(p.verbatim_item());
1344 // add hypersetup to the hyperref package options
1345 handle_hyperref(hypersetup);
1346 if (!hypersetup.empty()) {
1347 h_preamble << "\\hypersetup{"
1348 << join(hypersetup, ",") << '}';
1352 else if (is_known(t.cs(), known_if_3arg_commands)) {
1353 // prevent misparsing of \usepackage if it is used
1354 // as an argument (see e.g. our own output of
1355 // \@ifundefined above)
1356 string const arg1 = p.verbatim_item();
1357 string const arg2 = p.verbatim_item();
1358 string const arg3 = p.verbatim_item();
1359 // test case \@ifundefined{date}{}{\date{}}
1360 if (t.cs() == "@ifundefined" && arg1 == "date" &&
1361 arg2.empty() && arg3 == "\\date{}") {
1362 h_suppress_date = "true";
1363 // older tex2lyx versions did output
1364 // \@ifundefined{definecolor}{\usepackage{color}}{}
1365 } else if (t.cs() == "@ifundefined" &&
1366 arg1 == "definecolor" &&
1367 arg2 == "\\usepackage{color}" &&
1369 if (!in_lyx_preamble)
1370 h_preamble << package_beg_sep
1373 << "\\@ifundefined{definecolor}{color}{}"
1376 //\@ifundefined{showcaptionsetup}{}{%
1377 // \PassOptionsToPackage{caption=false}{subfig}}
1378 // that LyX uses for subfloats
1379 } else if (t.cs() == "@ifundefined" &&
1380 arg1 == "showcaptionsetup" && arg2.empty()
1381 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1383 } else if (!in_lyx_preamble) {
1384 h_preamble << t.asInput()
1385 << '{' << arg1 << '}'
1386 << '{' << arg2 << '}'
1387 << '{' << arg3 << '}';
1391 else if (is_known(t.cs(), known_if_commands)) {
1392 // must not parse anything in conditional code, since
1393 // LyX would output the parsed contents unconditionally
1394 if (!in_lyx_preamble)
1395 h_preamble << t.asInput();
1396 handle_if(p, in_lyx_preamble);
1399 else if (!t.cs().empty() && !in_lyx_preamble)
1400 h_preamble << '\\' << t.cs();
1403 // remove the whitespace
1406 // Force textclass if the user wanted it
1407 if (!forceclass.empty())
1408 h_textclass = forceclass;
1409 if (noweb_mode && !prefixIs(h_textclass, "literate-"))
1410 h_textclass.insert(0, "literate-");
1411 tc.setName(h_textclass);
1413 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1416 if (h_papersides.empty()) {
1419 h_papersides = ss.str();
1424 string babel2lyx(string const & language)
1426 char const * const * where = is_known(language, known_languages);
1428 return known_coded_languages[where - known_languages];
1433 string rgbcolor2code(string const & name)
1435 char const * const * where = is_known(name, known_basic_colors);
1437 // "red", "green" etc
1438 return known_basic_color_codes[where - known_basic_colors];
1440 // "255,0,0", "0,255,0" etc
1441 RGBColor c(RGBColorFromLaTeX(name));
1442 return X11hexname(c);