]> git.lyx.org Git - lyx.git/blob - src/tex2lyx/preamble.cpp
More paper sizes for tex2lyx
[lyx.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 "LayoutFile.h"
19 #include "Layout.h"
20 #include "Lexer.h"
21 #include "TextClass.h"
22
23 #include "support/convert.h"
24 #include "support/FileName.h"
25 #include "support/filetools.h"
26 #include "support/lstrings.h"
27
28 #include "support/regex.h"
29
30 #include <algorithm>
31 #include <iostream>
32 #include <sstream>
33 #include <string>
34 #include <vector>
35 #include <map>
36
37 using namespace std;
38 using namespace lyx::support;
39
40
41 namespace lyx {
42
43 // special columntypes
44 extern map<char, int> special_columns;
45
46 map<string, vector<string> > used_packages;
47 const char * const modules_placeholder = "\001modules\001";
48
49 // needed to handle encodings with babel
50 bool one_language = true;
51 string h_inputencoding = "auto";
52 string h_paragraph_separation    = "indent";
53
54 namespace {
55
56 //add this to known_languages when updating to lyxformat 266:
57 // "armenian" (needs special handling since not supported by standard babel)
58 //add these to known_languages when updating to lyxformat 268:
59 //"chinese-simplified", "chinese-traditional", "japanese", "korean"
60 // Both changes require first that support for non-babel languages (CJK,
61 // armtex) is added.
62 // add turkmen for lyxformat 383
63 /**
64  * known babel language names (including synonyms)
65  * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
66  * not yet supported by LyX: kurmanji
67  * please keep this in sync with known_coded_languages line by line!
68  */
69 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
70 "american", "arabic", "arabtex", "austrian", "bahasa", "bahasai", "bahasam",
71 "basque", "belarusian", "brazil", "brazilian", "breton", "british", "bulgarian",
72 "canadian", "canadien", "catalan", "croatian", "czech", "danish", "dutch",
73 "english", "esperanto", "estonian", "farsi", "finnish", "francais", "french",
74 "frenchb", "frenchle", "frenchpro", "galician", "german", "germanb", "greek",
75 "hebrew", "hungarian", "icelandic", "indon", "indonesian", "interlingua",
76 "irish", "italian", "kazakh", "latin", "latvian", "lithuanian", "lowersorbian",
77 "lsorbian", "magyar", "malay", "meyalu", "mongolian", "naustrian", "newzealand",
78 "ngerman", "ngermanb", "norsk", "nynorsk", "polutonikogreek", "polish",
79 "portuges", "portuguese", "romanian", "russian", "russianb", "samin",
80 "scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish",
81 "swedish", "thai", "turkish", "turkmen", "ukraineb", "ukrainian",
82 "uppersorbian", "UKenglish", "USenglish", "usorbian", "vietnam", "welsh",
83 0};
84
85 /**
86  * the same as known_languages with .lyx names
87  * please keep this in sync with known_languages line by line!
88  */
89 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
90 "american", "arabic_arabi", "arabic_arabtex", "austrian", "bahasa", "bahasa", "bahasam",
91 "basque", "belarusian", "brazilian", "brazilian", "breton", "british", "bulgarian",
92 "canadian", "canadien", "catalan", "croatian", "czech", "danish", "dutch",
93 "english", "esperanto", "estonian", "farsi", "finnish", "french", "french",
94 "french", "french", "french", "galician", "german", "german", "greek",
95 "hebrew", "magyar", "icelandic", "bahasa", "bahasa", "interlingua",
96 "irish", "italian", "kazakh", "latin", "latvian", "lithuanian", "lowersorbian",
97 "lowersorbian", "magyar", "bahasam", "bahasam", "mongolian", "naustrian", "english",
98 "ngerman", "ngerman", "norsk", "nynorsk", "polutonikogreek", "polish",
99 "portuguese", "portuguese", "romanian", "russian", "russian", "samin",
100 "scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish",
101 "swedish", "thai", "turkish", "turkmen", "ukrainian", "ukrainian",
102 "uppersorbian", "uppersorbian", "english", "english", "vietnamese", "welsh",
103 0};
104
105 /// languages with english quotes (.lyx names)
106 const char * const known_english_quotes_languages[] = {"american", "bahasa",
107 "bahasam", "brazilian", "canadian", "chinese-simplified", "english",
108 "esperanto", "hebrew", "irish", "korean", "portuguese", "scottish", "thai", 0};
109
110 /// languages with french quotes (.lyx names)
111 const char * const known_french_quotes_languages[] = {"albanian",
112 "arabic_arabi", "arabic_arabtex", "basque", "canadien", "catalan", "french",
113 "galician", "greek", "italian", "norsk", "nynorsk", "polutonikogreek",
114 "russian", "spanish", "spanish-mexico", "turkish", "turkmen", "ukrainian",
115 "vietnamese", 0};
116
117 /// languages with german quotes (.lyx names)
118 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
119 "czech", "german", "icelandic", "lithuanian", "lowersorbian", "naustrian",
120 "ngerman", "serbian", "serbian-latin", "slovak", "slovene", "uppersorbian", 0};
121
122 /// languages with polish quotes (.lyx names)
123 const char * const known_polish_quotes_languages[] = {"afrikaans", "croatian",
124 "dutch", "estonian", "magyar", "polish", "romanian", 0};
125
126 /// languages with swedish quotes (.lyx names)
127 const char * const known_swedish_quotes_languages[] = {"finnish",
128 "swedish", 0};
129
130 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
131
132 const char * const known_roman_fonts[] = { "ae", "bookman", "charter",
133 "cmr", "fourier", "lmodern", "mathpazo", "mathptmx", "newcent", 0};
134
135 const char * const known_sans_fonts[] = { "avant", "berasans", "cmbr", "cmss",
136 "helvet", "lmss", 0};
137
138 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
139 "courier", "lmtt", "luximono", "fourier", "lmodern", "mathpazo", "mathptmx",
140 "newcent", 0};
141
142 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
143 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
144 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
145 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
146 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
147
148 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
149 "executivepaper", "legalpaper", "letterpaper", 0};
150
151 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin", 
152 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
153
154 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
155 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
156 "columnsep", 0};
157
158 /// commands that can start an \if...\else...\endif sequence
159 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
160 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
161 "ifsidecap", "ifupgreek", 0};
162
163 /// conditional commands with three arguments like \@ifundefined{}{}{}
164 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
165 0};
166
167 // default settings
168 ostringstream h_preamble;
169 string h_textclass               = "article";
170 string h_use_default_options     = "false";
171 string h_options;
172 string h_language                = "english";
173 string h_language_package        = "default";
174 string h_font_roman              = "default";
175 string h_font_sans               = "default";
176 string h_font_typewriter         = "default";
177 string h_font_default_family     = "default";
178 string h_font_sc                 = "false";
179 string h_font_osf                = "false";
180 string h_font_sf_scale           = "100";
181 string h_font_tt_scale           = "100";
182 string h_graphics                = "default";
183 string h_float_placement;
184 string h_paperfontsize           = "default";
185 string h_spacing                 = "single";
186 string h_use_hyperref            = "0";
187 string h_pdf_title;
188 string h_pdf_author;
189 string h_pdf_subject;
190 string h_pdf_keywords;
191 string h_pdf_bookmarks           = "1";
192 string h_pdf_bookmarksnumbered   = "0";
193 string h_pdf_bookmarksopen       = "0";
194 string h_pdf_bookmarksopenlevel  = "1";
195 string h_pdf_breaklinks          = "0";
196 string h_pdf_pdfborder           = "0";
197 string h_pdf_colorlinks          = "0";
198 string h_pdf_backref             = "section";
199 string h_pdf_pdfusetitle         = "1";
200 string h_pdf_pagemode;
201 string h_pdf_quoted_options;
202 string h_papersize               = "default";
203 string h_use_geometry            = "false";
204 string h_use_amsmath             = "1";
205 string h_use_esint               = "1";
206 string h_use_mhchem              = "0";
207 string h_use_mathdots            = "0";
208 string h_cite_engine             = "basic";
209 string h_use_bibtopic            = "false";
210 string h_paperorientation        = "portrait";
211 string h_suppress_date           = "false";
212 string h_use_refstyle            = "0";
213 string h_notefontcolor;
214 string h_secnumdepth             = "3";
215 string h_tocdepth                = "3";
216 string h_defskip                 = "medskip";
217 string h_paragraph_indentation   = "default";
218 string h_quotes_language         = "english";
219 string h_papercolumns            = "1";
220 string h_papersides;
221 string h_paperpagestyle          = "default";
222 string h_listings_params;
223 string h_tracking_changes        = "false";
224 string h_output_changes          = "false";
225 string h_html_math_output        = "0";
226 string h_html_css_as_file        = "0";
227 string h_html_be_strict          = "false";
228 string h_margins;
229
230
231 // returns true if at least one of the options in what has been found
232 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
233 {
234         if (opts.empty())
235                 return false;
236
237         bool found = false;
238         // the last language option is the document language (for babel and LyX)
239         // the last size option is the document font size
240         vector<string>::iterator it;
241         vector<string>::iterator position = opts.begin();
242         for (; *what; ++what) {
243                 it = find(opts.begin(), opts.end(), *what);
244                 if (it != opts.end()) {
245                         if (it >= position) {
246                                 found = true;
247                                 target = *what;
248                                 position = it;
249                         }
250                 }
251         }
252         return found;
253 }
254
255
256 void delete_opt(vector<string> & opts, char const * const * what)
257 {
258         if (opts.empty())
259                 return;
260
261         // remove found options from the list
262         // do this after handle_opt to avoid potential memory leaks
263         vector<string>::iterator it;
264         for (; *what; ++what) {
265                 it = find(opts.begin(), opts.end(), *what);
266                 if (it != opts.end())
267                         opts.erase(it);
268         }
269 }
270
271
272 /*!
273  * Split a package options string (keyval format) into a vector.
274  * Example input:
275  *   authorformat=smallcaps,
276  *   commabeforerest,
277  *   titleformat=colonsep,
278  *   bibformat={tabular,ibidem,numbered}
279  */
280 vector<string> split_options(string const & input)
281 {
282         vector<string> options;
283         string option;
284         Parser p(input);
285         while (p.good()) {
286                 Token const & t = p.get_token();
287                 if (t.asInput() == ",") {
288                         options.push_back(trimSpaceAndEol(option));
289                         option.erase();
290                 } else if (t.asInput() == "=") {
291                         option += '=';
292                         p.skip_spaces(true);
293                         if (p.next_token().asInput() == "{")
294                                 option += '{' + p.getArg('{', '}') + '}';
295                 } else if (t.cat() != catSpace)
296                         option += t.asInput();
297         }
298
299         if (!option.empty())
300                 options.push_back(trimSpaceAndEol(option));
301
302         return options;
303 }
304
305
306 /*!
307  * Retrieve a keyval option "name={value with=sign}" named \p name from
308  * \p options and return the value.
309  * The found option is also removed from \p options.
310  */
311 string process_keyval_opt(vector<string> & options, string name)
312 {
313         for (size_t i = 0; i < options.size(); ++i) {
314                 vector<string> option;
315                 split(options[i], option, '=');
316                 if (option.size() < 2)
317                         continue;
318                 if (option[0] == name) {
319                         options.erase(options.begin() + i);
320                         option.erase(option.begin());
321                         return join(option, "=");
322                 }
323         }
324         return "";
325 }
326
327
328 /*!
329  * Add package \p name with options \p options to used_packages.
330  * Remove options from \p options that we don't want to output.
331  */
332 void add_package(string const & name, vector<string> & options)
333 {
334         // every package inherits the global options
335         if (used_packages.find(name) == used_packages.end())
336                 used_packages[name] = split_options(h_options);
337
338         vector<string> & v = used_packages[name];
339         v.insert(v.end(), options.begin(), options.end());
340         if (name == "jurabib") {
341                 // Don't output the order argument (see the cite command
342                 // handling code in text.cpp).
343                 vector<string>::iterator end =
344                         remove(options.begin(), options.end(), "natbiborder");
345                 end = remove(options.begin(), end, "jurabiborder");
346                 options.erase(end, options.end());
347         }
348 }
349
350
351 // Given is a string like "scaled=0.9", return 0.9 * 100
352 string const scale_as_percentage(string const & scale)
353 {
354         string::size_type pos = scale.find('=');
355         if (pos != string::npos) {
356                 string value = scale.substr(pos + 1);
357                 if (isStrDbl(value))
358                         return convert<string>(100 * convert<double>(value));
359         }
360         // If the input string didn't match our expectations.
361         // return the default value "100"
362         return "100";
363 }
364
365
366 string remove_braces(string const & value)
367 {
368         if (value.empty())
369                 return value;
370         if (value[0] == '{' && value[value.length()-1] == '}')
371                 return value.substr(1, value.length()-2);
372         return value;
373 }
374
375
376 void handle_hyperref(vector<string> & options)
377 {
378         // FIXME swallow inputencoding changes that might surround the
379         //       hyperref setup if it was written by LyX
380         h_use_hyperref = "1";
381         // swallow "unicode=true", since LyX does always write that
382         vector<string>::iterator it =
383                 find(options.begin(), options.end(), "unicode=true");
384         if (it != options.end())
385                 options.erase(it);
386         it = find(options.begin(), options.end(), "pdfusetitle");
387         if (it != options.end()) {
388                 h_pdf_pdfusetitle = "1";
389                 options.erase(it);
390         }
391         string bookmarks = process_keyval_opt(options, "bookmarks");
392         if (bookmarks == "true")
393                 h_pdf_bookmarks = "1";
394         else if (bookmarks == "false")
395                 h_pdf_bookmarks = "0";
396         if (h_pdf_bookmarks == "1") {
397                 string bookmarksnumbered =
398                         process_keyval_opt(options, "bookmarksnumbered");
399                 if (bookmarksnumbered == "true")
400                         h_pdf_bookmarksnumbered = "1";
401                 else if (bookmarksnumbered == "false")
402                         h_pdf_bookmarksnumbered = "0";
403                 string bookmarksopen =
404                         process_keyval_opt(options, "bookmarksopen");
405                 if (bookmarksopen == "true")
406                         h_pdf_bookmarksopen = "1";
407                 else if (bookmarksopen == "false")
408                         h_pdf_bookmarksopen = "0";
409                 if (h_pdf_bookmarksopen == "1") {
410                         string bookmarksopenlevel =
411                                 process_keyval_opt(options, "bookmarksopenlevel");
412                         if (!bookmarksopenlevel.empty())
413                                 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
414                 }
415         }
416         string breaklinks = process_keyval_opt(options, "breaklinks");
417         if (breaklinks == "true")
418                 h_pdf_breaklinks = "1";
419         else if (breaklinks == "false")
420                 h_pdf_breaklinks = "0";
421         string pdfborder = process_keyval_opt(options, "pdfborder");
422         if (pdfborder == "{0 0 0}")
423                 h_pdf_pdfborder = "1";
424         else if (pdfborder == "{0 0 1}")
425                 h_pdf_pdfborder = "0";
426         string backref = process_keyval_opt(options, "backref");
427         if (!backref.empty())
428                 h_pdf_backref = backref;
429         string colorlinks = process_keyval_opt(options, "colorlinks");
430         if (colorlinks == "true")
431                 h_pdf_colorlinks = "1";
432         else if (colorlinks == "false")
433                 h_pdf_colorlinks = "0";
434         string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
435         if (!pdfpagemode.empty())
436                 h_pdf_pagemode = pdfpagemode;
437         string pdftitle = process_keyval_opt(options, "pdftitle");
438         if (!pdftitle.empty()) {
439                 h_pdf_title = remove_braces(pdftitle);
440         }
441         string pdfauthor = process_keyval_opt(options, "pdfauthor");
442         if (!pdfauthor.empty()) {
443                 h_pdf_author = remove_braces(pdfauthor);
444         }
445         string pdfsubject = process_keyval_opt(options, "pdfsubject");
446         if (!pdfsubject.empty())
447                 h_pdf_subject = remove_braces(pdfsubject);
448         string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
449         if (!pdfkeywords.empty())
450                 h_pdf_keywords = remove_braces(pdfkeywords);
451         if (!options.empty()) {
452                 if (!h_pdf_quoted_options.empty())
453                         h_pdf_quoted_options += ',';
454                 h_pdf_quoted_options += join(options, ",");
455                 options.clear();
456         }
457 }
458
459
460 void handle_package(Parser &p, string const & name, string const & opts,
461                     bool in_lyx_preamble)
462 {
463         vector<string> options = split_options(opts);
464         add_package(name, options);
465         string scale;
466
467         // roman fonts
468         if (is_known(name, known_roman_fonts)) {
469                 h_font_roman = name;
470                 p.skip_spaces();
471         }
472
473         if (name == "fourier") {
474                 h_font_roman = "utopia";
475                 // when font uses real small capitals
476                 if (opts == "expert")
477                         h_font_sc = "true";
478         }
479
480         if (name == "mathpazo")
481                 h_font_roman = "palatino";
482
483         if (name == "mathptmx")
484                 h_font_roman = "times";
485
486         // sansserif fonts
487         if (is_known(name, known_sans_fonts)) {
488                 h_font_sans = name;
489                 if (!opts.empty()) {
490                         scale = opts;
491                         h_font_sf_scale = scale_as_percentage(scale);
492                 }
493         }
494
495         // typewriter fonts
496         if (is_known(name, known_typewriter_fonts)) {
497                 h_font_typewriter = name;
498                 if (!opts.empty()) {
499                         scale = opts;
500                         h_font_tt_scale = scale_as_percentage(scale);
501                 }
502         }
503
504         // font uses old-style figure
505         if (name == "eco")
506                 h_font_osf = "true";
507
508         else if (name == "amsmath" || name == "amssymb")
509                 h_use_amsmath = "2";
510
511         else if (name == "esint")
512                 h_use_esint = "2";
513
514         else if (name == "mhchem")
515                 h_use_mhchem = "2";
516
517         else if (name == "mathdots")
518                 h_use_mathdots = "2";
519
520         else if (name == "babel" && !opts.empty()) {
521                 // check if more than one option was used - used later for inputenc
522                 // in case inputenc is parsed before babel, set the encoding to auto
523                 if (options.begin() != options.end() - 1) {
524                         one_language = false;
525                         h_inputencoding = "auto";
526                 }
527                 // babel takes the last language of the option of its \usepackage
528                 // call as document language. If there is no such language option, the
529                 // last language in the documentclass options is used.
530                 handle_opt(options, known_languages, h_language);
531                 delete_opt(options, known_languages);
532         }
533
534         else if (name == "fontenc")
535                  ;// ignore this
536
537         else if (name == "inputenc" || name == "luainputenc") {
538                 // h_inputencoding is only set when there is not more than one
539                 // inputenc option because otherwise h_inputencoding must be
540                 // set to "auto" (the default encoding of the document language)
541                 // Therefore check for the "," character.
542                 // It is also only set when there is not more then one babel
543                 // language option but this is handled in the routine for babel.
544                 if (opts.find(",") == string::npos && one_language == true)
545                         h_inputencoding = opts;
546                 if (!options.empty())
547                         p.setEncoding(options.back());
548                 options.clear();
549         }
550
551         else if (name == "makeidx")
552                 ; // ignore this
553
554         else if (name == "prettyref")
555                 ; // ignore this
556
557         else if (name == "varioref")
558                 ; // ignore this
559
560         else if (name == "verbatim")
561                 ; // ignore this
562
563         else if (name == "nomencl")
564                 ; // ignore this
565
566         else if (name == "textcomp")
567                 ; // ignore this
568
569         else if (name == "url")
570                 ; // ignore this
571
572         else if (name == "subscript")
573                 ; // ignore this
574
575         else if (name == "color") {
576                 // with the following command this package is only loaded when needed for
577                 // undefined colors, since we only support the predefined colors
578                 h_preamble << "\\@ifundefined{definecolor}\n {\\usepackage{color}}{}\n";
579         }
580
581         else if (name == "graphicx")
582                 ; // ignore this
583
584         else if (name == "setspace")
585                 ; // ignore this
586
587         else if (name == "geometry")
588                 ; // Ignore this, the geometry settings are made by the \geometry
589                   // command. This command is handled below.
590
591         else if (is_known(name, known_languages))
592                 h_language = name;
593
594         else if (name == "natbib") {
595                 h_cite_engine = "natbib_authoryear";
596                 vector<string>::iterator it =
597                         find(options.begin(), options.end(), "authoryear");
598                 if (it != options.end())
599                         options.erase(it);
600                 else {
601                         it = find(options.begin(), options.end(), "numbers");
602                         if (it != options.end()) {
603                                 h_cite_engine = "natbib_numerical";
604                                 options.erase(it);
605                         }
606                 }
607         }
608
609         else if (name == "jurabib")
610                 h_cite_engine = "jurabib";
611
612         else if (name == "hyperref")
613                 handle_hyperref(options);
614
615         else if (!in_lyx_preamble) {
616                 if (options.empty())
617                         h_preamble << "\\usepackage{" << name << "}";
618                 else {
619                         h_preamble << "\\usepackage[" << opts << "]{" 
620                                    << name << "}";
621                         options.clear();
622                 }
623         }
624
625         // We need to do something with the options...
626         if (!options.empty())
627                 cerr << "Ignoring options '" << join(options, ",")
628                      << "' of package " << name << '.' << endl;
629
630         // remove the whitespace
631         p.skip_spaces();
632 }
633
634
635 void handle_if(Parser & p, bool in_lyx_preamble)
636 {
637         while (p.good()) {
638                 Token t = p.get_token();
639                 if (t.cat() == catEscape &&
640                     is_known(t.cs(), known_if_commands))
641                         handle_if(p, in_lyx_preamble);
642                 else {
643                         if (!in_lyx_preamble)
644                                 h_preamble << t.asInput();
645                         if (t.cat() == catEscape && t.cs() == "fi")
646                                 return;
647                 }
648         }
649 }
650
651
652 void end_preamble(ostream & os, TextClass const & /*textclass*/)
653 {
654         // translate from babel to LyX names
655         h_language = babel2lyx(h_language);
656
657         // set the quote language
658         // LyX only knows the following quotes languages:
659         // english, swedish, german, polish, french and danish
660         // (quotes for "japanese" and "chinese-traditional" are missing because
661         //  they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
662         // conversion list taken from
663         // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
664         // (quotes for kazakh and interlingua are unknown)
665         // danish
666         if (h_language == "danish")
667                 h_quotes_language = "danish";
668         // french
669         else if (is_known(h_language, known_french_quotes_languages))
670                 h_quotes_language = "french";
671         // german
672         else if (is_known(h_language, known_german_quotes_languages))
673                 h_quotes_language = "german";
674         // polish
675         else if (is_known(h_language, known_polish_quotes_languages))
676                 h_quotes_language = "polish";
677         // swedish
678         else if (is_known(h_language, known_swedish_quotes_languages))
679                 h_quotes_language = "swedish";
680         //english
681         else if (is_known(h_language, known_english_quotes_languages))
682                 h_quotes_language = "english";
683
684         // output the LyX file settings
685         os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
686            << "\\lyxformat " << LYX_FORMAT << '\n'
687            << "\\begin_document\n"
688            << "\\begin_header\n"
689            << "\\textclass " << h_textclass << "\n";
690         if (!h_preamble.str().empty())
691                 os << "\\begin_preamble\n" << h_preamble.str() << "\n\\end_preamble\n";
692         if (!h_options.empty())
693                 os << "\\options " << h_options << "\n";
694         os << "\\use_default_options " << h_use_default_options << "\n"
695            << modules_placeholder
696            << "\\language " << h_language << "\n"
697            << "\\language_package " << h_language_package << "\n"
698            << "\\inputencoding " << h_inputencoding << "\n"
699            << "\\font_roman " << h_font_roman << "\n"
700            << "\\font_sans " << h_font_sans << "\n"
701            << "\\font_typewriter " << h_font_typewriter << "\n"
702            << "\\font_default_family " << h_font_default_family << "\n"
703            << "\\font_sc " << h_font_sc << "\n"
704            << "\\font_osf " << h_font_osf << "\n"
705            << "\\font_sf_scale " << h_font_sf_scale << "\n"
706            << "\\font_tt_scale " << h_font_tt_scale << "\n"
707            << "\\graphics " << h_graphics << "\n";
708         if (!h_float_placement.empty())
709                 os << "\\float_placement " << h_float_placement << "\n";
710         os << "\\paperfontsize " << h_paperfontsize << "\n"
711            << "\\spacing " << h_spacing << "\n"
712            << "\\use_hyperref " << h_use_hyperref << '\n';
713         if (h_use_hyperref == "1") {
714                 if (!h_pdf_title.empty())
715                         os << "\\pdf_title \"" << h_pdf_title << "\"\n";
716                 if (!h_pdf_author.empty())
717                         os << "\\pdf_author \"" << h_pdf_author << "\"\n";
718                 if (!h_pdf_subject.empty())
719                         os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
720                 if (!h_pdf_keywords.empty())
721                         os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
722                 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
723                       "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
724                       "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
725                       "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
726                       "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
727                       "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
728                       "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
729                       "\\pdf_backref " << h_pdf_backref << "\n"
730                       "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
731                 if (!h_pdf_pagemode.empty())
732                         os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
733                 if (!h_pdf_quoted_options.empty())
734                         os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
735         }
736         os << "\\papersize " << h_papersize << "\n"
737            << "\\use_geometry " << h_use_geometry << "\n"
738            << "\\use_amsmath " << h_use_amsmath << "\n"
739            << "\\use_esint " << h_use_esint << "\n"
740            << "\\use_mhchem " << h_use_mhchem << "\n"
741            << "\\use_mathdots " << h_use_mathdots << "\n"
742            << "\\cite_engine " << h_cite_engine << "\n"
743            << "\\use_bibtopic " << h_use_bibtopic << "\n"
744            << "\\paperorientation " << h_paperorientation << '\n'
745            << "\\suppress_date " << h_suppress_date << '\n'
746            << "\\use_refstyle " << h_use_refstyle << '\n';
747         if (!h_notefontcolor.empty())
748                 os << "\\notefontcolor " << h_notefontcolor << '\n';
749         os << h_margins
750            << "\\secnumdepth " << h_secnumdepth << "\n"
751            << "\\tocdepth " << h_tocdepth << "\n"
752            << "\\paragraph_separation " << h_paragraph_separation << "\n";
753         if (h_paragraph_separation == "skip")
754                 os << "\\defskip " << h_defskip << "\n";
755         else
756                 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
757         os << "\\quotes_language " << h_quotes_language << "\n"
758            << "\\papercolumns " << h_papercolumns << "\n"
759            << "\\papersides " << h_papersides << "\n"
760            << "\\paperpagestyle " << h_paperpagestyle << "\n";
761         if (!h_listings_params.empty())
762                 os << "\\listings_params " << h_listings_params << "\n";
763         os << "\\tracking_changes " << h_tracking_changes << "\n"
764            << "\\output_changes " << h_output_changes << "\n"
765            << "\\html_math_output " << h_html_math_output << "\n"
766            << "\\html_css_as_file " << h_html_css_as_file << "\n"
767            << "\\html_be_strict " << h_html_be_strict << "\n"
768            << "\\end_header\n\n"
769            << "\\begin_body\n";
770         // clear preamble for subdocuments
771         h_preamble.str("");
772 }
773
774 } // anonymous namespace
775
776
777 void parse_preamble(Parser & p, ostream & os, 
778         string const & forceclass, TeX2LyXDocClass & tc)
779 {
780         // initialize fixed types
781         special_columns['D'] = 3;
782         bool is_full_document = false;
783         bool is_lyx_file = false;
784         bool in_lyx_preamble = false;
785
786         // determine whether this is a full document or a fragment for inclusion
787         while (p.good()) {
788                 Token const & t = p.get_token();
789
790                 if (t.cat() == catEscape && t.cs() == "documentclass") {
791                         is_full_document = true;
792                         break;
793                 }
794         }
795         p.reset();
796
797         while (is_full_document && p.good()) {
798                 Token const & t = p.get_token();
799
800 #ifdef FILEDEBUG
801                 cerr << "t: " << t << "\n";
802 #endif
803
804                 //
805                 // cat codes
806                 //
807                 if (!in_lyx_preamble &&
808                     (t.cat() == catLetter ||
809                      t.cat() == catSuper ||
810                      t.cat() == catSub ||
811                      t.cat() == catOther ||
812                      t.cat() == catMath ||
813                      t.cat() == catActive ||
814                      t.cat() == catBegin ||
815                      t.cat() == catEnd ||
816                      t.cat() == catAlign ||
817                      t.cat() == catParameter))
818                         h_preamble << t.cs();
819
820                 else if (!in_lyx_preamble && 
821                          (t.cat() == catSpace || t.cat() == catNewline))
822                         h_preamble << t.asInput();
823
824                 else if (t.cat() == catComment) {
825                         static regex const islyxfile("%% LyX .* created this file");
826                         static regex const usercommands("User specified LaTeX commands");
827
828                         string const comment = t.asInput();
829
830                         // magically switch encoding default if it looks like XeLaTeX
831                         static string const magicXeLaTeX =
832                                 "% This document must be compiled with XeLaTeX ";
833                         if (comment.size() > magicXeLaTeX.size() 
834                                   && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
835                                   && h_inputencoding == "auto") {
836                                 cerr << "XeLaTeX comment found, switching to UTF8\n";
837                                 h_inputencoding = "utf8";
838                         }
839                         smatch sub;
840                         if (regex_search(comment, sub, islyxfile)) {
841                                 is_lyx_file = true;
842                                 in_lyx_preamble = true;
843                         } else if (is_lyx_file
844                                    && regex_search(comment, sub, usercommands))
845                                 in_lyx_preamble = false;
846                         else if (!in_lyx_preamble)
847                                 h_preamble << t.asInput();
848                 }
849
850                 else if (t.cs() == "pagestyle")
851                         h_paperpagestyle = p.verbatim_item();
852
853                 else if (t.cs() == "makeatletter") {
854                         // LyX takes care of this
855                         p.setCatCode('@', catLetter);
856                 }
857
858                 else if (t.cs() == "makeatother") {
859                         // LyX takes care of this
860                         p.setCatCode('@', catOther);
861                 }
862
863                 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
864                       || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
865                       || t.cs() == "providecommand" || t.cs() == "providecommandx"
866                                 || t.cs() == "DeclareRobustCommand"
867                       || t.cs() == "DeclareRobustCommandx"
868                                 || t.cs() == "ProvideTextCommandDefault"
869                                 || t.cs() == "DeclareMathAccent") {
870                         bool star = false;
871                         if (p.next_token().character() == '*') {
872                                 p.get_token();
873                                 star = true;
874                         }
875                         string const name = p.verbatim_item();
876                         string const opt1 = p.getFullOpt();
877                         string const opt2 = p.getFullOpt();
878                         string const body = p.verbatim_item();
879                         // font settings
880                         if (name == "\\rmdefault")
881                                 if (is_known(body, known_roman_fonts))
882                                         h_font_roman = body;
883                         if (name == "\\sfdefault")
884                                 if (is_known(body, known_sans_fonts))
885                                         h_font_sans = body;
886                         if (name == "\\ttdefault")
887                                 if (is_known(body, known_typewriter_fonts))
888                                         h_font_typewriter = body;
889                         if (name == "\\familydefault") {
890                                 string family = body;
891                                 // remove leading "\"
892                                 h_font_default_family = family.erase(0,1);
893                         }
894
895                         // Add the command to the known commands
896                         add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
897
898                         // only non-lyxspecific stuff
899                         if (!in_lyx_preamble) {
900                                 ostringstream ss;
901                                 ss << '\\' << t.cs();
902                                 if (star)
903                                         ss << '*';
904                                 ss << '{' << name << '}' << opt1 << opt2
905                                    << '{' << body << "}";
906                                 h_preamble << ss.str();
907 /*
908                                 ostream & out = in_preamble ? h_preamble : os;
909                                 out << "\\" << t.cs() << "{" << name << "}"
910                                     << opts << "{" << body << "}";
911 */
912                         }
913                 }
914
915                 else if (t.cs() == "documentclass") {
916                         vector<string>::iterator it;
917                         vector<string> opts = split_options(p.getArg('[', ']'));
918                         handle_opt(opts, known_fontsizes, h_paperfontsize);
919                         delete_opt(opts, known_fontsizes);
920                         // delete "pt" at the end
921                         string::size_type i = h_paperfontsize.find("pt");
922                         if (i != string::npos)
923                                 h_paperfontsize.erase(i);
924                         // The documentclass options are always parsed before the options
925                         // of the babel call so that a language cannot overwrite the babel
926                         // options.
927                         handle_opt(opts, known_languages, h_language);
928                         delete_opt(opts, known_languages);
929
930                         // paper orientation
931                         if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
932                                 h_paperorientation = "landscape";
933                                 opts.erase(it);
934                         }
935                         // paper sides
936                         if ((it = find(opts.begin(), opts.end(), "oneside"))
937                                  != opts.end()) {
938                                 h_papersides = "1";
939                                 opts.erase(it);
940                         }
941                         if ((it = find(opts.begin(), opts.end(), "twoside"))
942                                  != opts.end()) {
943                                 h_papersides = "2";
944                                 opts.erase(it);
945                         }
946                         // paper columns
947                         if ((it = find(opts.begin(), opts.end(), "onecolumn"))
948                                  != opts.end()) {
949                                 h_papercolumns = "1";
950                                 opts.erase(it);
951                         }
952                         if ((it = find(opts.begin(), opts.end(), "twocolumn"))
953                                  != opts.end()) {
954                                 h_papercolumns = "2";
955                                 opts.erase(it);
956                         }
957                         // paper sizes
958                         // some size options are know to any document classes, other sizes
959                         // are handled by the \geometry command of the geometry package
960                         handle_opt(opts, known_class_paper_sizes, h_papersize);
961                         delete_opt(opts, known_class_paper_sizes);
962                         // the remaining options
963                         h_options = join(opts, ",");
964                         // FIXME This does not work for classes that have a
965                         //       different name in LyX than in LaTeX
966                         h_textclass = p.getArg('{', '}');
967                 }
968
969                 else if (t.cs() == "usepackage") {
970                         string const options = p.getArg('[', ']');
971                         string const name = p.getArg('{', '}');
972                         vector<string> vecnames;
973                         split(name, vecnames, ',');
974                         vector<string>::const_iterator it  = vecnames.begin();
975                         vector<string>::const_iterator end = vecnames.end();
976                         for (; it != end; ++it)
977                                 handle_package(p, trimSpaceAndEol(*it), options, 
978                                                in_lyx_preamble);
979                 }
980
981                 else if (t.cs() == "inputencoding") {
982                         string const encoding = p.getArg('{','}');
983                         h_inputencoding = encoding;
984                         p.setEncoding(encoding);
985                 }
986
987                 else if (t.cs() == "newenvironment") {
988                         string const name = p.getArg('{', '}');
989                         string const opt1 = p.getFullOpt();
990                         string const opt2 = p.getFullOpt();
991                         string const beg = p.verbatim_item();
992                         string const end = p.verbatim_item();
993                         if (!in_lyx_preamble) {
994                                 h_preamble << "\\newenvironment{" << name
995                                            << '}' << opt1 << opt2 << '{'
996                                            << beg << "}{" << end << '}';
997                         }
998                         add_known_environment(name, opt1, !opt2.empty(),
999                                               from_utf8(beg), from_utf8(end));
1000
1001                 }
1002
1003                 else if (t.cs() == "def") {
1004                         string name = p.get_token().cs();
1005                         while (p.next_token().cat() != catBegin)
1006                                 name += p.get_token().cs();
1007                         if (!in_lyx_preamble)
1008                                 h_preamble << "\\def\\" << name << '{'
1009                                            << p.verbatim_item() << "}";
1010                 }
1011
1012                 else if (t.cs() == "newcolumntype") {
1013                         string const name = p.getArg('{', '}');
1014                         trimSpaceAndEol(name);
1015                         int nargs = 0;
1016                         string opts = p.getOpt();
1017                         if (!opts.empty()) {
1018                                 istringstream is(string(opts, 1));
1019                                 is >> nargs;
1020                         }
1021                         special_columns[name[0]] = nargs;
1022                         h_preamble << "\\newcolumntype{" << name << "}";
1023                         if (nargs)
1024                                 h_preamble << "[" << nargs << "]";
1025                         h_preamble << "{" << p.verbatim_item() << "}";
1026                 }
1027
1028                 else if (t.cs() == "setcounter") {
1029                         string const name = p.getArg('{', '}');
1030                         string const content = p.getArg('{', '}');
1031                         if (name == "secnumdepth")
1032                                 h_secnumdepth = content;
1033                         else if (name == "tocdepth")
1034                                 h_tocdepth = content;
1035                         else
1036                                 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1037                 }
1038
1039                 else if (t.cs() == "setlength") {
1040                         string const name = p.verbatim_item();
1041                         string const content = p.verbatim_item();
1042                         // the paragraphs are only not indented when \parindent is set to zero
1043                         if (name == "\\parindent" && content != "") {
1044                                 if (content[0] == '0')
1045                                         h_paragraph_separation = "skip";
1046                                 else
1047                                         h_paragraph_indentation = translate_len(content);
1048                         } else if (name == "\\parskip") {
1049                                 if (content == "\\smallskipamount")
1050                                         h_defskip = "smallskip";
1051                                 else if (content == "\\medskipamount")
1052                                         h_defskip = "medskip";
1053                                 else if (content == "\\bigskipamount")
1054                                         h_defskip = "bigskip";
1055                                 else
1056                                         h_defskip = content;
1057                         } else
1058                                 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1059                 }
1060
1061                 else if (t.cs() == "onehalfspacing")
1062                         h_spacing = "onehalf";
1063
1064                 else if (t.cs() == "doublespacing")
1065                         h_spacing = "double";
1066
1067                 else if (t.cs() == "setstretch")
1068                         h_spacing = "other " + p.verbatim_item();
1069
1070                 else if (t.cs() == "begin") {
1071                         string const name = p.getArg('{', '}');
1072                         if (name == "document")
1073                                 break;
1074                         h_preamble << "\\begin{" << name << "}";
1075                 }
1076
1077                 else if (t.cs() == "geometry") {
1078                         h_use_geometry = "true";
1079                         vector<string> opts = split_options(p.getArg('{', '}'));
1080                         vector<string>::iterator it;
1081                         // paper orientation
1082                         if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1083                                 h_paperorientation = "landscape";
1084                                 opts.erase(it);
1085                         }
1086                         // paper size
1087                         handle_opt(opts, known_paper_sizes, h_papersize);
1088                         delete_opt(opts, known_paper_sizes);
1089                         // page margins
1090                         char const * const * margin = known_paper_margins;
1091                         int k = -1;
1092                         for (; *margin; ++margin) {
1093                                 k += 1;
1094                                 // search for the "=" in e.g. "lmargin=2cm" to get the value
1095                                 for(size_t i = 0; i != opts.size(); i++) {
1096                                         if (opts.at(i).find(*margin) != string::npos) {
1097                                                 string::size_type pos = opts.at(i).find("=");
1098                                                 string value = opts.at(i).substr(pos + 1);
1099                                                 string name = known_coded_paper_margins[k];
1100                                                 h_margins += "\\" + name + " " + value + "\n";
1101                                         }
1102                                 }
1103                         }
1104                 }
1105
1106                 else if (t.cs() == "definecolor") {
1107                         string const color = p.getArg('{', '}');
1108                         string const space = p.getArg('{', '}');
1109                         string const value = p.getArg('{', '}');
1110                         if (color == "note_fontcolor" && space == "rgb") {
1111                                 RGBColor c(RGBColorFromLaTeX(value));
1112                                 h_notefontcolor = X11hexname(c);
1113                         } else {
1114                                 h_preamble << "\\definecolor{" << color
1115                                            << "}{" << space << "}{" << value
1116                                            << '}';
1117                         }
1118                 }
1119
1120                 else if (t.cs() == "jurabibsetup") {
1121                         // FIXME p.getArg('{', '}') is most probably wrong (it
1122                         //       does not handle nested braces).
1123                         //       Use p.verbatim_item() instead.
1124                         vector<string> jurabibsetup =
1125                                 split_options(p.getArg('{', '}'));
1126                         // add jurabibsetup to the jurabib package options
1127                         add_package("jurabib", jurabibsetup);
1128                         if (!jurabibsetup.empty()) {
1129                                 h_preamble << "\\jurabibsetup{"
1130                                            << join(jurabibsetup, ",") << '}';
1131                         }
1132                 }
1133
1134                 else if (t.cs() == "hypersetup") {
1135                         vector<string> hypersetup =
1136                                 split_options(p.verbatim_item());
1137                         // add hypersetup to the hyperref package options
1138                         handle_hyperref(hypersetup);
1139                         if (!hypersetup.empty()) {
1140                                 h_preamble << "\\hypersetup{"
1141                                            << join(hypersetup, ",") << '}';
1142                         }
1143                 }
1144
1145                 else if (is_known(t.cs(), known_if_3arg_commands)) {
1146                         // prevent misparsing of \usepackage if it is used
1147                         // as an argument (see e.g. our own output of
1148                         // \@ifundefined above)
1149                         string const arg1 = p.verbatim_item();
1150                         string const arg2 = p.verbatim_item();
1151                         string const arg3 = p.verbatim_item();
1152                         if (!in_lyx_preamble) {
1153                                 h_preamble << t.asInput()
1154                                            << '{' << arg1 << '}'
1155                                            << '{' << arg2 << '}'
1156                                            << '{' << arg3 << '}';
1157                         }
1158                 }
1159
1160                 else if (is_known(t.cs(), known_if_commands)) {
1161                         // must not parse anything in conditional code, since
1162                         // LyX would output the parsed contents unconditionally
1163                         if (!in_lyx_preamble)
1164                                 h_preamble << t.asInput();
1165                         handle_if(p, in_lyx_preamble);
1166                 }
1167
1168                 else if (!t.cs().empty() && !in_lyx_preamble)
1169                         h_preamble << '\\' << t.cs();
1170         }
1171
1172         // remove the whitespace
1173         p.skip_spaces();
1174
1175         // Force textclass if the user wanted it
1176         if (!forceclass.empty())
1177                 h_textclass = forceclass;
1178         if (noweb_mode && !prefixIs(h_textclass, "literate-"))
1179                 h_textclass.insert(0, "literate-");
1180         tc.setName(h_textclass);
1181         if (!tc.load()) {
1182                 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1183                 exit(EXIT_FAILURE);
1184         }
1185         if (h_papersides.empty()) {
1186                 ostringstream ss;
1187                 ss << tc.sides();
1188                 h_papersides = ss.str();
1189         }
1190         end_preamble(os, tc);
1191 }
1192
1193
1194 /// translates a babel language name to a LyX language name
1195 string babel2lyx(string const & language)
1196 {
1197         char const * const * where = is_known(language, known_languages);
1198         if (where)
1199                 return known_coded_languages[where - known_languages];
1200         return language;
1201 }
1202
1203 // }])
1204
1205
1206 } // namespace lyx