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", "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 == "fontenc") {
738 h_fontencoding = getStringFromVector(options, ",");
739 /* We could do the following for better round trip support,
740 * but this makes the document less portable, so I skip it:
741 if (h_fontencoding == lyxrc.fontenc)
742 h_fontencoding = "global";
747 else if (name == "inputenc" || name == "luainputenc") {
748 // h_inputencoding is only set when there is not more than one
749 // inputenc option because otherwise h_inputencoding must be
750 // set to "auto" (the default encoding of the document language)
751 // Therefore check for the "," character.
752 // It is also only set when there is not more than one babel
754 if (opts.find(",") == string::npos && one_language == true)
755 h_inputencoding = opts;
756 if (!options.empty())
757 p.setEncoding(options.back());
761 else if (is_known(name, known_old_language_packages)) {
762 // known language packages from the times before babel
763 // if they are found and not also babel, they will be used as
764 // custom language package
765 h_language_package = "\\usepackage{" + name + "}";
768 else if (name == "prettyref")
769 ; // ignore this FIXME: Use the package separator mechanism instead
771 else if (name == "lyxskak") {
772 // ignore this and its options
773 const char * const o[] = {"ps", "mover", 0};
774 delete_opt(options, o);
777 else if (is_known(name, known_lyx_packages) && options.empty()) {
778 if (name == "splitidx")
779 h_use_indices = "true";
780 if (!in_lyx_preamble)
781 h_preamble << package_beg_sep << name
782 << package_mid_sep << "\\usepackage{"
783 << name << "}\n" << package_end_sep;
786 else if (name == "geometry")
787 handle_geometry(options);
789 else if (name == "subfig")
790 ; // ignore this FIXME: Use the package separator mechanism instead
792 else if (is_known(name, known_languages))
795 else if (name == "natbib") {
796 h_biblio_style = "plainnat";
797 h_cite_engine = "natbib";
798 h_cite_engine_type = "authoryear";
799 vector<string>::iterator it =
800 find(options.begin(), options.end(), "authoryear");
801 if (it != options.end())
804 it = find(options.begin(), options.end(), "numbers");
805 if (it != options.end()) {
806 h_cite_engine_type = "numerical";
812 else if (name == "jurabib") {
813 h_biblio_style = "jurabib";
814 h_cite_engine = "jurabib";
815 h_cite_engine_type = "authoryear";
818 else if (name == "hyperref")
819 handle_hyperref(options);
821 else if (!in_lyx_preamble) {
823 h_preamble << "\\usepackage{" << name << "}\n";
825 h_preamble << "\\usepackage[" << opts << "]{"
831 // We need to do something with the options...
832 if (!options.empty())
833 cerr << "Ignoring options '" << join(options, ",")
834 << "' of package " << name << '.' << endl;
836 // remove the whitespace
841 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
844 Token t = p.get_token();
845 if (t.cat() == catEscape &&
846 is_known(t.cs(), known_if_commands))
847 handle_if(p, in_lyx_preamble);
849 if (!in_lyx_preamble)
850 h_preamble << t.asInput();
851 if (t.cat() == catEscape && t.cs() == "fi")
858 bool Preamble::writeLyXHeader(ostream & os, bool subdoc)
860 // set the quote language
861 // LyX only knows the following quotes languages:
862 // english, swedish, german, polish, french and danish
863 // (quotes for "japanese" and "chinese-traditional" are missing because
864 // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
865 // conversion list taken from
866 // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
867 // (quotes for kazakh and interlingua are unknown)
869 if (h_language == "danish")
870 h_quotes_language = "danish";
872 else if (is_known(h_language, known_french_quotes_languages))
873 h_quotes_language = "french";
875 else if (is_known(h_language, known_german_quotes_languages))
876 h_quotes_language = "german";
878 else if (is_known(h_language, known_polish_quotes_languages))
879 h_quotes_language = "polish";
881 else if (is_known(h_language, known_swedish_quotes_languages))
882 h_quotes_language = "swedish";
884 else if (is_known(h_language, known_english_quotes_languages))
885 h_quotes_language = "english";
887 if (contains(h_float_placement, "H"))
888 registerAutomaticallyLoadedPackage("float");
889 if (h_spacing != "single" && h_spacing != "default")
890 registerAutomaticallyLoadedPackage("setspace");
891 if (h_use_packages["amsmath"] == "2") {
892 // amsbsy and amstext are already provided by amsmath
893 registerAutomaticallyLoadedPackage("amsbsy");
894 registerAutomaticallyLoadedPackage("amstext");
897 // output the LyX file settings
898 os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
899 << "\\lyxformat " << LYX_FORMAT << '\n'
900 << "\\begin_document\n"
901 << "\\begin_header\n"
902 << "\\textclass " << h_textclass << "\n";
903 string const raw = subdoc ? empty_string() : h_preamble.str();
905 os << "\\begin_preamble\n";
906 for (string::size_type i = 0; i < raw.size(); ++i) {
907 if (raw[i] == package_beg_sep) {
908 // Here follows some package loading code that
909 // must be skipped if the package is loaded
911 string::size_type j = raw.find(package_mid_sep, i);
912 if (j == string::npos)
914 string::size_type k = raw.find(package_end_sep, j);
915 if (k == string::npos)
917 string const package = raw.substr(i + 1, j - i - 1);
918 string const replacement = raw.substr(j + 1, k - j - 1);
919 if (auto_packages.find(package) == auto_packages.end())
925 os << "\n\\end_preamble\n";
927 if (!h_options.empty())
928 os << "\\options " << h_options << "\n";
929 os << "\\use_default_options " << h_use_default_options << "\n";
930 if (!used_modules.empty()) {
931 os << "\\begin_modules\n";
932 vector<string>::const_iterator const end = used_modules.end();
933 vector<string>::const_iterator it = used_modules.begin();
934 for (; it != end; ++it)
936 os << "\\end_modules\n";
938 os << "\\language " << h_language << "\n"
939 << "\\language_package " << h_language_package << "\n"
940 << "\\inputencoding " << h_inputencoding << "\n"
941 << "\\fontencoding " << h_fontencoding << "\n"
942 << "\\font_roman " << h_font_roman << "\n"
943 << "\\font_sans " << h_font_sans << "\n"
944 << "\\font_typewriter " << h_font_typewriter << "\n"
945 << "\\font_default_family " << h_font_default_family << "\n"
946 << "\\use_non_tex_fonts " << h_use_non_tex_fonts << "\n"
947 << "\\font_sc " << h_font_sc << "\n"
948 << "\\font_osf " << h_font_osf << "\n"
949 << "\\font_sf_scale " << h_font_sf_scale << "\n"
950 << "\\font_tt_scale " << h_font_tt_scale << "\n"
951 << "\\graphics " << h_graphics << "\n"
952 << "\\default_output_format " << h_default_output_format << "\n";
953 if (!h_float_placement.empty())
954 os << "\\float_placement " << h_float_placement << "\n";
955 os << "\\paperfontsize " << h_paperfontsize << "\n"
956 << "\\spacing " << h_spacing << "\n"
957 << "\\use_hyperref " << h_use_hyperref << '\n';
958 if (h_use_hyperref == "1") {
959 if (!h_pdf_title.empty())
960 os << "\\pdf_title \"" << h_pdf_title << "\"\n";
961 if (!h_pdf_author.empty())
962 os << "\\pdf_author \"" << h_pdf_author << "\"\n";
963 if (!h_pdf_subject.empty())
964 os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
965 if (!h_pdf_keywords.empty())
966 os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
967 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
968 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
969 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
970 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
971 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
972 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
973 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
974 "\\pdf_backref " << h_pdf_backref << "\n"
975 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
976 if (!h_pdf_pagemode.empty())
977 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
978 if (!h_pdf_quoted_options.empty())
979 os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
981 os << "\\papersize " << h_papersize << "\n"
982 << "\\use_geometry " << h_use_geometry << '\n';
983 for (map<string, string>::const_iterator it = h_use_packages.begin();
984 it != h_use_packages.end(); ++it)
985 os << "\\use_package " << it->first << ' ' << it->second << '\n';
986 os << "\\cite_engine " << h_cite_engine << '\n'
987 << "\\cite_engine_type " << h_cite_engine_type << '\n'
988 << "\\biblio_style " << h_biblio_style << "\n"
989 << "\\use_bibtopic " << h_use_bibtopic << "\n"
990 << "\\use_indices " << h_use_indices << "\n"
991 << "\\paperorientation " << h_paperorientation << '\n'
992 << "\\suppress_date " << h_suppress_date << '\n'
993 << "\\justification " << h_justification << '\n'
994 << "\\use_refstyle " << h_use_refstyle << '\n';
995 if (!h_fontcolor.empty())
996 os << "\\fontcolor " << h_fontcolor << '\n';
997 if (!h_notefontcolor.empty())
998 os << "\\notefontcolor " << h_notefontcolor << '\n';
999 if (!h_backgroundcolor.empty())
1000 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1001 if (!h_boxbgcolor.empty())
1002 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1004 << "\\secnumdepth " << h_secnumdepth << "\n"
1005 << "\\tocdepth " << h_tocdepth << "\n"
1006 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1007 if (h_paragraph_separation == "skip")
1008 os << "\\defskip " << h_defskip << "\n";
1010 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1011 os << "\\quotes_language " << h_quotes_language << "\n"
1012 << "\\papercolumns " << h_papercolumns << "\n"
1013 << "\\papersides " << h_papersides << "\n"
1014 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1015 if (!h_listings_params.empty())
1016 os << "\\listings_params " << h_listings_params << "\n";
1017 os << "\\tracking_changes " << h_tracking_changes << "\n"
1018 << "\\output_changes " << h_output_changes << "\n"
1019 << "\\html_math_output " << h_html_math_output << "\n"
1020 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1021 << "\\html_be_strict " << h_html_be_strict << "\n"
1023 << "\\end_header\n\n"
1024 << "\\begin_body\n";
1029 void Preamble::parse(Parser & p, string const & forceclass,
1030 TeX2LyXDocClass & tc)
1032 // initialize fixed types
1033 special_columns['D'] = 3;
1034 bool is_full_document = false;
1035 bool is_lyx_file = false;
1036 bool in_lyx_preamble = false;
1038 // determine whether this is a full document or a fragment for inclusion
1040 Token const & t = p.get_token();
1042 if (t.cat() == catEscape && t.cs() == "documentclass") {
1043 is_full_document = true;
1049 while (is_full_document && p.good()) {
1050 Token const & t = p.get_token();
1053 cerr << "t: " << t << "\n";
1059 if (!in_lyx_preamble &&
1060 (t.cat() == catLetter ||
1061 t.cat() == catSuper ||
1062 t.cat() == catSub ||
1063 t.cat() == catOther ||
1064 t.cat() == catMath ||
1065 t.cat() == catActive ||
1066 t.cat() == catBegin ||
1067 t.cat() == catEnd ||
1068 t.cat() == catAlign ||
1069 t.cat() == catParameter))
1070 h_preamble << t.cs();
1072 else if (!in_lyx_preamble &&
1073 (t.cat() == catSpace || t.cat() == catNewline))
1074 h_preamble << t.asInput();
1076 else if (t.cat() == catComment) {
1077 static regex const islyxfile("%% LyX .* created this file");
1078 static regex const usercommands("User specified LaTeX commands");
1080 string const comment = t.asInput();
1082 // magically switch encoding default if it looks like XeLaTeX
1083 static string const magicXeLaTeX =
1084 "% This document must be compiled with XeLaTeX ";
1085 if (comment.size() > magicXeLaTeX.size()
1086 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1087 && h_inputencoding == "auto") {
1088 cerr << "XeLaTeX comment found, switching to UTF8\n";
1089 h_inputencoding = "utf8";
1092 if (regex_search(comment, sub, islyxfile)) {
1094 in_lyx_preamble = true;
1095 } else if (is_lyx_file
1096 && regex_search(comment, sub, usercommands))
1097 in_lyx_preamble = false;
1098 else if (!in_lyx_preamble)
1099 h_preamble << t.asInput();
1102 else if (t.cs() == "pagestyle")
1103 h_paperpagestyle = p.verbatim_item();
1105 else if (t.cs() == "setdefaultlanguage") {
1107 // We don't yet care about non-language variant options
1108 // because LyX doesn't support this yet, see bug #8214
1110 string langopts = p.getOpt();
1111 // check if the option contains a variant, if yes, extract it
1112 string::size_type pos_var = langopts.find("variant");
1113 string::size_type i = langopts.find(',', pos_var);
1114 if (pos_var != string::npos){
1116 if (i == string::npos)
1117 variant = langopts.substr(pos_var + 8, langopts.length() - pos_var - 9);
1119 variant = langopts.substr(pos_var + 8, i - pos_var - 8);
1120 h_language = variant;
1124 h_language = p.verbatim_item();
1125 //finally translate the poyglossia name to a LyX name
1126 h_language = polyglossia2lyx(h_language);
1129 else if (t.cs() == "setotherlanguage") {
1130 // We don't yet care about the option because LyX doesn't
1131 // support this yet, see bug #8214
1132 p.hasOpt() ? p.getOpt() : string();
1136 else if (t.cs() == "setmainfont") {
1137 // we don't care about the option
1138 p.hasOpt() ? p.getOpt() : string();
1139 h_font_roman = p.getArg('{', '}');
1142 else if (t.cs() == "setsansfont") {
1143 // we don't care about the option
1144 p.hasOpt() ? p.getOpt() : string();
1145 h_font_sans = p.getArg('{', '}');
1148 else if (t.cs() == "setmonofont") {
1149 // we don't care about the option
1150 p.hasOpt() ? p.getOpt() : string();
1151 h_font_typewriter = p.getArg('{', '}');
1154 else if (t.cs() == "date") {
1155 string argument = p.getArg('{', '}');
1156 if (argument.empty())
1157 h_suppress_date = "true";
1159 h_preamble << t.asInput() << '{' << argument << '}';
1162 else if (t.cs() == "color") {
1163 string const space =
1164 (p.hasOpt() ? p.getOpt() : string());
1165 string argument = p.getArg('{', '}');
1166 // check the case that a standard color is used
1167 if (space.empty() && is_known(argument, known_basic_colors)) {
1168 h_fontcolor = rgbcolor2code(argument);
1169 preamble.registerAutomaticallyLoadedPackage("color");
1170 } else if (space.empty() && argument == "document_fontcolor")
1171 preamble.registerAutomaticallyLoadedPackage("color");
1172 // check the case that LyX's document_fontcolor is defined
1173 // but not used for \color
1175 h_preamble << t.asInput();
1177 h_preamble << space;
1178 h_preamble << '{' << argument << '}';
1179 // the color might already be set because \definecolor
1180 // is parsed before this
1185 else if (t.cs() == "pagecolor") {
1186 string argument = p.getArg('{', '}');
1187 // check the case that a standard color is used
1188 if (is_known(argument, known_basic_colors)) {
1189 h_backgroundcolor = rgbcolor2code(argument);
1190 } else if (argument == "page_backgroundcolor")
1191 preamble.registerAutomaticallyLoadedPackage("color");
1192 // check the case that LyX's page_backgroundcolor is defined
1193 // but not used for \pagecolor
1195 h_preamble << t.asInput() << '{' << argument << '}';
1196 // the color might already be set because \definecolor
1197 // is parsed before this
1198 h_backgroundcolor = "";
1202 else if (t.cs() == "makeatletter") {
1203 // LyX takes care of this
1204 p.setCatCode('@', catLetter);
1207 else if (t.cs() == "makeatother") {
1208 // LyX takes care of this
1209 p.setCatCode('@', catOther);
1212 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1213 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1214 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1215 || t.cs() == "DeclareRobustCommand"
1216 || t.cs() == "DeclareRobustCommandx"
1217 || t.cs() == "ProvideTextCommandDefault"
1218 || t.cs() == "DeclareMathAccent") {
1220 if (p.next_token().character() == '*') {
1224 string const name = p.verbatim_item();
1225 string const opt1 = p.getFullOpt();
1226 string const opt2 = p.getFullOpt();
1227 string const body = p.verbatim_item();
1229 if (name == "\\rmdefault")
1230 if (is_known(body, known_roman_fonts))
1231 h_font_roman = body;
1232 if (name == "\\sfdefault")
1233 if (is_known(body, known_sans_fonts))
1235 if (name == "\\ttdefault")
1236 if (is_known(body, known_typewriter_fonts))
1237 h_font_typewriter = body;
1238 if (name == "\\familydefault") {
1239 string family = body;
1240 // remove leading "\"
1241 h_font_default_family = family.erase(0,1);
1244 // remove the lyxdot definition that is re-added by LyX
1246 if (name == "\\lyxdot")
1247 in_lyx_preamble = true;
1249 // Add the command to the known commands
1250 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1252 // only non-lyxspecific stuff
1253 if (!in_lyx_preamble) {
1255 ss << '\\' << t.cs();
1258 ss << '{' << name << '}' << opt1 << opt2
1259 << '{' << body << "}";
1260 h_preamble << ss.str();
1262 ostream & out = in_preamble ? h_preamble : os;
1263 out << "\\" << t.cs() << "{" << name << "}"
1264 << opts << "{" << body << "}";
1269 else if (t.cs() == "documentclass") {
1270 vector<string>::iterator it;
1271 vector<string> opts = split_options(p.getArg('[', ']'));
1272 handle_opt(opts, known_fontsizes, h_paperfontsize);
1273 delete_opt(opts, known_fontsizes);
1274 // delete "pt" at the end
1275 string::size_type i = h_paperfontsize.find("pt");
1276 if (i != string::npos)
1277 h_paperfontsize.erase(i);
1278 // The documentclass options are always parsed before the options
1279 // of the babel call so that a language cannot overwrite the babel
1281 handle_opt(opts, known_languages, h_language);
1282 delete_opt(opts, known_languages);
1284 // paper orientation
1285 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1286 h_paperorientation = "landscape";
1290 if ((it = find(opts.begin(), opts.end(), "oneside"))
1295 if ((it = find(opts.begin(), opts.end(), "twoside"))
1301 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1303 h_papercolumns = "1";
1306 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1308 h_papercolumns = "2";
1312 // some size options are known to any document classes, other sizes
1313 // are handled by the \geometry command of the geometry package
1314 handle_opt(opts, known_class_paper_sizes, h_papersize);
1315 delete_opt(opts, known_class_paper_sizes);
1316 // the remaining options
1317 h_options = join(opts, ",");
1318 // FIXME This does not work for classes that have a
1319 // different name in LyX than in LaTeX
1320 h_textclass = p.getArg('{', '}');
1323 else if (t.cs() == "usepackage") {
1324 string const options = p.getArg('[', ']');
1325 string const name = p.getArg('{', '}');
1326 vector<string> vecnames;
1327 split(name, vecnames, ',');
1328 vector<string>::const_iterator it = vecnames.begin();
1329 vector<string>::const_iterator end = vecnames.end();
1330 for (; it != end; ++it)
1331 handle_package(p, trimSpaceAndEol(*it), options,
1335 else if (t.cs() == "inputencoding") {
1336 string const encoding = p.getArg('{','}');
1337 h_inputencoding = encoding;
1338 p.setEncoding(encoding);
1341 else if (t.cs() == "newenvironment") {
1342 string const name = p.getArg('{', '}');
1343 string const opt1 = p.getFullOpt();
1344 string const opt2 = p.getFullOpt();
1345 string const beg = p.verbatim_item();
1346 string const end = p.verbatim_item();
1347 if (!in_lyx_preamble) {
1348 h_preamble << "\\newenvironment{" << name
1349 << '}' << opt1 << opt2 << '{'
1350 << beg << "}{" << end << '}';
1352 add_known_environment(name, opt1, !opt2.empty(),
1353 from_utf8(beg), from_utf8(end));
1357 else if (t.cs() == "def") {
1358 string name = p.get_token().cs();
1359 // In fact, name may be more than the name:
1360 // In the test case of bug 8116
1361 // name == "csname SF@gobble@opt \endcsname".
1362 // Therefore, we need to use asInput() instead of cs().
1363 while (p.next_token().cat() != catBegin)
1364 name += p.get_token().asInput();
1365 if (!in_lyx_preamble)
1366 h_preamble << "\\def\\" << name << '{'
1367 << p.verbatim_item() << "}";
1370 else if (t.cs() == "newcolumntype") {
1371 string const name = p.getArg('{', '}');
1372 trimSpaceAndEol(name);
1374 string opts = p.getOpt();
1375 if (!opts.empty()) {
1376 istringstream is(string(opts, 1));
1379 special_columns[name[0]] = nargs;
1380 h_preamble << "\\newcolumntype{" << name << "}";
1382 h_preamble << "[" << nargs << "]";
1383 h_preamble << "{" << p.verbatim_item() << "}";
1386 else if (t.cs() == "setcounter") {
1387 string const name = p.getArg('{', '}');
1388 string const content = p.getArg('{', '}');
1389 if (name == "secnumdepth")
1390 h_secnumdepth = content;
1391 else if (name == "tocdepth")
1392 h_tocdepth = content;
1394 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1397 else if (t.cs() == "setlength") {
1398 string const name = p.verbatim_item();
1399 string const content = p.verbatim_item();
1400 // the paragraphs are only not indented when \parindent is set to zero
1401 if (name == "\\parindent" && content != "") {
1402 if (content[0] == '0')
1403 h_paragraph_separation = "skip";
1405 h_paragraph_indentation = translate_len(content);
1406 } else if (name == "\\parskip") {
1407 if (content == "\\smallskipamount")
1408 h_defskip = "smallskip";
1409 else if (content == "\\medskipamount")
1410 h_defskip = "medskip";
1411 else if (content == "\\bigskipamount")
1412 h_defskip = "bigskip";
1414 h_defskip = content;
1416 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1419 else if (t.cs() == "onehalfspacing")
1420 h_spacing = "onehalf";
1422 else if (t.cs() == "doublespacing")
1423 h_spacing = "double";
1425 else if (t.cs() == "setstretch")
1426 h_spacing = "other " + p.verbatim_item();
1428 else if (t.cs() == "begin") {
1429 string const name = p.getArg('{', '}');
1430 if (name == "document")
1432 h_preamble << "\\begin{" << name << "}";
1435 else if (t.cs() == "geometry") {
1436 vector<string> opts = split_options(p.getArg('{', '}'));
1437 handle_geometry(opts);
1440 else if (t.cs() == "definecolor") {
1441 string const color = p.getArg('{', '}');
1442 string const space = p.getArg('{', '}');
1443 string const value = p.getArg('{', '}');
1444 if (color == "document_fontcolor" && space == "rgb") {
1445 RGBColor c(RGBColorFromLaTeX(value));
1446 h_fontcolor = X11hexname(c);
1447 } else if (color == "note_fontcolor" && space == "rgb") {
1448 RGBColor c(RGBColorFromLaTeX(value));
1449 h_notefontcolor = X11hexname(c);
1450 } else if (color == "page_backgroundcolor" && space == "rgb") {
1451 RGBColor c(RGBColorFromLaTeX(value));
1452 h_backgroundcolor = X11hexname(c);
1453 } else if (color == "shadecolor" && space == "rgb") {
1454 RGBColor c(RGBColorFromLaTeX(value));
1455 h_boxbgcolor = X11hexname(c);
1457 h_preamble << "\\definecolor{" << color
1458 << "}{" << space << "}{" << value
1463 else if (t.cs() == "bibliographystyle")
1464 h_biblio_style = p.verbatim_item();
1466 else if (t.cs() == "jurabibsetup") {
1467 // FIXME p.getArg('{', '}') is most probably wrong (it
1468 // does not handle nested braces).
1469 // Use p.verbatim_item() instead.
1470 vector<string> jurabibsetup =
1471 split_options(p.getArg('{', '}'));
1472 // add jurabibsetup to the jurabib package options
1473 add_package("jurabib", jurabibsetup);
1474 if (!jurabibsetup.empty()) {
1475 h_preamble << "\\jurabibsetup{"
1476 << join(jurabibsetup, ",") << '}';
1480 else if (t.cs() == "hypersetup") {
1481 vector<string> hypersetup =
1482 split_options(p.verbatim_item());
1483 // add hypersetup to the hyperref package options
1484 handle_hyperref(hypersetup);
1485 if (!hypersetup.empty()) {
1486 h_preamble << "\\hypersetup{"
1487 << join(hypersetup, ",") << '}';
1491 else if (is_known(t.cs(), known_if_3arg_commands)) {
1492 // prevent misparsing of \usepackage if it is used
1493 // as an argument (see e.g. our own output of
1494 // \@ifundefined above)
1495 string const arg1 = p.verbatim_item();
1496 string const arg2 = p.verbatim_item();
1497 string const arg3 = p.verbatim_item();
1498 // test case \@ifundefined{date}{}{\date{}}
1499 if (t.cs() == "@ifundefined" && arg1 == "date" &&
1500 arg2.empty() && arg3 == "\\date{}") {
1501 h_suppress_date = "true";
1502 // older tex2lyx versions did output
1503 // \@ifundefined{definecolor}{\usepackage{color}}{}
1504 } else if (t.cs() == "@ifundefined" &&
1505 arg1 == "definecolor" &&
1506 arg2 == "\\usepackage{color}" &&
1508 if (!in_lyx_preamble)
1509 h_preamble << package_beg_sep
1512 << "\\@ifundefined{definecolor}{color}{}"
1515 //\@ifundefined{showcaptionsetup}{}{%
1516 // \PassOptionsToPackage{caption=false}{subfig}}
1517 // that LyX uses for subfloats
1518 } else if (t.cs() == "@ifundefined" &&
1519 arg1 == "showcaptionsetup" && arg2.empty()
1520 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1522 } else if (!in_lyx_preamble) {
1523 h_preamble << t.asInput()
1524 << '{' << arg1 << '}'
1525 << '{' << arg2 << '}'
1526 << '{' << arg3 << '}';
1530 else if (is_known(t.cs(), known_if_commands)) {
1531 // must not parse anything in conditional code, since
1532 // LyX would output the parsed contents unconditionally
1533 if (!in_lyx_preamble)
1534 h_preamble << t.asInput();
1535 handle_if(p, in_lyx_preamble);
1538 else if (!t.cs().empty() && !in_lyx_preamble)
1539 h_preamble << '\\' << t.cs();
1542 // remove the whitespace
1545 // Force textclass if the user wanted it
1546 if (!forceclass.empty())
1547 h_textclass = forceclass;
1548 if (noweb_mode && !prefixIs(h_textclass, "literate-"))
1549 h_textclass.insert(0, "literate-");
1550 tc.setName(h_textclass);
1552 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1555 if (h_papersides.empty()) {
1558 h_papersides = ss.str();
1563 string babel2lyx(string const & language)
1565 char const * const * where = is_known(language, known_languages);
1567 return known_coded_languages[where - known_languages];
1572 string polyglossia2lyx(string const & language)
1574 char const * const * where = is_known(language, polyglossia_languages);
1576 return coded_polyglossia_languages[where - polyglossia_languages];
1581 string rgbcolor2code(string const & name)
1583 char const * const * where = is_known(name, known_basic_colors);
1585 // "red", "green" etc
1586 return known_basic_color_codes[where - known_basic_colors];
1588 // "255,0,0", "0,255,0" etc
1589 RGBColor c(RGBColorFromLaTeX(name));
1590 return X11hexname(c);