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";
463 h_use_default_options = "false";
465 h_use_hyperref = "0";
467 h_use_mathdots = "0";
468 h_use_refstyle = "0";
469 h_use_undertilde = "0";
473 void Preamble::handle_hyperref(vector<string> & options)
475 // FIXME swallow inputencoding changes that might surround the
476 // hyperref setup if it was written by LyX
477 h_use_hyperref = "1";
478 // swallow "unicode=true", since LyX does always write that
479 vector<string>::iterator it =
480 find(options.begin(), options.end(), "unicode=true");
481 if (it != options.end())
483 it = find(options.begin(), options.end(), "pdfusetitle");
484 if (it != options.end()) {
485 h_pdf_pdfusetitle = "1";
488 string bookmarks = process_keyval_opt(options, "bookmarks");
489 if (bookmarks == "true")
490 h_pdf_bookmarks = "1";
491 else if (bookmarks == "false")
492 h_pdf_bookmarks = "0";
493 if (h_pdf_bookmarks == "1") {
494 string bookmarksnumbered =
495 process_keyval_opt(options, "bookmarksnumbered");
496 if (bookmarksnumbered == "true")
497 h_pdf_bookmarksnumbered = "1";
498 else if (bookmarksnumbered == "false")
499 h_pdf_bookmarksnumbered = "0";
500 string bookmarksopen =
501 process_keyval_opt(options, "bookmarksopen");
502 if (bookmarksopen == "true")
503 h_pdf_bookmarksopen = "1";
504 else if (bookmarksopen == "false")
505 h_pdf_bookmarksopen = "0";
506 if (h_pdf_bookmarksopen == "1") {
507 string bookmarksopenlevel =
508 process_keyval_opt(options, "bookmarksopenlevel");
509 if (!bookmarksopenlevel.empty())
510 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
513 string breaklinks = process_keyval_opt(options, "breaklinks");
514 if (breaklinks == "true")
515 h_pdf_breaklinks = "1";
516 else if (breaklinks == "false")
517 h_pdf_breaklinks = "0";
518 string pdfborder = process_keyval_opt(options, "pdfborder");
519 if (pdfborder == "{0 0 0}")
520 h_pdf_pdfborder = "1";
521 else if (pdfborder == "{0 0 1}")
522 h_pdf_pdfborder = "0";
523 string backref = process_keyval_opt(options, "backref");
524 if (!backref.empty())
525 h_pdf_backref = backref;
526 string colorlinks = process_keyval_opt(options, "colorlinks");
527 if (colorlinks == "true")
528 h_pdf_colorlinks = "1";
529 else if (colorlinks == "false")
530 h_pdf_colorlinks = "0";
531 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
532 if (!pdfpagemode.empty())
533 h_pdf_pagemode = pdfpagemode;
534 string pdftitle = process_keyval_opt(options, "pdftitle");
535 if (!pdftitle.empty()) {
536 h_pdf_title = remove_braces(pdftitle);
538 string pdfauthor = process_keyval_opt(options, "pdfauthor");
539 if (!pdfauthor.empty()) {
540 h_pdf_author = remove_braces(pdfauthor);
542 string pdfsubject = process_keyval_opt(options, "pdfsubject");
543 if (!pdfsubject.empty())
544 h_pdf_subject = remove_braces(pdfsubject);
545 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
546 if (!pdfkeywords.empty())
547 h_pdf_keywords = remove_braces(pdfkeywords);
548 if (!options.empty()) {
549 if (!h_pdf_quoted_options.empty())
550 h_pdf_quoted_options += ',';
551 h_pdf_quoted_options += join(options, ",");
557 void Preamble::handle_geometry(vector<string> & options)
559 h_use_geometry = "true";
560 vector<string>::iterator it;
562 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
563 h_paperorientation = "landscape";
567 // keyval version: "paper=letter"
568 string paper = process_keyval_opt(options, "paper");
570 h_papersize = paper + "paper";
571 // alternative version: "letterpaper"
572 handle_opt(options, known_paper_sizes, h_papersize);
573 delete_opt(options, known_paper_sizes);
575 char const * const * margin = known_paper_margins;
576 for (; *margin; ++margin) {
577 string value = process_keyval_opt(options, *margin);
578 if (!value.empty()) {
579 int k = margin - known_paper_margins;
580 string name = known_coded_paper_margins[k];
581 h_margins += '\\' + name + ' ' + value + '\n';
587 void Preamble::handle_package(Parser &p, string const & name,
588 string const & opts, bool in_lyx_preamble)
590 vector<string> options = split_options(opts);
591 add_package(name, options);
594 if (is_known(name, known_xetex_packages))
598 if (is_known(name, known_roman_fonts)) {
603 if (name == "fourier") {
604 h_font_roman = "utopia";
605 // when font uses real small capitals
606 if (opts == "expert")
610 else if (name == "mathpazo")
611 h_font_roman = "palatino";
613 else if (name == "mathptmx")
614 h_font_roman = "times";
617 if (is_known(name, known_sans_fonts)) {
621 h_font_sf_scale = scale_as_percentage(scale);
626 if (is_known(name, known_typewriter_fonts)) {
627 // fourier can be set as roman font _only_
628 // fourier as typewriter is handled in handling of \ttdefault
629 if (name != "fourier") {
630 h_font_typewriter = name;
633 h_font_tt_scale = scale_as_percentage(scale);
638 // font uses old-style figure
642 // after the detection and handling of special cases, we can remove the
643 // fonts, otherwise they would appear in the preamble, see bug #7856
644 if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
645 || is_known(name, known_typewriter_fonts))
648 else if (name == "amsmath" || name == "amssymb")
651 else if (name == "esint")
654 else if (name == "mhchem")
657 else if (name == "mathdots")
658 h_use_mathdots = "2";
660 else if (name == "undertilde")
661 h_use_undertilde = "2";
663 else if (name == "babel") {
664 h_language_package = "default";
665 // One might think we would have to do nothing if babel is loaded
666 // without any options to prevent pollution of the preamble with this
667 // babel call in every roundtrip.
668 // But the user could have defined babel-specific things afterwards. So
669 // we need to keep it in the preamble to prevent cases like bug #7861.
671 // check if more than one option was used - used later for inputenc
672 if (options.begin() != options.end() - 1)
673 one_language = false;
674 // babel takes the last language of the option of its \usepackage
675 // call as document language. If there is no such language option, the
676 // last language in the documentclass options is used.
677 handle_opt(options, known_languages, h_language);
678 // If babel is called with options, LyX puts them by default into the
679 // document class options. This works for most languages, except
680 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
681 // perhaps in future others.
682 // Therefore keep the babel call as it is as the user might have
684 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
685 delete_opt(options, known_languages);
688 h_preamble << "\\usepackage{babel}\n";
691 else if (name == "fontenc") {
692 h_fontencoding = getStringFromVector(options, ",");
693 /* We could do the following for better round trip support,
694 * but this makes the document less portable, so I skip it:
695 if (h_fontencoding == lyxrc.fontenc)
696 h_fontencoding = "global";
701 else if (name == "inputenc" || name == "luainputenc") {
702 // h_inputencoding is only set when there is not more than one
703 // inputenc option because otherwise h_inputencoding must be
704 // set to "auto" (the default encoding of the document language)
705 // Therefore check for the "," character.
706 // It is also only set when there is not more than one babel
708 if (opts.find(",") == string::npos && one_language == true)
709 h_inputencoding = opts;
710 if (!options.empty())
711 p.setEncoding(options.back());
715 else if (is_known(name, known_old_language_packages)) {
716 // known language packages from the times before babel
717 // if they are found and not also babel, they will be used as
718 // custom language package
719 h_language_package = "\\usepackage{" + name + "}";
722 else if (name == "prettyref")
723 ; // ignore this FIXME: Use the package separator mechanism instead
725 else if (name == "lyxskak") {
726 // ignore this and its options
727 const char * const o[] = {"ps", "mover", 0};
728 delete_opt(options, o);
731 else if (is_known(name, known_lyx_packages) && options.empty()) {
732 if (!in_lyx_preamble)
733 h_preamble << package_beg_sep << name
734 << package_mid_sep << "\\usepackage{"
735 << name << "}\n" << package_end_sep;
738 else if (name == "geometry")
739 handle_geometry(options);
741 else if (name == "subfig")
742 ; // ignore this FIXME: Use the package separator mechanism instead
744 else if (is_known(name, known_languages))
747 else if (name == "natbib") {
748 h_biblio_style = "plainnat";
749 h_cite_engine = "natbib_authoryear";
750 vector<string>::iterator it =
751 find(options.begin(), options.end(), "authoryear");
752 if (it != options.end())
755 it = find(options.begin(), options.end(), "numbers");
756 if (it != options.end()) {
757 h_cite_engine = "natbib_numerical";
763 else if (name == "jurabib") {
764 h_biblio_style = "jurabib";
765 h_cite_engine = "jurabib";
768 else if (name == "hyperref")
769 handle_hyperref(options);
771 else if (!in_lyx_preamble) {
773 h_preamble << "\\usepackage{" << name << "}";
775 h_preamble << "\\usepackage[" << opts << "]{"
781 // We need to do something with the options...
782 if (!options.empty())
783 cerr << "Ignoring options '" << join(options, ",")
784 << "' of package " << name << '.' << endl;
786 // remove the whitespace
791 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
794 Token t = p.get_token();
795 if (t.cat() == catEscape &&
796 is_known(t.cs(), known_if_commands))
797 handle_if(p, in_lyx_preamble);
799 if (!in_lyx_preamble)
800 h_preamble << t.asInput();
801 if (t.cat() == catEscape && t.cs() == "fi")
808 bool Preamble::writeLyXHeader(ostream & os, bool subdoc)
810 // translate from babel to LyX names
811 h_language = babel2lyx(h_language);
813 // set the quote language
814 // LyX only knows the following quotes languages:
815 // english, swedish, german, polish, french and danish
816 // (quotes for "japanese" and "chinese-traditional" are missing because
817 // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
818 // conversion list taken from
819 // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
820 // (quotes for kazakh and interlingua are unknown)
822 if (h_language == "danish")
823 h_quotes_language = "danish";
825 else if (is_known(h_language, known_french_quotes_languages))
826 h_quotes_language = "french";
828 else if (is_known(h_language, known_german_quotes_languages))
829 h_quotes_language = "german";
831 else if (is_known(h_language, known_polish_quotes_languages))
832 h_quotes_language = "polish";
834 else if (is_known(h_language, known_swedish_quotes_languages))
835 h_quotes_language = "swedish";
837 else if (is_known(h_language, known_english_quotes_languages))
838 h_quotes_language = "english";
840 if (contains(h_float_placement, "H"))
841 registerAutomaticallyLoadedPackage("float");
842 if (h_spacing != "single" && h_spacing != "default")
843 registerAutomaticallyLoadedPackage("setspace");
845 // output the LyX file settings
846 os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
847 << "\\lyxformat " << LYX_FORMAT << '\n'
848 << "\\begin_document\n"
849 << "\\begin_header\n"
850 << "\\textclass " << h_textclass << "\n";
851 string const raw = subdoc ? empty_string() : h_preamble.str();
853 os << "\\begin_preamble\n";
854 for (string::size_type i = 0; i < raw.size(); ++i) {
855 if (raw[i] == package_beg_sep) {
856 // Here follows some package loading code that
857 // must be skipped if the package is loaded
859 string::size_type j = raw.find(package_mid_sep, i);
860 if (j == string::npos)
862 string::size_type k = raw.find(package_end_sep, j);
863 if (k == string::npos)
865 string const package = raw.substr(i + 1, j - i - 1);
866 string const replacement = raw.substr(j + 1, k - j - 1);
867 if (auto_packages.find(package) == auto_packages.end())
873 os << "\n\\end_preamble\n";
875 if (!h_options.empty())
876 os << "\\options " << h_options << "\n";
877 os << "\\use_default_options " << h_use_default_options << "\n";
878 if (!used_modules.empty()) {
879 os << "\\begin_modules\n";
880 vector<string>::const_iterator const end = used_modules.end();
881 vector<string>::const_iterator it = used_modules.begin();
882 for (; it != end; it++)
884 os << "\\end_modules\n";
886 os << "\\language " << h_language << "\n"
887 << "\\language_package " << h_language_package << "\n"
888 << "\\inputencoding " << h_inputencoding << "\n"
889 << "\\fontencoding " << h_fontencoding << "\n"
890 << "\\font_roman " << h_font_roman << "\n"
891 << "\\font_sans " << h_font_sans << "\n"
892 << "\\font_typewriter " << h_font_typewriter << "\n"
893 << "\\font_default_family " << h_font_default_family << "\n"
894 << "\\font_sc " << h_font_sc << "\n"
895 << "\\font_osf " << h_font_osf << "\n"
896 << "\\font_sf_scale " << h_font_sf_scale << "\n"
897 << "\\font_tt_scale " << h_font_tt_scale << "\n"
898 << "\\graphics " << h_graphics << "\n";
899 if (!h_float_placement.empty())
900 os << "\\float_placement " << h_float_placement << "\n";
901 os << "\\paperfontsize " << h_paperfontsize << "\n"
902 << "\\spacing " << h_spacing << "\n"
903 << "\\use_hyperref " << h_use_hyperref << '\n';
904 if (h_use_hyperref == "1") {
905 if (!h_pdf_title.empty())
906 os << "\\pdf_title \"" << h_pdf_title << "\"\n";
907 if (!h_pdf_author.empty())
908 os << "\\pdf_author \"" << h_pdf_author << "\"\n";
909 if (!h_pdf_subject.empty())
910 os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
911 if (!h_pdf_keywords.empty())
912 os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
913 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
914 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
915 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
916 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
917 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
918 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
919 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
920 "\\pdf_backref " << h_pdf_backref << "\n"
921 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
922 if (!h_pdf_pagemode.empty())
923 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
924 if (!h_pdf_quoted_options.empty())
925 os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
927 os << "\\papersize " << h_papersize << "\n"
928 << "\\use_geometry " << h_use_geometry << "\n"
929 << "\\use_amsmath " << h_use_amsmath << "\n"
930 << "\\use_esint " << h_use_esint << "\n"
931 << "\\use_mhchem " << h_use_mhchem << "\n"
932 << "\\use_mathdots " << h_use_mathdots << "\n"
933 << "\\use_undertilde " << h_use_undertilde << "\n"
934 << "\\cite_engine " << h_cite_engine << "\n"
935 << "\\biblio_style " << h_biblio_style << "\n"
936 << "\\use_bibtopic " << h_use_bibtopic << "\n"
937 << "\\use_indices " << h_use_indices << "\n"
938 << "\\paperorientation " << h_paperorientation << '\n'
939 << "\\suppress_date " << h_suppress_date << '\n'
940 << "\\justification " << h_justification << '\n'
941 << "\\use_refstyle " << h_use_refstyle << '\n';
942 if (!h_fontcolor.empty())
943 os << "\\fontcolor " << h_fontcolor << '\n';
944 if (!h_notefontcolor.empty())
945 os << "\\notefontcolor " << h_notefontcolor << '\n';
946 if (!h_backgroundcolor.empty())
947 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
948 if (!h_boxbgcolor.empty())
949 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
951 << "\\secnumdepth " << h_secnumdepth << "\n"
952 << "\\tocdepth " << h_tocdepth << "\n"
953 << "\\paragraph_separation " << h_paragraph_separation << "\n";
954 if (h_paragraph_separation == "skip")
955 os << "\\defskip " << h_defskip << "\n";
957 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
958 os << "\\quotes_language " << h_quotes_language << "\n"
959 << "\\papercolumns " << h_papercolumns << "\n"
960 << "\\papersides " << h_papersides << "\n"
961 << "\\paperpagestyle " << h_paperpagestyle << "\n";
962 if (!h_listings_params.empty())
963 os << "\\listings_params " << h_listings_params << "\n";
964 os << "\\tracking_changes " << h_tracking_changes << "\n"
965 << "\\output_changes " << h_output_changes << "\n"
966 << "\\html_math_output " << h_html_math_output << "\n"
967 << "\\html_css_as_file " << h_html_css_as_file << "\n"
968 << "\\html_be_strict " << h_html_be_strict << "\n"
970 << "\\end_header\n\n"
976 void Preamble::parse(Parser & p, string const & forceclass,
977 TeX2LyXDocClass & tc)
979 // initialize fixed types
980 special_columns['D'] = 3;
981 bool is_full_document = false;
982 bool is_lyx_file = false;
983 bool in_lyx_preamble = false;
985 // determine whether this is a full document or a fragment for inclusion
987 Token const & t = p.get_token();
989 if (t.cat() == catEscape && t.cs() == "documentclass") {
990 is_full_document = true;
996 while (is_full_document && p.good()) {
997 Token const & t = p.get_token();
1000 cerr << "t: " << t << "\n";
1006 if (!in_lyx_preamble &&
1007 (t.cat() == catLetter ||
1008 t.cat() == catSuper ||
1009 t.cat() == catSub ||
1010 t.cat() == catOther ||
1011 t.cat() == catMath ||
1012 t.cat() == catActive ||
1013 t.cat() == catBegin ||
1014 t.cat() == catEnd ||
1015 t.cat() == catAlign ||
1016 t.cat() == catParameter))
1017 h_preamble << t.cs();
1019 else if (!in_lyx_preamble &&
1020 (t.cat() == catSpace || t.cat() == catNewline))
1021 h_preamble << t.asInput();
1023 else if (t.cat() == catComment) {
1024 static regex const islyxfile("%% LyX .* created this file");
1025 static regex const usercommands("User specified LaTeX commands");
1027 string const comment = t.asInput();
1029 // magically switch encoding default if it looks like XeLaTeX
1030 static string const magicXeLaTeX =
1031 "% This document must be compiled with XeLaTeX ";
1032 if (comment.size() > magicXeLaTeX.size()
1033 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1034 && h_inputencoding == "auto") {
1035 cerr << "XeLaTeX comment found, switching to UTF8\n";
1036 h_inputencoding = "utf8";
1039 if (regex_search(comment, sub, islyxfile)) {
1041 in_lyx_preamble = true;
1042 } else if (is_lyx_file
1043 && regex_search(comment, sub, usercommands))
1044 in_lyx_preamble = false;
1045 else if (!in_lyx_preamble)
1046 h_preamble << t.asInput();
1049 else if (t.cs() == "pagestyle")
1050 h_paperpagestyle = p.verbatim_item();
1052 else if (t.cs() == "date") {
1053 string argument = p.getArg('{', '}');
1054 if (argument.empty())
1055 h_suppress_date = "true";
1057 h_preamble << t.asInput() << '{' << argument << '}';
1060 else if (t.cs() == "color") {
1061 string const space =
1062 (p.hasOpt() ? p.getOpt() : string());
1063 string argument = p.getArg('{', '}');
1064 // check the case that a standard color is used
1065 if (space.empty() && is_known(argument, known_basic_colors)) {
1066 h_fontcolor = rgbcolor2code(argument);
1067 preamble.registerAutomaticallyLoadedPackage("color");
1068 } else if (space.empty() && argument == "document_fontcolor")
1069 preamble.registerAutomaticallyLoadedPackage("color");
1070 // check the case that LyX's document_fontcolor is defined
1071 // but not used for \color
1073 h_preamble << t.asInput();
1075 h_preamble << space;
1076 h_preamble << '{' << argument << '}';
1077 // the color might already be set because \definecolor
1078 // is parsed before this
1083 else if (t.cs() == "pagecolor") {
1084 string argument = p.getArg('{', '}');
1085 // check the case that a standard color is used
1086 if (is_known(argument, known_basic_colors)) {
1087 h_backgroundcolor = rgbcolor2code(argument);
1088 } else if (argument == "page_backgroundcolor")
1089 preamble.registerAutomaticallyLoadedPackage("color");
1090 // check the case that LyX's page_backgroundcolor is defined
1091 // but not used for \pagecolor
1093 h_preamble << t.asInput() << '{' << argument << '}';
1094 // the color might already be set because \definecolor
1095 // is parsed before this
1096 h_backgroundcolor = "";
1100 else if (t.cs() == "makeatletter") {
1101 // LyX takes care of this
1102 p.setCatCode('@', catLetter);
1105 else if (t.cs() == "makeatother") {
1106 // LyX takes care of this
1107 p.setCatCode('@', catOther);
1110 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1111 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1112 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1113 || t.cs() == "DeclareRobustCommand"
1114 || t.cs() == "DeclareRobustCommandx"
1115 || t.cs() == "ProvideTextCommandDefault"
1116 || t.cs() == "DeclareMathAccent") {
1118 if (p.next_token().character() == '*') {
1122 string const name = p.verbatim_item();
1123 string const opt1 = p.getFullOpt();
1124 string const opt2 = p.getFullOpt();
1125 string const body = p.verbatim_item();
1127 if (name == "\\rmdefault")
1128 if (is_known(body, known_roman_fonts))
1129 h_font_roman = body;
1130 if (name == "\\sfdefault")
1131 if (is_known(body, known_sans_fonts))
1133 if (name == "\\ttdefault")
1134 if (is_known(body, known_typewriter_fonts))
1135 h_font_typewriter = body;
1136 if (name == "\\familydefault") {
1137 string family = body;
1138 // remove leading "\"
1139 h_font_default_family = family.erase(0,1);
1142 // remove the lyxdot definition that is re-added by LyX
1144 if (name == "\\lyxdot")
1145 in_lyx_preamble = true;
1147 // Add the command to the known commands
1148 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1150 // only non-lyxspecific stuff
1151 if (!in_lyx_preamble) {
1153 ss << '\\' << t.cs();
1156 ss << '{' << name << '}' << opt1 << opt2
1157 << '{' << body << "}";
1158 h_preamble << ss.str();
1160 ostream & out = in_preamble ? h_preamble : os;
1161 out << "\\" << t.cs() << "{" << name << "}"
1162 << opts << "{" << body << "}";
1167 else if (t.cs() == "documentclass") {
1168 vector<string>::iterator it;
1169 vector<string> opts = split_options(p.getArg('[', ']'));
1170 handle_opt(opts, known_fontsizes, h_paperfontsize);
1171 delete_opt(opts, known_fontsizes);
1172 // delete "pt" at the end
1173 string::size_type i = h_paperfontsize.find("pt");
1174 if (i != string::npos)
1175 h_paperfontsize.erase(i);
1176 // The documentclass options are always parsed before the options
1177 // of the babel call so that a language cannot overwrite the babel
1179 handle_opt(opts, known_languages, h_language);
1180 delete_opt(opts, known_languages);
1182 // paper orientation
1183 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1184 h_paperorientation = "landscape";
1188 if ((it = find(opts.begin(), opts.end(), "oneside"))
1193 if ((it = find(opts.begin(), opts.end(), "twoside"))
1199 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1201 h_papercolumns = "1";
1204 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1206 h_papercolumns = "2";
1210 // some size options are known to any document classes, other sizes
1211 // are handled by the \geometry command of the geometry package
1212 handle_opt(opts, known_class_paper_sizes, h_papersize);
1213 delete_opt(opts, known_class_paper_sizes);
1214 // the remaining options
1215 h_options = join(opts, ",");
1216 // FIXME This does not work for classes that have a
1217 // different name in LyX than in LaTeX
1218 h_textclass = p.getArg('{', '}');
1221 else if (t.cs() == "usepackage") {
1222 string const options = p.getArg('[', ']');
1223 string const name = p.getArg('{', '}');
1224 vector<string> vecnames;
1225 split(name, vecnames, ',');
1226 vector<string>::const_iterator it = vecnames.begin();
1227 vector<string>::const_iterator end = vecnames.end();
1228 for (; it != end; ++it)
1229 handle_package(p, trimSpaceAndEol(*it), options,
1233 else if (t.cs() == "inputencoding") {
1234 string const encoding = p.getArg('{','}');
1235 h_inputencoding = encoding;
1236 p.setEncoding(encoding);
1239 else if (t.cs() == "newenvironment") {
1240 string const name = p.getArg('{', '}');
1241 string const opt1 = p.getFullOpt();
1242 string const opt2 = p.getFullOpt();
1243 string const beg = p.verbatim_item();
1244 string const end = p.verbatim_item();
1245 if (!in_lyx_preamble) {
1246 h_preamble << "\\newenvironment{" << name
1247 << '}' << opt1 << opt2 << '{'
1248 << beg << "}{" << end << '}';
1250 add_known_environment(name, opt1, !opt2.empty(),
1251 from_utf8(beg), from_utf8(end));
1255 else if (t.cs() == "def") {
1256 string name = p.get_token().cs();
1257 while (p.next_token().cat() != catBegin)
1258 name += p.get_token().cs();
1259 if (!in_lyx_preamble)
1260 h_preamble << "\\def\\" << name << '{'
1261 << p.verbatim_item() << "}";
1264 else if (t.cs() == "newcolumntype") {
1265 string const name = p.getArg('{', '}');
1266 trimSpaceAndEol(name);
1268 string opts = p.getOpt();
1269 if (!opts.empty()) {
1270 istringstream is(string(opts, 1));
1273 special_columns[name[0]] = nargs;
1274 h_preamble << "\\newcolumntype{" << name << "}";
1276 h_preamble << "[" << nargs << "]";
1277 h_preamble << "{" << p.verbatim_item() << "}";
1280 else if (t.cs() == "setcounter") {
1281 string const name = p.getArg('{', '}');
1282 string const content = p.getArg('{', '}');
1283 if (name == "secnumdepth")
1284 h_secnumdepth = content;
1285 else if (name == "tocdepth")
1286 h_tocdepth = content;
1288 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1291 else if (t.cs() == "setlength") {
1292 string const name = p.verbatim_item();
1293 string const content = p.verbatim_item();
1294 // the paragraphs are only not indented when \parindent is set to zero
1295 if (name == "\\parindent" && content != "") {
1296 if (content[0] == '0')
1297 h_paragraph_separation = "skip";
1299 h_paragraph_indentation = translate_len(content);
1300 } else if (name == "\\parskip") {
1301 if (content == "\\smallskipamount")
1302 h_defskip = "smallskip";
1303 else if (content == "\\medskipamount")
1304 h_defskip = "medskip";
1305 else if (content == "\\bigskipamount")
1306 h_defskip = "bigskip";
1308 h_defskip = content;
1310 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1313 else if (t.cs() == "onehalfspacing")
1314 h_spacing = "onehalf";
1316 else if (t.cs() == "doublespacing")
1317 h_spacing = "double";
1319 else if (t.cs() == "setstretch")
1320 h_spacing = "other " + p.verbatim_item();
1322 else if (t.cs() == "begin") {
1323 string const name = p.getArg('{', '}');
1324 if (name == "document")
1326 h_preamble << "\\begin{" << name << "}";
1329 else if (t.cs() == "geometry") {
1330 vector<string> opts = split_options(p.getArg('{', '}'));
1331 handle_geometry(opts);
1334 else if (t.cs() == "definecolor") {
1335 string const color = p.getArg('{', '}');
1336 string const space = p.getArg('{', '}');
1337 string const value = p.getArg('{', '}');
1338 if (color == "document_fontcolor" && space == "rgb") {
1339 RGBColor c(RGBColorFromLaTeX(value));
1340 h_fontcolor = X11hexname(c);
1341 } else if (color == "note_fontcolor" && space == "rgb") {
1342 RGBColor c(RGBColorFromLaTeX(value));
1343 h_notefontcolor = X11hexname(c);
1344 } else if (color == "page_backgroundcolor" && space == "rgb") {
1345 RGBColor c(RGBColorFromLaTeX(value));
1346 h_backgroundcolor = X11hexname(c);
1347 } else if (color == "shadecolor" && space == "rgb") {
1348 RGBColor c(RGBColorFromLaTeX(value));
1349 h_boxbgcolor = X11hexname(c);
1351 h_preamble << "\\definecolor{" << color
1352 << "}{" << space << "}{" << value
1357 else if (t.cs() == "bibliographystyle")
1358 h_biblio_style = p.verbatim_item();
1360 else if (t.cs() == "jurabibsetup") {
1361 // FIXME p.getArg('{', '}') is most probably wrong (it
1362 // does not handle nested braces).
1363 // Use p.verbatim_item() instead.
1364 vector<string> jurabibsetup =
1365 split_options(p.getArg('{', '}'));
1366 // add jurabibsetup to the jurabib package options
1367 add_package("jurabib", jurabibsetup);
1368 if (!jurabibsetup.empty()) {
1369 h_preamble << "\\jurabibsetup{"
1370 << join(jurabibsetup, ",") << '}';
1374 else if (t.cs() == "hypersetup") {
1375 vector<string> hypersetup =
1376 split_options(p.verbatim_item());
1377 // add hypersetup to the hyperref package options
1378 handle_hyperref(hypersetup);
1379 if (!hypersetup.empty()) {
1380 h_preamble << "\\hypersetup{"
1381 << join(hypersetup, ",") << '}';
1385 else if (is_known(t.cs(), known_if_3arg_commands)) {
1386 // prevent misparsing of \usepackage if it is used
1387 // as an argument (see e.g. our own output of
1388 // \@ifundefined above)
1389 string const arg1 = p.verbatim_item();
1390 string const arg2 = p.verbatim_item();
1391 string const arg3 = p.verbatim_item();
1392 // test case \@ifundefined{date}{}{\date{}}
1393 if (t.cs() == "@ifundefined" && arg1 == "date" &&
1394 arg2.empty() && arg3 == "\\date{}") {
1395 h_suppress_date = "true";
1396 // older tex2lyx versions did output
1397 // \@ifundefined{definecolor}{\usepackage{color}}{}
1398 } else if (t.cs() == "@ifundefined" &&
1399 arg1 == "definecolor" &&
1400 arg2 == "\\usepackage{color}" &&
1402 if (!in_lyx_preamble)
1403 h_preamble << package_beg_sep
1406 << "\\@ifundefined{definecolor}{color}{}"
1409 //\@ifundefined{showcaptionsetup}{}{%
1410 // \PassOptionsToPackage{caption=false}{subfig}}
1411 // that LyX uses for subfloats
1412 } else if (t.cs() == "@ifundefined" &&
1413 arg1 == "showcaptionsetup" && arg2.empty()
1414 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1416 } else if (!in_lyx_preamble) {
1417 h_preamble << t.asInput()
1418 << '{' << arg1 << '}'
1419 << '{' << arg2 << '}'
1420 << '{' << arg3 << '}';
1424 else if (is_known(t.cs(), known_if_commands)) {
1425 // must not parse anything in conditional code, since
1426 // LyX would output the parsed contents unconditionally
1427 if (!in_lyx_preamble)
1428 h_preamble << t.asInput();
1429 handle_if(p, in_lyx_preamble);
1432 else if (!t.cs().empty() && !in_lyx_preamble)
1433 h_preamble << '\\' << t.cs();
1436 // remove the whitespace
1439 // Force textclass if the user wanted it
1440 if (!forceclass.empty())
1441 h_textclass = forceclass;
1442 if (noweb_mode && !prefixIs(h_textclass, "literate-"))
1443 h_textclass.insert(0, "literate-");
1444 tc.setName(h_textclass);
1446 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1449 if (h_papersides.empty()) {
1452 h_papersides = ss.str();
1457 string babel2lyx(string const & language)
1459 char const * const * where = is_known(language, known_languages);
1461 return known_coded_languages[where - known_languages];
1466 string rgbcolor2code(string const & name)
1468 char const * const * where = is_known(name, known_basic_colors);
1470 // "red", "green" etc
1471 return known_basic_color_codes[where - known_basic_colors];
1473 // "255,0,0", "0,255,0" etc
1474 RGBColor c(RGBColorFromLaTeX(name));
1475 return X11hexname(c);