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 // codes used to remove packages that are loaded automatically by LyX.
175 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
176 const char package_beg_sep = '\001';
177 const char package_mid_sep = '\002';
178 const char package_end_sep = '\003';
181 // returns true if at least one of the options in what has been found
182 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
188 // the last language option is the document language (for babel and LyX)
189 // the last size option is the document font size
190 vector<string>::iterator it;
191 vector<string>::iterator position = opts.begin();
192 for (; *what; ++what) {
193 it = find(opts.begin(), opts.end(), *what);
194 if (it != opts.end()) {
195 if (it >= position) {
206 void delete_opt(vector<string> & opts, char const * const * what)
211 // remove found options from the list
212 // do this after handle_opt to avoid potential memory leaks
213 vector<string>::iterator it;
214 for (; *what; ++what) {
215 it = find(opts.begin(), opts.end(), *what);
216 if (it != opts.end())
223 * Split a package options string (keyval format) into a vector.
225 * authorformat=smallcaps,
227 * titleformat=colonsep,
228 * bibformat={tabular,ibidem,numbered}
230 vector<string> split_options(string const & input)
232 vector<string> options;
236 Token const & t = p.get_token();
237 if (t.asInput() == ",") {
238 options.push_back(trimSpaceAndEol(option));
240 } else if (t.asInput() == "=") {
243 if (p.next_token().asInput() == "{")
244 option += '{' + p.getArg('{', '}') + '}';
245 } else if (t.cat() != catSpace)
246 option += t.asInput();
250 options.push_back(trimSpaceAndEol(option));
257 * Retrieve a keyval option "name={value with=sign}" named \p name from
258 * \p options and return the value.
259 * The found option is also removed from \p options.
261 string process_keyval_opt(vector<string> & options, string name)
263 for (size_t i = 0; i < options.size(); ++i) {
264 vector<string> option;
265 split(options[i], option, '=');
266 if (option.size() < 2)
268 if (option[0] == name) {
269 options.erase(options.begin() + i);
270 option.erase(option.begin());
271 return join(option, "=");
277 } // anonymous namespace
280 bool Preamble::indentParagraphs() const
282 return h_paragraph_separation == "indent";
286 bool Preamble::isPackageUsed(string const & package) const
288 return used_packages.find(package) != used_packages.end();
292 vector<string> Preamble::getPackageOptions(string const & package) const
294 map<string, vector<string> >::const_iterator it = used_packages.find(package);
295 if (it != used_packages.end())
297 return vector<string>();
301 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
303 auto_packages.insert(package);
307 void Preamble::addModule(string const & module)
309 used_modules.push_back(module);
313 void Preamble::suppressDate(bool suppress)
316 h_suppress_date = "true";
318 h_suppress_date = "false";
322 void Preamble::registerAuthor(std::string const & name)
324 Author author(from_utf8(name), empty_docstring());
325 author.setUsed(true);
326 authors_.record(author);
327 h_tracking_changes = "true";
328 h_output_changes = "true";
332 Author const & Preamble::getAuthor(std::string const & name) const
334 Author author(from_utf8(name), empty_docstring());
335 for (AuthorList::Authors::const_iterator it = authors_.begin();
336 it != authors_.end(); it++)
339 static Author const dummy;
344 void Preamble::add_package(string const & name, vector<string> & options)
346 // every package inherits the global options
347 if (used_packages.find(name) == used_packages.end())
348 used_packages[name] = split_options(h_options);
350 vector<string> & v = used_packages[name];
351 v.insert(v.end(), options.begin(), options.end());
352 if (name == "jurabib") {
353 // Don't output the order argument (see the cite command
354 // handling code in text.cpp).
355 vector<string>::iterator end =
356 remove(options.begin(), options.end(), "natbiborder");
357 end = remove(options.begin(), end, "jurabiborder");
358 options.erase(end, options.end());
365 // Given is a string like "scaled=0.9", return 0.9 * 100
366 string const scale_as_percentage(string const & scale)
368 string::size_type pos = scale.find('=');
369 if (pos != string::npos) {
370 string value = scale.substr(pos + 1);
372 return convert<string>(100 * convert<double>(value));
374 // If the input string didn't match our expectations.
375 // return the default value "100"
380 string remove_braces(string const & value)
384 if (value[0] == '{' && value[value.length()-1] == '}')
385 return value.substr(1, value.length()-2);
389 } // anonymous namespace
392 Preamble::Preamble() : one_language(true)
396 h_cite_engine = "basic";
397 h_defskip = "medskip";
400 h_fontencoding = "default";
401 h_font_roman = "default";
402 h_font_sans = "default";
403 h_font_typewriter = "default";
404 h_font_default_family = "default";
406 h_font_osf = "false";
407 h_font_sf_scale = "100";
408 h_font_tt_scale = "100";
409 h_graphics = "default";
410 h_html_be_strict = "false";
411 h_html_css_as_file = "0";
412 h_html_math_output = "0";
413 h_inputencoding = "auto";
414 h_justification = "true";
415 h_language = "english";
416 h_language_package = "none";
421 h_output_changes = "false";
422 h_papercolumns = "1";
423 h_paperfontsize = "default";
424 h_paperorientation = "portrait";
425 h_paperpagestyle = "default";
427 h_papersize = "default";
428 h_paragraph_indentation = "default";
429 h_paragraph_separation = "indent";
434 h_pdf_bookmarks = "1";
435 h_pdf_bookmarksnumbered = "0";
436 h_pdf_bookmarksopen = "0";
437 h_pdf_bookmarksopenlevel = "1";
438 h_pdf_breaklinks = "0";
439 h_pdf_pdfborder = "0";
440 h_pdf_colorlinks = "0";
441 h_pdf_backref = "section";
442 h_pdf_pdfusetitle = "1";
444 //h_pdf_quoted_options;
445 h_quotes_language = "english";
447 h_spacing = "single";
448 h_suppress_date = "false";
449 h_textclass = "article";
451 h_tracking_changes = "false";
452 h_use_bibtopic = "false";
453 h_use_indices = "false";
454 h_use_geometry = "false";
456 h_use_default_options = "false";
458 h_use_hyperref = "0";
460 h_use_mathdots = "0";
461 h_use_refstyle = "0";
462 h_use_undertilde = "0";
466 void Preamble::handle_hyperref(vector<string> & options)
468 // FIXME swallow inputencoding changes that might surround the
469 // hyperref setup if it was written by LyX
470 h_use_hyperref = "1";
471 // swallow "unicode=true", since LyX does always write that
472 vector<string>::iterator it =
473 find(options.begin(), options.end(), "unicode=true");
474 if (it != options.end())
476 it = find(options.begin(), options.end(), "pdfusetitle");
477 if (it != options.end()) {
478 h_pdf_pdfusetitle = "1";
481 string bookmarks = process_keyval_opt(options, "bookmarks");
482 if (bookmarks == "true")
483 h_pdf_bookmarks = "1";
484 else if (bookmarks == "false")
485 h_pdf_bookmarks = "0";
486 if (h_pdf_bookmarks == "1") {
487 string bookmarksnumbered =
488 process_keyval_opt(options, "bookmarksnumbered");
489 if (bookmarksnumbered == "true")
490 h_pdf_bookmarksnumbered = "1";
491 else if (bookmarksnumbered == "false")
492 h_pdf_bookmarksnumbered = "0";
493 string bookmarksopen =
494 process_keyval_opt(options, "bookmarksopen");
495 if (bookmarksopen == "true")
496 h_pdf_bookmarksopen = "1";
497 else if (bookmarksopen == "false")
498 h_pdf_bookmarksopen = "0";
499 if (h_pdf_bookmarksopen == "1") {
500 string bookmarksopenlevel =
501 process_keyval_opt(options, "bookmarksopenlevel");
502 if (!bookmarksopenlevel.empty())
503 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
506 string breaklinks = process_keyval_opt(options, "breaklinks");
507 if (breaklinks == "true")
508 h_pdf_breaklinks = "1";
509 else if (breaklinks == "false")
510 h_pdf_breaklinks = "0";
511 string pdfborder = process_keyval_opt(options, "pdfborder");
512 if (pdfborder == "{0 0 0}")
513 h_pdf_pdfborder = "1";
514 else if (pdfborder == "{0 0 1}")
515 h_pdf_pdfborder = "0";
516 string backref = process_keyval_opt(options, "backref");
517 if (!backref.empty())
518 h_pdf_backref = backref;
519 string colorlinks = process_keyval_opt(options, "colorlinks");
520 if (colorlinks == "true")
521 h_pdf_colorlinks = "1";
522 else if (colorlinks == "false")
523 h_pdf_colorlinks = "0";
524 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
525 if (!pdfpagemode.empty())
526 h_pdf_pagemode = pdfpagemode;
527 string pdftitle = process_keyval_opt(options, "pdftitle");
528 if (!pdftitle.empty()) {
529 h_pdf_title = remove_braces(pdftitle);
531 string pdfauthor = process_keyval_opt(options, "pdfauthor");
532 if (!pdfauthor.empty()) {
533 h_pdf_author = remove_braces(pdfauthor);
535 string pdfsubject = process_keyval_opt(options, "pdfsubject");
536 if (!pdfsubject.empty())
537 h_pdf_subject = remove_braces(pdfsubject);
538 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
539 if (!pdfkeywords.empty())
540 h_pdf_keywords = remove_braces(pdfkeywords);
541 if (!options.empty()) {
542 if (!h_pdf_quoted_options.empty())
543 h_pdf_quoted_options += ',';
544 h_pdf_quoted_options += join(options, ",");
550 void Preamble::handle_geometry(vector<string> & options)
552 h_use_geometry = "true";
553 vector<string>::iterator it;
555 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
556 h_paperorientation = "landscape";
560 // keyval version: "paper=letter"
561 string paper = process_keyval_opt(options, "paper");
563 h_papersize = paper + "paper";
564 // alternative version: "letterpaper"
565 handle_opt(options, known_paper_sizes, h_papersize);
566 delete_opt(options, known_paper_sizes);
568 char const * const * margin = known_paper_margins;
569 for (; *margin; ++margin) {
570 string value = process_keyval_opt(options, *margin);
571 if (!value.empty()) {
572 int k = margin - known_paper_margins;
573 string name = known_coded_paper_margins[k];
574 h_margins += '\\' + name + ' ' + value + '\n';
580 void Preamble::handle_package(Parser &p, string const & name,
581 string const & opts, bool in_lyx_preamble)
583 vector<string> options = split_options(opts);
584 add_package(name, options);
587 if (is_known(name, known_xetex_packages))
591 if (is_known(name, known_roman_fonts)) {
596 if (name == "fourier") {
597 h_font_roman = "utopia";
598 // when font uses real small capitals
599 if (opts == "expert")
603 else if (name == "mathpazo")
604 h_font_roman = "palatino";
606 else if (name == "mathptmx")
607 h_font_roman = "times";
610 if (is_known(name, known_sans_fonts)) {
614 h_font_sf_scale = scale_as_percentage(scale);
619 if (is_known(name, known_typewriter_fonts)) {
620 // fourier can be set as roman font _only_
621 // fourier as typewriter is handled in handling of \ttdefault
622 if (name != "fourier") {
623 h_font_typewriter = name;
626 h_font_tt_scale = scale_as_percentage(scale);
631 // font uses old-style figure
635 // after the detection and handling of special cases, we can remove the
636 // fonts, otherwise they would appear in the preamble, see bug #7856
637 if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
638 || is_known(name, known_typewriter_fonts))
641 else if (name == "amsmath" || name == "amssymb")
644 else if (name == "esint")
647 else if (name == "mhchem")
650 else if (name == "mathdots")
651 h_use_mathdots = "2";
653 else if (name == "undertilde")
654 h_use_undertilde = "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 == "pdfpages")
719 ; // ignore this FIXME: Use the package separator mechanism instead
721 else if (name == "lyxskak") {
722 // ignore this and its options
723 if (!options.empty())
727 else if (name == "array" || name == "booktabs" || name == "calc" ||
728 name == "color" || name == "float" || name == "hhline" ||
729 name == "ifthen" || name == "longtable" || name == "makeidx" ||
730 name == "multirow" || name == "nomencl" || name == "rotfloat" ||
731 name == "splitidx" || name == "setspace" || name == "subscript" ||
732 name == "textcomp" || name == "ulem" || name == "url" ||
733 name == "varioref" || name == "verbatim" || name == "wrapfig") {
734 if (!in_lyx_preamble)
735 h_preamble << package_beg_sep << name
736 << package_mid_sep << "\\usepackage{"
737 << name << "}\n" << package_end_sep;
740 else if (name == "graphicx")
741 ; // ignore this FIXME: Use the package separator mechanism instead
743 else if (name == "geometry")
744 handle_geometry(options);
746 else if (name == "subfig")
747 ; // ignore this FIXME: Use the package separator mechanism instead
749 else if (is_known(name, known_languages))
752 else if (name == "natbib") {
753 h_cite_engine = "natbib_authoryear";
754 vector<string>::iterator it =
755 find(options.begin(), options.end(), "authoryear");
756 if (it != options.end())
759 it = find(options.begin(), options.end(), "numbers");
760 if (it != options.end()) {
761 h_cite_engine = "natbib_numerical";
767 else if (name == "jurabib")
768 h_cite_engine = "jurabib";
770 else if (name == "hyperref")
771 handle_hyperref(options);
773 else if (!in_lyx_preamble) {
775 h_preamble << "\\usepackage{" << name << "}";
777 h_preamble << "\\usepackage[" << opts << "]{"
783 // We need to do something with the options...
784 if (!options.empty())
785 cerr << "Ignoring options '" << join(options, ",")
786 << "' of package " << name << '.' << endl;
788 // remove the whitespace
793 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
796 Token t = p.get_token();
797 if (t.cat() == catEscape &&
798 is_known(t.cs(), known_if_commands))
799 handle_if(p, in_lyx_preamble);
801 if (!in_lyx_preamble)
802 h_preamble << t.asInput();
803 if (t.cat() == catEscape && t.cs() == "fi")
810 bool Preamble::writeLyXHeader(ostream & os, bool subdoc)
812 // translate from babel to LyX names
813 h_language = babel2lyx(h_language);
815 // set the quote language
816 // LyX only knows the following quotes languages:
817 // english, swedish, german, polish, french and danish
818 // (quotes for "japanese" and "chinese-traditional" are missing because
819 // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
820 // conversion list taken from
821 // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
822 // (quotes for kazakh and interlingua are unknown)
824 if (h_language == "danish")
825 h_quotes_language = "danish";
827 else if (is_known(h_language, known_french_quotes_languages))
828 h_quotes_language = "french";
830 else if (is_known(h_language, known_german_quotes_languages))
831 h_quotes_language = "german";
833 else if (is_known(h_language, known_polish_quotes_languages))
834 h_quotes_language = "polish";
836 else if (is_known(h_language, known_swedish_quotes_languages))
837 h_quotes_language = "swedish";
839 else if (is_known(h_language, known_english_quotes_languages))
840 h_quotes_language = "english";
842 if (contains(h_float_placement, "H"))
843 registerAutomaticallyLoadedPackage("float");
844 if (h_spacing != "single" && h_spacing != "default")
845 registerAutomaticallyLoadedPackage("setspace");
847 // output the LyX file settings
848 os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
849 << "\\lyxformat " << LYX_FORMAT << '\n'
850 << "\\begin_document\n"
851 << "\\begin_header\n"
852 << "\\textclass " << h_textclass << "\n";
853 string const raw = subdoc ? empty_string() : h_preamble.str();
855 os << "\\begin_preamble\n";
856 for (string::size_type i = 0; i < raw.size(); ++i) {
857 if (raw[i] == package_beg_sep) {
858 // Here follows some package loading code that
859 // must be skipped if the package is loaded
861 string::size_type j = raw.find(package_mid_sep, i);
862 if (j == string::npos)
864 string::size_type k = raw.find(package_end_sep, j);
865 if (k == string::npos)
867 string const package = raw.substr(i + 1, j - i - 1);
868 string const replacement = raw.substr(j + 1, k - j - 1);
869 if (auto_packages.find(package) == auto_packages.end())
875 os << "\n\\end_preamble\n";
877 if (!h_options.empty())
878 os << "\\options " << h_options << "\n";
879 os << "\\use_default_options " << h_use_default_options << "\n";
880 if (!used_modules.empty()) {
881 os << "\\begin_modules\n";
882 vector<string>::const_iterator const end = used_modules.end();
883 vector<string>::const_iterator it = used_modules.begin();
884 for (; it != end; it++)
886 os << "\\end_modules\n";
888 os << "\\language " << h_language << "\n"
889 << "\\language_package " << h_language_package << "\n"
890 << "\\inputencoding " << h_inputencoding << "\n"
891 << "\\fontencoding " << h_fontencoding << "\n"
892 << "\\font_roman " << h_font_roman << "\n"
893 << "\\font_sans " << h_font_sans << "\n"
894 << "\\font_typewriter " << h_font_typewriter << "\n"
895 << "\\font_default_family " << h_font_default_family << "\n"
896 << "\\font_sc " << h_font_sc << "\n"
897 << "\\font_osf " << h_font_osf << "\n"
898 << "\\font_sf_scale " << h_font_sf_scale << "\n"
899 << "\\font_tt_scale " << h_font_tt_scale << "\n"
900 << "\\graphics " << h_graphics << "\n";
901 if (!h_float_placement.empty())
902 os << "\\float_placement " << h_float_placement << "\n";
903 os << "\\paperfontsize " << h_paperfontsize << "\n"
904 << "\\spacing " << h_spacing << "\n"
905 << "\\use_hyperref " << h_use_hyperref << '\n';
906 if (h_use_hyperref == "1") {
907 if (!h_pdf_title.empty())
908 os << "\\pdf_title \"" << h_pdf_title << "\"\n";
909 if (!h_pdf_author.empty())
910 os << "\\pdf_author \"" << h_pdf_author << "\"\n";
911 if (!h_pdf_subject.empty())
912 os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
913 if (!h_pdf_keywords.empty())
914 os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
915 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
916 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
917 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
918 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
919 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
920 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
921 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
922 "\\pdf_backref " << h_pdf_backref << "\n"
923 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
924 if (!h_pdf_pagemode.empty())
925 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
926 if (!h_pdf_quoted_options.empty())
927 os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
929 os << "\\papersize " << h_papersize << "\n"
930 << "\\use_geometry " << h_use_geometry << "\n"
931 << "\\use_amsmath " << h_use_amsmath << "\n"
932 << "\\use_esint " << h_use_esint << "\n"
933 << "\\use_mhchem " << h_use_mhchem << "\n"
934 << "\\use_mathdots " << h_use_mathdots << "\n"
935 << "\\use_undertilde " << h_use_undertilde << "\n"
936 << "\\cite_engine " << h_cite_engine << "\n"
937 << "\\use_bibtopic " << h_use_bibtopic << "\n"
938 << "\\use_indices " << h_use_indices << "\n"
939 << "\\paperorientation " << h_paperorientation << '\n'
940 << "\\suppress_date " << h_suppress_date << '\n'
941 << "\\justification " << h_justification << '\n'
942 << "\\use_refstyle " << h_use_refstyle << '\n';
943 if (!h_fontcolor.empty())
944 os << "\\fontcolor " << h_fontcolor << '\n';
945 if (!h_notefontcolor.empty())
946 os << "\\notefontcolor " << h_notefontcolor << '\n';
947 if (!h_backgroundcolor.empty())
948 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
949 if (!h_boxbgcolor.empty())
950 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
952 << "\\secnumdepth " << h_secnumdepth << "\n"
953 << "\\tocdepth " << h_tocdepth << "\n"
954 << "\\paragraph_separation " << h_paragraph_separation << "\n";
955 if (h_paragraph_separation == "skip")
956 os << "\\defskip " << h_defskip << "\n";
958 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
959 os << "\\quotes_language " << h_quotes_language << "\n"
960 << "\\papercolumns " << h_papercolumns << "\n"
961 << "\\papersides " << h_papersides << "\n"
962 << "\\paperpagestyle " << h_paperpagestyle << "\n";
963 if (!h_listings_params.empty())
964 os << "\\listings_params " << h_listings_params << "\n";
965 os << "\\tracking_changes " << h_tracking_changes << "\n"
966 << "\\output_changes " << h_output_changes << "\n"
967 << "\\html_math_output " << h_html_math_output << "\n"
968 << "\\html_css_as_file " << h_html_css_as_file << "\n"
969 << "\\html_be_strict " << h_html_be_strict << "\n"
971 << "\\end_header\n\n"
977 void Preamble::parse(Parser & p, string const & forceclass,
978 TeX2LyXDocClass & tc)
980 // initialize fixed types
981 special_columns['D'] = 3;
982 bool is_full_document = false;
983 bool is_lyx_file = false;
984 bool in_lyx_preamble = false;
986 // determine whether this is a full document or a fragment for inclusion
988 Token const & t = p.get_token();
990 if (t.cat() == catEscape && t.cs() == "documentclass") {
991 is_full_document = true;
997 while (is_full_document && p.good()) {
998 Token const & t = p.get_token();
1001 cerr << "t: " << t << "\n";
1007 if (!in_lyx_preamble &&
1008 (t.cat() == catLetter ||
1009 t.cat() == catSuper ||
1010 t.cat() == catSub ||
1011 t.cat() == catOther ||
1012 t.cat() == catMath ||
1013 t.cat() == catActive ||
1014 t.cat() == catBegin ||
1015 t.cat() == catEnd ||
1016 t.cat() == catAlign ||
1017 t.cat() == catParameter))
1018 h_preamble << t.cs();
1020 else if (!in_lyx_preamble &&
1021 (t.cat() == catSpace || t.cat() == catNewline))
1022 h_preamble << t.asInput();
1024 else if (t.cat() == catComment) {
1025 static regex const islyxfile("%% LyX .* created this file");
1026 static regex const usercommands("User specified LaTeX commands");
1028 string const comment = t.asInput();
1030 // magically switch encoding default if it looks like XeLaTeX
1031 static string const magicXeLaTeX =
1032 "% This document must be compiled with XeLaTeX ";
1033 if (comment.size() > magicXeLaTeX.size()
1034 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1035 && h_inputencoding == "auto") {
1036 cerr << "XeLaTeX comment found, switching to UTF8\n";
1037 h_inputencoding = "utf8";
1040 if (regex_search(comment, sub, islyxfile)) {
1042 in_lyx_preamble = true;
1043 } else if (is_lyx_file
1044 && regex_search(comment, sub, usercommands))
1045 in_lyx_preamble = false;
1046 else if (!in_lyx_preamble)
1047 h_preamble << t.asInput();
1050 else if (t.cs() == "pagestyle")
1051 h_paperpagestyle = p.verbatim_item();
1053 else if (t.cs() == "date") {
1054 string argument = p.getArg('{', '}');
1055 if (argument.empty())
1056 h_suppress_date = "true";
1058 h_preamble << t.asInput() << '{' << argument << '}';
1061 else if (t.cs() == "color") {
1062 string const space =
1063 (p.hasOpt() ? p.getOpt() : string());
1064 string argument = p.getArg('{', '}');
1065 // check the case that a standard color is used
1066 if (space.empty() && is_known(argument, known_basic_colors)) {
1067 h_fontcolor = rgbcolor2code(argument);
1068 preamble.registerAutomaticallyLoadedPackage("color");
1069 } else if (space.empty() && argument == "document_fontcolor")
1070 preamble.registerAutomaticallyLoadedPackage("color");
1071 // check the case that LyX's document_fontcolor is defined
1072 // but not used for \color
1074 h_preamble << t.asInput();
1076 h_preamble << space;
1077 h_preamble << '{' << argument << '}';
1078 // the color might already be set because \definecolor
1079 // is parsed before this
1084 else if (t.cs() == "pagecolor") {
1085 string argument = p.getArg('{', '}');
1086 // check the case that a standard color is used
1087 if (is_known(argument, known_basic_colors)) {
1088 h_backgroundcolor = rgbcolor2code(argument);
1089 } else if (argument == "page_backgroundcolor")
1090 preamble.registerAutomaticallyLoadedPackage("color");
1091 // check the case that LyX's page_backgroundcolor is defined
1092 // but not used for \pagecolor
1094 h_preamble << t.asInput() << '{' << argument << '}';
1095 // the color might already be set because \definecolor
1096 // is parsed before this
1097 h_backgroundcolor = "";
1101 else if (t.cs() == "makeatletter") {
1102 // LyX takes care of this
1103 p.setCatCode('@', catLetter);
1106 else if (t.cs() == "makeatother") {
1107 // LyX takes care of this
1108 p.setCatCode('@', catOther);
1111 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1112 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1113 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1114 || t.cs() == "DeclareRobustCommand"
1115 || t.cs() == "DeclareRobustCommandx"
1116 || t.cs() == "ProvideTextCommandDefault"
1117 || t.cs() == "DeclareMathAccent") {
1119 if (p.next_token().character() == '*') {
1123 string const name = p.verbatim_item();
1124 string const opt1 = p.getFullOpt();
1125 string const opt2 = p.getFullOpt();
1126 string const body = p.verbatim_item();
1128 if (name == "\\rmdefault")
1129 if (is_known(body, known_roman_fonts))
1130 h_font_roman = body;
1131 if (name == "\\sfdefault")
1132 if (is_known(body, known_sans_fonts))
1134 if (name == "\\ttdefault")
1135 if (is_known(body, known_typewriter_fonts))
1136 h_font_typewriter = body;
1137 if (name == "\\familydefault") {
1138 string family = body;
1139 // remove leading "\"
1140 h_font_default_family = family.erase(0,1);
1143 // remove the lyxdot definition that is re-added by LyX
1145 if (name == "\\lyxdot")
1146 in_lyx_preamble = true;
1148 // Add the command to the known commands
1149 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1151 // only non-lyxspecific stuff
1152 if (!in_lyx_preamble) {
1154 ss << '\\' << t.cs();
1157 ss << '{' << name << '}' << opt1 << opt2
1158 << '{' << body << "}";
1159 h_preamble << ss.str();
1161 ostream & out = in_preamble ? h_preamble : os;
1162 out << "\\" << t.cs() << "{" << name << "}"
1163 << opts << "{" << body << "}";
1168 else if (t.cs() == "documentclass") {
1169 vector<string>::iterator it;
1170 vector<string> opts = split_options(p.getArg('[', ']'));
1171 handle_opt(opts, known_fontsizes, h_paperfontsize);
1172 delete_opt(opts, known_fontsizes);
1173 // delete "pt" at the end
1174 string::size_type i = h_paperfontsize.find("pt");
1175 if (i != string::npos)
1176 h_paperfontsize.erase(i);
1177 // The documentclass options are always parsed before the options
1178 // of the babel call so that a language cannot overwrite the babel
1180 handle_opt(opts, known_languages, h_language);
1181 delete_opt(opts, known_languages);
1183 // paper orientation
1184 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1185 h_paperorientation = "landscape";
1189 if ((it = find(opts.begin(), opts.end(), "oneside"))
1194 if ((it = find(opts.begin(), opts.end(), "twoside"))
1200 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1202 h_papercolumns = "1";
1205 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1207 h_papercolumns = "2";
1211 // some size options are known to any document classes, other sizes
1212 // are handled by the \geometry command of the geometry package
1213 handle_opt(opts, known_class_paper_sizes, h_papersize);
1214 delete_opt(opts, known_class_paper_sizes);
1215 // the remaining options
1216 h_options = join(opts, ",");
1217 // FIXME This does not work for classes that have a
1218 // different name in LyX than in LaTeX
1219 h_textclass = p.getArg('{', '}');
1222 else if (t.cs() == "usepackage") {
1223 string const options = p.getArg('[', ']');
1224 string const name = p.getArg('{', '}');
1225 vector<string> vecnames;
1226 split(name, vecnames, ',');
1227 vector<string>::const_iterator it = vecnames.begin();
1228 vector<string>::const_iterator end = vecnames.end();
1229 for (; it != end; ++it)
1230 handle_package(p, trimSpaceAndEol(*it), options,
1234 else if (t.cs() == "inputencoding") {
1235 string const encoding = p.getArg('{','}');
1236 h_inputencoding = encoding;
1237 p.setEncoding(encoding);
1240 else if (t.cs() == "newenvironment") {
1241 string const name = p.getArg('{', '}');
1242 string const opt1 = p.getFullOpt();
1243 string const opt2 = p.getFullOpt();
1244 string const beg = p.verbatim_item();
1245 string const end = p.verbatim_item();
1246 if (!in_lyx_preamble) {
1247 h_preamble << "\\newenvironment{" << name
1248 << '}' << opt1 << opt2 << '{'
1249 << beg << "}{" << end << '}';
1251 add_known_environment(name, opt1, !opt2.empty(),
1252 from_utf8(beg), from_utf8(end));
1256 else if (t.cs() == "def") {
1257 string name = p.get_token().cs();
1258 while (p.next_token().cat() != catBegin)
1259 name += p.get_token().cs();
1260 if (!in_lyx_preamble)
1261 h_preamble << "\\def\\" << name << '{'
1262 << p.verbatim_item() << "}";
1265 else if (t.cs() == "newcolumntype") {
1266 string const name = p.getArg('{', '}');
1267 trimSpaceAndEol(name);
1269 string opts = p.getOpt();
1270 if (!opts.empty()) {
1271 istringstream is(string(opts, 1));
1274 special_columns[name[0]] = nargs;
1275 h_preamble << "\\newcolumntype{" << name << "}";
1277 h_preamble << "[" << nargs << "]";
1278 h_preamble << "{" << p.verbatim_item() << "}";
1281 else if (t.cs() == "setcounter") {
1282 string const name = p.getArg('{', '}');
1283 string const content = p.getArg('{', '}');
1284 if (name == "secnumdepth")
1285 h_secnumdepth = content;
1286 else if (name == "tocdepth")
1287 h_tocdepth = content;
1289 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1292 else if (t.cs() == "setlength") {
1293 string const name = p.verbatim_item();
1294 string const content = p.verbatim_item();
1295 // the paragraphs are only not indented when \parindent is set to zero
1296 if (name == "\\parindent" && content != "") {
1297 if (content[0] == '0')
1298 h_paragraph_separation = "skip";
1300 h_paragraph_indentation = translate_len(content);
1301 } else if (name == "\\parskip") {
1302 if (content == "\\smallskipamount")
1303 h_defskip = "smallskip";
1304 else if (content == "\\medskipamount")
1305 h_defskip = "medskip";
1306 else if (content == "\\bigskipamount")
1307 h_defskip = "bigskip";
1309 h_defskip = content;
1311 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1314 else if (t.cs() == "onehalfspacing")
1315 h_spacing = "onehalf";
1317 else if (t.cs() == "doublespacing")
1318 h_spacing = "double";
1320 else if (t.cs() == "setstretch")
1321 h_spacing = "other " + p.verbatim_item();
1323 else if (t.cs() == "begin") {
1324 string const name = p.getArg('{', '}');
1325 if (name == "document")
1327 h_preamble << "\\begin{" << name << "}";
1330 else if (t.cs() == "geometry") {
1331 vector<string> opts = split_options(p.getArg('{', '}'));
1332 handle_geometry(opts);
1335 else if (t.cs() == "definecolor") {
1336 string const color = p.getArg('{', '}');
1337 string const space = p.getArg('{', '}');
1338 string const value = p.getArg('{', '}');
1339 if (color == "document_fontcolor" && space == "rgb") {
1340 RGBColor c(RGBColorFromLaTeX(value));
1341 h_fontcolor = X11hexname(c);
1342 } else if (color == "note_fontcolor" && space == "rgb") {
1343 RGBColor c(RGBColorFromLaTeX(value));
1344 h_notefontcolor = X11hexname(c);
1345 } else if (color == "page_backgroundcolor" && space == "rgb") {
1346 RGBColor c(RGBColorFromLaTeX(value));
1347 h_backgroundcolor = X11hexname(c);
1348 } else if (color == "shadecolor" && space == "rgb") {
1349 RGBColor c(RGBColorFromLaTeX(value));
1350 h_boxbgcolor = X11hexname(c);
1352 h_preamble << "\\definecolor{" << color
1353 << "}{" << space << "}{" << value
1358 else if (t.cs() == "jurabibsetup") {
1359 // FIXME p.getArg('{', '}') is most probably wrong (it
1360 // does not handle nested braces).
1361 // Use p.verbatim_item() instead.
1362 vector<string> jurabibsetup =
1363 split_options(p.getArg('{', '}'));
1364 // add jurabibsetup to the jurabib package options
1365 add_package("jurabib", jurabibsetup);
1366 if (!jurabibsetup.empty()) {
1367 h_preamble << "\\jurabibsetup{"
1368 << join(jurabibsetup, ",") << '}';
1372 else if (t.cs() == "hypersetup") {
1373 vector<string> hypersetup =
1374 split_options(p.verbatim_item());
1375 // add hypersetup to the hyperref package options
1376 handle_hyperref(hypersetup);
1377 if (!hypersetup.empty()) {
1378 h_preamble << "\\hypersetup{"
1379 << join(hypersetup, ",") << '}';
1383 else if (is_known(t.cs(), known_if_3arg_commands)) {
1384 // prevent misparsing of \usepackage if it is used
1385 // as an argument (see e.g. our own output of
1386 // \@ifundefined above)
1387 string const arg1 = p.verbatim_item();
1388 string const arg2 = p.verbatim_item();
1389 string const arg3 = p.verbatim_item();
1390 // test case \@ifundefined{date}{}{\date{}}
1391 if (t.cs() == "@ifundefined" && arg1 == "date" &&
1392 arg2.empty() && arg3 == "\\date{}") {
1393 h_suppress_date = "true";
1394 // older tex2lyx versions did output
1395 // \@ifundefined{definecolor}{\usepackage{color}}{}
1396 } else if (t.cs() == "@ifundefined" &&
1397 arg1 == "definecolor" &&
1398 arg2 == "\\usepackage{color}" &&
1400 if (!in_lyx_preamble)
1401 h_preamble << package_beg_sep
1404 << "\\@ifundefined{definecolor}{color}{}"
1407 //\@ifundefined{showcaptionsetup}{}{%
1408 // \PassOptionsToPackage{caption=false}{subfig}}
1409 // that LyX uses for subfloats
1410 } else if (t.cs() == "@ifundefined" &&
1411 arg1 == "showcaptionsetup" && arg2.empty()
1412 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1414 } else if (!in_lyx_preamble) {
1415 h_preamble << t.asInput()
1416 << '{' << arg1 << '}'
1417 << '{' << arg2 << '}'
1418 << '{' << arg3 << '}';
1422 else if (is_known(t.cs(), known_if_commands)) {
1423 // must not parse anything in conditional code, since
1424 // LyX would output the parsed contents unconditionally
1425 if (!in_lyx_preamble)
1426 h_preamble << t.asInput();
1427 handle_if(p, in_lyx_preamble);
1430 else if (!t.cs().empty() && !in_lyx_preamble)
1431 h_preamble << '\\' << t.cs();
1434 // remove the whitespace
1437 // Force textclass if the user wanted it
1438 if (!forceclass.empty())
1439 h_textclass = forceclass;
1440 if (noweb_mode && !prefixIs(h_textclass, "literate-"))
1441 h_textclass.insert(0, "literate-");
1442 tc.setName(h_textclass);
1444 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1447 if (h_papersides.empty()) {
1450 h_papersides = ss.str();
1455 string babel2lyx(string const & language)
1457 char const * const * where = is_known(language, known_languages);
1459 return known_coded_languages[where - known_languages];
1464 string rgbcolor2code(string const & name)
1466 char const * const * where = is_known(name, known_basic_colors);
1468 // "red", "green" etc
1469 return known_basic_color_codes[where - known_basic_colors];
1471 // "255,0,0", "0,255,0" etc
1472 RGBColor c(RGBColorFromLaTeX(name));
1473 return X11hexname(c);