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", "ccfonts",
137 "chancery", "charter", "cmr", "fourier", "lmodern", "mathpazo", "mathptmx",
140 const char * const known_sans_fonts[] = { "avant", "berasans", "cmbr", "cmss",
141 "helvet", "lmss", 0};
143 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
144 "courier", "lmtt", "luximono", "fourier", "lmodern", "mathpazo", "mathptmx",
147 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
148 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
149 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
150 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
151 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
153 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
154 "executivepaper", "legalpaper", "letterpaper", 0};
156 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
157 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
159 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
160 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
163 /// commands that can start an \if...\else...\endif sequence
164 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
165 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
166 "ifsidecap", "ifupgreek", 0};
168 /// conditional commands with three arguments like \@ifundefined{}{}{}
169 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
173 ostringstream h_preamble;
174 string h_textclass = "article";
175 string h_use_default_options = "false";
177 string h_language = "english";
178 string h_language_package = "default";
179 string h_fontencoding = "default";
180 string h_font_roman = "default";
181 string h_font_sans = "default";
182 string h_font_typewriter = "default";
183 string h_font_default_family = "default";
184 string h_font_sc = "false";
185 string h_font_osf = "false";
186 string h_font_sf_scale = "100";
187 string h_font_tt_scale = "100";
188 string h_graphics = "default";
189 string h_float_placement;
190 string h_paperfontsize = "default";
191 string h_spacing = "single";
192 string h_use_hyperref = "0";
195 string h_pdf_subject;
196 string h_pdf_keywords;
197 string h_pdf_bookmarks = "1";
198 string h_pdf_bookmarksnumbered = "0";
199 string h_pdf_bookmarksopen = "0";
200 string h_pdf_bookmarksopenlevel = "1";
201 string h_pdf_breaklinks = "0";
202 string h_pdf_pdfborder = "0";
203 string h_pdf_colorlinks = "0";
204 string h_pdf_backref = "section";
205 string h_pdf_pdfusetitle = "1";
206 string h_pdf_pagemode;
207 string h_pdf_quoted_options;
208 string h_papersize = "default";
209 string h_use_geometry = "false";
210 string h_use_amsmath = "1";
211 string h_use_esint = "1";
212 string h_use_mhchem = "0";
213 string h_use_mathdots = "0";
214 string h_use_undertilde = "0";
215 string h_cite_engine = "basic";
216 string h_use_bibtopic = "false";
217 string h_paperorientation = "portrait";
218 string h_suppress_date = "false";
219 string h_use_refstyle = "0";
220 string h_backgroundcolor;
223 string h_notefontcolor;
224 string h_secnumdepth = "3";
225 string h_tocdepth = "3";
226 string h_defskip = "medskip";
227 string h_paragraph_indentation = "default";
228 string h_quotes_language = "english";
229 string h_papercolumns = "1";
231 string h_paperpagestyle = "default";
232 string h_listings_params;
233 string h_tracking_changes = "false";
234 string h_output_changes = "false";
235 string h_html_math_output = "0";
236 string h_html_css_as_file = "0";
237 string h_html_be_strict = "false";
241 // returns true if at least one of the options in what has been found
242 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
248 // the last language option is the document language (for babel and LyX)
249 // the last size option is the document font size
250 vector<string>::iterator it;
251 vector<string>::iterator position = opts.begin();
252 for (; *what; ++what) {
253 it = find(opts.begin(), opts.end(), *what);
254 if (it != opts.end()) {
255 if (it >= position) {
266 void delete_opt(vector<string> & opts, char const * const * what)
271 // remove found options from the list
272 // do this after handle_opt to avoid potential memory leaks
273 vector<string>::iterator it;
274 for (; *what; ++what) {
275 it = find(opts.begin(), opts.end(), *what);
276 if (it != opts.end())
283 * Split a package options string (keyval format) into a vector.
285 * authorformat=smallcaps,
287 * titleformat=colonsep,
288 * bibformat={tabular,ibidem,numbered}
290 vector<string> split_options(string const & input)
292 vector<string> options;
296 Token const & t = p.get_token();
297 if (t.asInput() == ",") {
298 options.push_back(trimSpaceAndEol(option));
300 } else if (t.asInput() == "=") {
303 if (p.next_token().asInput() == "{")
304 option += '{' + p.getArg('{', '}') + '}';
305 } else if (t.cat() != catSpace)
306 option += t.asInput();
310 options.push_back(trimSpaceAndEol(option));
317 * Retrieve a keyval option "name={value with=sign}" named \p name from
318 * \p options and return the value.
319 * The found option is also removed from \p options.
321 string process_keyval_opt(vector<string> & options, string name)
323 for (size_t i = 0; i < options.size(); ++i) {
324 vector<string> option;
325 split(options[i], option, '=');
326 if (option.size() < 2)
328 if (option[0] == name) {
329 options.erase(options.begin() + i);
330 option.erase(option.begin());
331 return join(option, "=");
339 * Add package \p name with options \p options to used_packages.
340 * Remove options from \p options that we don't want to output.
342 void add_package(string const & name, vector<string> & options)
344 // every package inherits the global options
345 if (used_packages.find(name) == used_packages.end())
346 used_packages[name] = split_options(h_options);
348 vector<string> & v = used_packages[name];
349 v.insert(v.end(), options.begin(), options.end());
350 if (name == "jurabib") {
351 // Don't output the order argument (see the cite command
352 // handling code in text.cpp).
353 vector<string>::iterator end =
354 remove(options.begin(), options.end(), "natbiborder");
355 end = remove(options.begin(), end, "jurabiborder");
356 options.erase(end, options.end());
361 // Given is a string like "scaled=0.9", return 0.9 * 100
362 string const scale_as_percentage(string const & scale)
364 string::size_type pos = scale.find('=');
365 if (pos != string::npos) {
366 string value = scale.substr(pos + 1);
368 return convert<string>(100 * convert<double>(value));
370 // If the input string didn't match our expectations.
371 // return the default value "100"
376 string remove_braces(string const & value)
380 if (value[0] == '{' && value[value.length()-1] == '}')
381 return value.substr(1, value.length()-2);
386 void handle_hyperref(vector<string> & options)
388 // FIXME swallow inputencoding changes that might surround the
389 // hyperref setup if it was written by LyX
390 h_use_hyperref = "1";
391 // swallow "unicode=true", since LyX does always write that
392 vector<string>::iterator it =
393 find(options.begin(), options.end(), "unicode=true");
394 if (it != options.end())
396 it = find(options.begin(), options.end(), "pdfusetitle");
397 if (it != options.end()) {
398 h_pdf_pdfusetitle = "1";
401 string bookmarks = process_keyval_opt(options, "bookmarks");
402 if (bookmarks == "true")
403 h_pdf_bookmarks = "1";
404 else if (bookmarks == "false")
405 h_pdf_bookmarks = "0";
406 if (h_pdf_bookmarks == "1") {
407 string bookmarksnumbered =
408 process_keyval_opt(options, "bookmarksnumbered");
409 if (bookmarksnumbered == "true")
410 h_pdf_bookmarksnumbered = "1";
411 else if (bookmarksnumbered == "false")
412 h_pdf_bookmarksnumbered = "0";
413 string bookmarksopen =
414 process_keyval_opt(options, "bookmarksopen");
415 if (bookmarksopen == "true")
416 h_pdf_bookmarksopen = "1";
417 else if (bookmarksopen == "false")
418 h_pdf_bookmarksopen = "0";
419 if (h_pdf_bookmarksopen == "1") {
420 string bookmarksopenlevel =
421 process_keyval_opt(options, "bookmarksopenlevel");
422 if (!bookmarksopenlevel.empty())
423 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
426 string breaklinks = process_keyval_opt(options, "breaklinks");
427 if (breaklinks == "true")
428 h_pdf_breaklinks = "1";
429 else if (breaklinks == "false")
430 h_pdf_breaklinks = "0";
431 string pdfborder = process_keyval_opt(options, "pdfborder");
432 if (pdfborder == "{0 0 0}")
433 h_pdf_pdfborder = "1";
434 else if (pdfborder == "{0 0 1}")
435 h_pdf_pdfborder = "0";
436 string backref = process_keyval_opt(options, "backref");
437 if (!backref.empty())
438 h_pdf_backref = backref;
439 string colorlinks = process_keyval_opt(options, "colorlinks");
440 if (colorlinks == "true")
441 h_pdf_colorlinks = "1";
442 else if (colorlinks == "false")
443 h_pdf_colorlinks = "0";
444 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
445 if (!pdfpagemode.empty())
446 h_pdf_pagemode = pdfpagemode;
447 string pdftitle = process_keyval_opt(options, "pdftitle");
448 if (!pdftitle.empty()) {
449 h_pdf_title = remove_braces(pdftitle);
451 string pdfauthor = process_keyval_opt(options, "pdfauthor");
452 if (!pdfauthor.empty()) {
453 h_pdf_author = remove_braces(pdfauthor);
455 string pdfsubject = process_keyval_opt(options, "pdfsubject");
456 if (!pdfsubject.empty())
457 h_pdf_subject = remove_braces(pdfsubject);
458 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
459 if (!pdfkeywords.empty())
460 h_pdf_keywords = remove_braces(pdfkeywords);
461 if (!options.empty()) {
462 if (!h_pdf_quoted_options.empty())
463 h_pdf_quoted_options += ',';
464 h_pdf_quoted_options += join(options, ",");
470 void handle_package(Parser &p, string const & name, string const & opts,
471 bool in_lyx_preamble)
473 vector<string> options = split_options(opts);
474 add_package(name, options);
478 if (is_known(name, known_roman_fonts)) {
483 if (name == "fourier") {
484 h_font_roman = "utopia";
485 // when font uses real small capitals
486 if (opts == "expert")
490 if (name == "mathpazo")
491 h_font_roman = "palatino";
493 if (name == "mathptmx")
494 h_font_roman = "times";
497 if (is_known(name, known_sans_fonts)) {
501 h_font_sf_scale = scale_as_percentage(scale);
506 if (is_known(name, known_typewriter_fonts)) {
507 // fourier can be set as roman font _only_
508 // fourier as typewriter is handled in handling of \ttdefault
509 if (name != "fourier") {
510 h_font_typewriter = name;
513 h_font_tt_scale = scale_as_percentage(scale);
518 // font uses old-style figure
522 else if (name == "amsmath" || name == "amssymb")
525 else if (name == "esint")
528 else if (name == "mhchem")
531 else if (name == "mathdots")
532 h_use_mathdots = "2";
534 else if (name == "undertilde")
535 h_use_undertilde = "2";
537 else if (name == "babel") {
538 // we have to do nothing if babel is loaded without any options, otherwise
539 // we would pollute the preamble with this call in every roundtrip
541 // check if more than one option was used - used later for inputenc
542 // in case inputenc is parsed before babel, set the encoding to auto
543 if (options.begin() != options.end() - 1) {
544 one_language = false;
545 h_inputencoding = "auto";
547 // babel takes the last language of the option of its \usepackage
548 // call as document language. If there is no such language option, the
549 // last language in the documentclass options is used.
550 handle_opt(options, known_languages, h_language);
551 delete_opt(options, known_languages);
555 else if (name == "fontenc") {
556 h_fontencoding = getStringFromVector(options, ",");
557 // as of version LyX 2.0 "T1" is equal to the setting "global"
558 if (h_fontencoding == "T1")
559 h_fontencoding = "global";
563 else if (name == "inputenc" || name == "luainputenc") {
564 // h_inputencoding is only set when there is not more than one
565 // inputenc option because otherwise h_inputencoding must be
566 // set to "auto" (the default encoding of the document language)
567 // Therefore check for the "," character.
568 // It is also only set when there is not more then one babel
569 // language option but this is handled in the routine for babel.
570 if (opts.find(",") == string::npos && one_language == true)
571 h_inputencoding = opts;
572 if (!options.empty())
573 p.setEncoding(options.back());
577 else if (name == "makeidx")
580 else if (name == "prettyref")
583 else if (name == "varioref")
586 else if (name == "verbatim")
589 else if (name == "nomencl")
592 else if (name == "textcomp")
595 else if (name == "url")
598 else if (name == "subscript")
601 else if (name == "color") {
602 // with the following command this package is only loaded when needed for
603 // undefined colors, since we only support the predefined colors
604 // only add it if not yet added
605 if (!ifundefined_color_set)
606 h_preamble << "\\@ifundefined{definecolor}\n {\\usepackage{color}}{}\n";
609 else if (name == "graphicx")
612 else if (name == "setspace")
616 // do not ignore as long as we don't support all commands (e.g. \xout is missing)
617 else if (name == "ulem")
621 else if (name == "geometry")
622 ; // Ignore this, the geometry settings are made by the \geometry
623 // command. This command is handled below.
625 else if (is_known(name, known_languages))
628 else if (name == "natbib") {
629 h_cite_engine = "natbib_authoryear";
630 vector<string>::iterator it =
631 find(options.begin(), options.end(), "authoryear");
632 if (it != options.end())
635 it = find(options.begin(), options.end(), "numbers");
636 if (it != options.end()) {
637 h_cite_engine = "natbib_numerical";
643 else if (name == "jurabib")
644 h_cite_engine = "jurabib";
646 else if (name == "hyperref")
647 handle_hyperref(options);
649 else if (!in_lyx_preamble) {
651 h_preamble << "\\usepackage{" << name << "}";
653 h_preamble << "\\usepackage[" << opts << "]{"
659 // We need to do something with the options...
660 if (!options.empty())
661 cerr << "Ignoring options '" << join(options, ",")
662 << "' of package " << name << '.' << endl;
664 // remove the whitespace
669 void handle_if(Parser & p, bool in_lyx_preamble)
672 Token t = p.get_token();
673 if (t.cat() == catEscape &&
674 is_known(t.cs(), known_if_commands))
675 handle_if(p, in_lyx_preamble);
677 if (!in_lyx_preamble)
678 h_preamble << t.asInput();
679 if (t.cat() == catEscape && t.cs() == "fi")
686 void end_preamble(ostream & os, TextClass const & /*textclass*/)
688 // translate from babel to LyX names
689 h_language = babel2lyx(h_language);
691 // set the quote language
692 // LyX only knows the following quotes languages:
693 // english, swedish, german, polish, french and danish
694 // (quotes for "japanese" and "chinese-traditional" are missing because
695 // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
696 // conversion list taken from
697 // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
698 // (quotes for kazakh and interlingua are unknown)
700 if (h_language == "danish")
701 h_quotes_language = "danish";
703 else if (is_known(h_language, known_french_quotes_languages))
704 h_quotes_language = "french";
706 else if (is_known(h_language, known_german_quotes_languages))
707 h_quotes_language = "german";
709 else if (is_known(h_language, known_polish_quotes_languages))
710 h_quotes_language = "polish";
712 else if (is_known(h_language, known_swedish_quotes_languages))
713 h_quotes_language = "swedish";
715 else if (is_known(h_language, known_english_quotes_languages))
716 h_quotes_language = "english";
718 // output the LyX file settings
719 os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
720 << "\\lyxformat " << LYX_FORMAT << '\n'
721 << "\\begin_document\n"
722 << "\\begin_header\n"
723 << "\\textclass " << h_textclass << "\n";
724 if (!h_preamble.str().empty())
725 os << "\\begin_preamble\n" << h_preamble.str() << "\n\\end_preamble\n";
726 if (!h_options.empty())
727 os << "\\options " << h_options << "\n";
728 os << "\\use_default_options " << h_use_default_options << "\n"
729 << modules_placeholder
730 << "\\language " << h_language << "\n"
731 << "\\language_package " << h_language_package << "\n"
732 << "\\inputencoding " << h_inputencoding << "\n"
733 << "\\fontencoding " << h_fontencoding << "\n"
734 << "\\font_roman " << h_font_roman << "\n"
735 << "\\font_sans " << h_font_sans << "\n"
736 << "\\font_typewriter " << h_font_typewriter << "\n"
737 << "\\font_default_family " << h_font_default_family << "\n"
738 << "\\font_sc " << h_font_sc << "\n"
739 << "\\font_osf " << h_font_osf << "\n"
740 << "\\font_sf_scale " << h_font_sf_scale << "\n"
741 << "\\font_tt_scale " << h_font_tt_scale << "\n"
742 << "\\graphics " << h_graphics << "\n";
743 if (!h_float_placement.empty())
744 os << "\\float_placement " << h_float_placement << "\n";
745 os << "\\paperfontsize " << h_paperfontsize << "\n"
746 << "\\spacing " << h_spacing << "\n"
747 << "\\use_hyperref " << h_use_hyperref << '\n';
748 if (h_use_hyperref == "1") {
749 if (!h_pdf_title.empty())
750 os << "\\pdf_title \"" << h_pdf_title << "\"\n";
751 if (!h_pdf_author.empty())
752 os << "\\pdf_author \"" << h_pdf_author << "\"\n";
753 if (!h_pdf_subject.empty())
754 os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
755 if (!h_pdf_keywords.empty())
756 os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
757 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
758 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
759 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
760 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
761 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
762 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
763 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
764 "\\pdf_backref " << h_pdf_backref << "\n"
765 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
766 if (!h_pdf_pagemode.empty())
767 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
768 if (!h_pdf_quoted_options.empty())
769 os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
771 os << "\\papersize " << h_papersize << "\n"
772 << "\\use_geometry " << h_use_geometry << "\n"
773 << "\\use_amsmath " << h_use_amsmath << "\n"
774 << "\\use_esint " << h_use_esint << "\n"
775 << "\\use_mhchem " << h_use_mhchem << "\n"
776 << "\\use_mathdots " << h_use_mathdots << "\n"
777 << "\\use_undertilde " << h_use_undertilde << "\n"
778 << "\\cite_engine " << h_cite_engine << "\n"
779 << "\\use_bibtopic " << h_use_bibtopic << "\n"
780 << "\\paperorientation " << h_paperorientation << '\n'
781 << "\\suppress_date " << h_suppress_date << '\n'
782 << "\\use_refstyle " << h_use_refstyle << '\n';
783 if (!h_fontcolor.empty())
784 os << "\\fontcolor " << h_fontcolor << '\n';
785 if (!h_notefontcolor.empty())
786 os << "\\notefontcolor " << h_notefontcolor << '\n';
787 if (!h_backgroundcolor.empty())
788 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
789 if (!h_boxbgcolor.empty())
790 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
792 << "\\secnumdepth " << h_secnumdepth << "\n"
793 << "\\tocdepth " << h_tocdepth << "\n"
794 << "\\paragraph_separation " << h_paragraph_separation << "\n";
795 if (h_paragraph_separation == "skip")
796 os << "\\defskip " << h_defskip << "\n";
798 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
799 os << "\\quotes_language " << h_quotes_language << "\n"
800 << "\\papercolumns " << h_papercolumns << "\n"
801 << "\\papersides " << h_papersides << "\n"
802 << "\\paperpagestyle " << h_paperpagestyle << "\n";
803 if (!h_listings_params.empty())
804 os << "\\listings_params " << h_listings_params << "\n";
805 os << "\\tracking_changes " << h_tracking_changes << "\n"
806 << "\\output_changes " << h_output_changes << "\n"
807 << "\\html_math_output " << h_html_math_output << "\n"
808 << "\\html_css_as_file " << h_html_css_as_file << "\n"
809 << "\\html_be_strict " << h_html_be_strict << "\n"
810 << "\\end_header\n\n"
812 // clear preamble for subdocuments
816 } // anonymous namespace
819 void parse_preamble(Parser & p, ostream & os,
820 string const & forceclass, TeX2LyXDocClass & tc)
822 // initialize fixed types
823 special_columns['D'] = 3;
824 bool is_full_document = false;
825 bool is_lyx_file = false;
826 bool in_lyx_preamble = false;
828 // determine whether this is a full document or a fragment for inclusion
830 Token const & t = p.get_token();
832 if (t.cat() == catEscape && t.cs() == "documentclass") {
833 is_full_document = true;
839 while (is_full_document && p.good()) {
840 Token const & t = p.get_token();
843 cerr << "t: " << t << "\n";
849 if (!in_lyx_preamble &&
850 (t.cat() == catLetter ||
851 t.cat() == catSuper ||
853 t.cat() == catOther ||
854 t.cat() == catMath ||
855 t.cat() == catActive ||
856 t.cat() == catBegin ||
858 t.cat() == catAlign ||
859 t.cat() == catParameter))
860 h_preamble << t.cs();
862 else if (!in_lyx_preamble &&
863 (t.cat() == catSpace || t.cat() == catNewline))
864 h_preamble << t.asInput();
866 else if (t.cat() == catComment) {
867 static regex const islyxfile("%% LyX .* created this file");
868 static regex const usercommands("User specified LaTeX commands");
870 string const comment = t.asInput();
872 // magically switch encoding default if it looks like XeLaTeX
873 static string const magicXeLaTeX =
874 "% This document must be compiled with XeLaTeX ";
875 if (comment.size() > magicXeLaTeX.size()
876 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
877 && h_inputencoding == "auto") {
878 cerr << "XeLaTeX comment found, switching to UTF8\n";
879 h_inputencoding = "utf8";
882 if (regex_search(comment, sub, islyxfile)) {
884 in_lyx_preamble = true;
885 } else if (is_lyx_file
886 && regex_search(comment, sub, usercommands))
887 in_lyx_preamble = false;
888 else if (!in_lyx_preamble)
889 h_preamble << t.asInput();
892 else if (t.cs() == "pagestyle")
893 h_paperpagestyle = p.verbatim_item();
895 else if (t.cs() == "date") {
896 if (p.verbatim_item().empty())
897 h_suppress_date = "true";
900 else if (t.cs() == "makeatletter") {
901 // LyX takes care of this
902 p.setCatCode('@', catLetter);
905 else if (t.cs() == "makeatother") {
906 // LyX takes care of this
907 p.setCatCode('@', catOther);
910 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
911 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
912 || t.cs() == "providecommand" || t.cs() == "providecommandx"
913 || t.cs() == "DeclareRobustCommand"
914 || t.cs() == "DeclareRobustCommandx"
915 || t.cs() == "ProvideTextCommandDefault"
916 || t.cs() == "DeclareMathAccent") {
918 if (p.next_token().character() == '*') {
922 string const name = p.verbatim_item();
923 string const opt1 = p.getFullOpt();
924 string const opt2 = p.getFullOpt();
925 string const body = p.verbatim_item();
927 if (name == "\\rmdefault")
928 if (is_known(body, known_roman_fonts))
930 if (name == "\\sfdefault")
931 if (is_known(body, known_sans_fonts))
933 if (name == "\\ttdefault")
934 if (is_known(body, known_typewriter_fonts))
935 h_font_typewriter = body;
936 if (name == "\\familydefault") {
937 string family = body;
938 // remove leading "\"
939 h_font_default_family = family.erase(0,1);
942 // Add the command to the known commands
943 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
945 // only non-lyxspecific stuff
946 if (!in_lyx_preamble) {
948 ss << '\\' << t.cs();
951 ss << '{' << name << '}' << opt1 << opt2
952 << '{' << body << "}";
953 h_preamble << ss.str();
955 ostream & out = in_preamble ? h_preamble : os;
956 out << "\\" << t.cs() << "{" << name << "}"
957 << opts << "{" << body << "}";
962 else if (t.cs() == "documentclass") {
963 vector<string>::iterator it;
964 vector<string> opts = split_options(p.getArg('[', ']'));
965 handle_opt(opts, known_fontsizes, h_paperfontsize);
966 delete_opt(opts, known_fontsizes);
967 // delete "pt" at the end
968 string::size_type i = h_paperfontsize.find("pt");
969 if (i != string::npos)
970 h_paperfontsize.erase(i);
971 // The documentclass options are always parsed before the options
972 // of the babel call so that a language cannot overwrite the babel
974 handle_opt(opts, known_languages, h_language);
975 delete_opt(opts, known_languages);
978 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
979 h_paperorientation = "landscape";
983 if ((it = find(opts.begin(), opts.end(), "oneside"))
988 if ((it = find(opts.begin(), opts.end(), "twoside"))
994 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
996 h_papercolumns = "1";
999 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1001 h_papercolumns = "2";
1005 // some size options are know to any document classes, other sizes
1006 // are handled by the \geometry command of the geometry package
1007 handle_opt(opts, known_class_paper_sizes, h_papersize);
1008 delete_opt(opts, known_class_paper_sizes);
1009 // the remaining options
1010 h_options = join(opts, ",");
1011 // FIXME This does not work for classes that have a
1012 // different name in LyX than in LaTeX
1013 h_textclass = p.getArg('{', '}');
1016 else if (t.cs() == "usepackage") {
1017 string const options = p.getArg('[', ']');
1018 string const name = p.getArg('{', '}');
1019 vector<string> vecnames;
1020 split(name, vecnames, ',');
1021 vector<string>::const_iterator it = vecnames.begin();
1022 vector<string>::const_iterator end = vecnames.end();
1023 for (; it != end; ++it)
1024 handle_package(p, trimSpaceAndEol(*it), options,
1028 else if (t.cs() == "inputencoding") {
1029 string const encoding = p.getArg('{','}');
1030 h_inputencoding = encoding;
1031 p.setEncoding(encoding);
1034 else if (t.cs() == "newenvironment") {
1035 string const name = p.getArg('{', '}');
1036 string const opt1 = p.getFullOpt();
1037 string const opt2 = p.getFullOpt();
1038 string const beg = p.verbatim_item();
1039 string const end = p.verbatim_item();
1040 if (!in_lyx_preamble) {
1041 h_preamble << "\\newenvironment{" << name
1042 << '}' << opt1 << opt2 << '{'
1043 << beg << "}{" << end << '}';
1045 add_known_environment(name, opt1, !opt2.empty(),
1046 from_utf8(beg), from_utf8(end));
1050 else if (t.cs() == "def") {
1051 string name = p.get_token().cs();
1052 while (p.next_token().cat() != catBegin)
1053 name += p.get_token().cs();
1054 if (!in_lyx_preamble)
1055 h_preamble << "\\def\\" << name << '{'
1056 << p.verbatim_item() << "}";
1059 else if (t.cs() == "newcolumntype") {
1060 string const name = p.getArg('{', '}');
1061 trimSpaceAndEol(name);
1063 string opts = p.getOpt();
1064 if (!opts.empty()) {
1065 istringstream is(string(opts, 1));
1068 special_columns[name[0]] = nargs;
1069 h_preamble << "\\newcolumntype{" << name << "}";
1071 h_preamble << "[" << nargs << "]";
1072 h_preamble << "{" << p.verbatim_item() << "}";
1075 else if (t.cs() == "setcounter") {
1076 string const name = p.getArg('{', '}');
1077 string const content = p.getArg('{', '}');
1078 if (name == "secnumdepth")
1079 h_secnumdepth = content;
1080 else if (name == "tocdepth")
1081 h_tocdepth = content;
1083 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1086 else if (t.cs() == "setlength") {
1087 string const name = p.verbatim_item();
1088 string const content = p.verbatim_item();
1089 // the paragraphs are only not indented when \parindent is set to zero
1090 if (name == "\\parindent" && content != "") {
1091 if (content[0] == '0')
1092 h_paragraph_separation = "skip";
1094 h_paragraph_indentation = translate_len(content);
1095 } else if (name == "\\parskip") {
1096 if (content == "\\smallskipamount")
1097 h_defskip = "smallskip";
1098 else if (content == "\\medskipamount")
1099 h_defskip = "medskip";
1100 else if (content == "\\bigskipamount")
1101 h_defskip = "bigskip";
1103 h_defskip = content;
1105 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1108 else if (t.cs() == "onehalfspacing")
1109 h_spacing = "onehalf";
1111 else if (t.cs() == "doublespacing")
1112 h_spacing = "double";
1114 else if (t.cs() == "setstretch")
1115 h_spacing = "other " + p.verbatim_item();
1117 else if (t.cs() == "begin") {
1118 string const name = p.getArg('{', '}');
1119 if (name == "document")
1121 h_preamble << "\\begin{" << name << "}";
1124 else if (t.cs() == "geometry") {
1125 h_use_geometry = "true";
1126 vector<string> opts = split_options(p.getArg('{', '}'));
1127 vector<string>::iterator it;
1128 // paper orientation
1129 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1130 h_paperorientation = "landscape";
1134 handle_opt(opts, known_paper_sizes, h_papersize);
1135 delete_opt(opts, known_paper_sizes);
1137 char const * const * margin = known_paper_margins;
1139 for (; *margin; ++margin) {
1141 // search for the "=" in e.g. "lmargin=2cm" to get the value
1142 for(size_t i = 0; i != opts.size(); i++) {
1143 if (opts.at(i).find(*margin) != string::npos) {
1144 string::size_type pos = opts.at(i).find("=");
1145 string value = opts.at(i).substr(pos + 1);
1146 string name = known_coded_paper_margins[k];
1147 h_margins += "\\" + name + " " + value + "\n";
1153 else if (t.cs() == "definecolor") {
1154 string const color = p.getArg('{', '}');
1155 string const space = p.getArg('{', '}');
1156 string const value = p.getArg('{', '}');
1157 if (color == "document_fontcolor" && space == "rgb") {
1158 RGBColor c(RGBColorFromLaTeX(value));
1159 h_fontcolor = X11hexname(c);
1160 } else if (color == "note_fontcolor" && space == "rgb") {
1161 RGBColor c(RGBColorFromLaTeX(value));
1162 h_notefontcolor = X11hexname(c);
1163 } else if (color == "page_backgroundcolor" && space == "rgb") {
1164 RGBColor c(RGBColorFromLaTeX(value));
1165 h_backgroundcolor = X11hexname(c);
1166 } else if (color == "shadecolor" && space == "rgb") {
1167 RGBColor c(RGBColorFromLaTeX(value));
1168 h_boxbgcolor = X11hexname(c);
1170 h_preamble << "\\definecolor{" << color
1171 << "}{" << space << "}{" << value
1176 else if (t.cs() == "jurabibsetup") {
1177 // FIXME p.getArg('{', '}') is most probably wrong (it
1178 // does not handle nested braces).
1179 // Use p.verbatim_item() instead.
1180 vector<string> jurabibsetup =
1181 split_options(p.getArg('{', '}'));
1182 // add jurabibsetup to the jurabib package options
1183 add_package("jurabib", jurabibsetup);
1184 if (!jurabibsetup.empty()) {
1185 h_preamble << "\\jurabibsetup{"
1186 << join(jurabibsetup, ",") << '}';
1190 else if (t.cs() == "hypersetup") {
1191 vector<string> hypersetup =
1192 split_options(p.verbatim_item());
1193 // add hypersetup to the hyperref package options
1194 handle_hyperref(hypersetup);
1195 if (!hypersetup.empty()) {
1196 h_preamble << "\\hypersetup{"
1197 << join(hypersetup, ",") << '}';
1201 else if (is_known(t.cs(), known_if_3arg_commands)) {
1202 // prevent misparsing of \usepackage if it is used
1203 // as an argument (see e.g. our own output of
1204 // \@ifundefined above)
1205 string const arg1 = p.verbatim_item();
1206 string const arg2 = p.verbatim_item();
1207 string const arg3 = p.verbatim_item();
1208 // test case \@ifundefined{date}{}{\date{}}
1209 if (arg1 == "date" && arg2.empty() && arg3 == "\\date{}") {
1210 h_suppress_date = "true";
1211 // test case \@ifundefined{definecolor}{\usepackage{color}}{}
1212 // because we could pollute the preamble with it in roundtrips
1213 } else if (arg1 == "definecolor" && arg2 == "\\usepackage{color}"
1215 ifundefined_color_set = true;
1216 } else if (!in_lyx_preamble) {
1217 h_preamble << t.asInput()
1218 << '{' << arg1 << '}'
1219 << '{' << arg2 << '}'
1220 << '{' << arg3 << '}';
1224 else if (is_known(t.cs(), known_if_commands)) {
1225 // must not parse anything in conditional code, since
1226 // LyX would output the parsed contents unconditionally
1227 if (!in_lyx_preamble)
1228 h_preamble << t.asInput();
1229 handle_if(p, in_lyx_preamble);
1232 else if (!t.cs().empty() && !in_lyx_preamble)
1233 h_preamble << '\\' << t.cs();
1236 // remove the whitespace
1239 // Force textclass if the user wanted it
1240 if (!forceclass.empty())
1241 h_textclass = forceclass;
1242 if (noweb_mode && !prefixIs(h_textclass, "literate-"))
1243 h_textclass.insert(0, "literate-");
1244 tc.setName(h_textclass);
1246 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1249 if (h_papersides.empty()) {
1252 h_papersides = ss.str();
1254 end_preamble(os, tc);
1258 /// translates a babel language name to a LyX language name
1259 string babel2lyx(string const & language)
1261 char const * const * where = is_known(language, known_languages);
1263 return known_coded_languages[where - known_languages];