]> git.lyx.org Git - lyx.git/blob - src/tex2lyx/preamble.cpp
Increase tex2lyx output format to 416 (current trunk format).
[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         else if (name == "geometry")
600                 ; // Ignore this, the geometry settings are made by the \geometry
601                   // command. This command is handled below.
602
603         else if (is_known(name, known_languages))
604                 h_language = name;
605
606         else if (name == "natbib") {
607                 h_cite_engine = "natbib_authoryear";
608                 vector<string>::iterator it =
609                         find(options.begin(), options.end(), "authoryear");
610                 if (it != options.end())
611                         options.erase(it);
612                 else {
613                         it = find(options.begin(), options.end(), "numbers");
614                         if (it != options.end()) {
615                                 h_cite_engine = "natbib_numerical";
616                                 options.erase(it);
617                         }
618                 }
619         }
620
621         else if (name == "jurabib")
622                 h_cite_engine = "jurabib";
623
624         else if (name == "hyperref")
625                 handle_hyperref(options);
626
627         else if (!in_lyx_preamble) {
628                 if (options.empty())
629                         h_preamble << "\\usepackage{" << name << "}";
630                 else {
631                         h_preamble << "\\usepackage[" << opts << "]{" 
632                                    << name << "}";
633                         options.clear();
634                 }
635         }
636
637         // We need to do something with the options...
638         if (!options.empty())
639                 cerr << "Ignoring options '" << join(options, ",")
640                      << "' of package " << name << '.' << endl;
641
642         // remove the whitespace
643         p.skip_spaces();
644 }
645
646
647 void handle_if(Parser & p, bool in_lyx_preamble)
648 {
649         while (p.good()) {
650                 Token t = p.get_token();
651                 if (t.cat() == catEscape &&
652                     is_known(t.cs(), known_if_commands))
653                         handle_if(p, in_lyx_preamble);
654                 else {
655                         if (!in_lyx_preamble)
656                                 h_preamble << t.asInput();
657                         if (t.cat() == catEscape && t.cs() == "fi")
658                                 return;
659                 }
660         }
661 }
662
663
664 void end_preamble(ostream & os, TextClass const & /*textclass*/)
665 {
666         // translate from babel to LyX names
667         h_language = babel2lyx(h_language);
668
669         // set the quote language
670         // LyX only knows the following quotes languages:
671         // english, swedish, german, polish, french and danish
672         // (quotes for "japanese" and "chinese-traditional" are missing because
673         //  they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
674         // conversion list taken from
675         // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
676         // (quotes for kazakh and interlingua are unknown)
677         // danish
678         if (h_language == "danish")
679                 h_quotes_language = "danish";
680         // french
681         else if (is_known(h_language, known_french_quotes_languages))
682                 h_quotes_language = "french";
683         // german
684         else if (is_known(h_language, known_german_quotes_languages))
685                 h_quotes_language = "german";
686         // polish
687         else if (is_known(h_language, known_polish_quotes_languages))
688                 h_quotes_language = "polish";
689         // swedish
690         else if (is_known(h_language, known_swedish_quotes_languages))
691                 h_quotes_language = "swedish";
692         //english
693         else if (is_known(h_language, known_english_quotes_languages))
694                 h_quotes_language = "english";
695
696         // output the LyX file settings
697         os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
698            << "\\lyxformat " << LYX_FORMAT << '\n'
699            << "\\begin_document\n"
700            << "\\begin_header\n"
701            << "\\textclass " << h_textclass << "\n";
702         if (!h_preamble.str().empty())
703                 os << "\\begin_preamble\n" << h_preamble.str() << "\n\\end_preamble\n";
704         if (!h_options.empty())
705                 os << "\\options " << h_options << "\n";
706         os << "\\use_default_options " << h_use_default_options << "\n"
707            << modules_placeholder
708            << "\\language " << h_language << "\n"
709            << "\\language_package " << h_language_package << "\n"
710            << "\\inputencoding " << h_inputencoding << "\n"
711            << "\\fontencoding " << h_fontencoding << "\n"
712            << "\\font_roman " << h_font_roman << "\n"
713            << "\\font_sans " << h_font_sans << "\n"
714            << "\\font_typewriter " << h_font_typewriter << "\n"
715            << "\\font_default_family " << h_font_default_family << "\n"
716            << "\\font_sc " << h_font_sc << "\n"
717            << "\\font_osf " << h_font_osf << "\n"
718            << "\\font_sf_scale " << h_font_sf_scale << "\n"
719            << "\\font_tt_scale " << h_font_tt_scale << "\n"
720            << "\\graphics " << h_graphics << "\n";
721         if (!h_float_placement.empty())
722                 os << "\\float_placement " << h_float_placement << "\n";
723         os << "\\paperfontsize " << h_paperfontsize << "\n"
724            << "\\spacing " << h_spacing << "\n"
725            << "\\use_hyperref " << h_use_hyperref << '\n';
726         if (h_use_hyperref == "1") {
727                 if (!h_pdf_title.empty())
728                         os << "\\pdf_title \"" << h_pdf_title << "\"\n";
729                 if (!h_pdf_author.empty())
730                         os << "\\pdf_author \"" << h_pdf_author << "\"\n";
731                 if (!h_pdf_subject.empty())
732                         os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
733                 if (!h_pdf_keywords.empty())
734                         os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
735                 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
736                       "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
737                       "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
738                       "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
739                       "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
740                       "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
741                       "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
742                       "\\pdf_backref " << h_pdf_backref << "\n"
743                       "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
744                 if (!h_pdf_pagemode.empty())
745                         os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
746                 if (!h_pdf_quoted_options.empty())
747                         os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
748         }
749         os << "\\papersize " << h_papersize << "\n"
750            << "\\use_geometry " << h_use_geometry << "\n"
751            << "\\use_amsmath " << h_use_amsmath << "\n"
752            << "\\use_esint " << h_use_esint << "\n"
753            << "\\use_mhchem " << h_use_mhchem << "\n"
754            << "\\use_mathdots " << h_use_mathdots << "\n"
755            << "\\use_undertilde " << h_use_undertilde << "\n"
756            << "\\cite_engine " << h_cite_engine << "\n"
757            << "\\use_bibtopic " << h_use_bibtopic << "\n"
758            << "\\paperorientation " << h_paperorientation << '\n'
759            << "\\suppress_date " << h_suppress_date << '\n'
760            << "\\use_refstyle " << h_use_refstyle << '\n';
761         if (!h_notefontcolor.empty())
762                 os << "\\notefontcolor " << h_notefontcolor << '\n';
763         os << h_margins
764            << "\\secnumdepth " << h_secnumdepth << "\n"
765            << "\\tocdepth " << h_tocdepth << "\n"
766            << "\\paragraph_separation " << h_paragraph_separation << "\n";
767         if (h_paragraph_separation == "skip")
768                 os << "\\defskip " << h_defskip << "\n";
769         else
770                 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
771         os << "\\quotes_language " << h_quotes_language << "\n"
772            << "\\papercolumns " << h_papercolumns << "\n"
773            << "\\papersides " << h_papersides << "\n"
774            << "\\paperpagestyle " << h_paperpagestyle << "\n";
775         if (!h_listings_params.empty())
776                 os << "\\listings_params " << h_listings_params << "\n";
777         os << "\\tracking_changes " << h_tracking_changes << "\n"
778            << "\\output_changes " << h_output_changes << "\n"
779            << "\\html_math_output " << h_html_math_output << "\n"
780            << "\\html_css_as_file " << h_html_css_as_file << "\n"
781            << "\\html_be_strict " << h_html_be_strict << "\n"
782            << "\\end_header\n\n"
783            << "\\begin_body\n";
784         // clear preamble for subdocuments
785         h_preamble.str("");
786 }
787
788 } // anonymous namespace
789
790
791 void parse_preamble(Parser & p, ostream & os, 
792         string const & forceclass, TeX2LyXDocClass & tc)
793 {
794         // initialize fixed types
795         special_columns['D'] = 3;
796         bool is_full_document = false;
797         bool is_lyx_file = false;
798         bool in_lyx_preamble = false;
799
800         // determine whether this is a full document or a fragment for inclusion
801         while (p.good()) {
802                 Token const & t = p.get_token();
803
804                 if (t.cat() == catEscape && t.cs() == "documentclass") {
805                         is_full_document = true;
806                         break;
807                 }
808         }
809         p.reset();
810
811         while (is_full_document && p.good()) {
812                 Token const & t = p.get_token();
813
814 #ifdef FILEDEBUG
815                 cerr << "t: " << t << "\n";
816 #endif
817
818                 //
819                 // cat codes
820                 //
821                 if (!in_lyx_preamble &&
822                     (t.cat() == catLetter ||
823                      t.cat() == catSuper ||
824                      t.cat() == catSub ||
825                      t.cat() == catOther ||
826                      t.cat() == catMath ||
827                      t.cat() == catActive ||
828                      t.cat() == catBegin ||
829                      t.cat() == catEnd ||
830                      t.cat() == catAlign ||
831                      t.cat() == catParameter))
832                         h_preamble << t.cs();
833
834                 else if (!in_lyx_preamble && 
835                          (t.cat() == catSpace || t.cat() == catNewline))
836                         h_preamble << t.asInput();
837
838                 else if (t.cat() == catComment) {
839                         static regex const islyxfile("%% LyX .* created this file");
840                         static regex const usercommands("User specified LaTeX commands");
841
842                         string const comment = t.asInput();
843
844                         // magically switch encoding default if it looks like XeLaTeX
845                         static string const magicXeLaTeX =
846                                 "% This document must be compiled with XeLaTeX ";
847                         if (comment.size() > magicXeLaTeX.size() 
848                                   && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
849                                   && h_inputencoding == "auto") {
850                                 cerr << "XeLaTeX comment found, switching to UTF8\n";
851                                 h_inputencoding = "utf8";
852                         }
853                         smatch sub;
854                         if (regex_search(comment, sub, islyxfile)) {
855                                 is_lyx_file = true;
856                                 in_lyx_preamble = true;
857                         } else if (is_lyx_file
858                                    && regex_search(comment, sub, usercommands))
859                                 in_lyx_preamble = false;
860                         else if (!in_lyx_preamble)
861                                 h_preamble << t.asInput();
862                 }
863
864                 else if (t.cs() == "pagestyle")
865                         h_paperpagestyle = p.verbatim_item();
866
867                 else if (t.cs() == "makeatletter") {
868                         // LyX takes care of this
869                         p.setCatCode('@', catLetter);
870                 }
871
872                 else if (t.cs() == "makeatother") {
873                         // LyX takes care of this
874                         p.setCatCode('@', catOther);
875                 }
876
877                 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
878                       || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
879                       || t.cs() == "providecommand" || t.cs() == "providecommandx"
880                                 || t.cs() == "DeclareRobustCommand"
881                       || t.cs() == "DeclareRobustCommandx"
882                                 || t.cs() == "ProvideTextCommandDefault"
883                                 || t.cs() == "DeclareMathAccent") {
884                         bool star = false;
885                         if (p.next_token().character() == '*') {
886                                 p.get_token();
887                                 star = true;
888                         }
889                         string const name = p.verbatim_item();
890                         string const opt1 = p.getFullOpt();
891                         string const opt2 = p.getFullOpt();
892                         string const body = p.verbatim_item();
893                         // font settings
894                         if (name == "\\rmdefault")
895                                 if (is_known(body, known_roman_fonts))
896                                         h_font_roman = body;
897                         if (name == "\\sfdefault")
898                                 if (is_known(body, known_sans_fonts))
899                                         h_font_sans = body;
900                         if (name == "\\ttdefault")
901                                 if (is_known(body, known_typewriter_fonts))
902                                         h_font_typewriter = body;
903                         if (name == "\\familydefault") {
904                                 string family = body;
905                                 // remove leading "\"
906                                 h_font_default_family = family.erase(0,1);
907                         }
908
909                         // Add the command to the known commands
910                         add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
911
912                         // only non-lyxspecific stuff
913                         if (!in_lyx_preamble) {
914                                 ostringstream ss;
915                                 ss << '\\' << t.cs();
916                                 if (star)
917                                         ss << '*';
918                                 ss << '{' << name << '}' << opt1 << opt2
919                                    << '{' << body << "}";
920                                 h_preamble << ss.str();
921 /*
922                                 ostream & out = in_preamble ? h_preamble : os;
923                                 out << "\\" << t.cs() << "{" << name << "}"
924                                     << opts << "{" << body << "}";
925 */
926                         }
927                 }
928
929                 else if (t.cs() == "documentclass") {
930                         vector<string>::iterator it;
931                         vector<string> opts = split_options(p.getArg('[', ']'));
932                         handle_opt(opts, known_fontsizes, h_paperfontsize);
933                         delete_opt(opts, known_fontsizes);
934                         // delete "pt" at the end
935                         string::size_type i = h_paperfontsize.find("pt");
936                         if (i != string::npos)
937                                 h_paperfontsize.erase(i);
938                         // The documentclass options are always parsed before the options
939                         // of the babel call so that a language cannot overwrite the babel
940                         // options.
941                         handle_opt(opts, known_languages, h_language);
942                         delete_opt(opts, known_languages);
943
944                         // paper orientation
945                         if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
946                                 h_paperorientation = "landscape";
947                                 opts.erase(it);
948                         }
949                         // paper sides
950                         if ((it = find(opts.begin(), opts.end(), "oneside"))
951                                  != opts.end()) {
952                                 h_papersides = "1";
953                                 opts.erase(it);
954                         }
955                         if ((it = find(opts.begin(), opts.end(), "twoside"))
956                                  != opts.end()) {
957                                 h_papersides = "2";
958                                 opts.erase(it);
959                         }
960                         // paper columns
961                         if ((it = find(opts.begin(), opts.end(), "onecolumn"))
962                                  != opts.end()) {
963                                 h_papercolumns = "1";
964                                 opts.erase(it);
965                         }
966                         if ((it = find(opts.begin(), opts.end(), "twocolumn"))
967                                  != opts.end()) {
968                                 h_papercolumns = "2";
969                                 opts.erase(it);
970                         }
971                         // paper sizes
972                         // some size options are know to any document classes, other sizes
973                         // are handled by the \geometry command of the geometry package
974                         handle_opt(opts, known_class_paper_sizes, h_papersize);
975                         delete_opt(opts, known_class_paper_sizes);
976                         // the remaining options
977                         h_options = join(opts, ",");
978                         // FIXME This does not work for classes that have a
979                         //       different name in LyX than in LaTeX
980                         h_textclass = p.getArg('{', '}');
981                 }
982
983                 else if (t.cs() == "usepackage") {
984                         string const options = p.getArg('[', ']');
985                         string const name = p.getArg('{', '}');
986                         vector<string> vecnames;
987                         split(name, vecnames, ',');
988                         vector<string>::const_iterator it  = vecnames.begin();
989                         vector<string>::const_iterator end = vecnames.end();
990                         for (; it != end; ++it)
991                                 handle_package(p, trimSpaceAndEol(*it), options, 
992                                                in_lyx_preamble);
993                 }
994
995                 else if (t.cs() == "inputencoding") {
996                         string const encoding = p.getArg('{','}');
997                         h_inputencoding = encoding;
998                         p.setEncoding(encoding);
999                 }
1000
1001                 else if (t.cs() == "newenvironment") {
1002                         string const name = p.getArg('{', '}');
1003                         string const opt1 = p.getFullOpt();
1004                         string const opt2 = p.getFullOpt();
1005                         string const beg = p.verbatim_item();
1006                         string const end = p.verbatim_item();
1007                         if (!in_lyx_preamble) {
1008                                 h_preamble << "\\newenvironment{" << name
1009                                            << '}' << opt1 << opt2 << '{'
1010                                            << beg << "}{" << end << '}';
1011                         }
1012                         add_known_environment(name, opt1, !opt2.empty(),
1013                                               from_utf8(beg), from_utf8(end));
1014
1015                 }
1016
1017                 else if (t.cs() == "def") {
1018                         string name = p.get_token().cs();
1019                         while (p.next_token().cat() != catBegin)
1020                                 name += p.get_token().cs();
1021                         if (!in_lyx_preamble)
1022                                 h_preamble << "\\def\\" << name << '{'
1023                                            << p.verbatim_item() << "}";
1024                 }
1025
1026                 else if (t.cs() == "newcolumntype") {
1027                         string const name = p.getArg('{', '}');
1028                         trimSpaceAndEol(name);
1029                         int nargs = 0;
1030                         string opts = p.getOpt();
1031                         if (!opts.empty()) {
1032                                 istringstream is(string(opts, 1));
1033                                 is >> nargs;
1034                         }
1035                         special_columns[name[0]] = nargs;
1036                         h_preamble << "\\newcolumntype{" << name << "}";
1037                         if (nargs)
1038                                 h_preamble << "[" << nargs << "]";
1039                         h_preamble << "{" << p.verbatim_item() << "}";
1040                 }
1041
1042                 else if (t.cs() == "setcounter") {
1043                         string const name = p.getArg('{', '}');
1044                         string const content = p.getArg('{', '}');
1045                         if (name == "secnumdepth")
1046                                 h_secnumdepth = content;
1047                         else if (name == "tocdepth")
1048                                 h_tocdepth = content;
1049                         else
1050                                 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1051                 }
1052
1053                 else if (t.cs() == "setlength") {
1054                         string const name = p.verbatim_item();
1055                         string const content = p.verbatim_item();
1056                         // the paragraphs are only not indented when \parindent is set to zero
1057                         if (name == "\\parindent" && content != "") {
1058                                 if (content[0] == '0')
1059                                         h_paragraph_separation = "skip";
1060                                 else
1061                                         h_paragraph_indentation = translate_len(content);
1062                         } else if (name == "\\parskip") {
1063                                 if (content == "\\smallskipamount")
1064                                         h_defskip = "smallskip";
1065                                 else if (content == "\\medskipamount")
1066                                         h_defskip = "medskip";
1067                                 else if (content == "\\bigskipamount")
1068                                         h_defskip = "bigskip";
1069                                 else
1070                                         h_defskip = content;
1071                         } else
1072                                 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1073                 }
1074
1075                 else if (t.cs() == "onehalfspacing")
1076                         h_spacing = "onehalf";
1077
1078                 else if (t.cs() == "doublespacing")
1079                         h_spacing = "double";
1080
1081                 else if (t.cs() == "setstretch")
1082                         h_spacing = "other " + p.verbatim_item();
1083
1084                 else if (t.cs() == "begin") {
1085                         string const name = p.getArg('{', '}');
1086                         if (name == "document")
1087                                 break;
1088                         h_preamble << "\\begin{" << name << "}";
1089                 }
1090
1091                 else if (t.cs() == "geometry") {
1092                         h_use_geometry = "true";
1093                         vector<string> opts = split_options(p.getArg('{', '}'));
1094                         vector<string>::iterator it;
1095                         // paper orientation
1096                         if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1097                                 h_paperorientation = "landscape";
1098                                 opts.erase(it);
1099                         }
1100                         // paper size
1101                         handle_opt(opts, known_paper_sizes, h_papersize);
1102                         delete_opt(opts, known_paper_sizes);
1103                         // page margins
1104                         char const * const * margin = known_paper_margins;
1105                         int k = -1;
1106                         for (; *margin; ++margin) {
1107                                 k += 1;
1108                                 // search for the "=" in e.g. "lmargin=2cm" to get the value
1109                                 for(size_t i = 0; i != opts.size(); i++) {
1110                                         if (opts.at(i).find(*margin) != string::npos) {
1111                                                 string::size_type pos = opts.at(i).find("=");
1112                                                 string value = opts.at(i).substr(pos + 1);
1113                                                 string name = known_coded_paper_margins[k];
1114                                                 h_margins += "\\" + name + " " + value + "\n";
1115                                         }
1116                                 }
1117                         }
1118                 }
1119
1120                 else if (t.cs() == "definecolor") {
1121                         string const color = p.getArg('{', '}');
1122                         string const space = p.getArg('{', '}');
1123                         string const value = p.getArg('{', '}');
1124                         if (color == "note_fontcolor" && space == "rgb") {
1125                                 RGBColor c(RGBColorFromLaTeX(value));
1126                                 h_notefontcolor = X11hexname(c);
1127                         } else {
1128                                 h_preamble << "\\definecolor{" << color
1129                                            << "}{" << space << "}{" << value
1130                                            << '}';
1131                         }
1132                 }
1133
1134                 else if (t.cs() == "jurabibsetup") {
1135                         // FIXME p.getArg('{', '}') is most probably wrong (it
1136                         //       does not handle nested braces).
1137                         //       Use p.verbatim_item() instead.
1138                         vector<string> jurabibsetup =
1139                                 split_options(p.getArg('{', '}'));
1140                         // add jurabibsetup to the jurabib package options
1141                         add_package("jurabib", jurabibsetup);
1142                         if (!jurabibsetup.empty()) {
1143                                 h_preamble << "\\jurabibsetup{"
1144                                            << join(jurabibsetup, ",") << '}';
1145                         }
1146                 }
1147
1148                 else if (t.cs() == "hypersetup") {
1149                         vector<string> hypersetup =
1150                                 split_options(p.verbatim_item());
1151                         // add hypersetup to the hyperref package options
1152                         handle_hyperref(hypersetup);
1153                         if (!hypersetup.empty()) {
1154                                 h_preamble << "\\hypersetup{"
1155                                            << join(hypersetup, ",") << '}';
1156                         }
1157                 }
1158
1159                 else if (is_known(t.cs(), known_if_3arg_commands)) {
1160                         // prevent misparsing of \usepackage if it is used
1161                         // as an argument (see e.g. our own output of
1162                         // \@ifundefined above)
1163                         string const arg1 = p.verbatim_item();
1164                         string const arg2 = p.verbatim_item();
1165                         string const arg3 = p.verbatim_item();
1166                         if (!in_lyx_preamble) {
1167                                 h_preamble << t.asInput()
1168                                            << '{' << arg1 << '}'
1169                                            << '{' << arg2 << '}'
1170                                            << '{' << arg3 << '}';
1171                         }
1172                 }
1173
1174                 else if (is_known(t.cs(), known_if_commands)) {
1175                         // must not parse anything in conditional code, since
1176                         // LyX would output the parsed contents unconditionally
1177                         if (!in_lyx_preamble)
1178                                 h_preamble << t.asInput();
1179                         handle_if(p, in_lyx_preamble);
1180                 }
1181
1182                 else if (!t.cs().empty() && !in_lyx_preamble)
1183                         h_preamble << '\\' << t.cs();
1184         }
1185
1186         // remove the whitespace
1187         p.skip_spaces();
1188
1189         // Force textclass if the user wanted it
1190         if (!forceclass.empty())
1191                 h_textclass = forceclass;
1192         if (noweb_mode && !prefixIs(h_textclass, "literate-"))
1193                 h_textclass.insert(0, "literate-");
1194         tc.setName(h_textclass);
1195         if (!tc.load()) {
1196                 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1197                 exit(EXIT_FAILURE);
1198         }
1199         if (h_papersides.empty()) {
1200                 ostringstream ss;
1201                 ss << tc.sides();
1202                 h_papersides = ss.str();
1203         }
1204         end_preamble(os, tc);
1205 }
1206
1207
1208 /// translates a babel language name to a LyX language name
1209 string babel2lyx(string const & language)
1210 {
1211         char const * const * where = is_known(language, known_languages);
1212         if (where)
1213                 return known_coded_languages[where - known_languages];
1214         return language;
1215 }
1216
1217 // }])
1218
1219
1220 } // namespace lyx