]> git.lyx.org Git - features.git/blob - src/tex2lyx/preamble.cpp
77f72cdb315eaac7172cebebd3aef71b63094e3d
[features.git] / src / tex2lyx / preamble.cpp
1 /**
2  * \file preamble.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author André Pönitz
7  * \author Uwe Stöhr
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 // {[(
13
14 #include <config.h>
15
16 #include "tex2lyx.h"
17
18 #include "Layout.h"
19 #include "Lexer.h"
20 #include "TextClass.h"
21 #include "support/filetools.h"
22 #include "support/lstrings.h"
23
24 #include <algorithm>
25 #include <iostream>
26 #include <sstream>
27 #include <string>
28 #include <vector>
29 #include <map>
30
31
32 namespace lyx {
33
34 using std::find;
35 using std::istringstream;
36 using std::ostream;
37 using std::ostringstream;
38 using std::string;
39 using std::vector;
40 using std::cerr;
41 using std::endl;
42 using std::find;
43
44 using support::FileName;
45 using support::libFileSearch;
46
47 // special columntypes
48 extern std::map<char, int> special_columns;
49
50 std::map<string, vector<string> > used_packages;
51
52 namespace {
53
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};
65
66 const char * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
67
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};
73
74 const char * const known_roman_fonts[] = { "ae", "bookman", "charter",
75 "cmr", "fourier", "lmodern", "mathpazo", "mathptmx", "newcent", 0};
76
77 const char * const known_sans_fonts[] = { "avant", "berasans", "cmbr", "cmss",
78 "helvet", "lmss", 0};
79
80 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
81 "courier", "lmtt", "luximono", "fourier", "lmodern", "mathpazo", "mathptmx",
82 "newcent", 0};
83
84 // some ugly stuff
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";
119
120
121 void handle_opt(vector<string> & opts, char const * const * what, string & target)
122 {
123         if (opts.empty())
124                 return;
125
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";
131                         target = *what;
132                         opts.erase(it);
133                 }
134         }
135 }
136
137
138
139 /*!
140  * Split a package options string (keyval format) into a vector.
141  * Example input:
142  *   authorformat=smallcaps,
143  *   commabeforerest,
144  *   titleformat=colonsep,
145  *   bibformat={tabular,ibidem,numbered}
146  */
147 vector<string> split_options(string const & input)
148 {
149         vector<string> options;
150         string option;
151         Parser p(input);
152         while (p.good()) {
153                 Token const & t = p.get_token();
154                 if (t.asInput() == ",") {
155                         options.push_back(option);
156                         option.erase();
157                 } else if (t.asInput() == "=") {
158                         option += '=';
159                         p.skip_spaces(true);
160                         if (p.next_token().asInput() == "{")
161                                 option += '{' + p.getArg('{', '}') + '}';
162                 } else if (t.cat() != catSpace)
163                         option += t.asInput();
164         }
165
166         if (!option.empty())
167                 options.push_back(option);
168
169         return options;
170 }
171
172
173 /*!
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.
176  */
177 void add_package(string const & name, vector<string> & options)
178 {
179         // every package inherits the global options
180         if (used_packages.find(name) == used_packages.end())
181                 used_packages[name] = split_options(h_options);
182
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());
192         }
193 }
194
195
196 void handle_package(string const & name, string const & opts)
197 {
198         vector<string> options = split_options(opts);
199         add_package(name, options);
200         size_t pos;
201         string scale;
202
203         // cerr << "handle_package: '" << name << "'\n";
204
205         // roman fonts
206         if (is_known(name, known_roman_fonts))
207                 h_font_roman = name;
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";
214         // sansserif fonts
215         if (is_known(name, known_sans_fonts)) {
216                 h_font_sans = name;
217                 if (!opts.empty()) {
218                         scale = opts;
219                         pos = scale.find(".", 0);
220                         h_font_sf_scale = scale.erase(0, pos + 1);
221                 }
222         }
223         // typewriter fonts
224         if (is_known(name, known_typewriter_fonts)) {
225                 h_font_typewriter = name;
226                 if (!opts.empty()) {
227                         scale = opts;
228                         pos = scale.find(".", 0);
229                         h_font_tt_scale = scale.erase(0, pos + 1);
230                 }
231         }
232
233         else if (name == "amssymb")
234                 h_use_amsmath = "2";
235         else if (name == "esint")
236                 h_use_esint = "2";
237         else if (name == "babel")
238                 ; // ignore this
239         else if (name == "fontenc")
240                 ; // ignore this
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;
246                 options.clear();
247         } else if (name == "makeidx")
248                 ; // ignore this
249         else if (name == "verbatim")
250                 ; // ignore this
251         else if (name == "graphicx")
252                 ; // ignore this
253         else if (is_known(name, known_languages)) {
254                 h_language = name;
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())
261                         options.erase(it);
262                 else {
263                         it = find(options.begin(), options.end(), "numbers");
264                         if (it != options.end()) {
265                                 h_cite_engine = "natbib_numerical";
266                                 options.erase(it);
267                         }
268                 }
269         } else if (name == "jurabib") {
270                 h_cite_engine = "jurabib";
271         } else if (options.empty())
272                 h_preamble << "\\usepackage{" << name << "}\n";
273         else {
274                 h_preamble << "\\usepackage[" << opts << "]{" << name << "}\n";
275                 options.clear();
276         }
277
278         // We need to do something with the options...
279         if (!options.empty())
280                 cerr << "Ignoring options '" << join(options, ",")
281                      << "' of package " << name << '.' << endl;
282 }
283
284
285 void end_preamble(ostream & os, TextClass const & /*textclass*/)
286 {
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"
328            << "\\begin_body\n";
329         // clear preamble for subdocuments
330         h_preamble.str("");
331 }
332
333 } // anonymous namespace
334
335
336 TextClass const parse_preamble(Parser & p, ostream & os, string const & forceclass)
337 {
338         // initialize fixed types
339         special_columns['D'] = 3;
340         bool is_full_document = false;
341
342         // determine whether this is a full document or a fragment for inclusion
343         while (p.good()) {
344                 Token const & t = p.get_token();
345
346                 if (t.cat() == catEscape && t.cs() == "documentclass") {
347                         is_full_document = true;
348                         break;
349                 }
350         }
351         p.reset();
352
353         while (is_full_document && p.good()) {
354                 Token const & t = p.get_token();
355
356 #ifdef FILEDEBUG
357                 cerr << "t: " << t << "\n";
358 #endif
359
360                 //
361                 // cat codes
362                 //
363                 if (t.cat() == catLetter ||
364                           t.cat() == catSuper ||
365                           t.cat() == catSub ||
366                           t.cat() == catOther ||
367                           t.cat() == catMath ||
368                           t.cat() == catActive ||
369                           t.cat() == catBegin ||
370                           t.cat() == catEnd ||
371                           t.cat() == catAlign ||
372                           t.cat() == catParameter)
373                 ;//h_preamble << t.character();
374
375                 else if (t.cat() == catSpace || t.cat() == catNewline)
376                         ;//h_preamble << t.asInput();
377
378                 else if (t.cat() == catComment)
379                         ;//h_preamble << t.asInput();
380
381                 else if (t.cs() == "pagestyle")
382                         h_paperpagestyle = p.verbatim_item();
383
384                 else if (t.cs() == "makeatletter")
385                         p.setCatCode('@', catLetter);
386
387                 else if (t.cs() == "makeatother")
388                         p.setCatCode('@', catOther);
389
390                 else if (t.cs() == "newcommand" || t.cs() == "renewcommand"
391                             || t.cs() == "providecommand") {
392                         bool star = false;
393                         if (p.next_token().character() == '*') {
394                                 p.get_token();
395                                 star = true;
396                         }
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();
401                         // font settings
402                         if (name == "\\rmdefault")
403                                 if (is_known(body, known_roman_fonts))
404                                         h_font_roman = body;
405
406                         if (name == "\\sfdefault")
407                                 if (is_known(body, known_sans_fonts))
408                                         h_font_sans = body;
409
410                         if (name == "\\ttdefault")
411                                 if (is_known(body, known_typewriter_fonts))
412                                         h_font_typewriter = body;
413
414                         if (name == "\\familydefault") {
415                                 string family = body;
416                                 // remove leading "\"
417                                 h_font_default_family = family.erase(0,1);
418                         }
419                         // only non-lyxspecific stuff
420                         if (   name != "\\noun"
421                             && name != "\\tabularnewline"
422                             && name != "\\LyX"
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") {
433                                 ostringstream ss;
434                                 ss << '\\' << t.cs();
435                                 if (star)
436                                         ss << '*';
437                                 ss << '{' << name << '}' << opt1 << opt2
438                                    << '{' << body << "}";
439                                 h_preamble << ss.str();
440
441                                 // Add the command to the known commands
442                                 add_known_command(name, opt1, !opt2.empty());
443 /*
444                                 ostream & out = in_preamble ? h_preamble : os;
445                                 out << "\\" << t.cs() << "{" << name << "}"
446                                     << opts << "{" << body << "}";
447 */
448                         }
449
450                 }
451
452                 else if (t.cs() == "documentclass") {
453                         vector<string> opts;
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('{', '}');
464                 }
465
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());
476                         } else {
477                                 handle_package(name, options);
478                         }
479                 }
480
481                 else if (t.cs() == "newenvironment") {
482                         string const name = p.getArg('{', '}');
483                         ostringstream ss;
484                         ss << "\\newenvironment{" << name << "}";
485                         ss << p.getOpt();
486                         ss << p.getOpt();
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();
493                 }
494
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() << "}";
501                 }
502
503                 else if (t.cs() == "newcolumntype") {
504                         string const name = p.getArg('{', '}');
505                         trim(name);
506                         int nargs = 0;
507                         string opts = p.getOpt();
508                         if (!opts.empty()) {
509                                 istringstream is(string(opts, 1));
510                                 //cerr << "opt: " << is.str() << "\n";
511                                 is >> nargs;
512                         }
513                         special_columns[name[0]] = nargs;
514                         h_preamble << "\\newcolumntype{" << name << "}";
515                         if (nargs)
516                                 h_preamble << "[" << nargs << "]";
517                         h_preamble << "{" << p.verbatim_item() << "}";
518                 }
519
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;
527                         else
528                                 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
529                 }
530
531                 else if (t.cs() == "setlength") {
532                         string const name = p.verbatim_item();
533                         string const content = p.verbatim_item();
534                         // Is this correct?
535                         if (name == "parskip")
536                                 h_paragraph_separation = "skip";
537                         else if (name == "parindent")
538                                 h_paragraph_separation = "skip";
539                         else
540                                 h_preamble << "\\setlength{" << name << "}{" << content << "}";
541                 }
542
543                 else if (t.cs() == "begin") {
544                         string const name = p.getArg('{', '}');
545                         if (name == "document")
546                                 break;
547                         h_preamble << "\\begin{" << name << "}";
548                 }
549
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, ",") << '}';
558                         }
559                 }
560
561                 else if (!t.cs().empty())
562                         h_preamble << '\\' << t.cs();
563         }
564         p.skip_spaces();
565
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;
574                 exit(1);
575         }
576         TextClass textclass;
577         textclass.read(layoutfilename);
578         if (h_papersides.empty()) {
579                 ostringstream ss;
580                 ss << textclass.sides();
581                 h_papersides = ss.str();
582         }
583         end_preamble(os, textclass);
584         return textclass;
585 }
586
587
588 // }])
589
590
591 } // namespace lyx