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