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.
20 #include "LayoutFile.h"
23 #include "TextClass.h"
25 #include "support/convert.h"
26 #include "support/FileName.h"
27 #include "support/filetools.h"
28 #include "support/lstrings.h"
30 #include "support/regex.h"
36 using namespace lyx::support;
41 // special columntypes
42 extern map<char, int> special_columns;
48 // CJK languages are handled in text.cpp, polyglossia languages are listed
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", "japanese", "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", "japanese", "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",
91 /// languages with danish quotes (.lyx names)
92 const char * const known_danish_quotes_languages[] = {"danish", 0};
94 /// languages with english quotes (.lyx names)
95 const char * const known_english_quotes_languages[] = {"american", "australian",
96 "bahasa", "bahasam", "brazilian", "canadian", "chinese-simplified", "english",
97 "esperanto", "hebrew", "irish", "korean", "newzealand", "portuguese", "scottish",
100 /// languages with french quotes (.lyx names)
101 const char * const known_french_quotes_languages[] = {"albanian",
102 "arabic_arabi", "arabic_arabtex", "basque", "canadien", "catalan", "french",
103 "galician", "greek", "italian", "norsk", "nynorsk", "polutonikogreek",
104 "russian", "spanish", "spanish-mexico", "turkish", "turkmen", "ukrainian",
107 /// languages with german quotes (.lyx names)
108 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
109 "czech", "german", "icelandic", "lithuanian", "lowersorbian", "naustrian",
110 "ngerman", "serbian", "serbian-latin", "slovak", "slovene", "uppersorbian", 0};
112 /// languages with polish quotes (.lyx names)
113 const char * const known_polish_quotes_languages[] = {"afrikaans", "croatian",
114 "dutch", "estonian", "magyar", "polish", "romanian", 0};
116 /// languages with swedish quotes (.lyx names)
117 const char * const known_swedish_quotes_languages[] = {"finnish",
120 /// known language packages from the times before babel
121 const char * const known_old_language_packages[] = {"french", "frenchle",
122 "frenchpro", "german", "ngerman", "pmfrench", 0};
124 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
126 const char * const known_roman_fonts[] = { "ae", "beraserif", "bookman",
127 "ccfonts", "chancery", "charter", "cmr", "fourier", "garamondx", "lmodern",
128 "mathdesign", "mathpazo", "mathptmx", "newcent", "utopia", 0};
130 const char * const known_sans_fonts[] = { "avant", "berasans", "cmbr", "cmss",
131 "helvet", "kurier", "kurierl", "lmss", 0};
133 const char * const known_kurier_fonts[] = { "kurier", "kurierl", "kurier-condensed",
134 "kurier-light-condensed", 0};
136 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
137 "courier", "lmtt", "luximono", "fourier", "lmodern", "mathpazo", "mathptmx",
140 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
141 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
142 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
143 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
144 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
146 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
147 "executivepaper", "legalpaper", "letterpaper", 0};
149 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
150 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
152 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
153 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
156 /// commands that can start an \if...\else...\endif sequence
157 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
158 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
159 "ifsidecap", "ifupgreek", 0};
161 const char * const known_basic_colors[] = {"blue", "black", "cyan", "green",
162 "magenta", "red", "white", "yellow", 0};
164 const char * const known_basic_color_codes[] = {"#0000ff", "#000000", "#00ffff", "#00ff00",
165 "#ff00ff", "#ff0000", "#ffffff", "#ffff00", 0};
167 /// conditional commands with three arguments like \@ifundefined{}{}{}
168 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
171 /// packages that work only in xetex
172 /// polyglossia is handled separately
173 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
174 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
175 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
177 /// packages that are automatically skipped if loaded by LyX
178 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
179 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color", "float",
180 "fontspec", "graphicx", "hhline", "ifthen", "longtable", "makeidx", "multirow",
181 "nomencl", "pdfpages", "rotating", "rotfloat", "splitidx", "setspace",
182 "subscript", "textcomp", "ulem", "url", "varioref", "verbatim", "wrapfig",
185 // codes used to remove packages that are loaded automatically by LyX.
186 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
187 const char package_beg_sep = '\001';
188 const char package_mid_sep = '\002';
189 const char package_end_sep = '\003';
192 // returns true if at least one of the options in what has been found
193 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
199 // the last language option is the document language (for babel and LyX)
200 // the last size option is the document font size
201 vector<string>::iterator it;
202 vector<string>::iterator position = opts.begin();
203 for (; *what; ++what) {
204 it = find(opts.begin(), opts.end(), *what);
205 if (it != opts.end()) {
206 if (it >= position) {
217 void delete_opt(vector<string> & opts, char const * const * what)
222 // remove found options from the list
223 // do this after handle_opt to avoid potential memory leaks
224 vector<string>::iterator it;
225 for (; *what; ++what) {
226 it = find(opts.begin(), opts.end(), *what);
227 if (it != opts.end())
234 * Split a package options string (keyval format) into a vector.
236 * authorformat=smallcaps,
238 * titleformat=colonsep,
239 * bibformat={tabular,ibidem,numbered}
241 vector<string> split_options(string const & input)
243 vector<string> options;
247 Token const & t = p.get_token();
248 if (t.asInput() == ",") {
249 options.push_back(trimSpaceAndEol(option));
251 } else if (t.asInput() == "=") {
254 if (p.next_token().asInput() == "{")
255 option += '{' + p.getArg('{', '}') + '}';
256 } else if (t.cat() != catSpace)
257 option += t.asInput();
261 options.push_back(trimSpaceAndEol(option));
268 * Retrieve a keyval option "name={value with=sign}" named \p name from
269 * \p options and return the value.
270 * The found option is also removed from \p options.
272 string process_keyval_opt(vector<string> & options, string name)
274 for (size_t i = 0; i < options.size(); ++i) {
275 vector<string> option;
276 split(options[i], option, '=');
277 if (option.size() < 2)
279 if (option[0] == name) {
280 options.erase(options.begin() + i);
281 option.erase(option.begin());
282 return join(option, "=");
288 } // anonymous namespace
292 * known polyglossia language names (including variants)
294 const char * const Preamble::polyglossia_languages[] = {
295 "albanian", "croatian", "hebrew", "norsk", "swedish", "amharic", "czech", "hindi",
296 "nynorsk", "syriac", "arabic", "danish", "icelandic", "occitan", "tamil",
297 "armenian", "divehi", "interlingua", "polish", "telugu", "asturian", "dutch",
298 "irish", "portuges", "thai", "bahasai", "english", "italian", "romanian", "turkish",
299 "bahasam", "esperanto", "lao", "russian", "turkmen", "basque", "estonian", "latin",
300 "samin", "ukrainian", "bengali", "farsi", "latvian", "sanskrit", "urdu", "brazil",
301 "brazilian", "finnish", "lithuanian", "scottish", "usorbian", "breton", "french",
302 "lsorbian", "serbian", "vietnamese", "bulgarian", "galician", "magyar", "slovak",
303 "welsh", "catalan", "german", "malayalam", "slovenian", "coptic", "greek",
304 "marathi", "spanish",
305 "american", "ancient", "australian", "british", "monotonic", "newzealand",
309 * the same as polyglossia_languages with .lyx names
310 * please keep this in sync with polyglossia_languages line by line!
312 const char * const Preamble::coded_polyglossia_languages[] = {
313 "albanian", "croatian", "hebrew", "norsk", "swedish", "amharic", "czech", "hindi",
314 "nynorsk", "syriac", "arabic_arabi", "danish", "icelandic", "occitan", "tamil",
315 "armenian", "divehi", "interlingua", "polish", "telugu", "asturian", "dutch",
316 "irish", "portuges", "thai", "bahasa", "english", "italian", "romanian", "turkish",
317 "bahasam", "esperanto", "lao", "russian", "turkmen", "basque", "estonian", "latin",
318 "samin", "ukrainian", "bengali", "farsi", "latvian", "sanskrit", "urdu", "brazilian",
319 "brazilian", "finnish", "lithuanian", "scottish", "uppersorbian", "breton", "french",
320 "lowersorbian", "serbian", "vietnamese", "bulgarian", "galician", "magyar", "slovak",
321 "welsh", "catalan", "ngerman", "malayalam", "slovene", "coptic", "greek",
322 "marathi", "spanish",
323 "american", "ancientgreek", "australian", "british", "greek", "newzealand",
324 "polutonikogreek", 0};
327 bool Preamble::indentParagraphs() const
329 return h_paragraph_separation == "indent";
333 bool Preamble::isPackageUsed(string const & package) const
335 return used_packages.find(package) != used_packages.end();
339 vector<string> Preamble::getPackageOptions(string const & package) const
341 map<string, vector<string> >::const_iterator it = used_packages.find(package);
342 if (it != used_packages.end())
344 return vector<string>();
348 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
350 auto_packages.insert(package);
354 void Preamble::addModule(string const & module)
356 used_modules.push_back(module);
360 void Preamble::suppressDate(bool suppress)
363 h_suppress_date = "true";
365 h_suppress_date = "false";
369 void Preamble::registerAuthor(std::string const & name)
371 Author author(from_utf8(name), empty_docstring());
372 author.setUsed(true);
373 authors_.record(author);
374 h_tracking_changes = "true";
375 h_output_changes = "true";
379 Author const & Preamble::getAuthor(std::string const & name) const
381 Author author(from_utf8(name), empty_docstring());
382 for (AuthorList::Authors::const_iterator it = authors_.begin();
383 it != authors_.end(); ++it)
386 static Author const dummy;
391 void Preamble::add_package(string const & name, vector<string> & options)
393 // every package inherits the global options
394 if (used_packages.find(name) == used_packages.end())
395 used_packages[name] = split_options(h_options);
397 vector<string> & v = used_packages[name];
398 v.insert(v.end(), options.begin(), options.end());
399 if (name == "jurabib") {
400 // Don't output the order argument (see the cite command
401 // handling code in text.cpp).
402 vector<string>::iterator end =
403 remove(options.begin(), options.end(), "natbiborder");
404 end = remove(options.begin(), end, "jurabiborder");
405 options.erase(end, options.end());
412 // Given is a string like "scaled=0.9" or "Scale=0.9", return 0.9 * 100
413 bool scale_as_percentage(string const & scale, string & percentage)
415 string::size_type pos = scale.find('=');
416 if (pos != string::npos) {
417 string value = scale.substr(pos + 1);
418 if (isStrDbl(value)) {
419 percentage = convert<string>(100 * convert<double>(value));
427 string remove_braces(string const & value)
431 if (value[0] == '{' && value[value.length()-1] == '}')
432 return value.substr(1, value.length()-2);
436 } // anonymous namespace
439 Preamble::Preamble() : one_language(true), title_layout_found(false),
440 h_font_cjk_set(false)
444 h_biblio_style = "plain";
445 h_bibtex_command = "default";
446 h_cite_engine = "basic";
447 h_cite_engine_type = "numerical";
449 h_defskip = "medskip";
452 h_fontencoding = "default";
453 h_font_roman = "default";
454 h_font_sans = "default";
455 h_font_typewriter = "default";
456 h_font_math = "auto";
457 h_font_default_family = "default";
458 h_use_non_tex_fonts = "false";
460 h_font_osf = "false";
461 h_font_sf_scale = "100";
462 h_font_tt_scale = "100";
464 h_graphics = "default";
465 h_default_output_format = "default";
466 h_html_be_strict = "false";
467 h_html_css_as_file = "0";
468 h_html_math_output = "0";
470 h_index_command = "default";
471 h_inputencoding = "auto";
472 h_justification = "true";
473 h_language = "english";
474 h_language_package = "none";
476 h_maintain_unincluded_children = "false";
480 h_output_changes = "false";
482 //h_output_sync_macro
483 h_papercolumns = "1";
484 h_paperfontsize = "default";
485 h_paperorientation = "portrait";
486 h_paperpagestyle = "default";
488 h_papersize = "default";
489 h_paragraph_indentation = "default";
490 h_paragraph_separation = "indent";
495 h_pdf_bookmarks = "1";
496 h_pdf_bookmarksnumbered = "0";
497 h_pdf_bookmarksopen = "0";
498 h_pdf_bookmarksopenlevel = "1";
499 h_pdf_breaklinks = "0";
500 h_pdf_pdfborder = "0";
501 h_pdf_colorlinks = "0";
502 h_pdf_backref = "section";
503 h_pdf_pdfusetitle = "1";
505 //h_pdf_quoted_options;
506 h_quotes_language = "english";
509 h_spacing = "single";
510 h_suppress_date = "false";
511 h_textclass = "article";
513 h_tracking_changes = "false";
514 h_use_bibtopic = "false";
515 h_use_indices = "false";
516 h_use_geometry = "false";
517 h_use_default_options = "false";
518 h_use_hyperref = "false";
519 h_use_refstyle = "0";
520 h_use_packages["amsmath"] = "1";
521 h_use_packages["amssymb"] = "0";
522 h_use_packages["esint"] = "1";
523 h_use_packages["mhchem"] = "0";
524 h_use_packages["mathdots"] = "0";
525 h_use_packages["mathtools"] = "0";
526 h_use_packages["stackrel"] = "0";
527 h_use_packages["stmaryrd"] = "0";
528 h_use_packages["undertilde"] = "0";
532 void Preamble::handle_hyperref(vector<string> & options)
534 // FIXME swallow inputencoding changes that might surround the
535 // hyperref setup if it was written by LyX
536 h_use_hyperref = "true";
537 // swallow "unicode=true", since LyX does always write that
538 vector<string>::iterator it =
539 find(options.begin(), options.end(), "unicode=true");
540 if (it != options.end())
542 it = find(options.begin(), options.end(), "pdfusetitle");
543 if (it != options.end()) {
544 h_pdf_pdfusetitle = "1";
547 string bookmarks = process_keyval_opt(options, "bookmarks");
548 if (bookmarks == "true")
549 h_pdf_bookmarks = "1";
550 else if (bookmarks == "false")
551 h_pdf_bookmarks = "0";
552 if (h_pdf_bookmarks == "1") {
553 string bookmarksnumbered =
554 process_keyval_opt(options, "bookmarksnumbered");
555 if (bookmarksnumbered == "true")
556 h_pdf_bookmarksnumbered = "1";
557 else if (bookmarksnumbered == "false")
558 h_pdf_bookmarksnumbered = "0";
559 string bookmarksopen =
560 process_keyval_opt(options, "bookmarksopen");
561 if (bookmarksopen == "true")
562 h_pdf_bookmarksopen = "1";
563 else if (bookmarksopen == "false")
564 h_pdf_bookmarksopen = "0";
565 if (h_pdf_bookmarksopen == "1") {
566 string bookmarksopenlevel =
567 process_keyval_opt(options, "bookmarksopenlevel");
568 if (!bookmarksopenlevel.empty())
569 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
572 string breaklinks = process_keyval_opt(options, "breaklinks");
573 if (breaklinks == "true")
574 h_pdf_breaklinks = "1";
575 else if (breaklinks == "false")
576 h_pdf_breaklinks = "0";
577 string pdfborder = process_keyval_opt(options, "pdfborder");
578 if (pdfborder == "{0 0 0}")
579 h_pdf_pdfborder = "1";
580 else if (pdfborder == "{0 0 1}")
581 h_pdf_pdfborder = "0";
582 string backref = process_keyval_opt(options, "backref");
583 if (!backref.empty())
584 h_pdf_backref = backref;
585 string colorlinks = process_keyval_opt(options, "colorlinks");
586 if (colorlinks == "true")
587 h_pdf_colorlinks = "1";
588 else if (colorlinks == "false")
589 h_pdf_colorlinks = "0";
590 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
591 if (!pdfpagemode.empty())
592 h_pdf_pagemode = pdfpagemode;
593 string pdftitle = process_keyval_opt(options, "pdftitle");
594 if (!pdftitle.empty()) {
595 h_pdf_title = remove_braces(pdftitle);
597 string pdfauthor = process_keyval_opt(options, "pdfauthor");
598 if (!pdfauthor.empty()) {
599 h_pdf_author = remove_braces(pdfauthor);
601 string pdfsubject = process_keyval_opt(options, "pdfsubject");
602 if (!pdfsubject.empty())
603 h_pdf_subject = remove_braces(pdfsubject);
604 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
605 if (!pdfkeywords.empty())
606 h_pdf_keywords = remove_braces(pdfkeywords);
607 if (!options.empty()) {
608 if (!h_pdf_quoted_options.empty())
609 h_pdf_quoted_options += ',';
610 h_pdf_quoted_options += join(options, ",");
616 void Preamble::handle_geometry(vector<string> & options)
618 h_use_geometry = "true";
619 vector<string>::iterator it;
621 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
622 h_paperorientation = "landscape";
626 // keyval version: "paper=letter"
627 string paper = process_keyval_opt(options, "paper");
629 h_papersize = paper + "paper";
630 // alternative version: "letterpaper"
631 handle_opt(options, known_paper_sizes, h_papersize);
632 delete_opt(options, known_paper_sizes);
634 char const * const * margin = known_paper_margins;
635 for (; *margin; ++margin) {
636 string value = process_keyval_opt(options, *margin);
637 if (!value.empty()) {
638 int k = margin - known_paper_margins;
639 string name = known_coded_paper_margins[k];
640 h_margins += '\\' + name + ' ' + value + '\n';
646 void Preamble::handle_package(Parser &p, string const & name,
647 string const & opts, bool in_lyx_preamble)
649 vector<string> options = split_options(opts);
650 add_package(name, options);
651 char const * const * where = 0;
653 if (is_known(name, known_xetex_packages)) {
655 h_use_non_tex_fonts = "true";
656 registerAutomaticallyLoadedPackage("fontspec");
657 if (h_inputencoding == "auto")
658 p.setEncoding("UTF-8");
662 if (is_known(name, known_roman_fonts))
665 if (name == "fourier") {
666 h_font_roman = "utopia";
667 // when font uses real small capitals
668 if (opts == "expert")
672 if (name == "garamondx") {
673 h_font_roman = "garamondx";
678 if (name == "mathdesign") {
679 if (opts.find("charter") != string::npos)
680 h_font_roman = "md-charter";
681 if (opts.find("garamond") != string::npos)
682 h_font_roman = "md-garamond";
683 if (opts.find("utopia") != string::npos)
684 h_font_roman = "md-utopia";
685 if (opts.find("expert") != string::npos) {
691 else if (name == "mathpazo")
692 h_font_roman = "palatino";
694 else if (name == "mathptmx")
695 h_font_roman = "times";
698 if (is_known(name, known_sans_fonts)) {
700 if (options.size() == 1) {
701 if (scale_as_percentage(opts, h_font_sf_scale))
707 if (is_known(name, known_typewriter_fonts)) {
708 // fourier can be set as roman font _only_
709 // fourier as typewriter is handled in handling of \ttdefault
710 if (name != "fourier") {
711 h_font_typewriter = name;
712 if (options.size() == 1) {
713 if (scale_as_percentage(opts, h_font_tt_scale))
719 // font uses old-style figure
723 if (name == "refstyle")
724 h_use_refstyle = "1";
726 // after the detection and handling of special cases, we can remove the
727 // fonts, otherwise they would appear in the preamble, see bug #7856
728 if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
729 || is_known(name, known_typewriter_fonts))
732 else if (name == "amsmath" || name == "amssymb" ||
733 name == "esint" || name == "mhchem" || name == "mathdots" ||
734 name == "mathtools" || name == "stackrel" ||
735 name == "stmaryrd" || name == "undertilde")
736 h_use_packages[name] = "2";
738 else if (name == "babel") {
739 h_language_package = "default";
740 // One might think we would have to do nothing if babel is loaded
741 // without any options to prevent pollution of the preamble with this
742 // babel call in every roundtrip.
743 // But the user could have defined babel-specific things afterwards. So
744 // we need to keep it in the preamble to prevent cases like bug #7861.
746 // check if more than one option was used - used later for inputenc
747 if (options.begin() != options.end() - 1)
748 one_language = false;
749 // babel takes the last language of the option of its \usepackage
750 // call as document language. If there is no such language option, the
751 // last language in the documentclass options is used.
752 handle_opt(options, known_languages, h_language);
753 // translate the babel name to a LyX name
754 h_language = babel2lyx(h_language);
755 if (h_language == "japanese") {
756 // For Japanese, the encoding isn't indicated in the source
757 // file, and there's really not much we can do. We could
758 // 1) offer a list of possible encodings to choose from, or
759 // 2) determine the encoding of the file by inspecting it.
760 // For the time being, we leave the encoding alone so that
761 // we don't get iconv errors when making a wrong guess, and
762 // we will output a note at the top of the document
763 // explaining what to do.
764 Encoding const * const enc = encodings.fromIconvName(
765 p.getEncoding(), Encoding::japanese, false);
767 h_inputencoding = enc->name();
768 is_nonCJKJapanese = true;
769 // in this case babel can be removed from the preamble
770 registerAutomaticallyLoadedPackage("babel");
772 // If babel is called with options, LyX puts them by default into the
773 // document class options. This works for most languages, except
774 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
775 // perhaps in future others.
776 // Therefore keep the babel call as it is as the user might have
778 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
780 delete_opt(options, known_languages);
783 h_preamble << "\\usepackage{babel}\n";
786 else if (name == "polyglossia") {
787 h_language_package = "default";
788 h_default_output_format = "pdf4";
789 h_use_non_tex_fonts = "true";
791 registerAutomaticallyLoadedPackage("xunicode");
792 if (h_inputencoding == "auto")
793 p.setEncoding("UTF-8");
796 else if (name == "CJK") {
797 // set the encoding to "auto" because it might be set to "default" by the babel handling
798 // and this would not be correct for CJK
799 if (h_inputencoding == "default")
800 h_inputencoding = "auto";
801 registerAutomaticallyLoadedPackage("CJK");
804 else if (name == "CJKutf8") {
805 h_inputencoding = "UTF8";
806 p.setEncoding("UTF-8");
807 registerAutomaticallyLoadedPackage("CJKutf8");
810 else if (name == "fontenc") {
811 h_fontencoding = getStringFromVector(options, ",");
812 /* We could do the following for better round trip support,
813 * but this makes the document less portable, so I skip it:
814 if (h_fontencoding == lyxrc.fontenc)
815 h_fontencoding = "global";
820 else if (name == "inputenc" || name == "luainputenc") {
821 // h_inputencoding is only set when there is not more than one
822 // inputenc option because otherwise h_inputencoding must be
823 // set to "auto" (the default encoding of the document language)
824 // Therefore check for the "," character.
825 // It is also only set when there is not more than one babel
828 if (opts.find(",") == string::npos && one_language == true) {
829 h_inputencoding = opts;
830 // FIXME: if this line is used, tex2lyx swallows the next character
831 // in the file behind "{inputenc}"
832 //p.setEncoding(opts);
834 h_preamble << "\\usepackage[" << opts << "}{" << name << "}\n";
835 // FIXME: enabling this introduces bug #8525
836 //p.setEncoding(options.back(), Encoding::inputenc);
840 h_preamble << "\\usepackage{" << name << "}\n";
843 else if (name == "srcltx") {
846 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
849 h_output_sync_macro = "\\usepackage{srcltx}";
852 else if (is_known(name, known_old_language_packages)) {
853 // known language packages from the times before babel
854 // if they are found and not also babel, they will be used as
855 // custom language package
856 h_language_package = "\\usepackage{" + name + "}";
859 else if (name == "prettyref")
860 ; // ignore this FIXME: Use the package separator mechanism instead
862 else if (name == "lyxskak") {
863 // ignore this and its options
864 const char * const o[] = {"ps", "mover", 0};
865 delete_opt(options, o);
868 else if (is_known(name, known_lyx_packages) && options.empty()) {
869 if (name == "splitidx")
870 h_use_indices = "true";
871 if (!in_lyx_preamble) {
872 h_preamble << package_beg_sep << name
873 << package_mid_sep << "\\usepackage{"
875 if (p.next_token().cat() == catNewline ||
876 (p.next_token().cat() == catSpace &&
877 p.next_next_token().cat() == catNewline))
879 h_preamble << package_end_sep;
883 else if (name == "geometry")
884 handle_geometry(options);
886 else if (name == "subfig")
887 ; // ignore this FIXME: Use the package separator mechanism instead
889 else if ((where = is_known(name, known_languages)))
890 h_language = known_coded_languages[where - known_languages];
892 else if (name == "natbib") {
893 h_biblio_style = "plainnat";
894 h_cite_engine = "natbib";
895 h_cite_engine_type = "authoryear";
896 vector<string>::iterator it =
897 find(options.begin(), options.end(), "authoryear");
898 if (it != options.end())
901 it = find(options.begin(), options.end(), "numbers");
902 if (it != options.end()) {
903 h_cite_engine_type = "numerical";
909 else if (name == "jurabib") {
910 h_biblio_style = "jurabib";
911 h_cite_engine = "jurabib";
912 h_cite_engine_type = "authoryear";
915 else if (name == "hyperref")
916 handle_hyperref(options);
918 else if (!in_lyx_preamble) {
920 h_preamble << "\\usepackage{" << name << '}';
922 h_preamble << "\\usepackage[" << opts << "]{"
926 if (p.next_token().cat() == catNewline ||
927 (p.next_token().cat() == catSpace &&
928 p.next_next_token().cat() == catNewline))
932 // We need to do something with the options...
933 if (!options.empty())
934 cerr << "Ignoring options '" << join(options, ",")
935 << "' of package " << name << '.' << endl;
937 // remove the whitespace
942 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
945 Token t = p.get_token();
946 if (t.cat() == catEscape &&
947 is_known(t.cs(), known_if_commands))
948 handle_if(p, in_lyx_preamble);
950 if (!in_lyx_preamble)
951 h_preamble << t.asInput();
952 if (t.cat() == catEscape && t.cs() == "fi")
959 bool Preamble::writeLyXHeader(ostream & os, bool subdoc)
961 // set the quote language
962 // LyX only knows the following quotes languages:
963 // english, swedish, german, polish, french and danish
964 // (quotes for "japanese" and "chinese-traditional" are missing because
965 // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
966 // conversion list taken from
967 // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
968 // (quotes for kazakh and interlingua are unknown)
970 if (is_known(h_language, known_danish_quotes_languages))
971 h_quotes_language = "danish";
973 else if (is_known(h_language, known_french_quotes_languages))
974 h_quotes_language = "french";
976 else if (is_known(h_language, known_german_quotes_languages))
977 h_quotes_language = "german";
979 else if (is_known(h_language, known_polish_quotes_languages))
980 h_quotes_language = "polish";
982 else if (is_known(h_language, known_swedish_quotes_languages))
983 h_quotes_language = "swedish";
985 else if (is_known(h_language, known_english_quotes_languages))
986 h_quotes_language = "english";
988 if (contains(h_float_placement, "H"))
989 registerAutomaticallyLoadedPackage("float");
990 if (h_spacing != "single" && h_spacing != "default")
991 registerAutomaticallyLoadedPackage("setspace");
992 if (h_use_packages["amsmath"] == "2") {
993 // amsbsy and amstext are already provided by amsmath
994 registerAutomaticallyLoadedPackage("amsbsy");
995 registerAutomaticallyLoadedPackage("amstext");
998 // output the LyX file settings
999 os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
1000 << "\\lyxformat " << LYX_FORMAT << '\n'
1001 << "\\begin_document\n"
1002 << "\\begin_header\n"
1003 << "\\textclass " << h_textclass << "\n";
1004 string const raw = subdoc ? empty_string() : h_preamble.str();
1006 os << "\\begin_preamble\n";
1007 for (string::size_type i = 0; i < raw.size(); ++i) {
1008 if (raw[i] == package_beg_sep) {
1009 // Here follows some package loading code that
1010 // must be skipped if the package is loaded
1012 string::size_type j = raw.find(package_mid_sep, i);
1013 if (j == string::npos)
1015 string::size_type k = raw.find(package_end_sep, j);
1016 if (k == string::npos)
1018 string const package = raw.substr(i + 1, j - i - 1);
1019 string const replacement = raw.substr(j + 1, k - j - 1);
1020 if (auto_packages.find(package) == auto_packages.end())
1026 os << "\n\\end_preamble\n";
1028 if (!h_options.empty())
1029 os << "\\options " << h_options << "\n";
1030 os << "\\use_default_options " << h_use_default_options << "\n";
1031 if (!used_modules.empty()) {
1032 os << "\\begin_modules\n";
1033 vector<string>::const_iterator const end = used_modules.end();
1034 vector<string>::const_iterator it = used_modules.begin();
1035 for (; it != end; ++it)
1037 os << "\\end_modules\n";
1039 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1040 << "\\language " << h_language << "\n"
1041 << "\\language_package " << h_language_package << "\n"
1042 << "\\inputencoding " << h_inputencoding << "\n"
1043 << "\\fontencoding " << h_fontencoding << "\n"
1044 << "\\font_roman " << h_font_roman << "\n"
1045 << "\\font_sans " << h_font_sans << "\n"
1046 << "\\font_typewriter " << h_font_typewriter << "\n"
1047 << "\\font_math " << h_font_math << "\n"
1048 << "\\font_default_family " << h_font_default_family << "\n"
1049 << "\\use_non_tex_fonts " << h_use_non_tex_fonts << "\n"
1050 << "\\font_sc " << h_font_sc << "\n"
1051 << "\\font_osf " << h_font_osf << "\n"
1052 << "\\font_sf_scale " << h_font_sf_scale << "\n"
1053 << "\\font_tt_scale " << h_font_tt_scale << '\n';
1054 if (!h_font_cjk.empty())
1055 os << "\\font_cjk " << h_font_cjk << '\n';
1056 os << "\\graphics " << h_graphics << '\n'
1057 << "\\default_output_format " << h_default_output_format << "\n"
1058 << "\\output_sync " << h_output_sync << "\n";
1059 if (h_output_sync == "1")
1060 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1061 os << "\\bibtex_command " << h_bibtex_command << "\n"
1062 << "\\index_command " << h_index_command << "\n";
1063 if (!h_float_placement.empty())
1064 os << "\\float_placement " << h_float_placement << "\n";
1065 os << "\\paperfontsize " << h_paperfontsize << "\n"
1066 << "\\spacing " << h_spacing << "\n"
1067 << "\\use_hyperref " << h_use_hyperref << '\n';
1068 if (h_use_hyperref == "true") {
1069 if (!h_pdf_title.empty())
1070 os << "\\pdf_title \"" << h_pdf_title << "\"\n";
1071 if (!h_pdf_author.empty())
1072 os << "\\pdf_author \"" << h_pdf_author << "\"\n";
1073 if (!h_pdf_subject.empty())
1074 os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
1075 if (!h_pdf_keywords.empty())
1076 os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
1077 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1078 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1079 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1080 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1081 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1082 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1083 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1084 "\\pdf_backref " << h_pdf_backref << "\n"
1085 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1086 if (!h_pdf_pagemode.empty())
1087 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1088 if (!h_pdf_quoted_options.empty())
1089 os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
1091 os << "\\papersize " << h_papersize << "\n"
1092 << "\\use_geometry " << h_use_geometry << '\n';
1093 for (map<string, string>::const_iterator it = h_use_packages.begin();
1094 it != h_use_packages.end(); ++it)
1095 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1096 os << "\\cite_engine " << h_cite_engine << '\n'
1097 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1098 << "\\biblio_style " << h_biblio_style << "\n"
1099 << "\\use_bibtopic " << h_use_bibtopic << "\n"
1100 << "\\use_indices " << h_use_indices << "\n"
1101 << "\\paperorientation " << h_paperorientation << '\n'
1102 << "\\suppress_date " << h_suppress_date << '\n'
1103 << "\\justification " << h_justification << '\n'
1104 << "\\use_refstyle " << h_use_refstyle << '\n';
1105 if (!h_fontcolor.empty())
1106 os << "\\fontcolor " << h_fontcolor << '\n';
1107 if (!h_notefontcolor.empty())
1108 os << "\\notefontcolor " << h_notefontcolor << '\n';
1109 if (!h_backgroundcolor.empty())
1110 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1111 if (!h_boxbgcolor.empty())
1112 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1113 os << "\\index " << h_index << '\n'
1114 << "\\shortcut " << h_shortcut << '\n'
1115 << "\\color " << h_color << '\n'
1118 << "\\secnumdepth " << h_secnumdepth << "\n"
1119 << "\\tocdepth " << h_tocdepth << "\n"
1120 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1121 if (h_paragraph_separation == "skip")
1122 os << "\\defskip " << h_defskip << "\n";
1124 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1125 os << "\\quotes_language " << h_quotes_language << "\n"
1126 << "\\papercolumns " << h_papercolumns << "\n"
1127 << "\\papersides " << h_papersides << "\n"
1128 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1129 if (!h_listings_params.empty())
1130 os << "\\listings_params " << h_listings_params << "\n";
1131 os << "\\tracking_changes " << h_tracking_changes << "\n"
1132 << "\\output_changes " << h_output_changes << "\n"
1133 << "\\html_math_output " << h_html_math_output << "\n"
1134 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1135 << "\\html_be_strict " << h_html_be_strict << "\n"
1137 << "\\end_header\n\n"
1138 << "\\begin_body\n";
1143 void Preamble::parse(Parser & p, string const & forceclass,
1144 TeX2LyXDocClass & tc)
1146 // initialize fixed types
1147 special_columns['D'] = 3;
1148 bool is_full_document = false;
1149 bool is_lyx_file = false;
1150 bool in_lyx_preamble = false;
1152 // determine whether this is a full document or a fragment for inclusion
1154 Token const & t = p.get_token();
1156 if (t.cat() == catEscape && t.cs() == "documentclass") {
1157 is_full_document = true;
1163 while (is_full_document && p.good()) {
1164 Token const & t = p.get_token();
1167 cerr << "t: " << t << "\n";
1173 if (!in_lyx_preamble &&
1174 (t.cat() == catLetter ||
1175 t.cat() == catSuper ||
1176 t.cat() == catSub ||
1177 t.cat() == catOther ||
1178 t.cat() == catMath ||
1179 t.cat() == catActive ||
1180 t.cat() == catBegin ||
1181 t.cat() == catEnd ||
1182 t.cat() == catAlign ||
1183 t.cat() == catParameter))
1184 h_preamble << t.cs();
1186 else if (!in_lyx_preamble &&
1187 (t.cat() == catSpace || t.cat() == catNewline))
1188 h_preamble << t.asInput();
1190 else if (t.cat() == catComment) {
1191 static regex const islyxfile("%% LyX .* created this file");
1192 static regex const usercommands("User specified LaTeX commands");
1194 string const comment = t.asInput();
1196 // magically switch encoding default if it looks like XeLaTeX
1197 static string const magicXeLaTeX =
1198 "% This document must be compiled with XeLaTeX ";
1199 if (comment.size() > magicXeLaTeX.size()
1200 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1201 && h_inputencoding == "auto") {
1202 cerr << "XeLaTeX comment found, switching to UTF8\n";
1203 h_inputencoding = "utf8";
1206 if (regex_search(comment, sub, islyxfile)) {
1208 in_lyx_preamble = true;
1209 } else if (is_lyx_file
1210 && regex_search(comment, sub, usercommands))
1211 in_lyx_preamble = false;
1212 else if (!in_lyx_preamble)
1213 h_preamble << t.asInput();
1216 else if (t.cs() == "pagestyle")
1217 h_paperpagestyle = p.verbatim_item();
1219 else if (t.cs() == "setdefaultlanguage") {
1221 // We don't yet care about non-language variant options
1222 // because LyX doesn't support this yet, see bug #8214
1224 string langopts = p.getOpt();
1225 // check if the option contains a variant, if yes, extract it
1226 string::size_type pos_var = langopts.find("variant");
1227 string::size_type i = langopts.find(',', pos_var);
1228 string::size_type k = langopts.find('=', pos_var);
1229 if (pos_var != string::npos){
1231 if (i == string::npos)
1232 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1234 variant = langopts.substr(k + 1, i - k - 1);
1235 h_language = variant;
1239 h_language = p.verbatim_item();
1240 //finally translate the poyglossia name to a LyX name
1241 h_language = polyglossia2lyx(h_language);
1244 else if (t.cs() == "setotherlanguage") {
1245 // We don't yet care about the option because LyX doesn't
1246 // support this yet, see bug #8214
1247 p.hasOpt() ? p.getOpt() : string();
1251 else if (t.cs() == "setmainfont") {
1252 // we don't care about the option
1253 p.hasOpt() ? p.getOpt() : string();
1254 h_font_roman = p.getArg('{', '}');
1257 else if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1258 // LyX currently only supports the scale option
1261 string fontopts = p.getArg('[', ']');
1262 // check if the option contains a scaling, if yes, extract it
1263 string::size_type pos = fontopts.find("Scale");
1264 if (pos != string::npos) {
1265 string::size_type i = fontopts.find(',', pos);
1266 if (i == string::npos)
1267 scale_as_percentage(fontopts.substr(pos + 1), scale);
1269 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1272 if (t.cs() == "setsansfont") {
1274 h_font_sf_scale = scale;
1275 h_font_sans = p.getArg('{', '}');
1278 h_font_tt_scale = scale;
1279 h_font_typewriter = p.getArg('{', '}');
1283 else if (t.cs() == "date") {
1284 string argument = p.getArg('{', '}');
1285 if (argument.empty())
1286 h_suppress_date = "true";
1288 h_preamble << t.asInput() << '{' << argument << '}';
1291 else if (t.cs() == "color") {
1292 string const space =
1293 (p.hasOpt() ? p.getOpt() : string());
1294 string argument = p.getArg('{', '}');
1295 // check the case that a standard color is used
1296 if (space.empty() && is_known(argument, known_basic_colors)) {
1297 h_fontcolor = rgbcolor2code(argument);
1298 preamble.registerAutomaticallyLoadedPackage("color");
1299 } else if (space.empty() && argument == "document_fontcolor")
1300 preamble.registerAutomaticallyLoadedPackage("color");
1301 // check the case that LyX's document_fontcolor is defined
1302 // but not used for \color
1304 h_preamble << t.asInput();
1306 h_preamble << space;
1307 h_preamble << '{' << argument << '}';
1308 // the color might already be set because \definecolor
1309 // is parsed before this
1314 else if (t.cs() == "pagecolor") {
1315 string argument = p.getArg('{', '}');
1316 // check the case that a standard color is used
1317 if (is_known(argument, known_basic_colors)) {
1318 h_backgroundcolor = rgbcolor2code(argument);
1319 } else if (argument == "page_backgroundcolor")
1320 preamble.registerAutomaticallyLoadedPackage("color");
1321 // check the case that LyX's page_backgroundcolor is defined
1322 // but not used for \pagecolor
1324 h_preamble << t.asInput() << '{' << argument << '}';
1325 // the color might already be set because \definecolor
1326 // is parsed before this
1327 h_backgroundcolor = "";
1331 else if (t.cs() == "makeatletter") {
1332 // LyX takes care of this
1333 p.setCatcode('@', catLetter);
1336 else if (t.cs() == "makeatother") {
1337 // LyX takes care of this
1338 p.setCatcode('@', catOther);
1341 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1342 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1343 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1344 || t.cs() == "DeclareRobustCommand"
1345 || t.cs() == "DeclareRobustCommandx"
1346 || t.cs() == "ProvideTextCommandDefault"
1347 || t.cs() == "DeclareMathAccent") {
1349 if (p.next_token().character() == '*') {
1353 string const name = p.verbatim_item();
1354 string const opt1 = p.getFullOpt();
1355 string const opt2 = p.getFullOpt();
1356 string const body = p.verbatim_item();
1357 // store the in_lyx_preamble setting
1358 bool const was_in_lyx_preamble = in_lyx_preamble;
1360 if (name == "\\rmdefault")
1361 if (is_known(body, known_roman_fonts)) {
1362 h_font_roman = body;
1364 in_lyx_preamble = true;
1366 if (name == "\\sfdefault")
1367 if (is_known(body, known_sans_fonts)) {
1370 in_lyx_preamble = true;
1372 if (name == "\\ttdefault")
1373 if (is_known(body, known_typewriter_fonts)) {
1374 h_font_typewriter = body;
1376 in_lyx_preamble = true;
1378 if (name == "\\familydefault") {
1379 string family = body;
1380 // remove leading "\"
1381 h_font_default_family = family.erase(0,1);
1383 in_lyx_preamble = true;
1386 if (name == "\\bfdefault")
1387 // LyX re-adds this if a kurier font is used
1388 if (is_known(h_font_sans, known_kurier_fonts) && body == "b") {
1390 in_lyx_preamble = true;
1393 // remove the lyxdot definition that is re-added by LyX
1395 if (name == "\\lyxdot") {
1397 in_lyx_preamble = true;
1400 // Add the command to the known commands
1401 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1403 // only non-lyxspecific stuff
1404 if (!in_lyx_preamble) {
1406 ss << '\\' << t.cs();
1409 ss << '{' << name << '}' << opt1 << opt2
1410 << '{' << body << "}";
1411 h_preamble << ss.str();
1413 ostream & out = in_preamble ? h_preamble : os;
1414 out << "\\" << t.cs() << "{" << name << "}"
1415 << opts << "{" << body << "}";
1418 // restore the in_lyx_preamble setting
1419 in_lyx_preamble = was_in_lyx_preamble;
1422 else if (t.cs() == "edef"){
1423 // we only support this for kurier fonts
1424 string const command = p.next_token().asInput();
1426 if (command == "\\sfdefault") {
1428 if (h_font_sans == "kurier")
1429 h_font_sans = "kurier-condensed";
1430 if (h_font_sans == "kurierl")
1431 h_font_sans = "kurier-light-condensed";
1435 h_preamble << "\\edef" << command << "{" << p.getArg('{', '}') << "}\n";
1438 else if (t.cs() == "documentclass") {
1439 vector<string>::iterator it;
1440 vector<string> opts = split_options(p.getArg('[', ']'));
1441 handle_opt(opts, known_fontsizes, h_paperfontsize);
1442 delete_opt(opts, known_fontsizes);
1443 // delete "pt" at the end
1444 string::size_type i = h_paperfontsize.find("pt");
1445 if (i != string::npos)
1446 h_paperfontsize.erase(i);
1447 // The documentclass options are always parsed before the options
1448 // of the babel call so that a language cannot overwrite the babel
1450 handle_opt(opts, known_languages, h_language);
1451 delete_opt(opts, known_languages);
1453 // paper orientation
1454 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1455 h_paperorientation = "landscape";
1459 if ((it = find(opts.begin(), opts.end(), "oneside"))
1464 if ((it = find(opts.begin(), opts.end(), "twoside"))
1470 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1472 h_papercolumns = "1";
1475 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1477 h_papercolumns = "2";
1481 // some size options are known to any document classes, other sizes
1482 // are handled by the \geometry command of the geometry package
1483 handle_opt(opts, known_class_paper_sizes, h_papersize);
1484 delete_opt(opts, known_class_paper_sizes);
1485 // the remaining options
1486 h_options = join(opts, ",");
1487 // FIXME This does not work for classes that have a
1488 // different name in LyX than in LaTeX
1489 h_textclass = p.getArg('{', '}');
1493 else if (t.cs() == "usepackage") {
1494 string const options = p.getArg('[', ']');
1495 string const name = p.getArg('{', '}');
1496 vector<string> vecnames;
1497 split(name, vecnames, ',');
1498 vector<string>::const_iterator it = vecnames.begin();
1499 vector<string>::const_iterator end = vecnames.end();
1500 for (; it != end; ++it)
1501 handle_package(p, trimSpaceAndEol(*it), options,
1505 else if (t.cs() == "inputencoding") {
1506 string const encoding = p.getArg('{','}');
1507 h_inputencoding = encoding;
1508 p.setEncoding(encoding, Encoding::inputenc);
1511 else if (t.cs() == "newenvironment") {
1512 string const name = p.getArg('{', '}');
1513 string const opt1 = p.getFullOpt();
1514 string const opt2 = p.getFullOpt();
1515 string const beg = p.verbatim_item();
1516 string const end = p.verbatim_item();
1517 if (!in_lyx_preamble) {
1518 h_preamble << "\\newenvironment{" << name
1519 << '}' << opt1 << opt2 << '{'
1520 << beg << "}{" << end << '}';
1522 add_known_environment(name, opt1, !opt2.empty(),
1523 from_utf8(beg), from_utf8(end));
1527 else if (t.cs() == "newtheorem") {
1528 string const name = p.getArg('{', '}');
1529 string const opt1 = p.getFullOpt();
1530 string const opt2 = p.getFullOpt();
1531 string const body = p.verbatim_item();
1532 string const opt3 = p.getFullOpt();
1534 add_known_theorem(name, opt1, !opt2.empty(),
1535 from_utf8("\\newtheorem{" + name + '}' +
1536 opt1 + opt2 + '{' + body + '}' + opt3));
1538 if (!in_lyx_preamble)
1539 h_preamble << "\\newtheorem{" << name << '}'
1540 << opt1 << opt2 << '{' << '}' << opt3;
1543 else if (t.cs() == "def") {
1544 string name = p.get_token().cs();
1545 // In fact, name may be more than the name:
1546 // In the test case of bug 8116
1547 // name == "csname SF@gobble@opt \endcsname".
1548 // Therefore, we need to use asInput() instead of cs().
1549 while (p.next_token().cat() != catBegin)
1550 name += p.get_token().asInput();
1551 if (!in_lyx_preamble)
1552 h_preamble << "\\def\\" << name << '{'
1553 << p.verbatim_item() << "}";
1556 else if (t.cs() == "newcolumntype") {
1557 string const name = p.getArg('{', '}');
1558 trimSpaceAndEol(name);
1560 string opts = p.getOpt();
1561 if (!opts.empty()) {
1562 istringstream is(string(opts, 1));
1565 special_columns[name[0]] = nargs;
1566 h_preamble << "\\newcolumntype{" << name << "}";
1568 h_preamble << "[" << nargs << "]";
1569 h_preamble << "{" << p.verbatim_item() << "}";
1572 else if (t.cs() == "setcounter") {
1573 string const name = p.getArg('{', '}');
1574 string const content = p.getArg('{', '}');
1575 if (name == "secnumdepth")
1576 h_secnumdepth = content;
1577 else if (name == "tocdepth")
1578 h_tocdepth = content;
1580 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1583 else if (t.cs() == "setlength") {
1584 string const name = p.verbatim_item();
1585 string const content = p.verbatim_item();
1586 // the paragraphs are only not indented when \parindent is set to zero
1587 if (name == "\\parindent" && content != "") {
1588 if (content[0] == '0')
1589 h_paragraph_separation = "skip";
1591 h_paragraph_indentation = translate_len(content);
1592 } else if (name == "\\parskip") {
1593 if (content == "\\smallskipamount")
1594 h_defskip = "smallskip";
1595 else if (content == "\\medskipamount")
1596 h_defskip = "medskip";
1597 else if (content == "\\bigskipamount")
1598 h_defskip = "bigskip";
1600 h_defskip = content;
1602 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1605 else if (t.cs() == "onehalfspacing")
1606 h_spacing = "onehalf";
1608 else if (t.cs() == "doublespacing")
1609 h_spacing = "double";
1611 else if (t.cs() == "setstretch")
1612 h_spacing = "other " + p.verbatim_item();
1614 else if (t.cs() == "synctex") {
1615 // the scheme is \synctex=value
1616 // where value can only be "1" or "-1"
1617 h_output_sync = "1";
1618 // there can be any character behind the value (e.g. a linebreak or a '\'
1619 // therefore we extract it char by char
1621 string value = p.get_token().asInput();
1623 value += p.get_token().asInput();
1624 h_output_sync_macro = "\\synctex=" + value;
1627 else if (t.cs() == "begin") {
1628 string const name = p.getArg('{', '}');
1629 if (name == "document")
1631 h_preamble << "\\begin{" << name << "}";
1634 else if (t.cs() == "geometry") {
1635 vector<string> opts = split_options(p.getArg('{', '}'));
1636 handle_geometry(opts);
1639 else if (t.cs() == "definecolor") {
1640 string const color = p.getArg('{', '}');
1641 string const space = p.getArg('{', '}');
1642 string const value = p.getArg('{', '}');
1643 if (color == "document_fontcolor" && space == "rgb") {
1644 RGBColor c(RGBColorFromLaTeX(value));
1645 h_fontcolor = X11hexname(c);
1646 } else if (color == "note_fontcolor" && space == "rgb") {
1647 RGBColor c(RGBColorFromLaTeX(value));
1648 h_notefontcolor = X11hexname(c);
1649 } else if (color == "page_backgroundcolor" && space == "rgb") {
1650 RGBColor c(RGBColorFromLaTeX(value));
1651 h_backgroundcolor = X11hexname(c);
1652 } else if (color == "shadecolor" && space == "rgb") {
1653 RGBColor c(RGBColorFromLaTeX(value));
1654 h_boxbgcolor = X11hexname(c);
1656 h_preamble << "\\definecolor{" << color
1657 << "}{" << space << "}{" << value
1662 else if (t.cs() == "bibliographystyle")
1663 h_biblio_style = p.verbatim_item();
1665 else if (t.cs() == "jurabibsetup") {
1666 // FIXME p.getArg('{', '}') is most probably wrong (it
1667 // does not handle nested braces).
1668 // Use p.verbatim_item() instead.
1669 vector<string> jurabibsetup =
1670 split_options(p.getArg('{', '}'));
1671 // add jurabibsetup to the jurabib package options
1672 add_package("jurabib", jurabibsetup);
1673 if (!jurabibsetup.empty()) {
1674 h_preamble << "\\jurabibsetup{"
1675 << join(jurabibsetup, ",") << '}';
1679 else if (t.cs() == "hypersetup") {
1680 vector<string> hypersetup =
1681 split_options(p.verbatim_item());
1682 // add hypersetup to the hyperref package options
1683 handle_hyperref(hypersetup);
1684 if (!hypersetup.empty()) {
1685 h_preamble << "\\hypersetup{"
1686 << join(hypersetup, ",") << '}';
1690 else if (is_known(t.cs(), known_if_3arg_commands)) {
1691 // prevent misparsing of \usepackage if it is used
1692 // as an argument (see e.g. our own output of
1693 // \@ifundefined above)
1694 string const arg1 = p.verbatim_item();
1695 string const arg2 = p.verbatim_item();
1696 string const arg3 = p.verbatim_item();
1697 // test case \@ifundefined{date}{}{\date{}}
1698 if (t.cs() == "@ifundefined" && arg1 == "date" &&
1699 arg2.empty() && arg3 == "\\date{}") {
1700 h_suppress_date = "true";
1701 // older tex2lyx versions did output
1702 // \@ifundefined{definecolor}{\usepackage{color}}{}
1703 } else if (t.cs() == "@ifundefined" &&
1704 arg1 == "definecolor" &&
1705 arg2 == "\\usepackage{color}" &&
1707 if (!in_lyx_preamble)
1708 h_preamble << package_beg_sep
1711 << "\\@ifundefined{definecolor}{color}{}"
1714 //\@ifundefined{showcaptionsetup}{}{%
1715 // \PassOptionsToPackage{caption=false}{subfig}}
1716 // that LyX uses for subfloats
1717 } else if (t.cs() == "@ifundefined" &&
1718 arg1 == "showcaptionsetup" && arg2.empty()
1719 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1721 } else if (!in_lyx_preamble) {
1722 h_preamble << t.asInput()
1723 << '{' << arg1 << '}'
1724 << '{' << arg2 << '}'
1725 << '{' << arg3 << '}';
1729 else if (is_known(t.cs(), known_if_commands)) {
1730 // must not parse anything in conditional code, since
1731 // LyX would output the parsed contents unconditionally
1732 if (!in_lyx_preamble)
1733 h_preamble << t.asInput();
1734 handle_if(p, in_lyx_preamble);
1737 else if (!t.cs().empty() && !in_lyx_preamble)
1738 h_preamble << '\\' << t.cs();
1741 // remove the whitespace
1744 // Force textclass if the user wanted it
1745 if (!forceclass.empty())
1746 h_textclass = forceclass;
1747 tc.setName(h_textclass);
1749 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1752 if (h_papersides.empty()) {
1755 h_papersides = ss.str();
1758 // If the CJK package is used we cannot set the document language from
1759 // the babel options. Instead, we guess which language is used most
1760 // and set this one.
1761 default_language = h_language;
1762 if (is_full_document &&
1763 (auto_packages.find("CJK") != auto_packages.end() ||
1764 auto_packages.find("CJKutf8") != auto_packages.end())) {
1766 h_language = guessLanguage(p, default_language);
1772 string babel2lyx(string const & language)
1774 char const * const * where = is_known(language, known_languages);
1776 return known_coded_languages[where - known_languages];
1781 string Preamble::polyglossia2lyx(string const & language)
1783 char const * const * where = is_known(language, polyglossia_languages);
1785 return coded_polyglossia_languages[where - polyglossia_languages];
1790 string rgbcolor2code(string const & name)
1792 char const * const * where = is_known(name, known_basic_colors);
1794 // "red", "green" etc
1795 return known_basic_color_codes[where - known_basic_colors];
1797 // "255,0,0", "0,255,0" etc
1798 RGBColor c(RGBColorFromLaTeX(name));
1799 return X11hexname(c);