3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
9 * Full author contact details are available in file CREDITS.
20 #include "LayoutFile.h"
23 #include "TextClass.h"
25 #include "support/convert.h"
26 #include "support/FileName.h"
27 #include "support/filetools.h"
28 #include "support/lstrings.h"
30 #include "support/regex.h"
36 using namespace lyx::support;
41 // special columntypes
42 extern map<char, int> special_columns;
48 // CJK languages are handled in text.cpp, polyglossia languages are listed
51 * known babel language names (including synonyms)
52 * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
53 * please keep this in sync with known_coded_languages line by line!
55 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
56 "american", "arabic", "arabtex", "australian", "austrian", "bahasa", "bahasai",
57 "bahasam", "basque", "belarusian", "brazil", "brazilian", "breton", "british",
58 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
59 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "francais",
60 "french", "frenchb", "frenchle", "frenchpro", "galician", "german", "germanb",
61 "greek", "hebrew", "hungarian", "icelandic", "indon", "indonesian", "interlingua",
62 "irish", "italian", "japanese", "kazakh", "kurmanji", "latin", "latvian", "lithuanian",
63 "lowersorbian", "lsorbian", "magyar", "malay", "meyalu", "mongolian", "naustrian",
64 "newzealand", "ngerman", "ngermanb", "norsk", "nynorsk", "polutonikogreek", "polish",
65 "portuges", "portuguese", "romanian", "russian", "russianb", "samin",
66 "scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish",
67 "swedish", "thai", "turkish", "turkmen", "ukraineb", "ukrainian",
68 "uppersorbian", "UKenglish", "USenglish", "usorbian", "vietnam", "welsh",
72 * the same as known_languages with .lyx names
73 * please keep this in sync with known_languages line by line!
75 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
76 "american", "arabic_arabi", "arabic_arabtex", "australian", "austrian", "bahasa", "bahasa",
77 "bahasam", "basque", "belarusian", "brazilian", "brazilian", "breton", "british",
78 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
79 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "french",
80 "french", "french", "french", "french", "galician", "german", "german",
81 "greek", "hebrew", "magyar", "icelandic", "bahasa", "bahasa", "interlingua",
82 "irish", "italian", "japanese", "kazakh", "kurmanji", "latin", "latvian", "lithuanian",
83 "lowersorbian", "lowersorbian", "magyar", "bahasam", "bahasam", "mongolian", "naustrian",
84 "newzealand", "ngerman", "ngerman", "norsk", "nynorsk", "polutonikogreek", "polish",
85 "portuguese", "portuguese", "romanian", "russian", "russian", "samin",
86 "scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish",
87 "swedish", "thai", "turkish", "turkmen", "ukrainian", "ukrainian",
88 "uppersorbian", "uppersorbian", "english", "english", "vietnamese", "welsh",
91 /// languages with danish quotes (.lyx names)
92 const char * const known_danish_quotes_languages[] = {"danish", 0};
94 /// languages with english quotes (.lyx names)
95 const char * const known_english_quotes_languages[] = {"american", "australian",
96 "bahasa", "bahasam", "brazilian", "canadian", "chinese-simplified", "english",
97 "esperanto", "hebrew", "irish", "korean", "newzealand", "portuguese", "scottish",
100 /// languages with french quotes (.lyx names)
101 const char * const known_french_quotes_languages[] = {"albanian",
102 "arabic_arabi", "arabic_arabtex", "basque", "canadien", "catalan", "french",
103 "galician", "greek", "italian", "norsk", "nynorsk", "polutonikogreek",
104 "russian", "spanish", "spanish-mexico", "turkish", "turkmen", "ukrainian",
107 /// languages with german quotes (.lyx names)
108 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
109 "czech", "german", "icelandic", "lithuanian", "lowersorbian", "naustrian",
110 "ngerman", "serbian", "serbian-latin", "slovak", "slovene", "uppersorbian", 0};
112 /// languages with polish quotes (.lyx names)
113 const char * const known_polish_quotes_languages[] = {"afrikaans", "croatian",
114 "dutch", "estonian", "magyar", "polish", "romanian", 0};
116 /// languages with swedish quotes (.lyx names)
117 const char * const known_swedish_quotes_languages[] = {"finnish",
120 /// known language packages from the times before babel
121 const char * const known_old_language_packages[] = {"french", "frenchle",
122 "frenchpro", "german", "ngerman", "pmfrench", 0};
124 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
126 const char * const known_roman_fonts[] = { "ae", "beraserif", "bookman",
127 "ccfonts", "chancery", "charter", "cmr", "fourier", "garamondx", "libertine-type1",
128 "lmodern", "mathdesign", "mathpazo", "mathptmx", "newcent", "utopia", 0};
130 const char * const known_sans_fonts[] = { "avant", "berasans", "cmbr", "cmss",
131 "helvet", "kurier", "kurierl", "lmss", 0};
133 const char * const known_kurier_fonts[] = { "kurier", "kurierl", "kurier-condensed",
134 "kurier-light-condensed", 0};
136 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
137 "courier", "lmtt", "luximono", "fourier", "lmodern", "mathpazo", "mathptmx",
140 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
141 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
142 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
143 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
144 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
146 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
147 "executivepaper", "legalpaper", "letterpaper", 0};
149 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
150 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
152 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
153 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
156 /// commands that can start an \if...\else...\endif sequence
157 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
158 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
159 "ifsidecap", "ifupgreek", 0};
161 const char * const known_basic_colors[] = {"blue", "black", "cyan", "green",
162 "magenta", "red", "white", "yellow", 0};
164 const char * const known_basic_color_codes[] = {"#0000ff", "#000000", "#00ffff", "#00ff00",
165 "#ff00ff", "#ff0000", "#ffffff", "#ffff00", 0};
167 /// conditional commands with three arguments like \@ifundefined{}{}{}
168 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
171 /// packages that work only in xetex
172 /// polyglossia is handled separately
173 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
174 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
175 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
177 /// packages that are automatically skipped if loaded by LyX
178 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
179 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color", "float",
180 "fontspec", "graphicx", "hhline", "ifthen", "longtable", "makeidx", "multirow",
181 "nomencl", "pdfpages", "rotating", "rotfloat", "splitidx", "setspace",
182 "subscript", "textcomp", "ulem", "url", "varioref", "verbatim", "wrapfig",
185 // codes used to remove packages that are loaded automatically by LyX.
186 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
187 const char package_beg_sep = '\001';
188 const char package_mid_sep = '\002';
189 const char package_end_sep = '\003';
192 // returns true if at least one of the options in what has been found
193 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
199 // the last language option is the document language (for babel and LyX)
200 // the last size option is the document font size
201 vector<string>::iterator it;
202 vector<string>::iterator position = opts.begin();
203 for (; *what; ++what) {
204 it = find(opts.begin(), opts.end(), *what);
205 if (it != opts.end()) {
206 if (it >= position) {
217 void delete_opt(vector<string> & opts, char const * const * what)
222 // remove found options from the list
223 // do this after handle_opt to avoid potential memory leaks
224 vector<string>::iterator it;
225 for (; *what; ++what) {
226 it = find(opts.begin(), opts.end(), *what);
227 if (it != opts.end())
234 * Split a package options string (keyval format) into a vector.
236 * authorformat=smallcaps,
238 * titleformat=colonsep,
239 * bibformat={tabular,ibidem,numbered}
241 vector<string> split_options(string const & input)
243 vector<string> options;
247 Token const & t = p.get_token();
248 if (t.asInput() == ",") {
249 options.push_back(trimSpaceAndEol(option));
251 } else if (t.asInput() == "=") {
254 if (p.next_token().asInput() == "{")
255 option += '{' + p.getArg('{', '}') + '}';
256 } else if (t.cat() != catSpace)
257 option += t.asInput();
261 options.push_back(trimSpaceAndEol(option));
268 * Retrieve a keyval option "name={value with=sign}" named \p name from
269 * \p options and return the value.
270 * The found option is also removed from \p options.
272 string process_keyval_opt(vector<string> & options, string name)
274 for (size_t i = 0; i < options.size(); ++i) {
275 vector<string> option;
276 split(options[i], option, '=');
277 if (option.size() < 2)
279 if (option[0] == name) {
280 options.erase(options.begin() + i);
281 option.erase(option.begin());
282 return join(option, "=");
288 } // anonymous namespace
292 * known polyglossia language names (including variants)
294 const char * const Preamble::polyglossia_languages[] = {
295 "albanian", "croatian", "hebrew", "norsk", "swedish", "amharic", "czech", "hindi",
296 "nynorsk", "syriac", "arabic", "danish", "icelandic", "occitan", "tamil",
297 "armenian", "divehi", "interlingua", "polish", "telugu", "asturian", "dutch",
298 "irish", "portuges", "thai", "bahasai", "english", "italian", "romanian", "turkish",
299 "bahasam", "esperanto", "lao", "russian", "turkmen", "basque", "estonian", "latin",
300 "samin", "ukrainian", "bengali", "farsi", "latvian", "sanskrit", "urdu", "brazil",
301 "brazilian", "finnish", "lithuanian", "scottish", "usorbian", "breton", "french",
302 "lsorbian", "serbian", "vietnamese", "bulgarian", "galician", "magyar", "slovak",
303 "welsh", "catalan", "german", "malayalam", "slovenian", "coptic", "greek",
304 "marathi", "spanish",
305 "american", "ancient", "australian", "british", "monotonic", "newzealand",
309 * the same as polyglossia_languages with .lyx names
310 * please keep this in sync with polyglossia_languages line by line!
312 const char * const Preamble::coded_polyglossia_languages[] = {
313 "albanian", "croatian", "hebrew", "norsk", "swedish", "amharic", "czech", "hindi",
314 "nynorsk", "syriac", "arabic_arabi", "danish", "icelandic", "occitan", "tamil",
315 "armenian", "divehi", "interlingua", "polish", "telugu", "asturian", "dutch",
316 "irish", "portuges", "thai", "bahasa", "english", "italian", "romanian", "turkish",
317 "bahasam", "esperanto", "lao", "russian", "turkmen", "basque", "estonian", "latin",
318 "samin", "ukrainian", "bengali", "farsi", "latvian", "sanskrit", "urdu", "brazilian",
319 "brazilian", "finnish", "lithuanian", "scottish", "uppersorbian", "breton", "french",
320 "lowersorbian", "serbian", "vietnamese", "bulgarian", "galician", "magyar", "slovak",
321 "welsh", "catalan", "ngerman", "malayalam", "slovene", "coptic", "greek",
322 "marathi", "spanish",
323 "american", "ancientgreek", "australian", "british", "greek", "newzealand",
324 "polutonikogreek", 0};
327 bool Preamble::indentParagraphs() const
329 return h_paragraph_separation == "indent";
333 bool Preamble::isPackageUsed(string const & package) const
335 return used_packages.find(package) != used_packages.end();
339 vector<string> Preamble::getPackageOptions(string const & package) const
341 map<string, vector<string> >::const_iterator it = used_packages.find(package);
342 if (it != used_packages.end())
344 return vector<string>();
348 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
350 auto_packages.insert(package);
354 void Preamble::addModule(string const & module)
356 used_modules.push_back(module);
360 void Preamble::suppressDate(bool suppress)
363 h_suppress_date = "true";
365 h_suppress_date = "false";
369 void Preamble::registerAuthor(std::string const & name)
371 Author author(from_utf8(name), empty_docstring());
372 author.setUsed(true);
373 authors_.record(author);
374 h_tracking_changes = "true";
375 h_output_changes = "true";
379 Author const & Preamble::getAuthor(std::string const & name) const
381 Author author(from_utf8(name), empty_docstring());
382 for (AuthorList::Authors::const_iterator it = authors_.begin();
383 it != authors_.end(); ++it)
386 static Author const dummy;
391 void Preamble::add_package(string const & name, vector<string> & options)
393 // every package inherits the global options
394 if (used_packages.find(name) == used_packages.end())
395 used_packages[name] = split_options(h_options);
397 vector<string> & v = used_packages[name];
398 v.insert(v.end(), options.begin(), options.end());
399 if (name == "jurabib") {
400 // Don't output the order argument (see the cite command
401 // handling code in text.cpp).
402 vector<string>::iterator end =
403 remove(options.begin(), options.end(), "natbiborder");
404 end = remove(options.begin(), end, "jurabiborder");
405 options.erase(end, options.end());
412 // Given is a string like "scaled=0.9" or "Scale=0.9", return 0.9 * 100
413 bool scale_as_percentage(string const & scale, string & percentage)
415 string::size_type pos = scale.find('=');
416 if (pos != string::npos) {
417 string value = scale.substr(pos + 1);
418 if (isStrDbl(value)) {
419 percentage = convert<string>(100 * convert<double>(value));
427 string remove_braces(string const & value)
431 if (value[0] == '{' && value[value.length()-1] == '}')
432 return value.substr(1, value.length()-2);
436 } // anonymous namespace
439 Preamble::Preamble() : one_language(true), title_layout_found(false),
440 h_font_cjk_set(false)
444 h_biblio_style = "plain";
445 h_bibtex_command = "default";
446 h_cite_engine = "basic";
447 h_cite_engine_type = "numerical";
449 h_defskip = "medskip";
452 h_fontencoding = "default";
453 h_font_roman = "default";
454 h_font_sans = "default";
455 h_font_typewriter = "default";
456 h_font_math = "auto";
457 h_font_default_family = "default";
458 h_use_non_tex_fonts = "false";
460 h_font_osf = "false";
461 h_font_sf_scale = "100";
462 h_font_tt_scale = "100";
464 h_graphics = "default";
465 h_default_output_format = "default";
466 h_html_be_strict = "false";
467 h_html_css_as_file = "0";
468 h_html_math_output = "0";
470 h_index_command = "default";
471 h_inputencoding = "auto";
472 h_justification = "true";
473 h_language = "english";
474 h_language_package = "none";
476 h_maintain_unincluded_children = "false";
480 h_output_changes = "false";
482 //h_output_sync_macro
483 h_papercolumns = "1";
484 h_paperfontsize = "default";
485 h_paperorientation = "portrait";
486 h_paperpagestyle = "default";
488 h_papersize = "default";
489 h_paragraph_indentation = "default";
490 h_paragraph_separation = "indent";
495 h_pdf_bookmarks = "1";
496 h_pdf_bookmarksnumbered = "0";
497 h_pdf_bookmarksopen = "0";
498 h_pdf_bookmarksopenlevel = "1";
499 h_pdf_breaklinks = "0";
500 h_pdf_pdfborder = "0";
501 h_pdf_colorlinks = "0";
502 h_pdf_backref = "section";
503 h_pdf_pdfusetitle = "1";
505 //h_pdf_quoted_options;
506 h_quotes_language = "english";
509 h_spacing = "single";
510 h_suppress_date = "false";
511 h_textclass = "article";
513 h_tracking_changes = "false";
514 h_use_bibtopic = "false";
515 h_use_indices = "false";
516 h_use_geometry = "false";
517 h_use_default_options = "false";
518 h_use_hyperref = "false";
519 h_use_refstyle = "0";
520 h_use_packages["amsmath"] = "1";
521 h_use_packages["amssymb"] = "0";
522 h_use_packages["esint"] = "1";
523 h_use_packages["mhchem"] = "0";
524 h_use_packages["mathdots"] = "0";
525 h_use_packages["mathtools"] = "0";
526 h_use_packages["stackrel"] = "0";
527 h_use_packages["stmaryrd"] = "0";
528 h_use_packages["undertilde"] = "0";
532 void Preamble::handle_hyperref(vector<string> & options)
534 // FIXME swallow inputencoding changes that might surround the
535 // hyperref setup if it was written by LyX
536 h_use_hyperref = "true";
537 // swallow "unicode=true", since LyX does always write that
538 vector<string>::iterator it =
539 find(options.begin(), options.end(), "unicode=true");
540 if (it != options.end())
542 it = find(options.begin(), options.end(), "pdfusetitle");
543 if (it != options.end()) {
544 h_pdf_pdfusetitle = "1";
547 string bookmarks = process_keyval_opt(options, "bookmarks");
548 if (bookmarks == "true")
549 h_pdf_bookmarks = "1";
550 else if (bookmarks == "false")
551 h_pdf_bookmarks = "0";
552 if (h_pdf_bookmarks == "1") {
553 string bookmarksnumbered =
554 process_keyval_opt(options, "bookmarksnumbered");
555 if (bookmarksnumbered == "true")
556 h_pdf_bookmarksnumbered = "1";
557 else if (bookmarksnumbered == "false")
558 h_pdf_bookmarksnumbered = "0";
559 string bookmarksopen =
560 process_keyval_opt(options, "bookmarksopen");
561 if (bookmarksopen == "true")
562 h_pdf_bookmarksopen = "1";
563 else if (bookmarksopen == "false")
564 h_pdf_bookmarksopen = "0";
565 if (h_pdf_bookmarksopen == "1") {
566 string bookmarksopenlevel =
567 process_keyval_opt(options, "bookmarksopenlevel");
568 if (!bookmarksopenlevel.empty())
569 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
572 string breaklinks = process_keyval_opt(options, "breaklinks");
573 if (breaklinks == "true")
574 h_pdf_breaklinks = "1";
575 else if (breaklinks == "false")
576 h_pdf_breaklinks = "0";
577 string pdfborder = process_keyval_opt(options, "pdfborder");
578 if (pdfborder == "{0 0 0}")
579 h_pdf_pdfborder = "1";
580 else if (pdfborder == "{0 0 1}")
581 h_pdf_pdfborder = "0";
582 string backref = process_keyval_opt(options, "backref");
583 if (!backref.empty())
584 h_pdf_backref = backref;
585 string colorlinks = process_keyval_opt(options, "colorlinks");
586 if (colorlinks == "true")
587 h_pdf_colorlinks = "1";
588 else if (colorlinks == "false")
589 h_pdf_colorlinks = "0";
590 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
591 if (!pdfpagemode.empty())
592 h_pdf_pagemode = pdfpagemode;
593 string pdftitle = process_keyval_opt(options, "pdftitle");
594 if (!pdftitle.empty()) {
595 h_pdf_title = remove_braces(pdftitle);
597 string pdfauthor = process_keyval_opt(options, "pdfauthor");
598 if (!pdfauthor.empty()) {
599 h_pdf_author = remove_braces(pdfauthor);
601 string pdfsubject = process_keyval_opt(options, "pdfsubject");
602 if (!pdfsubject.empty())
603 h_pdf_subject = remove_braces(pdfsubject);
604 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
605 if (!pdfkeywords.empty())
606 h_pdf_keywords = remove_braces(pdfkeywords);
607 if (!options.empty()) {
608 if (!h_pdf_quoted_options.empty())
609 h_pdf_quoted_options += ',';
610 h_pdf_quoted_options += join(options, ",");
616 void Preamble::handle_geometry(vector<string> & options)
618 h_use_geometry = "true";
619 vector<string>::iterator it;
621 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
622 h_paperorientation = "landscape";
626 // keyval version: "paper=letter"
627 string paper = process_keyval_opt(options, "paper");
629 h_papersize = paper + "paper";
630 // alternative version: "letterpaper"
631 handle_opt(options, known_paper_sizes, h_papersize);
632 delete_opt(options, known_paper_sizes);
634 char const * const * margin = known_paper_margins;
635 for (; *margin; ++margin) {
636 string value = process_keyval_opt(options, *margin);
637 if (!value.empty()) {
638 int k = margin - known_paper_margins;
639 string name = known_coded_paper_margins[k];
640 h_margins += '\\' + name + ' ' + value + '\n';
646 void Preamble::handle_package(Parser &p, string const & name,
647 string const & opts, bool in_lyx_preamble)
649 vector<string> options = split_options(opts);
650 add_package(name, options);
651 char const * const * where = 0;
653 if (is_known(name, known_xetex_packages)) {
655 h_use_non_tex_fonts = "true";
656 registerAutomaticallyLoadedPackage("fontspec");
657 if (h_inputencoding == "auto")
658 p.setEncoding("UTF-8");
662 if (is_known(name, known_roman_fonts))
665 if (name == "fourier") {
666 h_font_roman = "utopia";
667 // when font uses real small capitals
668 if (opts == "expert")
672 if (name == "garamondx") {
673 h_font_roman = "garamondx";
678 if (name == "libertine-type1") {
679 h_font_roman = "libertine";
680 if (opts == "lining")
681 h_font_osf = "false";
686 if (name == "mathdesign") {
687 if (opts.find("charter") != string::npos)
688 h_font_roman = "md-charter";
689 if (opts.find("garamond") != string::npos)
690 h_font_roman = "md-garamond";
691 if (opts.find("utopia") != string::npos)
692 h_font_roman = "md-utopia";
693 if (opts.find("expert") != string::npos) {
699 else if (name == "mathpazo")
700 h_font_roman = "palatino";
702 else if (name == "mathptmx")
703 h_font_roman = "times";
706 if (is_known(name, known_sans_fonts)) {
708 if (options.size() == 1) {
709 if (scale_as_percentage(opts, h_font_sf_scale))
715 if (is_known(name, known_typewriter_fonts)) {
716 // fourier can be set as roman font _only_
717 // fourier as typewriter is handled in handling of \ttdefault
718 if (name != "fourier") {
719 h_font_typewriter = name;
720 if (options.size() == 1) {
721 if (scale_as_percentage(opts, h_font_tt_scale))
727 // font uses old-style figure
731 if (name == "refstyle")
732 h_use_refstyle = "1";
734 // after the detection and handling of special cases, we can remove the
735 // fonts, otherwise they would appear in the preamble, see bug #7856
736 if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
737 || is_known(name, known_typewriter_fonts))
740 else if (name == "amsmath" || name == "amssymb" ||
741 name == "esint" || name == "mhchem" || name == "mathdots" ||
742 name == "mathtools" || name == "stackrel" ||
743 name == "stmaryrd" || name == "undertilde")
744 h_use_packages[name] = "2";
746 else if (name == "babel") {
747 h_language_package = "default";
748 // One might think we would have to do nothing if babel is loaded
749 // without any options to prevent pollution of the preamble with this
750 // babel call in every roundtrip.
751 // But the user could have defined babel-specific things afterwards. So
752 // we need to keep it in the preamble to prevent cases like bug #7861.
754 // check if more than one option was used - used later for inputenc
755 if (options.begin() != options.end() - 1)
756 one_language = false;
757 // babel takes the last language of the option of its \usepackage
758 // call as document language. If there is no such language option, the
759 // last language in the documentclass options is used.
760 handle_opt(options, known_languages, h_language);
761 // translate the babel name to a LyX name
762 h_language = babel2lyx(h_language);
763 if (h_language == "japanese") {
764 // For Japanese, the encoding isn't indicated in the source
765 // file, and there's really not much we can do. We could
766 // 1) offer a list of possible encodings to choose from, or
767 // 2) determine the encoding of the file by inspecting it.
768 // For the time being, we leave the encoding alone so that
769 // we don't get iconv errors when making a wrong guess, and
770 // we will output a note at the top of the document
771 // explaining what to do.
772 Encoding const * const enc = encodings.fromIconvName(
773 p.getEncoding(), Encoding::japanese, false);
775 h_inputencoding = enc->name();
776 is_nonCJKJapanese = true;
777 // in this case babel can be removed from the preamble
778 registerAutomaticallyLoadedPackage("babel");
780 // If babel is called with options, LyX puts them by default into the
781 // document class options. This works for most languages, except
782 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
783 // perhaps in future others.
784 // Therefore keep the babel call as it is as the user might have
786 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
788 delete_opt(options, known_languages);
791 h_preamble << "\\usepackage{babel}\n";
794 else if (name == "polyglossia") {
795 h_language_package = "default";
796 h_default_output_format = "pdf4";
797 h_use_non_tex_fonts = "true";
799 registerAutomaticallyLoadedPackage("xunicode");
800 if (h_inputencoding == "auto")
801 p.setEncoding("UTF-8");
804 else if (name == "CJK") {
805 // set the encoding to "auto" because it might be set to "default" by the babel handling
806 // and this would not be correct for CJK
807 if (h_inputencoding == "default")
808 h_inputencoding = "auto";
809 registerAutomaticallyLoadedPackage("CJK");
812 else if (name == "CJKutf8") {
813 h_inputencoding = "UTF8";
814 p.setEncoding("UTF-8");
815 registerAutomaticallyLoadedPackage("CJKutf8");
818 else if (name == "fontenc") {
819 h_fontencoding = getStringFromVector(options, ",");
820 /* We could do the following for better round trip support,
821 * but this makes the document less portable, so I skip it:
822 if (h_fontencoding == lyxrc.fontenc)
823 h_fontencoding = "global";
828 else if (name == "inputenc" || name == "luainputenc") {
829 // h_inputencoding is only set when there is not more than one
830 // inputenc option because otherwise h_inputencoding must be
831 // set to "auto" (the default encoding of the document language)
832 // Therefore check for the "," character.
833 // It is also only set when there is not more than one babel
836 if (opts.find(",") == string::npos && one_language == true) {
837 h_inputencoding = opts;
838 // FIXME: if this line is used, tex2lyx swallows the next character
839 // in the file behind "{inputenc}"
840 //p.setEncoding(opts);
842 h_preamble << "\\usepackage[" << opts << "}{" << name << "}\n";
843 // FIXME: enabling this introduces bug #8525
844 //p.setEncoding(options.back(), Encoding::inputenc);
848 h_preamble << "\\usepackage{" << name << "}\n";
851 else if (name == "srcltx") {
854 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
857 h_output_sync_macro = "\\usepackage{srcltx}";
860 else if (is_known(name, known_old_language_packages)) {
861 // known language packages from the times before babel
862 // if they are found and not also babel, they will be used as
863 // custom language package
864 h_language_package = "\\usepackage{" + name + "}";
867 else if (name == "prettyref")
868 ; // ignore this FIXME: Use the package separator mechanism instead
870 else if (name == "lyxskak") {
871 // ignore this and its options
872 const char * const o[] = {"ps", "mover", 0};
873 delete_opt(options, o);
876 else if (is_known(name, known_lyx_packages) && options.empty()) {
877 if (name == "splitidx")
878 h_use_indices = "true";
879 if (!in_lyx_preamble) {
880 h_preamble << package_beg_sep << name
881 << package_mid_sep << "\\usepackage{"
883 if (p.next_token().cat() == catNewline ||
884 (p.next_token().cat() == catSpace &&
885 p.next_next_token().cat() == catNewline))
887 h_preamble << package_end_sep;
891 else if (name == "geometry")
892 handle_geometry(options);
894 else if (name == "subfig")
895 ; // ignore this FIXME: Use the package separator mechanism instead
897 else if ((where = is_known(name, known_languages)))
898 h_language = known_coded_languages[where - known_languages];
900 else if (name == "natbib") {
901 h_biblio_style = "plainnat";
902 h_cite_engine = "natbib";
903 h_cite_engine_type = "authoryear";
904 vector<string>::iterator it =
905 find(options.begin(), options.end(), "authoryear");
906 if (it != options.end())
909 it = find(options.begin(), options.end(), "numbers");
910 if (it != options.end()) {
911 h_cite_engine_type = "numerical";
917 else if (name == "jurabib") {
918 h_biblio_style = "jurabib";
919 h_cite_engine = "jurabib";
920 h_cite_engine_type = "authoryear";
923 else if (name == "hyperref")
924 handle_hyperref(options);
926 else if (!in_lyx_preamble) {
928 h_preamble << "\\usepackage{" << name << '}';
930 h_preamble << "\\usepackage[" << opts << "]{"
934 if (p.next_token().cat() == catNewline ||
935 (p.next_token().cat() == catSpace &&
936 p.next_next_token().cat() == catNewline))
940 // We need to do something with the options...
941 if (!options.empty())
942 cerr << "Ignoring options '" << join(options, ",")
943 << "' of package " << name << '.' << endl;
945 // remove the whitespace
950 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
953 Token t = p.get_token();
954 if (t.cat() == catEscape &&
955 is_known(t.cs(), known_if_commands))
956 handle_if(p, in_lyx_preamble);
958 if (!in_lyx_preamble)
959 h_preamble << t.asInput();
960 if (t.cat() == catEscape && t.cs() == "fi")
967 bool Preamble::writeLyXHeader(ostream & os, bool subdoc)
969 // set the quote language
970 // LyX only knows the following quotes languages:
971 // english, swedish, german, polish, french and danish
972 // (quotes for "japanese" and "chinese-traditional" are missing because
973 // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
974 // conversion list taken from
975 // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
976 // (quotes for kazakh and interlingua are unknown)
978 if (is_known(h_language, known_danish_quotes_languages))
979 h_quotes_language = "danish";
981 else if (is_known(h_language, known_french_quotes_languages))
982 h_quotes_language = "french";
984 else if (is_known(h_language, known_german_quotes_languages))
985 h_quotes_language = "german";
987 else if (is_known(h_language, known_polish_quotes_languages))
988 h_quotes_language = "polish";
990 else if (is_known(h_language, known_swedish_quotes_languages))
991 h_quotes_language = "swedish";
993 else if (is_known(h_language, known_english_quotes_languages))
994 h_quotes_language = "english";
996 if (contains(h_float_placement, "H"))
997 registerAutomaticallyLoadedPackage("float");
998 if (h_spacing != "single" && h_spacing != "default")
999 registerAutomaticallyLoadedPackage("setspace");
1000 if (h_use_packages["amsmath"] == "2") {
1001 // amsbsy and amstext are already provided by amsmath
1002 registerAutomaticallyLoadedPackage("amsbsy");
1003 registerAutomaticallyLoadedPackage("amstext");
1006 // output the LyX file settings
1007 os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
1008 << "\\lyxformat " << LYX_FORMAT << '\n'
1009 << "\\begin_document\n"
1010 << "\\begin_header\n"
1011 << "\\textclass " << h_textclass << "\n";
1012 string const raw = subdoc ? empty_string() : h_preamble.str();
1014 os << "\\begin_preamble\n";
1015 for (string::size_type i = 0; i < raw.size(); ++i) {
1016 if (raw[i] == package_beg_sep) {
1017 // Here follows some package loading code that
1018 // must be skipped if the package is loaded
1020 string::size_type j = raw.find(package_mid_sep, i);
1021 if (j == string::npos)
1023 string::size_type k = raw.find(package_end_sep, j);
1024 if (k == string::npos)
1026 string const package = raw.substr(i + 1, j - i - 1);
1027 string const replacement = raw.substr(j + 1, k - j - 1);
1028 if (auto_packages.find(package) == auto_packages.end())
1034 os << "\n\\end_preamble\n";
1036 if (!h_options.empty())
1037 os << "\\options " << h_options << "\n";
1038 os << "\\use_default_options " << h_use_default_options << "\n";
1039 if (!used_modules.empty()) {
1040 os << "\\begin_modules\n";
1041 vector<string>::const_iterator const end = used_modules.end();
1042 vector<string>::const_iterator it = used_modules.begin();
1043 for (; it != end; ++it)
1045 os << "\\end_modules\n";
1047 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1048 << "\\language " << h_language << "\n"
1049 << "\\language_package " << h_language_package << "\n"
1050 << "\\inputencoding " << h_inputencoding << "\n"
1051 << "\\fontencoding " << h_fontencoding << "\n"
1052 << "\\font_roman " << h_font_roman << "\n"
1053 << "\\font_sans " << h_font_sans << "\n"
1054 << "\\font_typewriter " << h_font_typewriter << "\n"
1055 << "\\font_math " << h_font_math << "\n"
1056 << "\\font_default_family " << h_font_default_family << "\n"
1057 << "\\use_non_tex_fonts " << h_use_non_tex_fonts << "\n"
1058 << "\\font_sc " << h_font_sc << "\n"
1059 << "\\font_osf " << h_font_osf << "\n"
1060 << "\\font_sf_scale " << h_font_sf_scale << "\n"
1061 << "\\font_tt_scale " << h_font_tt_scale << '\n';
1062 if (!h_font_cjk.empty())
1063 os << "\\font_cjk " << h_font_cjk << '\n';
1064 os << "\\graphics " << h_graphics << '\n'
1065 << "\\default_output_format " << h_default_output_format << "\n"
1066 << "\\output_sync " << h_output_sync << "\n";
1067 if (h_output_sync == "1")
1068 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1069 os << "\\bibtex_command " << h_bibtex_command << "\n"
1070 << "\\index_command " << h_index_command << "\n";
1071 if (!h_float_placement.empty())
1072 os << "\\float_placement " << h_float_placement << "\n";
1073 os << "\\paperfontsize " << h_paperfontsize << "\n"
1074 << "\\spacing " << h_spacing << "\n"
1075 << "\\use_hyperref " << h_use_hyperref << '\n';
1076 if (h_use_hyperref == "true") {
1077 if (!h_pdf_title.empty())
1078 os << "\\pdf_title \"" << h_pdf_title << "\"\n";
1079 if (!h_pdf_author.empty())
1080 os << "\\pdf_author \"" << h_pdf_author << "\"\n";
1081 if (!h_pdf_subject.empty())
1082 os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
1083 if (!h_pdf_keywords.empty())
1084 os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
1085 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1086 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1087 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1088 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1089 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1090 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1091 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1092 "\\pdf_backref " << h_pdf_backref << "\n"
1093 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1094 if (!h_pdf_pagemode.empty())
1095 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1096 if (!h_pdf_quoted_options.empty())
1097 os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
1099 os << "\\papersize " << h_papersize << "\n"
1100 << "\\use_geometry " << h_use_geometry << '\n';
1101 for (map<string, string>::const_iterator it = h_use_packages.begin();
1102 it != h_use_packages.end(); ++it)
1103 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1104 os << "\\cite_engine " << h_cite_engine << '\n'
1105 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1106 << "\\biblio_style " << h_biblio_style << "\n"
1107 << "\\use_bibtopic " << h_use_bibtopic << "\n"
1108 << "\\use_indices " << h_use_indices << "\n"
1109 << "\\paperorientation " << h_paperorientation << '\n'
1110 << "\\suppress_date " << h_suppress_date << '\n'
1111 << "\\justification " << h_justification << '\n'
1112 << "\\use_refstyle " << h_use_refstyle << '\n';
1113 if (!h_fontcolor.empty())
1114 os << "\\fontcolor " << h_fontcolor << '\n';
1115 if (!h_notefontcolor.empty())
1116 os << "\\notefontcolor " << h_notefontcolor << '\n';
1117 if (!h_backgroundcolor.empty())
1118 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1119 if (!h_boxbgcolor.empty())
1120 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1121 os << "\\index " << h_index << '\n'
1122 << "\\shortcut " << h_shortcut << '\n'
1123 << "\\color " << h_color << '\n'
1126 << "\\secnumdepth " << h_secnumdepth << "\n"
1127 << "\\tocdepth " << h_tocdepth << "\n"
1128 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1129 if (h_paragraph_separation == "skip")
1130 os << "\\defskip " << h_defskip << "\n";
1132 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1133 os << "\\quotes_language " << h_quotes_language << "\n"
1134 << "\\papercolumns " << h_papercolumns << "\n"
1135 << "\\papersides " << h_papersides << "\n"
1136 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1137 if (!h_listings_params.empty())
1138 os << "\\listings_params " << h_listings_params << "\n";
1139 os << "\\tracking_changes " << h_tracking_changes << "\n"
1140 << "\\output_changes " << h_output_changes << "\n"
1141 << "\\html_math_output " << h_html_math_output << "\n"
1142 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1143 << "\\html_be_strict " << h_html_be_strict << "\n"
1145 << "\\end_header\n\n"
1146 << "\\begin_body\n";
1151 void Preamble::parse(Parser & p, string const & forceclass,
1152 TeX2LyXDocClass & tc)
1154 // initialize fixed types
1155 special_columns['D'] = 3;
1156 bool is_full_document = false;
1157 bool is_lyx_file = false;
1158 bool in_lyx_preamble = false;
1160 // determine whether this is a full document or a fragment for inclusion
1162 Token const & t = p.get_token();
1164 if (t.cat() == catEscape && t.cs() == "documentclass") {
1165 is_full_document = true;
1171 while (is_full_document && p.good()) {
1172 Token const & t = p.get_token();
1175 cerr << "t: " << t << "\n";
1181 if (!in_lyx_preamble &&
1182 (t.cat() == catLetter ||
1183 t.cat() == catSuper ||
1184 t.cat() == catSub ||
1185 t.cat() == catOther ||
1186 t.cat() == catMath ||
1187 t.cat() == catActive ||
1188 t.cat() == catBegin ||
1189 t.cat() == catEnd ||
1190 t.cat() == catAlign ||
1191 t.cat() == catParameter))
1192 h_preamble << t.cs();
1194 else if (!in_lyx_preamble &&
1195 (t.cat() == catSpace || t.cat() == catNewline))
1196 h_preamble << t.asInput();
1198 else if (t.cat() == catComment) {
1199 static regex const islyxfile("%% LyX .* created this file");
1200 static regex const usercommands("User specified LaTeX commands");
1202 string const comment = t.asInput();
1204 // magically switch encoding default if it looks like XeLaTeX
1205 static string const magicXeLaTeX =
1206 "% This document must be compiled with XeLaTeX ";
1207 if (comment.size() > magicXeLaTeX.size()
1208 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1209 && h_inputencoding == "auto") {
1210 cerr << "XeLaTeX comment found, switching to UTF8\n";
1211 h_inputencoding = "utf8";
1214 if (regex_search(comment, sub, islyxfile)) {
1216 in_lyx_preamble = true;
1217 } else if (is_lyx_file
1218 && regex_search(comment, sub, usercommands))
1219 in_lyx_preamble = false;
1220 else if (!in_lyx_preamble)
1221 h_preamble << t.asInput();
1224 else if (t.cs() == "pagestyle")
1225 h_paperpagestyle = p.verbatim_item();
1227 else if (t.cs() == "setdefaultlanguage") {
1229 // We don't yet care about non-language variant options
1230 // because LyX doesn't support this yet, see bug #8214
1232 string langopts = p.getOpt();
1233 // check if the option contains a variant, if yes, extract it
1234 string::size_type pos_var = langopts.find("variant");
1235 string::size_type i = langopts.find(',', pos_var);
1236 string::size_type k = langopts.find('=', pos_var);
1237 if (pos_var != string::npos){
1239 if (i == string::npos)
1240 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1242 variant = langopts.substr(k + 1, i - k - 1);
1243 h_language = variant;
1247 h_language = p.verbatim_item();
1248 //finally translate the poyglossia name to a LyX name
1249 h_language = polyglossia2lyx(h_language);
1252 else if (t.cs() == "setotherlanguage") {
1253 // We don't yet care about the option because LyX doesn't
1254 // support this yet, see bug #8214
1255 p.hasOpt() ? p.getOpt() : string();
1259 else if (t.cs() == "setmainfont") {
1260 // we don't care about the option
1261 p.hasOpt() ? p.getOpt() : string();
1262 h_font_roman = p.getArg('{', '}');
1265 else if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1266 // LyX currently only supports the scale option
1269 string fontopts = p.getArg('[', ']');
1270 // check if the option contains a scaling, if yes, extract it
1271 string::size_type pos = fontopts.find("Scale");
1272 if (pos != string::npos) {
1273 string::size_type i = fontopts.find(',', pos);
1274 if (i == string::npos)
1275 scale_as_percentage(fontopts.substr(pos + 1), scale);
1277 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1280 if (t.cs() == "setsansfont") {
1282 h_font_sf_scale = scale;
1283 h_font_sans = p.getArg('{', '}');
1286 h_font_tt_scale = scale;
1287 h_font_typewriter = p.getArg('{', '}');
1291 else if (t.cs() == "date") {
1292 string argument = p.getArg('{', '}');
1293 if (argument.empty())
1294 h_suppress_date = "true";
1296 h_preamble << t.asInput() << '{' << argument << '}';
1299 else if (t.cs() == "color") {
1300 string const space =
1301 (p.hasOpt() ? p.getOpt() : string());
1302 string argument = p.getArg('{', '}');
1303 // check the case that a standard color is used
1304 if (space.empty() && is_known(argument, known_basic_colors)) {
1305 h_fontcolor = rgbcolor2code(argument);
1306 preamble.registerAutomaticallyLoadedPackage("color");
1307 } else if (space.empty() && argument == "document_fontcolor")
1308 preamble.registerAutomaticallyLoadedPackage("color");
1309 // check the case that LyX's document_fontcolor is defined
1310 // but not used for \color
1312 h_preamble << t.asInput();
1314 h_preamble << space;
1315 h_preamble << '{' << argument << '}';
1316 // the color might already be set because \definecolor
1317 // is parsed before this
1322 else if (t.cs() == "pagecolor") {
1323 string argument = p.getArg('{', '}');
1324 // check the case that a standard color is used
1325 if (is_known(argument, known_basic_colors)) {
1326 h_backgroundcolor = rgbcolor2code(argument);
1327 } else if (argument == "page_backgroundcolor")
1328 preamble.registerAutomaticallyLoadedPackage("color");
1329 // check the case that LyX's page_backgroundcolor is defined
1330 // but not used for \pagecolor
1332 h_preamble << t.asInput() << '{' << argument << '}';
1333 // the color might already be set because \definecolor
1334 // is parsed before this
1335 h_backgroundcolor = "";
1339 else if (t.cs() == "makeatletter") {
1340 // LyX takes care of this
1341 p.setCatcode('@', catLetter);
1344 else if (t.cs() == "makeatother") {
1345 // LyX takes care of this
1346 p.setCatcode('@', catOther);
1349 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1350 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1351 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1352 || t.cs() == "DeclareRobustCommand"
1353 || t.cs() == "DeclareRobustCommandx"
1354 || t.cs() == "ProvideTextCommandDefault"
1355 || t.cs() == "DeclareMathAccent") {
1357 if (p.next_token().character() == '*') {
1361 string const name = p.verbatim_item();
1362 string const opt1 = p.getFullOpt();
1363 string const opt2 = p.getFullOpt();
1364 string const body = p.verbatim_item();
1365 // store the in_lyx_preamble setting
1366 bool const was_in_lyx_preamble = in_lyx_preamble;
1368 if (name == "\\rmdefault")
1369 if (is_known(body, known_roman_fonts)) {
1370 h_font_roman = body;
1372 in_lyx_preamble = true;
1374 if (name == "\\sfdefault")
1375 if (is_known(body, known_sans_fonts)) {
1378 in_lyx_preamble = true;
1380 if (name == "\\ttdefault")
1381 if (is_known(body, known_typewriter_fonts)) {
1382 h_font_typewriter = body;
1384 in_lyx_preamble = true;
1386 if (name == "\\familydefault") {
1387 string family = body;
1388 // remove leading "\"
1389 h_font_default_family = family.erase(0,1);
1391 in_lyx_preamble = true;
1394 if (name == "\\bfdefault")
1395 // LyX re-adds this if a kurier font is used
1396 if (is_known(h_font_sans, known_kurier_fonts) && body == "b") {
1398 in_lyx_preamble = true;
1401 // remove the lyxdot definition that is re-added by LyX
1403 if (name == "\\lyxdot") {
1405 in_lyx_preamble = true;
1408 // Add the command to the known commands
1409 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1411 // only non-lyxspecific stuff
1412 if (!in_lyx_preamble) {
1414 ss << '\\' << t.cs();
1417 ss << '{' << name << '}' << opt1 << opt2
1418 << '{' << body << "}";
1419 h_preamble << ss.str();
1421 ostream & out = in_preamble ? h_preamble : os;
1422 out << "\\" << t.cs() << "{" << name << "}"
1423 << opts << "{" << body << "}";
1426 // restore the in_lyx_preamble setting
1427 in_lyx_preamble = was_in_lyx_preamble;
1430 else if (t.cs() == "edef"){
1431 // we only support this for kurier fonts
1432 string const command = p.next_token().asInput();
1434 if (command == "\\sfdefault") {
1436 if (h_font_sans == "kurier")
1437 h_font_sans = "kurier-condensed";
1438 if (h_font_sans == "kurierl")
1439 h_font_sans = "kurier-light-condensed";
1443 h_preamble << "\\edef" << command << "{" << p.getArg('{', '}') << "}\n";
1446 else if (t.cs() == "documentclass") {
1447 vector<string>::iterator it;
1448 vector<string> opts = split_options(p.getArg('[', ']'));
1449 handle_opt(opts, known_fontsizes, h_paperfontsize);
1450 delete_opt(opts, known_fontsizes);
1451 // delete "pt" at the end
1452 string::size_type i = h_paperfontsize.find("pt");
1453 if (i != string::npos)
1454 h_paperfontsize.erase(i);
1455 // The documentclass options are always parsed before the options
1456 // of the babel call so that a language cannot overwrite the babel
1458 handle_opt(opts, known_languages, h_language);
1459 delete_opt(opts, known_languages);
1461 // paper orientation
1462 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1463 h_paperorientation = "landscape";
1467 if ((it = find(opts.begin(), opts.end(), "oneside"))
1472 if ((it = find(opts.begin(), opts.end(), "twoside"))
1478 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1480 h_papercolumns = "1";
1483 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1485 h_papercolumns = "2";
1489 // some size options are known to any document classes, other sizes
1490 // are handled by the \geometry command of the geometry package
1491 handle_opt(opts, known_class_paper_sizes, h_papersize);
1492 delete_opt(opts, known_class_paper_sizes);
1493 // the remaining options
1494 h_options = join(opts, ",");
1495 // FIXME This does not work for classes that have a
1496 // different name in LyX than in LaTeX
1497 h_textclass = p.getArg('{', '}');
1501 else if (t.cs() == "usepackage") {
1502 string const options = p.getArg('[', ']');
1503 string const name = p.getArg('{', '}');
1504 vector<string> vecnames;
1505 split(name, vecnames, ',');
1506 vector<string>::const_iterator it = vecnames.begin();
1507 vector<string>::const_iterator end = vecnames.end();
1508 for (; it != end; ++it)
1509 handle_package(p, trimSpaceAndEol(*it), options,
1513 else if (t.cs() == "inputencoding") {
1514 string const encoding = p.getArg('{','}');
1515 h_inputencoding = encoding;
1516 p.setEncoding(encoding, Encoding::inputenc);
1519 else if (t.cs() == "newenvironment") {
1520 string const name = p.getArg('{', '}');
1521 string const opt1 = p.getFullOpt();
1522 string const opt2 = p.getFullOpt();
1523 string const beg = p.verbatim_item();
1524 string const end = p.verbatim_item();
1525 if (!in_lyx_preamble) {
1526 h_preamble << "\\newenvironment{" << name
1527 << '}' << opt1 << opt2 << '{'
1528 << beg << "}{" << end << '}';
1530 add_known_environment(name, opt1, !opt2.empty(),
1531 from_utf8(beg), from_utf8(end));
1535 else if (t.cs() == "newtheorem") {
1536 string const name = p.getArg('{', '}');
1537 string const opt1 = p.getFullOpt();
1538 string const opt2 = p.getFullOpt();
1539 string const body = p.verbatim_item();
1540 string const opt3 = p.getFullOpt();
1542 add_known_theorem(name, opt1, !opt2.empty(),
1543 from_utf8("\\newtheorem{" + name + '}' +
1544 opt1 + opt2 + '{' + body + '}' + opt3));
1546 if (!in_lyx_preamble)
1547 h_preamble << "\\newtheorem{" << name << '}'
1548 << opt1 << opt2 << '{' << '}' << opt3;
1551 else if (t.cs() == "def") {
1552 string name = p.get_token().cs();
1553 // In fact, name may be more than the name:
1554 // In the test case of bug 8116
1555 // name == "csname SF@gobble@opt \endcsname".
1556 // Therefore, we need to use asInput() instead of cs().
1557 while (p.next_token().cat() != catBegin)
1558 name += p.get_token().asInput();
1559 if (!in_lyx_preamble)
1560 h_preamble << "\\def\\" << name << '{'
1561 << p.verbatim_item() << "}";
1564 else if (t.cs() == "newcolumntype") {
1565 string const name = p.getArg('{', '}');
1566 trimSpaceAndEol(name);
1568 string opts = p.getOpt();
1569 if (!opts.empty()) {
1570 istringstream is(string(opts, 1));
1573 special_columns[name[0]] = nargs;
1574 h_preamble << "\\newcolumntype{" << name << "}";
1576 h_preamble << "[" << nargs << "]";
1577 h_preamble << "{" << p.verbatim_item() << "}";
1580 else if (t.cs() == "setcounter") {
1581 string const name = p.getArg('{', '}');
1582 string const content = p.getArg('{', '}');
1583 if (name == "secnumdepth")
1584 h_secnumdepth = content;
1585 else if (name == "tocdepth")
1586 h_tocdepth = content;
1588 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1591 else if (t.cs() == "setlength") {
1592 string const name = p.verbatim_item();
1593 string const content = p.verbatim_item();
1594 // the paragraphs are only not indented when \parindent is set to zero
1595 if (name == "\\parindent" && content != "") {
1596 if (content[0] == '0')
1597 h_paragraph_separation = "skip";
1599 h_paragraph_indentation = translate_len(content);
1600 } else if (name == "\\parskip") {
1601 if (content == "\\smallskipamount")
1602 h_defskip = "smallskip";
1603 else if (content == "\\medskipamount")
1604 h_defskip = "medskip";
1605 else if (content == "\\bigskipamount")
1606 h_defskip = "bigskip";
1608 h_defskip = content;
1610 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1613 else if (t.cs() == "onehalfspacing")
1614 h_spacing = "onehalf";
1616 else if (t.cs() == "doublespacing")
1617 h_spacing = "double";
1619 else if (t.cs() == "setstretch")
1620 h_spacing = "other " + p.verbatim_item();
1622 else if (t.cs() == "synctex") {
1623 // the scheme is \synctex=value
1624 // where value can only be "1" or "-1"
1625 h_output_sync = "1";
1626 // there can be any character behind the value (e.g. a linebreak or a '\'
1627 // therefore we extract it char by char
1629 string value = p.get_token().asInput();
1631 value += p.get_token().asInput();
1632 h_output_sync_macro = "\\synctex=" + value;
1635 else if (t.cs() == "begin") {
1636 string const name = p.getArg('{', '}');
1637 if (name == "document")
1639 h_preamble << "\\begin{" << name << "}";
1642 else if (t.cs() == "geometry") {
1643 vector<string> opts = split_options(p.getArg('{', '}'));
1644 handle_geometry(opts);
1647 else if (t.cs() == "definecolor") {
1648 string const color = p.getArg('{', '}');
1649 string const space = p.getArg('{', '}');
1650 string const value = p.getArg('{', '}');
1651 if (color == "document_fontcolor" && space == "rgb") {
1652 RGBColor c(RGBColorFromLaTeX(value));
1653 h_fontcolor = X11hexname(c);
1654 } else if (color == "note_fontcolor" && space == "rgb") {
1655 RGBColor c(RGBColorFromLaTeX(value));
1656 h_notefontcolor = X11hexname(c);
1657 } else if (color == "page_backgroundcolor" && space == "rgb") {
1658 RGBColor c(RGBColorFromLaTeX(value));
1659 h_backgroundcolor = X11hexname(c);
1660 } else if (color == "shadecolor" && space == "rgb") {
1661 RGBColor c(RGBColorFromLaTeX(value));
1662 h_boxbgcolor = X11hexname(c);
1664 h_preamble << "\\definecolor{" << color
1665 << "}{" << space << "}{" << value
1670 else if (t.cs() == "bibliographystyle")
1671 h_biblio_style = p.verbatim_item();
1673 else if (t.cs() == "jurabibsetup") {
1674 // FIXME p.getArg('{', '}') is most probably wrong (it
1675 // does not handle nested braces).
1676 // Use p.verbatim_item() instead.
1677 vector<string> jurabibsetup =
1678 split_options(p.getArg('{', '}'));
1679 // add jurabibsetup to the jurabib package options
1680 add_package("jurabib", jurabibsetup);
1681 if (!jurabibsetup.empty()) {
1682 h_preamble << "\\jurabibsetup{"
1683 << join(jurabibsetup, ",") << '}';
1687 else if (t.cs() == "hypersetup") {
1688 vector<string> hypersetup =
1689 split_options(p.verbatim_item());
1690 // add hypersetup to the hyperref package options
1691 handle_hyperref(hypersetup);
1692 if (!hypersetup.empty()) {
1693 h_preamble << "\\hypersetup{"
1694 << join(hypersetup, ",") << '}';
1698 else if (is_known(t.cs(), known_if_3arg_commands)) {
1699 // prevent misparsing of \usepackage if it is used
1700 // as an argument (see e.g. our own output of
1701 // \@ifundefined above)
1702 string const arg1 = p.verbatim_item();
1703 string const arg2 = p.verbatim_item();
1704 string const arg3 = p.verbatim_item();
1705 // test case \@ifundefined{date}{}{\date{}}
1706 if (t.cs() == "@ifundefined" && arg1 == "date" &&
1707 arg2.empty() && arg3 == "\\date{}") {
1708 h_suppress_date = "true";
1709 // older tex2lyx versions did output
1710 // \@ifundefined{definecolor}{\usepackage{color}}{}
1711 } else if (t.cs() == "@ifundefined" &&
1712 arg1 == "definecolor" &&
1713 arg2 == "\\usepackage{color}" &&
1715 if (!in_lyx_preamble)
1716 h_preamble << package_beg_sep
1719 << "\\@ifundefined{definecolor}{color}{}"
1722 //\@ifundefined{showcaptionsetup}{}{%
1723 // \PassOptionsToPackage{caption=false}{subfig}}
1724 // that LyX uses for subfloats
1725 } else if (t.cs() == "@ifundefined" &&
1726 arg1 == "showcaptionsetup" && arg2.empty()
1727 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1729 } else if (!in_lyx_preamble) {
1730 h_preamble << t.asInput()
1731 << '{' << arg1 << '}'
1732 << '{' << arg2 << '}'
1733 << '{' << arg3 << '}';
1737 else if (is_known(t.cs(), known_if_commands)) {
1738 // must not parse anything in conditional code, since
1739 // LyX would output the parsed contents unconditionally
1740 if (!in_lyx_preamble)
1741 h_preamble << t.asInput();
1742 handle_if(p, in_lyx_preamble);
1745 else if (!t.cs().empty() && !in_lyx_preamble)
1746 h_preamble << '\\' << t.cs();
1749 // remove the whitespace
1752 // Force textclass if the user wanted it
1753 if (!forceclass.empty())
1754 h_textclass = forceclass;
1755 tc.setName(h_textclass);
1757 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1760 if (h_papersides.empty()) {
1763 h_papersides = ss.str();
1766 // If the CJK package is used we cannot set the document language from
1767 // the babel options. Instead, we guess which language is used most
1768 // and set this one.
1769 default_language = h_language;
1770 if (is_full_document &&
1771 (auto_packages.find("CJK") != auto_packages.end() ||
1772 auto_packages.find("CJKutf8") != auto_packages.end())) {
1774 h_language = guessLanguage(p, default_language);
1780 string babel2lyx(string const & language)
1782 char const * const * where = is_known(language, known_languages);
1784 return known_coded_languages[where - known_languages];
1789 string Preamble::polyglossia2lyx(string const & language)
1791 char const * const * where = is_known(language, polyglossia_languages);
1793 return coded_polyglossia_languages[where - polyglossia_languages];
1798 string rgbcolor2code(string const & name)
1800 char const * const * where = is_known(name, known_basic_colors);
1802 // "red", "green" etc
1803 return known_basic_color_codes[where - known_basic_colors];
1805 // "255,0,0", "0,255,0" etc
1806 RGBColor c(RGBColorFromLaTeX(name));
1807 return X11hexname(c);