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 <boost/regex.hpp>
38 using namespace lyx::support;
44 // special columntypes
45 extern map<char, int> special_columns;
47 map<string, vector<string> > used_packages;
49 // needed to handle encodings with babel
50 bool one_language = true;
52 // to avoid that the babel options overwrite the documentclass options
53 bool documentclass_language;
57 const char * const known_languages[] = { "afrikaans", "american", "arabic",
58 "austrian", "bahasa", "basque", "belarusian", "brazil", "breton", "british",
59 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
60 "dutch", "english", "esperanto", "estonian", "finnish", "francais", "french",
61 "frenchb", "frenchle", "frenchpro", "galician", "german", "germanb", "greek",
62 "hebrew", "icelandic", "irish", "italian", "lsorbian", "magyar", "naustrian",
63 "ngerman", "ngermanb", "norsk", "nynorsk", "polish", "portuges", "romanian",
64 "russian", "russianb", "scottish", "serbian", "slovak", "slovene", "spanish",
65 "swedish", "thai", "turkish", "ukraineb", "ukrainian", "usorbian", "welsh", 0};
67 //note this when updating to lyxformat 305:
68 //bahasai, indonesian, and indon = equal to bahasa
69 //malay, and meyalu = equal to bahasam
71 const char * const known_french_languages[] = {"french", "frenchb", "francais",
72 "frenchle", "frenchpro", 0};
73 const char * const known_german_languages[] = {"german", "germanb", 0};
74 const char * const known_ngerman_languages[] = {"ngerman", "ngermanb", 0};
75 const char * const known_russian_languages[] = {"russian", "russianb", 0};
76 const char * const known_ukrainian_languages[] = {"ukrainian", "ukraineb", 0};
78 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
80 const char * const known_roman_fonts[] = { "ae", "bookman", "charter",
81 "cmr", "fourier", "lmodern", "mathpazo", "mathptmx", "newcent", 0};
83 const char * const known_sans_fonts[] = { "avant", "berasans", "cmbr", "cmss",
86 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
87 "courier", "lmtt", "luximono", "fourier", "lmodern", "mathpazo", "mathptmx",
90 const char * const known_paper_sizes[] = { "a3paper", "b3paper", "a4paper",
91 "b4paper", "a5paper", "b5paper", "executivepaper", "legalpaper",
94 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
95 "executivepaper", "legalpaper", "letterpaper", 0};
97 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
98 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
100 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
101 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
104 const char * const known_lyx_commands[] = { "binom", "cedilla", "cyrtext",
105 "dacute", "dgrave", "docedilla", "doogonek", "dosubhat", "dosubring",
106 "dosubtilde", "greektext", "guillemotleft", "guillemotright", "guilsinglleft",
107 "guilsinglright", "LyX", "lyxadded", "lyxarrow", "lyxdeleted", "lyxdot",
108 "lyxgreyedout", "lyxline", "lyxmathsym", "LyXParagraphLeftIndent",
109 "lyxrightaddress", "makenomenclature", "mathcircumflex", "noun", "ogonek",
110 "printnomenclature", "quotedblbase", "quotesinglbase", "rcap", "subhat",
111 "subring", "subtilde", "tabularnewline", "textcyr", "textgreek", 0};
113 const char * const known_lyx_comments[] = {
114 "%% Binom macro for standard LaTeX users\n",
115 "%% For printing a cirumflex inside a formula\n",
116 "%% Because html converters don't know tabularnewline\n",
117 "%% The greyedout annotation environment\n",
118 "%% A simple dot to overcome graphicx limitations\n",
119 "%% Change tracking with ulem\n",
120 "% the following is useful when we have the old nomencl.sty package\n",
121 "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% LyX specific LaTeX commands.\n",
122 "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% User specified LaTeX commands.\n",
126 ostringstream h_preamble;
127 string h_textclass = "article";
128 string h_options = string();
129 string h_language = "english";
130 string h_inputencoding = "auto";
131 string h_font_roman = "default";
132 string h_font_sans = "default";
133 string h_font_typewriter = "default";
134 string h_font_default_family = "default";
135 string h_font_sc = "false";
136 string h_font_osf = "false";
137 string h_font_sf_scale = "100";
138 string h_font_tt_scale = "100";
139 string h_graphics = "default";
140 string h_paperfontsize = "default";
141 string h_spacing = "single";
142 string h_papersize = "default";
143 string h_use_geometry = "false";
144 string h_use_amsmath = "1";
145 string h_use_esint = "1";
146 string h_cite_engine = "basic";
147 string h_use_bibtopic = "false";
148 string h_paperorientation = "portrait";
149 string h_secnumdepth = "3";
150 string h_tocdepth = "3";
151 string h_paragraph_separation = "indent";
152 string h_defskip = "medskip";
153 string h_quotes_language = "english";
154 string h_papercolumns = "1";
155 string h_papersides = string();
156 string h_paperpagestyle = "default";
157 string h_tracking_changes = "false";
158 string h_output_changes = "false";
159 string h_margins = "";
162 void handle_opt(vector<string> & opts, char const * const * what, string & target)
167 // the last language option is the document language (for babel and LyX)
168 // the last size option is the document font size
169 vector<string>::iterator it;
170 vector<string>::iterator position = opts.begin();
171 for (; *what; ++what) {
172 it = find(opts.begin(), opts.end(), *what);
173 if (it != opts.end()) {
174 documentclass_language = true;
175 if (it >= position) {
184 void delete_opt(vector<string> & opts, char const * const * what)
189 // remove found options from the list
190 // do this after handle_opt to avoid potential memory leaks and to be able
191 // to find in every case the last language option
192 vector<string>::iterator it;
193 for (; *what; ++what) {
194 it = find(opts.begin(), opts.end(), *what);
195 if (it != opts.end())
202 * Split a package options string (keyval format) into a vector.
204 * authorformat=smallcaps,
206 * titleformat=colonsep,
207 * bibformat={tabular,ibidem,numbered}
209 vector<string> split_options(string const & input)
211 vector<string> options;
215 Token const & t = p.get_token();
216 if (t.asInput() == ",") {
217 options.push_back(trim(option));
219 } else if (t.asInput() == "=") {
222 if (p.next_token().asInput() == "{")
223 option += '{' + p.getArg('{', '}') + '}';
224 } else if (t.cat() != catSpace)
225 option += t.asInput();
229 options.push_back(trim(option));
236 * Add package \p name with options \p options to used_packages.
237 * Remove options from \p options that we don't want to output.
239 void add_package(string const & name, vector<string> & options)
241 // every package inherits the global options
242 if (used_packages.find(name) == used_packages.end())
243 used_packages[name] = split_options(h_options);
245 vector<string> & v = used_packages[name];
246 v.insert(v.end(), options.begin(), options.end());
247 if (name == "jurabib") {
248 // Don't output the order argument (see the cite command
249 // handling code in text.cpp).
250 vector<string>::iterator end =
251 remove(options.begin(), options.end(), "natbiborder");
252 end = remove(options.begin(), end, "jurabiborder");
253 options.erase(end, options.end());
258 // Given is a string like "scaled=0.9", return 0.9 * 100
259 string const scale_as_percentage(string const & scale)
261 string::size_type pos = scale.find('=');
262 if (pos != string::npos) {
263 string value = scale.substr(pos + 1);
265 return convert<string>(100 * convert<double>(value));
267 // If the input string didn't match our expectations.
268 // return the default value "100"
273 void handle_package(Parser &p, string const & name, string const & opts)
275 vector<string> options = split_options(opts);
276 add_package(name, options);
280 if (is_known(name, known_roman_fonts)) {
285 if (name == "fourier") {
286 h_font_roman = "utopia";
287 // when font uses real small capitals
288 if (opts == "expert")
292 if (name == "mathpazo")
293 h_font_roman = "palatino";
295 if (name == "mathptmx")
296 h_font_roman = "times";
299 if (is_known(name, known_sans_fonts)) {
303 h_font_sf_scale = scale_as_percentage(scale);
308 if (is_known(name, known_typewriter_fonts)) {
309 h_font_typewriter = name;
312 h_font_tt_scale = scale_as_percentage(scale);
316 // font uses old-style figure
320 else if (name == "amsmath" || name == "amssymb")
323 else if (name == "esint")
326 else if (name == "babel" && !opts.empty()) {
327 // check if more than one option was used - used later for inputenc
328 // in case inputenc is parsed before babel, set the encoding to auto
329 if (options.begin() != options.end() - 1) {
330 one_language = false;
331 h_inputencoding = "auto";
333 // only set the document language when there was not already one set
334 // via the documentclass options
335 // babel takes the the last language given in the documentclass options
336 // as document language. If there is no such language option, the last
337 // option of its \usepackage call is used.
338 if (documentclass_language == false) {
339 handle_opt(options, known_languages, h_language);
340 delete_opt(options, known_languages);
341 if (is_known(h_language, known_french_languages))
342 h_language = "french";
343 else if (is_known(h_language, known_german_languages))
344 h_language = "german";
345 else if (is_known(h_language, known_ngerman_languages))
346 h_language = "ngerman";
347 else if (is_known(h_language, known_russian_languages))
348 h_language = "russian";
349 else if (is_known(h_language, known_ukrainian_languages))
350 h_language = "ukrainian";
351 h_quotes_language = h_language;
355 else if (name == "fontenc")
358 else if (name == "inputenc") {
359 // only set when there is not more than one inputenc
360 // option therefore check for the "," character also
361 // only set when there is not more then one babel
363 if (opts.find(",") == string::npos && one_language == true) {
365 //change ascii to auto to be in the unicode range, see
366 //http://bugzilla.lyx.org/show_bug.cgi?id=4719
367 h_inputencoding = "auto";
368 else if (!opts.empty())
369 h_inputencoding = opts;
371 if (!options.empty())
372 p.setEncoding(options.back());
376 else if (name == "makeidx")
379 else if (name == "prettyref")
382 else if (name == "varioref")
385 else if (name == "verbatim")
388 else if (name == "nomencl")
391 else if (name == "textcomp")
394 else if (name == "url")
397 else if (name == "color") {
398 // with the following command this package is only loaded when needed for
399 // undefined colors, since we only support the predefined colors
400 h_preamble << "\\@ifundefined{definecolor}\n {\\usepackage{color}}{}\n";
403 else if (name == "graphicx")
406 else if (name == "setspace")
409 else if (name == "geometry")
410 ; // Ignore this, the geometry settings are made by the \geometry
411 // command. This command is handled below.
413 else if (is_known(name, known_languages)) {
414 if (is_known(name, known_french_languages))
415 h_language = "french";
416 else if (is_known(name, known_german_languages))
417 h_language = "german";
418 else if (is_known(name, known_ngerman_languages))
419 h_language = "ngerman";
420 else if (is_known(name, known_russian_languages))
421 h_language = "russian";
422 else if (is_known(name, known_ukrainian_languages))
423 h_language = "ukrainian";
426 h_quotes_language = h_language;
429 else if (name == "natbib") {
430 h_cite_engine = "natbib_authoryear";
431 vector<string>::iterator it =
432 find(options.begin(), options.end(), "authoryear");
433 if (it != options.end())
436 it = find(options.begin(), options.end(), "numbers");
437 if (it != options.end()) {
438 h_cite_engine = "natbib_numerical";
444 else if (name == "jurabib")
445 h_cite_engine = "jurabib";
447 else if (name == "babel")
452 h_preamble << "\\usepackage{" << name << "}";
454 h_preamble << "\\usepackage[" << opts << "]{"
460 // We need to do something with the options...
461 if (!options.empty())
462 cerr << "Ignoring options '" << join(options, ",")
463 << "' of package " << name << '.' << endl;
465 // remove the whitespace
471 void end_preamble(ostream & os, TextClass const & /*textclass*/)
473 os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
474 << "\\lyxformat 258\n"
475 << "\\begin_document\n"
476 << "\\begin_header\n"
477 << "\\textclass " << h_textclass << "\n";
478 if (!h_preamble.str().empty())
479 os << "\\begin_preamble\n" << h_preamble.str() << "\n\\end_preamble\n";
480 if (!h_options.empty())
481 os << "\\options " << h_options << "\n";
482 os << "\\language " << h_language << "\n"
483 << "\\inputencoding " << h_inputencoding << "\n"
484 << "\\font_roman " << h_font_roman << "\n"
485 << "\\font_sans " << h_font_sans << "\n"
486 << "\\font_typewriter " << h_font_typewriter << "\n"
487 << "\\font_default_family " << h_font_default_family << "\n"
488 << "\\font_sc " << h_font_sc << "\n"
489 << "\\font_osf " << h_font_osf << "\n"
490 << "\\font_sf_scale " << h_font_sf_scale << "\n"
491 << "\\font_tt_scale " << h_font_tt_scale << "\n"
492 << "\\graphics " << h_graphics << "\n"
493 << "\\paperfontsize " << h_paperfontsize << "\n"
494 << "\\spacing " << h_spacing << "\n"
495 << "\\papersize " << h_papersize << "\n"
496 << "\\use_geometry " << h_use_geometry << "\n"
497 << "\\use_amsmath " << h_use_amsmath << "\n"
498 << "\\use_esint " << h_use_esint << "\n"
499 << "\\cite_engine " << h_cite_engine << "\n"
500 << "\\use_bibtopic " << h_use_bibtopic << "\n"
501 << "\\paperorientation " << h_paperorientation << "\n"
503 << "\\secnumdepth " << h_secnumdepth << "\n"
504 << "\\tocdepth " << h_tocdepth << "\n"
505 << "\\paragraph_separation " << h_paragraph_separation << "\n"
506 << "\\defskip " << h_defskip << "\n"
507 << "\\quotes_language " << h_quotes_language << "\n"
508 << "\\papercolumns " << h_papercolumns << "\n"
509 << "\\papersides " << h_papersides << "\n"
510 << "\\paperpagestyle " << h_paperpagestyle << "\n"
511 << "\\tracking_changes " << h_tracking_changes << "\n"
512 << "\\output_changes " << h_output_changes << "\n"
513 << "\\end_header\n\n"
515 // clear preamble for subdocuments
519 } // anonymous namespace
521 void parse_preamble(Parser & p, ostream & os,
522 string const & forceclass, TeX2LyXDocClass & tc)
524 // initialize fixed types
525 special_columns['D'] = 3;
526 bool is_full_document = false;
527 bool lyx_specific_preamble = false;
529 // determine whether this is a full document or a fragment for inclusion
531 Token const & t = p.get_token();
533 if (t.cat() == catEscape && t.cs() == "documentclass") {
534 is_full_document = true;
540 while (is_full_document && p.good()) {
541 Token const & t = p.get_token();
544 cerr << "t: " << t << "\n";
550 if ((t.cat() == catLetter ||
551 t.cat() == catSuper ||
553 t.cat() == catOther ||
554 t.cat() == catMath ||
555 t.cat() == catActive ||
556 t.cat() == catBegin ||
558 t.cat() == catAlign ||
559 t.cat() == catParameter))
560 h_preamble << t.character();
562 else if (t.cat() == catSpace || t.cat() == catNewline)
563 h_preamble << t.asInput();
565 else if (t.cat() == catComment) {
566 // regex to parse comments (currently not used)
567 //static regex const islyxfile("%% LyX .* created this file");
568 string const comment = t.asInput();
569 // magically switch encoding default if it looks like XeLaTeX
570 static string const magicXeLaTeX =
571 "% This document must be compiled with XeLaTeX ";
572 if (comment.size() > magicXeLaTeX.size()
573 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
574 && h_inputencoding == "auto") {
575 cerr << "XeLaTeX comment found, switching to UTF8\n";
576 h_inputencoding = "utf8";
579 // don't output LyX specific comments
580 if (!is_known(comment, known_lyx_comments))
581 h_preamble << t.asInput();
584 else if (t.cs() == "pagestyle")
585 h_paperpagestyle = p.verbatim_item();
587 else if (t.cs() == "makeatletter") {
588 // LyX takes care of this
589 p.setCatCode('@', catLetter);
592 else if (t.cs() == "makeatother") {
593 // LyX takes care of this
594 p.setCatCode('@', catOther);
597 else if (t.cs() == "newcommand" || t.cs() == "renewcommand"
598 || t.cs() == "providecommand"
599 || t.cs() == "DeclareRobustCommand"
600 || t.cs() == "ProvideTextCommandDefault"
601 || t.cs() == "DeclareMathAccent") {
603 if (p.next_token().character() == '*') {
607 string const name = p.verbatim_item();
608 string const opt1 = p.getOpt();
609 string const opt2 = p.getFullOpt();
610 string const body = p.verbatim_item();
612 if (name == "\\rmdefault")
613 if (is_known(body, known_roman_fonts))
615 if (name == "\\sfdefault")
616 if (is_known(body, known_sans_fonts))
618 if (name == "\\ttdefault")
619 if (is_known(body, known_typewriter_fonts))
620 h_font_typewriter = body;
621 if (name == "\\familydefault") {
622 string family = body;
623 // remove leading "\"
624 h_font_default_family = family.erase(0,1);
626 // LyX specific commands that will automatically be set by LyX
627 string lyx_command = name;
628 // remove the leading "\"
629 lyx_command.erase(0,1);
630 lyx_specific_preamble = false;
631 // allow redefinitions of LyX specific commands
632 if (is_known(lyx_command, known_lyx_commands)
633 && (t.cs() != "renewcommand"))
634 lyx_specific_preamble = true;
635 // only non-lyxspecific stuff
636 if (!lyx_specific_preamble) {
638 ss << '\\' << t.cs();
641 ss << '{' << name << '}' << opt1 << opt2
642 << '{' << body << "}";
643 h_preamble << ss.str();
645 // Add the command to the known commands
646 add_known_command(name, opt1, !opt2.empty());
648 ostream & out = in_preamble ? h_preamble : os;
649 out << "\\" << t.cs() << "{" << name << "}"
650 << opts << "{" << body << "}";
655 else if (t.cs() == "documentclass") {
656 vector<string>::iterator it;
657 vector<string> opts = split_options(p.getArg('[', ']'));
658 handle_opt(opts, known_fontsizes, h_paperfontsize);
659 delete_opt(opts, known_fontsizes);
660 // delete "pt" at the end
661 string::size_type i = h_paperfontsize.find("pt");
662 if (i != string::npos)
663 h_paperfontsize.erase(i);
664 // to avoid that the babel options overwrite the documentclass options
665 documentclass_language = false;
666 handle_opt(opts, known_languages, h_language);
667 delete_opt(opts, known_languages);
668 if (is_known(h_language, known_french_languages))
669 h_language = "french";
670 else if (is_known(h_language, known_german_languages))
671 h_language = "german";
672 else if (is_known(h_language, known_ngerman_languages))
673 h_language = "ngerman";
674 else if (is_known(h_language, known_russian_languages))
675 h_language = "russian";
676 else if (is_known(h_language, known_ukrainian_languages))
677 h_language = "ukrainian";
678 h_quotes_language = h_language;
680 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
681 h_paperorientation = "landscape";
685 if ((it = find(opts.begin(), opts.end(), "oneside"))
690 if ((it = find(opts.begin(), opts.end(), "twoside"))
696 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
698 h_papercolumns = "1";
701 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
703 h_papercolumns = "2";
707 // some size options are know to any document classes, other sizes
708 // are handled by the \geometry command of the geometry package
709 handle_opt(opts, known_class_paper_sizes, h_papersize);
710 delete_opt(opts, known_class_paper_sizes);
711 // the remaining options
712 h_options = join(opts, ",");
713 h_textclass = p.getArg('{', '}');
716 else if (t.cs() == "usepackage") {
717 string const options = p.getArg('[', ']');
718 string const name = p.getArg('{', '}');
719 vector<string> vecnames;
720 split(name, vecnames, ',');
721 vector<string>::const_iterator it = vecnames.begin();
722 vector<string>::const_iterator end = vecnames.end();
723 for (; it != end; ++it)
724 handle_package(p, trim(*it), options);
727 else if (t.cs() == "inputencoding") {
728 string const encoding = p.getArg('{','}');
729 h_inputencoding = encoding;
730 p.setEncoding(encoding);
733 else if (t.cs() == "newenvironment") {
734 string const name = p.getArg('{', '}');
736 // only non LyX specific stuff is output
737 ss << "\\newenvironment{" << name << "}";
740 ss << '{' << p.verbatim_item() << '}';
741 ss << '{' << p.verbatim_item() << '}';
742 if (!is_known(name, known_lyx_commands))
743 h_preamble << ss.str();
746 else if (t.cs() == "def") {
747 string name = p.get_token().cs();
748 while (p.next_token().cat() != catBegin)
749 name += p.get_token().asString();
750 if (!is_known(name, known_lyx_commands))
751 h_preamble << "\\def\\" << name << '{'
752 << p.verbatim_item() << "}";
755 else if (t.cs() == "newcolumntype") {
756 string const name = p.getArg('{', '}');
759 string opts = p.getOpt();
761 istringstream is(string(opts, 1));
764 special_columns[name[0]] = nargs;
765 h_preamble << "\\newcolumntype{" << name << "}";
767 h_preamble << "[" << nargs << "]";
768 h_preamble << "{" << p.verbatim_item() << "}";
771 else if (t.cs() == "setcounter") {
772 string const name = p.getArg('{', '}');
773 string const content = p.getArg('{', '}');
774 if (name == "secnumdepth")
775 h_secnumdepth = content;
776 else if (name == "tocdepth")
777 h_tocdepth = content;
779 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
782 else if (t.cs() == "setlength") {
783 string const name = p.verbatim_item();
784 string const content = p.verbatim_item();
785 // the paragraphs are only not indented when \parindent is set to zero
786 if (name == "\\parindent" && content != "") {
787 if (content[0] == '0')
788 h_paragraph_separation = "skip";
789 } else if (name == "\\parskip") {
790 if (content == "\\smallskipamount")
791 h_defskip = "smallskip";
792 else if (content == "\\medskipamount")
793 h_defskip = "medskip";
794 else if (content == "\\bigskipamount")
795 h_defskip = "bigskip";
799 h_preamble << "\\setlength{" << name << "}{" << content << "}";
802 else if (t.cs() == "onehalfspacing")
803 h_spacing = "onehalf";
805 else if (t.cs() == "doublespacing")
806 h_spacing = "double";
808 else if (t.cs() == "setstretch")
809 h_spacing = "other " + p.verbatim_item();
811 else if (t.cs() == "begin") {
812 string const name = p.getArg('{', '}');
813 if (name == "document")
815 h_preamble << "\\begin{" << name << "}";
818 else if (t.cs() == "geometry") {
819 h_use_geometry = "true";
820 vector<string> opts = split_options(p.getArg('{', '}'));
821 vector<string>::iterator it;
823 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
824 h_paperorientation = "landscape";
828 handle_opt(opts, known_paper_sizes, h_papersize);
829 delete_opt(opts, known_paper_sizes);
831 char const * const * margin = known_paper_margins;
833 for (; *margin; ++margin) {
835 // search for the "=" in e.g. "lmargin=2cm" to get the value
836 for(size_t i = 0; i != opts.size(); i++) {
837 if (opts.at(i).find(*margin) != string::npos) {
838 string::size_type pos = opts.at(i).find("=");
839 string value = opts.at(i).substr(pos + 1);
840 string name = known_coded_paper_margins[k];
841 h_margins += "\\" + name + " " + value + "\n";
847 else if (t.cs() == "jurabibsetup") {
848 vector<string> jurabibsetup =
849 split_options(p.getArg('{', '}'));
850 // add jurabibsetup to the jurabib package options
851 add_package("jurabib", jurabibsetup);
852 if (!jurabibsetup.empty()) {
853 h_preamble << "\\jurabibsetup{"
854 << join(jurabibsetup, ",") << '}';
858 else if (!t.cs().empty())
859 h_preamble << '\\' << t.cs();
861 // remove the whitespace
865 // remove the whitespace
868 // Force textclass if the user wanted it
869 if (!forceclass.empty())
870 h_textclass = forceclass;
871 if (noweb_mode && !prefixIs(h_textclass, "literate-"))
872 h_textclass.insert(0, "literate-");
873 FileName layoutfilename = libFileSearch("layouts", h_textclass, "layout");
874 if (layoutfilename.empty()) {
875 cerr << "Error: Could not find layout file for textclass \"" << h_textclass << "\"." << endl;
878 tc.read(layoutfilename);
879 if (h_papersides.empty()) {
882 h_papersides = ss.str();
884 end_preamble(os, tc);