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;
43 const char * const modules_placeholder = "\001modules\001";
49 //add this to known_languages when updating to lyxformat 266:
50 // "armenian" (needs special handling since not supported by standard babel)
51 //add these to known_languages when updating to lyxformat 268:
52 //"chinese-simplified", "chinese-traditional", "japanese", "korean"
53 // Both changes require first that support for non-babel languages (CJK,
56 * known babel language names (including synonyms)
57 * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
58 * not yet supported by LyX: kurmanji
59 * please keep this in sync with known_coded_languages line by line!
61 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
62 "american", "arabic", "arabtex", "austrian", "bahasa", "bahasai", "bahasam",
63 "basque", "belarusian", "brazil", "brazilian", "breton", "british", "bulgarian",
64 "canadian", "canadien", "catalan", "croatian", "czech", "danish", "dutch",
65 "english", "esperanto", "estonian", "farsi", "finnish", "francais", "french",
66 "frenchb", "frenchle", "frenchpro", "galician", "german", "germanb", "greek",
67 "hebrew", "hungarian", "icelandic", "indon", "indonesian", "interlingua",
68 "irish", "italian", "kazakh", "latin", "latvian", "lithuanian", "lowersorbian",
69 "lsorbian", "magyar", "malay", "meyalu", "mongolian", "naustrian", "newzealand",
70 "ngerman", "ngermanb", "norsk", "nynorsk", "polutonikogreek", "polish",
71 "portuges", "portuguese", "romanian", "russian", "russianb", "samin",
72 "scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish",
73 "swedish", "thai", "turkish", "turkmen", "ukraineb", "ukrainian",
74 "uppersorbian", "UKenglish", "USenglish", "usorbian", "vietnam", "welsh",
78 * the same as known_languages with .lyx names
79 * please keep this in sync with known_languages line by line!
81 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
82 "american", "arabic_arabi", "arabic_arabtex", "austrian", "bahasa", "bahasa", "bahasam",
83 "basque", "belarusian", "brazilian", "brazilian", "breton", "british", "bulgarian",
84 "canadian", "canadien", "catalan", "croatian", "czech", "danish", "dutch",
85 "english", "esperanto", "estonian", "farsi", "finnish", "french", "french",
86 "french", "french", "french", "galician", "german", "german", "greek",
87 "hebrew", "magyar", "icelandic", "bahasa", "bahasa", "interlingua",
88 "irish", "italian", "kazakh", "latin", "latvian", "lithuanian", "lowersorbian",
89 "lowersorbian", "magyar", "bahasam", "bahasam", "mongolian", "naustrian", "english",
90 "ngerman", "ngerman", "norsk", "nynorsk", "polutonikogreek", "polish",
91 "portuguese", "portuguese", "romanian", "russian", "russian", "samin",
92 "scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish",
93 "swedish", "thai", "turkish", "turkmen", "ukrainian", "ukrainian",
94 "uppersorbian", "uppersorbian", "english", "english", "vietnamese", "welsh",
97 /// languages with english quotes (.lyx names)
98 const char * const known_english_quotes_languages[] = {"american", "bahasa",
99 "bahasam", "brazilian", "canadian", "chinese-simplified", "english",
100 "esperanto", "hebrew", "irish", "korean", "portuguese", "scottish", "thai", 0};
102 /// languages with french quotes (.lyx names)
103 const char * const known_french_quotes_languages[] = {"albanian",
104 "arabic_arabi", "arabic_arabtex", "basque", "canadien", "catalan", "french",
105 "galician", "greek", "italian", "norsk", "nynorsk", "polutonikogreek",
106 "russian", "spanish", "spanish-mexico", "turkish", "turkmen", "ukrainian",
109 /// languages with german quotes (.lyx names)
110 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
111 "czech", "german", "icelandic", "lithuanian", "lowersorbian", "naustrian",
112 "ngerman", "serbian", "serbian-latin", "slovak", "slovene", "uppersorbian", 0};
114 /// languages with polish quotes (.lyx names)
115 const char * const known_polish_quotes_languages[] = {"afrikaans", "croatian",
116 "dutch", "estonian", "magyar", "polish", "romanian", 0};
118 /// languages with swedish quotes (.lyx names)
119 const char * const known_swedish_quotes_languages[] = {"finnish",
122 /// known language packages from the times before babel
123 const char * const known_old_language_packages[] = {"french", "frenchle",
124 "frenchpro", "german", "ngerman", "pmfrench", 0};
126 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
128 const char * const known_roman_fonts[] = { "ae", "beraserif", "bookman",
129 "ccfonts", "chancery", "charter", "cmr", "fourier", "lmodern", "mathpazo",
130 "mathptmx", "newcent", "utopia", 0};
132 const char * const known_sans_fonts[] = { "avant", "berasans", "cmbr", "cmss",
133 "helvet", "lmss", 0};
135 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
136 "courier", "lmtt", "luximono", "fourier", "lmodern", "mathpazo", "mathptmx",
139 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
140 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
141 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
142 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
143 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
145 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
146 "executivepaper", "legalpaper", "letterpaper", 0};
148 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
149 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
151 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
152 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
155 /// commands that can start an \if...\else...\endif sequence
156 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
157 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
158 "ifsidecap", "ifupgreek", 0};
160 const char * const known_basic_colors[] = {"blue", "black", "cyan", "green",
161 "magenta", "red", "white", "yellow", 0};
163 const char * const known_basic_color_codes[] = {"#0000ff", "#000000", "#00ffff", "#00ff00",
164 "#ff00ff", "#ff0000", "#ffffff", "#ffff00", 0};
166 /// conditional commands with three arguments like \@ifundefined{}{}{}
167 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
171 // returns true if at least one of the options in what has been found
172 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
178 // the last language option is the document language (for babel and LyX)
179 // the last size option is the document font size
180 vector<string>::iterator it;
181 vector<string>::iterator position = opts.begin();
182 for (; *what; ++what) {
183 it = find(opts.begin(), opts.end(), *what);
184 if (it != opts.end()) {
185 if (it >= position) {
196 void delete_opt(vector<string> & opts, char const * const * what)
201 // remove found options from the list
202 // do this after handle_opt to avoid potential memory leaks
203 vector<string>::iterator it;
204 for (; *what; ++what) {
205 it = find(opts.begin(), opts.end(), *what);
206 if (it != opts.end())
213 * Split a package options string (keyval format) into a vector.
215 * authorformat=smallcaps,
217 * titleformat=colonsep,
218 * bibformat={tabular,ibidem,numbered}
220 vector<string> split_options(string const & input)
222 vector<string> options;
226 Token const & t = p.get_token();
227 if (t.asInput() == ",") {
228 options.push_back(trimSpaceAndEol(option));
230 } else if (t.asInput() == "=") {
233 if (p.next_token().asInput() == "{")
234 option += '{' + p.getArg('{', '}') + '}';
235 } else if (t.cat() != catSpace)
236 option += t.asInput();
240 options.push_back(trimSpaceAndEol(option));
247 * Retrieve a keyval option "name={value with=sign}" named \p name from
248 * \p options and return the value.
249 * The found option is also removed from \p options.
251 string process_keyval_opt(vector<string> & options, string name)
253 for (size_t i = 0; i < options.size(); ++i) {
254 vector<string> option;
255 split(options[i], option, '=');
256 if (option.size() < 2)
258 if (option[0] == name) {
259 options.erase(options.begin() + i);
260 option.erase(option.begin());
261 return join(option, "=");
267 } // anonymous namespace
270 bool Preamble::indentParagraphs() const
272 return h_paragraph_separation == "indent";
276 bool Preamble::isPackageUsed(string const & package) const
278 return used_packages.find(package) != used_packages.end();
282 vector<string> Preamble::getPackageOptions(string const & package) const
284 map<string, vector<string> >::const_iterator it = used_packages.find(package);
285 if (it != used_packages.end())
287 return vector<string>();
291 string Preamble::addModules(string const & lyxpreamble, string const & modules)
293 return subst(lyxpreamble, modules_placeholder, modules);
297 void Preamble::add_package(string const & name, vector<string> & options)
299 // every package inherits the global options
300 if (used_packages.find(name) == used_packages.end())
301 used_packages[name] = split_options(h_options);
303 vector<string> & v = used_packages[name];
304 v.insert(v.end(), options.begin(), options.end());
305 if (name == "jurabib") {
306 // Don't output the order argument (see the cite command
307 // handling code in text.cpp).
308 vector<string>::iterator end =
309 remove(options.begin(), options.end(), "natbiborder");
310 end = remove(options.begin(), end, "jurabiborder");
311 options.erase(end, options.end());
318 // Given is a string like "scaled=0.9", return 0.9 * 100
319 string const scale_as_percentage(string const & scale)
321 string::size_type pos = scale.find('=');
322 if (pos != string::npos) {
323 string value = scale.substr(pos + 1);
325 return convert<string>(100 * convert<double>(value));
327 // If the input string didn't match our expectations.
328 // return the default value "100"
333 string remove_braces(string const & value)
337 if (value[0] == '{' && value[value.length()-1] == '}')
338 return value.substr(1, value.length()-2);
342 } // anonymous namespace
345 Preamble::Preamble() : one_language(true), ifundefined_color_set(false)
349 h_cite_engine = "basic";
350 h_defskip = "medskip";
353 h_fontencoding = "default";
354 h_font_roman = "default";
355 h_font_sans = "default";
356 h_font_typewriter = "default";
357 h_font_default_family = "default";
359 h_font_osf = "false";
360 h_font_sf_scale = "100";
361 h_font_tt_scale = "100";
362 h_graphics = "default";
363 h_html_be_strict = "false";
364 h_html_css_as_file = "0";
365 h_html_math_output = "0";
366 h_inputencoding = "auto";
367 h_language = "english";
368 h_language_package = "none";
373 h_output_changes = "false";
374 h_papercolumns = "1";
375 h_paperfontsize = "default";
376 h_paperorientation = "portrait";
377 h_paperpagestyle = "default";
379 h_papersize = "default";
380 h_paragraph_indentation = "default";
381 h_paragraph_separation = "indent";
386 h_pdf_bookmarks = "1";
387 h_pdf_bookmarksnumbered = "0";
388 h_pdf_bookmarksopen = "0";
389 h_pdf_bookmarksopenlevel = "1";
390 h_pdf_breaklinks = "0";
391 h_pdf_pdfborder = "0";
392 h_pdf_colorlinks = "0";
393 h_pdf_backref = "section";
394 h_pdf_pdfusetitle = "1";
396 //h_pdf_quoted_options;
397 h_quotes_language = "english";
399 h_spacing = "single";
400 h_suppress_date = "false";
401 h_textclass = "article";
403 h_tracking_changes = "false";
404 h_use_bibtopic = "false";
405 h_use_geometry = "false";
407 h_use_default_options = "false";
409 h_use_hyperref = "0";
411 h_use_mathdots = "0";
412 h_use_refstyle = "0";
413 h_use_undertilde = "0";
417 void Preamble::handle_hyperref(vector<string> & options)
419 // FIXME swallow inputencoding changes that might surround the
420 // hyperref setup if it was written by LyX
421 h_use_hyperref = "1";
422 // swallow "unicode=true", since LyX does always write that
423 vector<string>::iterator it =
424 find(options.begin(), options.end(), "unicode=true");
425 if (it != options.end())
427 it = find(options.begin(), options.end(), "pdfusetitle");
428 if (it != options.end()) {
429 h_pdf_pdfusetitle = "1";
432 string bookmarks = process_keyval_opt(options, "bookmarks");
433 if (bookmarks == "true")
434 h_pdf_bookmarks = "1";
435 else if (bookmarks == "false")
436 h_pdf_bookmarks = "0";
437 if (h_pdf_bookmarks == "1") {
438 string bookmarksnumbered =
439 process_keyval_opt(options, "bookmarksnumbered");
440 if (bookmarksnumbered == "true")
441 h_pdf_bookmarksnumbered = "1";
442 else if (bookmarksnumbered == "false")
443 h_pdf_bookmarksnumbered = "0";
444 string bookmarksopen =
445 process_keyval_opt(options, "bookmarksopen");
446 if (bookmarksopen == "true")
447 h_pdf_bookmarksopen = "1";
448 else if (bookmarksopen == "false")
449 h_pdf_bookmarksopen = "0";
450 if (h_pdf_bookmarksopen == "1") {
451 string bookmarksopenlevel =
452 process_keyval_opt(options, "bookmarksopenlevel");
453 if (!bookmarksopenlevel.empty())
454 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
457 string breaklinks = process_keyval_opt(options, "breaklinks");
458 if (breaklinks == "true")
459 h_pdf_breaklinks = "1";
460 else if (breaklinks == "false")
461 h_pdf_breaklinks = "0";
462 string pdfborder = process_keyval_opt(options, "pdfborder");
463 if (pdfborder == "{0 0 0}")
464 h_pdf_pdfborder = "1";
465 else if (pdfborder == "{0 0 1}")
466 h_pdf_pdfborder = "0";
467 string backref = process_keyval_opt(options, "backref");
468 if (!backref.empty())
469 h_pdf_backref = backref;
470 string colorlinks = process_keyval_opt(options, "colorlinks");
471 if (colorlinks == "true")
472 h_pdf_colorlinks = "1";
473 else if (colorlinks == "false")
474 h_pdf_colorlinks = "0";
475 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
476 if (!pdfpagemode.empty())
477 h_pdf_pagemode = pdfpagemode;
478 string pdftitle = process_keyval_opt(options, "pdftitle");
479 if (!pdftitle.empty()) {
480 h_pdf_title = remove_braces(pdftitle);
482 string pdfauthor = process_keyval_opt(options, "pdfauthor");
483 if (!pdfauthor.empty()) {
484 h_pdf_author = remove_braces(pdfauthor);
486 string pdfsubject = process_keyval_opt(options, "pdfsubject");
487 if (!pdfsubject.empty())
488 h_pdf_subject = remove_braces(pdfsubject);
489 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
490 if (!pdfkeywords.empty())
491 h_pdf_keywords = remove_braces(pdfkeywords);
492 if (!options.empty()) {
493 if (!h_pdf_quoted_options.empty())
494 h_pdf_quoted_options += ',';
495 h_pdf_quoted_options += join(options, ",");
501 void Preamble::handle_package(Parser &p, string const & name,
502 string const & opts, bool in_lyx_preamble)
504 vector<string> options = split_options(opts);
505 add_package(name, options);
509 if (is_known(name, known_roman_fonts)) {
514 if (name == "fourier") {
515 h_font_roman = "utopia";
516 // when font uses real small capitals
517 if (opts == "expert")
521 if (name == "mathpazo")
522 h_font_roman = "palatino";
524 if (name == "mathptmx")
525 h_font_roman = "times";
528 if (is_known(name, known_sans_fonts)) {
532 h_font_sf_scale = scale_as_percentage(scale);
537 if (is_known(name, known_typewriter_fonts)) {
538 // fourier can be set as roman font _only_
539 // fourier as typewriter is handled in handling of \ttdefault
540 if (name != "fourier") {
541 h_font_typewriter = name;
544 h_font_tt_scale = scale_as_percentage(scale);
549 // font uses old-style figure
553 // after the detection and handling of special cases, we can remove the
554 // fonts, otherwise they would appear in the preamble, see bug #7856
555 if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
556 || is_known(name, known_typewriter_fonts))
559 else if (name == "amsmath" || name == "amssymb")
562 else if (name == "esint")
565 else if (name == "mhchem")
568 else if (name == "mathdots")
569 h_use_mathdots = "2";
571 else if (name == "undertilde")
572 h_use_undertilde = "2";
574 else if (name == "babel") {
575 h_language_package = "default";
576 // One might think we would have to do nothing if babel is loaded
577 // without any options to prevent pollution of the preamble with this
578 // babel call in every roundtrip.
579 // But the user could have defined babel-specific things afterwards. So
580 // we need to keep it in the preamble to prevent cases like bug #7861.
582 // check if more than one option was used - used later for inputenc
583 // in case inputenc is parsed before babel, set the encoding to auto
584 if (options.begin() != options.end() - 1)
585 one_language = false;
586 // babel takes the last language of the option of its \usepackage
587 // call as document language. If there is no such language option, the
588 // last language in the documentclass options is used.
589 handle_opt(options, known_languages, h_language);
590 // If babel is called with options, LyX puts them by default into the
591 // document class options. This works for most languages, except
592 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
593 // perhaps in future others.
594 // Therefore keep the babel call as it is as the user might have
596 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
597 delete_opt(options, known_languages);
600 h_preamble << "\\usepackage{babel}\n";
603 else if (name == "fontenc") {
604 h_fontencoding = getStringFromVector(options, ",");
605 /* We could do the following for better round trip support,
606 * but this makes the document less portable, so I skip it:
607 if (h_fontencoding == lyxrc.fontenc)
608 h_fontencoding = "global";
613 else if (name == "inputenc" || name == "luainputenc") {
614 // h_inputencoding is only set when there is not more than one
615 // inputenc option because otherwise h_inputencoding must be
616 // set to "auto" (the default encoding of the document language)
617 // Therefore check for the "," character.
618 // It is also only set when there is not more then one babel
619 // language option but this is handled in the routine for babel.
620 if (opts.find(",") == string::npos && one_language == true)
621 h_inputencoding = opts;
622 if (!options.empty())
623 p.setEncoding(options.back());
627 else if (is_known(name, known_old_language_packages)) {
628 // known language packages from the times before babel
629 // if they are found and not also babel, they will be used as
630 // cutom language package
631 h_language_package = "\\usepackage{" + name + "}";
634 else if (name == "makeidx")
637 else if (name == "prettyref")
640 else if (name == "varioref")
643 else if (name == "verbatim")
646 else if (name == "nomencl")
649 else if (name == "textcomp")
652 else if (name == "url")
655 else if (name == "subscript")
658 else if (name == "color") {
659 // with the following command this package is only loaded when needed for
660 // undefined colors, since we only support the predefined colors
661 // only add it if not yet added
662 if (!ifundefined_color_set)
663 h_preamble << "\\@ifundefined{definecolor}\n {\\usepackage{color}}{}\n";
666 else if (name == "graphicx")
669 else if (name == "setspace")
673 // do not ignore as long as we don't support all commands (e.g. \xout is missing)
674 // and as long as we don't support change tracking
675 else if (name == "ulem")
679 else if (name == "geometry")
680 ; // Ignore this, the geometry settings are made by the \geometry
681 // command. This command is handled below.
683 else if (name == "rotfloat")
686 else if (name == "wrapfig")
689 else if (name == "subfig")
692 else if (is_known(name, known_languages))
695 else if (name == "natbib") {
696 h_cite_engine = "natbib_authoryear";
697 vector<string>::iterator it =
698 find(options.begin(), options.end(), "authoryear");
699 if (it != options.end())
702 it = find(options.begin(), options.end(), "numbers");
703 if (it != options.end()) {
704 h_cite_engine = "natbib_numerical";
710 else if (name == "jurabib")
711 h_cite_engine = "jurabib";
713 else if (name == "hyperref")
714 handle_hyperref(options);
716 else if (!in_lyx_preamble) {
718 h_preamble << "\\usepackage{" << name << "}";
720 h_preamble << "\\usepackage[" << opts << "]{"
726 // We need to do something with the options...
727 if (!options.empty())
728 cerr << "Ignoring options '" << join(options, ",")
729 << "' of package " << name << '.' << endl;
731 // remove the whitespace
736 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
739 Token t = p.get_token();
740 if (t.cat() == catEscape &&
741 is_known(t.cs(), known_if_commands))
742 handle_if(p, in_lyx_preamble);
744 if (!in_lyx_preamble)
745 h_preamble << t.asInput();
746 if (t.cat() == catEscape && t.cs() == "fi")
753 void Preamble::end_preamble(ostream & os, TeX2LyXDocClass const & /*tc*/)
755 // translate from babel to LyX names
756 h_language = babel2lyx(h_language);
758 // set the quote language
759 // LyX only knows the following quotes languages:
760 // english, swedish, german, polish, french and danish
761 // (quotes for "japanese" and "chinese-traditional" are missing because
762 // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
763 // conversion list taken from
764 // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
765 // (quotes for kazakh and interlingua are unknown)
767 if (h_language == "danish")
768 h_quotes_language = "danish";
770 else if (is_known(h_language, known_french_quotes_languages))
771 h_quotes_language = "french";
773 else if (is_known(h_language, known_german_quotes_languages))
774 h_quotes_language = "german";
776 else if (is_known(h_language, known_polish_quotes_languages))
777 h_quotes_language = "polish";
779 else if (is_known(h_language, known_swedish_quotes_languages))
780 h_quotes_language = "swedish";
782 else if (is_known(h_language, known_english_quotes_languages))
783 h_quotes_language = "english";
785 // output the LyX file settings
786 os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
787 << "\\lyxformat " << LYX_FORMAT << '\n'
788 << "\\begin_document\n"
789 << "\\begin_header\n"
790 << "\\textclass " << h_textclass << "\n";
791 if (!h_preamble.str().empty())
792 os << "\\begin_preamble\n" << h_preamble.str() << "\n\\end_preamble\n";
793 if (!h_options.empty())
794 os << "\\options " << h_options << "\n";
795 os << "\\use_default_options " << h_use_default_options << "\n"
796 << modules_placeholder
797 << "\\language " << h_language << "\n"
798 << "\\language_package " << h_language_package << "\n"
799 << "\\inputencoding " << h_inputencoding << "\n"
800 << "\\fontencoding " << h_fontencoding << "\n"
801 << "\\font_roman " << h_font_roman << "\n"
802 << "\\font_sans " << h_font_sans << "\n"
803 << "\\font_typewriter " << h_font_typewriter << "\n"
804 << "\\font_default_family " << h_font_default_family << "\n"
805 << "\\font_sc " << h_font_sc << "\n"
806 << "\\font_osf " << h_font_osf << "\n"
807 << "\\font_sf_scale " << h_font_sf_scale << "\n"
808 << "\\font_tt_scale " << h_font_tt_scale << "\n"
809 << "\\graphics " << h_graphics << "\n";
810 if (!h_float_placement.empty())
811 os << "\\float_placement " << h_float_placement << "\n";
812 os << "\\paperfontsize " << h_paperfontsize << "\n"
813 << "\\spacing " << h_spacing << "\n"
814 << "\\use_hyperref " << h_use_hyperref << '\n';
815 if (h_use_hyperref == "1") {
816 if (!h_pdf_title.empty())
817 os << "\\pdf_title \"" << h_pdf_title << "\"\n";
818 if (!h_pdf_author.empty())
819 os << "\\pdf_author \"" << h_pdf_author << "\"\n";
820 if (!h_pdf_subject.empty())
821 os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
822 if (!h_pdf_keywords.empty())
823 os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
824 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
825 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
826 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
827 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
828 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
829 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
830 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
831 "\\pdf_backref " << h_pdf_backref << "\n"
832 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
833 if (!h_pdf_pagemode.empty())
834 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
835 if (!h_pdf_quoted_options.empty())
836 os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
838 os << "\\papersize " << h_papersize << "\n"
839 << "\\use_geometry " << h_use_geometry << "\n"
840 << "\\use_amsmath " << h_use_amsmath << "\n"
841 << "\\use_esint " << h_use_esint << "\n"
842 << "\\use_mhchem " << h_use_mhchem << "\n"
843 << "\\use_mathdots " << h_use_mathdots << "\n"
844 << "\\use_undertilde " << h_use_undertilde << "\n"
845 << "\\cite_engine " << h_cite_engine << "\n"
846 << "\\use_bibtopic " << h_use_bibtopic << "\n"
847 << "\\paperorientation " << h_paperorientation << '\n'
848 << "\\suppress_date " << h_suppress_date << '\n'
849 << "\\use_refstyle " << h_use_refstyle << '\n';
850 if (!h_fontcolor.empty())
851 os << "\\fontcolor " << h_fontcolor << '\n';
852 if (!h_notefontcolor.empty())
853 os << "\\notefontcolor " << h_notefontcolor << '\n';
854 if (!h_backgroundcolor.empty())
855 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
856 if (!h_boxbgcolor.empty())
857 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
859 << "\\secnumdepth " << h_secnumdepth << "\n"
860 << "\\tocdepth " << h_tocdepth << "\n"
861 << "\\paragraph_separation " << h_paragraph_separation << "\n";
862 if (h_paragraph_separation == "skip")
863 os << "\\defskip " << h_defskip << "\n";
865 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
866 os << "\\quotes_language " << h_quotes_language << "\n"
867 << "\\papercolumns " << h_papercolumns << "\n"
868 << "\\papersides " << h_papersides << "\n"
869 << "\\paperpagestyle " << h_paperpagestyle << "\n";
870 if (!h_listings_params.empty())
871 os << "\\listings_params " << h_listings_params << "\n";
872 os << "\\tracking_changes " << h_tracking_changes << "\n"
873 << "\\output_changes " << h_output_changes << "\n"
874 << "\\html_math_output " << h_html_math_output << "\n"
875 << "\\html_css_as_file " << h_html_css_as_file << "\n"
876 << "\\html_be_strict " << h_html_be_strict << "\n"
877 << "\\end_header\n\n"
879 // clear preamble for subdocuments
884 void Preamble::parse(Parser & p, ostream & os,
885 string const & forceclass, TeX2LyXDocClass & tc)
887 // initialize fixed types
888 special_columns['D'] = 3;
889 bool is_full_document = false;
890 bool is_lyx_file = false;
891 bool in_lyx_preamble = false;
893 // determine whether this is a full document or a fragment for inclusion
895 Token const & t = p.get_token();
897 if (t.cat() == catEscape && t.cs() == "documentclass") {
898 is_full_document = true;
904 while (is_full_document && p.good()) {
905 Token const & t = p.get_token();
908 cerr << "t: " << t << "\n";
914 if (!in_lyx_preamble &&
915 (t.cat() == catLetter ||
916 t.cat() == catSuper ||
918 t.cat() == catOther ||
919 t.cat() == catMath ||
920 t.cat() == catActive ||
921 t.cat() == catBegin ||
923 t.cat() == catAlign ||
924 t.cat() == catParameter))
925 h_preamble << t.cs();
927 else if (!in_lyx_preamble &&
928 (t.cat() == catSpace || t.cat() == catNewline))
929 h_preamble << t.asInput();
931 else if (t.cat() == catComment) {
932 static regex const islyxfile("%% LyX .* created this file");
933 static regex const usercommands("User specified LaTeX commands");
935 string const comment = t.asInput();
937 // magically switch encoding default if it looks like XeLaTeX
938 static string const magicXeLaTeX =
939 "% This document must be compiled with XeLaTeX ";
940 if (comment.size() > magicXeLaTeX.size()
941 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
942 && h_inputencoding == "auto") {
943 cerr << "XeLaTeX comment found, switching to UTF8\n";
944 h_inputencoding = "utf8";
947 if (regex_search(comment, sub, islyxfile)) {
949 in_lyx_preamble = true;
950 } else if (is_lyx_file
951 && regex_search(comment, sub, usercommands))
952 in_lyx_preamble = false;
953 else if (!in_lyx_preamble)
954 h_preamble << t.asInput();
957 else if (t.cs() == "pagestyle")
958 h_paperpagestyle = p.verbatim_item();
960 else if (t.cs() == "date") {
961 string argument = p.getArg('{', '}');
962 if (argument.empty())
963 h_suppress_date = "true";
965 h_preamble << t.asInput() << '{' << argument << '}';
968 else if (t.cs() == "color") {
969 string argument = p.getArg('{', '}');
970 // check the case that a standard color is used
971 if (is_known(argument, known_basic_colors))
972 h_fontcolor = color2code(argument);
973 // check the case that LyX's document_fontcolor is defined
974 // but not used for \color
975 if (argument != "document_fontcolor"
976 && !is_known(argument, known_basic_colors)) {
977 h_preamble << t.asInput() << '{' << argument << '}';
978 // the color might already be set because \definecolor
979 // is parsed before this
984 else if (t.cs() == "pagecolor") {
985 string argument = p.getArg('{', '}');
986 // check the case that a standard color is used
987 if (is_known(argument, known_basic_colors))
988 h_backgroundcolor = color2code(argument);
989 // check the case that LyX's page_backgroundcolor is defined
990 // but not used for \pagecolor
991 if (argument != "page_backgroundcolor"
992 && !is_known(argument, known_basic_colors)) {
993 h_preamble << t.asInput() << '{' << argument << '}';
994 // the color might already be set because \definecolor
995 // is parsed before this
996 h_backgroundcolor = "";
1000 else if (t.cs() == "makeatletter") {
1001 // LyX takes care of this
1002 p.setCatCode('@', catLetter);
1005 else if (t.cs() == "makeatother") {
1006 // LyX takes care of this
1007 p.setCatCode('@', catOther);
1010 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1011 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1012 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1013 || t.cs() == "DeclareRobustCommand"
1014 || t.cs() == "DeclareRobustCommandx"
1015 || t.cs() == "ProvideTextCommandDefault"
1016 || t.cs() == "DeclareMathAccent") {
1018 if (p.next_token().character() == '*') {
1022 string const name = p.verbatim_item();
1023 string const opt1 = p.getFullOpt();
1024 string const opt2 = p.getFullOpt();
1025 string const body = p.verbatim_item();
1027 if (name == "\\rmdefault")
1028 if (is_known(body, known_roman_fonts))
1029 h_font_roman = body;
1030 if (name == "\\sfdefault")
1031 if (is_known(body, known_sans_fonts))
1033 if (name == "\\ttdefault")
1034 if (is_known(body, known_typewriter_fonts))
1035 h_font_typewriter = body;
1036 if (name == "\\familydefault") {
1037 string family = body;
1038 // remove leading "\"
1039 h_font_default_family = family.erase(0,1);
1042 // Add the command to the known commands
1043 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1045 // only non-lyxspecific stuff
1046 if (!in_lyx_preamble) {
1048 ss << '\\' << t.cs();
1051 ss << '{' << name << '}' << opt1 << opt2
1052 << '{' << body << "}";
1053 h_preamble << ss.str();
1055 ostream & out = in_preamble ? h_preamble : os;
1056 out << "\\" << t.cs() << "{" << name << "}"
1057 << opts << "{" << body << "}";
1062 else if (t.cs() == "documentclass") {
1063 vector<string>::iterator it;
1064 vector<string> opts = split_options(p.getArg('[', ']'));
1065 handle_opt(opts, known_fontsizes, h_paperfontsize);
1066 delete_opt(opts, known_fontsizes);
1067 // delete "pt" at the end
1068 string::size_type i = h_paperfontsize.find("pt");
1069 if (i != string::npos)
1070 h_paperfontsize.erase(i);
1071 // The documentclass options are always parsed before the options
1072 // of the babel call so that a language cannot overwrite the babel
1074 handle_opt(opts, known_languages, h_language);
1075 delete_opt(opts, known_languages);
1077 // paper orientation
1078 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1079 h_paperorientation = "landscape";
1083 if ((it = find(opts.begin(), opts.end(), "oneside"))
1088 if ((it = find(opts.begin(), opts.end(), "twoside"))
1094 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1096 h_papercolumns = "1";
1099 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1101 h_papercolumns = "2";
1105 // some size options are know to any document classes, other sizes
1106 // are handled by the \geometry command of the geometry package
1107 handle_opt(opts, known_class_paper_sizes, h_papersize);
1108 delete_opt(opts, known_class_paper_sizes);
1109 // the remaining options
1110 h_options = join(opts, ",");
1111 // FIXME This does not work for classes that have a
1112 // different name in LyX than in LaTeX
1113 h_textclass = p.getArg('{', '}');
1116 else if (t.cs() == "usepackage") {
1117 string const options = p.getArg('[', ']');
1118 string const name = p.getArg('{', '}');
1119 vector<string> vecnames;
1120 split(name, vecnames, ',');
1121 vector<string>::const_iterator it = vecnames.begin();
1122 vector<string>::const_iterator end = vecnames.end();
1123 for (; it != end; ++it)
1124 handle_package(p, trimSpaceAndEol(*it), options,
1128 else if (t.cs() == "inputencoding") {
1129 string const encoding = p.getArg('{','}');
1130 h_inputencoding = encoding;
1131 p.setEncoding(encoding);
1134 else if (t.cs() == "newenvironment") {
1135 string const name = p.getArg('{', '}');
1136 string const opt1 = p.getFullOpt();
1137 string const opt2 = p.getFullOpt();
1138 string const beg = p.verbatim_item();
1139 string const end = p.verbatim_item();
1140 if (!in_lyx_preamble) {
1141 h_preamble << "\\newenvironment{" << name
1142 << '}' << opt1 << opt2 << '{'
1143 << beg << "}{" << end << '}';
1145 add_known_environment(name, opt1, !opt2.empty(),
1146 from_utf8(beg), from_utf8(end));
1150 else if (t.cs() == "def") {
1151 string name = p.get_token().cs();
1152 while (p.next_token().cat() != catBegin)
1153 name += p.get_token().cs();
1154 if (!in_lyx_preamble)
1155 h_preamble << "\\def\\" << name << '{'
1156 << p.verbatim_item() << "}";
1159 else if (t.cs() == "newcolumntype") {
1160 string const name = p.getArg('{', '}');
1161 trimSpaceAndEol(name);
1163 string opts = p.getOpt();
1164 if (!opts.empty()) {
1165 istringstream is(string(opts, 1));
1168 special_columns[name[0]] = nargs;
1169 h_preamble << "\\newcolumntype{" << name << "}";
1171 h_preamble << "[" << nargs << "]";
1172 h_preamble << "{" << p.verbatim_item() << "}";
1175 else if (t.cs() == "setcounter") {
1176 string const name = p.getArg('{', '}');
1177 string const content = p.getArg('{', '}');
1178 if (name == "secnumdepth")
1179 h_secnumdepth = content;
1180 else if (name == "tocdepth")
1181 h_tocdepth = content;
1183 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1186 else if (t.cs() == "setlength") {
1187 string const name = p.verbatim_item();
1188 string const content = p.verbatim_item();
1189 // the paragraphs are only not indented when \parindent is set to zero
1190 if (name == "\\parindent" && content != "") {
1191 if (content[0] == '0')
1192 h_paragraph_separation = "skip";
1194 h_paragraph_indentation = translate_len(content);
1195 } else if (name == "\\parskip") {
1196 if (content == "\\smallskipamount")
1197 h_defskip = "smallskip";
1198 else if (content == "\\medskipamount")
1199 h_defskip = "medskip";
1200 else if (content == "\\bigskipamount")
1201 h_defskip = "bigskip";
1203 h_defskip = content;
1205 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1208 else if (t.cs() == "onehalfspacing")
1209 h_spacing = "onehalf";
1211 else if (t.cs() == "doublespacing")
1212 h_spacing = "double";
1214 else if (t.cs() == "setstretch")
1215 h_spacing = "other " + p.verbatim_item();
1217 else if (t.cs() == "begin") {
1218 string const name = p.getArg('{', '}');
1219 if (name == "document")
1221 h_preamble << "\\begin{" << name << "}";
1224 else if (t.cs() == "geometry") {
1225 h_use_geometry = "true";
1226 vector<string> opts = split_options(p.getArg('{', '}'));
1227 vector<string>::iterator it;
1228 // paper orientation
1229 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1230 h_paperorientation = "landscape";
1234 handle_opt(opts, known_paper_sizes, h_papersize);
1235 delete_opt(opts, known_paper_sizes);
1237 char const * const * margin = known_paper_margins;
1239 for (; *margin; ++margin) {
1241 // search for the "=" in e.g. "lmargin=2cm" to get the value
1242 for(size_t i = 0; i != opts.size(); i++) {
1243 if (opts.at(i).find(*margin) != string::npos) {
1244 string::size_type pos = opts.at(i).find("=");
1245 string value = opts.at(i).substr(pos + 1);
1246 string name = known_coded_paper_margins[k];
1247 h_margins += "\\" + name + " " + value + "\n";
1253 else if (t.cs() == "definecolor") {
1254 string const color = p.getArg('{', '}');
1255 string const space = p.getArg('{', '}');
1256 string const value = p.getArg('{', '}');
1257 if (color == "document_fontcolor" && space == "rgb") {
1258 RGBColor c(RGBColorFromLaTeX(value));
1259 h_fontcolor = X11hexname(c);
1260 } else if (color == "note_fontcolor" && space == "rgb") {
1261 RGBColor c(RGBColorFromLaTeX(value));
1262 h_notefontcolor = X11hexname(c);
1263 } else if (color == "page_backgroundcolor" && space == "rgb") {
1264 RGBColor c(RGBColorFromLaTeX(value));
1265 h_backgroundcolor = X11hexname(c);
1266 } else if (color == "shadecolor" && space == "rgb") {
1267 RGBColor c(RGBColorFromLaTeX(value));
1268 h_boxbgcolor = X11hexname(c);
1270 h_preamble << "\\definecolor{" << color
1271 << "}{" << space << "}{" << value
1276 else if (t.cs() == "jurabibsetup") {
1277 // FIXME p.getArg('{', '}') is most probably wrong (it
1278 // does not handle nested braces).
1279 // Use p.verbatim_item() instead.
1280 vector<string> jurabibsetup =
1281 split_options(p.getArg('{', '}'));
1282 // add jurabibsetup to the jurabib package options
1283 add_package("jurabib", jurabibsetup);
1284 if (!jurabibsetup.empty()) {
1285 h_preamble << "\\jurabibsetup{"
1286 << join(jurabibsetup, ",") << '}';
1290 else if (t.cs() == "hypersetup") {
1291 vector<string> hypersetup =
1292 split_options(p.verbatim_item());
1293 // add hypersetup to the hyperref package options
1294 handle_hyperref(hypersetup);
1295 if (!hypersetup.empty()) {
1296 h_preamble << "\\hypersetup{"
1297 << join(hypersetup, ",") << '}';
1301 else if (is_known(t.cs(), known_if_3arg_commands)) {
1302 // prevent misparsing of \usepackage if it is used
1303 // as an argument (see e.g. our own output of
1304 // \@ifundefined above)
1305 string const arg1 = p.verbatim_item();
1306 string const arg2 = p.verbatim_item();
1307 string const arg3 = p.verbatim_item();
1308 // test case \@ifundefined{date}{}{\date{}}
1309 if (arg1 == "date" && arg2.empty() && arg3 == "\\date{}") {
1310 h_suppress_date = "true";
1311 // test case \@ifundefined{definecolor}{\usepackage{color}}{}
1312 // because we could pollute the preamble with it in roundtrips
1313 } else if (arg1 == "definecolor" && arg2 == "\\usepackage{color}"
1315 ifundefined_color_set = true;
1317 //\@ifundefined{showcaptionsetup}{}{%
1318 // \PassOptionsToPackage{caption=false}{subfig}}
1319 // that LyX uses for subfloats
1320 } else if (arg1 == "showcaptionsetup" && arg2.empty()
1321 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1323 } else if (!in_lyx_preamble) {
1324 h_preamble << t.asInput()
1325 << '{' << arg1 << '}'
1326 << '{' << arg2 << '}'
1327 << '{' << arg3 << '}';
1331 else if (is_known(t.cs(), known_if_commands)) {
1332 // must not parse anything in conditional code, since
1333 // LyX would output the parsed contents unconditionally
1334 if (!in_lyx_preamble)
1335 h_preamble << t.asInput();
1336 handle_if(p, in_lyx_preamble);
1339 else if (!t.cs().empty() && !in_lyx_preamble)
1340 h_preamble << '\\' << t.cs();
1343 // remove the whitespace
1346 // Force textclass if the user wanted it
1347 if (!forceclass.empty())
1348 h_textclass = forceclass;
1349 if (noweb_mode && !prefixIs(h_textclass, "literate-"))
1350 h_textclass.insert(0, "literate-");
1351 tc.setName(h_textclass);
1353 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1356 if (h_papersides.empty()) {
1359 h_papersides = ss.str();
1361 end_preamble(os, tc);
1365 string babel2lyx(string const & language)
1367 char const * const * where = is_known(language, known_languages);
1369 return known_coded_languages[where - known_languages];
1374 string color2code(string const & name)
1376 char const * const * where = is_known(name, known_basic_colors);
1378 return known_basic_color_codes[where - known_basic_colors];