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;
46 // CJK languages are handled in text.cpp, polyglossia languages are listed
49 * known babel language names (including synonyms)
50 * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
51 * please keep this in sync with known_coded_languages line by line!
53 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
54 "american", "arabic", "arabtex", "australian", "austrian", "bahasa", "bahasai",
55 "bahasam", "basque", "belarusian", "bosnian", "brazil", "brazilian", "breton", "british",
56 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
57 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "francais",
58 "french", "frenchb", "frenchle", "frenchpro", "friulan", "galician", "german", "germanb",
59 "georgian", "greek", "hebrew", "hungarian", "icelandic", "indon", "indonesian",
60 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
61 "latvian", "lithuanian", "lowersorbian", "lsorbian", "macedonian", "magyar", "malay", "meyalu",
62 "mongolian", "naustrian", "newzealand", "ngerman", "ngermanb", "norsk", "nswissgerman",
63 "nynorsk", "piedmontese", "polutonikogreek", "polish", "portuges", "portuguese",
64 "romanian", "romansh", "russian", "russianb", "samin", "scottish", "serbian", "serbian-latin",
65 "slovak", "slovene", "spanish", "swedish", "swissgerman", "thai", "turkish", "turkmen",
66 "ukraineb", "ukrainian", "uppersorbian", "UKenglish", "USenglish", "usorbian",
71 * the same as known_languages with .lyx names
72 * please keep this in sync with known_languages line by line!
74 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
75 "american", "arabic_arabi", "arabic_arabtex", "australian", "austrian", "bahasa", "bahasa",
76 "bahasam", "basque", "belarusian", "bosnian", "brazilian", "brazilian", "breton", "british",
77 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
78 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "french",
79 "french", "french", "french", "french", "friulan", "galician", "german", "german",
80 "georgian", "greek", "hebrew", "magyar", "icelandic", "bahasa", "bahasa",
81 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
82 "latvian", "lithuanian", "lowersorbian", "lowersorbian", "macedonian", "magyar", "bahasam", "bahasam",
83 "mongolian", "naustrian", "newzealand", "ngerman", "ngerman", "norsk", "german-ch",
84 "nynorsk", "piedmontese", "polutonikogreek", "polish", "portuguese", "portuguese",
85 "romanian", "romansh", "russian", "russian", "samin", "scottish", "serbian", "serbian-latin",
86 "slovak", "slovene", "spanish", "swedish", "german-ch-old", "thai", "turkish", "turkmen",
87 "ukrainian", "ukrainian", "uppersorbian", "english", "english", "uppersorbian",
88 "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", "friulan",
103 "galician", "greek", "italian", "norsk", "nynorsk", "piedmontese", "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", "georgian", "icelandic", "lithuanian", "lowersorbian", "macedonian",
110 "naustrian", "ngerman", "romansh", "serbian", "serbian-latin", "slovak", "slovene",
113 /// languages with polish quotes (.lyx names)
114 const char * const known_polish_quotes_languages[] = {"afrikaans", "bosnian", "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", "libertineMono-type1", "lmodern",
138 "mathpazo", "mathptmx", "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[] = {"black", "blue", "brown", "cyan",
164 "darkgray", "gray", "green", "lightgray", "lime", "magenta", "orange", "olive",
165 "pink", "purple", "red", "teal", "violet", "white", "yellow", 0};
167 const char * const known_basic_color_codes[] = {"#000000", "#0000ff", "#964B00", "#00ffff",
168 "#a9a9a9", "#808080", "#00ff00", "#d3d3d3", "#bfff00", "#ff00ff", "#ff7f00", "#808000",
169 "#ffc0cb", "#800080", "#ff0000", "#008080", "#8f00ff", "#ffffff", "#ffff00", 0};
171 /// conditional commands with three arguments like \@ifundefined{}{}{}
172 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
175 /// packages that work only in xetex
176 /// polyglossia is handled separately
177 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
178 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
179 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
181 /// packages that are automatically skipped if loaded by LyX
182 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
183 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color",
184 "float", "fontspec", "framed", "graphicx", "hhline", "ifthen", "longtable",
185 "makeidx", "multirow", "nomencl", "pdfpages", "prettyref", "refstyle", "rotating",
186 "rotfloat", "splitidx", "setspace", "subscript", "textcomp", "tipa", "tipx",
187 "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xcolor", "xunicode", 0};
189 // codes used to remove packages that are loaded automatically by LyX.
190 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
191 const char package_beg_sep = '\001';
192 const char package_mid_sep = '\002';
193 const char package_end_sep = '\003';
196 // returns true if at least one of the options in what has been found
197 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
203 // the last language option is the document language (for babel and LyX)
204 // the last size option is the document font size
205 vector<string>::iterator it;
206 vector<string>::iterator position = opts.begin();
207 for (; *what; ++what) {
208 it = find(opts.begin(), opts.end(), *what);
209 if (it != opts.end()) {
210 if (it >= position) {
221 void delete_opt(vector<string> & opts, char const * const * what)
226 // remove found options from the list
227 // do this after handle_opt to avoid potential memory leaks
228 vector<string>::iterator it;
229 for (; *what; ++what) {
230 it = find(opts.begin(), opts.end(), *what);
231 if (it != opts.end())
238 * Split a package options string (keyval format) into a vector.
240 * authorformat=smallcaps,
242 * titleformat=colonsep,
243 * bibformat={tabular,ibidem,numbered}
245 vector<string> split_options(string const & input)
247 vector<string> options;
251 Token const & t = p.get_token();
252 if (t.asInput() == ",") {
253 options.push_back(trimSpaceAndEol(option));
255 } else if (t.asInput() == "=") {
258 if (p.next_token().asInput() == "{")
259 option += '{' + p.getArg('{', '}') + '}';
260 } else if (t.cat() != catSpace)
261 option += t.asInput();
265 options.push_back(trimSpaceAndEol(option));
272 * Retrieve a keyval option "name={value with=sign}" named \p name from
273 * \p options and return the value.
274 * The found option is also removed from \p options.
276 string process_keyval_opt(vector<string> & options, string name)
278 for (size_t i = 0; i < options.size(); ++i) {
279 vector<string> option;
280 split(options[i], option, '=');
281 if (option.size() < 2)
283 if (option[0] == name) {
284 options.erase(options.begin() + i);
285 option.erase(option.begin());
286 return join(option, "=");
292 } // anonymous namespace
296 * known polyglossia language names (including variants)
297 * FIXME: support spelling=old for german variants (german vs. ngerman LyX names etc)
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", "austrian",
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", "naustrian",
328 "american", "ancientgreek", "australian", "british", "greek", "newzealand",
329 "polutonikogreek", 0};
332 bool Preamble::usePolyglossia() const
334 return h_use_non_tex_fonts && h_language_package == "default";
338 bool Preamble::indentParagraphs() const
340 return h_paragraph_separation == "indent";
344 bool Preamble::isPackageUsed(string const & package) const
346 return used_packages.find(package) != used_packages.end();
350 vector<string> Preamble::getPackageOptions(string const & package) const
352 map<string, vector<string> >::const_iterator it = used_packages.find(package);
353 if (it != used_packages.end())
355 return vector<string>();
359 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
361 auto_packages.insert(package);
365 void Preamble::addModule(string const & module)
367 used_modules.push_back(module);
371 void Preamble::suppressDate(bool suppress)
374 h_suppress_date = "true";
376 h_suppress_date = "false";
380 void Preamble::registerAuthor(std::string const & name)
382 Author author(from_utf8(name), empty_docstring());
383 author.setUsed(true);
384 authors_.record(author);
385 h_tracking_changes = "true";
386 h_output_changes = "true";
390 Author const & Preamble::getAuthor(std::string const & name) const
392 Author author(from_utf8(name), empty_docstring());
393 for (AuthorList::Authors::const_iterator it = authors_.begin();
394 it != authors_.end(); ++it)
397 static Author const dummy;
402 int Preamble::getSpecialTableColumnArguments(char c) const
404 map<char, int>::const_iterator it = special_columns_.find(c);
405 if (it == special_columns_.end())
411 void Preamble::add_package(string const & name, vector<string> & options)
413 // every package inherits the global options
414 if (used_packages.find(name) == used_packages.end())
415 used_packages[name] = split_options(h_options);
417 vector<string> & v = used_packages[name];
418 v.insert(v.end(), options.begin(), options.end());
419 if (name == "jurabib") {
420 // Don't output the order argument (see the cite command
421 // handling code in text.cpp).
422 vector<string>::iterator end =
423 remove(options.begin(), options.end(), "natbiborder");
424 end = remove(options.begin(), end, "jurabiborder");
425 options.erase(end, options.end());
432 // Given is a string like "scaled=0.9" or "Scale=0.9", return 0.9 * 100
433 bool scale_as_percentage(string const & scale, string & percentage)
435 string::size_type pos = scale.find('=');
436 if (pos != string::npos) {
437 string value = scale.substr(pos + 1);
438 if (isStrDbl(value)) {
439 percentage = convert<string>(
440 static_cast<int>(100 * convert<double>(value)));
448 string remove_braces(string const & value)
452 if (value[0] == '{' && value[value.length()-1] == '}')
453 return value.substr(1, value.length()-2);
457 } // anonymous namespace
460 Preamble::Preamble() : one_language(true), explicit_babel(false),
461 title_layout_found(false), index_number(0), h_font_cjk_set(false),
462 h_use_microtype(false)
466 h_biblio_style = "plain";
467 h_bibtex_command = "default";
468 h_cite_engine = "basic";
469 h_cite_engine_type = "default";
471 h_defskip = "medskip";
474 h_fontencoding = "default";
475 h_font_roman[0] = "default";
476 h_font_roman[1] = "default";
477 h_font_sans[0] = "default";
478 h_font_sans[1] = "default";
479 h_font_typewriter[0] = "default";
480 h_font_typewriter[1] = "default";
481 h_font_math[0] = "auto";
482 h_font_math[1] = "auto";
483 h_font_default_family = "default";
484 h_use_non_tex_fonts = false;
486 h_font_osf = "false";
487 h_font_sf_scale[0] = "100";
488 h_font_sf_scale[1] = "100";
489 h_font_tt_scale[0] = "100";
490 h_font_tt_scale[1] = "100";
492 h_graphics = "default";
493 h_default_output_format = "default";
494 h_html_be_strict = "false";
495 h_html_css_as_file = "0";
496 h_html_math_output = "0";
497 h_index[0] = "Index";
498 h_index_command = "default";
499 h_inputencoding = "auto";
500 h_justification = "true";
501 h_language = "english";
502 h_language_package = "none";
504 h_maintain_unincluded_children = "false";
508 h_output_changes = "false";
510 //h_output_sync_macro
511 h_papercolumns = "1";
512 h_paperfontsize = "default";
513 h_paperorientation = "portrait";
514 h_paperpagestyle = "default";
516 h_papersize = "default";
517 h_paragraph_indentation = "default";
518 h_paragraph_separation = "indent";
523 h_pdf_bookmarks = "0";
524 h_pdf_bookmarksnumbered = "0";
525 h_pdf_bookmarksopen = "0";
526 h_pdf_bookmarksopenlevel = "1";
527 h_pdf_breaklinks = "0";
528 h_pdf_pdfborder = "0";
529 h_pdf_colorlinks = "0";
530 h_pdf_backref = "section";
531 h_pdf_pdfusetitle = "0";
533 //h_pdf_quoted_options;
534 h_quotes_language = "english";
536 h_shortcut[0] = "idx";
537 h_spacing = "single";
538 h_save_transient_properties = "true";
539 h_suppress_date = "false";
540 h_textclass = "article";
542 h_tracking_changes = "false";
543 h_use_bibtopic = "false";
544 h_use_indices = "false";
545 h_use_geometry = "false";
546 h_use_default_options = "false";
547 h_use_hyperref = "false";
548 h_use_microtype = false;
549 h_use_refstyle = false;
550 h_use_packages["amsmath"] = "1";
551 h_use_packages["amssymb"] = "0";
552 h_use_packages["cancel"] = "0";
553 h_use_packages["esint"] = "1";
554 h_use_packages["mhchem"] = "0";
555 h_use_packages["mathdots"] = "0";
556 h_use_packages["mathtools"] = "0";
557 h_use_packages["stackrel"] = "0";
558 h_use_packages["stmaryrd"] = "0";
559 h_use_packages["undertilde"] = "0";
563 void Preamble::handle_hyperref(vector<string> & options)
565 // FIXME swallow inputencoding changes that might surround the
566 // hyperref setup if it was written by LyX
567 h_use_hyperref = "true";
568 // swallow "unicode=true", since LyX does always write that
569 vector<string>::iterator it =
570 find(options.begin(), options.end(), "unicode=true");
571 if (it != options.end())
573 it = find(options.begin(), options.end(), "pdfusetitle");
574 if (it != options.end()) {
575 h_pdf_pdfusetitle = "1";
578 string bookmarks = process_keyval_opt(options, "bookmarks");
579 if (bookmarks == "true")
580 h_pdf_bookmarks = "1";
581 else if (bookmarks == "false")
582 h_pdf_bookmarks = "0";
583 if (h_pdf_bookmarks == "1") {
584 string bookmarksnumbered =
585 process_keyval_opt(options, "bookmarksnumbered");
586 if (bookmarksnumbered == "true")
587 h_pdf_bookmarksnumbered = "1";
588 else if (bookmarksnumbered == "false")
589 h_pdf_bookmarksnumbered = "0";
590 string bookmarksopen =
591 process_keyval_opt(options, "bookmarksopen");
592 if (bookmarksopen == "true")
593 h_pdf_bookmarksopen = "1";
594 else if (bookmarksopen == "false")
595 h_pdf_bookmarksopen = "0";
596 if (h_pdf_bookmarksopen == "1") {
597 string bookmarksopenlevel =
598 process_keyval_opt(options, "bookmarksopenlevel");
599 if (!bookmarksopenlevel.empty())
600 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
603 string breaklinks = process_keyval_opt(options, "breaklinks");
604 if (breaklinks == "true")
605 h_pdf_breaklinks = "1";
606 else if (breaklinks == "false")
607 h_pdf_breaklinks = "0";
608 string pdfborder = process_keyval_opt(options, "pdfborder");
609 if (pdfborder == "{0 0 0}")
610 h_pdf_pdfborder = "1";
611 else if (pdfborder == "{0 0 1}")
612 h_pdf_pdfborder = "0";
613 string backref = process_keyval_opt(options, "backref");
614 if (!backref.empty())
615 h_pdf_backref = backref;
616 string colorlinks = process_keyval_opt(options, "colorlinks");
617 if (colorlinks == "true")
618 h_pdf_colorlinks = "1";
619 else if (colorlinks == "false")
620 h_pdf_colorlinks = "0";
621 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
622 if (!pdfpagemode.empty())
623 h_pdf_pagemode = pdfpagemode;
624 string pdftitle = process_keyval_opt(options, "pdftitle");
625 if (!pdftitle.empty()) {
626 h_pdf_title = remove_braces(pdftitle);
628 string pdfauthor = process_keyval_opt(options, "pdfauthor");
629 if (!pdfauthor.empty()) {
630 h_pdf_author = remove_braces(pdfauthor);
632 string pdfsubject = process_keyval_opt(options, "pdfsubject");
633 if (!pdfsubject.empty())
634 h_pdf_subject = remove_braces(pdfsubject);
635 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
636 if (!pdfkeywords.empty())
637 h_pdf_keywords = remove_braces(pdfkeywords);
638 if (!options.empty()) {
639 if (!h_pdf_quoted_options.empty())
640 h_pdf_quoted_options += ',';
641 h_pdf_quoted_options += join(options, ",");
647 void Preamble::handle_geometry(vector<string> & options)
649 h_use_geometry = "true";
650 vector<string>::iterator it;
652 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
653 h_paperorientation = "landscape";
657 // keyval version: "paper=letter"
658 string paper = process_keyval_opt(options, "paper");
660 h_papersize = paper + "paper";
661 // alternative version: "letterpaper"
662 handle_opt(options, known_paper_sizes, h_papersize);
663 delete_opt(options, known_paper_sizes);
665 char const * const * margin = known_paper_margins;
666 for (; *margin; ++margin) {
667 string value = process_keyval_opt(options, *margin);
668 if (!value.empty()) {
669 int k = margin - known_paper_margins;
670 string name = known_coded_paper_margins[k];
671 h_margins += '\\' + name + ' ' + value + '\n';
677 void Preamble::handle_package(Parser &p, string const & name,
678 string const & opts, bool in_lyx_preamble,
681 vector<string> options = split_options(opts);
682 add_package(name, options);
683 char const * const * where = 0;
685 if (is_known(name, known_xetex_packages)) {
687 h_use_non_tex_fonts = true;
688 registerAutomaticallyLoadedPackage("fontspec");
689 if (h_inputencoding == "auto")
690 p.setEncoding("UTF-8");
694 if (is_known(name, known_roman_fonts))
695 h_font_roman[0] = name;
697 if (name == "fourier") {
698 h_font_roman[0] = "utopia";
699 // when font uses real small capitals
700 if (opts == "expert")
704 if (name == "garamondx") {
705 h_font_roman[0] = "garamondx";
710 if (name == "libertine") {
711 h_font_roman[0] = "libertine";
712 // this automatically invokes biolinum
713 h_font_sans[0] = "biolinum";
716 else if (opts == "lining")
717 h_font_osf = "false";
720 if (name == "libertine-type1") {
721 h_font_roman[0] = "libertine";
722 // NOTE: contrary to libertine.sty, libertine-type1
723 // does not automatically invoke biolinum
724 if (opts == "lining")
725 h_font_osf = "false";
726 else if (opts == "osf")
730 if (name == "mathdesign") {
731 if (opts.find("charter") != string::npos)
732 h_font_roman[0] = "md-charter";
733 if (opts.find("garamond") != string::npos)
734 h_font_roman[0] = "md-garamond";
735 if (opts.find("utopia") != string::npos)
736 h_font_roman[0] = "md-utopia";
737 if (opts.find("expert") != string::npos) {
743 else if (name == "mathpazo")
744 h_font_roman[0] = "palatino";
746 else if (name == "mathptmx")
747 h_font_roman[0] = "times";
750 if (is_known(name, known_sans_fonts)) {
751 h_font_sans[0] = name;
752 if (options.size() >= 1) {
753 if (scale_as_percentage(opts, h_font_sf_scale[0]))
758 if (name == "biolinum-type1") {
759 h_font_sans[0] = "biolinum";
760 // biolinum can have several options, e.g. [osf,scaled=0.97]
761 string::size_type pos = opts.find("osf");
762 if (pos != string::npos)
767 if (is_known(name, known_typewriter_fonts)) {
768 // fourier can be set as roman font _only_
769 // fourier as typewriter is handled in handling of \ttdefault
770 if (name != "fourier") {
771 h_font_typewriter[0] = name;
772 if (options.size() >= 1) {
773 if (scale_as_percentage(opts, h_font_tt_scale[0]))
779 if (name == "libertineMono-type1") {
780 h_font_typewriter[0] = "libertine-mono";
783 // font uses old-style figure
788 if (is_known(name, known_math_fonts))
789 h_font_math[0] = name;
791 if (name == "newtxmath") {
793 h_font_math[0] = "newtxmath";
794 else if (opts == "garamondx")
795 h_font_math[0] = "garamondx-ntxm";
796 else if (opts == "libertine")
797 h_font_math[0] = "libertine-ntxm";
798 else if (opts == "minion")
799 h_font_math[0] = "minion-ntxm";
804 h_font_math[0] = "iwona-math";
806 if (name == "kurier")
808 h_font_math[0] = "kurier-math";
810 // after the detection and handling of special cases, we can remove the
811 // fonts, otherwise they would appear in the preamble, see bug #7856
812 if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
813 || is_known(name, known_typewriter_fonts) || is_known(name, known_math_fonts))
815 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
816 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
817 name == "esint" || name == "mhchem" || name == "mathdots" ||
818 name == "mathtools" || name == "stackrel" ||
819 name == "stmaryrd" || name == "undertilde")
820 h_use_packages[name] = "2";
822 else if (name == "babel") {
823 h_language_package = "default";
824 // One might think we would have to do nothing if babel is loaded
825 // without any options to prevent pollution of the preamble with this
826 // babel call in every roundtrip.
827 // But the user could have defined babel-specific things afterwards. So
828 // we need to keep it in the preamble to prevent cases like bug #7861.
830 // check if more than one option was used - used later for inputenc
831 if (options.begin() != options.end() - 1)
832 one_language = false;
833 // babel takes the last language of the option of its \usepackage
834 // call as document language. If there is no such language option, the
835 // last language in the documentclass options is used.
836 handle_opt(options, known_languages, h_language);
837 // translate the babel name to a LyX name
838 h_language = babel2lyx(h_language);
839 if (h_language == "japanese") {
840 // For Japanese, the encoding isn't indicated in the source
841 // file, and there's really not much we can do. We could
842 // 1) offer a list of possible encodings to choose from, or
843 // 2) determine the encoding of the file by inspecting it.
844 // For the time being, we leave the encoding alone so that
845 // we don't get iconv errors when making a wrong guess, and
846 // we will output a note at the top of the document
847 // explaining what to do.
848 Encoding const * const enc = encodings.fromIconvName(
849 p.getEncoding(), Encoding::japanese, false);
851 h_inputencoding = enc->name();
852 is_nonCJKJapanese = true;
853 // in this case babel can be removed from the preamble
854 registerAutomaticallyLoadedPackage("babel");
856 // If babel is called with options, LyX puts them by default into the
857 // document class options. This works for most languages, except
858 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
859 // perhaps in future others.
860 // Therefore keep the babel call as it is as the user might have
862 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
864 delete_opt(options, known_languages);
866 h_preamble << "\\usepackage{babel}\n";
867 explicit_babel = true;
871 else if (name == "polyglossia") {
872 h_language_package = "default";
873 h_default_output_format = "pdf4";
874 h_use_non_tex_fonts = true;
876 registerAutomaticallyLoadedPackage("xunicode");
877 if (h_inputencoding == "auto")
878 p.setEncoding("UTF-8");
881 else if (name == "CJK") {
882 // set the encoding to "auto" because it might be set to "default" by the babel handling
883 // and this would not be correct for CJK
884 if (h_inputencoding == "default")
885 h_inputencoding = "auto";
886 registerAutomaticallyLoadedPackage("CJK");
889 else if (name == "CJKutf8") {
890 h_inputencoding = "utf8-cjk";
891 p.setEncoding("UTF-8");
892 registerAutomaticallyLoadedPackage("CJKutf8");
895 else if (name == "fontenc") {
896 h_fontencoding = getStringFromVector(options, ",");
897 /* We could do the following for better round trip support,
898 * but this makes the document less portable, so I skip it:
899 if (h_fontencoding == lyxrc.fontenc)
900 h_fontencoding = "global";
905 else if (name == "inputenc" || name == "luainputenc") {
906 // h_inputencoding is only set when there is not more than one
907 // inputenc option because otherwise h_inputencoding must be
908 // set to "auto" (the default encoding of the document language)
909 // Therefore check that exactly one option is passed to inputenc.
910 // It is also only set when there is not more than one babel
912 if (!options.empty()) {
913 string const encoding = options.back();
914 Encoding const * const enc = encodings.fromLaTeXName(
915 encoding, Encoding::inputenc, true);
918 cerr << "Unknown encoding " << encoding
919 << ". Ignoring." << std::endl;
921 if (!enc->unsafe() && options.size() == 1 && one_language == true)
922 h_inputencoding = enc->name();
923 p.setEncoding(enc->iconvName());
929 else if (name == "srcltx") {
932 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
935 h_output_sync_macro = "\\usepackage{srcltx}";
938 else if (is_known(name, known_old_language_packages)) {
939 // known language packages from the times before babel
940 // if they are found and not also babel, they will be used as
941 // custom language package
942 h_language_package = "\\usepackage{" + name + "}";
945 else if (name == "lyxskak") {
946 // ignore this and its options
947 const char * const o[] = {"ps", "mover", 0};
948 delete_opt(options, o);
951 else if (is_known(name, known_lyx_packages) && options.empty()) {
952 if (name == "splitidx")
953 h_use_indices = "true";
954 if (name == "refstyle")
955 h_use_refstyle = true;
956 else if (name == "prettyref")
957 h_use_refstyle = false;
958 if (!in_lyx_preamble) {
959 h_preamble << package_beg_sep << name
960 << package_mid_sep << "\\usepackage{"
962 if (p.next_token().cat() == catNewline ||
963 (p.next_token().cat() == catSpace &&
964 p.next_next_token().cat() == catNewline))
966 h_preamble << package_end_sep;
970 else if (name == "geometry")
971 handle_geometry(options);
973 else if (name == "subfig")
974 ; // ignore this FIXME: Use the package separator mechanism instead
976 else if ((where = is_known(name, known_languages)))
977 h_language = known_coded_languages[where - known_languages];
979 else if (name == "natbib") {
980 h_biblio_style = "plainnat";
981 h_cite_engine = "natbib";
982 h_cite_engine_type = "authoryear";
983 vector<string>::iterator it =
984 find(options.begin(), options.end(), "authoryear");
985 if (it != options.end())
988 it = find(options.begin(), options.end(), "numbers");
989 if (it != options.end()) {
990 h_cite_engine_type = "numerical";
996 else if (name == "jurabib") {
997 h_biblio_style = "jurabib";
998 h_cite_engine = "jurabib";
999 h_cite_engine_type = "authoryear";
1002 else if (name == "bibtopic")
1003 h_use_bibtopic = "true";
1005 else if (name == "hyperref")
1006 handle_hyperref(options);
1008 else if (name == "algorithm2e") {
1009 // Load "algorithm2e" module
1010 addModule("algorithm2e");
1011 // Add the package options to the global document options
1012 if (!options.empty()) {
1013 if (h_options.empty())
1014 h_options = join(options, ",");
1016 h_options += ',' + join(options, ",");
1019 else if (name == "microtype") {
1020 //we internally support only microtype without params
1021 if (options.empty())
1022 h_use_microtype = true;
1024 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1027 else if (!in_lyx_preamble) {
1028 if (options.empty())
1029 h_preamble << "\\usepackage{" << name << '}';
1031 h_preamble << "\\usepackage[" << opts << "]{"
1035 if (p.next_token().cat() == catNewline ||
1036 (p.next_token().cat() == catSpace &&
1037 p.next_next_token().cat() == catNewline))
1041 // We need to do something with the options...
1042 if (!options.empty() && !detectEncoding)
1043 cerr << "Ignoring options '" << join(options, ",")
1044 << "' of package " << name << '.' << endl;
1046 // remove the whitespace
1051 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1054 Token t = p.get_token();
1055 if (t.cat() == catEscape &&
1056 is_known(t.cs(), known_if_commands))
1057 handle_if(p, in_lyx_preamble);
1059 if (!in_lyx_preamble)
1060 h_preamble << t.asInput();
1061 if (t.cat() == catEscape && t.cs() == "fi")
1068 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1070 // set the quote language
1071 // LyX only knows the following quotes languages:
1072 // english, swedish, german, polish, french and danish
1073 // (quotes for "japanese" and "chinese-traditional" are missing because
1074 // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
1075 // conversion list taken from
1076 // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
1077 // (quotes for kazakh and interlingua are unknown)
1079 if (is_known(h_language, known_danish_quotes_languages))
1080 h_quotes_language = "danish";
1082 else if (is_known(h_language, known_french_quotes_languages))
1083 h_quotes_language = "french";
1085 else if (is_known(h_language, known_german_quotes_languages))
1086 h_quotes_language = "german";
1088 else if (is_known(h_language, known_polish_quotes_languages))
1089 h_quotes_language = "polish";
1091 else if (is_known(h_language, known_swedish_quotes_languages))
1092 h_quotes_language = "swedish";
1094 else if (is_known(h_language, known_english_quotes_languages))
1095 h_quotes_language = "english";
1097 if (contains(h_float_placement, "H"))
1098 registerAutomaticallyLoadedPackage("float");
1099 if (h_spacing != "single" && h_spacing != "default")
1100 registerAutomaticallyLoadedPackage("setspace");
1101 if (h_use_packages["amsmath"] == "2") {
1102 // amsbsy and amstext are already provided by amsmath
1103 registerAutomaticallyLoadedPackage("amsbsy");
1104 registerAutomaticallyLoadedPackage("amstext");
1107 // output the LyX file settings
1108 // Important: Keep the version formatting in sync with LyX and
1109 // lyx2lyx (bug 7951)
1110 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1111 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1112 << lyx_version_minor << '\n'
1113 << "\\lyxformat " << LYX_FORMAT << '\n'
1114 << "\\begin_document\n"
1115 << "\\begin_header\n"
1116 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1117 << "\\origin " << origin << "\n"
1118 << "\\textclass " << h_textclass << "\n";
1119 string const raw = subdoc ? empty_string() : h_preamble.str();
1121 os << "\\begin_preamble\n";
1122 for (string::size_type i = 0; i < raw.size(); ++i) {
1123 if (raw[i] == package_beg_sep) {
1124 // Here follows some package loading code that
1125 // must be skipped if the package is loaded
1127 string::size_type j = raw.find(package_mid_sep, i);
1128 if (j == string::npos)
1130 string::size_type k = raw.find(package_end_sep, j);
1131 if (k == string::npos)
1133 string const package = raw.substr(i + 1, j - i - 1);
1134 string const replacement = raw.substr(j + 1, k - j - 1);
1135 if (auto_packages.find(package) == auto_packages.end())
1141 os << "\n\\end_preamble\n";
1143 if (!h_options.empty())
1144 os << "\\options " << h_options << "\n";
1145 os << "\\use_default_options " << h_use_default_options << "\n";
1146 if (!used_modules.empty()) {
1147 os << "\\begin_modules\n";
1148 vector<string>::const_iterator const end = used_modules.end();
1149 vector<string>::const_iterator it = used_modules.begin();
1150 for (; it != end; ++it)
1152 os << "\\end_modules\n";
1154 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1155 << "\\language " << h_language << "\n"
1156 << "\\language_package " << h_language_package << "\n"
1157 << "\\inputencoding " << h_inputencoding << "\n"
1158 << "\\fontencoding " << h_fontencoding << "\n"
1159 << "\\font_roman \"" << h_font_roman[0]
1160 << "\" \"" << h_font_roman[1] << "\"\n"
1161 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1162 << "\\font_typewriter \"" << h_font_typewriter[0]
1163 << "\" \"" << h_font_typewriter[1] << "\"\n"
1164 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1165 << "\\font_default_family " << h_font_default_family << "\n"
1166 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1167 << "\\font_sc " << h_font_sc << "\n"
1168 << "\\font_osf " << h_font_osf << "\n"
1169 << "\\font_sf_scale " << h_font_sf_scale[0]
1170 << ' ' << h_font_sf_scale[1] << '\n'
1171 << "\\font_tt_scale " << h_font_tt_scale[0]
1172 << ' ' << h_font_tt_scale[1] << '\n';
1173 if (!h_font_cjk.empty())
1174 os << "\\font_cjk " << h_font_cjk << '\n';
1175 os << "\\use_microtype " << h_use_microtype << '\n'
1176 << "\\graphics " << h_graphics << '\n'
1177 << "\\default_output_format " << h_default_output_format << "\n"
1178 << "\\output_sync " << h_output_sync << "\n";
1179 if (h_output_sync == "1")
1180 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1181 os << "\\bibtex_command " << h_bibtex_command << "\n"
1182 << "\\index_command " << h_index_command << "\n";
1183 if (!h_float_placement.empty())
1184 os << "\\float_placement " << h_float_placement << "\n";
1185 os << "\\paperfontsize " << h_paperfontsize << "\n"
1186 << "\\spacing " << h_spacing << "\n"
1187 << "\\use_hyperref " << h_use_hyperref << '\n';
1188 if (h_use_hyperref == "true") {
1189 if (!h_pdf_title.empty())
1190 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1191 if (!h_pdf_author.empty())
1192 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1193 if (!h_pdf_subject.empty())
1194 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1195 if (!h_pdf_keywords.empty())
1196 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1197 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1198 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1199 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1200 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1201 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1202 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1203 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1204 "\\pdf_backref " << h_pdf_backref << "\n"
1205 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1206 if (!h_pdf_pagemode.empty())
1207 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1208 if (!h_pdf_quoted_options.empty())
1209 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1211 os << "\\papersize " << h_papersize << "\n"
1212 << "\\use_geometry " << h_use_geometry << '\n';
1213 for (map<string, string>::const_iterator it = h_use_packages.begin();
1214 it != h_use_packages.end(); ++it)
1215 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1216 os << "\\cite_engine " << h_cite_engine << '\n'
1217 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1218 << "\\biblio_style " << h_biblio_style << "\n"
1219 << "\\use_bibtopic " << h_use_bibtopic << "\n"
1220 << "\\use_indices " << h_use_indices << "\n"
1221 << "\\paperorientation " << h_paperorientation << '\n'
1222 << "\\suppress_date " << h_suppress_date << '\n'
1223 << "\\justification " << h_justification << '\n'
1224 << "\\use_refstyle " << h_use_refstyle << '\n';
1225 if (!h_fontcolor.empty())
1226 os << "\\fontcolor " << h_fontcolor << '\n';
1227 if (!h_notefontcolor.empty())
1228 os << "\\notefontcolor " << h_notefontcolor << '\n';
1229 if (!h_backgroundcolor.empty())
1230 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1231 if (!h_boxbgcolor.empty())
1232 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1233 if (index_number != 0)
1234 for (int i = 0; i < index_number; i++) {
1235 os << "\\index " << h_index[i] << '\n'
1236 << "\\shortcut " << h_shortcut[i] << '\n'
1237 << "\\color " << h_color << '\n'
1241 os << "\\index " << h_index[0] << '\n'
1242 << "\\shortcut " << h_shortcut[0] << '\n'
1243 << "\\color " << h_color << '\n'
1247 << "\\secnumdepth " << h_secnumdepth << "\n"
1248 << "\\tocdepth " << h_tocdepth << "\n"
1249 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1250 if (h_paragraph_separation == "skip")
1251 os << "\\defskip " << h_defskip << "\n";
1253 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1254 os << "\\quotes_language " << h_quotes_language << "\n"
1255 << "\\papercolumns " << h_papercolumns << "\n"
1256 << "\\papersides " << h_papersides << "\n"
1257 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1258 if (!h_listings_params.empty())
1259 os << "\\listings_params " << h_listings_params << "\n";
1260 os << "\\tracking_changes " << h_tracking_changes << "\n"
1261 << "\\output_changes " << h_output_changes << "\n"
1262 << "\\html_math_output " << h_html_math_output << "\n"
1263 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1264 << "\\html_be_strict " << h_html_be_strict << "\n"
1266 << "\\end_header\n\n"
1267 << "\\begin_body\n";
1272 void Preamble::parse(Parser & p, string const & forceclass,
1273 TeX2LyXDocClass & tc)
1275 // initialize fixed types
1276 special_columns_['D'] = 3;
1277 parse(p, forceclass, false, tc);
1281 void Preamble::parse(Parser & p, string const & forceclass,
1282 bool detectEncoding, TeX2LyXDocClass & tc)
1284 bool is_full_document = false;
1285 bool is_lyx_file = false;
1286 bool in_lyx_preamble = false;
1288 // determine whether this is a full document or a fragment for inclusion
1290 Token const & t = p.get_token();
1292 if (t.cat() == catEscape && t.cs() == "documentclass") {
1293 is_full_document = true;
1299 if (detectEncoding && !is_full_document)
1302 while (is_full_document && p.good()) {
1303 if (detectEncoding && h_inputencoding != "auto" &&
1304 h_inputencoding != "default")
1307 Token const & t = p.get_token();
1310 if (!detectEncoding)
1311 cerr << "t: " << t << '\n';
1317 if (!in_lyx_preamble &&
1318 (t.cat() == catLetter ||
1319 t.cat() == catSuper ||
1320 t.cat() == catSub ||
1321 t.cat() == catOther ||
1322 t.cat() == catMath ||
1323 t.cat() == catActive ||
1324 t.cat() == catBegin ||
1325 t.cat() == catEnd ||
1326 t.cat() == catAlign ||
1327 t.cat() == catParameter))
1328 h_preamble << t.cs();
1330 else if (!in_lyx_preamble &&
1331 (t.cat() == catSpace || t.cat() == catNewline))
1332 h_preamble << t.asInput();
1334 else if (t.cat() == catComment) {
1335 static regex const islyxfile("%% LyX .* created this file");
1336 static regex const usercommands("User specified LaTeX commands");
1338 string const comment = t.asInput();
1340 // magically switch encoding default if it looks like XeLaTeX
1341 static string const magicXeLaTeX =
1342 "% This document must be compiled with XeLaTeX ";
1343 if (comment.size() > magicXeLaTeX.size()
1344 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1345 && h_inputencoding == "auto") {
1346 if (!detectEncoding)
1347 cerr << "XeLaTeX comment found, switching to UTF8\n";
1348 h_inputencoding = "utf8";
1351 if (regex_search(comment, sub, islyxfile)) {
1353 in_lyx_preamble = true;
1354 } else if (is_lyx_file
1355 && regex_search(comment, sub, usercommands))
1356 in_lyx_preamble = false;
1357 else if (!in_lyx_preamble)
1358 h_preamble << t.asInput();
1361 else if (t.cs() == "pagestyle")
1362 h_paperpagestyle = p.verbatim_item();
1364 else if (t.cs() == "setdefaultlanguage") {
1366 // We don't yet care about non-language variant options
1367 // because LyX doesn't support this yet, see bug #8214
1369 string langopts = p.getOpt();
1370 // check if the option contains a variant, if yes, extract it
1371 string::size_type pos_var = langopts.find("variant");
1372 string::size_type i = langopts.find(',', pos_var);
1373 string::size_type k = langopts.find('=', pos_var);
1374 if (pos_var != string::npos){
1376 if (i == string::npos)
1377 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1379 variant = langopts.substr(k + 1, i - k - 1);
1380 h_language = variant;
1384 h_language = p.verbatim_item();
1385 //finally translate the poyglossia name to a LyX name
1386 h_language = polyglossia2lyx(h_language);
1389 else if (t.cs() == "setotherlanguage") {
1390 // We don't yet care about the option because LyX doesn't
1391 // support this yet, see bug #8214
1392 p.hasOpt() ? p.getOpt() : string();
1396 else if (t.cs() == "setmainfont") {
1397 // we don't care about the option
1398 p.hasOpt() ? p.getOpt() : string();
1399 h_font_roman[1] = p.getArg('{', '}');
1402 else if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1403 // LyX currently only supports the scale option
1406 string fontopts = p.getArg('[', ']');
1407 // check if the option contains a scaling, if yes, extract it
1408 string::size_type pos = fontopts.find("Scale");
1409 if (pos != string::npos) {
1410 string::size_type i = fontopts.find(',', pos);
1411 if (i == string::npos)
1412 scale_as_percentage(fontopts.substr(pos + 1), scale);
1414 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1417 if (t.cs() == "setsansfont") {
1419 h_font_sf_scale[1] = scale;
1420 h_font_sans[1] = p.getArg('{', '}');
1423 h_font_tt_scale[1] = scale;
1424 h_font_typewriter[1] = p.getArg('{', '}');
1428 else if (t.cs() == "date") {
1429 string argument = p.getArg('{', '}');
1430 if (argument.empty())
1431 h_suppress_date = "true";
1433 h_preamble << t.asInput() << '{' << argument << '}';
1436 else if (t.cs() == "color") {
1437 string const space =
1438 (p.hasOpt() ? p.getOpt() : string());
1439 string argument = p.getArg('{', '}');
1440 // check the case that a standard color is used
1441 if (space.empty() && is_known(argument, known_basic_colors)) {
1442 h_fontcolor = rgbcolor2code(argument);
1443 registerAutomaticallyLoadedPackage("color");
1444 } else if (space.empty() && argument == "document_fontcolor")
1445 registerAutomaticallyLoadedPackage("color");
1446 // check the case that LyX's document_fontcolor is defined
1447 // but not used for \color
1449 h_preamble << t.asInput();
1451 h_preamble << space;
1452 h_preamble << '{' << argument << '}';
1453 // the color might already be set because \definecolor
1454 // is parsed before this
1459 else if (t.cs() == "pagecolor") {
1460 string argument = p.getArg('{', '}');
1461 // check the case that a standard color is used
1462 if (is_known(argument, known_basic_colors)) {
1463 h_backgroundcolor = rgbcolor2code(argument);
1464 } else if (argument == "page_backgroundcolor")
1465 registerAutomaticallyLoadedPackage("color");
1466 // check the case that LyX's page_backgroundcolor is defined
1467 // but not used for \pagecolor
1469 h_preamble << t.asInput() << '{' << argument << '}';
1470 // the color might already be set because \definecolor
1471 // is parsed before this
1472 h_backgroundcolor = "";
1476 else if (t.cs() == "makeatletter") {
1477 // LyX takes care of this
1478 p.setCatcode('@', catLetter);
1481 else if (t.cs() == "makeatother") {
1482 // LyX takes care of this
1483 p.setCatcode('@', catOther);
1486 else if (t.cs() == "makeindex") {
1487 // LyX will re-add this if a print index command is found
1491 else if (t.cs() == "newindex") {
1492 string const indexname = p.getArg('[', ']');
1493 string const shortcut = p.verbatim_item();
1494 if (!indexname.empty())
1495 h_index[index_number] = indexname;
1497 h_index[index_number] = shortcut;
1498 h_shortcut[index_number] = shortcut;
1503 else if (t.cs() == "RS@ifundefined") {
1504 string const name = p.verbatim_item();
1505 string const body1 = p.verbatim_item();
1506 string const body2 = p.verbatim_item();
1507 // only non-lyxspecific stuff
1508 if (in_lyx_preamble &&
1509 (name == "subsecref" || name == "thmref" || name == "lemref"))
1513 ss << '\\' << t.cs();
1514 ss << '{' << name << '}'
1515 << '{' << body1 << '}'
1516 << '{' << body2 << '}';
1517 h_preamble << ss.str();
1521 else if (t.cs() == "AtBeginDocument") {
1522 string const name = p.verbatim_item();
1523 // only non-lyxspecific stuff
1524 if (in_lyx_preamble &&
1525 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
1526 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
1527 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
1528 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
1529 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
1530 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
1531 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
1532 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
1533 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
1534 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
1535 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
1536 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
1537 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
1538 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
1539 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
1543 ss << '\\' << t.cs();
1544 ss << '{' << name << '}';
1545 h_preamble << ss.str();
1549 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1550 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1551 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1552 || t.cs() == "DeclareRobustCommand"
1553 || t.cs() == "DeclareRobustCommandx"
1554 || t.cs() == "ProvideTextCommandDefault"
1555 || t.cs() == "DeclareMathAccent") {
1557 if (p.next_token().character() == '*') {
1561 string const name = p.verbatim_item();
1562 string const opt1 = p.getFullOpt();
1563 string const opt2 = p.getFullOpt();
1564 string const body = p.verbatim_item();
1565 // store the in_lyx_preamble setting
1566 bool const was_in_lyx_preamble = in_lyx_preamble;
1568 if (name == "\\rmdefault")
1569 if (is_known(body, known_roman_fonts)) {
1570 h_font_roman[0] = body;
1572 in_lyx_preamble = true;
1574 if (name == "\\sfdefault")
1575 if (is_known(body, known_sans_fonts)) {
1576 h_font_sans[0] = body;
1578 in_lyx_preamble = true;
1580 if (name == "\\ttdefault")
1581 if (is_known(body, known_typewriter_fonts)) {
1582 h_font_typewriter[0] = body;
1584 in_lyx_preamble = true;
1586 if (name == "\\familydefault") {
1587 string family = body;
1588 // remove leading "\"
1589 h_font_default_family = family.erase(0,1);
1591 in_lyx_preamble = true;
1594 // remove LyX-specific definitions that are re-added by LyX
1596 // \lyxline is an ancient command that is converted by tex2lyx into
1597 // a \rule therefore remove its preamble code
1598 if (name == "\\lyxdot" || name == "\\lyxarrow"
1599 || name == "\\lyxline" || name == "\\LyX") {
1601 in_lyx_preamble = true;
1604 // Add the command to the known commands
1605 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1607 // only non-lyxspecific stuff
1608 if (!in_lyx_preamble) {
1610 ss << '\\' << t.cs();
1613 ss << '{' << name << '}' << opt1 << opt2
1614 << '{' << body << "}";
1615 h_preamble << ss.str();
1617 ostream & out = in_preamble ? h_preamble : os;
1618 out << "\\" << t.cs() << "{" << name << "}"
1619 << opts << "{" << body << "}";
1622 // restore the in_lyx_preamble setting
1623 in_lyx_preamble = was_in_lyx_preamble;
1626 else if (t.cs() == "documentclass") {
1627 vector<string>::iterator it;
1628 vector<string> opts = split_options(p.getArg('[', ']'));
1629 handle_opt(opts, known_fontsizes, h_paperfontsize);
1630 delete_opt(opts, known_fontsizes);
1631 // delete "pt" at the end
1632 string::size_type i = h_paperfontsize.find("pt");
1633 if (i != string::npos)
1634 h_paperfontsize.erase(i);
1635 // The documentclass options are always parsed before the options
1636 // of the babel call so that a language cannot overwrite the babel
1638 handle_opt(opts, known_languages, h_language);
1639 delete_opt(opts, known_languages);
1641 // paper orientation
1642 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1643 h_paperorientation = "landscape";
1647 if ((it = find(opts.begin(), opts.end(), "oneside"))
1652 if ((it = find(opts.begin(), opts.end(), "twoside"))
1658 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1660 h_papercolumns = "1";
1663 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1665 h_papercolumns = "2";
1669 // some size options are known to any document classes, other sizes
1670 // are handled by the \geometry command of the geometry package
1671 handle_opt(opts, known_class_paper_sizes, h_papersize);
1672 delete_opt(opts, known_class_paper_sizes);
1673 // the remaining options
1674 h_options = join(opts, ",");
1675 // FIXME This does not work for classes that have a
1676 // different name in LyX than in LaTeX
1677 h_textclass = p.getArg('{', '}');
1681 else if (t.cs() == "usepackage") {
1682 string const options = p.getArg('[', ']');
1683 string const name = p.getArg('{', '}');
1684 vector<string> vecnames;
1685 split(name, vecnames, ',');
1686 vector<string>::const_iterator it = vecnames.begin();
1687 vector<string>::const_iterator end = vecnames.end();
1688 for (; it != end; ++it)
1689 handle_package(p, trimSpaceAndEol(*it), options,
1690 in_lyx_preamble, detectEncoding);
1693 else if (t.cs() == "inputencoding") {
1694 string const encoding = p.getArg('{','}');
1695 Encoding const * const enc = encodings.fromLaTeXName(
1696 encoding, Encoding::inputenc, true);
1698 if (!detectEncoding)
1699 cerr << "Unknown encoding " << encoding
1700 << ". Ignoring." << std::endl;
1703 h_inputencoding = enc->name();
1704 p.setEncoding(enc->iconvName());
1708 else if (t.cs() == "newenvironment") {
1709 string const name = p.getArg('{', '}');
1710 string const opt1 = p.getFullOpt();
1711 string const opt2 = p.getFullOpt();
1712 string const beg = p.verbatim_item();
1713 string const end = p.verbatim_item();
1714 if (!in_lyx_preamble) {
1715 h_preamble << "\\newenvironment{" << name
1716 << '}' << opt1 << opt2 << '{'
1717 << beg << "}{" << end << '}';
1719 add_known_environment(name, opt1, !opt2.empty(),
1720 from_utf8(beg), from_utf8(end));
1724 else if (t.cs() == "newtheorem") {
1725 string const name = p.getArg('{', '}');
1726 string const opt1 = p.getFullOpt();
1727 string const opt2 = p.getFullOpt();
1728 string const body = p.verbatim_item();
1729 string const opt3 = p.getFullOpt();
1731 string const complete = "\\newtheorem{" + name + '}' +
1732 opt1 + opt2 + '{' + body + '}' + opt3;
1734 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
1736 if (!in_lyx_preamble)
1737 h_preamble << complete;
1740 else if (t.cs() == "def") {
1741 string name = p.get_token().cs();
1742 // In fact, name may be more than the name:
1743 // In the test case of bug 8116
1744 // name == "csname SF@gobble@opt \endcsname".
1745 // Therefore, we need to use asInput() instead of cs().
1746 while (p.next_token().cat() != catBegin)
1747 name += p.get_token().asInput();
1748 if (!in_lyx_preamble)
1749 h_preamble << "\\def\\" << name << '{'
1750 << p.verbatim_item() << "}";
1753 else if (t.cs() == "newcolumntype") {
1754 string const name = p.getArg('{', '}');
1755 trimSpaceAndEol(name);
1757 string opts = p.getOpt();
1758 if (!opts.empty()) {
1759 istringstream is(string(opts, 1));
1762 special_columns_[name[0]] = nargs;
1763 h_preamble << "\\newcolumntype{" << name << "}";
1765 h_preamble << "[" << nargs << "]";
1766 h_preamble << "{" << p.verbatim_item() << "}";
1769 else if (t.cs() == "setcounter") {
1770 string const name = p.getArg('{', '}');
1771 string const content = p.getArg('{', '}');
1772 if (name == "secnumdepth")
1773 h_secnumdepth = content;
1774 else if (name == "tocdepth")
1775 h_tocdepth = content;
1777 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1780 else if (t.cs() == "setlength") {
1781 string const name = p.verbatim_item();
1782 string const content = p.verbatim_item();
1783 // the paragraphs are only not indented when \parindent is set to zero
1784 if (name == "\\parindent" && content != "") {
1785 if (content[0] == '0')
1786 h_paragraph_separation = "skip";
1788 h_paragraph_indentation = translate_len(content);
1789 } else if (name == "\\parskip") {
1790 if (content == "\\smallskipamount")
1791 h_defskip = "smallskip";
1792 else if (content == "\\medskipamount")
1793 h_defskip = "medskip";
1794 else if (content == "\\bigskipamount")
1795 h_defskip = "bigskip";
1797 h_defskip = translate_len(content);
1799 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1802 else if (t.cs() == "onehalfspacing")
1803 h_spacing = "onehalf";
1805 else if (t.cs() == "doublespacing")
1806 h_spacing = "double";
1808 else if (t.cs() == "setstretch")
1809 h_spacing = "other " + p.verbatim_item();
1811 else if (t.cs() == "synctex") {
1812 // the scheme is \synctex=value
1813 // where value can only be "1" or "-1"
1814 h_output_sync = "1";
1815 // there can be any character behind the value (e.g. a linebreak or a '\'
1816 // therefore we extract it char by char
1818 string value = p.get_token().asInput();
1820 value += p.get_token().asInput();
1821 h_output_sync_macro = "\\synctex=" + value;
1824 else if (t.cs() == "begin") {
1825 string const name = p.getArg('{', '}');
1826 if (name == "document")
1828 h_preamble << "\\begin{" << name << "}";
1831 else if (t.cs() == "geometry") {
1832 vector<string> opts = split_options(p.getArg('{', '}'));
1833 handle_geometry(opts);
1836 else if (t.cs() == "definecolor") {
1837 string const color = p.getArg('{', '}');
1838 string const space = p.getArg('{', '}');
1839 string const value = p.getArg('{', '}');
1840 if (color == "document_fontcolor" && space == "rgb") {
1841 RGBColor c(RGBColorFromLaTeX(value));
1842 h_fontcolor = X11hexname(c);
1843 } else if (color == "note_fontcolor" && space == "rgb") {
1844 RGBColor c(RGBColorFromLaTeX(value));
1845 h_notefontcolor = X11hexname(c);
1846 } else if (color == "page_backgroundcolor" && space == "rgb") {
1847 RGBColor c(RGBColorFromLaTeX(value));
1848 h_backgroundcolor = X11hexname(c);
1849 } else if (color == "shadecolor" && space == "rgb") {
1850 RGBColor c(RGBColorFromLaTeX(value));
1851 h_boxbgcolor = X11hexname(c);
1853 h_preamble << "\\definecolor{" << color
1854 << "}{" << space << "}{" << value
1859 else if (t.cs() == "bibliographystyle")
1860 h_biblio_style = p.verbatim_item();
1862 else if (t.cs() == "jurabibsetup") {
1863 // FIXME p.getArg('{', '}') is most probably wrong (it
1864 // does not handle nested braces).
1865 // Use p.verbatim_item() instead.
1866 vector<string> jurabibsetup =
1867 split_options(p.getArg('{', '}'));
1868 // add jurabibsetup to the jurabib package options
1869 add_package("jurabib", jurabibsetup);
1870 if (!jurabibsetup.empty()) {
1871 h_preamble << "\\jurabibsetup{"
1872 << join(jurabibsetup, ",") << '}';
1876 else if (t.cs() == "hypersetup") {
1877 vector<string> hypersetup =
1878 split_options(p.verbatim_item());
1879 // add hypersetup to the hyperref package options
1880 handle_hyperref(hypersetup);
1881 if (!hypersetup.empty()) {
1882 h_preamble << "\\hypersetup{"
1883 << join(hypersetup, ",") << '}';
1887 else if (is_known(t.cs(), known_if_3arg_commands)) {
1888 // prevent misparsing of \usepackage if it is used
1889 // as an argument (see e.g. our own output of
1890 // \@ifundefined above)
1891 string const arg1 = p.verbatim_item();
1892 string const arg2 = p.verbatim_item();
1893 string const arg3 = p.verbatim_item();
1894 // test case \@ifundefined{date}{}{\date{}}
1895 if (t.cs() == "@ifundefined" && arg1 == "date" &&
1896 arg2.empty() && arg3 == "\\date{}") {
1897 h_suppress_date = "true";
1898 // older tex2lyx versions did output
1899 // \@ifundefined{definecolor}{\usepackage{color}}{}
1900 } else if (t.cs() == "@ifundefined" &&
1901 arg1 == "definecolor" &&
1902 arg2 == "\\usepackage{color}" &&
1904 if (!in_lyx_preamble)
1905 h_preamble << package_beg_sep
1908 << "\\@ifundefined{definecolor}{color}{}"
1911 //\@ifundefined{showcaptionsetup}{}{%
1912 // \PassOptionsToPackage{caption=false}{subfig}}
1913 // that LyX uses for subfloats
1914 } else if (t.cs() == "@ifundefined" &&
1915 arg1 == "showcaptionsetup" && arg2.empty()
1916 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1918 } else if (!in_lyx_preamble) {
1919 h_preamble << t.asInput()
1920 << '{' << arg1 << '}'
1921 << '{' << arg2 << '}'
1922 << '{' << arg3 << '}';
1926 else if (is_known(t.cs(), known_if_commands)) {
1927 // must not parse anything in conditional code, since
1928 // LyX would output the parsed contents unconditionally
1929 if (!in_lyx_preamble)
1930 h_preamble << t.asInput();
1931 handle_if(p, in_lyx_preamble);
1934 else if (!t.cs().empty() && !in_lyx_preamble)
1935 h_preamble << '\\' << t.cs();
1938 // remove the whitespace
1941 // Force textclass if the user wanted it
1942 if (!forceclass.empty())
1943 h_textclass = forceclass;
1944 tc.setName(h_textclass);
1946 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1949 if (h_papersides.empty()) {
1952 h_papersides = ss.str();
1955 // If the CJK package is used we cannot set the document language from
1956 // the babel options. Instead, we guess which language is used most
1957 // and set this one.
1958 default_language = h_language;
1959 if (is_full_document &&
1960 (auto_packages.find("CJK") != auto_packages.end() ||
1961 auto_packages.find("CJKutf8") != auto_packages.end())) {
1963 h_language = guessLanguage(p, default_language);
1965 if (explicit_babel && h_language != default_language) {
1966 // We set the document language to a CJK language,
1967 // but babel is explicitly called in the user preamble
1968 // without options. LyX will not add the default
1969 // language to the document options if it is either
1970 // english, or no text is set as default language.
1971 // Therefore we need to add a language option explicitly.
1972 // FIXME: It would be better to remove all babel calls
1973 // from the user preamble, but this is difficult
1974 // without re-introducing bug 7861.
1975 if (h_options.empty())
1976 h_options = lyx2babel(default_language);
1978 h_options += ',' + lyx2babel(default_language);
1984 string Preamble::parseEncoding(Parser & p, string const & forceclass)
1986 TeX2LyXDocClass dummy;
1987 parse(p, forceclass, true, dummy);
1988 if (h_inputencoding != "auto" && h_inputencoding != "default")
1989 return h_inputencoding;
1994 string babel2lyx(string const & language)
1996 char const * const * where = is_known(language, known_languages);
1998 return known_coded_languages[where - known_languages];
2003 string lyx2babel(string const & language)
2005 char const * const * where = is_known(language, known_coded_languages);
2007 return known_languages[where - known_coded_languages];
2012 string Preamble::polyglossia2lyx(string const & language)
2014 char const * const * where = is_known(language, polyglossia_languages);
2016 return coded_polyglossia_languages[where - polyglossia_languages];
2021 string rgbcolor2code(string const & name)
2023 char const * const * where = is_known(name, known_basic_colors);
2025 // "red", "green" etc
2026 return known_basic_color_codes[where - known_basic_colors];
2028 // "255,0,0", "0,255,0" etc
2029 RGBColor c(RGBColorFromLaTeX(name));
2030 return X11hexname(c);