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