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", "mathpazo",
128 "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 else if (name == "mathpazo")
673 h_font_roman = "palatino";
675 else if (name == "mathptmx")
676 h_font_roman = "times";
679 if (is_known(name, known_sans_fonts)) {
681 if (options.size() == 1) {
682 if (scale_as_percentage(opts, h_font_sf_scale))
688 if (is_known(name, known_typewriter_fonts)) {
689 // fourier can be set as roman font _only_
690 // fourier as typewriter is handled in handling of \ttdefault
691 if (name != "fourier") {
692 h_font_typewriter = name;
693 if (options.size() == 1) {
694 if (scale_as_percentage(opts, h_font_tt_scale))
700 // font uses old-style figure
704 if (name == "refstyle")
705 h_use_refstyle = "1";
707 // after the detection and handling of special cases, we can remove the
708 // fonts, otherwise they would appear in the preamble, see bug #7856
709 if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
710 || is_known(name, known_typewriter_fonts))
713 else if (name == "amsmath" || name == "amssymb" ||
714 name == "esint" || name == "mhchem" || name == "mathdots" ||
715 name == "mathtools" || name == "stackrel" ||
716 name == "stmaryrd" || name == "undertilde")
717 h_use_packages[name] = "2";
719 else if (name == "babel") {
720 h_language_package = "default";
721 // One might think we would have to do nothing if babel is loaded
722 // without any options to prevent pollution of the preamble with this
723 // babel call in every roundtrip.
724 // But the user could have defined babel-specific things afterwards. So
725 // we need to keep it in the preamble to prevent cases like bug #7861.
727 // check if more than one option was used - used later for inputenc
728 if (options.begin() != options.end() - 1)
729 one_language = false;
730 // babel takes the last language of the option of its \usepackage
731 // call as document language. If there is no such language option, the
732 // last language in the documentclass options is used.
733 handle_opt(options, known_languages, h_language);
734 // translate the babel name to a LyX name
735 h_language = babel2lyx(h_language);
736 if (h_language == "japanese") {
737 // For Japanese, the encoding isn't indicated in the source
738 // file, and there's really not much we can do. We could
739 // 1) offer a list of possible encodings to choose from, or
740 // 2) determine the encoding of the file by inspecting it.
741 // For the time being, we leave the encoding alone so that
742 // we don't get iconv errors when making a wrong guess, and
743 // we will output a note at the top of the document
744 // explaining what to do.
745 Encoding const * const enc = encodings.fromIconvName(
746 p.getEncoding(), Encoding::japanese, false);
748 h_inputencoding = enc->name();
749 is_nonCJKJapanese = true;
750 // in this case babel can be removed from the preamble
751 registerAutomaticallyLoadedPackage("babel");
753 // If babel is called with options, LyX puts them by default into the
754 // document class options. This works for most languages, except
755 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
756 // perhaps in future others.
757 // Therefore keep the babel call as it is as the user might have
759 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
761 delete_opt(options, known_languages);
764 h_preamble << "\\usepackage{babel}\n";
767 else if (name == "polyglossia") {
768 h_language_package = "default";
769 h_default_output_format = "pdf4";
770 h_use_non_tex_fonts = "true";
772 registerAutomaticallyLoadedPackage("xunicode");
773 if (h_inputencoding == "auto")
774 p.setEncoding("UTF-8");
777 else if (name == "CJK") {
778 // set the encoding to "auto" because it might be set to "default" by the babel handling
779 // and this would not be correct for CJK
780 if (h_inputencoding == "default")
781 h_inputencoding = "auto";
782 registerAutomaticallyLoadedPackage("CJK");
785 else if (name == "CJKutf8") {
786 h_inputencoding = "UTF8";
787 p.setEncoding("UTF-8");
788 registerAutomaticallyLoadedPackage("CJKutf8");
791 else if (name == "fontenc") {
792 h_fontencoding = getStringFromVector(options, ",");
793 /* We could do the following for better round trip support,
794 * but this makes the document less portable, so I skip it:
795 if (h_fontencoding == lyxrc.fontenc)
796 h_fontencoding = "global";
801 else if (name == "inputenc" || name == "luainputenc") {
802 // h_inputencoding is only set when there is not more than one
803 // inputenc option because otherwise h_inputencoding must be
804 // set to "auto" (the default encoding of the document language)
805 // Therefore check for the "," character.
806 // It is also only set when there is not more than one babel
809 if (opts.find(",") == string::npos && one_language == true) {
810 h_inputencoding = opts;
811 // FIXME: if this line is used, tex2lyx swallows the next character
812 // in the file behind "{inputenc}"
813 //p.setEncoding(opts);
815 h_preamble << "\\usepackage[" << opts << "}{" << name << "}\n";
816 // FIXME: enabling this introduces bug #8525
817 //p.setEncoding(options.back(), Encoding::inputenc);
821 h_preamble << "\\usepackage{" << name << "}\n";
824 else if (name == "srcltx") {
827 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
830 h_output_sync_macro = "\\usepackage{srcltx}";
833 else if (is_known(name, known_old_language_packages)) {
834 // known language packages from the times before babel
835 // if they are found and not also babel, they will be used as
836 // custom language package
837 h_language_package = "\\usepackage{" + name + "}";
840 else if (name == "prettyref")
841 ; // ignore this FIXME: Use the package separator mechanism instead
843 else if (name == "lyxskak") {
844 // ignore this and its options
845 const char * const o[] = {"ps", "mover", 0};
846 delete_opt(options, o);
849 else if (is_known(name, known_lyx_packages) && options.empty()) {
850 if (name == "splitidx")
851 h_use_indices = "true";
852 if (!in_lyx_preamble) {
853 h_preamble << package_beg_sep << name
854 << package_mid_sep << "\\usepackage{"
856 if (p.next_token().cat() == catNewline ||
857 (p.next_token().cat() == catSpace &&
858 p.next_next_token().cat() == catNewline))
860 h_preamble << package_end_sep;
864 else if (name == "geometry")
865 handle_geometry(options);
867 else if (name == "subfig")
868 ; // ignore this FIXME: Use the package separator mechanism instead
870 else if ((where = is_known(name, known_languages)))
871 h_language = known_coded_languages[where - known_languages];
873 else if (name == "natbib") {
874 h_biblio_style = "plainnat";
875 h_cite_engine = "natbib";
876 h_cite_engine_type = "authoryear";
877 vector<string>::iterator it =
878 find(options.begin(), options.end(), "authoryear");
879 if (it != options.end())
882 it = find(options.begin(), options.end(), "numbers");
883 if (it != options.end()) {
884 h_cite_engine_type = "numerical";
890 else if (name == "jurabib") {
891 h_biblio_style = "jurabib";
892 h_cite_engine = "jurabib";
893 h_cite_engine_type = "authoryear";
896 else if (name == "hyperref")
897 handle_hyperref(options);
899 else if (!in_lyx_preamble) {
901 h_preamble << "\\usepackage{" << name << '}';
903 h_preamble << "\\usepackage[" << opts << "]{"
907 if (p.next_token().cat() == catNewline ||
908 (p.next_token().cat() == catSpace &&
909 p.next_next_token().cat() == catNewline))
913 // We need to do something with the options...
914 if (!options.empty())
915 cerr << "Ignoring options '" << join(options, ",")
916 << "' of package " << name << '.' << endl;
918 // remove the whitespace
923 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
926 Token t = p.get_token();
927 if (t.cat() == catEscape &&
928 is_known(t.cs(), known_if_commands))
929 handle_if(p, in_lyx_preamble);
931 if (!in_lyx_preamble)
932 h_preamble << t.asInput();
933 if (t.cat() == catEscape && t.cs() == "fi")
940 bool Preamble::writeLyXHeader(ostream & os, bool subdoc)
942 // set the quote language
943 // LyX only knows the following quotes languages:
944 // english, swedish, german, polish, french and danish
945 // (quotes for "japanese" and "chinese-traditional" are missing because
946 // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
947 // conversion list taken from
948 // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
949 // (quotes for kazakh and interlingua are unknown)
951 if (is_known(h_language, known_danish_quotes_languages))
952 h_quotes_language = "danish";
954 else if (is_known(h_language, known_french_quotes_languages))
955 h_quotes_language = "french";
957 else if (is_known(h_language, known_german_quotes_languages))
958 h_quotes_language = "german";
960 else if (is_known(h_language, known_polish_quotes_languages))
961 h_quotes_language = "polish";
963 else if (is_known(h_language, known_swedish_quotes_languages))
964 h_quotes_language = "swedish";
966 else if (is_known(h_language, known_english_quotes_languages))
967 h_quotes_language = "english";
969 if (contains(h_float_placement, "H"))
970 registerAutomaticallyLoadedPackage("float");
971 if (h_spacing != "single" && h_spacing != "default")
972 registerAutomaticallyLoadedPackage("setspace");
973 if (h_use_packages["amsmath"] == "2") {
974 // amsbsy and amstext are already provided by amsmath
975 registerAutomaticallyLoadedPackage("amsbsy");
976 registerAutomaticallyLoadedPackage("amstext");
979 // output the LyX file settings
980 os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
981 << "\\lyxformat " << LYX_FORMAT << '\n'
982 << "\\begin_document\n"
983 << "\\begin_header\n"
984 << "\\textclass " << h_textclass << "\n";
985 string const raw = subdoc ? empty_string() : h_preamble.str();
987 os << "\\begin_preamble\n";
988 for (string::size_type i = 0; i < raw.size(); ++i) {
989 if (raw[i] == package_beg_sep) {
990 // Here follows some package loading code that
991 // must be skipped if the package is loaded
993 string::size_type j = raw.find(package_mid_sep, i);
994 if (j == string::npos)
996 string::size_type k = raw.find(package_end_sep, j);
997 if (k == string::npos)
999 string const package = raw.substr(i + 1, j - i - 1);
1000 string const replacement = raw.substr(j + 1, k - j - 1);
1001 if (auto_packages.find(package) == auto_packages.end())
1007 os << "\n\\end_preamble\n";
1009 if (!h_options.empty())
1010 os << "\\options " << h_options << "\n";
1011 os << "\\use_default_options " << h_use_default_options << "\n";
1012 if (!used_modules.empty()) {
1013 os << "\\begin_modules\n";
1014 vector<string>::const_iterator const end = used_modules.end();
1015 vector<string>::const_iterator it = used_modules.begin();
1016 for (; it != end; ++it)
1018 os << "\\end_modules\n";
1020 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1021 << "\\language " << h_language << "\n"
1022 << "\\language_package " << h_language_package << "\n"
1023 << "\\inputencoding " << h_inputencoding << "\n"
1024 << "\\fontencoding " << h_fontencoding << "\n"
1025 << "\\font_roman " << h_font_roman << "\n"
1026 << "\\font_sans " << h_font_sans << "\n"
1027 << "\\font_typewriter " << h_font_typewriter << "\n"
1028 << "\\font_math " << h_font_math << "\n"
1029 << "\\font_default_family " << h_font_default_family << "\n"
1030 << "\\use_non_tex_fonts " << h_use_non_tex_fonts << "\n"
1031 << "\\font_sc " << h_font_sc << "\n"
1032 << "\\font_osf " << h_font_osf << "\n"
1033 << "\\font_sf_scale " << h_font_sf_scale << "\n"
1034 << "\\font_tt_scale " << h_font_tt_scale << '\n';
1035 if (!h_font_cjk.empty())
1036 os << "\\font_cjk " << h_font_cjk << '\n';
1037 os << "\\graphics " << h_graphics << '\n'
1038 << "\\default_output_format " << h_default_output_format << "\n"
1039 << "\\output_sync " << h_output_sync << "\n";
1040 if (h_output_sync == "1")
1041 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1042 os << "\\bibtex_command " << h_bibtex_command << "\n"
1043 << "\\index_command " << h_index_command << "\n";
1044 if (!h_float_placement.empty())
1045 os << "\\float_placement " << h_float_placement << "\n";
1046 os << "\\paperfontsize " << h_paperfontsize << "\n"
1047 << "\\spacing " << h_spacing << "\n"
1048 << "\\use_hyperref " << h_use_hyperref << '\n';
1049 if (h_use_hyperref == "true") {
1050 if (!h_pdf_title.empty())
1051 os << "\\pdf_title \"" << h_pdf_title << "\"\n";
1052 if (!h_pdf_author.empty())
1053 os << "\\pdf_author \"" << h_pdf_author << "\"\n";
1054 if (!h_pdf_subject.empty())
1055 os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
1056 if (!h_pdf_keywords.empty())
1057 os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
1058 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1059 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1060 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1061 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1062 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1063 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1064 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1065 "\\pdf_backref " << h_pdf_backref << "\n"
1066 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1067 if (!h_pdf_pagemode.empty())
1068 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1069 if (!h_pdf_quoted_options.empty())
1070 os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
1072 os << "\\papersize " << h_papersize << "\n"
1073 << "\\use_geometry " << h_use_geometry << '\n';
1074 for (map<string, string>::const_iterator it = h_use_packages.begin();
1075 it != h_use_packages.end(); ++it)
1076 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1077 os << "\\cite_engine " << h_cite_engine << '\n'
1078 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1079 << "\\biblio_style " << h_biblio_style << "\n"
1080 << "\\use_bibtopic " << h_use_bibtopic << "\n"
1081 << "\\use_indices " << h_use_indices << "\n"
1082 << "\\paperorientation " << h_paperorientation << '\n'
1083 << "\\suppress_date " << h_suppress_date << '\n'
1084 << "\\justification " << h_justification << '\n'
1085 << "\\use_refstyle " << h_use_refstyle << '\n';
1086 if (!h_fontcolor.empty())
1087 os << "\\fontcolor " << h_fontcolor << '\n';
1088 if (!h_notefontcolor.empty())
1089 os << "\\notefontcolor " << h_notefontcolor << '\n';
1090 if (!h_backgroundcolor.empty())
1091 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1092 if (!h_boxbgcolor.empty())
1093 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1094 os << "\\index " << h_index << '\n'
1095 << "\\shortcut " << h_shortcut << '\n'
1096 << "\\color " << h_color << '\n'
1099 << "\\secnumdepth " << h_secnumdepth << "\n"
1100 << "\\tocdepth " << h_tocdepth << "\n"
1101 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1102 if (h_paragraph_separation == "skip")
1103 os << "\\defskip " << h_defskip << "\n";
1105 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1106 os << "\\quotes_language " << h_quotes_language << "\n"
1107 << "\\papercolumns " << h_papercolumns << "\n"
1108 << "\\papersides " << h_papersides << "\n"
1109 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1110 if (!h_listings_params.empty())
1111 os << "\\listings_params " << h_listings_params << "\n";
1112 os << "\\tracking_changes " << h_tracking_changes << "\n"
1113 << "\\output_changes " << h_output_changes << "\n"
1114 << "\\html_math_output " << h_html_math_output << "\n"
1115 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1116 << "\\html_be_strict " << h_html_be_strict << "\n"
1118 << "\\end_header\n\n"
1119 << "\\begin_body\n";
1124 void Preamble::parse(Parser & p, string const & forceclass,
1125 TeX2LyXDocClass & tc)
1127 // initialize fixed types
1128 special_columns['D'] = 3;
1129 bool is_full_document = false;
1130 bool is_lyx_file = false;
1131 bool in_lyx_preamble = false;
1133 // determine whether this is a full document or a fragment for inclusion
1135 Token const & t = p.get_token();
1137 if (t.cat() == catEscape && t.cs() == "documentclass") {
1138 is_full_document = true;
1144 while (is_full_document && p.good()) {
1145 Token const & t = p.get_token();
1148 cerr << "t: " << t << "\n";
1154 if (!in_lyx_preamble &&
1155 (t.cat() == catLetter ||
1156 t.cat() == catSuper ||
1157 t.cat() == catSub ||
1158 t.cat() == catOther ||
1159 t.cat() == catMath ||
1160 t.cat() == catActive ||
1161 t.cat() == catBegin ||
1162 t.cat() == catEnd ||
1163 t.cat() == catAlign ||
1164 t.cat() == catParameter))
1165 h_preamble << t.cs();
1167 else if (!in_lyx_preamble &&
1168 (t.cat() == catSpace || t.cat() == catNewline))
1169 h_preamble << t.asInput();
1171 else if (t.cat() == catComment) {
1172 static regex const islyxfile("%% LyX .* created this file");
1173 static regex const usercommands("User specified LaTeX commands");
1175 string const comment = t.asInput();
1177 // magically switch encoding default if it looks like XeLaTeX
1178 static string const magicXeLaTeX =
1179 "% This document must be compiled with XeLaTeX ";
1180 if (comment.size() > magicXeLaTeX.size()
1181 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1182 && h_inputencoding == "auto") {
1183 cerr << "XeLaTeX comment found, switching to UTF8\n";
1184 h_inputencoding = "utf8";
1187 if (regex_search(comment, sub, islyxfile)) {
1189 in_lyx_preamble = true;
1190 } else if (is_lyx_file
1191 && regex_search(comment, sub, usercommands))
1192 in_lyx_preamble = false;
1193 else if (!in_lyx_preamble)
1194 h_preamble << t.asInput();
1197 else if (t.cs() == "pagestyle")
1198 h_paperpagestyle = p.verbatim_item();
1200 else if (t.cs() == "setdefaultlanguage") {
1202 // We don't yet care about non-language variant options
1203 // because LyX doesn't support this yet, see bug #8214
1205 string langopts = p.getOpt();
1206 // check if the option contains a variant, if yes, extract it
1207 string::size_type pos_var = langopts.find("variant");
1208 string::size_type i = langopts.find(',', pos_var);
1209 string::size_type k = langopts.find('=', pos_var);
1210 if (pos_var != string::npos){
1212 if (i == string::npos)
1213 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1215 variant = langopts.substr(k + 1, i - k - 1);
1216 h_language = variant;
1220 h_language = p.verbatim_item();
1221 //finally translate the poyglossia name to a LyX name
1222 h_language = polyglossia2lyx(h_language);
1225 else if (t.cs() == "setotherlanguage") {
1226 // We don't yet care about the option because LyX doesn't
1227 // support this yet, see bug #8214
1228 p.hasOpt() ? p.getOpt() : string();
1232 else if (t.cs() == "setmainfont") {
1233 // we don't care about the option
1234 p.hasOpt() ? p.getOpt() : string();
1235 h_font_roman = p.getArg('{', '}');
1238 else if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1239 // LyX currently only supports the scale option
1242 string fontopts = p.getArg('[', ']');
1243 // check if the option contains a scaling, if yes, extract it
1244 string::size_type pos = fontopts.find("Scale");
1245 if (pos != string::npos) {
1246 string::size_type i = fontopts.find(',', pos);
1247 if (i == string::npos)
1248 scale_as_percentage(fontopts.substr(pos + 1), scale);
1250 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1253 if (t.cs() == "setsansfont") {
1255 h_font_sf_scale = scale;
1256 h_font_sans = p.getArg('{', '}');
1259 h_font_tt_scale = scale;
1260 h_font_typewriter = p.getArg('{', '}');
1264 else if (t.cs() == "date") {
1265 string argument = p.getArg('{', '}');
1266 if (argument.empty())
1267 h_suppress_date = "true";
1269 h_preamble << t.asInput() << '{' << argument << '}';
1272 else if (t.cs() == "color") {
1273 string const space =
1274 (p.hasOpt() ? p.getOpt() : string());
1275 string argument = p.getArg('{', '}');
1276 // check the case that a standard color is used
1277 if (space.empty() && is_known(argument, known_basic_colors)) {
1278 h_fontcolor = rgbcolor2code(argument);
1279 preamble.registerAutomaticallyLoadedPackage("color");
1280 } else if (space.empty() && argument == "document_fontcolor")
1281 preamble.registerAutomaticallyLoadedPackage("color");
1282 // check the case that LyX's document_fontcolor is defined
1283 // but not used for \color
1285 h_preamble << t.asInput();
1287 h_preamble << space;
1288 h_preamble << '{' << argument << '}';
1289 // the color might already be set because \definecolor
1290 // is parsed before this
1295 else if (t.cs() == "pagecolor") {
1296 string argument = p.getArg('{', '}');
1297 // check the case that a standard color is used
1298 if (is_known(argument, known_basic_colors)) {
1299 h_backgroundcolor = rgbcolor2code(argument);
1300 } else if (argument == "page_backgroundcolor")
1301 preamble.registerAutomaticallyLoadedPackage("color");
1302 // check the case that LyX's page_backgroundcolor is defined
1303 // but not used for \pagecolor
1305 h_preamble << t.asInput() << '{' << argument << '}';
1306 // the color might already be set because \definecolor
1307 // is parsed before this
1308 h_backgroundcolor = "";
1312 else if (t.cs() == "makeatletter") {
1313 // LyX takes care of this
1314 p.setCatcode('@', catLetter);
1317 else if (t.cs() == "makeatother") {
1318 // LyX takes care of this
1319 p.setCatcode('@', catOther);
1322 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1323 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1324 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1325 || t.cs() == "DeclareRobustCommand"
1326 || t.cs() == "DeclareRobustCommandx"
1327 || t.cs() == "ProvideTextCommandDefault"
1328 || t.cs() == "DeclareMathAccent") {
1330 if (p.next_token().character() == '*') {
1334 string const name = p.verbatim_item();
1335 string const opt1 = p.getFullOpt();
1336 string const opt2 = p.getFullOpt();
1337 string const body = p.verbatim_item();
1338 // store the in_lyx_preamble setting
1339 bool const was_in_lyx_preamble = in_lyx_preamble;
1341 if (name == "\\rmdefault")
1342 if (is_known(body, known_roman_fonts)) {
1343 h_font_roman = body;
1345 in_lyx_preamble = true;
1347 if (name == "\\sfdefault")
1348 if (is_known(body, known_sans_fonts)) {
1351 in_lyx_preamble = true;
1353 if (name == "\\ttdefault")
1354 if (is_known(body, known_typewriter_fonts)) {
1355 h_font_typewriter = body;
1357 in_lyx_preamble = true;
1359 if (name == "\\familydefault") {
1360 string family = body;
1361 // remove leading "\"
1362 h_font_default_family = family.erase(0,1);
1364 in_lyx_preamble = true;
1367 if (name == "\\bfdefault")
1368 // LyX re-adds this if a kurier font is used
1369 if (is_known(h_font_sans, known_kurier_fonts) && body == "b") {
1371 in_lyx_preamble = true;
1374 // remove the lyxdot definition that is re-added by LyX
1376 if (name == "\\lyxdot") {
1378 in_lyx_preamble = true;
1381 // Add the command to the known commands
1382 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1384 // only non-lyxspecific stuff
1385 if (!in_lyx_preamble) {
1387 ss << '\\' << t.cs();
1390 ss << '{' << name << '}' << opt1 << opt2
1391 << '{' << body << "}";
1392 h_preamble << ss.str();
1394 ostream & out = in_preamble ? h_preamble : os;
1395 out << "\\" << t.cs() << "{" << name << "}"
1396 << opts << "{" << body << "}";
1399 // restore the in_lyx_preamble setting
1400 in_lyx_preamble = was_in_lyx_preamble;
1403 else if (t.cs() == "edef"){
1404 // we only support this for kurier fonts
1405 string const command = p.next_token().asInput();
1407 if (command == "\\sfdefault") {
1409 if (h_font_sans == "kurier")
1410 h_font_sans = "kurier-condensed";
1411 if (h_font_sans == "kurierl")
1412 h_font_sans = "kurier-light-condensed";
1416 h_preamble << "\\edef" << command << "{" << p.getArg('{', '}') << "}\n";
1419 else if (t.cs() == "documentclass") {
1420 vector<string>::iterator it;
1421 vector<string> opts = split_options(p.getArg('[', ']'));
1422 handle_opt(opts, known_fontsizes, h_paperfontsize);
1423 delete_opt(opts, known_fontsizes);
1424 // delete "pt" at the end
1425 string::size_type i = h_paperfontsize.find("pt");
1426 if (i != string::npos)
1427 h_paperfontsize.erase(i);
1428 // The documentclass options are always parsed before the options
1429 // of the babel call so that a language cannot overwrite the babel
1431 handle_opt(opts, known_languages, h_language);
1432 delete_opt(opts, known_languages);
1434 // paper orientation
1435 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1436 h_paperorientation = "landscape";
1440 if ((it = find(opts.begin(), opts.end(), "oneside"))
1445 if ((it = find(opts.begin(), opts.end(), "twoside"))
1451 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1453 h_papercolumns = "1";
1456 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1458 h_papercolumns = "2";
1462 // some size options are known to any document classes, other sizes
1463 // are handled by the \geometry command of the geometry package
1464 handle_opt(opts, known_class_paper_sizes, h_papersize);
1465 delete_opt(opts, known_class_paper_sizes);
1466 // the remaining options
1467 h_options = join(opts, ",");
1468 // FIXME This does not work for classes that have a
1469 // different name in LyX than in LaTeX
1470 h_textclass = p.getArg('{', '}');
1474 else if (t.cs() == "usepackage") {
1475 string const options = p.getArg('[', ']');
1476 string const name = p.getArg('{', '}');
1477 vector<string> vecnames;
1478 split(name, vecnames, ',');
1479 vector<string>::const_iterator it = vecnames.begin();
1480 vector<string>::const_iterator end = vecnames.end();
1481 for (; it != end; ++it)
1482 handle_package(p, trimSpaceAndEol(*it), options,
1486 else if (t.cs() == "inputencoding") {
1487 string const encoding = p.getArg('{','}');
1488 h_inputencoding = encoding;
1489 p.setEncoding(encoding, Encoding::inputenc);
1492 else if (t.cs() == "newenvironment") {
1493 string const name = p.getArg('{', '}');
1494 string const opt1 = p.getFullOpt();
1495 string const opt2 = p.getFullOpt();
1496 string const beg = p.verbatim_item();
1497 string const end = p.verbatim_item();
1498 if (!in_lyx_preamble) {
1499 h_preamble << "\\newenvironment{" << name
1500 << '}' << opt1 << opt2 << '{'
1501 << beg << "}{" << end << '}';
1503 add_known_environment(name, opt1, !opt2.empty(),
1504 from_utf8(beg), from_utf8(end));
1508 else if (t.cs() == "newtheorem") {
1509 string const name = p.getArg('{', '}');
1510 string const opt1 = p.getFullOpt();
1511 string const opt2 = p.getFullOpt();
1512 string const body = p.verbatim_item();
1513 string const opt3 = p.getFullOpt();
1515 add_known_theorem(name, opt1, !opt2.empty(),
1516 from_utf8("\\newtheorem{" + name + '}' +
1517 opt1 + opt2 + '{' + body + '}' + opt3));
1519 if (!in_lyx_preamble)
1520 h_preamble << "\\newtheorem{" << name << '}'
1521 << opt1 << opt2 << '{' << '}' << opt3;
1524 else if (t.cs() == "def") {
1525 string name = p.get_token().cs();
1526 // In fact, name may be more than the name:
1527 // In the test case of bug 8116
1528 // name == "csname SF@gobble@opt \endcsname".
1529 // Therefore, we need to use asInput() instead of cs().
1530 while (p.next_token().cat() != catBegin)
1531 name += p.get_token().asInput();
1532 if (!in_lyx_preamble)
1533 h_preamble << "\\def\\" << name << '{'
1534 << p.verbatim_item() << "}";
1537 else if (t.cs() == "newcolumntype") {
1538 string const name = p.getArg('{', '}');
1539 trimSpaceAndEol(name);
1541 string opts = p.getOpt();
1542 if (!opts.empty()) {
1543 istringstream is(string(opts, 1));
1546 special_columns[name[0]] = nargs;
1547 h_preamble << "\\newcolumntype{" << name << "}";
1549 h_preamble << "[" << nargs << "]";
1550 h_preamble << "{" << p.verbatim_item() << "}";
1553 else if (t.cs() == "setcounter") {
1554 string const name = p.getArg('{', '}');
1555 string const content = p.getArg('{', '}');
1556 if (name == "secnumdepth")
1557 h_secnumdepth = content;
1558 else if (name == "tocdepth")
1559 h_tocdepth = content;
1561 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1564 else if (t.cs() == "setlength") {
1565 string const name = p.verbatim_item();
1566 string const content = p.verbatim_item();
1567 // the paragraphs are only not indented when \parindent is set to zero
1568 if (name == "\\parindent" && content != "") {
1569 if (content[0] == '0')
1570 h_paragraph_separation = "skip";
1572 h_paragraph_indentation = translate_len(content);
1573 } else if (name == "\\parskip") {
1574 if (content == "\\smallskipamount")
1575 h_defskip = "smallskip";
1576 else if (content == "\\medskipamount")
1577 h_defskip = "medskip";
1578 else if (content == "\\bigskipamount")
1579 h_defskip = "bigskip";
1581 h_defskip = content;
1583 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1586 else if (t.cs() == "onehalfspacing")
1587 h_spacing = "onehalf";
1589 else if (t.cs() == "doublespacing")
1590 h_spacing = "double";
1592 else if (t.cs() == "setstretch")
1593 h_spacing = "other " + p.verbatim_item();
1595 else if (t.cs() == "synctex") {
1596 // the scheme is \synctex=value
1597 // where value can only be "1" or "-1"
1598 h_output_sync = "1";
1599 // there can be any character behind the value (e.g. a linebreak or a '\'
1600 // therefore we extract it char by char
1602 string value = p.get_token().asInput();
1604 value += p.get_token().asInput();
1605 h_output_sync_macro = "\\synctex=" + value;
1608 else if (t.cs() == "begin") {
1609 string const name = p.getArg('{', '}');
1610 if (name == "document")
1612 h_preamble << "\\begin{" << name << "}";
1615 else if (t.cs() == "geometry") {
1616 vector<string> opts = split_options(p.getArg('{', '}'));
1617 handle_geometry(opts);
1620 else if (t.cs() == "definecolor") {
1621 string const color = p.getArg('{', '}');
1622 string const space = p.getArg('{', '}');
1623 string const value = p.getArg('{', '}');
1624 if (color == "document_fontcolor" && space == "rgb") {
1625 RGBColor c(RGBColorFromLaTeX(value));
1626 h_fontcolor = X11hexname(c);
1627 } else if (color == "note_fontcolor" && space == "rgb") {
1628 RGBColor c(RGBColorFromLaTeX(value));
1629 h_notefontcolor = X11hexname(c);
1630 } else if (color == "page_backgroundcolor" && space == "rgb") {
1631 RGBColor c(RGBColorFromLaTeX(value));
1632 h_backgroundcolor = X11hexname(c);
1633 } else if (color == "shadecolor" && space == "rgb") {
1634 RGBColor c(RGBColorFromLaTeX(value));
1635 h_boxbgcolor = X11hexname(c);
1637 h_preamble << "\\definecolor{" << color
1638 << "}{" << space << "}{" << value
1643 else if (t.cs() == "bibliographystyle")
1644 h_biblio_style = p.verbatim_item();
1646 else if (t.cs() == "jurabibsetup") {
1647 // FIXME p.getArg('{', '}') is most probably wrong (it
1648 // does not handle nested braces).
1649 // Use p.verbatim_item() instead.
1650 vector<string> jurabibsetup =
1651 split_options(p.getArg('{', '}'));
1652 // add jurabibsetup to the jurabib package options
1653 add_package("jurabib", jurabibsetup);
1654 if (!jurabibsetup.empty()) {
1655 h_preamble << "\\jurabibsetup{"
1656 << join(jurabibsetup, ",") << '}';
1660 else if (t.cs() == "hypersetup") {
1661 vector<string> hypersetup =
1662 split_options(p.verbatim_item());
1663 // add hypersetup to the hyperref package options
1664 handle_hyperref(hypersetup);
1665 if (!hypersetup.empty()) {
1666 h_preamble << "\\hypersetup{"
1667 << join(hypersetup, ",") << '}';
1671 else if (is_known(t.cs(), known_if_3arg_commands)) {
1672 // prevent misparsing of \usepackage if it is used
1673 // as an argument (see e.g. our own output of
1674 // \@ifundefined above)
1675 string const arg1 = p.verbatim_item();
1676 string const arg2 = p.verbatim_item();
1677 string const arg3 = p.verbatim_item();
1678 // test case \@ifundefined{date}{}{\date{}}
1679 if (t.cs() == "@ifundefined" && arg1 == "date" &&
1680 arg2.empty() && arg3 == "\\date{}") {
1681 h_suppress_date = "true";
1682 // older tex2lyx versions did output
1683 // \@ifundefined{definecolor}{\usepackage{color}}{}
1684 } else if (t.cs() == "@ifundefined" &&
1685 arg1 == "definecolor" &&
1686 arg2 == "\\usepackage{color}" &&
1688 if (!in_lyx_preamble)
1689 h_preamble << package_beg_sep
1692 << "\\@ifundefined{definecolor}{color}{}"
1695 //\@ifundefined{showcaptionsetup}{}{%
1696 // \PassOptionsToPackage{caption=false}{subfig}}
1697 // that LyX uses for subfloats
1698 } else if (t.cs() == "@ifundefined" &&
1699 arg1 == "showcaptionsetup" && arg2.empty()
1700 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1702 } else if (!in_lyx_preamble) {
1703 h_preamble << t.asInput()
1704 << '{' << arg1 << '}'
1705 << '{' << arg2 << '}'
1706 << '{' << arg3 << '}';
1710 else if (is_known(t.cs(), known_if_commands)) {
1711 // must not parse anything in conditional code, since
1712 // LyX would output the parsed contents unconditionally
1713 if (!in_lyx_preamble)
1714 h_preamble << t.asInput();
1715 handle_if(p, in_lyx_preamble);
1718 else if (!t.cs().empty() && !in_lyx_preamble)
1719 h_preamble << '\\' << t.cs();
1722 // remove the whitespace
1725 // Force textclass if the user wanted it
1726 if (!forceclass.empty())
1727 h_textclass = forceclass;
1728 tc.setName(h_textclass);
1730 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1733 if (h_papersides.empty()) {
1736 h_papersides = ss.str();
1739 // If the CJK package is used we cannot set the document language from
1740 // the babel options. Instead, we guess which language is used most
1741 // and set this one.
1742 default_language = h_language;
1743 if (is_full_document &&
1744 (auto_packages.find("CJK") != auto_packages.end() ||
1745 auto_packages.find("CJKutf8") != auto_packages.end())) {
1747 h_language = guessLanguage(p, default_language);
1753 string babel2lyx(string const & language)
1755 char const * const * where = is_known(language, known_languages);
1757 return known_coded_languages[where - known_languages];
1762 string Preamble::polyglossia2lyx(string const & language)
1764 char const * const * where = is_known(language, polyglossia_languages);
1766 return coded_polyglossia_languages[where - polyglossia_languages];
1771 string rgbcolor2code(string const & name)
1773 char const * const * where = is_known(name, known_basic_colors);
1775 // "red", "green" etc
1776 return known_basic_color_codes[where - known_basic_colors];
1778 // "255,0,0", "0,255,0" etc
1779 RGBColor c(RGBColorFromLaTeX(name));
1780 return X11hexname(c);