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() == "makeatletter") {
883 // LyX takes care of this
884 p.setCatCode('@', catLetter);
887 else if (t.cs() == "makeatother") {
888 // LyX takes care of this
889 p.setCatCode('@', catOther);
892 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
893 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
894 || t.cs() == "providecommand" || t.cs() == "providecommandx"
895 || t.cs() == "DeclareRobustCommand"
896 || t.cs() == "DeclareRobustCommandx"
897 || t.cs() == "ProvideTextCommandDefault"
898 || t.cs() == "DeclareMathAccent") {
900 if (p.next_token().character() == '*') {
904 string const name = p.verbatim_item();
905 string const opt1 = p.getFullOpt();
906 string const opt2 = p.getFullOpt();
907 string const body = p.verbatim_item();
909 if (name == "\\rmdefault")
910 if (is_known(body, known_roman_fonts))
912 if (name == "\\sfdefault")
913 if (is_known(body, known_sans_fonts))
915 if (name == "\\ttdefault")
916 if (is_known(body, known_typewriter_fonts))
917 h_font_typewriter = body;
918 if (name == "\\familydefault") {
919 string family = body;
920 // remove leading "\"
921 h_font_default_family = family.erase(0,1);
924 // Add the command to the known commands
925 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
927 // only non-lyxspecific stuff
928 if (!in_lyx_preamble) {
930 ss << '\\' << t.cs();
933 ss << '{' << name << '}' << opt1 << opt2
934 << '{' << body << "}";
935 h_preamble << ss.str();
937 ostream & out = in_preamble ? h_preamble : os;
938 out << "\\" << t.cs() << "{" << name << "}"
939 << opts << "{" << body << "}";
944 else if (t.cs() == "documentclass") {
945 vector<string>::iterator it;
946 vector<string> opts = split_options(p.getArg('[', ']'));
947 handle_opt(opts, known_fontsizes, h_paperfontsize);
948 delete_opt(opts, known_fontsizes);
949 // delete "pt" at the end
950 string::size_type i = h_paperfontsize.find("pt");
951 if (i != string::npos)
952 h_paperfontsize.erase(i);
953 // The documentclass options are always parsed before the options
954 // of the babel call so that a language cannot overwrite the babel
956 handle_opt(opts, known_languages, h_language);
957 delete_opt(opts, known_languages);
960 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
961 h_paperorientation = "landscape";
965 if ((it = find(opts.begin(), opts.end(), "oneside"))
970 if ((it = find(opts.begin(), opts.end(), "twoside"))
976 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
978 h_papercolumns = "1";
981 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
983 h_papercolumns = "2";
987 // some size options are know to any document classes, other sizes
988 // are handled by the \geometry command of the geometry package
989 handle_opt(opts, known_class_paper_sizes, h_papersize);
990 delete_opt(opts, known_class_paper_sizes);
991 // the remaining options
992 h_options = join(opts, ",");
993 // FIXME This does not work for classes that have a
994 // different name in LyX than in LaTeX
995 h_textclass = p.getArg('{', '}');
998 else if (t.cs() == "usepackage") {
999 string const options = p.getArg('[', ']');
1000 string const name = p.getArg('{', '}');
1001 vector<string> vecnames;
1002 split(name, vecnames, ',');
1003 vector<string>::const_iterator it = vecnames.begin();
1004 vector<string>::const_iterator end = vecnames.end();
1005 for (; it != end; ++it)
1006 handle_package(p, trimSpaceAndEol(*it), options,
1010 else if (t.cs() == "inputencoding") {
1011 string const encoding = p.getArg('{','}');
1012 h_inputencoding = encoding;
1013 p.setEncoding(encoding);
1016 else if (t.cs() == "newenvironment") {
1017 string const name = p.getArg('{', '}');
1018 string const opt1 = p.getFullOpt();
1019 string const opt2 = p.getFullOpt();
1020 string const beg = p.verbatim_item();
1021 string const end = p.verbatim_item();
1022 if (!in_lyx_preamble) {
1023 h_preamble << "\\newenvironment{" << name
1024 << '}' << opt1 << opt2 << '{'
1025 << beg << "}{" << end << '}';
1027 add_known_environment(name, opt1, !opt2.empty(),
1028 from_utf8(beg), from_utf8(end));
1032 else if (t.cs() == "def") {
1033 string name = p.get_token().cs();
1034 while (p.next_token().cat() != catBegin)
1035 name += p.get_token().cs();
1036 if (!in_lyx_preamble)
1037 h_preamble << "\\def\\" << name << '{'
1038 << p.verbatim_item() << "}";
1041 else if (t.cs() == "newcolumntype") {
1042 string const name = p.getArg('{', '}');
1043 trimSpaceAndEol(name);
1045 string opts = p.getOpt();
1046 if (!opts.empty()) {
1047 istringstream is(string(opts, 1));
1050 special_columns[name[0]] = nargs;
1051 h_preamble << "\\newcolumntype{" << name << "}";
1053 h_preamble << "[" << nargs << "]";
1054 h_preamble << "{" << p.verbatim_item() << "}";
1057 else if (t.cs() == "setcounter") {
1058 string const name = p.getArg('{', '}');
1059 string const content = p.getArg('{', '}');
1060 if (name == "secnumdepth")
1061 h_secnumdepth = content;
1062 else if (name == "tocdepth")
1063 h_tocdepth = content;
1065 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1068 else if (t.cs() == "setlength") {
1069 string const name = p.verbatim_item();
1070 string const content = p.verbatim_item();
1071 // the paragraphs are only not indented when \parindent is set to zero
1072 if (name == "\\parindent" && content != "") {
1073 if (content[0] == '0')
1074 h_paragraph_separation = "skip";
1076 h_paragraph_indentation = translate_len(content);
1077 } else if (name == "\\parskip") {
1078 if (content == "\\smallskipamount")
1079 h_defskip = "smallskip";
1080 else if (content == "\\medskipamount")
1081 h_defskip = "medskip";
1082 else if (content == "\\bigskipamount")
1083 h_defskip = "bigskip";
1085 h_defskip = content;
1087 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1090 else if (t.cs() == "onehalfspacing")
1091 h_spacing = "onehalf";
1093 else if (t.cs() == "doublespacing")
1094 h_spacing = "double";
1096 else if (t.cs() == "setstretch")
1097 h_spacing = "other " + p.verbatim_item();
1099 else if (t.cs() == "begin") {
1100 string const name = p.getArg('{', '}');
1101 if (name == "document")
1103 h_preamble << "\\begin{" << name << "}";
1106 else if (t.cs() == "geometry") {
1107 h_use_geometry = "true";
1108 vector<string> opts = split_options(p.getArg('{', '}'));
1109 vector<string>::iterator it;
1110 // paper orientation
1111 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1112 h_paperorientation = "landscape";
1116 handle_opt(opts, known_paper_sizes, h_papersize);
1117 delete_opt(opts, known_paper_sizes);
1119 char const * const * margin = known_paper_margins;
1121 for (; *margin; ++margin) {
1123 // search for the "=" in e.g. "lmargin=2cm" to get the value
1124 for(size_t i = 0; i != opts.size(); i++) {
1125 if (opts.at(i).find(*margin) != string::npos) {
1126 string::size_type pos = opts.at(i).find("=");
1127 string value = opts.at(i).substr(pos + 1);
1128 string name = known_coded_paper_margins[k];
1129 h_margins += "\\" + name + " " + value + "\n";
1135 else if (t.cs() == "definecolor") {
1136 string const color = p.getArg('{', '}');
1137 string const space = p.getArg('{', '}');
1138 string const value = p.getArg('{', '}');
1139 if (color == "document_fontcolor" && space == "rgb") {
1140 RGBColor c(RGBColorFromLaTeX(value));
1141 h_fontcolor = X11hexname(c);
1142 } else if (color == "note_fontcolor" && space == "rgb") {
1143 RGBColor c(RGBColorFromLaTeX(value));
1144 h_notefontcolor = X11hexname(c);
1145 } else if (color == "page_backgroundcolor" && space == "rgb") {
1146 RGBColor c(RGBColorFromLaTeX(value));
1147 h_backgroundcolor = X11hexname(c);
1148 } else if (color == "shadecolor" && space == "rgb") {
1149 RGBColor c(RGBColorFromLaTeX(value));
1150 h_boxbgcolor = X11hexname(c);
1152 h_preamble << "\\definecolor{" << color
1153 << "}{" << space << "}{" << value
1158 else if (t.cs() == "jurabibsetup") {
1159 // FIXME p.getArg('{', '}') is most probably wrong (it
1160 // does not handle nested braces).
1161 // Use p.verbatim_item() instead.
1162 vector<string> jurabibsetup =
1163 split_options(p.getArg('{', '}'));
1164 // add jurabibsetup to the jurabib package options
1165 add_package("jurabib", jurabibsetup);
1166 if (!jurabibsetup.empty()) {
1167 h_preamble << "\\jurabibsetup{"
1168 << join(jurabibsetup, ",") << '}';
1172 else if (t.cs() == "hypersetup") {
1173 vector<string> hypersetup =
1174 split_options(p.verbatim_item());
1175 // add hypersetup to the hyperref package options
1176 handle_hyperref(hypersetup);
1177 if (!hypersetup.empty()) {
1178 h_preamble << "\\hypersetup{"
1179 << join(hypersetup, ",") << '}';
1183 else if (is_known(t.cs(), known_if_3arg_commands)) {
1184 // prevent misparsing of \usepackage if it is used
1185 // as an argument (see e.g. our own output of
1186 // \@ifundefined above)
1187 string const arg1 = p.verbatim_item();
1188 string const arg2 = p.verbatim_item();
1189 string const arg3 = p.verbatim_item();
1190 // test case \@ifundefined{date}{}{\date{}}
1191 if (arg1 == "date" && arg2.empty() && arg3 == "\\date{}") {
1192 h_suppress_date = "true";
1193 } else if (!in_lyx_preamble) {
1194 h_preamble << t.asInput()
1195 << '{' << arg1 << '}'
1196 << '{' << arg2 << '}'
1197 << '{' << arg3 << '}';
1201 else if (is_known(t.cs(), known_if_commands)) {
1202 // must not parse anything in conditional code, since
1203 // LyX would output the parsed contents unconditionally
1204 if (!in_lyx_preamble)
1205 h_preamble << t.asInput();
1206 handle_if(p, in_lyx_preamble);
1209 else if (!t.cs().empty() && !in_lyx_preamble)
1210 h_preamble << '\\' << t.cs();
1213 // remove the whitespace
1216 // Force textclass if the user wanted it
1217 if (!forceclass.empty())
1218 h_textclass = forceclass;
1219 if (noweb_mode && !prefixIs(h_textclass, "literate-"))
1220 h_textclass.insert(0, "literate-");
1221 tc.setName(h_textclass);
1223 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1226 if (h_papersides.empty()) {
1229 h_papersides = ss.str();
1231 end_preamble(os, tc);
1235 /// translates a babel language name to a LyX language name
1236 string babel2lyx(string const & language)
1238 char const * const * where = is_known(language, known_languages);
1240 return known_coded_languages[where - known_languages];