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",
128 "libertine-type1", "lmodern", "mathdesign", "mathpazo", "mathptmx", "newcent",
129 "tgbonum", "tgchorus", "tgpagella", "tgschola", "tgtermes", "utopia", 0};
131 const char * const known_sans_fonts[] = { "avant", "berasans", "biolinum-type1",
132 "cmbr", "cmss", "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", "txtt", 0};
141 const char * const known_math_fonts[] = { "eulervm", "newtxmath", 0};
143 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
144 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
145 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
146 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
147 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
149 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
150 "executivepaper", "legalpaper", "letterpaper", 0};
152 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
153 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
155 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
156 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
159 /// commands that can start an \if...\else...\endif sequence
160 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
161 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
162 "ifsidecap", "ifupgreek", 0};
164 const char * const known_basic_colors[] = {"blue", "black", "cyan", "green",
165 "magenta", "red", "white", "yellow", 0};
167 const char * const known_basic_color_codes[] = {"#0000ff", "#000000", "#00ffff", "#00ff00",
168 "#ff00ff", "#ff0000", "#ffffff", "#ffff00", 0};
170 /// conditional commands with three arguments like \@ifundefined{}{}{}
171 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
174 /// packages that work only in xetex
175 /// polyglossia is handled separately
176 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
177 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
178 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
180 /// packages that are automatically skipped if loaded by LyX
181 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
182 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color", "float",
183 "fontspec", "graphicx", "hhline", "ifthen", "longtable", "makeidx", "multirow",
184 "nomencl", "pdfpages", "rotating", "rotfloat", "splitidx", "setspace",
185 "subscript", "textcomp", "ulem", "url", "varioref", "verbatim", "wrapfig",
188 // codes used to remove packages that are loaded automatically by LyX.
189 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
190 const char package_beg_sep = '\001';
191 const char package_mid_sep = '\002';
192 const char package_end_sep = '\003';
195 // returns true if at least one of the options in what has been found
196 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
202 // the last language option is the document language (for babel and LyX)
203 // the last size option is the document font size
204 vector<string>::iterator it;
205 vector<string>::iterator position = opts.begin();
206 for (; *what; ++what) {
207 it = find(opts.begin(), opts.end(), *what);
208 if (it != opts.end()) {
209 if (it >= position) {
220 void delete_opt(vector<string> & opts, char const * const * what)
225 // remove found options from the list
226 // do this after handle_opt to avoid potential memory leaks
227 vector<string>::iterator it;
228 for (; *what; ++what) {
229 it = find(opts.begin(), opts.end(), *what);
230 if (it != opts.end())
237 * Split a package options string (keyval format) into a vector.
239 * authorformat=smallcaps,
241 * titleformat=colonsep,
242 * bibformat={tabular,ibidem,numbered}
244 vector<string> split_options(string const & input)
246 vector<string> options;
250 Token const & t = p.get_token();
251 if (t.asInput() == ",") {
252 options.push_back(trimSpaceAndEol(option));
254 } else if (t.asInput() == "=") {
257 if (p.next_token().asInput() == "{")
258 option += '{' + p.getArg('{', '}') + '}';
259 } else if (t.cat() != catSpace)
260 option += t.asInput();
264 options.push_back(trimSpaceAndEol(option));
271 * Retrieve a keyval option "name={value with=sign}" named \p name from
272 * \p options and return the value.
273 * The found option is also removed from \p options.
275 string process_keyval_opt(vector<string> & options, string name)
277 for (size_t i = 0; i < options.size(); ++i) {
278 vector<string> option;
279 split(options[i], option, '=');
280 if (option.size() < 2)
282 if (option[0] == name) {
283 options.erase(options.begin() + i);
284 option.erase(option.begin());
285 return join(option, "=");
291 } // anonymous namespace
295 * known polyglossia language names (including variants)
297 const char * const Preamble::polyglossia_languages[] = {
298 "albanian", "croatian", "hebrew", "norsk", "swedish", "amharic", "czech", "hindi",
299 "nynorsk", "syriac", "arabic", "danish", "icelandic", "occitan", "tamil",
300 "armenian", "divehi", "interlingua", "polish", "telugu", "asturian", "dutch",
301 "irish", "portuges", "thai", "bahasai", "english", "italian", "romanian", "turkish",
302 "bahasam", "esperanto", "lao", "russian", "turkmen", "basque", "estonian", "latin",
303 "samin", "ukrainian", "bengali", "farsi", "latvian", "sanskrit", "urdu", "brazil",
304 "brazilian", "finnish", "lithuanian", "scottish", "usorbian", "breton", "french",
305 "lsorbian", "serbian", "vietnamese", "bulgarian", "galician", "magyar", "slovak",
306 "welsh", "catalan", "german", "malayalam", "slovenian", "coptic", "greek",
307 "marathi", "spanish",
308 "american", "ancient", "australian", "british", "monotonic", "newzealand",
312 * the same as polyglossia_languages with .lyx names
313 * please keep this in sync with polyglossia_languages line by line!
315 const char * const Preamble::coded_polyglossia_languages[] = {
316 "albanian", "croatian", "hebrew", "norsk", "swedish", "amharic", "czech", "hindi",
317 "nynorsk", "syriac", "arabic_arabi", "danish", "icelandic", "occitan", "tamil",
318 "armenian", "divehi", "interlingua", "polish", "telugu", "asturian", "dutch",
319 "irish", "portuges", "thai", "bahasa", "english", "italian", "romanian", "turkish",
320 "bahasam", "esperanto", "lao", "russian", "turkmen", "basque", "estonian", "latin",
321 "samin", "ukrainian", "bengali", "farsi", "latvian", "sanskrit", "urdu", "brazilian",
322 "brazilian", "finnish", "lithuanian", "scottish", "uppersorbian", "breton", "french",
323 "lowersorbian", "serbian", "vietnamese", "bulgarian", "galician", "magyar", "slovak",
324 "welsh", "catalan", "ngerman", "malayalam", "slovene", "coptic", "greek",
325 "marathi", "spanish",
326 "american", "ancientgreek", "australian", "british", "greek", "newzealand",
327 "polutonikogreek", 0};
330 bool Preamble::indentParagraphs() const
332 return h_paragraph_separation == "indent";
336 bool Preamble::isPackageUsed(string const & package) const
338 return used_packages.find(package) != used_packages.end();
342 vector<string> Preamble::getPackageOptions(string const & package) const
344 map<string, vector<string> >::const_iterator it = used_packages.find(package);
345 if (it != used_packages.end())
347 return vector<string>();
351 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
353 auto_packages.insert(package);
357 void Preamble::addModule(string const & module)
359 used_modules.push_back(module);
363 void Preamble::suppressDate(bool suppress)
366 h_suppress_date = "true";
368 h_suppress_date = "false";
372 void Preamble::registerAuthor(std::string const & name)
374 Author author(from_utf8(name), empty_docstring());
375 author.setUsed(true);
376 authors_.record(author);
377 h_tracking_changes = "true";
378 h_output_changes = "true";
382 Author const & Preamble::getAuthor(std::string const & name) const
384 Author author(from_utf8(name), empty_docstring());
385 for (AuthorList::Authors::const_iterator it = authors_.begin();
386 it != authors_.end(); ++it)
389 static Author const dummy;
394 void Preamble::add_package(string const & name, vector<string> & options)
396 // every package inherits the global options
397 if (used_packages.find(name) == used_packages.end())
398 used_packages[name] = split_options(h_options);
400 vector<string> & v = used_packages[name];
401 v.insert(v.end(), options.begin(), options.end());
402 if (name == "jurabib") {
403 // Don't output the order argument (see the cite command
404 // handling code in text.cpp).
405 vector<string>::iterator end =
406 remove(options.begin(), options.end(), "natbiborder");
407 end = remove(options.begin(), end, "jurabiborder");
408 options.erase(end, options.end());
415 // Given is a string like "scaled=0.9" or "Scale=0.9", return 0.9 * 100
416 bool scale_as_percentage(string const & scale, string & percentage)
418 string::size_type pos = scale.find('=');
419 if (pos != string::npos) {
420 string value = scale.substr(pos + 1);
421 if (isStrDbl(value)) {
422 percentage = convert<string>(100 * convert<double>(value));
430 string remove_braces(string const & value)
434 if (value[0] == '{' && value[value.length()-1] == '}')
435 return value.substr(1, value.length()-2);
439 } // anonymous namespace
442 Preamble::Preamble() : one_language(true), title_layout_found(false),
443 h_font_cjk_set(false)
447 h_biblio_style = "plain";
448 h_bibtex_command = "default";
449 h_cite_engine = "basic";
450 h_cite_engine_type = "numerical";
452 h_defskip = "medskip";
455 h_fontencoding = "default";
456 h_font_roman = "default";
457 h_font_sans = "default";
458 h_font_typewriter = "default";
459 h_font_math = "auto";
460 h_font_default_family = "default";
461 h_use_non_tex_fonts = "false";
463 h_font_osf = "false";
464 h_font_sf_scale = "100";
465 h_font_tt_scale = "100";
467 h_graphics = "default";
468 h_default_output_format = "default";
469 h_html_be_strict = "false";
470 h_html_css_as_file = "0";
471 h_html_math_output = "0";
473 h_index_command = "default";
474 h_inputencoding = "auto";
475 h_justification = "true";
476 h_language = "english";
477 h_language_package = "none";
479 h_maintain_unincluded_children = "false";
483 h_output_changes = "false";
485 //h_output_sync_macro
486 h_papercolumns = "1";
487 h_paperfontsize = "default";
488 h_paperorientation = "portrait";
489 h_paperpagestyle = "default";
491 h_papersize = "default";
492 h_paragraph_indentation = "default";
493 h_paragraph_separation = "indent";
498 h_pdf_bookmarks = "1";
499 h_pdf_bookmarksnumbered = "0";
500 h_pdf_bookmarksopen = "0";
501 h_pdf_bookmarksopenlevel = "1";
502 h_pdf_breaklinks = "0";
503 h_pdf_pdfborder = "0";
504 h_pdf_colorlinks = "0";
505 h_pdf_backref = "section";
506 h_pdf_pdfusetitle = "1";
508 //h_pdf_quoted_options;
509 h_quotes_language = "english";
512 h_spacing = "single";
513 h_suppress_date = "false";
514 h_textclass = "article";
516 h_tracking_changes = "false";
517 h_use_bibtopic = "false";
518 h_use_indices = "false";
519 h_use_geometry = "false";
520 h_use_default_options = "false";
521 h_use_hyperref = "false";
522 h_use_refstyle = "0";
523 h_use_packages["amsmath"] = "1";
524 h_use_packages["amssymb"] = "0";
525 h_use_packages["esint"] = "1";
526 h_use_packages["mhchem"] = "0";
527 h_use_packages["mathdots"] = "0";
528 h_use_packages["mathtools"] = "0";
529 h_use_packages["stackrel"] = "0";
530 h_use_packages["stmaryrd"] = "0";
531 h_use_packages["undertilde"] = "0";
535 void Preamble::handle_hyperref(vector<string> & options)
537 // FIXME swallow inputencoding changes that might surround the
538 // hyperref setup if it was written by LyX
539 h_use_hyperref = "true";
540 // swallow "unicode=true", since LyX does always write that
541 vector<string>::iterator it =
542 find(options.begin(), options.end(), "unicode=true");
543 if (it != options.end())
545 it = find(options.begin(), options.end(), "pdfusetitle");
546 if (it != options.end()) {
547 h_pdf_pdfusetitle = "1";
550 string bookmarks = process_keyval_opt(options, "bookmarks");
551 if (bookmarks == "true")
552 h_pdf_bookmarks = "1";
553 else if (bookmarks == "false")
554 h_pdf_bookmarks = "0";
555 if (h_pdf_bookmarks == "1") {
556 string bookmarksnumbered =
557 process_keyval_opt(options, "bookmarksnumbered");
558 if (bookmarksnumbered == "true")
559 h_pdf_bookmarksnumbered = "1";
560 else if (bookmarksnumbered == "false")
561 h_pdf_bookmarksnumbered = "0";
562 string bookmarksopen =
563 process_keyval_opt(options, "bookmarksopen");
564 if (bookmarksopen == "true")
565 h_pdf_bookmarksopen = "1";
566 else if (bookmarksopen == "false")
567 h_pdf_bookmarksopen = "0";
568 if (h_pdf_bookmarksopen == "1") {
569 string bookmarksopenlevel =
570 process_keyval_opt(options, "bookmarksopenlevel");
571 if (!bookmarksopenlevel.empty())
572 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
575 string breaklinks = process_keyval_opt(options, "breaklinks");
576 if (breaklinks == "true")
577 h_pdf_breaklinks = "1";
578 else if (breaklinks == "false")
579 h_pdf_breaklinks = "0";
580 string pdfborder = process_keyval_opt(options, "pdfborder");
581 if (pdfborder == "{0 0 0}")
582 h_pdf_pdfborder = "1";
583 else if (pdfborder == "{0 0 1}")
584 h_pdf_pdfborder = "0";
585 string backref = process_keyval_opt(options, "backref");
586 if (!backref.empty())
587 h_pdf_backref = backref;
588 string colorlinks = process_keyval_opt(options, "colorlinks");
589 if (colorlinks == "true")
590 h_pdf_colorlinks = "1";
591 else if (colorlinks == "false")
592 h_pdf_colorlinks = "0";
593 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
594 if (!pdfpagemode.empty())
595 h_pdf_pagemode = pdfpagemode;
596 string pdftitle = process_keyval_opt(options, "pdftitle");
597 if (!pdftitle.empty()) {
598 h_pdf_title = remove_braces(pdftitle);
600 string pdfauthor = process_keyval_opt(options, "pdfauthor");
601 if (!pdfauthor.empty()) {
602 h_pdf_author = remove_braces(pdfauthor);
604 string pdfsubject = process_keyval_opt(options, "pdfsubject");
605 if (!pdfsubject.empty())
606 h_pdf_subject = remove_braces(pdfsubject);
607 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
608 if (!pdfkeywords.empty())
609 h_pdf_keywords = remove_braces(pdfkeywords);
610 if (!options.empty()) {
611 if (!h_pdf_quoted_options.empty())
612 h_pdf_quoted_options += ',';
613 h_pdf_quoted_options += join(options, ",");
619 void Preamble::handle_geometry(vector<string> & options)
621 h_use_geometry = "true";
622 vector<string>::iterator it;
624 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
625 h_paperorientation = "landscape";
629 // keyval version: "paper=letter"
630 string paper = process_keyval_opt(options, "paper");
632 h_papersize = paper + "paper";
633 // alternative version: "letterpaper"
634 handle_opt(options, known_paper_sizes, h_papersize);
635 delete_opt(options, known_paper_sizes);
637 char const * const * margin = known_paper_margins;
638 for (; *margin; ++margin) {
639 string value = process_keyval_opt(options, *margin);
640 if (!value.empty()) {
641 int k = margin - known_paper_margins;
642 string name = known_coded_paper_margins[k];
643 h_margins += '\\' + name + ' ' + value + '\n';
649 void Preamble::handle_package(Parser &p, string const & name,
650 string const & opts, bool in_lyx_preamble)
652 vector<string> options = split_options(opts);
653 add_package(name, options);
654 char const * const * where = 0;
656 if (is_known(name, known_xetex_packages)) {
658 h_use_non_tex_fonts = "true";
659 registerAutomaticallyLoadedPackage("fontspec");
660 if (h_inputencoding == "auto")
661 p.setEncoding("UTF-8");
665 if (is_known(name, known_roman_fonts))
668 if (name == "fourier") {
669 h_font_roman = "utopia";
670 // when font uses real small capitals
671 if (opts == "expert")
675 if (name == "garamondx") {
676 h_font_roman = "garamondx";
681 if (name == "libertine") {
682 h_font_roman = "libertine-legacy";
687 if (name == "libertine-type1") {
688 h_font_roman = "libertine";
689 if (opts == "lining")
690 h_font_osf = "false";
695 if (name == "mathdesign") {
696 if (opts.find("charter") != string::npos)
697 h_font_roman = "md-charter";
698 if (opts.find("garamond") != string::npos)
699 h_font_roman = "md-garamond";
700 if (opts.find("utopia") != string::npos)
701 h_font_roman = "md-utopia";
702 if (opts.find("expert") != string::npos) {
708 else if (name == "mathpazo")
709 h_font_roman = "palatino";
711 else if (name == "mathptmx")
712 h_font_roman = "times";
715 if (is_known(name, known_sans_fonts)) {
717 if (options.size() == 1) {
718 if (scale_as_percentage(opts, h_font_sf_scale))
723 if (name == "biolinum-type1")
724 h_font_sans = "biolinum";
727 if (is_known(name, known_typewriter_fonts)) {
728 // fourier can be set as roman font _only_
729 // fourier as typewriter is handled in handling of \ttdefault
730 if (name != "fourier") {
731 h_font_typewriter = name;
732 if (options.size() == 1) {
733 if (scale_as_percentage(opts, h_font_tt_scale))
739 // font uses old-style figure
744 if (is_known(name, known_math_fonts))
747 if (name == "newtxmath") {
749 h_font_math = "newtxmath";
750 else if (opts == "garamondx")
751 h_font_math = "garamondx-ntxm";
752 else if (opts == "libertine")
753 h_font_math = "libertine-ntxm";
754 else if (opts == "minion")
755 h_font_math = "minion-ntxm";
758 if (name == "refstyle")
759 h_use_refstyle = "1";
761 // after the detection and handling of special cases, we can remove the
762 // fonts, otherwise they would appear in the preamble, see bug #7856
763 if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
764 || is_known(name, known_typewriter_fonts) || is_known(name, known_math_fonts))
767 else if (name == "amsmath" || name == "amssymb" ||
768 name == "esint" || name == "mhchem" || name == "mathdots" ||
769 name == "mathtools" || name == "stackrel" ||
770 name == "stmaryrd" || name == "undertilde")
771 h_use_packages[name] = "2";
773 else if (name == "babel") {
774 h_language_package = "default";
775 // One might think we would have to do nothing if babel is loaded
776 // without any options to prevent pollution of the preamble with this
777 // babel call in every roundtrip.
778 // But the user could have defined babel-specific things afterwards. So
779 // we need to keep it in the preamble to prevent cases like bug #7861.
781 // check if more than one option was used - used later for inputenc
782 if (options.begin() != options.end() - 1)
783 one_language = false;
784 // babel takes the last language of the option of its \usepackage
785 // call as document language. If there is no such language option, the
786 // last language in the documentclass options is used.
787 handle_opt(options, known_languages, h_language);
788 // translate the babel name to a LyX name
789 h_language = babel2lyx(h_language);
790 if (h_language == "japanese") {
791 // For Japanese, the encoding isn't indicated in the source
792 // file, and there's really not much we can do. We could
793 // 1) offer a list of possible encodings to choose from, or
794 // 2) determine the encoding of the file by inspecting it.
795 // For the time being, we leave the encoding alone so that
796 // we don't get iconv errors when making a wrong guess, and
797 // we will output a note at the top of the document
798 // explaining what to do.
799 Encoding const * const enc = encodings.fromIconvName(
800 p.getEncoding(), Encoding::japanese, false);
802 h_inputencoding = enc->name();
803 is_nonCJKJapanese = true;
804 // in this case babel can be removed from the preamble
805 registerAutomaticallyLoadedPackage("babel");
807 // If babel is called with options, LyX puts them by default into the
808 // document class options. This works for most languages, except
809 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
810 // perhaps in future others.
811 // Therefore keep the babel call as it is as the user might have
813 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
815 delete_opt(options, known_languages);
818 h_preamble << "\\usepackage{babel}\n";
821 else if (name == "polyglossia") {
822 h_language_package = "default";
823 h_default_output_format = "pdf4";
824 h_use_non_tex_fonts = "true";
826 registerAutomaticallyLoadedPackage("xunicode");
827 if (h_inputencoding == "auto")
828 p.setEncoding("UTF-8");
831 else if (name == "CJK") {
832 // set the encoding to "auto" because it might be set to "default" by the babel handling
833 // and this would not be correct for CJK
834 if (h_inputencoding == "default")
835 h_inputencoding = "auto";
836 registerAutomaticallyLoadedPackage("CJK");
839 else if (name == "CJKutf8") {
840 h_inputencoding = "UTF8";
841 p.setEncoding("UTF-8");
842 registerAutomaticallyLoadedPackage("CJKutf8");
845 else if (name == "fontenc") {
846 h_fontencoding = getStringFromVector(options, ",");
847 /* We could do the following for better round trip support,
848 * but this makes the document less portable, so I skip it:
849 if (h_fontencoding == lyxrc.fontenc)
850 h_fontencoding = "global";
855 else if (name == "inputenc" || name == "luainputenc") {
856 // h_inputencoding is only set when there is not more than one
857 // inputenc option because otherwise h_inputencoding must be
858 // set to "auto" (the default encoding of the document language)
859 // Therefore check for the "," character.
860 // It is also only set when there is not more than one babel
862 if (opts.find(",") == string::npos && one_language == true)
863 h_inputencoding = opts;
864 if (!options.empty())
865 p.setEncoding(options.back(), Encoding::inputenc);
869 else if (name == "srcltx") {
872 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
875 h_output_sync_macro = "\\usepackage{srcltx}";
878 else if (is_known(name, known_old_language_packages)) {
879 // known language packages from the times before babel
880 // if they are found and not also babel, they will be used as
881 // custom language package
882 h_language_package = "\\usepackage{" + name + "}";
885 else if (name == "prettyref")
886 ; // ignore this FIXME: Use the package separator mechanism instead
888 else if (name == "lyxskak") {
889 // ignore this and its options
890 const char * const o[] = {"ps", "mover", 0};
891 delete_opt(options, o);
894 else if (is_known(name, known_lyx_packages) && options.empty()) {
895 if (name == "splitidx")
896 h_use_indices = "true";
897 if (!in_lyx_preamble) {
898 h_preamble << package_beg_sep << name
899 << package_mid_sep << "\\usepackage{"
901 if (p.next_token().cat() == catNewline ||
902 (p.next_token().cat() == catSpace &&
903 p.next_next_token().cat() == catNewline))
905 h_preamble << package_end_sep;
909 else if (name == "geometry")
910 handle_geometry(options);
912 else if (name == "subfig")
913 ; // ignore this FIXME: Use the package separator mechanism instead
915 else if ((where = is_known(name, known_languages)))
916 h_language = known_coded_languages[where - known_languages];
918 else if (name == "natbib") {
919 h_biblio_style = "plainnat";
920 h_cite_engine = "natbib";
921 h_cite_engine_type = "authoryear";
922 vector<string>::iterator it =
923 find(options.begin(), options.end(), "authoryear");
924 if (it != options.end())
927 it = find(options.begin(), options.end(), "numbers");
928 if (it != options.end()) {
929 h_cite_engine_type = "numerical";
935 else if (name == "jurabib") {
936 h_biblio_style = "jurabib";
937 h_cite_engine = "jurabib";
938 h_cite_engine_type = "authoryear";
941 else if (name == "hyperref")
942 handle_hyperref(options);
944 else if (!in_lyx_preamble) {
946 h_preamble << "\\usepackage{" << name << '}';
948 h_preamble << "\\usepackage[" << opts << "]{"
952 if (p.next_token().cat() == catNewline ||
953 (p.next_token().cat() == catSpace &&
954 p.next_next_token().cat() == catNewline))
958 // We need to do something with the options...
959 if (!options.empty())
960 cerr << "Ignoring options '" << join(options, ",")
961 << "' of package " << name << '.' << endl;
963 // remove the whitespace
968 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
971 Token t = p.get_token();
972 if (t.cat() == catEscape &&
973 is_known(t.cs(), known_if_commands))
974 handle_if(p, in_lyx_preamble);
976 if (!in_lyx_preamble)
977 h_preamble << t.asInput();
978 if (t.cat() == catEscape && t.cs() == "fi")
985 bool Preamble::writeLyXHeader(ostream & os, bool subdoc)
987 // set the quote language
988 // LyX only knows the following quotes languages:
989 // english, swedish, german, polish, french and danish
990 // (quotes for "japanese" and "chinese-traditional" are missing because
991 // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
992 // conversion list taken from
993 // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
994 // (quotes for kazakh and interlingua are unknown)
996 if (is_known(h_language, known_danish_quotes_languages))
997 h_quotes_language = "danish";
999 else if (is_known(h_language, known_french_quotes_languages))
1000 h_quotes_language = "french";
1002 else if (is_known(h_language, known_german_quotes_languages))
1003 h_quotes_language = "german";
1005 else if (is_known(h_language, known_polish_quotes_languages))
1006 h_quotes_language = "polish";
1008 else if (is_known(h_language, known_swedish_quotes_languages))
1009 h_quotes_language = "swedish";
1011 else if (is_known(h_language, known_english_quotes_languages))
1012 h_quotes_language = "english";
1014 if (contains(h_float_placement, "H"))
1015 registerAutomaticallyLoadedPackage("float");
1016 if (h_spacing != "single" && h_spacing != "default")
1017 registerAutomaticallyLoadedPackage("setspace");
1018 if (h_use_packages["amsmath"] == "2") {
1019 // amsbsy and amstext are already provided by amsmath
1020 registerAutomaticallyLoadedPackage("amsbsy");
1021 registerAutomaticallyLoadedPackage("amstext");
1024 // output the LyX file settings
1025 os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
1026 << "\\lyxformat " << LYX_FORMAT << '\n'
1027 << "\\begin_document\n"
1028 << "\\begin_header\n"
1029 << "\\textclass " << h_textclass << "\n";
1030 string const raw = subdoc ? empty_string() : h_preamble.str();
1032 os << "\\begin_preamble\n";
1033 for (string::size_type i = 0; i < raw.size(); ++i) {
1034 if (raw[i] == package_beg_sep) {
1035 // Here follows some package loading code that
1036 // must be skipped if the package is loaded
1038 string::size_type j = raw.find(package_mid_sep, i);
1039 if (j == string::npos)
1041 string::size_type k = raw.find(package_end_sep, j);
1042 if (k == string::npos)
1044 string const package = raw.substr(i + 1, j - i - 1);
1045 string const replacement = raw.substr(j + 1, k - j - 1);
1046 if (auto_packages.find(package) == auto_packages.end())
1052 os << "\n\\end_preamble\n";
1054 if (!h_options.empty())
1055 os << "\\options " << h_options << "\n";
1056 os << "\\use_default_options " << h_use_default_options << "\n";
1057 if (!used_modules.empty()) {
1058 os << "\\begin_modules\n";
1059 vector<string>::const_iterator const end = used_modules.end();
1060 vector<string>::const_iterator it = used_modules.begin();
1061 for (; it != end; ++it)
1063 os << "\\end_modules\n";
1065 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1066 << "\\language " << h_language << "\n"
1067 << "\\language_package " << h_language_package << "\n"
1068 << "\\inputencoding " << h_inputencoding << "\n"
1069 << "\\fontencoding " << h_fontencoding << "\n"
1070 << "\\font_roman " << h_font_roman << "\n"
1071 << "\\font_sans " << h_font_sans << "\n"
1072 << "\\font_typewriter " << h_font_typewriter << "\n"
1073 << "\\font_math " << h_font_math << "\n"
1074 << "\\font_default_family " << h_font_default_family << "\n"
1075 << "\\use_non_tex_fonts " << h_use_non_tex_fonts << "\n"
1076 << "\\font_sc " << h_font_sc << "\n"
1077 << "\\font_osf " << h_font_osf << "\n"
1078 << "\\font_sf_scale " << h_font_sf_scale << "\n"
1079 << "\\font_tt_scale " << h_font_tt_scale << '\n';
1080 if (!h_font_cjk.empty())
1081 os << "\\font_cjk " << h_font_cjk << '\n';
1082 os << "\\graphics " << h_graphics << '\n'
1083 << "\\default_output_format " << h_default_output_format << "\n"
1084 << "\\output_sync " << h_output_sync << "\n";
1085 if (h_output_sync == "1")
1086 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1087 os << "\\bibtex_command " << h_bibtex_command << "\n"
1088 << "\\index_command " << h_index_command << "\n";
1089 if (!h_float_placement.empty())
1090 os << "\\float_placement " << h_float_placement << "\n";
1091 os << "\\paperfontsize " << h_paperfontsize << "\n"
1092 << "\\spacing " << h_spacing << "\n"
1093 << "\\use_hyperref " << h_use_hyperref << '\n';
1094 if (h_use_hyperref == "true") {
1095 if (!h_pdf_title.empty())
1096 os << "\\pdf_title \"" << h_pdf_title << "\"\n";
1097 if (!h_pdf_author.empty())
1098 os << "\\pdf_author \"" << h_pdf_author << "\"\n";
1099 if (!h_pdf_subject.empty())
1100 os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
1101 if (!h_pdf_keywords.empty())
1102 os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
1103 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1104 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1105 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1106 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1107 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1108 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1109 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1110 "\\pdf_backref " << h_pdf_backref << "\n"
1111 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1112 if (!h_pdf_pagemode.empty())
1113 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1114 if (!h_pdf_quoted_options.empty())
1115 os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
1117 os << "\\papersize " << h_papersize << "\n"
1118 << "\\use_geometry " << h_use_geometry << '\n';
1119 for (map<string, string>::const_iterator it = h_use_packages.begin();
1120 it != h_use_packages.end(); ++it)
1121 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1122 os << "\\cite_engine " << h_cite_engine << '\n'
1123 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1124 << "\\biblio_style " << h_biblio_style << "\n"
1125 << "\\use_bibtopic " << h_use_bibtopic << "\n"
1126 << "\\use_indices " << h_use_indices << "\n"
1127 << "\\paperorientation " << h_paperorientation << '\n'
1128 << "\\suppress_date " << h_suppress_date << '\n'
1129 << "\\justification " << h_justification << '\n'
1130 << "\\use_refstyle " << h_use_refstyle << '\n';
1131 if (!h_fontcolor.empty())
1132 os << "\\fontcolor " << h_fontcolor << '\n';
1133 if (!h_notefontcolor.empty())
1134 os << "\\notefontcolor " << h_notefontcolor << '\n';
1135 if (!h_backgroundcolor.empty())
1136 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1137 if (!h_boxbgcolor.empty())
1138 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1139 os << "\\index " << h_index << '\n'
1140 << "\\shortcut " << h_shortcut << '\n'
1141 << "\\color " << h_color << '\n'
1144 << "\\secnumdepth " << h_secnumdepth << "\n"
1145 << "\\tocdepth " << h_tocdepth << "\n"
1146 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1147 if (h_paragraph_separation == "skip")
1148 os << "\\defskip " << h_defskip << "\n";
1150 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1151 os << "\\quotes_language " << h_quotes_language << "\n"
1152 << "\\papercolumns " << h_papercolumns << "\n"
1153 << "\\papersides " << h_papersides << "\n"
1154 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1155 if (!h_listings_params.empty())
1156 os << "\\listings_params " << h_listings_params << "\n";
1157 os << "\\tracking_changes " << h_tracking_changes << "\n"
1158 << "\\output_changes " << h_output_changes << "\n"
1159 << "\\html_math_output " << h_html_math_output << "\n"
1160 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1161 << "\\html_be_strict " << h_html_be_strict << "\n"
1163 << "\\end_header\n\n"
1164 << "\\begin_body\n";
1169 void Preamble::parse(Parser & p, string const & forceclass,
1170 TeX2LyXDocClass & tc)
1172 // initialize fixed types
1173 special_columns['D'] = 3;
1174 bool is_full_document = false;
1175 bool is_lyx_file = false;
1176 bool in_lyx_preamble = false;
1178 // determine whether this is a full document or a fragment for inclusion
1180 Token const & t = p.get_token();
1182 if (t.cat() == catEscape && t.cs() == "documentclass") {
1183 is_full_document = true;
1189 while (is_full_document && p.good()) {
1190 Token const & t = p.get_token();
1193 cerr << "t: " << t << "\n";
1199 if (!in_lyx_preamble &&
1200 (t.cat() == catLetter ||
1201 t.cat() == catSuper ||
1202 t.cat() == catSub ||
1203 t.cat() == catOther ||
1204 t.cat() == catMath ||
1205 t.cat() == catActive ||
1206 t.cat() == catBegin ||
1207 t.cat() == catEnd ||
1208 t.cat() == catAlign ||
1209 t.cat() == catParameter))
1210 h_preamble << t.cs();
1212 else if (!in_lyx_preamble &&
1213 (t.cat() == catSpace || t.cat() == catNewline))
1214 h_preamble << t.asInput();
1216 else if (t.cat() == catComment) {
1217 static regex const islyxfile("%% LyX .* created this file");
1218 static regex const usercommands("User specified LaTeX commands");
1220 string const comment = t.asInput();
1222 // magically switch encoding default if it looks like XeLaTeX
1223 static string const magicXeLaTeX =
1224 "% This document must be compiled with XeLaTeX ";
1225 if (comment.size() > magicXeLaTeX.size()
1226 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1227 && h_inputencoding == "auto") {
1228 cerr << "XeLaTeX comment found, switching to UTF8\n";
1229 h_inputencoding = "utf8";
1232 if (regex_search(comment, sub, islyxfile)) {
1234 in_lyx_preamble = true;
1235 } else if (is_lyx_file
1236 && regex_search(comment, sub, usercommands))
1237 in_lyx_preamble = false;
1238 else if (!in_lyx_preamble)
1239 h_preamble << t.asInput();
1242 else if (t.cs() == "pagestyle")
1243 h_paperpagestyle = p.verbatim_item();
1245 else if (t.cs() == "setdefaultlanguage") {
1247 // We don't yet care about non-language variant options
1248 // because LyX doesn't support this yet, see bug #8214
1250 string langopts = p.getOpt();
1251 // check if the option contains a variant, if yes, extract it
1252 string::size_type pos_var = langopts.find("variant");
1253 string::size_type i = langopts.find(',', pos_var);
1254 string::size_type k = langopts.find('=', pos_var);
1255 if (pos_var != string::npos){
1257 if (i == string::npos)
1258 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1260 variant = langopts.substr(k + 1, i - k - 1);
1261 h_language = variant;
1265 h_language = p.verbatim_item();
1266 //finally translate the poyglossia name to a LyX name
1267 h_language = polyglossia2lyx(h_language);
1270 else if (t.cs() == "setotherlanguage") {
1271 // We don't yet care about the option because LyX doesn't
1272 // support this yet, see bug #8214
1273 p.hasOpt() ? p.getOpt() : string();
1277 else if (t.cs() == "setmainfont") {
1278 // we don't care about the option
1279 p.hasOpt() ? p.getOpt() : string();
1280 h_font_roman = p.getArg('{', '}');
1283 else if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1284 // LyX currently only supports the scale option
1287 string fontopts = p.getArg('[', ']');
1288 // check if the option contains a scaling, if yes, extract it
1289 string::size_type pos = fontopts.find("Scale");
1290 if (pos != string::npos) {
1291 string::size_type i = fontopts.find(',', pos);
1292 if (i == string::npos)
1293 scale_as_percentage(fontopts.substr(pos + 1), scale);
1295 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1298 if (t.cs() == "setsansfont") {
1300 h_font_sf_scale = scale;
1301 h_font_sans = p.getArg('{', '}');
1304 h_font_tt_scale = scale;
1305 h_font_typewriter = p.getArg('{', '}');
1309 else if (t.cs() == "date") {
1310 string argument = p.getArg('{', '}');
1311 if (argument.empty())
1312 h_suppress_date = "true";
1314 h_preamble << t.asInput() << '{' << argument << '}';
1317 else if (t.cs() == "color") {
1318 string const space =
1319 (p.hasOpt() ? p.getOpt() : string());
1320 string argument = p.getArg('{', '}');
1321 // check the case that a standard color is used
1322 if (space.empty() && is_known(argument, known_basic_colors)) {
1323 h_fontcolor = rgbcolor2code(argument);
1324 preamble.registerAutomaticallyLoadedPackage("color");
1325 } else if (space.empty() && argument == "document_fontcolor")
1326 preamble.registerAutomaticallyLoadedPackage("color");
1327 // check the case that LyX's document_fontcolor is defined
1328 // but not used for \color
1330 h_preamble << t.asInput();
1332 h_preamble << space;
1333 h_preamble << '{' << argument << '}';
1334 // the color might already be set because \definecolor
1335 // is parsed before this
1340 else if (t.cs() == "pagecolor") {
1341 string argument = p.getArg('{', '}');
1342 // check the case that a standard color is used
1343 if (is_known(argument, known_basic_colors)) {
1344 h_backgroundcolor = rgbcolor2code(argument);
1345 } else if (argument == "page_backgroundcolor")
1346 preamble.registerAutomaticallyLoadedPackage("color");
1347 // check the case that LyX's page_backgroundcolor is defined
1348 // but not used for \pagecolor
1350 h_preamble << t.asInput() << '{' << argument << '}';
1351 // the color might already be set because \definecolor
1352 // is parsed before this
1353 h_backgroundcolor = "";
1357 else if (t.cs() == "makeatletter") {
1358 // LyX takes care of this
1359 p.setCatcode('@', catLetter);
1362 else if (t.cs() == "makeatother") {
1363 // LyX takes care of this
1364 p.setCatcode('@', catOther);
1367 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1368 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1369 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1370 || t.cs() == "DeclareRobustCommand"
1371 || t.cs() == "DeclareRobustCommandx"
1372 || t.cs() == "ProvideTextCommandDefault"
1373 || t.cs() == "DeclareMathAccent") {
1375 if (p.next_token().character() == '*') {
1379 string const name = p.verbatim_item();
1380 string const opt1 = p.getFullOpt();
1381 string const opt2 = p.getFullOpt();
1382 string const body = p.verbatim_item();
1383 // store the in_lyx_preamble setting
1384 bool const was_in_lyx_preamble = in_lyx_preamble;
1386 if (name == "\\rmdefault")
1387 if (is_known(body, known_roman_fonts)) {
1388 h_font_roman = body;
1390 in_lyx_preamble = true;
1392 if (name == "\\sfdefault")
1393 if (is_known(body, known_sans_fonts)) {
1396 in_lyx_preamble = true;
1398 if (name == "\\ttdefault")
1399 if (is_known(body, known_typewriter_fonts)) {
1400 h_font_typewriter = body;
1402 in_lyx_preamble = true;
1404 if (name == "\\familydefault") {
1405 string family = body;
1406 // remove leading "\"
1407 h_font_default_family = family.erase(0,1);
1409 in_lyx_preamble = true;
1412 if (name == "\\bfdefault")
1413 // LyX re-adds this if a kurier font is used
1414 if (is_known(h_font_sans, known_kurier_fonts) && body == "b") {
1416 in_lyx_preamble = true;
1419 // remove the lyxdot definition that is re-added by LyX
1421 if (name == "\\lyxdot") {
1423 in_lyx_preamble = true;
1426 // Add the command to the known commands
1427 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1429 // only non-lyxspecific stuff
1430 if (!in_lyx_preamble) {
1432 ss << '\\' << t.cs();
1435 ss << '{' << name << '}' << opt1 << opt2
1436 << '{' << body << "}";
1437 h_preamble << ss.str();
1439 ostream & out = in_preamble ? h_preamble : os;
1440 out << "\\" << t.cs() << "{" << name << "}"
1441 << opts << "{" << body << "}";
1444 // restore the in_lyx_preamble setting
1445 in_lyx_preamble = was_in_lyx_preamble;
1448 else if (t.cs() == "edef"){
1449 // we only support this for kurier fonts
1450 string const command = p.next_token().asInput();
1452 if (command == "\\sfdefault") {
1454 if (h_font_sans == "kurier")
1455 h_font_sans = "kurier-condensed";
1456 if (h_font_sans == "kurierl")
1457 h_font_sans = "kurier-light-condensed";
1461 h_preamble << "\\edef" << command << "{" << p.getArg('{', '}') << "}\n";
1464 else if (t.cs() == "documentclass") {
1465 vector<string>::iterator it;
1466 vector<string> opts = split_options(p.getArg('[', ']'));
1467 handle_opt(opts, known_fontsizes, h_paperfontsize);
1468 delete_opt(opts, known_fontsizes);
1469 // delete "pt" at the end
1470 string::size_type i = h_paperfontsize.find("pt");
1471 if (i != string::npos)
1472 h_paperfontsize.erase(i);
1473 // The documentclass options are always parsed before the options
1474 // of the babel call so that a language cannot overwrite the babel
1476 handle_opt(opts, known_languages, h_language);
1477 delete_opt(opts, known_languages);
1479 // paper orientation
1480 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1481 h_paperorientation = "landscape";
1485 if ((it = find(opts.begin(), opts.end(), "oneside"))
1490 if ((it = find(opts.begin(), opts.end(), "twoside"))
1496 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1498 h_papercolumns = "1";
1501 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1503 h_papercolumns = "2";
1507 // some size options are known to any document classes, other sizes
1508 // are handled by the \geometry command of the geometry package
1509 handle_opt(opts, known_class_paper_sizes, h_papersize);
1510 delete_opt(opts, known_class_paper_sizes);
1511 // the remaining options
1512 h_options = join(opts, ",");
1513 // FIXME This does not work for classes that have a
1514 // different name in LyX than in LaTeX
1515 h_textclass = p.getArg('{', '}');
1519 else if (t.cs() == "usepackage") {
1520 string const options = p.getArg('[', ']');
1521 string const name = p.getArg('{', '}');
1522 vector<string> vecnames;
1523 split(name, vecnames, ',');
1524 vector<string>::const_iterator it = vecnames.begin();
1525 vector<string>::const_iterator end = vecnames.end();
1526 for (; it != end; ++it)
1527 handle_package(p, trimSpaceAndEol(*it), options,
1531 else if (t.cs() == "inputencoding") {
1532 string const encoding = p.getArg('{','}');
1533 h_inputencoding = encoding;
1534 p.setEncoding(encoding, Encoding::inputenc);
1537 else if (t.cs() == "newenvironment") {
1538 string const name = p.getArg('{', '}');
1539 string const opt1 = p.getFullOpt();
1540 string const opt2 = p.getFullOpt();
1541 string const beg = p.verbatim_item();
1542 string const end = p.verbatim_item();
1543 if (!in_lyx_preamble) {
1544 h_preamble << "\\newenvironment{" << name
1545 << '}' << opt1 << opt2 << '{'
1546 << beg << "}{" << end << '}';
1548 add_known_environment(name, opt1, !opt2.empty(),
1549 from_utf8(beg), from_utf8(end));
1553 else if (t.cs() == "newtheorem") {
1554 string const name = p.getArg('{', '}');
1555 string const opt1 = p.getFullOpt();
1556 string const opt2 = p.getFullOpt();
1557 string const body = p.verbatim_item();
1558 string const opt3 = p.getFullOpt();
1560 add_known_theorem(name, opt1, !opt2.empty(),
1561 from_utf8("\\newtheorem{" + name + '}' +
1562 opt1 + opt2 + '{' + body + '}' + opt3));
1564 if (!in_lyx_preamble)
1565 h_preamble << "\\newtheorem{" << name << '}'
1566 << opt1 << opt2 << '{' << '}' << opt3;
1569 else if (t.cs() == "def") {
1570 string name = p.get_token().cs();
1571 // In fact, name may be more than the name:
1572 // In the test case of bug 8116
1573 // name == "csname SF@gobble@opt \endcsname".
1574 // Therefore, we need to use asInput() instead of cs().
1575 while (p.next_token().cat() != catBegin)
1576 name += p.get_token().asInput();
1577 if (!in_lyx_preamble)
1578 h_preamble << "\\def\\" << name << '{'
1579 << p.verbatim_item() << "}";
1582 else if (t.cs() == "newcolumntype") {
1583 string const name = p.getArg('{', '}');
1584 trimSpaceAndEol(name);
1586 string opts = p.getOpt();
1587 if (!opts.empty()) {
1588 istringstream is(string(opts, 1));
1591 special_columns[name[0]] = nargs;
1592 h_preamble << "\\newcolumntype{" << name << "}";
1594 h_preamble << "[" << nargs << "]";
1595 h_preamble << "{" << p.verbatim_item() << "}";
1598 else if (t.cs() == "setcounter") {
1599 string const name = p.getArg('{', '}');
1600 string const content = p.getArg('{', '}');
1601 if (name == "secnumdepth")
1602 h_secnumdepth = content;
1603 else if (name == "tocdepth")
1604 h_tocdepth = content;
1606 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1609 else if (t.cs() == "setlength") {
1610 string const name = p.verbatim_item();
1611 string const content = p.verbatim_item();
1612 // the paragraphs are only not indented when \parindent is set to zero
1613 if (name == "\\parindent" && content != "") {
1614 if (content[0] == '0')
1615 h_paragraph_separation = "skip";
1617 h_paragraph_indentation = translate_len(content);
1618 } else if (name == "\\parskip") {
1619 if (content == "\\smallskipamount")
1620 h_defskip = "smallskip";
1621 else if (content == "\\medskipamount")
1622 h_defskip = "medskip";
1623 else if (content == "\\bigskipamount")
1624 h_defskip = "bigskip";
1626 h_defskip = content;
1628 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1631 else if (t.cs() == "onehalfspacing")
1632 h_spacing = "onehalf";
1634 else if (t.cs() == "doublespacing")
1635 h_spacing = "double";
1637 else if (t.cs() == "setstretch")
1638 h_spacing = "other " + p.verbatim_item();
1640 else if (t.cs() == "synctex") {
1641 // the scheme is \synctex=value
1642 // where value can only be "1" or "-1"
1643 h_output_sync = "1";
1644 // there can be any character behind the value (e.g. a linebreak or a '\'
1645 // therefore we extract it char by char
1647 string value = p.get_token().asInput();
1649 value += p.get_token().asInput();
1650 h_output_sync_macro = "\\synctex=" + value;
1653 else if (t.cs() == "begin") {
1654 string const name = p.getArg('{', '}');
1655 if (name == "document")
1657 h_preamble << "\\begin{" << name << "}";
1660 else if (t.cs() == "geometry") {
1661 vector<string> opts = split_options(p.getArg('{', '}'));
1662 handle_geometry(opts);
1665 else if (t.cs() == "definecolor") {
1666 string const color = p.getArg('{', '}');
1667 string const space = p.getArg('{', '}');
1668 string const value = p.getArg('{', '}');
1669 if (color == "document_fontcolor" && space == "rgb") {
1670 RGBColor c(RGBColorFromLaTeX(value));
1671 h_fontcolor = X11hexname(c);
1672 } else if (color == "note_fontcolor" && space == "rgb") {
1673 RGBColor c(RGBColorFromLaTeX(value));
1674 h_notefontcolor = X11hexname(c);
1675 } else if (color == "page_backgroundcolor" && space == "rgb") {
1676 RGBColor c(RGBColorFromLaTeX(value));
1677 h_backgroundcolor = X11hexname(c);
1678 } else if (color == "shadecolor" && space == "rgb") {
1679 RGBColor c(RGBColorFromLaTeX(value));
1680 h_boxbgcolor = X11hexname(c);
1682 h_preamble << "\\definecolor{" << color
1683 << "}{" << space << "}{" << value
1688 else if (t.cs() == "bibliographystyle")
1689 h_biblio_style = p.verbatim_item();
1691 else if (t.cs() == "jurabibsetup") {
1692 // FIXME p.getArg('{', '}') is most probably wrong (it
1693 // does not handle nested braces).
1694 // Use p.verbatim_item() instead.
1695 vector<string> jurabibsetup =
1696 split_options(p.getArg('{', '}'));
1697 // add jurabibsetup to the jurabib package options
1698 add_package("jurabib", jurabibsetup);
1699 if (!jurabibsetup.empty()) {
1700 h_preamble << "\\jurabibsetup{"
1701 << join(jurabibsetup, ",") << '}';
1705 else if (t.cs() == "hypersetup") {
1706 vector<string> hypersetup =
1707 split_options(p.verbatim_item());
1708 // add hypersetup to the hyperref package options
1709 handle_hyperref(hypersetup);
1710 if (!hypersetup.empty()) {
1711 h_preamble << "\\hypersetup{"
1712 << join(hypersetup, ",") << '}';
1716 else if (is_known(t.cs(), known_if_3arg_commands)) {
1717 // prevent misparsing of \usepackage if it is used
1718 // as an argument (see e.g. our own output of
1719 // \@ifundefined above)
1720 string const arg1 = p.verbatim_item();
1721 string const arg2 = p.verbatim_item();
1722 string const arg3 = p.verbatim_item();
1723 // test case \@ifundefined{date}{}{\date{}}
1724 if (t.cs() == "@ifundefined" && arg1 == "date" &&
1725 arg2.empty() && arg3 == "\\date{}") {
1726 h_suppress_date = "true";
1727 // older tex2lyx versions did output
1728 // \@ifundefined{definecolor}{\usepackage{color}}{}
1729 } else if (t.cs() == "@ifundefined" &&
1730 arg1 == "definecolor" &&
1731 arg2 == "\\usepackage{color}" &&
1733 if (!in_lyx_preamble)
1734 h_preamble << package_beg_sep
1737 << "\\@ifundefined{definecolor}{color}{}"
1740 //\@ifundefined{showcaptionsetup}{}{%
1741 // \PassOptionsToPackage{caption=false}{subfig}}
1742 // that LyX uses for subfloats
1743 } else if (t.cs() == "@ifundefined" &&
1744 arg1 == "showcaptionsetup" && arg2.empty()
1745 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1747 } else if (!in_lyx_preamble) {
1748 h_preamble << t.asInput()
1749 << '{' << arg1 << '}'
1750 << '{' << arg2 << '}'
1751 << '{' << arg3 << '}';
1755 else if (is_known(t.cs(), known_if_commands)) {
1756 // must not parse anything in conditional code, since
1757 // LyX would output the parsed contents unconditionally
1758 if (!in_lyx_preamble)
1759 h_preamble << t.asInput();
1760 handle_if(p, in_lyx_preamble);
1763 else if (!t.cs().empty() && !in_lyx_preamble)
1764 h_preamble << '\\' << t.cs();
1767 // remove the whitespace
1770 // Force textclass if the user wanted it
1771 if (!forceclass.empty())
1772 h_textclass = forceclass;
1773 tc.setName(h_textclass);
1775 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1778 if (h_papersides.empty()) {
1781 h_papersides = ss.str();
1784 // If the CJK package is used we cannot set the document language from
1785 // the babel options. Instead, we guess which language is used most
1786 // and set this one.
1787 default_language = h_language;
1788 if (is_full_document &&
1789 (auto_packages.find("CJK") != auto_packages.end() ||
1790 auto_packages.find("CJKutf8") != auto_packages.end())) {
1792 h_language = guessLanguage(p, default_language);
1798 string babel2lyx(string const & language)
1800 char const * const * where = is_known(language, known_languages);
1802 return known_coded_languages[where - known_languages];
1807 string Preamble::polyglossia2lyx(string const & language)
1809 char const * const * where = is_known(language, polyglossia_languages);
1811 return coded_polyglossia_languages[where - polyglossia_languages];
1816 string rgbcolor2code(string const & name)
1818 char const * const * where = is_known(name, known_basic_colors);
1820 // "red", "green" etc
1821 return known_basic_color_codes[where - known_basic_colors];
1823 // "255,0,0", "0,255,0" etc
1824 RGBColor c(RGBColorFromLaTeX(name));
1825 return X11hexname(c);