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";
56 //add this to known_languages when updating to lyxformat 266:
57 // "armenian" (needs special handling since not supported by standard babel)
58 //add these to known_languages when updating to lyxformat 268:
59 //"chinese-simplified", "chinese-traditional", "japanese", "korean"
60 // Both changes require first that support for non-babel languages (CJK,
62 // add turkmen for lyxformat 383
64 * known babel language names (including synonyms)
65 * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
66 * not yet supported by LyX: kurmanji
67 * please keep this in sync with known_coded_languages line by line!
69 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
70 "american", "arabic", "arabtex", "austrian", "bahasa", "bahasai", "bahasam",
71 "basque", "belarusian", "brazil", "brazilian", "breton", "british", "bulgarian",
72 "canadian", "canadien", "catalan", "croatian", "czech", "danish", "dutch",
73 "english", "esperanto", "estonian", "farsi", "finnish", "francais", "french",
74 "frenchb", "frenchle", "frenchpro", "galician", "german", "germanb", "greek",
75 "hebrew", "hungarian", "icelandic", "indon", "indonesian", "interlingua",
76 "irish", "italian", "kazakh", "latin", "latvian", "lithuanian", "lowersorbian",
77 "lsorbian", "magyar", "malay", "meyalu", "mongolian", "naustrian", "newzealand",
78 "ngerman", "ngermanb", "norsk", "nynorsk", "polutonikogreek", "polish",
79 "portuges", "portuguese", "romanian", "russian", "russianb", "samin",
80 "scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish",
81 "swedish", "thai", "turkish", "turkmen", "ukraineb", "ukrainian",
82 "uppersorbian", "UKenglish", "USenglish", "usorbian", "vietnam", "welsh",
86 * the same as known_languages with .lyx names
87 * please keep this in sync with known_languages line by line!
89 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
90 "american", "arabic_arabi", "arabic_arabtex", "austrian", "bahasa", "bahasa", "bahasam",
91 "basque", "belarusian", "brazilian", "brazilian", "breton", "british", "bulgarian",
92 "canadian", "canadien", "catalan", "croatian", "czech", "danish", "dutch",
93 "english", "esperanto", "estonian", "farsi", "finnish", "french", "french",
94 "french", "french", "french", "galician", "german", "german", "greek",
95 "hebrew", "magyar", "icelandic", "bahasa", "bahasa", "interlingua",
96 "irish", "italian", "kazakh", "latin", "latvian", "lithuanian", "lowersorbian",
97 "lowersorbian", "magyar", "bahasam", "bahasam", "mongolian", "naustrian", "english",
98 "ngerman", "ngerman", "norsk", "nynorsk", "polutonikogreek", "polish",
99 "portuguese", "portuguese", "romanian", "russian", "russian", "samin",
100 "scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish",
101 "swedish", "thai", "turkish", "turkmen", "ukrainian", "ukrainian",
102 "uppersorbian", "uppersorbian", "english", "english", "vietnamese", "welsh",
105 /// languages with english quotes (.lyx names)
106 const char * const known_english_quotes_languages[] = {"american", "bahasa",
107 "bahasam", "brazilian", "canadian", "chinese-simplified", "english",
108 "esperanto", "hebrew", "irish", "korean", "portuguese", "scottish", "thai", 0};
110 /// languages with french quotes (.lyx names)
111 const char * const known_french_quotes_languages[] = {"albanian",
112 "arabic_arabi", "arabic_arabtex", "basque", "canadien", "catalan", "french",
113 "galician", "greek", "italian", "norsk", "nynorsk", "polutonikogreek",
114 "russian", "spanish", "spanish-mexico", "turkish", "turkmen", "ukrainian",
117 /// languages with german quotes (.lyx names)
118 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
119 "czech", "german", "icelandic", "lithuanian", "lowersorbian", "naustrian",
120 "ngerman", "serbian", "serbian-latin", "slovak", "slovene", "uppersorbian", 0};
122 /// languages with polish quotes (.lyx names)
123 const char * const known_polish_quotes_languages[] = {"afrikaans", "croatian",
124 "dutch", "estonian", "magyar", "polish", "romanian", 0};
126 /// languages with swedish quotes (.lyx names)
127 const char * const known_swedish_quotes_languages[] = {"finnish",
130 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
132 const char * const known_roman_fonts[] = { "ae", "bookman", "charter",
133 "cmr", "fourier", "lmodern", "mathpazo", "mathptmx", "newcent", 0};
135 const char * const known_sans_fonts[] = { "avant", "berasans", "cmbr", "cmss",
136 "helvet", "lmss", 0};
138 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
139 "courier", "lmtt", "luximono", "fourier", "lmodern", "mathpazo", "mathptmx",
142 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
143 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
144 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
145 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
146 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
148 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
149 "executivepaper", "legalpaper", "letterpaper", 0};
151 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
152 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
154 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
155 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
158 /// commands that can start an \if...\else...\endif sequence
159 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
160 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
161 "ifsidecap", "ifupgreek", 0};
163 /// conditional commands with three arguments like \@ifundefined{}{}{}
164 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
168 ostringstream h_preamble;
169 string h_textclass = "article";
170 string h_use_default_options = "false";
172 string h_language = "english";
173 string h_language_package = "default";
174 string h_fontencoding = "default";
175 string h_font_roman = "default";
176 string h_font_sans = "default";
177 string h_font_typewriter = "default";
178 string h_font_default_family = "default";
179 string h_font_sc = "false";
180 string h_font_osf = "false";
181 string h_font_sf_scale = "100";
182 string h_font_tt_scale = "100";
183 string h_graphics = "default";
184 string h_float_placement;
185 string h_paperfontsize = "default";
186 string h_spacing = "single";
187 string h_use_hyperref = "0";
190 string h_pdf_subject;
191 string h_pdf_keywords;
192 string h_pdf_bookmarks = "1";
193 string h_pdf_bookmarksnumbered = "0";
194 string h_pdf_bookmarksopen = "0";
195 string h_pdf_bookmarksopenlevel = "1";
196 string h_pdf_breaklinks = "0";
197 string h_pdf_pdfborder = "0";
198 string h_pdf_colorlinks = "0";
199 string h_pdf_backref = "section";
200 string h_pdf_pdfusetitle = "1";
201 string h_pdf_pagemode;
202 string h_pdf_quoted_options;
203 string h_papersize = "default";
204 string h_use_geometry = "false";
205 string h_use_amsmath = "1";
206 string h_use_esint = "1";
207 string h_use_mhchem = "0";
208 string h_use_mathdots = "0";
209 string h_use_undertilde = "0";
210 string h_cite_engine = "basic";
211 string h_use_bibtopic = "false";
212 string h_paperorientation = "portrait";
213 string h_suppress_date = "false";
214 string h_use_refstyle = "0";
215 string h_notefontcolor;
216 string h_secnumdepth = "3";
217 string h_tocdepth = "3";
218 string h_defskip = "medskip";
219 string h_paragraph_indentation = "default";
220 string h_quotes_language = "english";
221 string h_papercolumns = "1";
223 string h_paperpagestyle = "default";
224 string h_listings_params;
225 string h_tracking_changes = "false";
226 string h_output_changes = "false";
227 string h_html_math_output = "0";
228 string h_html_css_as_file = "0";
229 string h_html_be_strict = "false";
233 // returns true if at least one of the options in what has been found
234 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
240 // the last language option is the document language (for babel and LyX)
241 // the last size option is the document font size
242 vector<string>::iterator it;
243 vector<string>::iterator position = opts.begin();
244 for (; *what; ++what) {
245 it = find(opts.begin(), opts.end(), *what);
246 if (it != opts.end()) {
247 if (it >= position) {
258 void delete_opt(vector<string> & opts, char const * const * what)
263 // remove found options from the list
264 // do this after handle_opt to avoid potential memory leaks
265 vector<string>::iterator it;
266 for (; *what; ++what) {
267 it = find(opts.begin(), opts.end(), *what);
268 if (it != opts.end())
275 * Split a package options string (keyval format) into a vector.
277 * authorformat=smallcaps,
279 * titleformat=colonsep,
280 * bibformat={tabular,ibidem,numbered}
282 vector<string> split_options(string const & input)
284 vector<string> options;
288 Token const & t = p.get_token();
289 if (t.asInput() == ",") {
290 options.push_back(trimSpaceAndEol(option));
292 } else if (t.asInput() == "=") {
295 if (p.next_token().asInput() == "{")
296 option += '{' + p.getArg('{', '}') + '}';
297 } else if (t.cat() != catSpace)
298 option += t.asInput();
302 options.push_back(trimSpaceAndEol(option));
309 * Retrieve a keyval option "name={value with=sign}" named \p name from
310 * \p options and return the value.
311 * The found option is also removed from \p options.
313 string process_keyval_opt(vector<string> & options, string name)
315 for (size_t i = 0; i < options.size(); ++i) {
316 vector<string> option;
317 split(options[i], option, '=');
318 if (option.size() < 2)
320 if (option[0] == name) {
321 options.erase(options.begin() + i);
322 option.erase(option.begin());
323 return join(option, "=");
331 * Add package \p name with options \p options to used_packages.
332 * Remove options from \p options that we don't want to output.
334 void add_package(string const & name, vector<string> & options)
336 // every package inherits the global options
337 if (used_packages.find(name) == used_packages.end())
338 used_packages[name] = split_options(h_options);
340 vector<string> & v = used_packages[name];
341 v.insert(v.end(), options.begin(), options.end());
342 if (name == "jurabib") {
343 // Don't output the order argument (see the cite command
344 // handling code in text.cpp).
345 vector<string>::iterator end =
346 remove(options.begin(), options.end(), "natbiborder");
347 end = remove(options.begin(), end, "jurabiborder");
348 options.erase(end, options.end());
353 // Given is a string like "scaled=0.9", return 0.9 * 100
354 string const scale_as_percentage(string const & scale)
356 string::size_type pos = scale.find('=');
357 if (pos != string::npos) {
358 string value = scale.substr(pos + 1);
360 return convert<string>(100 * convert<double>(value));
362 // If the input string didn't match our expectations.
363 // return the default value "100"
368 string remove_braces(string const & value)
372 if (value[0] == '{' && value[value.length()-1] == '}')
373 return value.substr(1, value.length()-2);
378 void handle_hyperref(vector<string> & options)
380 // FIXME swallow inputencoding changes that might surround the
381 // hyperref setup if it was written by LyX
382 h_use_hyperref = "1";
383 // swallow "unicode=true", since LyX does always write that
384 vector<string>::iterator it =
385 find(options.begin(), options.end(), "unicode=true");
386 if (it != options.end())
388 it = find(options.begin(), options.end(), "pdfusetitle");
389 if (it != options.end()) {
390 h_pdf_pdfusetitle = "1";
393 string bookmarks = process_keyval_opt(options, "bookmarks");
394 if (bookmarks == "true")
395 h_pdf_bookmarks = "1";
396 else if (bookmarks == "false")
397 h_pdf_bookmarks = "0";
398 if (h_pdf_bookmarks == "1") {
399 string bookmarksnumbered =
400 process_keyval_opt(options, "bookmarksnumbered");
401 if (bookmarksnumbered == "true")
402 h_pdf_bookmarksnumbered = "1";
403 else if (bookmarksnumbered == "false")
404 h_pdf_bookmarksnumbered = "0";
405 string bookmarksopen =
406 process_keyval_opt(options, "bookmarksopen");
407 if (bookmarksopen == "true")
408 h_pdf_bookmarksopen = "1";
409 else if (bookmarksopen == "false")
410 h_pdf_bookmarksopen = "0";
411 if (h_pdf_bookmarksopen == "1") {
412 string bookmarksopenlevel =
413 process_keyval_opt(options, "bookmarksopenlevel");
414 if (!bookmarksopenlevel.empty())
415 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
418 string breaklinks = process_keyval_opt(options, "breaklinks");
419 if (breaklinks == "true")
420 h_pdf_breaklinks = "1";
421 else if (breaklinks == "false")
422 h_pdf_breaklinks = "0";
423 string pdfborder = process_keyval_opt(options, "pdfborder");
424 if (pdfborder == "{0 0 0}")
425 h_pdf_pdfborder = "1";
426 else if (pdfborder == "{0 0 1}")
427 h_pdf_pdfborder = "0";
428 string backref = process_keyval_opt(options, "backref");
429 if (!backref.empty())
430 h_pdf_backref = backref;
431 string colorlinks = process_keyval_opt(options, "colorlinks");
432 if (colorlinks == "true")
433 h_pdf_colorlinks = "1";
434 else if (colorlinks == "false")
435 h_pdf_colorlinks = "0";
436 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
437 if (!pdfpagemode.empty())
438 h_pdf_pagemode = pdfpagemode;
439 string pdftitle = process_keyval_opt(options, "pdftitle");
440 if (!pdftitle.empty()) {
441 h_pdf_title = remove_braces(pdftitle);
443 string pdfauthor = process_keyval_opt(options, "pdfauthor");
444 if (!pdfauthor.empty()) {
445 h_pdf_author = remove_braces(pdfauthor);
447 string pdfsubject = process_keyval_opt(options, "pdfsubject");
448 if (!pdfsubject.empty())
449 h_pdf_subject = remove_braces(pdfsubject);
450 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
451 if (!pdfkeywords.empty())
452 h_pdf_keywords = remove_braces(pdfkeywords);
453 if (!options.empty()) {
454 if (!h_pdf_quoted_options.empty())
455 h_pdf_quoted_options += ',';
456 h_pdf_quoted_options += join(options, ",");
462 void handle_package(Parser &p, string const & name, string const & opts,
463 bool in_lyx_preamble)
465 vector<string> options = split_options(opts);
466 add_package(name, options);
470 if (is_known(name, known_roman_fonts)) {
475 if (name == "fourier") {
476 h_font_roman = "utopia";
477 // when font uses real small capitals
478 if (opts == "expert")
482 if (name == "mathpazo")
483 h_font_roman = "palatino";
485 if (name == "mathptmx")
486 h_font_roman = "times";
489 if (is_known(name, known_sans_fonts)) {
493 h_font_sf_scale = scale_as_percentage(scale);
498 if (is_known(name, known_typewriter_fonts)) {
499 h_font_typewriter = name;
502 h_font_tt_scale = scale_as_percentage(scale);
506 // font uses old-style figure
510 else if (name == "amsmath" || name == "amssymb")
513 else if (name == "esint")
516 else if (name == "mhchem")
519 else if (name == "mathdots")
520 h_use_mathdots = "2";
522 else if (name == "undertilde")
523 h_use_undertilde = "2";
525 else if (name == "babel" && !opts.empty()) {
526 // check if more than one option was used - used later for inputenc
527 // in case inputenc is parsed before babel, set the encoding to auto
528 if (options.begin() != options.end() - 1) {
529 one_language = false;
530 h_inputencoding = "auto";
532 // babel takes the last language of the option of its \usepackage
533 // call as document language. If there is no such language option, the
534 // last language in the documentclass options is used.
535 handle_opt(options, known_languages, h_language);
536 delete_opt(options, known_languages);
539 else if (name == "fontenc") {
540 h_fontencoding = getStringFromVector(options, ",");
541 /* We could do the following for better round trip support,
542 * but this makes the document less portable, so I skip it:
543 if (h_fontencoding == lyxrc.fontenc)
544 h_fontencoding = "global";
549 else if (name == "inputenc" || name == "luainputenc") {
550 // h_inputencoding is only set when there is not more than one
551 // inputenc option because otherwise h_inputencoding must be
552 // set to "auto" (the default encoding of the document language)
553 // Therefore check for the "," character.
554 // It is also only set when there is not more then one babel
555 // language option but this is handled in the routine for babel.
556 if (opts.find(",") == string::npos && one_language == true)
557 h_inputencoding = opts;
558 if (!options.empty())
559 p.setEncoding(options.back());
563 else if (name == "makeidx")
566 else if (name == "prettyref")
569 else if (name == "varioref")
572 else if (name == "verbatim")
575 else if (name == "nomencl")
578 else if (name == "textcomp")
581 else if (name == "url")
584 else if (name == "subscript")
587 else if (name == "color") {
588 // with the following command this package is only loaded when needed for
589 // undefined colors, since we only support the predefined colors
590 h_preamble << "\\@ifundefined{definecolor}\n {\\usepackage{color}}{}\n";
593 else if (name == "graphicx")
596 else if (name == "setspace")
599 else if (name == "geometry")
600 ; // Ignore this, the geometry settings are made by the \geometry
601 // command. This command is handled below.
603 else if (is_known(name, known_languages))
606 else if (name == "natbib") {
607 h_cite_engine = "natbib_authoryear";
608 vector<string>::iterator it =
609 find(options.begin(), options.end(), "authoryear");
610 if (it != options.end())
613 it = find(options.begin(), options.end(), "numbers");
614 if (it != options.end()) {
615 h_cite_engine = "natbib_numerical";
621 else if (name == "jurabib")
622 h_cite_engine = "jurabib";
624 else if (name == "hyperref")
625 handle_hyperref(options);
627 else if (!in_lyx_preamble) {
629 h_preamble << "\\usepackage{" << name << "}";
631 h_preamble << "\\usepackage[" << opts << "]{"
637 // We need to do something with the options...
638 if (!options.empty())
639 cerr << "Ignoring options '" << join(options, ",")
640 << "' of package " << name << '.' << endl;
642 // remove the whitespace
647 void handle_if(Parser & p, bool in_lyx_preamble)
650 Token t = p.get_token();
651 if (t.cat() == catEscape &&
652 is_known(t.cs(), known_if_commands))
653 handle_if(p, in_lyx_preamble);
655 if (!in_lyx_preamble)
656 h_preamble << t.asInput();
657 if (t.cat() == catEscape && t.cs() == "fi")
664 void end_preamble(ostream & os, TextClass const & /*textclass*/)
666 // translate from babel to LyX names
667 h_language = babel2lyx(h_language);
669 // set the quote language
670 // LyX only knows the following quotes languages:
671 // english, swedish, german, polish, french and danish
672 // (quotes for "japanese" and "chinese-traditional" are missing because
673 // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
674 // conversion list taken from
675 // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
676 // (quotes for kazakh and interlingua are unknown)
678 if (h_language == "danish")
679 h_quotes_language = "danish";
681 else if (is_known(h_language, known_french_quotes_languages))
682 h_quotes_language = "french";
684 else if (is_known(h_language, known_german_quotes_languages))
685 h_quotes_language = "german";
687 else if (is_known(h_language, known_polish_quotes_languages))
688 h_quotes_language = "polish";
690 else if (is_known(h_language, known_swedish_quotes_languages))
691 h_quotes_language = "swedish";
693 else if (is_known(h_language, known_english_quotes_languages))
694 h_quotes_language = "english";
696 // output the LyX file settings
697 os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
698 << "\\lyxformat " << LYX_FORMAT << '\n'
699 << "\\begin_document\n"
700 << "\\begin_header\n"
701 << "\\textclass " << h_textclass << "\n";
702 if (!h_preamble.str().empty())
703 os << "\\begin_preamble\n" << h_preamble.str() << "\n\\end_preamble\n";
704 if (!h_options.empty())
705 os << "\\options " << h_options << "\n";
706 os << "\\use_default_options " << h_use_default_options << "\n"
707 << modules_placeholder
708 << "\\language " << h_language << "\n"
709 << "\\language_package " << h_language_package << "\n"
710 << "\\inputencoding " << h_inputencoding << "\n"
711 << "\\fontencoding " << h_fontencoding << "\n"
712 << "\\font_roman " << h_font_roman << "\n"
713 << "\\font_sans " << h_font_sans << "\n"
714 << "\\font_typewriter " << h_font_typewriter << "\n"
715 << "\\font_default_family " << h_font_default_family << "\n"
716 << "\\font_sc " << h_font_sc << "\n"
717 << "\\font_osf " << h_font_osf << "\n"
718 << "\\font_sf_scale " << h_font_sf_scale << "\n"
719 << "\\font_tt_scale " << h_font_tt_scale << "\n"
720 << "\\graphics " << h_graphics << "\n";
721 if (!h_float_placement.empty())
722 os << "\\float_placement " << h_float_placement << "\n";
723 os << "\\paperfontsize " << h_paperfontsize << "\n"
724 << "\\spacing " << h_spacing << "\n"
725 << "\\use_hyperref " << h_use_hyperref << '\n';
726 if (h_use_hyperref == "1") {
727 if (!h_pdf_title.empty())
728 os << "\\pdf_title \"" << h_pdf_title << "\"\n";
729 if (!h_pdf_author.empty())
730 os << "\\pdf_author \"" << h_pdf_author << "\"\n";
731 if (!h_pdf_subject.empty())
732 os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
733 if (!h_pdf_keywords.empty())
734 os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
735 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
736 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
737 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
738 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
739 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
740 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
741 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
742 "\\pdf_backref " << h_pdf_backref << "\n"
743 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
744 if (!h_pdf_pagemode.empty())
745 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
746 if (!h_pdf_quoted_options.empty())
747 os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
749 os << "\\papersize " << h_papersize << "\n"
750 << "\\use_geometry " << h_use_geometry << "\n"
751 << "\\use_amsmath " << h_use_amsmath << "\n"
752 << "\\use_esint " << h_use_esint << "\n"
753 << "\\use_mhchem " << h_use_mhchem << "\n"
754 << "\\use_mathdots " << h_use_mathdots << "\n"
755 << "\\use_undertilde " << h_use_undertilde << "\n"
756 << "\\cite_engine " << h_cite_engine << "\n"
757 << "\\use_bibtopic " << h_use_bibtopic << "\n"
758 << "\\paperorientation " << h_paperorientation << '\n'
759 << "\\suppress_date " << h_suppress_date << '\n'
760 << "\\use_refstyle " << h_use_refstyle << '\n';
761 if (!h_notefontcolor.empty())
762 os << "\\notefontcolor " << h_notefontcolor << '\n';
764 << "\\secnumdepth " << h_secnumdepth << "\n"
765 << "\\tocdepth " << h_tocdepth << "\n"
766 << "\\paragraph_separation " << h_paragraph_separation << "\n";
767 if (h_paragraph_separation == "skip")
768 os << "\\defskip " << h_defskip << "\n";
770 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
771 os << "\\quotes_language " << h_quotes_language << "\n"
772 << "\\papercolumns " << h_papercolumns << "\n"
773 << "\\papersides " << h_papersides << "\n"
774 << "\\paperpagestyle " << h_paperpagestyle << "\n";
775 if (!h_listings_params.empty())
776 os << "\\listings_params " << h_listings_params << "\n";
777 os << "\\tracking_changes " << h_tracking_changes << "\n"
778 << "\\output_changes " << h_output_changes << "\n"
779 << "\\html_math_output " << h_html_math_output << "\n"
780 << "\\html_css_as_file " << h_html_css_as_file << "\n"
781 << "\\html_be_strict " << h_html_be_strict << "\n"
782 << "\\end_header\n\n"
784 // clear preamble for subdocuments
788 } // anonymous namespace
791 void parse_preamble(Parser & p, ostream & os,
792 string const & forceclass, TeX2LyXDocClass & tc)
794 // initialize fixed types
795 special_columns['D'] = 3;
796 bool is_full_document = false;
797 bool is_lyx_file = false;
798 bool in_lyx_preamble = false;
800 // determine whether this is a full document or a fragment for inclusion
802 Token const & t = p.get_token();
804 if (t.cat() == catEscape && t.cs() == "documentclass") {
805 is_full_document = true;
811 while (is_full_document && p.good()) {
812 Token const & t = p.get_token();
815 cerr << "t: " << t << "\n";
821 if (!in_lyx_preamble &&
822 (t.cat() == catLetter ||
823 t.cat() == catSuper ||
825 t.cat() == catOther ||
826 t.cat() == catMath ||
827 t.cat() == catActive ||
828 t.cat() == catBegin ||
830 t.cat() == catAlign ||
831 t.cat() == catParameter))
832 h_preamble << t.cs();
834 else if (!in_lyx_preamble &&
835 (t.cat() == catSpace || t.cat() == catNewline))
836 h_preamble << t.asInput();
838 else if (t.cat() == catComment) {
839 static regex const islyxfile("%% LyX .* created this file");
840 static regex const usercommands("User specified LaTeX commands");
842 string const comment = t.asInput();
844 // magically switch encoding default if it looks like XeLaTeX
845 static string const magicXeLaTeX =
846 "% This document must be compiled with XeLaTeX ";
847 if (comment.size() > magicXeLaTeX.size()
848 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
849 && h_inputencoding == "auto") {
850 cerr << "XeLaTeX comment found, switching to UTF8\n";
851 h_inputencoding = "utf8";
854 if (regex_search(comment, sub, islyxfile)) {
856 in_lyx_preamble = true;
857 } else if (is_lyx_file
858 && regex_search(comment, sub, usercommands))
859 in_lyx_preamble = false;
860 else if (!in_lyx_preamble)
861 h_preamble << t.asInput();
864 else if (t.cs() == "pagestyle")
865 h_paperpagestyle = p.verbatim_item();
867 else if (t.cs() == "makeatletter") {
868 // LyX takes care of this
869 p.setCatCode('@', catLetter);
872 else if (t.cs() == "makeatother") {
873 // LyX takes care of this
874 p.setCatCode('@', catOther);
877 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
878 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
879 || t.cs() == "providecommand" || t.cs() == "providecommandx"
880 || t.cs() == "DeclareRobustCommand"
881 || t.cs() == "DeclareRobustCommandx"
882 || t.cs() == "ProvideTextCommandDefault"
883 || t.cs() == "DeclareMathAccent") {
885 if (p.next_token().character() == '*') {
889 string const name = p.verbatim_item();
890 string const opt1 = p.getFullOpt();
891 string const opt2 = p.getFullOpt();
892 string const body = p.verbatim_item();
894 if (name == "\\rmdefault")
895 if (is_known(body, known_roman_fonts))
897 if (name == "\\sfdefault")
898 if (is_known(body, known_sans_fonts))
900 if (name == "\\ttdefault")
901 if (is_known(body, known_typewriter_fonts))
902 h_font_typewriter = body;
903 if (name == "\\familydefault") {
904 string family = body;
905 // remove leading "\"
906 h_font_default_family = family.erase(0,1);
909 // Add the command to the known commands
910 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
912 // only non-lyxspecific stuff
913 if (!in_lyx_preamble) {
915 ss << '\\' << t.cs();
918 ss << '{' << name << '}' << opt1 << opt2
919 << '{' << body << "}";
920 h_preamble << ss.str();
922 ostream & out = in_preamble ? h_preamble : os;
923 out << "\\" << t.cs() << "{" << name << "}"
924 << opts << "{" << body << "}";
929 else if (t.cs() == "documentclass") {
930 vector<string>::iterator it;
931 vector<string> opts = split_options(p.getArg('[', ']'));
932 handle_opt(opts, known_fontsizes, h_paperfontsize);
933 delete_opt(opts, known_fontsizes);
934 // delete "pt" at the end
935 string::size_type i = h_paperfontsize.find("pt");
936 if (i != string::npos)
937 h_paperfontsize.erase(i);
938 // The documentclass options are always parsed before the options
939 // of the babel call so that a language cannot overwrite the babel
941 handle_opt(opts, known_languages, h_language);
942 delete_opt(opts, known_languages);
945 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
946 h_paperorientation = "landscape";
950 if ((it = find(opts.begin(), opts.end(), "oneside"))
955 if ((it = find(opts.begin(), opts.end(), "twoside"))
961 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
963 h_papercolumns = "1";
966 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
968 h_papercolumns = "2";
972 // some size options are know to any document classes, other sizes
973 // are handled by the \geometry command of the geometry package
974 handle_opt(opts, known_class_paper_sizes, h_papersize);
975 delete_opt(opts, known_class_paper_sizes);
976 // the remaining options
977 h_options = join(opts, ",");
978 // FIXME This does not work for classes that have a
979 // different name in LyX than in LaTeX
980 h_textclass = p.getArg('{', '}');
983 else if (t.cs() == "usepackage") {
984 string const options = p.getArg('[', ']');
985 string const name = p.getArg('{', '}');
986 vector<string> vecnames;
987 split(name, vecnames, ',');
988 vector<string>::const_iterator it = vecnames.begin();
989 vector<string>::const_iterator end = vecnames.end();
990 for (; it != end; ++it)
991 handle_package(p, trimSpaceAndEol(*it), options,
995 else if (t.cs() == "inputencoding") {
996 string const encoding = p.getArg('{','}');
997 h_inputencoding = encoding;
998 p.setEncoding(encoding);
1001 else if (t.cs() == "newenvironment") {
1002 string const name = p.getArg('{', '}');
1003 string const opt1 = p.getFullOpt();
1004 string const opt2 = p.getFullOpt();
1005 string const beg = p.verbatim_item();
1006 string const end = p.verbatim_item();
1007 if (!in_lyx_preamble) {
1008 h_preamble << "\\newenvironment{" << name
1009 << '}' << opt1 << opt2 << '{'
1010 << beg << "}{" << end << '}';
1012 add_known_environment(name, opt1, !opt2.empty(),
1013 from_utf8(beg), from_utf8(end));
1017 else if (t.cs() == "def") {
1018 string name = p.get_token().cs();
1019 while (p.next_token().cat() != catBegin)
1020 name += p.get_token().cs();
1021 if (!in_lyx_preamble)
1022 h_preamble << "\\def\\" << name << '{'
1023 << p.verbatim_item() << "}";
1026 else if (t.cs() == "newcolumntype") {
1027 string const name = p.getArg('{', '}');
1028 trimSpaceAndEol(name);
1030 string opts = p.getOpt();
1031 if (!opts.empty()) {
1032 istringstream is(string(opts, 1));
1035 special_columns[name[0]] = nargs;
1036 h_preamble << "\\newcolumntype{" << name << "}";
1038 h_preamble << "[" << nargs << "]";
1039 h_preamble << "{" << p.verbatim_item() << "}";
1042 else if (t.cs() == "setcounter") {
1043 string const name = p.getArg('{', '}');
1044 string const content = p.getArg('{', '}');
1045 if (name == "secnumdepth")
1046 h_secnumdepth = content;
1047 else if (name == "tocdepth")
1048 h_tocdepth = content;
1050 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1053 else if (t.cs() == "setlength") {
1054 string const name = p.verbatim_item();
1055 string const content = p.verbatim_item();
1056 // the paragraphs are only not indented when \parindent is set to zero
1057 if (name == "\\parindent" && content != "") {
1058 if (content[0] == '0')
1059 h_paragraph_separation = "skip";
1061 h_paragraph_indentation = translate_len(content);
1062 } else if (name == "\\parskip") {
1063 if (content == "\\smallskipamount")
1064 h_defskip = "smallskip";
1065 else if (content == "\\medskipamount")
1066 h_defskip = "medskip";
1067 else if (content == "\\bigskipamount")
1068 h_defskip = "bigskip";
1070 h_defskip = content;
1072 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1075 else if (t.cs() == "onehalfspacing")
1076 h_spacing = "onehalf";
1078 else if (t.cs() == "doublespacing")
1079 h_spacing = "double";
1081 else if (t.cs() == "setstretch")
1082 h_spacing = "other " + p.verbatim_item();
1084 else if (t.cs() == "begin") {
1085 string const name = p.getArg('{', '}');
1086 if (name == "document")
1088 h_preamble << "\\begin{" << name << "}";
1091 else if (t.cs() == "geometry") {
1092 h_use_geometry = "true";
1093 vector<string> opts = split_options(p.getArg('{', '}'));
1094 vector<string>::iterator it;
1095 // paper orientation
1096 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1097 h_paperorientation = "landscape";
1101 handle_opt(opts, known_paper_sizes, h_papersize);
1102 delete_opt(opts, known_paper_sizes);
1104 char const * const * margin = known_paper_margins;
1106 for (; *margin; ++margin) {
1108 // search for the "=" in e.g. "lmargin=2cm" to get the value
1109 for(size_t i = 0; i != opts.size(); i++) {
1110 if (opts.at(i).find(*margin) != string::npos) {
1111 string::size_type pos = opts.at(i).find("=");
1112 string value = opts.at(i).substr(pos + 1);
1113 string name = known_coded_paper_margins[k];
1114 h_margins += "\\" + name + " " + value + "\n";
1120 else if (t.cs() == "definecolor") {
1121 string const color = p.getArg('{', '}');
1122 string const space = p.getArg('{', '}');
1123 string const value = p.getArg('{', '}');
1124 if (color == "note_fontcolor" && space == "rgb") {
1125 RGBColor c(RGBColorFromLaTeX(value));
1126 h_notefontcolor = X11hexname(c);
1128 h_preamble << "\\definecolor{" << color
1129 << "}{" << space << "}{" << value
1134 else if (t.cs() == "jurabibsetup") {
1135 // FIXME p.getArg('{', '}') is most probably wrong (it
1136 // does not handle nested braces).
1137 // Use p.verbatim_item() instead.
1138 vector<string> jurabibsetup =
1139 split_options(p.getArg('{', '}'));
1140 // add jurabibsetup to the jurabib package options
1141 add_package("jurabib", jurabibsetup);
1142 if (!jurabibsetup.empty()) {
1143 h_preamble << "\\jurabibsetup{"
1144 << join(jurabibsetup, ",") << '}';
1148 else if (t.cs() == "hypersetup") {
1149 vector<string> hypersetup =
1150 split_options(p.verbatim_item());
1151 // add hypersetup to the hyperref package options
1152 handle_hyperref(hypersetup);
1153 if (!hypersetup.empty()) {
1154 h_preamble << "\\hypersetup{"
1155 << join(hypersetup, ",") << '}';
1159 else if (is_known(t.cs(), known_if_3arg_commands)) {
1160 // prevent misparsing of \usepackage if it is used
1161 // as an argument (see e.g. our own output of
1162 // \@ifundefined above)
1163 string const arg1 = p.verbatim_item();
1164 string const arg2 = p.verbatim_item();
1165 string const arg3 = p.verbatim_item();
1166 if (!in_lyx_preamble) {
1167 h_preamble << t.asInput()
1168 << '{' << arg1 << '}'
1169 << '{' << arg2 << '}'
1170 << '{' << arg3 << '}';
1174 else if (is_known(t.cs(), known_if_commands)) {
1175 // must not parse anything in conditional code, since
1176 // LyX would output the parsed contents unconditionally
1177 if (!in_lyx_preamble)
1178 h_preamble << t.asInput();
1179 handle_if(p, in_lyx_preamble);
1182 else if (!t.cs().empty() && !in_lyx_preamble)
1183 h_preamble << '\\' << t.cs();
1186 // remove the whitespace
1189 // Force textclass if the user wanted it
1190 if (!forceclass.empty())
1191 h_textclass = forceclass;
1192 if (noweb_mode && !prefixIs(h_textclass, "literate-"))
1193 h_textclass.insert(0, "literate-");
1194 tc.setName(h_textclass);
1196 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1199 if (h_papersides.empty()) {
1202 h_papersides = ss.str();
1204 end_preamble(os, tc);
1208 /// translates a babel language name to a LyX language name
1209 string babel2lyx(string const & language)
1211 char const * const * where = is_known(language, known_languages);
1213 return known_coded_languages[where - known_languages];