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_backgroundcolor;
218 string h_notefontcolor;
219 string h_secnumdepth = "3";
220 string h_tocdepth = "3";
221 string h_defskip = "medskip";
222 string h_paragraph_indentation = "default";
223 string h_quotes_language = "english";
224 string h_papercolumns = "1";
226 string h_paperpagestyle = "default";
227 string h_listings_params;
228 string h_tracking_changes = "false";
229 string h_output_changes = "false";
230 string h_html_math_output = "0";
231 string h_html_css_as_file = "0";
232 string h_html_be_strict = "false";
236 // returns true if at least one of the options in what has been found
237 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
243 // the last language option is the document language (for babel and LyX)
244 // the last size option is the document font size
245 vector<string>::iterator it;
246 vector<string>::iterator position = opts.begin();
247 for (; *what; ++what) {
248 it = find(opts.begin(), opts.end(), *what);
249 if (it != opts.end()) {
250 if (it >= position) {
261 void delete_opt(vector<string> & opts, char const * const * what)
266 // remove found options from the list
267 // do this after handle_opt to avoid potential memory leaks
268 vector<string>::iterator it;
269 for (; *what; ++what) {
270 it = find(opts.begin(), opts.end(), *what);
271 if (it != opts.end())
278 * Split a package options string (keyval format) into a vector.
280 * authorformat=smallcaps,
282 * titleformat=colonsep,
283 * bibformat={tabular,ibidem,numbered}
285 vector<string> split_options(string const & input)
287 vector<string> options;
291 Token const & t = p.get_token();
292 if (t.asInput() == ",") {
293 options.push_back(trimSpaceAndEol(option));
295 } else if (t.asInput() == "=") {
298 if (p.next_token().asInput() == "{")
299 option += '{' + p.getArg('{', '}') + '}';
300 } else if (t.cat() != catSpace)
301 option += t.asInput();
305 options.push_back(trimSpaceAndEol(option));
312 * Retrieve a keyval option "name={value with=sign}" named \p name from
313 * \p options and return the value.
314 * The found option is also removed from \p options.
316 string process_keyval_opt(vector<string> & options, string name)
318 for (size_t i = 0; i < options.size(); ++i) {
319 vector<string> option;
320 split(options[i], option, '=');
321 if (option.size() < 2)
323 if (option[0] == name) {
324 options.erase(options.begin() + i);
325 option.erase(option.begin());
326 return join(option, "=");
334 * Add package \p name with options \p options to used_packages.
335 * Remove options from \p options that we don't want to output.
337 void add_package(string const & name, vector<string> & options)
339 // every package inherits the global options
340 if (used_packages.find(name) == used_packages.end())
341 used_packages[name] = split_options(h_options);
343 vector<string> & v = used_packages[name];
344 v.insert(v.end(), options.begin(), options.end());
345 if (name == "jurabib") {
346 // Don't output the order argument (see the cite command
347 // handling code in text.cpp).
348 vector<string>::iterator end =
349 remove(options.begin(), options.end(), "natbiborder");
350 end = remove(options.begin(), end, "jurabiborder");
351 options.erase(end, options.end());
356 // Given is a string like "scaled=0.9", return 0.9 * 100
357 string const scale_as_percentage(string const & scale)
359 string::size_type pos = scale.find('=');
360 if (pos != string::npos) {
361 string value = scale.substr(pos + 1);
363 return convert<string>(100 * convert<double>(value));
365 // If the input string didn't match our expectations.
366 // return the default value "100"
371 string remove_braces(string const & value)
375 if (value[0] == '{' && value[value.length()-1] == '}')
376 return value.substr(1, value.length()-2);
381 void handle_hyperref(vector<string> & options)
383 // FIXME swallow inputencoding changes that might surround the
384 // hyperref setup if it was written by LyX
385 h_use_hyperref = "1";
386 // swallow "unicode=true", since LyX does always write that
387 vector<string>::iterator it =
388 find(options.begin(), options.end(), "unicode=true");
389 if (it != options.end())
391 it = find(options.begin(), options.end(), "pdfusetitle");
392 if (it != options.end()) {
393 h_pdf_pdfusetitle = "1";
396 string bookmarks = process_keyval_opt(options, "bookmarks");
397 if (bookmarks == "true")
398 h_pdf_bookmarks = "1";
399 else if (bookmarks == "false")
400 h_pdf_bookmarks = "0";
401 if (h_pdf_bookmarks == "1") {
402 string bookmarksnumbered =
403 process_keyval_opt(options, "bookmarksnumbered");
404 if (bookmarksnumbered == "true")
405 h_pdf_bookmarksnumbered = "1";
406 else if (bookmarksnumbered == "false")
407 h_pdf_bookmarksnumbered = "0";
408 string bookmarksopen =
409 process_keyval_opt(options, "bookmarksopen");
410 if (bookmarksopen == "true")
411 h_pdf_bookmarksopen = "1";
412 else if (bookmarksopen == "false")
413 h_pdf_bookmarksopen = "0";
414 if (h_pdf_bookmarksopen == "1") {
415 string bookmarksopenlevel =
416 process_keyval_opt(options, "bookmarksopenlevel");
417 if (!bookmarksopenlevel.empty())
418 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
421 string breaklinks = process_keyval_opt(options, "breaklinks");
422 if (breaklinks == "true")
423 h_pdf_breaklinks = "1";
424 else if (breaklinks == "false")
425 h_pdf_breaklinks = "0";
426 string pdfborder = process_keyval_opt(options, "pdfborder");
427 if (pdfborder == "{0 0 0}")
428 h_pdf_pdfborder = "1";
429 else if (pdfborder == "{0 0 1}")
430 h_pdf_pdfborder = "0";
431 string backref = process_keyval_opt(options, "backref");
432 if (!backref.empty())
433 h_pdf_backref = backref;
434 string colorlinks = process_keyval_opt(options, "colorlinks");
435 if (colorlinks == "true")
436 h_pdf_colorlinks = "1";
437 else if (colorlinks == "false")
438 h_pdf_colorlinks = "0";
439 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
440 if (!pdfpagemode.empty())
441 h_pdf_pagemode = pdfpagemode;
442 string pdftitle = process_keyval_opt(options, "pdftitle");
443 if (!pdftitle.empty()) {
444 h_pdf_title = remove_braces(pdftitle);
446 string pdfauthor = process_keyval_opt(options, "pdfauthor");
447 if (!pdfauthor.empty()) {
448 h_pdf_author = remove_braces(pdfauthor);
450 string pdfsubject = process_keyval_opt(options, "pdfsubject");
451 if (!pdfsubject.empty())
452 h_pdf_subject = remove_braces(pdfsubject);
453 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
454 if (!pdfkeywords.empty())
455 h_pdf_keywords = remove_braces(pdfkeywords);
456 if (!options.empty()) {
457 if (!h_pdf_quoted_options.empty())
458 h_pdf_quoted_options += ',';
459 h_pdf_quoted_options += join(options, ",");
465 void handle_package(Parser &p, string const & name, string const & opts,
466 bool in_lyx_preamble)
468 vector<string> options = split_options(opts);
469 add_package(name, options);
473 if (is_known(name, known_roman_fonts)) {
478 if (name == "fourier") {
479 h_font_roman = "utopia";
480 // when font uses real small capitals
481 if (opts == "expert")
485 if (name == "mathpazo")
486 h_font_roman = "palatino";
488 if (name == "mathptmx")
489 h_font_roman = "times";
492 if (is_known(name, known_sans_fonts)) {
496 h_font_sf_scale = scale_as_percentage(scale);
501 if (is_known(name, known_typewriter_fonts)) {
502 h_font_typewriter = name;
505 h_font_tt_scale = scale_as_percentage(scale);
509 // font uses old-style figure
513 else if (name == "amsmath" || name == "amssymb")
516 else if (name == "esint")
519 else if (name == "mhchem")
522 else if (name == "mathdots")
523 h_use_mathdots = "2";
525 else if (name == "undertilde")
526 h_use_undertilde = "2";
528 else if (name == "babel" && !opts.empty()) {
529 // check if more than one option was used - used later for inputenc
530 // in case inputenc is parsed before babel, set the encoding to auto
531 if (options.begin() != options.end() - 1) {
532 one_language = false;
533 h_inputencoding = "auto";
535 // babel takes the last language of the option of its \usepackage
536 // call as document language. If there is no such language option, the
537 // last language in the documentclass options is used.
538 handle_opt(options, known_languages, h_language);
539 delete_opt(options, known_languages);
542 else if (name == "fontenc") {
543 h_fontencoding = getStringFromVector(options, ",");
544 /* We could do the following for better round trip support,
545 * but this makes the document less portable, so I skip it:
546 if (h_fontencoding == lyxrc.fontenc)
547 h_fontencoding = "global";
552 else if (name == "inputenc" || name == "luainputenc") {
553 // h_inputencoding is only set when there is not more than one
554 // inputenc option because otherwise h_inputencoding must be
555 // set to "auto" (the default encoding of the document language)
556 // Therefore check for the "," character.
557 // It is also only set when there is not more then one babel
558 // language option but this is handled in the routine for babel.
559 if (opts.find(",") == string::npos && one_language == true)
560 h_inputencoding = opts;
561 if (!options.empty())
562 p.setEncoding(options.back());
566 else if (name == "makeidx")
569 else if (name == "prettyref")
572 else if (name == "varioref")
575 else if (name == "verbatim")
578 else if (name == "nomencl")
581 else if (name == "textcomp")
584 else if (name == "url")
587 else if (name == "subscript")
590 else if (name == "color") {
591 // with the following command this package is only loaded when needed for
592 // undefined colors, since we only support the predefined colors
593 h_preamble << "\\@ifundefined{definecolor}\n {\\usepackage{color}}{}\n";
596 else if (name == "graphicx")
599 else if (name == "setspace")
603 // do not ignore as long as we don't support all commands (e.g. \xout is missing)
604 else if (name == "ulem")
608 else if (name == "geometry")
609 ; // Ignore this, the geometry settings are made by the \geometry
610 // command. This command is handled below.
612 else if (is_known(name, known_languages))
615 else if (name == "natbib") {
616 h_cite_engine = "natbib_authoryear";
617 vector<string>::iterator it =
618 find(options.begin(), options.end(), "authoryear");
619 if (it != options.end())
622 it = find(options.begin(), options.end(), "numbers");
623 if (it != options.end()) {
624 h_cite_engine = "natbib_numerical";
630 else if (name == "jurabib")
631 h_cite_engine = "jurabib";
633 else if (name == "hyperref")
634 handle_hyperref(options);
636 else if (!in_lyx_preamble) {
638 h_preamble << "\\usepackage{" << name << "}";
640 h_preamble << "\\usepackage[" << opts << "]{"
646 // We need to do something with the options...
647 if (!options.empty())
648 cerr << "Ignoring options '" << join(options, ",")
649 << "' of package " << name << '.' << endl;
651 // remove the whitespace
656 void handle_if(Parser & p, bool in_lyx_preamble)
659 Token t = p.get_token();
660 if (t.cat() == catEscape &&
661 is_known(t.cs(), known_if_commands))
662 handle_if(p, in_lyx_preamble);
664 if (!in_lyx_preamble)
665 h_preamble << t.asInput();
666 if (t.cat() == catEscape && t.cs() == "fi")
673 void end_preamble(ostream & os, TextClass const & /*textclass*/)
675 // translate from babel to LyX names
676 h_language = babel2lyx(h_language);
678 // set the quote language
679 // LyX only knows the following quotes languages:
680 // english, swedish, german, polish, french and danish
681 // (quotes for "japanese" and "chinese-traditional" are missing because
682 // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
683 // conversion list taken from
684 // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
685 // (quotes for kazakh and interlingua are unknown)
687 if (h_language == "danish")
688 h_quotes_language = "danish";
690 else if (is_known(h_language, known_french_quotes_languages))
691 h_quotes_language = "french";
693 else if (is_known(h_language, known_german_quotes_languages))
694 h_quotes_language = "german";
696 else if (is_known(h_language, known_polish_quotes_languages))
697 h_quotes_language = "polish";
699 else if (is_known(h_language, known_swedish_quotes_languages))
700 h_quotes_language = "swedish";
702 else if (is_known(h_language, known_english_quotes_languages))
703 h_quotes_language = "english";
705 // output the LyX file settings
706 os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
707 << "\\lyxformat " << LYX_FORMAT << '\n'
708 << "\\begin_document\n"
709 << "\\begin_header\n"
710 << "\\textclass " << h_textclass << "\n";
711 if (!h_preamble.str().empty())
712 os << "\\begin_preamble\n" << h_preamble.str() << "\n\\end_preamble\n";
713 if (!h_options.empty())
714 os << "\\options " << h_options << "\n";
715 os << "\\use_default_options " << h_use_default_options << "\n"
716 << modules_placeholder
717 << "\\language " << h_language << "\n"
718 << "\\language_package " << h_language_package << "\n"
719 << "\\inputencoding " << h_inputencoding << "\n"
720 << "\\fontencoding " << h_fontencoding << "\n"
721 << "\\font_roman " << h_font_roman << "\n"
722 << "\\font_sans " << h_font_sans << "\n"
723 << "\\font_typewriter " << h_font_typewriter << "\n"
724 << "\\font_default_family " << h_font_default_family << "\n"
725 << "\\font_sc " << h_font_sc << "\n"
726 << "\\font_osf " << h_font_osf << "\n"
727 << "\\font_sf_scale " << h_font_sf_scale << "\n"
728 << "\\font_tt_scale " << h_font_tt_scale << "\n"
729 << "\\graphics " << h_graphics << "\n";
730 if (!h_float_placement.empty())
731 os << "\\float_placement " << h_float_placement << "\n";
732 os << "\\paperfontsize " << h_paperfontsize << "\n"
733 << "\\spacing " << h_spacing << "\n"
734 << "\\use_hyperref " << h_use_hyperref << '\n';
735 if (h_use_hyperref == "1") {
736 if (!h_pdf_title.empty())
737 os << "\\pdf_title \"" << h_pdf_title << "\"\n";
738 if (!h_pdf_author.empty())
739 os << "\\pdf_author \"" << h_pdf_author << "\"\n";
740 if (!h_pdf_subject.empty())
741 os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
742 if (!h_pdf_keywords.empty())
743 os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
744 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
745 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
746 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
747 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
748 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
749 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
750 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
751 "\\pdf_backref " << h_pdf_backref << "\n"
752 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
753 if (!h_pdf_pagemode.empty())
754 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
755 if (!h_pdf_quoted_options.empty())
756 os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
758 os << "\\papersize " << h_papersize << "\n"
759 << "\\use_geometry " << h_use_geometry << "\n"
760 << "\\use_amsmath " << h_use_amsmath << "\n"
761 << "\\use_esint " << h_use_esint << "\n"
762 << "\\use_mhchem " << h_use_mhchem << "\n"
763 << "\\use_mathdots " << h_use_mathdots << "\n"
764 << "\\use_undertilde " << h_use_undertilde << "\n"
765 << "\\cite_engine " << h_cite_engine << "\n"
766 << "\\use_bibtopic " << h_use_bibtopic << "\n"
767 << "\\paperorientation " << h_paperorientation << '\n'
768 << "\\suppress_date " << h_suppress_date << '\n'
769 << "\\use_refstyle " << h_use_refstyle << '\n';
770 if (!h_fontcolor.empty())
771 os << "\\fontcolor " << h_fontcolor << '\n';
772 if (!h_notefontcolor.empty())
773 os << "\\notefontcolor " << h_notefontcolor << '\n';
774 if (!h_backgroundcolor.empty())
775 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
776 if (!h_boxbgcolor.empty())
777 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
779 << "\\secnumdepth " << h_secnumdepth << "\n"
780 << "\\tocdepth " << h_tocdepth << "\n"
781 << "\\paragraph_separation " << h_paragraph_separation << "\n";
782 if (h_paragraph_separation == "skip")
783 os << "\\defskip " << h_defskip << "\n";
785 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
786 os << "\\quotes_language " << h_quotes_language << "\n"
787 << "\\papercolumns " << h_papercolumns << "\n"
788 << "\\papersides " << h_papersides << "\n"
789 << "\\paperpagestyle " << h_paperpagestyle << "\n";
790 if (!h_listings_params.empty())
791 os << "\\listings_params " << h_listings_params << "\n";
792 os << "\\tracking_changes " << h_tracking_changes << "\n"
793 << "\\output_changes " << h_output_changes << "\n"
794 << "\\html_math_output " << h_html_math_output << "\n"
795 << "\\html_css_as_file " << h_html_css_as_file << "\n"
796 << "\\html_be_strict " << h_html_be_strict << "\n"
797 << "\\end_header\n\n"
799 // clear preamble for subdocuments
803 } // anonymous namespace
806 void parse_preamble(Parser & p, ostream & os,
807 string const & forceclass, TeX2LyXDocClass & tc)
809 // initialize fixed types
810 special_columns['D'] = 3;
811 bool is_full_document = false;
812 bool is_lyx_file = false;
813 bool in_lyx_preamble = false;
815 // determine whether this is a full document or a fragment for inclusion
817 Token const & t = p.get_token();
819 if (t.cat() == catEscape && t.cs() == "documentclass") {
820 is_full_document = true;
826 while (is_full_document && p.good()) {
827 Token const & t = p.get_token();
830 cerr << "t: " << t << "\n";
836 if (!in_lyx_preamble &&
837 (t.cat() == catLetter ||
838 t.cat() == catSuper ||
840 t.cat() == catOther ||
841 t.cat() == catMath ||
842 t.cat() == catActive ||
843 t.cat() == catBegin ||
845 t.cat() == catAlign ||
846 t.cat() == catParameter))
847 h_preamble << t.cs();
849 else if (!in_lyx_preamble &&
850 (t.cat() == catSpace || t.cat() == catNewline))
851 h_preamble << t.asInput();
853 else if (t.cat() == catComment) {
854 static regex const islyxfile("%% LyX .* created this file");
855 static regex const usercommands("User specified LaTeX commands");
857 string const comment = t.asInput();
859 // magically switch encoding default if it looks like XeLaTeX
860 static string const magicXeLaTeX =
861 "% This document must be compiled with XeLaTeX ";
862 if (comment.size() > magicXeLaTeX.size()
863 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
864 && h_inputencoding == "auto") {
865 cerr << "XeLaTeX comment found, switching to UTF8\n";
866 h_inputencoding = "utf8";
869 if (regex_search(comment, sub, islyxfile)) {
871 in_lyx_preamble = true;
872 } else if (is_lyx_file
873 && regex_search(comment, sub, usercommands))
874 in_lyx_preamble = false;
875 else if (!in_lyx_preamble)
876 h_preamble << t.asInput();
879 else if (t.cs() == "pagestyle")
880 h_paperpagestyle = p.verbatim_item();
882 else if (t.cs() == "date") {
883 if (p.verbatim_item().empty())
884 h_suppress_date = "true";
887 else if (t.cs() == "makeatletter") {
888 // LyX takes care of this
889 p.setCatCode('@', catLetter);
892 else if (t.cs() == "makeatother") {
893 // LyX takes care of this
894 p.setCatCode('@', catOther);
897 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
898 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
899 || t.cs() == "providecommand" || t.cs() == "providecommandx"
900 || t.cs() == "DeclareRobustCommand"
901 || t.cs() == "DeclareRobustCommandx"
902 || t.cs() == "ProvideTextCommandDefault"
903 || t.cs() == "DeclareMathAccent") {
905 if (p.next_token().character() == '*') {
909 string const name = p.verbatim_item();
910 string const opt1 = p.getFullOpt();
911 string const opt2 = p.getFullOpt();
912 string const body = p.verbatim_item();
914 if (name == "\\rmdefault")
915 if (is_known(body, known_roman_fonts))
917 if (name == "\\sfdefault")
918 if (is_known(body, known_sans_fonts))
920 if (name == "\\ttdefault")
921 if (is_known(body, known_typewriter_fonts))
922 h_font_typewriter = body;
923 if (name == "\\familydefault") {
924 string family = body;
925 // remove leading "\"
926 h_font_default_family = family.erase(0,1);
929 // Add the command to the known commands
930 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
932 // only non-lyxspecific stuff
933 if (!in_lyx_preamble) {
935 ss << '\\' << t.cs();
938 ss << '{' << name << '}' << opt1 << opt2
939 << '{' << body << "}";
940 h_preamble << ss.str();
942 ostream & out = in_preamble ? h_preamble : os;
943 out << "\\" << t.cs() << "{" << name << "}"
944 << opts << "{" << body << "}";
949 else if (t.cs() == "documentclass") {
950 vector<string>::iterator it;
951 vector<string> opts = split_options(p.getArg('[', ']'));
952 handle_opt(opts, known_fontsizes, h_paperfontsize);
953 delete_opt(opts, known_fontsizes);
954 // delete "pt" at the end
955 string::size_type i = h_paperfontsize.find("pt");
956 if (i != string::npos)
957 h_paperfontsize.erase(i);
958 // The documentclass options are always parsed before the options
959 // of the babel call so that a language cannot overwrite the babel
961 handle_opt(opts, known_languages, h_language);
962 delete_opt(opts, known_languages);
965 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
966 h_paperorientation = "landscape";
970 if ((it = find(opts.begin(), opts.end(), "oneside"))
975 if ((it = find(opts.begin(), opts.end(), "twoside"))
981 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
983 h_papercolumns = "1";
986 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
988 h_papercolumns = "2";
992 // some size options are know to any document classes, other sizes
993 // are handled by the \geometry command of the geometry package
994 handle_opt(opts, known_class_paper_sizes, h_papersize);
995 delete_opt(opts, known_class_paper_sizes);
996 // the remaining options
997 h_options = join(opts, ",");
998 // FIXME This does not work for classes that have a
999 // different name in LyX than in LaTeX
1000 h_textclass = p.getArg('{', '}');
1003 else if (t.cs() == "usepackage") {
1004 string const options = p.getArg('[', ']');
1005 string const name = p.getArg('{', '}');
1006 vector<string> vecnames;
1007 split(name, vecnames, ',');
1008 vector<string>::const_iterator it = vecnames.begin();
1009 vector<string>::const_iterator end = vecnames.end();
1010 for (; it != end; ++it)
1011 handle_package(p, trimSpaceAndEol(*it), options,
1015 else if (t.cs() == "inputencoding") {
1016 string const encoding = p.getArg('{','}');
1017 h_inputencoding = encoding;
1018 p.setEncoding(encoding);
1021 else if (t.cs() == "newenvironment") {
1022 string const name = p.getArg('{', '}');
1023 string const opt1 = p.getFullOpt();
1024 string const opt2 = p.getFullOpt();
1025 string const beg = p.verbatim_item();
1026 string const end = p.verbatim_item();
1027 if (!in_lyx_preamble) {
1028 h_preamble << "\\newenvironment{" << name
1029 << '}' << opt1 << opt2 << '{'
1030 << beg << "}{" << end << '}';
1032 add_known_environment(name, opt1, !opt2.empty(),
1033 from_utf8(beg), from_utf8(end));
1037 else if (t.cs() == "def") {
1038 string name = p.get_token().cs();
1039 while (p.next_token().cat() != catBegin)
1040 name += p.get_token().cs();
1041 if (!in_lyx_preamble)
1042 h_preamble << "\\def\\" << name << '{'
1043 << p.verbatim_item() << "}";
1046 else if (t.cs() == "newcolumntype") {
1047 string const name = p.getArg('{', '}');
1048 trimSpaceAndEol(name);
1050 string opts = p.getOpt();
1051 if (!opts.empty()) {
1052 istringstream is(string(opts, 1));
1055 special_columns[name[0]] = nargs;
1056 h_preamble << "\\newcolumntype{" << name << "}";
1058 h_preamble << "[" << nargs << "]";
1059 h_preamble << "{" << p.verbatim_item() << "}";
1062 else if (t.cs() == "setcounter") {
1063 string const name = p.getArg('{', '}');
1064 string const content = p.getArg('{', '}');
1065 if (name == "secnumdepth")
1066 h_secnumdepth = content;
1067 else if (name == "tocdepth")
1068 h_tocdepth = content;
1070 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1073 else if (t.cs() == "setlength") {
1074 string const name = p.verbatim_item();
1075 string const content = p.verbatim_item();
1076 // the paragraphs are only not indented when \parindent is set to zero
1077 if (name == "\\parindent" && content != "") {
1078 if (content[0] == '0')
1079 h_paragraph_separation = "skip";
1081 h_paragraph_indentation = translate_len(content);
1082 } else if (name == "\\parskip") {
1083 if (content == "\\smallskipamount")
1084 h_defskip = "smallskip";
1085 else if (content == "\\medskipamount")
1086 h_defskip = "medskip";
1087 else if (content == "\\bigskipamount")
1088 h_defskip = "bigskip";
1090 h_defskip = content;
1092 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1095 else if (t.cs() == "onehalfspacing")
1096 h_spacing = "onehalf";
1098 else if (t.cs() == "doublespacing")
1099 h_spacing = "double";
1101 else if (t.cs() == "setstretch")
1102 h_spacing = "other " + p.verbatim_item();
1104 else if (t.cs() == "begin") {
1105 string const name = p.getArg('{', '}');
1106 if (name == "document")
1108 h_preamble << "\\begin{" << name << "}";
1111 else if (t.cs() == "geometry") {
1112 h_use_geometry = "true";
1113 vector<string> opts = split_options(p.getArg('{', '}'));
1114 vector<string>::iterator it;
1115 // paper orientation
1116 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1117 h_paperorientation = "landscape";
1121 handle_opt(opts, known_paper_sizes, h_papersize);
1122 delete_opt(opts, known_paper_sizes);
1124 char const * const * margin = known_paper_margins;
1126 for (; *margin; ++margin) {
1128 // search for the "=" in e.g. "lmargin=2cm" to get the value
1129 for(size_t i = 0; i != opts.size(); i++) {
1130 if (opts.at(i).find(*margin) != string::npos) {
1131 string::size_type pos = opts.at(i).find("=");
1132 string value = opts.at(i).substr(pos + 1);
1133 string name = known_coded_paper_margins[k];
1134 h_margins += "\\" + name + " " + value + "\n";
1140 else if (t.cs() == "definecolor") {
1141 string const color = p.getArg('{', '}');
1142 string const space = p.getArg('{', '}');
1143 string const value = p.getArg('{', '}');
1144 if (color == "document_fontcolor" && space == "rgb") {
1145 RGBColor c(RGBColorFromLaTeX(value));
1146 h_fontcolor = X11hexname(c);
1147 } else if (color == "note_fontcolor" && space == "rgb") {
1148 RGBColor c(RGBColorFromLaTeX(value));
1149 h_notefontcolor = X11hexname(c);
1150 } else if (color == "page_backgroundcolor" && space == "rgb") {
1151 RGBColor c(RGBColorFromLaTeX(value));
1152 h_backgroundcolor = X11hexname(c);
1153 } else if (color == "shadecolor" && space == "rgb") {
1154 RGBColor c(RGBColorFromLaTeX(value));
1155 h_boxbgcolor = X11hexname(c);
1157 h_preamble << "\\definecolor{" << color
1158 << "}{" << space << "}{" << value
1163 else if (t.cs() == "jurabibsetup") {
1164 // FIXME p.getArg('{', '}') is most probably wrong (it
1165 // does not handle nested braces).
1166 // Use p.verbatim_item() instead.
1167 vector<string> jurabibsetup =
1168 split_options(p.getArg('{', '}'));
1169 // add jurabibsetup to the jurabib package options
1170 add_package("jurabib", jurabibsetup);
1171 if (!jurabibsetup.empty()) {
1172 h_preamble << "\\jurabibsetup{"
1173 << join(jurabibsetup, ",") << '}';
1177 else if (t.cs() == "hypersetup") {
1178 vector<string> hypersetup =
1179 split_options(p.verbatim_item());
1180 // add hypersetup to the hyperref package options
1181 handle_hyperref(hypersetup);
1182 if (!hypersetup.empty()) {
1183 h_preamble << "\\hypersetup{"
1184 << join(hypersetup, ",") << '}';
1188 else if (is_known(t.cs(), known_if_3arg_commands)) {
1189 // prevent misparsing of \usepackage if it is used
1190 // as an argument (see e.g. our own output of
1191 // \@ifundefined above)
1192 string const arg1 = p.verbatim_item();
1193 string const arg2 = p.verbatim_item();
1194 string const arg3 = p.verbatim_item();
1195 // test case \@ifundefined{date}{}{\date{}}
1196 if (arg1 == "date" && arg2.empty() && arg3 == "\\date{}") {
1197 h_suppress_date = "true";
1198 } else if (!in_lyx_preamble) {
1199 h_preamble << t.asInput()
1200 << '{' << arg1 << '}'
1201 << '{' << arg2 << '}'
1202 << '{' << arg3 << '}';
1206 else if (is_known(t.cs(), known_if_commands)) {
1207 // must not parse anything in conditional code, since
1208 // LyX would output the parsed contents unconditionally
1209 if (!in_lyx_preamble)
1210 h_preamble << t.asInput();
1211 handle_if(p, in_lyx_preamble);
1214 else if (!t.cs().empty() && !in_lyx_preamble)
1215 h_preamble << '\\' << t.cs();
1218 // remove the whitespace
1221 // Force textclass if the user wanted it
1222 if (!forceclass.empty())
1223 h_textclass = forceclass;
1224 if (noweb_mode && !prefixIs(h_textclass, "literate-"))
1225 h_textclass.insert(0, "literate-");
1226 tc.setName(h_textclass);
1228 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1231 if (h_papersides.empty()) {
1234 h_papersides = ss.str();
1236 end_preamble(os, tc);
1240 /// translates a babel language name to a LyX language name
1241 string babel2lyx(string const & language)
1243 char const * const * where = is_known(language, known_languages);
1245 return known_coded_languages[where - known_languages];