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_cite_engine = "basic";
210 string h_use_bibtopic = "false";
211 string h_paperorientation = "portrait";
212 string h_suppress_date = "false";
213 string h_use_refstyle = "0";
214 string h_notefontcolor;
215 string h_secnumdepth = "3";
216 string h_tocdepth = "3";
217 string h_defskip = "medskip";
218 string h_paragraph_indentation = "default";
219 string h_quotes_language = "english";
220 string h_papercolumns = "1";
222 string h_paperpagestyle = "default";
223 string h_listings_params;
224 string h_tracking_changes = "false";
225 string h_output_changes = "false";
226 string h_html_math_output = "0";
227 string h_html_css_as_file = "0";
228 string h_html_be_strict = "false";
232 // returns true if at least one of the options in what has been found
233 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
239 // the last language option is the document language (for babel and LyX)
240 // the last size option is the document font size
241 vector<string>::iterator it;
242 vector<string>::iterator position = opts.begin();
243 for (; *what; ++what) {
244 it = find(opts.begin(), opts.end(), *what);
245 if (it != opts.end()) {
246 if (it >= position) {
257 void delete_opt(vector<string> & opts, char const * const * what)
262 // remove found options from the list
263 // do this after handle_opt to avoid potential memory leaks
264 vector<string>::iterator it;
265 for (; *what; ++what) {
266 it = find(opts.begin(), opts.end(), *what);
267 if (it != opts.end())
274 * Split a package options string (keyval format) into a vector.
276 * authorformat=smallcaps,
278 * titleformat=colonsep,
279 * bibformat={tabular,ibidem,numbered}
281 vector<string> split_options(string const & input)
283 vector<string> options;
287 Token const & t = p.get_token();
288 if (t.asInput() == ",") {
289 options.push_back(trimSpaceAndEol(option));
291 } else if (t.asInput() == "=") {
294 if (p.next_token().asInput() == "{")
295 option += '{' + p.getArg('{', '}') + '}';
296 } else if (t.cat() != catSpace)
297 option += t.asInput();
301 options.push_back(trimSpaceAndEol(option));
308 * Retrieve a keyval option "name={value with=sign}" named \p name from
309 * \p options and return the value.
310 * The found option is also removed from \p options.
312 string process_keyval_opt(vector<string> & options, string name)
314 for (size_t i = 0; i < options.size(); ++i) {
315 vector<string> option;
316 split(options[i], option, '=');
317 if (option.size() < 2)
319 if (option[0] == name) {
320 options.erase(options.begin() + i);
321 option.erase(option.begin());
322 return join(option, "=");
330 * Add package \p name with options \p options to used_packages.
331 * Remove options from \p options that we don't want to output.
333 void add_package(string const & name, vector<string> & options)
335 // every package inherits the global options
336 if (used_packages.find(name) == used_packages.end())
337 used_packages[name] = split_options(h_options);
339 vector<string> & v = used_packages[name];
340 v.insert(v.end(), options.begin(), options.end());
341 if (name == "jurabib") {
342 // Don't output the order argument (see the cite command
343 // handling code in text.cpp).
344 vector<string>::iterator end =
345 remove(options.begin(), options.end(), "natbiborder");
346 end = remove(options.begin(), end, "jurabiborder");
347 options.erase(end, options.end());
352 // Given is a string like "scaled=0.9", return 0.9 * 100
353 string const scale_as_percentage(string const & scale)
355 string::size_type pos = scale.find('=');
356 if (pos != string::npos) {
357 string value = scale.substr(pos + 1);
359 return convert<string>(100 * convert<double>(value));
361 // If the input string didn't match our expectations.
362 // return the default value "100"
367 string remove_braces(string const & value)
371 if (value[0] == '{' && value[value.length()-1] == '}')
372 return value.substr(1, value.length()-2);
377 void handle_hyperref(vector<string> & options)
379 // FIXME swallow inputencoding changes that might surround the
380 // hyperref setup if it was written by LyX
381 h_use_hyperref = "1";
382 // swallow "unicode=true", since LyX does always write that
383 vector<string>::iterator it =
384 find(options.begin(), options.end(), "unicode=true");
385 if (it != options.end())
387 it = find(options.begin(), options.end(), "pdfusetitle");
388 if (it != options.end()) {
389 h_pdf_pdfusetitle = "1";
392 string bookmarks = process_keyval_opt(options, "bookmarks");
393 if (bookmarks == "true")
394 h_pdf_bookmarks = "1";
395 else if (bookmarks == "false")
396 h_pdf_bookmarks = "0";
397 if (h_pdf_bookmarks == "1") {
398 string bookmarksnumbered =
399 process_keyval_opt(options, "bookmarksnumbered");
400 if (bookmarksnumbered == "true")
401 h_pdf_bookmarksnumbered = "1";
402 else if (bookmarksnumbered == "false")
403 h_pdf_bookmarksnumbered = "0";
404 string bookmarksopen =
405 process_keyval_opt(options, "bookmarksopen");
406 if (bookmarksopen == "true")
407 h_pdf_bookmarksopen = "1";
408 else if (bookmarksopen == "false")
409 h_pdf_bookmarksopen = "0";
410 if (h_pdf_bookmarksopen == "1") {
411 string bookmarksopenlevel =
412 process_keyval_opt(options, "bookmarksopenlevel");
413 if (!bookmarksopenlevel.empty())
414 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
417 string breaklinks = process_keyval_opt(options, "breaklinks");
418 if (breaklinks == "true")
419 h_pdf_breaklinks = "1";
420 else if (breaklinks == "false")
421 h_pdf_breaklinks = "0";
422 string pdfborder = process_keyval_opt(options, "pdfborder");
423 if (pdfborder == "{0 0 0}")
424 h_pdf_pdfborder = "1";
425 else if (pdfborder == "{0 0 1}")
426 h_pdf_pdfborder = "0";
427 string backref = process_keyval_opt(options, "backref");
428 if (!backref.empty())
429 h_pdf_backref = backref;
430 string colorlinks = process_keyval_opt(options, "colorlinks");
431 if (colorlinks == "true")
432 h_pdf_colorlinks = "1";
433 else if (colorlinks == "false")
434 h_pdf_colorlinks = "0";
435 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
436 if (!pdfpagemode.empty())
437 h_pdf_pagemode = pdfpagemode;
438 string pdftitle = process_keyval_opt(options, "pdftitle");
439 if (!pdftitle.empty()) {
440 h_pdf_title = remove_braces(pdftitle);
442 string pdfauthor = process_keyval_opt(options, "pdfauthor");
443 if (!pdfauthor.empty()) {
444 h_pdf_author = remove_braces(pdfauthor);
446 string pdfsubject = process_keyval_opt(options, "pdfsubject");
447 if (!pdfsubject.empty())
448 h_pdf_subject = remove_braces(pdfsubject);
449 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
450 if (!pdfkeywords.empty())
451 h_pdf_keywords = remove_braces(pdfkeywords);
452 if (!options.empty()) {
453 if (!h_pdf_quoted_options.empty())
454 h_pdf_quoted_options += ',';
455 h_pdf_quoted_options += join(options, ",");
461 void handle_package(Parser &p, string const & name, string const & opts,
462 bool in_lyx_preamble)
464 vector<string> options = split_options(opts);
465 add_package(name, options);
469 if (is_known(name, known_roman_fonts)) {
474 if (name == "fourier") {
475 h_font_roman = "utopia";
476 // when font uses real small capitals
477 if (opts == "expert")
481 if (name == "mathpazo")
482 h_font_roman = "palatino";
484 if (name == "mathptmx")
485 h_font_roman = "times";
488 if (is_known(name, known_sans_fonts)) {
492 h_font_sf_scale = scale_as_percentage(scale);
497 if (is_known(name, known_typewriter_fonts)) {
498 h_font_typewriter = name;
501 h_font_tt_scale = scale_as_percentage(scale);
505 // font uses old-style figure
509 else if (name == "amsmath" || name == "amssymb")
512 else if (name == "esint")
515 else if (name == "mhchem")
518 else if (name == "mathdots")
519 h_use_mathdots = "2";
521 else if (name == "babel" && !opts.empty()) {
522 // check if more than one option was used - used later for inputenc
523 // in case inputenc is parsed before babel, set the encoding to auto
524 if (options.begin() != options.end() - 1) {
525 one_language = false;
526 h_inputencoding = "auto";
528 // babel takes the last language of the option of its \usepackage
529 // call as document language. If there is no such language option, the
530 // last language in the documentclass options is used.
531 handle_opt(options, known_languages, h_language);
532 delete_opt(options, known_languages);
535 else if (name == "fontenc") {
536 h_fontencoding = getStringFromVector(options, ",");
537 /* We could do the following for better round trip support,
538 * but this makes the document less portable, so I skip it:
539 if (h_fontencoding == lyxrc.fontenc)
540 h_fontencoding = "global";
545 else if (name == "inputenc" || name == "luainputenc") {
546 // h_inputencoding is only set when there is not more than one
547 // inputenc option because otherwise h_inputencoding must be
548 // set to "auto" (the default encoding of the document language)
549 // Therefore check for the "," character.
550 // It is also only set when there is not more then one babel
551 // language option but this is handled in the routine for babel.
552 if (opts.find(",") == string::npos && one_language == true)
553 h_inputencoding = opts;
554 if (!options.empty())
555 p.setEncoding(options.back());
559 else if (name == "makeidx")
562 else if (name == "prettyref")
565 else if (name == "varioref")
568 else if (name == "verbatim")
571 else if (name == "nomencl")
574 else if (name == "textcomp")
577 else if (name == "url")
580 else if (name == "subscript")
583 else if (name == "color") {
584 // with the following command this package is only loaded when needed for
585 // undefined colors, since we only support the predefined colors
586 h_preamble << "\\@ifundefined{definecolor}\n {\\usepackage{color}}{}\n";
589 else if (name == "graphicx")
592 else if (name == "setspace")
595 else if (name == "geometry")
596 ; // Ignore this, the geometry settings are made by the \geometry
597 // command. This command is handled below.
599 else if (is_known(name, known_languages))
602 else if (name == "natbib") {
603 h_cite_engine = "natbib_authoryear";
604 vector<string>::iterator it =
605 find(options.begin(), options.end(), "authoryear");
606 if (it != options.end())
609 it = find(options.begin(), options.end(), "numbers");
610 if (it != options.end()) {
611 h_cite_engine = "natbib_numerical";
617 else if (name == "jurabib")
618 h_cite_engine = "jurabib";
620 else if (name == "hyperref")
621 handle_hyperref(options);
623 else if (!in_lyx_preamble) {
625 h_preamble << "\\usepackage{" << name << "}";
627 h_preamble << "\\usepackage[" << opts << "]{"
633 // We need to do something with the options...
634 if (!options.empty())
635 cerr << "Ignoring options '" << join(options, ",")
636 << "' of package " << name << '.' << endl;
638 // remove the whitespace
643 void handle_if(Parser & p, bool in_lyx_preamble)
646 Token t = p.get_token();
647 if (t.cat() == catEscape &&
648 is_known(t.cs(), known_if_commands))
649 handle_if(p, in_lyx_preamble);
651 if (!in_lyx_preamble)
652 h_preamble << t.asInput();
653 if (t.cat() == catEscape && t.cs() == "fi")
660 void end_preamble(ostream & os, TextClass const & /*textclass*/)
662 // translate from babel to LyX names
663 h_language = babel2lyx(h_language);
665 // set the quote language
666 // LyX only knows the following quotes languages:
667 // english, swedish, german, polish, french and danish
668 // (quotes for "japanese" and "chinese-traditional" are missing because
669 // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
670 // conversion list taken from
671 // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
672 // (quotes for kazakh and interlingua are unknown)
674 if (h_language == "danish")
675 h_quotes_language = "danish";
677 else if (is_known(h_language, known_french_quotes_languages))
678 h_quotes_language = "french";
680 else if (is_known(h_language, known_german_quotes_languages))
681 h_quotes_language = "german";
683 else if (is_known(h_language, known_polish_quotes_languages))
684 h_quotes_language = "polish";
686 else if (is_known(h_language, known_swedish_quotes_languages))
687 h_quotes_language = "swedish";
689 else if (is_known(h_language, known_english_quotes_languages))
690 h_quotes_language = "english";
692 // output the LyX file settings
693 os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
694 << "\\lyxformat " << LYX_FORMAT << '\n'
695 << "\\begin_document\n"
696 << "\\begin_header\n"
697 << "\\textclass " << h_textclass << "\n";
698 if (!h_preamble.str().empty())
699 os << "\\begin_preamble\n" << h_preamble.str() << "\n\\end_preamble\n";
700 if (!h_options.empty())
701 os << "\\options " << h_options << "\n";
702 os << "\\use_default_options " << h_use_default_options << "\n"
703 << modules_placeholder
704 << "\\language " << h_language << "\n"
705 << "\\language_package " << h_language_package << "\n"
706 << "\\inputencoding " << h_inputencoding << "\n"
707 << "\\fontencoding " << h_fontencoding << "\n"
708 << "\\font_roman " << h_font_roman << "\n"
709 << "\\font_sans " << h_font_sans << "\n"
710 << "\\font_typewriter " << h_font_typewriter << "\n"
711 << "\\font_default_family " << h_font_default_family << "\n"
712 << "\\font_sc " << h_font_sc << "\n"
713 << "\\font_osf " << h_font_osf << "\n"
714 << "\\font_sf_scale " << h_font_sf_scale << "\n"
715 << "\\font_tt_scale " << h_font_tt_scale << "\n"
716 << "\\graphics " << h_graphics << "\n";
717 if (!h_float_placement.empty())
718 os << "\\float_placement " << h_float_placement << "\n";
719 os << "\\paperfontsize " << h_paperfontsize << "\n"
720 << "\\spacing " << h_spacing << "\n"
721 << "\\use_hyperref " << h_use_hyperref << '\n';
722 if (h_use_hyperref == "1") {
723 if (!h_pdf_title.empty())
724 os << "\\pdf_title \"" << h_pdf_title << "\"\n";
725 if (!h_pdf_author.empty())
726 os << "\\pdf_author \"" << h_pdf_author << "\"\n";
727 if (!h_pdf_subject.empty())
728 os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
729 if (!h_pdf_keywords.empty())
730 os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
731 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
732 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
733 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
734 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
735 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
736 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
737 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
738 "\\pdf_backref " << h_pdf_backref << "\n"
739 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
740 if (!h_pdf_pagemode.empty())
741 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
742 if (!h_pdf_quoted_options.empty())
743 os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
745 os << "\\papersize " << h_papersize << "\n"
746 << "\\use_geometry " << h_use_geometry << "\n"
747 << "\\use_amsmath " << h_use_amsmath << "\n"
748 << "\\use_esint " << h_use_esint << "\n"
749 << "\\use_mhchem " << h_use_mhchem << "\n"
750 << "\\use_mathdots " << h_use_mathdots << "\n"
751 << "\\cite_engine " << h_cite_engine << "\n"
752 << "\\use_bibtopic " << h_use_bibtopic << "\n"
753 << "\\paperorientation " << h_paperorientation << '\n'
754 << "\\suppress_date " << h_suppress_date << '\n'
755 << "\\use_refstyle " << h_use_refstyle << '\n';
756 if (!h_notefontcolor.empty())
757 os << "\\notefontcolor " << h_notefontcolor << '\n';
759 << "\\secnumdepth " << h_secnumdepth << "\n"
760 << "\\tocdepth " << h_tocdepth << "\n"
761 << "\\paragraph_separation " << h_paragraph_separation << "\n";
762 if (h_paragraph_separation == "skip")
763 os << "\\defskip " << h_defskip << "\n";
765 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
766 os << "\\quotes_language " << h_quotes_language << "\n"
767 << "\\papercolumns " << h_papercolumns << "\n"
768 << "\\papersides " << h_papersides << "\n"
769 << "\\paperpagestyle " << h_paperpagestyle << "\n";
770 if (!h_listings_params.empty())
771 os << "\\listings_params " << h_listings_params << "\n";
772 os << "\\tracking_changes " << h_tracking_changes << "\n"
773 << "\\output_changes " << h_output_changes << "\n"
774 << "\\html_math_output " << h_html_math_output << "\n"
775 << "\\html_css_as_file " << h_html_css_as_file << "\n"
776 << "\\html_be_strict " << h_html_be_strict << "\n"
777 << "\\end_header\n\n"
779 // clear preamble for subdocuments
783 } // anonymous namespace
786 void parse_preamble(Parser & p, ostream & os,
787 string const & forceclass, TeX2LyXDocClass & tc)
789 // initialize fixed types
790 special_columns['D'] = 3;
791 bool is_full_document = false;
792 bool is_lyx_file = false;
793 bool in_lyx_preamble = false;
795 // determine whether this is a full document or a fragment for inclusion
797 Token const & t = p.get_token();
799 if (t.cat() == catEscape && t.cs() == "documentclass") {
800 is_full_document = true;
806 while (is_full_document && p.good()) {
807 Token const & t = p.get_token();
810 cerr << "t: " << t << "\n";
816 if (!in_lyx_preamble &&
817 (t.cat() == catLetter ||
818 t.cat() == catSuper ||
820 t.cat() == catOther ||
821 t.cat() == catMath ||
822 t.cat() == catActive ||
823 t.cat() == catBegin ||
825 t.cat() == catAlign ||
826 t.cat() == catParameter))
827 h_preamble << t.cs();
829 else if (!in_lyx_preamble &&
830 (t.cat() == catSpace || t.cat() == catNewline))
831 h_preamble << t.asInput();
833 else if (t.cat() == catComment) {
834 static regex const islyxfile("%% LyX .* created this file");
835 static regex const usercommands("User specified LaTeX commands");
837 string const comment = t.asInput();
839 // magically switch encoding default if it looks like XeLaTeX
840 static string const magicXeLaTeX =
841 "% This document must be compiled with XeLaTeX ";
842 if (comment.size() > magicXeLaTeX.size()
843 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
844 && h_inputencoding == "auto") {
845 cerr << "XeLaTeX comment found, switching to UTF8\n";
846 h_inputencoding = "utf8";
849 if (regex_search(comment, sub, islyxfile)) {
851 in_lyx_preamble = true;
852 } else if (is_lyx_file
853 && regex_search(comment, sub, usercommands))
854 in_lyx_preamble = false;
855 else if (!in_lyx_preamble)
856 h_preamble << t.asInput();
859 else if (t.cs() == "pagestyle")
860 h_paperpagestyle = p.verbatim_item();
862 else if (t.cs() == "makeatletter") {
863 // LyX takes care of this
864 p.setCatCode('@', catLetter);
867 else if (t.cs() == "makeatother") {
868 // LyX takes care of this
869 p.setCatCode('@', catOther);
872 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
873 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
874 || t.cs() == "providecommand" || t.cs() == "providecommandx"
875 || t.cs() == "DeclareRobustCommand"
876 || t.cs() == "DeclareRobustCommandx"
877 || t.cs() == "ProvideTextCommandDefault"
878 || t.cs() == "DeclareMathAccent") {
880 if (p.next_token().character() == '*') {
884 string const name = p.verbatim_item();
885 string const opt1 = p.getFullOpt();
886 string const opt2 = p.getFullOpt();
887 string const body = p.verbatim_item();
889 if (name == "\\rmdefault")
890 if (is_known(body, known_roman_fonts))
892 if (name == "\\sfdefault")
893 if (is_known(body, known_sans_fonts))
895 if (name == "\\ttdefault")
896 if (is_known(body, known_typewriter_fonts))
897 h_font_typewriter = body;
898 if (name == "\\familydefault") {
899 string family = body;
900 // remove leading "\"
901 h_font_default_family = family.erase(0,1);
904 // Add the command to the known commands
905 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
907 // only non-lyxspecific stuff
908 if (!in_lyx_preamble) {
910 ss << '\\' << t.cs();
913 ss << '{' << name << '}' << opt1 << opt2
914 << '{' << body << "}";
915 h_preamble << ss.str();
917 ostream & out = in_preamble ? h_preamble : os;
918 out << "\\" << t.cs() << "{" << name << "}"
919 << opts << "{" << body << "}";
924 else if (t.cs() == "documentclass") {
925 vector<string>::iterator it;
926 vector<string> opts = split_options(p.getArg('[', ']'));
927 handle_opt(opts, known_fontsizes, h_paperfontsize);
928 delete_opt(opts, known_fontsizes);
929 // delete "pt" at the end
930 string::size_type i = h_paperfontsize.find("pt");
931 if (i != string::npos)
932 h_paperfontsize.erase(i);
933 // The documentclass options are always parsed before the options
934 // of the babel call so that a language cannot overwrite the babel
936 handle_opt(opts, known_languages, h_language);
937 delete_opt(opts, known_languages);
940 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
941 h_paperorientation = "landscape";
945 if ((it = find(opts.begin(), opts.end(), "oneside"))
950 if ((it = find(opts.begin(), opts.end(), "twoside"))
956 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
958 h_papercolumns = "1";
961 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
963 h_papercolumns = "2";
967 // some size options are know to any document classes, other sizes
968 // are handled by the \geometry command of the geometry package
969 handle_opt(opts, known_class_paper_sizes, h_papersize);
970 delete_opt(opts, known_class_paper_sizes);
971 // the remaining options
972 h_options = join(opts, ",");
973 // FIXME This does not work for classes that have a
974 // different name in LyX than in LaTeX
975 h_textclass = p.getArg('{', '}');
978 else if (t.cs() == "usepackage") {
979 string const options = p.getArg('[', ']');
980 string const name = p.getArg('{', '}');
981 vector<string> vecnames;
982 split(name, vecnames, ',');
983 vector<string>::const_iterator it = vecnames.begin();
984 vector<string>::const_iterator end = vecnames.end();
985 for (; it != end; ++it)
986 handle_package(p, trimSpaceAndEol(*it), options,
990 else if (t.cs() == "inputencoding") {
991 string const encoding = p.getArg('{','}');
992 h_inputencoding = encoding;
993 p.setEncoding(encoding);
996 else if (t.cs() == "newenvironment") {
997 string const name = p.getArg('{', '}');
998 string const opt1 = p.getFullOpt();
999 string const opt2 = p.getFullOpt();
1000 string const beg = p.verbatim_item();
1001 string const end = p.verbatim_item();
1002 if (!in_lyx_preamble) {
1003 h_preamble << "\\newenvironment{" << name
1004 << '}' << opt1 << opt2 << '{'
1005 << beg << "}{" << end << '}';
1007 add_known_environment(name, opt1, !opt2.empty(),
1008 from_utf8(beg), from_utf8(end));
1012 else if (t.cs() == "def") {
1013 string name = p.get_token().cs();
1014 while (p.next_token().cat() != catBegin)
1015 name += p.get_token().cs();
1016 if (!in_lyx_preamble)
1017 h_preamble << "\\def\\" << name << '{'
1018 << p.verbatim_item() << "}";
1021 else if (t.cs() == "newcolumntype") {
1022 string const name = p.getArg('{', '}');
1023 trimSpaceAndEol(name);
1025 string opts = p.getOpt();
1026 if (!opts.empty()) {
1027 istringstream is(string(opts, 1));
1030 special_columns[name[0]] = nargs;
1031 h_preamble << "\\newcolumntype{" << name << "}";
1033 h_preamble << "[" << nargs << "]";
1034 h_preamble << "{" << p.verbatim_item() << "}";
1037 else if (t.cs() == "setcounter") {
1038 string const name = p.getArg('{', '}');
1039 string const content = p.getArg('{', '}');
1040 if (name == "secnumdepth")
1041 h_secnumdepth = content;
1042 else if (name == "tocdepth")
1043 h_tocdepth = content;
1045 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1048 else if (t.cs() == "setlength") {
1049 string const name = p.verbatim_item();
1050 string const content = p.verbatim_item();
1051 // the paragraphs are only not indented when \parindent is set to zero
1052 if (name == "\\parindent" && content != "") {
1053 if (content[0] == '0')
1054 h_paragraph_separation = "skip";
1056 h_paragraph_indentation = translate_len(content);
1057 } else if (name == "\\parskip") {
1058 if (content == "\\smallskipamount")
1059 h_defskip = "smallskip";
1060 else if (content == "\\medskipamount")
1061 h_defskip = "medskip";
1062 else if (content == "\\bigskipamount")
1063 h_defskip = "bigskip";
1065 h_defskip = content;
1067 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1070 else if (t.cs() == "onehalfspacing")
1071 h_spacing = "onehalf";
1073 else if (t.cs() == "doublespacing")
1074 h_spacing = "double";
1076 else if (t.cs() == "setstretch")
1077 h_spacing = "other " + p.verbatim_item();
1079 else if (t.cs() == "begin") {
1080 string const name = p.getArg('{', '}');
1081 if (name == "document")
1083 h_preamble << "\\begin{" << name << "}";
1086 else if (t.cs() == "geometry") {
1087 h_use_geometry = "true";
1088 vector<string> opts = split_options(p.getArg('{', '}'));
1089 vector<string>::iterator it;
1090 // paper orientation
1091 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1092 h_paperorientation = "landscape";
1096 handle_opt(opts, known_paper_sizes, h_papersize);
1097 delete_opt(opts, known_paper_sizes);
1099 char const * const * margin = known_paper_margins;
1101 for (; *margin; ++margin) {
1103 // search for the "=" in e.g. "lmargin=2cm" to get the value
1104 for(size_t i = 0; i != opts.size(); i++) {
1105 if (opts.at(i).find(*margin) != string::npos) {
1106 string::size_type pos = opts.at(i).find("=");
1107 string value = opts.at(i).substr(pos + 1);
1108 string name = known_coded_paper_margins[k];
1109 h_margins += "\\" + name + " " + value + "\n";
1115 else if (t.cs() == "definecolor") {
1116 string const color = p.getArg('{', '}');
1117 string const space = p.getArg('{', '}');
1118 string const value = p.getArg('{', '}');
1119 if (color == "note_fontcolor" && space == "rgb") {
1120 RGBColor c(RGBColorFromLaTeX(value));
1121 h_notefontcolor = X11hexname(c);
1123 h_preamble << "\\definecolor{" << color
1124 << "}{" << space << "}{" << value
1129 else if (t.cs() == "jurabibsetup") {
1130 // FIXME p.getArg('{', '}') is most probably wrong (it
1131 // does not handle nested braces).
1132 // Use p.verbatim_item() instead.
1133 vector<string> jurabibsetup =
1134 split_options(p.getArg('{', '}'));
1135 // add jurabibsetup to the jurabib package options
1136 add_package("jurabib", jurabibsetup);
1137 if (!jurabibsetup.empty()) {
1138 h_preamble << "\\jurabibsetup{"
1139 << join(jurabibsetup, ",") << '}';
1143 else if (t.cs() == "hypersetup") {
1144 vector<string> hypersetup =
1145 split_options(p.verbatim_item());
1146 // add hypersetup to the hyperref package options
1147 handle_hyperref(hypersetup);
1148 if (!hypersetup.empty()) {
1149 h_preamble << "\\hypersetup{"
1150 << join(hypersetup, ",") << '}';
1154 else if (is_known(t.cs(), known_if_3arg_commands)) {
1155 // prevent misparsing of \usepackage if it is used
1156 // as an argument (see e.g. our own output of
1157 // \@ifundefined above)
1158 string const arg1 = p.verbatim_item();
1159 string const arg2 = p.verbatim_item();
1160 string const arg3 = p.verbatim_item();
1161 if (!in_lyx_preamble) {
1162 h_preamble << t.asInput()
1163 << '{' << arg1 << '}'
1164 << '{' << arg2 << '}'
1165 << '{' << arg3 << '}';
1169 else if (is_known(t.cs(), known_if_commands)) {
1170 // must not parse anything in conditional code, since
1171 // LyX would output the parsed contents unconditionally
1172 if (!in_lyx_preamble)
1173 h_preamble << t.asInput();
1174 handle_if(p, in_lyx_preamble);
1177 else if (!t.cs().empty() && !in_lyx_preamble)
1178 h_preamble << '\\' << t.cs();
1181 // remove the whitespace
1184 // Force textclass if the user wanted it
1185 if (!forceclass.empty())
1186 h_textclass = forceclass;
1187 if (noweb_mode && !prefixIs(h_textclass, "literate-"))
1188 h_textclass.insert(0, "literate-");
1189 tc.setName(h_textclass);
1191 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1194 if (h_papersides.empty()) {
1197 h_papersides = ss.str();
1199 end_preamble(os, tc);
1203 /// translates a babel language name to a LyX language name
1204 string babel2lyx(string const & language)
1206 char const * const * where = is_known(language, known_languages);
1208 return known_coded_languages[where - known_languages];