3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
9 * Full author contact details are available in file CREDITS.
19 #include "LayoutFile.h"
22 #include "TextClass.h"
24 #include "support/convert.h"
25 #include "support/FileName.h"
26 #include "support/filetools.h"
27 #include "support/lstrings.h"
29 #include "support/regex.h"
35 using namespace lyx::support;
40 // special columntypes
41 extern map<char, int> special_columns;
47 // "chinese-simplified", "chinese-traditional", "japanese-cjk", "korean"
48 // cannot be supported because it is impossible to determine the correct document
49 // language if CJK is used.
50 // FIXME: missing support for "japanese" (non-CJK)
52 * known babel language names (including synonyms)
53 * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
54 * please keep this in sync with known_coded_languages line by line!
56 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
57 "american", "arabic", "arabtex", "australian", "austrian", "bahasa", "bahasai",
58 "bahasam", "basque", "belarusian", "brazil", "brazilian", "breton", "british",
59 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
60 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "francais",
61 "french", "frenchb", "frenchle", "frenchpro", "galician", "german", "germanb",
62 "greek", "hebrew", "hungarian", "icelandic", "indon", "indonesian", "interlingua",
63 "irish", "italian", "kazakh", "kurmanji", "latin", "latvian", "lithuanian",
64 "lowersorbian", "lsorbian", "magyar", "malay", "meyalu", "mongolian", "naustrian",
65 "newzealand", "ngerman", "ngermanb", "norsk", "nynorsk", "polutonikogreek", "polish",
66 "portuges", "portuguese", "romanian", "russian", "russianb", "samin",
67 "scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish",
68 "swedish", "thai", "turkish", "turkmen", "ukraineb", "ukrainian",
69 "uppersorbian", "UKenglish", "USenglish", "usorbian", "vietnam", "welsh",
73 * the same as known_languages with .lyx names
74 * please keep this in sync with known_languages line by line!
76 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
77 "american", "arabic_arabi", "arabic_arabtex", "australian", "austrian", "bahasa", "bahasa",
78 "bahasam", "basque", "belarusian", "brazilian", "brazilian", "breton", "british",
79 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
80 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "french",
81 "french", "french", "french", "french", "galician", "german", "german",
82 "greek", "hebrew", "magyar", "icelandic", "bahasa", "bahasa", "interlingua",
83 "irish", "italian", "kazakh", "kurmanji", "latin", "latvian", "lithuanian",
84 "lowersorbian", "lowersorbian", "magyar", "bahasam", "bahasam", "mongolian", "naustrian",
85 "newzealand", "ngerman", "ngerman", "norsk", "nynorsk", "polutonikogreek", "polish",
86 "portuguese", "portuguese", "romanian", "russian", "russian", "samin",
87 "scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish",
88 "swedish", "thai", "turkish", "turkmen", "ukrainian", "ukrainian",
89 "uppersorbian", "uppersorbian", "english", "english", "vietnamese", "welsh",
93 * known polyglossia language names (including variants)
95 const char * const polyglossia_languages[] = {
96 "albanian", "croatian", "hebrew", "norsk", "swedish", "amharic", "czech", "hindi",
97 "nynorsk", "syriac", "arabic", "danish", "icelandic", "occitan", "tamil",
98 "armenian", "divehi", "interlingua", "polish", "telugu", "asturian", "dutch",
99 "irish", "portuges", "thai", "bahasai", "english", "italian", "romanian", "turkish",
100 "bahasam", "esperanto", "lao", "russian", "turkmen", "basque", "estonian", "latin",
101 "samin", "ukrainian", "bengali", "farsi", "latvian", "sanskrit", "urdu", "brazil",
102 "brazilian", "finnish", "lithuanian", "scottish", "usorbian", "breton", "french",
103 "lsorbian", "serbian", "vietnamese", "bulgarian", "galician", "magyar", "slovak",
104 "welsh", "catalan", "german", "malayalam", "slovenian", "coptic", "greek",
105 "marathi", "spanish",
106 "american", "ancient", "australian", "british", "monotonic", "newzealand",
110 * the same as polyglossia_languages with .lyx names
111 * please keep this in sync with polyglossia_languages line by line!
113 const char * const coded_polyglossia_languages[] = {
114 "albanian", "croatian", "hebrew", "norsk", "swedish", "amharic", "czech", "hindi",
115 "nynorsk", "syriac", "arabic_arabi", "danish", "icelandic", "occitan", "tamil",
116 "armenian", "divehi", "interlingua", "polish", "telugu", "asturian", "dutch",
117 "irish", "portuges", "thai", "bahasa", "english", "italian", "romanian", "turkish",
118 "bahasam", "esperanto", "lao", "russian", "turkmen", "basque", "estonian", "latin",
119 "samin", "ukrainian", "bengali", "farsi", "latvian", "sanskrit", "urdu", "brazilian",
120 "brazilian", "finnish", "lithuanian", "scottish", "uppersorbian", "breton", "french",
121 "lowersorbian", "serbian", "vietnamese", "bulgarian", "galician", "magyar", "slovak",
122 "welsh", "catalan", "ngerman", "malayalam", "slovene", "coptic", "greek",
123 "marathi", "spanish",
124 "american", "ancientgreek", "australian", "british", "greek", "newzealand",
125 "polutonikogreek", 0};
127 /// languages with english quotes (.lyx names)
128 const char * const known_english_quotes_languages[] = {"american", "australian",
129 "bahasa", "bahasam", "brazilian", "canadian", "chinese-simplified", "english",
130 "esperanto", "hebrew", "irish", "korean", "newzealand", "portuguese", "scottish",
133 /// languages with french quotes (.lyx names)
134 const char * const known_french_quotes_languages[] = {"albanian",
135 "arabic_arabi", "arabic_arabtex", "basque", "canadien", "catalan", "french",
136 "galician", "greek", "italian", "norsk", "nynorsk", "polutonikogreek",
137 "russian", "spanish", "spanish-mexico", "turkish", "turkmen", "ukrainian",
140 /// languages with german quotes (.lyx names)
141 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
142 "czech", "german", "icelandic", "lithuanian", "lowersorbian", "naustrian",
143 "ngerman", "serbian", "serbian-latin", "slovak", "slovene", "uppersorbian", 0};
145 /// languages with polish quotes (.lyx names)
146 const char * const known_polish_quotes_languages[] = {"afrikaans", "croatian",
147 "dutch", "estonian", "magyar", "polish", "romanian", 0};
149 /// languages with swedish quotes (.lyx names)
150 const char * const known_swedish_quotes_languages[] = {"finnish",
153 /// known language packages from the times before babel
154 const char * const known_old_language_packages[] = {"french", "frenchle",
155 "frenchpro", "german", "ngerman", "pmfrench", 0};
157 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
159 const char * const known_roman_fonts[] = { "ae", "beraserif", "bookman",
160 "ccfonts", "chancery", "charter", "cmr", "fourier", "lmodern", "mathpazo",
161 "mathptmx", "newcent", "utopia", 0};
163 const char * const known_sans_fonts[] = { "avant", "berasans", "cmbr", "cmss",
164 "helvet", "lmss", 0};
166 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
167 "courier", "lmtt", "luximono", "fourier", "lmodern", "mathpazo", "mathptmx",
170 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
171 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
172 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
173 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
174 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
176 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
177 "executivepaper", "legalpaper", "letterpaper", 0};
179 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
180 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
182 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
183 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
186 /// commands that can start an \if...\else...\endif sequence
187 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
188 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
189 "ifsidecap", "ifupgreek", 0};
191 const char * const known_basic_colors[] = {"blue", "black", "cyan", "green",
192 "magenta", "red", "white", "yellow", 0};
194 const char * const known_basic_color_codes[] = {"#0000ff", "#000000", "#00ffff", "#00ff00",
195 "#ff00ff", "#ff0000", "#ffffff", "#ffff00", 0};
197 /// conditional commands with three arguments like \@ifundefined{}{}{}
198 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
201 /// packages that work only in xetex
202 /// polyglossia is handled separately
203 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
204 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
205 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
207 /// packages that are automatically skipped if loaded by LyX
208 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
209 "amstext", "amsthm", "array", "booktabs", "calc", "CJK", "color", "float", "fontspec",
210 "graphicx", "hhline", "ifthen", "longtable", "makeidx", "multirow",
211 "nomencl", "pdfpages", "rotating", "rotfloat", "splitidx", "setspace",
212 "subscript", "textcomp", "ulem", "url", "varioref", "verbatim", "wrapfig",
215 // codes used to remove packages that are loaded automatically by LyX.
216 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
217 const char package_beg_sep = '\001';
218 const char package_mid_sep = '\002';
219 const char package_end_sep = '\003';
222 // returns true if at least one of the options in what has been found
223 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
229 // the last language option is the document language (for babel and LyX)
230 // the last size option is the document font size
231 vector<string>::iterator it;
232 vector<string>::iterator position = opts.begin();
233 for (; *what; ++what) {
234 it = find(opts.begin(), opts.end(), *what);
235 if (it != opts.end()) {
236 if (it >= position) {
247 void delete_opt(vector<string> & opts, char const * const * what)
252 // remove found options from the list
253 // do this after handle_opt to avoid potential memory leaks
254 vector<string>::iterator it;
255 for (; *what; ++what) {
256 it = find(opts.begin(), opts.end(), *what);
257 if (it != opts.end())
264 * Split a package options string (keyval format) into a vector.
266 * authorformat=smallcaps,
268 * titleformat=colonsep,
269 * bibformat={tabular,ibidem,numbered}
271 vector<string> split_options(string const & input)
273 vector<string> options;
277 Token const & t = p.get_token();
278 if (t.asInput() == ",") {
279 options.push_back(trimSpaceAndEol(option));
281 } else if (t.asInput() == "=") {
284 if (p.next_token().asInput() == "{")
285 option += '{' + p.getArg('{', '}') + '}';
286 } else if (t.cat() != catSpace)
287 option += t.asInput();
291 options.push_back(trimSpaceAndEol(option));
298 * Retrieve a keyval option "name={value with=sign}" named \p name from
299 * \p options and return the value.
300 * The found option is also removed from \p options.
302 string process_keyval_opt(vector<string> & options, string name)
304 for (size_t i = 0; i < options.size(); ++i) {
305 vector<string> option;
306 split(options[i], option, '=');
307 if (option.size() < 2)
309 if (option[0] == name) {
310 options.erase(options.begin() + i);
311 option.erase(option.begin());
312 return join(option, "=");
318 } // anonymous namespace
321 bool Preamble::indentParagraphs() const
323 return h_paragraph_separation == "indent";
327 bool Preamble::isPackageUsed(string const & package) const
329 return used_packages.find(package) != used_packages.end();
333 vector<string> Preamble::getPackageOptions(string const & package) const
335 map<string, vector<string> >::const_iterator it = used_packages.find(package);
336 if (it != used_packages.end())
338 return vector<string>();
342 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
344 auto_packages.insert(package);
348 void Preamble::addModule(string const & module)
350 used_modules.push_back(module);
354 void Preamble::suppressDate(bool suppress)
357 h_suppress_date = "true";
359 h_suppress_date = "false";
363 void Preamble::registerAuthor(std::string const & name)
365 Author author(from_utf8(name), empty_docstring());
366 author.setUsed(true);
367 authors_.record(author);
368 h_tracking_changes = "true";
369 h_output_changes = "true";
373 Author const & Preamble::getAuthor(std::string const & name) const
375 Author author(from_utf8(name), empty_docstring());
376 for (AuthorList::Authors::const_iterator it = authors_.begin();
377 it != authors_.end(); ++it)
380 static Author const dummy;
385 void Preamble::add_package(string const & name, vector<string> & options)
387 // every package inherits the global options
388 if (used_packages.find(name) == used_packages.end())
389 used_packages[name] = split_options(h_options);
391 vector<string> & v = used_packages[name];
392 v.insert(v.end(), options.begin(), options.end());
393 if (name == "jurabib") {
394 // Don't output the order argument (see the cite command
395 // handling code in text.cpp).
396 vector<string>::iterator end =
397 remove(options.begin(), options.end(), "natbiborder");
398 end = remove(options.begin(), end, "jurabiborder");
399 options.erase(end, options.end());
406 // Given is a string like "scaled=0.9", return 0.9 * 100
407 string const scale_as_percentage(string const & scale)
409 string::size_type pos = scale.find('=');
410 if (pos != string::npos) {
411 string value = scale.substr(pos + 1);
413 return convert<string>(100 * convert<double>(value));
415 // If the input string didn't match our expectations.
416 // return the default value "100"
421 string remove_braces(string const & value)
425 if (value[0] == '{' && value[value.length()-1] == '}')
426 return value.substr(1, value.length()-2);
430 } // anonymous namespace
433 Preamble::Preamble() : one_language(true), title_layout_found(false)
437 h_biblio_style = "plain";
438 h_bibtex_command = "default";
439 h_cite_engine = "basic";
440 h_cite_engine_type = "numerical";
442 h_defskip = "medskip";
445 h_fontencoding = "default";
446 h_font_roman = "default";
447 h_font_sans = "default";
448 h_font_typewriter = "default";
449 h_font_default_family = "default";
450 h_use_non_tex_fonts = "false";
452 h_font_osf = "false";
453 h_font_sf_scale = "100";
454 h_font_tt_scale = "100";
455 h_graphics = "default";
456 h_default_output_format = "default";
457 h_html_be_strict = "false";
458 h_html_css_as_file = "0";
459 h_html_math_output = "0";
461 h_index_command = "default";
462 h_inputencoding = "auto";
463 h_justification = "true";
464 h_language = "english";
465 h_language_package = "none";
467 h_maintain_unincluded_children = "false";
471 h_output_changes = "false";
473 h_papercolumns = "1";
474 h_paperfontsize = "default";
475 h_paperorientation = "portrait";
476 h_paperpagestyle = "default";
478 h_papersize = "default";
479 h_paragraph_indentation = "default";
480 h_paragraph_separation = "indent";
485 h_pdf_bookmarks = "1";
486 h_pdf_bookmarksnumbered = "0";
487 h_pdf_bookmarksopen = "0";
488 h_pdf_bookmarksopenlevel = "1";
489 h_pdf_breaklinks = "0";
490 h_pdf_pdfborder = "0";
491 h_pdf_colorlinks = "0";
492 h_pdf_backref = "section";
493 h_pdf_pdfusetitle = "1";
495 //h_pdf_quoted_options;
496 h_quotes_language = "english";
499 h_spacing = "single";
500 h_suppress_date = "false";
501 h_textclass = "article";
503 h_tracking_changes = "false";
504 h_use_bibtopic = "false";
505 h_use_indices = "false";
506 h_use_geometry = "false";
507 h_use_default_options = "false";
508 h_use_hyperref = "0";
509 h_use_refstyle = "1";
510 h_use_packages["amsmath"] = "1";
511 h_use_packages["amssymb"] = "1";
512 h_use_packages["esint"] = "1";
513 h_use_packages["mhchem"] = "1";
514 h_use_packages["mathdots"] = "1";
515 h_use_packages["mathtools"] = "1";
516 h_use_packages["undertilde"] = "1";
520 void Preamble::handle_hyperref(vector<string> & options)
522 // FIXME swallow inputencoding changes that might surround the
523 // hyperref setup if it was written by LyX
524 h_use_hyperref = "1";
525 // swallow "unicode=true", since LyX does always write that
526 vector<string>::iterator it =
527 find(options.begin(), options.end(), "unicode=true");
528 if (it != options.end())
530 it = find(options.begin(), options.end(), "pdfusetitle");
531 if (it != options.end()) {
532 h_pdf_pdfusetitle = "1";
535 string bookmarks = process_keyval_opt(options, "bookmarks");
536 if (bookmarks == "true")
537 h_pdf_bookmarks = "1";
538 else if (bookmarks == "false")
539 h_pdf_bookmarks = "0";
540 if (h_pdf_bookmarks == "1") {
541 string bookmarksnumbered =
542 process_keyval_opt(options, "bookmarksnumbered");
543 if (bookmarksnumbered == "true")
544 h_pdf_bookmarksnumbered = "1";
545 else if (bookmarksnumbered == "false")
546 h_pdf_bookmarksnumbered = "0";
547 string bookmarksopen =
548 process_keyval_opt(options, "bookmarksopen");
549 if (bookmarksopen == "true")
550 h_pdf_bookmarksopen = "1";
551 else if (bookmarksopen == "false")
552 h_pdf_bookmarksopen = "0";
553 if (h_pdf_bookmarksopen == "1") {
554 string bookmarksopenlevel =
555 process_keyval_opt(options, "bookmarksopenlevel");
556 if (!bookmarksopenlevel.empty())
557 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
560 string breaklinks = process_keyval_opt(options, "breaklinks");
561 if (breaklinks == "true")
562 h_pdf_breaklinks = "1";
563 else if (breaklinks == "false")
564 h_pdf_breaklinks = "0";
565 string pdfborder = process_keyval_opt(options, "pdfborder");
566 if (pdfborder == "{0 0 0}")
567 h_pdf_pdfborder = "1";
568 else if (pdfborder == "{0 0 1}")
569 h_pdf_pdfborder = "0";
570 string backref = process_keyval_opt(options, "backref");
571 if (!backref.empty())
572 h_pdf_backref = backref;
573 string colorlinks = process_keyval_opt(options, "colorlinks");
574 if (colorlinks == "true")
575 h_pdf_colorlinks = "1";
576 else if (colorlinks == "false")
577 h_pdf_colorlinks = "0";
578 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
579 if (!pdfpagemode.empty())
580 h_pdf_pagemode = pdfpagemode;
581 string pdftitle = process_keyval_opt(options, "pdftitle");
582 if (!pdftitle.empty()) {
583 h_pdf_title = remove_braces(pdftitle);
585 string pdfauthor = process_keyval_opt(options, "pdfauthor");
586 if (!pdfauthor.empty()) {
587 h_pdf_author = remove_braces(pdfauthor);
589 string pdfsubject = process_keyval_opt(options, "pdfsubject");
590 if (!pdfsubject.empty())
591 h_pdf_subject = remove_braces(pdfsubject);
592 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
593 if (!pdfkeywords.empty())
594 h_pdf_keywords = remove_braces(pdfkeywords);
595 if (!options.empty()) {
596 if (!h_pdf_quoted_options.empty())
597 h_pdf_quoted_options += ',';
598 h_pdf_quoted_options += join(options, ",");
604 void Preamble::handle_geometry(vector<string> & options)
606 h_use_geometry = "true";
607 vector<string>::iterator it;
609 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
610 h_paperorientation = "landscape";
614 // keyval version: "paper=letter"
615 string paper = process_keyval_opt(options, "paper");
617 h_papersize = paper + "paper";
618 // alternative version: "letterpaper"
619 handle_opt(options, known_paper_sizes, h_papersize);
620 delete_opt(options, known_paper_sizes);
622 char const * const * margin = known_paper_margins;
623 for (; *margin; ++margin) {
624 string value = process_keyval_opt(options, *margin);
625 if (!value.empty()) {
626 int k = margin - known_paper_margins;
627 string name = known_coded_paper_margins[k];
628 h_margins += '\\' + name + ' ' + value + '\n';
634 void Preamble::handle_package(Parser &p, string const & name,
635 string const & opts, bool in_lyx_preamble)
637 vector<string> options = split_options(opts);
638 add_package(name, options);
641 if (is_known(name, known_xetex_packages)) {
643 h_use_non_tex_fonts = "true";
644 registerAutomaticallyLoadedPackage("fontspec");
645 if (h_inputencoding == "auto")
646 p.setEncoding("utf8");
650 if (is_known(name, known_roman_fonts)) {
655 if (name == "fourier") {
656 h_font_roman = "utopia";
657 // when font uses real small capitals
658 if (opts == "expert")
662 else if (name == "mathpazo")
663 h_font_roman = "palatino";
665 else if (name == "mathptmx")
666 h_font_roman = "times";
669 if (is_known(name, known_sans_fonts)) {
673 h_font_sf_scale = scale_as_percentage(scale);
678 if (is_known(name, known_typewriter_fonts)) {
679 // fourier can be set as roman font _only_
680 // fourier as typewriter is handled in handling of \ttdefault
681 if (name != "fourier") {
682 h_font_typewriter = name;
685 h_font_tt_scale = scale_as_percentage(scale);
690 // font uses old-style figure
694 // after the detection and handling of special cases, we can remove the
695 // fonts, otherwise they would appear in the preamble, see bug #7856
696 if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
697 || is_known(name, known_typewriter_fonts))
700 else if (name == "amsmath" || name == "amssymb" ||
701 name == "esint" || name == "mhchem" || name == "mathdots" ||
702 name == "mathtools" || name == "undertilde")
703 h_use_packages[name] = "2";
705 else if (name == "babel") {
706 h_language_package = "default";
707 // One might think we would have to do nothing if babel is loaded
708 // without any options to prevent pollution of the preamble with this
709 // babel call in every roundtrip.
710 // But the user could have defined babel-specific things afterwards. So
711 // we need to keep it in the preamble to prevent cases like bug #7861.
713 // check if more than one option was used - used later for inputenc
714 if (options.begin() != options.end() - 1)
715 one_language = false;
716 // babel takes the last language of the option of its \usepackage
717 // call as document language. If there is no such language option, the
718 // last language in the documentclass options is used.
719 handle_opt(options, known_languages, h_language);
720 // If babel is called with options, LyX puts them by default into the
721 // document class options. This works for most languages, except
722 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
723 // perhaps in future others.
724 // Therefore keep the babel call as it is as the user might have
726 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
727 delete_opt(options, known_languages);
728 // finally translate the babel name to a LyX name
729 h_language = babel2lyx(h_language);
732 h_preamble << "\\usepackage{babel}\n";
735 else if (name == "polyglossia") {
736 h_language_package = "default";
737 h_default_output_format = "pdf4";
738 h_use_non_tex_fonts = "true";
740 registerAutomaticallyLoadedPackage("xunicode");
741 if (h_inputencoding == "auto")
742 p.setEncoding("utf8");
745 else if (name == "CJK") {
746 // It is impossible to determine the document language if CJK is used.
747 // All we can do is to notify the user that he has to set this by hisself.
749 // set the encoding to "auto" because it might be set to "default" by the babel handling
750 // and this would not be correct for CJK
751 h_inputencoding = "auto";
752 registerAutomaticallyLoadedPackage("CJK");
755 else if (name == "fontenc") {
756 h_fontencoding = getStringFromVector(options, ",");
757 /* We could do the following for better round trip support,
758 * but this makes the document less portable, so I skip it:
759 if (h_fontencoding == lyxrc.fontenc)
760 h_fontencoding = "global";
765 else if (name == "inputenc" || name == "luainputenc") {
766 // h_inputencoding is only set when there is not more than one
767 // inputenc option because otherwise h_inputencoding must be
768 // set to "auto" (the default encoding of the document language)
769 // Therefore check for the "," character.
770 // It is also only set when there is not more than one babel
772 if (opts.find(",") == string::npos && one_language == true)
773 h_inputencoding = opts;
774 if (!options.empty())
775 p.setEncoding(options.back());
779 else if (is_known(name, known_old_language_packages)) {
780 // known language packages from the times before babel
781 // if they are found and not also babel, they will be used as
782 // custom language package
783 h_language_package = "\\usepackage{" + name + "}";
786 else if (name == "prettyref")
787 ; // ignore this FIXME: Use the package separator mechanism instead
789 else if (name == "lyxskak") {
790 // ignore this and its options
791 const char * const o[] = {"ps", "mover", 0};
792 delete_opt(options, o);
795 else if (is_known(name, known_lyx_packages) && options.empty()) {
796 if (name == "splitidx")
797 h_use_indices = "true";
798 if (!in_lyx_preamble)
799 h_preamble << package_beg_sep << name
800 << package_mid_sep << "\\usepackage{"
801 << name << "}\n" << package_end_sep;
804 else if (name == "geometry")
805 handle_geometry(options);
807 else if (name == "subfig")
808 ; // ignore this FIXME: Use the package separator mechanism instead
810 else if (is_known(name, known_languages))
813 else if (name == "natbib") {
814 h_biblio_style = "plainnat";
815 h_cite_engine = "natbib";
816 h_cite_engine_type = "authoryear";
817 vector<string>::iterator it =
818 find(options.begin(), options.end(), "authoryear");
819 if (it != options.end())
822 it = find(options.begin(), options.end(), "numbers");
823 if (it != options.end()) {
824 h_cite_engine_type = "numerical";
830 else if (name == "jurabib") {
831 h_biblio_style = "jurabib";
832 h_cite_engine = "jurabib";
833 h_cite_engine_type = "authoryear";
836 else if (name == "hyperref")
837 handle_hyperref(options);
839 else if (!in_lyx_preamble) {
841 h_preamble << "\\usepackage{" << name << "}\n";
843 h_preamble << "\\usepackage[" << opts << "]{"
849 // We need to do something with the options...
850 if (!options.empty())
851 cerr << "Ignoring options '" << join(options, ",")
852 << "' of package " << name << '.' << endl;
854 // remove the whitespace
859 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
862 Token t = p.get_token();
863 if (t.cat() == catEscape &&
864 is_known(t.cs(), known_if_commands))
865 handle_if(p, in_lyx_preamble);
867 if (!in_lyx_preamble)
868 h_preamble << t.asInput();
869 if (t.cat() == catEscape && t.cs() == "fi")
876 bool Preamble::writeLyXHeader(ostream & os, bool subdoc)
878 // set the quote language
879 // LyX only knows the following quotes languages:
880 // english, swedish, german, polish, french and danish
881 // (quotes for "japanese" and "chinese-traditional" are missing because
882 // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
883 // conversion list taken from
884 // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
885 // (quotes for kazakh and interlingua are unknown)
887 if (h_language == "danish")
888 h_quotes_language = "danish";
890 else if (is_known(h_language, known_french_quotes_languages))
891 h_quotes_language = "french";
893 else if (is_known(h_language, known_german_quotes_languages))
894 h_quotes_language = "german";
896 else if (is_known(h_language, known_polish_quotes_languages))
897 h_quotes_language = "polish";
899 else if (is_known(h_language, known_swedish_quotes_languages))
900 h_quotes_language = "swedish";
902 else if (is_known(h_language, known_english_quotes_languages))
903 h_quotes_language = "english";
905 if (contains(h_float_placement, "H"))
906 registerAutomaticallyLoadedPackage("float");
907 if (h_spacing != "single" && h_spacing != "default")
908 registerAutomaticallyLoadedPackage("setspace");
909 if (h_use_packages["amsmath"] == "2") {
910 // amsbsy and amstext are already provided by amsmath
911 registerAutomaticallyLoadedPackage("amsbsy");
912 registerAutomaticallyLoadedPackage("amstext");
915 // output the LyX file settings
916 os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
917 << "\\lyxformat " << LYX_FORMAT << '\n'
918 << "\\begin_document\n"
919 << "\\begin_header\n"
920 << "\\textclass " << h_textclass << "\n";
921 string const raw = subdoc ? empty_string() : h_preamble.str();
923 os << "\\begin_preamble\n";
924 for (string::size_type i = 0; i < raw.size(); ++i) {
925 if (raw[i] == package_beg_sep) {
926 // Here follows some package loading code that
927 // must be skipped if the package is loaded
929 string::size_type j = raw.find(package_mid_sep, i);
930 if (j == string::npos)
932 string::size_type k = raw.find(package_end_sep, j);
933 if (k == string::npos)
935 string const package = raw.substr(i + 1, j - i - 1);
936 string const replacement = raw.substr(j + 1, k - j - 1);
937 if (auto_packages.find(package) == auto_packages.end())
943 os << "\n\\end_preamble\n";
945 if (!h_options.empty())
946 os << "\\options " << h_options << "\n";
947 os << "\\use_default_options " << h_use_default_options << "\n";
948 if (!used_modules.empty()) {
949 os << "\\begin_modules\n";
950 vector<string>::const_iterator const end = used_modules.end();
951 vector<string>::const_iterator it = used_modules.begin();
952 for (; it != end; ++it)
954 os << "\\end_modules\n";
956 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
957 << "\\language " << h_language << "\n"
958 << "\\language_package " << h_language_package << "\n"
959 << "\\inputencoding " << h_inputencoding << "\n"
960 << "\\fontencoding " << h_fontencoding << "\n"
961 << "\\font_roman " << h_font_roman << "\n"
962 << "\\font_sans " << h_font_sans << "\n"
963 << "\\font_typewriter " << h_font_typewriter << "\n"
964 << "\\font_default_family " << h_font_default_family << "\n"
965 << "\\use_non_tex_fonts " << h_use_non_tex_fonts << "\n"
966 << "\\font_sc " << h_font_sc << "\n"
967 << "\\font_osf " << h_font_osf << "\n"
968 << "\\font_sf_scale " << h_font_sf_scale << "\n"
969 << "\\font_tt_scale " << h_font_tt_scale << "\n\n"
970 << "\\graphics " << h_graphics << "\n"
971 << "\\default_output_format " << h_default_output_format << "\n"
972 << "\\output_sync " << h_output_sync << "\n"
973 << "\\bibtex_command " << h_bibtex_command << "\n"
974 << "\\index_command " << h_index_command << "\n";
975 if (!h_float_placement.empty())
976 os << "\\float_placement " << h_float_placement << "\n";
977 os << "\\paperfontsize " << h_paperfontsize << "\n"
978 << "\\spacing " << h_spacing << "\n"
979 << "\\use_hyperref " << h_use_hyperref << '\n';
980 if (h_use_hyperref == "1") {
981 if (!h_pdf_title.empty())
982 os << "\\pdf_title \"" << h_pdf_title << "\"\n";
983 if (!h_pdf_author.empty())
984 os << "\\pdf_author \"" << h_pdf_author << "\"\n";
985 if (!h_pdf_subject.empty())
986 os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
987 if (!h_pdf_keywords.empty())
988 os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
989 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
990 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
991 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
992 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
993 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
994 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
995 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
996 "\\pdf_backref " << h_pdf_backref << "\n"
997 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
998 if (!h_pdf_pagemode.empty())
999 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1000 if (!h_pdf_quoted_options.empty())
1001 os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
1003 os << "\\papersize " << h_papersize << "\n"
1004 << "\\use_geometry " << h_use_geometry << '\n';
1005 for (map<string, string>::const_iterator it = h_use_packages.begin();
1006 it != h_use_packages.end(); ++it)
1007 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1008 os << "\\cite_engine " << h_cite_engine << '\n'
1009 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1010 << "\\biblio_style " << h_biblio_style << "\n"
1011 << "\\use_bibtopic " << h_use_bibtopic << "\n"
1012 << "\\use_indices " << h_use_indices << "\n"
1013 << "\\paperorientation " << h_paperorientation << '\n'
1014 << "\\suppress_date " << h_suppress_date << '\n'
1015 << "\\justification " << h_justification << '\n'
1016 << "\\use_refstyle " << h_use_refstyle << '\n';
1017 if (!h_fontcolor.empty())
1018 os << "\\fontcolor " << h_fontcolor << '\n';
1019 if (!h_notefontcolor.empty())
1020 os << "\\notefontcolor " << h_notefontcolor << '\n';
1021 if (!h_backgroundcolor.empty())
1022 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1023 if (!h_boxbgcolor.empty())
1024 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1025 os << "\\index " << h_index << '\n'
1026 << "\\boxbgcolor " << h_boxbgcolor << '\n'
1027 << "\\color " << h_color << '\n'
1030 << "\\secnumdepth " << h_secnumdepth << "\n"
1031 << "\\tocdepth " << h_tocdepth << "\n"
1032 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1033 if (h_paragraph_separation == "skip")
1034 os << "\\defskip " << h_defskip << "\n";
1036 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1037 os << "\\quotes_language " << h_quotes_language << "\n"
1038 << "\\papercolumns " << h_papercolumns << "\n"
1039 << "\\papersides " << h_papersides << "\n"
1040 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1041 if (!h_listings_params.empty())
1042 os << "\\listings_params " << h_listings_params << "\n";
1043 os << "\\tracking_changes " << h_tracking_changes << "\n"
1044 << "\\output_changes " << h_output_changes << "\n"
1045 << "\\html_math_output " << h_html_math_output << "\n"
1046 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1047 << "\\html_be_strict " << h_html_be_strict << "\n"
1049 << "\\end_header\n\n"
1050 << "\\begin_body\n";
1055 void Preamble::parse(Parser & p, string const & forceclass,
1056 TeX2LyXDocClass & tc)
1058 // initialize fixed types
1059 special_columns['D'] = 3;
1060 bool is_full_document = false;
1061 bool is_lyx_file = false;
1062 bool in_lyx_preamble = false;
1064 // determine whether this is a full document or a fragment for inclusion
1066 Token const & t = p.get_token();
1068 if (t.cat() == catEscape && t.cs() == "documentclass") {
1069 is_full_document = true;
1075 while (is_full_document && p.good()) {
1076 Token const & t = p.get_token();
1079 cerr << "t: " << t << "\n";
1085 if (!in_lyx_preamble &&
1086 (t.cat() == catLetter ||
1087 t.cat() == catSuper ||
1088 t.cat() == catSub ||
1089 t.cat() == catOther ||
1090 t.cat() == catMath ||
1091 t.cat() == catActive ||
1092 t.cat() == catBegin ||
1093 t.cat() == catEnd ||
1094 t.cat() == catAlign ||
1095 t.cat() == catParameter))
1096 h_preamble << t.cs();
1098 else if (!in_lyx_preamble &&
1099 (t.cat() == catSpace || t.cat() == catNewline))
1100 h_preamble << t.asInput();
1102 else if (t.cat() == catComment) {
1103 static regex const islyxfile("%% LyX .* created this file");
1104 static regex const usercommands("User specified LaTeX commands");
1106 string const comment = t.asInput();
1108 // magically switch encoding default if it looks like XeLaTeX
1109 static string const magicXeLaTeX =
1110 "% This document must be compiled with XeLaTeX ";
1111 if (comment.size() > magicXeLaTeX.size()
1112 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1113 && h_inputencoding == "auto") {
1114 cerr << "XeLaTeX comment found, switching to UTF8\n";
1115 h_inputencoding = "utf8";
1118 if (regex_search(comment, sub, islyxfile)) {
1120 in_lyx_preamble = true;
1121 } else if (is_lyx_file
1122 && regex_search(comment, sub, usercommands))
1123 in_lyx_preamble = false;
1124 else if (!in_lyx_preamble)
1125 h_preamble << t.asInput();
1128 else if (t.cs() == "pagestyle")
1129 h_paperpagestyle = p.verbatim_item();
1131 else if (t.cs() == "setdefaultlanguage") {
1133 // We don't yet care about non-language variant options
1134 // because LyX doesn't support this yet, see bug #8214
1136 string langopts = p.getOpt();
1137 // check if the option contains a variant, if yes, extract it
1138 string::size_type pos_var = langopts.find("variant");
1139 string::size_type i = langopts.find(',', pos_var);
1140 if (pos_var != string::npos){
1142 if (i == string::npos)
1143 variant = langopts.substr(pos_var + 8, langopts.length() - pos_var - 9);
1145 variant = langopts.substr(pos_var + 8, i - pos_var - 8);
1146 h_language = variant;
1150 h_language = p.verbatim_item();
1151 //finally translate the poyglossia name to a LyX name
1152 h_language = polyglossia2lyx(h_language);
1155 else if (t.cs() == "setotherlanguage") {
1156 // We don't yet care about the option because LyX doesn't
1157 // support this yet, see bug #8214
1158 p.hasOpt() ? p.getOpt() : string();
1162 else if (t.cs() == "setmainfont") {
1163 // we don't care about the option
1164 p.hasOpt() ? p.getOpt() : string();
1165 h_font_roman = p.getArg('{', '}');
1168 else if (t.cs() == "setsansfont") {
1169 // we don't care about the option
1170 p.hasOpt() ? p.getOpt() : string();
1171 h_font_sans = p.getArg('{', '}');
1174 else if (t.cs() == "setmonofont") {
1175 // we don't care about the option
1176 p.hasOpt() ? p.getOpt() : string();
1177 h_font_typewriter = p.getArg('{', '}');
1180 else if (t.cs() == "date") {
1181 string argument = p.getArg('{', '}');
1182 if (argument.empty())
1183 h_suppress_date = "true";
1185 h_preamble << t.asInput() << '{' << argument << '}';
1188 else if (t.cs() == "color") {
1189 string const space =
1190 (p.hasOpt() ? p.getOpt() : string());
1191 string argument = p.getArg('{', '}');
1192 // check the case that a standard color is used
1193 if (space.empty() && is_known(argument, known_basic_colors)) {
1194 h_fontcolor = rgbcolor2code(argument);
1195 preamble.registerAutomaticallyLoadedPackage("color");
1196 } else if (space.empty() && argument == "document_fontcolor")
1197 preamble.registerAutomaticallyLoadedPackage("color");
1198 // check the case that LyX's document_fontcolor is defined
1199 // but not used for \color
1201 h_preamble << t.asInput();
1203 h_preamble << space;
1204 h_preamble << '{' << argument << '}';
1205 // the color might already be set because \definecolor
1206 // is parsed before this
1211 else if (t.cs() == "pagecolor") {
1212 string argument = p.getArg('{', '}');
1213 // check the case that a standard color is used
1214 if (is_known(argument, known_basic_colors)) {
1215 h_backgroundcolor = rgbcolor2code(argument);
1216 } else if (argument == "page_backgroundcolor")
1217 preamble.registerAutomaticallyLoadedPackage("color");
1218 // check the case that LyX's page_backgroundcolor is defined
1219 // but not used for \pagecolor
1221 h_preamble << t.asInput() << '{' << argument << '}';
1222 // the color might already be set because \definecolor
1223 // is parsed before this
1224 h_backgroundcolor = "";
1228 else if (t.cs() == "makeatletter") {
1229 // LyX takes care of this
1230 p.setCatCode('@', catLetter);
1233 else if (t.cs() == "makeatother") {
1234 // LyX takes care of this
1235 p.setCatCode('@', catOther);
1238 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1239 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1240 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1241 || t.cs() == "DeclareRobustCommand"
1242 || t.cs() == "DeclareRobustCommandx"
1243 || t.cs() == "ProvideTextCommandDefault"
1244 || t.cs() == "DeclareMathAccent") {
1246 if (p.next_token().character() == '*') {
1250 string const name = p.verbatim_item();
1251 string const opt1 = p.getFullOpt();
1252 string const opt2 = p.getFullOpt();
1253 string const body = p.verbatim_item();
1255 if (name == "\\rmdefault")
1256 if (is_known(body, known_roman_fonts))
1257 h_font_roman = body;
1258 if (name == "\\sfdefault")
1259 if (is_known(body, known_sans_fonts))
1261 if (name == "\\ttdefault")
1262 if (is_known(body, known_typewriter_fonts))
1263 h_font_typewriter = body;
1264 if (name == "\\familydefault") {
1265 string family = body;
1266 // remove leading "\"
1267 h_font_default_family = family.erase(0,1);
1270 // remove the lyxdot definition that is re-added by LyX
1272 if (name == "\\lyxdot")
1273 in_lyx_preamble = true;
1275 // Add the command to the known commands
1276 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1278 // only non-lyxspecific stuff
1279 if (!in_lyx_preamble) {
1281 ss << '\\' << t.cs();
1284 ss << '{' << name << '}' << opt1 << opt2
1285 << '{' << body << "}";
1286 h_preamble << ss.str();
1288 ostream & out = in_preamble ? h_preamble : os;
1289 out << "\\" << t.cs() << "{" << name << "}"
1290 << opts << "{" << body << "}";
1295 else if (t.cs() == "documentclass") {
1296 vector<string>::iterator it;
1297 vector<string> opts = split_options(p.getArg('[', ']'));
1298 handle_opt(opts, known_fontsizes, h_paperfontsize);
1299 delete_opt(opts, known_fontsizes);
1300 // delete "pt" at the end
1301 string::size_type i = h_paperfontsize.find("pt");
1302 if (i != string::npos)
1303 h_paperfontsize.erase(i);
1304 // The documentclass options are always parsed before the options
1305 // of the babel call so that a language cannot overwrite the babel
1307 handle_opt(opts, known_languages, h_language);
1308 delete_opt(opts, known_languages);
1310 // paper orientation
1311 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1312 h_paperorientation = "landscape";
1316 if ((it = find(opts.begin(), opts.end(), "oneside"))
1321 if ((it = find(opts.begin(), opts.end(), "twoside"))
1327 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1329 h_papercolumns = "1";
1332 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1334 h_papercolumns = "2";
1338 // some size options are known to any document classes, other sizes
1339 // are handled by the \geometry command of the geometry package
1340 handle_opt(opts, known_class_paper_sizes, h_papersize);
1341 delete_opt(opts, known_class_paper_sizes);
1342 // the remaining options
1343 h_options = join(opts, ",");
1344 // FIXME This does not work for classes that have a
1345 // different name in LyX than in LaTeX
1346 h_textclass = p.getArg('{', '}');
1349 else if (t.cs() == "usepackage") {
1350 string const options = p.getArg('[', ']');
1351 string const name = p.getArg('{', '}');
1352 vector<string> vecnames;
1353 split(name, vecnames, ',');
1354 vector<string>::const_iterator it = vecnames.begin();
1355 vector<string>::const_iterator end = vecnames.end();
1356 for (; it != end; ++it)
1357 handle_package(p, trimSpaceAndEol(*it), options,
1361 else if (t.cs() == "inputencoding") {
1362 string const encoding = p.getArg('{','}');
1363 h_inputencoding = encoding;
1364 p.setEncoding(encoding);
1367 else if (t.cs() == "newenvironment") {
1368 string const name = p.getArg('{', '}');
1369 string const opt1 = p.getFullOpt();
1370 string const opt2 = p.getFullOpt();
1371 string const beg = p.verbatim_item();
1372 string const end = p.verbatim_item();
1373 if (!in_lyx_preamble) {
1374 h_preamble << "\\newenvironment{" << name
1375 << '}' << opt1 << opt2 << '{'
1376 << beg << "}{" << end << '}';
1378 add_known_environment(name, opt1, !opt2.empty(),
1379 from_utf8(beg), from_utf8(end));
1383 else if (t.cs() == "def") {
1384 string name = p.get_token().cs();
1385 // In fact, name may be more than the name:
1386 // In the test case of bug 8116
1387 // name == "csname SF@gobble@opt \endcsname".
1388 // Therefore, we need to use asInput() instead of cs().
1389 while (p.next_token().cat() != catBegin)
1390 name += p.get_token().asInput();
1391 if (!in_lyx_preamble)
1392 h_preamble << "\\def\\" << name << '{'
1393 << p.verbatim_item() << "}";
1396 else if (t.cs() == "newcolumntype") {
1397 string const name = p.getArg('{', '}');
1398 trimSpaceAndEol(name);
1400 string opts = p.getOpt();
1401 if (!opts.empty()) {
1402 istringstream is(string(opts, 1));
1405 special_columns[name[0]] = nargs;
1406 h_preamble << "\\newcolumntype{" << name << "}";
1408 h_preamble << "[" << nargs << "]";
1409 h_preamble << "{" << p.verbatim_item() << "}";
1412 else if (t.cs() == "setcounter") {
1413 string const name = p.getArg('{', '}');
1414 string const content = p.getArg('{', '}');
1415 if (name == "secnumdepth")
1416 h_secnumdepth = content;
1417 else if (name == "tocdepth")
1418 h_tocdepth = content;
1420 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1423 else if (t.cs() == "setlength") {
1424 string const name = p.verbatim_item();
1425 string const content = p.verbatim_item();
1426 // the paragraphs are only not indented when \parindent is set to zero
1427 if (name == "\\parindent" && content != "") {
1428 if (content[0] == '0')
1429 h_paragraph_separation = "skip";
1431 h_paragraph_indentation = translate_len(content);
1432 } else if (name == "\\parskip") {
1433 if (content == "\\smallskipamount")
1434 h_defskip = "smallskip";
1435 else if (content == "\\medskipamount")
1436 h_defskip = "medskip";
1437 else if (content == "\\bigskipamount")
1438 h_defskip = "bigskip";
1440 h_defskip = content;
1442 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1445 else if (t.cs() == "onehalfspacing")
1446 h_spacing = "onehalf";
1448 else if (t.cs() == "doublespacing")
1449 h_spacing = "double";
1451 else if (t.cs() == "setstretch")
1452 h_spacing = "other " + p.verbatim_item();
1454 else if (t.cs() == "begin") {
1455 string const name = p.getArg('{', '}');
1456 if (name == "document")
1458 h_preamble << "\\begin{" << name << "}";
1461 else if (t.cs() == "geometry") {
1462 vector<string> opts = split_options(p.getArg('{', '}'));
1463 handle_geometry(opts);
1466 else if (t.cs() == "definecolor") {
1467 string const color = p.getArg('{', '}');
1468 string const space = p.getArg('{', '}');
1469 string const value = p.getArg('{', '}');
1470 if (color == "document_fontcolor" && space == "rgb") {
1471 RGBColor c(RGBColorFromLaTeX(value));
1472 h_fontcolor = X11hexname(c);
1473 } else if (color == "note_fontcolor" && space == "rgb") {
1474 RGBColor c(RGBColorFromLaTeX(value));
1475 h_notefontcolor = X11hexname(c);
1476 } else if (color == "page_backgroundcolor" && space == "rgb") {
1477 RGBColor c(RGBColorFromLaTeX(value));
1478 h_backgroundcolor = X11hexname(c);
1479 } else if (color == "shadecolor" && space == "rgb") {
1480 RGBColor c(RGBColorFromLaTeX(value));
1481 h_boxbgcolor = X11hexname(c);
1483 h_preamble << "\\definecolor{" << color
1484 << "}{" << space << "}{" << value
1489 else if (t.cs() == "bibliographystyle")
1490 h_biblio_style = p.verbatim_item();
1492 else if (t.cs() == "jurabibsetup") {
1493 // FIXME p.getArg('{', '}') is most probably wrong (it
1494 // does not handle nested braces).
1495 // Use p.verbatim_item() instead.
1496 vector<string> jurabibsetup =
1497 split_options(p.getArg('{', '}'));
1498 // add jurabibsetup to the jurabib package options
1499 add_package("jurabib", jurabibsetup);
1500 if (!jurabibsetup.empty()) {
1501 h_preamble << "\\jurabibsetup{"
1502 << join(jurabibsetup, ",") << '}';
1506 else if (t.cs() == "hypersetup") {
1507 vector<string> hypersetup =
1508 split_options(p.verbatim_item());
1509 // add hypersetup to the hyperref package options
1510 handle_hyperref(hypersetup);
1511 if (!hypersetup.empty()) {
1512 h_preamble << "\\hypersetup{"
1513 << join(hypersetup, ",") << '}';
1517 else if (is_known(t.cs(), known_if_3arg_commands)) {
1518 // prevent misparsing of \usepackage if it is used
1519 // as an argument (see e.g. our own output of
1520 // \@ifundefined above)
1521 string const arg1 = p.verbatim_item();
1522 string const arg2 = p.verbatim_item();
1523 string const arg3 = p.verbatim_item();
1524 // test case \@ifundefined{date}{}{\date{}}
1525 if (t.cs() == "@ifundefined" && arg1 == "date" &&
1526 arg2.empty() && arg3 == "\\date{}") {
1527 h_suppress_date = "true";
1528 // older tex2lyx versions did output
1529 // \@ifundefined{definecolor}{\usepackage{color}}{}
1530 } else if (t.cs() == "@ifundefined" &&
1531 arg1 == "definecolor" &&
1532 arg2 == "\\usepackage{color}" &&
1534 if (!in_lyx_preamble)
1535 h_preamble << package_beg_sep
1538 << "\\@ifundefined{definecolor}{color}{}"
1541 //\@ifundefined{showcaptionsetup}{}{%
1542 // \PassOptionsToPackage{caption=false}{subfig}}
1543 // that LyX uses for subfloats
1544 } else if (t.cs() == "@ifundefined" &&
1545 arg1 == "showcaptionsetup" && arg2.empty()
1546 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1548 } else if (!in_lyx_preamble) {
1549 h_preamble << t.asInput()
1550 << '{' << arg1 << '}'
1551 << '{' << arg2 << '}'
1552 << '{' << arg3 << '}';
1556 else if (is_known(t.cs(), known_if_commands)) {
1557 // must not parse anything in conditional code, since
1558 // LyX would output the parsed contents unconditionally
1559 if (!in_lyx_preamble)
1560 h_preamble << t.asInput();
1561 handle_if(p, in_lyx_preamble);
1564 else if (!t.cs().empty() && !in_lyx_preamble)
1565 h_preamble << '\\' << t.cs();
1568 // remove the whitespace
1571 // Force textclass if the user wanted it
1572 if (!forceclass.empty())
1573 h_textclass = forceclass;
1574 if (noweb_mode && !prefixIs(h_textclass, "literate-"))
1575 h_textclass.insert(0, "literate-");
1576 tc.setName(h_textclass);
1578 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1581 if (h_papersides.empty()) {
1584 h_papersides = ss.str();
1589 string babel2lyx(string const & language)
1591 char const * const * where = is_known(language, known_languages);
1593 return known_coded_languages[where - known_languages];
1598 string polyglossia2lyx(string const & language)
1600 char const * const * where = is_known(language, polyglossia_languages);
1602 return coded_polyglossia_languages[where - polyglossia_languages];
1607 string rgbcolor2code(string const & name)
1609 char const * const * where = is_known(name, known_basic_colors);
1611 // "red", "green" etc
1612 return known_basic_color_codes[where - known_basic_colors];
1614 // "255,0,0", "0,255,0" etc
1615 RGBColor c(RGBColorFromLaTeX(name));
1616 return X11hexname(c);