]> git.lyx.org Git - lyx.git/blob - src/tex2lyx/preamble.C
5226dbbe1f0853c4e9bdc0b96012654ffc83654c
[lyx.git] / src / tex2lyx / preamble.C
1 /**
2  * \file preamble.C
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  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 // {[(
12
13 #include <config.h>
14
15 #include "tex2lyx.h"
16
17 #include "layout.h"
18 #include "lyxtextclass.h"
19 #include "lyxlex.h"
20 #include "support/filetools.h"
21
22 #include <algorithm>
23 #include <iostream>
24 #include <sstream>
25 #include <string>
26 #include <vector>
27 #include <map>
28
29 using std::istringstream;
30 using std::ostream;
31 using std::ostringstream;
32 using std::string;
33 using std::vector;
34 using std::cerr;
35 using std::endl;
36
37 using lyx::support::LibFileSearch;
38
39 // special columntypes
40 extern std::map<char, int> special_columns;
41
42 std::map<string, vector<string> > used_packages;
43
44 namespace {
45
46 const char * const known_languages[] = { "austrian", "babel", "bahasa",
47 "basque", "breton", "british", "bulgarian", "catalan", "croatian", "czech",
48 "danish", "dutch", "english", "esperanto", "estonian", "finnish",
49 "francais", "french", "frenchb", "frenchle", "frenchpro",
50 "galician", "german", "germanb", "greek", "hebcal", "hebfont",
51 "hebrew", "hebrew_newcode", "hebrew_oldcode", "hebrew_p", "hyphen",
52 "icelandic", "irish", "italian", "latin", "lgrcmr", "lgrcmro", "lgrcmss",
53 "lgrcmtt", "lgrenc", "lgrlcmss", "lgrlcmtt", "lheclas", "lhecmr",
54 "lhecmss", "lhecmtt", "lhecrml", "lheenc", "lhefr", "lheredis", "lheshold",
55 "lheshscr", "lheshstk", "lsorbian", "magyar", "naustrian", "ngermanb",
56 "ngerman", "norsk", "polish", "portuges", "rlbabel", "romanian",
57 "russianb", "samin", "scottish", "serbian", "slovak", "slovene", "spanish",
58 "swedish", "turkish", "ukraineb", "usorbian", "welsh", 0};
59
60 const char * const known_french_languages[] = {"french", "frenchb", "francais",
61                                                "frenchle", "frenchpro", 0};
62 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
63
64 // some ugly stuff
65 ostringstream h_preamble;
66 string h_textclass               = "article";
67 string h_options                 = string();
68 string h_language                = "english";
69 string h_inputencoding           = "latin1";
70 string h_fontscheme              = "default";
71 string h_graphics                = "default";
72 string h_paperfontsize           = "default";
73 string h_spacing                 = "single";
74 string h_papersize               = "default";
75 string h_use_geometry            = "false";
76 string h_use_amsmath             = "0";
77 string h_cite_engine             = "basic";
78 string h_use_bibtopic            = "false";
79 string h_paperorientation        = "portrait";
80 string h_secnumdepth             = "3";
81 string h_tocdepth                = "3";
82 string h_paragraph_separation    = "indent";
83 string h_defskip                 = "medskip";
84 string h_quotes_language         = "english";
85 string h_papercolumns            = "1";
86 string h_papersides              = string();
87 string h_paperpagestyle          = "default";
88 string h_tracking_changes        = "false";
89 string h_output_changes          = "false";
90
91
92 void handle_opt(vector<string> & opts, char const * const * what, string & target)
93 {
94         if (opts.empty())
95                 return;
96
97         for ( ; *what; ++what) {
98                 vector<string>::iterator it = find(opts.begin(), opts.end(), *what);
99                 if (it != opts.end()) {
100                         //cerr << "### found option '" << *what << "'\n";
101                         target = *what;
102                         opts.erase(it);
103                         return;
104                 }
105         }
106 }
107
108
109 /*!
110  * Split a package options string (keyval format) into a vector.
111  * Example input:
112  *   authorformat=smallcaps,
113  *   commabeforerest,
114  *   titleformat=colonsep,
115  *   bibformat={tabular,ibidem,numbered}
116  */
117 vector<string> split_options(string const & input)
118 {
119         vector<string> options;
120         string option;
121         Parser p(input);
122         while (p.good()) {
123                 Token const & t = p.get_token();
124                 if (t.asInput() == ",") {
125                         options.push_back(option);
126                         option.erase();
127                 } else if (t.asInput() == "=") {
128                         option += '=';
129                         p.skip_spaces(true);
130                         if (p.next_token().asInput() == "{")
131                                 option += '{' + p.getArg('{', '}') + '}';
132                 } else if (t.cat() != catSpace)
133                         option += t.asInput();
134         }
135
136         if (!option.empty())
137                 options.push_back(option);
138
139         return options;
140 }
141
142
143 /*!
144  * Add package \p name with options \p options to used_packages.
145  * Remove options from \p options that we don't want to output.
146  */
147 void add_package(string const & name, vector<string> & options)
148 {
149         // every package inherits the global options
150         if (used_packages.find(name) == used_packages.end())
151                 used_packages[name] = split_options(h_options);
152
153         vector<string> & v = used_packages[name];
154         v.insert(v.end(), options.begin(), options.end());
155         if (name == "jurabib") {
156                 // Don't output the order argument (see the cite command
157                 // handling code in text.C).
158                 vector<string>::iterator end =
159                         remove(options.begin(), options.end(), "natbiborder");
160                 end = remove(options.begin(), end, "jurabiborder");
161                 options.erase(end, options.end());
162         }
163 }
164
165
166 void handle_package(string const & name, string const & opts)
167 {
168         vector<string> options = split_options(opts);
169         add_package(name, options);
170
171         //cerr << "handle_package: '" << name << "'\n";
172         if (name == "ae")
173                 h_fontscheme = "ae";
174         else if (name == "aecompl")
175                 h_fontscheme = "ae";
176         else if (name == "amsmath")
177                 h_use_amsmath = "1";
178         else if (name == "amssymb")
179                 h_use_amsmath = "1";
180         else if (name == "babel")
181                 ; // ignore this
182         else if (name == "fontenc")
183                 ; // ignore this
184         else if (name == "inputenc") {
185                 h_inputencoding = opts;
186                 options.clear();
187         } else if (name == "makeidx")
188                 ; // ignore this
189         else if (name == "verbatim")
190                 ; // ignore this
191         else if (name == "graphicx")
192                 ; // ignore this
193         else if (is_known(name, known_languages)) {
194                 if (is_known(name, known_french_languages)) {
195                         h_language = "french";
196                         h_quotes_language = "french";
197                 } else {
198                         h_language = name;
199                         h_quotes_language = name;
200                 }
201
202         } else if (name == "natbib") {
203                 h_cite_engine = "natbib_authoryear";
204                 vector<string>::iterator it =
205                         find(options.begin(), options.end(), "authoryear");
206                 if (it != options.end())
207                         options.erase(it);
208                 else {
209                         it = find(options.begin(), options.end(), "numbers");
210                         if (it != options.end()) {
211                                 h_cite_engine = "natbib_numerical";
212                                 options.erase(it);
213                         }
214                 }
215         } else if (name == "jurabib") {
216                 h_cite_engine = "jurabib";
217         } else if (options.empty())
218                 h_preamble << "\\usepackage{" << name << "}\n";
219         else {
220                 h_preamble << "\\usepackage[" << opts << "]{" << name << "}\n";
221                 options.clear();
222         }
223
224         // We need to do something with the options...
225         if (!options.empty())
226                 cerr << "Ignoring options '" << join(options, ",")
227                      << "' of package " << name << '.' << endl;
228 }
229
230
231
232 void end_preamble(ostream & os, LyXTextClass const & /*textclass*/)
233 {
234         os << "#LyX file created by  tex2lyx 0.1.2\n"
235            << "\\lyxformat 245\n"
236            << "\\begin_document\n"
237            << "\\begin_header\n"
238            << "\\textclass " << h_textclass << "\n"
239            << "\\begin_preamble\n" << h_preamble.str() << "\n\\end_preamble\n";
240         if (!h_options.empty())
241            os << "\\options " << h_options << "\n";
242         os << "\\language " << h_language << "\n"
243            << "\\inputencoding " << h_inputencoding << "\n"
244            << "\\fontscheme " << h_fontscheme << "\n"
245            << "\\graphics " << h_graphics << "\n"
246            << "\\paperfontsize " << h_paperfontsize << "\n"
247            << "\\spacing " << h_spacing << "\n"
248            << "\\papersize " << h_papersize << "\n"
249            << "\\use_geometry " << h_use_geometry << "\n"
250            << "\\use_amsmath " << h_use_amsmath << "\n"
251            << "\\cite_engine " << h_cite_engine << "\n"
252            << "\\use_bibtopic " << h_use_bibtopic << "\n"
253            << "\\paperorientation " << h_paperorientation << "\n"
254            << "\\secnumdepth " << h_secnumdepth << "\n"
255            << "\\tocdepth " << h_tocdepth << "\n"
256            << "\\paragraph_separation " << h_paragraph_separation << "\n"
257            << "\\defskip " << h_defskip << "\n"
258            << "\\quotes_language " << h_quotes_language << "\n"
259            << "\\papercolumns " << h_papercolumns << "\n"
260            << "\\papersides " << h_papersides << "\n"
261            << "\\paperpagestyle " << h_paperpagestyle << "\n"
262            << "\\tracking_changes " << h_tracking_changes << "\n"
263            << "\\output_changes " << h_output_changes << "\n"
264            << "\\end_header\n\n"
265            << "\\begin_body\n";
266         // clear preamble for subdocuments
267         h_preamble.str("");
268 }
269
270 } // anonymous namespace
271
272 LyXTextClass const parse_preamble(Parser & p, ostream & os, string const & forceclass)
273 {
274         // initialize fixed types
275         special_columns['D'] = 3;
276         bool is_full_document = false;
277
278         // determine whether this is a full document or a fragment for inclusion
279         while (p.good()) {
280                 Token const & t = p.get_token();
281
282                 if (t.cat() == catEscape && t.cs() == "documentclass") {
283                         is_full_document = true;
284                         break;
285                 }
286         }
287         p.reset();
288
289         while (is_full_document && p.good()) {
290                 Token const & t = p.get_token();
291
292 #ifdef FILEDEBUG
293                 cerr << "t: " << t << "\n";
294 #endif
295
296                 //
297                 // cat codes
298                 //
299                 if (t.cat() == catLetter ||
300                           t.cat() == catSuper ||
301                           t.cat() == catSub ||
302                           t.cat() == catOther ||
303                           t.cat() == catMath ||
304                           t.cat() == catActive ||
305                           t.cat() == catBegin ||
306                           t.cat() == catEnd ||
307                           t.cat() == catAlign ||
308                           t.cat() == catParameter)
309                 h_preamble << t.character();
310
311                 else if (t.cat() == catSpace || t.cat() == catNewline)
312                         h_preamble << t.asInput();
313
314                 else if (t.cat() == catComment)
315                         h_preamble << t.asInput();
316
317                 else if (t.cs() == "pagestyle")
318                         h_paperpagestyle = p.verbatim_item();
319
320                 else if (t.cs() == "makeatletter") {
321                         p.setCatCode('@', catLetter);
322                         h_preamble << "\\makeatletter";
323                 }
324
325                 else if (t.cs() == "makeatother") {
326                         p.setCatCode('@', catOther);
327                         h_preamble << "\\makeatother";
328                 }
329
330                 else if (t.cs() == "newcommand" || t.cs() == "renewcommand"
331                             || t.cs() == "providecommand") {
332                         bool star = false;
333                         if (p.next_token().character() == '*') {
334                                 p.get_token();
335                                 star = true;
336                         }
337                         string const name = p.verbatim_item();
338                         string const opt1 = p.getOpt();
339                         string const opt2 = p.getFullOpt();
340                         string const body = p.verbatim_item();
341                         // only non-lyxspecific stuff
342                         if (   name != "\\noun"
343                             && name != "\\tabularnewline"
344                             && name != "\\LyX"
345                             && name != "\\lyxline"
346                             && name != "\\lyxaddress"
347                             && name != "\\lyxrightaddress"
348                             && name != "\\lyxdot"
349                             && name != "\\boldsymbol"
350                             && name != "\\lyxarrow") {
351                                 ostringstream ss;
352                                 ss << '\\' << t.cs();
353                                 if (star)
354                                         ss << '*';
355                                 ss << '{' << name << '}' << opt1 << opt2
356                                    << '{' << body << "}";
357                                 h_preamble << ss.str();
358
359                                 // Add the command to the known commands
360                                 add_known_command(name, opt1, !opt2.empty());
361 /*
362                                 ostream & out = in_preamble ? h_preamble : os;
363                                 out << "\\" << t.cs() << "{" << name << "}"
364                                     << opts << "{" << body << "}";
365 */
366                         }
367                 }
368
369                 else if (t.cs() == "documentclass") {
370                         vector<string> opts;
371                         split(p.getArg('[', ']'), opts, ',');
372                         handle_opt(opts, known_languages, h_language);
373                         if (is_known(h_language, known_french_languages))
374                                 h_language = "french";
375                         handle_opt(opts, known_fontsizes, h_paperfontsize);
376                         // delete "pt" at the end
377                         string::size_type i = h_paperfontsize.find("pt");
378                         if (i != string::npos)
379                                 h_paperfontsize.erase(i);
380                         h_quotes_language = h_language;
381                         h_options = join(opts, ",");
382                         h_textclass = p.getArg('{', '}');
383                 }
384
385                 else if (t.cs() == "usepackage") {
386                         string const options = p.getArg('[', ']');
387                         string const name = p.getArg('{', '}');
388                         if (options.empty() && name.find(',')) {
389                                 vector<string> vecnames;
390                                 split(name, vecnames, ',');
391                                 vector<string>::const_iterator it  = vecnames.begin();
392                                 vector<string>::const_iterator end = vecnames.end();
393                                 for (; it != end; ++it)
394                                         handle_package(trim(*it), string());
395                         } else {
396                                 handle_package(name, options);
397                         }
398                 }
399
400                 else if (t.cs() == "newenvironment") {
401                         string const name = p.getArg('{', '}');
402                         ostringstream ss;
403                         ss << "\\newenvironment{" << name << "}";
404                         ss << p.getOpt();
405                         ss << p.getOpt();
406                         ss << '{' << p.verbatim_item() << '}';
407                         ss << '{' << p.verbatim_item() << '}';
408                         if (name != "lyxcode" && name != "lyxlist" &&
409                             name != "lyxrightadress" && name != "lyxaddress")
410                                 h_preamble << ss.str();
411                 }
412
413                 else if (t.cs() == "def") {
414                         string name = p.get_token().cs();
415                         while (p.next_token().cat() != catBegin)
416                                 name += p.get_token().asString();
417                         h_preamble << "\\def\\" << name << '{'
418                                    << p.verbatim_item() << "}";
419                 }
420
421                 else if (t.cs() == "newcolumntype") {
422                         string const name = p.getArg('{', '}');
423                         trim(name);
424                         int nargs = 0;
425                         string opts = p.getOpt();
426                         if (!opts.empty()) {
427                                 istringstream is(string(opts, 1));
428                                 //cerr << "opt: " << is.str() << "\n";
429                                 is >> nargs;
430                         }
431                         special_columns[name[0]] = nargs;
432                         h_preamble << "\\newcolumntype{" << name << "}";
433                         if (nargs)
434                                 h_preamble << "[" << nargs << "]";
435                         h_preamble << "{" << p.verbatim_item() << "}";
436                 }
437
438                 else if (t.cs() == "setcounter") {
439                         string const name = p.getArg('{', '}');
440                         string const content = p.getArg('{', '}');
441                         if (name == "secnumdepth")
442                                 h_secnumdepth = content;
443                         else if (name == "tocdepth")
444                                 h_tocdepth = content;
445                         else
446                                 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
447                 }
448
449                 else if (t.cs() == "setlength") {
450                         string const name = p.verbatim_item();
451                         string const content = p.verbatim_item();
452                         // Is this correct?
453                         if (name == "parskip")
454                                 h_paragraph_separation = "skip";
455                         else if (name == "parindent")
456                                 h_paragraph_separation = "skip";
457                         else
458                                 h_preamble << "\\setlength{" << name << "}{" << content << "}";
459                 }
460
461                 else if (t.cs() == "begin") {
462                         string const name = p.getArg('{', '}');
463                         if (name == "document")
464                                 break;
465                         h_preamble << "\\begin{" << name << "}";
466                 }
467
468                 else if (t.cs() == "jurabibsetup") {
469                         vector<string> jurabibsetup =
470                                 split_options(p.getArg('{', '}'));
471                         // add jurabibsetup to the jurabib package options
472                         add_package("jurabib", jurabibsetup);
473                         if (!jurabibsetup.empty()) {
474                                 h_preamble << "\\jurabibsetup{"
475                                            << join(jurabibsetup, ",") << '}';
476                         }
477                 }
478
479                 else if (!t.cs().empty())
480                         h_preamble << '\\' << t.cs();
481         }
482         p.skip_spaces();
483
484         // Force textclass if the user wanted it
485         if (!forceclass.empty()) {
486                 h_textclass = forceclass;
487         }
488         string layoutfilename = LibFileSearch("layouts", h_textclass, "layout");
489         if (layoutfilename.empty()) {
490                 cerr << "Error: Could not find layout file for textclass \"" << h_textclass << "\"." << endl;
491                 exit(1);
492         }
493         LyXTextClass textclass;
494         textclass.Read(layoutfilename);
495         if (h_papersides.empty()) {
496                 ostringstream ss;
497                 ss << textclass.sides();
498                 h_papersides = ss.str();
499         }
500         end_preamble(os, textclass);
501         return textclass;
502 }
503
504 // }])