3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
9 * Full author contact details are available in file CREDITS.
20 #include "TextClass.h"
21 #include "support/filetools.h"
22 #include "support/lstrings.h"
35 using std::istringstream;
37 using std::ostringstream;
44 using support::FileName;
45 using support::libFileSearch;
47 // special columntypes
48 extern std::map<char, int> special_columns;
50 std::map<string, vector<string> > used_packages;
54 const char * const known_languages[] = { "afrikaans", "albanian", "american",
55 "arabic_arabtex", "arabic_arabi", "armenian", "austrian", "bahasa", "basque",
56 "brazilian", "breton", "british", "bulgarian", "canadian", "canadien",
57 "catalan", "chinese-simplified", "chinese-traditional", "croatian", "czech",
58 "danish", "dutch", "english", "esperanto", "estonian", "farsi", "finnish",
59 "francais", "french", "galician", "german", "greek", "hebrew", "icelandic",
60 "irish", "italian", "japanese", "japanese-plain", "kazakh", "korean", "latin",
61 "latvian", "lithuanian", "lowersorbian", "magyar", "naustrian", "ngerman",
62 "norsk", "nynorsk ", "polish", "portuges", "romanian", "russian", "samin",
63 "scottish", "serbian", "slovak", "slovene", "spanish", "swedish", "thai",
64 "turkish", "ukrainian", "uppersorbian", "vietnamese", "welsh", 0};
66 const char * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
68 const char * const known_encodings[] = { "auto", "latin1", "latin2", "latin3",
69 "latin4", "latin5", "latin9", "latin10", "iso88595", "8859-6", "iso-8859-7",
70 "8859-8", "l7xen", "cp437", "cp437de", "cp850", "cp852", "cp855", "cp858",
71 "cp862", "cp865", "cp866", "cp1250", "cp1251", "cp1252", "cp1255", "cp1256",
72 "cp1257", "koi8-r", "koi8-u", "pt154", "pt254", "utf8", 0};
74 const char * const known_roman_fonts[] = { "ae", "bookman", "charter",
75 "cmr", "fourier", "lmodern", "mathpazo", "mathptmx", "newcent", 0};
77 const char * const known_sans_fonts[] = { "avant", "berasans", "cmbr", "cmss",
80 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
81 "courier", "lmtt", "luximono", "fourier", "lmodern", "mathpazo", "mathptmx",
85 ostringstream h_preamble;
86 string h_textclass = "article";
87 string h_options = string();
88 string h_language = "english";
89 string h_inputencoding = "auto";
90 string h_font_roman = "default";
91 string h_font_sans = "default";
92 string h_font_typewriter = "default";
93 string h_font_default_family = "default";
94 string h_font_sc = "false";
95 string h_font_osf = "false";
96 string h_font_sf_scale = "100";
97 string h_font_tt_scale = "100";
98 string h_graphics = "default";
99 string h_paperfontsize = "default";
100 string h_spacing = "single";
101 string h_papersize = "default";
102 string h_use_geometry = "false";
103 string h_use_amsmath = "1";
104 string h_use_esint = "1";
105 string h_cite_engine = "basic";
106 string h_use_bibtopic = "false";
107 string h_paperorientation = "portrait";
108 string h_secnumdepth = "3";
109 string h_tocdepth = "3";
110 string h_paragraph_separation = "indent";
111 string h_defskip = "medskip";
112 string h_quotes_language = "english";
113 string h_papercolumns = "1";
114 string h_papersides = string();
115 string h_paperpagestyle = "default";
116 string h_tracking_changes = "false";
117 string h_output_changes = "false";
118 string h_use_hyperref = "false";
121 void handle_opt(vector<string> & opts, char const * const * what, string & target)
126 for (; *what; ++what) {
127 vector<string>::iterator it = find(opts.begin(), opts.end(), *what);
128 // the last language option is the document language
129 if (it != opts.end()) {
130 //cerr << "### found option '" << *what << "'\n";
140 * Split a package options string (keyval format) into a vector.
142 * authorformat=smallcaps,
144 * titleformat=colonsep,
145 * bibformat={tabular,ibidem,numbered}
147 vector<string> split_options(string const & input)
149 vector<string> options;
153 Token const & t = p.get_token();
154 if (t.asInput() == ",") {
155 options.push_back(option);
157 } else if (t.asInput() == "=") {
160 if (p.next_token().asInput() == "{")
161 option += '{' + p.getArg('{', '}') + '}';
162 } else if (t.cat() != catSpace)
163 option += t.asInput();
167 options.push_back(option);
174 * Add package \p name with options \p options to used_packages.
175 * Remove options from \p options that we don't want to output.
177 void add_package(string const & name, vector<string> & options)
179 // every package inherits the global options
180 if (used_packages.find(name) == used_packages.end())
181 used_packages[name] = split_options(h_options);
183 vector<string> & v = used_packages[name];
184 v.insert(v.end(), options.begin(), options.end());
185 if (name == "jurabib") {
186 // Don't output the order argument (see the cite command
187 // handling code in text.cpp).
188 vector<string>::iterator end =
189 remove(options.begin(), options.end(), "natbiborder");
190 end = remove(options.begin(), end, "jurabiborder");
191 options.erase(end, options.end());
196 void handle_package(string const & name, string const & opts)
198 vector<string> options = split_options(opts);
199 add_package(name, options);
203 // cerr << "handle_package: '" << name << "'\n";
206 if (is_known(name, known_roman_fonts))
208 if (name == "fourier")
209 h_font_roman = "utopia";
210 if (name == "mathpazo")
211 h_font_roman = "palatino";
212 if (name == "mathptmx")
213 h_font_roman = "times";
215 if (is_known(name, known_sans_fonts)) {
219 pos = scale.find(".", 0);
220 h_font_sf_scale = scale.erase(0, pos + 1);
224 if (is_known(name, known_typewriter_fonts)) {
225 h_font_typewriter = name;
228 pos = scale.find(".", 0);
229 h_font_tt_scale = scale.erase(0, pos + 1);
233 else if (name == "amssymb")
235 else if (name == "esint")
237 else if (name == "babel")
239 else if (name == "fontenc")
241 else if (name == "inputenc") {
242 // only set when there are not more than one inputenc option
243 // therefore check for the "," character
244 if ((pos = opts.find(",", 0)) == string::npos)
245 h_inputencoding = opts;
247 } else if (name == "makeidx")
249 else if (name == "verbatim")
251 else if (name == "graphicx")
253 else if (is_known(name, known_languages)) {
255 h_quotes_language = name;
256 } else if (name == "natbib") {
257 h_cite_engine = "natbib_authoryear";
258 vector<string>::iterator it =
259 find(options.begin(), options.end(), "authoryear");
260 if (it != options.end())
263 it = find(options.begin(), options.end(), "numbers");
264 if (it != options.end()) {
265 h_cite_engine = "natbib_numerical";
269 } else if (name == "jurabib") {
270 h_cite_engine = "jurabib";
271 } else if (options.empty())
272 h_preamble << "\\usepackage{" << name << "}\n";
274 h_preamble << "\\usepackage[" << opts << "]{" << name << "}\n";
278 // We need to do something with the options...
279 if (!options.empty())
280 cerr << "Ignoring options '" << join(options, ",")
281 << "' of package " << name << '.' << endl;
285 void end_preamble(ostream & os, TextClass const & /*textclass*/)
287 os << "#LyX file created by tex2lyx 0.1.5\n"
288 << "\\lyxformat 245\n"
289 << "\\begin_document\n"
290 << "\\begin_header\n"
291 << "\\textclass " << h_textclass << "\n";
292 if (!h_preamble.str().empty())
293 os << "\\begin_preamble\n" << h_preamble.str() << "\n\\end_preamble\n";
294 if (!h_options.empty())
295 os << "\\options " << h_options << "\n";
296 os << "\\language " << h_language << "\n"
297 << "\\inputencoding " << h_inputencoding << "\n"
298 << "\\font_roman " << h_font_roman << "\n"
299 << "\\font_sans " << h_font_sans << "\n"
300 << "\\font_typewriter " << h_font_typewriter << "\n"
301 << "\\font_default_family " << h_font_default_family << "\n"
302 << "\\font_sc " << h_font_sc << "\n"
303 << "\\font_osf " << h_font_osf << "\n"
304 << "\\font_sf_scale " << h_font_sf_scale << "\n"
305 << "\\font_tt_scale " << h_font_tt_scale << "\n"
306 << "\\graphics " << h_graphics << "\n"
307 << "\\paperfontsize " << h_paperfontsize << "\n"
308 << "\\spacing " << h_spacing << "\n"
309 << "\\use_hyperref " << h_use_hyperref << "\n"
310 << "\\papersize " << h_papersize << "\n"
311 << "\\use_geometry " << h_use_geometry << "\n"
312 << "\\use_amsmath " << h_use_amsmath << "\n"
313 << "\\use_esint " << h_use_esint << "\n"
314 << "\\cite_engine " << h_cite_engine << "\n"
315 << "\\use_bibtopic " << h_use_bibtopic << "\n"
316 << "\\paperorientation " << h_paperorientation << "\n"
317 << "\\secnumdepth " << h_secnumdepth << "\n"
318 << "\\tocdepth " << h_tocdepth << "\n"
319 << "\\paragraph_separation " << h_paragraph_separation << "\n"
320 << "\\defskip " << h_defskip << "\n"
321 << "\\quotes_language " << h_quotes_language << "\n"
322 << "\\papercolumns " << h_papercolumns << "\n"
323 << "\\papersides " << h_papersides << "\n"
324 << "\\paperpagestyle " << h_paperpagestyle << "\n"
325 << "\\tracking_changes " << h_tracking_changes << "\n"
326 << "\\output_changes " << h_output_changes << "\n"
327 << "\\end_header\n\n"
329 // clear preamble for subdocuments
333 } // anonymous namespace
336 TextClass const parse_preamble(Parser & p, ostream & os, string const & forceclass)
338 // initialize fixed types
339 special_columns['D'] = 3;
340 bool is_full_document = false;
342 // determine whether this is a full document or a fragment for inclusion
344 Token const & t = p.get_token();
346 if (t.cat() == catEscape && t.cs() == "documentclass") {
347 is_full_document = true;
353 while (is_full_document && p.good()) {
354 Token const & t = p.get_token();
357 cerr << "t: " << t << "\n";
363 if (t.cat() == catLetter ||
364 t.cat() == catSuper ||
366 t.cat() == catOther ||
367 t.cat() == catMath ||
368 t.cat() == catActive ||
369 t.cat() == catBegin ||
371 t.cat() == catAlign ||
372 t.cat() == catParameter)
373 ;//h_preamble << t.character();
375 else if (t.cat() == catSpace || t.cat() == catNewline)
376 ;//h_preamble << t.asInput();
378 else if (t.cat() == catComment)
379 ;//h_preamble << t.asInput();
381 else if (t.cs() == "pagestyle")
382 h_paperpagestyle = p.verbatim_item();
384 else if (t.cs() == "makeatletter")
385 p.setCatCode('@', catLetter);
387 else if (t.cs() == "makeatother")
388 p.setCatCode('@', catOther);
390 else if (t.cs() == "newcommand" || t.cs() == "renewcommand"
391 || t.cs() == "providecommand") {
393 if (p.next_token().character() == '*') {
397 string const name = p.verbatim_item();
398 string const opt1 = p.getOpt();
399 string const opt2 = p.getFullOpt();
400 string const body = p.verbatim_item();
402 if (name == "\\rmdefault")
403 if (is_known(body, known_roman_fonts))
406 if (name == "\\sfdefault")
407 if (is_known(body, known_sans_fonts))
410 if (name == "\\ttdefault")
411 if (is_known(body, known_typewriter_fonts))
412 h_font_typewriter = body;
414 if (name == "\\familydefault") {
415 string family = body;
416 // remove leading "\"
417 h_font_default_family = family.erase(0,1);
419 // only non-lyxspecific stuff
420 if ( name != "\\noun"
421 && name != "\\tabularnewline"
423 && name != "\\lyxline"
424 && name != "\\lyxaddress"
425 && name != "\\lyxrightaddress"
426 && name != "\\lyxdot"
427 && name != "\\boldsymbol"
428 && name != "\\lyxarrow"
429 && name != "\\rmdefault"
430 && name != "\\sfdefault"
431 && name != "\\ttdefault"
432 && name != "\\familydefault") {
434 ss << '\\' << t.cs();
437 ss << '{' << name << '}' << opt1 << opt2
438 << '{' << body << "}";
439 h_preamble << ss.str();
441 // Add the command to the known commands
442 add_known_command(name, opt1, !opt2.empty());
444 ostream & out = in_preamble ? h_preamble : os;
445 out << "\\" << t.cs() << "{" << name << "}"
446 << opts << "{" << body << "}";
452 else if (t.cs() == "documentclass") {
454 split(p.getArg('[', ']'), opts, ',');
455 handle_opt(opts, known_fontsizes, h_paperfontsize);
456 handle_opt(opts, known_languages, h_language);
457 // delete "pt" at the end
458 string::size_type i = h_paperfontsize.find("pt");
459 if (i != string::npos)
460 h_paperfontsize.erase(i);
461 h_quotes_language = h_language;
462 h_options = join(opts, ",");
463 h_textclass = p.getArg('{', '}');
466 else if (t.cs() == "usepackage") {
467 string const options = p.getArg('[', ']');
468 string const name = p.getArg('{', '}');
469 if (options.empty() && name.find(',')) {
470 vector<string> vecnames;
471 split(name, vecnames, ',');
472 vector<string>::const_iterator it = vecnames.begin();
473 vector<string>::const_iterator end = vecnames.end();
474 for (; it != end; ++it)
475 handle_package(trim(*it), string());
477 handle_package(name, options);
481 else if (t.cs() == "newenvironment") {
482 string const name = p.getArg('{', '}');
484 ss << "\\newenvironment{" << name << "}";
487 ss << '{' << p.verbatim_item() << '}';
488 ss << '{' << p.verbatim_item() << '}';
489 if (name != "lyxcode" && name != "lyxlist" &&
490 name != "lyxrightadress" &&
491 name != "lyxaddress" && name != "lyxgreyedout")
492 h_preamble << ss.str();
495 else if (t.cs() == "def") {
496 string name = p.get_token().cs();
497 while (p.next_token().cat() != catBegin)
498 name += p.get_token().asString();
499 h_preamble << "\\def\\" << name << '{'
500 << p.verbatim_item() << "}";
503 else if (t.cs() == "newcolumntype") {
504 string const name = p.getArg('{', '}');
507 string opts = p.getOpt();
509 istringstream is(string(opts, 1));
510 //cerr << "opt: " << is.str() << "\n";
513 special_columns[name[0]] = nargs;
514 h_preamble << "\\newcolumntype{" << name << "}";
516 h_preamble << "[" << nargs << "]";
517 h_preamble << "{" << p.verbatim_item() << "}";
520 else if (t.cs() == "setcounter") {
521 string const name = p.getArg('{', '}');
522 string const content = p.getArg('{', '}');
523 if (name == "secnumdepth")
524 h_secnumdepth = content;
525 else if (name == "tocdepth")
526 h_tocdepth = content;
528 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
531 else if (t.cs() == "setlength") {
532 string const name = p.verbatim_item();
533 string const content = p.verbatim_item();
535 if (name == "parskip")
536 h_paragraph_separation = "skip";
537 else if (name == "parindent")
538 h_paragraph_separation = "skip";
540 h_preamble << "\\setlength{" << name << "}{" << content << "}";
543 else if (t.cs() == "begin") {
544 string const name = p.getArg('{', '}');
545 if (name == "document")
547 h_preamble << "\\begin{" << name << "}";
550 else if (t.cs() == "jurabibsetup") {
551 vector<string> jurabibsetup =
552 split_options(p.getArg('{', '}'));
553 // add jurabibsetup to the jurabib package options
554 add_package("jurabib", jurabibsetup);
555 if (!jurabibsetup.empty()) {
556 h_preamble << "\\jurabibsetup{"
557 << join(jurabibsetup, ",") << '}';
561 else if (!t.cs().empty())
562 h_preamble << '\\' << t.cs();
566 // Force textclass if the user wanted it
567 if (!forceclass.empty())
568 h_textclass = forceclass;
569 if (noweb_mode && !lyx::support::prefixIs(h_textclass, "literate-"))
570 h_textclass.insert(0, "literate-");
571 FileName layoutfilename = libFileSearch("layouts", h_textclass, "layout");
572 if (layoutfilename.empty()) {
573 cerr << "Error: Could not find layout file for textclass \"" << h_textclass << "\"." << endl;
577 textclass.read(layoutfilename);
578 if (h_papersides.empty()) {
580 ss << textclass.sides();
581 h_papersides = ss.str();
583 end_preamble(os, textclass);