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.
19 #include "LayoutFile.h"
22 #include "TextClass.h"
24 #include "support/convert.h"
25 #include "support/FileName.h"
26 #include "support/filetools.h"
27 #include "support/lstrings.h"
29 #include "support/regex.h"
35 using namespace lyx::support;
40 // special columntypes
41 extern map<char, int> special_columns;
47 // "chinese-simplified", "chinese-traditional", "japanese-cjk", "korean"
48 // cannot be supported because it is impossible to determine the correct document
49 // language if CJK is used.
50 // FIXME: missing support for "japanese" (non-CJK)
52 * known babel language names (including synonyms)
53 * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
54 * please keep this in sync with known_coded_languages line by line!
56 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
57 "american", "arabic", "arabtex", "australian", "austrian", "bahasa", "bahasai",
58 "bahasam", "basque", "belarusian", "brazil", "brazilian", "breton", "british",
59 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
60 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "francais",
61 "french", "frenchb", "frenchle", "frenchpro", "galician", "german", "germanb",
62 "greek", "hebrew", "hungarian", "icelandic", "indon", "indonesian", "interlingua",
63 "irish", "italian", "kazakh", "kurmanji", "latin", "latvian", "lithuanian",
64 "lowersorbian", "lsorbian", "magyar", "malay", "meyalu", "mongolian", "naustrian",
65 "newzealand", "ngerman", "ngermanb", "norsk", "nynorsk", "polutonikogreek", "polish",
66 "portuges", "portuguese", "romanian", "russian", "russianb", "samin",
67 "scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish",
68 "swedish", "thai", "turkish", "turkmen", "ukraineb", "ukrainian",
69 "uppersorbian", "UKenglish", "USenglish", "usorbian", "vietnam", "welsh",
73 * the same as known_languages with .lyx names
74 * please keep this in sync with known_languages line by line!
76 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
77 "american", "arabic_arabi", "arabic_arabtex", "australian", "austrian", "bahasa", "bahasa",
78 "bahasam", "basque", "belarusian", "brazilian", "brazilian", "breton", "british",
79 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
80 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "french",
81 "french", "french", "french", "french", "galician", "german", "german",
82 "greek", "hebrew", "magyar", "icelandic", "bahasa", "bahasa", "interlingua",
83 "irish", "italian", "kazakh", "kurmanji", "latin", "latvian", "lithuanian",
84 "lowersorbian", "lowersorbian", "magyar", "bahasam", "bahasam", "mongolian", "naustrian",
85 "newzealand", "ngerman", "ngerman", "norsk", "nynorsk", "polutonikogreek", "polish",
86 "portuguese", "portuguese", "romanian", "russian", "russian", "samin",
87 "scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish",
88 "swedish", "thai", "turkish", "turkmen", "ukrainian", "ukrainian",
89 "uppersorbian", "uppersorbian", "english", "english", "vietnamese", "welsh",
93 * known polyglossia language names (including variants)
95 const char * const polyglossia_languages[] = {
96 "albanian", "croatian", "hebrew", "norsk", "swedish", "amharic", "czech", "hindi",
97 "nynorsk", "syriac", "arabic", "danish", "icelandic", "occitan", "tamil",
98 "armenian", "divehi", "interlingua", "polish", "telugu", "asturian", "dutch",
99 "irish", "portuges", "thai", "bahasai", "english", "italian", "romanian", "turkish",
100 "bahasam", "esperanto", "lao", "russian", "turkmen", "basque", "estonian", "latin",
101 "samin", "ukrainian", "bengali", "farsi", "latvian", "sanskrit", "urdu", "brazil",
102 "brazilian", "finnish", "lithuanian", "scottish", "usorbian", "breton", "french",
103 "lsorbian", "serbian", "vietnamese", "bulgarian", "galician", "magyar", "slovak",
104 "welsh", "catalan", "german", "malayalam", "slovenian", "coptic", "greek",
105 "marathi", "spanish",
106 "american", "ancient", "australian", "british", "monotonic", "newzealand",
110 * the same as polyglossia_languages with .lyx names
111 * please keep this in sync with polyglossia_languages line by line!
113 const char * const coded_polyglossia_languages[] = {
114 "albanian", "croatian", "hebrew", "norsk", "swedish", "amharic", "czech", "hindi",
115 "nynorsk", "syriac", "arabic_arabi", "danish", "icelandic", "occitan", "tamil",
116 "armenian", "divehi", "interlingua", "polish", "telugu", "asturian", "dutch",
117 "irish", "portuges", "thai", "bahasa", "english", "italian", "romanian", "turkish",
118 "bahasam", "esperanto", "lao", "russian", "turkmen", "basque", "estonian", "latin",
119 "samin", "ukrainian", "bengali", "farsi", "latvian", "sanskrit", "urdu", "brazilian",
120 "brazilian", "finnish", "lithuanian", "scottish", "uppersorbian", "breton", "french",
121 "lowersorbian", "serbian", "vietnamese", "bulgarian", "galician", "magyar", "slovak",
122 "welsh", "catalan", "ngerman", "malayalam", "slovene", "coptic", "greek",
123 "marathi", "spanish",
124 "american", "ancientgreek", "australian", "british", "greek", "newzealand",
125 "polutonikogreek", 0};
127 /// languages with english quotes (.lyx names)
128 const char * const known_english_quotes_languages[] = {"american", "australian",
129 "bahasa", "bahasam", "brazilian", "canadian", "chinese-simplified", "english",
130 "esperanto", "hebrew", "irish", "korean", "newzealand", "portuguese", "scottish",
133 /// languages with french quotes (.lyx names)
134 const char * const known_french_quotes_languages[] = {"albanian",
135 "arabic_arabi", "arabic_arabtex", "basque", "canadien", "catalan", "french",
136 "galician", "greek", "italian", "norsk", "nynorsk", "polutonikogreek",
137 "russian", "spanish", "spanish-mexico", "turkish", "turkmen", "ukrainian",
140 /// languages with german quotes (.lyx names)
141 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
142 "czech", "german", "icelandic", "lithuanian", "lowersorbian", "naustrian",
143 "ngerman", "serbian", "serbian-latin", "slovak", "slovene", "uppersorbian", 0};
145 /// languages with polish quotes (.lyx names)
146 const char * const known_polish_quotes_languages[] = {"afrikaans", "croatian",
147 "dutch", "estonian", "magyar", "polish", "romanian", 0};
149 /// languages with swedish quotes (.lyx names)
150 const char * const known_swedish_quotes_languages[] = {"finnish",
153 /// known language packages from the times before babel
154 const char * const known_old_language_packages[] = {"french", "frenchle",
155 "frenchpro", "german", "ngerman", "pmfrench", 0};
157 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
159 const char * const known_roman_fonts[] = { "ae", "beraserif", "bookman",
160 "ccfonts", "chancery", "charter", "cmr", "fourier", "lmodern", "mathpazo",
161 "mathptmx", "newcent", "utopia", 0};
163 const char * const known_sans_fonts[] = { "avant", "berasans", "cmbr", "cmss",
164 "helvet", "lmss", 0};
166 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
167 "courier", "lmtt", "luximono", "fourier", "lmodern", "mathpazo", "mathptmx",
170 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
171 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
172 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
173 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
174 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
176 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
177 "executivepaper", "legalpaper", "letterpaper", 0};
179 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
180 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
182 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
183 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
186 /// commands that can start an \if...\else...\endif sequence
187 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
188 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
189 "ifsidecap", "ifupgreek", 0};
191 const char * const known_basic_colors[] = {"blue", "black", "cyan", "green",
192 "magenta", "red", "white", "yellow", 0};
194 const char * const known_basic_color_codes[] = {"#0000ff", "#000000", "#00ffff", "#00ff00",
195 "#ff00ff", "#ff0000", "#ffffff", "#ffff00", 0};
197 /// conditional commands with three arguments like \@ifundefined{}{}{}
198 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
201 /// packages that work only in xetex
202 /// polyglossia is handled separately
203 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
204 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
205 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
207 /// packages that are automatically skipped if loaded by LyX
208 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
209 "amstext", "amsthm", "array", "booktabs", "calc", "CJK", "color", "float", "fontspec",
210 "graphicx", "hhline", "ifthen", "longtable", "makeidx", "multirow",
211 "nomencl", "pdfpages", "rotating", "rotfloat", "splitidx", "setspace",
212 "subscript", "textcomp", "ulem", "url", "varioref", "verbatim", "wrapfig",
215 // codes used to remove packages that are loaded automatically by LyX.
216 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
217 const char package_beg_sep = '\001';
218 const char package_mid_sep = '\002';
219 const char package_end_sep = '\003';
222 // returns true if at least one of the options in what has been found
223 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
229 // the last language option is the document language (for babel and LyX)
230 // the last size option is the document font size
231 vector<string>::iterator it;
232 vector<string>::iterator position = opts.begin();
233 for (; *what; ++what) {
234 it = find(opts.begin(), opts.end(), *what);
235 if (it != opts.end()) {
236 if (it >= position) {
247 void delete_opt(vector<string> & opts, char const * const * what)
252 // remove found options from the list
253 // do this after handle_opt to avoid potential memory leaks
254 vector<string>::iterator it;
255 for (; *what; ++what) {
256 it = find(opts.begin(), opts.end(), *what);
257 if (it != opts.end())
264 * Split a package options string (keyval format) into a vector.
266 * authorformat=smallcaps,
268 * titleformat=colonsep,
269 * bibformat={tabular,ibidem,numbered}
271 vector<string> split_options(string const & input)
273 vector<string> options;
277 Token const & t = p.get_token();
278 if (t.asInput() == ",") {
279 options.push_back(trimSpaceAndEol(option));
281 } else if (t.asInput() == "=") {
284 if (p.next_token().asInput() == "{")
285 option += '{' + p.getArg('{', '}') + '}';
286 } else if (t.cat() != catSpace)
287 option += t.asInput();
291 options.push_back(trimSpaceAndEol(option));
298 * Retrieve a keyval option "name={value with=sign}" named \p name from
299 * \p options and return the value.
300 * The found option is also removed from \p options.
302 string process_keyval_opt(vector<string> & options, string name)
304 for (size_t i = 0; i < options.size(); ++i) {
305 vector<string> option;
306 split(options[i], option, '=');
307 if (option.size() < 2)
309 if (option[0] == name) {
310 options.erase(options.begin() + i);
311 option.erase(option.begin());
312 return join(option, "=");
318 } // anonymous namespace
321 bool Preamble::indentParagraphs() const
323 return h_paragraph_separation == "indent";
327 bool Preamble::isPackageUsed(string const & package) const
329 return used_packages.find(package) != used_packages.end();
333 vector<string> Preamble::getPackageOptions(string const & package) const
335 map<string, vector<string> >::const_iterator it = used_packages.find(package);
336 if (it != used_packages.end())
338 return vector<string>();
342 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
344 auto_packages.insert(package);
348 void Preamble::addModule(string const & module)
350 used_modules.push_back(module);
354 void Preamble::suppressDate(bool suppress)
357 h_suppress_date = "true";
359 h_suppress_date = "false";
363 void Preamble::registerAuthor(std::string const & name)
365 Author author(from_utf8(name), empty_docstring());
366 author.setUsed(true);
367 authors_.record(author);
368 h_tracking_changes = "true";
369 h_output_changes = "true";
373 Author const & Preamble::getAuthor(std::string const & name) const
375 Author author(from_utf8(name), empty_docstring());
376 for (AuthorList::Authors::const_iterator it = authors_.begin();
377 it != authors_.end(); ++it)
380 static Author const dummy;
385 void Preamble::add_package(string const & name, vector<string> & options)
387 // every package inherits the global options
388 if (used_packages.find(name) == used_packages.end())
389 used_packages[name] = split_options(h_options);
391 vector<string> & v = used_packages[name];
392 v.insert(v.end(), options.begin(), options.end());
393 if (name == "jurabib") {
394 // Don't output the order argument (see the cite command
395 // handling code in text.cpp).
396 vector<string>::iterator end =
397 remove(options.begin(), options.end(), "natbiborder");
398 end = remove(options.begin(), end, "jurabiborder");
399 options.erase(end, options.end());
406 // Given is a string like "scaled=0.9", return 0.9 * 100
407 string const scale_as_percentage(string const & scale)
409 string::size_type pos = scale.find('=');
410 if (pos != string::npos) {
411 string value = scale.substr(pos + 1);
413 return convert<string>(100 * convert<double>(value));
415 // If the input string didn't match our expectations.
416 // return the default value "100"
421 string remove_braces(string const & value)
425 if (value[0] == '{' && value[value.length()-1] == '}')
426 return value.substr(1, value.length()-2);
430 } // anonymous namespace
433 Preamble::Preamble() : one_language(true), title_layout_found(false)
437 h_biblio_style = "plain";
438 h_cite_engine = "basic";
439 h_cite_engine_type = "numerical";
440 h_defskip = "medskip";
443 h_fontencoding = "default";
444 h_font_roman = "default";
445 h_font_sans = "default";
446 h_font_typewriter = "default";
447 h_font_default_family = "default";
448 h_use_non_tex_fonts = "false";
450 h_font_osf = "false";
451 h_font_sf_scale = "100";
452 h_font_tt_scale = "100";
453 h_graphics = "default";
454 h_default_output_format = "default";
455 h_html_be_strict = "false";
456 h_html_css_as_file = "0";
457 h_html_math_output = "0";
458 h_inputencoding = "auto";
459 h_justification = "true";
460 h_language = "english";
461 h_language_package = "none";
466 h_output_changes = "false";
467 h_papercolumns = "1";
468 h_paperfontsize = "default";
469 h_paperorientation = "portrait";
470 h_paperpagestyle = "default";
472 h_papersize = "default";
473 h_paragraph_indentation = "default";
474 h_paragraph_separation = "indent";
479 h_pdf_bookmarks = "1";
480 h_pdf_bookmarksnumbered = "0";
481 h_pdf_bookmarksopen = "0";
482 h_pdf_bookmarksopenlevel = "1";
483 h_pdf_breaklinks = "0";
484 h_pdf_pdfborder = "0";
485 h_pdf_colorlinks = "0";
486 h_pdf_backref = "section";
487 h_pdf_pdfusetitle = "1";
489 //h_pdf_quoted_options;
490 h_quotes_language = "english";
492 h_spacing = "single";
493 h_suppress_date = "false";
494 h_textclass = "article";
496 h_tracking_changes = "false";
497 h_use_bibtopic = "false";
498 h_use_indices = "false";
499 h_use_geometry = "false";
500 h_use_default_options = "false";
501 h_use_hyperref = "0";
502 h_use_refstyle = "0";
503 h_use_packages["amsmath"] = "1";
504 h_use_packages["amssymb"] = "0";
505 h_use_packages["esint"] = "1";
506 h_use_packages["mhchem"] = "0";
507 h_use_packages["mathdots"] = "0";
508 h_use_packages["mathtools"] = "0";
509 h_use_packages["undertilde"] = "0";
513 void Preamble::handle_hyperref(vector<string> & options)
515 // FIXME swallow inputencoding changes that might surround the
516 // hyperref setup if it was written by LyX
517 h_use_hyperref = "1";
518 // swallow "unicode=true", since LyX does always write that
519 vector<string>::iterator it =
520 find(options.begin(), options.end(), "unicode=true");
521 if (it != options.end())
523 it = find(options.begin(), options.end(), "pdfusetitle");
524 if (it != options.end()) {
525 h_pdf_pdfusetitle = "1";
528 string bookmarks = process_keyval_opt(options, "bookmarks");
529 if (bookmarks == "true")
530 h_pdf_bookmarks = "1";
531 else if (bookmarks == "false")
532 h_pdf_bookmarks = "0";
533 if (h_pdf_bookmarks == "1") {
534 string bookmarksnumbered =
535 process_keyval_opt(options, "bookmarksnumbered");
536 if (bookmarksnumbered == "true")
537 h_pdf_bookmarksnumbered = "1";
538 else if (bookmarksnumbered == "false")
539 h_pdf_bookmarksnumbered = "0";
540 string bookmarksopen =
541 process_keyval_opt(options, "bookmarksopen");
542 if (bookmarksopen == "true")
543 h_pdf_bookmarksopen = "1";
544 else if (bookmarksopen == "false")
545 h_pdf_bookmarksopen = "0";
546 if (h_pdf_bookmarksopen == "1") {
547 string bookmarksopenlevel =
548 process_keyval_opt(options, "bookmarksopenlevel");
549 if (!bookmarksopenlevel.empty())
550 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
553 string breaklinks = process_keyval_opt(options, "breaklinks");
554 if (breaklinks == "true")
555 h_pdf_breaklinks = "1";
556 else if (breaklinks == "false")
557 h_pdf_breaklinks = "0";
558 string pdfborder = process_keyval_opt(options, "pdfborder");
559 if (pdfborder == "{0 0 0}")
560 h_pdf_pdfborder = "1";
561 else if (pdfborder == "{0 0 1}")
562 h_pdf_pdfborder = "0";
563 string backref = process_keyval_opt(options, "backref");
564 if (!backref.empty())
565 h_pdf_backref = backref;
566 string colorlinks = process_keyval_opt(options, "colorlinks");
567 if (colorlinks == "true")
568 h_pdf_colorlinks = "1";
569 else if (colorlinks == "false")
570 h_pdf_colorlinks = "0";
571 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
572 if (!pdfpagemode.empty())
573 h_pdf_pagemode = pdfpagemode;
574 string pdftitle = process_keyval_opt(options, "pdftitle");
575 if (!pdftitle.empty()) {
576 h_pdf_title = remove_braces(pdftitle);
578 string pdfauthor = process_keyval_opt(options, "pdfauthor");
579 if (!pdfauthor.empty()) {
580 h_pdf_author = remove_braces(pdfauthor);
582 string pdfsubject = process_keyval_opt(options, "pdfsubject");
583 if (!pdfsubject.empty())
584 h_pdf_subject = remove_braces(pdfsubject);
585 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
586 if (!pdfkeywords.empty())
587 h_pdf_keywords = remove_braces(pdfkeywords);
588 if (!options.empty()) {
589 if (!h_pdf_quoted_options.empty())
590 h_pdf_quoted_options += ',';
591 h_pdf_quoted_options += join(options, ",");
597 void Preamble::handle_geometry(vector<string> & options)
599 h_use_geometry = "true";
600 vector<string>::iterator it;
602 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
603 h_paperorientation = "landscape";
607 // keyval version: "paper=letter"
608 string paper = process_keyval_opt(options, "paper");
610 h_papersize = paper + "paper";
611 // alternative version: "letterpaper"
612 handle_opt(options, known_paper_sizes, h_papersize);
613 delete_opt(options, known_paper_sizes);
615 char const * const * margin = known_paper_margins;
616 for (; *margin; ++margin) {
617 string value = process_keyval_opt(options, *margin);
618 if (!value.empty()) {
619 int k = margin - known_paper_margins;
620 string name = known_coded_paper_margins[k];
621 h_margins += '\\' + name + ' ' + value + '\n';
627 void Preamble::handle_package(Parser &p, string const & name,
628 string const & opts, bool in_lyx_preamble)
630 vector<string> options = split_options(opts);
631 add_package(name, options);
634 if (is_known(name, known_xetex_packages)) {
636 h_use_non_tex_fonts = "true";
637 registerAutomaticallyLoadedPackage("fontspec");
638 if (h_inputencoding == "auto")
639 p.setEncoding("utf8");
643 if (is_known(name, known_roman_fonts)) {
648 if (name == "fourier") {
649 h_font_roman = "utopia";
650 // when font uses real small capitals
651 if (opts == "expert")
655 else if (name == "mathpazo")
656 h_font_roman = "palatino";
658 else if (name == "mathptmx")
659 h_font_roman = "times";
662 if (is_known(name, known_sans_fonts)) {
666 h_font_sf_scale = scale_as_percentage(scale);
671 if (is_known(name, known_typewriter_fonts)) {
672 // fourier can be set as roman font _only_
673 // fourier as typewriter is handled in handling of \ttdefault
674 if (name != "fourier") {
675 h_font_typewriter = name;
678 h_font_tt_scale = scale_as_percentage(scale);
683 // font uses old-style figure
687 // after the detection and handling of special cases, we can remove the
688 // fonts, otherwise they would appear in the preamble, see bug #7856
689 if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
690 || is_known(name, known_typewriter_fonts))
693 else if (name == "amsmath" || name == "amssymb" ||
694 name == "esint" || name == "mhchem" || name == "mathdots" ||
695 name == "mathtools" || name == "undertilde")
696 h_use_packages[name] = "2";
698 else if (name == "babel") {
699 h_language_package = "default";
700 // One might think we would have to do nothing if babel is loaded
701 // without any options to prevent pollution of the preamble with this
702 // babel call in every roundtrip.
703 // But the user could have defined babel-specific things afterwards. So
704 // we need to keep it in the preamble to prevent cases like bug #7861.
706 // check if more than one option was used - used later for inputenc
707 if (options.begin() != options.end() - 1)
708 one_language = false;
709 // babel takes the last language of the option of its \usepackage
710 // call as document language. If there is no such language option, the
711 // last language in the documentclass options is used.
712 handle_opt(options, known_languages, h_language);
713 // If babel is called with options, LyX puts them by default into the
714 // document class options. This works for most languages, except
715 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
716 // perhaps in future others.
717 // Therefore keep the babel call as it is as the user might have
719 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
720 delete_opt(options, known_languages);
721 // finally translate the babel name to a LyX name
722 h_language = babel2lyx(h_language);
725 h_preamble << "\\usepackage{babel}\n";
728 else if (name == "polyglossia") {
729 h_language_package = "default";
730 h_default_output_format = "pdf4";
731 h_use_non_tex_fonts = "true";
733 registerAutomaticallyLoadedPackage("xunicode");
734 if (h_inputencoding == "auto")
735 p.setEncoding("utf8");
738 else if (name == "CJK") {
739 // It is impossible to determine the document language if CJK is used.
740 // All we can do is to notify the user that he has to set this by hisself.
742 // set the encoding to "auto" because it might be set to "default" by the babel handling
743 // and this would not be correct for CJK
744 h_inputencoding = "auto";
745 registerAutomaticallyLoadedPackage("CJK");
748 else if (name == "fontenc") {
749 h_fontencoding = getStringFromVector(options, ",");
750 /* We could do the following for better round trip support,
751 * but this makes the document less portable, so I skip it:
752 if (h_fontencoding == lyxrc.fontenc)
753 h_fontencoding = "global";
758 else if (name == "inputenc" || name == "luainputenc") {
759 // h_inputencoding is only set when there is not more than one
760 // inputenc option because otherwise h_inputencoding must be
761 // set to "auto" (the default encoding of the document language)
762 // Therefore check for the "," character.
763 // It is also only set when there is not more than one babel
765 if (opts.find(",") == string::npos && one_language == true)
766 h_inputencoding = opts;
767 if (!options.empty())
768 p.setEncoding(options.back());
772 else if (is_known(name, known_old_language_packages)) {
773 // known language packages from the times before babel
774 // if they are found and not also babel, they will be used as
775 // custom language package
776 h_language_package = "\\usepackage{" + name + "}";
779 else if (name == "prettyref")
780 ; // ignore this FIXME: Use the package separator mechanism instead
782 else if (name == "lyxskak") {
783 // ignore this and its options
784 const char * const o[] = {"ps", "mover", 0};
785 delete_opt(options, o);
788 else if (is_known(name, known_lyx_packages) && options.empty()) {
789 if (name == "splitidx")
790 h_use_indices = "true";
791 if (!in_lyx_preamble)
792 h_preamble << package_beg_sep << name
793 << package_mid_sep << "\\usepackage{"
794 << name << "}\n" << package_end_sep;
797 else if (name == "geometry")
798 handle_geometry(options);
800 else if (name == "subfig")
801 ; // ignore this FIXME: Use the package separator mechanism instead
803 else if (is_known(name, known_languages))
806 else if (name == "natbib") {
807 h_biblio_style = "plainnat";
808 h_cite_engine = "natbib";
809 h_cite_engine_type = "authoryear";
810 vector<string>::iterator it =
811 find(options.begin(), options.end(), "authoryear");
812 if (it != options.end())
815 it = find(options.begin(), options.end(), "numbers");
816 if (it != options.end()) {
817 h_cite_engine_type = "numerical";
823 else if (name == "jurabib") {
824 h_biblio_style = "jurabib";
825 h_cite_engine = "jurabib";
826 h_cite_engine_type = "authoryear";
829 else if (name == "hyperref")
830 handle_hyperref(options);
832 else if (!in_lyx_preamble) {
834 h_preamble << "\\usepackage{" << name << "}\n";
836 h_preamble << "\\usepackage[" << opts << "]{"
842 // We need to do something with the options...
843 if (!options.empty())
844 cerr << "Ignoring options '" << join(options, ",")
845 << "' of package " << name << '.' << endl;
847 // remove the whitespace
852 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
855 Token t = p.get_token();
856 if (t.cat() == catEscape &&
857 is_known(t.cs(), known_if_commands))
858 handle_if(p, in_lyx_preamble);
860 if (!in_lyx_preamble)
861 h_preamble << t.asInput();
862 if (t.cat() == catEscape && t.cs() == "fi")
869 bool Preamble::writeLyXHeader(ostream & os, bool subdoc)
871 // set the quote language
872 // LyX only knows the following quotes languages:
873 // english, swedish, german, polish, french and danish
874 // (quotes for "japanese" and "chinese-traditional" are missing because
875 // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
876 // conversion list taken from
877 // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
878 // (quotes for kazakh and interlingua are unknown)
880 if (h_language == "danish")
881 h_quotes_language = "danish";
883 else if (is_known(h_language, known_french_quotes_languages))
884 h_quotes_language = "french";
886 else if (is_known(h_language, known_german_quotes_languages))
887 h_quotes_language = "german";
889 else if (is_known(h_language, known_polish_quotes_languages))
890 h_quotes_language = "polish";
892 else if (is_known(h_language, known_swedish_quotes_languages))
893 h_quotes_language = "swedish";
895 else if (is_known(h_language, known_english_quotes_languages))
896 h_quotes_language = "english";
898 if (contains(h_float_placement, "H"))
899 registerAutomaticallyLoadedPackage("float");
900 if (h_spacing != "single" && h_spacing != "default")
901 registerAutomaticallyLoadedPackage("setspace");
902 if (h_use_packages["amsmath"] == "2") {
903 // amsbsy and amstext are already provided by amsmath
904 registerAutomaticallyLoadedPackage("amsbsy");
905 registerAutomaticallyLoadedPackage("amstext");
908 // output the LyX file settings
909 os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
910 << "\\lyxformat " << LYX_FORMAT << '\n'
911 << "\\begin_document\n"
912 << "\\begin_header\n"
913 << "\\textclass " << h_textclass << "\n";
914 string const raw = subdoc ? empty_string() : h_preamble.str();
916 os << "\\begin_preamble\n";
917 for (string::size_type i = 0; i < raw.size(); ++i) {
918 if (raw[i] == package_beg_sep) {
919 // Here follows some package loading code that
920 // must be skipped if the package is loaded
922 string::size_type j = raw.find(package_mid_sep, i);
923 if (j == string::npos)
925 string::size_type k = raw.find(package_end_sep, j);
926 if (k == string::npos)
928 string const package = raw.substr(i + 1, j - i - 1);
929 string const replacement = raw.substr(j + 1, k - j - 1);
930 if (auto_packages.find(package) == auto_packages.end())
936 os << "\n\\end_preamble\n";
938 if (!h_options.empty())
939 os << "\\options " << h_options << "\n";
940 os << "\\use_default_options " << h_use_default_options << "\n";
941 if (!used_modules.empty()) {
942 os << "\\begin_modules\n";
943 vector<string>::const_iterator const end = used_modules.end();
944 vector<string>::const_iterator it = used_modules.begin();
945 for (; it != end; ++it)
947 os << "\\end_modules\n";
949 os << "\\language " << h_language << "\n"
950 << "\\language_package " << h_language_package << "\n"
951 << "\\inputencoding " << h_inputencoding << "\n"
952 << "\\fontencoding " << h_fontencoding << "\n"
953 << "\\font_roman " << h_font_roman << "\n"
954 << "\\font_sans " << h_font_sans << "\n"
955 << "\\font_typewriter " << h_font_typewriter << "\n"
956 << "\\font_default_family " << h_font_default_family << "\n"
957 << "\\use_non_tex_fonts " << h_use_non_tex_fonts << "\n"
958 << "\\font_sc " << h_font_sc << "\n"
959 << "\\font_osf " << h_font_osf << "\n"
960 << "\\font_sf_scale " << h_font_sf_scale << "\n"
961 << "\\font_tt_scale " << h_font_tt_scale << "\n"
962 << "\\graphics " << h_graphics << "\n"
963 << "\\default_output_format " << h_default_output_format << "\n";
964 if (!h_float_placement.empty())
965 os << "\\float_placement " << h_float_placement << "\n";
966 os << "\\paperfontsize " << h_paperfontsize << "\n"
967 << "\\spacing " << h_spacing << "\n"
968 << "\\use_hyperref " << h_use_hyperref << '\n';
969 if (h_use_hyperref == "1") {
970 if (!h_pdf_title.empty())
971 os << "\\pdf_title \"" << h_pdf_title << "\"\n";
972 if (!h_pdf_author.empty())
973 os << "\\pdf_author \"" << h_pdf_author << "\"\n";
974 if (!h_pdf_subject.empty())
975 os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
976 if (!h_pdf_keywords.empty())
977 os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
978 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
979 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
980 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
981 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
982 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
983 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
984 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
985 "\\pdf_backref " << h_pdf_backref << "\n"
986 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
987 if (!h_pdf_pagemode.empty())
988 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
989 if (!h_pdf_quoted_options.empty())
990 os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
992 os << "\\papersize " << h_papersize << "\n"
993 << "\\use_geometry " << h_use_geometry << '\n';
994 for (map<string, string>::const_iterator it = h_use_packages.begin();
995 it != h_use_packages.end(); ++it)
996 os << "\\use_package " << it->first << ' ' << it->second << '\n';
997 os << "\\cite_engine " << h_cite_engine << '\n'
998 << "\\cite_engine_type " << h_cite_engine_type << '\n'
999 << "\\biblio_style " << h_biblio_style << "\n"
1000 << "\\use_bibtopic " << h_use_bibtopic << "\n"
1001 << "\\use_indices " << h_use_indices << "\n"
1002 << "\\paperorientation " << h_paperorientation << '\n'
1003 << "\\suppress_date " << h_suppress_date << '\n'
1004 << "\\justification " << h_justification << '\n'
1005 << "\\use_refstyle " << h_use_refstyle << '\n';
1006 if (!h_fontcolor.empty())
1007 os << "\\fontcolor " << h_fontcolor << '\n';
1008 if (!h_notefontcolor.empty())
1009 os << "\\notefontcolor " << h_notefontcolor << '\n';
1010 if (!h_backgroundcolor.empty())
1011 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1012 if (!h_boxbgcolor.empty())
1013 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1015 << "\\secnumdepth " << h_secnumdepth << "\n"
1016 << "\\tocdepth " << h_tocdepth << "\n"
1017 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1018 if (h_paragraph_separation == "skip")
1019 os << "\\defskip " << h_defskip << "\n";
1021 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1022 os << "\\quotes_language " << h_quotes_language << "\n"
1023 << "\\papercolumns " << h_papercolumns << "\n"
1024 << "\\papersides " << h_papersides << "\n"
1025 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1026 if (!h_listings_params.empty())
1027 os << "\\listings_params " << h_listings_params << "\n";
1028 os << "\\tracking_changes " << h_tracking_changes << "\n"
1029 << "\\output_changes " << h_output_changes << "\n"
1030 << "\\html_math_output " << h_html_math_output << "\n"
1031 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1032 << "\\html_be_strict " << h_html_be_strict << "\n"
1034 << "\\end_header\n\n"
1035 << "\\begin_body\n";
1040 void Preamble::parse(Parser & p, string const & forceclass,
1041 TeX2LyXDocClass & tc)
1043 // initialize fixed types
1044 special_columns['D'] = 3;
1045 bool is_full_document = false;
1046 bool is_lyx_file = false;
1047 bool in_lyx_preamble = false;
1049 // determine whether this is a full document or a fragment for inclusion
1051 Token const & t = p.get_token();
1053 if (t.cat() == catEscape && t.cs() == "documentclass") {
1054 is_full_document = true;
1060 while (is_full_document && p.good()) {
1061 Token const & t = p.get_token();
1064 cerr << "t: " << t << "\n";
1070 if (!in_lyx_preamble &&
1071 (t.cat() == catLetter ||
1072 t.cat() == catSuper ||
1073 t.cat() == catSub ||
1074 t.cat() == catOther ||
1075 t.cat() == catMath ||
1076 t.cat() == catActive ||
1077 t.cat() == catBegin ||
1078 t.cat() == catEnd ||
1079 t.cat() == catAlign ||
1080 t.cat() == catParameter))
1081 h_preamble << t.cs();
1083 else if (!in_lyx_preamble &&
1084 (t.cat() == catSpace || t.cat() == catNewline))
1085 h_preamble << t.asInput();
1087 else if (t.cat() == catComment) {
1088 static regex const islyxfile("%% LyX .* created this file");
1089 static regex const usercommands("User specified LaTeX commands");
1091 string const comment = t.asInput();
1093 // magically switch encoding default if it looks like XeLaTeX
1094 static string const magicXeLaTeX =
1095 "% This document must be compiled with XeLaTeX ";
1096 if (comment.size() > magicXeLaTeX.size()
1097 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1098 && h_inputencoding == "auto") {
1099 cerr << "XeLaTeX comment found, switching to UTF8\n";
1100 h_inputencoding = "utf8";
1103 if (regex_search(comment, sub, islyxfile)) {
1105 in_lyx_preamble = true;
1106 } else if (is_lyx_file
1107 && regex_search(comment, sub, usercommands))
1108 in_lyx_preamble = false;
1109 else if (!in_lyx_preamble)
1110 h_preamble << t.asInput();
1113 else if (t.cs() == "pagestyle")
1114 h_paperpagestyle = p.verbatim_item();
1116 else if (t.cs() == "setdefaultlanguage") {
1118 // We don't yet care about non-language variant options
1119 // because LyX doesn't support this yet, see bug #8214
1121 string langopts = p.getOpt();
1122 // check if the option contains a variant, if yes, extract it
1123 string::size_type pos_var = langopts.find("variant");
1124 string::size_type i = langopts.find(',', pos_var);
1125 if (pos_var != string::npos){
1127 if (i == string::npos)
1128 variant = langopts.substr(pos_var + 8, langopts.length() - pos_var - 9);
1130 variant = langopts.substr(pos_var + 8, i - pos_var - 8);
1131 h_language = variant;
1135 h_language = p.verbatim_item();
1136 //finally translate the poyglossia name to a LyX name
1137 h_language = polyglossia2lyx(h_language);
1140 else if (t.cs() == "setotherlanguage") {
1141 // We don't yet care about the option because LyX doesn't
1142 // support this yet, see bug #8214
1143 p.hasOpt() ? p.getOpt() : string();
1147 else if (t.cs() == "setmainfont") {
1148 // we don't care about the option
1149 p.hasOpt() ? p.getOpt() : string();
1150 h_font_roman = p.getArg('{', '}');
1153 else if (t.cs() == "setsansfont") {
1154 // we don't care about the option
1155 p.hasOpt() ? p.getOpt() : string();
1156 h_font_sans = p.getArg('{', '}');
1159 else if (t.cs() == "setmonofont") {
1160 // we don't care about the option
1161 p.hasOpt() ? p.getOpt() : string();
1162 h_font_typewriter = p.getArg('{', '}');
1165 else if (t.cs() == "date") {
1166 string argument = p.getArg('{', '}');
1167 if (argument.empty())
1168 h_suppress_date = "true";
1170 h_preamble << t.asInput() << '{' << argument << '}';
1173 else if (t.cs() == "color") {
1174 string const space =
1175 (p.hasOpt() ? p.getOpt() : string());
1176 string argument = p.getArg('{', '}');
1177 // check the case that a standard color is used
1178 if (space.empty() && is_known(argument, known_basic_colors)) {
1179 h_fontcolor = rgbcolor2code(argument);
1180 preamble.registerAutomaticallyLoadedPackage("color");
1181 } else if (space.empty() && argument == "document_fontcolor")
1182 preamble.registerAutomaticallyLoadedPackage("color");
1183 // check the case that LyX's document_fontcolor is defined
1184 // but not used for \color
1186 h_preamble << t.asInput();
1188 h_preamble << space;
1189 h_preamble << '{' << argument << '}';
1190 // the color might already be set because \definecolor
1191 // is parsed before this
1196 else if (t.cs() == "pagecolor") {
1197 string argument = p.getArg('{', '}');
1198 // check the case that a standard color is used
1199 if (is_known(argument, known_basic_colors)) {
1200 h_backgroundcolor = rgbcolor2code(argument);
1201 } else if (argument == "page_backgroundcolor")
1202 preamble.registerAutomaticallyLoadedPackage("color");
1203 // check the case that LyX's page_backgroundcolor is defined
1204 // but not used for \pagecolor
1206 h_preamble << t.asInput() << '{' << argument << '}';
1207 // the color might already be set because \definecolor
1208 // is parsed before this
1209 h_backgroundcolor = "";
1213 else if (t.cs() == "makeatletter") {
1214 // LyX takes care of this
1215 p.setCatCode('@', catLetter);
1218 else if (t.cs() == "makeatother") {
1219 // LyX takes care of this
1220 p.setCatCode('@', catOther);
1223 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1224 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1225 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1226 || t.cs() == "DeclareRobustCommand"
1227 || t.cs() == "DeclareRobustCommandx"
1228 || t.cs() == "ProvideTextCommandDefault"
1229 || t.cs() == "DeclareMathAccent") {
1231 if (p.next_token().character() == '*') {
1235 string const name = p.verbatim_item();
1236 string const opt1 = p.getFullOpt();
1237 string const opt2 = p.getFullOpt();
1238 string const body = p.verbatim_item();
1240 if (name == "\\rmdefault")
1241 if (is_known(body, known_roman_fonts))
1242 h_font_roman = body;
1243 if (name == "\\sfdefault")
1244 if (is_known(body, known_sans_fonts))
1246 if (name == "\\ttdefault")
1247 if (is_known(body, known_typewriter_fonts))
1248 h_font_typewriter = body;
1249 if (name == "\\familydefault") {
1250 string family = body;
1251 // remove leading "\"
1252 h_font_default_family = family.erase(0,1);
1255 // remove the lyxdot definition that is re-added by LyX
1257 if (name == "\\lyxdot")
1258 in_lyx_preamble = true;
1260 // Add the command to the known commands
1261 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1263 // only non-lyxspecific stuff
1264 if (!in_lyx_preamble) {
1266 ss << '\\' << t.cs();
1269 ss << '{' << name << '}' << opt1 << opt2
1270 << '{' << body << "}";
1271 h_preamble << ss.str();
1273 ostream & out = in_preamble ? h_preamble : os;
1274 out << "\\" << t.cs() << "{" << name << "}"
1275 << opts << "{" << body << "}";
1280 else if (t.cs() == "documentclass") {
1281 vector<string>::iterator it;
1282 vector<string> opts = split_options(p.getArg('[', ']'));
1283 handle_opt(opts, known_fontsizes, h_paperfontsize);
1284 delete_opt(opts, known_fontsizes);
1285 // delete "pt" at the end
1286 string::size_type i = h_paperfontsize.find("pt");
1287 if (i != string::npos)
1288 h_paperfontsize.erase(i);
1289 // The documentclass options are always parsed before the options
1290 // of the babel call so that a language cannot overwrite the babel
1292 handle_opt(opts, known_languages, h_language);
1293 delete_opt(opts, known_languages);
1295 // paper orientation
1296 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1297 h_paperorientation = "landscape";
1301 if ((it = find(opts.begin(), opts.end(), "oneside"))
1306 if ((it = find(opts.begin(), opts.end(), "twoside"))
1312 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1314 h_papercolumns = "1";
1317 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1319 h_papercolumns = "2";
1323 // some size options are known to any document classes, other sizes
1324 // are handled by the \geometry command of the geometry package
1325 handle_opt(opts, known_class_paper_sizes, h_papersize);
1326 delete_opt(opts, known_class_paper_sizes);
1327 // the remaining options
1328 h_options = join(opts, ",");
1329 // FIXME This does not work for classes that have a
1330 // different name in LyX than in LaTeX
1331 h_textclass = p.getArg('{', '}');
1334 else if (t.cs() == "usepackage") {
1335 string const options = p.getArg('[', ']');
1336 string const name = p.getArg('{', '}');
1337 vector<string> vecnames;
1338 split(name, vecnames, ',');
1339 vector<string>::const_iterator it = vecnames.begin();
1340 vector<string>::const_iterator end = vecnames.end();
1341 for (; it != end; ++it)
1342 handle_package(p, trimSpaceAndEol(*it), options,
1346 else if (t.cs() == "inputencoding") {
1347 string const encoding = p.getArg('{','}');
1348 h_inputencoding = encoding;
1349 p.setEncoding(encoding);
1352 else if (t.cs() == "newenvironment") {
1353 string const name = p.getArg('{', '}');
1354 string const opt1 = p.getFullOpt();
1355 string const opt2 = p.getFullOpt();
1356 string const beg = p.verbatim_item();
1357 string const end = p.verbatim_item();
1358 if (!in_lyx_preamble) {
1359 h_preamble << "\\newenvironment{" << name
1360 << '}' << opt1 << opt2 << '{'
1361 << beg << "}{" << end << '}';
1363 add_known_environment(name, opt1, !opt2.empty(),
1364 from_utf8(beg), from_utf8(end));
1368 else if (t.cs() == "def") {
1369 string name = p.get_token().cs();
1370 // In fact, name may be more than the name:
1371 // In the test case of bug 8116
1372 // name == "csname SF@gobble@opt \endcsname".
1373 // Therefore, we need to use asInput() instead of cs().
1374 while (p.next_token().cat() != catBegin)
1375 name += p.get_token().asInput();
1376 if (!in_lyx_preamble)
1377 h_preamble << "\\def\\" << name << '{'
1378 << p.verbatim_item() << "}";
1381 else if (t.cs() == "newcolumntype") {
1382 string const name = p.getArg('{', '}');
1383 trimSpaceAndEol(name);
1385 string opts = p.getOpt();
1386 if (!opts.empty()) {
1387 istringstream is(string(opts, 1));
1390 special_columns[name[0]] = nargs;
1391 h_preamble << "\\newcolumntype{" << name << "}";
1393 h_preamble << "[" << nargs << "]";
1394 h_preamble << "{" << p.verbatim_item() << "}";
1397 else if (t.cs() == "setcounter") {
1398 string const name = p.getArg('{', '}');
1399 string const content = p.getArg('{', '}');
1400 if (name == "secnumdepth")
1401 h_secnumdepth = content;
1402 else if (name == "tocdepth")
1403 h_tocdepth = content;
1405 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1408 else if (t.cs() == "setlength") {
1409 string const name = p.verbatim_item();
1410 string const content = p.verbatim_item();
1411 // the paragraphs are only not indented when \parindent is set to zero
1412 if (name == "\\parindent" && content != "") {
1413 if (content[0] == '0')
1414 h_paragraph_separation = "skip";
1416 h_paragraph_indentation = translate_len(content);
1417 } else if (name == "\\parskip") {
1418 if (content == "\\smallskipamount")
1419 h_defskip = "smallskip";
1420 else if (content == "\\medskipamount")
1421 h_defskip = "medskip";
1422 else if (content == "\\bigskipamount")
1423 h_defskip = "bigskip";
1425 h_defskip = content;
1427 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1430 else if (t.cs() == "onehalfspacing")
1431 h_spacing = "onehalf";
1433 else if (t.cs() == "doublespacing")
1434 h_spacing = "double";
1436 else if (t.cs() == "setstretch")
1437 h_spacing = "other " + p.verbatim_item();
1439 else if (t.cs() == "begin") {
1440 string const name = p.getArg('{', '}');
1441 if (name == "document")
1443 h_preamble << "\\begin{" << name << "}";
1446 else if (t.cs() == "geometry") {
1447 vector<string> opts = split_options(p.getArg('{', '}'));
1448 handle_geometry(opts);
1451 else if (t.cs() == "definecolor") {
1452 string const color = p.getArg('{', '}');
1453 string const space = p.getArg('{', '}');
1454 string const value = p.getArg('{', '}');
1455 if (color == "document_fontcolor" && space == "rgb") {
1456 RGBColor c(RGBColorFromLaTeX(value));
1457 h_fontcolor = X11hexname(c);
1458 } else if (color == "note_fontcolor" && space == "rgb") {
1459 RGBColor c(RGBColorFromLaTeX(value));
1460 h_notefontcolor = X11hexname(c);
1461 } else if (color == "page_backgroundcolor" && space == "rgb") {
1462 RGBColor c(RGBColorFromLaTeX(value));
1463 h_backgroundcolor = X11hexname(c);
1464 } else if (color == "shadecolor" && space == "rgb") {
1465 RGBColor c(RGBColorFromLaTeX(value));
1466 h_boxbgcolor = X11hexname(c);
1468 h_preamble << "\\definecolor{" << color
1469 << "}{" << space << "}{" << value
1474 else if (t.cs() == "bibliographystyle")
1475 h_biblio_style = p.verbatim_item();
1477 else if (t.cs() == "jurabibsetup") {
1478 // FIXME p.getArg('{', '}') is most probably wrong (it
1479 // does not handle nested braces).
1480 // Use p.verbatim_item() instead.
1481 vector<string> jurabibsetup =
1482 split_options(p.getArg('{', '}'));
1483 // add jurabibsetup to the jurabib package options
1484 add_package("jurabib", jurabibsetup);
1485 if (!jurabibsetup.empty()) {
1486 h_preamble << "\\jurabibsetup{"
1487 << join(jurabibsetup, ",") << '}';
1491 else if (t.cs() == "hypersetup") {
1492 vector<string> hypersetup =
1493 split_options(p.verbatim_item());
1494 // add hypersetup to the hyperref package options
1495 handle_hyperref(hypersetup);
1496 if (!hypersetup.empty()) {
1497 h_preamble << "\\hypersetup{"
1498 << join(hypersetup, ",") << '}';
1502 else if (is_known(t.cs(), known_if_3arg_commands)) {
1503 // prevent misparsing of \usepackage if it is used
1504 // as an argument (see e.g. our own output of
1505 // \@ifundefined above)
1506 string const arg1 = p.verbatim_item();
1507 string const arg2 = p.verbatim_item();
1508 string const arg3 = p.verbatim_item();
1509 // test case \@ifundefined{date}{}{\date{}}
1510 if (t.cs() == "@ifundefined" && arg1 == "date" &&
1511 arg2.empty() && arg3 == "\\date{}") {
1512 h_suppress_date = "true";
1513 // older tex2lyx versions did output
1514 // \@ifundefined{definecolor}{\usepackage{color}}{}
1515 } else if (t.cs() == "@ifundefined" &&
1516 arg1 == "definecolor" &&
1517 arg2 == "\\usepackage{color}" &&
1519 if (!in_lyx_preamble)
1520 h_preamble << package_beg_sep
1523 << "\\@ifundefined{definecolor}{color}{}"
1526 //\@ifundefined{showcaptionsetup}{}{%
1527 // \PassOptionsToPackage{caption=false}{subfig}}
1528 // that LyX uses for subfloats
1529 } else if (t.cs() == "@ifundefined" &&
1530 arg1 == "showcaptionsetup" && arg2.empty()
1531 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1533 } else if (!in_lyx_preamble) {
1534 h_preamble << t.asInput()
1535 << '{' << arg1 << '}'
1536 << '{' << arg2 << '}'
1537 << '{' << arg3 << '}';
1541 else if (is_known(t.cs(), known_if_commands)) {
1542 // must not parse anything in conditional code, since
1543 // LyX would output the parsed contents unconditionally
1544 if (!in_lyx_preamble)
1545 h_preamble << t.asInput();
1546 handle_if(p, in_lyx_preamble);
1549 else if (!t.cs().empty() && !in_lyx_preamble)
1550 h_preamble << '\\' << t.cs();
1553 // remove the whitespace
1556 // Force textclass if the user wanted it
1557 if (!forceclass.empty())
1558 h_textclass = forceclass;
1559 if (noweb_mode && !prefixIs(h_textclass, "literate-"))
1560 h_textclass.insert(0, "literate-");
1561 tc.setName(h_textclass);
1563 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1566 if (h_papersides.empty()) {
1569 h_papersides = ss.str();
1574 string babel2lyx(string const & language)
1576 char const * const * where = is_known(language, known_languages);
1578 return known_coded_languages[where - known_languages];
1583 string polyglossia2lyx(string const & language)
1585 char const * const * where = is_known(language, polyglossia_languages);
1587 return coded_polyglossia_languages[where - polyglossia_languages];
1592 string rgbcolor2code(string const & name)
1594 char const * const * where = is_known(name, known_basic_colors);
1596 // "red", "green" etc
1597 return known_basic_color_codes[where - known_basic_colors];
1599 // "255,0,0", "0,255,0" etc
1600 RGBColor c(RGBColorFromLaTeX(name));
1601 return X11hexname(c);