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",
123 "%% LyX 2.0.0svn created this file. For more info, see http://www.lyx.org/.\n",
127 ostringstream h_preamble;
128 string h_textclass = "article";
129 string h_options = string();
130 string h_language = "english";
131 string h_inputencoding = "auto";
132 string h_font_roman = "default";
133 string h_font_sans = "default";
134 string h_font_typewriter = "default";
135 string h_font_default_family = "default";
136 string h_font_sc = "false";
137 string h_font_osf = "false";
138 string h_font_sf_scale = "100";
139 string h_font_tt_scale = "100";
140 string h_graphics = "default";
141 string h_paperfontsize = "default";
142 string h_spacing = "single";
143 string h_papersize = "default";
144 string h_use_geometry = "false";
145 string h_use_amsmath = "1";
146 string h_use_esint = "1";
147 string h_cite_engine = "basic";
148 string h_use_bibtopic = "false";
149 string h_paperorientation = "portrait";
150 string h_secnumdepth = "3";
151 string h_tocdepth = "3";
152 string h_paragraph_separation = "indent";
153 string h_defskip = "medskip";
154 string h_quotes_language = "english";
155 string h_papercolumns = "1";
156 string h_papersides = string();
157 string h_paperpagestyle = "default";
158 string h_tracking_changes = "false";
159 string h_output_changes = "false";
160 string h_margins = "";
163 void handle_opt(vector<string> & opts, char const * const * what, string & target)
168 // the last language option is the document language (for babel and LyX)
169 // the last size option is the document font size
170 vector<string>::iterator it;
171 vector<string>::iterator position = opts.begin();
172 for (; *what; ++what) {
173 it = find(opts.begin(), opts.end(), *what);
174 if (it != opts.end()) {
175 documentclass_language = true;
176 if (it >= position) {
185 void delete_opt(vector<string> & opts, char const * const * what)
190 // remove found options from the list
191 // do this after handle_opt to avoid potential memory leaks and to be able
192 // to find in every case the last language option
193 vector<string>::iterator it;
194 for (; *what; ++what) {
195 it = find(opts.begin(), opts.end(), *what);
196 if (it != opts.end())
203 * Split a package options string (keyval format) into a vector.
205 * authorformat=smallcaps,
207 * titleformat=colonsep,
208 * bibformat={tabular,ibidem,numbered}
210 vector<string> split_options(string const & input)
212 vector<string> options;
216 Token const & t = p.get_token();
217 if (t.asInput() == ",") {
218 options.push_back(trim(option));
220 } else if (t.asInput() == "=") {
223 if (p.next_token().asInput() == "{")
224 option += '{' + p.getArg('{', '}') + '}';
225 } else if (t.cat() != catSpace)
226 option += t.asInput();
230 options.push_back(trim(option));
237 * Add package \p name with options \p options to used_packages.
238 * Remove options from \p options that we don't want to output.
240 void add_package(string const & name, vector<string> & options)
242 // every package inherits the global options
243 if (used_packages.find(name) == used_packages.end())
244 used_packages[name] = split_options(h_options);
246 vector<string> & v = used_packages[name];
247 v.insert(v.end(), options.begin(), options.end());
248 if (name == "jurabib") {
249 // Don't output the order argument (see the cite command
250 // handling code in text.cpp).
251 vector<string>::iterator end =
252 remove(options.begin(), options.end(), "natbiborder");
253 end = remove(options.begin(), end, "jurabiborder");
254 options.erase(end, options.end());
259 // Given is a string like "scaled=0.9", return 0.9 * 100
260 string const scale_as_percentage(string const & scale)
262 string::size_type pos = scale.find('=');
263 if (pos != string::npos) {
264 string value = scale.substr(pos + 1);
266 return convert<string>(100 * convert<double>(value));
268 // If the input string didn't match our expectations.
269 // return the default value "100"
274 void handle_package(Parser &p, string const & name, string const & opts)
276 vector<string> options = split_options(opts);
277 add_package(name, options);
281 if (is_known(name, known_roman_fonts)) {
286 if (name == "fourier") {
287 h_font_roman = "utopia";
288 // when font uses real small capitals
289 if (opts == "expert")
293 if (name == "mathpazo")
294 h_font_roman = "palatino";
296 if (name == "mathptmx")
297 h_font_roman = "times";
300 if (is_known(name, known_sans_fonts)) {
304 h_font_sf_scale = scale_as_percentage(scale);
309 if (is_known(name, known_typewriter_fonts)) {
310 h_font_typewriter = name;
313 h_font_tt_scale = scale_as_percentage(scale);
317 // font uses old-style figure
321 else if (name == "amsmath" || name == "amssymb")
324 else if (name == "esint")
327 else if (name == "babel" && !opts.empty()) {
328 // check if more than one option was used - used later for inputenc
329 // in case inputenc is parsed before babel, set the encoding to auto
330 if (options.begin() != options.end() - 1) {
331 one_language = false;
332 h_inputencoding = "auto";
334 // only set the document language when there was not already one set
335 // via the documentclass options
336 // babel takes the the last language given in the documentclass options
337 // as document language. If there is no such language option, the last
338 // option of its \usepackage call is used.
339 if (documentclass_language == false) {
340 handle_opt(options, known_languages, h_language);
341 delete_opt(options, known_languages);
342 if (is_known(h_language, known_french_languages))
343 h_language = "french";
344 else if (is_known(h_language, known_german_languages))
345 h_language = "german";
346 else if (is_known(h_language, known_ngerman_languages))
347 h_language = "ngerman";
348 else if (is_known(h_language, known_russian_languages))
349 h_language = "russian";
350 else if (is_known(h_language, known_ukrainian_languages))
351 h_language = "ukrainian";
352 h_quotes_language = h_language;
356 else if (name == "fontenc")
359 else if (name == "inputenc") {
360 // only set when there is not more than one inputenc
361 // option therefore check for the "," character also
362 // only set when there is not more then one babel
364 if (opts.find(",") == string::npos && one_language == true) {
366 //change ascii to auto to be in the unicode range, see
367 //http://bugzilla.lyx.org/show_bug.cgi?id=4719
368 h_inputencoding = "auto";
369 else if (!opts.empty())
370 h_inputencoding = opts;
372 if (!options.empty())
373 p.setEncoding(options.back());
377 else if (name == "makeidx")
380 else if (name == "prettyref")
383 else if (name == "varioref")
386 else if (name == "verbatim")
389 else if (name == "nomencl")
392 else if (name == "url")
395 else if (name == "color") {
396 // with the following command this package is only loaded when needed for
397 // undefined colors, since we only support the predefined colors
398 h_preamble << "\\@ifundefined{definecolor}\n {\\usepackage{color}}{}\n";
401 else if (name == "graphicx")
404 else if (name == "setspace")
407 else if (name == "geometry")
408 ; // Ignore this, the geometry settings are made by the \geometry
409 // command. This command is handled below.
411 else if (is_known(name, known_languages)) {
412 if (is_known(name, known_french_languages))
413 h_language = "french";
414 else if (is_known(name, known_german_languages))
415 h_language = "german";
416 else if (is_known(name, known_ngerman_languages))
417 h_language = "ngerman";
418 else if (is_known(name, known_russian_languages))
419 h_language = "russian";
420 else if (is_known(name, known_ukrainian_languages))
421 h_language = "ukrainian";
424 h_quotes_language = h_language;
427 else if (name == "natbib") {
428 h_cite_engine = "natbib_authoryear";
429 vector<string>::iterator it =
430 find(options.begin(), options.end(), "authoryear");
431 if (it != options.end())
434 it = find(options.begin(), options.end(), "numbers");
435 if (it != options.end()) {
436 h_cite_engine = "natbib_numerical";
442 else if (name == "jurabib")
443 h_cite_engine = "jurabib";
445 else if (name == "babel")
450 h_preamble << "\\usepackage{" << name << "}";
452 h_preamble << "\\usepackage[" << opts << "]{"
458 // We need to do something with the options...
459 if (!options.empty())
460 cerr << "Ignoring options '" << join(options, ",")
461 << "' of package " << name << '.' << endl;
463 // remove the whitespace
469 void end_preamble(ostream & os, TextClass const & /*textclass*/)
471 os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
472 << "\\lyxformat 258\n"
473 << "\\begin_document\n"
474 << "\\begin_header\n"
475 << "\\textclass " << h_textclass << "\n";
476 if (!h_preamble.str().empty())
477 os << "\\begin_preamble\n" << h_preamble.str() << "\n\\end_preamble\n";
478 if (!h_options.empty())
479 os << "\\options " << h_options << "\n";
480 os << "\\language " << h_language << "\n"
481 << "\\inputencoding " << h_inputencoding << "\n"
482 << "\\font_roman " << h_font_roman << "\n"
483 << "\\font_sans " << h_font_sans << "\n"
484 << "\\font_typewriter " << h_font_typewriter << "\n"
485 << "\\font_default_family " << h_font_default_family << "\n"
486 << "\\font_sc " << h_font_sc << "\n"
487 << "\\font_osf " << h_font_osf << "\n"
488 << "\\font_sf_scale " << h_font_sf_scale << "\n"
489 << "\\font_tt_scale " << h_font_tt_scale << "\n"
490 << "\\graphics " << h_graphics << "\n"
491 << "\\paperfontsize " << h_paperfontsize << "\n"
492 << "\\spacing " << h_spacing << "\n"
493 << "\\papersize " << h_papersize << "\n"
494 << "\\use_geometry " << h_use_geometry << "\n"
495 << "\\use_amsmath " << h_use_amsmath << "\n"
496 << "\\use_esint " << h_use_esint << "\n"
497 << "\\cite_engine " << h_cite_engine << "\n"
498 << "\\use_bibtopic " << h_use_bibtopic << "\n"
499 << "\\paperorientation " << h_paperorientation << "\n"
501 << "\\secnumdepth " << h_secnumdepth << "\n"
502 << "\\tocdepth " << h_tocdepth << "\n"
503 << "\\paragraph_separation " << h_paragraph_separation << "\n"
504 << "\\defskip " << h_defskip << "\n"
505 << "\\quotes_language " << h_quotes_language << "\n"
506 << "\\papercolumns " << h_papercolumns << "\n"
507 << "\\papersides " << h_papersides << "\n"
508 << "\\paperpagestyle " << h_paperpagestyle << "\n"
509 << "\\tracking_changes " << h_tracking_changes << "\n"
510 << "\\output_changes " << h_output_changes << "\n"
511 << "\\end_header\n\n"
513 // clear preamble for subdocuments
517 } // anonymous namespace
519 void parse_preamble(Parser & p, ostream & os,
520 string const & forceclass, TeX2LyXDocClass & tc)
522 // initialize fixed types
523 special_columns['D'] = 3;
524 bool is_full_document = false;
525 bool is_lyx_file = false;
526 bool lyx_specific_preamble = false;
528 // determine whether this is a full document or a fragment for inclusion
530 Token const & t = p.get_token();
532 if (t.cat() == catEscape && t.cs() == "documentclass") {
533 is_full_document = true;
539 while (is_full_document && p.good()) {
540 Token const & t = p.get_token();
543 cerr << "t: " << t << "\n";
549 if ((t.cat() == catLetter ||
550 t.cat() == catSuper ||
552 t.cat() == catOther ||
553 t.cat() == catMath ||
554 t.cat() == catActive ||
555 t.cat() == catBegin ||
557 t.cat() == catAlign ||
558 t.cat() == catParameter))
559 h_preamble << t.character();
561 else if (t.cat() == catSpace || t.cat() == catNewline)
562 h_preamble << t.asInput();
564 else if (t.cat() == catComment) {
565 // regex to parse comments
566 static regex const islyxfile("%% LyX .* created this file");
567 static regex const usercommands("User specified LaTeX commands");
569 string const comment = t.asInput();
571 // magically switch encoding default if it looks like XeLaTeX
572 static string const magicXeLaTeX =
573 "% This document must be compiled with XeLaTeX ";
574 if (comment.size() > magicXeLaTeX.size()
575 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
576 && h_inputencoding == "auto") {
577 cerr << "XeLaTeX comment found, switching to UTF8\n";
578 h_inputencoding = "utf8";
582 if (regex_search(comment, sub, islyxfile))
584 // don't output LyX specific comments
585 if (!is_known(comment, known_lyx_comments))
586 h_preamble << t.asInput();
589 else if (t.cs() == "pagestyle")
590 h_paperpagestyle = p.verbatim_item();
592 else if (t.cs() == "makeatletter") {
593 // LyX takes care of this
594 p.setCatCode('@', catLetter);
597 else if (t.cs() == "makeatother") {
598 // LyX takes care of this
599 p.setCatCode('@', catOther);
602 else if (t.cs() == "newcommand" || t.cs() == "renewcommand"
603 || t.cs() == "providecommand"
604 || t.cs() == "DeclareRobustCommand"
605 || t.cs() == "ProvideTextCommandDefault"
606 || t.cs() == "DeclareMathAccent") {
608 if (p.next_token().character() == '*') {
612 string const name = p.verbatim_item();
613 string const opt1 = p.getOpt();
614 string const opt2 = p.getFullOpt();
615 string const body = p.verbatim_item();
617 if (name == "\\rmdefault")
618 if (is_known(body, known_roman_fonts))
620 if (name == "\\sfdefault")
621 if (is_known(body, known_sans_fonts))
623 if (name == "\\ttdefault")
624 if (is_known(body, known_typewriter_fonts))
625 h_font_typewriter = body;
626 if (name == "\\familydefault") {
627 string family = body;
628 // remove leading "\"
629 h_font_default_family = family.erase(0,1);
631 // LyX specific commands that will automatically be set by LyX
632 string lyx_command = name;
633 // remove the leading "\"
634 lyx_command.erase(0,1);
635 if (is_known(lyx_command, known_lyx_commands))
636 lyx_specific_preamble = true;
637 // only non-lyxspecific stuff
638 if (!lyx_specific_preamble) {
640 ss << '\\' << t.cs();
643 ss << '{' << name << '}' << opt1 << opt2
644 << '{' << body << "}";
645 h_preamble << ss.str();
647 // Add the command to the known commands
648 add_known_command(name, opt1, !opt2.empty());
650 ostream & out = in_preamble ? h_preamble : os;
651 out << "\\" << t.cs() << "{" << name << "}"
652 << opts << "{" << body << "}";
657 else if (t.cs() == "documentclass") {
658 vector<string>::iterator it;
659 vector<string> opts = split_options(p.getArg('[', ']'));
660 handle_opt(opts, known_fontsizes, h_paperfontsize);
661 delete_opt(opts, known_fontsizes);
662 // delete "pt" at the end
663 string::size_type i = h_paperfontsize.find("pt");
664 if (i != string::npos)
665 h_paperfontsize.erase(i);
666 // to avoid that the babel options overwrite the documentclass options
667 documentclass_language = false;
668 handle_opt(opts, known_languages, h_language);
669 delete_opt(opts, known_languages);
670 if (is_known(h_language, known_french_languages))
671 h_language = "french";
672 else if (is_known(h_language, known_german_languages))
673 h_language = "german";
674 else if (is_known(h_language, known_ngerman_languages))
675 h_language = "ngerman";
676 else if (is_known(h_language, known_russian_languages))
677 h_language = "russian";
678 else if (is_known(h_language, known_ukrainian_languages))
679 h_language = "ukrainian";
680 h_quotes_language = h_language;
682 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
683 h_paperorientation = "landscape";
687 if ((it = find(opts.begin(), opts.end(), "oneside"))
692 if ((it = find(opts.begin(), opts.end(), "twoside"))
698 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
700 h_papercolumns = "1";
703 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
705 h_papercolumns = "2";
709 // some size options are know to any document classes, other sizes
710 // are handled by the \geometry command of the geometry package
711 handle_opt(opts, known_class_paper_sizes, h_papersize);
712 delete_opt(opts, known_class_paper_sizes);
713 // the remaining options
714 h_options = join(opts, ",");
715 h_textclass = p.getArg('{', '}');
718 else if (t.cs() == "usepackage") {
719 string const options = p.getArg('[', ']');
720 string const name = p.getArg('{', '}');
721 vector<string> vecnames;
722 split(name, vecnames, ',');
723 vector<string>::const_iterator it = vecnames.begin();
724 vector<string>::const_iterator end = vecnames.end();
725 for (; it != end; ++it)
726 handle_package(p, trim(*it), options);
729 else if (t.cs() == "inputencoding") {
730 string const encoding = p.getArg('{','}');
731 h_inputencoding = encoding;
732 p.setEncoding(encoding);
735 else if (t.cs() == "newenvironment") {
736 string const name = p.getArg('{', '}');
738 // only non LyX specific stuff is output
739 ss << "\\newenvironment{" << name << "}";
742 ss << '{' << p.verbatim_item() << '}';
743 ss << '{' << p.verbatim_item() << '}';
744 if (!is_known(name, known_lyx_commands))
745 h_preamble << ss.str();
748 else if (t.cs() == "def") {
749 string name = p.get_token().cs();
750 while (p.next_token().cat() != catBegin)
751 name += p.get_token().asString();
752 if (!is_known(name, known_lyx_commands))
753 h_preamble << "\\def\\" << name << '{'
754 << p.verbatim_item() << "}";
757 else if (t.cs() == "newcolumntype") {
758 string const name = p.getArg('{', '}');
761 string opts = p.getOpt();
763 istringstream is(string(opts, 1));
766 special_columns[name[0]] = nargs;
767 h_preamble << "\\newcolumntype{" << name << "}";
769 h_preamble << "[" << nargs << "]";
770 h_preamble << "{" << p.verbatim_item() << "}";
773 else if (t.cs() == "setcounter") {
774 string const name = p.getArg('{', '}');
775 string const content = p.getArg('{', '}');
776 if (name == "secnumdepth")
777 h_secnumdepth = content;
778 else if (name == "tocdepth")
779 h_tocdepth = content;
781 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
784 else if (t.cs() == "setlength") {
785 string const name = p.verbatim_item();
786 string const content = p.verbatim_item();
787 // the paragraphs are only not indented when \parindent is set to zero
788 if (name == "\\parindent" && content != "") {
789 if (content[0] == '0')
790 h_paragraph_separation = "skip";
791 } else if (name == "\\parskip") {
792 if (content == "\\smallskipamount")
793 h_defskip = "smallskip";
794 else if (content == "\\medskipamount")
795 h_defskip = "medskip";
796 else if (content == "\\bigskipamount")
797 h_defskip = "bigskip";
801 h_preamble << "\\setlength{" << name << "}{" << content << "}";
804 else if (t.cs() == "onehalfspacing")
805 h_spacing = "onehalf";
807 else if (t.cs() == "doublespacing")
808 h_spacing = "double";
810 else if (t.cs() == "setstretch")
811 h_spacing = "other " + p.verbatim_item();
813 else if (t.cs() == "begin") {
814 string const name = p.getArg('{', '}');
815 if (name == "document")
817 h_preamble << "\\begin{" << name << "}";
820 else if (t.cs() == "geometry") {
821 h_use_geometry = "true";
822 vector<string> opts = split_options(p.getArg('{', '}'));
823 vector<string>::iterator it;
825 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
826 h_paperorientation = "landscape";
830 handle_opt(opts, known_paper_sizes, h_papersize);
831 delete_opt(opts, known_paper_sizes);
833 char const * const * margin = known_paper_margins;
835 for (; *margin; ++margin) {
837 // search for the "=" in e.g. "lmargin=2cm" to get the value
838 for(size_t i = 0; i != opts.size(); i++) {
839 if (opts.at(i).find(*margin) != string::npos) {
840 string::size_type pos = opts.at(i).find("=");
841 string value = opts.at(i).substr(pos + 1);
842 string name = known_coded_paper_margins[k];
843 h_margins += "\\" + name + " " + value + "\n";
849 else if (t.cs() == "jurabibsetup") {
850 vector<string> jurabibsetup =
851 split_options(p.getArg('{', '}'));
852 // add jurabibsetup to the jurabib package options
853 add_package("jurabib", jurabibsetup);
854 if (!jurabibsetup.empty()) {
855 h_preamble << "\\jurabibsetup{"
856 << join(jurabibsetup, ",") << '}';
860 else if (!t.cs().empty())
861 h_preamble << '\\' << t.cs();
863 // remove the whitespace
867 // remove the whitespace
870 // Force textclass if the user wanted it
871 if (!forceclass.empty())
872 h_textclass = forceclass;
873 if (noweb_mode && !prefixIs(h_textclass, "literate-"))
874 h_textclass.insert(0, "literate-");
875 FileName layoutfilename = libFileSearch("layouts", h_textclass, "layout");
876 if (layoutfilename.empty()) {
877 cerr << "Error: Could not find layout file for textclass \"" << h_textclass << "\"." << endl;
880 tc.read(layoutfilename);
881 if (h_papersides.empty()) {
884 h_papersides = ss.str();
886 end_preamble(os, tc);