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