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", "tgbonum",
129 "tgchorus", "tgpagella", "tgschola", "tgtermes", "utopia", 0};
131 const char * const known_sans_fonts[] = { "avant", "berasans", "cmbr", "cmss",
132 "helvet", "kurier", "kurierl", "lmss", "tgadventor", "tgheros", 0};
134 const char * const known_kurier_fonts[] = { "kurier", "kurierl", "kurier-condensed",
135 "kurier-light-condensed", 0};
137 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
138 "courier", "lmtt", "luximono", "fourier", "lmodern", "mathpazo", "mathptmx",
139 "newcent", "tgcursor", 0};
141 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
142 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
143 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
144 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
145 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
147 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
148 "executivepaper", "legalpaper", "letterpaper", 0};
150 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
151 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
153 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
154 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
157 /// commands that can start an \if...\else...\endif sequence
158 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
159 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
160 "ifsidecap", "ifupgreek", 0};
162 const char * const known_basic_colors[] = {"blue", "black", "cyan", "green",
163 "magenta", "red", "white", "yellow", 0};
165 const char * const known_basic_color_codes[] = {"#0000ff", "#000000", "#00ffff", "#00ff00",
166 "#ff00ff", "#ff0000", "#ffffff", "#ffff00", 0};
168 /// conditional commands with three arguments like \@ifundefined{}{}{}
169 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
172 /// packages that work only in xetex
173 /// polyglossia is handled separately
174 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
175 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
176 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
178 /// packages that are automatically skipped if loaded by LyX
179 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
180 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color", "float",
181 "fontspec", "graphicx", "hhline", "ifthen", "longtable", "makeidx", "multirow",
182 "nomencl", "pdfpages", "rotating", "rotfloat", "splitidx", "setspace",
183 "subscript", "textcomp", "ulem", "url", "varioref", "verbatim", "wrapfig",
186 // codes used to remove packages that are loaded automatically by LyX.
187 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
188 const char package_beg_sep = '\001';
189 const char package_mid_sep = '\002';
190 const char package_end_sep = '\003';
193 // returns true if at least one of the options in what has been found
194 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
200 // the last language option is the document language (for babel and LyX)
201 // the last size option is the document font size
202 vector<string>::iterator it;
203 vector<string>::iterator position = opts.begin();
204 for (; *what; ++what) {
205 it = find(opts.begin(), opts.end(), *what);
206 if (it != opts.end()) {
207 if (it >= position) {
218 void delete_opt(vector<string> & opts, char const * const * what)
223 // remove found options from the list
224 // do this after handle_opt to avoid potential memory leaks
225 vector<string>::iterator it;
226 for (; *what; ++what) {
227 it = find(opts.begin(), opts.end(), *what);
228 if (it != opts.end())
235 * Split a package options string (keyval format) into a vector.
237 * authorformat=smallcaps,
239 * titleformat=colonsep,
240 * bibformat={tabular,ibidem,numbered}
242 vector<string> split_options(string const & input)
244 vector<string> options;
248 Token const & t = p.get_token();
249 if (t.asInput() == ",") {
250 options.push_back(trimSpaceAndEol(option));
252 } else if (t.asInput() == "=") {
255 if (p.next_token().asInput() == "{")
256 option += '{' + p.getArg('{', '}') + '}';
257 } else if (t.cat() != catSpace)
258 option += t.asInput();
262 options.push_back(trimSpaceAndEol(option));
269 * Retrieve a keyval option "name={value with=sign}" named \p name from
270 * \p options and return the value.
271 * The found option is also removed from \p options.
273 string process_keyval_opt(vector<string> & options, string name)
275 for (size_t i = 0; i < options.size(); ++i) {
276 vector<string> option;
277 split(options[i], option, '=');
278 if (option.size() < 2)
280 if (option[0] == name) {
281 options.erase(options.begin() + i);
282 option.erase(option.begin());
283 return join(option, "=");
289 } // anonymous namespace
293 * known polyglossia language names (including variants)
295 const char * const Preamble::polyglossia_languages[] = {
296 "albanian", "croatian", "hebrew", "norsk", "swedish", "amharic", "czech", "hindi",
297 "nynorsk", "syriac", "arabic", "danish", "icelandic", "occitan", "tamil",
298 "armenian", "divehi", "interlingua", "polish", "telugu", "asturian", "dutch",
299 "irish", "portuges", "thai", "bahasai", "english", "italian", "romanian", "turkish",
300 "bahasam", "esperanto", "lao", "russian", "turkmen", "basque", "estonian", "latin",
301 "samin", "ukrainian", "bengali", "farsi", "latvian", "sanskrit", "urdu", "brazil",
302 "brazilian", "finnish", "lithuanian", "scottish", "usorbian", "breton", "french",
303 "lsorbian", "serbian", "vietnamese", "bulgarian", "galician", "magyar", "slovak",
304 "welsh", "catalan", "german", "malayalam", "slovenian", "coptic", "greek",
305 "marathi", "spanish",
306 "american", "ancient", "australian", "british", "monotonic", "newzealand",
310 * the same as polyglossia_languages with .lyx names
311 * please keep this in sync with polyglossia_languages line by line!
313 const char * const Preamble::coded_polyglossia_languages[] = {
314 "albanian", "croatian", "hebrew", "norsk", "swedish", "amharic", "czech", "hindi",
315 "nynorsk", "syriac", "arabic_arabi", "danish", "icelandic", "occitan", "tamil",
316 "armenian", "divehi", "interlingua", "polish", "telugu", "asturian", "dutch",
317 "irish", "portuges", "thai", "bahasa", "english", "italian", "romanian", "turkish",
318 "bahasam", "esperanto", "lao", "russian", "turkmen", "basque", "estonian", "latin",
319 "samin", "ukrainian", "bengali", "farsi", "latvian", "sanskrit", "urdu", "brazilian",
320 "brazilian", "finnish", "lithuanian", "scottish", "uppersorbian", "breton", "french",
321 "lowersorbian", "serbian", "vietnamese", "bulgarian", "galician", "magyar", "slovak",
322 "welsh", "catalan", "ngerman", "malayalam", "slovene", "coptic", "greek",
323 "marathi", "spanish",
324 "american", "ancientgreek", "australian", "british", "greek", "newzealand",
325 "polutonikogreek", 0};
328 bool Preamble::indentParagraphs() const
330 return h_paragraph_separation == "indent";
334 bool Preamble::isPackageUsed(string const & package) const
336 return used_packages.find(package) != used_packages.end();
340 vector<string> Preamble::getPackageOptions(string const & package) const
342 map<string, vector<string> >::const_iterator it = used_packages.find(package);
343 if (it != used_packages.end())
345 return vector<string>();
349 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
351 auto_packages.insert(package);
355 void Preamble::addModule(string const & module)
357 used_modules.push_back(module);
361 void Preamble::suppressDate(bool suppress)
364 h_suppress_date = "true";
366 h_suppress_date = "false";
370 void Preamble::registerAuthor(std::string const & name)
372 Author author(from_utf8(name), empty_docstring());
373 author.setUsed(true);
374 authors_.record(author);
375 h_tracking_changes = "true";
376 h_output_changes = "true";
380 Author const & Preamble::getAuthor(std::string const & name) const
382 Author author(from_utf8(name), empty_docstring());
383 for (AuthorList::Authors::const_iterator it = authors_.begin();
384 it != authors_.end(); ++it)
387 static Author const dummy;
392 void Preamble::add_package(string const & name, vector<string> & options)
394 // every package inherits the global options
395 if (used_packages.find(name) == used_packages.end())
396 used_packages[name] = split_options(h_options);
398 vector<string> & v = used_packages[name];
399 v.insert(v.end(), options.begin(), options.end());
400 if (name == "jurabib") {
401 // Don't output the order argument (see the cite command
402 // handling code in text.cpp).
403 vector<string>::iterator end =
404 remove(options.begin(), options.end(), "natbiborder");
405 end = remove(options.begin(), end, "jurabiborder");
406 options.erase(end, options.end());
413 // Given is a string like "scaled=0.9" or "Scale=0.9", return 0.9 * 100
414 bool scale_as_percentage(string const & scale, string & percentage)
416 string::size_type pos = scale.find('=');
417 if (pos != string::npos) {
418 string value = scale.substr(pos + 1);
419 if (isStrDbl(value)) {
420 percentage = convert<string>(100 * convert<double>(value));
428 string remove_braces(string const & value)
432 if (value[0] == '{' && value[value.length()-1] == '}')
433 return value.substr(1, value.length()-2);
437 } // anonymous namespace
440 Preamble::Preamble() : one_language(true), title_layout_found(false),
441 h_font_cjk_set(false)
445 h_biblio_style = "plain";
446 h_bibtex_command = "default";
447 h_cite_engine = "basic";
448 h_cite_engine_type = "numerical";
450 h_defskip = "medskip";
453 h_fontencoding = "default";
454 h_font_roman = "default";
455 h_font_sans = "default";
456 h_font_typewriter = "default";
457 h_font_math = "auto";
458 h_font_default_family = "default";
459 h_use_non_tex_fonts = "false";
461 h_font_osf = "false";
462 h_font_sf_scale = "100";
463 h_font_tt_scale = "100";
465 h_graphics = "default";
466 h_default_output_format = "default";
467 h_html_be_strict = "false";
468 h_html_css_as_file = "0";
469 h_html_math_output = "0";
471 h_index_command = "default";
472 h_inputencoding = "auto";
473 h_justification = "true";
474 h_language = "english";
475 h_language_package = "none";
477 h_maintain_unincluded_children = "false";
481 h_output_changes = "false";
483 //h_output_sync_macro
484 h_papercolumns = "1";
485 h_paperfontsize = "default";
486 h_paperorientation = "portrait";
487 h_paperpagestyle = "default";
489 h_papersize = "default";
490 h_paragraph_indentation = "default";
491 h_paragraph_separation = "indent";
496 h_pdf_bookmarks = "1";
497 h_pdf_bookmarksnumbered = "0";
498 h_pdf_bookmarksopen = "0";
499 h_pdf_bookmarksopenlevel = "1";
500 h_pdf_breaklinks = "0";
501 h_pdf_pdfborder = "0";
502 h_pdf_colorlinks = "0";
503 h_pdf_backref = "section";
504 h_pdf_pdfusetitle = "1";
506 //h_pdf_quoted_options;
507 h_quotes_language = "english";
510 h_spacing = "single";
511 h_suppress_date = "false";
512 h_textclass = "article";
514 h_tracking_changes = "false";
515 h_use_bibtopic = "false";
516 h_use_indices = "false";
517 h_use_geometry = "false";
518 h_use_default_options = "false";
519 h_use_hyperref = "false";
520 h_use_refstyle = "0";
521 h_use_packages["amsmath"] = "1";
522 h_use_packages["amssymb"] = "0";
523 h_use_packages["esint"] = "1";
524 h_use_packages["mhchem"] = "0";
525 h_use_packages["mathdots"] = "0";
526 h_use_packages["mathtools"] = "0";
527 h_use_packages["stackrel"] = "0";
528 h_use_packages["stmaryrd"] = "0";
529 h_use_packages["undertilde"] = "0";
533 void Preamble::handle_hyperref(vector<string> & options)
535 // FIXME swallow inputencoding changes that might surround the
536 // hyperref setup if it was written by LyX
537 h_use_hyperref = "true";
538 // swallow "unicode=true", since LyX does always write that
539 vector<string>::iterator it =
540 find(options.begin(), options.end(), "unicode=true");
541 if (it != options.end())
543 it = find(options.begin(), options.end(), "pdfusetitle");
544 if (it != options.end()) {
545 h_pdf_pdfusetitle = "1";
548 string bookmarks = process_keyval_opt(options, "bookmarks");
549 if (bookmarks == "true")
550 h_pdf_bookmarks = "1";
551 else if (bookmarks == "false")
552 h_pdf_bookmarks = "0";
553 if (h_pdf_bookmarks == "1") {
554 string bookmarksnumbered =
555 process_keyval_opt(options, "bookmarksnumbered");
556 if (bookmarksnumbered == "true")
557 h_pdf_bookmarksnumbered = "1";
558 else if (bookmarksnumbered == "false")
559 h_pdf_bookmarksnumbered = "0";
560 string bookmarksopen =
561 process_keyval_opt(options, "bookmarksopen");
562 if (bookmarksopen == "true")
563 h_pdf_bookmarksopen = "1";
564 else if (bookmarksopen == "false")
565 h_pdf_bookmarksopen = "0";
566 if (h_pdf_bookmarksopen == "1") {
567 string bookmarksopenlevel =
568 process_keyval_opt(options, "bookmarksopenlevel");
569 if (!bookmarksopenlevel.empty())
570 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
573 string breaklinks = process_keyval_opt(options, "breaklinks");
574 if (breaklinks == "true")
575 h_pdf_breaklinks = "1";
576 else if (breaklinks == "false")
577 h_pdf_breaklinks = "0";
578 string pdfborder = process_keyval_opt(options, "pdfborder");
579 if (pdfborder == "{0 0 0}")
580 h_pdf_pdfborder = "1";
581 else if (pdfborder == "{0 0 1}")
582 h_pdf_pdfborder = "0";
583 string backref = process_keyval_opt(options, "backref");
584 if (!backref.empty())
585 h_pdf_backref = backref;
586 string colorlinks = process_keyval_opt(options, "colorlinks");
587 if (colorlinks == "true")
588 h_pdf_colorlinks = "1";
589 else if (colorlinks == "false")
590 h_pdf_colorlinks = "0";
591 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
592 if (!pdfpagemode.empty())
593 h_pdf_pagemode = pdfpagemode;
594 string pdftitle = process_keyval_opt(options, "pdftitle");
595 if (!pdftitle.empty()) {
596 h_pdf_title = remove_braces(pdftitle);
598 string pdfauthor = process_keyval_opt(options, "pdfauthor");
599 if (!pdfauthor.empty()) {
600 h_pdf_author = remove_braces(pdfauthor);
602 string pdfsubject = process_keyval_opt(options, "pdfsubject");
603 if (!pdfsubject.empty())
604 h_pdf_subject = remove_braces(pdfsubject);
605 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
606 if (!pdfkeywords.empty())
607 h_pdf_keywords = remove_braces(pdfkeywords);
608 if (!options.empty()) {
609 if (!h_pdf_quoted_options.empty())
610 h_pdf_quoted_options += ',';
611 h_pdf_quoted_options += join(options, ",");
617 void Preamble::handle_geometry(vector<string> & options)
619 h_use_geometry = "true";
620 vector<string>::iterator it;
622 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
623 h_paperorientation = "landscape";
627 // keyval version: "paper=letter"
628 string paper = process_keyval_opt(options, "paper");
630 h_papersize = paper + "paper";
631 // alternative version: "letterpaper"
632 handle_opt(options, known_paper_sizes, h_papersize);
633 delete_opt(options, known_paper_sizes);
635 char const * const * margin = known_paper_margins;
636 for (; *margin; ++margin) {
637 string value = process_keyval_opt(options, *margin);
638 if (!value.empty()) {
639 int k = margin - known_paper_margins;
640 string name = known_coded_paper_margins[k];
641 h_margins += '\\' + name + ' ' + value + '\n';
647 void Preamble::handle_package(Parser &p, string const & name,
648 string const & opts, bool in_lyx_preamble)
650 vector<string> options = split_options(opts);
651 add_package(name, options);
652 char const * const * where = 0;
654 if (is_known(name, known_xetex_packages)) {
656 h_use_non_tex_fonts = "true";
657 registerAutomaticallyLoadedPackage("fontspec");
658 if (h_inputencoding == "auto")
659 p.setEncoding("UTF-8");
663 if (is_known(name, known_roman_fonts))
666 if (name == "fourier") {
667 h_font_roman = "utopia";
668 // when font uses real small capitals
669 if (opts == "expert")
673 if (name == "garamondx") {
674 h_font_roman = "garamondx";
679 if (name == "libertine-type1") {
680 h_font_roman = "libertine";
681 if (opts == "lining")
682 h_font_osf = "false";
687 if (name == "mathdesign") {
688 if (opts.find("charter") != string::npos)
689 h_font_roman = "md-charter";
690 if (opts.find("garamond") != string::npos)
691 h_font_roman = "md-garamond";
692 if (opts.find("utopia") != string::npos)
693 h_font_roman = "md-utopia";
694 if (opts.find("expert") != string::npos) {
700 else if (name == "mathpazo")
701 h_font_roman = "palatino";
703 else if (name == "mathptmx")
704 h_font_roman = "times";
707 if (is_known(name, known_sans_fonts)) {
709 if (options.size() == 1) {
710 if (scale_as_percentage(opts, h_font_sf_scale))
716 if (is_known(name, known_typewriter_fonts)) {
717 // fourier can be set as roman font _only_
718 // fourier as typewriter is handled in handling of \ttdefault
719 if (name != "fourier") {
720 h_font_typewriter = name;
721 if (options.size() == 1) {
722 if (scale_as_percentage(opts, h_font_tt_scale))
728 // font uses old-style figure
732 if (name == "refstyle")
733 h_use_refstyle = "1";
735 // after the detection and handling of special cases, we can remove the
736 // fonts, otherwise they would appear in the preamble, see bug #7856
737 if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
738 || is_known(name, known_typewriter_fonts))
741 else if (name == "amsmath" || name == "amssymb" ||
742 name == "esint" || name == "mhchem" || name == "mathdots" ||
743 name == "mathtools" || name == "stackrel" ||
744 name == "stmaryrd" || name == "undertilde")
745 h_use_packages[name] = "2";
747 else if (name == "babel") {
748 h_language_package = "default";
749 // One might think we would have to do nothing if babel is loaded
750 // without any options to prevent pollution of the preamble with this
751 // babel call in every roundtrip.
752 // But the user could have defined babel-specific things afterwards. So
753 // we need to keep it in the preamble to prevent cases like bug #7861.
755 // check if more than one option was used - used later for inputenc
756 if (options.begin() != options.end() - 1)
757 one_language = false;
758 // babel takes the last language of the option of its \usepackage
759 // call as document language. If there is no such language option, the
760 // last language in the documentclass options is used.
761 handle_opt(options, known_languages, h_language);
762 // translate the babel name to a LyX name
763 h_language = babel2lyx(h_language);
764 if (h_language == "japanese") {
765 // For Japanese, the encoding isn't indicated in the source
766 // file, and there's really not much we can do. We could
767 // 1) offer a list of possible encodings to choose from, or
768 // 2) determine the encoding of the file by inspecting it.
769 // For the time being, we leave the encoding alone so that
770 // we don't get iconv errors when making a wrong guess, and
771 // we will output a note at the top of the document
772 // explaining what to do.
773 Encoding const * const enc = encodings.fromIconvName(
774 p.getEncoding(), Encoding::japanese, false);
776 h_inputencoding = enc->name();
777 is_nonCJKJapanese = true;
778 // in this case babel can be removed from the preamble
779 registerAutomaticallyLoadedPackage("babel");
781 // If babel is called with options, LyX puts them by default into the
782 // document class options. This works for most languages, except
783 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
784 // perhaps in future others.
785 // Therefore keep the babel call as it is as the user might have
787 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
789 delete_opt(options, known_languages);
792 h_preamble << "\\usepackage{babel}\n";
795 else if (name == "polyglossia") {
796 h_language_package = "default";
797 h_default_output_format = "pdf4";
798 h_use_non_tex_fonts = "true";
800 registerAutomaticallyLoadedPackage("xunicode");
801 if (h_inputencoding == "auto")
802 p.setEncoding("UTF-8");
805 else if (name == "CJK") {
806 // set the encoding to "auto" because it might be set to "default" by the babel handling
807 // and this would not be correct for CJK
808 if (h_inputencoding == "default")
809 h_inputencoding = "auto";
810 registerAutomaticallyLoadedPackage("CJK");
813 else if (name == "CJKutf8") {
814 h_inputencoding = "UTF8";
815 p.setEncoding("UTF-8");
816 registerAutomaticallyLoadedPackage("CJKutf8");
819 else if (name == "fontenc") {
820 h_fontencoding = getStringFromVector(options, ",");
821 /* We could do the following for better round trip support,
822 * but this makes the document less portable, so I skip it:
823 if (h_fontencoding == lyxrc.fontenc)
824 h_fontencoding = "global";
829 else if (name == "inputenc" || name == "luainputenc") {
830 // h_inputencoding is only set when there is not more than one
831 // inputenc option because otherwise h_inputencoding must be
832 // set to "auto" (the default encoding of the document language)
833 // Therefore check for the "," character.
834 // It is also only set when there is not more than one babel
837 if (opts.find(",") == string::npos && one_language == true) {
838 h_inputencoding = opts;
839 // FIXME: if this line is used, tex2lyx swallows the next character
840 // in the file behind "{inputenc}"
841 //p.setEncoding(opts);
843 h_preamble << "\\usepackage[" << opts << "}{" << name << "}\n";
844 // FIXME: enabling this introduces bug #8525
845 //p.setEncoding(options.back(), Encoding::inputenc);
849 h_preamble << "\\usepackage{" << name << "}\n";
852 else if (name == "srcltx") {
855 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
858 h_output_sync_macro = "\\usepackage{srcltx}";
861 else if (is_known(name, known_old_language_packages)) {
862 // known language packages from the times before babel
863 // if they are found and not also babel, they will be used as
864 // custom language package
865 h_language_package = "\\usepackage{" + name + "}";
868 else if (name == "prettyref")
869 ; // ignore this FIXME: Use the package separator mechanism instead
871 else if (name == "lyxskak") {
872 // ignore this and its options
873 const char * const o[] = {"ps", "mover", 0};
874 delete_opt(options, o);
877 else if (is_known(name, known_lyx_packages) && options.empty()) {
878 if (name == "splitidx")
879 h_use_indices = "true";
880 if (!in_lyx_preamble) {
881 h_preamble << package_beg_sep << name
882 << package_mid_sep << "\\usepackage{"
884 if (p.next_token().cat() == catNewline ||
885 (p.next_token().cat() == catSpace &&
886 p.next_next_token().cat() == catNewline))
888 h_preamble << package_end_sep;
892 else if (name == "geometry")
893 handle_geometry(options);
895 else if (name == "subfig")
896 ; // ignore this FIXME: Use the package separator mechanism instead
898 else if ((where = is_known(name, known_languages)))
899 h_language = known_coded_languages[where - known_languages];
901 else if (name == "natbib") {
902 h_biblio_style = "plainnat";
903 h_cite_engine = "natbib";
904 h_cite_engine_type = "authoryear";
905 vector<string>::iterator it =
906 find(options.begin(), options.end(), "authoryear");
907 if (it != options.end())
910 it = find(options.begin(), options.end(), "numbers");
911 if (it != options.end()) {
912 h_cite_engine_type = "numerical";
918 else if (name == "jurabib") {
919 h_biblio_style = "jurabib";
920 h_cite_engine = "jurabib";
921 h_cite_engine_type = "authoryear";
924 else if (name == "hyperref")
925 handle_hyperref(options);
927 else if (!in_lyx_preamble) {
929 h_preamble << "\\usepackage{" << name << '}';
931 h_preamble << "\\usepackage[" << opts << "]{"
935 if (p.next_token().cat() == catNewline ||
936 (p.next_token().cat() == catSpace &&
937 p.next_next_token().cat() == catNewline))
941 // We need to do something with the options...
942 if (!options.empty())
943 cerr << "Ignoring options '" << join(options, ",")
944 << "' of package " << name << '.' << endl;
946 // remove the whitespace
951 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
954 Token t = p.get_token();
955 if (t.cat() == catEscape &&
956 is_known(t.cs(), known_if_commands))
957 handle_if(p, in_lyx_preamble);
959 if (!in_lyx_preamble)
960 h_preamble << t.asInput();
961 if (t.cat() == catEscape && t.cs() == "fi")
968 bool Preamble::writeLyXHeader(ostream & os, bool subdoc)
970 // set the quote language
971 // LyX only knows the following quotes languages:
972 // english, swedish, german, polish, french and danish
973 // (quotes for "japanese" and "chinese-traditional" are missing because
974 // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
975 // conversion list taken from
976 // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
977 // (quotes for kazakh and interlingua are unknown)
979 if (is_known(h_language, known_danish_quotes_languages))
980 h_quotes_language = "danish";
982 else if (is_known(h_language, known_french_quotes_languages))
983 h_quotes_language = "french";
985 else if (is_known(h_language, known_german_quotes_languages))
986 h_quotes_language = "german";
988 else if (is_known(h_language, known_polish_quotes_languages))
989 h_quotes_language = "polish";
991 else if (is_known(h_language, known_swedish_quotes_languages))
992 h_quotes_language = "swedish";
994 else if (is_known(h_language, known_english_quotes_languages))
995 h_quotes_language = "english";
997 if (contains(h_float_placement, "H"))
998 registerAutomaticallyLoadedPackage("float");
999 if (h_spacing != "single" && h_spacing != "default")
1000 registerAutomaticallyLoadedPackage("setspace");
1001 if (h_use_packages["amsmath"] == "2") {
1002 // amsbsy and amstext are already provided by amsmath
1003 registerAutomaticallyLoadedPackage("amsbsy");
1004 registerAutomaticallyLoadedPackage("amstext");
1007 // output the LyX file settings
1008 os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
1009 << "\\lyxformat " << LYX_FORMAT << '\n'
1010 << "\\begin_document\n"
1011 << "\\begin_header\n"
1012 << "\\textclass " << h_textclass << "\n";
1013 string const raw = subdoc ? empty_string() : h_preamble.str();
1015 os << "\\begin_preamble\n";
1016 for (string::size_type i = 0; i < raw.size(); ++i) {
1017 if (raw[i] == package_beg_sep) {
1018 // Here follows some package loading code that
1019 // must be skipped if the package is loaded
1021 string::size_type j = raw.find(package_mid_sep, i);
1022 if (j == string::npos)
1024 string::size_type k = raw.find(package_end_sep, j);
1025 if (k == string::npos)
1027 string const package = raw.substr(i + 1, j - i - 1);
1028 string const replacement = raw.substr(j + 1, k - j - 1);
1029 if (auto_packages.find(package) == auto_packages.end())
1035 os << "\n\\end_preamble\n";
1037 if (!h_options.empty())
1038 os << "\\options " << h_options << "\n";
1039 os << "\\use_default_options " << h_use_default_options << "\n";
1040 if (!used_modules.empty()) {
1041 os << "\\begin_modules\n";
1042 vector<string>::const_iterator const end = used_modules.end();
1043 vector<string>::const_iterator it = used_modules.begin();
1044 for (; it != end; ++it)
1046 os << "\\end_modules\n";
1048 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1049 << "\\language " << h_language << "\n"
1050 << "\\language_package " << h_language_package << "\n"
1051 << "\\inputencoding " << h_inputencoding << "\n"
1052 << "\\fontencoding " << h_fontencoding << "\n"
1053 << "\\font_roman " << h_font_roman << "\n"
1054 << "\\font_sans " << h_font_sans << "\n"
1055 << "\\font_typewriter " << h_font_typewriter << "\n"
1056 << "\\font_math " << h_font_math << "\n"
1057 << "\\font_default_family " << h_font_default_family << "\n"
1058 << "\\use_non_tex_fonts " << h_use_non_tex_fonts << "\n"
1059 << "\\font_sc " << h_font_sc << "\n"
1060 << "\\font_osf " << h_font_osf << "\n"
1061 << "\\font_sf_scale " << h_font_sf_scale << "\n"
1062 << "\\font_tt_scale " << h_font_tt_scale << '\n';
1063 if (!h_font_cjk.empty())
1064 os << "\\font_cjk " << h_font_cjk << '\n';
1065 os << "\\graphics " << h_graphics << '\n'
1066 << "\\default_output_format " << h_default_output_format << "\n"
1067 << "\\output_sync " << h_output_sync << "\n";
1068 if (h_output_sync == "1")
1069 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1070 os << "\\bibtex_command " << h_bibtex_command << "\n"
1071 << "\\index_command " << h_index_command << "\n";
1072 if (!h_float_placement.empty())
1073 os << "\\float_placement " << h_float_placement << "\n";
1074 os << "\\paperfontsize " << h_paperfontsize << "\n"
1075 << "\\spacing " << h_spacing << "\n"
1076 << "\\use_hyperref " << h_use_hyperref << '\n';
1077 if (h_use_hyperref == "true") {
1078 if (!h_pdf_title.empty())
1079 os << "\\pdf_title \"" << h_pdf_title << "\"\n";
1080 if (!h_pdf_author.empty())
1081 os << "\\pdf_author \"" << h_pdf_author << "\"\n";
1082 if (!h_pdf_subject.empty())
1083 os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
1084 if (!h_pdf_keywords.empty())
1085 os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
1086 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1087 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1088 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1089 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1090 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1091 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1092 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1093 "\\pdf_backref " << h_pdf_backref << "\n"
1094 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1095 if (!h_pdf_pagemode.empty())
1096 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1097 if (!h_pdf_quoted_options.empty())
1098 os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
1100 os << "\\papersize " << h_papersize << "\n"
1101 << "\\use_geometry " << h_use_geometry << '\n';
1102 for (map<string, string>::const_iterator it = h_use_packages.begin();
1103 it != h_use_packages.end(); ++it)
1104 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1105 os << "\\cite_engine " << h_cite_engine << '\n'
1106 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1107 << "\\biblio_style " << h_biblio_style << "\n"
1108 << "\\use_bibtopic " << h_use_bibtopic << "\n"
1109 << "\\use_indices " << h_use_indices << "\n"
1110 << "\\paperorientation " << h_paperorientation << '\n'
1111 << "\\suppress_date " << h_suppress_date << '\n'
1112 << "\\justification " << h_justification << '\n'
1113 << "\\use_refstyle " << h_use_refstyle << '\n';
1114 if (!h_fontcolor.empty())
1115 os << "\\fontcolor " << h_fontcolor << '\n';
1116 if (!h_notefontcolor.empty())
1117 os << "\\notefontcolor " << h_notefontcolor << '\n';
1118 if (!h_backgroundcolor.empty())
1119 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1120 if (!h_boxbgcolor.empty())
1121 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1122 os << "\\index " << h_index << '\n'
1123 << "\\shortcut " << h_shortcut << '\n'
1124 << "\\color " << h_color << '\n'
1127 << "\\secnumdepth " << h_secnumdepth << "\n"
1128 << "\\tocdepth " << h_tocdepth << "\n"
1129 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1130 if (h_paragraph_separation == "skip")
1131 os << "\\defskip " << h_defskip << "\n";
1133 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1134 os << "\\quotes_language " << h_quotes_language << "\n"
1135 << "\\papercolumns " << h_papercolumns << "\n"
1136 << "\\papersides " << h_papersides << "\n"
1137 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1138 if (!h_listings_params.empty())
1139 os << "\\listings_params " << h_listings_params << "\n";
1140 os << "\\tracking_changes " << h_tracking_changes << "\n"
1141 << "\\output_changes " << h_output_changes << "\n"
1142 << "\\html_math_output " << h_html_math_output << "\n"
1143 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1144 << "\\html_be_strict " << h_html_be_strict << "\n"
1146 << "\\end_header\n\n"
1147 << "\\begin_body\n";
1152 void Preamble::parse(Parser & p, string const & forceclass,
1153 TeX2LyXDocClass & tc)
1155 // initialize fixed types
1156 special_columns['D'] = 3;
1157 bool is_full_document = false;
1158 bool is_lyx_file = false;
1159 bool in_lyx_preamble = false;
1161 // determine whether this is a full document or a fragment for inclusion
1163 Token const & t = p.get_token();
1165 if (t.cat() == catEscape && t.cs() == "documentclass") {
1166 is_full_document = true;
1172 while (is_full_document && p.good()) {
1173 Token const & t = p.get_token();
1176 cerr << "t: " << t << "\n";
1182 if (!in_lyx_preamble &&
1183 (t.cat() == catLetter ||
1184 t.cat() == catSuper ||
1185 t.cat() == catSub ||
1186 t.cat() == catOther ||
1187 t.cat() == catMath ||
1188 t.cat() == catActive ||
1189 t.cat() == catBegin ||
1190 t.cat() == catEnd ||
1191 t.cat() == catAlign ||
1192 t.cat() == catParameter))
1193 h_preamble << t.cs();
1195 else if (!in_lyx_preamble &&
1196 (t.cat() == catSpace || t.cat() == catNewline))
1197 h_preamble << t.asInput();
1199 else if (t.cat() == catComment) {
1200 static regex const islyxfile("%% LyX .* created this file");
1201 static regex const usercommands("User specified LaTeX commands");
1203 string const comment = t.asInput();
1205 // magically switch encoding default if it looks like XeLaTeX
1206 static string const magicXeLaTeX =
1207 "% This document must be compiled with XeLaTeX ";
1208 if (comment.size() > magicXeLaTeX.size()
1209 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1210 && h_inputencoding == "auto") {
1211 cerr << "XeLaTeX comment found, switching to UTF8\n";
1212 h_inputencoding = "utf8";
1215 if (regex_search(comment, sub, islyxfile)) {
1217 in_lyx_preamble = true;
1218 } else if (is_lyx_file
1219 && regex_search(comment, sub, usercommands))
1220 in_lyx_preamble = false;
1221 else if (!in_lyx_preamble)
1222 h_preamble << t.asInput();
1225 else if (t.cs() == "pagestyle")
1226 h_paperpagestyle = p.verbatim_item();
1228 else if (t.cs() == "setdefaultlanguage") {
1230 // We don't yet care about non-language variant options
1231 // because LyX doesn't support this yet, see bug #8214
1233 string langopts = p.getOpt();
1234 // check if the option contains a variant, if yes, extract it
1235 string::size_type pos_var = langopts.find("variant");
1236 string::size_type i = langopts.find(',', pos_var);
1237 string::size_type k = langopts.find('=', pos_var);
1238 if (pos_var != string::npos){
1240 if (i == string::npos)
1241 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1243 variant = langopts.substr(k + 1, i - k - 1);
1244 h_language = variant;
1248 h_language = p.verbatim_item();
1249 //finally translate the poyglossia name to a LyX name
1250 h_language = polyglossia2lyx(h_language);
1253 else if (t.cs() == "setotherlanguage") {
1254 // We don't yet care about the option because LyX doesn't
1255 // support this yet, see bug #8214
1256 p.hasOpt() ? p.getOpt() : string();
1260 else if (t.cs() == "setmainfont") {
1261 // we don't care about the option
1262 p.hasOpt() ? p.getOpt() : string();
1263 h_font_roman = p.getArg('{', '}');
1266 else if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1267 // LyX currently only supports the scale option
1270 string fontopts = p.getArg('[', ']');
1271 // check if the option contains a scaling, if yes, extract it
1272 string::size_type pos = fontopts.find("Scale");
1273 if (pos != string::npos) {
1274 string::size_type i = fontopts.find(',', pos);
1275 if (i == string::npos)
1276 scale_as_percentage(fontopts.substr(pos + 1), scale);
1278 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1281 if (t.cs() == "setsansfont") {
1283 h_font_sf_scale = scale;
1284 h_font_sans = p.getArg('{', '}');
1287 h_font_tt_scale = scale;
1288 h_font_typewriter = p.getArg('{', '}');
1292 else if (t.cs() == "date") {
1293 string argument = p.getArg('{', '}');
1294 if (argument.empty())
1295 h_suppress_date = "true";
1297 h_preamble << t.asInput() << '{' << argument << '}';
1300 else if (t.cs() == "color") {
1301 string const space =
1302 (p.hasOpt() ? p.getOpt() : string());
1303 string argument = p.getArg('{', '}');
1304 // check the case that a standard color is used
1305 if (space.empty() && is_known(argument, known_basic_colors)) {
1306 h_fontcolor = rgbcolor2code(argument);
1307 preamble.registerAutomaticallyLoadedPackage("color");
1308 } else if (space.empty() && argument == "document_fontcolor")
1309 preamble.registerAutomaticallyLoadedPackage("color");
1310 // check the case that LyX's document_fontcolor is defined
1311 // but not used for \color
1313 h_preamble << t.asInput();
1315 h_preamble << space;
1316 h_preamble << '{' << argument << '}';
1317 // the color might already be set because \definecolor
1318 // is parsed before this
1323 else if (t.cs() == "pagecolor") {
1324 string argument = p.getArg('{', '}');
1325 // check the case that a standard color is used
1326 if (is_known(argument, known_basic_colors)) {
1327 h_backgroundcolor = rgbcolor2code(argument);
1328 } else if (argument == "page_backgroundcolor")
1329 preamble.registerAutomaticallyLoadedPackage("color");
1330 // check the case that LyX's page_backgroundcolor is defined
1331 // but not used for \pagecolor
1333 h_preamble << t.asInput() << '{' << argument << '}';
1334 // the color might already be set because \definecolor
1335 // is parsed before this
1336 h_backgroundcolor = "";
1340 else if (t.cs() == "makeatletter") {
1341 // LyX takes care of this
1342 p.setCatcode('@', catLetter);
1345 else if (t.cs() == "makeatother") {
1346 // LyX takes care of this
1347 p.setCatcode('@', catOther);
1350 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1351 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1352 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1353 || t.cs() == "DeclareRobustCommand"
1354 || t.cs() == "DeclareRobustCommandx"
1355 || t.cs() == "ProvideTextCommandDefault"
1356 || t.cs() == "DeclareMathAccent") {
1358 if (p.next_token().character() == '*') {
1362 string const name = p.verbatim_item();
1363 string const opt1 = p.getFullOpt();
1364 string const opt2 = p.getFullOpt();
1365 string const body = p.verbatim_item();
1366 // store the in_lyx_preamble setting
1367 bool const was_in_lyx_preamble = in_lyx_preamble;
1369 if (name == "\\rmdefault")
1370 if (is_known(body, known_roman_fonts)) {
1371 h_font_roman = body;
1373 in_lyx_preamble = true;
1375 if (name == "\\sfdefault")
1376 if (is_known(body, known_sans_fonts)) {
1379 in_lyx_preamble = true;
1381 if (name == "\\ttdefault")
1382 if (is_known(body, known_typewriter_fonts)) {
1383 h_font_typewriter = body;
1385 in_lyx_preamble = true;
1387 if (name == "\\familydefault") {
1388 string family = body;
1389 // remove leading "\"
1390 h_font_default_family = family.erase(0,1);
1392 in_lyx_preamble = true;
1395 if (name == "\\bfdefault")
1396 // LyX re-adds this if a kurier font is used
1397 if (is_known(h_font_sans, known_kurier_fonts) && body == "b") {
1399 in_lyx_preamble = true;
1402 // remove the lyxdot definition that is re-added by LyX
1404 if (name == "\\lyxdot") {
1406 in_lyx_preamble = true;
1409 // Add the command to the known commands
1410 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1412 // only non-lyxspecific stuff
1413 if (!in_lyx_preamble) {
1415 ss << '\\' << t.cs();
1418 ss << '{' << name << '}' << opt1 << opt2
1419 << '{' << body << "}";
1420 h_preamble << ss.str();
1422 ostream & out = in_preamble ? h_preamble : os;
1423 out << "\\" << t.cs() << "{" << name << "}"
1424 << opts << "{" << body << "}";
1427 // restore the in_lyx_preamble setting
1428 in_lyx_preamble = was_in_lyx_preamble;
1431 else if (t.cs() == "edef"){
1432 // we only support this for kurier fonts
1433 string const command = p.next_token().asInput();
1435 if (command == "\\sfdefault") {
1437 if (h_font_sans == "kurier")
1438 h_font_sans = "kurier-condensed";
1439 if (h_font_sans == "kurierl")
1440 h_font_sans = "kurier-light-condensed";
1444 h_preamble << "\\edef" << command << "{" << p.getArg('{', '}') << "}\n";
1447 else if (t.cs() == "documentclass") {
1448 vector<string>::iterator it;
1449 vector<string> opts = split_options(p.getArg('[', ']'));
1450 handle_opt(opts, known_fontsizes, h_paperfontsize);
1451 delete_opt(opts, known_fontsizes);
1452 // delete "pt" at the end
1453 string::size_type i = h_paperfontsize.find("pt");
1454 if (i != string::npos)
1455 h_paperfontsize.erase(i);
1456 // The documentclass options are always parsed before the options
1457 // of the babel call so that a language cannot overwrite the babel
1459 handle_opt(opts, known_languages, h_language);
1460 delete_opt(opts, known_languages);
1462 // paper orientation
1463 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1464 h_paperorientation = "landscape";
1468 if ((it = find(opts.begin(), opts.end(), "oneside"))
1473 if ((it = find(opts.begin(), opts.end(), "twoside"))
1479 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1481 h_papercolumns = "1";
1484 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1486 h_papercolumns = "2";
1490 // some size options are known to any document classes, other sizes
1491 // are handled by the \geometry command of the geometry package
1492 handle_opt(opts, known_class_paper_sizes, h_papersize);
1493 delete_opt(opts, known_class_paper_sizes);
1494 // the remaining options
1495 h_options = join(opts, ",");
1496 // FIXME This does not work for classes that have a
1497 // different name in LyX than in LaTeX
1498 h_textclass = p.getArg('{', '}');
1502 else if (t.cs() == "usepackage") {
1503 string const options = p.getArg('[', ']');
1504 string const name = p.getArg('{', '}');
1505 vector<string> vecnames;
1506 split(name, vecnames, ',');
1507 vector<string>::const_iterator it = vecnames.begin();
1508 vector<string>::const_iterator end = vecnames.end();
1509 for (; it != end; ++it)
1510 handle_package(p, trimSpaceAndEol(*it), options,
1514 else if (t.cs() == "inputencoding") {
1515 string const encoding = p.getArg('{','}');
1516 h_inputencoding = encoding;
1517 p.setEncoding(encoding, Encoding::inputenc);
1520 else if (t.cs() == "newenvironment") {
1521 string const name = p.getArg('{', '}');
1522 string const opt1 = p.getFullOpt();
1523 string const opt2 = p.getFullOpt();
1524 string const beg = p.verbatim_item();
1525 string const end = p.verbatim_item();
1526 if (!in_lyx_preamble) {
1527 h_preamble << "\\newenvironment{" << name
1528 << '}' << opt1 << opt2 << '{'
1529 << beg << "}{" << end << '}';
1531 add_known_environment(name, opt1, !opt2.empty(),
1532 from_utf8(beg), from_utf8(end));
1536 else if (t.cs() == "newtheorem") {
1537 string const name = p.getArg('{', '}');
1538 string const opt1 = p.getFullOpt();
1539 string const opt2 = p.getFullOpt();
1540 string const body = p.verbatim_item();
1541 string const opt3 = p.getFullOpt();
1543 add_known_theorem(name, opt1, !opt2.empty(),
1544 from_utf8("\\newtheorem{" + name + '}' +
1545 opt1 + opt2 + '{' + body + '}' + opt3));
1547 if (!in_lyx_preamble)
1548 h_preamble << "\\newtheorem{" << name << '}'
1549 << opt1 << opt2 << '{' << '}' << opt3;
1552 else if (t.cs() == "def") {
1553 string name = p.get_token().cs();
1554 // In fact, name may be more than the name:
1555 // In the test case of bug 8116
1556 // name == "csname SF@gobble@opt \endcsname".
1557 // Therefore, we need to use asInput() instead of cs().
1558 while (p.next_token().cat() != catBegin)
1559 name += p.get_token().asInput();
1560 if (!in_lyx_preamble)
1561 h_preamble << "\\def\\" << name << '{'
1562 << p.verbatim_item() << "}";
1565 else if (t.cs() == "newcolumntype") {
1566 string const name = p.getArg('{', '}');
1567 trimSpaceAndEol(name);
1569 string opts = p.getOpt();
1570 if (!opts.empty()) {
1571 istringstream is(string(opts, 1));
1574 special_columns[name[0]] = nargs;
1575 h_preamble << "\\newcolumntype{" << name << "}";
1577 h_preamble << "[" << nargs << "]";
1578 h_preamble << "{" << p.verbatim_item() << "}";
1581 else if (t.cs() == "setcounter") {
1582 string const name = p.getArg('{', '}');
1583 string const content = p.getArg('{', '}');
1584 if (name == "secnumdepth")
1585 h_secnumdepth = content;
1586 else if (name == "tocdepth")
1587 h_tocdepth = content;
1589 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1592 else if (t.cs() == "setlength") {
1593 string const name = p.verbatim_item();
1594 string const content = p.verbatim_item();
1595 // the paragraphs are only not indented when \parindent is set to zero
1596 if (name == "\\parindent" && content != "") {
1597 if (content[0] == '0')
1598 h_paragraph_separation = "skip";
1600 h_paragraph_indentation = translate_len(content);
1601 } else if (name == "\\parskip") {
1602 if (content == "\\smallskipamount")
1603 h_defskip = "smallskip";
1604 else if (content == "\\medskipamount")
1605 h_defskip = "medskip";
1606 else if (content == "\\bigskipamount")
1607 h_defskip = "bigskip";
1609 h_defskip = content;
1611 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1614 else if (t.cs() == "onehalfspacing")
1615 h_spacing = "onehalf";
1617 else if (t.cs() == "doublespacing")
1618 h_spacing = "double";
1620 else if (t.cs() == "setstretch")
1621 h_spacing = "other " + p.verbatim_item();
1623 else if (t.cs() == "synctex") {
1624 // the scheme is \synctex=value
1625 // where value can only be "1" or "-1"
1626 h_output_sync = "1";
1627 // there can be any character behind the value (e.g. a linebreak or a '\'
1628 // therefore we extract it char by char
1630 string value = p.get_token().asInput();
1632 value += p.get_token().asInput();
1633 h_output_sync_macro = "\\synctex=" + value;
1636 else if (t.cs() == "begin") {
1637 string const name = p.getArg('{', '}');
1638 if (name == "document")
1640 h_preamble << "\\begin{" << name << "}";
1643 else if (t.cs() == "geometry") {
1644 vector<string> opts = split_options(p.getArg('{', '}'));
1645 handle_geometry(opts);
1648 else if (t.cs() == "definecolor") {
1649 string const color = p.getArg('{', '}');
1650 string const space = p.getArg('{', '}');
1651 string const value = p.getArg('{', '}');
1652 if (color == "document_fontcolor" && space == "rgb") {
1653 RGBColor c(RGBColorFromLaTeX(value));
1654 h_fontcolor = X11hexname(c);
1655 } else if (color == "note_fontcolor" && space == "rgb") {
1656 RGBColor c(RGBColorFromLaTeX(value));
1657 h_notefontcolor = X11hexname(c);
1658 } else if (color == "page_backgroundcolor" && space == "rgb") {
1659 RGBColor c(RGBColorFromLaTeX(value));
1660 h_backgroundcolor = X11hexname(c);
1661 } else if (color == "shadecolor" && space == "rgb") {
1662 RGBColor c(RGBColorFromLaTeX(value));
1663 h_boxbgcolor = X11hexname(c);
1665 h_preamble << "\\definecolor{" << color
1666 << "}{" << space << "}{" << value
1671 else if (t.cs() == "bibliographystyle")
1672 h_biblio_style = p.verbatim_item();
1674 else if (t.cs() == "jurabibsetup") {
1675 // FIXME p.getArg('{', '}') is most probably wrong (it
1676 // does not handle nested braces).
1677 // Use p.verbatim_item() instead.
1678 vector<string> jurabibsetup =
1679 split_options(p.getArg('{', '}'));
1680 // add jurabibsetup to the jurabib package options
1681 add_package("jurabib", jurabibsetup);
1682 if (!jurabibsetup.empty()) {
1683 h_preamble << "\\jurabibsetup{"
1684 << join(jurabibsetup, ",") << '}';
1688 else if (t.cs() == "hypersetup") {
1689 vector<string> hypersetup =
1690 split_options(p.verbatim_item());
1691 // add hypersetup to the hyperref package options
1692 handle_hyperref(hypersetup);
1693 if (!hypersetup.empty()) {
1694 h_preamble << "\\hypersetup{"
1695 << join(hypersetup, ",") << '}';
1699 else if (is_known(t.cs(), known_if_3arg_commands)) {
1700 // prevent misparsing of \usepackage if it is used
1701 // as an argument (see e.g. our own output of
1702 // \@ifundefined above)
1703 string const arg1 = p.verbatim_item();
1704 string const arg2 = p.verbatim_item();
1705 string const arg3 = p.verbatim_item();
1706 // test case \@ifundefined{date}{}{\date{}}
1707 if (t.cs() == "@ifundefined" && arg1 == "date" &&
1708 arg2.empty() && arg3 == "\\date{}") {
1709 h_suppress_date = "true";
1710 // older tex2lyx versions did output
1711 // \@ifundefined{definecolor}{\usepackage{color}}{}
1712 } else if (t.cs() == "@ifundefined" &&
1713 arg1 == "definecolor" &&
1714 arg2 == "\\usepackage{color}" &&
1716 if (!in_lyx_preamble)
1717 h_preamble << package_beg_sep
1720 << "\\@ifundefined{definecolor}{color}{}"
1723 //\@ifundefined{showcaptionsetup}{}{%
1724 // \PassOptionsToPackage{caption=false}{subfig}}
1725 // that LyX uses for subfloats
1726 } else if (t.cs() == "@ifundefined" &&
1727 arg1 == "showcaptionsetup" && arg2.empty()
1728 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1730 } else if (!in_lyx_preamble) {
1731 h_preamble << t.asInput()
1732 << '{' << arg1 << '}'
1733 << '{' << arg2 << '}'
1734 << '{' << arg3 << '}';
1738 else if (is_known(t.cs(), known_if_commands)) {
1739 // must not parse anything in conditional code, since
1740 // LyX would output the parsed contents unconditionally
1741 if (!in_lyx_preamble)
1742 h_preamble << t.asInput();
1743 handle_if(p, in_lyx_preamble);
1746 else if (!t.cs().empty() && !in_lyx_preamble)
1747 h_preamble << '\\' << t.cs();
1750 // remove the whitespace
1753 // Force textclass if the user wanted it
1754 if (!forceclass.empty())
1755 h_textclass = forceclass;
1756 tc.setName(h_textclass);
1758 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1761 if (h_papersides.empty()) {
1764 h_papersides = ss.str();
1767 // If the CJK package is used we cannot set the document language from
1768 // the babel options. Instead, we guess which language is used most
1769 // and set this one.
1770 default_language = h_language;
1771 if (is_full_document &&
1772 (auto_packages.find("CJK") != auto_packages.end() ||
1773 auto_packages.find("CJKutf8") != auto_packages.end())) {
1775 h_language = guessLanguage(p, default_language);
1781 string babel2lyx(string const & language)
1783 char const * const * where = is_known(language, known_languages);
1785 return known_coded_languages[where - known_languages];
1790 string Preamble::polyglossia2lyx(string const & language)
1792 char const * const * where = is_known(language, polyglossia_languages);
1794 return coded_polyglossia_languages[where - polyglossia_languages];
1799 string rgbcolor2code(string const & name)
1801 char const * const * where = is_known(name, known_basic_colors);
1803 // "red", "green" etc
1804 return known_basic_color_codes[where - known_basic_colors];
1806 // "255,0,0", "0,255,0" etc
1807 RGBColor c(RGBColorFromLaTeX(name));
1808 return X11hexname(c);