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 "Preamble.h"
17 #include "tex2lyx.h"
18
19 #include "Encoding.h"
20 #include "LayoutFile.h"
21 #include "Layout.h"
22 #include "Lexer.h"
23 #include "TextClass.h"
24 #include "version.h"
25
26 #include "support/convert.h"
27 #include "support/FileName.h"
28 #include "support/filetools.h"
29 #include "support/lstrings.h"
30
31 #include "support/regex.h"
32
33 #include <algorithm>
34 #include <iostream>
35
36 using namespace std;
37 using namespace lyx::support;
38
39
40 namespace lyx {
41
42 Preamble preamble;
43
44 namespace {
45
46 // CJK languages are handled in text.cpp, polyglossia languages are listed
47 // further down.
48 /**
49 * known babel language names (including synonyms)
50 * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
51 * please keep this in sync with known_coded_languages line by line!
52 */
53 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
54 "american", "arabic", "arabtex", "australian", "austrian", "bahasa", "bahasai",
55 "bahasam", "basque", "belarusian", "bosnian", "brazil", "brazilian", "breton", "british",
56 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
57 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "francais",
58 "french", "frenchb", "frenchle", "frenchpro", "friulan", "galician", "german", "germanb",
59 "georgian", "greek", "hebrew", "hungarian", "icelandic", "indon", "indonesian",
60 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
61 "latvian", "lithuanian", "lowersorbian", "lsorbian", "macedonian", "magyar", "malay", "meyalu",
62 "mongolian", "naustrian", "newzealand", "ngerman", "ngermanb", "norsk", "nswissgerman",
63 "nynorsk", "piedmontese", "polutonikogreek", "polish", "portuges", "portuguese",
64 "romanian", "romansh", "russian", "russianb", "samin", "scottish", "serbian", "serbian-latin",
65 "slovak", "slovene", "spanish", "swedish", "swissgerman", "thai", "turkish", "turkmen",
66 "ukraineb", "ukrainian", "uppersorbian", "UKenglish", "USenglish", "usorbian",
67 "vietnam", "welsh",
68 0};
69
70 /**
71 * the same as known_languages with .lyx names
72 * please keep this in sync with known_languages line by line!
73 */
74 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
75 "american", "arabic_arabi", "arabic_arabtex", "australian", "austrian", "bahasa", "bahasa",
76 "bahasam", "basque", "belarusian", "bosnian", "brazilian", "brazilian", "breton", "british",
77 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
78 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "french",
79 "french", "french", "french", "french", "friulan", "galician", "german", "german",
80 "georgian", "greek", "hebrew", "magyar", "icelandic", "bahasa", "bahasa",
81 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
82 "latvian", "lithuanian", "lowersorbian", "lowersorbian", "macedonian", "magyar", "bahasam", "bahasam",
83 "mongolian", "naustrian", "newzealand", "ngerman", "ngerman", "norsk", "german-ch",
84 "nynorsk", "piedmontese", "polutonikogreek", "polish", "portuguese", "portuguese",
85 "romanian", "romansh", "russian", "russian", "samin", "scottish", "serbian", "serbian-latin",
86 "slovak", "slovene", "spanish", "swedish", "german-ch-old", "thai", "turkish", "turkmen",
87 "ukrainian", "ukrainian", "uppersorbian", "english", "english", "uppersorbian",
88 "vietnamese", "welsh",
89 0};
90
91 /// languages with british quotes (.lyx names)
92 const char * const known_british_quotes_languages[] = {"british", "welsh", 0};
93
94 /// languages with cjk quotes (.lyx names)
95 const char * const known_cjk_quotes_languages[] = {"chinese-traditional",
96 "japanese", "japanese-cjk", 0};
97
98 /// languages with cjk-angle quotes (.lyx names)
99 const char * const known_cjkangle_quotes_languages[] = {"korean", 0};
100
101 /// languages with danish quotes (.lyx names)
102 const char * const known_danish_quotes_languages[] = {"danish", 0};
103
104 /// languages with english quotes (.lyx names)
105 const char * const known_english_quotes_languages[] = {"american", "australian",
106 "bahasa", "bahasam", "brazilian", "canadian", "chinese-simplified", "english",
107 "esperanto", "farsi", "interlingua", "irish", "newzealand", "scottish",
108 "thai", "turkish", "vietnamese", 0};
109
110 /// languages with french quotes (.lyx names)
111 const char * const known_french_quotes_languages[] = {"ancientgreek",
112 "arabic_arabi", "arabic_arabtex", "asturian", "belarusian", "breton",
113 "canadien", "catalan", "french", "friulan", "galician", "italian", "occitan",
114 "piedmontese", "portuguese", "spanish", "spanish-mexico", 0};
115
116 /// languages with german quotes (.lyx names)
117 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
118 "czech", "estonian", "georgian", "german", "icelandic", "latvian", "lithuanian",
119 "lowersorbian", "macedonian", "naustrian", "ngerman", "romansh", "slovak", "slovene",
120 "uppersorbian", 0};
121
122 /// languages with polish quotes (.lyx names)
123 const char * const known_polish_quotes_languages[] = {"afrikaans", "bosnian", "croatian",
124 "dutch", "magyar", "polish", "romanian", "serbian", "serbian-latin", 0};
125
126 /// languages with russian quotes (.lyx names)
127 const char * const known_russian_quotes_languages[] = {"russian", "ukrainian", 0};
128
129 /// languages with swedish quotes (.lyx names)
130 const char * const known_swedish_quotes_languages[] = {"finnish", "swedish", 0};
131
132 /// languages with swiss quotes (.lyx names)
133 const char * const known_swiss_quotes_languages[] = {"albanian",
134 "armenian", "basque", "german-ch", "german-ch-old",
135 "norsk", "nynorsk", "turkmen", "ukrainian", "vietnamese", 0};
136
137 /// known language packages from the times before babel
138 const char * const known_old_language_packages[] = {"french", "frenchle",
139 "frenchpro", "german", "ngerman", "pmfrench", 0};
140
141 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
142
143 const char * const known_roman_fonts[] = { "ae", "beraserif", "bookman",
144 "ccfonts", "chancery", "charter", "cmr", "cochineal", "crimson", "fourier",
145 "garamondx", "libertine", "libertineRoman", "libertine-type1", "lmodern", "mathdesign",
146 "mathpazo", "mathptmx", "MinionPro", "newcent", "NotoSerif-TLF", "tgbonum", "tgchorus",
147 "tgpagella", "tgschola", "tgtermes", "utopia", 0 };
148
149 const char * const known_sans_fonts[] = { "avant", "berasans", "biolinum",
150 "biolinum-type1", "cmbr", "cmss", "helvet", "iwona", "iwonac", "iwonal", "iwonalc",
151 "kurier", "kurierc", "kurierl", "kurierlc", "lmss", "NotoSans-TLF",
152 "tgadventor", "tgheros", "uop", 0 };
153
154 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
155 "courier", "lmtt", "luximono", "fourier", "libertineMono", "libertineMono-type1", "lmodern",
156 "mathpazo", "mathptmx", "newcent", "NotoMono-TLF", "tgcursor", "txtt", 0 };
157
158 const char * const known_math_fonts[] = { "eulervm", "newtxmath", 0};
159
160 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
161 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
162 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
163 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
164 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
165
166 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
167 "executivepaper", "legalpaper", "letterpaper", 0};
168
169 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
170 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
171
172 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
173 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
174 "columnsep", 0};
175
176 /// commands that can start an \if...\else...\endif sequence
177 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
178 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
179 "ifsidecap", "ifupgreek", 0};
180
181 const char * const known_basic_colors[] = {"black", "blue", "brown", "cyan",
182 "darkgray", "gray", "green", "lightgray", "lime", "magenta", "orange", "olive",
183 "pink", "purple", "red", "teal", "violet", "white", "yellow", 0};
184
185 const char * const known_basic_color_codes[] = {"#000000", "#0000ff", "#964B00", "#00ffff",
186 "#a9a9a9", "#808080", "#00ff00", "#d3d3d3", "#bfff00", "#ff00ff", "#ff7f00", "#808000",
187 "#ffc0cb", "#800080", "#ff0000", "#008080", "#8f00ff", "#ffffff", "#ffff00", 0};
188
189 /// conditional commands with three arguments like \@ifundefined{}{}{}
190 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
191 0};
192
193 /*!
194 * Known file extensions for TeX files as used by \\includeonly
195 */
196 char const * const known_tex_extensions[] = {"tex", 0};
197
198 /// packages that work only in xetex
199 /// polyglossia is handled separately
200 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
201 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
202 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
203
204 /// packages that are automatically skipped if loaded by LyX
205 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
206 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color",
207 "float", "fontspec", "framed", "graphicx", "hhline", "ifthen", "longtable",
208 "makeidx", "minted", "multirow", "nomencl", "pdfpages", "prettyref", "refstyle",
209 "rotating", "rotfloat", "splitidx", "setspace", "subscript", "textcomp", "tipa",
210 "tipx", "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xcolor",
211 "xunicode", 0};
212
213 // codes used to remove packages that are loaded automatically by LyX.
214 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
215 const char package_beg_sep = '\001';
216 const char package_mid_sep = '\002';
217 const char package_end_sep = '\003';
218
219
220 // returns true if at least one of the options in what has been found
handle_opt(vector<string> & opts,char const * const * what,string & target)221 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
222 {
223 if (opts.empty())
224 return false;
225
226 bool found = false;
227 // the last language option is the document language (for babel and LyX)
228 // the last size option is the document font size
229 vector<string>::iterator it;
230 vector<string>::iterator position = opts.begin();
231 for (; *what; ++what) {
232 it = find(opts.begin(), opts.end(), *what);
233 if (it != opts.end()) {
234 if (it >= position) {
235 found = true;
236 target = *what;
237 position = it;
238 }
239 }
240 }
241 return found;
242 }
243
244
delete_opt(vector<string> & opts,char const * const * what)245 void delete_opt(vector<string> & opts, char const * const * what)
246 {
247 if (opts.empty())
248 return;
249
250 // remove found options from the list
251 // do this after handle_opt to avoid potential memory leaks
252 vector<string>::iterator it;
253 for (; *what; ++what) {
254 it = find(opts.begin(), opts.end(), *what);
255 if (it != opts.end())
256 opts.erase(it);
257 }
258 }
259
260
261 /*!
262 * Split a package options string (keyval format) into a vector.
263 * Example input:
264 * authorformat=smallcaps,
265 * commabeforerest,
266 * titleformat=colonsep,
267 * bibformat={tabular,ibidem,numbered}
268 */
split_options(string const & input)269 vector<string> split_options(string const & input)
270 {
271 vector<string> options;
272 string option;
273 Parser p(input);
274 while (p.good()) {
275 Token const & t = p.get_token();
276 if (t.asInput() == ",") {
277 options.push_back(trimSpaceAndEol(option));
278 option.erase();
279 } else if (t.asInput() == "=") {
280 option += '=';
281 p.skip_spaces(true);
282 if (p.next_token().asInput() == "{")
283 option += '{' + p.getArg('{', '}') + '}';
284 } else if (t.cat() != catSpace && t.cat() != catComment)
285 option += t.asInput();
286 }
287
288 if (!option.empty())
289 options.push_back(trimSpaceAndEol(option));
290
291 return options;
292 }
293
294
295 /*!
296 * Retrieve a keyval option "name={value with=sign}" named \p name from
297 * \p options and return the value.
298 * The found option is also removed from \p options.
299 */
process_keyval_opt(vector<string> & options,string name)300 string process_keyval_opt(vector<string> & options, string name)
301 {
302 for (size_t i = 0; i < options.size(); ++i) {
303 vector<string> option;
304 split(options[i], option, '=');
305 if (option.size() < 2)
306 continue;
307 if (option[0] == name) {
308 options.erase(options.begin() + i);
309 option.erase(option.begin());
310 return join(option, "=");
311 }
312 }
313 return "";
314 }
315
316 } // anonymous namespace
317
318
319 /**
320 * known polyglossia language names (including variants)
321 * FIXME: support spelling=old for german variants (german vs. ngerman LyX names etc)
322 */
323 const char * const Preamble::polyglossia_languages[] = {
324 "albanian", "american", "amharic", "ancient", "arabic", "armenian", "asturian", "australian",
325 "bahasai", "bahasam", "basque", "bengali", "brazil", "brazilian", "breton", "british", "bulgarian",
326 "catalan", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
327 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
328 "galician", "greek", "monotonic", "hebrew", "hindi",
329 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer",
330 "lao", "latin", "latvian", "lithuanian", "lsorbian", "magyar", "malayalam", "marathi",
331 "austrian", "newzealand", "german", "norsk", "nynorsk", "occitan",
332 "piedmontese", "polish", "polytonic", "portuges", "romanian", "romansh", "russian",
333 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovenian", "spanish", "swedish", "syriac",
334 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
335 "ukrainian", "urdu", "usorbian", "vietnamese", "welsh", 0};
336 // not yet supported by LyX: "korean", "nko"
337
338 /**
339 * the same as polyglossia_languages with .lyx names
340 * please keep this in sync with polyglossia_languages line by line!
341 */
342 const char * const Preamble::coded_polyglossia_languages[] = {
343 "albanian", "american", "amharic", "ancientgreek", "arabic_arabi", "armenian", "asturian", "australian",
344 "bahasa", "bahasam", "basque", "bengali", "brazilian", "brazilian", "breton", "british", "bulgarian",
345 "catalan", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
346 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
347 "galician", "greek", "greek", "hebrew", "hindi",
348 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer",
349 "lao", "latin", "latvian", "lithuanian", "lowersorbian", "magyar", "malayalam", "marathi",
350 "naustrian","newzealand", "ngerman", "norsk", "nynorsk", "occitan",
351 "piedmontese", "polish", "polutonikogreek", "portuges", "romanian", "romansh", "russian",
352 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovene", "spanish", "swedish", "syriac",
353 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
354 "ukrainian", "urdu", "uppersorbian", "vietnamese", "welsh", 0};
355 // not yet supported by LyX: "korean-polyglossia", "nko"
356
357
usePolyglossia() const358 bool Preamble::usePolyglossia() const
359 {
360 return h_use_non_tex_fonts && h_language_package == "default";
361 }
362
363
indentParagraphs() const364 bool Preamble::indentParagraphs() const
365 {
366 return h_paragraph_separation == "indent";
367 }
368
369
isPackageUsed(string const & package) const370 bool Preamble::isPackageUsed(string const & package) const
371 {
372 return used_packages.find(package) != used_packages.end();
373 }
374
375
isPackageAutoLoaded(string const & package) const376 bool Preamble::isPackageAutoLoaded(string const & package) const
377 {
378 return auto_packages.find(package) != auto_packages.end();
379 }
380
381
getPackageOptions(string const & package) const382 vector<string> Preamble::getPackageOptions(string const & package) const
383 {
384 map<string, vector<string> >::const_iterator it = used_packages.find(package);
385 if (it != used_packages.end())
386 return it->second;
387 return vector<string>();
388 }
389
390
registerAutomaticallyLoadedPackage(std::string const & package)391 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
392 {
393 auto_packages.insert(package);
394 }
395
396
addModule(string const & module)397 void Preamble::addModule(string const & module)
398 {
399 for (auto const & m : used_modules) {
400 if (m == module)
401 return;
402 }
403 used_modules.push_back(module);
404 }
405
406
suppressDate(bool suppress)407 void Preamble::suppressDate(bool suppress)
408 {
409 if (suppress)
410 h_suppress_date = "true";
411 else
412 h_suppress_date = "false";
413 }
414
415
registerAuthor(std::string const & name)416 void Preamble::registerAuthor(std::string const & name)
417 {
418 Author author(from_utf8(name), empty_docstring());
419 author.setUsed(true);
420 authors_.record(author);
421 h_tracking_changes = "true";
422 h_output_changes = "true";
423 }
424
425
getAuthor(std::string const & name) const426 Author const & Preamble::getAuthor(std::string const & name) const
427 {
428 Author author(from_utf8(name), empty_docstring());
429 for (AuthorList::Authors::const_iterator it = authors_.begin();
430 it != authors_.end(); ++it)
431 if (*it == author)
432 return *it;
433 static Author const dummy;
434 return dummy;
435 }
436
437
getSpecialTableColumnArguments(char c) const438 int Preamble::getSpecialTableColumnArguments(char c) const
439 {
440 map<char, int>::const_iterator it = special_columns_.find(c);
441 if (it == special_columns_.end())
442 return -1;
443 return it->second;
444 }
445
446
add_package(string const & name,vector<string> & options)447 void Preamble::add_package(string const & name, vector<string> & options)
448 {
449 // every package inherits the global options
450 if (used_packages.find(name) == used_packages.end())
451 used_packages[name] = split_options(h_options);
452
453 // Insert options passed via PassOptionsToPackage
454 for (auto const & p : extra_package_options_) {
455 if (p.first == name) {
456 vector<string> eo = getVectorFromString(p.second);
457 for (auto const & eoi : eo)
458 options.push_back(eoi);
459 }
460
461 }
462 vector<string> & v = used_packages[name];
463 v.insert(v.end(), options.begin(), options.end());
464 if (name == "jurabib") {
465 // Don't output the order argument (see the cite command
466 // handling code in text.cpp).
467 vector<string>::iterator end =
468 remove(options.begin(), options.end(), "natbiborder");
469 end = remove(options.begin(), end, "jurabiborder");
470 options.erase(end, options.end());
471 }
472 }
473
474
475 namespace {
476
477 // Given is a string like "scaled=0.9" or "Scale=0.9", return 0.9 * 100
scale_as_percentage(string const & scale,string & percentage)478 bool scale_as_percentage(string const & scale, string & percentage)
479 {
480 string::size_type pos = scale.find('=');
481 if (pos != string::npos) {
482 string value = scale.substr(pos + 1);
483 if (isStrDbl(value)) {
484 percentage = convert<string>(
485 static_cast<int>(100 * convert<double>(value)));
486 return true;
487 }
488 }
489 return false;
490 }
491
492
remove_braces(string const & value)493 string remove_braces(string const & value)
494 {
495 if (value.empty())
496 return value;
497 if (value[0] == '{' && value[value.length()-1] == '}')
498 return value.substr(1, value.length()-2);
499 return value;
500 }
501
502 } // anonymous namespace
503
504
Preamble()505 Preamble::Preamble() : one_language(true), explicit_babel(false),
506 title_layout_found(false), index_number(0), h_font_cjk_set(false),
507 h_use_microtype("false")
508 {
509 //h_backgroundcolor;
510 //h_boxbgcolor;
511 h_biblio_style = "plain";
512 h_bibtex_command = "default";
513 h_cite_engine = "basic";
514 h_cite_engine_type = "default";
515 h_color = "#008000";
516 h_defskip = "medskip";
517 h_dynamic_quotes = false;
518 //h_float_placement;
519 //h_fontcolor;
520 h_fontencoding = "default";
521 h_font_roman[0] = "default";
522 h_font_roman[1] = "default";
523 h_font_sans[0] = "default";
524 h_font_sans[1] = "default";
525 h_font_typewriter[0] = "default";
526 h_font_typewriter[1] = "default";
527 h_font_math[0] = "auto";
528 h_font_math[1] = "auto";
529 h_font_default_family = "default";
530 h_use_non_tex_fonts = false;
531 h_font_sc = "false";
532 h_font_osf = "false";
533 h_font_sf_scale[0] = "100";
534 h_font_sf_scale[1] = "100";
535 h_font_tt_scale[0] = "100";
536 h_font_tt_scale[1] = "100";
537 //h_font_cjk
538 h_is_mathindent = "0";
539 h_math_numbering_side = "default";
540 h_graphics = "default";
541 h_default_output_format = "default";
542 h_html_be_strict = "false";
543 h_html_css_as_file = "0";
544 h_html_math_output = "0";
545 h_index[0] = "Index";
546 h_index_command = "default";
547 h_inputencoding = "auto";
548 h_justification = "true";
549 h_language = "english";
550 h_language_package = "none";
551 //h_listings_params;
552 h_maintain_unincluded_children = "false";
553 //h_margins;
554 //h_notefontcolor;
555 //h_options;
556 h_output_changes = "false";
557 h_output_sync = "0";
558 //h_output_sync_macro
559 h_papercolumns = "1";
560 h_paperfontsize = "default";
561 h_paperorientation = "portrait";
562 h_paperpagestyle = "default";
563 //h_papersides;
564 h_papersize = "default";
565 h_paragraph_indentation = "default";
566 h_paragraph_separation = "indent";
567 //h_pdf_title;
568 //h_pdf_author;
569 //h_pdf_subject;
570 //h_pdf_keywords;
571 h_pdf_bookmarks = "0";
572 h_pdf_bookmarksnumbered = "0";
573 h_pdf_bookmarksopen = "0";
574 h_pdf_bookmarksopenlevel = "1";
575 h_pdf_breaklinks = "0";
576 h_pdf_pdfborder = "0";
577 h_pdf_colorlinks = "0";
578 h_pdf_backref = "section";
579 h_pdf_pdfusetitle = "0";
580 //h_pdf_pagemode;
581 //h_pdf_quoted_options;
582 h_quotes_style = "english";
583 h_secnumdepth = "3";
584 h_shortcut[0] = "idx";
585 h_spacing = "single";
586 h_save_transient_properties = "true";
587 h_suppress_date = "false";
588 h_textclass = "article";
589 h_tocdepth = "3";
590 h_tracking_changes = "false";
591 h_use_bibtopic = "false";
592 h_use_dash_ligatures = "true";
593 h_use_indices = "false";
594 h_use_geometry = "false";
595 h_use_default_options = "false";
596 h_use_hyperref = "false";
597 h_use_microtype = "false";
598 h_use_refstyle = false;
599 h_use_minted = false;
600 h_use_packages["amsmath"] = "1";
601 h_use_packages["amssymb"] = "0";
602 h_use_packages["cancel"] = "0";
603 h_use_packages["esint"] = "1";
604 h_use_packages["mhchem"] = "0";
605 h_use_packages["mathdots"] = "0";
606 h_use_packages["mathtools"] = "0";
607 h_use_packages["stackrel"] = "0";
608 h_use_packages["stmaryrd"] = "0";
609 h_use_packages["undertilde"] = "0";
610 }
611
612
handle_hyperref(vector<string> & options)613 void Preamble::handle_hyperref(vector<string> & options)
614 {
615 // FIXME swallow inputencoding changes that might surround the
616 // hyperref setup if it was written by LyX
617 h_use_hyperref = "true";
618 // swallow "unicode=true", since LyX does always write that
619 vector<string>::iterator it =
620 find(options.begin(), options.end(), "unicode=true");
621 if (it != options.end())
622 options.erase(it);
623 it = find(options.begin(), options.end(), "pdfusetitle");
624 if (it != options.end()) {
625 h_pdf_pdfusetitle = "1";
626 options.erase(it);
627 }
628 string bookmarks = process_keyval_opt(options, "bookmarks");
629 if (bookmarks == "true")
630 h_pdf_bookmarks = "1";
631 else if (bookmarks == "false")
632 h_pdf_bookmarks = "0";
633 if (h_pdf_bookmarks == "1") {
634 string bookmarksnumbered =
635 process_keyval_opt(options, "bookmarksnumbered");
636 if (bookmarksnumbered == "true")
637 h_pdf_bookmarksnumbered = "1";
638 else if (bookmarksnumbered == "false")
639 h_pdf_bookmarksnumbered = "0";
640 string bookmarksopen =
641 process_keyval_opt(options, "bookmarksopen");
642 if (bookmarksopen == "true")
643 h_pdf_bookmarksopen = "1";
644 else if (bookmarksopen == "false")
645 h_pdf_bookmarksopen = "0";
646 if (h_pdf_bookmarksopen == "1") {
647 string bookmarksopenlevel =
648 process_keyval_opt(options, "bookmarksopenlevel");
649 if (!bookmarksopenlevel.empty())
650 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
651 }
652 }
653 string breaklinks = process_keyval_opt(options, "breaklinks");
654 if (breaklinks == "true")
655 h_pdf_breaklinks = "1";
656 else if (breaklinks == "false")
657 h_pdf_breaklinks = "0";
658 string pdfborder = process_keyval_opt(options, "pdfborder");
659 if (pdfborder == "{0 0 0}")
660 h_pdf_pdfborder = "1";
661 else if (pdfborder == "{0 0 1}")
662 h_pdf_pdfborder = "0";
663 string backref = process_keyval_opt(options, "backref");
664 if (!backref.empty())
665 h_pdf_backref = backref;
666 string colorlinks = process_keyval_opt(options, "colorlinks");
667 if (colorlinks == "true")
668 h_pdf_colorlinks = "1";
669 else if (colorlinks == "false")
670 h_pdf_colorlinks = "0";
671 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
672 if (!pdfpagemode.empty())
673 h_pdf_pagemode = pdfpagemode;
674 string pdftitle = process_keyval_opt(options, "pdftitle");
675 if (!pdftitle.empty()) {
676 h_pdf_title = remove_braces(pdftitle);
677 }
678 string pdfauthor = process_keyval_opt(options, "pdfauthor");
679 if (!pdfauthor.empty()) {
680 h_pdf_author = remove_braces(pdfauthor);
681 }
682 string pdfsubject = process_keyval_opt(options, "pdfsubject");
683 if (!pdfsubject.empty())
684 h_pdf_subject = remove_braces(pdfsubject);
685 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
686 if (!pdfkeywords.empty())
687 h_pdf_keywords = remove_braces(pdfkeywords);
688 if (!options.empty()) {
689 if (!h_pdf_quoted_options.empty())
690 h_pdf_quoted_options += ',';
691 h_pdf_quoted_options += join(options, ",");
692 options.clear();
693 }
694 }
695
696
handle_geometry(vector<string> & options)697 void Preamble::handle_geometry(vector<string> & options)
698 {
699 h_use_geometry = "true";
700 vector<string>::iterator it;
701 // paper orientation
702 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
703 h_paperorientation = "landscape";
704 options.erase(it);
705 }
706 // paper size
707 // keyval version: "paper=letter"
708 string paper = process_keyval_opt(options, "paper");
709 if (!paper.empty())
710 h_papersize = paper + "paper";
711 // alternative version: "letterpaper"
712 handle_opt(options, known_paper_sizes, h_papersize);
713 delete_opt(options, known_paper_sizes);
714 // page margins
715 char const * const * margin = known_paper_margins;
716 for (; *margin; ++margin) {
717 string value = process_keyval_opt(options, *margin);
718 if (!value.empty()) {
719 int k = margin - known_paper_margins;
720 string name = known_coded_paper_margins[k];
721 h_margins += '\\' + name + ' ' + value + '\n';
722 }
723 }
724 }
725
726
handle_package(Parser & p,string const & name,string const & opts,bool in_lyx_preamble,bool detectEncoding)727 void Preamble::handle_package(Parser &p, string const & name,
728 string const & opts, bool in_lyx_preamble,
729 bool detectEncoding)
730 {
731 vector<string> options = split_options(opts);
732 add_package(name, options);
733
734 if (is_known(name, known_xetex_packages)) {
735 xetex = true;
736 h_use_non_tex_fonts = true;
737 registerAutomaticallyLoadedPackage("fontspec");
738 if (h_inputencoding == "auto")
739 p.setEncoding("UTF-8");
740 }
741
742 // roman fonts
743 if (is_known(name, known_roman_fonts))
744 h_font_roman[0] = name;
745
746 if (name == "fourier") {
747 h_font_roman[0] = "utopia";
748 // when font uses real small capitals
749 if (opts == "expert")
750 h_font_sc = "true";
751 }
752
753 if (name == "garamondx") {
754 h_font_roman[0] = "garamondx";
755 if (opts == "osfI")
756 h_font_osf = "true";
757 }
758
759 if (name == "libertine") {
760 h_font_roman[0] = "libertine";
761 // this automatically invokes biolinum
762 h_font_sans[0] = "biolinum";
763 // as well as libertineMono
764 h_font_typewriter[0] = "libertine-mono";
765 if (opts == "osf")
766 h_font_osf = "true";
767 else if (opts == "lining")
768 h_font_osf = "false";
769 }
770
771 if (name == "libertineRoman" || name == "libertine-type1") {
772 h_font_roman[0] = "libertine";
773 // NOTE: contrary to libertine.sty, libertineRoman
774 // and libertine-type1 do not automatically invoke
775 // biolinum and libertineMono
776 if (opts == "lining")
777 h_font_osf = "false";
778 else if (opts == "osf")
779 h_font_osf = "true";
780 }
781
782 if (name == "MinionPro") {
783 h_font_roman[0] = "minionpro";
784 if (opts.find("lf") != string::npos)
785 h_font_osf = "false";
786 else
787 h_font_osf = "true";
788 if (opts.find("onlytext") != string::npos)
789 h_font_math[0] = "default";
790 else
791 h_font_math[0] = "auto";
792 }
793
794 if (name == "mathdesign") {
795 if (opts.find("charter") != string::npos)
796 h_font_roman[0] = "md-charter";
797 if (opts.find("garamond") != string::npos)
798 h_font_roman[0] = "md-garamond";
799 if (opts.find("utopia") != string::npos)
800 h_font_roman[0] = "md-utopia";
801 if (opts.find("expert") != string::npos) {
802 h_font_sc = "true";
803 h_font_osf = "true";
804 }
805 }
806
807 else if (name == "mathpazo")
808 h_font_roman[0] = "palatino";
809
810 else if (name == "mathptmx")
811 h_font_roman[0] = "times";
812
813 if (name == "crimson")
814 h_font_roman[0] = "cochineal";
815
816 if (name == "cochineal") {
817 h_font_roman[0] = "cochineal";
818 // cochineal can have several options, e.g. [proportional,osf]
819 string::size_type pos = opts.find("osf");
820 if (pos != string::npos)
821 h_font_osf = "true";
822 }
823
824 if (name == "noto") {
825 // noto can have several options
826 if (opts.empty())
827 h_font_roman[0] = "NotoSerif-TLF";
828 string::size_type pos = opts.find("rm");
829 if (pos != string::npos)
830 h_font_roman[0] = "NotoSerif-TLF";
831 pos = opts.find("sf");
832 if (pos != string::npos)
833 h_font_sans[0] = "NotoSans-TLF";
834 pos = opts.find("nott");
835 if (pos != string::npos) {
836 h_font_roman[0] = "NotoSerif-TLF";
837 h_font_sans[0] = "NotoSans-TLF";
838 }
839 // noto as typewriter is handled in handling of \ttdefault
840 // special cases are handled in handling of \rmdefault and \sfdefault
841 }
842
843 // sansserif fonts
844 if (is_known(name, known_sans_fonts)) {
845 h_font_sans[0] = name;
846 if (options.size() >= 1) {
847 if (scale_as_percentage(opts, h_font_sf_scale[0]))
848 options.clear();
849 }
850 }
851
852 if (name == "biolinum" || name == "biolinum-type1") {
853 h_font_sans[0] = "biolinum";
854 // biolinum can have several options, e.g. [osf,scaled=0.97]
855 string::size_type pos = opts.find("osf");
856 if (pos != string::npos)
857 h_font_osf = "true";
858 }
859
860 // typewriter fonts
861 if (is_known(name, known_typewriter_fonts)) {
862 // fourier can be set as roman font _only_
863 // fourier as typewriter is handled in handling of \ttdefault
864 if (name != "fourier") {
865 h_font_typewriter[0] = name;
866 if (options.size() >= 1) {
867 if (scale_as_percentage(opts, h_font_tt_scale[0]))
868 options.clear();
869 }
870 }
871 }
872
873 if (name == "libertineMono" || name == "libertineMono-type1")
874 h_font_typewriter[0] = "libertine-mono";
875
876 // font uses old-style figure
877 if (name == "eco")
878 h_font_osf = "true";
879
880 // math fonts
881 if (is_known(name, known_math_fonts))
882 h_font_math[0] = name;
883
884 if (name == "newtxmath") {
885 if (opts.empty())
886 h_font_math[0] = "newtxmath";
887 else if (opts == "garamondx")
888 h_font_math[0] = "garamondx-ntxm";
889 else if (opts == "libertine")
890 h_font_math[0] = "libertine-ntxm";
891 else if (opts == "minion")
892 h_font_math[0] = "minion-ntxm";
893 else if (opts == "cochineal")
894 h_font_math[0] = "cochineal-ntxm";
895 }
896
897 if (name == "iwona")
898 if (opts == "math")
899 h_font_math[0] = "iwona-math";
900
901 if (name == "kurier")
902 if (opts == "math")
903 h_font_math[0] = "kurier-math";
904
905 // after the detection and handling of special cases, we can remove the
906 // fonts, otherwise they would appear in the preamble, see bug #7856
907 if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
908 || is_known(name, known_typewriter_fonts) || is_known(name, known_math_fonts))
909 ;
910 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
911 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
912 name == "esint" || name == "mhchem" || name == "mathdots" ||
913 name == "mathtools" || name == "stackrel" ||
914 name == "stmaryrd" || name == "undertilde") {
915 h_use_packages[name] = "2";
916 registerAutomaticallyLoadedPackage(name);
917 }
918
919 else if (name == "babel") {
920 h_language_package = "default";
921 // One might think we would have to do nothing if babel is loaded
922 // without any options to prevent pollution of the preamble with this
923 // babel call in every roundtrip.
924 // But the user could have defined babel-specific things afterwards. So
925 // we need to keep it in the preamble to prevent cases like bug #7861.
926 if (!opts.empty()) {
927 // check if more than one option was used - used later for inputenc
928 if (options.begin() != options.end() - 1)
929 one_language = false;
930 // babel takes the last language of the option of its \usepackage
931 // call as document language. If there is no such language option, the
932 // last language in the documentclass options is used.
933 handle_opt(options, known_languages, h_language);
934 // translate the babel name to a LyX name
935 h_language = babel2lyx(h_language);
936 if (h_language == "japanese") {
937 // For Japanese, the encoding isn't indicated in the source
938 // file, and there's really not much we can do. We could
939 // 1) offer a list of possible encodings to choose from, or
940 // 2) determine the encoding of the file by inspecting it.
941 // For the time being, we leave the encoding alone so that
942 // we don't get iconv errors when making a wrong guess, and
943 // we will output a note at the top of the document
944 // explaining what to do.
945 Encoding const * const enc = encodings.fromIconvName(
946 p.getEncoding(), Encoding::japanese, false);
947 if (enc)
948 h_inputencoding = enc->name();
949 is_nonCJKJapanese = true;
950 // in this case babel can be removed from the preamble
951 registerAutomaticallyLoadedPackage("babel");
952 } else {
953 // If babel is called with options, LyX puts them by default into the
954 // document class options. This works for most languages, except
955 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
956 // perhaps in future others.
957 // Therefore keep the babel call as it is as the user might have
958 // reasons for it.
959 string const babelcall = "\\usepackage[" + opts + "]{babel}\n";
960 if (!contains(h_preamble.str(), babelcall))
961 h_preamble << babelcall;
962 }
963 delete_opt(options, known_languages);
964 } else {
965 if (!contains(h_preamble.str(), "\\usepackage{babel}\n"))
966 h_preamble << "\\usepackage{babel}\n";
967 explicit_babel = true;
968 }
969 }
970
971 else if (name == "polyglossia") {
972 h_language_package = "default";
973 h_default_output_format = "pdf4";
974 h_use_non_tex_fonts = true;
975 xetex = true;
976 registerAutomaticallyLoadedPackage("xunicode");
977 if (h_inputencoding == "auto")
978 p.setEncoding("UTF-8");
979 }
980
981 else if (name == "CJK") {
982 // set the encoding to "auto" because it might be set to "default" by the babel handling
983 // and this would not be correct for CJK
984 if (h_inputencoding == "default")
985 h_inputencoding = "auto";
986 registerAutomaticallyLoadedPackage("CJK");
987 }
988
989 else if (name == "CJKutf8") {
990 h_inputencoding = "utf8-cjk";
991 p.setEncoding("UTF-8");
992 registerAutomaticallyLoadedPackage("CJKutf8");
993 }
994
995 else if (name == "fontenc") {
996 h_fontencoding = getStringFromVector(options, ",");
997 /* We could do the following for better round trip support,
998 * but this makes the document less portable, so I skip it:
999 if (h_fontencoding == lyxrc.fontenc)
1000 h_fontencoding = "global";
1001 */
1002 options.clear();
1003 }
1004
1005 else if (name == "inputenc" || name == "luainputenc") {
1006 // h_inputencoding is only set when there is not more than one
1007 // inputenc option because otherwise h_inputencoding must be
1008 // set to "auto" (the default encoding of the document language)
1009 // Therefore check that exactly one option is passed to inputenc.
1010 // It is also only set when there is not more than one babel
1011 // language option.
1012 if (!options.empty()) {
1013 string const encoding = options.back();
1014 Encoding const * const enc = encodings.fromLaTeXName(
1015 encoding, Encoding::inputenc, true);
1016 if (!enc) {
1017 if (!detectEncoding)
1018 cerr << "Unknown encoding " << encoding
1019 << ". Ignoring." << std::endl;
1020 } else {
1021 if (!enc->unsafe() && options.size() == 1 && one_language == true)
1022 h_inputencoding = enc->name();
1023 p.setEncoding(enc->iconvName());
1024 }
1025 options.clear();
1026 }
1027 }
1028
1029 else if (name == "srcltx") {
1030 h_output_sync = "1";
1031 if (!opts.empty()) {
1032 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
1033 options.clear();
1034 } else
1035 h_output_sync_macro = "\\usepackage{srcltx}";
1036 }
1037
1038 else if (is_known(name, known_old_language_packages)) {
1039 // known language packages from the times before babel
1040 // if they are found and not also babel, they will be used as
1041 // custom language package
1042 h_language_package = "\\usepackage{" + name + "}";
1043 }
1044
1045 else if (name == "lyxskak") {
1046 // ignore this and its options
1047 const char * const o[] = {"ps", "mover", 0};
1048 delete_opt(options, o);
1049 }
1050
1051 else if (is_known(name, known_lyx_packages) && options.empty()) {
1052 if (name == "splitidx")
1053 h_use_indices = "true";
1054 else if (name == "minted")
1055 h_use_minted = true;
1056 else if (name == "refstyle")
1057 h_use_refstyle = true;
1058 else if (name == "prettyref")
1059 h_use_refstyle = false;
1060 if (!in_lyx_preamble) {
1061 h_preamble << package_beg_sep << name
1062 << package_mid_sep << "\\usepackage{"
1063 << name << '}';
1064 if (p.next_token().cat() == catNewline ||
1065 (p.next_token().cat() == catSpace &&
1066 p.next_next_token().cat() == catNewline))
1067 h_preamble << '\n';
1068 h_preamble << package_end_sep;
1069 }
1070 }
1071
1072 else if (name == "geometry")
1073 handle_geometry(options);
1074
1075 else if (name == "subfig")
1076 ; // ignore this FIXME: Use the package separator mechanism instead
1077
1078 else if (char const * const * where = is_known(name, known_languages))
1079 h_language = known_coded_languages[where - known_languages];
1080
1081 else if (name == "natbib") {
1082 h_biblio_style = "plainnat";
1083 h_cite_engine = "natbib";
1084 h_cite_engine_type = "authoryear";
1085 vector<string>::iterator it =
1086 find(options.begin(), options.end(), "authoryear");
1087 if (it != options.end())
1088 options.erase(it);
1089 else {
1090 it = find(options.begin(), options.end(), "numbers");
1091 if (it != options.end()) {
1092 h_cite_engine_type = "numerical";
1093 options.erase(it);
1094 }
1095 }
1096 if (!options.empty())
1097 h_biblio_options = join(options, ",");
1098 }
1099
1100 else if (name == "biblatex") {
1101 h_biblio_style = "plainnat";
1102 h_cite_engine = "biblatex";
1103 h_cite_engine_type = "authoryear";
1104 string opt;
1105 vector<string>::iterator it =
1106 find(options.begin(), options.end(), "natbib");
1107 if (it != options.end()) {
1108 options.erase(it);
1109 h_cite_engine = "biblatex-natbib";
1110 } else {
1111 opt = process_keyval_opt(options, "natbib");
1112 if (opt == "true")
1113 h_cite_engine = "biblatex-natbib";
1114 }
1115 opt = process_keyval_opt(options, "style");
1116 if (!opt.empty()) {
1117 h_biblatex_citestyle = opt;
1118 h_biblatex_bibstyle = opt;
1119 } else {
1120 opt = process_keyval_opt(options, "citestyle");
1121 if (!opt.empty())
1122 h_biblatex_citestyle = opt;
1123 opt = process_keyval_opt(options, "bibstyle");
1124 if (!opt.empty())
1125 h_biblatex_bibstyle = opt;
1126 }
1127 opt = process_keyval_opt(options, "refsection");
1128 if (!opt.empty()) {
1129 if (opt == "none" || opt == "part"
1130 || opt == "chapter" || opt == "section"
1131 || opt == "subsection")
1132 h_multibib = opt;
1133 else
1134 cerr << "Ignoring unkown refesection value '"
1135 << opt << "'.";
1136 }
1137 if (!options.empty()) {
1138 h_biblio_options = join(options, ",");
1139 options.clear();
1140 }
1141 }
1142
1143 else if (name == "jurabib") {
1144 h_biblio_style = "jurabib";
1145 h_cite_engine = "jurabib";
1146 h_cite_engine_type = "authoryear";
1147 if (!options.empty())
1148 h_biblio_options = join(options, ",");
1149 }
1150
1151 else if (name == "bibtopic")
1152 h_use_bibtopic = "true";
1153
1154 else if (name == "chapterbib")
1155 h_multibib = "child";
1156
1157 else if (name == "hyperref")
1158 handle_hyperref(options);
1159
1160 else if (name == "algorithm2e") {
1161 // Load "algorithm2e" module
1162 addModule("algorithm2e");
1163 // Add the package options to the global document options
1164 if (!options.empty()) {
1165 if (h_options.empty())
1166 h_options = join(options, ",");
1167 else
1168 h_options += ',' + join(options, ",");
1169 }
1170 }
1171 else if (name == "microtype") {
1172 //we internally support only microtype without params
1173 if (options.empty())
1174 h_use_microtype = "true";
1175 else
1176 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1177 }
1178
1179 else if (!in_lyx_preamble) {
1180 if (options.empty())
1181 h_preamble << "\\usepackage{" << name << '}';
1182 else {
1183 h_preamble << "\\usepackage[" << opts << "]{"
1184 << name << '}';
1185 options.clear();
1186 }
1187 if (p.next_token().cat() == catNewline ||
1188 (p.next_token().cat() == catSpace &&
1189 p.next_next_token().cat() == catNewline))
1190 h_preamble << '\n';
1191 }
1192
1193 // We need to do something with the options...
1194 if (!options.empty() && !detectEncoding)
1195 cerr << "Ignoring options '" << join(options, ",")
1196 << "' of package " << name << '.' << endl;
1197
1198 // remove the whitespace
1199 p.skip_spaces();
1200 }
1201
1202
handle_if(Parser & p,bool in_lyx_preamble)1203 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1204 {
1205 while (p.good()) {
1206 Token t = p.get_token();
1207 if (t.cat() == catEscape &&
1208 is_known(t.cs(), known_if_commands))
1209 handle_if(p, in_lyx_preamble);
1210 else {
1211 if (!in_lyx_preamble)
1212 h_preamble << t.asInput();
1213 if (t.cat() == catEscape && t.cs() == "fi")
1214 return;
1215 }
1216 }
1217 }
1218
1219
writeLyXHeader(ostream & os,bool subdoc,string const & outfiledir)1220 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1221 {
1222 if (contains(h_float_placement, "H"))
1223 registerAutomaticallyLoadedPackage("float");
1224 if (h_spacing != "single" && h_spacing != "default")
1225 registerAutomaticallyLoadedPackage("setspace");
1226 if (h_use_packages["amsmath"] == "2") {
1227 // amsbsy and amstext are already provided by amsmath
1228 registerAutomaticallyLoadedPackage("amsbsy");
1229 registerAutomaticallyLoadedPackage("amstext");
1230 }
1231
1232 // output the LyX file settings
1233 // Important: Keep the version formatting in sync with LyX and
1234 // lyx2lyx (bug 7951)
1235 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1236 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1237 << lyx_version_minor << '\n'
1238 << "\\lyxformat " << LYX_FORMAT << '\n'
1239 << "\\begin_document\n"
1240 << "\\begin_header\n"
1241 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1242 << "\\origin " << origin << "\n"
1243 << "\\textclass " << h_textclass << "\n";
1244 string const raw = subdoc ? empty_string() : h_preamble.str();
1245 if (!raw.empty()) {
1246 os << "\\begin_preamble\n";
1247 for (string::size_type i = 0; i < raw.size(); ++i) {
1248 if (raw[i] == package_beg_sep) {
1249 // Here follows some package loading code that
1250 // must be skipped if the package is loaded
1251 // automatically.
1252 string::size_type j = raw.find(package_mid_sep, i);
1253 if (j == string::npos)
1254 return false;
1255 string::size_type k = raw.find(package_end_sep, j);
1256 if (k == string::npos)
1257 return false;
1258 string const package = raw.substr(i + 1, j - i - 1);
1259 string const replacement = raw.substr(j + 1, k - j - 1);
1260 if (auto_packages.find(package) == auto_packages.end())
1261 os << replacement;
1262 i = k;
1263 } else
1264 os.put(raw[i]);
1265 }
1266 os << "\n\\end_preamble\n";
1267 }
1268 if (!h_options.empty())
1269 os << "\\options " << h_options << "\n";
1270 os << "\\use_default_options " << h_use_default_options << "\n";
1271 if (!used_modules.empty()) {
1272 os << "\\begin_modules\n";
1273 vector<string>::const_iterator const end = used_modules.end();
1274 vector<string>::const_iterator it = used_modules.begin();
1275 for (; it != end; ++it)
1276 os << *it << '\n';
1277 os << "\\end_modules\n";
1278 }
1279 if (!h_includeonlys.empty()) {
1280 os << "\\begin_includeonly\n";
1281 for (auto const & iofile : h_includeonlys)
1282 os << iofile << '\n';
1283 os << "\\end_includeonly\n";
1284 }
1285 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1286 << "\\language " << h_language << "\n"
1287 << "\\language_package " << h_language_package << "\n"
1288 << "\\inputencoding " << h_inputencoding << "\n"
1289 << "\\fontencoding " << h_fontencoding << "\n"
1290 << "\\font_roman \"" << h_font_roman[0]
1291 << "\" \"" << h_font_roman[1] << "\"\n"
1292 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1293 << "\\font_typewriter \"" << h_font_typewriter[0]
1294 << "\" \"" << h_font_typewriter[1] << "\"\n"
1295 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1296 << "\\font_default_family " << h_font_default_family << "\n"
1297 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1298 << "\\font_sc " << h_font_sc << "\n"
1299 << "\\font_osf " << h_font_osf << "\n"
1300 << "\\font_sf_scale " << h_font_sf_scale[0]
1301 << ' ' << h_font_sf_scale[1] << '\n'
1302 << "\\font_tt_scale " << h_font_tt_scale[0]
1303 << ' ' << h_font_tt_scale[1] << '\n';
1304 if (!h_font_cjk.empty())
1305 os << "\\font_cjk " << h_font_cjk << '\n';
1306 os << "\\use_microtype " << h_use_microtype << '\n'
1307 << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
1308 << "\\graphics " << h_graphics << '\n'
1309 << "\\default_output_format " << h_default_output_format << "\n"
1310 << "\\output_sync " << h_output_sync << "\n";
1311 if (h_output_sync == "1")
1312 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1313 os << "\\bibtex_command " << h_bibtex_command << "\n"
1314 << "\\index_command " << h_index_command << "\n";
1315 if (!h_float_placement.empty())
1316 os << "\\float_placement " << h_float_placement << "\n";
1317 os << "\\paperfontsize " << h_paperfontsize << "\n"
1318 << "\\spacing " << h_spacing << "\n"
1319 << "\\use_hyperref " << h_use_hyperref << '\n';
1320 if (h_use_hyperref == "true") {
1321 if (!h_pdf_title.empty())
1322 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1323 if (!h_pdf_author.empty())
1324 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1325 if (!h_pdf_subject.empty())
1326 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1327 if (!h_pdf_keywords.empty())
1328 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1329 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1330 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1331 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1332 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1333 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1334 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1335 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1336 "\\pdf_backref " << h_pdf_backref << "\n"
1337 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1338 if (!h_pdf_pagemode.empty())
1339 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1340 if (!h_pdf_quoted_options.empty())
1341 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1342 }
1343 os << "\\papersize " << h_papersize << "\n"
1344 << "\\use_geometry " << h_use_geometry << '\n';
1345 for (map<string, string>::const_iterator it = h_use_packages.begin();
1346 it != h_use_packages.end(); ++it)
1347 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1348 os << "\\cite_engine " << h_cite_engine << '\n'
1349 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1350 << "\\biblio_style " << h_biblio_style << "\n"
1351 << "\\use_bibtopic " << h_use_bibtopic << "\n";
1352 if (!h_biblio_options.empty())
1353 os << "\\biblio_options " << h_biblio_options << "\n";
1354 if (!h_biblatex_bibstyle.empty())
1355 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
1356 if (!h_biblatex_citestyle.empty())
1357 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
1358 if (!h_multibib.empty())
1359 os << "\\multibib " << h_multibib << "\n";
1360 os << "\\use_indices " << h_use_indices << "\n"
1361 << "\\paperorientation " << h_paperorientation << '\n'
1362 << "\\suppress_date " << h_suppress_date << '\n'
1363 << "\\justification " << h_justification << '\n'
1364 << "\\use_refstyle " << h_use_refstyle << '\n'
1365 << "\\use_minted " << h_use_minted << '\n';
1366 if (!h_fontcolor.empty())
1367 os << "\\fontcolor " << h_fontcolor << '\n';
1368 if (!h_notefontcolor.empty())
1369 os << "\\notefontcolor " << h_notefontcolor << '\n';
1370 if (!h_backgroundcolor.empty())
1371 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1372 if (!h_boxbgcolor.empty())
1373 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1374 if (index_number != 0)
1375 for (int i = 0; i < index_number; i++) {
1376 os << "\\index " << h_index[i] << '\n'
1377 << "\\shortcut " << h_shortcut[i] << '\n'
1378 << "\\color " << h_color << '\n'
1379 << "\\end_index\n";
1380 }
1381 else {
1382 os << "\\index " << h_index[0] << '\n'
1383 << "\\shortcut " << h_shortcut[0] << '\n'
1384 << "\\color " << h_color << '\n'
1385 << "\\end_index\n";
1386 }
1387 os << h_margins
1388 << "\\secnumdepth " << h_secnumdepth << "\n"
1389 << "\\tocdepth " << h_tocdepth << "\n"
1390 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1391 if (h_paragraph_separation == "skip")
1392 os << "\\defskip " << h_defskip << "\n";
1393 else
1394 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1395 os << "\\is_math_indent " << h_is_mathindent << "\n";
1396 if (!h_mathindentation.empty())
1397 os << "\\math_indentation " << h_mathindentation << "\n";
1398 os << "\\math_numbering_side " << h_math_numbering_side << "\n";
1399 os << "\\quotes_style " << h_quotes_style << "\n"
1400 << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
1401 << "\\papercolumns " << h_papercolumns << "\n"
1402 << "\\papersides " << h_papersides << "\n"
1403 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1404 if (!h_listings_params.empty())
1405 os << "\\listings_params " << h_listings_params << "\n";
1406 os << "\\tracking_changes " << h_tracking_changes << "\n"
1407 << "\\output_changes " << h_output_changes << "\n"
1408 << "\\html_math_output " << h_html_math_output << "\n"
1409 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1410 << "\\html_be_strict " << h_html_be_strict << "\n"
1411 << authors_
1412 << "\\end_header\n\n"
1413 << "\\begin_body\n";
1414 return true;
1415 }
1416
1417
parse(Parser & p,string const & forceclass,TeX2LyXDocClass & tc)1418 void Preamble::parse(Parser & p, string const & forceclass,
1419 TeX2LyXDocClass & tc)
1420 {
1421 // initialize fixed types
1422 special_columns_['D'] = 3;
1423 parse(p, forceclass, false, tc);
1424 }
1425
1426
parse(Parser & p,string const & forceclass,bool detectEncoding,TeX2LyXDocClass & tc)1427 void Preamble::parse(Parser & p, string const & forceclass,
1428 bool detectEncoding, TeX2LyXDocClass & tc)
1429 {
1430 bool is_full_document = false;
1431 bool is_lyx_file = false;
1432 bool in_lyx_preamble = false;
1433
1434 // determine whether this is a full document or a fragment for inclusion
1435 while (p.good()) {
1436 Token const & t = p.get_token();
1437
1438 if (t.cat() == catEscape && t.cs() == "documentclass") {
1439 is_full_document = true;
1440 break;
1441 }
1442 }
1443 p.reset();
1444
1445 if (detectEncoding && !is_full_document)
1446 return;
1447
1448 while (is_full_document && p.good()) {
1449 if (detectEncoding && h_inputencoding != "auto" &&
1450 h_inputencoding != "default")
1451 return;
1452
1453 Token const & t = p.get_token();
1454
1455 #ifdef FILEDEBUG
1456 if (!detectEncoding)
1457 cerr << "t: " << t << '\n';
1458 #endif
1459
1460 //
1461 // cat codes
1462 //
1463 if (!in_lyx_preamble &&
1464 (t.cat() == catLetter ||
1465 t.cat() == catSuper ||
1466 t.cat() == catSub ||
1467 t.cat() == catOther ||
1468 t.cat() == catMath ||
1469 t.cat() == catActive ||
1470 t.cat() == catBegin ||
1471 t.cat() == catEnd ||
1472 t.cat() == catAlign ||
1473 t.cat() == catParameter)) {
1474 h_preamble << t.cs();
1475 continue;
1476 }
1477
1478 if (!in_lyx_preamble &&
1479 (t.cat() == catSpace || t.cat() == catNewline)) {
1480 h_preamble << t.asInput();
1481 continue;
1482 }
1483
1484 if (t.cat() == catComment) {
1485 static regex const islyxfile("%% LyX .* created this file");
1486 static regex const usercommands("User specified LaTeX commands");
1487
1488 string const comment = t.asInput();
1489
1490 // magically switch encoding default if it looks like XeLaTeX
1491 static string const magicXeLaTeX =
1492 "% This document must be compiled with XeLaTeX ";
1493 if (comment.size() > magicXeLaTeX.size()
1494 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1495 && h_inputencoding == "auto") {
1496 if (!detectEncoding)
1497 cerr << "XeLaTeX comment found, switching to UTF8\n";
1498 h_inputencoding = "utf8";
1499 }
1500 smatch sub;
1501 if (regex_search(comment, sub, islyxfile)) {
1502 is_lyx_file = true;
1503 in_lyx_preamble = true;
1504 } else if (is_lyx_file
1505 && regex_search(comment, sub, usercommands))
1506 in_lyx_preamble = false;
1507 else if (!in_lyx_preamble)
1508 h_preamble << t.asInput();
1509 continue;
1510 }
1511
1512 if (t.cs() == "PassOptionsToPackage") {
1513 string const poptions = p.getArg('{', '}');
1514 string const package = p.verbatim_item();
1515 extra_package_options_.insert(make_pair(package, poptions));
1516 continue;
1517 }
1518
1519 if (t.cs() == "pagestyle") {
1520 h_paperpagestyle = p.verbatim_item();
1521 continue;
1522 }
1523
1524 if (t.cs() == "setdefaultlanguage") {
1525 xetex = true;
1526 // We don't yet care about non-language variant options
1527 // because LyX doesn't support this yet, see bug #8214
1528 if (p.hasOpt()) {
1529 string langopts = p.getOpt();
1530 // check if the option contains a variant, if yes, extract it
1531 string::size_type pos_var = langopts.find("variant");
1532 string::size_type i = langopts.find(',', pos_var);
1533 string::size_type k = langopts.find('=', pos_var);
1534 if (pos_var != string::npos){
1535 string variant;
1536 if (i == string::npos)
1537 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1538 else
1539 variant = langopts.substr(k + 1, i - k - 1);
1540 h_language = variant;
1541 }
1542 p.verbatim_item();
1543 } else
1544 h_language = p.verbatim_item();
1545 //finally translate the poyglossia name to a LyX name
1546 h_language = polyglossia2lyx(h_language);
1547 continue;
1548 }
1549
1550 if (t.cs() == "setotherlanguage") {
1551 // We don't yet care about the option because LyX doesn't
1552 // support this yet, see bug #8214
1553 p.hasOpt() ? p.getOpt() : string();
1554 p.verbatim_item();
1555 continue;
1556 }
1557
1558 if (t.cs() == "setmainfont") {
1559 // we don't care about the option
1560 p.hasOpt() ? p.getOpt() : string();
1561 h_font_roman[1] = p.getArg('{', '}');
1562 continue;
1563 }
1564
1565 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1566 // LyX currently only supports the scale option
1567 string scale;
1568 if (p.hasOpt()) {
1569 string fontopts = p.getArg('[', ']');
1570 // check if the option contains a scaling, if yes, extract it
1571 string::size_type pos = fontopts.find("Scale");
1572 if (pos != string::npos) {
1573 string::size_type i = fontopts.find(',', pos);
1574 if (i == string::npos)
1575 scale_as_percentage(fontopts.substr(pos + 1), scale);
1576 else
1577 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1578 }
1579 }
1580 if (t.cs() == "setsansfont") {
1581 if (!scale.empty())
1582 h_font_sf_scale[1] = scale;
1583 h_font_sans[1] = p.getArg('{', '}');
1584 } else {
1585 if (!scale.empty())
1586 h_font_tt_scale[1] = scale;
1587 h_font_typewriter[1] = p.getArg('{', '}');
1588 }
1589 continue;
1590 }
1591
1592 if (t.cs() == "date") {
1593 string argument = p.getArg('{', '}');
1594 if (argument.empty())
1595 h_suppress_date = "true";
1596 else
1597 h_preamble << t.asInput() << '{' << argument << '}';
1598 continue;
1599 }
1600
1601 if (t.cs() == "color") {
1602 string const space =
1603 (p.hasOpt() ? p.getOpt() : string());
1604 string argument = p.getArg('{', '}');
1605 // check the case that a standard color is used
1606 if (space.empty() && is_known(argument, known_basic_colors)) {
1607 h_fontcolor = rgbcolor2code(argument);
1608 registerAutomaticallyLoadedPackage("color");
1609 } else if (space.empty() && argument == "document_fontcolor")
1610 registerAutomaticallyLoadedPackage("color");
1611 // check the case that LyX's document_fontcolor is defined
1612 // but not used for \color
1613 else {
1614 h_preamble << t.asInput();
1615 if (!space.empty())
1616 h_preamble << space;
1617 h_preamble << '{' << argument << '}';
1618 // the color might already be set because \definecolor
1619 // is parsed before this
1620 h_fontcolor = "";
1621 }
1622 continue;
1623 }
1624
1625 if (t.cs() == "pagecolor") {
1626 string argument = p.getArg('{', '}');
1627 // check the case that a standard color is used
1628 if (is_known(argument, known_basic_colors)) {
1629 h_backgroundcolor = rgbcolor2code(argument);
1630 } else if (argument == "page_backgroundcolor")
1631 registerAutomaticallyLoadedPackage("color");
1632 // check the case that LyX's page_backgroundcolor is defined
1633 // but not used for \pagecolor
1634 else {
1635 h_preamble << t.asInput() << '{' << argument << '}';
1636 // the color might already be set because \definecolor
1637 // is parsed before this
1638 h_backgroundcolor = "";
1639 }
1640 continue;
1641 }
1642
1643 if (t.cs() == "makeatletter") {
1644 // LyX takes care of this
1645 p.setCatcode('@', catLetter);
1646 continue;
1647 }
1648
1649 if (t.cs() == "makeatother") {
1650 // LyX takes care of this
1651 p.setCatcode('@', catOther);
1652 continue;
1653 }
1654
1655 if (t.cs() == "makeindex") {
1656 // LyX will re-add this if a print index command is found
1657 p.skip_spaces();
1658 continue;
1659 }
1660
1661 if (t.cs() == "newindex") {
1662 string const indexname = p.getArg('[', ']');
1663 string const shortcut = p.verbatim_item();
1664 if (!indexname.empty())
1665 h_index[index_number] = indexname;
1666 else
1667 h_index[index_number] = shortcut;
1668 h_shortcut[index_number] = shortcut;
1669 index_number += 1;
1670 p.skip_spaces();
1671 continue;
1672 }
1673
1674 if (t.cs() == "addbibresource") {
1675 biblatex_bibliographies.push_back(removeExtension(p.getArg('{', '}')));
1676 continue;
1677 }
1678
1679 if (t.cs() == "bibliography") {
1680 vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
1681 biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
1682 continue;
1683 }
1684
1685 if (t.cs() == "RS@ifundefined") {
1686 string const name = p.verbatim_item();
1687 string const body1 = p.verbatim_item();
1688 string const body2 = p.verbatim_item();
1689 // only non-lyxspecific stuff
1690 if (in_lyx_preamble &&
1691 (name == "subsecref" || name == "thmref" || name == "lemref"))
1692 p.skip_spaces();
1693 else {
1694 ostringstream ss;
1695 ss << '\\' << t.cs();
1696 ss << '{' << name << '}'
1697 << '{' << body1 << '}'
1698 << '{' << body2 << '}';
1699 h_preamble << ss.str();
1700 }
1701 continue;
1702 }
1703
1704 if (t.cs() == "AtBeginDocument") {
1705 string const name = p.verbatim_item();
1706 // only non-lyxspecific stuff
1707 if (in_lyx_preamble &&
1708 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
1709 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
1710 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
1711 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
1712 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
1713 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
1714 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
1715 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
1716 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
1717 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
1718 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
1719 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
1720 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
1721 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
1722 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
1723 p.skip_spaces();
1724 else {
1725 ostringstream ss;
1726 ss << '\\' << t.cs();
1727 ss << '{' << name << '}';
1728 h_preamble << ss.str();
1729 }
1730 continue;
1731 }
1732
1733 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1734 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1735 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1736 || t.cs() == "DeclareRobustCommand"
1737 || t.cs() == "DeclareRobustCommandx"
1738 || t.cs() == "ProvideTextCommandDefault"
1739 || t.cs() == "DeclareMathAccent") {
1740 bool star = false;
1741 if (p.next_token().character() == '*') {
1742 p.get_token();
1743 star = true;
1744 }
1745 string const name = p.verbatim_item();
1746 string const opt1 = p.getFullOpt();
1747 string const opt2 = p.getFullOpt();
1748 string const body = p.verbatim_item();
1749 // store the in_lyx_preamble setting
1750 bool const was_in_lyx_preamble = in_lyx_preamble;
1751 // font settings
1752 if (name == "\\rmdefault")
1753 if (is_known(body, known_roman_fonts)) {
1754 h_font_roman[0] = body;
1755 p.skip_spaces();
1756 in_lyx_preamble = true;
1757 }
1758 if (name == "\\sfdefault")
1759 if (is_known(body, known_sans_fonts)) {
1760 h_font_sans[0] = body;
1761 p.skip_spaces();
1762 in_lyx_preamble = true;
1763 }
1764 if (name == "\\ttdefault")
1765 if (is_known(body, known_typewriter_fonts)) {
1766 h_font_typewriter[0] = body;
1767 p.skip_spaces();
1768 in_lyx_preamble = true;
1769 }
1770 if (name == "\\familydefault") {
1771 string family = body;
1772 // remove leading "\"
1773 h_font_default_family = family.erase(0,1);
1774 p.skip_spaces();
1775 in_lyx_preamble = true;
1776 }
1777
1778 // remove LyX-specific definitions that are re-added by LyX
1779 // if necessary
1780 // \lyxline is an ancient command that is converted by tex2lyx into
1781 // a \rule therefore remove its preamble code
1782 if (name == "\\lyxdot" || name == "\\lyxarrow"
1783 || name == "\\lyxline" || name == "\\LyX") {
1784 p.skip_spaces();
1785 in_lyx_preamble = true;
1786 }
1787
1788 // Add the command to the known commands
1789 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1790
1791 // only non-lyxspecific stuff
1792 if (!in_lyx_preamble) {
1793 ostringstream ss;
1794 ss << '\\' << t.cs();
1795 if (star)
1796 ss << '*';
1797 ss << '{' << name << '}' << opt1 << opt2
1798 << '{' << body << "}";
1799 if (prefixIs(t.cs(), "renew") || !contains(h_preamble.str(), ss.str()))
1800 h_preamble << ss.str();
1801 /*
1802 ostream & out = in_preamble ? h_preamble : os;
1803 out << "\\" << t.cs() << "{" << name << "}"
1804 << opts << "{" << body << "}";
1805 */
1806 }
1807 // restore the in_lyx_preamble setting
1808 in_lyx_preamble = was_in_lyx_preamble;
1809 continue;
1810 }
1811
1812 if (t.cs() == "documentclass") {
1813 vector<string>::iterator it;
1814 vector<string> opts = split_options(p.getArg('[', ']'));
1815 handle_opt(opts, known_fontsizes, h_paperfontsize);
1816 delete_opt(opts, known_fontsizes);
1817 // delete "pt" at the end
1818 string::size_type i = h_paperfontsize.find("pt");
1819 if (i != string::npos)
1820 h_paperfontsize.erase(i);
1821 // The documentclass options are always parsed before the options
1822 // of the babel call so that a language cannot overwrite the babel
1823 // options.
1824 handle_opt(opts, known_languages, h_language);
1825 delete_opt(opts, known_languages);
1826
1827 // math indentation
1828 if ((it = find(opts.begin(), opts.end(), "fleqn"))
1829 != opts.end()) {
1830 h_is_mathindent = "1";
1831 opts.erase(it);
1832 }
1833 // formula numbering side
1834 if ((it = find(opts.begin(), opts.end(), "leqno"))
1835 != opts.end()) {
1836 h_math_numbering_side = "left";
1837 opts.erase(it);
1838 }
1839 else if ((it = find(opts.begin(), opts.end(), "reqno"))
1840 != opts.end()) {
1841 h_math_numbering_side = "right";
1842 opts.erase(it);
1843 }
1844
1845 // paper orientation
1846 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1847 h_paperorientation = "landscape";
1848 opts.erase(it);
1849 }
1850 // paper sides
1851 if ((it = find(opts.begin(), opts.end(), "oneside"))
1852 != opts.end()) {
1853 h_papersides = "1";
1854 opts.erase(it);
1855 }
1856 if ((it = find(opts.begin(), opts.end(), "twoside"))
1857 != opts.end()) {
1858 h_papersides = "2";
1859 opts.erase(it);
1860 }
1861 // paper columns
1862 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1863 != opts.end()) {
1864 h_papercolumns = "1";
1865 opts.erase(it);
1866 }
1867 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1868 != opts.end()) {
1869 h_papercolumns = "2";
1870 opts.erase(it);
1871 }
1872 // paper sizes
1873 // some size options are known to any document classes, other sizes
1874 // are handled by the \geometry command of the geometry package
1875 handle_opt(opts, known_class_paper_sizes, h_papersize);
1876 delete_opt(opts, known_class_paper_sizes);
1877 // the remaining options
1878 h_options = join(opts, ",");
1879 // FIXME This does not work for classes that have a
1880 // different name in LyX than in LaTeX
1881 h_textclass = p.getArg('{', '}');
1882 p.skip_spaces();
1883 continue;
1884 }
1885
1886 if (t.cs() == "usepackage") {
1887 string const options = p.getArg('[', ']');
1888 string const name = p.getArg('{', '}');
1889 vector<string> vecnames;
1890 split(name, vecnames, ',');
1891 vector<string>::const_iterator it = vecnames.begin();
1892 vector<string>::const_iterator end = vecnames.end();
1893 for (; it != end; ++it)
1894 handle_package(p, trimSpaceAndEol(*it), options,
1895 in_lyx_preamble, detectEncoding);
1896 continue;
1897 }
1898
1899 if (t.cs() == "inputencoding") {
1900 string const encoding = p.getArg('{','}');
1901 Encoding const * const enc = encodings.fromLaTeXName(
1902 encoding, Encoding::inputenc, true);
1903 if (!enc) {
1904 if (!detectEncoding)
1905 cerr << "Unknown encoding " << encoding
1906 << ". Ignoring." << std::endl;
1907 } else {
1908 if (!enc->unsafe())
1909 h_inputencoding = enc->name();
1910 p.setEncoding(enc->iconvName());
1911 }
1912 continue;
1913 }
1914
1915 if (t.cs() == "newenvironment") {
1916 string const name = p.getArg('{', '}');
1917 string const opt1 = p.getFullOpt();
1918 string const opt2 = p.getFullOpt();
1919 string const beg = p.verbatim_item();
1920 string const end = p.verbatim_item();
1921 if (!in_lyx_preamble) {
1922 h_preamble << "\\newenvironment{" << name
1923 << '}' << opt1 << opt2 << '{'
1924 << beg << "}{" << end << '}';
1925 }
1926 add_known_environment(name, opt1, !opt2.empty(),
1927 from_utf8(beg), from_utf8(end));
1928 continue;
1929 }
1930
1931 if (t.cs() == "newtheorem") {
1932 bool star = false;
1933 if (p.next_token().character() == '*') {
1934 p.get_token();
1935 star = true;
1936 }
1937 string const name = p.getArg('{', '}');
1938 string const opt1 = p.getFullOpt();
1939 string const opt2 = p.getFullOpt();
1940 string const body = p.verbatim_item();
1941 string const opt3 = p.getFullOpt();
1942 string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
1943
1944 string const complete = cmd + "{" + name + '}' +
1945 opt1 + opt2 + '{' + body + '}' + opt3;
1946
1947 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
1948
1949 if (!in_lyx_preamble)
1950 h_preamble << complete;
1951 continue;
1952 }
1953
1954 if (t.cs() == "def") {
1955 string name = p.get_token().cs();
1956 // In fact, name may be more than the name:
1957 // In the test case of bug 8116
1958 // name == "csname SF@gobble@opt \endcsname".
1959 // Therefore, we need to use asInput() instead of cs().
1960 while (p.next_token().cat() != catBegin)
1961 name += p.get_token().asInput();
1962 if (!in_lyx_preamble)
1963 h_preamble << "\\def\\" << name << '{'
1964 << p.verbatim_item() << "}";
1965 continue;
1966 }
1967
1968 if (t.cs() == "newcolumntype") {
1969 string const name = p.getArg('{', '}');
1970 trimSpaceAndEol(name);
1971 int nargs = 0;
1972 string opts = p.getOpt();
1973 if (!opts.empty()) {
1974 istringstream is(string(opts, 1));
1975 is >> nargs;
1976 }
1977 special_columns_[name[0]] = nargs;
1978 h_preamble << "\\newcolumntype{" << name << "}";
1979 if (nargs)
1980 h_preamble << "[" << nargs << "]";
1981 h_preamble << "{" << p.verbatim_item() << "}";
1982 continue;
1983 }
1984
1985 if (t.cs() == "setcounter") {
1986 string const name = p.getArg('{', '}');
1987 string const content = p.getArg('{', '}');
1988 if (name == "secnumdepth")
1989 h_secnumdepth = content;
1990 else if (name == "tocdepth")
1991 h_tocdepth = content;
1992 else
1993 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1994 continue;
1995 }
1996
1997 if (t.cs() == "setlength") {
1998 string const name = p.verbatim_item();
1999 string const content = p.verbatim_item();
2000 // the paragraphs are only not indented when \parindent is set to zero
2001 if (name == "\\parindent" && content != "") {
2002 if (content[0] == '0')
2003 h_paragraph_separation = "skip";
2004 else
2005 h_paragraph_indentation = translate_len(content);
2006 } else if (name == "\\parskip") {
2007 if (content == "\\smallskipamount")
2008 h_defskip = "smallskip";
2009 else if (content == "\\medskipamount")
2010 h_defskip = "medskip";
2011 else if (content == "\\bigskipamount")
2012 h_defskip = "bigskip";
2013 else
2014 h_defskip = translate_len(content);
2015 } else if (name == "\\mathindent") {
2016 h_mathindentation = translate_len(content);
2017 } else
2018 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2019 continue;
2020 }
2021
2022 if (t.cs() == "onehalfspacing") {
2023 h_spacing = "onehalf";
2024 continue;
2025 }
2026
2027 if (t.cs() == "doublespacing") {
2028 h_spacing = "double";
2029 continue;
2030 }
2031
2032 if (t.cs() == "setstretch") {
2033 h_spacing = "other " + p.verbatim_item();
2034 continue;
2035 }
2036
2037 if (t.cs() == "synctex") {
2038 // the scheme is \synctex=value
2039 // where value can only be "1" or "-1"
2040 h_output_sync = "1";
2041 // there can be any character behind the value (e.g. a linebreak or a '\'
2042 // therefore we extract it char by char
2043 p.get_token();
2044 string value = p.get_token().asInput();
2045 if (value == "-")
2046 value += p.get_token().asInput();
2047 h_output_sync_macro = "\\synctex=" + value;
2048 continue;
2049 }
2050
2051 if (t.cs() == "begin") {
2052 string const name = p.getArg('{', '}');
2053 if (name == "document")
2054 break;
2055 h_preamble << "\\begin{" << name << "}";
2056 continue;
2057 }
2058
2059 if (t.cs() == "geometry") {
2060 vector<string> opts = split_options(p.getArg('{', '}'));
2061 handle_geometry(opts);
2062 continue;
2063 }
2064
2065 if (t.cs() == "definecolor") {
2066 string const color = p.getArg('{', '}');
2067 string const space = p.getArg('{', '}');
2068 string const value = p.getArg('{', '}');
2069 if (color == "document_fontcolor" && space == "rgb") {
2070 RGBColor c(RGBColorFromLaTeX(value));
2071 h_fontcolor = X11hexname(c);
2072 } else if (color == "note_fontcolor" && space == "rgb") {
2073 RGBColor c(RGBColorFromLaTeX(value));
2074 h_notefontcolor = X11hexname(c);
2075 } else if (color == "page_backgroundcolor" && space == "rgb") {
2076 RGBColor c(RGBColorFromLaTeX(value));
2077 h_backgroundcolor = X11hexname(c);
2078 } else if (color == "shadecolor" && space == "rgb") {
2079 RGBColor c(RGBColorFromLaTeX(value));
2080 h_boxbgcolor = X11hexname(c);
2081 } else {
2082 h_preamble << "\\definecolor{" << color
2083 << "}{" << space << "}{" << value
2084 << '}';
2085 }
2086 continue;
2087 }
2088
2089 if (t.cs() == "bibliographystyle") {
2090 h_biblio_style = p.verbatim_item();
2091 continue;
2092 }
2093
2094 if (t.cs() == "jurabibsetup") {
2095 // FIXME p.getArg('{', '}') is most probably wrong (it
2096 // does not handle nested braces).
2097 // Use p.verbatim_item() instead.
2098 vector<string> jurabibsetup =
2099 split_options(p.getArg('{', '}'));
2100 // add jurabibsetup to the jurabib package options
2101 add_package("jurabib", jurabibsetup);
2102 if (!jurabibsetup.empty()) {
2103 h_preamble << "\\jurabibsetup{"
2104 << join(jurabibsetup, ",") << '}';
2105 }
2106 continue;
2107 }
2108
2109 if (t.cs() == "hypersetup") {
2110 vector<string> hypersetup =
2111 split_options(p.verbatim_item());
2112 // add hypersetup to the hyperref package options
2113 handle_hyperref(hypersetup);
2114 if (!hypersetup.empty()) {
2115 h_preamble << "\\hypersetup{"
2116 << join(hypersetup, ",") << '}';
2117 }
2118 continue;
2119 }
2120
2121 if (t.cs() == "includeonly") {
2122 vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
2123 for (auto & iofile : includeonlys) {
2124 string filename(normalize_filename(iofile));
2125 string const path = getMasterFilePath(true);
2126 // We want to preserve relative/absolute filenames,
2127 // therefore path is only used for testing
2128 if (!makeAbsPath(filename, path).exists()) {
2129 // The file extension is probably missing.
2130 // Now try to find it out.
2131 string const tex_name =
2132 find_file(filename, path,
2133 known_tex_extensions);
2134 if (!tex_name.empty())
2135 filename = tex_name;
2136 }
2137 string outname;
2138 if (makeAbsPath(filename, path).exists())
2139 fix_child_filename(filename);
2140 else
2141 cerr << "Warning: Could not find included file '"
2142 << filename << "'." << endl;
2143 outname = changeExtension(filename, "lyx");
2144 h_includeonlys.push_back(outname);
2145 }
2146 continue;
2147 }
2148
2149 if (is_known(t.cs(), known_if_3arg_commands)) {
2150 // prevent misparsing of \usepackage if it is used
2151 // as an argument (see e.g. our own output of
2152 // \@ifundefined above)
2153 string const arg1 = p.verbatim_item();
2154 string const arg2 = p.verbatim_item();
2155 string const arg3 = p.verbatim_item();
2156 // test case \@ifundefined{date}{}{\date{}}
2157 if (t.cs() == "@ifundefined" && arg1 == "date" &&
2158 arg2.empty() && arg3 == "\\date{}") {
2159 h_suppress_date = "true";
2160 // older tex2lyx versions did output
2161 // \@ifundefined{definecolor}{\usepackage{color}}{}
2162 } else if (t.cs() == "@ifundefined" &&
2163 arg1 == "definecolor" &&
2164 arg2 == "\\usepackage{color}" &&
2165 arg3.empty()) {
2166 if (!in_lyx_preamble)
2167 h_preamble << package_beg_sep
2168 << "color"
2169 << package_mid_sep
2170 << "\\@ifundefined{definecolor}{color}{}"
2171 << package_end_sep;
2172 // test for case
2173 //\@ifundefined{showcaptionsetup}{}{%
2174 // \PassOptionsToPackage{caption=false}{subfig}}
2175 // that LyX uses for subfloats
2176 } else if (t.cs() == "@ifundefined" &&
2177 arg1 == "showcaptionsetup" && arg2.empty()
2178 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
2179 ; // do nothing
2180 } else if (!in_lyx_preamble) {
2181 h_preamble << t.asInput()
2182 << '{' << arg1 << '}'
2183 << '{' << arg2 << '}'
2184 << '{' << arg3 << '}';
2185 }
2186 continue;
2187 }
2188
2189 if (is_known(t.cs(), known_if_commands)) {
2190 // must not parse anything in conditional code, since
2191 // LyX would output the parsed contents unconditionally
2192 if (!in_lyx_preamble)
2193 h_preamble << t.asInput();
2194 handle_if(p, in_lyx_preamble);
2195 continue;
2196 }
2197
2198 if (!t.cs().empty() && !in_lyx_preamble) {
2199 h_preamble << '\\' << t.cs();
2200 continue;
2201 }
2202 }
2203
2204 // remove the whitespace
2205 p.skip_spaces();
2206
2207 // Force textclass if the user wanted it
2208 if (!forceclass.empty())
2209 h_textclass = forceclass;
2210 tc.setName(h_textclass);
2211 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2212 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2213 exit(EXIT_FAILURE);
2214 }
2215 if (h_papersides.empty()) {
2216 ostringstream ss;
2217 ss << tc.sides();
2218 h_papersides = ss.str();
2219 }
2220
2221 // If the CJK package is used we cannot set the document language from
2222 // the babel options. Instead, we guess which language is used most
2223 // and set this one.
2224 default_language = h_language;
2225 if (is_full_document &&
2226 (auto_packages.find("CJK") != auto_packages.end() ||
2227 auto_packages.find("CJKutf8") != auto_packages.end())) {
2228 p.pushPosition();
2229 h_language = guessLanguage(p, default_language);
2230 p.popPosition();
2231 if (explicit_babel && h_language != default_language) {
2232 // We set the document language to a CJK language,
2233 // but babel is explicitly called in the user preamble
2234 // without options. LyX will not add the default
2235 // language to the document options if it is either
2236 // english, or no text is set as default language.
2237 // Therefore we need to add a language option explicitly.
2238 // FIXME: It would be better to remove all babel calls
2239 // from the user preamble, but this is difficult
2240 // without re-introducing bug 7861.
2241 if (h_options.empty())
2242 h_options = lyx2babel(default_language);
2243 else
2244 h_options += ',' + lyx2babel(default_language);
2245 }
2246 }
2247
2248 // Finally, set the quote style.
2249 // LyX knows the following quotes styles:
2250 // british, cjk, cjkangle, danish, english, french, german,
2251 // polish, russian, swedish and swiss
2252 // conversion list taken from
2253 // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
2254 // (quotes for kazakh are unknown)
2255 // british
2256 if (is_known(h_language, known_british_quotes_languages))
2257 h_quotes_style = "british";
2258 // cjk
2259 else if (is_known(h_language, known_cjk_quotes_languages))
2260 h_quotes_style = "cjk";
2261 // cjkangle
2262 else if (is_known(h_language, known_cjkangle_quotes_languages))
2263 h_quotes_style = "cjkangle";
2264 // danish
2265 else if (is_known(h_language, known_danish_quotes_languages))
2266 h_quotes_style = "danish";
2267 // french
2268 else if (is_known(h_language, known_french_quotes_languages))
2269 h_quotes_style = "french";
2270 // german
2271 else if (is_known(h_language, known_german_quotes_languages))
2272 h_quotes_style = "german";
2273 // polish
2274 else if (is_known(h_language, known_polish_quotes_languages))
2275 h_quotes_style = "polish";
2276 // russian
2277 else if (is_known(h_language, known_russian_quotes_languages))
2278 h_quotes_style = "russian";
2279 // swedish
2280 else if (is_known(h_language, known_swedish_quotes_languages))
2281 h_quotes_style = "swedish";
2282 // swiss
2283 else if (is_known(h_language, known_swiss_quotes_languages))
2284 h_quotes_style = "swiss";
2285 // english
2286 else if (is_known(h_language, known_english_quotes_languages))
2287 h_quotes_style = "english";
2288 }
2289
2290
parseEncoding(Parser & p,string const & forceclass)2291 string Preamble::parseEncoding(Parser & p, string const & forceclass)
2292 {
2293 TeX2LyXDocClass dummy;
2294 parse(p, forceclass, true, dummy);
2295 if (h_inputencoding != "auto" && h_inputencoding != "default")
2296 return h_inputencoding;
2297 return "";
2298 }
2299
2300
babel2lyx(string const & language)2301 string babel2lyx(string const & language)
2302 {
2303 char const * const * where = is_known(language, known_languages);
2304 if (where)
2305 return known_coded_languages[where - known_languages];
2306 return language;
2307 }
2308
2309
lyx2babel(string const & language)2310 string lyx2babel(string const & language)
2311 {
2312 char const * const * where = is_known(language, known_coded_languages);
2313 if (where)
2314 return known_languages[where - known_coded_languages];
2315 return language;
2316 }
2317
2318
polyglossia2lyx(string const & language)2319 string Preamble::polyglossia2lyx(string const & language)
2320 {
2321 char const * const * where = is_known(language, polyglossia_languages);
2322 if (where)
2323 return coded_polyglossia_languages[where - polyglossia_languages];
2324 return language;
2325 }
2326
2327
rgbcolor2code(string const & name)2328 string rgbcolor2code(string const & name)
2329 {
2330 char const * const * where = is_known(name, known_basic_colors);
2331 if (where) {
2332 // "red", "green" etc
2333 return known_basic_color_codes[where - known_basic_colors];
2334 }
2335 // "255,0,0", "0,255,0" etc
2336 RGBColor c(RGBColorFromLaTeX(name));
2337 return X11hexname(c);
2338 }
2339
2340 // }])
2341
2342
2343 } // namespace lyx
2344