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", "beraserif", "bookman",
141 "ccfonts", "chancery", "charter", "cmr", "fourier", "lmodern", "mathpazo",
142 "mathptmx", "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 const char * const known_basic_colors[] = {"blue", "black", "cyan", "green",
173 "magenta", "red", "white", "yellow", 0};
175 const char * const known_basic_color_codes[] = {"#0000ff", "#000000", "#00ffff", "#00ff00",
176 "#ff00ff", "#ff0000", "#ffffff", "#ffff00", 0};
178 /// conditional commands with three arguments like \@ifundefined{}{}{}
179 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
183 ostringstream h_preamble;
184 string h_textclass = "article";
185 string h_use_default_options = "false";
187 string h_language = "english";
188 string h_language_package = "none";
189 string h_fontencoding = "default";
190 string h_font_roman = "default";
191 string h_font_sans = "default";
192 string h_font_typewriter = "default";
193 string h_font_default_family = "default";
194 string h_font_sc = "false";
195 string h_font_osf = "false";
196 string h_font_sf_scale = "100";
197 string h_font_tt_scale = "100";
198 string h_graphics = "default";
199 string h_float_placement;
200 string h_paperfontsize = "default";
201 string h_spacing = "single";
202 string h_use_hyperref = "0";
205 string h_pdf_subject;
206 string h_pdf_keywords;
207 string h_pdf_bookmarks = "1";
208 string h_pdf_bookmarksnumbered = "0";
209 string h_pdf_bookmarksopen = "0";
210 string h_pdf_bookmarksopenlevel = "1";
211 string h_pdf_breaklinks = "0";
212 string h_pdf_pdfborder = "0";
213 string h_pdf_colorlinks = "0";
214 string h_pdf_backref = "section";
215 string h_pdf_pdfusetitle = "1";
216 string h_pdf_pagemode;
217 string h_pdf_quoted_options;
218 string h_papersize = "default";
219 string h_use_geometry = "false";
220 string h_use_amsmath = "1";
221 string h_use_esint = "1";
222 string h_use_mhchem = "0";
223 string h_use_mathdots = "0";
224 string h_use_undertilde = "0";
225 string h_cite_engine = "basic";
226 string h_use_bibtopic = "false";
227 string h_paperorientation = "portrait";
228 string h_suppress_date = "false";
229 string h_use_refstyle = "0";
230 string h_backgroundcolor;
233 string h_notefontcolor;
234 string h_secnumdepth = "3";
235 string h_tocdepth = "3";
236 string h_defskip = "medskip";
237 string h_paragraph_indentation = "default";
238 string h_quotes_language = "english";
239 string h_papercolumns = "1";
241 string h_paperpagestyle = "default";
242 string h_listings_params;
243 string h_tracking_changes = "false";
244 string h_output_changes = "false";
245 string h_html_math_output = "0";
246 string h_html_css_as_file = "0";
247 string h_html_be_strict = "false";
251 // returns true if at least one of the options in what has been found
252 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
258 // the last language option is the document language (for babel and LyX)
259 // the last size option is the document font size
260 vector<string>::iterator it;
261 vector<string>::iterator position = opts.begin();
262 for (; *what; ++what) {
263 it = find(opts.begin(), opts.end(), *what);
264 if (it != opts.end()) {
265 if (it >= position) {
276 void delete_opt(vector<string> & opts, char const * const * what)
281 // remove found options from the list
282 // do this after handle_opt to avoid potential memory leaks
283 vector<string>::iterator it;
284 for (; *what; ++what) {
285 it = find(opts.begin(), opts.end(), *what);
286 if (it != opts.end())
293 * Split a package options string (keyval format) into a vector.
295 * authorformat=smallcaps,
297 * titleformat=colonsep,
298 * bibformat={tabular,ibidem,numbered}
300 vector<string> split_options(string const & input)
302 vector<string> options;
306 Token const & t = p.get_token();
307 if (t.asInput() == ",") {
308 options.push_back(trimSpaceAndEol(option));
310 } else if (t.asInput() == "=") {
313 if (p.next_token().asInput() == "{")
314 option += '{' + p.getArg('{', '}') + '}';
315 } else if (t.cat() != catSpace)
316 option += t.asInput();
320 options.push_back(trimSpaceAndEol(option));
327 * Retrieve a keyval option "name={value with=sign}" named \p name from
328 * \p options and return the value.
329 * The found option is also removed from \p options.
331 string process_keyval_opt(vector<string> & options, string name)
333 for (size_t i = 0; i < options.size(); ++i) {
334 vector<string> option;
335 split(options[i], option, '=');
336 if (option.size() < 2)
338 if (option[0] == name) {
339 options.erase(options.begin() + i);
340 option.erase(option.begin());
341 return join(option, "=");
349 * Add package \p name with options \p options to used_packages.
350 * Remove options from \p options that we don't want to output.
352 void add_package(string const & name, vector<string> & options)
354 // every package inherits the global options
355 if (used_packages.find(name) == used_packages.end())
356 used_packages[name] = split_options(h_options);
358 vector<string> & v = used_packages[name];
359 v.insert(v.end(), options.begin(), options.end());
360 if (name == "jurabib") {
361 // Don't output the order argument (see the cite command
362 // handling code in text.cpp).
363 vector<string>::iterator end =
364 remove(options.begin(), options.end(), "natbiborder");
365 end = remove(options.begin(), end, "jurabiborder");
366 options.erase(end, options.end());
371 // Given is a string like "scaled=0.9", return 0.9 * 100
372 string const scale_as_percentage(string const & scale)
374 string::size_type pos = scale.find('=');
375 if (pos != string::npos) {
376 string value = scale.substr(pos + 1);
378 return convert<string>(100 * convert<double>(value));
380 // If the input string didn't match our expectations.
381 // return the default value "100"
386 string remove_braces(string const & value)
390 if (value[0] == '{' && value[value.length()-1] == '}')
391 return value.substr(1, value.length()-2);
396 void handle_hyperref(vector<string> & options)
398 // FIXME swallow inputencoding changes that might surround the
399 // hyperref setup if it was written by LyX
400 h_use_hyperref = "1";
401 // swallow "unicode=true", since LyX does always write that
402 vector<string>::iterator it =
403 find(options.begin(), options.end(), "unicode=true");
404 if (it != options.end())
406 it = find(options.begin(), options.end(), "pdfusetitle");
407 if (it != options.end()) {
408 h_pdf_pdfusetitle = "1";
411 string bookmarks = process_keyval_opt(options, "bookmarks");
412 if (bookmarks == "true")
413 h_pdf_bookmarks = "1";
414 else if (bookmarks == "false")
415 h_pdf_bookmarks = "0";
416 if (h_pdf_bookmarks == "1") {
417 string bookmarksnumbered =
418 process_keyval_opt(options, "bookmarksnumbered");
419 if (bookmarksnumbered == "true")
420 h_pdf_bookmarksnumbered = "1";
421 else if (bookmarksnumbered == "false")
422 h_pdf_bookmarksnumbered = "0";
423 string bookmarksopen =
424 process_keyval_opt(options, "bookmarksopen");
425 if (bookmarksopen == "true")
426 h_pdf_bookmarksopen = "1";
427 else if (bookmarksopen == "false")
428 h_pdf_bookmarksopen = "0";
429 if (h_pdf_bookmarksopen == "1") {
430 string bookmarksopenlevel =
431 process_keyval_opt(options, "bookmarksopenlevel");
432 if (!bookmarksopenlevel.empty())
433 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
436 string breaklinks = process_keyval_opt(options, "breaklinks");
437 if (breaklinks == "true")
438 h_pdf_breaklinks = "1";
439 else if (breaklinks == "false")
440 h_pdf_breaklinks = "0";
441 string pdfborder = process_keyval_opt(options, "pdfborder");
442 if (pdfborder == "{0 0 0}")
443 h_pdf_pdfborder = "1";
444 else if (pdfborder == "{0 0 1}")
445 h_pdf_pdfborder = "0";
446 string backref = process_keyval_opt(options, "backref");
447 if (!backref.empty())
448 h_pdf_backref = backref;
449 string colorlinks = process_keyval_opt(options, "colorlinks");
450 if (colorlinks == "true")
451 h_pdf_colorlinks = "1";
452 else if (colorlinks == "false")
453 h_pdf_colorlinks = "0";
454 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
455 if (!pdfpagemode.empty())
456 h_pdf_pagemode = pdfpagemode;
457 string pdftitle = process_keyval_opt(options, "pdftitle");
458 if (!pdftitle.empty()) {
459 h_pdf_title = remove_braces(pdftitle);
461 string pdfauthor = process_keyval_opt(options, "pdfauthor");
462 if (!pdfauthor.empty()) {
463 h_pdf_author = remove_braces(pdfauthor);
465 string pdfsubject = process_keyval_opt(options, "pdfsubject");
466 if (!pdfsubject.empty())
467 h_pdf_subject = remove_braces(pdfsubject);
468 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
469 if (!pdfkeywords.empty())
470 h_pdf_keywords = remove_braces(pdfkeywords);
471 if (!options.empty()) {
472 if (!h_pdf_quoted_options.empty())
473 h_pdf_quoted_options += ',';
474 h_pdf_quoted_options += join(options, ",");
480 void handle_package(Parser &p, string const & name, string const & opts,
481 bool in_lyx_preamble)
483 vector<string> options = split_options(opts);
484 add_package(name, options);
488 if (is_known(name, known_roman_fonts)) {
493 if (name == "fourier") {
494 h_font_roman = "utopia";
495 // when font uses real small capitals
496 if (opts == "expert")
500 if (name == "mathpazo")
501 h_font_roman = "palatino";
503 if (name == "mathptmx")
504 h_font_roman = "times";
507 if (is_known(name, known_sans_fonts)) {
511 h_font_sf_scale = scale_as_percentage(scale);
516 if (is_known(name, known_typewriter_fonts)) {
517 // fourier can be set as roman font _only_
518 // fourier as typewriter is handled in handling of \ttdefault
519 if (name != "fourier") {
520 h_font_typewriter = name;
523 h_font_tt_scale = scale_as_percentage(scale);
528 // font uses old-style figure
532 // after the detection and handling of special cases, we can remove the
533 // fonts, otherwise they would appear in the preamble, see bug #7856
534 if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
535 || is_known(name, known_typewriter_fonts))
538 else if (name == "amsmath" || name == "amssymb")
541 else if (name == "esint")
544 else if (name == "mhchem")
547 else if (name == "mathdots")
548 h_use_mathdots = "2";
550 else if (name == "undertilde")
551 h_use_undertilde = "2";
553 else if (name == "babel") {
554 h_language_package = "default";
555 // we have to do nothing if babel is loaded without any options, otherwise
556 // we would pollute the preamble with this call in every roundtrip
558 // check if more than one option was used - used later for inputenc
559 // in case inputenc is parsed before babel, set the encoding to auto
560 if (options.begin() != options.end() - 1) {
561 one_language = false;
562 h_inputencoding = "auto";
564 // babel takes the last language of the option of its \usepackage
565 // call as document language. If there is no such language option, the
566 // last language in the documentclass options is used.
567 handle_opt(options, known_languages, h_language);
568 delete_opt(options, known_languages);
572 else if (name == "fontenc") {
573 h_fontencoding = getStringFromVector(options, ",");
574 /* We could do the following for better round trip support,
575 * but this makes the document less portable, so I skip it:
576 if (h_fontencoding == lyxrc.fontenc)
577 h_fontencoding = "global";
582 else if (name == "inputenc" || name == "luainputenc") {
583 // h_inputencoding is only set when there is not more than one
584 // inputenc option because otherwise h_inputencoding must be
585 // set to "auto" (the default encoding of the document language)
586 // Therefore check for the "," character.
587 // It is also only set when there is not more then one babel
588 // language option but this is handled in the routine for babel.
589 if (opts.find(",") == string::npos && one_language == true)
590 h_inputencoding = opts;
591 if (!options.empty())
592 p.setEncoding(options.back());
596 else if (is_known(name, known_old_language_packages)) {
597 // known language packages from the times before babel
598 // if they are found and not also babel, they will be used as
599 // cutom language package
600 h_language_package = "\\usepackage{" + name + "}";
603 else if (name == "makeidx")
606 else if (name == "prettyref")
609 else if (name == "varioref")
612 else if (name == "verbatim")
615 else if (name == "nomencl")
618 else if (name == "textcomp")
621 else if (name == "url")
624 else if (name == "subscript")
627 else if (name == "color") {
628 // with the following command this package is only loaded when needed for
629 // undefined colors, since we only support the predefined colors
630 // only add it if not yet added
631 if (!ifundefined_color_set)
632 h_preamble << "\\@ifundefined{definecolor}\n {\\usepackage{color}}{}\n";
635 else if (name == "graphicx")
638 else if (name == "setspace")
642 // do not ignore as long as we don't support all commands (e.g. \xout is missing)
643 else if (name == "ulem")
647 else if (name == "geometry")
648 ; // Ignore this, the geometry settings are made by the \geometry
649 // command. This command is handled below.
651 else if (name == "rotfloat")
654 else if (name == "wrapfig")
657 else if (name == "subfig")
660 else if (is_known(name, known_languages))
663 else if (name == "natbib") {
664 h_cite_engine = "natbib_authoryear";
665 vector<string>::iterator it =
666 find(options.begin(), options.end(), "authoryear");
667 if (it != options.end())
670 it = find(options.begin(), options.end(), "numbers");
671 if (it != options.end()) {
672 h_cite_engine = "natbib_numerical";
678 else if (name == "jurabib")
679 h_cite_engine = "jurabib";
681 else if (name == "hyperref")
682 handle_hyperref(options);
684 else if (!in_lyx_preamble) {
686 h_preamble << "\\usepackage{" << name << "}";
688 h_preamble << "\\usepackage[" << opts << "]{"
694 // We need to do something with the options...
695 if (!options.empty())
696 cerr << "Ignoring options '" << join(options, ",")
697 << "' of package " << name << '.' << endl;
699 // remove the whitespace
704 void handle_if(Parser & p, bool in_lyx_preamble)
707 Token t = p.get_token();
708 if (t.cat() == catEscape &&
709 is_known(t.cs(), known_if_commands))
710 handle_if(p, in_lyx_preamble);
712 if (!in_lyx_preamble)
713 h_preamble << t.asInput();
714 if (t.cat() == catEscape && t.cs() == "fi")
721 void end_preamble(ostream & os, TextClass const & /*textclass*/)
723 // translate from babel to LyX names
724 h_language = babel2lyx(h_language);
726 // set the quote language
727 // LyX only knows the following quotes languages:
728 // english, swedish, german, polish, french and danish
729 // (quotes for "japanese" and "chinese-traditional" are missing because
730 // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
731 // conversion list taken from
732 // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
733 // (quotes for kazakh and interlingua are unknown)
735 if (h_language == "danish")
736 h_quotes_language = "danish";
738 else if (is_known(h_language, known_french_quotes_languages))
739 h_quotes_language = "french";
741 else if (is_known(h_language, known_german_quotes_languages))
742 h_quotes_language = "german";
744 else if (is_known(h_language, known_polish_quotes_languages))
745 h_quotes_language = "polish";
747 else if (is_known(h_language, known_swedish_quotes_languages))
748 h_quotes_language = "swedish";
750 else if (is_known(h_language, known_english_quotes_languages))
751 h_quotes_language = "english";
753 // output the LyX file settings
754 os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
755 << "\\lyxformat " << LYX_FORMAT << '\n'
756 << "\\begin_document\n"
757 << "\\begin_header\n"
758 << "\\textclass " << h_textclass << "\n";
759 if (!h_preamble.str().empty())
760 os << "\\begin_preamble\n" << h_preamble.str() << "\n\\end_preamble\n";
761 if (!h_options.empty())
762 os << "\\options " << h_options << "\n";
763 os << "\\use_default_options " << h_use_default_options << "\n"
764 << modules_placeholder
765 << "\\language " << h_language << "\n"
766 << "\\language_package " << h_language_package << "\n"
767 << "\\inputencoding " << h_inputencoding << "\n"
768 << "\\fontencoding " << h_fontencoding << "\n"
769 << "\\font_roman " << h_font_roman << "\n"
770 << "\\font_sans " << h_font_sans << "\n"
771 << "\\font_typewriter " << h_font_typewriter << "\n"
772 << "\\font_default_family " << h_font_default_family << "\n"
773 << "\\font_sc " << h_font_sc << "\n"
774 << "\\font_osf " << h_font_osf << "\n"
775 << "\\font_sf_scale " << h_font_sf_scale << "\n"
776 << "\\font_tt_scale " << h_font_tt_scale << "\n"
777 << "\\graphics " << h_graphics << "\n";
778 if (!h_float_placement.empty())
779 os << "\\float_placement " << h_float_placement << "\n";
780 os << "\\paperfontsize " << h_paperfontsize << "\n"
781 << "\\spacing " << h_spacing << "\n"
782 << "\\use_hyperref " << h_use_hyperref << '\n';
783 if (h_use_hyperref == "1") {
784 if (!h_pdf_title.empty())
785 os << "\\pdf_title \"" << h_pdf_title << "\"\n";
786 if (!h_pdf_author.empty())
787 os << "\\pdf_author \"" << h_pdf_author << "\"\n";
788 if (!h_pdf_subject.empty())
789 os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
790 if (!h_pdf_keywords.empty())
791 os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
792 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
793 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
794 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
795 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
796 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
797 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
798 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
799 "\\pdf_backref " << h_pdf_backref << "\n"
800 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
801 if (!h_pdf_pagemode.empty())
802 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
803 if (!h_pdf_quoted_options.empty())
804 os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
806 os << "\\papersize " << h_papersize << "\n"
807 << "\\use_geometry " << h_use_geometry << "\n"
808 << "\\use_amsmath " << h_use_amsmath << "\n"
809 << "\\use_esint " << h_use_esint << "\n"
810 << "\\use_mhchem " << h_use_mhchem << "\n"
811 << "\\use_mathdots " << h_use_mathdots << "\n"
812 << "\\use_undertilde " << h_use_undertilde << "\n"
813 << "\\cite_engine " << h_cite_engine << "\n"
814 << "\\use_bibtopic " << h_use_bibtopic << "\n"
815 << "\\paperorientation " << h_paperorientation << '\n'
816 << "\\suppress_date " << h_suppress_date << '\n'
817 << "\\use_refstyle " << h_use_refstyle << '\n';
818 if (!h_fontcolor.empty())
819 os << "\\fontcolor " << h_fontcolor << '\n';
820 if (!h_notefontcolor.empty())
821 os << "\\notefontcolor " << h_notefontcolor << '\n';
822 if (!h_backgroundcolor.empty())
823 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
824 if (!h_boxbgcolor.empty())
825 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
827 << "\\secnumdepth " << h_secnumdepth << "\n"
828 << "\\tocdepth " << h_tocdepth << "\n"
829 << "\\paragraph_separation " << h_paragraph_separation << "\n";
830 if (h_paragraph_separation == "skip")
831 os << "\\defskip " << h_defskip << "\n";
833 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
834 os << "\\quotes_language " << h_quotes_language << "\n"
835 << "\\papercolumns " << h_papercolumns << "\n"
836 << "\\papersides " << h_papersides << "\n"
837 << "\\paperpagestyle " << h_paperpagestyle << "\n";
838 if (!h_listings_params.empty())
839 os << "\\listings_params " << h_listings_params << "\n";
840 os << "\\tracking_changes " << h_tracking_changes << "\n"
841 << "\\output_changes " << h_output_changes << "\n"
842 << "\\html_math_output " << h_html_math_output << "\n"
843 << "\\html_css_as_file " << h_html_css_as_file << "\n"
844 << "\\html_be_strict " << h_html_be_strict << "\n"
845 << "\\end_header\n\n"
847 // clear preamble for subdocuments
851 } // anonymous namespace
854 void parse_preamble(Parser & p, ostream & os,
855 string const & forceclass, TeX2LyXDocClass & tc)
857 // initialize fixed types
858 special_columns['D'] = 3;
859 bool is_full_document = false;
860 bool is_lyx_file = false;
861 bool in_lyx_preamble = false;
863 // determine whether this is a full document or a fragment for inclusion
865 Token const & t = p.get_token();
867 if (t.cat() == catEscape && t.cs() == "documentclass") {
868 is_full_document = true;
874 while (is_full_document && p.good()) {
875 Token const & t = p.get_token();
878 cerr << "t: " << t << "\n";
884 if (!in_lyx_preamble &&
885 (t.cat() == catLetter ||
886 t.cat() == catSuper ||
888 t.cat() == catOther ||
889 t.cat() == catMath ||
890 t.cat() == catActive ||
891 t.cat() == catBegin ||
893 t.cat() == catAlign ||
894 t.cat() == catParameter))
895 h_preamble << t.cs();
897 else if (!in_lyx_preamble &&
898 (t.cat() == catSpace || t.cat() == catNewline))
899 h_preamble << t.asInput();
901 else if (t.cat() == catComment) {
902 static regex const islyxfile("%% LyX .* created this file");
903 static regex const usercommands("User specified LaTeX commands");
905 string const comment = t.asInput();
907 // magically switch encoding default if it looks like XeLaTeX
908 static string const magicXeLaTeX =
909 "% This document must be compiled with XeLaTeX ";
910 if (comment.size() > magicXeLaTeX.size()
911 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
912 && h_inputencoding == "auto") {
913 cerr << "XeLaTeX comment found, switching to UTF8\n";
914 h_inputencoding = "utf8";
917 if (regex_search(comment, sub, islyxfile)) {
919 in_lyx_preamble = true;
920 } else if (is_lyx_file
921 && regex_search(comment, sub, usercommands))
922 in_lyx_preamble = false;
923 else if (!in_lyx_preamble)
924 h_preamble << t.asInput();
927 else if (t.cs() == "pagestyle")
928 h_paperpagestyle = p.verbatim_item();
930 else if (t.cs() == "date") {
931 string argument = p.getArg('{', '}');
932 if (argument.empty())
933 h_suppress_date = "true";
935 h_preamble << t.asInput() << '{' << argument << '}';
938 else if (t.cs() == "color") {
939 string argument = p.getArg('{', '}');
940 // check the case that a standard color is used
941 if (is_known(argument, known_basic_colors))
942 h_fontcolor = color2code(argument);
943 // check the case that LyX's document_fontcolor is defined
944 // but not used for \color
945 if (argument != "document_fontcolor"
946 && !is_known(argument, known_basic_colors)) {
947 h_preamble << t.asInput() << '{' << argument << '}';
948 // the color might already be set because \definecolor
949 // is parsed before this
954 else if (t.cs() == "pagecolor") {
955 string argument = p.getArg('{', '}');
956 // check the case that a standard color is used
957 if (is_known(argument, known_basic_colors))
958 h_backgroundcolor = color2code(argument);
959 // check the case that LyX's page_backgroundcolor is defined
960 // but not used for \pagecolor
961 if (argument != "page_backgroundcolor"
962 && !is_known(argument, known_basic_colors)) {
963 h_preamble << t.asInput() << '{' << argument << '}';
964 // the color might already be set because \definecolor
965 // is parsed before this
966 h_backgroundcolor = "";
970 else if (t.cs() == "makeatletter") {
971 // LyX takes care of this
972 p.setCatCode('@', catLetter);
975 else if (t.cs() == "makeatother") {
976 // LyX takes care of this
977 p.setCatCode('@', catOther);
980 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
981 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
982 || t.cs() == "providecommand" || t.cs() == "providecommandx"
983 || t.cs() == "DeclareRobustCommand"
984 || t.cs() == "DeclareRobustCommandx"
985 || t.cs() == "ProvideTextCommandDefault"
986 || t.cs() == "DeclareMathAccent") {
988 if (p.next_token().character() == '*') {
992 string const name = p.verbatim_item();
993 string const opt1 = p.getFullOpt();
994 string const opt2 = p.getFullOpt();
995 string const body = p.verbatim_item();
997 if (name == "\\rmdefault")
998 if (is_known(body, known_roman_fonts))
1000 if (name == "\\sfdefault")
1001 if (is_known(body, known_sans_fonts))
1003 if (name == "\\ttdefault")
1004 if (is_known(body, known_typewriter_fonts))
1005 h_font_typewriter = body;
1006 if (name == "\\familydefault") {
1007 string family = body;
1008 // remove leading "\"
1009 h_font_default_family = family.erase(0,1);
1012 // Add the command to the known commands
1013 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1015 // only non-lyxspecific stuff
1016 if (!in_lyx_preamble) {
1018 ss << '\\' << t.cs();
1021 ss << '{' << name << '}' << opt1 << opt2
1022 << '{' << body << "}";
1023 h_preamble << ss.str();
1025 ostream & out = in_preamble ? h_preamble : os;
1026 out << "\\" << t.cs() << "{" << name << "}"
1027 << opts << "{" << body << "}";
1032 else if (t.cs() == "documentclass") {
1033 vector<string>::iterator it;
1034 vector<string> opts = split_options(p.getArg('[', ']'));
1035 handle_opt(opts, known_fontsizes, h_paperfontsize);
1036 delete_opt(opts, known_fontsizes);
1037 // delete "pt" at the end
1038 string::size_type i = h_paperfontsize.find("pt");
1039 if (i != string::npos)
1040 h_paperfontsize.erase(i);
1041 // The documentclass options are always parsed before the options
1042 // of the babel call so that a language cannot overwrite the babel
1044 handle_opt(opts, known_languages, h_language);
1045 delete_opt(opts, known_languages);
1047 // paper orientation
1048 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1049 h_paperorientation = "landscape";
1053 if ((it = find(opts.begin(), opts.end(), "oneside"))
1058 if ((it = find(opts.begin(), opts.end(), "twoside"))
1064 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1066 h_papercolumns = "1";
1069 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1071 h_papercolumns = "2";
1075 // some size options are know to any document classes, other sizes
1076 // are handled by the \geometry command of the geometry package
1077 handle_opt(opts, known_class_paper_sizes, h_papersize);
1078 delete_opt(opts, known_class_paper_sizes);
1079 // the remaining options
1080 h_options = join(opts, ",");
1081 // FIXME This does not work for classes that have a
1082 // different name in LyX than in LaTeX
1083 h_textclass = p.getArg('{', '}');
1086 else if (t.cs() == "usepackage") {
1087 string const options = p.getArg('[', ']');
1088 string const name = p.getArg('{', '}');
1089 vector<string> vecnames;
1090 split(name, vecnames, ',');
1091 vector<string>::const_iterator it = vecnames.begin();
1092 vector<string>::const_iterator end = vecnames.end();
1093 for (; it != end; ++it)
1094 handle_package(p, trimSpaceAndEol(*it), options,
1098 else if (t.cs() == "inputencoding") {
1099 string const encoding = p.getArg('{','}');
1100 h_inputencoding = encoding;
1101 p.setEncoding(encoding);
1104 else if (t.cs() == "newenvironment") {
1105 string const name = p.getArg('{', '}');
1106 string const opt1 = p.getFullOpt();
1107 string const opt2 = p.getFullOpt();
1108 string const beg = p.verbatim_item();
1109 string const end = p.verbatim_item();
1110 if (!in_lyx_preamble) {
1111 h_preamble << "\\newenvironment{" << name
1112 << '}' << opt1 << opt2 << '{'
1113 << beg << "}{" << end << '}';
1115 add_known_environment(name, opt1, !opt2.empty(),
1116 from_utf8(beg), from_utf8(end));
1120 else if (t.cs() == "def") {
1121 string name = p.get_token().cs();
1122 while (p.next_token().cat() != catBegin)
1123 name += p.get_token().cs();
1124 if (!in_lyx_preamble)
1125 h_preamble << "\\def\\" << name << '{'
1126 << p.verbatim_item() << "}";
1129 else if (t.cs() == "newcolumntype") {
1130 string const name = p.getArg('{', '}');
1131 trimSpaceAndEol(name);
1133 string opts = p.getOpt();
1134 if (!opts.empty()) {
1135 istringstream is(string(opts, 1));
1138 special_columns[name[0]] = nargs;
1139 h_preamble << "\\newcolumntype{" << name << "}";
1141 h_preamble << "[" << nargs << "]";
1142 h_preamble << "{" << p.verbatim_item() << "}";
1145 else if (t.cs() == "setcounter") {
1146 string const name = p.getArg('{', '}');
1147 string const content = p.getArg('{', '}');
1148 if (name == "secnumdepth")
1149 h_secnumdepth = content;
1150 else if (name == "tocdepth")
1151 h_tocdepth = content;
1153 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1156 else if (t.cs() == "setlength") {
1157 string const name = p.verbatim_item();
1158 string const content = p.verbatim_item();
1159 // the paragraphs are only not indented when \parindent is set to zero
1160 if (name == "\\parindent" && content != "") {
1161 if (content[0] == '0')
1162 h_paragraph_separation = "skip";
1164 h_paragraph_indentation = translate_len(content);
1165 } else if (name == "\\parskip") {
1166 if (content == "\\smallskipamount")
1167 h_defskip = "smallskip";
1168 else if (content == "\\medskipamount")
1169 h_defskip = "medskip";
1170 else if (content == "\\bigskipamount")
1171 h_defskip = "bigskip";
1173 h_defskip = content;
1175 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1178 else if (t.cs() == "onehalfspacing")
1179 h_spacing = "onehalf";
1181 else if (t.cs() == "doublespacing")
1182 h_spacing = "double";
1184 else if (t.cs() == "setstretch")
1185 h_spacing = "other " + p.verbatim_item();
1187 else if (t.cs() == "begin") {
1188 string const name = p.getArg('{', '}');
1189 if (name == "document")
1191 h_preamble << "\\begin{" << name << "}";
1194 else if (t.cs() == "geometry") {
1195 h_use_geometry = "true";
1196 vector<string> opts = split_options(p.getArg('{', '}'));
1197 vector<string>::iterator it;
1198 // paper orientation
1199 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1200 h_paperorientation = "landscape";
1204 handle_opt(opts, known_paper_sizes, h_papersize);
1205 delete_opt(opts, known_paper_sizes);
1207 char const * const * margin = known_paper_margins;
1209 for (; *margin; ++margin) {
1211 // search for the "=" in e.g. "lmargin=2cm" to get the value
1212 for(size_t i = 0; i != opts.size(); i++) {
1213 if (opts.at(i).find(*margin) != string::npos) {
1214 string::size_type pos = opts.at(i).find("=");
1215 string value = opts.at(i).substr(pos + 1);
1216 string name = known_coded_paper_margins[k];
1217 h_margins += "\\" + name + " " + value + "\n";
1223 else if (t.cs() == "definecolor") {
1224 string const color = p.getArg('{', '}');
1225 string const space = p.getArg('{', '}');
1226 string const value = p.getArg('{', '}');
1227 if (color == "document_fontcolor" && space == "rgb") {
1228 RGBColor c(RGBColorFromLaTeX(value));
1229 h_fontcolor = X11hexname(c);
1230 } else if (color == "note_fontcolor" && space == "rgb") {
1231 RGBColor c(RGBColorFromLaTeX(value));
1232 h_notefontcolor = X11hexname(c);
1233 } else if (color == "page_backgroundcolor" && space == "rgb") {
1234 RGBColor c(RGBColorFromLaTeX(value));
1235 h_backgroundcolor = X11hexname(c);
1236 } else if (color == "shadecolor" && space == "rgb") {
1237 RGBColor c(RGBColorFromLaTeX(value));
1238 h_boxbgcolor = X11hexname(c);
1240 h_preamble << "\\definecolor{" << color
1241 << "}{" << space << "}{" << value
1246 else if (t.cs() == "jurabibsetup") {
1247 // FIXME p.getArg('{', '}') is most probably wrong (it
1248 // does not handle nested braces).
1249 // Use p.verbatim_item() instead.
1250 vector<string> jurabibsetup =
1251 split_options(p.getArg('{', '}'));
1252 // add jurabibsetup to the jurabib package options
1253 add_package("jurabib", jurabibsetup);
1254 if (!jurabibsetup.empty()) {
1255 h_preamble << "\\jurabibsetup{"
1256 << join(jurabibsetup, ",") << '}';
1260 else if (t.cs() == "hypersetup") {
1261 vector<string> hypersetup =
1262 split_options(p.verbatim_item());
1263 // add hypersetup to the hyperref package options
1264 handle_hyperref(hypersetup);
1265 if (!hypersetup.empty()) {
1266 h_preamble << "\\hypersetup{"
1267 << join(hypersetup, ",") << '}';
1271 else if (is_known(t.cs(), known_if_3arg_commands)) {
1272 // prevent misparsing of \usepackage if it is used
1273 // as an argument (see e.g. our own output of
1274 // \@ifundefined above)
1275 string const arg1 = p.verbatim_item();
1276 string const arg2 = p.verbatim_item();
1277 string const arg3 = p.verbatim_item();
1278 // test case \@ifundefined{date}{}{\date{}}
1279 if (arg1 == "date" && arg2.empty() && arg3 == "\\date{}") {
1280 h_suppress_date = "true";
1281 // test case \@ifundefined{definecolor}{\usepackage{color}}{}
1282 // because we could pollute the preamble with it in roundtrips
1283 } else if (arg1 == "definecolor" && arg2 == "\\usepackage{color}"
1285 ifundefined_color_set = true;
1287 //\@ifundefined{showcaptionsetup}{}{%
1288 // \PassOptionsToPackage{caption=false}{subfig}}
1289 // that LyX uses for subfloats
1290 } else if (arg1 == "showcaptionsetup" && arg2.empty()
1291 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1293 } else if (!in_lyx_preamble) {
1294 h_preamble << t.asInput()
1295 << '{' << arg1 << '}'
1296 << '{' << arg2 << '}'
1297 << '{' << arg3 << '}';
1301 else if (is_known(t.cs(), known_if_commands)) {
1302 // must not parse anything in conditional code, since
1303 // LyX would output the parsed contents unconditionally
1304 if (!in_lyx_preamble)
1305 h_preamble << t.asInput();
1306 handle_if(p, in_lyx_preamble);
1309 else if (!t.cs().empty() && !in_lyx_preamble)
1310 h_preamble << '\\' << t.cs();
1313 // remove the whitespace
1316 // Force textclass if the user wanted it
1317 if (!forceclass.empty())
1318 h_textclass = forceclass;
1319 if (noweb_mode && !prefixIs(h_textclass, "literate-"))
1320 h_textclass.insert(0, "literate-");
1321 tc.setName(h_textclass);
1323 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1326 if (h_papersides.empty()) {
1329 h_papersides = ss.str();
1331 end_preamble(os, tc);
1335 /// translates a babel language name to a LyX language name
1336 string babel2lyx(string const & language)
1338 char const * const * where = is_known(language, known_languages);
1340 return known_coded_languages[where - known_languages];
1345 /// translates a color name to a LyX color code
1346 string color2code(string const & name)
1348 char const * const * where = is_known(name, known_basic_colors);
1350 return known_basic_color_codes[where - known_basic_colors];