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.
18 #include "LayoutFile.h"
21 #include "TextClass.h"
23 #include "support/convert.h"
24 #include "support/FileName.h"
25 #include "support/filetools.h"
26 #include "support/lstrings.h"
28 #include "support/regex.h"
38 using namespace lyx::support;
43 // special columntypes
44 extern map<char, int> special_columns;
46 map<string, vector<string> > used_packages;
47 const char * const modules_placeholder = "\001modules\001";
49 // needed to handle encodings with babel
50 bool one_language = true;
51 string h_inputencoding = "auto";
52 string h_paragraph_separation = "indent";
54 // necessary to avoid that our preamble stuff is added at each tex2lyx run
55 // which would pollute the preamble when doing roundtrips
56 bool ifundefined_color_set = false;
60 //add this to known_languages when updating to lyxformat 266:
61 // "armenian" (needs special handling since not supported by standard babel)
62 //add these to known_languages when updating to lyxformat 268:
63 //"chinese-simplified", "chinese-traditional", "japanese", "korean"
64 // Both changes require first that support for non-babel languages (CJK,
66 // add turkmen for lyxformat 383
68 * known babel language names (including synonyms)
69 * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
70 * not yet supported by LyX: kurmanji
71 * please keep this in sync with known_coded_languages line by line!
73 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
74 "american", "arabic", "arabtex", "austrian", "bahasa", "bahasai", "bahasam",
75 "basque", "belarusian", "brazil", "brazilian", "breton", "british", "bulgarian",
76 "canadian", "canadien", "catalan", "croatian", "czech", "danish", "dutch",
77 "english", "esperanto", "estonian", "farsi", "finnish", "francais", "french",
78 "frenchb", "frenchle", "frenchpro", "galician", "german", "germanb", "greek",
79 "hebrew", "hungarian", "icelandic", "indon", "indonesian", "interlingua",
80 "irish", "italian", "kazakh", "latin", "latvian", "lithuanian", "lowersorbian",
81 "lsorbian", "magyar", "malay", "meyalu", "mongolian", "naustrian", "newzealand",
82 "ngerman", "ngermanb", "norsk", "nynorsk", "polutonikogreek", "polish",
83 "portuges", "portuguese", "romanian", "russian", "russianb", "samin",
84 "scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish",
85 "swedish", "thai", "turkish", "turkmen", "ukraineb", "ukrainian",
86 "uppersorbian", "UKenglish", "USenglish", "usorbian", "vietnam", "welsh",
90 * the same as known_languages with .lyx names
91 * please keep this in sync with known_languages line by line!
93 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
94 "american", "arabic_arabi", "arabic_arabtex", "austrian", "bahasa", "bahasa", "bahasam",
95 "basque", "belarusian", "brazilian", "brazilian", "breton", "british", "bulgarian",
96 "canadian", "canadien", "catalan", "croatian", "czech", "danish", "dutch",
97 "english", "esperanto", "estonian", "farsi", "finnish", "french", "french",
98 "french", "french", "french", "galician", "german", "german", "greek",
99 "hebrew", "magyar", "icelandic", "bahasa", "bahasa", "interlingua",
100 "irish", "italian", "kazakh", "latin", "latvian", "lithuanian", "lowersorbian",
101 "lowersorbian", "magyar", "bahasam", "bahasam", "mongolian", "naustrian", "english",
102 "ngerman", "ngerman", "norsk", "nynorsk", "polutonikogreek", "polish",
103 "portuguese", "portuguese", "romanian", "russian", "russian", "samin",
104 "scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish",
105 "swedish", "thai", "turkish", "turkmen", "ukrainian", "ukrainian",
106 "uppersorbian", "uppersorbian", "english", "english", "vietnamese", "welsh",
109 /// languages with english quotes (.lyx names)
110 const char * const known_english_quotes_languages[] = {"american", "bahasa",
111 "bahasam", "brazilian", "canadian", "chinese-simplified", "english",
112 "esperanto", "hebrew", "irish", "korean", "portuguese", "scottish", "thai", 0};
114 /// languages with french quotes (.lyx names)
115 const char * const known_french_quotes_languages[] = {"albanian",
116 "arabic_arabi", "arabic_arabtex", "basque", "canadien", "catalan", "french",
117 "galician", "greek", "italian", "norsk", "nynorsk", "polutonikogreek",
118 "russian", "spanish", "spanish-mexico", "turkish", "turkmen", "ukrainian",
121 /// languages with german quotes (.lyx names)
122 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
123 "czech", "german", "icelandic", "lithuanian", "lowersorbian", "naustrian",
124 "ngerman", "serbian", "serbian-latin", "slovak", "slovene", "uppersorbian", 0};
126 /// languages with polish quotes (.lyx names)
127 const char * const known_polish_quotes_languages[] = {"afrikaans", "croatian",
128 "dutch", "estonian", "magyar", "polish", "romanian", 0};
130 /// languages with swedish quotes (.lyx names)
131 const char * const known_swedish_quotes_languages[] = {"finnish",
134 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
136 const char * const known_roman_fonts[] = { "ae", "bookman", "charter",
137 "cmr", "fourier", "lmodern", "mathpazo", "mathptmx", "newcent", 0};
139 const char * const known_sans_fonts[] = { "avant", "berasans", "cmbr", "cmss",
140 "helvet", "lmss", 0};
142 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
143 "courier", "lmtt", "luximono", "fourier", "lmodern", "mathpazo", "mathptmx",
146 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
147 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
148 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
149 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
150 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
152 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
153 "executivepaper", "legalpaper", "letterpaper", 0};
155 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
156 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
158 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
159 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
162 /// commands that can start an \if...\else...\endif sequence
163 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
164 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
165 "ifsidecap", "ifupgreek", 0};
167 /// conditional commands with three arguments like \@ifundefined{}{}{}
168 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
172 ostringstream h_preamble;
173 string h_textclass = "article";
174 string h_use_default_options = "false";
176 string h_language = "english";
177 string h_language_package = "default";
178 string h_fontencoding = "default";
179 string h_font_roman = "default";
180 string h_font_sans = "default";
181 string h_font_typewriter = "default";
182 string h_font_default_family = "default";
183 string h_font_sc = "false";
184 string h_font_osf = "false";
185 string h_font_sf_scale = "100";
186 string h_font_tt_scale = "100";
187 string h_graphics = "default";
188 string h_float_placement;
189 string h_paperfontsize = "default";
190 string h_spacing = "single";
191 string h_use_hyperref = "0";
194 string h_pdf_subject;
195 string h_pdf_keywords;
196 string h_pdf_bookmarks = "1";
197 string h_pdf_bookmarksnumbered = "0";
198 string h_pdf_bookmarksopen = "0";
199 string h_pdf_bookmarksopenlevel = "1";
200 string h_pdf_breaklinks = "0";
201 string h_pdf_pdfborder = "0";
202 string h_pdf_colorlinks = "0";
203 string h_pdf_backref = "section";
204 string h_pdf_pdfusetitle = "1";
205 string h_pdf_pagemode;
206 string h_pdf_quoted_options;
207 string h_papersize = "default";
208 string h_use_geometry = "false";
209 string h_use_amsmath = "1";
210 string h_use_esint = "1";
211 string h_use_mhchem = "0";
212 string h_use_mathdots = "0";
213 string h_use_undertilde = "0";
214 string h_cite_engine = "basic";
215 string h_use_bibtopic = "false";
216 string h_paperorientation = "portrait";
217 string h_suppress_date = "false";
218 string h_use_refstyle = "0";
219 string h_backgroundcolor;
222 string h_notefontcolor;
223 string h_secnumdepth = "3";
224 string h_tocdepth = "3";
225 string h_defskip = "medskip";
226 string h_paragraph_indentation = "default";
227 string h_quotes_language = "english";
228 string h_papercolumns = "1";
230 string h_paperpagestyle = "default";
231 string h_listings_params;
232 string h_tracking_changes = "false";
233 string h_output_changes = "false";
234 string h_html_math_output = "0";
235 string h_html_css_as_file = "0";
236 string h_html_be_strict = "false";
240 // returns true if at least one of the options in what has been found
241 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
247 // the last language option is the document language (for babel and LyX)
248 // the last size option is the document font size
249 vector<string>::iterator it;
250 vector<string>::iterator position = opts.begin();
251 for (; *what; ++what) {
252 it = find(opts.begin(), opts.end(), *what);
253 if (it != opts.end()) {
254 if (it >= position) {
265 void delete_opt(vector<string> & opts, char const * const * what)
270 // remove found options from the list
271 // do this after handle_opt to avoid potential memory leaks
272 vector<string>::iterator it;
273 for (; *what; ++what) {
274 it = find(opts.begin(), opts.end(), *what);
275 if (it != opts.end())
282 * Split a package options string (keyval format) into a vector.
284 * authorformat=smallcaps,
286 * titleformat=colonsep,
287 * bibformat={tabular,ibidem,numbered}
289 vector<string> split_options(string const & input)
291 vector<string> options;
295 Token const & t = p.get_token();
296 if (t.asInput() == ",") {
297 options.push_back(trimSpaceAndEol(option));
299 } else if (t.asInput() == "=") {
302 if (p.next_token().asInput() == "{")
303 option += '{' + p.getArg('{', '}') + '}';
304 } else if (t.cat() != catSpace)
305 option += t.asInput();
309 options.push_back(trimSpaceAndEol(option));
316 * Retrieve a keyval option "name={value with=sign}" named \p name from
317 * \p options and return the value.
318 * The found option is also removed from \p options.
320 string process_keyval_opt(vector<string> & options, string name)
322 for (size_t i = 0; i < options.size(); ++i) {
323 vector<string> option;
324 split(options[i], option, '=');
325 if (option.size() < 2)
327 if (option[0] == name) {
328 options.erase(options.begin() + i);
329 option.erase(option.begin());
330 return join(option, "=");
338 * Add package \p name with options \p options to used_packages.
339 * Remove options from \p options that we don't want to output.
341 void add_package(string const & name, vector<string> & options)
343 // every package inherits the global options
344 if (used_packages.find(name) == used_packages.end())
345 used_packages[name] = split_options(h_options);
347 vector<string> & v = used_packages[name];
348 v.insert(v.end(), options.begin(), options.end());
349 if (name == "jurabib") {
350 // Don't output the order argument (see the cite command
351 // handling code in text.cpp).
352 vector<string>::iterator end =
353 remove(options.begin(), options.end(), "natbiborder");
354 end = remove(options.begin(), end, "jurabiborder");
355 options.erase(end, options.end());
360 // Given is a string like "scaled=0.9", return 0.9 * 100
361 string const scale_as_percentage(string const & scale)
363 string::size_type pos = scale.find('=');
364 if (pos != string::npos) {
365 string value = scale.substr(pos + 1);
367 return convert<string>(100 * convert<double>(value));
369 // If the input string didn't match our expectations.
370 // return the default value "100"
375 string remove_braces(string const & value)
379 if (value[0] == '{' && value[value.length()-1] == '}')
380 return value.substr(1, value.length()-2);
385 void handle_hyperref(vector<string> & options)
387 // FIXME swallow inputencoding changes that might surround the
388 // hyperref setup if it was written by LyX
389 h_use_hyperref = "1";
390 // swallow "unicode=true", since LyX does always write that
391 vector<string>::iterator it =
392 find(options.begin(), options.end(), "unicode=true");
393 if (it != options.end())
395 it = find(options.begin(), options.end(), "pdfusetitle");
396 if (it != options.end()) {
397 h_pdf_pdfusetitle = "1";
400 string bookmarks = process_keyval_opt(options, "bookmarks");
401 if (bookmarks == "true")
402 h_pdf_bookmarks = "1";
403 else if (bookmarks == "false")
404 h_pdf_bookmarks = "0";
405 if (h_pdf_bookmarks == "1") {
406 string bookmarksnumbered =
407 process_keyval_opt(options, "bookmarksnumbered");
408 if (bookmarksnumbered == "true")
409 h_pdf_bookmarksnumbered = "1";
410 else if (bookmarksnumbered == "false")
411 h_pdf_bookmarksnumbered = "0";
412 string bookmarksopen =
413 process_keyval_opt(options, "bookmarksopen");
414 if (bookmarksopen == "true")
415 h_pdf_bookmarksopen = "1";
416 else if (bookmarksopen == "false")
417 h_pdf_bookmarksopen = "0";
418 if (h_pdf_bookmarksopen == "1") {
419 string bookmarksopenlevel =
420 process_keyval_opt(options, "bookmarksopenlevel");
421 if (!bookmarksopenlevel.empty())
422 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
425 string breaklinks = process_keyval_opt(options, "breaklinks");
426 if (breaklinks == "true")
427 h_pdf_breaklinks = "1";
428 else if (breaklinks == "false")
429 h_pdf_breaklinks = "0";
430 string pdfborder = process_keyval_opt(options, "pdfborder");
431 if (pdfborder == "{0 0 0}")
432 h_pdf_pdfborder = "1";
433 else if (pdfborder == "{0 0 1}")
434 h_pdf_pdfborder = "0";
435 string backref = process_keyval_opt(options, "backref");
436 if (!backref.empty())
437 h_pdf_backref = backref;
438 string colorlinks = process_keyval_opt(options, "colorlinks");
439 if (colorlinks == "true")
440 h_pdf_colorlinks = "1";
441 else if (colorlinks == "false")
442 h_pdf_colorlinks = "0";
443 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
444 if (!pdfpagemode.empty())
445 h_pdf_pagemode = pdfpagemode;
446 string pdftitle = process_keyval_opt(options, "pdftitle");
447 if (!pdftitle.empty()) {
448 h_pdf_title = remove_braces(pdftitle);
450 string pdfauthor = process_keyval_opt(options, "pdfauthor");
451 if (!pdfauthor.empty()) {
452 h_pdf_author = remove_braces(pdfauthor);
454 string pdfsubject = process_keyval_opt(options, "pdfsubject");
455 if (!pdfsubject.empty())
456 h_pdf_subject = remove_braces(pdfsubject);
457 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
458 if (!pdfkeywords.empty())
459 h_pdf_keywords = remove_braces(pdfkeywords);
460 if (!options.empty()) {
461 if (!h_pdf_quoted_options.empty())
462 h_pdf_quoted_options += ',';
463 h_pdf_quoted_options += join(options, ",");
469 void handle_package(Parser &p, string const & name, string const & opts,
470 bool in_lyx_preamble)
472 vector<string> options = split_options(opts);
473 add_package(name, options);
477 if (is_known(name, known_roman_fonts)) {
482 if (name == "fourier") {
483 h_font_roman = "utopia";
484 // when font uses real small capitals
485 if (opts == "expert")
489 if (name == "mathpazo")
490 h_font_roman = "palatino";
492 if (name == "mathptmx")
493 h_font_roman = "times";
496 if (is_known(name, known_sans_fonts)) {
500 h_font_sf_scale = scale_as_percentage(scale);
505 if (is_known(name, known_typewriter_fonts)) {
506 // fourier can be set as roman font _only_
507 // fourier as typewriter is handled in handling of \ttdefault
508 if (name != "fourier") {
509 h_font_typewriter = name;
512 h_font_tt_scale = scale_as_percentage(scale);
517 // font uses old-style figure
521 else if (name == "amsmath" || name == "amssymb")
524 else if (name == "esint")
527 else if (name == "mhchem")
530 else if (name == "mathdots")
531 h_use_mathdots = "2";
533 else if (name == "undertilde")
534 h_use_undertilde = "2";
536 else if (name == "babel") {
537 // we have to do nothing if babel is loaded without any options, otherwise
538 // we would pollute the preamble with this call in every roundtrip
540 // check if more than one option was used - used later for inputenc
541 // in case inputenc is parsed before babel, set the encoding to auto
542 if (options.begin() != options.end() - 1) {
543 one_language = false;
544 h_inputencoding = "auto";
546 // babel takes the last language of the option of its \usepackage
547 // call as document language. If there is no such language option, the
548 // last language in the documentclass options is used.
549 handle_opt(options, known_languages, h_language);
550 delete_opt(options, known_languages);
554 else if (name == "fontenc") {
555 h_fontencoding = getStringFromVector(options, ",");
556 // as of version LyX 2.0 "T1" is equal to the setting "global"
557 if (h_fontencoding == "T1")
558 h_fontencoding = "global";
562 else if (name == "inputenc" || name == "luainputenc") {
563 // h_inputencoding is only set when there is not more than one
564 // inputenc option because otherwise h_inputencoding must be
565 // set to "auto" (the default encoding of the document language)
566 // Therefore check for the "," character.
567 // It is also only set when there is not more then one babel
568 // language option but this is handled in the routine for babel.
569 if (opts.find(",") == string::npos && one_language == true)
570 h_inputencoding = opts;
571 if (!options.empty())
572 p.setEncoding(options.back());
576 else if (name == "makeidx")
579 else if (name == "prettyref")
582 else if (name == "varioref")
585 else if (name == "verbatim")
588 else if (name == "nomencl")
591 else if (name == "textcomp")
594 else if (name == "url")
597 else if (name == "subscript")
600 else if (name == "color") {
601 // with the following command this package is only loaded when needed for
602 // undefined colors, since we only support the predefined colors
603 // only add it if not yet added
604 if (!ifundefined_color_set)
605 h_preamble << "\\@ifundefined{definecolor}\n {\\usepackage{color}}{}\n";
608 else if (name == "graphicx")
611 else if (name == "setspace")
615 // do not ignore as long as we don't support all commands (e.g. \xout is missing)
616 else if (name == "ulem")
620 else if (name == "geometry")
621 ; // Ignore this, the geometry settings are made by the \geometry
622 // command. This command is handled below.
624 else if (is_known(name, known_languages))
627 else if (name == "natbib") {
628 h_cite_engine = "natbib_authoryear";
629 vector<string>::iterator it =
630 find(options.begin(), options.end(), "authoryear");
631 if (it != options.end())
634 it = find(options.begin(), options.end(), "numbers");
635 if (it != options.end()) {
636 h_cite_engine = "natbib_numerical";
642 else if (name == "jurabib")
643 h_cite_engine = "jurabib";
645 else if (name == "hyperref")
646 handle_hyperref(options);
648 else if (!in_lyx_preamble) {
650 h_preamble << "\\usepackage{" << name << "}";
652 h_preamble << "\\usepackage[" << opts << "]{"
658 // We need to do something with the options...
659 if (!options.empty())
660 cerr << "Ignoring options '" << join(options, ",")
661 << "' of package " << name << '.' << endl;
663 // remove the whitespace
668 void handle_if(Parser & p, bool in_lyx_preamble)
671 Token t = p.get_token();
672 if (t.cat() == catEscape &&
673 is_known(t.cs(), known_if_commands))
674 handle_if(p, in_lyx_preamble);
676 if (!in_lyx_preamble)
677 h_preamble << t.asInput();
678 if (t.cat() == catEscape && t.cs() == "fi")
685 void end_preamble(ostream & os, TextClass const & /*textclass*/)
687 // translate from babel to LyX names
688 h_language = babel2lyx(h_language);
690 // set the quote language
691 // LyX only knows the following quotes languages:
692 // english, swedish, german, polish, french and danish
693 // (quotes for "japanese" and "chinese-traditional" are missing because
694 // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
695 // conversion list taken from
696 // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
697 // (quotes for kazakh and interlingua are unknown)
699 if (h_language == "danish")
700 h_quotes_language = "danish";
702 else if (is_known(h_language, known_french_quotes_languages))
703 h_quotes_language = "french";
705 else if (is_known(h_language, known_german_quotes_languages))
706 h_quotes_language = "german";
708 else if (is_known(h_language, known_polish_quotes_languages))
709 h_quotes_language = "polish";
711 else if (is_known(h_language, known_swedish_quotes_languages))
712 h_quotes_language = "swedish";
714 else if (is_known(h_language, known_english_quotes_languages))
715 h_quotes_language = "english";
717 // output the LyX file settings
718 os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
719 << "\\lyxformat " << LYX_FORMAT << '\n'
720 << "\\begin_document\n"
721 << "\\begin_header\n"
722 << "\\textclass " << h_textclass << "\n";
723 if (!h_preamble.str().empty())
724 os << "\\begin_preamble\n" << h_preamble.str() << "\n\\end_preamble\n";
725 if (!h_options.empty())
726 os << "\\options " << h_options << "\n";
727 os << "\\use_default_options " << h_use_default_options << "\n"
728 << modules_placeholder
729 << "\\language " << h_language << "\n"
730 << "\\language_package " << h_language_package << "\n"
731 << "\\inputencoding " << h_inputencoding << "\n"
732 << "\\fontencoding " << h_fontencoding << "\n"
733 << "\\font_roman " << h_font_roman << "\n"
734 << "\\font_sans " << h_font_sans << "\n"
735 << "\\font_typewriter " << h_font_typewriter << "\n"
736 << "\\font_default_family " << h_font_default_family << "\n"
737 << "\\font_sc " << h_font_sc << "\n"
738 << "\\font_osf " << h_font_osf << "\n"
739 << "\\font_sf_scale " << h_font_sf_scale << "\n"
740 << "\\font_tt_scale " << h_font_tt_scale << "\n"
741 << "\\graphics " << h_graphics << "\n";
742 if (!h_float_placement.empty())
743 os << "\\float_placement " << h_float_placement << "\n";
744 os << "\\paperfontsize " << h_paperfontsize << "\n"
745 << "\\spacing " << h_spacing << "\n"
746 << "\\use_hyperref " << h_use_hyperref << '\n';
747 if (h_use_hyperref == "1") {
748 if (!h_pdf_title.empty())
749 os << "\\pdf_title \"" << h_pdf_title << "\"\n";
750 if (!h_pdf_author.empty())
751 os << "\\pdf_author \"" << h_pdf_author << "\"\n";
752 if (!h_pdf_subject.empty())
753 os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
754 if (!h_pdf_keywords.empty())
755 os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
756 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
757 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
758 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
759 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
760 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
761 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
762 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
763 "\\pdf_backref " << h_pdf_backref << "\n"
764 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
765 if (!h_pdf_pagemode.empty())
766 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
767 if (!h_pdf_quoted_options.empty())
768 os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
770 os << "\\papersize " << h_papersize << "\n"
771 << "\\use_geometry " << h_use_geometry << "\n"
772 << "\\use_amsmath " << h_use_amsmath << "\n"
773 << "\\use_esint " << h_use_esint << "\n"
774 << "\\use_mhchem " << h_use_mhchem << "\n"
775 << "\\use_mathdots " << h_use_mathdots << "\n"
776 << "\\use_undertilde " << h_use_undertilde << "\n"
777 << "\\cite_engine " << h_cite_engine << "\n"
778 << "\\use_bibtopic " << h_use_bibtopic << "\n"
779 << "\\paperorientation " << h_paperorientation << '\n'
780 << "\\suppress_date " << h_suppress_date << '\n'
781 << "\\use_refstyle " << h_use_refstyle << '\n';
782 if (!h_fontcolor.empty())
783 os << "\\fontcolor " << h_fontcolor << '\n';
784 if (!h_notefontcolor.empty())
785 os << "\\notefontcolor " << h_notefontcolor << '\n';
786 if (!h_backgroundcolor.empty())
787 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
788 if (!h_boxbgcolor.empty())
789 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
791 << "\\secnumdepth " << h_secnumdepth << "\n"
792 << "\\tocdepth " << h_tocdepth << "\n"
793 << "\\paragraph_separation " << h_paragraph_separation << "\n";
794 if (h_paragraph_separation == "skip")
795 os << "\\defskip " << h_defskip << "\n";
797 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
798 os << "\\quotes_language " << h_quotes_language << "\n"
799 << "\\papercolumns " << h_papercolumns << "\n"
800 << "\\papersides " << h_papersides << "\n"
801 << "\\paperpagestyle " << h_paperpagestyle << "\n";
802 if (!h_listings_params.empty())
803 os << "\\listings_params " << h_listings_params << "\n";
804 os << "\\tracking_changes " << h_tracking_changes << "\n"
805 << "\\output_changes " << h_output_changes << "\n"
806 << "\\html_math_output " << h_html_math_output << "\n"
807 << "\\html_css_as_file " << h_html_css_as_file << "\n"
808 << "\\html_be_strict " << h_html_be_strict << "\n"
809 << "\\end_header\n\n"
811 // clear preamble for subdocuments
815 } // anonymous namespace
818 void parse_preamble(Parser & p, ostream & os,
819 string const & forceclass, TeX2LyXDocClass & tc)
821 // initialize fixed types
822 special_columns['D'] = 3;
823 bool is_full_document = false;
824 bool is_lyx_file = false;
825 bool in_lyx_preamble = false;
827 // determine whether this is a full document or a fragment for inclusion
829 Token const & t = p.get_token();
831 if (t.cat() == catEscape && t.cs() == "documentclass") {
832 is_full_document = true;
838 while (is_full_document && p.good()) {
839 Token const & t = p.get_token();
842 cerr << "t: " << t << "\n";
848 if (!in_lyx_preamble &&
849 (t.cat() == catLetter ||
850 t.cat() == catSuper ||
852 t.cat() == catOther ||
853 t.cat() == catMath ||
854 t.cat() == catActive ||
855 t.cat() == catBegin ||
857 t.cat() == catAlign ||
858 t.cat() == catParameter))
859 h_preamble << t.cs();
861 else if (!in_lyx_preamble &&
862 (t.cat() == catSpace || t.cat() == catNewline))
863 h_preamble << t.asInput();
865 else if (t.cat() == catComment) {
866 static regex const islyxfile("%% LyX .* created this file");
867 static regex const usercommands("User specified LaTeX commands");
869 string const comment = t.asInput();
871 // magically switch encoding default if it looks like XeLaTeX
872 static string const magicXeLaTeX =
873 "% This document must be compiled with XeLaTeX ";
874 if (comment.size() > magicXeLaTeX.size()
875 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
876 && h_inputencoding == "auto") {
877 cerr << "XeLaTeX comment found, switching to UTF8\n";
878 h_inputencoding = "utf8";
881 if (regex_search(comment, sub, islyxfile)) {
883 in_lyx_preamble = true;
884 } else if (is_lyx_file
885 && regex_search(comment, sub, usercommands))
886 in_lyx_preamble = false;
887 else if (!in_lyx_preamble)
888 h_preamble << t.asInput();
891 else if (t.cs() == "pagestyle")
892 h_paperpagestyle = p.verbatim_item();
894 else if (t.cs() == "date") {
895 if (p.verbatim_item().empty())
896 h_suppress_date = "true";
899 else if (t.cs() == "makeatletter") {
900 // LyX takes care of this
901 p.setCatCode('@', catLetter);
904 else if (t.cs() == "makeatother") {
905 // LyX takes care of this
906 p.setCatCode('@', catOther);
909 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
910 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
911 || t.cs() == "providecommand" || t.cs() == "providecommandx"
912 || t.cs() == "DeclareRobustCommand"
913 || t.cs() == "DeclareRobustCommandx"
914 || t.cs() == "ProvideTextCommandDefault"
915 || t.cs() == "DeclareMathAccent") {
917 if (p.next_token().character() == '*') {
921 string const name = p.verbatim_item();
922 string const opt1 = p.getFullOpt();
923 string const opt2 = p.getFullOpt();
924 string const body = p.verbatim_item();
926 if (name == "\\rmdefault")
927 if (is_known(body, known_roman_fonts))
929 if (name == "\\sfdefault")
930 if (is_known(body, known_sans_fonts))
932 if (name == "\\ttdefault")
933 if (is_known(body, known_typewriter_fonts))
934 h_font_typewriter = body;
935 if (name == "\\familydefault") {
936 string family = body;
937 // remove leading "\"
938 h_font_default_family = family.erase(0,1);
941 // Add the command to the known commands
942 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
944 // only non-lyxspecific stuff
945 if (!in_lyx_preamble) {
947 ss << '\\' << t.cs();
950 ss << '{' << name << '}' << opt1 << opt2
951 << '{' << body << "}";
952 h_preamble << ss.str();
954 ostream & out = in_preamble ? h_preamble : os;
955 out << "\\" << t.cs() << "{" << name << "}"
956 << opts << "{" << body << "}";
961 else if (t.cs() == "documentclass") {
962 vector<string>::iterator it;
963 vector<string> opts = split_options(p.getArg('[', ']'));
964 handle_opt(opts, known_fontsizes, h_paperfontsize);
965 delete_opt(opts, known_fontsizes);
966 // delete "pt" at the end
967 string::size_type i = h_paperfontsize.find("pt");
968 if (i != string::npos)
969 h_paperfontsize.erase(i);
970 // The documentclass options are always parsed before the options
971 // of the babel call so that a language cannot overwrite the babel
973 handle_opt(opts, known_languages, h_language);
974 delete_opt(opts, known_languages);
977 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
978 h_paperorientation = "landscape";
982 if ((it = find(opts.begin(), opts.end(), "oneside"))
987 if ((it = find(opts.begin(), opts.end(), "twoside"))
993 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
995 h_papercolumns = "1";
998 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1000 h_papercolumns = "2";
1004 // some size options are know to any document classes, other sizes
1005 // are handled by the \geometry command of the geometry package
1006 handle_opt(opts, known_class_paper_sizes, h_papersize);
1007 delete_opt(opts, known_class_paper_sizes);
1008 // the remaining options
1009 h_options = join(opts, ",");
1010 // FIXME This does not work for classes that have a
1011 // different name in LyX than in LaTeX
1012 h_textclass = p.getArg('{', '}');
1015 else if (t.cs() == "usepackage") {
1016 string const options = p.getArg('[', ']');
1017 string const name = p.getArg('{', '}');
1018 vector<string> vecnames;
1019 split(name, vecnames, ',');
1020 vector<string>::const_iterator it = vecnames.begin();
1021 vector<string>::const_iterator end = vecnames.end();
1022 for (; it != end; ++it)
1023 handle_package(p, trimSpaceAndEol(*it), options,
1027 else if (t.cs() == "inputencoding") {
1028 string const encoding = p.getArg('{','}');
1029 h_inputencoding = encoding;
1030 p.setEncoding(encoding);
1033 else if (t.cs() == "newenvironment") {
1034 string const name = p.getArg('{', '}');
1035 string const opt1 = p.getFullOpt();
1036 string const opt2 = p.getFullOpt();
1037 string const beg = p.verbatim_item();
1038 string const end = p.verbatim_item();
1039 if (!in_lyx_preamble) {
1040 h_preamble << "\\newenvironment{" << name
1041 << '}' << opt1 << opt2 << '{'
1042 << beg << "}{" << end << '}';
1044 add_known_environment(name, opt1, !opt2.empty(),
1045 from_utf8(beg), from_utf8(end));
1049 else if (t.cs() == "def") {
1050 string name = p.get_token().cs();
1051 while (p.next_token().cat() != catBegin)
1052 name += p.get_token().cs();
1053 if (!in_lyx_preamble)
1054 h_preamble << "\\def\\" << name << '{'
1055 << p.verbatim_item() << "}";
1058 else if (t.cs() == "newcolumntype") {
1059 string const name = p.getArg('{', '}');
1060 trimSpaceAndEol(name);
1062 string opts = p.getOpt();
1063 if (!opts.empty()) {
1064 istringstream is(string(opts, 1));
1067 special_columns[name[0]] = nargs;
1068 h_preamble << "\\newcolumntype{" << name << "}";
1070 h_preamble << "[" << nargs << "]";
1071 h_preamble << "{" << p.verbatim_item() << "}";
1074 else if (t.cs() == "setcounter") {
1075 string const name = p.getArg('{', '}');
1076 string const content = p.getArg('{', '}');
1077 if (name == "secnumdepth")
1078 h_secnumdepth = content;
1079 else if (name == "tocdepth")
1080 h_tocdepth = content;
1082 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1085 else if (t.cs() == "setlength") {
1086 string const name = p.verbatim_item();
1087 string const content = p.verbatim_item();
1088 // the paragraphs are only not indented when \parindent is set to zero
1089 if (name == "\\parindent" && content != "") {
1090 if (content[0] == '0')
1091 h_paragraph_separation = "skip";
1093 h_paragraph_indentation = translate_len(content);
1094 } else if (name == "\\parskip") {
1095 if (content == "\\smallskipamount")
1096 h_defskip = "smallskip";
1097 else if (content == "\\medskipamount")
1098 h_defskip = "medskip";
1099 else if (content == "\\bigskipamount")
1100 h_defskip = "bigskip";
1102 h_defskip = content;
1104 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1107 else if (t.cs() == "onehalfspacing")
1108 h_spacing = "onehalf";
1110 else if (t.cs() == "doublespacing")
1111 h_spacing = "double";
1113 else if (t.cs() == "setstretch")
1114 h_spacing = "other " + p.verbatim_item();
1116 else if (t.cs() == "begin") {
1117 string const name = p.getArg('{', '}');
1118 if (name == "document")
1120 h_preamble << "\\begin{" << name << "}";
1123 else if (t.cs() == "geometry") {
1124 h_use_geometry = "true";
1125 vector<string> opts = split_options(p.getArg('{', '}'));
1126 vector<string>::iterator it;
1127 // paper orientation
1128 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1129 h_paperorientation = "landscape";
1133 handle_opt(opts, known_paper_sizes, h_papersize);
1134 delete_opt(opts, known_paper_sizes);
1136 char const * const * margin = known_paper_margins;
1138 for (; *margin; ++margin) {
1140 // search for the "=" in e.g. "lmargin=2cm" to get the value
1141 for(size_t i = 0; i != opts.size(); i++) {
1142 if (opts.at(i).find(*margin) != string::npos) {
1143 string::size_type pos = opts.at(i).find("=");
1144 string value = opts.at(i).substr(pos + 1);
1145 string name = known_coded_paper_margins[k];
1146 h_margins += "\\" + name + " " + value + "\n";
1152 else if (t.cs() == "definecolor") {
1153 string const color = p.getArg('{', '}');
1154 string const space = p.getArg('{', '}');
1155 string const value = p.getArg('{', '}');
1156 if (color == "document_fontcolor" && space == "rgb") {
1157 RGBColor c(RGBColorFromLaTeX(value));
1158 h_fontcolor = X11hexname(c);
1159 } else if (color == "note_fontcolor" && space == "rgb") {
1160 RGBColor c(RGBColorFromLaTeX(value));
1161 h_notefontcolor = X11hexname(c);
1162 } else if (color == "page_backgroundcolor" && space == "rgb") {
1163 RGBColor c(RGBColorFromLaTeX(value));
1164 h_backgroundcolor = X11hexname(c);
1165 } else if (color == "shadecolor" && space == "rgb") {
1166 RGBColor c(RGBColorFromLaTeX(value));
1167 h_boxbgcolor = X11hexname(c);
1169 h_preamble << "\\definecolor{" << color
1170 << "}{" << space << "}{" << value
1175 else if (t.cs() == "jurabibsetup") {
1176 // FIXME p.getArg('{', '}') is most probably wrong (it
1177 // does not handle nested braces).
1178 // Use p.verbatim_item() instead.
1179 vector<string> jurabibsetup =
1180 split_options(p.getArg('{', '}'));
1181 // add jurabibsetup to the jurabib package options
1182 add_package("jurabib", jurabibsetup);
1183 if (!jurabibsetup.empty()) {
1184 h_preamble << "\\jurabibsetup{"
1185 << join(jurabibsetup, ",") << '}';
1189 else if (t.cs() == "hypersetup") {
1190 vector<string> hypersetup =
1191 split_options(p.verbatim_item());
1192 // add hypersetup to the hyperref package options
1193 handle_hyperref(hypersetup);
1194 if (!hypersetup.empty()) {
1195 h_preamble << "\\hypersetup{"
1196 << join(hypersetup, ",") << '}';
1200 else if (is_known(t.cs(), known_if_3arg_commands)) {
1201 // prevent misparsing of \usepackage if it is used
1202 // as an argument (see e.g. our own output of
1203 // \@ifundefined above)
1204 string const arg1 = p.verbatim_item();
1205 string const arg2 = p.verbatim_item();
1206 string const arg3 = p.verbatim_item();
1207 // test case \@ifundefined{date}{}{\date{}}
1208 if (arg1 == "date" && arg2.empty() && arg3 == "\\date{}") {
1209 h_suppress_date = "true";
1210 // test case \@ifundefined{definecolor}{\usepackage{color}}{}
1211 // because we could pollute the preamble with it in roundtrips
1212 } else if (arg1 == "definecolor" && arg2 == "\\usepackage{color}"
1214 ifundefined_color_set = true;
1215 } else if (!in_lyx_preamble) {
1216 h_preamble << t.asInput()
1217 << '{' << arg1 << '}'
1218 << '{' << arg2 << '}'
1219 << '{' << arg3 << '}';
1223 else if (is_known(t.cs(), known_if_commands)) {
1224 // must not parse anything in conditional code, since
1225 // LyX would output the parsed contents unconditionally
1226 if (!in_lyx_preamble)
1227 h_preamble << t.asInput();
1228 handle_if(p, in_lyx_preamble);
1231 else if (!t.cs().empty() && !in_lyx_preamble)
1232 h_preamble << '\\' << t.cs();
1235 // remove the whitespace
1238 // Force textclass if the user wanted it
1239 if (!forceclass.empty())
1240 h_textclass = forceclass;
1241 if (noweb_mode && !prefixIs(h_textclass, "literate-"))
1242 h_textclass.insert(0, "literate-");
1243 tc.setName(h_textclass);
1245 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1248 if (h_papersides.empty()) {
1251 h_papersides = ss.str();
1253 end_preamble(os, tc);
1257 /// translates a babel language name to a LyX language name
1258 string babel2lyx(string const & language)
1260 char const * const * where = is_known(language, known_languages);
1262 return known_coded_languages[where - known_languages];