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";
54 // necessary to avoid that our preamble stuff is added at each tex2lyx run
55 // which would pollute the preamble when doing roundtrips
56 bool ifundefined_color_set = false;
60 //add this to known_languages when updating to lyxformat 266:
61 // "armenian" (needs special handling since not supported by standard babel)
62 //add these to known_languages when updating to lyxformat 268:
63 //"chinese-simplified", "chinese-traditional", "japanese", "korean"
64 // Both changes require first that support for non-babel languages (CJK,
66 // add turkmen for lyxformat 383
68 * known babel language names (including synonyms)
69 * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
70 * not yet supported by LyX: kurmanji
71 * please keep this in sync with known_coded_languages line by line!
73 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
74 "american", "arabic", "arabtex", "austrian", "bahasa", "bahasai", "bahasam",
75 "basque", "belarusian", "brazil", "brazilian", "breton", "british", "bulgarian",
76 "canadian", "canadien", "catalan", "croatian", "czech", "danish", "dutch",
77 "english", "esperanto", "estonian", "farsi", "finnish", "francais", "french",
78 "frenchb", "frenchle", "frenchpro", "galician", "german", "germanb", "greek",
79 "hebrew", "hungarian", "icelandic", "indon", "indonesian", "interlingua",
80 "irish", "italian", "kazakh", "latin", "latvian", "lithuanian", "lowersorbian",
81 "lsorbian", "magyar", "malay", "meyalu", "mongolian", "naustrian", "newzealand",
82 "ngerman", "ngermanb", "norsk", "nynorsk", "polutonikogreek", "polish",
83 "portuges", "portuguese", "romanian", "russian", "russianb", "samin",
84 "scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish",
85 "swedish", "thai", "turkish", "turkmen", "ukraineb", "ukrainian",
86 "uppersorbian", "UKenglish", "USenglish", "usorbian", "vietnam", "welsh",
90 * the same as known_languages with .lyx names
91 * please keep this in sync with known_languages line by line!
93 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
94 "american", "arabic_arabi", "arabic_arabtex", "austrian", "bahasa", "bahasa", "bahasam",
95 "basque", "belarusian", "brazilian", "brazilian", "breton", "british", "bulgarian",
96 "canadian", "canadien", "catalan", "croatian", "czech", "danish", "dutch",
97 "english", "esperanto", "estonian", "farsi", "finnish", "french", "french",
98 "french", "french", "french", "galician", "german", "german", "greek",
99 "hebrew", "magyar", "icelandic", "bahasa", "bahasa", "interlingua",
100 "irish", "italian", "kazakh", "latin", "latvian", "lithuanian", "lowersorbian",
101 "lowersorbian", "magyar", "bahasam", "bahasam", "mongolian", "naustrian", "english",
102 "ngerman", "ngerman", "norsk", "nynorsk", "polutonikogreek", "polish",
103 "portuguese", "portuguese", "romanian", "russian", "russian", "samin",
104 "scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish",
105 "swedish", "thai", "turkish", "turkmen", "ukrainian", "ukrainian",
106 "uppersorbian", "uppersorbian", "english", "english", "vietnamese", "welsh",
109 /// languages with english quotes (.lyx names)
110 const char * const known_english_quotes_languages[] = {"american", "bahasa",
111 "bahasam", "brazilian", "canadian", "chinese-simplified", "english",
112 "esperanto", "hebrew", "irish", "korean", "portuguese", "scottish", "thai", 0};
114 /// languages with french quotes (.lyx names)
115 const char * const known_french_quotes_languages[] = {"albanian",
116 "arabic_arabi", "arabic_arabtex", "basque", "canadien", "catalan", "french",
117 "galician", "greek", "italian", "norsk", "nynorsk", "polutonikogreek",
118 "russian", "spanish", "spanish-mexico", "turkish", "turkmen", "ukrainian",
121 /// languages with german quotes (.lyx names)
122 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
123 "czech", "german", "icelandic", "lithuanian", "lowersorbian", "naustrian",
124 "ngerman", "serbian", "serbian-latin", "slovak", "slovene", "uppersorbian", 0};
126 /// languages with polish quotes (.lyx names)
127 const char * const known_polish_quotes_languages[] = {"afrikaans", "croatian",
128 "dutch", "estonian", "magyar", "polish", "romanian", 0};
130 /// languages with swedish quotes (.lyx names)
131 const char * const known_swedish_quotes_languages[] = {"finnish",
134 /// known language packages from the times before babel
135 const char * const known_old_language_packages[] = {"french", "frenchle",
136 "frenchpro", "german", "ngerman", "pmfrench", 0};
138 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
140 const char * const known_roman_fonts[] = { "ae", "bookman", "ccfonts",
141 "chancery", "charter", "cmr", "fourier", "lmodern", "mathpazo", "mathptmx",
142 "newcent", "utopia", 0};
144 const char * const known_sans_fonts[] = { "avant", "berasans", "cmbr", "cmss",
145 "helvet", "lmss", 0};
147 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
148 "courier", "lmtt", "luximono", "fourier", "lmodern", "mathpazo", "mathptmx",
151 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
152 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
153 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
154 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
155 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
157 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
158 "executivepaper", "legalpaper", "letterpaper", 0};
160 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
161 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
163 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
164 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
167 /// commands that can start an \if...\else...\endif sequence
168 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
169 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
170 "ifsidecap", "ifupgreek", 0};
172 /// conditional commands with three arguments like \@ifundefined{}{}{}
173 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
177 ostringstream h_preamble;
178 string h_textclass = "article";
179 string h_use_default_options = "false";
181 string h_language = "english";
182 string h_language_package = "none";
183 string h_fontencoding = "default";
184 string h_font_roman = "default";
185 string h_font_sans = "default";
186 string h_font_typewriter = "default";
187 string h_font_default_family = "default";
188 string h_font_sc = "false";
189 string h_font_osf = "false";
190 string h_font_sf_scale = "100";
191 string h_font_tt_scale = "100";
192 string h_graphics = "default";
193 string h_float_placement;
194 string h_paperfontsize = "default";
195 string h_spacing = "single";
196 string h_use_hyperref = "0";
199 string h_pdf_subject;
200 string h_pdf_keywords;
201 string h_pdf_bookmarks = "1";
202 string h_pdf_bookmarksnumbered = "0";
203 string h_pdf_bookmarksopen = "0";
204 string h_pdf_bookmarksopenlevel = "1";
205 string h_pdf_breaklinks = "0";
206 string h_pdf_pdfborder = "0";
207 string h_pdf_colorlinks = "0";
208 string h_pdf_backref = "section";
209 string h_pdf_pdfusetitle = "1";
210 string h_pdf_pagemode;
211 string h_pdf_quoted_options;
212 string h_papersize = "default";
213 string h_use_geometry = "false";
214 string h_use_amsmath = "1";
215 string h_use_esint = "1";
216 string h_use_mhchem = "0";
217 string h_use_mathdots = "0";
218 string h_use_undertilde = "0";
219 string h_cite_engine = "basic";
220 string h_use_bibtopic = "false";
221 string h_paperorientation = "portrait";
222 string h_suppress_date = "false";
223 string h_use_refstyle = "0";
224 string h_backgroundcolor;
227 string h_notefontcolor;
228 string h_secnumdepth = "3";
229 string h_tocdepth = "3";
230 string h_defskip = "medskip";
231 string h_paragraph_indentation = "default";
232 string h_quotes_language = "english";
233 string h_papercolumns = "1";
235 string h_paperpagestyle = "default";
236 string h_listings_params;
237 string h_tracking_changes = "false";
238 string h_output_changes = "false";
239 string h_html_math_output = "0";
240 string h_html_css_as_file = "0";
241 string h_html_be_strict = "false";
245 // returns true if at least one of the options in what has been found
246 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
252 // the last language option is the document language (for babel and LyX)
253 // the last size option is the document font size
254 vector<string>::iterator it;
255 vector<string>::iterator position = opts.begin();
256 for (; *what; ++what) {
257 it = find(opts.begin(), opts.end(), *what);
258 if (it != opts.end()) {
259 if (it >= position) {
270 void delete_opt(vector<string> & opts, char const * const * what)
275 // remove found options from the list
276 // do this after handle_opt to avoid potential memory leaks
277 vector<string>::iterator it;
278 for (; *what; ++what) {
279 it = find(opts.begin(), opts.end(), *what);
280 if (it != opts.end())
287 * Split a package options string (keyval format) into a vector.
289 * authorformat=smallcaps,
291 * titleformat=colonsep,
292 * bibformat={tabular,ibidem,numbered}
294 vector<string> split_options(string const & input)
296 vector<string> options;
300 Token const & t = p.get_token();
301 if (t.asInput() == ",") {
302 options.push_back(trimSpaceAndEol(option));
304 } else if (t.asInput() == "=") {
307 if (p.next_token().asInput() == "{")
308 option += '{' + p.getArg('{', '}') + '}';
309 } else if (t.cat() != catSpace)
310 option += t.asInput();
314 options.push_back(trimSpaceAndEol(option));
321 * Retrieve a keyval option "name={value with=sign}" named \p name from
322 * \p options and return the value.
323 * The found option is also removed from \p options.
325 string process_keyval_opt(vector<string> & options, string name)
327 for (size_t i = 0; i < options.size(); ++i) {
328 vector<string> option;
329 split(options[i], option, '=');
330 if (option.size() < 2)
332 if (option[0] == name) {
333 options.erase(options.begin() + i);
334 option.erase(option.begin());
335 return join(option, "=");
343 * Add package \p name with options \p options to used_packages.
344 * Remove options from \p options that we don't want to output.
346 void add_package(string const & name, vector<string> & options)
348 // every package inherits the global options
349 if (used_packages.find(name) == used_packages.end())
350 used_packages[name] = split_options(h_options);
352 vector<string> & v = used_packages[name];
353 v.insert(v.end(), options.begin(), options.end());
354 if (name == "jurabib") {
355 // Don't output the order argument (see the cite command
356 // handling code in text.cpp).
357 vector<string>::iterator end =
358 remove(options.begin(), options.end(), "natbiborder");
359 end = remove(options.begin(), end, "jurabiborder");
360 options.erase(end, options.end());
365 // Given is a string like "scaled=0.9", return 0.9 * 100
366 string const scale_as_percentage(string const & scale)
368 string::size_type pos = scale.find('=');
369 if (pos != string::npos) {
370 string value = scale.substr(pos + 1);
372 return convert<string>(100 * convert<double>(value));
374 // If the input string didn't match our expectations.
375 // return the default value "100"
380 string remove_braces(string const & value)
384 if (value[0] == '{' && value[value.length()-1] == '}')
385 return value.substr(1, value.length()-2);
390 void handle_hyperref(vector<string> & options)
392 // FIXME swallow inputencoding changes that might surround the
393 // hyperref setup if it was written by LyX
394 h_use_hyperref = "1";
395 // swallow "unicode=true", since LyX does always write that
396 vector<string>::iterator it =
397 find(options.begin(), options.end(), "unicode=true");
398 if (it != options.end())
400 it = find(options.begin(), options.end(), "pdfusetitle");
401 if (it != options.end()) {
402 h_pdf_pdfusetitle = "1";
405 string bookmarks = process_keyval_opt(options, "bookmarks");
406 if (bookmarks == "true")
407 h_pdf_bookmarks = "1";
408 else if (bookmarks == "false")
409 h_pdf_bookmarks = "0";
410 if (h_pdf_bookmarks == "1") {
411 string bookmarksnumbered =
412 process_keyval_opt(options, "bookmarksnumbered");
413 if (bookmarksnumbered == "true")
414 h_pdf_bookmarksnumbered = "1";
415 else if (bookmarksnumbered == "false")
416 h_pdf_bookmarksnumbered = "0";
417 string bookmarksopen =
418 process_keyval_opt(options, "bookmarksopen");
419 if (bookmarksopen == "true")
420 h_pdf_bookmarksopen = "1";
421 else if (bookmarksopen == "false")
422 h_pdf_bookmarksopen = "0";
423 if (h_pdf_bookmarksopen == "1") {
424 string bookmarksopenlevel =
425 process_keyval_opt(options, "bookmarksopenlevel");
426 if (!bookmarksopenlevel.empty())
427 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
430 string breaklinks = process_keyval_opt(options, "breaklinks");
431 if (breaklinks == "true")
432 h_pdf_breaklinks = "1";
433 else if (breaklinks == "false")
434 h_pdf_breaklinks = "0";
435 string pdfborder = process_keyval_opt(options, "pdfborder");
436 if (pdfborder == "{0 0 0}")
437 h_pdf_pdfborder = "1";
438 else if (pdfborder == "{0 0 1}")
439 h_pdf_pdfborder = "0";
440 string backref = process_keyval_opt(options, "backref");
441 if (!backref.empty())
442 h_pdf_backref = backref;
443 string colorlinks = process_keyval_opt(options, "colorlinks");
444 if (colorlinks == "true")
445 h_pdf_colorlinks = "1";
446 else if (colorlinks == "false")
447 h_pdf_colorlinks = "0";
448 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
449 if (!pdfpagemode.empty())
450 h_pdf_pagemode = pdfpagemode;
451 string pdftitle = process_keyval_opt(options, "pdftitle");
452 if (!pdftitle.empty()) {
453 h_pdf_title = remove_braces(pdftitle);
455 string pdfauthor = process_keyval_opt(options, "pdfauthor");
456 if (!pdfauthor.empty()) {
457 h_pdf_author = remove_braces(pdfauthor);
459 string pdfsubject = process_keyval_opt(options, "pdfsubject");
460 if (!pdfsubject.empty())
461 h_pdf_subject = remove_braces(pdfsubject);
462 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
463 if (!pdfkeywords.empty())
464 h_pdf_keywords = remove_braces(pdfkeywords);
465 if (!options.empty()) {
466 if (!h_pdf_quoted_options.empty())
467 h_pdf_quoted_options += ',';
468 h_pdf_quoted_options += join(options, ",");
474 void handle_package(Parser &p, string const & name, string const & opts,
475 bool in_lyx_preamble)
477 vector<string> options = split_options(opts);
478 add_package(name, options);
482 if (is_known(name, known_roman_fonts)) {
487 if (name == "fourier") {
488 h_font_roman = "utopia";
489 // when font uses real small capitals
490 if (opts == "expert")
494 if (name == "mathpazo")
495 h_font_roman = "palatino";
497 if (name == "mathptmx")
498 h_font_roman = "times";
501 if (is_known(name, known_sans_fonts)) {
505 h_font_sf_scale = scale_as_percentage(scale);
510 if (is_known(name, known_typewriter_fonts)) {
511 // fourier can be set as roman font _only_
512 // fourier as typewriter is handled in handling of \ttdefault
513 if (name != "fourier") {
514 h_font_typewriter = name;
517 h_font_tt_scale = scale_as_percentage(scale);
522 // font uses old-style figure
526 else if (name == "amsmath" || name == "amssymb")
529 else if (name == "esint")
532 else if (name == "mhchem")
535 else if (name == "mathdots")
536 h_use_mathdots = "2";
538 else if (name == "undertilde")
539 h_use_undertilde = "2";
541 else if (name == "babel") {
542 h_language_package = "default";
543 // we have to do nothing if babel is loaded without any options, otherwise
544 // we would pollute the preamble with this call in every roundtrip
546 // check if more than one option was used - used later for inputenc
547 // in case inputenc is parsed before babel, set the encoding to auto
548 if (options.begin() != options.end() - 1) {
549 one_language = false;
550 h_inputencoding = "auto";
552 // babel takes the last language of the option of its \usepackage
553 // call as document language. If there is no such language option, the
554 // last language in the documentclass options is used.
555 handle_opt(options, known_languages, h_language);
556 delete_opt(options, known_languages);
560 else if (name == "fontenc") {
561 h_fontencoding = getStringFromVector(options, ",");
562 // as of version LyX 2.0 "T1" is equal to the setting "global"
563 if (h_fontencoding == "T1")
564 h_fontencoding = "global";
568 else if (name == "inputenc" || name == "luainputenc") {
569 // h_inputencoding is only set when there is not more than one
570 // inputenc option because otherwise h_inputencoding must be
571 // set to "auto" (the default encoding of the document language)
572 // Therefore check for the "," character.
573 // It is also only set when there is not more then one babel
574 // language option but this is handled in the routine for babel.
575 if (opts.find(",") == string::npos && one_language == true)
576 h_inputencoding = opts;
577 if (!options.empty())
578 p.setEncoding(options.back());
582 else if (is_known(name, known_old_language_packages)) {
583 // known language packages from the times before babel
584 // if they are found and not also babel, they will be used as
585 // cutom language package
586 h_language_package = "\\usepackage{" + name + "}";
589 else if (name == "makeidx")
592 else if (name == "prettyref")
595 else if (name == "varioref")
598 else if (name == "verbatim")
601 else if (name == "nomencl")
604 else if (name == "textcomp")
607 else if (name == "url")
610 else if (name == "subscript")
613 else if (name == "color") {
614 // with the following command this package is only loaded when needed for
615 // undefined colors, since we only support the predefined colors
616 // only add it if not yet added
617 if (!ifundefined_color_set)
618 h_preamble << "\\@ifundefined{definecolor}\n {\\usepackage{color}}{}\n";
621 else if (name == "graphicx")
624 else if (name == "setspace")
628 // do not ignore as long as we don't support all commands (e.g. \xout is missing)
629 else if (name == "ulem")
633 else if (name == "geometry")
634 ; // Ignore this, the geometry settings are made by the \geometry
635 // command. This command is handled below.
637 else if (is_known(name, known_languages))
640 else if (name == "natbib") {
641 h_cite_engine = "natbib_authoryear";
642 vector<string>::iterator it =
643 find(options.begin(), options.end(), "authoryear");
644 if (it != options.end())
647 it = find(options.begin(), options.end(), "numbers");
648 if (it != options.end()) {
649 h_cite_engine = "natbib_numerical";
655 else if (name == "jurabib")
656 h_cite_engine = "jurabib";
658 else if (name == "hyperref")
659 handle_hyperref(options);
661 else if (!in_lyx_preamble) {
663 h_preamble << "\\usepackage{" << name << "}";
665 h_preamble << "\\usepackage[" << opts << "]{"
671 // We need to do something with the options...
672 if (!options.empty())
673 cerr << "Ignoring options '" << join(options, ",")
674 << "' of package " << name << '.' << endl;
676 // remove the whitespace
681 void handle_if(Parser & p, bool in_lyx_preamble)
684 Token t = p.get_token();
685 if (t.cat() == catEscape &&
686 is_known(t.cs(), known_if_commands))
687 handle_if(p, in_lyx_preamble);
689 if (!in_lyx_preamble)
690 h_preamble << t.asInput();
691 if (t.cat() == catEscape && t.cs() == "fi")
698 void end_preamble(ostream & os, TextClass const & /*textclass*/)
700 // translate from babel to LyX names
701 h_language = babel2lyx(h_language);
703 // set the quote language
704 // LyX only knows the following quotes languages:
705 // english, swedish, german, polish, french and danish
706 // (quotes for "japanese" and "chinese-traditional" are missing because
707 // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
708 // conversion list taken from
709 // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
710 // (quotes for kazakh and interlingua are unknown)
712 if (h_language == "danish")
713 h_quotes_language = "danish";
715 else if (is_known(h_language, known_french_quotes_languages))
716 h_quotes_language = "french";
718 else if (is_known(h_language, known_german_quotes_languages))
719 h_quotes_language = "german";
721 else if (is_known(h_language, known_polish_quotes_languages))
722 h_quotes_language = "polish";
724 else if (is_known(h_language, known_swedish_quotes_languages))
725 h_quotes_language = "swedish";
727 else if (is_known(h_language, known_english_quotes_languages))
728 h_quotes_language = "english";
730 // output the LyX file settings
731 os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
732 << "\\lyxformat " << LYX_FORMAT << '\n'
733 << "\\begin_document\n"
734 << "\\begin_header\n"
735 << "\\textclass " << h_textclass << "\n";
736 if (!h_preamble.str().empty())
737 os << "\\begin_preamble\n" << h_preamble.str() << "\n\\end_preamble\n";
738 if (!h_options.empty())
739 os << "\\options " << h_options << "\n";
740 os << "\\use_default_options " << h_use_default_options << "\n"
741 << modules_placeholder
742 << "\\language " << h_language << "\n"
743 << "\\language_package " << h_language_package << "\n"
744 << "\\inputencoding " << h_inputencoding << "\n"
745 << "\\fontencoding " << h_fontencoding << "\n"
746 << "\\font_roman " << h_font_roman << "\n"
747 << "\\font_sans " << h_font_sans << "\n"
748 << "\\font_typewriter " << h_font_typewriter << "\n"
749 << "\\font_default_family " << h_font_default_family << "\n"
750 << "\\font_sc " << h_font_sc << "\n"
751 << "\\font_osf " << h_font_osf << "\n"
752 << "\\font_sf_scale " << h_font_sf_scale << "\n"
753 << "\\font_tt_scale " << h_font_tt_scale << "\n"
754 << "\\graphics " << h_graphics << "\n";
755 if (!h_float_placement.empty())
756 os << "\\float_placement " << h_float_placement << "\n";
757 os << "\\paperfontsize " << h_paperfontsize << "\n"
758 << "\\spacing " << h_spacing << "\n"
759 << "\\use_hyperref " << h_use_hyperref << '\n';
760 if (h_use_hyperref == "1") {
761 if (!h_pdf_title.empty())
762 os << "\\pdf_title \"" << h_pdf_title << "\"\n";
763 if (!h_pdf_author.empty())
764 os << "\\pdf_author \"" << h_pdf_author << "\"\n";
765 if (!h_pdf_subject.empty())
766 os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
767 if (!h_pdf_keywords.empty())
768 os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
769 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
770 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
771 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
772 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
773 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
774 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
775 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
776 "\\pdf_backref " << h_pdf_backref << "\n"
777 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
778 if (!h_pdf_pagemode.empty())
779 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
780 if (!h_pdf_quoted_options.empty())
781 os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
783 os << "\\papersize " << h_papersize << "\n"
784 << "\\use_geometry " << h_use_geometry << "\n"
785 << "\\use_amsmath " << h_use_amsmath << "\n"
786 << "\\use_esint " << h_use_esint << "\n"
787 << "\\use_mhchem " << h_use_mhchem << "\n"
788 << "\\use_mathdots " << h_use_mathdots << "\n"
789 << "\\use_undertilde " << h_use_undertilde << "\n"
790 << "\\cite_engine " << h_cite_engine << "\n"
791 << "\\use_bibtopic " << h_use_bibtopic << "\n"
792 << "\\paperorientation " << h_paperorientation << '\n'
793 << "\\suppress_date " << h_suppress_date << '\n'
794 << "\\use_refstyle " << h_use_refstyle << '\n';
795 if (!h_fontcolor.empty())
796 os << "\\fontcolor " << h_fontcolor << '\n';
797 if (!h_notefontcolor.empty())
798 os << "\\notefontcolor " << h_notefontcolor << '\n';
799 if (!h_backgroundcolor.empty())
800 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
801 if (!h_boxbgcolor.empty())
802 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
804 << "\\secnumdepth " << h_secnumdepth << "\n"
805 << "\\tocdepth " << h_tocdepth << "\n"
806 << "\\paragraph_separation " << h_paragraph_separation << "\n";
807 if (h_paragraph_separation == "skip")
808 os << "\\defskip " << h_defskip << "\n";
810 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
811 os << "\\quotes_language " << h_quotes_language << "\n"
812 << "\\papercolumns " << h_papercolumns << "\n"
813 << "\\papersides " << h_papersides << "\n"
814 << "\\paperpagestyle " << h_paperpagestyle << "\n";
815 if (!h_listings_params.empty())
816 os << "\\listings_params " << h_listings_params << "\n";
817 os << "\\tracking_changes " << h_tracking_changes << "\n"
818 << "\\output_changes " << h_output_changes << "\n"
819 << "\\html_math_output " << h_html_math_output << "\n"
820 << "\\html_css_as_file " << h_html_css_as_file << "\n"
821 << "\\html_be_strict " << h_html_be_strict << "\n"
822 << "\\end_header\n\n"
824 // clear preamble for subdocuments
828 } // anonymous namespace
831 void parse_preamble(Parser & p, ostream & os,
832 string const & forceclass, TeX2LyXDocClass & tc)
834 // initialize fixed types
835 special_columns['D'] = 3;
836 bool is_full_document = false;
837 bool is_lyx_file = false;
838 bool in_lyx_preamble = false;
840 // determine whether this is a full document or a fragment for inclusion
842 Token const & t = p.get_token();
844 if (t.cat() == catEscape && t.cs() == "documentclass") {
845 is_full_document = true;
851 while (is_full_document && p.good()) {
852 Token const & t = p.get_token();
855 cerr << "t: " << t << "\n";
861 if (!in_lyx_preamble &&
862 (t.cat() == catLetter ||
863 t.cat() == catSuper ||
865 t.cat() == catOther ||
866 t.cat() == catMath ||
867 t.cat() == catActive ||
868 t.cat() == catBegin ||
870 t.cat() == catAlign ||
871 t.cat() == catParameter))
872 h_preamble << t.cs();
874 else if (!in_lyx_preamble &&
875 (t.cat() == catSpace || t.cat() == catNewline))
876 h_preamble << t.asInput();
878 else if (t.cat() == catComment) {
879 static regex const islyxfile("%% LyX .* created this file");
880 static regex const usercommands("User specified LaTeX commands");
882 string const comment = t.asInput();
884 // magically switch encoding default if it looks like XeLaTeX
885 static string const magicXeLaTeX =
886 "% This document must be compiled with XeLaTeX ";
887 if (comment.size() > magicXeLaTeX.size()
888 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
889 && h_inputencoding == "auto") {
890 cerr << "XeLaTeX comment found, switching to UTF8\n";
891 h_inputencoding = "utf8";
894 if (regex_search(comment, sub, islyxfile)) {
896 in_lyx_preamble = true;
897 } else if (is_lyx_file
898 && regex_search(comment, sub, usercommands))
899 in_lyx_preamble = false;
900 else if (!in_lyx_preamble)
901 h_preamble << t.asInput();
904 else if (t.cs() == "pagestyle")
905 h_paperpagestyle = p.verbatim_item();
907 else if (t.cs() == "date") {
908 if (p.verbatim_item().empty())
909 h_suppress_date = "true";
912 else if (t.cs() == "makeatletter") {
913 // LyX takes care of this
914 p.setCatCode('@', catLetter);
917 else if (t.cs() == "makeatother") {
918 // LyX takes care of this
919 p.setCatCode('@', catOther);
922 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
923 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
924 || t.cs() == "providecommand" || t.cs() == "providecommandx"
925 || t.cs() == "DeclareRobustCommand"
926 || t.cs() == "DeclareRobustCommandx"
927 || t.cs() == "ProvideTextCommandDefault"
928 || t.cs() == "DeclareMathAccent") {
930 if (p.next_token().character() == '*') {
934 string const name = p.verbatim_item();
935 string const opt1 = p.getFullOpt();
936 string const opt2 = p.getFullOpt();
937 string const body = p.verbatim_item();
939 if (name == "\\rmdefault")
940 if (is_known(body, known_roman_fonts))
942 if (name == "\\sfdefault")
943 if (is_known(body, known_sans_fonts))
945 if (name == "\\ttdefault")
946 if (is_known(body, known_typewriter_fonts))
947 h_font_typewriter = body;
948 if (name == "\\familydefault") {
949 string family = body;
950 // remove leading "\"
951 h_font_default_family = family.erase(0,1);
954 // Add the command to the known commands
955 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
957 // only non-lyxspecific stuff
958 if (!in_lyx_preamble) {
960 ss << '\\' << t.cs();
963 ss << '{' << name << '}' << opt1 << opt2
964 << '{' << body << "}";
965 h_preamble << ss.str();
967 ostream & out = in_preamble ? h_preamble : os;
968 out << "\\" << t.cs() << "{" << name << "}"
969 << opts << "{" << body << "}";
974 else if (t.cs() == "documentclass") {
975 vector<string>::iterator it;
976 vector<string> opts = split_options(p.getArg('[', ']'));
977 handle_opt(opts, known_fontsizes, h_paperfontsize);
978 delete_opt(opts, known_fontsizes);
979 // delete "pt" at the end
980 string::size_type i = h_paperfontsize.find("pt");
981 if (i != string::npos)
982 h_paperfontsize.erase(i);
983 // The documentclass options are always parsed before the options
984 // of the babel call so that a language cannot overwrite the babel
986 handle_opt(opts, known_languages, h_language);
987 delete_opt(opts, known_languages);
990 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
991 h_paperorientation = "landscape";
995 if ((it = find(opts.begin(), opts.end(), "oneside"))
1000 if ((it = find(opts.begin(), opts.end(), "twoside"))
1006 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1008 h_papercolumns = "1";
1011 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1013 h_papercolumns = "2";
1017 // some size options are know to any document classes, other sizes
1018 // are handled by the \geometry command of the geometry package
1019 handle_opt(opts, known_class_paper_sizes, h_papersize);
1020 delete_opt(opts, known_class_paper_sizes);
1021 // the remaining options
1022 h_options = join(opts, ",");
1023 // FIXME This does not work for classes that have a
1024 // different name in LyX than in LaTeX
1025 h_textclass = p.getArg('{', '}');
1028 else if (t.cs() == "usepackage") {
1029 string const options = p.getArg('[', ']');
1030 string const name = p.getArg('{', '}');
1031 vector<string> vecnames;
1032 split(name, vecnames, ',');
1033 vector<string>::const_iterator it = vecnames.begin();
1034 vector<string>::const_iterator end = vecnames.end();
1035 for (; it != end; ++it)
1036 handle_package(p, trimSpaceAndEol(*it), options,
1040 else if (t.cs() == "inputencoding") {
1041 string const encoding = p.getArg('{','}');
1042 h_inputencoding = encoding;
1043 p.setEncoding(encoding);
1046 else if (t.cs() == "newenvironment") {
1047 string const name = p.getArg('{', '}');
1048 string const opt1 = p.getFullOpt();
1049 string const opt2 = p.getFullOpt();
1050 string const beg = p.verbatim_item();
1051 string const end = p.verbatim_item();
1052 if (!in_lyx_preamble) {
1053 h_preamble << "\\newenvironment{" << name
1054 << '}' << opt1 << opt2 << '{'
1055 << beg << "}{" << end << '}';
1057 add_known_environment(name, opt1, !opt2.empty(),
1058 from_utf8(beg), from_utf8(end));
1062 else if (t.cs() == "def") {
1063 string name = p.get_token().cs();
1064 while (p.next_token().cat() != catBegin)
1065 name += p.get_token().cs();
1066 if (!in_lyx_preamble)
1067 h_preamble << "\\def\\" << name << '{'
1068 << p.verbatim_item() << "}";
1071 else if (t.cs() == "newcolumntype") {
1072 string const name = p.getArg('{', '}');
1073 trimSpaceAndEol(name);
1075 string opts = p.getOpt();
1076 if (!opts.empty()) {
1077 istringstream is(string(opts, 1));
1080 special_columns[name[0]] = nargs;
1081 h_preamble << "\\newcolumntype{" << name << "}";
1083 h_preamble << "[" << nargs << "]";
1084 h_preamble << "{" << p.verbatim_item() << "}";
1087 else if (t.cs() == "setcounter") {
1088 string const name = p.getArg('{', '}');
1089 string const content = p.getArg('{', '}');
1090 if (name == "secnumdepth")
1091 h_secnumdepth = content;
1092 else if (name == "tocdepth")
1093 h_tocdepth = content;
1095 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1098 else if (t.cs() == "setlength") {
1099 string const name = p.verbatim_item();
1100 string const content = p.verbatim_item();
1101 // the paragraphs are only not indented when \parindent is set to zero
1102 if (name == "\\parindent" && content != "") {
1103 if (content[0] == '0')
1104 h_paragraph_separation = "skip";
1106 h_paragraph_indentation = translate_len(content);
1107 } else if (name == "\\parskip") {
1108 if (content == "\\smallskipamount")
1109 h_defskip = "smallskip";
1110 else if (content == "\\medskipamount")
1111 h_defskip = "medskip";
1112 else if (content == "\\bigskipamount")
1113 h_defskip = "bigskip";
1115 h_defskip = content;
1117 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1120 else if (t.cs() == "onehalfspacing")
1121 h_spacing = "onehalf";
1123 else if (t.cs() == "doublespacing")
1124 h_spacing = "double";
1126 else if (t.cs() == "setstretch")
1127 h_spacing = "other " + p.verbatim_item();
1129 else if (t.cs() == "begin") {
1130 string const name = p.getArg('{', '}');
1131 if (name == "document")
1133 h_preamble << "\\begin{" << name << "}";
1136 else if (t.cs() == "geometry") {
1137 h_use_geometry = "true";
1138 vector<string> opts = split_options(p.getArg('{', '}'));
1139 vector<string>::iterator it;
1140 // paper orientation
1141 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1142 h_paperorientation = "landscape";
1146 handle_opt(opts, known_paper_sizes, h_papersize);
1147 delete_opt(opts, known_paper_sizes);
1149 char const * const * margin = known_paper_margins;
1151 for (; *margin; ++margin) {
1153 // search for the "=" in e.g. "lmargin=2cm" to get the value
1154 for(size_t i = 0; i != opts.size(); i++) {
1155 if (opts.at(i).find(*margin) != string::npos) {
1156 string::size_type pos = opts.at(i).find("=");
1157 string value = opts.at(i).substr(pos + 1);
1158 string name = known_coded_paper_margins[k];
1159 h_margins += "\\" + name + " " + value + "\n";
1165 else if (t.cs() == "definecolor") {
1166 string const color = p.getArg('{', '}');
1167 string const space = p.getArg('{', '}');
1168 string const value = p.getArg('{', '}');
1169 if (color == "document_fontcolor" && space == "rgb") {
1170 RGBColor c(RGBColorFromLaTeX(value));
1171 h_fontcolor = X11hexname(c);
1172 } else if (color == "note_fontcolor" && space == "rgb") {
1173 RGBColor c(RGBColorFromLaTeX(value));
1174 h_notefontcolor = X11hexname(c);
1175 } else if (color == "page_backgroundcolor" && space == "rgb") {
1176 RGBColor c(RGBColorFromLaTeX(value));
1177 h_backgroundcolor = X11hexname(c);
1178 } else if (color == "shadecolor" && space == "rgb") {
1179 RGBColor c(RGBColorFromLaTeX(value));
1180 h_boxbgcolor = X11hexname(c);
1182 h_preamble << "\\definecolor{" << color
1183 << "}{" << space << "}{" << value
1188 else if (t.cs() == "jurabibsetup") {
1189 // FIXME p.getArg('{', '}') is most probably wrong (it
1190 // does not handle nested braces).
1191 // Use p.verbatim_item() instead.
1192 vector<string> jurabibsetup =
1193 split_options(p.getArg('{', '}'));
1194 // add jurabibsetup to the jurabib package options
1195 add_package("jurabib", jurabibsetup);
1196 if (!jurabibsetup.empty()) {
1197 h_preamble << "\\jurabibsetup{"
1198 << join(jurabibsetup, ",") << '}';
1202 else if (t.cs() == "hypersetup") {
1203 vector<string> hypersetup =
1204 split_options(p.verbatim_item());
1205 // add hypersetup to the hyperref package options
1206 handle_hyperref(hypersetup);
1207 if (!hypersetup.empty()) {
1208 h_preamble << "\\hypersetup{"
1209 << join(hypersetup, ",") << '}';
1213 else if (is_known(t.cs(), known_if_3arg_commands)) {
1214 // prevent misparsing of \usepackage if it is used
1215 // as an argument (see e.g. our own output of
1216 // \@ifundefined above)
1217 string const arg1 = p.verbatim_item();
1218 string const arg2 = p.verbatim_item();
1219 string const arg3 = p.verbatim_item();
1220 // test case \@ifundefined{date}{}{\date{}}
1221 if (arg1 == "date" && arg2.empty() && arg3 == "\\date{}") {
1222 h_suppress_date = "true";
1223 // test case \@ifundefined{definecolor}{\usepackage{color}}{}
1224 // because we could pollute the preamble with it in roundtrips
1225 } else if (arg1 == "definecolor" && arg2 == "\\usepackage{color}"
1227 ifundefined_color_set = true;
1228 } else if (!in_lyx_preamble) {
1229 h_preamble << t.asInput()
1230 << '{' << arg1 << '}'
1231 << '{' << arg2 << '}'
1232 << '{' << arg3 << '}';
1236 else if (is_known(t.cs(), known_if_commands)) {
1237 // must not parse anything in conditional code, since
1238 // LyX would output the parsed contents unconditionally
1239 if (!in_lyx_preamble)
1240 h_preamble << t.asInput();
1241 handle_if(p, in_lyx_preamble);
1244 else if (!t.cs().empty() && !in_lyx_preamble)
1245 h_preamble << '\\' << t.cs();
1248 // remove the whitespace
1251 // Force textclass if the user wanted it
1252 if (!forceclass.empty())
1253 h_textclass = forceclass;
1254 if (noweb_mode && !prefixIs(h_textclass, "literate-"))
1255 h_textclass.insert(0, "literate-");
1256 tc.setName(h_textclass);
1258 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1261 if (h_papersides.empty()) {
1264 h_papersides = ss.str();
1266 end_preamble(os, tc);
1270 /// translates a babel language name to a LyX language name
1271 string babel2lyx(string const & language)
1273 char const * const * where = is_known(language, known_languages);
1275 return known_coded_languages[where - known_languages];