3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
9 * Full author contact details are available in file CREDITS.
19 #include "LayoutFile.h"
22 #include "TextClass.h"
24 #include "support/convert.h"
25 #include "support/FileName.h"
26 #include "support/filetools.h"
27 #include "support/lstrings.h"
29 #include "support/regex.h"
35 using namespace lyx::support;
40 // special columntypes
41 extern map<char, int> special_columns;
47 // CJK languages are handled in text.cpp, polyglossia languages are listed
50 * known babel language names (including synonyms)
51 * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
52 * please keep this in sync with known_coded_languages line by line!
54 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
55 "american", "arabic", "arabtex", "australian", "austrian", "bahasa", "bahasai",
56 "bahasam", "basque", "belarusian", "brazil", "brazilian", "breton", "british",
57 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
58 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "francais",
59 "french", "frenchb", "frenchle", "frenchpro", "galician", "german", "germanb",
60 "greek", "hebrew", "hungarian", "icelandic", "indon", "indonesian", "interlingua",
61 "irish", "italian", "japanese", "kazakh", "kurmanji", "latin", "latvian", "lithuanian",
62 "lowersorbian", "lsorbian", "magyar", "malay", "meyalu", "mongolian", "naustrian",
63 "newzealand", "ngerman", "ngermanb", "norsk", "nynorsk", "polutonikogreek", "polish",
64 "portuges", "portuguese", "romanian", "russian", "russianb", "samin",
65 "scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish",
66 "swedish", "thai", "turkish", "turkmen", "ukraineb", "ukrainian",
67 "uppersorbian", "UKenglish", "USenglish", "usorbian", "vietnam", "welsh",
71 * the same as known_languages with .lyx names
72 * please keep this in sync with known_languages line by line!
74 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
75 "american", "arabic_arabi", "arabic_arabtex", "australian", "austrian", "bahasa", "bahasa",
76 "bahasam", "basque", "belarusian", "brazilian", "brazilian", "breton", "british",
77 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
78 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "french",
79 "french", "french", "french", "french", "galician", "german", "german",
80 "greek", "hebrew", "magyar", "icelandic", "bahasa", "bahasa", "interlingua",
81 "irish", "italian", "japanese", "kazakh", "kurmanji", "latin", "latvian", "lithuanian",
82 "lowersorbian", "lowersorbian", "magyar", "bahasam", "bahasam", "mongolian", "naustrian",
83 "newzealand", "ngerman", "ngerman", "norsk", "nynorsk", "polutonikogreek", "polish",
84 "portuguese", "portuguese", "romanian", "russian", "russian", "samin",
85 "scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish",
86 "swedish", "thai", "turkish", "turkmen", "ukrainian", "ukrainian",
87 "uppersorbian", "uppersorbian", "english", "english", "vietnamese", "welsh",
90 /// languages with danish quotes (.lyx names)
91 const char * const known_danish_quotes_languages[] = {"danish", 0};
93 /// languages with english quotes (.lyx names)
94 const char * const known_english_quotes_languages[] = {"american", "australian",
95 "bahasa", "bahasam", "brazilian", "canadian", "chinese-simplified", "english",
96 "esperanto", "hebrew", "irish", "korean", "newzealand", "portuguese", "scottish",
99 /// languages with french quotes (.lyx names)
100 const char * const known_french_quotes_languages[] = {"albanian",
101 "arabic_arabi", "arabic_arabtex", "basque", "canadien", "catalan", "french",
102 "galician", "greek", "italian", "norsk", "nynorsk", "polutonikogreek",
103 "russian", "spanish", "spanish-mexico", "turkish", "turkmen", "ukrainian",
106 /// languages with german quotes (.lyx names)
107 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
108 "czech", "german", "icelandic", "lithuanian", "lowersorbian", "naustrian",
109 "ngerman", "serbian", "serbian-latin", "slovak", "slovene", "uppersorbian", 0};
111 /// languages with polish quotes (.lyx names)
112 const char * const known_polish_quotes_languages[] = {"afrikaans", "croatian",
113 "dutch", "estonian", "magyar", "polish", "romanian", 0};
115 /// languages with swedish quotes (.lyx names)
116 const char * const known_swedish_quotes_languages[] = {"finnish",
119 /// known language packages from the times before babel
120 const char * const known_old_language_packages[] = {"french", "frenchle",
121 "frenchpro", "german", "ngerman", "pmfrench", 0};
123 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
125 const char * const known_roman_fonts[] = { "ae", "beraserif", "bookman",
126 "ccfonts", "chancery", "charter", "cmr", "fourier", "lmodern", "mathpazo",
127 "mathptmx", "newcent", "utopia", 0};
129 const char * const known_sans_fonts[] = { "avant", "berasans", "cmbr", "cmss",
130 "helvet", "lmss", 0};
132 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
133 "courier", "lmtt", "luximono", "fourier", "lmodern", "mathpazo", "mathptmx",
136 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
137 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
138 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
139 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
140 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
142 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
143 "executivepaper", "legalpaper", "letterpaper", 0};
145 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
146 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
148 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
149 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
152 /// commands that can start an \if...\else...\endif sequence
153 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
154 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
155 "ifsidecap", "ifupgreek", 0};
157 const char * const known_basic_colors[] = {"blue", "black", "cyan", "green",
158 "magenta", "red", "white", "yellow", 0};
160 const char * const known_basic_color_codes[] = {"#0000ff", "#000000", "#00ffff", "#00ff00",
161 "#ff00ff", "#ff0000", "#ffffff", "#ffff00", 0};
163 /// conditional commands with three arguments like \@ifundefined{}{}{}
164 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
167 /// packages that work only in xetex
168 /// polyglossia is handled separately
169 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
170 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
171 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
173 /// packages that are automatically skipped if loaded by LyX
174 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
175 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color", "float",
176 "fontspec", "graphicx", "hhline", "ifthen", "longtable", "makeidx", "multirow",
177 "nomencl", "pdfpages", "rotating", "rotfloat", "splitidx", "setspace",
178 "subscript", "textcomp", "ulem", "url", "varioref", "verbatim", "wrapfig",
181 // codes used to remove packages that are loaded automatically by LyX.
182 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
183 const char package_beg_sep = '\001';
184 const char package_mid_sep = '\002';
185 const char package_end_sep = '\003';
188 // returns true if at least one of the options in what has been found
189 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
195 // the last language option is the document language (for babel and LyX)
196 // the last size option is the document font size
197 vector<string>::iterator it;
198 vector<string>::iterator position = opts.begin();
199 for (; *what; ++what) {
200 it = find(opts.begin(), opts.end(), *what);
201 if (it != opts.end()) {
202 if (it >= position) {
213 void delete_opt(vector<string> & opts, char const * const * what)
218 // remove found options from the list
219 // do this after handle_opt to avoid potential memory leaks
220 vector<string>::iterator it;
221 for (; *what; ++what) {
222 it = find(opts.begin(), opts.end(), *what);
223 if (it != opts.end())
230 * Split a package options string (keyval format) into a vector.
232 * authorformat=smallcaps,
234 * titleformat=colonsep,
235 * bibformat={tabular,ibidem,numbered}
237 vector<string> split_options(string const & input)
239 vector<string> options;
243 Token const & t = p.get_token();
244 if (t.asInput() == ",") {
245 options.push_back(trimSpaceAndEol(option));
247 } else if (t.asInput() == "=") {
250 if (p.next_token().asInput() == "{")
251 option += '{' + p.getArg('{', '}') + '}';
252 } else if (t.cat() != catSpace)
253 option += t.asInput();
257 options.push_back(trimSpaceAndEol(option));
264 * Retrieve a keyval option "name={value with=sign}" named \p name from
265 * \p options and return the value.
266 * The found option is also removed from \p options.
268 string process_keyval_opt(vector<string> & options, string name)
270 for (size_t i = 0; i < options.size(); ++i) {
271 vector<string> option;
272 split(options[i], option, '=');
273 if (option.size() < 2)
275 if (option[0] == name) {
276 options.erase(options.begin() + i);
277 option.erase(option.begin());
278 return join(option, "=");
284 } // anonymous namespace
288 * known polyglossia language names (including variants)
290 const char * const Preamble::polyglossia_languages[] = {
291 "albanian", "croatian", "hebrew", "norsk", "swedish", "amharic", "czech", "hindi",
292 "nynorsk", "syriac", "arabic", "danish", "icelandic", "occitan", "tamil",
293 "armenian", "divehi", "interlingua", "polish", "telugu", "asturian", "dutch",
294 "irish", "portuges", "thai", "bahasai", "english", "italian", "romanian", "turkish",
295 "bahasam", "esperanto", "lao", "russian", "turkmen", "basque", "estonian", "latin",
296 "samin", "ukrainian", "bengali", "farsi", "latvian", "sanskrit", "urdu", "brazil",
297 "brazilian", "finnish", "lithuanian", "scottish", "usorbian", "breton", "french",
298 "lsorbian", "serbian", "vietnamese", "bulgarian", "galician", "magyar", "slovak",
299 "welsh", "catalan", "german", "malayalam", "slovenian", "coptic", "greek",
300 "marathi", "spanish",
301 "american", "ancient", "australian", "british", "monotonic", "newzealand",
305 * the same as polyglossia_languages with .lyx names
306 * please keep this in sync with polyglossia_languages line by line!
308 const char * const Preamble::coded_polyglossia_languages[] = {
309 "albanian", "croatian", "hebrew", "norsk", "swedish", "amharic", "czech", "hindi",
310 "nynorsk", "syriac", "arabic_arabi", "danish", "icelandic", "occitan", "tamil",
311 "armenian", "divehi", "interlingua", "polish", "telugu", "asturian", "dutch",
312 "irish", "portuges", "thai", "bahasa", "english", "italian", "romanian", "turkish",
313 "bahasam", "esperanto", "lao", "russian", "turkmen", "basque", "estonian", "latin",
314 "samin", "ukrainian", "bengali", "farsi", "latvian", "sanskrit", "urdu", "brazilian",
315 "brazilian", "finnish", "lithuanian", "scottish", "uppersorbian", "breton", "french",
316 "lowersorbian", "serbian", "vietnamese", "bulgarian", "galician", "magyar", "slovak",
317 "welsh", "catalan", "ngerman", "malayalam", "slovene", "coptic", "greek",
318 "marathi", "spanish",
319 "american", "ancientgreek", "australian", "british", "greek", "newzealand",
320 "polutonikogreek", 0};
323 bool Preamble::indentParagraphs() const
325 return h_paragraph_separation == "indent";
329 bool Preamble::isPackageUsed(string const & package) const
331 return used_packages.find(package) != used_packages.end();
335 vector<string> Preamble::getPackageOptions(string const & package) const
337 map<string, vector<string> >::const_iterator it = used_packages.find(package);
338 if (it != used_packages.end())
340 return vector<string>();
344 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
346 auto_packages.insert(package);
350 void Preamble::addModule(string const & module)
352 used_modules.push_back(module);
356 void Preamble::suppressDate(bool suppress)
359 h_suppress_date = "true";
361 h_suppress_date = "false";
365 void Preamble::registerAuthor(std::string const & name)
367 Author author(from_utf8(name), empty_docstring());
368 author.setUsed(true);
369 authors_.record(author);
370 h_tracking_changes = "true";
371 h_output_changes = "true";
375 Author const & Preamble::getAuthor(std::string const & name) const
377 Author author(from_utf8(name), empty_docstring());
378 for (AuthorList::Authors::const_iterator it = authors_.begin();
379 it != authors_.end(); ++it)
382 static Author const dummy;
387 void Preamble::add_package(string const & name, vector<string> & options)
389 // every package inherits the global options
390 if (used_packages.find(name) == used_packages.end())
391 used_packages[name] = split_options(h_options);
393 vector<string> & v = used_packages[name];
394 v.insert(v.end(), options.begin(), options.end());
395 if (name == "jurabib") {
396 // Don't output the order argument (see the cite command
397 // handling code in text.cpp).
398 vector<string>::iterator end =
399 remove(options.begin(), options.end(), "natbiborder");
400 end = remove(options.begin(), end, "jurabiborder");
401 options.erase(end, options.end());
408 // Given is a string like "scaled=0.9" or "Scale=0.9", return 0.9 * 100
409 bool scale_as_percentage(string const & scale, string & percentage)
411 string::size_type pos = scale.find('=');
412 if (pos != string::npos) {
413 string value = scale.substr(pos + 1);
414 if (isStrDbl(value)) {
415 percentage = convert<string>(100 * convert<double>(value));
423 string remove_braces(string const & value)
427 if (value[0] == '{' && value[value.length()-1] == '}')
428 return value.substr(1, value.length()-2);
432 } // anonymous namespace
435 Preamble::Preamble() : one_language(true), title_layout_found(false),
436 h_font_cjk_set(false)
440 h_biblio_style = "plain";
441 h_bibtex_command = "default";
442 h_cite_engine = "basic";
443 h_cite_engine_type = "numerical";
445 h_defskip = "medskip";
448 h_fontencoding = "default";
449 h_font_roman = "default";
450 h_font_sans = "default";
451 h_font_typewriter = "default";
452 h_font_math = "auto";
453 h_font_default_family = "default";
454 h_use_non_tex_fonts = "false";
456 h_font_osf = "false";
457 h_font_sf_scale = "100";
458 h_font_tt_scale = "100";
460 h_graphics = "default";
461 h_default_output_format = "default";
462 h_html_be_strict = "false";
463 h_html_css_as_file = "0";
464 h_html_math_output = "0";
466 h_index_command = "default";
467 h_inputencoding = "auto";
468 h_justification = "true";
469 h_language = "english";
470 h_language_package = "none";
472 h_maintain_unincluded_children = "false";
476 h_output_changes = "false";
478 //h_output_sync_macro
479 h_papercolumns = "1";
480 h_paperfontsize = "default";
481 h_paperorientation = "portrait";
482 h_paperpagestyle = "default";
484 h_papersize = "default";
485 h_paragraph_indentation = "default";
486 h_paragraph_separation = "indent";
491 h_pdf_bookmarks = "1";
492 h_pdf_bookmarksnumbered = "0";
493 h_pdf_bookmarksopen = "0";
494 h_pdf_bookmarksopenlevel = "1";
495 h_pdf_breaklinks = "0";
496 h_pdf_pdfborder = "0";
497 h_pdf_colorlinks = "0";
498 h_pdf_backref = "section";
499 h_pdf_pdfusetitle = "1";
501 //h_pdf_quoted_options;
502 h_quotes_language = "english";
505 h_spacing = "single";
506 h_suppress_date = "false";
507 h_textclass = "article";
509 h_tracking_changes = "false";
510 h_use_bibtopic = "false";
511 h_use_indices = "false";
512 h_use_geometry = "false";
513 h_use_default_options = "false";
514 h_use_hyperref = "false";
515 h_use_refstyle = "0";
516 h_use_packages["amsmath"] = "1";
517 h_use_packages["amssymb"] = "0";
518 h_use_packages["esint"] = "1";
519 h_use_packages["mhchem"] = "0";
520 h_use_packages["mathdots"] = "0";
521 h_use_packages["mathtools"] = "0";
522 h_use_packages["stackrel"] = "0";
523 h_use_packages["stmaryrd"] = "0";
524 h_use_packages["undertilde"] = "0";
528 void Preamble::handle_hyperref(vector<string> & options)
530 // FIXME swallow inputencoding changes that might surround the
531 // hyperref setup if it was written by LyX
532 h_use_hyperref = "true";
533 // swallow "unicode=true", since LyX does always write that
534 vector<string>::iterator it =
535 find(options.begin(), options.end(), "unicode=true");
536 if (it != options.end())
538 it = find(options.begin(), options.end(), "pdfusetitle");
539 if (it != options.end()) {
540 h_pdf_pdfusetitle = "1";
543 string bookmarks = process_keyval_opt(options, "bookmarks");
544 if (bookmarks == "true")
545 h_pdf_bookmarks = "1";
546 else if (bookmarks == "false")
547 h_pdf_bookmarks = "0";
548 if (h_pdf_bookmarks == "1") {
549 string bookmarksnumbered =
550 process_keyval_opt(options, "bookmarksnumbered");
551 if (bookmarksnumbered == "true")
552 h_pdf_bookmarksnumbered = "1";
553 else if (bookmarksnumbered == "false")
554 h_pdf_bookmarksnumbered = "0";
555 string bookmarksopen =
556 process_keyval_opt(options, "bookmarksopen");
557 if (bookmarksopen == "true")
558 h_pdf_bookmarksopen = "1";
559 else if (bookmarksopen == "false")
560 h_pdf_bookmarksopen = "0";
561 if (h_pdf_bookmarksopen == "1") {
562 string bookmarksopenlevel =
563 process_keyval_opt(options, "bookmarksopenlevel");
564 if (!bookmarksopenlevel.empty())
565 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
568 string breaklinks = process_keyval_opt(options, "breaklinks");
569 if (breaklinks == "true")
570 h_pdf_breaklinks = "1";
571 else if (breaklinks == "false")
572 h_pdf_breaklinks = "0";
573 string pdfborder = process_keyval_opt(options, "pdfborder");
574 if (pdfborder == "{0 0 0}")
575 h_pdf_pdfborder = "1";
576 else if (pdfborder == "{0 0 1}")
577 h_pdf_pdfborder = "0";
578 string backref = process_keyval_opt(options, "backref");
579 if (!backref.empty())
580 h_pdf_backref = backref;
581 string colorlinks = process_keyval_opt(options, "colorlinks");
582 if (colorlinks == "true")
583 h_pdf_colorlinks = "1";
584 else if (colorlinks == "false")
585 h_pdf_colorlinks = "0";
586 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
587 if (!pdfpagemode.empty())
588 h_pdf_pagemode = pdfpagemode;
589 string pdftitle = process_keyval_opt(options, "pdftitle");
590 if (!pdftitle.empty()) {
591 h_pdf_title = remove_braces(pdftitle);
593 string pdfauthor = process_keyval_opt(options, "pdfauthor");
594 if (!pdfauthor.empty()) {
595 h_pdf_author = remove_braces(pdfauthor);
597 string pdfsubject = process_keyval_opt(options, "pdfsubject");
598 if (!pdfsubject.empty())
599 h_pdf_subject = remove_braces(pdfsubject);
600 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
601 if (!pdfkeywords.empty())
602 h_pdf_keywords = remove_braces(pdfkeywords);
603 if (!options.empty()) {
604 if (!h_pdf_quoted_options.empty())
605 h_pdf_quoted_options += ',';
606 h_pdf_quoted_options += join(options, ",");
612 void Preamble::handle_geometry(vector<string> & options)
614 h_use_geometry = "true";
615 vector<string>::iterator it;
617 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
618 h_paperorientation = "landscape";
622 // keyval version: "paper=letter"
623 string paper = process_keyval_opt(options, "paper");
625 h_papersize = paper + "paper";
626 // alternative version: "letterpaper"
627 handle_opt(options, known_paper_sizes, h_papersize);
628 delete_opt(options, known_paper_sizes);
630 char const * const * margin = known_paper_margins;
631 for (; *margin; ++margin) {
632 string value = process_keyval_opt(options, *margin);
633 if (!value.empty()) {
634 int k = margin - known_paper_margins;
635 string name = known_coded_paper_margins[k];
636 h_margins += '\\' + name + ' ' + value + '\n';
642 void Preamble::handle_package(Parser &p, string const & name,
643 string const & opts, bool in_lyx_preamble)
645 vector<string> options = split_options(opts);
646 add_package(name, options);
647 char const * const * where = 0;
649 if (is_known(name, known_xetex_packages)) {
651 h_use_non_tex_fonts = "true";
652 registerAutomaticallyLoadedPackage("fontspec");
653 if (h_inputencoding == "auto")
654 p.setEncoding("utf8");
658 if (is_known(name, known_roman_fonts))
661 if (name == "fourier") {
662 h_font_roman = "utopia";
663 // when font uses real small capitals
664 if (opts == "expert")
668 else if (name == "mathpazo")
669 h_font_roman = "palatino";
671 else if (name == "mathptmx")
672 h_font_roman = "times";
675 if (is_known(name, known_sans_fonts)) {
677 if (options.size() == 1) {
678 if (scale_as_percentage(opts, h_font_sf_scale))
684 if (is_known(name, known_typewriter_fonts)) {
685 // fourier can be set as roman font _only_
686 // fourier as typewriter is handled in handling of \ttdefault
687 if (name != "fourier") {
688 h_font_typewriter = name;
689 if (options.size() == 1) {
690 if (scale_as_percentage(opts, h_font_tt_scale))
696 // font uses old-style figure
700 // after the detection and handling of special cases, we can remove the
701 // fonts, otherwise they would appear in the preamble, see bug #7856
702 if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
703 || is_known(name, known_typewriter_fonts))
706 else if (name == "amsmath" || name == "amssymb" ||
707 name == "esint" || name == "mhchem" || name == "mathdots" ||
708 name == "mathtools" || name == "stackrel" ||
709 name == "stmaryrd" || name == "undertilde")
710 h_use_packages[name] = "2";
712 else if (name == "babel") {
713 h_language_package = "default";
714 // One might think we would have to do nothing if babel is loaded
715 // without any options to prevent pollution of the preamble with this
716 // babel call in every roundtrip.
717 // But the user could have defined babel-specific things afterwards. So
718 // we need to keep it in the preamble to prevent cases like bug #7861.
720 // check if more than one option was used - used later for inputenc
721 if (options.begin() != options.end() - 1)
722 one_language = false;
723 // babel takes the last language of the option of its \usepackage
724 // call as document language. If there is no such language option, the
725 // last language in the documentclass options is used.
726 handle_opt(options, known_languages, h_language);
727 // translate the babel name to a LyX name
728 h_language = babel2lyx(h_language);
729 // for Japanese we assume EUC-JP as encoding
730 // but we cannot determine the exact encoding and thus output also a note
731 if (h_language == "japanese") {
732 h_inputencoding = "euc";
733 p.setEncoding("EUC-JP");
734 is_nonCJKJapanese = true;
735 // in this case babel can be removed from the preamble
736 registerAutomaticallyLoadedPackage("babel");
738 // If babel is called with options, LyX puts them by default into the
739 // document class options. This works for most languages, except
740 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
741 // perhaps in future others.
742 // Therefore keep the babel call as it is as the user might have
744 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
746 delete_opt(options, known_languages);
749 h_preamble << "\\usepackage{babel}\n";
752 else if (name == "polyglossia") {
753 h_language_package = "default";
754 h_default_output_format = "pdf4";
755 h_use_non_tex_fonts = "true";
757 registerAutomaticallyLoadedPackage("xunicode");
758 if (h_inputencoding == "auto")
759 p.setEncoding("utf8");
762 else if (name == "CJK") {
763 // set the encoding to "auto" because it might be set to "default" by the babel handling
764 // and this would not be correct for CJK
765 if (h_inputencoding == "default")
766 h_inputencoding = "auto";
767 registerAutomaticallyLoadedPackage("CJK");
770 else if (name == "CJKutf8") {
771 h_inputencoding = "UTF8";
772 p.setEncoding(h_inputencoding);
773 registerAutomaticallyLoadedPackage("CJKutf8");
776 else if (name == "fontenc") {
777 h_fontencoding = getStringFromVector(options, ",");
778 /* We could do the following for better round trip support,
779 * but this makes the document less portable, so I skip it:
780 if (h_fontencoding == lyxrc.fontenc)
781 h_fontencoding = "global";
786 else if (name == "inputenc" || name == "luainputenc") {
787 // h_inputencoding is only set when there is not more than one
788 // inputenc option because otherwise h_inputencoding must be
789 // set to "auto" (the default encoding of the document language)
790 // Therefore check for the "," character.
791 // It is also only set when there is not more than one babel
793 if (opts.find(",") == string::npos && one_language == true)
794 h_inputencoding = opts;
795 if (!options.empty())
796 p.setEncoding(options.back());
800 else if (name == "srcltx") {
803 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
806 h_output_sync_macro = "\\usepackage{srcltx}";
809 else if (is_known(name, known_old_language_packages)) {
810 // known language packages from the times before babel
811 // if they are found and not also babel, they will be used as
812 // custom language package
813 h_language_package = "\\usepackage{" + name + "}";
816 else if (name == "prettyref")
817 ; // ignore this FIXME: Use the package separator mechanism instead
819 else if (name == "lyxskak") {
820 // ignore this and its options
821 const char * const o[] = {"ps", "mover", 0};
822 delete_opt(options, o);
825 else if (is_known(name, known_lyx_packages) && options.empty()) {
826 if (name == "splitidx")
827 h_use_indices = "true";
828 if (!in_lyx_preamble) {
829 h_preamble << package_beg_sep << name
830 << package_mid_sep << "\\usepackage{"
832 if (p.next_token().cat() == catNewline ||
833 (p.next_token().cat() == catSpace &&
834 p.next_next_token().cat() == catNewline))
836 h_preamble << package_end_sep;
840 else if (name == "geometry")
841 handle_geometry(options);
843 else if (name == "subfig")
844 ; // ignore this FIXME: Use the package separator mechanism instead
846 else if ((where = is_known(name, known_languages)))
847 h_language = known_coded_languages[where - known_languages];
849 else if (name == "natbib") {
850 h_biblio_style = "plainnat";
851 h_cite_engine = "natbib";
852 h_cite_engine_type = "authoryear";
853 vector<string>::iterator it =
854 find(options.begin(), options.end(), "authoryear");
855 if (it != options.end())
858 it = find(options.begin(), options.end(), "numbers");
859 if (it != options.end()) {
860 h_cite_engine_type = "numerical";
866 else if (name == "jurabib") {
867 h_biblio_style = "jurabib";
868 h_cite_engine = "jurabib";
869 h_cite_engine_type = "authoryear";
872 else if (name == "hyperref")
873 handle_hyperref(options);
875 else if (!in_lyx_preamble) {
877 h_preamble << "\\usepackage{" << name << '}';
879 h_preamble << "\\usepackage[" << opts << "]{"
883 if (p.next_token().cat() == catNewline ||
884 (p.next_token().cat() == catSpace &&
885 p.next_next_token().cat() == catNewline))
889 // We need to do something with the options...
890 if (!options.empty())
891 cerr << "Ignoring options '" << join(options, ",")
892 << "' of package " << name << '.' << endl;
894 // remove the whitespace
899 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
902 Token t = p.get_token();
903 if (t.cat() == catEscape &&
904 is_known(t.cs(), known_if_commands))
905 handle_if(p, in_lyx_preamble);
907 if (!in_lyx_preamble)
908 h_preamble << t.asInput();
909 if (t.cat() == catEscape && t.cs() == "fi")
916 bool Preamble::writeLyXHeader(ostream & os, bool subdoc)
918 // set the quote language
919 // LyX only knows the following quotes languages:
920 // english, swedish, german, polish, french and danish
921 // (quotes for "japanese" and "chinese-traditional" are missing because
922 // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
923 // conversion list taken from
924 // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
925 // (quotes for kazakh and interlingua are unknown)
927 if (is_known(h_language, known_danish_quotes_languages))
928 h_quotes_language = "danish";
930 else if (is_known(h_language, known_french_quotes_languages))
931 h_quotes_language = "french";
933 else if (is_known(h_language, known_german_quotes_languages))
934 h_quotes_language = "german";
936 else if (is_known(h_language, known_polish_quotes_languages))
937 h_quotes_language = "polish";
939 else if (is_known(h_language, known_swedish_quotes_languages))
940 h_quotes_language = "swedish";
942 else if (is_known(h_language, known_english_quotes_languages))
943 h_quotes_language = "english";
945 if (contains(h_float_placement, "H"))
946 registerAutomaticallyLoadedPackage("float");
947 if (h_spacing != "single" && h_spacing != "default")
948 registerAutomaticallyLoadedPackage("setspace");
949 if (h_use_packages["amsmath"] == "2") {
950 // amsbsy and amstext are already provided by amsmath
951 registerAutomaticallyLoadedPackage("amsbsy");
952 registerAutomaticallyLoadedPackage("amstext");
955 // output the LyX file settings
956 os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
957 << "\\lyxformat " << LYX_FORMAT << '\n'
958 << "\\begin_document\n"
959 << "\\begin_header\n"
960 << "\\textclass " << h_textclass << "\n";
961 string const raw = subdoc ? empty_string() : h_preamble.str();
963 os << "\\begin_preamble\n";
964 for (string::size_type i = 0; i < raw.size(); ++i) {
965 if (raw[i] == package_beg_sep) {
966 // Here follows some package loading code that
967 // must be skipped if the package is loaded
969 string::size_type j = raw.find(package_mid_sep, i);
970 if (j == string::npos)
972 string::size_type k = raw.find(package_end_sep, j);
973 if (k == string::npos)
975 string const package = raw.substr(i + 1, j - i - 1);
976 string const replacement = raw.substr(j + 1, k - j - 1);
977 if (auto_packages.find(package) == auto_packages.end())
983 os << "\n\\end_preamble\n";
985 if (!h_options.empty())
986 os << "\\options " << h_options << "\n";
987 os << "\\use_default_options " << h_use_default_options << "\n";
988 if (!used_modules.empty()) {
989 os << "\\begin_modules\n";
990 vector<string>::const_iterator const end = used_modules.end();
991 vector<string>::const_iterator it = used_modules.begin();
992 for (; it != end; ++it)
994 os << "\\end_modules\n";
996 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
997 << "\\language " << h_language << "\n"
998 << "\\language_package " << h_language_package << "\n"
999 << "\\inputencoding " << h_inputencoding << "\n"
1000 << "\\fontencoding " << h_fontencoding << "\n"
1001 << "\\font_roman " << h_font_roman << "\n"
1002 << "\\font_sans " << h_font_sans << "\n"
1003 << "\\font_typewriter " << h_font_typewriter << "\n"
1004 << "\\font_math " << h_font_math << "\n"
1005 << "\\font_default_family " << h_font_default_family << "\n"
1006 << "\\use_non_tex_fonts " << h_use_non_tex_fonts << "\n"
1007 << "\\font_sc " << h_font_sc << "\n"
1008 << "\\font_osf " << h_font_osf << "\n"
1009 << "\\font_sf_scale " << h_font_sf_scale << "\n"
1010 << "\\font_tt_scale " << h_font_tt_scale << '\n';
1011 if (!h_font_cjk.empty())
1012 os << "\\font_cjk " << h_font_cjk << '\n';
1013 os << "\\graphics " << h_graphics << '\n'
1014 << "\\default_output_format " << h_default_output_format << "\n"
1015 << "\\output_sync " << h_output_sync << "\n";
1016 if (h_output_sync == "1")
1017 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1018 os << "\\bibtex_command " << h_bibtex_command << "\n"
1019 << "\\index_command " << h_index_command << "\n";
1020 if (!h_float_placement.empty())
1021 os << "\\float_placement " << h_float_placement << "\n";
1022 os << "\\paperfontsize " << h_paperfontsize << "\n"
1023 << "\\spacing " << h_spacing << "\n"
1024 << "\\use_hyperref " << h_use_hyperref << '\n';
1025 if (h_use_hyperref == "true") {
1026 if (!h_pdf_title.empty())
1027 os << "\\pdf_title \"" << h_pdf_title << "\"\n";
1028 if (!h_pdf_author.empty())
1029 os << "\\pdf_author \"" << h_pdf_author << "\"\n";
1030 if (!h_pdf_subject.empty())
1031 os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
1032 if (!h_pdf_keywords.empty())
1033 os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
1034 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1035 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1036 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1037 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1038 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1039 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1040 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1041 "\\pdf_backref " << h_pdf_backref << "\n"
1042 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1043 if (!h_pdf_pagemode.empty())
1044 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1045 if (!h_pdf_quoted_options.empty())
1046 os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
1048 os << "\\papersize " << h_papersize << "\n"
1049 << "\\use_geometry " << h_use_geometry << '\n';
1050 for (map<string, string>::const_iterator it = h_use_packages.begin();
1051 it != h_use_packages.end(); ++it)
1052 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1053 os << "\\cite_engine " << h_cite_engine << '\n'
1054 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1055 << "\\biblio_style " << h_biblio_style << "\n"
1056 << "\\use_bibtopic " << h_use_bibtopic << "\n"
1057 << "\\use_indices " << h_use_indices << "\n"
1058 << "\\paperorientation " << h_paperorientation << '\n'
1059 << "\\suppress_date " << h_suppress_date << '\n'
1060 << "\\justification " << h_justification << '\n'
1061 << "\\use_refstyle " << h_use_refstyle << '\n';
1062 if (!h_fontcolor.empty())
1063 os << "\\fontcolor " << h_fontcolor << '\n';
1064 if (!h_notefontcolor.empty())
1065 os << "\\notefontcolor " << h_notefontcolor << '\n';
1066 if (!h_backgroundcolor.empty())
1067 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1068 if (!h_boxbgcolor.empty())
1069 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1070 os << "\\index " << h_index << '\n'
1071 << "\\shortcut " << h_shortcut << '\n'
1072 << "\\color " << h_color << '\n'
1075 << "\\secnumdepth " << h_secnumdepth << "\n"
1076 << "\\tocdepth " << h_tocdepth << "\n"
1077 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1078 if (h_paragraph_separation == "skip")
1079 os << "\\defskip " << h_defskip << "\n";
1081 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1082 os << "\\quotes_language " << h_quotes_language << "\n"
1083 << "\\papercolumns " << h_papercolumns << "\n"
1084 << "\\papersides " << h_papersides << "\n"
1085 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1086 if (!h_listings_params.empty())
1087 os << "\\listings_params " << h_listings_params << "\n";
1088 os << "\\tracking_changes " << h_tracking_changes << "\n"
1089 << "\\output_changes " << h_output_changes << "\n"
1090 << "\\html_math_output " << h_html_math_output << "\n"
1091 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1092 << "\\html_be_strict " << h_html_be_strict << "\n"
1094 << "\\end_header\n\n"
1095 << "\\begin_body\n";
1100 void Preamble::parse(Parser & p, string const & forceclass,
1101 TeX2LyXDocClass & tc)
1103 // initialize fixed types
1104 special_columns['D'] = 3;
1105 bool is_full_document = false;
1106 bool is_lyx_file = false;
1107 bool in_lyx_preamble = false;
1109 // determine whether this is a full document or a fragment for inclusion
1111 Token const & t = p.get_token();
1113 if (t.cat() == catEscape && t.cs() == "documentclass") {
1114 is_full_document = true;
1120 while (is_full_document && p.good()) {
1121 Token const & t = p.get_token();
1124 cerr << "t: " << t << "\n";
1130 if (!in_lyx_preamble &&
1131 (t.cat() == catLetter ||
1132 t.cat() == catSuper ||
1133 t.cat() == catSub ||
1134 t.cat() == catOther ||
1135 t.cat() == catMath ||
1136 t.cat() == catActive ||
1137 t.cat() == catBegin ||
1138 t.cat() == catEnd ||
1139 t.cat() == catAlign ||
1140 t.cat() == catParameter))
1141 h_preamble << t.cs();
1143 else if (!in_lyx_preamble &&
1144 (t.cat() == catSpace || t.cat() == catNewline))
1145 h_preamble << t.asInput();
1147 else if (t.cat() == catComment) {
1148 static regex const islyxfile("%% LyX .* created this file");
1149 static regex const usercommands("User specified LaTeX commands");
1151 string const comment = t.asInput();
1153 // magically switch encoding default if it looks like XeLaTeX
1154 static string const magicXeLaTeX =
1155 "% This document must be compiled with XeLaTeX ";
1156 if (comment.size() > magicXeLaTeX.size()
1157 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1158 && h_inputencoding == "auto") {
1159 cerr << "XeLaTeX comment found, switching to UTF8\n";
1160 h_inputencoding = "utf8";
1163 if (regex_search(comment, sub, islyxfile)) {
1165 in_lyx_preamble = true;
1166 } else if (is_lyx_file
1167 && regex_search(comment, sub, usercommands))
1168 in_lyx_preamble = false;
1169 else if (!in_lyx_preamble)
1170 h_preamble << t.asInput();
1173 else if (t.cs() == "pagestyle")
1174 h_paperpagestyle = p.verbatim_item();
1176 else if (t.cs() == "setdefaultlanguage") {
1178 // We don't yet care about non-language variant options
1179 // because LyX doesn't support this yet, see bug #8214
1181 string langopts = p.getOpt();
1182 // check if the option contains a variant, if yes, extract it
1183 string::size_type pos_var = langopts.find("variant");
1184 string::size_type i = langopts.find(',', pos_var);
1185 string::size_type k = langopts.find('=', pos_var);
1186 if (pos_var != string::npos){
1188 if (i == string::npos)
1189 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1191 variant = langopts.substr(k + 1, i - k - 1);
1192 h_language = variant;
1196 h_language = p.verbatim_item();
1197 //finally translate the poyglossia name to a LyX name
1198 h_language = polyglossia2lyx(h_language);
1201 else if (t.cs() == "setotherlanguage") {
1202 // We don't yet care about the option because LyX doesn't
1203 // support this yet, see bug #8214
1204 p.hasOpt() ? p.getOpt() : string();
1208 else if (t.cs() == "setmainfont") {
1209 // we don't care about the option
1210 p.hasOpt() ? p.getOpt() : string();
1211 h_font_roman = p.getArg('{', '}');
1214 else if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1215 // LyX currently only supports the scale option
1218 string fontopts = p.getArg('[', ']');
1219 // check if the option contains a scaling, if yes, extract it
1220 string::size_type pos = fontopts.find("Scale");
1221 if (pos != string::npos) {
1222 string::size_type i = fontopts.find(',', pos);
1223 if (i == string::npos)
1224 scale_as_percentage(fontopts.substr(pos + 1), scale);
1226 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1229 if (t.cs() == "setsansfont") {
1231 h_font_sf_scale = scale;
1232 h_font_sans = p.getArg('{', '}');
1235 h_font_tt_scale = scale;
1236 h_font_typewriter = p.getArg('{', '}');
1240 else if (t.cs() == "date") {
1241 string argument = p.getArg('{', '}');
1242 if (argument.empty())
1243 h_suppress_date = "true";
1245 h_preamble << t.asInput() << '{' << argument << '}';
1248 else if (t.cs() == "color") {
1249 string const space =
1250 (p.hasOpt() ? p.getOpt() : string());
1251 string argument = p.getArg('{', '}');
1252 // check the case that a standard color is used
1253 if (space.empty() && is_known(argument, known_basic_colors)) {
1254 h_fontcolor = rgbcolor2code(argument);
1255 preamble.registerAutomaticallyLoadedPackage("color");
1256 } else if (space.empty() && argument == "document_fontcolor")
1257 preamble.registerAutomaticallyLoadedPackage("color");
1258 // check the case that LyX's document_fontcolor is defined
1259 // but not used for \color
1261 h_preamble << t.asInput();
1263 h_preamble << space;
1264 h_preamble << '{' << argument << '}';
1265 // the color might already be set because \definecolor
1266 // is parsed before this
1271 else if (t.cs() == "pagecolor") {
1272 string argument = p.getArg('{', '}');
1273 // check the case that a standard color is used
1274 if (is_known(argument, known_basic_colors)) {
1275 h_backgroundcolor = rgbcolor2code(argument);
1276 } else if (argument == "page_backgroundcolor")
1277 preamble.registerAutomaticallyLoadedPackage("color");
1278 // check the case that LyX's page_backgroundcolor is defined
1279 // but not used for \pagecolor
1281 h_preamble << t.asInput() << '{' << argument << '}';
1282 // the color might already be set because \definecolor
1283 // is parsed before this
1284 h_backgroundcolor = "";
1288 else if (t.cs() == "makeatletter") {
1289 // LyX takes care of this
1290 p.setCatCode('@', catLetter);
1293 else if (t.cs() == "makeatother") {
1294 // LyX takes care of this
1295 p.setCatCode('@', catOther);
1298 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1299 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1300 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1301 || t.cs() == "DeclareRobustCommand"
1302 || t.cs() == "DeclareRobustCommandx"
1303 || t.cs() == "ProvideTextCommandDefault"
1304 || t.cs() == "DeclareMathAccent") {
1306 if (p.next_token().character() == '*') {
1310 string const name = p.verbatim_item();
1311 string const opt1 = p.getFullOpt();
1312 string const opt2 = p.getFullOpt();
1313 string const body = p.verbatim_item();
1315 if (name == "\\rmdefault")
1316 if (is_known(body, known_roman_fonts))
1317 h_font_roman = body;
1318 if (name == "\\sfdefault")
1319 if (is_known(body, known_sans_fonts))
1321 if (name == "\\ttdefault")
1322 if (is_known(body, known_typewriter_fonts))
1323 h_font_typewriter = body;
1324 if (name == "\\familydefault") {
1325 string family = body;
1326 // remove leading "\"
1327 h_font_default_family = family.erase(0,1);
1330 // remove the lyxdot definition that is re-added by LyX
1332 if (name == "\\lyxdot")
1333 in_lyx_preamble = true;
1335 // Add the command to the known commands
1336 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1338 // only non-lyxspecific stuff
1339 if (!in_lyx_preamble) {
1341 ss << '\\' << t.cs();
1344 ss << '{' << name << '}' << opt1 << opt2
1345 << '{' << body << "}";
1346 h_preamble << ss.str();
1348 ostream & out = in_preamble ? h_preamble : os;
1349 out << "\\" << t.cs() << "{" << name << "}"
1350 << opts << "{" << body << "}";
1355 else if (t.cs() == "documentclass") {
1356 vector<string>::iterator it;
1357 vector<string> opts = split_options(p.getArg('[', ']'));
1358 handle_opt(opts, known_fontsizes, h_paperfontsize);
1359 delete_opt(opts, known_fontsizes);
1360 // delete "pt" at the end
1361 string::size_type i = h_paperfontsize.find("pt");
1362 if (i != string::npos)
1363 h_paperfontsize.erase(i);
1364 // The documentclass options are always parsed before the options
1365 // of the babel call so that a language cannot overwrite the babel
1367 handle_opt(opts, known_languages, h_language);
1368 delete_opt(opts, known_languages);
1370 // paper orientation
1371 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1372 h_paperorientation = "landscape";
1376 if ((it = find(opts.begin(), opts.end(), "oneside"))
1381 if ((it = find(opts.begin(), opts.end(), "twoside"))
1387 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1389 h_papercolumns = "1";
1392 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1394 h_papercolumns = "2";
1398 // some size options are known to any document classes, other sizes
1399 // are handled by the \geometry command of the geometry package
1400 handle_opt(opts, known_class_paper_sizes, h_papersize);
1401 delete_opt(opts, known_class_paper_sizes);
1402 // the remaining options
1403 h_options = join(opts, ",");
1404 // FIXME This does not work for classes that have a
1405 // different name in LyX than in LaTeX
1406 h_textclass = p.getArg('{', '}');
1409 else if (t.cs() == "usepackage") {
1410 string const options = p.getArg('[', ']');
1411 string const name = p.getArg('{', '}');
1412 vector<string> vecnames;
1413 split(name, vecnames, ',');
1414 vector<string>::const_iterator it = vecnames.begin();
1415 vector<string>::const_iterator end = vecnames.end();
1416 for (; it != end; ++it)
1417 handle_package(p, trimSpaceAndEol(*it), options,
1421 else if (t.cs() == "inputencoding") {
1422 string const encoding = p.getArg('{','}');
1423 h_inputencoding = encoding;
1424 p.setEncoding(encoding);
1427 else if (t.cs() == "newenvironment") {
1428 string const name = p.getArg('{', '}');
1429 string const opt1 = p.getFullOpt();
1430 string const opt2 = p.getFullOpt();
1431 string const beg = p.verbatim_item();
1432 string const end = p.verbatim_item();
1433 if (!in_lyx_preamble) {
1434 h_preamble << "\\newenvironment{" << name
1435 << '}' << opt1 << opt2 << '{'
1436 << beg << "}{" << end << '}';
1438 add_known_environment(name, opt1, !opt2.empty(),
1439 from_utf8(beg), from_utf8(end));
1443 else if (t.cs() == "newtheorem") {
1444 string const name = p.getArg('{', '}');
1445 string const opt1 = p.getFullOpt();
1446 string const opt2 = p.getFullOpt();
1447 string const body = p.verbatim_item();
1448 string const opt3 = p.getFullOpt();
1450 add_known_theorem(name, opt1, !opt2.empty(),
1451 from_utf8("\\newtheorem{" + name + '}' +
1452 opt1 + opt2 + '{' + body + '}' + opt3));
1454 if (!in_lyx_preamble)
1455 h_preamble << "\\newtheorem{" << name << '}'
1456 << opt1 << opt2 << '{' << '}' << opt3;
1459 else if (t.cs() == "def") {
1460 string name = p.get_token().cs();
1461 // In fact, name may be more than the name:
1462 // In the test case of bug 8116
1463 // name == "csname SF@gobble@opt \endcsname".
1464 // Therefore, we need to use asInput() instead of cs().
1465 while (p.next_token().cat() != catBegin)
1466 name += p.get_token().asInput();
1467 if (!in_lyx_preamble)
1468 h_preamble << "\\def\\" << name << '{'
1469 << p.verbatim_item() << "}";
1472 else if (t.cs() == "newcolumntype") {
1473 string const name = p.getArg('{', '}');
1474 trimSpaceAndEol(name);
1476 string opts = p.getOpt();
1477 if (!opts.empty()) {
1478 istringstream is(string(opts, 1));
1481 special_columns[name[0]] = nargs;
1482 h_preamble << "\\newcolumntype{" << name << "}";
1484 h_preamble << "[" << nargs << "]";
1485 h_preamble << "{" << p.verbatim_item() << "}";
1488 else if (t.cs() == "setcounter") {
1489 string const name = p.getArg('{', '}');
1490 string const content = p.getArg('{', '}');
1491 if (name == "secnumdepth")
1492 h_secnumdepth = content;
1493 else if (name == "tocdepth")
1494 h_tocdepth = content;
1496 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1499 else if (t.cs() == "setlength") {
1500 string const name = p.verbatim_item();
1501 string const content = p.verbatim_item();
1502 // the paragraphs are only not indented when \parindent is set to zero
1503 if (name == "\\parindent" && content != "") {
1504 if (content[0] == '0')
1505 h_paragraph_separation = "skip";
1507 h_paragraph_indentation = translate_len(content);
1508 } else if (name == "\\parskip") {
1509 if (content == "\\smallskipamount")
1510 h_defskip = "smallskip";
1511 else if (content == "\\medskipamount")
1512 h_defskip = "medskip";
1513 else if (content == "\\bigskipamount")
1514 h_defskip = "bigskip";
1516 h_defskip = content;
1518 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1521 else if (t.cs() == "onehalfspacing")
1522 h_spacing = "onehalf";
1524 else if (t.cs() == "doublespacing")
1525 h_spacing = "double";
1527 else if (t.cs() == "setstretch")
1528 h_spacing = "other " + p.verbatim_item();
1530 else if (t.cs() == "synctex") {
1531 // the scheme is \synctex=value
1532 // where value can only be "1" or "-1"
1533 h_output_sync = "1";
1534 // there can be any character behind the value (e.g. a linebreak or a '\'
1535 // therefore we extract it char by char
1537 string value = p.get_token().asInput();
1539 value += p.get_token().asInput();
1540 h_output_sync_macro = "\\synctex=" + value;
1543 else if (t.cs() == "begin") {
1544 string const name = p.getArg('{', '}');
1545 if (name == "document")
1547 h_preamble << "\\begin{" << name << "}";
1550 else if (t.cs() == "geometry") {
1551 vector<string> opts = split_options(p.getArg('{', '}'));
1552 handle_geometry(opts);
1555 else if (t.cs() == "definecolor") {
1556 string const color = p.getArg('{', '}');
1557 string const space = p.getArg('{', '}');
1558 string const value = p.getArg('{', '}');
1559 if (color == "document_fontcolor" && space == "rgb") {
1560 RGBColor c(RGBColorFromLaTeX(value));
1561 h_fontcolor = X11hexname(c);
1562 } else if (color == "note_fontcolor" && space == "rgb") {
1563 RGBColor c(RGBColorFromLaTeX(value));
1564 h_notefontcolor = X11hexname(c);
1565 } else if (color == "page_backgroundcolor" && space == "rgb") {
1566 RGBColor c(RGBColorFromLaTeX(value));
1567 h_backgroundcolor = X11hexname(c);
1568 } else if (color == "shadecolor" && space == "rgb") {
1569 RGBColor c(RGBColorFromLaTeX(value));
1570 h_boxbgcolor = X11hexname(c);
1572 h_preamble << "\\definecolor{" << color
1573 << "}{" << space << "}{" << value
1578 else if (t.cs() == "bibliographystyle")
1579 h_biblio_style = p.verbatim_item();
1581 else if (t.cs() == "jurabibsetup") {
1582 // FIXME p.getArg('{', '}') is most probably wrong (it
1583 // does not handle nested braces).
1584 // Use p.verbatim_item() instead.
1585 vector<string> jurabibsetup =
1586 split_options(p.getArg('{', '}'));
1587 // add jurabibsetup to the jurabib package options
1588 add_package("jurabib", jurabibsetup);
1589 if (!jurabibsetup.empty()) {
1590 h_preamble << "\\jurabibsetup{"
1591 << join(jurabibsetup, ",") << '}';
1595 else if (t.cs() == "hypersetup") {
1596 vector<string> hypersetup =
1597 split_options(p.verbatim_item());
1598 // add hypersetup to the hyperref package options
1599 handle_hyperref(hypersetup);
1600 if (!hypersetup.empty()) {
1601 h_preamble << "\\hypersetup{"
1602 << join(hypersetup, ",") << '}';
1606 else if (is_known(t.cs(), known_if_3arg_commands)) {
1607 // prevent misparsing of \usepackage if it is used
1608 // as an argument (see e.g. our own output of
1609 // \@ifundefined above)
1610 string const arg1 = p.verbatim_item();
1611 string const arg2 = p.verbatim_item();
1612 string const arg3 = p.verbatim_item();
1613 // test case \@ifundefined{date}{}{\date{}}
1614 if (t.cs() == "@ifundefined" && arg1 == "date" &&
1615 arg2.empty() && arg3 == "\\date{}") {
1616 h_suppress_date = "true";
1617 // older tex2lyx versions did output
1618 // \@ifundefined{definecolor}{\usepackage{color}}{}
1619 } else if (t.cs() == "@ifundefined" &&
1620 arg1 == "definecolor" &&
1621 arg2 == "\\usepackage{color}" &&
1623 if (!in_lyx_preamble)
1624 h_preamble << package_beg_sep
1627 << "\\@ifundefined{definecolor}{color}{}"
1630 //\@ifundefined{showcaptionsetup}{}{%
1631 // \PassOptionsToPackage{caption=false}{subfig}}
1632 // that LyX uses for subfloats
1633 } else if (t.cs() == "@ifundefined" &&
1634 arg1 == "showcaptionsetup" && arg2.empty()
1635 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1637 } else if (!in_lyx_preamble) {
1638 h_preamble << t.asInput()
1639 << '{' << arg1 << '}'
1640 << '{' << arg2 << '}'
1641 << '{' << arg3 << '}';
1645 else if (is_known(t.cs(), known_if_commands)) {
1646 // must not parse anything in conditional code, since
1647 // LyX would output the parsed contents unconditionally
1648 if (!in_lyx_preamble)
1649 h_preamble << t.asInput();
1650 handle_if(p, in_lyx_preamble);
1653 else if (!t.cs().empty() && !in_lyx_preamble)
1654 h_preamble << '\\' << t.cs();
1657 // remove the whitespace
1660 // Force textclass if the user wanted it
1661 if (!forceclass.empty())
1662 h_textclass = forceclass;
1663 tc.setName(h_textclass);
1665 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1668 if (h_papersides.empty()) {
1671 h_papersides = ss.str();
1674 // If the CJK package is used we cannot set the document language from
1675 // the babel options. Instead, we guess which language is used most
1676 // and set this one.
1677 default_language = h_language;
1678 if (is_full_document &&
1679 (auto_packages.find("CJK") != auto_packages.end() ||
1680 auto_packages.find("CJKutf8") != auto_packages.end())) {
1682 h_language = guessLanguage(p, default_language);
1688 string babel2lyx(string const & language)
1690 char const * const * where = is_known(language, known_languages);
1692 return known_coded_languages[where - known_languages];
1697 string Preamble::polyglossia2lyx(string const & language)
1699 char const * const * where = is_known(language, polyglossia_languages);
1701 return coded_polyglossia_languages[where - polyglossia_languages];
1706 string rgbcolor2code(string const & name)
1708 char const * const * where = is_known(name, known_basic_colors);
1710 // "red", "green" etc
1711 return known_basic_color_codes[where - known_basic_colors];
1713 // "255,0,0", "0,255,0" etc
1714 RGBColor c(RGBColorFromLaTeX(name));
1715 return X11hexname(c);