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.
18 #include "LayoutFile.h"
21 #include "TextClass.h"
23 #include "support/convert.h"
24 #include "support/FileName.h"
25 #include "support/filetools.h"
26 #include "support/lstrings.h"
28 #include "support/regex.h"
38 using namespace lyx::support;
43 // special columntypes
44 extern map<char, int> special_columns;
46 map<string, vector<string> > used_packages;
47 const char * const modules_placeholder = "\001modules\001";
49 // needed to handle encodings with babel
50 bool one_language = true;
51 string h_inputencoding = "auto";
52 string h_paragraph_separation = "indent";
56 //add this to known_languages when updating to lyxformat 266:
57 // "armenian" (needs special handling since not supported by standard babel)
58 //add these to known_languages when updating to lyxformat 268:
59 //"chinese-simplified", "chinese-traditional", "japanese", "korean"
60 // Both changes require first that support for non-babel languages (CJK,
62 // add turkmen for lyxformat 383
64 * known babel language names (including synonyms)
65 * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
66 * not yet supported by LyX: kurmanji
67 * please keep this in sync with known_coded_languages line by line!
69 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
70 "american", "arabic", "arabtex", "austrian", "bahasa", "bahasai", "bahasam",
71 "basque", "belarusian", "brazil", "brazilian", "breton", "british", "bulgarian",
72 "canadian", "canadien", "catalan", "croatian", "czech", "danish", "dutch",
73 "english", "esperanto", "estonian", "farsi", "finnish", "francais", "french",
74 "frenchb", "frenchle", "frenchpro", "galician", "german", "germanb", "greek",
75 "hebrew", "hungarian", "icelandic", "indon", "indonesian", "interlingua",
76 "irish", "italian", "kazakh", "latin", "latvian", "lithuanian", "lowersorbian",
77 "lsorbian", "magyar", "malay", "meyalu", "mongolian", "naustrian", "newzealand",
78 "ngerman", "ngermanb", "norsk", "nynorsk", "polutonikogreek", "polish",
79 "portuges", "portuguese", "romanian", "russian", "russianb", "samin",
80 "scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish",
81 "swedish", "thai", "turkish", "turkmen", "ukraineb", "ukrainian",
82 "uppersorbian", "UKenglish", "USenglish", "usorbian", "vietnam", "welsh",
86 * the same as known_languages with .lyx names
87 * please keep this in sync with known_languages line by line!
89 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
90 "american", "arabic_arabi", "arabic_arabtex", "austrian", "bahasa", "bahasa", "bahasam",
91 "basque", "belarusian", "brazilian", "brazilian", "breton", "british", "bulgarian",
92 "canadian", "canadien", "catalan", "croatian", "czech", "danish", "dutch",
93 "english", "esperanto", "estonian", "farsi", "finnish", "french", "french",
94 "french", "french", "french", "galician", "german", "german", "greek",
95 "hebrew", "magyar", "icelandic", "bahasa", "bahasa", "interlingua",
96 "irish", "italian", "kazakh", "latin", "latvian", "lithuanian", "lowersorbian",
97 "lowersorbian", "magyar", "bahasam", "bahasam", "mongolian", "naustrian", "english",
98 "ngerman", "ngerman", "norsk", "nynorsk", "polutonikogreek", "polish",
99 "portuguese", "portuguese", "romanian", "russian", "russian", "samin",
100 "scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish",
101 "swedish", "thai", "turkish", "turkmen", "ukrainian", "ukrainian",
102 "uppersorbian", "uppersorbian", "english", "english", "vietnamese", "welsh",
105 /// languages with english quotes (.lyx names)
106 const char * const known_english_quotes_languages[] = {"american", "bahasa",
107 "bahasam", "brazilian", "canadian", "chinese-simplified", "english",
108 "esperanto", "hebrew", "irish", "korean", "portuguese", "scottish", "thai", 0};
110 /// languages with french quotes (.lyx names)
111 const char * const known_french_quotes_languages[] = {"albanian",
112 "arabic_arabi", "arabic_arabtex", "basque", "canadien", "catalan", "french",
113 "galician", "greek", "italian", "norsk", "nynorsk", "polutonikogreek",
114 "russian", "spanish", "spanish-mexico", "turkish", "turkmen", "ukrainian",
117 /// languages with german quotes (.lyx names)
118 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
119 "czech", "german", "icelandic", "lithuanian", "lowersorbian", "naustrian",
120 "ngerman", "serbian", "serbian-latin", "slovak", "slovene", "uppersorbian", 0};
122 /// languages with polish quotes (.lyx names)
123 const char * const known_polish_quotes_languages[] = {"afrikaans", "croatian",
124 "dutch", "estonian", "magyar", "polish", "romanian", 0};
126 /// languages with swedish quotes (.lyx names)
127 const char * const known_swedish_quotes_languages[] = {"finnish",
130 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
132 const char * const known_roman_fonts[] = { "ae", "bookman", "charter",
133 "cmr", "fourier", "lmodern", "mathpazo", "mathptmx", "newcent", 0};
135 const char * const known_sans_fonts[] = { "avant", "berasans", "cmbr", "cmss",
136 "helvet", "lmss", 0};
138 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
139 "courier", "lmtt", "luximono", "fourier", "lmodern", "mathpazo", "mathptmx",
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 /// conditional commands with three arguments like \@ifundefined{}{}{}
164 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
168 ostringstream h_preamble;
169 string h_textclass = "article";
170 string h_use_default_options = "false";
172 string h_language = "english";
173 string h_language_package = "default";
174 string h_font_roman = "default";
175 string h_font_sans = "default";
176 string h_font_typewriter = "default";
177 string h_font_default_family = "default";
178 string h_font_sc = "false";
179 string h_font_osf = "false";
180 string h_font_sf_scale = "100";
181 string h_font_tt_scale = "100";
182 string h_graphics = "default";
183 string h_float_placement;
184 string h_paperfontsize = "default";
185 string h_spacing = "single";
186 string h_use_hyperref = "0";
189 string h_pdf_subject;
190 string h_pdf_keywords;
191 string h_pdf_bookmarks = "1";
192 string h_pdf_bookmarksnumbered = "0";
193 string h_pdf_bookmarksopen = "0";
194 string h_pdf_bookmarksopenlevel = "1";
195 string h_pdf_breaklinks = "0";
196 string h_pdf_pdfborder = "0";
197 string h_pdf_colorlinks = "0";
198 string h_pdf_backref = "section";
199 string h_pdf_pdfusetitle = "1";
200 string h_pdf_pagemode;
201 string h_pdf_quoted_options;
202 string h_papersize = "default";
203 string h_use_geometry = "false";
204 string h_use_amsmath = "1";
205 string h_use_esint = "1";
206 string h_use_mhchem = "0";
207 string h_use_mathdots = "0";
208 string h_cite_engine = "basic";
209 string h_use_bibtopic = "false";
210 string h_paperorientation = "portrait";
211 string h_suppress_date = "false";
212 string h_use_refstyle = "0";
213 string h_notefontcolor;
214 string h_secnumdepth = "3";
215 string h_tocdepth = "3";
216 string h_defskip = "medskip";
217 string h_paragraph_indentation = "default";
218 string h_quotes_language = "english";
219 string h_papercolumns = "1";
221 string h_paperpagestyle = "default";
222 string h_listings_params;
223 string h_tracking_changes = "false";
224 string h_output_changes = "false";
225 string h_html_math_output = "0";
226 string h_html_css_as_file = "0";
227 string h_html_be_strict = "false";
231 // returns true if at least one of the options in what has been found
232 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
238 // the last language option is the document language (for babel and LyX)
239 // the last size option is the document font size
240 vector<string>::iterator it;
241 vector<string>::iterator position = opts.begin();
242 for (; *what; ++what) {
243 it = find(opts.begin(), opts.end(), *what);
244 if (it != opts.end()) {
245 if (it >= position) {
256 void delete_opt(vector<string> & opts, char const * const * what)
261 // remove found options from the list
262 // do this after handle_opt to avoid potential memory leaks
263 vector<string>::iterator it;
264 for (; *what; ++what) {
265 it = find(opts.begin(), opts.end(), *what);
266 if (it != opts.end())
273 * Split a package options string (keyval format) into a vector.
275 * authorformat=smallcaps,
277 * titleformat=colonsep,
278 * bibformat={tabular,ibidem,numbered}
280 vector<string> split_options(string const & input)
282 vector<string> options;
286 Token const & t = p.get_token();
287 if (t.asInput() == ",") {
288 options.push_back(trimSpaceAndEol(option));
290 } else if (t.asInput() == "=") {
293 if (p.next_token().asInput() == "{")
294 option += '{' + p.getArg('{', '}') + '}';
295 } else if (t.cat() != catSpace)
296 option += t.asInput();
300 options.push_back(trimSpaceAndEol(option));
307 * Retrieve a keyval option "name={value with=sign}" named \p name from
308 * \p options and return the value.
309 * The found option is also removed from \p options.
311 string process_keyval_opt(vector<string> & options, string name)
313 for (size_t i = 0; i < options.size(); ++i) {
314 vector<string> option;
315 split(options[i], option, '=');
316 if (option.size() < 2)
318 if (option[0] == name) {
319 options.erase(options.begin() + i);
320 option.erase(option.begin());
321 return join(option, "=");
329 * Add package \p name with options \p options to used_packages.
330 * Remove options from \p options that we don't want to output.
332 void add_package(string const & name, vector<string> & options)
334 // every package inherits the global options
335 if (used_packages.find(name) == used_packages.end())
336 used_packages[name] = split_options(h_options);
338 vector<string> & v = used_packages[name];
339 v.insert(v.end(), options.begin(), options.end());
340 if (name == "jurabib") {
341 // Don't output the order argument (see the cite command
342 // handling code in text.cpp).
343 vector<string>::iterator end =
344 remove(options.begin(), options.end(), "natbiborder");
345 end = remove(options.begin(), end, "jurabiborder");
346 options.erase(end, options.end());
351 // Given is a string like "scaled=0.9", return 0.9 * 100
352 string const scale_as_percentage(string const & scale)
354 string::size_type pos = scale.find('=');
355 if (pos != string::npos) {
356 string value = scale.substr(pos + 1);
358 return convert<string>(100 * convert<double>(value));
360 // If the input string didn't match our expectations.
361 // return the default value "100"
366 string remove_braces(string const & value)
370 if (value[0] == '{' && value[value.length()-1] == '}')
371 return value.substr(1, value.length()-2);
376 void handle_hyperref(vector<string> & options)
378 // FIXME swallow inputencoding changes that might surround the
379 // hyperref setup if it was written by LyX
380 h_use_hyperref = "1";
381 // swallow "unicode=true", since LyX does always write that
382 vector<string>::iterator it =
383 find(options.begin(), options.end(), "unicode=true");
384 if (it != options.end())
386 it = find(options.begin(), options.end(), "pdfusetitle");
387 if (it != options.end()) {
388 h_pdf_pdfusetitle = "1";
391 string bookmarks = process_keyval_opt(options, "bookmarks");
392 if (bookmarks == "true")
393 h_pdf_bookmarks = "1";
394 else if (bookmarks == "false")
395 h_pdf_bookmarks = "0";
396 if (h_pdf_bookmarks == "1") {
397 string bookmarksnumbered =
398 process_keyval_opt(options, "bookmarksnumbered");
399 if (bookmarksnumbered == "true")
400 h_pdf_bookmarksnumbered = "1";
401 else if (bookmarksnumbered == "false")
402 h_pdf_bookmarksnumbered = "0";
403 string bookmarksopen =
404 process_keyval_opt(options, "bookmarksopen");
405 if (bookmarksopen == "true")
406 h_pdf_bookmarksopen = "1";
407 else if (bookmarksopen == "false")
408 h_pdf_bookmarksopen = "0";
409 if (h_pdf_bookmarksopen == "1") {
410 string bookmarksopenlevel =
411 process_keyval_opt(options, "bookmarksopenlevel");
412 if (!bookmarksopenlevel.empty())
413 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
416 string breaklinks = process_keyval_opt(options, "breaklinks");
417 if (breaklinks == "true")
418 h_pdf_breaklinks = "1";
419 else if (breaklinks == "false")
420 h_pdf_breaklinks = "0";
421 string pdfborder = process_keyval_opt(options, "pdfborder");
422 if (pdfborder == "{0 0 0}")
423 h_pdf_pdfborder = "1";
424 else if (pdfborder == "{0 0 1}")
425 h_pdf_pdfborder = "0";
426 string backref = process_keyval_opt(options, "backref");
427 if (!backref.empty())
428 h_pdf_backref = backref;
429 string colorlinks = process_keyval_opt(options, "colorlinks");
430 if (colorlinks == "true")
431 h_pdf_colorlinks = "1";
432 else if (colorlinks == "false")
433 h_pdf_colorlinks = "0";
434 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
435 if (!pdfpagemode.empty())
436 h_pdf_pagemode = pdfpagemode;
437 string pdftitle = process_keyval_opt(options, "pdftitle");
438 if (!pdftitle.empty()) {
439 h_pdf_title = remove_braces(pdftitle);
441 string pdfauthor = process_keyval_opt(options, "pdfauthor");
442 if (!pdfauthor.empty()) {
443 h_pdf_author = remove_braces(pdfauthor);
445 string pdfsubject = process_keyval_opt(options, "pdfsubject");
446 if (!pdfsubject.empty())
447 h_pdf_subject = remove_braces(pdfsubject);
448 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
449 if (!pdfkeywords.empty())
450 h_pdf_keywords = remove_braces(pdfkeywords);
451 if (!options.empty()) {
452 if (!h_pdf_quoted_options.empty())
453 h_pdf_quoted_options += ',';
454 h_pdf_quoted_options += join(options, ",");
460 void handle_package(Parser &p, string const & name, string const & opts,
461 bool in_lyx_preamble)
463 vector<string> options = split_options(opts);
464 add_package(name, options);
468 if (is_known(name, known_roman_fonts)) {
473 if (name == "fourier") {
474 h_font_roman = "utopia";
475 // when font uses real small capitals
476 if (opts == "expert")
480 if (name == "mathpazo")
481 h_font_roman = "palatino";
483 if (name == "mathptmx")
484 h_font_roman = "times";
487 if (is_known(name, known_sans_fonts)) {
491 h_font_sf_scale = scale_as_percentage(scale);
496 if (is_known(name, known_typewriter_fonts)) {
497 h_font_typewriter = name;
500 h_font_tt_scale = scale_as_percentage(scale);
504 // font uses old-style figure
508 else if (name == "amsmath" || name == "amssymb")
511 else if (name == "esint")
514 else if (name == "mhchem")
517 else if (name == "mathdots")
518 h_use_mathdots = "2";
520 else if (name == "babel" && !opts.empty()) {
521 // check if more than one option was used - used later for inputenc
522 // in case inputenc is parsed before babel, set the encoding to auto
523 if (options.begin() != options.end() - 1) {
524 one_language = false;
525 h_inputencoding = "auto";
527 // babel takes the last language of the option of its \usepackage
528 // call as document language. If there is no such language option, the
529 // last language in the documentclass options is used.
530 handle_opt(options, known_languages, h_language);
531 delete_opt(options, known_languages);
534 else if (name == "fontenc")
537 else if (name == "inputenc" || name == "luainputenc") {
538 // h_inputencoding is only set when there is not more than one
539 // inputenc option because otherwise h_inputencoding must be
540 // set to "auto" (the default encoding of the document language)
541 // Therefore check for the "," character.
542 // It is also only set when there is not more then one babel
543 // language option but this is handled in the routine for babel.
544 if (opts.find(",") == string::npos && one_language == true)
545 h_inputencoding = opts;
546 if (!options.empty())
547 p.setEncoding(options.back());
551 else if (name == "makeidx")
554 else if (name == "prettyref")
557 else if (name == "varioref")
560 else if (name == "verbatim")
563 else if (name == "nomencl")
566 else if (name == "textcomp")
569 else if (name == "url")
572 else if (name == "subscript")
575 else if (name == "color") {
576 // with the following command this package is only loaded when needed for
577 // undefined colors, since we only support the predefined colors
578 h_preamble << "\\@ifundefined{definecolor}\n {\\usepackage{color}}{}\n";
581 else if (name == "graphicx")
584 else if (name == "setspace")
587 else if (name == "geometry")
588 ; // Ignore this, the geometry settings are made by the \geometry
589 // command. This command is handled below.
591 else if (is_known(name, known_languages))
594 else if (name == "natbib") {
595 h_cite_engine = "natbib_authoryear";
596 vector<string>::iterator it =
597 find(options.begin(), options.end(), "authoryear");
598 if (it != options.end())
601 it = find(options.begin(), options.end(), "numbers");
602 if (it != options.end()) {
603 h_cite_engine = "natbib_numerical";
609 else if (name == "jurabib")
610 h_cite_engine = "jurabib";
612 else if (name == "hyperref")
613 handle_hyperref(options);
615 else if (!in_lyx_preamble) {
617 h_preamble << "\\usepackage{" << name << "}";
619 h_preamble << "\\usepackage[" << opts << "]{"
625 // We need to do something with the options...
626 if (!options.empty())
627 cerr << "Ignoring options '" << join(options, ",")
628 << "' of package " << name << '.' << endl;
630 // remove the whitespace
635 void handle_if(Parser & p, bool in_lyx_preamble)
638 Token t = p.get_token();
639 if (t.cat() == catEscape &&
640 is_known(t.cs(), known_if_commands))
641 handle_if(p, in_lyx_preamble);
643 if (!in_lyx_preamble)
644 h_preamble << t.asInput();
645 if (t.cat() == catEscape && t.cs() == "fi")
652 void end_preamble(ostream & os, TextClass const & /*textclass*/)
654 // translate from babel to LyX names
655 h_language = babel2lyx(h_language);
657 // set the quote language
658 // LyX only knows the following quotes languages:
659 // english, swedish, german, polish, french and danish
660 // (quotes for "japanese" and "chinese-traditional" are missing because
661 // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
662 // conversion list taken from
663 // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
664 // (quotes for kazakh and interlingua are unknown)
666 if (h_language == "danish")
667 h_quotes_language = "danish";
669 else if (is_known(h_language, known_french_quotes_languages))
670 h_quotes_language = "french";
672 else if (is_known(h_language, known_german_quotes_languages))
673 h_quotes_language = "german";
675 else if (is_known(h_language, known_polish_quotes_languages))
676 h_quotes_language = "polish";
678 else if (is_known(h_language, known_swedish_quotes_languages))
679 h_quotes_language = "swedish";
681 else if (is_known(h_language, known_english_quotes_languages))
682 h_quotes_language = "english";
684 // output the LyX file settings
685 os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
686 << "\\lyxformat " << LYX_FORMAT << '\n'
687 << "\\begin_document\n"
688 << "\\begin_header\n"
689 << "\\textclass " << h_textclass << "\n";
690 if (!h_preamble.str().empty())
691 os << "\\begin_preamble\n" << h_preamble.str() << "\n\\end_preamble\n";
692 if (!h_options.empty())
693 os << "\\options " << h_options << "\n";
694 os << "\\use_default_options " << h_use_default_options << "\n"
695 << modules_placeholder
696 << "\\language " << h_language << "\n"
697 << "\\language_package " << h_language_package << "\n"
698 << "\\inputencoding " << h_inputencoding << "\n"
699 << "\\font_roman " << h_font_roman << "\n"
700 << "\\font_sans " << h_font_sans << "\n"
701 << "\\font_typewriter " << h_font_typewriter << "\n"
702 << "\\font_default_family " << h_font_default_family << "\n"
703 << "\\font_sc " << h_font_sc << "\n"
704 << "\\font_osf " << h_font_osf << "\n"
705 << "\\font_sf_scale " << h_font_sf_scale << "\n"
706 << "\\font_tt_scale " << h_font_tt_scale << "\n"
707 << "\\graphics " << h_graphics << "\n";
708 if (!h_float_placement.empty())
709 os << "\\float_placement " << h_float_placement << "\n";
710 os << "\\paperfontsize " << h_paperfontsize << "\n"
711 << "\\spacing " << h_spacing << "\n"
712 << "\\use_hyperref " << h_use_hyperref << '\n';
713 if (h_use_hyperref == "1") {
714 if (!h_pdf_title.empty())
715 os << "\\pdf_title \"" << h_pdf_title << "\"\n";
716 if (!h_pdf_author.empty())
717 os << "\\pdf_author \"" << h_pdf_author << "\"\n";
718 if (!h_pdf_subject.empty())
719 os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
720 if (!h_pdf_keywords.empty())
721 os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
722 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
723 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
724 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
725 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
726 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
727 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
728 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
729 "\\pdf_backref " << h_pdf_backref << "\n"
730 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
731 if (!h_pdf_pagemode.empty())
732 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
733 if (!h_pdf_quoted_options.empty())
734 os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
736 os << "\\papersize " << h_papersize << "\n"
737 << "\\use_geometry " << h_use_geometry << "\n"
738 << "\\use_amsmath " << h_use_amsmath << "\n"
739 << "\\use_esint " << h_use_esint << "\n"
740 << "\\use_mhchem " << h_use_mhchem << "\n"
741 << "\\use_mathdots " << h_use_mathdots << "\n"
742 << "\\cite_engine " << h_cite_engine << "\n"
743 << "\\use_bibtopic " << h_use_bibtopic << "\n"
744 << "\\paperorientation " << h_paperorientation << '\n'
745 << "\\suppress_date " << h_suppress_date << '\n'
746 << "\\use_refstyle " << h_use_refstyle << '\n';
747 if (!h_notefontcolor.empty())
748 os << "\\notefontcolor " << h_notefontcolor << '\n';
750 << "\\secnumdepth " << h_secnumdepth << "\n"
751 << "\\tocdepth " << h_tocdepth << "\n"
752 << "\\paragraph_separation " << h_paragraph_separation << "\n";
753 if (h_paragraph_separation == "skip")
754 os << "\\defskip " << h_defskip << "\n";
756 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
757 os << "\\quotes_language " << h_quotes_language << "\n"
758 << "\\papercolumns " << h_papercolumns << "\n"
759 << "\\papersides " << h_papersides << "\n"
760 << "\\paperpagestyle " << h_paperpagestyle << "\n";
761 if (!h_listings_params.empty())
762 os << "\\listings_params " << h_listings_params << "\n";
763 os << "\\tracking_changes " << h_tracking_changes << "\n"
764 << "\\output_changes " << h_output_changes << "\n"
765 << "\\html_math_output " << h_html_math_output << "\n"
766 << "\\html_css_as_file " << h_html_css_as_file << "\n"
767 << "\\html_be_strict " << h_html_be_strict << "\n"
768 << "\\end_header\n\n"
770 // clear preamble for subdocuments
774 } // anonymous namespace
777 void parse_preamble(Parser & p, ostream & os,
778 string const & forceclass, TeX2LyXDocClass & tc)
780 // initialize fixed types
781 special_columns['D'] = 3;
782 bool is_full_document = false;
783 bool is_lyx_file = false;
784 bool in_lyx_preamble = false;
786 // determine whether this is a full document or a fragment for inclusion
788 Token const & t = p.get_token();
790 if (t.cat() == catEscape && t.cs() == "documentclass") {
791 is_full_document = true;
797 while (is_full_document && p.good()) {
798 Token const & t = p.get_token();
801 cerr << "t: " << t << "\n";
807 if (!in_lyx_preamble &&
808 (t.cat() == catLetter ||
809 t.cat() == catSuper ||
811 t.cat() == catOther ||
812 t.cat() == catMath ||
813 t.cat() == catActive ||
814 t.cat() == catBegin ||
816 t.cat() == catAlign ||
817 t.cat() == catParameter))
818 h_preamble << t.cs();
820 else if (!in_lyx_preamble &&
821 (t.cat() == catSpace || t.cat() == catNewline))
822 h_preamble << t.asInput();
824 else if (t.cat() == catComment) {
825 static regex const islyxfile("%% LyX .* created this file");
826 static regex const usercommands("User specified LaTeX commands");
828 string const comment = t.asInput();
830 // magically switch encoding default if it looks like XeLaTeX
831 static string const magicXeLaTeX =
832 "% This document must be compiled with XeLaTeX ";
833 if (comment.size() > magicXeLaTeX.size()
834 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
835 && h_inputencoding == "auto") {
836 cerr << "XeLaTeX comment found, switching to UTF8\n";
837 h_inputencoding = "utf8";
840 if (regex_search(comment, sub, islyxfile)) {
842 in_lyx_preamble = true;
843 } else if (is_lyx_file
844 && regex_search(comment, sub, usercommands))
845 in_lyx_preamble = false;
846 else if (!in_lyx_preamble)
847 h_preamble << t.asInput();
850 else if (t.cs() == "pagestyle")
851 h_paperpagestyle = p.verbatim_item();
853 else if (t.cs() == "makeatletter") {
854 // LyX takes care of this
855 p.setCatCode('@', catLetter);
858 else if (t.cs() == "makeatother") {
859 // LyX takes care of this
860 p.setCatCode('@', catOther);
863 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
864 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
865 || t.cs() == "providecommand" || t.cs() == "providecommandx"
866 || t.cs() == "DeclareRobustCommand"
867 || t.cs() == "DeclareRobustCommandx"
868 || t.cs() == "ProvideTextCommandDefault"
869 || t.cs() == "DeclareMathAccent") {
871 if (p.next_token().character() == '*') {
875 string const name = p.verbatim_item();
876 string const opt1 = p.getFullOpt();
877 string const opt2 = p.getFullOpt();
878 string const body = p.verbatim_item();
880 if (name == "\\rmdefault")
881 if (is_known(body, known_roman_fonts))
883 if (name == "\\sfdefault")
884 if (is_known(body, known_sans_fonts))
886 if (name == "\\ttdefault")
887 if (is_known(body, known_typewriter_fonts))
888 h_font_typewriter = body;
889 if (name == "\\familydefault") {
890 string family = body;
891 // remove leading "\"
892 h_font_default_family = family.erase(0,1);
895 // Add the command to the known commands
896 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
898 // only non-lyxspecific stuff
899 if (!in_lyx_preamble) {
901 ss << '\\' << t.cs();
904 ss << '{' << name << '}' << opt1 << opt2
905 << '{' << body << "}";
906 h_preamble << ss.str();
908 ostream & out = in_preamble ? h_preamble : os;
909 out << "\\" << t.cs() << "{" << name << "}"
910 << opts << "{" << body << "}";
915 else if (t.cs() == "documentclass") {
916 vector<string>::iterator it;
917 vector<string> opts = split_options(p.getArg('[', ']'));
918 handle_opt(opts, known_fontsizes, h_paperfontsize);
919 delete_opt(opts, known_fontsizes);
920 // delete "pt" at the end
921 string::size_type i = h_paperfontsize.find("pt");
922 if (i != string::npos)
923 h_paperfontsize.erase(i);
924 // The documentclass options are always parsed before the options
925 // of the babel call so that a language cannot overwrite the babel
927 handle_opt(opts, known_languages, h_language);
928 delete_opt(opts, known_languages);
931 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
932 h_paperorientation = "landscape";
936 if ((it = find(opts.begin(), opts.end(), "oneside"))
941 if ((it = find(opts.begin(), opts.end(), "twoside"))
947 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
949 h_papercolumns = "1";
952 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
954 h_papercolumns = "2";
958 // some size options are know to any document classes, other sizes
959 // are handled by the \geometry command of the geometry package
960 handle_opt(opts, known_class_paper_sizes, h_papersize);
961 delete_opt(opts, known_class_paper_sizes);
962 // the remaining options
963 h_options = join(opts, ",");
964 // FIXME This does not work for classes that have a
965 // different name in LyX than in LaTeX
966 h_textclass = p.getArg('{', '}');
969 else if (t.cs() == "usepackage") {
970 string const options = p.getArg('[', ']');
971 string const name = p.getArg('{', '}');
972 vector<string> vecnames;
973 split(name, vecnames, ',');
974 vector<string>::const_iterator it = vecnames.begin();
975 vector<string>::const_iterator end = vecnames.end();
976 for (; it != end; ++it)
977 handle_package(p, trimSpaceAndEol(*it), options,
981 else if (t.cs() == "inputencoding") {
982 string const encoding = p.getArg('{','}');
983 h_inputencoding = encoding;
984 p.setEncoding(encoding);
987 else if (t.cs() == "newenvironment") {
988 string const name = p.getArg('{', '}');
989 string const opt1 = p.getFullOpt();
990 string const opt2 = p.getFullOpt();
991 string const beg = p.verbatim_item();
992 string const end = p.verbatim_item();
993 if (!in_lyx_preamble) {
994 h_preamble << "\\newenvironment{" << name
995 << '}' << opt1 << opt2 << '{'
996 << beg << "}{" << end << '}';
998 add_known_environment(name, opt1, !opt2.empty(),
999 from_utf8(beg), from_utf8(end));
1003 else if (t.cs() == "def") {
1004 string name = p.get_token().cs();
1005 while (p.next_token().cat() != catBegin)
1006 name += p.get_token().cs();
1007 if (!in_lyx_preamble)
1008 h_preamble << "\\def\\" << name << '{'
1009 << p.verbatim_item() << "}";
1012 else if (t.cs() == "newcolumntype") {
1013 string const name = p.getArg('{', '}');
1014 trimSpaceAndEol(name);
1016 string opts = p.getOpt();
1017 if (!opts.empty()) {
1018 istringstream is(string(opts, 1));
1021 special_columns[name[0]] = nargs;
1022 h_preamble << "\\newcolumntype{" << name << "}";
1024 h_preamble << "[" << nargs << "]";
1025 h_preamble << "{" << p.verbatim_item() << "}";
1028 else if (t.cs() == "setcounter") {
1029 string const name = p.getArg('{', '}');
1030 string const content = p.getArg('{', '}');
1031 if (name == "secnumdepth")
1032 h_secnumdepth = content;
1033 else if (name == "tocdepth")
1034 h_tocdepth = content;
1036 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1039 else if (t.cs() == "setlength") {
1040 string const name = p.verbatim_item();
1041 string const content = p.verbatim_item();
1042 // the paragraphs are only not indented when \parindent is set to zero
1043 if (name == "\\parindent" && content != "") {
1044 if (content[0] == '0')
1045 h_paragraph_separation = "skip";
1047 h_paragraph_indentation = translate_len(content);
1048 } else if (name == "\\parskip") {
1049 if (content == "\\smallskipamount")
1050 h_defskip = "smallskip";
1051 else if (content == "\\medskipamount")
1052 h_defskip = "medskip";
1053 else if (content == "\\bigskipamount")
1054 h_defskip = "bigskip";
1056 h_defskip = content;
1058 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1061 else if (t.cs() == "onehalfspacing")
1062 h_spacing = "onehalf";
1064 else if (t.cs() == "doublespacing")
1065 h_spacing = "double";
1067 else if (t.cs() == "setstretch")
1068 h_spacing = "other " + p.verbatim_item();
1070 else if (t.cs() == "begin") {
1071 string const name = p.getArg('{', '}');
1072 if (name == "document")
1074 h_preamble << "\\begin{" << name << "}";
1077 else if (t.cs() == "geometry") {
1078 h_use_geometry = "true";
1079 vector<string> opts = split_options(p.getArg('{', '}'));
1080 vector<string>::iterator it;
1081 // paper orientation
1082 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1083 h_paperorientation = "landscape";
1087 handle_opt(opts, known_paper_sizes, h_papersize);
1088 delete_opt(opts, known_paper_sizes);
1090 char const * const * margin = known_paper_margins;
1092 for (; *margin; ++margin) {
1094 // search for the "=" in e.g. "lmargin=2cm" to get the value
1095 for(size_t i = 0; i != opts.size(); i++) {
1096 if (opts.at(i).find(*margin) != string::npos) {
1097 string::size_type pos = opts.at(i).find("=");
1098 string value = opts.at(i).substr(pos + 1);
1099 string name = known_coded_paper_margins[k];
1100 h_margins += "\\" + name + " " + value + "\n";
1106 else if (t.cs() == "definecolor") {
1107 string const color = p.getArg('{', '}');
1108 string const space = p.getArg('{', '}');
1109 string const value = p.getArg('{', '}');
1110 if (color == "note_fontcolor" && space == "rgb") {
1111 RGBColor c(RGBColorFromLaTeX(value));
1112 h_notefontcolor = X11hexname(c);
1114 h_preamble << "\\definecolor{" << color
1115 << "}{" << space << "}{" << value
1120 else if (t.cs() == "jurabibsetup") {
1121 // FIXME p.getArg('{', '}') is most probably wrong (it
1122 // does not handle nested braces).
1123 // Use p.verbatim_item() instead.
1124 vector<string> jurabibsetup =
1125 split_options(p.getArg('{', '}'));
1126 // add jurabibsetup to the jurabib package options
1127 add_package("jurabib", jurabibsetup);
1128 if (!jurabibsetup.empty()) {
1129 h_preamble << "\\jurabibsetup{"
1130 << join(jurabibsetup, ",") << '}';
1134 else if (t.cs() == "hypersetup") {
1135 vector<string> hypersetup =
1136 split_options(p.verbatim_item());
1137 // add hypersetup to the hyperref package options
1138 handle_hyperref(hypersetup);
1139 if (!hypersetup.empty()) {
1140 h_preamble << "\\hypersetup{"
1141 << join(hypersetup, ",") << '}';
1145 else if (is_known(t.cs(), known_if_3arg_commands)) {
1146 // prevent misparsing of \usepackage if it is used
1147 // as an argument (see e.g. our own output of
1148 // \@ifundefined above)
1149 string const arg1 = p.verbatim_item();
1150 string const arg2 = p.verbatim_item();
1151 string const arg3 = p.verbatim_item();
1152 if (!in_lyx_preamble) {
1153 h_preamble << t.asInput()
1154 << '{' << arg1 << '}'
1155 << '{' << arg2 << '}'
1156 << '{' << arg3 << '}';
1160 else if (is_known(t.cs(), known_if_commands)) {
1161 // must not parse anything in conditional code, since
1162 // LyX would output the parsed contents unconditionally
1163 if (!in_lyx_preamble)
1164 h_preamble << t.asInput();
1165 handle_if(p, in_lyx_preamble);
1168 else if (!t.cs().empty() && !in_lyx_preamble)
1169 h_preamble << '\\' << t.cs();
1172 // remove the whitespace
1175 // Force textclass if the user wanted it
1176 if (!forceclass.empty())
1177 h_textclass = forceclass;
1178 if (noweb_mode && !prefixIs(h_textclass, "literate-"))
1179 h_textclass.insert(0, "literate-");
1180 tc.setName(h_textclass);
1182 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1185 if (h_papersides.empty()) {
1188 h_papersides = ss.str();
1190 end_preamble(os, tc);
1194 /// translates a babel language name to a LyX language name
1195 string babel2lyx(string const & language)
1197 char const * const * where = is_known(language, known_languages);
1199 return known_coded_languages[where - known_languages];