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_defskip = "medskip";
407 h_fontencoding = "default";
408 h_font_roman = "default";
409 h_font_sans = "default";
410 h_font_typewriter = "default";
411 h_font_default_family = "default";
413 h_font_osf = "false";
414 h_font_sf_scale = "100";
415 h_font_tt_scale = "100";
416 h_graphics = "default";
417 h_html_be_strict = "false";
418 h_html_css_as_file = "0";
419 h_html_math_output = "0";
420 h_inputencoding = "auto";
421 h_justification = "true";
422 h_language = "english";
423 h_language_package = "none";
428 h_output_changes = "false";
429 h_papercolumns = "1";
430 h_paperfontsize = "default";
431 h_paperorientation = "portrait";
432 h_paperpagestyle = "default";
434 h_papersize = "default";
435 h_paragraph_indentation = "default";
436 h_paragraph_separation = "indent";
441 h_pdf_bookmarks = "1";
442 h_pdf_bookmarksnumbered = "0";
443 h_pdf_bookmarksopen = "0";
444 h_pdf_bookmarksopenlevel = "1";
445 h_pdf_breaklinks = "0";
446 h_pdf_pdfborder = "0";
447 h_pdf_colorlinks = "0";
448 h_pdf_backref = "section";
449 h_pdf_pdfusetitle = "1";
451 //h_pdf_quoted_options;
452 h_quotes_language = "english";
454 h_spacing = "single";
455 h_suppress_date = "false";
456 h_textclass = "article";
458 h_tracking_changes = "false";
459 h_use_bibtopic = "false";
460 h_use_indices = "false";
461 h_use_geometry = "false";
462 h_use_default_options = "false";
463 h_use_hyperref = "0";
464 h_use_refstyle = "0";
465 h_use_packages["amsmath"] = "1";
466 h_use_packages["esint"] = "1";
467 h_use_packages["mhchem"] = "0";
468 h_use_packages["mathdots"] = "0";
469 h_use_packages["mathtools"] = "0";
470 h_use_packages["undertilde"] = "0";
474 void Preamble::handle_hyperref(vector<string> & options)
476 // FIXME swallow inputencoding changes that might surround the
477 // hyperref setup if it was written by LyX
478 h_use_hyperref = "1";
479 // swallow "unicode=true", since LyX does always write that
480 vector<string>::iterator it =
481 find(options.begin(), options.end(), "unicode=true");
482 if (it != options.end())
484 it = find(options.begin(), options.end(), "pdfusetitle");
485 if (it != options.end()) {
486 h_pdf_pdfusetitle = "1";
489 string bookmarks = process_keyval_opt(options, "bookmarks");
490 if (bookmarks == "true")
491 h_pdf_bookmarks = "1";
492 else if (bookmarks == "false")
493 h_pdf_bookmarks = "0";
494 if (h_pdf_bookmarks == "1") {
495 string bookmarksnumbered =
496 process_keyval_opt(options, "bookmarksnumbered");
497 if (bookmarksnumbered == "true")
498 h_pdf_bookmarksnumbered = "1";
499 else if (bookmarksnumbered == "false")
500 h_pdf_bookmarksnumbered = "0";
501 string bookmarksopen =
502 process_keyval_opt(options, "bookmarksopen");
503 if (bookmarksopen == "true")
504 h_pdf_bookmarksopen = "1";
505 else if (bookmarksopen == "false")
506 h_pdf_bookmarksopen = "0";
507 if (h_pdf_bookmarksopen == "1") {
508 string bookmarksopenlevel =
509 process_keyval_opt(options, "bookmarksopenlevel");
510 if (!bookmarksopenlevel.empty())
511 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
514 string breaklinks = process_keyval_opt(options, "breaklinks");
515 if (breaklinks == "true")
516 h_pdf_breaklinks = "1";
517 else if (breaklinks == "false")
518 h_pdf_breaklinks = "0";
519 string pdfborder = process_keyval_opt(options, "pdfborder");
520 if (pdfborder == "{0 0 0}")
521 h_pdf_pdfborder = "1";
522 else if (pdfborder == "{0 0 1}")
523 h_pdf_pdfborder = "0";
524 string backref = process_keyval_opt(options, "backref");
525 if (!backref.empty())
526 h_pdf_backref = backref;
527 string colorlinks = process_keyval_opt(options, "colorlinks");
528 if (colorlinks == "true")
529 h_pdf_colorlinks = "1";
530 else if (colorlinks == "false")
531 h_pdf_colorlinks = "0";
532 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
533 if (!pdfpagemode.empty())
534 h_pdf_pagemode = pdfpagemode;
535 string pdftitle = process_keyval_opt(options, "pdftitle");
536 if (!pdftitle.empty()) {
537 h_pdf_title = remove_braces(pdftitle);
539 string pdfauthor = process_keyval_opt(options, "pdfauthor");
540 if (!pdfauthor.empty()) {
541 h_pdf_author = remove_braces(pdfauthor);
543 string pdfsubject = process_keyval_opt(options, "pdfsubject");
544 if (!pdfsubject.empty())
545 h_pdf_subject = remove_braces(pdfsubject);
546 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
547 if (!pdfkeywords.empty())
548 h_pdf_keywords = remove_braces(pdfkeywords);
549 if (!options.empty()) {
550 if (!h_pdf_quoted_options.empty())
551 h_pdf_quoted_options += ',';
552 h_pdf_quoted_options += join(options, ",");
558 void Preamble::handle_geometry(vector<string> & options)
560 h_use_geometry = "true";
561 vector<string>::iterator it;
563 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
564 h_paperorientation = "landscape";
568 // keyval version: "paper=letter"
569 string paper = process_keyval_opt(options, "paper");
571 h_papersize = paper + "paper";
572 // alternative version: "letterpaper"
573 handle_opt(options, known_paper_sizes, h_papersize);
574 delete_opt(options, known_paper_sizes);
576 char const * const * margin = known_paper_margins;
577 for (; *margin; ++margin) {
578 string value = process_keyval_opt(options, *margin);
579 if (!value.empty()) {
580 int k = margin - known_paper_margins;
581 string name = known_coded_paper_margins[k];
582 h_margins += '\\' + name + ' ' + value + '\n';
588 void Preamble::handle_package(Parser &p, string const & name,
589 string const & opts, bool in_lyx_preamble)
591 vector<string> options = split_options(opts);
592 add_package(name, options);
595 if (is_known(name, known_xetex_packages))
599 if (is_known(name, known_roman_fonts)) {
604 if (name == "fourier") {
605 h_font_roman = "utopia";
606 // when font uses real small capitals
607 if (opts == "expert")
611 else if (name == "mathpazo")
612 h_font_roman = "palatino";
614 else if (name == "mathptmx")
615 h_font_roman = "times";
618 if (is_known(name, known_sans_fonts)) {
622 h_font_sf_scale = scale_as_percentage(scale);
627 if (is_known(name, known_typewriter_fonts)) {
628 // fourier can be set as roman font _only_
629 // fourier as typewriter is handled in handling of \ttdefault
630 if (name != "fourier") {
631 h_font_typewriter = name;
634 h_font_tt_scale = scale_as_percentage(scale);
639 // font uses old-style figure
643 // after the detection and handling of special cases, we can remove the
644 // fonts, otherwise they would appear in the preamble, see bug #7856
645 if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
646 || is_known(name, known_typewriter_fonts))
649 else if (name == "amsmath" || name == "amssymb")
650 h_use_packages["amsmath"] = "2";
652 else if (name == "esint" || name == "mhchem" || name == "mathdots" ||
653 name == "mathtools" || name == "undertilde")
654 h_use_packages[name] = "2";
656 else if (name == "babel") {
657 h_language_package = "default";
658 // One might think we would have to do nothing if babel is loaded
659 // without any options to prevent pollution of the preamble with this
660 // babel call in every roundtrip.
661 // But the user could have defined babel-specific things afterwards. So
662 // we need to keep it in the preamble to prevent cases like bug #7861.
664 // check if more than one option was used - used later for inputenc
665 if (options.begin() != options.end() - 1)
666 one_language = false;
667 // babel takes the last language of the option of its \usepackage
668 // call as document language. If there is no such language option, the
669 // last language in the documentclass options is used.
670 handle_opt(options, known_languages, h_language);
671 // If babel is called with options, LyX puts them by default into the
672 // document class options. This works for most languages, except
673 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
674 // perhaps in future others.
675 // Therefore keep the babel call as it is as the user might have
677 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
678 delete_opt(options, known_languages);
681 h_preamble << "\\usepackage{babel}\n";
684 else if (name == "fontenc") {
685 h_fontencoding = getStringFromVector(options, ",");
686 /* We could do the following for better round trip support,
687 * but this makes the document less portable, so I skip it:
688 if (h_fontencoding == lyxrc.fontenc)
689 h_fontencoding = "global";
694 else if (name == "inputenc" || name == "luainputenc") {
695 // h_inputencoding is only set when there is not more than one
696 // inputenc option because otherwise h_inputencoding must be
697 // set to "auto" (the default encoding of the document language)
698 // Therefore check for the "," character.
699 // It is also only set when there is not more than one babel
701 if (opts.find(",") == string::npos && one_language == true)
702 h_inputencoding = opts;
703 if (!options.empty())
704 p.setEncoding(options.back());
708 else if (is_known(name, known_old_language_packages)) {
709 // known language packages from the times before babel
710 // if they are found and not also babel, they will be used as
711 // custom language package
712 h_language_package = "\\usepackage{" + name + "}";
715 else if (name == "prettyref")
716 ; // ignore this FIXME: Use the package separator mechanism instead
718 else if (name == "lyxskak") {
719 // ignore this and its options
720 const char * const o[] = {"ps", "mover", 0};
721 delete_opt(options, o);
724 else if (is_known(name, known_lyx_packages) && options.empty()) {
725 if (!in_lyx_preamble)
726 h_preamble << package_beg_sep << name
727 << package_mid_sep << "\\usepackage{"
728 << name << "}\n" << package_end_sep;
731 else if (name == "geometry")
732 handle_geometry(options);
734 else if (name == "subfig")
735 ; // ignore this FIXME: Use the package separator mechanism instead
737 else if (is_known(name, known_languages))
740 else if (name == "natbib") {
741 h_biblio_style = "plainnat";
742 h_cite_engine = "natbib_authoryear";
743 vector<string>::iterator it =
744 find(options.begin(), options.end(), "authoryear");
745 if (it != options.end())
748 it = find(options.begin(), options.end(), "numbers");
749 if (it != options.end()) {
750 h_cite_engine = "natbib_numerical";
756 else if (name == "jurabib") {
757 h_biblio_style = "jurabib";
758 h_cite_engine = "jurabib";
761 else if (name == "hyperref")
762 handle_hyperref(options);
764 else if (!in_lyx_preamble) {
766 h_preamble << "\\usepackage{" << name << "}";
768 h_preamble << "\\usepackage[" << opts << "]{"
774 // We need to do something with the options...
775 if (!options.empty())
776 cerr << "Ignoring options '" << join(options, ",")
777 << "' of package " << name << '.' << endl;
779 // remove the whitespace
784 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
787 Token t = p.get_token();
788 if (t.cat() == catEscape &&
789 is_known(t.cs(), known_if_commands))
790 handle_if(p, in_lyx_preamble);
792 if (!in_lyx_preamble)
793 h_preamble << t.asInput();
794 if (t.cat() == catEscape && t.cs() == "fi")
801 bool Preamble::writeLyXHeader(ostream & os, bool subdoc)
803 // translate from babel to LyX names
804 h_language = babel2lyx(h_language);
806 // set the quote language
807 // LyX only knows the following quotes languages:
808 // english, swedish, german, polish, french and danish
809 // (quotes for "japanese" and "chinese-traditional" are missing because
810 // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
811 // conversion list taken from
812 // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
813 // (quotes for kazakh and interlingua are unknown)
815 if (h_language == "danish")
816 h_quotes_language = "danish";
818 else if (is_known(h_language, known_french_quotes_languages))
819 h_quotes_language = "french";
821 else if (is_known(h_language, known_german_quotes_languages))
822 h_quotes_language = "german";
824 else if (is_known(h_language, known_polish_quotes_languages))
825 h_quotes_language = "polish";
827 else if (is_known(h_language, known_swedish_quotes_languages))
828 h_quotes_language = "swedish";
830 else if (is_known(h_language, known_english_quotes_languages))
831 h_quotes_language = "english";
833 if (contains(h_float_placement, "H"))
834 registerAutomaticallyLoadedPackage("float");
835 if (h_spacing != "single" && h_spacing != "default")
836 registerAutomaticallyLoadedPackage("setspace");
838 // output the LyX file settings
839 os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
840 << "\\lyxformat " << LYX_FORMAT << '\n'
841 << "\\begin_document\n"
842 << "\\begin_header\n"
843 << "\\textclass " << h_textclass << "\n";
844 string const raw = subdoc ? empty_string() : h_preamble.str();
846 os << "\\begin_preamble\n";
847 for (string::size_type i = 0; i < raw.size(); ++i) {
848 if (raw[i] == package_beg_sep) {
849 // Here follows some package loading code that
850 // must be skipped if the package is loaded
852 string::size_type j = raw.find(package_mid_sep, i);
853 if (j == string::npos)
855 string::size_type k = raw.find(package_end_sep, j);
856 if (k == string::npos)
858 string const package = raw.substr(i + 1, j - i - 1);
859 string const replacement = raw.substr(j + 1, k - j - 1);
860 if (auto_packages.find(package) == auto_packages.end())
866 os << "\n\\end_preamble\n";
868 if (!h_options.empty())
869 os << "\\options " << h_options << "\n";
870 os << "\\use_default_options " << h_use_default_options << "\n";
871 if (!used_modules.empty()) {
872 os << "\\begin_modules\n";
873 vector<string>::const_iterator const end = used_modules.end();
874 vector<string>::const_iterator it = used_modules.begin();
875 for (; it != end; it++)
877 os << "\\end_modules\n";
879 os << "\\language " << h_language << "\n"
880 << "\\language_package " << h_language_package << "\n"
881 << "\\inputencoding " << h_inputencoding << "\n"
882 << "\\fontencoding " << h_fontencoding << "\n"
883 << "\\font_roman " << h_font_roman << "\n"
884 << "\\font_sans " << h_font_sans << "\n"
885 << "\\font_typewriter " << h_font_typewriter << "\n"
886 << "\\font_default_family " << h_font_default_family << "\n"
887 << "\\font_sc " << h_font_sc << "\n"
888 << "\\font_osf " << h_font_osf << "\n"
889 << "\\font_sf_scale " << h_font_sf_scale << "\n"
890 << "\\font_tt_scale " << h_font_tt_scale << "\n"
891 << "\\graphics " << h_graphics << "\n";
892 if (!h_float_placement.empty())
893 os << "\\float_placement " << h_float_placement << "\n";
894 os << "\\paperfontsize " << h_paperfontsize << "\n"
895 << "\\spacing " << h_spacing << "\n"
896 << "\\use_hyperref " << h_use_hyperref << '\n';
897 if (h_use_hyperref == "1") {
898 if (!h_pdf_title.empty())
899 os << "\\pdf_title \"" << h_pdf_title << "\"\n";
900 if (!h_pdf_author.empty())
901 os << "\\pdf_author \"" << h_pdf_author << "\"\n";
902 if (!h_pdf_subject.empty())
903 os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
904 if (!h_pdf_keywords.empty())
905 os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
906 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
907 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
908 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
909 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
910 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
911 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
912 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
913 "\\pdf_backref " << h_pdf_backref << "\n"
914 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
915 if (!h_pdf_pagemode.empty())
916 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
917 if (!h_pdf_quoted_options.empty())
918 os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
920 os << "\\papersize " << h_papersize << "\n"
921 << "\\use_geometry " << h_use_geometry << '\n';
922 for (map<string, string>::const_iterator it = h_use_packages.begin();
923 it != h_use_packages.end(); it++)
924 os << "\\use_package " << it->first << ' ' << it->second << '\n';
925 os << "\\cite_engine " << h_cite_engine << '\n'
926 << "\\biblio_style " << h_biblio_style << "\n"
927 << "\\use_bibtopic " << h_use_bibtopic << "\n"
928 << "\\use_indices " << h_use_indices << "\n"
929 << "\\paperorientation " << h_paperorientation << '\n'
930 << "\\suppress_date " << h_suppress_date << '\n'
931 << "\\justification " << h_justification << '\n'
932 << "\\use_refstyle " << h_use_refstyle << '\n';
933 if (!h_fontcolor.empty())
934 os << "\\fontcolor " << h_fontcolor << '\n';
935 if (!h_notefontcolor.empty())
936 os << "\\notefontcolor " << h_notefontcolor << '\n';
937 if (!h_backgroundcolor.empty())
938 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
939 if (!h_boxbgcolor.empty())
940 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
942 << "\\secnumdepth " << h_secnumdepth << "\n"
943 << "\\tocdepth " << h_tocdepth << "\n"
944 << "\\paragraph_separation " << h_paragraph_separation << "\n";
945 if (h_paragraph_separation == "skip")
946 os << "\\defskip " << h_defskip << "\n";
948 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
949 os << "\\quotes_language " << h_quotes_language << "\n"
950 << "\\papercolumns " << h_papercolumns << "\n"
951 << "\\papersides " << h_papersides << "\n"
952 << "\\paperpagestyle " << h_paperpagestyle << "\n";
953 if (!h_listings_params.empty())
954 os << "\\listings_params " << h_listings_params << "\n";
955 os << "\\tracking_changes " << h_tracking_changes << "\n"
956 << "\\output_changes " << h_output_changes << "\n"
957 << "\\html_math_output " << h_html_math_output << "\n"
958 << "\\html_css_as_file " << h_html_css_as_file << "\n"
959 << "\\html_be_strict " << h_html_be_strict << "\n"
961 << "\\end_header\n\n"
967 void Preamble::parse(Parser & p, string const & forceclass,
968 TeX2LyXDocClass & tc)
970 // initialize fixed types
971 special_columns['D'] = 3;
972 bool is_full_document = false;
973 bool is_lyx_file = false;
974 bool in_lyx_preamble = false;
976 // determine whether this is a full document or a fragment for inclusion
978 Token const & t = p.get_token();
980 if (t.cat() == catEscape && t.cs() == "documentclass") {
981 is_full_document = true;
987 while (is_full_document && p.good()) {
988 Token const & t = p.get_token();
991 cerr << "t: " << t << "\n";
997 if (!in_lyx_preamble &&
998 (t.cat() == catLetter ||
999 t.cat() == catSuper ||
1000 t.cat() == catSub ||
1001 t.cat() == catOther ||
1002 t.cat() == catMath ||
1003 t.cat() == catActive ||
1004 t.cat() == catBegin ||
1005 t.cat() == catEnd ||
1006 t.cat() == catAlign ||
1007 t.cat() == catParameter))
1008 h_preamble << t.cs();
1010 else if (!in_lyx_preamble &&
1011 (t.cat() == catSpace || t.cat() == catNewline))
1012 h_preamble << t.asInput();
1014 else if (t.cat() == catComment) {
1015 static regex const islyxfile("%% LyX .* created this file");
1016 static regex const usercommands("User specified LaTeX commands");
1018 string const comment = t.asInput();
1020 // magically switch encoding default if it looks like XeLaTeX
1021 static string const magicXeLaTeX =
1022 "% This document must be compiled with XeLaTeX ";
1023 if (comment.size() > magicXeLaTeX.size()
1024 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1025 && h_inputencoding == "auto") {
1026 cerr << "XeLaTeX comment found, switching to UTF8\n";
1027 h_inputencoding = "utf8";
1030 if (regex_search(comment, sub, islyxfile)) {
1032 in_lyx_preamble = true;
1033 } else if (is_lyx_file
1034 && regex_search(comment, sub, usercommands))
1035 in_lyx_preamble = false;
1036 else if (!in_lyx_preamble)
1037 h_preamble << t.asInput();
1040 else if (t.cs() == "pagestyle")
1041 h_paperpagestyle = p.verbatim_item();
1043 else if (t.cs() == "date") {
1044 string argument = p.getArg('{', '}');
1045 if (argument.empty())
1046 h_suppress_date = "true";
1048 h_preamble << t.asInput() << '{' << argument << '}';
1051 else if (t.cs() == "color") {
1052 string const space =
1053 (p.hasOpt() ? p.getOpt() : string());
1054 string argument = p.getArg('{', '}');
1055 // check the case that a standard color is used
1056 if (space.empty() && is_known(argument, known_basic_colors)) {
1057 h_fontcolor = rgbcolor2code(argument);
1058 preamble.registerAutomaticallyLoadedPackage("color");
1059 } else if (space.empty() && argument == "document_fontcolor")
1060 preamble.registerAutomaticallyLoadedPackage("color");
1061 // check the case that LyX's document_fontcolor is defined
1062 // but not used for \color
1064 h_preamble << t.asInput();
1066 h_preamble << space;
1067 h_preamble << '{' << argument << '}';
1068 // the color might already be set because \definecolor
1069 // is parsed before this
1074 else if (t.cs() == "pagecolor") {
1075 string argument = p.getArg('{', '}');
1076 // check the case that a standard color is used
1077 if (is_known(argument, known_basic_colors)) {
1078 h_backgroundcolor = rgbcolor2code(argument);
1079 } else if (argument == "page_backgroundcolor")
1080 preamble.registerAutomaticallyLoadedPackage("color");
1081 // check the case that LyX's page_backgroundcolor is defined
1082 // but not used for \pagecolor
1084 h_preamble << t.asInput() << '{' << argument << '}';
1085 // the color might already be set because \definecolor
1086 // is parsed before this
1087 h_backgroundcolor = "";
1091 else if (t.cs() == "makeatletter") {
1092 // LyX takes care of this
1093 p.setCatCode('@', catLetter);
1096 else if (t.cs() == "makeatother") {
1097 // LyX takes care of this
1098 p.setCatCode('@', catOther);
1101 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1102 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1103 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1104 || t.cs() == "DeclareRobustCommand"
1105 || t.cs() == "DeclareRobustCommandx"
1106 || t.cs() == "ProvideTextCommandDefault"
1107 || t.cs() == "DeclareMathAccent") {
1109 if (p.next_token().character() == '*') {
1113 string const name = p.verbatim_item();
1114 string const opt1 = p.getFullOpt();
1115 string const opt2 = p.getFullOpt();
1116 string const body = p.verbatim_item();
1118 if (name == "\\rmdefault")
1119 if (is_known(body, known_roman_fonts))
1120 h_font_roman = body;
1121 if (name == "\\sfdefault")
1122 if (is_known(body, known_sans_fonts))
1124 if (name == "\\ttdefault")
1125 if (is_known(body, known_typewriter_fonts))
1126 h_font_typewriter = body;
1127 if (name == "\\familydefault") {
1128 string family = body;
1129 // remove leading "\"
1130 h_font_default_family = family.erase(0,1);
1133 // remove the lyxdot definition that is re-added by LyX
1135 if (name == "\\lyxdot")
1136 in_lyx_preamble = true;
1138 // Add the command to the known commands
1139 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1141 // only non-lyxspecific stuff
1142 if (!in_lyx_preamble) {
1144 ss << '\\' << t.cs();
1147 ss << '{' << name << '}' << opt1 << opt2
1148 << '{' << body << "}";
1149 h_preamble << ss.str();
1151 ostream & out = in_preamble ? h_preamble : os;
1152 out << "\\" << t.cs() << "{" << name << "}"
1153 << opts << "{" << body << "}";
1158 else if (t.cs() == "documentclass") {
1159 vector<string>::iterator it;
1160 vector<string> opts = split_options(p.getArg('[', ']'));
1161 handle_opt(opts, known_fontsizes, h_paperfontsize);
1162 delete_opt(opts, known_fontsizes);
1163 // delete "pt" at the end
1164 string::size_type i = h_paperfontsize.find("pt");
1165 if (i != string::npos)
1166 h_paperfontsize.erase(i);
1167 // The documentclass options are always parsed before the options
1168 // of the babel call so that a language cannot overwrite the babel
1170 handle_opt(opts, known_languages, h_language);
1171 delete_opt(opts, known_languages);
1173 // paper orientation
1174 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1175 h_paperorientation = "landscape";
1179 if ((it = find(opts.begin(), opts.end(), "oneside"))
1184 if ((it = find(opts.begin(), opts.end(), "twoside"))
1190 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1192 h_papercolumns = "1";
1195 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1197 h_papercolumns = "2";
1201 // some size options are known to any document classes, other sizes
1202 // are handled by the \geometry command of the geometry package
1203 handle_opt(opts, known_class_paper_sizes, h_papersize);
1204 delete_opt(opts, known_class_paper_sizes);
1205 // the remaining options
1206 h_options = join(opts, ",");
1207 // FIXME This does not work for classes that have a
1208 // different name in LyX than in LaTeX
1209 h_textclass = p.getArg('{', '}');
1212 else if (t.cs() == "usepackage") {
1213 string const options = p.getArg('[', ']');
1214 string const name = p.getArg('{', '}');
1215 vector<string> vecnames;
1216 split(name, vecnames, ',');
1217 vector<string>::const_iterator it = vecnames.begin();
1218 vector<string>::const_iterator end = vecnames.end();
1219 for (; it != end; ++it)
1220 handle_package(p, trimSpaceAndEol(*it), options,
1224 else if (t.cs() == "inputencoding") {
1225 string const encoding = p.getArg('{','}');
1226 h_inputencoding = encoding;
1227 p.setEncoding(encoding);
1230 else if (t.cs() == "newenvironment") {
1231 string const name = p.getArg('{', '}');
1232 string const opt1 = p.getFullOpt();
1233 string const opt2 = p.getFullOpt();
1234 string const beg = p.verbatim_item();
1235 string const end = p.verbatim_item();
1236 if (!in_lyx_preamble) {
1237 h_preamble << "\\newenvironment{" << name
1238 << '}' << opt1 << opt2 << '{'
1239 << beg << "}{" << end << '}';
1241 add_known_environment(name, opt1, !opt2.empty(),
1242 from_utf8(beg), from_utf8(end));
1246 else if (t.cs() == "def") {
1247 string name = p.get_token().cs();
1248 while (p.next_token().cat() != catBegin)
1249 name += p.get_token().cs();
1250 if (!in_lyx_preamble)
1251 h_preamble << "\\def\\" << name << '{'
1252 << p.verbatim_item() << "}";
1255 else if (t.cs() == "newcolumntype") {
1256 string const name = p.getArg('{', '}');
1257 trimSpaceAndEol(name);
1259 string opts = p.getOpt();
1260 if (!opts.empty()) {
1261 istringstream is(string(opts, 1));
1264 special_columns[name[0]] = nargs;
1265 h_preamble << "\\newcolumntype{" << name << "}";
1267 h_preamble << "[" << nargs << "]";
1268 h_preamble << "{" << p.verbatim_item() << "}";
1271 else if (t.cs() == "setcounter") {
1272 string const name = p.getArg('{', '}');
1273 string const content = p.getArg('{', '}');
1274 if (name == "secnumdepth")
1275 h_secnumdepth = content;
1276 else if (name == "tocdepth")
1277 h_tocdepth = content;
1279 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1282 else if (t.cs() == "setlength") {
1283 string const name = p.verbatim_item();
1284 string const content = p.verbatim_item();
1285 // the paragraphs are only not indented when \parindent is set to zero
1286 if (name == "\\parindent" && content != "") {
1287 if (content[0] == '0')
1288 h_paragraph_separation = "skip";
1290 h_paragraph_indentation = translate_len(content);
1291 } else if (name == "\\parskip") {
1292 if (content == "\\smallskipamount")
1293 h_defskip = "smallskip";
1294 else if (content == "\\medskipamount")
1295 h_defskip = "medskip";
1296 else if (content == "\\bigskipamount")
1297 h_defskip = "bigskip";
1299 h_defskip = content;
1301 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1304 else if (t.cs() == "onehalfspacing")
1305 h_spacing = "onehalf";
1307 else if (t.cs() == "doublespacing")
1308 h_spacing = "double";
1310 else if (t.cs() == "setstretch")
1311 h_spacing = "other " + p.verbatim_item();
1313 else if (t.cs() == "begin") {
1314 string const name = p.getArg('{', '}');
1315 if (name == "document")
1317 h_preamble << "\\begin{" << name << "}";
1320 else if (t.cs() == "geometry") {
1321 vector<string> opts = split_options(p.getArg('{', '}'));
1322 handle_geometry(opts);
1325 else if (t.cs() == "definecolor") {
1326 string const color = p.getArg('{', '}');
1327 string const space = p.getArg('{', '}');
1328 string const value = p.getArg('{', '}');
1329 if (color == "document_fontcolor" && space == "rgb") {
1330 RGBColor c(RGBColorFromLaTeX(value));
1331 h_fontcolor = X11hexname(c);
1332 } else if (color == "note_fontcolor" && space == "rgb") {
1333 RGBColor c(RGBColorFromLaTeX(value));
1334 h_notefontcolor = X11hexname(c);
1335 } else if (color == "page_backgroundcolor" && space == "rgb") {
1336 RGBColor c(RGBColorFromLaTeX(value));
1337 h_backgroundcolor = X11hexname(c);
1338 } else if (color == "shadecolor" && space == "rgb") {
1339 RGBColor c(RGBColorFromLaTeX(value));
1340 h_boxbgcolor = X11hexname(c);
1342 h_preamble << "\\definecolor{" << color
1343 << "}{" << space << "}{" << value
1348 else if (t.cs() == "bibliographystyle")
1349 h_biblio_style = p.verbatim_item();
1351 else if (t.cs() == "jurabibsetup") {
1352 // FIXME p.getArg('{', '}') is most probably wrong (it
1353 // does not handle nested braces).
1354 // Use p.verbatim_item() instead.
1355 vector<string> jurabibsetup =
1356 split_options(p.getArg('{', '}'));
1357 // add jurabibsetup to the jurabib package options
1358 add_package("jurabib", jurabibsetup);
1359 if (!jurabibsetup.empty()) {
1360 h_preamble << "\\jurabibsetup{"
1361 << join(jurabibsetup, ",") << '}';
1365 else if (t.cs() == "hypersetup") {
1366 vector<string> hypersetup =
1367 split_options(p.verbatim_item());
1368 // add hypersetup to the hyperref package options
1369 handle_hyperref(hypersetup);
1370 if (!hypersetup.empty()) {
1371 h_preamble << "\\hypersetup{"
1372 << join(hypersetup, ",") << '}';
1376 else if (is_known(t.cs(), known_if_3arg_commands)) {
1377 // prevent misparsing of \usepackage if it is used
1378 // as an argument (see e.g. our own output of
1379 // \@ifundefined above)
1380 string const arg1 = p.verbatim_item();
1381 string const arg2 = p.verbatim_item();
1382 string const arg3 = p.verbatim_item();
1383 // test case \@ifundefined{date}{}{\date{}}
1384 if (t.cs() == "@ifundefined" && arg1 == "date" &&
1385 arg2.empty() && arg3 == "\\date{}") {
1386 h_suppress_date = "true";
1387 // older tex2lyx versions did output
1388 // \@ifundefined{definecolor}{\usepackage{color}}{}
1389 } else if (t.cs() == "@ifundefined" &&
1390 arg1 == "definecolor" &&
1391 arg2 == "\\usepackage{color}" &&
1393 if (!in_lyx_preamble)
1394 h_preamble << package_beg_sep
1397 << "\\@ifundefined{definecolor}{color}{}"
1400 //\@ifundefined{showcaptionsetup}{}{%
1401 // \PassOptionsToPackage{caption=false}{subfig}}
1402 // that LyX uses for subfloats
1403 } else if (t.cs() == "@ifundefined" &&
1404 arg1 == "showcaptionsetup" && arg2.empty()
1405 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1407 } else if (!in_lyx_preamble) {
1408 h_preamble << t.asInput()
1409 << '{' << arg1 << '}'
1410 << '{' << arg2 << '}'
1411 << '{' << arg3 << '}';
1415 else if (is_known(t.cs(), known_if_commands)) {
1416 // must not parse anything in conditional code, since
1417 // LyX would output the parsed contents unconditionally
1418 if (!in_lyx_preamble)
1419 h_preamble << t.asInput();
1420 handle_if(p, in_lyx_preamble);
1423 else if (!t.cs().empty() && !in_lyx_preamble)
1424 h_preamble << '\\' << t.cs();
1427 // remove the whitespace
1430 // Force textclass if the user wanted it
1431 if (!forceclass.empty())
1432 h_textclass = forceclass;
1433 if (noweb_mode && !prefixIs(h_textclass, "literate-"))
1434 h_textclass.insert(0, "literate-");
1435 tc.setName(h_textclass);
1437 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1440 if (h_papersides.empty()) {
1443 h_papersides = ss.str();
1448 string babel2lyx(string const & language)
1450 char const * const * where = is_known(language, known_languages);
1452 return known_coded_languages[where - known_languages];
1457 string rgbcolor2code(string const & name)
1459 char const * const * where = is_known(name, known_basic_colors);
1461 // "red", "green" etc
1462 return known_basic_color_codes[where - known_basic_colors];
1464 // "255,0,0", "0,255,0" etc
1465 RGBColor c(RGBColorFromLaTeX(name));
1466 return X11hexname(c);