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"
26 #include "support/convert.h"
27 #include "support/FileName.h"
28 #include "support/filetools.h"
29 #include "support/lstrings.h"
31 #include "support/regex.h"
37 using namespace lyx::support;
42 // special columntypes
43 extern map<char, int> special_columns;
49 // CJK languages are handled in text.cpp, polyglossia languages are listed
52 * known babel language names (including synonyms)
53 * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
54 * please keep this in sync with known_coded_languages line by line!
56 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
57 "american", "arabic", "arabtex", "australian", "austrian", "bahasa", "bahasai",
58 "bahasam", "basque", "belarusian", "brazil", "brazilian", "breton", "british",
59 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
60 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "francais",
61 "french", "frenchb", "frenchle", "frenchpro", "galician", "german", "germanb",
62 "greek", "hebrew", "hungarian", "icelandic", "indon", "indonesian", "interlingua",
63 "irish", "italian", "japanese", "kazakh", "kurmanji", "latin", "latvian", "lithuanian",
64 "lowersorbian", "lsorbian", "magyar", "malay", "meyalu", "mongolian", "naustrian",
65 "newzealand", "ngerman", "ngermanb", "norsk", "nynorsk", "polutonikogreek", "polish",
66 "portuges", "portuguese", "romanian", "russian", "russianb", "samin",
67 "scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish",
68 "swedish", "thai", "turkish", "turkmen", "ukraineb", "ukrainian",
69 "uppersorbian", "UKenglish", "USenglish", "usorbian", "vietnam", "welsh",
73 * the same as known_languages with .lyx names
74 * please keep this in sync with known_languages line by line!
76 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
77 "american", "arabic_arabi", "arabic_arabtex", "australian", "austrian", "bahasa", "bahasa",
78 "bahasam", "basque", "belarusian", "brazilian", "brazilian", "breton", "british",
79 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
80 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "french",
81 "french", "french", "french", "french", "galician", "german", "german",
82 "greek", "hebrew", "magyar", "icelandic", "bahasa", "bahasa", "interlingua",
83 "irish", "italian", "japanese", "kazakh", "kurmanji", "latin", "latvian", "lithuanian",
84 "lowersorbian", "lowersorbian", "magyar", "bahasam", "bahasam", "mongolian", "naustrian",
85 "newzealand", "ngerman", "ngerman", "norsk", "nynorsk", "polutonikogreek", "polish",
86 "portuguese", "portuguese", "romanian", "russian", "russian", "samin",
87 "scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish",
88 "swedish", "thai", "turkish", "turkmen", "ukrainian", "ukrainian",
89 "uppersorbian", "uppersorbian", "english", "english", "vietnamese", "welsh",
92 /// languages with danish quotes (.lyx names)
93 const char * const known_danish_quotes_languages[] = {"danish", 0};
95 /// languages with english quotes (.lyx names)
96 const char * const known_english_quotes_languages[] = {"american", "australian",
97 "bahasa", "bahasam", "brazilian", "canadian", "chinese-simplified", "english",
98 "esperanto", "hebrew", "irish", "korean", "newzealand", "portuguese", "scottish",
101 /// languages with french quotes (.lyx names)
102 const char * const known_french_quotes_languages[] = {"albanian",
103 "arabic_arabi", "arabic_arabtex", "basque", "canadien", "catalan", "french",
104 "galician", "greek", "italian", "norsk", "nynorsk", "polutonikogreek",
105 "russian", "spanish", "spanish-mexico", "turkish", "turkmen", "ukrainian",
108 /// languages with german quotes (.lyx names)
109 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
110 "czech", "german", "icelandic", "lithuanian", "lowersorbian", "naustrian",
111 "ngerman", "serbian", "serbian-latin", "slovak", "slovene", "uppersorbian", 0};
113 /// languages with polish quotes (.lyx names)
114 const char * const known_polish_quotes_languages[] = {"afrikaans", "croatian",
115 "dutch", "estonian", "magyar", "polish", "romanian", 0};
117 /// languages with swedish quotes (.lyx names)
118 const char * const known_swedish_quotes_languages[] = {"finnish",
121 /// known language packages from the times before babel
122 const char * const known_old_language_packages[] = {"french", "frenchle",
123 "frenchpro", "german", "ngerman", "pmfrench", 0};
125 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
127 const char * const known_roman_fonts[] = { "ae", "beraserif", "bookman",
128 "ccfonts", "chancery", "charter", "cmr", "fourier", "garamondx", "libertine",
129 "libertine-type1", "lmodern", "mathdesign", "mathpazo", "mathptmx", "newcent",
130 "tgbonum", "tgchorus", "tgpagella", "tgschola", "tgtermes", "utopia", 0};
132 const char * const known_sans_fonts[] = { "avant", "berasans", "biolinum-type1",
133 "cmbr", "cmss", "helvet", "iwona", "iwonac", "iwonal", "iwonalc", "kurier",
134 "kurierc", "kurierl", "kurierlc", "lmss", "tgadventor", "tgheros", 0};
136 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
137 "courier", "lmtt", "luximono", "fourier", "lmodern", "mathpazo", "mathptmx",
138 "newcent", "tgcursor", "txtt", 0};
140 const char * const known_math_fonts[] = { "eulervm", "newtxmath", 0};
142 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
143 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
144 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
145 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
146 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
148 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
149 "executivepaper", "legalpaper", "letterpaper", 0};
151 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
152 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
154 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
155 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
158 /// commands that can start an \if...\else...\endif sequence
159 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
160 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
161 "ifsidecap", "ifupgreek", 0};
163 const char * const known_basic_colors[] = {"blue", "black", "cyan", "green",
164 "magenta", "red", "white", "yellow", 0};
166 const char * const known_basic_color_codes[] = {"#0000ff", "#000000", "#00ffff",
167 "#00ff00", "#ff00ff", "#ff0000", "#ffffff", "#ffff00", 0};
169 /// conditional commands with three arguments like \@ifundefined{}{}{}
170 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
173 /// packages that work only in xetex
174 /// polyglossia is handled separately
175 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
176 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
177 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
179 /// packages that are automatically skipped if loaded by LyX
180 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
181 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color",
182 "float", "fontspec", "graphicx", "hhline", "ifthen", "longtable", "makeidx",
183 "multirow", "nomencl", "pdfpages", "prettyref", "refstyle", "rotating",
184 "rotfloat", "splitidx", "setspace", "subscript", "textcomp", "tipa", "tipx",
185 "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xunicode", 0};
187 // used for the handling of \newindex
188 int index_number = 0;
190 // codes used to remove packages that are loaded automatically by LyX.
191 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
192 const char package_beg_sep = '\001';
193 const char package_mid_sep = '\002';
194 const char package_end_sep = '\003';
197 // returns true if at least one of the options in what has been found
198 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
204 // the last language option is the document language (for babel and LyX)
205 // the last size option is the document font size
206 vector<string>::iterator it;
207 vector<string>::iterator position = opts.begin();
208 for (; *what; ++what) {
209 it = find(opts.begin(), opts.end(), *what);
210 if (it != opts.end()) {
211 if (it >= position) {
222 void delete_opt(vector<string> & opts, char const * const * what)
227 // remove found options from the list
228 // do this after handle_opt to avoid potential memory leaks
229 vector<string>::iterator it;
230 for (; *what; ++what) {
231 it = find(opts.begin(), opts.end(), *what);
232 if (it != opts.end())
239 * Split a package options string (keyval format) into a vector.
241 * authorformat=smallcaps,
243 * titleformat=colonsep,
244 * bibformat={tabular,ibidem,numbered}
246 vector<string> split_options(string const & input)
248 vector<string> options;
252 Token const & t = p.get_token();
253 if (t.asInput() == ",") {
254 options.push_back(trimSpaceAndEol(option));
256 } else if (t.asInput() == "=") {
259 if (p.next_token().asInput() == "{")
260 option += '{' + p.getArg('{', '}') + '}';
261 } else if (t.cat() != catSpace)
262 option += t.asInput();
266 options.push_back(trimSpaceAndEol(option));
273 * Retrieve a keyval option "name={value with=sign}" named \p name from
274 * \p options and return the value.
275 * The found option is also removed from \p options.
277 string process_keyval_opt(vector<string> & options, string name)
279 for (size_t i = 0; i < options.size(); ++i) {
280 vector<string> option;
281 split(options[i], option, '=');
282 if (option.size() < 2)
284 if (option[0] == name) {
285 options.erase(options.begin() + i);
286 option.erase(option.begin());
287 return join(option, "=");
293 } // anonymous namespace
297 * known polyglossia language names (including variants)
299 const char * const Preamble::polyglossia_languages[] = {
300 "albanian", "croatian", "hebrew", "norsk", "swedish", "amharic", "czech", "hindi",
301 "nynorsk", "syriac", "arabic", "danish", "icelandic", "occitan", "tamil",
302 "armenian", "divehi", "interlingua", "polish", "telugu", "asturian", "dutch",
303 "irish", "portuges", "thai", "bahasai", "english", "italian", "romanian", "turkish",
304 "bahasam", "esperanto", "lao", "russian", "turkmen", "basque", "estonian", "latin",
305 "samin", "ukrainian", "bengali", "farsi", "latvian", "sanskrit", "tibetan", "urdu",
306 "brazil", "brazilian", "finnish", "lithuanian", "scottish", "usorbian", "breton",
307 "french", "lsorbian", "serbian", "vietnamese", "bulgarian", "galician", "magyar",
308 "slovak", "welsh", "catalan", "german", "malayalam", "slovenian", "coptic", "greek",
309 "marathi", "spanish",
310 "american", "ancient", "australian", "british", "monotonic", "newzealand",
314 * the same as polyglossia_languages with .lyx names
315 * please keep this in sync with polyglossia_languages line by line!
317 const char * const Preamble::coded_polyglossia_languages[] = {
318 "albanian", "croatian", "hebrew", "norsk", "swedish", "amharic", "czech", "hindi",
319 "nynorsk", "syriac", "arabic_arabi", "danish", "icelandic", "occitan", "tamil",
320 "armenian", "divehi", "interlingua", "polish", "telugu", "asturian", "dutch",
321 "irish", "portuges", "thai", "bahasa", "english", "italian", "romanian", "turkish",
322 "bahasam", "esperanto", "lao", "russian", "turkmen", "basque", "estonian", "latin",
323 "samin", "ukrainian", "bengali", "farsi", "latvian", "sanskrit", "tibetan", "urdu",
324 "brazilian", "brazilian", "finnish", "lithuanian", "scottish", "uppersorbian", "breton",
325 "french", "lowersorbian", "serbian", "vietnamese", "bulgarian", "galician", "magyar",
326 "slovak", "welsh", "catalan", "ngerman", "malayalam", "slovene", "coptic", "greek",
327 "marathi", "spanish",
328 "american", "ancientgreek", "australian", "british", "greek", "newzealand",
329 "polutonikogreek", 0};
332 bool Preamble::indentParagraphs() const
334 return h_paragraph_separation == "indent";
338 bool Preamble::isPackageUsed(string const & package) const
340 return used_packages.find(package) != used_packages.end();
344 vector<string> Preamble::getPackageOptions(string const & package) const
346 map<string, vector<string> >::const_iterator it = used_packages.find(package);
347 if (it != used_packages.end())
349 return vector<string>();
353 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
355 auto_packages.insert(package);
359 void Preamble::addModule(string const & module)
361 used_modules.push_back(module);
365 void Preamble::suppressDate(bool suppress)
368 h_suppress_date = "true";
370 h_suppress_date = "false";
374 void Preamble::registerAuthor(std::string const & name)
376 Author author(from_utf8(name), empty_docstring());
377 author.setUsed(true);
378 authors_.record(author);
379 h_tracking_changes = "true";
380 h_output_changes = "true";
384 Author const & Preamble::getAuthor(std::string const & name) const
386 Author author(from_utf8(name), empty_docstring());
387 for (AuthorList::Authors::const_iterator it = authors_.begin();
388 it != authors_.end(); ++it)
391 static Author const dummy;
396 void Preamble::add_package(string const & name, vector<string> & options)
398 // every package inherits the global options
399 if (used_packages.find(name) == used_packages.end())
400 used_packages[name] = split_options(h_options);
402 vector<string> & v = used_packages[name];
403 v.insert(v.end(), options.begin(), options.end());
404 if (name == "jurabib") {
405 // Don't output the order argument (see the cite command
406 // handling code in text.cpp).
407 vector<string>::iterator end =
408 remove(options.begin(), options.end(), "natbiborder");
409 end = remove(options.begin(), end, "jurabiborder");
410 options.erase(end, options.end());
417 // Given is a string like "scaled=0.9" or "Scale=0.9", return 0.9 * 100
418 bool scale_as_percentage(string const & scale, string & percentage)
420 string::size_type pos = scale.find('=');
421 if (pos != string::npos) {
422 string value = scale.substr(pos + 1);
423 if (isStrDbl(value)) {
424 percentage = convert<string>(100 * convert<double>(value));
432 string remove_braces(string const & value)
436 if (value[0] == '{' && value[value.length()-1] == '}')
437 return value.substr(1, value.length()-2);
441 } // anonymous namespace
444 Preamble::Preamble() : one_language(true), explicit_babel(false),
445 title_layout_found(false), h_font_cjk_set(false)
449 h_biblio_style = "plain";
450 h_bibtex_command = "default";
451 h_cite_engine = "basic";
452 h_cite_engine_type = "default";
454 h_defskip = "medskip";
457 h_fontencoding = "default";
458 h_font_roman = "default";
459 h_font_sans = "default";
460 h_font_typewriter = "default";
461 h_font_math = "auto";
462 h_font_default_family = "default";
463 h_use_non_tex_fonts = "false";
465 h_font_osf = "false";
466 h_font_sf_scale = "100";
467 h_font_tt_scale = "100";
469 h_graphics = "default";
470 h_default_output_format = "default";
471 h_html_be_strict = "false";
472 h_html_css_as_file = "0";
473 h_html_math_output = "0";
474 h_index[0] = "Index";
475 h_index_command = "default";
476 h_inputencoding = "auto";
477 h_justification = "true";
478 h_language = "english";
479 h_language_package = "none";
481 h_maintain_unincluded_children = "false";
485 h_output_changes = "false";
487 //h_output_sync_macro
488 h_papercolumns = "1";
489 h_paperfontsize = "default";
490 h_paperorientation = "portrait";
491 h_paperpagestyle = "default";
493 h_papersize = "default";
494 h_paragraph_indentation = "default";
495 h_paragraph_separation = "indent";
500 h_pdf_bookmarks = "0";
501 h_pdf_bookmarksnumbered = "0";
502 h_pdf_bookmarksopen = "0";
503 h_pdf_bookmarksopenlevel = "1";
504 h_pdf_breaklinks = "0";
505 h_pdf_pdfborder = "0";
506 h_pdf_colorlinks = "0";
507 h_pdf_backref = "section";
508 h_pdf_pdfusetitle = "0";
510 //h_pdf_quoted_options;
511 h_quotes_language = "english";
513 h_shortcut[0] = "idx";
514 h_spacing = "single";
515 h_suppress_date = "false";
516 h_textclass = "article";
518 h_tracking_changes = "false";
519 h_use_bibtopic = "false";
520 h_use_indices = "false";
521 h_use_geometry = "false";
522 h_use_default_options = "false";
523 h_use_hyperref = "false";
524 h_use_refstyle = false;
525 h_use_packages["amsmath"] = "1";
526 h_use_packages["amssymb"] = "0";
527 h_use_packages["cancel"] = "0";
528 h_use_packages["esint"] = "1";
529 h_use_packages["mhchem"] = "0";
530 h_use_packages["mathdots"] = "0";
531 h_use_packages["mathtools"] = "0";
532 h_use_packages["stackrel"] = "0";
533 h_use_packages["stmaryrd"] = "0";
534 h_use_packages["undertilde"] = "0";
538 void Preamble::handle_hyperref(vector<string> & options)
540 // FIXME swallow inputencoding changes that might surround the
541 // hyperref setup if it was written by LyX
542 h_use_hyperref = "true";
543 // swallow "unicode=true", since LyX does always write that
544 vector<string>::iterator it =
545 find(options.begin(), options.end(), "unicode=true");
546 if (it != options.end())
548 it = find(options.begin(), options.end(), "pdfusetitle");
549 if (it != options.end()) {
550 h_pdf_pdfusetitle = "1";
553 string bookmarks = process_keyval_opt(options, "bookmarks");
554 if (bookmarks == "true")
555 h_pdf_bookmarks = "1";
556 else if (bookmarks == "false")
557 h_pdf_bookmarks = "0";
558 if (h_pdf_bookmarks == "1") {
559 string bookmarksnumbered =
560 process_keyval_opt(options, "bookmarksnumbered");
561 if (bookmarksnumbered == "true")
562 h_pdf_bookmarksnumbered = "1";
563 else if (bookmarksnumbered == "false")
564 h_pdf_bookmarksnumbered = "0";
565 string bookmarksopen =
566 process_keyval_opt(options, "bookmarksopen");
567 if (bookmarksopen == "true")
568 h_pdf_bookmarksopen = "1";
569 else if (bookmarksopen == "false")
570 h_pdf_bookmarksopen = "0";
571 if (h_pdf_bookmarksopen == "1") {
572 string bookmarksopenlevel =
573 process_keyval_opt(options, "bookmarksopenlevel");
574 if (!bookmarksopenlevel.empty())
575 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
578 string breaklinks = process_keyval_opt(options, "breaklinks");
579 if (breaklinks == "true")
580 h_pdf_breaklinks = "1";
581 else if (breaklinks == "false")
582 h_pdf_breaklinks = "0";
583 string pdfborder = process_keyval_opt(options, "pdfborder");
584 if (pdfborder == "{0 0 0}")
585 h_pdf_pdfborder = "1";
586 else if (pdfborder == "{0 0 1}")
587 h_pdf_pdfborder = "0";
588 string backref = process_keyval_opt(options, "backref");
589 if (!backref.empty())
590 h_pdf_backref = backref;
591 string colorlinks = process_keyval_opt(options, "colorlinks");
592 if (colorlinks == "true")
593 h_pdf_colorlinks = "1";
594 else if (colorlinks == "false")
595 h_pdf_colorlinks = "0";
596 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
597 if (!pdfpagemode.empty())
598 h_pdf_pagemode = pdfpagemode;
599 string pdftitle = process_keyval_opt(options, "pdftitle");
600 if (!pdftitle.empty()) {
601 h_pdf_title = remove_braces(pdftitle);
603 string pdfauthor = process_keyval_opt(options, "pdfauthor");
604 if (!pdfauthor.empty()) {
605 h_pdf_author = remove_braces(pdfauthor);
607 string pdfsubject = process_keyval_opt(options, "pdfsubject");
608 if (!pdfsubject.empty())
609 h_pdf_subject = remove_braces(pdfsubject);
610 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
611 if (!pdfkeywords.empty())
612 h_pdf_keywords = remove_braces(pdfkeywords);
613 if (!options.empty()) {
614 if (!h_pdf_quoted_options.empty())
615 h_pdf_quoted_options += ',';
616 h_pdf_quoted_options += join(options, ",");
622 void Preamble::handle_geometry(vector<string> & options)
624 h_use_geometry = "true";
625 vector<string>::iterator it;
627 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
628 h_paperorientation = "landscape";
632 // keyval version: "paper=letter"
633 string paper = process_keyval_opt(options, "paper");
635 h_papersize = paper + "paper";
636 // alternative version: "letterpaper"
637 handle_opt(options, known_paper_sizes, h_papersize);
638 delete_opt(options, known_paper_sizes);
640 char const * const * margin = known_paper_margins;
641 for (; *margin; ++margin) {
642 string value = process_keyval_opt(options, *margin);
643 if (!value.empty()) {
644 int k = margin - known_paper_margins;
645 string name = known_coded_paper_margins[k];
646 h_margins += '\\' + name + ' ' + value + '\n';
652 void Preamble::handle_package(Parser &p, string const & name,
653 string const & opts, bool in_lyx_preamble)
655 vector<string> options = split_options(opts);
656 add_package(name, options);
657 char const * const * where = 0;
659 if (is_known(name, known_xetex_packages)) {
661 h_use_non_tex_fonts = "true";
662 registerAutomaticallyLoadedPackage("fontspec");
663 if (h_inputencoding == "auto")
664 p.setEncoding("UTF-8");
668 if (is_known(name, known_roman_fonts))
671 if (name == "fourier") {
672 h_font_roman = "utopia";
673 // when font uses real small capitals
674 if (opts == "expert")
678 if (name == "garamondx") {
679 h_font_roman = "garamondx";
684 if (name == "libertine") {
685 h_font_roman = "libertine";
686 // this automatically invokes biolinum
687 h_font_sans = "biolinum";
690 else if (opts == "lining")
691 h_font_osf = "false";
694 if (name == "libertine-type1") {
695 h_font_roman = "libertine";
696 // NOTE: contrary to libertine.sty, libertine-type1
697 // does not automatically invoke biolinum
698 if (opts == "lining")
699 h_font_osf = "false";
700 else if (opts == "osf")
704 if (name == "mathdesign") {
705 if (opts.find("charter") != string::npos)
706 h_font_roman = "md-charter";
707 if (opts.find("garamond") != string::npos)
708 h_font_roman = "md-garamond";
709 if (opts.find("utopia") != string::npos)
710 h_font_roman = "md-utopia";
711 if (opts.find("expert") != string::npos) {
717 else if (name == "mathpazo")
718 h_font_roman = "palatino";
720 else if (name == "mathptmx")
721 h_font_roman = "times";
724 if (is_known(name, known_sans_fonts)) {
726 if (options.size() == 1) {
727 if (scale_as_percentage(opts, h_font_sf_scale))
732 if (name == "biolinum-type1")
733 h_font_sans = "biolinum";
736 if (is_known(name, known_typewriter_fonts)) {
737 // fourier can be set as roman font _only_
738 // fourier as typewriter is handled in handling of \ttdefault
739 if (name != "fourier") {
740 h_font_typewriter = name;
741 if (options.size() == 1) {
742 if (scale_as_percentage(opts, h_font_tt_scale))
748 // font uses old-style figure
753 if (is_known(name, known_math_fonts))
756 if (name == "newtxmath") {
758 h_font_math = "newtxmath";
759 else if (opts == "garamondx")
760 h_font_math = "garamondx-ntxm";
761 else if (opts == "libertine")
762 h_font_math = "libertine-ntxm";
763 else if (opts == "minion")
764 h_font_math = "minion-ntxm";
767 // after the detection and handling of special cases, we can remove the
768 // fonts, otherwise they would appear in the preamble, see bug #7856
769 if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
770 || is_known(name, known_typewriter_fonts) || is_known(name, known_math_fonts))
773 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
774 name == "esint" || name == "mhchem" || name == "mathdots" ||
775 name == "mathtools" || name == "stackrel" ||
776 name == "stmaryrd" || name == "undertilde")
777 h_use_packages[name] = "2";
779 else if (name == "babel") {
780 h_language_package = "default";
781 // One might think we would have to do nothing if babel is loaded
782 // without any options to prevent pollution of the preamble with this
783 // babel call in every roundtrip.
784 // But the user could have defined babel-specific things afterwards. So
785 // we need to keep it in the preamble to prevent cases like bug #7861.
787 // check if more than one option was used - used later for inputenc
788 if (options.begin() != options.end() - 1)
789 one_language = false;
790 // babel takes the last language of the option of its \usepackage
791 // call as document language. If there is no such language option, the
792 // last language in the documentclass options is used.
793 handle_opt(options, known_languages, h_language);
794 // translate the babel name to a LyX name
795 h_language = babel2lyx(h_language);
796 if (h_language == "japanese") {
797 // For Japanese, the encoding isn't indicated in the source
798 // file, and there's really not much we can do. We could
799 // 1) offer a list of possible encodings to choose from, or
800 // 2) determine the encoding of the file by inspecting it.
801 // For the time being, we leave the encoding alone so that
802 // we don't get iconv errors when making a wrong guess, and
803 // we will output a note at the top of the document
804 // explaining what to do.
805 Encoding const * const enc = encodings.fromIconvName(
806 p.getEncoding(), Encoding::japanese, false);
808 h_inputencoding = enc->name();
809 is_nonCJKJapanese = true;
810 // in this case babel can be removed from the preamble
811 registerAutomaticallyLoadedPackage("babel");
813 // If babel is called with options, LyX puts them by default into the
814 // document class options. This works for most languages, except
815 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
816 // perhaps in future others.
817 // Therefore keep the babel call as it is as the user might have
819 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
821 delete_opt(options, known_languages);
823 h_preamble << "\\usepackage{babel}\n";
824 explicit_babel = true;
828 else if (name == "polyglossia") {
829 h_language_package = "default";
830 h_default_output_format = "pdf4";
831 h_use_non_tex_fonts = "true";
833 registerAutomaticallyLoadedPackage("xunicode");
834 if (h_inputencoding == "auto")
835 p.setEncoding("UTF-8");
838 else if (name == "CJK") {
839 // set the encoding to "auto" because it might be set to "default" by the babel handling
840 // and this would not be correct for CJK
841 if (h_inputencoding == "default")
842 h_inputencoding = "auto";
843 registerAutomaticallyLoadedPackage("CJK");
846 else if (name == "CJKutf8") {
847 h_inputencoding = "utf8-cjk";
848 p.setEncoding("UTF-8");
849 registerAutomaticallyLoadedPackage("CJKutf8");
852 else if (name == "fontenc") {
853 h_fontencoding = getStringFromVector(options, ",");
854 /* We could do the following for better round trip support,
855 * but this makes the document less portable, so I skip it:
856 if (h_fontencoding == lyxrc.fontenc)
857 h_fontencoding = "global";
862 else if (name == "inputenc" || name == "luainputenc") {
863 // h_inputencoding is only set when there is not more than one
864 // inputenc option because otherwise h_inputencoding must be
865 // set to "auto" (the default encoding of the document language)
866 // Therefore check that exactly one option is passed to inputenc.
867 // It is also only set when there is not more than one babel
869 if (!options.empty()) {
870 string const encoding = options.back();
871 Encoding const * const enc = encodings.fromLaTeXName(
872 encoding, Encoding::inputenc, true);
874 cerr << "Unknown encoding " << encoding << ". Ignoring." << std::endl;
876 if (!enc->unsafe() && options.size() == 1 && one_language == true)
877 h_inputencoding = enc->name();
878 p.setEncoding(enc->iconvName());
884 else if (name == "srcltx") {
887 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
890 h_output_sync_macro = "\\usepackage{srcltx}";
893 else if (is_known(name, known_old_language_packages)) {
894 // known language packages from the times before babel
895 // if they are found and not also babel, they will be used as
896 // custom language package
897 h_language_package = "\\usepackage{" + name + "}";
900 else if (name == "lyxskak") {
901 // ignore this and its options
902 const char * const o[] = {"ps", "mover", 0};
903 delete_opt(options, o);
906 else if (is_known(name, known_lyx_packages) && options.empty()) {
907 if (name == "splitidx")
908 h_use_indices = "true";
909 if (name == "refstyle")
910 h_use_refstyle = true;
911 else if (name == "prettyref")
912 h_use_refstyle = false;
913 if (!in_lyx_preamble) {
914 h_preamble << package_beg_sep << name
915 << package_mid_sep << "\\usepackage{"
917 if (p.next_token().cat() == catNewline ||
918 (p.next_token().cat() == catSpace &&
919 p.next_next_token().cat() == catNewline))
921 h_preamble << package_end_sep;
925 else if (name == "geometry")
926 handle_geometry(options);
928 else if (name == "subfig")
929 ; // ignore this FIXME: Use the package separator mechanism instead
931 else if ((where = is_known(name, known_languages)))
932 h_language = known_coded_languages[where - known_languages];
934 else if (name == "natbib") {
935 h_biblio_style = "plainnat";
936 h_cite_engine = "natbib";
937 h_cite_engine_type = "authoryear";
938 vector<string>::iterator it =
939 find(options.begin(), options.end(), "authoryear");
940 if (it != options.end())
943 it = find(options.begin(), options.end(), "numbers");
944 if (it != options.end()) {
945 h_cite_engine_type = "numerical";
951 else if (name == "jurabib") {
952 h_biblio_style = "jurabib";
953 h_cite_engine = "jurabib";
954 h_cite_engine_type = "authoryear";
957 else if (name == "hyperref")
958 handle_hyperref(options);
960 else if (name == "algorithm2e") {
961 // Load "algorithm2e" module
962 addModule("algorithm2e");
963 // Add the package options to the global document options
964 if (!options.empty()) {
965 if (h_options.empty())
966 h_options = join(options, ",");
968 h_options += ',' + join(options, ",");
972 else if (!in_lyx_preamble) {
974 h_preamble << "\\usepackage{" << name << '}';
976 h_preamble << "\\usepackage[" << opts << "]{"
980 if (p.next_token().cat() == catNewline ||
981 (p.next_token().cat() == catSpace &&
982 p.next_next_token().cat() == catNewline))
986 // We need to do something with the options...
987 if (!options.empty())
988 cerr << "Ignoring options '" << join(options, ",")
989 << "' of package " << name << '.' << endl;
991 // remove the whitespace
996 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
999 Token t = p.get_token();
1000 if (t.cat() == catEscape &&
1001 is_known(t.cs(), known_if_commands))
1002 handle_if(p, in_lyx_preamble);
1004 if (!in_lyx_preamble)
1005 h_preamble << t.asInput();
1006 if (t.cat() == catEscape && t.cs() == "fi")
1013 bool Preamble::writeLyXHeader(ostream & os, bool subdoc)
1015 // set the quote language
1016 // LyX only knows the following quotes languages:
1017 // english, swedish, german, polish, french and danish
1018 // (quotes for "japanese" and "chinese-traditional" are missing because
1019 // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
1020 // conversion list taken from
1021 // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
1022 // (quotes for kazakh and interlingua are unknown)
1024 if (is_known(h_language, known_danish_quotes_languages))
1025 h_quotes_language = "danish";
1027 else if (is_known(h_language, known_french_quotes_languages))
1028 h_quotes_language = "french";
1030 else if (is_known(h_language, known_german_quotes_languages))
1031 h_quotes_language = "german";
1033 else if (is_known(h_language, known_polish_quotes_languages))
1034 h_quotes_language = "polish";
1036 else if (is_known(h_language, known_swedish_quotes_languages))
1037 h_quotes_language = "swedish";
1039 else if (is_known(h_language, known_english_quotes_languages))
1040 h_quotes_language = "english";
1042 if (contains(h_float_placement, "H"))
1043 registerAutomaticallyLoadedPackage("float");
1044 if (h_spacing != "single" && h_spacing != "default")
1045 registerAutomaticallyLoadedPackage("setspace");
1046 if (h_use_packages["amsmath"] == "2") {
1047 // amsbsy and amstext are already provided by amsmath
1048 registerAutomaticallyLoadedPackage("amsbsy");
1049 registerAutomaticallyLoadedPackage("amstext");
1052 // output the LyX file settings
1053 // Important: Keep the version formatting in sync with LyX and
1054 // lyx2lyx (bug 7951)
1055 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1056 << lyx_version_minor << '\n'
1057 << "\\lyxformat " << LYX_FORMAT << '\n'
1058 << "\\begin_document\n"
1059 << "\\begin_header\n"
1060 << "\\textclass " << h_textclass << "\n";
1061 string const raw = subdoc ? empty_string() : h_preamble.str();
1063 os << "\\begin_preamble\n";
1064 for (string::size_type i = 0; i < raw.size(); ++i) {
1065 if (raw[i] == package_beg_sep) {
1066 // Here follows some package loading code that
1067 // must be skipped if the package is loaded
1069 string::size_type j = raw.find(package_mid_sep, i);
1070 if (j == string::npos)
1072 string::size_type k = raw.find(package_end_sep, j);
1073 if (k == string::npos)
1075 string const package = raw.substr(i + 1, j - i - 1);
1076 string const replacement = raw.substr(j + 1, k - j - 1);
1077 if (auto_packages.find(package) == auto_packages.end())
1083 os << "\n\\end_preamble\n";
1085 if (!h_options.empty())
1086 os << "\\options " << h_options << "\n";
1087 os << "\\use_default_options " << h_use_default_options << "\n";
1088 if (!used_modules.empty()) {
1089 os << "\\begin_modules\n";
1090 vector<string>::const_iterator const end = used_modules.end();
1091 vector<string>::const_iterator it = used_modules.begin();
1092 for (; it != end; ++it)
1094 os << "\\end_modules\n";
1096 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1097 << "\\language " << h_language << "\n"
1098 << "\\language_package " << h_language_package << "\n"
1099 << "\\inputencoding " << h_inputencoding << "\n"
1100 << "\\fontencoding " << h_fontencoding << "\n"
1101 << "\\font_roman " << h_font_roman << "\n"
1102 << "\\font_sans " << h_font_sans << "\n"
1103 << "\\font_typewriter " << h_font_typewriter << "\n"
1104 << "\\font_math " << h_font_math << "\n"
1105 << "\\font_default_family " << h_font_default_family << "\n"
1106 << "\\use_non_tex_fonts " << h_use_non_tex_fonts << "\n"
1107 << "\\font_sc " << h_font_sc << "\n"
1108 << "\\font_osf " << h_font_osf << "\n"
1109 << "\\font_sf_scale " << h_font_sf_scale << "\n"
1110 << "\\font_tt_scale " << h_font_tt_scale << '\n';
1111 if (!h_font_cjk.empty())
1112 os << "\\font_cjk " << h_font_cjk << '\n';
1113 os << "\\graphics " << h_graphics << '\n'
1114 << "\\default_output_format " << h_default_output_format << "\n"
1115 << "\\output_sync " << h_output_sync << "\n";
1116 if (h_output_sync == "1")
1117 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1118 os << "\\bibtex_command " << h_bibtex_command << "\n"
1119 << "\\index_command " << h_index_command << "\n";
1120 if (!h_float_placement.empty())
1121 os << "\\float_placement " << h_float_placement << "\n";
1122 os << "\\paperfontsize " << h_paperfontsize << "\n"
1123 << "\\spacing " << h_spacing << "\n"
1124 << "\\use_hyperref " << h_use_hyperref << '\n';
1125 if (h_use_hyperref == "true") {
1126 if (!h_pdf_title.empty())
1127 os << "\\pdf_title \"" << h_pdf_title << "\"\n";
1128 if (!h_pdf_author.empty())
1129 os << "\\pdf_author \"" << h_pdf_author << "\"\n";
1130 if (!h_pdf_subject.empty())
1131 os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
1132 if (!h_pdf_keywords.empty())
1133 os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
1134 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1135 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1136 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1137 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1138 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1139 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1140 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1141 "\\pdf_backref " << h_pdf_backref << "\n"
1142 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1143 if (!h_pdf_pagemode.empty())
1144 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1145 if (!h_pdf_quoted_options.empty())
1146 os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
1148 os << "\\papersize " << h_papersize << "\n"
1149 << "\\use_geometry " << h_use_geometry << '\n';
1150 for (map<string, string>::const_iterator it = h_use_packages.begin();
1151 it != h_use_packages.end(); ++it)
1152 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1153 os << "\\cite_engine " << h_cite_engine << '\n'
1154 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1155 << "\\biblio_style " << h_biblio_style << "\n"
1156 << "\\use_bibtopic " << h_use_bibtopic << "\n"
1157 << "\\use_indices " << h_use_indices << "\n"
1158 << "\\paperorientation " << h_paperorientation << '\n'
1159 << "\\suppress_date " << h_suppress_date << '\n'
1160 << "\\justification " << h_justification << '\n'
1161 << "\\use_refstyle " << h_use_refstyle << '\n';
1162 if (!h_fontcolor.empty())
1163 os << "\\fontcolor " << h_fontcolor << '\n';
1164 if (!h_notefontcolor.empty())
1165 os << "\\notefontcolor " << h_notefontcolor << '\n';
1166 if (!h_backgroundcolor.empty())
1167 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1168 if (!h_boxbgcolor.empty())
1169 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1170 if (index_number != 0)
1171 for (int i = 0; i < index_number; i++) {
1172 os << "\\index " << h_index[i] << '\n'
1173 << "\\shortcut " << h_shortcut[i] << '\n'
1174 << "\\color " << h_color << '\n'
1178 os << "\\index " << h_index[0] << '\n'
1179 << "\\shortcut " << h_shortcut[0] << '\n'
1180 << "\\color " << h_color << '\n'
1184 << "\\secnumdepth " << h_secnumdepth << "\n"
1185 << "\\tocdepth " << h_tocdepth << "\n"
1186 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1187 if (h_paragraph_separation == "skip")
1188 os << "\\defskip " << h_defskip << "\n";
1190 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1191 os << "\\quotes_language " << h_quotes_language << "\n"
1192 << "\\papercolumns " << h_papercolumns << "\n"
1193 << "\\papersides " << h_papersides << "\n"
1194 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1195 if (!h_listings_params.empty())
1196 os << "\\listings_params " << h_listings_params << "\n";
1197 os << "\\tracking_changes " << h_tracking_changes << "\n"
1198 << "\\output_changes " << h_output_changes << "\n"
1199 << "\\html_math_output " << h_html_math_output << "\n"
1200 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1201 << "\\html_be_strict " << h_html_be_strict << "\n"
1203 << "\\end_header\n\n"
1204 << "\\begin_body\n";
1209 void Preamble::parse(Parser & p, string const & forceclass,
1210 TeX2LyXDocClass & tc)
1212 // initialize fixed types
1213 special_columns['D'] = 3;
1214 bool is_full_document = false;
1215 bool is_lyx_file = false;
1216 bool in_lyx_preamble = false;
1218 // determine whether this is a full document or a fragment for inclusion
1220 Token const & t = p.get_token();
1222 if (t.cat() == catEscape && t.cs() == "documentclass") {
1223 is_full_document = true;
1229 while (is_full_document && p.good()) {
1230 Token const & t = p.get_token();
1233 cerr << "t: " << t << "\n";
1239 if (!in_lyx_preamble &&
1240 (t.cat() == catLetter ||
1241 t.cat() == catSuper ||
1242 t.cat() == catSub ||
1243 t.cat() == catOther ||
1244 t.cat() == catMath ||
1245 t.cat() == catActive ||
1246 t.cat() == catBegin ||
1247 t.cat() == catEnd ||
1248 t.cat() == catAlign ||
1249 t.cat() == catParameter))
1250 h_preamble << t.cs();
1252 else if (!in_lyx_preamble &&
1253 (t.cat() == catSpace || t.cat() == catNewline))
1254 h_preamble << t.asInput();
1256 else if (t.cat() == catComment) {
1257 static regex const islyxfile("%% LyX .* created this file");
1258 static regex const usercommands("User specified LaTeX commands");
1260 string const comment = t.asInput();
1262 // magically switch encoding default if it looks like XeLaTeX
1263 static string const magicXeLaTeX =
1264 "% This document must be compiled with XeLaTeX ";
1265 if (comment.size() > magicXeLaTeX.size()
1266 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1267 && h_inputencoding == "auto") {
1268 cerr << "XeLaTeX comment found, switching to UTF8\n";
1269 h_inputencoding = "utf8";
1272 if (regex_search(comment, sub, islyxfile)) {
1274 in_lyx_preamble = true;
1275 } else if (is_lyx_file
1276 && regex_search(comment, sub, usercommands))
1277 in_lyx_preamble = false;
1278 else if (!in_lyx_preamble)
1279 h_preamble << t.asInput();
1282 else if (t.cs() == "pagestyle")
1283 h_paperpagestyle = p.verbatim_item();
1285 else if (t.cs() == "setdefaultlanguage") {
1287 // We don't yet care about non-language variant options
1288 // because LyX doesn't support this yet, see bug #8214
1290 string langopts = p.getOpt();
1291 // check if the option contains a variant, if yes, extract it
1292 string::size_type pos_var = langopts.find("variant");
1293 string::size_type i = langopts.find(',', pos_var);
1294 string::size_type k = langopts.find('=', pos_var);
1295 if (pos_var != string::npos){
1297 if (i == string::npos)
1298 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1300 variant = langopts.substr(k + 1, i - k - 1);
1301 h_language = variant;
1305 h_language = p.verbatim_item();
1306 //finally translate the poyglossia name to a LyX name
1307 h_language = polyglossia2lyx(h_language);
1310 else if (t.cs() == "setotherlanguage") {
1311 // We don't yet care about the option because LyX doesn't
1312 // support this yet, see bug #8214
1313 p.hasOpt() ? p.getOpt() : string();
1317 else if (t.cs() == "setmainfont") {
1318 // we don't care about the option
1319 p.hasOpt() ? p.getOpt() : string();
1320 h_font_roman = p.getArg('{', '}');
1323 else if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1324 // LyX currently only supports the scale option
1327 string fontopts = p.getArg('[', ']');
1328 // check if the option contains a scaling, if yes, extract it
1329 string::size_type pos = fontopts.find("Scale");
1330 if (pos != string::npos) {
1331 string::size_type i = fontopts.find(',', pos);
1332 if (i == string::npos)
1333 scale_as_percentage(fontopts.substr(pos + 1), scale);
1335 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1338 if (t.cs() == "setsansfont") {
1340 h_font_sf_scale = scale;
1341 h_font_sans = p.getArg('{', '}');
1344 h_font_tt_scale = scale;
1345 h_font_typewriter = p.getArg('{', '}');
1349 else if (t.cs() == "date") {
1350 string argument = p.getArg('{', '}');
1351 if (argument.empty())
1352 h_suppress_date = "true";
1354 h_preamble << t.asInput() << '{' << argument << '}';
1357 else if (t.cs() == "color") {
1358 string const space =
1359 (p.hasOpt() ? p.getOpt() : string());
1360 string argument = p.getArg('{', '}');
1361 // check the case that a standard color is used
1362 if (space.empty() && is_known(argument, known_basic_colors)) {
1363 h_fontcolor = rgbcolor2code(argument);
1364 preamble.registerAutomaticallyLoadedPackage("color");
1365 } else if (space.empty() && argument == "document_fontcolor")
1366 preamble.registerAutomaticallyLoadedPackage("color");
1367 // check the case that LyX's document_fontcolor is defined
1368 // but not used for \color
1370 h_preamble << t.asInput();
1372 h_preamble << space;
1373 h_preamble << '{' << argument << '}';
1374 // the color might already be set because \definecolor
1375 // is parsed before this
1380 else if (t.cs() == "pagecolor") {
1381 string argument = p.getArg('{', '}');
1382 // check the case that a standard color is used
1383 if (is_known(argument, known_basic_colors)) {
1384 h_backgroundcolor = rgbcolor2code(argument);
1385 } else if (argument == "page_backgroundcolor")
1386 preamble.registerAutomaticallyLoadedPackage("color");
1387 // check the case that LyX's page_backgroundcolor is defined
1388 // but not used for \pagecolor
1390 h_preamble << t.asInput() << '{' << argument << '}';
1391 // the color might already be set because \definecolor
1392 // is parsed before this
1393 h_backgroundcolor = "";
1397 else if (t.cs() == "makeatletter") {
1398 // LyX takes care of this
1399 p.setCatcode('@', catLetter);
1402 else if (t.cs() == "makeatother") {
1403 // LyX takes care of this
1404 p.setCatcode('@', catOther);
1407 else if (t.cs() == "makeindex") {
1408 // LyX will re-add this if a print index command is found
1412 else if (t.cs() == "newindex") {
1413 string const indexname = p.getArg('[', ']');
1414 string const shortcut = p.verbatim_item();
1415 if (!indexname.empty())
1416 h_index[index_number] = indexname;
1418 h_index[index_number] = shortcut;
1419 h_shortcut[index_number] = shortcut;
1424 else if (t.cs() == "RS@ifundefined") {
1425 string const name = p.verbatim_item();
1426 string const body1 = p.verbatim_item();
1427 string const body2 = p.verbatim_item();
1428 // only non-lyxspecific stuff
1429 if (in_lyx_preamble &&
1430 (name == "subref" || name == "thmref" || name == "lemref"))
1434 ss << '\\' << t.cs();
1435 ss << '{' << name << '}'
1436 << '{' << body1 << '}'
1437 << '{' << body2 << '}';
1438 h_preamble << ss.str();
1442 else if (t.cs() == "AtBeginDocument") {
1443 string const name = p.verbatim_item();
1444 // only non-lyxspecific stuff
1445 if (in_lyx_preamble &&
1446 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
1447 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
1448 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
1449 || name == "\\providecommand\\subref[1]{\\ref{sub:#1}}"
1450 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
1451 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
1452 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
1453 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
1454 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
1455 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
1456 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
1457 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
1458 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
1459 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
1460 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
1464 ss << '\\' << t.cs();
1465 ss << '{' << name << '}';
1466 h_preamble << ss.str();
1470 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1471 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1472 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1473 || t.cs() == "DeclareRobustCommand"
1474 || t.cs() == "DeclareRobustCommandx"
1475 || t.cs() == "ProvideTextCommandDefault"
1476 || t.cs() == "DeclareMathAccent") {
1478 if (p.next_token().character() == '*') {
1482 string const name = p.verbatim_item();
1483 string const opt1 = p.getFullOpt();
1484 string const opt2 = p.getFullOpt();
1485 string const body = p.verbatim_item();
1486 // store the in_lyx_preamble setting
1487 bool const was_in_lyx_preamble = in_lyx_preamble;
1489 if (name == "\\rmdefault")
1490 if (is_known(body, known_roman_fonts)) {
1491 h_font_roman = body;
1493 in_lyx_preamble = true;
1495 if (name == "\\sfdefault")
1496 if (is_known(body, known_sans_fonts)) {
1499 in_lyx_preamble = true;
1501 if (name == "\\ttdefault")
1502 if (is_known(body, known_typewriter_fonts)) {
1503 h_font_typewriter = body;
1505 in_lyx_preamble = true;
1507 if (name == "\\familydefault") {
1508 string family = body;
1509 // remove leading "\"
1510 h_font_default_family = family.erase(0,1);
1512 in_lyx_preamble = true;
1515 // remove the lyxdot definition that is re-added by LyX
1517 if (name == "\\lyxdot") {
1519 in_lyx_preamble = true;
1522 // Add the command to the known commands
1523 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1525 // only non-lyxspecific stuff
1526 if (!in_lyx_preamble) {
1528 ss << '\\' << t.cs();
1531 ss << '{' << name << '}' << opt1 << opt2
1532 << '{' << body << "}";
1533 h_preamble << ss.str();
1535 ostream & out = in_preamble ? h_preamble : os;
1536 out << "\\" << t.cs() << "{" << name << "}"
1537 << opts << "{" << body << "}";
1540 // restore the in_lyx_preamble setting
1541 in_lyx_preamble = was_in_lyx_preamble;
1544 else if (t.cs() == "documentclass") {
1545 vector<string>::iterator it;
1546 vector<string> opts = split_options(p.getArg('[', ']'));
1547 handle_opt(opts, known_fontsizes, h_paperfontsize);
1548 delete_opt(opts, known_fontsizes);
1549 // delete "pt" at the end
1550 string::size_type i = h_paperfontsize.find("pt");
1551 if (i != string::npos)
1552 h_paperfontsize.erase(i);
1553 // The documentclass options are always parsed before the options
1554 // of the babel call so that a language cannot overwrite the babel
1556 handle_opt(opts, known_languages, h_language);
1557 delete_opt(opts, known_languages);
1559 // paper orientation
1560 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1561 h_paperorientation = "landscape";
1565 if ((it = find(opts.begin(), opts.end(), "oneside"))
1570 if ((it = find(opts.begin(), opts.end(), "twoside"))
1576 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1578 h_papercolumns = "1";
1581 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1583 h_papercolumns = "2";
1587 // some size options are known to any document classes, other sizes
1588 // are handled by the \geometry command of the geometry package
1589 handle_opt(opts, known_class_paper_sizes, h_papersize);
1590 delete_opt(opts, known_class_paper_sizes);
1591 // the remaining options
1592 h_options = join(opts, ",");
1593 // FIXME This does not work for classes that have a
1594 // different name in LyX than in LaTeX
1595 h_textclass = p.getArg('{', '}');
1599 else if (t.cs() == "usepackage") {
1600 string const options = p.getArg('[', ']');
1601 string const name = p.getArg('{', '}');
1602 vector<string> vecnames;
1603 split(name, vecnames, ',');
1604 vector<string>::const_iterator it = vecnames.begin();
1605 vector<string>::const_iterator end = vecnames.end();
1606 for (; it != end; ++it)
1607 handle_package(p, trimSpaceAndEol(*it), options,
1611 else if (t.cs() == "inputencoding") {
1612 string const encoding = p.getArg('{','}');
1613 Encoding const * const enc = encodings.fromLaTeXName(
1614 encoding, Encoding::inputenc, true);
1616 cerr << "Unknown encoding " << encoding << ". Ignoring." << std::endl;
1619 h_inputencoding = enc->name();
1620 p.setEncoding(enc->iconvName());
1624 else if (t.cs() == "newenvironment") {
1625 string const name = p.getArg('{', '}');
1626 string const opt1 = p.getFullOpt();
1627 string const opt2 = p.getFullOpt();
1628 string const beg = p.verbatim_item();
1629 string const end = p.verbatim_item();
1630 if (!in_lyx_preamble) {
1631 h_preamble << "\\newenvironment{" << name
1632 << '}' << opt1 << opt2 << '{'
1633 << beg << "}{" << end << '}';
1635 add_known_environment(name, opt1, !opt2.empty(),
1636 from_utf8(beg), from_utf8(end));
1640 else if (t.cs() == "newtheorem") {
1641 string const name = p.getArg('{', '}');
1642 string const opt1 = p.getFullOpt();
1643 string const opt2 = p.getFullOpt();
1644 string const body = p.verbatim_item();
1645 string const opt3 = p.getFullOpt();
1647 add_known_theorem(name, opt1, !opt2.empty(),
1648 from_utf8("\\newtheorem{" + name + '}' +
1649 opt1 + opt2 + '{' + body + '}' + opt3));
1651 if (!in_lyx_preamble)
1652 h_preamble << "\\newtheorem{" << name << '}'
1653 << opt1 << opt2 << '{' << '}' << opt3;
1656 else if (t.cs() == "def") {
1657 string name = p.get_token().cs();
1658 // In fact, name may be more than the name:
1659 // In the test case of bug 8116
1660 // name == "csname SF@gobble@opt \endcsname".
1661 // Therefore, we need to use asInput() instead of cs().
1662 while (p.next_token().cat() != catBegin)
1663 name += p.get_token().asInput();
1664 if (!in_lyx_preamble)
1665 h_preamble << "\\def\\" << name << '{'
1666 << p.verbatim_item() << "}";
1669 else if (t.cs() == "newcolumntype") {
1670 string const name = p.getArg('{', '}');
1671 trimSpaceAndEol(name);
1673 string opts = p.getOpt();
1674 if (!opts.empty()) {
1675 istringstream is(string(opts, 1));
1678 special_columns[name[0]] = nargs;
1679 h_preamble << "\\newcolumntype{" << name << "}";
1681 h_preamble << "[" << nargs << "]";
1682 h_preamble << "{" << p.verbatim_item() << "}";
1685 else if (t.cs() == "setcounter") {
1686 string const name = p.getArg('{', '}');
1687 string const content = p.getArg('{', '}');
1688 if (name == "secnumdepth")
1689 h_secnumdepth = content;
1690 else if (name == "tocdepth")
1691 h_tocdepth = content;
1693 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1696 else if (t.cs() == "setlength") {
1697 string const name = p.verbatim_item();
1698 string const content = p.verbatim_item();
1699 // the paragraphs are only not indented when \parindent is set to zero
1700 if (name == "\\parindent" && content != "") {
1701 if (content[0] == '0')
1702 h_paragraph_separation = "skip";
1704 h_paragraph_indentation = translate_len(content);
1705 } else if (name == "\\parskip") {
1706 if (content == "\\smallskipamount")
1707 h_defskip = "smallskip";
1708 else if (content == "\\medskipamount")
1709 h_defskip = "medskip";
1710 else if (content == "\\bigskipamount")
1711 h_defskip = "bigskip";
1713 h_defskip = content;
1715 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1718 else if (t.cs() == "onehalfspacing")
1719 h_spacing = "onehalf";
1721 else if (t.cs() == "doublespacing")
1722 h_spacing = "double";
1724 else if (t.cs() == "setstretch")
1725 h_spacing = "other " + p.verbatim_item();
1727 else if (t.cs() == "synctex") {
1728 // the scheme is \synctex=value
1729 // where value can only be "1" or "-1"
1730 h_output_sync = "1";
1731 // there can be any character behind the value (e.g. a linebreak or a '\'
1732 // therefore we extract it char by char
1734 string value = p.get_token().asInput();
1736 value += p.get_token().asInput();
1737 h_output_sync_macro = "\\synctex=" + value;
1740 else if (t.cs() == "begin") {
1741 string const name = p.getArg('{', '}');
1742 if (name == "document")
1744 h_preamble << "\\begin{" << name << "}";
1747 else if (t.cs() == "geometry") {
1748 vector<string> opts = split_options(p.getArg('{', '}'));
1749 handle_geometry(opts);
1752 else if (t.cs() == "definecolor") {
1753 string const color = p.getArg('{', '}');
1754 string const space = p.getArg('{', '}');
1755 string const value = p.getArg('{', '}');
1756 if (color == "document_fontcolor" && space == "rgb") {
1757 RGBColor c(RGBColorFromLaTeX(value));
1758 h_fontcolor = X11hexname(c);
1759 } else if (color == "note_fontcolor" && space == "rgb") {
1760 RGBColor c(RGBColorFromLaTeX(value));
1761 h_notefontcolor = X11hexname(c);
1762 } else if (color == "page_backgroundcolor" && space == "rgb") {
1763 RGBColor c(RGBColorFromLaTeX(value));
1764 h_backgroundcolor = X11hexname(c);
1765 } else if (color == "shadecolor" && space == "rgb") {
1766 RGBColor c(RGBColorFromLaTeX(value));
1767 h_boxbgcolor = X11hexname(c);
1769 h_preamble << "\\definecolor{" << color
1770 << "}{" << space << "}{" << value
1775 else if (t.cs() == "bibliographystyle")
1776 h_biblio_style = p.verbatim_item();
1778 else if (t.cs() == "jurabibsetup") {
1779 // FIXME p.getArg('{', '}') is most probably wrong (it
1780 // does not handle nested braces).
1781 // Use p.verbatim_item() instead.
1782 vector<string> jurabibsetup =
1783 split_options(p.getArg('{', '}'));
1784 // add jurabibsetup to the jurabib package options
1785 add_package("jurabib", jurabibsetup);
1786 if (!jurabibsetup.empty()) {
1787 h_preamble << "\\jurabibsetup{"
1788 << join(jurabibsetup, ",") << '}';
1792 else if (t.cs() == "hypersetup") {
1793 vector<string> hypersetup =
1794 split_options(p.verbatim_item());
1795 // add hypersetup to the hyperref package options
1796 handle_hyperref(hypersetup);
1797 if (!hypersetup.empty()) {
1798 h_preamble << "\\hypersetup{"
1799 << join(hypersetup, ",") << '}';
1803 else if (is_known(t.cs(), known_if_3arg_commands)) {
1804 // prevent misparsing of \usepackage if it is used
1805 // as an argument (see e.g. our own output of
1806 // \@ifundefined above)
1807 string const arg1 = p.verbatim_item();
1808 string const arg2 = p.verbatim_item();
1809 string const arg3 = p.verbatim_item();
1810 // test case \@ifundefined{date}{}{\date{}}
1811 if (t.cs() == "@ifundefined" && arg1 == "date" &&
1812 arg2.empty() && arg3 == "\\date{}") {
1813 h_suppress_date = "true";
1814 // older tex2lyx versions did output
1815 // \@ifundefined{definecolor}{\usepackage{color}}{}
1816 } else if (t.cs() == "@ifundefined" &&
1817 arg1 == "definecolor" &&
1818 arg2 == "\\usepackage{color}" &&
1820 if (!in_lyx_preamble)
1821 h_preamble << package_beg_sep
1824 << "\\@ifundefined{definecolor}{color}{}"
1827 //\@ifundefined{showcaptionsetup}{}{%
1828 // \PassOptionsToPackage{caption=false}{subfig}}
1829 // that LyX uses for subfloats
1830 } else if (t.cs() == "@ifundefined" &&
1831 arg1 == "showcaptionsetup" && arg2.empty()
1832 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1834 } else if (!in_lyx_preamble) {
1835 h_preamble << t.asInput()
1836 << '{' << arg1 << '}'
1837 << '{' << arg2 << '}'
1838 << '{' << arg3 << '}';
1842 else if (is_known(t.cs(), known_if_commands)) {
1843 // must not parse anything in conditional code, since
1844 // LyX would output the parsed contents unconditionally
1845 if (!in_lyx_preamble)
1846 h_preamble << t.asInput();
1847 handle_if(p, in_lyx_preamble);
1850 else if (!t.cs().empty() && !in_lyx_preamble)
1851 h_preamble << '\\' << t.cs();
1854 // remove the whitespace
1857 // Force textclass if the user wanted it
1858 if (!forceclass.empty())
1859 h_textclass = forceclass;
1860 tc.setName(h_textclass);
1862 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1865 if (h_papersides.empty()) {
1868 h_papersides = ss.str();
1871 // If the CJK package is used we cannot set the document language from
1872 // the babel options. Instead, we guess which language is used most
1873 // and set this one.
1874 default_language = h_language;
1875 if (is_full_document &&
1876 (auto_packages.find("CJK") != auto_packages.end() ||
1877 auto_packages.find("CJKutf8") != auto_packages.end())) {
1879 h_language = guessLanguage(p, default_language);
1881 if (explicit_babel && h_language != default_language) {
1882 // We set the document language to a CJK language,
1883 // but babel is explicitly called in the user preamble
1884 // without options. LyX will not add the default
1885 // language to the document options if it is either
1886 // english, or no text is set as default language.
1887 // Therefore we need to add a language option explicitly.
1888 // FIXME: It would be better to remove all babel calls
1889 // from the user preamble, but this is difficult
1890 // without re-introducing bug 7861.
1891 if (h_options.empty())
1892 h_options = lyx2babel(default_language);
1894 h_options += ',' + lyx2babel(default_language);
1900 string babel2lyx(string const & language)
1902 char const * const * where = is_known(language, known_languages);
1904 return known_coded_languages[where - known_languages];
1909 string lyx2babel(string const & language)
1911 char const * const * where = is_known(language, known_coded_languages);
1913 return known_languages[where - known_coded_languages];
1918 string Preamble::polyglossia2lyx(string const & language)
1920 char const * const * where = is_known(language, polyglossia_languages);
1922 return coded_polyglossia_languages[where - polyglossia_languages];
1927 string rgbcolor2code(string const & name)
1929 char const * const * where = is_known(name, known_basic_colors);
1931 // "red", "green" etc
1932 return known_basic_color_codes[where - known_basic_colors];
1934 // "255,0,0", "0,255,0" etc
1935 RGBColor c(RGBColorFromLaTeX(name));
1936 return X11hexname(c);