3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
9 * Full author contact details are available in file CREDITS.
19 #include "LayoutFile.h"
22 #include "TextClass.h"
24 #include "support/convert.h"
25 #include "support/FileName.h"
26 #include "support/filetools.h"
27 #include "support/lstrings.h"
29 #include "support/regex.h"
35 using namespace lyx::support;
40 // special columntypes
41 extern map<char, int> special_columns;
47 //add this to known_languages when updating to lyxformat 266:
48 // "armenian" (needs special handling since not supported by standard babel)
49 //add these to known_languages when updating to lyxformat 268:
50 //"chinese-simplified", "chinese-traditional", "japanese", "korean"
51 // Both changes require first that support for non-babel languages (CJK,
54 * known babel language names (including synonyms)
55 * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
56 * not yet supported by LyX: kurmanji
57 * please keep this in sync with known_coded_languages line by line!
59 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
60 "american", "arabic", "arabtex", "austrian", "bahasa", "bahasai", "bahasam",
61 "basque", "belarusian", "brazil", "brazilian", "breton", "british", "bulgarian",
62 "canadian", "canadien", "catalan", "croatian", "czech", "danish", "dutch",
63 "english", "esperanto", "estonian", "farsi", "finnish", "francais", "french",
64 "frenchb", "frenchle", "frenchpro", "galician", "german", "germanb", "greek",
65 "hebrew", "hungarian", "icelandic", "indon", "indonesian", "interlingua",
66 "irish", "italian", "kazakh", "latin", "latvian", "lithuanian", "lowersorbian",
67 "lsorbian", "magyar", "malay", "meyalu", "mongolian", "naustrian", "newzealand",
68 "ngerman", "ngermanb", "norsk", "nynorsk", "polutonikogreek", "polish",
69 "portuges", "portuguese", "romanian", "russian", "russianb", "samin",
70 "scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish",
71 "swedish", "thai", "turkish", "turkmen", "ukraineb", "ukrainian",
72 "uppersorbian", "UKenglish", "USenglish", "usorbian", "vietnam", "welsh",
76 * the same as known_languages with .lyx names
77 * please keep this in sync with known_languages line by line!
79 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
80 "american", "arabic_arabi", "arabic_arabtex", "austrian", "bahasa", "bahasa", "bahasam",
81 "basque", "belarusian", "brazilian", "brazilian", "breton", "british", "bulgarian",
82 "canadian", "canadien", "catalan", "croatian", "czech", "danish", "dutch",
83 "english", "esperanto", "estonian", "farsi", "finnish", "french", "french",
84 "french", "french", "french", "galician", "german", "german", "greek",
85 "hebrew", "magyar", "icelandic", "bahasa", "bahasa", "interlingua",
86 "irish", "italian", "kazakh", "latin", "latvian", "lithuanian", "lowersorbian",
87 "lowersorbian", "magyar", "bahasam", "bahasam", "mongolian", "naustrian", "english",
88 "ngerman", "ngerman", "norsk", "nynorsk", "polutonikogreek", "polish",
89 "portuguese", "portuguese", "romanian", "russian", "russian", "samin",
90 "scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish",
91 "swedish", "thai", "turkish", "turkmen", "ukrainian", "ukrainian",
92 "uppersorbian", "uppersorbian", "english", "english", "vietnamese", "welsh",
95 /// languages with english quotes (.lyx names)
96 const char * const known_english_quotes_languages[] = {"american", "bahasa",
97 "bahasam", "brazilian", "canadian", "chinese-simplified", "english",
98 "esperanto", "hebrew", "irish", "korean", "portuguese", "scottish", "thai", 0};
100 /// languages with french quotes (.lyx names)
101 const char * const known_french_quotes_languages[] = {"albanian",
102 "arabic_arabi", "arabic_arabtex", "basque", "canadien", "catalan", "french",
103 "galician", "greek", "italian", "norsk", "nynorsk", "polutonikogreek",
104 "russian", "spanish", "spanish-mexico", "turkish", "turkmen", "ukrainian",
107 /// languages with german quotes (.lyx names)
108 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
109 "czech", "german", "icelandic", "lithuanian", "lowersorbian", "naustrian",
110 "ngerman", "serbian", "serbian-latin", "slovak", "slovene", "uppersorbian", 0};
112 /// languages with polish quotes (.lyx names)
113 const char * const known_polish_quotes_languages[] = {"afrikaans", "croatian",
114 "dutch", "estonian", "magyar", "polish", "romanian", 0};
116 /// languages with swedish quotes (.lyx names)
117 const char * const known_swedish_quotes_languages[] = {"finnish",
120 /// known language packages from the times before babel
121 const char * const known_old_language_packages[] = {"french", "frenchle",
122 "frenchpro", "german", "ngerman", "pmfrench", 0};
124 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
126 const char * const known_roman_fonts[] = { "ae", "beraserif", "bookman",
127 "ccfonts", "chancery", "charter", "cmr", "fourier", "lmodern", "mathpazo",
128 "mathptmx", "newcent", "utopia", 0};
130 const char * const known_sans_fonts[] = { "avant", "berasans", "cmbr", "cmss",
131 "helvet", "lmss", 0};
133 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
134 "courier", "lmtt", "luximono", "fourier", "lmodern", "mathpazo", "mathptmx",
137 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
138 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
139 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
140 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
141 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
143 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
144 "executivepaper", "legalpaper", "letterpaper", 0};
146 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
147 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
149 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
150 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
153 /// commands that can start an \if...\else...\endif sequence
154 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
155 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
156 "ifsidecap", "ifupgreek", 0};
158 const char * const known_basic_colors[] = {"blue", "black", "cyan", "green",
159 "magenta", "red", "white", "yellow", 0};
161 const char * const known_basic_color_codes[] = {"#0000ff", "#000000", "#00ffff", "#00ff00",
162 "#ff00ff", "#ff0000", "#ffffff", "#ffff00", 0};
164 /// conditional commands with three arguments like \@ifundefined{}{}{}
165 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
169 // returns true if at least one of the options in what has been found
170 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
176 // the last language option is the document language (for babel and LyX)
177 // the last size option is the document font size
178 vector<string>::iterator it;
179 vector<string>::iterator position = opts.begin();
180 for (; *what; ++what) {
181 it = find(opts.begin(), opts.end(), *what);
182 if (it != opts.end()) {
183 if (it >= position) {
194 void delete_opt(vector<string> & opts, char const * const * what)
199 // remove found options from the list
200 // do this after handle_opt to avoid potential memory leaks
201 vector<string>::iterator it;
202 for (; *what; ++what) {
203 it = find(opts.begin(), opts.end(), *what);
204 if (it != opts.end())
211 * Split a package options string (keyval format) into a vector.
213 * authorformat=smallcaps,
215 * titleformat=colonsep,
216 * bibformat={tabular,ibidem,numbered}
218 vector<string> split_options(string const & input)
220 vector<string> options;
224 Token const & t = p.get_token();
225 if (t.asInput() == ",") {
226 options.push_back(trimSpaceAndEol(option));
228 } else if (t.asInput() == "=") {
231 if (p.next_token().asInput() == "{")
232 option += '{' + p.getArg('{', '}') + '}';
233 } else if (t.cat() != catSpace)
234 option += t.asInput();
238 options.push_back(trimSpaceAndEol(option));
245 * Retrieve a keyval option "name={value with=sign}" named \p name from
246 * \p options and return the value.
247 * The found option is also removed from \p options.
249 string process_keyval_opt(vector<string> & options, string name)
251 for (size_t i = 0; i < options.size(); ++i) {
252 vector<string> option;
253 split(options[i], option, '=');
254 if (option.size() < 2)
256 if (option[0] == name) {
257 options.erase(options.begin() + i);
258 option.erase(option.begin());
259 return join(option, "=");
265 } // anonymous namespace
268 bool Preamble::indentParagraphs() const
270 return h_paragraph_separation == "indent";
274 bool Preamble::isPackageUsed(string const & package) const
276 return used_packages.find(package) != used_packages.end();
280 vector<string> Preamble::getPackageOptions(string const & package) const
282 map<string, vector<string> >::const_iterator it = used_packages.find(package);
283 if (it != used_packages.end())
285 return vector<string>();
289 void Preamble::addModule(string const & module)
291 used_modules.push_back(module);
295 void Preamble::suppressDate(bool suppress)
298 h_suppress_date = "true";
300 h_suppress_date = "false";
304 void Preamble::add_package(string const & name, vector<string> & options)
306 // every package inherits the global options
307 if (used_packages.find(name) == used_packages.end())
308 used_packages[name] = split_options(h_options);
310 vector<string> & v = used_packages[name];
311 v.insert(v.end(), options.begin(), options.end());
312 if (name == "jurabib") {
313 // Don't output the order argument (see the cite command
314 // handling code in text.cpp).
315 vector<string>::iterator end =
316 remove(options.begin(), options.end(), "natbiborder");
317 end = remove(options.begin(), end, "jurabiborder");
318 options.erase(end, options.end());
325 // Given is a string like "scaled=0.9", return 0.9 * 100
326 string const scale_as_percentage(string const & scale)
328 string::size_type pos = scale.find('=');
329 if (pos != string::npos) {
330 string value = scale.substr(pos + 1);
332 return convert<string>(100 * convert<double>(value));
334 // If the input string didn't match our expectations.
335 // return the default value "100"
340 string remove_braces(string const & value)
344 if (value[0] == '{' && value[value.length()-1] == '}')
345 return value.substr(1, value.length()-2);
349 } // anonymous namespace
352 Preamble::Preamble() : one_language(true), ifundefined_color_set(false)
356 h_cite_engine = "basic";
357 h_defskip = "medskip";
360 h_fontencoding = "default";
361 h_font_roman = "default";
362 h_font_sans = "default";
363 h_font_typewriter = "default";
364 h_font_default_family = "default";
366 h_font_osf = "false";
367 h_font_sf_scale = "100";
368 h_font_tt_scale = "100";
369 h_graphics = "default";
370 h_html_be_strict = "false";
371 h_html_css_as_file = "0";
372 h_html_math_output = "0";
373 h_inputencoding = "auto";
374 h_language = "english";
375 h_language_package = "none";
380 h_output_changes = "false";
381 h_papercolumns = "1";
382 h_paperfontsize = "default";
383 h_paperorientation = "portrait";
384 h_paperpagestyle = "default";
386 h_papersize = "default";
387 h_paragraph_indentation = "default";
388 h_paragraph_separation = "indent";
393 h_pdf_bookmarks = "1";
394 h_pdf_bookmarksnumbered = "0";
395 h_pdf_bookmarksopen = "0";
396 h_pdf_bookmarksopenlevel = "1";
397 h_pdf_breaklinks = "0";
398 h_pdf_pdfborder = "0";
399 h_pdf_colorlinks = "0";
400 h_pdf_backref = "section";
401 h_pdf_pdfusetitle = "1";
403 //h_pdf_quoted_options;
404 h_quotes_language = "english";
406 h_spacing = "single";
407 h_suppress_date = "false";
408 h_textclass = "article";
410 h_tracking_changes = "false";
411 h_use_bibtopic = "false";
412 h_use_geometry = "false";
414 h_use_default_options = "false";
416 h_use_hyperref = "0";
418 h_use_mathdots = "0";
419 h_use_refstyle = "0";
420 h_use_undertilde = "0";
424 void Preamble::handle_hyperref(vector<string> & options)
426 // FIXME swallow inputencoding changes that might surround the
427 // hyperref setup if it was written by LyX
428 h_use_hyperref = "1";
429 // swallow "unicode=true", since LyX does always write that
430 vector<string>::iterator it =
431 find(options.begin(), options.end(), "unicode=true");
432 if (it != options.end())
434 it = find(options.begin(), options.end(), "pdfusetitle");
435 if (it != options.end()) {
436 h_pdf_pdfusetitle = "1";
439 string bookmarks = process_keyval_opt(options, "bookmarks");
440 if (bookmarks == "true")
441 h_pdf_bookmarks = "1";
442 else if (bookmarks == "false")
443 h_pdf_bookmarks = "0";
444 if (h_pdf_bookmarks == "1") {
445 string bookmarksnumbered =
446 process_keyval_opt(options, "bookmarksnumbered");
447 if (bookmarksnumbered == "true")
448 h_pdf_bookmarksnumbered = "1";
449 else if (bookmarksnumbered == "false")
450 h_pdf_bookmarksnumbered = "0";
451 string bookmarksopen =
452 process_keyval_opt(options, "bookmarksopen");
453 if (bookmarksopen == "true")
454 h_pdf_bookmarksopen = "1";
455 else if (bookmarksopen == "false")
456 h_pdf_bookmarksopen = "0";
457 if (h_pdf_bookmarksopen == "1") {
458 string bookmarksopenlevel =
459 process_keyval_opt(options, "bookmarksopenlevel");
460 if (!bookmarksopenlevel.empty())
461 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
464 string breaklinks = process_keyval_opt(options, "breaklinks");
465 if (breaklinks == "true")
466 h_pdf_breaklinks = "1";
467 else if (breaklinks == "false")
468 h_pdf_breaklinks = "0";
469 string pdfborder = process_keyval_opt(options, "pdfborder");
470 if (pdfborder == "{0 0 0}")
471 h_pdf_pdfborder = "1";
472 else if (pdfborder == "{0 0 1}")
473 h_pdf_pdfborder = "0";
474 string backref = process_keyval_opt(options, "backref");
475 if (!backref.empty())
476 h_pdf_backref = backref;
477 string colorlinks = process_keyval_opt(options, "colorlinks");
478 if (colorlinks == "true")
479 h_pdf_colorlinks = "1";
480 else if (colorlinks == "false")
481 h_pdf_colorlinks = "0";
482 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
483 if (!pdfpagemode.empty())
484 h_pdf_pagemode = pdfpagemode;
485 string pdftitle = process_keyval_opt(options, "pdftitle");
486 if (!pdftitle.empty()) {
487 h_pdf_title = remove_braces(pdftitle);
489 string pdfauthor = process_keyval_opt(options, "pdfauthor");
490 if (!pdfauthor.empty()) {
491 h_pdf_author = remove_braces(pdfauthor);
493 string pdfsubject = process_keyval_opt(options, "pdfsubject");
494 if (!pdfsubject.empty())
495 h_pdf_subject = remove_braces(pdfsubject);
496 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
497 if (!pdfkeywords.empty())
498 h_pdf_keywords = remove_braces(pdfkeywords);
499 if (!options.empty()) {
500 if (!h_pdf_quoted_options.empty())
501 h_pdf_quoted_options += ',';
502 h_pdf_quoted_options += join(options, ",");
508 void Preamble::handle_package(Parser &p, string const & name,
509 string const & opts, bool in_lyx_preamble)
511 vector<string> options = split_options(opts);
512 add_package(name, options);
516 if (is_known(name, known_roman_fonts)) {
521 if (name == "fourier") {
522 h_font_roman = "utopia";
523 // when font uses real small capitals
524 if (opts == "expert")
528 if (name == "mathpazo")
529 h_font_roman = "palatino";
531 if (name == "mathptmx")
532 h_font_roman = "times";
535 if (is_known(name, known_sans_fonts)) {
539 h_font_sf_scale = scale_as_percentage(scale);
544 if (is_known(name, known_typewriter_fonts)) {
545 // fourier can be set as roman font _only_
546 // fourier as typewriter is handled in handling of \ttdefault
547 if (name != "fourier") {
548 h_font_typewriter = name;
551 h_font_tt_scale = scale_as_percentage(scale);
556 // font uses old-style figure
560 // after the detection and handling of special cases, we can remove the
561 // fonts, otherwise they would appear in the preamble, see bug #7856
562 if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
563 || is_known(name, known_typewriter_fonts))
566 else if (name == "amsmath" || name == "amssymb")
569 else if (name == "esint")
572 else if (name == "mhchem")
575 else if (name == "mathdots")
576 h_use_mathdots = "2";
578 else if (name == "undertilde")
579 h_use_undertilde = "2";
581 else if (name == "babel") {
582 h_language_package = "default";
583 // One might think we would have to do nothing if babel is loaded
584 // without any options to prevent pollution of the preamble with this
585 // babel call in every roundtrip.
586 // But the user could have defined babel-specific things afterwards. So
587 // we need to keep it in the preamble to prevent cases like bug #7861.
589 // check if more than one option was used - used later for inputenc
590 // in case inputenc is parsed before babel, set the encoding to auto
591 if (options.begin() != options.end() - 1)
592 one_language = false;
593 // babel takes the last language of the option of its \usepackage
594 // call as document language. If there is no such language option, the
595 // last language in the documentclass options is used.
596 handle_opt(options, known_languages, h_language);
597 // If babel is called with options, LyX puts them by default into the
598 // document class options. This works for most languages, except
599 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
600 // perhaps in future others.
601 // Therefore keep the babel call as it is as the user might have
603 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
604 delete_opt(options, known_languages);
607 h_preamble << "\\usepackage{babel}\n";
610 else if (name == "fontenc") {
611 h_fontencoding = getStringFromVector(options, ",");
612 /* We could do the following for better round trip support,
613 * but this makes the document less portable, so I skip it:
614 if (h_fontencoding == lyxrc.fontenc)
615 h_fontencoding = "global";
620 else if (name == "inputenc" || name == "luainputenc") {
621 // h_inputencoding is only set when there is not more than one
622 // inputenc option because otherwise h_inputencoding must be
623 // set to "auto" (the default encoding of the document language)
624 // Therefore check for the "," character.
625 // It is also only set when there is not more then one babel
626 // language option but this is handled in the routine for babel.
627 if (opts.find(",") == string::npos && one_language == true)
628 h_inputencoding = opts;
629 if (!options.empty())
630 p.setEncoding(options.back());
634 else if (is_known(name, known_old_language_packages)) {
635 // known language packages from the times before babel
636 // if they are found and not also babel, they will be used as
637 // cutom language package
638 h_language_package = "\\usepackage{" + name + "}";
641 else if (name == "makeidx")
644 else if (name == "prettyref")
647 else if (name == "varioref")
650 else if (name == "verbatim")
653 else if (name == "nomencl")
656 else if (name == "textcomp")
659 else if (name == "url")
662 else if (name == "subscript")
665 else if (name == "color") {
666 // with the following command this package is only loaded when needed for
667 // undefined colors, since we only support the predefined colors
668 // only add it if not yet added
669 if (!ifundefined_color_set)
670 h_preamble << "\\@ifundefined{definecolor}\n {\\usepackage{color}}{}\n";
673 else if (name == "graphicx")
676 else if (name == "setspace")
680 // do not ignore as long as we don't support all commands (e.g. \xout is missing)
681 // and as long as we don't support change tracking
682 else if (name == "ulem")
686 else if (name == "geometry")
687 ; // Ignore this, the geometry settings are made by the \geometry
688 // command. This command is handled below.
690 else if (name == "rotfloat")
693 else if (name == "wrapfig")
696 else if (name == "subfig")
699 else if (is_known(name, known_languages))
702 else if (name == "natbib") {
703 h_cite_engine = "natbib_authoryear";
704 vector<string>::iterator it =
705 find(options.begin(), options.end(), "authoryear");
706 if (it != options.end())
709 it = find(options.begin(), options.end(), "numbers");
710 if (it != options.end()) {
711 h_cite_engine = "natbib_numerical";
717 else if (name == "jurabib")
718 h_cite_engine = "jurabib";
720 else if (name == "hyperref")
721 handle_hyperref(options);
723 else if (!in_lyx_preamble) {
725 h_preamble << "\\usepackage{" << name << "}";
727 h_preamble << "\\usepackage[" << opts << "]{"
733 // We need to do something with the options...
734 if (!options.empty())
735 cerr << "Ignoring options '" << join(options, ",")
736 << "' of package " << name << '.' << endl;
738 // remove the whitespace
743 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
746 Token t = p.get_token();
747 if (t.cat() == catEscape &&
748 is_known(t.cs(), known_if_commands))
749 handle_if(p, in_lyx_preamble);
751 if (!in_lyx_preamble)
752 h_preamble << t.asInput();
753 if (t.cat() == catEscape && t.cs() == "fi")
760 void Preamble::writeLyXHeader(ostream & os)
762 // translate from babel to LyX names
763 h_language = babel2lyx(h_language);
765 // set the quote language
766 // LyX only knows the following quotes languages:
767 // english, swedish, german, polish, french and danish
768 // (quotes for "japanese" and "chinese-traditional" are missing because
769 // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
770 // conversion list taken from
771 // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
772 // (quotes for kazakh and interlingua are unknown)
774 if (h_language == "danish")
775 h_quotes_language = "danish";
777 else if (is_known(h_language, known_french_quotes_languages))
778 h_quotes_language = "french";
780 else if (is_known(h_language, known_german_quotes_languages))
781 h_quotes_language = "german";
783 else if (is_known(h_language, known_polish_quotes_languages))
784 h_quotes_language = "polish";
786 else if (is_known(h_language, known_swedish_quotes_languages))
787 h_quotes_language = "swedish";
789 else if (is_known(h_language, known_english_quotes_languages))
790 h_quotes_language = "english";
792 // output the LyX file settings
793 os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
794 << "\\lyxformat " << LYX_FORMAT << '\n'
795 << "\\begin_document\n"
796 << "\\begin_header\n"
797 << "\\textclass " << h_textclass << "\n";
798 if (!h_preamble.str().empty())
799 os << "\\begin_preamble\n" << h_preamble.str() << "\n\\end_preamble\n";
800 if (!h_options.empty())
801 os << "\\options " << h_options << "\n";
802 os << "\\use_default_options " << h_use_default_options << "\n";
803 if (!used_modules.empty()) {
804 os << "\\begin_modules\n";
805 vector<string>::const_iterator const end = used_modules.end();
806 vector<string>::const_iterator it = used_modules.begin();
807 for (; it != end; it++)
809 os << "\\end_modules\n";
811 os << "\\language " << h_language << "\n"
812 << "\\language_package " << h_language_package << "\n"
813 << "\\inputencoding " << h_inputencoding << "\n"
814 << "\\fontencoding " << h_fontencoding << "\n"
815 << "\\font_roman " << h_font_roman << "\n"
816 << "\\font_sans " << h_font_sans << "\n"
817 << "\\font_typewriter " << h_font_typewriter << "\n"
818 << "\\font_default_family " << h_font_default_family << "\n"
819 << "\\font_sc " << h_font_sc << "\n"
820 << "\\font_osf " << h_font_osf << "\n"
821 << "\\font_sf_scale " << h_font_sf_scale << "\n"
822 << "\\font_tt_scale " << h_font_tt_scale << "\n"
823 << "\\graphics " << h_graphics << "\n";
824 if (!h_float_placement.empty())
825 os << "\\float_placement " << h_float_placement << "\n";
826 os << "\\paperfontsize " << h_paperfontsize << "\n"
827 << "\\spacing " << h_spacing << "\n"
828 << "\\use_hyperref " << h_use_hyperref << '\n';
829 if (h_use_hyperref == "1") {
830 if (!h_pdf_title.empty())
831 os << "\\pdf_title \"" << h_pdf_title << "\"\n";
832 if (!h_pdf_author.empty())
833 os << "\\pdf_author \"" << h_pdf_author << "\"\n";
834 if (!h_pdf_subject.empty())
835 os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
836 if (!h_pdf_keywords.empty())
837 os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
838 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
839 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
840 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
841 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
842 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
843 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
844 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
845 "\\pdf_backref " << h_pdf_backref << "\n"
846 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
847 if (!h_pdf_pagemode.empty())
848 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
849 if (!h_pdf_quoted_options.empty())
850 os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
852 os << "\\papersize " << h_papersize << "\n"
853 << "\\use_geometry " << h_use_geometry << "\n"
854 << "\\use_amsmath " << h_use_amsmath << "\n"
855 << "\\use_esint " << h_use_esint << "\n"
856 << "\\use_mhchem " << h_use_mhchem << "\n"
857 << "\\use_mathdots " << h_use_mathdots << "\n"
858 << "\\use_undertilde " << h_use_undertilde << "\n"
859 << "\\cite_engine " << h_cite_engine << "\n"
860 << "\\use_bibtopic " << h_use_bibtopic << "\n"
861 << "\\paperorientation " << h_paperorientation << '\n'
862 << "\\suppress_date " << h_suppress_date << '\n'
863 << "\\use_refstyle " << h_use_refstyle << '\n';
864 if (!h_fontcolor.empty())
865 os << "\\fontcolor " << h_fontcolor << '\n';
866 if (!h_notefontcolor.empty())
867 os << "\\notefontcolor " << h_notefontcolor << '\n';
868 if (!h_backgroundcolor.empty())
869 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
870 if (!h_boxbgcolor.empty())
871 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
873 << "\\secnumdepth " << h_secnumdepth << "\n"
874 << "\\tocdepth " << h_tocdepth << "\n"
875 << "\\paragraph_separation " << h_paragraph_separation << "\n";
876 if (h_paragraph_separation == "skip")
877 os << "\\defskip " << h_defskip << "\n";
879 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
880 os << "\\quotes_language " << h_quotes_language << "\n"
881 << "\\papercolumns " << h_papercolumns << "\n"
882 << "\\papersides " << h_papersides << "\n"
883 << "\\paperpagestyle " << h_paperpagestyle << "\n";
884 if (!h_listings_params.empty())
885 os << "\\listings_params " << h_listings_params << "\n";
886 os << "\\tracking_changes " << h_tracking_changes << "\n"
887 << "\\output_changes " << h_output_changes << "\n"
888 << "\\html_math_output " << h_html_math_output << "\n"
889 << "\\html_css_as_file " << h_html_css_as_file << "\n"
890 << "\\html_be_strict " << h_html_be_strict << "\n"
891 << "\\end_header\n\n"
893 // clear preamble for subdocuments
898 void Preamble::parse(Parser & p, string const & forceclass,
899 TeX2LyXDocClass & tc)
901 // initialize fixed types
902 special_columns['D'] = 3;
903 bool is_full_document = false;
904 bool is_lyx_file = false;
905 bool in_lyx_preamble = false;
907 // determine whether this is a full document or a fragment for inclusion
909 Token const & t = p.get_token();
911 if (t.cat() == catEscape && t.cs() == "documentclass") {
912 is_full_document = true;
918 while (is_full_document && p.good()) {
919 Token const & t = p.get_token();
922 cerr << "t: " << t << "\n";
928 if (!in_lyx_preamble &&
929 (t.cat() == catLetter ||
930 t.cat() == catSuper ||
932 t.cat() == catOther ||
933 t.cat() == catMath ||
934 t.cat() == catActive ||
935 t.cat() == catBegin ||
937 t.cat() == catAlign ||
938 t.cat() == catParameter))
939 h_preamble << t.cs();
941 else if (!in_lyx_preamble &&
942 (t.cat() == catSpace || t.cat() == catNewline))
943 h_preamble << t.asInput();
945 else if (t.cat() == catComment) {
946 static regex const islyxfile("%% LyX .* created this file");
947 static regex const usercommands("User specified LaTeX commands");
949 string const comment = t.asInput();
951 // magically switch encoding default if it looks like XeLaTeX
952 static string const magicXeLaTeX =
953 "% This document must be compiled with XeLaTeX ";
954 if (comment.size() > magicXeLaTeX.size()
955 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
956 && h_inputencoding == "auto") {
957 cerr << "XeLaTeX comment found, switching to UTF8\n";
958 h_inputencoding = "utf8";
961 if (regex_search(comment, sub, islyxfile)) {
963 in_lyx_preamble = true;
964 } else if (is_lyx_file
965 && regex_search(comment, sub, usercommands))
966 in_lyx_preamble = false;
967 else if (!in_lyx_preamble)
968 h_preamble << t.asInput();
971 else if (t.cs() == "pagestyle")
972 h_paperpagestyle = p.verbatim_item();
974 else if (t.cs() == "date") {
975 string argument = p.getArg('{', '}');
976 if (argument.empty())
977 h_suppress_date = "true";
979 h_preamble << t.asInput() << '{' << argument << '}';
982 else if (t.cs() == "color") {
983 string argument = p.getArg('{', '}');
984 // check the case that a standard color is used
985 if (is_known(argument, known_basic_colors))
986 h_fontcolor = color2code(argument);
987 // check the case that LyX's document_fontcolor is defined
988 // but not used for \color
989 if (argument != "document_fontcolor"
990 && !is_known(argument, known_basic_colors)) {
991 h_preamble << t.asInput() << '{' << argument << '}';
992 // the color might already be set because \definecolor
993 // is parsed before this
998 else if (t.cs() == "pagecolor") {
999 string argument = p.getArg('{', '}');
1000 // check the case that a standard color is used
1001 if (is_known(argument, known_basic_colors))
1002 h_backgroundcolor = color2code(argument);
1003 // check the case that LyX's page_backgroundcolor is defined
1004 // but not used for \pagecolor
1005 if (argument != "page_backgroundcolor"
1006 && !is_known(argument, known_basic_colors)) {
1007 h_preamble << t.asInput() << '{' << argument << '}';
1008 // the color might already be set because \definecolor
1009 // is parsed before this
1010 h_backgroundcolor = "";
1014 else if (t.cs() == "makeatletter") {
1015 // LyX takes care of this
1016 p.setCatCode('@', catLetter);
1019 else if (t.cs() == "makeatother") {
1020 // LyX takes care of this
1021 p.setCatCode('@', catOther);
1024 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1025 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1026 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1027 || t.cs() == "DeclareRobustCommand"
1028 || t.cs() == "DeclareRobustCommandx"
1029 || t.cs() == "ProvideTextCommandDefault"
1030 || t.cs() == "DeclareMathAccent") {
1032 if (p.next_token().character() == '*') {
1036 string const name = p.verbatim_item();
1037 string const opt1 = p.getFullOpt();
1038 string const opt2 = p.getFullOpt();
1039 string const body = p.verbatim_item();
1041 if (name == "\\rmdefault")
1042 if (is_known(body, known_roman_fonts))
1043 h_font_roman = body;
1044 if (name == "\\sfdefault")
1045 if (is_known(body, known_sans_fonts))
1047 if (name == "\\ttdefault")
1048 if (is_known(body, known_typewriter_fonts))
1049 h_font_typewriter = body;
1050 if (name == "\\familydefault") {
1051 string family = body;
1052 // remove leading "\"
1053 h_font_default_family = family.erase(0,1);
1056 // Add the command to the known commands
1057 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1059 // only non-lyxspecific stuff
1060 if (!in_lyx_preamble) {
1062 ss << '\\' << t.cs();
1065 ss << '{' << name << '}' << opt1 << opt2
1066 << '{' << body << "}";
1067 h_preamble << ss.str();
1069 ostream & out = in_preamble ? h_preamble : os;
1070 out << "\\" << t.cs() << "{" << name << "}"
1071 << opts << "{" << body << "}";
1076 else if (t.cs() == "documentclass") {
1077 vector<string>::iterator it;
1078 vector<string> opts = split_options(p.getArg('[', ']'));
1079 handle_opt(opts, known_fontsizes, h_paperfontsize);
1080 delete_opt(opts, known_fontsizes);
1081 // delete "pt" at the end
1082 string::size_type i = h_paperfontsize.find("pt");
1083 if (i != string::npos)
1084 h_paperfontsize.erase(i);
1085 // The documentclass options are always parsed before the options
1086 // of the babel call so that a language cannot overwrite the babel
1088 handle_opt(opts, known_languages, h_language);
1089 delete_opt(opts, known_languages);
1091 // paper orientation
1092 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1093 h_paperorientation = "landscape";
1097 if ((it = find(opts.begin(), opts.end(), "oneside"))
1102 if ((it = find(opts.begin(), opts.end(), "twoside"))
1108 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1110 h_papercolumns = "1";
1113 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1115 h_papercolumns = "2";
1119 // some size options are know to any document classes, other sizes
1120 // are handled by the \geometry command of the geometry package
1121 handle_opt(opts, known_class_paper_sizes, h_papersize);
1122 delete_opt(opts, known_class_paper_sizes);
1123 // the remaining options
1124 h_options = join(opts, ",");
1125 // FIXME This does not work for classes that have a
1126 // different name in LyX than in LaTeX
1127 h_textclass = p.getArg('{', '}');
1130 else if (t.cs() == "usepackage") {
1131 string const options = p.getArg('[', ']');
1132 string const name = p.getArg('{', '}');
1133 vector<string> vecnames;
1134 split(name, vecnames, ',');
1135 vector<string>::const_iterator it = vecnames.begin();
1136 vector<string>::const_iterator end = vecnames.end();
1137 for (; it != end; ++it)
1138 handle_package(p, trimSpaceAndEol(*it), options,
1142 else if (t.cs() == "inputencoding") {
1143 string const encoding = p.getArg('{','}');
1144 h_inputencoding = encoding;
1145 p.setEncoding(encoding);
1148 else if (t.cs() == "newenvironment") {
1149 string const name = p.getArg('{', '}');
1150 string const opt1 = p.getFullOpt();
1151 string const opt2 = p.getFullOpt();
1152 string const beg = p.verbatim_item();
1153 string const end = p.verbatim_item();
1154 if (!in_lyx_preamble) {
1155 h_preamble << "\\newenvironment{" << name
1156 << '}' << opt1 << opt2 << '{'
1157 << beg << "}{" << end << '}';
1159 add_known_environment(name, opt1, !opt2.empty(),
1160 from_utf8(beg), from_utf8(end));
1164 else if (t.cs() == "def") {
1165 string name = p.get_token().cs();
1166 while (p.next_token().cat() != catBegin)
1167 name += p.get_token().cs();
1168 if (!in_lyx_preamble)
1169 h_preamble << "\\def\\" << name << '{'
1170 << p.verbatim_item() << "}";
1173 else if (t.cs() == "newcolumntype") {
1174 string const name = p.getArg('{', '}');
1175 trimSpaceAndEol(name);
1177 string opts = p.getOpt();
1178 if (!opts.empty()) {
1179 istringstream is(string(opts, 1));
1182 special_columns[name[0]] = nargs;
1183 h_preamble << "\\newcolumntype{" << name << "}";
1185 h_preamble << "[" << nargs << "]";
1186 h_preamble << "{" << p.verbatim_item() << "}";
1189 else if (t.cs() == "setcounter") {
1190 string const name = p.getArg('{', '}');
1191 string const content = p.getArg('{', '}');
1192 if (name == "secnumdepth")
1193 h_secnumdepth = content;
1194 else if (name == "tocdepth")
1195 h_tocdepth = content;
1197 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1200 else if (t.cs() == "setlength") {
1201 string const name = p.verbatim_item();
1202 string const content = p.verbatim_item();
1203 // the paragraphs are only not indented when \parindent is set to zero
1204 if (name == "\\parindent" && content != "") {
1205 if (content[0] == '0')
1206 h_paragraph_separation = "skip";
1208 h_paragraph_indentation = translate_len(content);
1209 } else if (name == "\\parskip") {
1210 if (content == "\\smallskipamount")
1211 h_defskip = "smallskip";
1212 else if (content == "\\medskipamount")
1213 h_defskip = "medskip";
1214 else if (content == "\\bigskipamount")
1215 h_defskip = "bigskip";
1217 h_defskip = content;
1219 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1222 else if (t.cs() == "onehalfspacing")
1223 h_spacing = "onehalf";
1225 else if (t.cs() == "doublespacing")
1226 h_spacing = "double";
1228 else if (t.cs() == "setstretch")
1229 h_spacing = "other " + p.verbatim_item();
1231 else if (t.cs() == "begin") {
1232 string const name = p.getArg('{', '}');
1233 if (name == "document")
1235 h_preamble << "\\begin{" << name << "}";
1238 else if (t.cs() == "geometry") {
1239 h_use_geometry = "true";
1240 vector<string> opts = split_options(p.getArg('{', '}'));
1241 vector<string>::iterator it;
1242 // paper orientation
1243 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1244 h_paperorientation = "landscape";
1248 handle_opt(opts, known_paper_sizes, h_papersize);
1249 delete_opt(opts, known_paper_sizes);
1251 char const * const * margin = known_paper_margins;
1253 for (; *margin; ++margin) {
1255 // search for the "=" in e.g. "lmargin=2cm" to get the value
1256 for(size_t i = 0; i != opts.size(); i++) {
1257 if (opts.at(i).find(*margin) != string::npos) {
1258 string::size_type pos = opts.at(i).find("=");
1259 string value = opts.at(i).substr(pos + 1);
1260 string name = known_coded_paper_margins[k];
1261 h_margins += "\\" + name + " " + value + "\n";
1267 else if (t.cs() == "definecolor") {
1268 string const color = p.getArg('{', '}');
1269 string const space = p.getArg('{', '}');
1270 string const value = p.getArg('{', '}');
1271 if (color == "document_fontcolor" && space == "rgb") {
1272 RGBColor c(RGBColorFromLaTeX(value));
1273 h_fontcolor = X11hexname(c);
1274 } else if (color == "note_fontcolor" && space == "rgb") {
1275 RGBColor c(RGBColorFromLaTeX(value));
1276 h_notefontcolor = X11hexname(c);
1277 } else if (color == "page_backgroundcolor" && space == "rgb") {
1278 RGBColor c(RGBColorFromLaTeX(value));
1279 h_backgroundcolor = X11hexname(c);
1280 } else if (color == "shadecolor" && space == "rgb") {
1281 RGBColor c(RGBColorFromLaTeX(value));
1282 h_boxbgcolor = X11hexname(c);
1284 h_preamble << "\\definecolor{" << color
1285 << "}{" << space << "}{" << value
1290 else if (t.cs() == "jurabibsetup") {
1291 // FIXME p.getArg('{', '}') is most probably wrong (it
1292 // does not handle nested braces).
1293 // Use p.verbatim_item() instead.
1294 vector<string> jurabibsetup =
1295 split_options(p.getArg('{', '}'));
1296 // add jurabibsetup to the jurabib package options
1297 add_package("jurabib", jurabibsetup);
1298 if (!jurabibsetup.empty()) {
1299 h_preamble << "\\jurabibsetup{"
1300 << join(jurabibsetup, ",") << '}';
1304 else if (t.cs() == "hypersetup") {
1305 vector<string> hypersetup =
1306 split_options(p.verbatim_item());
1307 // add hypersetup to the hyperref package options
1308 handle_hyperref(hypersetup);
1309 if (!hypersetup.empty()) {
1310 h_preamble << "\\hypersetup{"
1311 << join(hypersetup, ",") << '}';
1315 else if (is_known(t.cs(), known_if_3arg_commands)) {
1316 // prevent misparsing of \usepackage if it is used
1317 // as an argument (see e.g. our own output of
1318 // \@ifundefined above)
1319 string const arg1 = p.verbatim_item();
1320 string const arg2 = p.verbatim_item();
1321 string const arg3 = p.verbatim_item();
1322 // test case \@ifundefined{date}{}{\date{}}
1323 if (arg1 == "date" && arg2.empty() && arg3 == "\\date{}") {
1324 h_suppress_date = "true";
1325 // test case \@ifundefined{definecolor}{\usepackage{color}}{}
1326 // because we could pollute the preamble with it in roundtrips
1327 } else if (arg1 == "definecolor" && arg2 == "\\usepackage{color}"
1329 ifundefined_color_set = true;
1331 //\@ifundefined{showcaptionsetup}{}{%
1332 // \PassOptionsToPackage{caption=false}{subfig}}
1333 // that LyX uses for subfloats
1334 } else if (arg1 == "showcaptionsetup" && arg2.empty()
1335 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1337 } else if (!in_lyx_preamble) {
1338 h_preamble << t.asInput()
1339 << '{' << arg1 << '}'
1340 << '{' << arg2 << '}'
1341 << '{' << arg3 << '}';
1345 else if (is_known(t.cs(), known_if_commands)) {
1346 // must not parse anything in conditional code, since
1347 // LyX would output the parsed contents unconditionally
1348 if (!in_lyx_preamble)
1349 h_preamble << t.asInput();
1350 handle_if(p, in_lyx_preamble);
1353 else if (!t.cs().empty() && !in_lyx_preamble)
1354 h_preamble << '\\' << t.cs();
1357 // remove the whitespace
1360 // Force textclass if the user wanted it
1361 if (!forceclass.empty())
1362 h_textclass = forceclass;
1363 if (noweb_mode && !prefixIs(h_textclass, "literate-"))
1364 h_textclass.insert(0, "literate-");
1365 tc.setName(h_textclass);
1367 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1370 if (h_papersides.empty()) {
1373 h_papersides = ss.str();
1378 string babel2lyx(string const & language)
1380 char const * const * where = is_known(language, known_languages);
1382 return known_coded_languages[where - known_languages];
1387 string color2code(string const & name)
1389 char const * const * where = is_known(name, known_basic_colors);
1391 return known_basic_color_codes[where - known_basic_colors];