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", "brazilian", "breton",
59 "british", "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech",
60 "danish", "dutch", "english", "esperanto", "estonian", "finnish", "francais",
61 "french", "frenchb", "frenchle", "frenchpro", "galician", "german", "germanb",
62 "greek", "hebrew", "icelandic", "irish", "italian", "lsorbian", "magyar",
63 "naustrian", "ngerman", "ngermanb", "norsk", "nynorsk", "polish", "portuges",
64 "portuguese", "romanian", "russian", "russianb", "scottish", "serbian", "slovak",
65 "slovene", "spanish", "swedish", "thai", "turkish", "ukraineb", "ukrainian",
66 "usorbian", "welsh", 0};
68 //note this when updating to lyxformat 305:
69 //bahasai, indonesian, and indon = equal to bahasa
70 //malay and meyalu = equal to bahasam
72 const char * const known_brazilian_languages[] = {"brazil", "brazilian", 0};
73 const char * const known_french_languages[] = {"french", "frenchb", "francais",
74 "frenchle", "frenchpro", 0};
75 const char * const known_german_languages[] = {"german", "germanb", 0};
76 const char * const known_ngerman_languages[] = {"ngerman", "ngermanb", 0};
77 const char * const known_portuguese_languages[] = {"portuges", "portuguese", 0};
78 const char * const known_russian_languages[] = {"russian", "russianb", 0};
79 const char * const known_ukrainian_languages[] = {"ukrainian", "ukraineb", 0};
81 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
83 const char * const known_roman_fonts[] = { "ae", "bookman", "charter",
84 "cmr", "fourier", "lmodern", "mathpazo", "mathptmx", "newcent", 0};
86 const char * const known_sans_fonts[] = { "avant", "berasans", "cmbr", "cmss",
89 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
90 "courier", "lmtt", "luximono", "fourier", "lmodern", "mathpazo", "mathptmx",
93 const char * const known_paper_sizes[] = { "a3paper", "b3paper", "a4paper",
94 "b4paper", "a5paper", "b5paper", "executivepaper", "legalpaper",
97 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
98 "executivepaper", "legalpaper", "letterpaper", 0};
100 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
101 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
103 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
104 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
107 const char * const known_lyx_commands[] = { "binom", "cedilla", "cyrtext",
108 "dacute", "dgrave", "docedilla", "doogonek", "dosubhat", "dosubring",
109 "dosubtilde", "greektext", "guillemotleft", "guillemotright", "guilsinglleft",
110 "guilsinglright", "LyX", "lyxadded", "lyxaddress", "lyxarrow", "lyxcode",
111 "lyxdeleted", "lyxdot", "lyxgreyedout", "lyxline", "lyxlist", "lyxmathsym",
112 "LyXParagraphLeftIndent", "lyxrightaddress", "makenomenclature",
113 "mathcircumflex", "noun", "ogonek", "printnomenclature", "quotedblbase",
114 "quotesinglbase", "rcap", "subhat", "subring", "subtilde", "tabularnewline",
115 "textcyr", "textgreek", 0};
117 const char * const known_lyx_comments[] = {
118 "%% Binom macro for standard LaTeX users\n",
119 "%% For printing a cirumflex inside a formula\n",
120 "%% Because html converters don't know tabularnewline\n",
121 "%% The greyedout annotation environment\n",
122 "%% A simple dot to overcome graphicx limitations\n",
123 "%% Change tracking with ulem\n",
124 "% the following is useful when we have the old nomencl.sty package\n",
125 "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% LyX specific LaTeX commands.\n",
126 "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% User specified LaTeX commands.\n",
127 "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Textclass specific LaTeX commands.\n",
131 ostringstream h_preamble;
132 string h_textclass = "article";
133 string h_options = string();
134 string h_language = "english";
135 string h_inputencoding = "auto";
136 string h_font_roman = "default";
137 string h_font_sans = "default";
138 string h_font_typewriter = "default";
139 string h_font_default_family = "default";
140 string h_font_sc = "false";
141 string h_font_osf = "false";
142 string h_font_sf_scale = "100";
143 string h_font_tt_scale = "100";
144 string h_graphics = "default";
145 string h_paperfontsize = "default";
146 string h_spacing = "single";
147 string h_papersize = "default";
148 string h_use_geometry = "false";
149 string h_use_amsmath = "1";
150 string h_use_esint = "1";
151 string h_cite_engine = "basic";
152 string h_use_bibtopic = "false";
153 string h_paperorientation = "portrait";
154 string h_secnumdepth = "3";
155 string h_tocdepth = "3";
156 string h_paragraph_separation = "indent";
157 string h_defskip = "medskip";
158 string h_quotes_language = "english";
159 string h_papercolumns = "1";
160 string h_papersides = string();
161 string h_paperpagestyle = "default";
162 string h_tracking_changes = "false";
163 string h_output_changes = "false";
164 string h_margins = "";
167 void handle_opt(vector<string> & opts, char const * const * what, string & target)
172 // the last language option is the document language (for babel and LyX)
173 // the last size option is the document font size
174 vector<string>::iterator it;
175 vector<string>::iterator position = opts.begin();
176 for (; *what; ++what) {
177 it = find(opts.begin(), opts.end(), *what);
178 if (it != opts.end()) {
179 documentclass_language = true;
180 if (it >= position) {
189 void delete_opt(vector<string> & opts, char const * const * what)
194 // remove found options from the list
195 // do this after handle_opt to avoid potential memory leaks and to be able
196 // to find in every case the last language option
197 vector<string>::iterator it;
198 for (; *what; ++what) {
199 it = find(opts.begin(), opts.end(), *what);
200 if (it != opts.end())
207 * Split a package options string (keyval format) into a vector.
209 * authorformat=smallcaps,
211 * titleformat=colonsep,
212 * bibformat={tabular,ibidem,numbered}
214 vector<string> split_options(string const & input)
216 vector<string> options;
220 Token const & t = p.get_token();
221 if (t.asInput() == ",") {
222 options.push_back(trim(option));
224 } else if (t.asInput() == "=") {
227 if (p.next_token().asInput() == "{")
228 option += '{' + p.getArg('{', '}') + '}';
229 } else if (t.cat() != catSpace)
230 option += t.asInput();
234 options.push_back(trim(option));
241 * Add package \p name with options \p options to used_packages.
242 * Remove options from \p options that we don't want to output.
244 void add_package(string const & name, vector<string> & options)
246 // every package inherits the global options
247 if (used_packages.find(name) == used_packages.end())
248 used_packages[name] = split_options(h_options);
250 vector<string> & v = used_packages[name];
251 v.insert(v.end(), options.begin(), options.end());
252 if (name == "jurabib") {
253 // Don't output the order argument (see the cite command
254 // handling code in text.cpp).
255 vector<string>::iterator end =
256 remove(options.begin(), options.end(), "natbiborder");
257 end = remove(options.begin(), end, "jurabiborder");
258 options.erase(end, options.end());
263 // Given is a string like "scaled=0.9", return 0.9 * 100
264 string const scale_as_percentage(string const & scale)
266 string::size_type pos = scale.find('=');
267 if (pos != string::npos) {
268 string value = scale.substr(pos + 1);
270 return convert<string>(100 * convert<double>(value));
272 // If the input string didn't match our expectations.
273 // return the default value "100"
278 void handle_package(Parser &p, string const & name, string const & opts)
280 vector<string> options = split_options(opts);
281 add_package(name, options);
285 if (is_known(name, known_roman_fonts)) {
290 if (name == "fourier") {
291 h_font_roman = "utopia";
292 // when font uses real small capitals
293 if (opts == "expert")
297 if (name == "mathpazo")
298 h_font_roman = "palatino";
300 if (name == "mathptmx")
301 h_font_roman = "times";
304 if (is_known(name, known_sans_fonts)) {
308 h_font_sf_scale = scale_as_percentage(scale);
313 if (is_known(name, known_typewriter_fonts)) {
314 h_font_typewriter = name;
317 h_font_tt_scale = scale_as_percentage(scale);
321 // font uses old-style figure
325 else if (name == "amsmath" || name == "amssymb")
328 else if (name == "esint")
331 else if (name == "babel" && !opts.empty()) {
332 // check if more than one option was used - used later for inputenc
333 // in case inputenc is parsed before babel, set the encoding to auto
334 if (options.begin() != options.end() - 1) {
335 one_language = false;
336 h_inputencoding = "auto";
338 // only set the document language when there was not already one set
339 // via the documentclass options
340 // babel takes the the last language given in the documentclass options
341 // as document language. If there is no such language option, the last
342 // option of its \usepackage call is used.
343 if (documentclass_language == false) {
344 handle_opt(options, known_languages, h_language);
345 delete_opt(options, known_languages);
346 if (is_known(h_language, known_brazilian_languages))
347 h_language = "brazilian";
348 else if (is_known(h_language, known_french_languages))
349 h_language = "french";
350 else if (is_known(h_language, known_german_languages))
351 h_language = "german";
352 else if (is_known(h_language, known_ngerman_languages))
353 h_language = "ngerman";
354 else if (is_known(h_language, known_portuguese_languages))
355 h_language = "portuguese";
356 else if (is_known(h_language, known_russian_languages))
357 h_language = "russian";
358 else if (is_known(h_language, known_ukrainian_languages))
359 h_language = "ukrainian";
360 h_quotes_language = h_language;
364 else if (name == "fontenc")
367 else if (name == "inputenc") {
368 // only set when there is not more than one inputenc
369 // option therefore check for the "," character also
370 // only set when there is not more then one babel
372 if (opts.find(",") == string::npos && one_language == true) {
374 //change ascii to auto to be in the unicode range, see
375 //http://bugzilla.lyx.org/show_bug.cgi?id=4719
376 h_inputencoding = "auto";
377 else if (!opts.empty())
378 h_inputencoding = opts;
380 if (!options.empty())
381 p.setEncoding(options.back());
385 else if (name == "makeidx")
388 else if (name == "prettyref")
391 else if (name == "varioref")
394 else if (name == "verbatim")
397 else if (name == "nomencl")
400 else if (name == "textcomp")
403 else if (name == "url")
406 else if (name == "color") {
407 // with the following command this package is only loaded when needed for
408 // undefined colors, since we only support the predefined colors
409 h_preamble << "\\@ifundefined{definecolor}\n {\\usepackage{color}}{}\n";
412 else if (name == "graphicx")
415 else if (name == "setspace")
418 else if (name == "geometry")
419 ; // Ignore this, the geometry settings are made by the \geometry
420 // command. This command is handled below.
422 else if (is_known(name, known_languages)) {
423 if (is_known(h_language, known_brazilian_languages))
424 h_language = "brazilian";
425 else if (is_known(name, known_french_languages))
426 h_language = "french";
427 else if (is_known(name, known_german_languages))
428 h_language = "german";
429 else if (is_known(name, known_ngerman_languages))
430 h_language = "ngerman";
431 else if (is_known(h_language, known_portuguese_languages))
432 h_language = "portuguese";
433 else if (is_known(name, known_russian_languages))
434 h_language = "russian";
435 else if (is_known(name, known_ukrainian_languages))
436 h_language = "ukrainian";
439 h_quotes_language = h_language;
442 else if (name == "natbib") {
443 h_cite_engine = "natbib_authoryear";
444 vector<string>::iterator it =
445 find(options.begin(), options.end(), "authoryear");
446 if (it != options.end())
449 it = find(options.begin(), options.end(), "numbers");
450 if (it != options.end()) {
451 h_cite_engine = "natbib_numerical";
457 else if (name == "jurabib")
458 h_cite_engine = "jurabib";
460 else if (name == "babel")
465 h_preamble << "\\usepackage{" << name << "}";
467 h_preamble << "\\usepackage[" << opts << "]{"
473 // We need to do something with the options...
474 if (!options.empty())
475 cerr << "Ignoring options '" << join(options, ",")
476 << "' of package " << name << '.' << endl;
478 // remove the whitespace
484 void end_preamble(ostream & os, TextClass const & /*textclass*/)
486 os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
487 << "\\lyxformat 264\n"
488 << "\\begin_document\n"
489 << "\\begin_header\n"
490 << "\\textclass " << h_textclass << "\n";
491 if (!h_preamble.str().empty())
492 os << "\\begin_preamble\n" << h_preamble.str() << "\n\\end_preamble\n";
493 if (!h_options.empty())
494 os << "\\options " << h_options << "\n";
495 os << "\\language " << h_language << "\n"
496 << "\\inputencoding " << h_inputencoding << "\n"
497 << "\\font_roman " << h_font_roman << "\n"
498 << "\\font_sans " << h_font_sans << "\n"
499 << "\\font_typewriter " << h_font_typewriter << "\n"
500 << "\\font_default_family " << h_font_default_family << "\n"
501 << "\\font_sc " << h_font_sc << "\n"
502 << "\\font_osf " << h_font_osf << "\n"
503 << "\\font_sf_scale " << h_font_sf_scale << "\n"
504 << "\\font_tt_scale " << h_font_tt_scale << "\n"
505 << "\\graphics " << h_graphics << "\n"
506 << "\\paperfontsize " << h_paperfontsize << "\n"
507 << "\\spacing " << h_spacing << "\n"
508 << "\\papersize " << h_papersize << "\n"
509 << "\\use_geometry " << h_use_geometry << "\n"
510 << "\\use_amsmath " << h_use_amsmath << "\n"
511 << "\\use_esint " << h_use_esint << "\n"
512 << "\\cite_engine " << h_cite_engine << "\n"
513 << "\\use_bibtopic " << h_use_bibtopic << "\n"
514 << "\\paperorientation " << h_paperorientation << "\n"
516 << "\\secnumdepth " << h_secnumdepth << "\n"
517 << "\\tocdepth " << h_tocdepth << "\n"
518 << "\\paragraph_separation " << h_paragraph_separation << "\n"
519 << "\\defskip " << h_defskip << "\n"
520 << "\\quotes_language " << h_quotes_language << "\n"
521 << "\\papercolumns " << h_papercolumns << "\n"
522 << "\\papersides " << h_papersides << "\n"
523 << "\\paperpagestyle " << h_paperpagestyle << "\n"
524 << "\\tracking_changes " << h_tracking_changes << "\n"
525 << "\\output_changes " << h_output_changes << "\n"
526 << "\\end_header\n\n"
528 // clear preamble for subdocuments
532 } // anonymous namespace
534 void parse_preamble(Parser & p, ostream & os,
535 string const & forceclass, TeX2LyXDocClass & tc)
537 // initialize fixed types
538 special_columns['D'] = 3;
539 bool is_full_document = false;
540 bool lyx_specific_preamble = false;
542 // determine whether this is a full document or a fragment for inclusion
544 Token const & t = p.get_token();
546 if (t.cat() == catEscape && t.cs() == "documentclass") {
547 is_full_document = true;
553 while (is_full_document && p.good()) {
554 Token const & t = p.get_token();
557 cerr << "t: " << t << "\n";
563 if ((t.cat() == catLetter ||
564 t.cat() == catSuper ||
566 t.cat() == catOther ||
567 t.cat() == catMath ||
568 t.cat() == catActive ||
569 t.cat() == catBegin ||
571 t.cat() == catAlign ||
572 t.cat() == catParameter))
573 h_preamble << t.character();
575 else if (t.cat() == catSpace || t.cat() == catNewline)
576 h_preamble << t.asInput();
578 else if (t.cat() == catComment) {
579 // regex to parse comments (currently not used)
580 //static regex const islyxfile("%% LyX .* created this file");
581 string const comment = t.asInput();
582 // magically switch encoding default if it looks like XeLaTeX
583 static string const magicXeLaTeX =
584 "% This document must be compiled with XeLaTeX ";
585 if (comment.size() > magicXeLaTeX.size()
586 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
587 && h_inputencoding == "auto") {
588 cerr << "XeLaTeX comment found, switching to UTF8\n";
589 h_inputencoding = "utf8";
592 // don't output LyX specific comments
593 if (!is_known(comment, known_lyx_comments))
594 h_preamble << t.asInput();
597 else if (t.cs() == "pagestyle")
598 h_paperpagestyle = p.verbatim_item();
600 else if (t.cs() == "makeatletter") {
601 // LyX takes care of this
602 p.setCatCode('@', catLetter);
605 else if (t.cs() == "makeatother") {
606 // LyX takes care of this
607 p.setCatCode('@', catOther);
610 else if (t.cs() == "newcommand" || t.cs() == "renewcommand"
611 || t.cs() == "providecommand"
612 || t.cs() == "DeclareRobustCommand"
613 || t.cs() == "ProvideTextCommandDefault"
614 || t.cs() == "DeclareMathAccent") {
616 if (p.next_token().character() == '*') {
620 string const name = p.verbatim_item();
621 string const opt1 = p.getOpt();
622 string const opt2 = p.getFullOpt();
623 string const body = p.verbatim_item();
625 if (name == "\\rmdefault")
626 if (is_known(body, known_roman_fonts))
628 if (name == "\\sfdefault")
629 if (is_known(body, known_sans_fonts))
631 if (name == "\\ttdefault")
632 if (is_known(body, known_typewriter_fonts))
633 h_font_typewriter = body;
634 if (name == "\\familydefault") {
635 string family = body;
636 // remove leading "\"
637 h_font_default_family = family.erase(0,1);
639 // LyX specific commands that will automatically be set by LyX
640 string lyx_command = name;
641 // remove the leading "\"
642 lyx_command.erase(0,1);
643 lyx_specific_preamble = false;
644 // allow redefinitions of LyX specific commands
645 if (is_known(lyx_command, known_lyx_commands)
646 && (t.cs() != "renewcommand"))
647 lyx_specific_preamble = true;
648 // only non-lyxspecific stuff
649 if (!lyx_specific_preamble) {
651 ss << '\\' << t.cs();
654 ss << '{' << name << '}' << opt1 << opt2
655 << '{' << body << "}";
656 h_preamble << ss.str();
658 // Add the command to the known commands
659 add_known_command(name, opt1, !opt2.empty());
661 ostream & out = in_preamble ? h_preamble : os;
662 out << "\\" << t.cs() << "{" << name << "}"
663 << opts << "{" << body << "}";
668 else if (t.cs() == "documentclass") {
669 vector<string>::iterator it;
670 vector<string> opts = split_options(p.getArg('[', ']'));
671 handle_opt(opts, known_fontsizes, h_paperfontsize);
672 delete_opt(opts, known_fontsizes);
673 // delete "pt" at the end
674 string::size_type i = h_paperfontsize.find("pt");
675 if (i != string::npos)
676 h_paperfontsize.erase(i);
677 // to avoid that the babel options overwrite the documentclass options
678 documentclass_language = false;
679 handle_opt(opts, known_languages, h_language);
680 delete_opt(opts, known_languages);
681 if (is_known(h_language, known_brazilian_languages))
682 h_language = "brazilian";
683 else if (is_known(h_language, known_french_languages))
684 h_language = "french";
685 else if (is_known(h_language, known_german_languages))
686 h_language = "german";
687 else if (is_known(h_language, known_ngerman_languages))
688 h_language = "ngerman";
689 else if (is_known(h_language, known_portuguese_languages))
690 h_language = "portuguese";
691 else if (is_known(h_language, known_russian_languages))
692 h_language = "russian";
693 else if (is_known(h_language, known_ukrainian_languages))
694 h_language = "ukrainian";
695 h_quotes_language = h_language;
697 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
698 h_paperorientation = "landscape";
702 if ((it = find(opts.begin(), opts.end(), "oneside"))
707 if ((it = find(opts.begin(), opts.end(), "twoside"))
713 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
715 h_papercolumns = "1";
718 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
720 h_papercolumns = "2";
724 // some size options are know to any document classes, other sizes
725 // are handled by the \geometry command of the geometry package
726 handle_opt(opts, known_class_paper_sizes, h_papersize);
727 delete_opt(opts, known_class_paper_sizes);
728 // the remaining options
729 h_options = join(opts, ",");
730 h_textclass = p.getArg('{', '}');
733 else if (t.cs() == "usepackage") {
734 string const options = p.getArg('[', ']');
735 string const name = p.getArg('{', '}');
736 vector<string> vecnames;
737 split(name, vecnames, ',');
738 vector<string>::const_iterator it = vecnames.begin();
739 vector<string>::const_iterator end = vecnames.end();
740 for (; it != end; ++it)
741 handle_package(p, trim(*it), options);
744 else if (t.cs() == "inputencoding") {
745 string const encoding = p.getArg('{','}');
746 h_inputencoding = encoding;
747 p.setEncoding(encoding);
750 else if (t.cs() == "newenvironment") {
751 string const name = p.getArg('{', '}');
753 // only non LyX specific stuff is output
754 ss << "\\newenvironment{" << name << "}";
757 ss << '{' << p.verbatim_item() << '}';
758 ss << '{' << p.verbatim_item() << '}';
759 if (!is_known(name, known_lyx_commands))
760 h_preamble << ss.str();
763 else if (t.cs() == "def") {
764 string name = p.get_token().cs();
765 while (p.next_token().cat() != catBegin)
766 name += p.get_token().asString();
767 if (!is_known(name, known_lyx_commands))
768 h_preamble << "\\def\\" << name << '{'
769 << p.verbatim_item() << "}";
772 else if (t.cs() == "newcolumntype") {
773 string const name = p.getArg('{', '}');
776 string opts = p.getOpt();
778 istringstream is(string(opts, 1));
781 special_columns[name[0]] = nargs;
782 h_preamble << "\\newcolumntype{" << name << "}";
784 h_preamble << "[" << nargs << "]";
785 h_preamble << "{" << p.verbatim_item() << "}";
788 else if (t.cs() == "setcounter") {
789 string const name = p.getArg('{', '}');
790 string const content = p.getArg('{', '}');
791 if (name == "secnumdepth")
792 h_secnumdepth = content;
793 else if (name == "tocdepth")
794 h_tocdepth = content;
796 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
799 else if (t.cs() == "setlength") {
800 string const name = p.verbatim_item();
801 string const content = p.verbatim_item();
802 // the paragraphs are only not indented when \parindent is set to zero
803 if (name == "\\parindent" && content != "") {
804 if (content[0] == '0')
805 h_paragraph_separation = "skip";
806 } else if (name == "\\parskip") {
807 if (content == "\\smallskipamount")
808 h_defskip = "smallskip";
809 else if (content == "\\medskipamount")
810 h_defskip = "medskip";
811 else if (content == "\\bigskipamount")
812 h_defskip = "bigskip";
816 h_preamble << "\\setlength{" << name << "}{" << content << "}";
819 else if (t.cs() == "onehalfspacing")
820 h_spacing = "onehalf";
822 else if (t.cs() == "doublespacing")
823 h_spacing = "double";
825 else if (t.cs() == "setstretch")
826 h_spacing = "other " + p.verbatim_item();
828 else if (t.cs() == "begin") {
829 string const name = p.getArg('{', '}');
830 if (name == "document")
832 h_preamble << "\\begin{" << name << "}";
835 else if (t.cs() == "geometry") {
836 h_use_geometry = "true";
837 vector<string> opts = split_options(p.getArg('{', '}'));
838 vector<string>::iterator it;
840 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
841 h_paperorientation = "landscape";
845 handle_opt(opts, known_paper_sizes, h_papersize);
846 delete_opt(opts, known_paper_sizes);
848 char const * const * margin = known_paper_margins;
850 for (; *margin; ++margin) {
852 // search for the "=" in e.g. "lmargin=2cm" to get the value
853 for(size_t i = 0; i != opts.size(); i++) {
854 if (opts.at(i).find(*margin) != string::npos) {
855 string::size_type pos = opts.at(i).find("=");
856 string value = opts.at(i).substr(pos + 1);
857 string name = known_coded_paper_margins[k];
858 h_margins += "\\" + name + " " + value + "\n";
864 else if (t.cs() == "jurabibsetup") {
865 vector<string> jurabibsetup =
866 split_options(p.getArg('{', '}'));
867 // add jurabibsetup to the jurabib package options
868 add_package("jurabib", jurabibsetup);
869 if (!jurabibsetup.empty()) {
870 h_preamble << "\\jurabibsetup{"
871 << join(jurabibsetup, ",") << '}';
875 else if (!t.cs().empty())
876 h_preamble << '\\' << t.cs();
878 // remove the whitespace
882 // remove the whitespace
885 // Force textclass if the user wanted it
886 if (!forceclass.empty())
887 h_textclass = forceclass;
888 if (noweb_mode && !prefixIs(h_textclass, "literate-"))
889 h_textclass.insert(0, "literate-");
890 FileName layoutfilename = libFileSearch("layouts", h_textclass, "layout");
891 if (layoutfilename.empty()) {
892 cerr << "Error: Could not find layout file for textclass \"" << h_textclass << "\"." << endl;
895 tc.read(layoutfilename);
896 if (h_papersides.empty()) {
899 h_papersides = ss.str();
901 end_preamble(os, tc);