X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Ftex2lyx%2Fpreamble.cpp;h=4dd8d7f28addd729cf2f2bc08a0adbab126f4edd;hb=c9a726bd43ed568a6fb3e9e4ce1d759f16790d3a;hp=414ab15f7988be283ef1b23897c43f8e43ccb693;hpb=863aebb17704433808e5996476f5b90049236418;p=lyx.git diff --git a/src/tex2lyx/preamble.cpp b/src/tex2lyx/preamble.cpp index 414ab15f79..4dd8d7f28a 100644 --- a/src/tex2lyx/preamble.cpp +++ b/src/tex2lyx/preamble.cpp @@ -15,13 +15,18 @@ #include "tex2lyx.h" +#include "LayoutFile.h" #include "Layout.h" #include "Lexer.h" #include "TextClass.h" + #include "support/convert.h" +#include "support/FileName.h" #include "support/filetools.h" #include "support/lstrings.h" +#include + #include #include #include @@ -29,26 +34,23 @@ #include #include +using namespace std; +using namespace lyx::support; +using boost::regex; +using boost::smatch; namespace lyx { -using std::istringstream; -using std::ostream; -using std::ostringstream; -using std::string; -using std::vector; -using std::cerr; -using std::endl; -using std::find; +// special columntypes +extern map special_columns; -using support::FileName; -using support::libFileSearch; -using support::isStrDbl; +map > used_packages; -// special columntypes -extern std::map special_columns; +// needed to handle encodings with babel +bool one_language = true; -std::map > used_packages; +// to avoid that the babel options overwrite the documentclass options +bool documentclass_language; namespace { @@ -85,7 +87,21 @@ const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt", "courier", "lmtt", "luximono", "fourier", "lmodern", "mathpazo", "mathptmx", "newcent", 0}; -// some ugly stuff +const char * const known_paper_sizes[] = { "a3paper", "b3paper", "a4paper", +"b4paper", "a5paper", "b5paper", "executivepaper", "legalpaper", +"letterpaper", 0}; + +const char * const known_class_paper_sizes[] = { "a4paper", "a5paper", +"executivepaper", "legalpaper", "letterpaper", 0}; + +const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin", +"bmargin", "headheight", "headsep", "footskip", "columnsep", 0}; + +const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin", +"rightmargin", "bottommargin", "headheight", "headsep", "footskip", +"columnsep", 0}; + +// default settings ostringstream h_preamble; string h_textclass = "article"; string h_options = string(); @@ -104,7 +120,8 @@ string h_paperfontsize = "default"; string h_spacing = "single"; string h_papersize = "default"; string h_use_geometry = "false"; -string h_use_amsmath = "0"; +string h_use_amsmath = "1"; +string h_use_esint = "1"; string h_cite_engine = "basic"; string h_use_bibtopic = "false"; string h_paperorientation = "portrait"; @@ -118,6 +135,7 @@ string h_papersides = string(); string h_paperpagestyle = "default"; string h_tracking_changes = "false"; string h_output_changes = "false"; +string h_margins = ""; void handle_opt(vector & opts, char const * const * what, string & target) @@ -132,17 +150,33 @@ void handle_opt(vector & opts, char const * const * what, string & targe for (; *what; ++what) { it = find(opts.begin(), opts.end(), *what); if (it != opts.end()) { + documentclass_language = true; if (it >= position) { target = *what; position = it; } - // remove found options from the list - opts.erase(it); } } } +void delete_opt(vector & opts, char const * const * what) +{ + if (opts.empty()) + return; + + // remove found options from the list + // do this after handle_opt to avoid potential memory leaks and to be able + // to find in every case the last language option + vector::iterator it; + for (; *what; ++what) { + it = find(opts.begin(), opts.end(), *what); + if (it != opts.end()) + opts.erase(it); + } +} + + /*! * Split a package options string (keyval format) into a vector. * Example input: @@ -200,84 +234,171 @@ void add_package(string const & name, vector & options) } -void handle_package(string const & name, string const & opts) +// Given is a string like "scaled=0.9", return 0.9 * 100 +string const scale_as_percentage(string const & scale) +{ + string::size_type pos = scale.find('='); + if (pos != string::npos) { + string value = scale.substr(pos + 1); + if (isStrDbl(value)) + return convert(100 * convert(value)); + } + // If the input string didn't match our expectations. + // return the default value "100" + return "100"; +} + + +void handle_package(Parser &p, string const & name, string const & opts, + bool in_lyx_preamble) { vector options = split_options(opts); add_package(name, options); - size_t pos; string scale; // roman fonts - if (is_known(name, known_roman_fonts)) + if (is_known(name, known_roman_fonts)) { h_font_roman = name; + p.skip_spaces(); + } + if (name == "fourier") { h_font_roman = "utopia"; // when font uses real small capitals if (opts == "expert") h_font_sc = "true"; + p.skip_spaces(); } - if (name == "mathpazo") + + if (name == "mathpazo") { h_font_roman = "palatino"; - if (name == "mathptmx") + p.skip_spaces(); + } + + if (name == "mathptmx") { h_font_roman = "times"; + p.skip_spaces(); + } + // sansserif fonts if (is_known(name, known_sans_fonts)) { h_font_sans = name; if (!opts.empty()) { scale = opts; - // the option is in the form "scaled=0.9" - // therefore cut of before the "=" - pos = scale.find("="); - if (pos != string::npos) { - scale.erase(0, pos + 1); - if (isStrDbl(scale)) { - // LyX needs the scale as integer, therfore multiply by 100 - scale = convert(100 * convert(scale)); - h_font_sf_scale = scale; - } - } + h_font_sf_scale = scale_as_percentage(scale); } + p.skip_spaces(); } + // typewriter fonts if (is_known(name, known_typewriter_fonts)) { h_font_typewriter = name; if (!opts.empty()) { scale = opts; - // the option is in the form "scaled=0.9" - // therefore cut of before the "=" - pos = scale.find("="); - if (pos != string::npos) { - scale.erase(0, pos + 1); - if (isStrDbl(scale)) { - // LyX needs the scale as integer, therfore multiply by 100 - scale = convert(100 * convert(scale)); - h_font_tt_scale = scale; - } - } + h_font_tt_scale = scale_as_percentage(scale); } + p.skip_spaces(); } + // font uses old-style figure - if (name == "eco") + if (name == "eco") { h_font_osf = "true"; + p.skip_spaces(); + } + + else if (name == "amsmath" || name == "amssymb") { + h_use_amsmath = "2"; + p.skip_spaces(); + } - else if (name == "amsmath" || name == "amssymb") - h_use_amsmath = "1"; - else if (name == "babel") - ; // ignore this + else if (name == "esint") { + h_use_esint = "2"; + p.skip_spaces(); + } + + else if (name == "babel" && !opts.empty()) { + // check if more than one option was used - used later for inputenc + // in case inputenc is parsed before babel, set the encoding to auto + if (options.begin() != options.end() - 1) { + one_language = false; + h_inputencoding = "auto"; + } + // only set the document language when there was not already one set + // via the documentclass options + // babel takes the the last language given in the documentclass options + // as document language. If there is no such language option, the last + // option of its \usepackage call is used. + if (documentclass_language == false) { + handle_opt(options, known_languages, h_language); + delete_opt(options, known_languages); + if (is_known(h_language, known_french_languages)) + h_language = "french"; + else if (is_known(h_language, known_german_languages)) + h_language = "german"; + else if (is_known(h_language, known_ngerman_languages)) + h_language = "ngerman"; + else if (is_known(h_language, known_russian_languages)) + h_language = "russian"; + else if (is_known(h_language, known_ukrainian_languages)) + h_language = "ukrainian"; + h_quotes_language = h_language; + } + p.skip_spaces(); + } else if (name == "fontenc") - ; // ignore this + p.skip_spaces(); // ignore this + else if (name == "inputenc") { - // only set when there is not more than one inputenc option - // therefore check for the "," character - if (opts.find(",") == string::npos) - h_inputencoding = opts; + // only set when there is not more than one inputenc + // option therefore check for the "," character also + // only set when there is not more then one babel + // language option + if (opts.find(",") == string::npos && one_language == true) { + if (opts == "ascii") + //change ascii to auto to be in the unicode range, see + //http://bugzilla.lyx.org/show_bug.cgi?id=4719 + h_inputencoding = "auto"; + else if (!opts.empty()) + h_inputencoding = opts; + } + if (!options.empty()) + p.setEncoding(options.back()); options.clear(); - } else if (name == "makeidx") - ; // ignore this + p.skip_spaces(); + } + + else if (name == "makeidx") + p.skip_spaces(); // ignore this + + else if (name == "prettyref") + p.skip_spaces(); // ignore this + + else if (name == "varioref") + p.skip_spaces(); // ignore this + else if (name == "verbatim") - ; // ignore this + p.skip_spaces(); // ignore this + + else if (name == "url") + p.skip_spaces(); // ignore this + + else if (name == "color") { + // with the following command this package is only loaded when needed for + // undefined colors, since we only support the predefined colors + h_preamble << "\\@ifundefined{definecolor}\n {\\usepackage{color}}{}\n"; + p.skip_spaces(); + } + else if (name == "graphicx") - ; // ignore this + p.skip_spaces(); // ignore this + + else if (name == "setspace") + p.skip_spaces(); // ignore this + + else if (name == "geometry") + p.skip_spaces(); // Ignore this, the geometry settings are made by the \geometry + // command. This command is handled below. + else if (is_known(name, known_languages)) { if (is_known(name, known_french_languages)) h_language = "french"; @@ -292,8 +413,10 @@ void handle_package(string const & name, string const & opts) else h_language = name; h_quotes_language = h_language; + p.skip_spaces(); + } - } else if (name == "natbib") { + else if (name == "natbib") { h_cite_engine = "natbib_authoryear"; vector::iterator it = find(options.begin(), options.end(), "authoryear"); @@ -306,13 +429,22 @@ void handle_package(string const & name, string const & opts) options.erase(it); } } - } else if (name == "jurabib") { + p.skip_spaces(); + } + + else if (name == "jurabib") { h_cite_engine = "jurabib"; - } else if (options.empty()) - h_preamble << "\\usepackage{" << name << "}\n"; - else { - h_preamble << "\\usepackage[" << opts << "]{" << name << "}\n"; - options.clear(); + p.skip_spaces(); + } + + else if (!in_lyx_preamble) { + if (options.empty()) + h_preamble << "\\usepackage{" << name << "}"; + else { + h_preamble << "\\usepackage[" << opts << "]{" + << name << "}"; + options.clear(); + } } // We need to do something with the options... @@ -326,7 +458,7 @@ void handle_package(string const & name, string const & opts) void end_preamble(ostream & os, TextClass const & /*textclass*/) { os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n" - << "\\lyxformat 247\n" + << "\\lyxformat 256\n" << "\\begin_document\n" << "\\begin_header\n" << "\\textclass " << h_textclass << "\n"; @@ -350,9 +482,11 @@ void end_preamble(ostream & os, TextClass const & /*textclass*/) << "\\papersize " << h_papersize << "\n" << "\\use_geometry " << h_use_geometry << "\n" << "\\use_amsmath " << h_use_amsmath << "\n" + << "\\use_esint " << h_use_esint << "\n" << "\\cite_engine " << h_cite_engine << "\n" << "\\use_bibtopic " << h_use_bibtopic << "\n" << "\\paperorientation " << h_paperorientation << "\n" + << h_margins << "\\secnumdepth " << h_secnumdepth << "\n" << "\\tocdepth " << h_tocdepth << "\n" << "\\paragraph_separation " << h_paragraph_separation << "\n" @@ -371,11 +505,14 @@ void end_preamble(ostream & os, TextClass const & /*textclass*/) } // anonymous namespace -TextClass const parse_preamble(Parser & p, ostream & os, string const & forceclass) +void parse_preamble(Parser & p, ostream & os, + string const & forceclass, TeX2LyXDocClass & tc) { // initialize fixed types special_columns['D'] = 3; bool is_full_document = false; + bool is_lyx_file = false; + bool in_lyx_preamble = false; // determine whether this is a full document or a fragment for inclusion while (p.good()) { @@ -398,35 +535,66 @@ TextClass const parse_preamble(Parser & p, ostream & os, string const & forcecla // // cat codes // - if (t.cat() == catLetter || - t.cat() == catSuper || - t.cat() == catSub || - t.cat() == catOther || - t.cat() == catMath || - t.cat() == catActive || - t.cat() == catBegin || - t.cat() == catEnd || - t.cat() == catAlign || - t.cat() == catParameter) - h_preamble << t.character(); - - else if (t.cat() == catSpace || t.cat() == catNewline) + if (!in_lyx_preamble && + (t.cat() == catLetter || + t.cat() == catSuper || + t.cat() == catSub || + t.cat() == catOther || + t.cat() == catMath || + t.cat() == catActive || + t.cat() == catBegin || + t.cat() == catEnd || + t.cat() == catAlign || + t.cat() == catParameter)) + h_preamble << t.character(); + + else if (!in_lyx_preamble && + (t.cat() == catSpace || t.cat() == catNewline)) h_preamble << t.asInput(); - else if (t.cat() == catComment) - h_preamble << t.asInput(); + else if (t.cat() == catComment) { + // regex to parse comments + static regex const islyxfile("%% LyX .* created this file"); + static regex const usercommands("User specified LaTeX commands"); + + string const comment = t.asInput(); + + // magically switch encoding default if it looks like XeLaTeX + static string const magicXeLaTeX = + "% This document must be compiled with XeLaTeX "; + if (comment.size() > magicXeLaTeX.size() + && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX + && h_inputencoding == "auto") { + cerr << "XeLaTeX comment found, switching to UTF8\n"; + h_inputencoding = "utf8"; + } + + smatch sub; + if (regex_search(comment, sub, islyxfile)) { + is_lyx_file = true; + in_lyx_preamble = true; + } else if (is_lyx_file + && regex_search(comment, sub, usercommands)) + in_lyx_preamble = false; + else if (!in_lyx_preamble) + h_preamble << t.asInput(); + } else if (t.cs() == "pagestyle") h_paperpagestyle = p.verbatim_item(); else if (t.cs() == "makeatletter") { + if (!is_lyx_file || !in_lyx_preamble + || p.getCatCode('@') != catLetter) + h_preamble << "\\makeatletter"; p.setCatCode('@', catLetter); - h_preamble << "\\makeatletter"; } else if (t.cs() == "makeatother") { + if (!is_lyx_file || !in_lyx_preamble + || p.getCatCode('@') != catOther) + h_preamble << "\\makeatother"; p.setCatCode('@', catOther); - h_preamble << "\\makeatother"; } else if (t.cs() == "newcommand" || t.cs() == "renewcommand" @@ -459,19 +627,7 @@ TextClass const parse_preamble(Parser & p, ostream & os, string const & forcecla h_font_default_family = family.erase(0,1); } // only non-lyxspecific stuff - if ( name != "\\noun" - && name != "\\tabularnewline" - && name != "\\LyX" - && name != "\\lyxline" - && name != "\\lyxaddress" - && name != "\\lyxrightaddress" - && name != "\\lyxdot" - && name != "\\boldsymbol" - && name != "\\lyxarrow" - && name != "\\rmdefault" - && name != "\\sfdefault" - && name != "\\ttdefault" - && name != "\\familydefault") { + if (!in_lyx_preamble) { ostringstream ss; ss << '\\' << t.cs(); if (star) @@ -491,8 +647,18 @@ TextClass const parse_preamble(Parser & p, ostream & os, string const & forcecla } else if (t.cs() == "documentclass") { + vector::iterator it; vector opts = split_options(p.getArg('[', ']')); + handle_opt(opts, known_fontsizes, h_paperfontsize); + delete_opt(opts, known_fontsizes); + // delete "pt" at the end + string::size_type i = h_paperfontsize.find("pt"); + if (i != string::npos) + h_paperfontsize.erase(i); + // to avoid that the babel options overwrite the documentclass options + documentclass_language = false; handle_opt(opts, known_languages, h_language); + delete_opt(opts, known_languages); if (is_known(h_language, known_french_languages)) h_language = "french"; else if (is_known(h_language, known_german_languages)) @@ -503,12 +669,40 @@ TextClass const parse_preamble(Parser & p, ostream & os, string const & forcecla h_language = "russian"; else if (is_known(h_language, known_ukrainian_languages)) h_language = "ukrainian"; - handle_opt(opts, known_fontsizes, h_paperfontsize); - // delete "pt" at the end - string::size_type i = h_paperfontsize.find("pt"); - if (i != string::npos) - h_paperfontsize.erase(i); h_quotes_language = h_language; + // paper orientation + if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) { + h_paperorientation = "landscape"; + opts.erase(it); + } + // paper sides + if ((it = find(opts.begin(), opts.end(), "oneside")) + != opts.end()) { + h_papersides = "1"; + opts.erase(it); + } + if ((it = find(opts.begin(), opts.end(), "twoside")) + != opts.end()) { + h_papersides = "2"; + opts.erase(it); + } + // paper columns + if ((it = find(opts.begin(), opts.end(), "onecolumn")) + != opts.end()) { + h_papercolumns = "1"; + opts.erase(it); + } + if ((it = find(opts.begin(), opts.end(), "twocolumn")) + != opts.end()) { + h_papercolumns = "2"; + opts.erase(it); + } + // paper sizes + // some size options are know to any document classes, other sizes + // are handled by the \geometry command of the geometry package + handle_opt(opts, known_class_paper_sizes, h_papersize); + delete_opt(opts, known_class_paper_sizes); + // the remaining options h_options = join(opts, ","); h_textclass = p.getArg('{', '}'); } @@ -516,16 +710,19 @@ TextClass const parse_preamble(Parser & p, ostream & os, string const & forcecla else if (t.cs() == "usepackage") { string const options = p.getArg('[', ']'); string const name = p.getArg('{', '}'); - if (options.empty() && name.find(',')) { - vector vecnames; - split(name, vecnames, ','); - vector::const_iterator it = vecnames.begin(); - vector::const_iterator end = vecnames.end(); - for (; it != end; ++it) - handle_package(trim(*it), string()); - } else { - handle_package(name, options); - } + vector vecnames; + split(name, vecnames, ','); + vector::const_iterator it = vecnames.begin(); + vector::const_iterator end = vecnames.end(); + for (; it != end; ++it) + handle_package(p, trim(*it), options, + in_lyx_preamble); + } + + else if (t.cs() == "inputencoding") { + string const encoding = p.getArg('{','}'); + h_inputencoding = encoding; + p.setEncoding(encoding); } else if (t.cs() == "newenvironment") { @@ -536,9 +733,7 @@ TextClass const parse_preamble(Parser & p, ostream & os, string const & forcecla ss << p.getOpt(); ss << '{' << p.verbatim_item() << '}'; ss << '{' << p.verbatim_item() << '}'; - if (name != "lyxcode" && name != "lyxlist" && - name != "lyxrightadress" && - name != "lyxaddress" && name != "lyxgreyedout") + if (!in_lyx_preamble) h_preamble << ss.str(); } @@ -546,8 +741,9 @@ TextClass const parse_preamble(Parser & p, ostream & os, string const & forcecla string name = p.get_token().cs(); while (p.next_token().cat() != catBegin) name += p.get_token().asString(); - h_preamble << "\\def\\" << name << '{' - << p.verbatim_item() << "}"; + if (!in_lyx_preamble) + h_preamble << "\\def\\" << name << '{' + << p.verbatim_item() << "}"; } else if (t.cs() == "newcolumntype") { @@ -557,7 +753,6 @@ TextClass const parse_preamble(Parser & p, ostream & os, string const & forcecla string opts = p.getOpt(); if (!opts.empty()) { istringstream is(string(opts, 1)); - //cerr << "opt: " << is.str() << "\n"; is >> nargs; } special_columns[name[0]] = nargs; @@ -581,15 +776,32 @@ TextClass const parse_preamble(Parser & p, ostream & os, string const & forcecla else if (t.cs() == "setlength") { string const name = p.verbatim_item(); string const content = p.verbatim_item(); - // Is this correct? - if (name == "parskip") - h_paragraph_separation = "skip"; - else if (name == "parindent") - h_paragraph_separation = "skip"; - else + // the paragraphs are only not indented when \parindent is set to zero + if (name == "\\parindent" && content != "") { + if (content[0] == '0') + h_paragraph_separation = "skip"; + } else if (name == "\\parskip") { + if (content == "\\smallskipamount") + h_defskip = "smallskip"; + else if (content == "\\medskipamount") + h_defskip = "medskip"; + else if (content == "\\bigskipamount") + h_defskip = "bigskip"; + else + h_defskip = content; + } else h_preamble << "\\setlength{" << name << "}{" << content << "}"; } + else if (t.cs() == "onehalfspacing") + h_spacing = "onehalf"; + + else if (t.cs() == "doublespacing") + h_spacing = "double"; + + else if (t.cs() == "setstretch") + h_spacing = "other " + p.verbatim_item(); + else if (t.cs() == "begin") { string const name = p.getArg('{', '}'); if (name == "document") @@ -597,6 +809,35 @@ TextClass const parse_preamble(Parser & p, ostream & os, string const & forcecla h_preamble << "\\begin{" << name << "}"; } + else if (t.cs() == "geometry") { + h_use_geometry = "true"; + vector opts = split_options(p.getArg('{', '}')); + vector::iterator it; + // paper orientation + if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) { + h_paperorientation = "landscape"; + opts.erase(it); + } + // paper size + handle_opt(opts, known_paper_sizes, h_papersize); + delete_opt(opts, known_paper_sizes); + // page margins + char const * const * margin = known_paper_margins; + int k = -1; + for (; *margin; ++margin) { + k += 1; + // search for the "=" in e.g. "lmargin=2cm" to get the value + for(size_t i = 0; i != opts.size(); i++) { + if (opts.at(i).find(*margin) != string::npos) { + string::size_type pos = opts.at(i).find("="); + string value = opts.at(i).substr(pos + 1); + string name = known_coded_paper_margins[k]; + h_margins += "\\" + name + " " + value + "\n"; + } + } + } + } + else if (t.cs() == "jurabibsetup") { vector jurabibsetup = split_options(p.getArg('{', '}')); @@ -608,7 +849,7 @@ TextClass const parse_preamble(Parser & p, ostream & os, string const & forcecla } } - else if (!t.cs().empty()) + else if (!t.cs().empty() && !in_lyx_preamble) h_preamble << '\\' << t.cs(); } p.skip_spaces(); @@ -616,22 +857,20 @@ TextClass const parse_preamble(Parser & p, ostream & os, string const & forcecla // Force textclass if the user wanted it if (!forceclass.empty()) h_textclass = forceclass; - if (noweb_mode && !lyx::support::prefixIs(h_textclass, "literate-")) + if (noweb_mode && !prefixIs(h_textclass, "literate-")) h_textclass.insert(0, "literate-"); FileName layoutfilename = libFileSearch("layouts", h_textclass, "layout"); if (layoutfilename.empty()) { cerr << "Error: Could not find layout file for textclass \"" << h_textclass << "\"." << endl; exit(1); } - TextClass textclass; - textclass.read(layoutfilename); + tc.read(layoutfilename); if (h_papersides.empty()) { ostringstream ss; - ss << textclass.sides(); + ss << tc.sides(); h_papersides = ss.str(); } - end_preamble(os, textclass); - return textclass; + end_preamble(os, tc); } // }])