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