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 //add these to known_languages when updating to lyxformat 268:
48 //"chinese-simplified", "chinese-traditional", "japanese", "korean"
49 // This requires first that support for CJK languages is added: bug #4377.
51 * known babel language names (including synonyms)
52 * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
53 * please keep this in sync with known_coded_languages line by line!
55 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
56 "american", "arabic", "arabtex", "australian", "austrian", "bahasa", "bahasai",
57 "bahasam", "basque", "belarusian", "brazil", "brazilian", "breton", "british",
58 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
59 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "francais",
60 "french", "frenchb", "frenchle", "frenchpro", "galician", "german", "germanb",
61 "greek", "hebrew", "hungarian", "icelandic", "indon", "indonesian", "interlingua",
62 "irish", "italian", "kazakh", "kurmanji", "latin", "latvian", "lithuanian",
63 "lowersorbian", "lsorbian", "magyar", "malay", "meyalu", "mongolian", "naustrian",
64 "newzealand", "ngerman", "ngermanb", "norsk", "nynorsk", "polutonikogreek", "polish",
65 "portuges", "portuguese", "romanian", "russian", "russianb", "samin",
66 "scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish",
67 "swedish", "thai", "turkish", "turkmen", "ukraineb", "ukrainian",
68 "uppersorbian", "UKenglish", "USenglish", "usorbian", "vietnam", "welsh",
72 * the same as known_languages with .lyx names
73 * please keep this in sync with known_languages line by line!
75 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
76 "american", "arabic_arabi", "arabic_arabtex", "australian", "austrian", "bahasa", "bahasa",
77 "bahasam", "basque", "belarusian", "brazilian", "brazilian", "breton", "british",
78 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
79 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "french",
80 "french", "french", "french", "french", "galician", "german", "german",
81 "greek", "hebrew", "magyar", "icelandic", "bahasa", "bahasa", "interlingua",
82 "irish", "italian", "kazakh", "kurmanji", "latin", "latvian", "lithuanian",
83 "lowersorbian", "lowersorbian", "magyar", "bahasam", "bahasam", "mongolian", "naustrian",
84 "newzealand", "ngerman", "ngerman", "norsk", "nynorsk", "polutonikogreek", "polish",
85 "portuguese", "portuguese", "romanian", "russian", "russian", "samin",
86 "scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish",
87 "swedish", "thai", "turkish", "turkmen", "ukrainian", "ukrainian",
88 "uppersorbian", "uppersorbian", "english", "english", "vietnamese", "welsh",
92 * known polyglossia language names (including variants)
94 const char * const polyglossia_languages[] = {
95 "albanian", "croatian", "hebrew", "norsk", "swedish", "amharic", "czech", "hindi",
96 "nynorsk", "syriac", "arabic", "danish", "icelandic", "occitan", "tamil",
97 "armenian", "divehi", "interlingua", "polish", "telugu", "asturian", "dutch",
98 "irish", "portuges", "thai", "bahasai", "english", "italian", "romanian", "turkish",
99 "bahasam", "esperanto", "lao", "russian", "turkmen", "basque", "estonian", "latin",
100 "samin", "ukrainian", "bengali", "farsi", "latvian", "sanskrit", "urdu", "brazil",
101 "brazilian", "finnish", "lithuanian", "scottish", "usorbian", "breton", "french",
102 "lsorbian", "serbian", "vietnamese", "bulgarian", "galician", "magyar", "slovak",
103 "welsh", "catalan", "german", "malayalam", "slovenian", "coptic", "greek",
104 "marathi", "spanish",
105 "american", "ancient", "australian", "british", "monotonic", "newzealand",
109 * the same as polyglossia_languages with .lyx names
110 * please keep this in sync with polyglossia_languages line by line!
112 const char * const coded_polyglossia_languages[] = {
113 "albanian", "croatian", "hebrew", "norsk", "swedish", "amharic", "czech", "hindi",
114 "nynorsk", "syriac", "arabic_arabi", "danish", "icelandic", "occitan", "tamil",
115 "armenian", "divehi", "interlingua", "polish", "telugu", "asturian", "dutch",
116 "irish", "portuges", "thai", "bahasa", "english", "italian", "romanian", "turkish",
117 "bahasam", "esperanto", "lao", "russian", "turkmen", "basque", "estonian", "latin",
118 "samin", "ukrainian", "bengali", "farsi", "latvian", "sanskrit", "urdu", "brazilian",
119 "brazilian", "finnish", "lithuanian", "scottish", "uppersorbian", "breton", "french",
120 "lowersorbian", "serbian", "vietnamese", "bulgarian", "galician", "magyar", "slovak",
121 "welsh", "catalan", "ngerman", "malayalam", "slovene", "coptic", "greek",
122 "marathi", "spanish",
123 "american", "ancientgreek", "australian", "british", "greek", "newzealand",
124 "polutonikogreek", 0};
126 /// languages with english quotes (.lyx names)
127 const char * const known_english_quotes_languages[] = {"american", "australian",
128 "bahasa", "bahasam", "brazilian", "canadian", "chinese-simplified", "english",
129 "esperanto", "hebrew", "irish", "korean", "newzealand", "portuguese", "scottish",
132 /// languages with french quotes (.lyx names)
133 const char * const known_french_quotes_languages[] = {"albanian",
134 "arabic_arabi", "arabic_arabtex", "basque", "canadien", "catalan", "french",
135 "galician", "greek", "italian", "norsk", "nynorsk", "polutonikogreek",
136 "russian", "spanish", "spanish-mexico", "turkish", "turkmen", "ukrainian",
139 /// languages with german quotes (.lyx names)
140 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
141 "czech", "german", "icelandic", "lithuanian", "lowersorbian", "naustrian",
142 "ngerman", "serbian", "serbian-latin", "slovak", "slovene", "uppersorbian", 0};
144 /// languages with polish quotes (.lyx names)
145 const char * const known_polish_quotes_languages[] = {"afrikaans", "croatian",
146 "dutch", "estonian", "magyar", "polish", "romanian", 0};
148 /// languages with swedish quotes (.lyx names)
149 const char * const known_swedish_quotes_languages[] = {"finnish",
152 /// known language packages from the times before babel
153 const char * const known_old_language_packages[] = {"french", "frenchle",
154 "frenchpro", "german", "ngerman", "pmfrench", 0};
156 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
158 const char * const known_roman_fonts[] = { "ae", "beraserif", "bookman",
159 "ccfonts", "chancery", "charter", "cmr", "fourier", "lmodern", "mathpazo",
160 "mathptmx", "newcent", "utopia", 0};
162 const char * const known_sans_fonts[] = { "avant", "berasans", "cmbr", "cmss",
163 "helvet", "lmss", 0};
165 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
166 "courier", "lmtt", "luximono", "fourier", "lmodern", "mathpazo", "mathptmx",
169 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
170 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
171 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
172 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
173 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
175 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
176 "executivepaper", "legalpaper", "letterpaper", 0};
178 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
179 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
181 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
182 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
185 /// commands that can start an \if...\else...\endif sequence
186 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
187 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
188 "ifsidecap", "ifupgreek", 0};
190 const char * const known_basic_colors[] = {"blue", "black", "cyan", "green",
191 "magenta", "red", "white", "yellow", 0};
193 const char * const known_basic_color_codes[] = {"#0000ff", "#000000", "#00ffff", "#00ff00",
194 "#ff00ff", "#ff0000", "#ffffff", "#ffff00", 0};
196 /// conditional commands with three arguments like \@ifundefined{}{}{}
197 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
200 /// packages that work only in xetex
201 /// polyglossia is handled separately
202 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
203 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
204 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
206 /// packages that are automatically skipped if loaded by LyX
207 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
208 "amstext", "amsthm", "array", "booktabs", "calc", "CJK", "color", "float", "fontspec",
209 "graphicx", "hhline", "ifthen", "longtable", "makeidx", "multirow",
210 "nomencl", "pdfpages", "rotating", "rotfloat", "splitidx", "setspace",
211 "subscript", "textcomp", "ulem", "url", "varioref", "verbatim", "wrapfig",
214 // codes used to remove packages that are loaded automatically by LyX.
215 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
216 const char package_beg_sep = '\001';
217 const char package_mid_sep = '\002';
218 const char package_end_sep = '\003';
221 // returns true if at least one of the options in what has been found
222 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
228 // the last language option is the document language (for babel and LyX)
229 // the last size option is the document font size
230 vector<string>::iterator it;
231 vector<string>::iterator position = opts.begin();
232 for (; *what; ++what) {
233 it = find(opts.begin(), opts.end(), *what);
234 if (it != opts.end()) {
235 if (it >= position) {
246 void delete_opt(vector<string> & opts, char const * const * what)
251 // remove found options from the list
252 // do this after handle_opt to avoid potential memory leaks
253 vector<string>::iterator it;
254 for (; *what; ++what) {
255 it = find(opts.begin(), opts.end(), *what);
256 if (it != opts.end())
263 * Split a package options string (keyval format) into a vector.
265 * authorformat=smallcaps,
267 * titleformat=colonsep,
268 * bibformat={tabular,ibidem,numbered}
270 vector<string> split_options(string const & input)
272 vector<string> options;
276 Token const & t = p.get_token();
277 if (t.asInput() == ",") {
278 options.push_back(trimSpaceAndEol(option));
280 } else if (t.asInput() == "=") {
283 if (p.next_token().asInput() == "{")
284 option += '{' + p.getArg('{', '}') + '}';
285 } else if (t.cat() != catSpace)
286 option += t.asInput();
290 options.push_back(trimSpaceAndEol(option));
297 * Retrieve a keyval option "name={value with=sign}" named \p name from
298 * \p options and return the value.
299 * The found option is also removed from \p options.
301 string process_keyval_opt(vector<string> & options, string name)
303 for (size_t i = 0; i < options.size(); ++i) {
304 vector<string> option;
305 split(options[i], option, '=');
306 if (option.size() < 2)
308 if (option[0] == name) {
309 options.erase(options.begin() + i);
310 option.erase(option.begin());
311 return join(option, "=");
317 } // anonymous namespace
320 bool Preamble::indentParagraphs() const
322 return h_paragraph_separation == "indent";
326 bool Preamble::isPackageUsed(string const & package) const
328 return used_packages.find(package) != used_packages.end();
332 vector<string> Preamble::getPackageOptions(string const & package) const
334 map<string, vector<string> >::const_iterator it = used_packages.find(package);
335 if (it != used_packages.end())
337 return vector<string>();
341 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
343 auto_packages.insert(package);
347 void Preamble::addModule(string const & module)
349 used_modules.push_back(module);
353 void Preamble::suppressDate(bool suppress)
356 h_suppress_date = "true";
358 h_suppress_date = "false";
362 void Preamble::registerAuthor(std::string const & name)
364 Author author(from_utf8(name), empty_docstring());
365 author.setUsed(true);
366 authors_.record(author);
367 h_tracking_changes = "true";
368 h_output_changes = "true";
372 Author const & Preamble::getAuthor(std::string const & name) const
374 Author author(from_utf8(name), empty_docstring());
375 for (AuthorList::Authors::const_iterator it = authors_.begin();
376 it != authors_.end(); ++it)
379 static Author const dummy;
384 void Preamble::add_package(string const & name, vector<string> & options)
386 // every package inherits the global options
387 if (used_packages.find(name) == used_packages.end())
388 used_packages[name] = split_options(h_options);
390 vector<string> & v = used_packages[name];
391 v.insert(v.end(), options.begin(), options.end());
392 if (name == "jurabib") {
393 // Don't output the order argument (see the cite command
394 // handling code in text.cpp).
395 vector<string>::iterator end =
396 remove(options.begin(), options.end(), "natbiborder");
397 end = remove(options.begin(), end, "jurabiborder");
398 options.erase(end, options.end());
405 // Given is a string like "scaled=0.9", return 0.9 * 100
406 string const scale_as_percentage(string const & scale)
408 string::size_type pos = scale.find('=');
409 if (pos != string::npos) {
410 string value = scale.substr(pos + 1);
412 return convert<string>(100 * convert<double>(value));
414 // If the input string didn't match our expectations.
415 // return the default value "100"
420 string remove_braces(string const & value)
424 if (value[0] == '{' && value[value.length()-1] == '}')
425 return value.substr(1, value.length()-2);
429 } // anonymous namespace
432 Preamble::Preamble() : one_language(true), title_layout_found(false)
436 h_biblio_style = "plain";
437 h_cite_engine = "basic";
438 h_cite_engine_type = "numerical";
439 h_defskip = "medskip";
442 h_fontencoding = "default";
443 h_font_roman = "default";
444 h_font_sans = "default";
445 h_font_typewriter = "default";
446 h_font_default_family = "default";
447 h_use_non_tex_fonts = "false";
449 h_font_osf = "false";
450 h_font_sf_scale = "100";
451 h_font_tt_scale = "100";
452 h_graphics = "default";
453 h_default_output_format = "default";
454 h_html_be_strict = "false";
455 h_html_css_as_file = "0";
456 h_html_math_output = "0";
457 h_inputencoding = "auto";
458 h_justification = "true";
459 h_language = "english";
460 h_language_package = "none";
465 h_output_changes = "false";
466 h_papercolumns = "1";
467 h_paperfontsize = "default";
468 h_paperorientation = "portrait";
469 h_paperpagestyle = "default";
471 h_papersize = "default";
472 h_paragraph_indentation = "default";
473 h_paragraph_separation = "indent";
478 h_pdf_bookmarks = "1";
479 h_pdf_bookmarksnumbered = "0";
480 h_pdf_bookmarksopen = "0";
481 h_pdf_bookmarksopenlevel = "1";
482 h_pdf_breaklinks = "0";
483 h_pdf_pdfborder = "0";
484 h_pdf_colorlinks = "0";
485 h_pdf_backref = "section";
486 h_pdf_pdfusetitle = "1";
488 //h_pdf_quoted_options;
489 h_quotes_language = "english";
491 h_spacing = "single";
492 h_suppress_date = "false";
493 h_textclass = "article";
495 h_tracking_changes = "false";
496 h_use_bibtopic = "false";
497 h_use_indices = "false";
498 h_use_geometry = "false";
499 h_use_default_options = "false";
500 h_use_hyperref = "0";
501 h_use_refstyle = "0";
502 h_use_packages["amsmath"] = "1";
503 h_use_packages["amssymb"] = "0";
504 h_use_packages["esint"] = "1";
505 h_use_packages["mhchem"] = "0";
506 h_use_packages["mathdots"] = "0";
507 h_use_packages["mathtools"] = "0";
508 h_use_packages["undertilde"] = "0";
512 void Preamble::handle_hyperref(vector<string> & options)
514 // FIXME swallow inputencoding changes that might surround the
515 // hyperref setup if it was written by LyX
516 h_use_hyperref = "1";
517 // swallow "unicode=true", since LyX does always write that
518 vector<string>::iterator it =
519 find(options.begin(), options.end(), "unicode=true");
520 if (it != options.end())
522 it = find(options.begin(), options.end(), "pdfusetitle");
523 if (it != options.end()) {
524 h_pdf_pdfusetitle = "1";
527 string bookmarks = process_keyval_opt(options, "bookmarks");
528 if (bookmarks == "true")
529 h_pdf_bookmarks = "1";
530 else if (bookmarks == "false")
531 h_pdf_bookmarks = "0";
532 if (h_pdf_bookmarks == "1") {
533 string bookmarksnumbered =
534 process_keyval_opt(options, "bookmarksnumbered");
535 if (bookmarksnumbered == "true")
536 h_pdf_bookmarksnumbered = "1";
537 else if (bookmarksnumbered == "false")
538 h_pdf_bookmarksnumbered = "0";
539 string bookmarksopen =
540 process_keyval_opt(options, "bookmarksopen");
541 if (bookmarksopen == "true")
542 h_pdf_bookmarksopen = "1";
543 else if (bookmarksopen == "false")
544 h_pdf_bookmarksopen = "0";
545 if (h_pdf_bookmarksopen == "1") {
546 string bookmarksopenlevel =
547 process_keyval_opt(options, "bookmarksopenlevel");
548 if (!bookmarksopenlevel.empty())
549 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
552 string breaklinks = process_keyval_opt(options, "breaklinks");
553 if (breaklinks == "true")
554 h_pdf_breaklinks = "1";
555 else if (breaklinks == "false")
556 h_pdf_breaklinks = "0";
557 string pdfborder = process_keyval_opt(options, "pdfborder");
558 if (pdfborder == "{0 0 0}")
559 h_pdf_pdfborder = "1";
560 else if (pdfborder == "{0 0 1}")
561 h_pdf_pdfborder = "0";
562 string backref = process_keyval_opt(options, "backref");
563 if (!backref.empty())
564 h_pdf_backref = backref;
565 string colorlinks = process_keyval_opt(options, "colorlinks");
566 if (colorlinks == "true")
567 h_pdf_colorlinks = "1";
568 else if (colorlinks == "false")
569 h_pdf_colorlinks = "0";
570 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
571 if (!pdfpagemode.empty())
572 h_pdf_pagemode = pdfpagemode;
573 string pdftitle = process_keyval_opt(options, "pdftitle");
574 if (!pdftitle.empty()) {
575 h_pdf_title = remove_braces(pdftitle);
577 string pdfauthor = process_keyval_opt(options, "pdfauthor");
578 if (!pdfauthor.empty()) {
579 h_pdf_author = remove_braces(pdfauthor);
581 string pdfsubject = process_keyval_opt(options, "pdfsubject");
582 if (!pdfsubject.empty())
583 h_pdf_subject = remove_braces(pdfsubject);
584 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
585 if (!pdfkeywords.empty())
586 h_pdf_keywords = remove_braces(pdfkeywords);
587 if (!options.empty()) {
588 if (!h_pdf_quoted_options.empty())
589 h_pdf_quoted_options += ',';
590 h_pdf_quoted_options += join(options, ",");
596 void Preamble::handle_geometry(vector<string> & options)
598 h_use_geometry = "true";
599 vector<string>::iterator it;
601 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
602 h_paperorientation = "landscape";
606 // keyval version: "paper=letter"
607 string paper = process_keyval_opt(options, "paper");
609 h_papersize = paper + "paper";
610 // alternative version: "letterpaper"
611 handle_opt(options, known_paper_sizes, h_papersize);
612 delete_opt(options, known_paper_sizes);
614 char const * const * margin = known_paper_margins;
615 for (; *margin; ++margin) {
616 string value = process_keyval_opt(options, *margin);
617 if (!value.empty()) {
618 int k = margin - known_paper_margins;
619 string name = known_coded_paper_margins[k];
620 h_margins += '\\' + name + ' ' + value + '\n';
626 void Preamble::handle_package(Parser &p, string const & name,
627 string const & opts, bool in_lyx_preamble)
629 vector<string> options = split_options(opts);
630 add_package(name, options);
633 if (is_known(name, known_xetex_packages)) {
635 h_use_non_tex_fonts = "true";
636 registerAutomaticallyLoadedPackage("fontspec");
637 if (h_inputencoding == "auto")
638 p.setEncoding("utf8");
642 if (is_known(name, known_roman_fonts)) {
647 if (name == "fourier") {
648 h_font_roman = "utopia";
649 // when font uses real small capitals
650 if (opts == "expert")
654 else if (name == "mathpazo")
655 h_font_roman = "palatino";
657 else if (name == "mathptmx")
658 h_font_roman = "times";
661 if (is_known(name, known_sans_fonts)) {
665 h_font_sf_scale = scale_as_percentage(scale);
670 if (is_known(name, known_typewriter_fonts)) {
671 // fourier can be set as roman font _only_
672 // fourier as typewriter is handled in handling of \ttdefault
673 if (name != "fourier") {
674 h_font_typewriter = name;
677 h_font_tt_scale = scale_as_percentage(scale);
682 // font uses old-style figure
686 // after the detection and handling of special cases, we can remove the
687 // fonts, otherwise they would appear in the preamble, see bug #7856
688 if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
689 || is_known(name, known_typewriter_fonts))
692 else if (name == "amsmath" || name == "amssymb" ||
693 name == "esint" || name == "mhchem" || name == "mathdots" ||
694 name == "mathtools" || name == "undertilde")
695 h_use_packages[name] = "2";
697 else if (name == "babel") {
698 h_language_package = "default";
699 // One might think we would have to do nothing if babel is loaded
700 // without any options to prevent pollution of the preamble with this
701 // babel call in every roundtrip.
702 // But the user could have defined babel-specific things afterwards. So
703 // we need to keep it in the preamble to prevent cases like bug #7861.
705 // check if more than one option was used - used later for inputenc
706 if (options.begin() != options.end() - 1)
707 one_language = false;
708 // babel takes the last language of the option of its \usepackage
709 // call as document language. If there is no such language option, the
710 // last language in the documentclass options is used.
711 handle_opt(options, known_languages, h_language);
712 // If babel is called with options, LyX puts them by default into the
713 // document class options. This works for most languages, except
714 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
715 // perhaps in future others.
716 // Therefore keep the babel call as it is as the user might have
718 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
719 delete_opt(options, known_languages);
720 // finally translate the babel name to a LyX name
721 h_language = babel2lyx(h_language);
724 h_preamble << "\\usepackage{babel}\n";
727 else if (name == "polyglossia") {
728 h_language_package = "default";
729 h_default_output_format = "pdf4";
730 h_use_non_tex_fonts = "true";
732 registerAutomaticallyLoadedPackage("xunicode");
733 if (h_inputencoding == "auto")
734 p.setEncoding("utf8");
737 else if (name == "CJK") {
738 // It is impossible to determine the document language if CJK is used.
739 // All we can do is to notify the user that he has to set this by hisself.
741 // set the encoding to "auto" because it might be set to "default" by the babel handling
742 // and this would not be correct for CJK
743 h_inputencoding = "auto";
744 registerAutomaticallyLoadedPackage("CJK");
747 else if (name == "fontenc") {
748 h_fontencoding = getStringFromVector(options, ",");
749 /* We could do the following for better round trip support,
750 * but this makes the document less portable, so I skip it:
751 if (h_fontencoding == lyxrc.fontenc)
752 h_fontencoding = "global";
757 else if (name == "inputenc" || name == "luainputenc") {
758 // h_inputencoding is only set when there is not more than one
759 // inputenc option because otherwise h_inputencoding must be
760 // set to "auto" (the default encoding of the document language)
761 // Therefore check for the "," character.
762 // It is also only set when there is not more than one babel
764 if (opts.find(",") == string::npos && one_language == true)
765 h_inputencoding = opts;
766 if (!options.empty())
767 p.setEncoding(options.back());
771 else if (is_known(name, known_old_language_packages)) {
772 // known language packages from the times before babel
773 // if they are found and not also babel, they will be used as
774 // custom language package
775 h_language_package = "\\usepackage{" + name + "}";
778 else if (name == "prettyref")
779 ; // ignore this FIXME: Use the package separator mechanism instead
781 else if (name == "lyxskak") {
782 // ignore this and its options
783 const char * const o[] = {"ps", "mover", 0};
784 delete_opt(options, o);
787 else if (is_known(name, known_lyx_packages) && options.empty()) {
788 if (name == "splitidx")
789 h_use_indices = "true";
790 if (!in_lyx_preamble)
791 h_preamble << package_beg_sep << name
792 << package_mid_sep << "\\usepackage{"
793 << name << "}\n" << package_end_sep;
796 else if (name == "geometry")
797 handle_geometry(options);
799 else if (name == "subfig")
800 ; // ignore this FIXME: Use the package separator mechanism instead
802 else if (is_known(name, known_languages))
805 else if (name == "natbib") {
806 h_biblio_style = "plainnat";
807 h_cite_engine = "natbib";
808 h_cite_engine_type = "authoryear";
809 vector<string>::iterator it =
810 find(options.begin(), options.end(), "authoryear");
811 if (it != options.end())
814 it = find(options.begin(), options.end(), "numbers");
815 if (it != options.end()) {
816 h_cite_engine_type = "numerical";
822 else if (name == "jurabib") {
823 h_biblio_style = "jurabib";
824 h_cite_engine = "jurabib";
825 h_cite_engine_type = "authoryear";
828 else if (name == "hyperref")
829 handle_hyperref(options);
831 else if (!in_lyx_preamble) {
833 h_preamble << "\\usepackage{" << name << "}\n";
835 h_preamble << "\\usepackage[" << opts << "]{"
841 // We need to do something with the options...
842 if (!options.empty())
843 cerr << "Ignoring options '" << join(options, ",")
844 << "' of package " << name << '.' << endl;
846 // remove the whitespace
851 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
854 Token t = p.get_token();
855 if (t.cat() == catEscape &&
856 is_known(t.cs(), known_if_commands))
857 handle_if(p, in_lyx_preamble);
859 if (!in_lyx_preamble)
860 h_preamble << t.asInput();
861 if (t.cat() == catEscape && t.cs() == "fi")
868 bool Preamble::writeLyXHeader(ostream & os, bool subdoc)
870 // set the quote language
871 // LyX only knows the following quotes languages:
872 // english, swedish, german, polish, french and danish
873 // (quotes for "japanese" and "chinese-traditional" are missing because
874 // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
875 // conversion list taken from
876 // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
877 // (quotes for kazakh and interlingua are unknown)
879 if (h_language == "danish")
880 h_quotes_language = "danish";
882 else if (is_known(h_language, known_french_quotes_languages))
883 h_quotes_language = "french";
885 else if (is_known(h_language, known_german_quotes_languages))
886 h_quotes_language = "german";
888 else if (is_known(h_language, known_polish_quotes_languages))
889 h_quotes_language = "polish";
891 else if (is_known(h_language, known_swedish_quotes_languages))
892 h_quotes_language = "swedish";
894 else if (is_known(h_language, known_english_quotes_languages))
895 h_quotes_language = "english";
897 if (contains(h_float_placement, "H"))
898 registerAutomaticallyLoadedPackage("float");
899 if (h_spacing != "single" && h_spacing != "default")
900 registerAutomaticallyLoadedPackage("setspace");
901 if (h_use_packages["amsmath"] == "2") {
902 // amsbsy and amstext are already provided by amsmath
903 registerAutomaticallyLoadedPackage("amsbsy");
904 registerAutomaticallyLoadedPackage("amstext");
907 // output the LyX file settings
908 os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
909 << "\\lyxformat " << LYX_FORMAT << '\n'
910 << "\\begin_document\n"
911 << "\\begin_header\n"
912 << "\\textclass " << h_textclass << "\n";
913 string const raw = subdoc ? empty_string() : h_preamble.str();
915 os << "\\begin_preamble\n";
916 for (string::size_type i = 0; i < raw.size(); ++i) {
917 if (raw[i] == package_beg_sep) {
918 // Here follows some package loading code that
919 // must be skipped if the package is loaded
921 string::size_type j = raw.find(package_mid_sep, i);
922 if (j == string::npos)
924 string::size_type k = raw.find(package_end_sep, j);
925 if (k == string::npos)
927 string const package = raw.substr(i + 1, j - i - 1);
928 string const replacement = raw.substr(j + 1, k - j - 1);
929 if (auto_packages.find(package) == auto_packages.end())
935 os << "\n\\end_preamble\n";
937 if (!h_options.empty())
938 os << "\\options " << h_options << "\n";
939 os << "\\use_default_options " << h_use_default_options << "\n";
940 if (!used_modules.empty()) {
941 os << "\\begin_modules\n";
942 vector<string>::const_iterator const end = used_modules.end();
943 vector<string>::const_iterator it = used_modules.begin();
944 for (; it != end; ++it)
946 os << "\\end_modules\n";
948 os << "\\language " << h_language << "\n"
949 << "\\language_package " << h_language_package << "\n"
950 << "\\inputencoding " << h_inputencoding << "\n"
951 << "\\fontencoding " << h_fontencoding << "\n"
952 << "\\font_roman " << h_font_roman << "\n"
953 << "\\font_sans " << h_font_sans << "\n"
954 << "\\font_typewriter " << h_font_typewriter << "\n"
955 << "\\font_default_family " << h_font_default_family << "\n"
956 << "\\use_non_tex_fonts " << h_use_non_tex_fonts << "\n"
957 << "\\font_sc " << h_font_sc << "\n"
958 << "\\font_osf " << h_font_osf << "\n"
959 << "\\font_sf_scale " << h_font_sf_scale << "\n"
960 << "\\font_tt_scale " << h_font_tt_scale << "\n"
961 << "\\graphics " << h_graphics << "\n"
962 << "\\default_output_format " << h_default_output_format << "\n";
963 if (!h_float_placement.empty())
964 os << "\\float_placement " << h_float_placement << "\n";
965 os << "\\paperfontsize " << h_paperfontsize << "\n"
966 << "\\spacing " << h_spacing << "\n"
967 << "\\use_hyperref " << h_use_hyperref << '\n';
968 if (h_use_hyperref == "1") {
969 if (!h_pdf_title.empty())
970 os << "\\pdf_title \"" << h_pdf_title << "\"\n";
971 if (!h_pdf_author.empty())
972 os << "\\pdf_author \"" << h_pdf_author << "\"\n";
973 if (!h_pdf_subject.empty())
974 os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
975 if (!h_pdf_keywords.empty())
976 os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
977 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
978 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
979 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
980 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
981 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
982 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
983 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
984 "\\pdf_backref " << h_pdf_backref << "\n"
985 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
986 if (!h_pdf_pagemode.empty())
987 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
988 if (!h_pdf_quoted_options.empty())
989 os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
991 os << "\\papersize " << h_papersize << "\n"
992 << "\\use_geometry " << h_use_geometry << '\n';
993 for (map<string, string>::const_iterator it = h_use_packages.begin();
994 it != h_use_packages.end(); ++it)
995 os << "\\use_package " << it->first << ' ' << it->second << '\n';
996 os << "\\cite_engine " << h_cite_engine << '\n'
997 << "\\cite_engine_type " << h_cite_engine_type << '\n'
998 << "\\biblio_style " << h_biblio_style << "\n"
999 << "\\use_bibtopic " << h_use_bibtopic << "\n"
1000 << "\\use_indices " << h_use_indices << "\n"
1001 << "\\paperorientation " << h_paperorientation << '\n'
1002 << "\\suppress_date " << h_suppress_date << '\n'
1003 << "\\justification " << h_justification << '\n'
1004 << "\\use_refstyle " << h_use_refstyle << '\n';
1005 if (!h_fontcolor.empty())
1006 os << "\\fontcolor " << h_fontcolor << '\n';
1007 if (!h_notefontcolor.empty())
1008 os << "\\notefontcolor " << h_notefontcolor << '\n';
1009 if (!h_backgroundcolor.empty())
1010 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1011 if (!h_boxbgcolor.empty())
1012 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1014 << "\\secnumdepth " << h_secnumdepth << "\n"
1015 << "\\tocdepth " << h_tocdepth << "\n"
1016 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1017 if (h_paragraph_separation == "skip")
1018 os << "\\defskip " << h_defskip << "\n";
1020 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1021 os << "\\quotes_language " << h_quotes_language << "\n"
1022 << "\\papercolumns " << h_papercolumns << "\n"
1023 << "\\papersides " << h_papersides << "\n"
1024 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1025 if (!h_listings_params.empty())
1026 os << "\\listings_params " << h_listings_params << "\n";
1027 os << "\\tracking_changes " << h_tracking_changes << "\n"
1028 << "\\output_changes " << h_output_changes << "\n"
1029 << "\\html_math_output " << h_html_math_output << "\n"
1030 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1031 << "\\html_be_strict " << h_html_be_strict << "\n"
1033 << "\\end_header\n\n"
1034 << "\\begin_body\n";
1039 void Preamble::parse(Parser & p, string const & forceclass,
1040 TeX2LyXDocClass & tc)
1042 // initialize fixed types
1043 special_columns['D'] = 3;
1044 bool is_full_document = false;
1045 bool is_lyx_file = false;
1046 bool in_lyx_preamble = false;
1048 // determine whether this is a full document or a fragment for inclusion
1050 Token const & t = p.get_token();
1052 if (t.cat() == catEscape && t.cs() == "documentclass") {
1053 is_full_document = true;
1059 while (is_full_document && p.good()) {
1060 Token const & t = p.get_token();
1063 cerr << "t: " << t << "\n";
1069 if (!in_lyx_preamble &&
1070 (t.cat() == catLetter ||
1071 t.cat() == catSuper ||
1072 t.cat() == catSub ||
1073 t.cat() == catOther ||
1074 t.cat() == catMath ||
1075 t.cat() == catActive ||
1076 t.cat() == catBegin ||
1077 t.cat() == catEnd ||
1078 t.cat() == catAlign ||
1079 t.cat() == catParameter))
1080 h_preamble << t.cs();
1082 else if (!in_lyx_preamble &&
1083 (t.cat() == catSpace || t.cat() == catNewline))
1084 h_preamble << t.asInput();
1086 else if (t.cat() == catComment) {
1087 static regex const islyxfile("%% LyX .* created this file");
1088 static regex const usercommands("User specified LaTeX commands");
1090 string const comment = t.asInput();
1092 // magically switch encoding default if it looks like XeLaTeX
1093 static string const magicXeLaTeX =
1094 "% This document must be compiled with XeLaTeX ";
1095 if (comment.size() > magicXeLaTeX.size()
1096 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1097 && h_inputencoding == "auto") {
1098 cerr << "XeLaTeX comment found, switching to UTF8\n";
1099 h_inputencoding = "utf8";
1102 if (regex_search(comment, sub, islyxfile)) {
1104 in_lyx_preamble = true;
1105 } else if (is_lyx_file
1106 && regex_search(comment, sub, usercommands))
1107 in_lyx_preamble = false;
1108 else if (!in_lyx_preamble)
1109 h_preamble << t.asInput();
1112 else if (t.cs() == "pagestyle")
1113 h_paperpagestyle = p.verbatim_item();
1115 else if (t.cs() == "setdefaultlanguage") {
1117 // We don't yet care about non-language variant options
1118 // because LyX doesn't support this yet, see bug #8214
1120 string langopts = p.getOpt();
1121 // check if the option contains a variant, if yes, extract it
1122 string::size_type pos_var = langopts.find("variant");
1123 string::size_type i = langopts.find(',', pos_var);
1124 if (pos_var != string::npos){
1126 if (i == string::npos)
1127 variant = langopts.substr(pos_var + 8, langopts.length() - pos_var - 9);
1129 variant = langopts.substr(pos_var + 8, i - pos_var - 8);
1130 h_language = variant;
1134 h_language = p.verbatim_item();
1135 //finally translate the poyglossia name to a LyX name
1136 h_language = polyglossia2lyx(h_language);
1139 else if (t.cs() == "setotherlanguage") {
1140 // We don't yet care about the option because LyX doesn't
1141 // support this yet, see bug #8214
1142 p.hasOpt() ? p.getOpt() : string();
1146 else if (t.cs() == "setmainfont") {
1147 // we don't care about the option
1148 p.hasOpt() ? p.getOpt() : string();
1149 h_font_roman = p.getArg('{', '}');
1152 else if (t.cs() == "setsansfont") {
1153 // we don't care about the option
1154 p.hasOpt() ? p.getOpt() : string();
1155 h_font_sans = p.getArg('{', '}');
1158 else if (t.cs() == "setmonofont") {
1159 // we don't care about the option
1160 p.hasOpt() ? p.getOpt() : string();
1161 h_font_typewriter = p.getArg('{', '}');
1164 else if (t.cs() == "date") {
1165 string argument = p.getArg('{', '}');
1166 if (argument.empty())
1167 h_suppress_date = "true";
1169 h_preamble << t.asInput() << '{' << argument << '}';
1172 else if (t.cs() == "color") {
1173 string const space =
1174 (p.hasOpt() ? p.getOpt() : string());
1175 string argument = p.getArg('{', '}');
1176 // check the case that a standard color is used
1177 if (space.empty() && is_known(argument, known_basic_colors)) {
1178 h_fontcolor = rgbcolor2code(argument);
1179 preamble.registerAutomaticallyLoadedPackage("color");
1180 } else if (space.empty() && argument == "document_fontcolor")
1181 preamble.registerAutomaticallyLoadedPackage("color");
1182 // check the case that LyX's document_fontcolor is defined
1183 // but not used for \color
1185 h_preamble << t.asInput();
1187 h_preamble << space;
1188 h_preamble << '{' << argument << '}';
1189 // the color might already be set because \definecolor
1190 // is parsed before this
1195 else if (t.cs() == "pagecolor") {
1196 string argument = p.getArg('{', '}');
1197 // check the case that a standard color is used
1198 if (is_known(argument, known_basic_colors)) {
1199 h_backgroundcolor = rgbcolor2code(argument);
1200 } else if (argument == "page_backgroundcolor")
1201 preamble.registerAutomaticallyLoadedPackage("color");
1202 // check the case that LyX's page_backgroundcolor is defined
1203 // but not used for \pagecolor
1205 h_preamble << t.asInput() << '{' << argument << '}';
1206 // the color might already be set because \definecolor
1207 // is parsed before this
1208 h_backgroundcolor = "";
1212 else if (t.cs() == "makeatletter") {
1213 // LyX takes care of this
1214 p.setCatCode('@', catLetter);
1217 else if (t.cs() == "makeatother") {
1218 // LyX takes care of this
1219 p.setCatCode('@', catOther);
1222 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1223 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1224 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1225 || t.cs() == "DeclareRobustCommand"
1226 || t.cs() == "DeclareRobustCommandx"
1227 || t.cs() == "ProvideTextCommandDefault"
1228 || t.cs() == "DeclareMathAccent") {
1230 if (p.next_token().character() == '*') {
1234 string const name = p.verbatim_item();
1235 string const opt1 = p.getFullOpt();
1236 string const opt2 = p.getFullOpt();
1237 string const body = p.verbatim_item();
1239 if (name == "\\rmdefault")
1240 if (is_known(body, known_roman_fonts))
1241 h_font_roman = body;
1242 if (name == "\\sfdefault")
1243 if (is_known(body, known_sans_fonts))
1245 if (name == "\\ttdefault")
1246 if (is_known(body, known_typewriter_fonts))
1247 h_font_typewriter = body;
1248 if (name == "\\familydefault") {
1249 string family = body;
1250 // remove leading "\"
1251 h_font_default_family = family.erase(0,1);
1254 // remove the lyxdot definition that is re-added by LyX
1256 if (name == "\\lyxdot")
1257 in_lyx_preamble = true;
1259 // Add the command to the known commands
1260 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1262 // only non-lyxspecific stuff
1263 if (!in_lyx_preamble) {
1265 ss << '\\' << t.cs();
1268 ss << '{' << name << '}' << opt1 << opt2
1269 << '{' << body << "}";
1270 h_preamble << ss.str();
1272 ostream & out = in_preamble ? h_preamble : os;
1273 out << "\\" << t.cs() << "{" << name << "}"
1274 << opts << "{" << body << "}";
1279 else if (t.cs() == "documentclass") {
1280 vector<string>::iterator it;
1281 vector<string> opts = split_options(p.getArg('[', ']'));
1282 handle_opt(opts, known_fontsizes, h_paperfontsize);
1283 delete_opt(opts, known_fontsizes);
1284 // delete "pt" at the end
1285 string::size_type i = h_paperfontsize.find("pt");
1286 if (i != string::npos)
1287 h_paperfontsize.erase(i);
1288 // The documentclass options are always parsed before the options
1289 // of the babel call so that a language cannot overwrite the babel
1291 handle_opt(opts, known_languages, h_language);
1292 delete_opt(opts, known_languages);
1294 // paper orientation
1295 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1296 h_paperorientation = "landscape";
1300 if ((it = find(opts.begin(), opts.end(), "oneside"))
1305 if ((it = find(opts.begin(), opts.end(), "twoside"))
1311 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1313 h_papercolumns = "1";
1316 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1318 h_papercolumns = "2";
1322 // some size options are known to any document classes, other sizes
1323 // are handled by the \geometry command of the geometry package
1324 handle_opt(opts, known_class_paper_sizes, h_papersize);
1325 delete_opt(opts, known_class_paper_sizes);
1326 // the remaining options
1327 h_options = join(opts, ",");
1328 // FIXME This does not work for classes that have a
1329 // different name in LyX than in LaTeX
1330 h_textclass = p.getArg('{', '}');
1333 else if (t.cs() == "usepackage") {
1334 string const options = p.getArg('[', ']');
1335 string const name = p.getArg('{', '}');
1336 vector<string> vecnames;
1337 split(name, vecnames, ',');
1338 vector<string>::const_iterator it = vecnames.begin();
1339 vector<string>::const_iterator end = vecnames.end();
1340 for (; it != end; ++it)
1341 handle_package(p, trimSpaceAndEol(*it), options,
1345 else if (t.cs() == "inputencoding") {
1346 string const encoding = p.getArg('{','}');
1347 h_inputencoding = encoding;
1348 p.setEncoding(encoding);
1351 else if (t.cs() == "newenvironment") {
1352 string const name = p.getArg('{', '}');
1353 string const opt1 = p.getFullOpt();
1354 string const opt2 = p.getFullOpt();
1355 string const beg = p.verbatim_item();
1356 string const end = p.verbatim_item();
1357 if (!in_lyx_preamble) {
1358 h_preamble << "\\newenvironment{" << name
1359 << '}' << opt1 << opt2 << '{'
1360 << beg << "}{" << end << '}';
1362 add_known_environment(name, opt1, !opt2.empty(),
1363 from_utf8(beg), from_utf8(end));
1367 else if (t.cs() == "def") {
1368 string name = p.get_token().cs();
1369 // In fact, name may be more than the name:
1370 // In the test case of bug 8116
1371 // name == "csname SF@gobble@opt \endcsname".
1372 // Therefore, we need to use asInput() instead of cs().
1373 while (p.next_token().cat() != catBegin)
1374 name += p.get_token().asInput();
1375 if (!in_lyx_preamble)
1376 h_preamble << "\\def\\" << name << '{'
1377 << p.verbatim_item() << "}";
1380 else if (t.cs() == "newcolumntype") {
1381 string const name = p.getArg('{', '}');
1382 trimSpaceAndEol(name);
1384 string opts = p.getOpt();
1385 if (!opts.empty()) {
1386 istringstream is(string(opts, 1));
1389 special_columns[name[0]] = nargs;
1390 h_preamble << "\\newcolumntype{" << name << "}";
1392 h_preamble << "[" << nargs << "]";
1393 h_preamble << "{" << p.verbatim_item() << "}";
1396 else if (t.cs() == "setcounter") {
1397 string const name = p.getArg('{', '}');
1398 string const content = p.getArg('{', '}');
1399 if (name == "secnumdepth")
1400 h_secnumdepth = content;
1401 else if (name == "tocdepth")
1402 h_tocdepth = content;
1404 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1407 else if (t.cs() == "setlength") {
1408 string const name = p.verbatim_item();
1409 string const content = p.verbatim_item();
1410 // the paragraphs are only not indented when \parindent is set to zero
1411 if (name == "\\parindent" && content != "") {
1412 if (content[0] == '0')
1413 h_paragraph_separation = "skip";
1415 h_paragraph_indentation = translate_len(content);
1416 } else if (name == "\\parskip") {
1417 if (content == "\\smallskipamount")
1418 h_defskip = "smallskip";
1419 else if (content == "\\medskipamount")
1420 h_defskip = "medskip";
1421 else if (content == "\\bigskipamount")
1422 h_defskip = "bigskip";
1424 h_defskip = content;
1426 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1429 else if (t.cs() == "onehalfspacing")
1430 h_spacing = "onehalf";
1432 else if (t.cs() == "doublespacing")
1433 h_spacing = "double";
1435 else if (t.cs() == "setstretch")
1436 h_spacing = "other " + p.verbatim_item();
1438 else if (t.cs() == "begin") {
1439 string const name = p.getArg('{', '}');
1440 if (name == "document")
1442 h_preamble << "\\begin{" << name << "}";
1445 else if (t.cs() == "geometry") {
1446 vector<string> opts = split_options(p.getArg('{', '}'));
1447 handle_geometry(opts);
1450 else if (t.cs() == "definecolor") {
1451 string const color = p.getArg('{', '}');
1452 string const space = p.getArg('{', '}');
1453 string const value = p.getArg('{', '}');
1454 if (color == "document_fontcolor" && space == "rgb") {
1455 RGBColor c(RGBColorFromLaTeX(value));
1456 h_fontcolor = X11hexname(c);
1457 } else if (color == "note_fontcolor" && space == "rgb") {
1458 RGBColor c(RGBColorFromLaTeX(value));
1459 h_notefontcolor = X11hexname(c);
1460 } else if (color == "page_backgroundcolor" && space == "rgb") {
1461 RGBColor c(RGBColorFromLaTeX(value));
1462 h_backgroundcolor = X11hexname(c);
1463 } else if (color == "shadecolor" && space == "rgb") {
1464 RGBColor c(RGBColorFromLaTeX(value));
1465 h_boxbgcolor = X11hexname(c);
1467 h_preamble << "\\definecolor{" << color
1468 << "}{" << space << "}{" << value
1473 else if (t.cs() == "bibliographystyle")
1474 h_biblio_style = p.verbatim_item();
1476 else if (t.cs() == "jurabibsetup") {
1477 // FIXME p.getArg('{', '}') is most probably wrong (it
1478 // does not handle nested braces).
1479 // Use p.verbatim_item() instead.
1480 vector<string> jurabibsetup =
1481 split_options(p.getArg('{', '}'));
1482 // add jurabibsetup to the jurabib package options
1483 add_package("jurabib", jurabibsetup);
1484 if (!jurabibsetup.empty()) {
1485 h_preamble << "\\jurabibsetup{"
1486 << join(jurabibsetup, ",") << '}';
1490 else if (t.cs() == "hypersetup") {
1491 vector<string> hypersetup =
1492 split_options(p.verbatim_item());
1493 // add hypersetup to the hyperref package options
1494 handle_hyperref(hypersetup);
1495 if (!hypersetup.empty()) {
1496 h_preamble << "\\hypersetup{"
1497 << join(hypersetup, ",") << '}';
1501 else if (is_known(t.cs(), known_if_3arg_commands)) {
1502 // prevent misparsing of \usepackage if it is used
1503 // as an argument (see e.g. our own output of
1504 // \@ifundefined above)
1505 string const arg1 = p.verbatim_item();
1506 string const arg2 = p.verbatim_item();
1507 string const arg3 = p.verbatim_item();
1508 // test case \@ifundefined{date}{}{\date{}}
1509 if (t.cs() == "@ifundefined" && arg1 == "date" &&
1510 arg2.empty() && arg3 == "\\date{}") {
1511 h_suppress_date = "true";
1512 // older tex2lyx versions did output
1513 // \@ifundefined{definecolor}{\usepackage{color}}{}
1514 } else if (t.cs() == "@ifundefined" &&
1515 arg1 == "definecolor" &&
1516 arg2 == "\\usepackage{color}" &&
1518 if (!in_lyx_preamble)
1519 h_preamble << package_beg_sep
1522 << "\\@ifundefined{definecolor}{color}{}"
1525 //\@ifundefined{showcaptionsetup}{}{%
1526 // \PassOptionsToPackage{caption=false}{subfig}}
1527 // that LyX uses for subfloats
1528 } else if (t.cs() == "@ifundefined" &&
1529 arg1 == "showcaptionsetup" && arg2.empty()
1530 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1532 } else if (!in_lyx_preamble) {
1533 h_preamble << t.asInput()
1534 << '{' << arg1 << '}'
1535 << '{' << arg2 << '}'
1536 << '{' << arg3 << '}';
1540 else if (is_known(t.cs(), known_if_commands)) {
1541 // must not parse anything in conditional code, since
1542 // LyX would output the parsed contents unconditionally
1543 if (!in_lyx_preamble)
1544 h_preamble << t.asInput();
1545 handle_if(p, in_lyx_preamble);
1548 else if (!t.cs().empty() && !in_lyx_preamble)
1549 h_preamble << '\\' << t.cs();
1552 // remove the whitespace
1555 // Force textclass if the user wanted it
1556 if (!forceclass.empty())
1557 h_textclass = forceclass;
1558 if (noweb_mode && !prefixIs(h_textclass, "literate-"))
1559 h_textclass.insert(0, "literate-");
1560 tc.setName(h_textclass);
1562 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1565 if (h_papersides.empty()) {
1568 h_papersides = ss.str();
1573 string babel2lyx(string const & language)
1575 char const * const * where = is_known(language, known_languages);
1577 return known_coded_languages[where - known_languages];
1582 string polyglossia2lyx(string const & language)
1584 char const * const * where = is_known(language, polyglossia_languages);
1586 return coded_polyglossia_languages[where - polyglossia_languages];
1591 string rgbcolor2code(string const & name)
1593 char const * const * where = is_known(name, known_basic_colors);
1595 // "red", "green" etc
1596 return known_basic_color_codes[where - known_basic_colors];
1598 // "255,0,0", "0,255,0" etc
1599 RGBColor c(RGBColorFromLaTeX(name));
1600 return X11hexname(c);