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", "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 == "mathdesign") {
673 // when font uses real small capitals
674 if (opts.find("charter") != string::npos)
675 h_font_roman = "md-charter";
676 if (opts.find("garamond") != string::npos)
677 h_font_roman = "md-garamond";
678 if (opts.find("utopia") != string::npos)
679 h_font_roman = "md-utopia";
680 if (opts.find("expert") != string::npos) {
686 else if (name == "mathpazo")
687 h_font_roman = "palatino";
689 else if (name == "mathptmx")
690 h_font_roman = "times";
693 if (is_known(name, known_sans_fonts)) {
695 if (options.size() == 1) {
696 if (scale_as_percentage(opts, h_font_sf_scale))
702 if (is_known(name, known_typewriter_fonts)) {
703 // fourier can be set as roman font _only_
704 // fourier as typewriter is handled in handling of \ttdefault
705 if (name != "fourier") {
706 h_font_typewriter = name;
707 if (options.size() == 1) {
708 if (scale_as_percentage(opts, h_font_tt_scale))
714 // font uses old-style figure
718 if (name == "refstyle")
719 h_use_refstyle = "1";
721 // after the detection and handling of special cases, we can remove the
722 // fonts, otherwise they would appear in the preamble, see bug #7856
723 if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
724 || is_known(name, known_typewriter_fonts))
727 else if (name == "amsmath" || name == "amssymb" ||
728 name == "esint" || name == "mhchem" || name == "mathdots" ||
729 name == "mathtools" || name == "stackrel" ||
730 name == "stmaryrd" || name == "undertilde")
731 h_use_packages[name] = "2";
733 else if (name == "babel") {
734 h_language_package = "default";
735 // One might think we would have to do nothing if babel is loaded
736 // without any options to prevent pollution of the preamble with this
737 // babel call in every roundtrip.
738 // But the user could have defined babel-specific things afterwards. So
739 // we need to keep it in the preamble to prevent cases like bug #7861.
741 // check if more than one option was used - used later for inputenc
742 if (options.begin() != options.end() - 1)
743 one_language = false;
744 // babel takes the last language of the option of its \usepackage
745 // call as document language. If there is no such language option, the
746 // last language in the documentclass options is used.
747 handle_opt(options, known_languages, h_language);
748 // translate the babel name to a LyX name
749 h_language = babel2lyx(h_language);
750 if (h_language == "japanese") {
751 // For Japanese, the encoding isn't indicated in the source
752 // file, and there's really not much we can do. We could
753 // 1) offer a list of possible encodings to choose from, or
754 // 2) determine the encoding of the file by inspecting it.
755 // For the time being, we leave the encoding alone so that
756 // we don't get iconv errors when making a wrong guess, and
757 // we will output a note at the top of the document
758 // explaining what to do.
759 Encoding const * const enc = encodings.fromIconvName(
760 p.getEncoding(), Encoding::japanese, false);
762 h_inputencoding = enc->name();
763 is_nonCJKJapanese = true;
764 // in this case babel can be removed from the preamble
765 registerAutomaticallyLoadedPackage("babel");
767 // If babel is called with options, LyX puts them by default into the
768 // document class options. This works for most languages, except
769 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
770 // perhaps in future others.
771 // Therefore keep the babel call as it is as the user might have
773 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
775 delete_opt(options, known_languages);
778 h_preamble << "\\usepackage{babel}\n";
781 else if (name == "polyglossia") {
782 h_language_package = "default";
783 h_default_output_format = "pdf4";
784 h_use_non_tex_fonts = "true";
786 registerAutomaticallyLoadedPackage("xunicode");
787 if (h_inputencoding == "auto")
788 p.setEncoding("UTF-8");
791 else if (name == "CJK") {
792 // set the encoding to "auto" because it might be set to "default" by the babel handling
793 // and this would not be correct for CJK
794 if (h_inputencoding == "default")
795 h_inputencoding = "auto";
796 registerAutomaticallyLoadedPackage("CJK");
799 else if (name == "CJKutf8") {
800 h_inputencoding = "UTF8";
801 p.setEncoding("UTF-8");
802 registerAutomaticallyLoadedPackage("CJKutf8");
805 else if (name == "fontenc") {
806 h_fontencoding = getStringFromVector(options, ",");
807 /* We could do the following for better round trip support,
808 * but this makes the document less portable, so I skip it:
809 if (h_fontencoding == lyxrc.fontenc)
810 h_fontencoding = "global";
815 else if (name == "inputenc" || name == "luainputenc") {
816 // h_inputencoding is only set when there is not more than one
817 // inputenc option because otherwise h_inputencoding must be
818 // set to "auto" (the default encoding of the document language)
819 // Therefore check for the "," character.
820 // It is also only set when there is not more than one babel
823 if (opts.find(",") == string::npos && one_language == true) {
824 h_inputencoding = opts;
825 // FIXME: if this line is used, tex2lyx swallows the next character
826 // in the file behind "{inputenc}"
827 //p.setEncoding(opts);
829 h_preamble << "\\usepackage[" << opts << "}{" << name << "}\n";
830 // FIXME: enabling this introduces bug #8525
831 //p.setEncoding(options.back(), Encoding::inputenc);
835 h_preamble << "\\usepackage{" << name << "}\n";
838 else if (name == "srcltx") {
841 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
844 h_output_sync_macro = "\\usepackage{srcltx}";
847 else if (is_known(name, known_old_language_packages)) {
848 // known language packages from the times before babel
849 // if they are found and not also babel, they will be used as
850 // custom language package
851 h_language_package = "\\usepackage{" + name + "}";
854 else if (name == "prettyref")
855 ; // ignore this FIXME: Use the package separator mechanism instead
857 else if (name == "lyxskak") {
858 // ignore this and its options
859 const char * const o[] = {"ps", "mover", 0};
860 delete_opt(options, o);
863 else if (is_known(name, known_lyx_packages) && options.empty()) {
864 if (name == "splitidx")
865 h_use_indices = "true";
866 if (!in_lyx_preamble) {
867 h_preamble << package_beg_sep << name
868 << package_mid_sep << "\\usepackage{"
870 if (p.next_token().cat() == catNewline ||
871 (p.next_token().cat() == catSpace &&
872 p.next_next_token().cat() == catNewline))
874 h_preamble << package_end_sep;
878 else if (name == "geometry")
879 handle_geometry(options);
881 else if (name == "subfig")
882 ; // ignore this FIXME: Use the package separator mechanism instead
884 else if ((where = is_known(name, known_languages)))
885 h_language = known_coded_languages[where - known_languages];
887 else if (name == "natbib") {
888 h_biblio_style = "plainnat";
889 h_cite_engine = "natbib";
890 h_cite_engine_type = "authoryear";
891 vector<string>::iterator it =
892 find(options.begin(), options.end(), "authoryear");
893 if (it != options.end())
896 it = find(options.begin(), options.end(), "numbers");
897 if (it != options.end()) {
898 h_cite_engine_type = "numerical";
904 else if (name == "jurabib") {
905 h_biblio_style = "jurabib";
906 h_cite_engine = "jurabib";
907 h_cite_engine_type = "authoryear";
910 else if (name == "hyperref")
911 handle_hyperref(options);
913 else if (!in_lyx_preamble) {
915 h_preamble << "\\usepackage{" << name << '}';
917 h_preamble << "\\usepackage[" << opts << "]{"
921 if (p.next_token().cat() == catNewline ||
922 (p.next_token().cat() == catSpace &&
923 p.next_next_token().cat() == catNewline))
927 // We need to do something with the options...
928 if (!options.empty())
929 cerr << "Ignoring options '" << join(options, ",")
930 << "' of package " << name << '.' << endl;
932 // remove the whitespace
937 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
940 Token t = p.get_token();
941 if (t.cat() == catEscape &&
942 is_known(t.cs(), known_if_commands))
943 handle_if(p, in_lyx_preamble);
945 if (!in_lyx_preamble)
946 h_preamble << t.asInput();
947 if (t.cat() == catEscape && t.cs() == "fi")
954 bool Preamble::writeLyXHeader(ostream & os, bool subdoc)
956 // set the quote language
957 // LyX only knows the following quotes languages:
958 // english, swedish, german, polish, french and danish
959 // (quotes for "japanese" and "chinese-traditional" are missing because
960 // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
961 // conversion list taken from
962 // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
963 // (quotes for kazakh and interlingua are unknown)
965 if (is_known(h_language, known_danish_quotes_languages))
966 h_quotes_language = "danish";
968 else if (is_known(h_language, known_french_quotes_languages))
969 h_quotes_language = "french";
971 else if (is_known(h_language, known_german_quotes_languages))
972 h_quotes_language = "german";
974 else if (is_known(h_language, known_polish_quotes_languages))
975 h_quotes_language = "polish";
977 else if (is_known(h_language, known_swedish_quotes_languages))
978 h_quotes_language = "swedish";
980 else if (is_known(h_language, known_english_quotes_languages))
981 h_quotes_language = "english";
983 if (contains(h_float_placement, "H"))
984 registerAutomaticallyLoadedPackage("float");
985 if (h_spacing != "single" && h_spacing != "default")
986 registerAutomaticallyLoadedPackage("setspace");
987 if (h_use_packages["amsmath"] == "2") {
988 // amsbsy and amstext are already provided by amsmath
989 registerAutomaticallyLoadedPackage("amsbsy");
990 registerAutomaticallyLoadedPackage("amstext");
993 // output the LyX file settings
994 os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
995 << "\\lyxformat " << LYX_FORMAT << '\n'
996 << "\\begin_document\n"
997 << "\\begin_header\n"
998 << "\\textclass " << h_textclass << "\n";
999 string const raw = subdoc ? empty_string() : h_preamble.str();
1001 os << "\\begin_preamble\n";
1002 for (string::size_type i = 0; i < raw.size(); ++i) {
1003 if (raw[i] == package_beg_sep) {
1004 // Here follows some package loading code that
1005 // must be skipped if the package is loaded
1007 string::size_type j = raw.find(package_mid_sep, i);
1008 if (j == string::npos)
1010 string::size_type k = raw.find(package_end_sep, j);
1011 if (k == string::npos)
1013 string const package = raw.substr(i + 1, j - i - 1);
1014 string const replacement = raw.substr(j + 1, k - j - 1);
1015 if (auto_packages.find(package) == auto_packages.end())
1021 os << "\n\\end_preamble\n";
1023 if (!h_options.empty())
1024 os << "\\options " << h_options << "\n";
1025 os << "\\use_default_options " << h_use_default_options << "\n";
1026 if (!used_modules.empty()) {
1027 os << "\\begin_modules\n";
1028 vector<string>::const_iterator const end = used_modules.end();
1029 vector<string>::const_iterator it = used_modules.begin();
1030 for (; it != end; ++it)
1032 os << "\\end_modules\n";
1034 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1035 << "\\language " << h_language << "\n"
1036 << "\\language_package " << h_language_package << "\n"
1037 << "\\inputencoding " << h_inputencoding << "\n"
1038 << "\\fontencoding " << h_fontencoding << "\n"
1039 << "\\font_roman " << h_font_roman << "\n"
1040 << "\\font_sans " << h_font_sans << "\n"
1041 << "\\font_typewriter " << h_font_typewriter << "\n"
1042 << "\\font_math " << h_font_math << "\n"
1043 << "\\font_default_family " << h_font_default_family << "\n"
1044 << "\\use_non_tex_fonts " << h_use_non_tex_fonts << "\n"
1045 << "\\font_sc " << h_font_sc << "\n"
1046 << "\\font_osf " << h_font_osf << "\n"
1047 << "\\font_sf_scale " << h_font_sf_scale << "\n"
1048 << "\\font_tt_scale " << h_font_tt_scale << '\n';
1049 if (!h_font_cjk.empty())
1050 os << "\\font_cjk " << h_font_cjk << '\n';
1051 os << "\\graphics " << h_graphics << '\n'
1052 << "\\default_output_format " << h_default_output_format << "\n"
1053 << "\\output_sync " << h_output_sync << "\n";
1054 if (h_output_sync == "1")
1055 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1056 os << "\\bibtex_command " << h_bibtex_command << "\n"
1057 << "\\index_command " << h_index_command << "\n";
1058 if (!h_float_placement.empty())
1059 os << "\\float_placement " << h_float_placement << "\n";
1060 os << "\\paperfontsize " << h_paperfontsize << "\n"
1061 << "\\spacing " << h_spacing << "\n"
1062 << "\\use_hyperref " << h_use_hyperref << '\n';
1063 if (h_use_hyperref == "true") {
1064 if (!h_pdf_title.empty())
1065 os << "\\pdf_title \"" << h_pdf_title << "\"\n";
1066 if (!h_pdf_author.empty())
1067 os << "\\pdf_author \"" << h_pdf_author << "\"\n";
1068 if (!h_pdf_subject.empty())
1069 os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
1070 if (!h_pdf_keywords.empty())
1071 os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
1072 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1073 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1074 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1075 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1076 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1077 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1078 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1079 "\\pdf_backref " << h_pdf_backref << "\n"
1080 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1081 if (!h_pdf_pagemode.empty())
1082 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1083 if (!h_pdf_quoted_options.empty())
1084 os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
1086 os << "\\papersize " << h_papersize << "\n"
1087 << "\\use_geometry " << h_use_geometry << '\n';
1088 for (map<string, string>::const_iterator it = h_use_packages.begin();
1089 it != h_use_packages.end(); ++it)
1090 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1091 os << "\\cite_engine " << h_cite_engine << '\n'
1092 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1093 << "\\biblio_style " << h_biblio_style << "\n"
1094 << "\\use_bibtopic " << h_use_bibtopic << "\n"
1095 << "\\use_indices " << h_use_indices << "\n"
1096 << "\\paperorientation " << h_paperorientation << '\n'
1097 << "\\suppress_date " << h_suppress_date << '\n'
1098 << "\\justification " << h_justification << '\n'
1099 << "\\use_refstyle " << h_use_refstyle << '\n';
1100 if (!h_fontcolor.empty())
1101 os << "\\fontcolor " << h_fontcolor << '\n';
1102 if (!h_notefontcolor.empty())
1103 os << "\\notefontcolor " << h_notefontcolor << '\n';
1104 if (!h_backgroundcolor.empty())
1105 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1106 if (!h_boxbgcolor.empty())
1107 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1108 os << "\\index " << h_index << '\n'
1109 << "\\shortcut " << h_shortcut << '\n'
1110 << "\\color " << h_color << '\n'
1113 << "\\secnumdepth " << h_secnumdepth << "\n"
1114 << "\\tocdepth " << h_tocdepth << "\n"
1115 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1116 if (h_paragraph_separation == "skip")
1117 os << "\\defskip " << h_defskip << "\n";
1119 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1120 os << "\\quotes_language " << h_quotes_language << "\n"
1121 << "\\papercolumns " << h_papercolumns << "\n"
1122 << "\\papersides " << h_papersides << "\n"
1123 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1124 if (!h_listings_params.empty())
1125 os << "\\listings_params " << h_listings_params << "\n";
1126 os << "\\tracking_changes " << h_tracking_changes << "\n"
1127 << "\\output_changes " << h_output_changes << "\n"
1128 << "\\html_math_output " << h_html_math_output << "\n"
1129 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1130 << "\\html_be_strict " << h_html_be_strict << "\n"
1132 << "\\end_header\n\n"
1133 << "\\begin_body\n";
1138 void Preamble::parse(Parser & p, string const & forceclass,
1139 TeX2LyXDocClass & tc)
1141 // initialize fixed types
1142 special_columns['D'] = 3;
1143 bool is_full_document = false;
1144 bool is_lyx_file = false;
1145 bool in_lyx_preamble = false;
1147 // determine whether this is a full document or a fragment for inclusion
1149 Token const & t = p.get_token();
1151 if (t.cat() == catEscape && t.cs() == "documentclass") {
1152 is_full_document = true;
1158 while (is_full_document && p.good()) {
1159 Token const & t = p.get_token();
1162 cerr << "t: " << t << "\n";
1168 if (!in_lyx_preamble &&
1169 (t.cat() == catLetter ||
1170 t.cat() == catSuper ||
1171 t.cat() == catSub ||
1172 t.cat() == catOther ||
1173 t.cat() == catMath ||
1174 t.cat() == catActive ||
1175 t.cat() == catBegin ||
1176 t.cat() == catEnd ||
1177 t.cat() == catAlign ||
1178 t.cat() == catParameter))
1179 h_preamble << t.cs();
1181 else if (!in_lyx_preamble &&
1182 (t.cat() == catSpace || t.cat() == catNewline))
1183 h_preamble << t.asInput();
1185 else if (t.cat() == catComment) {
1186 static regex const islyxfile("%% LyX .* created this file");
1187 static regex const usercommands("User specified LaTeX commands");
1189 string const comment = t.asInput();
1191 // magically switch encoding default if it looks like XeLaTeX
1192 static string const magicXeLaTeX =
1193 "% This document must be compiled with XeLaTeX ";
1194 if (comment.size() > magicXeLaTeX.size()
1195 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1196 && h_inputencoding == "auto") {
1197 cerr << "XeLaTeX comment found, switching to UTF8\n";
1198 h_inputencoding = "utf8";
1201 if (regex_search(comment, sub, islyxfile)) {
1203 in_lyx_preamble = true;
1204 } else if (is_lyx_file
1205 && regex_search(comment, sub, usercommands))
1206 in_lyx_preamble = false;
1207 else if (!in_lyx_preamble)
1208 h_preamble << t.asInput();
1211 else if (t.cs() == "pagestyle")
1212 h_paperpagestyle = p.verbatim_item();
1214 else if (t.cs() == "setdefaultlanguage") {
1216 // We don't yet care about non-language variant options
1217 // because LyX doesn't support this yet, see bug #8214
1219 string langopts = p.getOpt();
1220 // check if the option contains a variant, if yes, extract it
1221 string::size_type pos_var = langopts.find("variant");
1222 string::size_type i = langopts.find(',', pos_var);
1223 string::size_type k = langopts.find('=', pos_var);
1224 if (pos_var != string::npos){
1226 if (i == string::npos)
1227 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1229 variant = langopts.substr(k + 1, i - k - 1);
1230 h_language = variant;
1234 h_language = p.verbatim_item();
1235 //finally translate the poyglossia name to a LyX name
1236 h_language = polyglossia2lyx(h_language);
1239 else if (t.cs() == "setotherlanguage") {
1240 // We don't yet care about the option because LyX doesn't
1241 // support this yet, see bug #8214
1242 p.hasOpt() ? p.getOpt() : string();
1246 else if (t.cs() == "setmainfont") {
1247 // we don't care about the option
1248 p.hasOpt() ? p.getOpt() : string();
1249 h_font_roman = p.getArg('{', '}');
1252 else if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1253 // LyX currently only supports the scale option
1256 string fontopts = p.getArg('[', ']');
1257 // check if the option contains a scaling, if yes, extract it
1258 string::size_type pos = fontopts.find("Scale");
1259 if (pos != string::npos) {
1260 string::size_type i = fontopts.find(',', pos);
1261 if (i == string::npos)
1262 scale_as_percentage(fontopts.substr(pos + 1), scale);
1264 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1267 if (t.cs() == "setsansfont") {
1269 h_font_sf_scale = scale;
1270 h_font_sans = p.getArg('{', '}');
1273 h_font_tt_scale = scale;
1274 h_font_typewriter = p.getArg('{', '}');
1278 else if (t.cs() == "date") {
1279 string argument = p.getArg('{', '}');
1280 if (argument.empty())
1281 h_suppress_date = "true";
1283 h_preamble << t.asInput() << '{' << argument << '}';
1286 else if (t.cs() == "color") {
1287 string const space =
1288 (p.hasOpt() ? p.getOpt() : string());
1289 string argument = p.getArg('{', '}');
1290 // check the case that a standard color is used
1291 if (space.empty() && is_known(argument, known_basic_colors)) {
1292 h_fontcolor = rgbcolor2code(argument);
1293 preamble.registerAutomaticallyLoadedPackage("color");
1294 } else if (space.empty() && argument == "document_fontcolor")
1295 preamble.registerAutomaticallyLoadedPackage("color");
1296 // check the case that LyX's document_fontcolor is defined
1297 // but not used for \color
1299 h_preamble << t.asInput();
1301 h_preamble << space;
1302 h_preamble << '{' << argument << '}';
1303 // the color might already be set because \definecolor
1304 // is parsed before this
1309 else if (t.cs() == "pagecolor") {
1310 string argument = p.getArg('{', '}');
1311 // check the case that a standard color is used
1312 if (is_known(argument, known_basic_colors)) {
1313 h_backgroundcolor = rgbcolor2code(argument);
1314 } else if (argument == "page_backgroundcolor")
1315 preamble.registerAutomaticallyLoadedPackage("color");
1316 // check the case that LyX's page_backgroundcolor is defined
1317 // but not used for \pagecolor
1319 h_preamble << t.asInput() << '{' << argument << '}';
1320 // the color might already be set because \definecolor
1321 // is parsed before this
1322 h_backgroundcolor = "";
1326 else if (t.cs() == "makeatletter") {
1327 // LyX takes care of this
1328 p.setCatcode('@', catLetter);
1331 else if (t.cs() == "makeatother") {
1332 // LyX takes care of this
1333 p.setCatcode('@', catOther);
1336 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1337 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1338 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1339 || t.cs() == "DeclareRobustCommand"
1340 || t.cs() == "DeclareRobustCommandx"
1341 || t.cs() == "ProvideTextCommandDefault"
1342 || t.cs() == "DeclareMathAccent") {
1344 if (p.next_token().character() == '*') {
1348 string const name = p.verbatim_item();
1349 string const opt1 = p.getFullOpt();
1350 string const opt2 = p.getFullOpt();
1351 string const body = p.verbatim_item();
1352 // store the in_lyx_preamble setting
1353 bool const was_in_lyx_preamble = in_lyx_preamble;
1355 if (name == "\\rmdefault")
1356 if (is_known(body, known_roman_fonts)) {
1357 h_font_roman = body;
1359 in_lyx_preamble = true;
1361 if (name == "\\sfdefault")
1362 if (is_known(body, known_sans_fonts)) {
1365 in_lyx_preamble = true;
1367 if (name == "\\ttdefault")
1368 if (is_known(body, known_typewriter_fonts)) {
1369 h_font_typewriter = body;
1371 in_lyx_preamble = true;
1373 if (name == "\\familydefault") {
1374 string family = body;
1375 // remove leading "\"
1376 h_font_default_family = family.erase(0,1);
1378 in_lyx_preamble = true;
1381 if (name == "\\bfdefault")
1382 // LyX re-adds this if a kurier font is used
1383 if (is_known(h_font_sans, known_kurier_fonts) && body == "b") {
1385 in_lyx_preamble = true;
1388 // remove the lyxdot definition that is re-added by LyX
1390 if (name == "\\lyxdot") {
1392 in_lyx_preamble = true;
1395 // Add the command to the known commands
1396 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1398 // only non-lyxspecific stuff
1399 if (!in_lyx_preamble) {
1401 ss << '\\' << t.cs();
1404 ss << '{' << name << '}' << opt1 << opt2
1405 << '{' << body << "}";
1406 h_preamble << ss.str();
1408 ostream & out = in_preamble ? h_preamble : os;
1409 out << "\\" << t.cs() << "{" << name << "}"
1410 << opts << "{" << body << "}";
1413 // restore the in_lyx_preamble setting
1414 in_lyx_preamble = was_in_lyx_preamble;
1417 else if (t.cs() == "edef"){
1418 // we only support this for kurier fonts
1419 string const command = p.next_token().asInput();
1421 if (command == "\\sfdefault") {
1423 if (h_font_sans == "kurier")
1424 h_font_sans = "kurier-condensed";
1425 if (h_font_sans == "kurierl")
1426 h_font_sans = "kurier-light-condensed";
1430 h_preamble << "\\edef" << command << "{" << p.getArg('{', '}') << "}\n";
1433 else if (t.cs() == "documentclass") {
1434 vector<string>::iterator it;
1435 vector<string> opts = split_options(p.getArg('[', ']'));
1436 handle_opt(opts, known_fontsizes, h_paperfontsize);
1437 delete_opt(opts, known_fontsizes);
1438 // delete "pt" at the end
1439 string::size_type i = h_paperfontsize.find("pt");
1440 if (i != string::npos)
1441 h_paperfontsize.erase(i);
1442 // The documentclass options are always parsed before the options
1443 // of the babel call so that a language cannot overwrite the babel
1445 handle_opt(opts, known_languages, h_language);
1446 delete_opt(opts, known_languages);
1448 // paper orientation
1449 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1450 h_paperorientation = "landscape";
1454 if ((it = find(opts.begin(), opts.end(), "oneside"))
1459 if ((it = find(opts.begin(), opts.end(), "twoside"))
1465 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1467 h_papercolumns = "1";
1470 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1472 h_papercolumns = "2";
1476 // some size options are known to any document classes, other sizes
1477 // are handled by the \geometry command of the geometry package
1478 handle_opt(opts, known_class_paper_sizes, h_papersize);
1479 delete_opt(opts, known_class_paper_sizes);
1480 // the remaining options
1481 h_options = join(opts, ",");
1482 // FIXME This does not work for classes that have a
1483 // different name in LyX than in LaTeX
1484 h_textclass = p.getArg('{', '}');
1488 else if (t.cs() == "usepackage") {
1489 string const options = p.getArg('[', ']');
1490 string const name = p.getArg('{', '}');
1491 vector<string> vecnames;
1492 split(name, vecnames, ',');
1493 vector<string>::const_iterator it = vecnames.begin();
1494 vector<string>::const_iterator end = vecnames.end();
1495 for (; it != end; ++it)
1496 handle_package(p, trimSpaceAndEol(*it), options,
1500 else if (t.cs() == "inputencoding") {
1501 string const encoding = p.getArg('{','}');
1502 h_inputencoding = encoding;
1503 p.setEncoding(encoding, Encoding::inputenc);
1506 else if (t.cs() == "newenvironment") {
1507 string const name = p.getArg('{', '}');
1508 string const opt1 = p.getFullOpt();
1509 string const opt2 = p.getFullOpt();
1510 string const beg = p.verbatim_item();
1511 string const end = p.verbatim_item();
1512 if (!in_lyx_preamble) {
1513 h_preamble << "\\newenvironment{" << name
1514 << '}' << opt1 << opt2 << '{'
1515 << beg << "}{" << end << '}';
1517 add_known_environment(name, opt1, !opt2.empty(),
1518 from_utf8(beg), from_utf8(end));
1522 else if (t.cs() == "newtheorem") {
1523 string const name = p.getArg('{', '}');
1524 string const opt1 = p.getFullOpt();
1525 string const opt2 = p.getFullOpt();
1526 string const body = p.verbatim_item();
1527 string const opt3 = p.getFullOpt();
1529 add_known_theorem(name, opt1, !opt2.empty(),
1530 from_utf8("\\newtheorem{" + name + '}' +
1531 opt1 + opt2 + '{' + body + '}' + opt3));
1533 if (!in_lyx_preamble)
1534 h_preamble << "\\newtheorem{" << name << '}'
1535 << opt1 << opt2 << '{' << '}' << opt3;
1538 else if (t.cs() == "def") {
1539 string name = p.get_token().cs();
1540 // In fact, name may be more than the name:
1541 // In the test case of bug 8116
1542 // name == "csname SF@gobble@opt \endcsname".
1543 // Therefore, we need to use asInput() instead of cs().
1544 while (p.next_token().cat() != catBegin)
1545 name += p.get_token().asInput();
1546 if (!in_lyx_preamble)
1547 h_preamble << "\\def\\" << name << '{'
1548 << p.verbatim_item() << "}";
1551 else if (t.cs() == "newcolumntype") {
1552 string const name = p.getArg('{', '}');
1553 trimSpaceAndEol(name);
1555 string opts = p.getOpt();
1556 if (!opts.empty()) {
1557 istringstream is(string(opts, 1));
1560 special_columns[name[0]] = nargs;
1561 h_preamble << "\\newcolumntype{" << name << "}";
1563 h_preamble << "[" << nargs << "]";
1564 h_preamble << "{" << p.verbatim_item() << "}";
1567 else if (t.cs() == "setcounter") {
1568 string const name = p.getArg('{', '}');
1569 string const content = p.getArg('{', '}');
1570 if (name == "secnumdepth")
1571 h_secnumdepth = content;
1572 else if (name == "tocdepth")
1573 h_tocdepth = content;
1575 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1578 else if (t.cs() == "setlength") {
1579 string const name = p.verbatim_item();
1580 string const content = p.verbatim_item();
1581 // the paragraphs are only not indented when \parindent is set to zero
1582 if (name == "\\parindent" && content != "") {
1583 if (content[0] == '0')
1584 h_paragraph_separation = "skip";
1586 h_paragraph_indentation = translate_len(content);
1587 } else if (name == "\\parskip") {
1588 if (content == "\\smallskipamount")
1589 h_defskip = "smallskip";
1590 else if (content == "\\medskipamount")
1591 h_defskip = "medskip";
1592 else if (content == "\\bigskipamount")
1593 h_defskip = "bigskip";
1595 h_defskip = content;
1597 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1600 else if (t.cs() == "onehalfspacing")
1601 h_spacing = "onehalf";
1603 else if (t.cs() == "doublespacing")
1604 h_spacing = "double";
1606 else if (t.cs() == "setstretch")
1607 h_spacing = "other " + p.verbatim_item();
1609 else if (t.cs() == "synctex") {
1610 // the scheme is \synctex=value
1611 // where value can only be "1" or "-1"
1612 h_output_sync = "1";
1613 // there can be any character behind the value (e.g. a linebreak or a '\'
1614 // therefore we extract it char by char
1616 string value = p.get_token().asInput();
1618 value += p.get_token().asInput();
1619 h_output_sync_macro = "\\synctex=" + value;
1622 else if (t.cs() == "begin") {
1623 string const name = p.getArg('{', '}');
1624 if (name == "document")
1626 h_preamble << "\\begin{" << name << "}";
1629 else if (t.cs() == "geometry") {
1630 vector<string> opts = split_options(p.getArg('{', '}'));
1631 handle_geometry(opts);
1634 else if (t.cs() == "definecolor") {
1635 string const color = p.getArg('{', '}');
1636 string const space = p.getArg('{', '}');
1637 string const value = p.getArg('{', '}');
1638 if (color == "document_fontcolor" && space == "rgb") {
1639 RGBColor c(RGBColorFromLaTeX(value));
1640 h_fontcolor = X11hexname(c);
1641 } else if (color == "note_fontcolor" && space == "rgb") {
1642 RGBColor c(RGBColorFromLaTeX(value));
1643 h_notefontcolor = X11hexname(c);
1644 } else if (color == "page_backgroundcolor" && space == "rgb") {
1645 RGBColor c(RGBColorFromLaTeX(value));
1646 h_backgroundcolor = X11hexname(c);
1647 } else if (color == "shadecolor" && space == "rgb") {
1648 RGBColor c(RGBColorFromLaTeX(value));
1649 h_boxbgcolor = X11hexname(c);
1651 h_preamble << "\\definecolor{" << color
1652 << "}{" << space << "}{" << value
1657 else if (t.cs() == "bibliographystyle")
1658 h_biblio_style = p.verbatim_item();
1660 else if (t.cs() == "jurabibsetup") {
1661 // FIXME p.getArg('{', '}') is most probably wrong (it
1662 // does not handle nested braces).
1663 // Use p.verbatim_item() instead.
1664 vector<string> jurabibsetup =
1665 split_options(p.getArg('{', '}'));
1666 // add jurabibsetup to the jurabib package options
1667 add_package("jurabib", jurabibsetup);
1668 if (!jurabibsetup.empty()) {
1669 h_preamble << "\\jurabibsetup{"
1670 << join(jurabibsetup, ",") << '}';
1674 else if (t.cs() == "hypersetup") {
1675 vector<string> hypersetup =
1676 split_options(p.verbatim_item());
1677 // add hypersetup to the hyperref package options
1678 handle_hyperref(hypersetup);
1679 if (!hypersetup.empty()) {
1680 h_preamble << "\\hypersetup{"
1681 << join(hypersetup, ",") << '}';
1685 else if (is_known(t.cs(), known_if_3arg_commands)) {
1686 // prevent misparsing of \usepackage if it is used
1687 // as an argument (see e.g. our own output of
1688 // \@ifundefined above)
1689 string const arg1 = p.verbatim_item();
1690 string const arg2 = p.verbatim_item();
1691 string const arg3 = p.verbatim_item();
1692 // test case \@ifundefined{date}{}{\date{}}
1693 if (t.cs() == "@ifundefined" && arg1 == "date" &&
1694 arg2.empty() && arg3 == "\\date{}") {
1695 h_suppress_date = "true";
1696 // older tex2lyx versions did output
1697 // \@ifundefined{definecolor}{\usepackage{color}}{}
1698 } else if (t.cs() == "@ifundefined" &&
1699 arg1 == "definecolor" &&
1700 arg2 == "\\usepackage{color}" &&
1702 if (!in_lyx_preamble)
1703 h_preamble << package_beg_sep
1706 << "\\@ifundefined{definecolor}{color}{}"
1709 //\@ifundefined{showcaptionsetup}{}{%
1710 // \PassOptionsToPackage{caption=false}{subfig}}
1711 // that LyX uses for subfloats
1712 } else if (t.cs() == "@ifundefined" &&
1713 arg1 == "showcaptionsetup" && arg2.empty()
1714 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1716 } else if (!in_lyx_preamble) {
1717 h_preamble << t.asInput()
1718 << '{' << arg1 << '}'
1719 << '{' << arg2 << '}'
1720 << '{' << arg3 << '}';
1724 else if (is_known(t.cs(), known_if_commands)) {
1725 // must not parse anything in conditional code, since
1726 // LyX would output the parsed contents unconditionally
1727 if (!in_lyx_preamble)
1728 h_preamble << t.asInput();
1729 handle_if(p, in_lyx_preamble);
1732 else if (!t.cs().empty() && !in_lyx_preamble)
1733 h_preamble << '\\' << t.cs();
1736 // remove the whitespace
1739 // Force textclass if the user wanted it
1740 if (!forceclass.empty())
1741 h_textclass = forceclass;
1742 tc.setName(h_textclass);
1744 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1747 if (h_papersides.empty()) {
1750 h_papersides = ss.str();
1753 // If the CJK package is used we cannot set the document language from
1754 // the babel options. Instead, we guess which language is used most
1755 // and set this one.
1756 default_language = h_language;
1757 if (is_full_document &&
1758 (auto_packages.find("CJK") != auto_packages.end() ||
1759 auto_packages.find("CJKutf8") != auto_packages.end())) {
1761 h_language = guessLanguage(p, default_language);
1767 string babel2lyx(string const & language)
1769 char const * const * where = is_known(language, known_languages);
1771 return known_coded_languages[where - known_languages];
1776 string Preamble::polyglossia2lyx(string const & language)
1778 char const * const * where = is_known(language, polyglossia_languages);
1780 return coded_polyglossia_languages[where - polyglossia_languages];
1785 string rgbcolor2code(string const & name)
1787 char const * const * where = is_known(name, known_basic_colors);
1789 // "red", "green" etc
1790 return known_basic_color_codes[where - known_basic_colors];
1792 // "255,0,0", "0,255,0" etc
1793 RGBColor c(RGBColorFromLaTeX(name));
1794 return X11hexname(c);