From: Georg Baum Date: Sun, 4 Dec 2011 16:16:32 +0000 (+0000) Subject: Mass backport tex2lyx bug fixes. X-Git-Tag: 2.0.3~161 X-Git-Url: https://git.lyx.org/gitweb/?a=commitdiff_plain;h=159875f201d58b4450d9a91bf18d6293f4b31c17;p=features.git Mass backport tex2lyx bug fixes. tex2lyx is no identical with the version in trunk (except for cosmetic changes and file formats > 413). The output of the test cases is either unchanged or improved. git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/branches/BRANCH_2_0_X@40364 a592a061-630c-0410-9148-cb99ea01b6c8 --- diff --git a/development/cmake/src/tex2lyx/CMakeLists.txt b/development/cmake/src/tex2lyx/CMakeLists.txt index 141d1b1182..b6b0cd1118 100644 --- a/development/cmake/src/tex2lyx/CMakeLists.txt +++ b/development/cmake/src/tex2lyx/CMakeLists.txt @@ -12,8 +12,8 @@ project(${_tex2lyx}) set(LINKED_sources ${TOP_SRC_DIR}/src/lengthcommon.cpp) set(LINKED_headers) -foreach(_src insets/InsetLayout Color Counters - Encoding FloatList Floating FontInfo +foreach(_src insets/InsetLayout Author Color Counters + Encoding FloatList Floating FontInfo LaTeXPackages Layout LayoutFile LayoutModuleList Lexer ModuleList TextClass Spacing version) list(APPEND LINKED_sources ${TOP_SRC_DIR}/src/${_src}.cpp) diff --git a/development/scons/scons_manifest.py b/development/scons/scons_manifest.py index 266beabf28..f938bb0a6f 100644 --- a/development/scons/scons_manifest.py +++ b/development/scons/scons_manifest.py @@ -598,6 +598,7 @@ src_mathed_extra_files = Split(''' src_tex2lyx_header_files = Split(''' Context.h Parser.h + Preamble.h tex2lyx.h ''') @@ -607,7 +608,7 @@ src_tex2lyx_files = Split(''' Context.cpp math.cpp Parser.cpp - preamble.cpp + Preamble.cpp table.cpp tex2lyx.cpp text.cpp @@ -621,12 +622,14 @@ src_tex2lyx_copied_header_files = Split(''' src_tex2lyx_copied_files = Split(''' + Author.cpp Color.cpp Counters.cpp Encoding.cpp FloatList.cpp Floating.cpp FontInfo.cpp + LaTeXPackages.cpp Layout.cpp LayoutFile.cpp LayoutModuleList.cpp diff --git a/lib/syntax.default b/lib/syntax.default index f713cfae8a..9bfed3faba 100644 --- a/lib/syntax.default +++ b/lib/syntax.default @@ -370,6 +370,9 @@ $$ % is some code that may occur in a .tex file created by LyX. The re-import % works only because the first argument of \texorpdfstring is specified as % translatable in this file. +% If a command puts the contents of an argument inside an own group, use +% "group" instead of "translate". Otherwise things like font changes would +% survive the end of the group in LyX (bug 3036). \abstractname \Acrobatmenu{}{} % from the hyperref package @@ -515,8 +518,8 @@ $$ \makelabels \maketitle \MakeShortVerb{} % from doc.sty, argument must be verbatim -\markboth{}{translate} -\markright{translate} +\markboth{group}{group} +\markright{group} \mathversion{} \mbox{translate} \mddefault @@ -703,30 +706,30 @@ thebibliography{} % Environments that start math mode. % $...$, $$...$$, \(...\) and \[...\] are hardcoded in tex2lyx. -% The arguments are currently ignored. +% The arguments are currently ignored (apart from displaymath). \begin{mathenvironments} -equation -equation* -eqnarray -eqnarray* -align -align* -gather -gather* -multline -multline* -math -displaymath -flalign -flalign +equation{displaymath} +equation*{displaymath} +eqnarray{displaymath} +eqnarray*{displaymath} +align{displaymath} +align*{displaymath} +gather{displaymath} +gather*{displaymath} +multline{displaymath} +multline*{displaymath} +math{} +displaymath{displaymath} +flalign{displaymath} +flalign{displaymath} % These require extra args -alignat -alignat* -xalignat -xalignat* -xxalignat +alignat{}{displaymath} +alignat*{displaymath} +xalignat{}{displaymath} +xalignat*{}{displaymath} +xxalignat{}{displaymath} % These are not known by LyX but work nevertheless: -empheq +empheq[]{}{displaymath} \end{mathenvironments} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/src/Changes.h b/src/Changes.h index 435d44bca3..84c4fea707 100644 --- a/src/Changes.h +++ b/src/Changes.h @@ -41,7 +41,7 @@ public: DELETED // deleted text }; - explicit Change(Type t = UNCHANGED, int a = 0, time_t ct = current_time()) + explicit Change(Type t = UNCHANGED, int a = 0, time_t ct = support::current_time()) : type(t), author(a), changetime(ct) {} /// is the change similar to the given change such that both can be merged? diff --git a/src/LaTeXFeatures.cpp b/src/LaTeXFeatures.cpp index da17de0d41..d3a3fc2c0b 100644 --- a/src/LaTeXFeatures.cpp +++ b/src/LaTeXFeatures.cpp @@ -24,6 +24,7 @@ #include "Floating.h" #include "FloatList.h" #include "Language.h" +#include "LaTeXPackages.h" #include "Layout.h" #include "Lexer.h" #include "LyXRC.h" @@ -281,8 +282,6 @@ static docstring const lyxref_def = from_ascii( // ///////////////////////////////////////////////////////////////////// -LaTeXFeatures::Packages LaTeXFeatures::packages_; - LaTeXFeatures::LaTeXFeatures(Buffer const & b, BufferParams const & p, OutputParams const & r) @@ -334,36 +333,6 @@ void LaTeXFeatures::require(set const & names) } -void LaTeXFeatures::getAvailable() -{ - Lexer lex; - support::FileName const real_file = libFileSearch("", "packages.lst"); - - if (real_file.empty()) - return; - - lex.setFile(real_file); - - if (!lex.isOK()) - return; - - // Make sure that we are clean - packages_.clear(); - - bool finished = false; - // Parse config-file - while (lex.isOK() && !finished) { - switch (lex.lex()) { - case Lexer::LEX_FEOF: - finished = true; - break; - default: - packages_.insert(lex.getString()); - } - } -} - - void LaTeXFeatures::useLayout(docstring const & layoutname) { // Some code to avoid loops in dependency definition @@ -441,13 +410,7 @@ bool LaTeXFeatures::isAvailable(string const & name) //LYXERR0("from=[" << from << "] to=[" << to << "]"); return theConverters().isReachable(from, to); } - - if (packages_.empty()) - getAvailable(); - string n = name; - if (suffixIs(n, ".sty")) - n.erase(name.length() - 4); - return packages_.find(n) != packages_.end(); + return LaTeXPackages::isAvailable(name); } diff --git a/src/LaTeXFeatures.h b/src/LaTeXFeatures.h index a31ea3a2e5..8cbe3dcb0f 100644 --- a/src/LaTeXFeatures.h +++ b/src/LaTeXFeatures.h @@ -86,8 +86,6 @@ public: void require(std::string const & name); /// Add a set of feature names requirements void require(std::set const & names); - /// Which of the required packages are installed? - static void getAvailable(); /// Is the (required) package available? static bool isAvailable(std::string const & name); /// Has the package been required? @@ -149,10 +147,6 @@ private: typedef std::list SnippetList; /// SnippetList preamble_snippets_; - /// The available (required) packages - typedef std::set Packages; - /// - static Packages packages_; /// typedef std::set LanguageList; /// used languages (only those that are supported by babel) diff --git a/src/LaTeXPackages.cpp b/src/LaTeXPackages.cpp new file mode 100644 index 0000000000..a231a2294a --- /dev/null +++ b/src/LaTeXPackages.cpp @@ -0,0 +1,75 @@ +/** + * \file LaTeXPackages.cpp + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author José Matos + * \author Lars Gullik Bjønnes + * \author Jean-Marc Lasgouttes + * \author Jürgen Vigna + * \author André Pönitz + * + * Full author contact details are available in file CREDITS. + */ + +#include + +#include "LaTeXPackages.h" + +#include "Lexer.h" + +#include "support/FileName.h" +#include "support/filetools.h" +#include "support/lstrings.h" + + +using namespace std; +using namespace lyx::support; + + +namespace lyx { + +LaTeXPackages::Packages LaTeXPackages::packages_; + + +void LaTeXPackages::getAvailable() +{ + Lexer lex; + support::FileName const real_file = libFileSearch("", "packages.lst"); + + if (real_file.empty()) + return; + + lex.setFile(real_file); + + if (!lex.isOK()) + return; + + // Make sure that we are clean + packages_.clear(); + + bool finished = false; + // Parse config-file + while (lex.isOK() && !finished) { + switch (lex.lex()) { + case Lexer::LEX_FEOF: + finished = true; + break; + default: + packages_.insert(lex.getString()); + } + } +} + + +bool LaTeXPackages::isAvailable(string const & name) +{ + if (packages_.empty()) + getAvailable(); + string n = name; + if (suffixIs(n, ".sty")) + n.erase(name.length() - 4); + return packages_.find(n) != packages_.end(); +} + +} // namespace lyx diff --git a/src/LaTeXPackages.h b/src/LaTeXPackages.h new file mode 100644 index 0000000000..ff67e0a460 --- /dev/null +++ b/src/LaTeXPackages.h @@ -0,0 +1,41 @@ +// -*- C++ -*- +/** + * \file LaTeXPackages.h + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author Lars Gullik Bjønnes + * \author Jean-Marc Lasgouttes + * + * Full author contact details are available in file CREDITS. + */ + +#ifndef LATEXPACKAGES_H +#define LATEXPACKAGES_H + +#include +#include + + +namespace lyx { + + +/** The list of avilable LaTeX packages + */ +class LaTeXPackages { +public: + /// Which of the required packages are installed? + static void getAvailable(); + /// Is the (required) package available? + static bool isAvailable(std::string const & name); +private: + /// The available (required) packages + typedef std::set Packages; + /// + static Packages packages_; +}; + + +} // namespace lyx + +#endif diff --git a/src/Makefile.am b/src/Makefile.am index b1f8869721..4c9e979886 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -141,6 +141,7 @@ SOURCEFILESCORE = \ Language.cpp \ LaTeX.cpp \ LaTeXFeatures.cpp \ + LaTeXPackages.cpp \ LayoutFile.cpp \ LayoutModuleList.cpp \ Length.cpp \ @@ -239,6 +240,7 @@ HEADERFILESCORE = \ KeySequence.h \ Language.h \ LaTeXFeatures.h \ + LaTeXPackages.h \ LaTeX.h \ Layout.h \ LayoutEnums.h \ diff --git a/src/SpellChecker.h b/src/SpellChecker.h index 55651e0231..f94f5c5f3f 100644 --- a/src/SpellChecker.h +++ b/src/SpellChecker.h @@ -14,7 +14,6 @@ #define SPELL_BASE_H #include "support/strfwd.h" -#include "support/lyxtime.h" namespace lyx { diff --git a/src/frontends/qt4/GuiApplication.cpp b/src/frontends/qt4/GuiApplication.cpp index ed60ecc18b..a2c60c9d29 100644 --- a/src/frontends/qt4/GuiApplication.cpp +++ b/src/frontends/qt4/GuiApplication.cpp @@ -41,7 +41,7 @@ #include "Intl.h" #include "KeyMap.h" #include "Language.h" -#include "LaTeXFeatures.h" +#include "LaTeXPackages.h" #include "Lexer.h" #include "LyX.h" #include "LyXAction.h" @@ -1211,7 +1211,7 @@ void GuiApplication::reconfigure(string const & option) current_view_->message(_("Reloading configuration...")); lyxrc.read(libFileSearch(QString(), "lyxrc.defaults"), false); // Re-read packages.lst - LaTeXFeatures::getAvailable(); + LaTeXPackages::getAvailable(); if (ret) Alert::information(_("System reconfiguration failed"), diff --git a/src/frontends/qt4/GuiChanges.cpp b/src/frontends/qt4/GuiChanges.cpp index 63268ae24e..a27d24f580 100644 --- a/src/frontends/qt4/GuiChanges.cpp +++ b/src/frontends/qt4/GuiChanges.cpp @@ -34,6 +34,7 @@ namespace lyx { namespace frontend { using support::bformat; +using support::formatted_time; GuiChanges::GuiChanges(GuiView & lv) : GuiDialog(lv, "changes", qt_("Merge Changes")) diff --git a/src/support/lyxtime.cpp b/src/support/lyxtime.cpp index 7f2530d415..c302531e2e 100644 --- a/src/support/lyxtime.cpp +++ b/src/support/lyxtime.cpp @@ -12,9 +12,18 @@ #include "support/lyxtime.h" +#include "support/debug.h" +#include "support/environment.h" +#include "support/lstrings.h" +#include "support/qstring_helpers.h" + +#include +#include + using namespace std; namespace lyx { +namespace support { time_t current_time() { @@ -30,4 +39,63 @@ string const formatted_time(time_t t, string const & fmt) return string(date); } + +time_t from_ctime(string t) +{ + // Example for the format: "Sun Nov 6 10:39:39 2011\n" + // Generously remove trailing '\n' (and other whitespace if needed) + t = trim(t, " \t\r\n"); +#if QT_VERSION >= 0x040400 + // toDateTime() is too stupid to recognize variable amounts of + // whitespace (needed because ctime() outputs double spaces before + // single digit day numbers and hours) + t = subst(t, " ", " "); + QString const format("ddd MMM d H:mm:ss yyyy"); + QLocale loc("C"); + QDateTime loc_dt = loc.toDateTime(toqstr(t), format); + if (!loc_dt.isValid()) { + LYXERR(Debug::LOCALE, "Could not parse `" << t + << "´ (invalid format)"); + return static_cast(-1); + } + return loc_dt.toTime_t(); +#elif defined(_WIN32) +#error "The minimum required Qt version on windows is Qt 4.4." +#else + // strptime() is not available on windows (defined by POSIX) + + // strptime() uses the current locale, so we need to switch to "C" + LYXERR(Debug::LOCALE, "Setting LC_ALL and LC_TIME to C"); + string oldLC_ALL = getEnv("LC_ALL"); + string oldLC_TIME = getEnv("LC_TIME"); + if (!setEnv("LC_ALL", "C")) + LYXERR(Debug::LOCALE, "\t... LC_ALL failed!"); + if (!setEnv("LC_TIME", "C")) + LYXERR(Debug::LOCALE, "\t... LC_TIME failed!"); + + struct tm loc_tm; + char const * const format = "%a%n%b%n%d%n%T%n%Y"; + char * remainder = strptime(t.c_str(), format, &loc_tm); + + LYXERR(Debug::LOCALE, "Resetting LC_ALL and LC_TIME"); + if(!setEnv("LC_TIME", oldLC_TIME)) + LYXERR(Debug::LOCALE, "\t... LC_TIME failed!"); + if (!setEnv("LC_ALL", oldLC_ALL)) + LYXERR(Debug::LOCALE, "\t... LC_ALL failed!"); + + if (!remainder) { + LYXERR(Debug::LOCALE, "Could not parse `" << t + << "´ (invalid format)"); + return static_cast(-1); + } + if (*remainder != '\0') { + LYXERR(Debug::LOCALE, "Could not parse `" << t + << "´ (excess characters)"); + return static_cast(-1); + } + return mktime(&loc_tm); +#endif +} + +} // namespace support } // namespace lyx diff --git a/src/support/lyxtime.h b/src/support/lyxtime.h index 6a8bd27682..620965aebc 100644 --- a/src/support/lyxtime.h +++ b/src/support/lyxtime.h @@ -18,6 +18,7 @@ namespace lyx { +namespace support { time_t current_time(); @@ -27,6 +28,15 @@ time_t current_time(); */ std::string const formatted_time(time_t t, std::string const & fmt); +/** + * Inverse of ctime(). + * Since ctime() outputs the local time, the caller needs to ensure that the + * time zone and daylight saving time are the same as when \p t was created + * by ctime(). + */ +time_t from_ctime(std::string t); + +} // namespace support } // namespace lyx #endif // LYXTIME_H diff --git a/src/tex2lyx/Makefile.am b/src/tex2lyx/Makefile.am index fc39503b90..04aa8ec3b6 100644 --- a/src/tex2lyx/Makefile.am +++ b/src/tex2lyx/Makefile.am @@ -29,6 +29,7 @@ TEST_FILES = \ test/test-structure.tex LINKED_FILES = \ + ../Author.cpp \ ../Color.cpp \ ../Counters.cpp \ ../Encoding.cpp \ @@ -36,6 +37,7 @@ LINKED_FILES = \ ../Floating.cpp \ ../FontInfo.cpp \ ../insets/InsetLayout.cpp \ + ../LaTeXPackages.cpp \ ../Layout.cpp \ ../LayoutFile.cpp \ ../LayoutModuleList.cpp \ @@ -57,7 +59,8 @@ tex2lyx_SOURCES = \ math.cpp \ Parser.cpp \ Parser.h \ - preamble.cpp \ + Preamble.cpp \ + Preamble.h \ table.cpp \ tex2lyx.cpp \ tex2lyx.h \ diff --git a/src/tex2lyx/Parser.cpp b/src/tex2lyx/Parser.cpp index 5fafef9944..c48301207a 100644 --- a/src/tex2lyx/Parser.cpp +++ b/src/tex2lyx/Parser.cpp @@ -220,6 +220,20 @@ Token const Parser::next_token() } +// We return a copy here because the tokens_ vector may get reallocated +Token const Parser::next_next_token() +{ + static const Token dummy; + // If good() has not been called after the last get_token() we need + // to tokenize two more tokens. + if (pos_ + 1 >= tokens_.size()) { + tokenize_one(); + tokenize_one(); + } + return pos_ + 1 < tokens_.size() ? tokens_[pos_ + 1] : dummy; +} + + // We return a copy here because the tokens_ vector may get reallocated Token const Parser::get_token() { @@ -238,8 +252,7 @@ bool Parser::isParagraph() if (curr_token().cat() == catNewline && (curr_token().cs().size() > 1 || (next_token().cat() == catSpace && - pos_ < tokens_.size() - 1 && - tokens_[pos_ + 1].cat() == catNewline))) + next_next_token().cat() == catNewline))) return true; if (curr_token().cat() == catEscape && curr_token().cs() == "par") return true; diff --git a/src/tex2lyx/Parser.h b/src/tex2lyx/Parser.h index dbb202ddf0..5e749e3019 100644 --- a/src/tex2lyx/Parser.h +++ b/src/tex2lyx/Parser.h @@ -213,6 +213,8 @@ public: Token const curr_token() const; /// The next token. Token const next_token(); + /// The next but one token. + Token const next_next_token(); /// Make the next token current and return that. Token const get_token(); /// \return whether the current token starts a new paragraph diff --git a/src/tex2lyx/Preamble.cpp b/src/tex2lyx/Preamble.cpp new file mode 100644 index 0000000000..ff1d0419ba --- /dev/null +++ b/src/tex2lyx/Preamble.cpp @@ -0,0 +1,1481 @@ +/** + * \file Preamble.cpp + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author André Pönitz + * \author Uwe Stöhr + * + * Full author contact details are available in file CREDITS. + */ + +// {[( + +#include + +#include "Preamble.h" +#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 "support/regex.h" + +#include +#include + +using namespace std; +using namespace lyx::support; + + +namespace lyx { + +// special columntypes +extern map special_columns; + +Preamble preamble; + +namespace { + +//add this to known_languages when updating to lyxformat 266: +// "armenian" (needs special handling since not supported by standard babel) +//add these to known_languages when updating to lyxformat 268: +//"chinese-simplified", "chinese-traditional", "japanese", "korean" +// Both changes require first that support for non-babel languages (CJK, +// armtex) is added. +/** + * known babel language names (including synonyms) + * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai + * not yet supported by LyX: kurmanji + * please keep this in sync with known_coded_languages line by line! + */ +const char * const known_languages[] = {"acadian", "afrikaans", "albanian", +"american", "arabic", "arabtex", "austrian", "bahasa", "bahasai", "bahasam", +"basque", "belarusian", "brazil", "brazilian", "breton", "british", "bulgarian", +"canadian", "canadien", "catalan", "croatian", "czech", "danish", "dutch", +"english", "esperanto", "estonian", "farsi", "finnish", "francais", "french", +"frenchb", "frenchle", "frenchpro", "galician", "german", "germanb", "greek", +"hebrew", "hungarian", "icelandic", "indon", "indonesian", "interlingua", +"irish", "italian", "kazakh", "latin", "latvian", "lithuanian", "lowersorbian", +"lsorbian", "magyar", "malay", "meyalu", "mongolian", "naustrian", "newzealand", +"ngerman", "ngermanb", "norsk", "nynorsk", "polutonikogreek", "polish", +"portuges", "portuguese", "romanian", "russian", "russianb", "samin", +"scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish", +"swedish", "thai", "turkish", "turkmen", "ukraineb", "ukrainian", +"uppersorbian", "UKenglish", "USenglish", "usorbian", "vietnam", "welsh", +0}; + +/** + * the same as known_languages with .lyx names + * please keep this in sync with known_languages line by line! + */ +const char * const known_coded_languages[] = {"french", "afrikaans", "albanian", +"american", "arabic_arabi", "arabic_arabtex", "austrian", "bahasa", "bahasa", "bahasam", +"basque", "belarusian", "brazilian", "brazilian", "breton", "british", "bulgarian", +"canadian", "canadien", "catalan", "croatian", "czech", "danish", "dutch", +"english", "esperanto", "estonian", "farsi", "finnish", "french", "french", +"french", "french", "french", "galician", "german", "german", "greek", +"hebrew", "magyar", "icelandic", "bahasa", "bahasa", "interlingua", +"irish", "italian", "kazakh", "latin", "latvian", "lithuanian", "lowersorbian", +"lowersorbian", "magyar", "bahasam", "bahasam", "mongolian", "naustrian", "english", +"ngerman", "ngerman", "norsk", "nynorsk", "polutonikogreek", "polish", +"portuguese", "portuguese", "romanian", "russian", "russian", "samin", +"scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish", +"swedish", "thai", "turkish", "turkmen", "ukrainian", "ukrainian", +"uppersorbian", "uppersorbian", "english", "english", "vietnamese", "welsh", +0}; + +/// languages with english quotes (.lyx names) +const char * const known_english_quotes_languages[] = {"american", "bahasa", +"bahasam", "brazilian", "canadian", "chinese-simplified", "english", +"esperanto", "hebrew", "irish", "korean", "portuguese", "scottish", "thai", 0}; + +/// languages with french quotes (.lyx names) +const char * const known_french_quotes_languages[] = {"albanian", +"arabic_arabi", "arabic_arabtex", "basque", "canadien", "catalan", "french", +"galician", "greek", "italian", "norsk", "nynorsk", "polutonikogreek", +"russian", "spanish", "spanish-mexico", "turkish", "turkmen", "ukrainian", +"vietnamese", 0}; + +/// languages with german quotes (.lyx names) +const char * const known_german_quotes_languages[] = {"austrian", "bulgarian", +"czech", "german", "icelandic", "lithuanian", "lowersorbian", "naustrian", +"ngerman", "serbian", "serbian-latin", "slovak", "slovene", "uppersorbian", 0}; + +/// languages with polish quotes (.lyx names) +const char * const known_polish_quotes_languages[] = {"afrikaans", "croatian", +"dutch", "estonian", "magyar", "polish", "romanian", 0}; + +/// languages with swedish quotes (.lyx names) +const char * const known_swedish_quotes_languages[] = {"finnish", +"swedish", 0}; + +/// known language packages from the times before babel +const char * const known_old_language_packages[] = {"french", "frenchle", +"frenchpro", "german", "ngerman", "pmfrench", 0}; + +char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 }; + +const char * const known_roman_fonts[] = { "ae", "beraserif", "bookman", +"ccfonts", "chancery", "charter", "cmr", "fourier", "lmodern", "mathpazo", +"mathptmx", "newcent", "utopia", 0}; + +const char * const known_sans_fonts[] = { "avant", "berasans", "cmbr", "cmss", +"helvet", "lmss", 0}; + +const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt", +"courier", "lmtt", "luximono", "fourier", "lmodern", "mathpazo", "mathptmx", +"newcent", 0}; + +const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper", +"a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper", +"b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper", +"c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper", +"letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 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}; + +/// commands that can start an \if...\else...\endif sequence +const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket", +"ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf", +"ifsidecap", "ifupgreek", 0}; + +const char * const known_basic_colors[] = {"blue", "black", "cyan", "green", +"magenta", "red", "white", "yellow", 0}; + +const char * const known_basic_color_codes[] = {"#0000ff", "#000000", "#00ffff", "#00ff00", +"#ff00ff", "#ff0000", "#ffffff", "#ffff00", 0}; + +/// conditional commands with three arguments like \@ifundefined{}{}{} +const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists", +0}; + +/// packages that work only in xetex +const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian", +"fontbook", "fontwrap", "mathspec", "philokalia", "polyglossia", "unisugar", +"xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0}; + +// codes used to remove packages that are loaded automatically by LyX. +// Syntax: package_beg_seppackage_mid_seppackage_end_sep +const char package_beg_sep = '\001'; +const char package_mid_sep = '\002'; +const char package_end_sep = '\003'; + + +// returns true if at least one of the options in what has been found +bool handle_opt(vector & opts, char const * const * what, string & target) +{ + if (opts.empty()) + return false; + + bool found = false; + // the last language option is the document language (for babel and LyX) + // the last size option is the document font size + vector::iterator it; + vector::iterator position = opts.begin(); + for (; *what; ++what) { + it = find(opts.begin(), opts.end(), *what); + if (it != opts.end()) { + if (it >= position) { + found = true; + target = *what; + position = it; + } + } + } + return found; +} + + +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 + 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: + * authorformat=smallcaps, + * commabeforerest, + * titleformat=colonsep, + * bibformat={tabular,ibidem,numbered} + */ +vector split_options(string const & input) +{ + vector options; + string option; + Parser p(input); + while (p.good()) { + Token const & t = p.get_token(); + if (t.asInput() == ",") { + options.push_back(trim(option)); + option.erase(); + } else if (t.asInput() == "=") { + option += '='; + p.skip_spaces(true); + if (p.next_token().asInput() == "{") + option += '{' + p.getArg('{', '}') + '}'; + } else if (t.cat() != catSpace) + option += t.asInput(); + } + + if (!option.empty()) + options.push_back(trim(option)); + + return options; +} + + +/*! + * Retrieve a keyval option "name={value with=sign}" named \p name from + * \p options and return the value. + * The found option is also removed from \p options. + */ +string process_keyval_opt(vector & options, string name) +{ + for (size_t i = 0; i < options.size(); ++i) { + vector option; + split(options[i], option, '='); + if (option.size() < 2) + continue; + if (option[0] == name) { + options.erase(options.begin() + i); + option.erase(option.begin()); + return join(option, "="); + } + } + return ""; +} + +} // anonymous namespace + + +bool Preamble::indentParagraphs() const +{ + return h_paragraph_separation == "indent"; +} + + +bool Preamble::isPackageUsed(string const & package) const +{ + return used_packages.find(package) != used_packages.end(); +} + + +vector Preamble::getPackageOptions(string const & package) const +{ + map >::const_iterator it = used_packages.find(package); + if (it != used_packages.end()) + return it->second; + return vector(); +} + + +void Preamble::registerAutomaticallyLoadedPackage(std::string const & package) +{ + auto_packages.insert(package); +} + + +void Preamble::addModule(string const & module) +{ + used_modules.push_back(module); +} + + +void Preamble::suppressDate(bool suppress) +{ + if (suppress) + h_suppress_date = "true"; + else + h_suppress_date = "false"; +} + + +void Preamble::registerAuthor(std::string const & name) +{ + Author author(from_utf8(name), empty_docstring()); + author.setUsed(true); + authors_.record(author); + h_tracking_changes = "true"; + h_output_changes = "true"; +} + + +Author const & Preamble::getAuthor(std::string const & name) const +{ + Author author(from_utf8(name), empty_docstring()); + for (AuthorList::Authors::const_iterator it = authors_.begin(); + it != authors_.end(); it++) + if (*it == author) + return *it; + static Author const dummy; + return dummy; +} + + +void Preamble::add_package(string const & name, vector & options) +{ + // every package inherits the global options + if (used_packages.find(name) == used_packages.end()) + used_packages[name] = split_options(h_options); + + vector & v = used_packages[name]; + v.insert(v.end(), options.begin(), options.end()); + if (name == "jurabib") { + // Don't output the order argument (see the cite command + // handling code in text.cpp). + vector::iterator end = + remove(options.begin(), options.end(), "natbiborder"); + end = remove(options.begin(), end, "jurabiborder"); + options.erase(end, options.end()); + } +} + + +namespace { + +// 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"; +} + + +string remove_braces(string const & value) +{ + if (value.empty()) + return value; + if (value[0] == '{' && value[value.length()-1] == '}') + return value.substr(1, value.length()-2); + return value; +} + +} // anonymous namespace + + +Preamble::Preamble() : one_language(true) +{ + //h_backgroundcolor; + //h_boxbgcolor; + h_cite_engine = "basic"; + h_defskip = "medskip"; + //h_float_placement; + //h_fontcolor; + h_fontencoding = "default"; + h_font_roman = "default"; + h_font_sans = "default"; + h_font_typewriter = "default"; + h_font_default_family = "default"; + h_font_sc = "false"; + h_font_osf = "false"; + h_font_sf_scale = "100"; + h_font_tt_scale = "100"; + h_graphics = "default"; + h_html_be_strict = "false"; + h_html_css_as_file = "0"; + h_html_math_output = "0"; + h_inputencoding = "auto"; + h_language = "english"; + h_language_package = "none"; + //h_listings_params; + //h_margins; + //h_notefontcolor; + //h_options; + h_output_changes = "false"; + h_papercolumns = "1"; + h_paperfontsize = "default"; + h_paperorientation = "portrait"; + h_paperpagestyle = "default"; + //h_papersides; + h_papersize = "default"; + h_paragraph_indentation = "default"; + h_paragraph_separation = "indent"; + //h_pdf_title; + //h_pdf_author; + //h_pdf_subject; + //h_pdf_keywords; + h_pdf_bookmarks = "1"; + h_pdf_bookmarksnumbered = "0"; + h_pdf_bookmarksopen = "0"; + h_pdf_bookmarksopenlevel = "1"; + h_pdf_breaklinks = "0"; + h_pdf_pdfborder = "0"; + h_pdf_colorlinks = "0"; + h_pdf_backref = "section"; + h_pdf_pdfusetitle = "1"; + //h_pdf_pagemode; + //h_pdf_quoted_options; + h_quotes_language = "english"; + h_secnumdepth = "3"; + h_spacing = "single"; + h_suppress_date = "false"; + h_textclass = "article"; + h_tocdepth = "3"; + h_tracking_changes = "false"; + h_use_bibtopic = "false"; + h_use_indices = "false"; + h_use_geometry = "false"; + h_use_amsmath = "1"; + h_use_default_options = "false"; + h_use_esint = "1"; + h_use_hyperref = "0"; + h_use_mhchem = "0"; + h_use_mathdots = "0"; + h_use_refstyle = "0"; +} + + +void Preamble::handle_hyperref(vector & options) +{ + // FIXME swallow inputencoding changes that might surround the + // hyperref setup if it was written by LyX + h_use_hyperref = "1"; + // swallow "unicode=true", since LyX does always write that + vector::iterator it = + find(options.begin(), options.end(), "unicode=true"); + if (it != options.end()) + options.erase(it); + it = find(options.begin(), options.end(), "pdfusetitle"); + if (it != options.end()) { + h_pdf_pdfusetitle = "1"; + options.erase(it); + } + string bookmarks = process_keyval_opt(options, "bookmarks"); + if (bookmarks == "true") + h_pdf_bookmarks = "1"; + else if (bookmarks == "false") + h_pdf_bookmarks = "0"; + if (h_pdf_bookmarks == "1") { + string bookmarksnumbered = + process_keyval_opt(options, "bookmarksnumbered"); + if (bookmarksnumbered == "true") + h_pdf_bookmarksnumbered = "1"; + else if (bookmarksnumbered == "false") + h_pdf_bookmarksnumbered = "0"; + string bookmarksopen = + process_keyval_opt(options, "bookmarksopen"); + if (bookmarksopen == "true") + h_pdf_bookmarksopen = "1"; + else if (bookmarksopen == "false") + h_pdf_bookmarksopen = "0"; + if (h_pdf_bookmarksopen == "1") { + string bookmarksopenlevel = + process_keyval_opt(options, "bookmarksopenlevel"); + if (!bookmarksopenlevel.empty()) + h_pdf_bookmarksopenlevel = bookmarksopenlevel; + } + } + string breaklinks = process_keyval_opt(options, "breaklinks"); + if (breaklinks == "true") + h_pdf_breaklinks = "1"; + else if (breaklinks == "false") + h_pdf_breaklinks = "0"; + string pdfborder = process_keyval_opt(options, "pdfborder"); + if (pdfborder == "{0 0 0}") + h_pdf_pdfborder = "1"; + else if (pdfborder == "{0 0 1}") + h_pdf_pdfborder = "0"; + string backref = process_keyval_opt(options, "backref"); + if (!backref.empty()) + h_pdf_backref = backref; + string colorlinks = process_keyval_opt(options, "colorlinks"); + if (colorlinks == "true") + h_pdf_colorlinks = "1"; + else if (colorlinks == "false") + h_pdf_colorlinks = "0"; + string pdfpagemode = process_keyval_opt(options, "pdfpagemode"); + if (!pdfpagemode.empty()) + h_pdf_pagemode = pdfpagemode; + string pdftitle = process_keyval_opt(options, "pdftitle"); + if (!pdftitle.empty()) { + h_pdf_title = remove_braces(pdftitle); + } + string pdfauthor = process_keyval_opt(options, "pdfauthor"); + if (!pdfauthor.empty()) { + h_pdf_author = remove_braces(pdfauthor); + } + string pdfsubject = process_keyval_opt(options, "pdfsubject"); + if (!pdfsubject.empty()) + h_pdf_subject = remove_braces(pdfsubject); + string pdfkeywords = process_keyval_opt(options, "pdfkeywords"); + if (!pdfkeywords.empty()) + h_pdf_keywords = remove_braces(pdfkeywords); + if (!options.empty()) { + if (!h_pdf_quoted_options.empty()) + h_pdf_quoted_options += ','; + h_pdf_quoted_options += join(options, ","); + options.clear(); + } +} + + +void Preamble::handle_geometry(vector & options) +{ + h_use_geometry = "true"; + vector::iterator it; + // paper orientation + if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) { + h_paperorientation = "landscape"; + options.erase(it); + } + // paper size + // keyval version: "paper=letter" + string paper = process_keyval_opt(options, "paper"); + if (!paper.empty()) + h_papersize = paper + "paper"; + // alternative version: "letterpaper" + handle_opt(options, known_paper_sizes, h_papersize); + delete_opt(options, known_paper_sizes); + // page margins + char const * const * margin = known_paper_margins; + for (; *margin; ++margin) { + string value = process_keyval_opt(options, *margin); + if (!value.empty()) { + int k = margin - known_paper_margins; + string name = known_coded_paper_margins[k]; + h_margins += '\\' + name + ' ' + value + '\n'; + } + } +} + + +void Preamble::handle_package(Parser &p, string const & name, + string const & opts, bool in_lyx_preamble) +{ + vector options = split_options(opts); + add_package(name, options); + string scale; + + if (is_known(name, known_xetex_packages)) + xetex = true; + + // 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"; + } + + if (name == "mathpazo") + h_font_roman = "palatino"; + + if (name == "mathptmx") + h_font_roman = "times"; + + // sansserif fonts + if (is_known(name, known_sans_fonts)) { + h_font_sans = name; + if (!opts.empty()) { + scale = opts; + h_font_sf_scale = scale_as_percentage(scale); + } + } + + // typewriter fonts + if (is_known(name, known_typewriter_fonts)) { + // fourier can be set as roman font _only_ + // fourier as typewriter is handled in handling of \ttdefault + if (name != "fourier") { + h_font_typewriter = name; + if (!opts.empty()) { + scale = opts; + h_font_tt_scale = scale_as_percentage(scale); + } + } + } + + // font uses old-style figure + if (name == "eco") + h_font_osf = "true"; + + // after the detection and handling of special cases, we can remove the + // fonts, otherwise they would appear in the preamble, see bug #7856 + if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts) + || is_known(name, known_typewriter_fonts)) + ; + + else if (name == "amsmath" || name == "amssymb") + h_use_amsmath = "2"; + + else if (name == "esint") + h_use_esint = "2"; + + else if (name == "mhchem") + h_use_mhchem = "2"; + + else if (name == "mathdots") + h_use_mathdots = "2"; + + else if (name == "babel") { + h_language_package = "default"; + // One might think we would have to do nothing if babel is loaded + // without any options to prevent pollution of the preamble with this + // babel call in every roundtrip. + // But the user could have defined babel-specific things afterwards. So + // we need to keep it in the preamble to prevent cases like bug #7861. + if (!opts.empty()) { + // check if more than one option was used - used later for inputenc + if (options.begin() != options.end() - 1) + one_language = false; + // babel takes the last language of the option of its \usepackage + // call as document language. If there is no such language option, the + // last language in the documentclass options is used. + handle_opt(options, known_languages, h_language); + // If babel is called with options, LyX puts them by default into the + // document class options. This works for most languages, except + // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and + // perhaps in future others. + // Therefore keep the babel call as it is as the user might have + // reasons for it. + h_preamble << "\\usepackage[" << opts << "]{babel}\n"; + delete_opt(options, known_languages); + } + else + h_preamble << "\\usepackage{babel}\n"; + } + + else if (name == "fontenc") { + h_fontencoding = getStringFromVector(options, ","); + /* We could do the following for better round trip support, + * but this makes the document less portable, so I skip it: + if (h_fontencoding == lyxrc.fontenc) + h_fontencoding = "global"; + */ + options.clear(); + } + + else if (name == "inputenc" || name == "luainputenc") { + // h_inputencoding is only set when there is not more than one + // inputenc option because otherwise h_inputencoding must be + // set to "auto" (the default encoding of the document language) + // Therefore check for the "," character. + // It is also only set when there is not more than one babel + // language option. + if (opts.find(",") == string::npos && one_language == true) + h_inputencoding = opts; + if (!options.empty()) + p.setEncoding(options.back()); + options.clear(); + } + + else if (is_known(name, known_old_language_packages)) { + // known language packages from the times before babel + // if they are found and not also babel, they will be used as + // custom language package + h_language_package = "\\usepackage{" + name + "}"; + } + + else if (name == "prettyref") + ; // ignore this FIXME: Use the package separator mechanism instead + + else if (name == "varioref") + ; // ignore this FIXME: Use the package separator mechanism instead + + else if (name == "verbatim") + ; // ignore this FIXME: Use the package separator mechanism instead + + else if (name == "textcomp") + ; // ignore this FIXME: Use the package separator mechanism instead + + else if (name == "lyxskak") { + // ignore this and its options + if (!options.empty()) + options.clear(); + } + + else if (name == "array" || name == "booktabs" || name == "float" || + name == "color" || name == "hhline" || name == "longtable" || + name == "makeidx" || name == "nomencl" || name == "splitidx" || + name == "setspace" || name == "subscript" || name == "ulem" || + name == "url") { + if (!in_lyx_preamble) + h_preamble << package_beg_sep << name + << package_mid_sep << "\\usepackage{" + << name << '}' << package_end_sep; + } + + else if (name == "graphicx") + ; // ignore this FIXME: Use the package separator mechanism instead + + else if (name == "geometry") + handle_geometry(options); + + else if (name == "rotfloat") + ; // ignore this FIXME: Use the package separator mechanism instead + + else if (name == "wrapfig") + ; // ignore this FIXME: Use the package separator mechanism instead + + else if (name == "subfig") + ; // ignore this FIXME: Use the package separator mechanism instead + + else if (is_known(name, known_languages)) + h_language = name; + + else if (name == "natbib") { + h_cite_engine = "natbib_authoryear"; + vector::iterator it = + find(options.begin(), options.end(), "authoryear"); + if (it != options.end()) + options.erase(it); + else { + it = find(options.begin(), options.end(), "numbers"); + if (it != options.end()) { + h_cite_engine = "natbib_numerical"; + options.erase(it); + } + } + } + + else if (name == "jurabib") + h_cite_engine = "jurabib"; + + else if (name == "hyperref") + handle_hyperref(options); + + 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... + if (!options.empty()) + cerr << "Ignoring options '" << join(options, ",") + << "' of package " << name << '.' << endl; + + // remove the whitespace + p.skip_spaces(); +} + + +void Preamble::handle_if(Parser & p, bool in_lyx_preamble) +{ + while (p.good()) { + Token t = p.get_token(); + if (t.cat() == catEscape && + is_known(t.cs(), known_if_commands)) + handle_if(p, in_lyx_preamble); + else { + if (!in_lyx_preamble) + h_preamble << t.asInput(); + if (t.cat() == catEscape && t.cs() == "fi") + return; + } + } +} + + +bool Preamble::writeLyXHeader(ostream & os, bool subdoc) +{ + // translate from babel to LyX names + h_language = babel2lyx(h_language); + + // set the quote language + // LyX only knows the following quotes languages: + // english, swedish, german, polish, french and danish + // (quotes for "japanese" and "chinese-traditional" are missing because + // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383) + // conversion list taken from + // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage + // (quotes for kazakh and interlingua are unknown) + // danish + if (h_language == "danish") + h_quotes_language = "danish"; + // french + else if (is_known(h_language, known_french_quotes_languages)) + h_quotes_language = "french"; + // german + else if (is_known(h_language, known_german_quotes_languages)) + h_quotes_language = "german"; + // polish + else if (is_known(h_language, known_polish_quotes_languages)) + h_quotes_language = "polish"; + // swedish + else if (is_known(h_language, known_swedish_quotes_languages)) + h_quotes_language = "swedish"; + //english + else if (is_known(h_language, known_english_quotes_languages)) + h_quotes_language = "english"; + + if (contains(h_float_placement, "H")) + registerAutomaticallyLoadedPackage("float"); + if (h_spacing != "single" && h_spacing != "default") + registerAutomaticallyLoadedPackage("setspace"); + + // output the LyX file settings + os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n" + << "\\lyxformat " << LYX_FORMAT << '\n' + << "\\begin_document\n" + << "\\begin_header\n" + << "\\textclass " << h_textclass << "\n"; + string const raw = subdoc ? empty_string() : h_preamble.str(); + if (!raw.empty()) { + os << "\\begin_preamble\n"; + for (string::size_type i = 0; i < raw.size(); ++i) { + if (raw[i] == package_beg_sep) { + // Here follows some package loading code that + // must be skipped if the package is loaded + // automatically. + string::size_type j = raw.find(package_mid_sep, i); + if (j == string::npos) + return false; + string::size_type k = raw.find(package_end_sep, j); + if (k == string::npos) + return false; + string const package = raw.substr(i + 1, j - i - 1); + string const replacement = raw.substr(j + 1, k - j - 1); + if (auto_packages.find(package) == auto_packages.end()) + os << replacement; + i = k; + } else + os.put(raw[i]); + } + os << "\n\\end_preamble\n"; + } + if (!h_options.empty()) + os << "\\options " << h_options << "\n"; + os << "\\use_default_options " << h_use_default_options << "\n"; + if (!used_modules.empty()) { + os << "\\begin_modules\n"; + vector::const_iterator const end = used_modules.end(); + vector::const_iterator it = used_modules.begin(); + for (; it != end; it++) + os << *it << '\n'; + os << "\\end_modules\n"; + } + os << "\\language " << h_language << "\n" + << "\\language_package " << h_language_package << "\n" + << "\\inputencoding " << h_inputencoding << "\n" + << "\\fontencoding " << h_fontencoding << "\n" + << "\\font_roman " << h_font_roman << "\n" + << "\\font_sans " << h_font_sans << "\n" + << "\\font_typewriter " << h_font_typewriter << "\n" + << "\\font_default_family " << h_font_default_family << "\n" + << "\\font_sc " << h_font_sc << "\n" + << "\\font_osf " << h_font_osf << "\n" + << "\\font_sf_scale " << h_font_sf_scale << "\n" + << "\\font_tt_scale " << h_font_tt_scale << "\n" + << "\\graphics " << h_graphics << "\n"; + if (!h_float_placement.empty()) + os << "\\float_placement " << h_float_placement << "\n"; + os << "\\paperfontsize " << h_paperfontsize << "\n" + << "\\spacing " << h_spacing << "\n" + << "\\use_hyperref " << h_use_hyperref << '\n'; + if (h_use_hyperref == "1") { + if (!h_pdf_title.empty()) + os << "\\pdf_title \"" << h_pdf_title << "\"\n"; + if (!h_pdf_author.empty()) + os << "\\pdf_author \"" << h_pdf_author << "\"\n"; + if (!h_pdf_subject.empty()) + os << "\\pdf_subject \"" << h_pdf_subject << "\"\n"; + if (!h_pdf_keywords.empty()) + os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n"; + os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n" + "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n" + "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n" + "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n" + "\\pdf_breaklinks " << h_pdf_breaklinks << "\n" + "\\pdf_pdfborder " << h_pdf_pdfborder << "\n" + "\\pdf_colorlinks " << h_pdf_colorlinks << "\n" + "\\pdf_backref " << h_pdf_backref << "\n" + "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n'; + if (!h_pdf_pagemode.empty()) + os << "\\pdf_pagemode " << h_pdf_pagemode << '\n'; + if (!h_pdf_quoted_options.empty()) + os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n"; + } + os << "\\papersize " << h_papersize << "\n" + << "\\use_geometry " << h_use_geometry << "\n" + << "\\use_amsmath " << h_use_amsmath << "\n" + << "\\use_esint " << h_use_esint << "\n" + << "\\use_mhchem " << h_use_mhchem << "\n" + << "\\use_mathdots " << h_use_mathdots << "\n" + << "\\cite_engine " << h_cite_engine << "\n" + << "\\use_bibtopic " << h_use_bibtopic << "\n" + << "\\use_indices " << h_use_indices << "\n" + << "\\paperorientation " << h_paperorientation << '\n' + << "\\suppress_date " << h_suppress_date << '\n' + << "\\use_refstyle " << h_use_refstyle << '\n'; + if (!h_fontcolor.empty()) + os << "\\fontcolor " << h_fontcolor << '\n'; + if (!h_notefontcolor.empty()) + os << "\\notefontcolor " << h_notefontcolor << '\n'; + if (!h_backgroundcolor.empty()) + os << "\\backgroundcolor " << h_backgroundcolor << '\n'; + if (!h_boxbgcolor.empty()) + os << "\\boxbgcolor " << h_boxbgcolor << '\n'; + os << h_margins + << "\\secnumdepth " << h_secnumdepth << "\n" + << "\\tocdepth " << h_tocdepth << "\n" + << "\\paragraph_separation " << h_paragraph_separation << "\n"; + if (h_paragraph_separation == "skip") + os << "\\defskip " << h_defskip << "\n"; + else + os << "\\paragraph_indentation " << h_paragraph_indentation << "\n"; + os << "\\quotes_language " << h_quotes_language << "\n" + << "\\papercolumns " << h_papercolumns << "\n" + << "\\papersides " << h_papersides << "\n" + << "\\paperpagestyle " << h_paperpagestyle << "\n"; + if (!h_listings_params.empty()) + os << "\\listings_params " << h_listings_params << "\n"; + os << "\\tracking_changes " << h_tracking_changes << "\n" + << "\\output_changes " << h_output_changes << "\n" + << "\\html_math_output " << h_html_math_output << "\n" + << "\\html_css_as_file " << h_html_css_as_file << "\n" + << "\\html_be_strict " << h_html_be_strict << "\n" + << authors_ + << "\\end_header\n\n" + << "\\begin_body\n"; + return true; +} + + +void Preamble::parse(Parser & p, 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()) { + Token const & t = p.get_token(); + + if (t.cat() == catEscape && t.cs() == "documentclass") { + is_full_document = true; + break; + } + } + p.reset(); + + while (is_full_document && p.good()) { + Token const & t = p.get_token(); + +#ifdef FILEDEBUG + cerr << "t: " << t << "\n"; +#endif + + // + // cat codes + // + 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.cs(); + + else if (!in_lyx_preamble && + (t.cat() == catSpace || t.cat() == catNewline)) + h_preamble << t.asInput(); + + else if (t.cat() == catComment) { + 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() == "date") { + string argument = p.getArg('{', '}'); + if (argument.empty()) + h_suppress_date = "true"; + else + h_preamble << t.asInput() << '{' << argument << '}'; + } + + else if (t.cs() == "color") { + string const space = + (p.hasOpt() ? p.getOpt() : string()); + string argument = p.getArg('{', '}'); + // check the case that a standard color is used + if (space.empty() && is_known(argument, known_basic_colors)) { + h_fontcolor = rgbcolor2code(argument); + preamble.registerAutomaticallyLoadedPackage("color"); + } else if (space.empty() && argument == "document_fontcolor") + preamble.registerAutomaticallyLoadedPackage("color"); + // check the case that LyX's document_fontcolor is defined + // but not used for \color + else { + h_preamble << t.asInput(); + if (!space.empty()) + h_preamble << space; + h_preamble << '{' << argument << '}'; + // the color might already be set because \definecolor + // is parsed before this + h_fontcolor = ""; + } + } + + else if (t.cs() == "pagecolor") { + string argument = p.getArg('{', '}'); + // check the case that a standard color is used + if (is_known(argument, known_basic_colors)) { + h_backgroundcolor = rgbcolor2code(argument); + } else if (argument == "page_backgroundcolor") + preamble.registerAutomaticallyLoadedPackage("color"); + // check the case that LyX's page_backgroundcolor is defined + // but not used for \pagecolor + else { + h_preamble << t.asInput() << '{' << argument << '}'; + // the color might already be set because \definecolor + // is parsed before this + h_backgroundcolor = ""; + } + } + + else if (t.cs() == "makeatletter") { + // LyX takes care of this + p.setCatCode('@', catLetter); + } + + else if (t.cs() == "makeatother") { + // LyX takes care of this + p.setCatCode('@', catOther); + } + + else if (t.cs() == "newcommand" || t.cs() == "newcommandx" + || t.cs() == "renewcommand" || t.cs() == "renewcommandx" + || t.cs() == "providecommand" || t.cs() == "providecommandx" + || t.cs() == "DeclareRobustCommand" + || t.cs() == "DeclareRobustCommandx" + || t.cs() == "ProvideTextCommandDefault" + || t.cs() == "DeclareMathAccent") { + bool star = false; + if (p.next_token().character() == '*') { + p.get_token(); + star = true; + } + string const name = p.verbatim_item(); + string const opt1 = p.getFullOpt(); + string const opt2 = p.getFullOpt(); + string const body = p.verbatim_item(); + // font settings + if (name == "\\rmdefault") + if (is_known(body, known_roman_fonts)) + h_font_roman = body; + if (name == "\\sfdefault") + if (is_known(body, known_sans_fonts)) + h_font_sans = body; + if (name == "\\ttdefault") + if (is_known(body, known_typewriter_fonts)) + h_font_typewriter = body; + if (name == "\\familydefault") { + string family = body; + // remove leading "\" + h_font_default_family = family.erase(0,1); + } + + // remove the lyxdot definition that is re-added by LyX + // if necessary + if (name == "\\lyxdot") + in_lyx_preamble = true; + + // Add the command to the known commands + add_known_command(name, opt1, !opt2.empty(), from_utf8(body)); + + // only non-lyxspecific stuff + if (!in_lyx_preamble) { + ostringstream ss; + ss << '\\' << t.cs(); + if (star) + ss << '*'; + ss << '{' << name << '}' << opt1 << opt2 + << '{' << body << "}"; + h_preamble << ss.str(); +/* + ostream & out = in_preamble ? h_preamble : os; + out << "\\" << t.cs() << "{" << name << "}" + << opts << "{" << body << "}"; +*/ + } + } + + 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); + // The documentclass options are always parsed before the options + // of the babel call so that a language cannot overwrite the babel + // options. + handle_opt(opts, known_languages, h_language); + delete_opt(opts, known_languages); + + // 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 known 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, ","); + // FIXME This does not work for classes that have a + // different name in LyX than in LaTeX + h_textclass = p.getArg('{', '}'); + } + + else if (t.cs() == "usepackage") { + string const options = p.getArg('[', ']'); + string const name = p.getArg('{', '}'); + 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") { + string const name = p.getArg('{', '}'); + string const opt1 = p.getFullOpt(); + string const opt2 = p.getFullOpt(); + string const beg = p.verbatim_item(); + string const end = p.verbatim_item(); + if (!in_lyx_preamble) { + h_preamble << "\\newenvironment{" << name + << '}' << opt1 << opt2 << '{' + << beg << "}{" << end << '}'; + } + add_known_environment(name, opt1, !opt2.empty(), + from_utf8(beg), from_utf8(end)); + + } + + else if (t.cs() == "def") { + string name = p.get_token().cs(); + while (p.next_token().cat() != catBegin) + name += p.get_token().cs(); + if (!in_lyx_preamble) + h_preamble << "\\def\\" << name << '{' + << p.verbatim_item() << "}"; + } + + else if (t.cs() == "newcolumntype") { + string const name = p.getArg('{', '}'); + trim(name); + int nargs = 0; + string opts = p.getOpt(); + if (!opts.empty()) { + istringstream is(string(opts, 1)); + is >> nargs; + } + special_columns[name[0]] = nargs; + h_preamble << "\\newcolumntype{" << name << "}"; + if (nargs) + h_preamble << "[" << nargs << "]"; + h_preamble << "{" << p.verbatim_item() << "}"; + } + + else if (t.cs() == "setcounter") { + string const name = p.getArg('{', '}'); + string const content = p.getArg('{', '}'); + if (name == "secnumdepth") + h_secnumdepth = content; + else if (name == "tocdepth") + h_tocdepth = content; + else + h_preamble << "\\setcounter{" << name << "}{" << content << "}"; + } + + else if (t.cs() == "setlength") { + string const name = p.verbatim_item(); + string const content = p.verbatim_item(); + // 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 + h_paragraph_indentation = translate_len(content); + } 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") + break; + h_preamble << "\\begin{" << name << "}"; + } + + else if (t.cs() == "geometry") { + vector opts = split_options(p.getArg('{', '}')); + handle_geometry(opts); + } + + else if (t.cs() == "definecolor") { + string const color = p.getArg('{', '}'); + string const space = p.getArg('{', '}'); + string const value = p.getArg('{', '}'); + if (color == "document_fontcolor" && space == "rgb") { + RGBColor c(RGBColorFromLaTeX(value)); + h_fontcolor = X11hexname(c); + } else if (color == "note_fontcolor" && space == "rgb") { + RGBColor c(RGBColorFromLaTeX(value)); + h_notefontcolor = X11hexname(c); + } else if (color == "page_backgroundcolor" && space == "rgb") { + RGBColor c(RGBColorFromLaTeX(value)); + h_backgroundcolor = X11hexname(c); + } else if (color == "shadecolor" && space == "rgb") { + RGBColor c(RGBColorFromLaTeX(value)); + h_boxbgcolor = X11hexname(c); + } else { + h_preamble << "\\definecolor{" << color + << "}{" << space << "}{" << value + << '}'; + } + } + + else if (t.cs() == "jurabibsetup") { + // FIXME p.getArg('{', '}') is most probably wrong (it + // does not handle nested braces). + // Use p.verbatim_item() instead. + vector jurabibsetup = + split_options(p.getArg('{', '}')); + // add jurabibsetup to the jurabib package options + add_package("jurabib", jurabibsetup); + if (!jurabibsetup.empty()) { + h_preamble << "\\jurabibsetup{" + << join(jurabibsetup, ",") << '}'; + } + } + + else if (t.cs() == "hypersetup") { + vector hypersetup = + split_options(p.verbatim_item()); + // add hypersetup to the hyperref package options + handle_hyperref(hypersetup); + if (!hypersetup.empty()) { + h_preamble << "\\hypersetup{" + << join(hypersetup, ",") << '}'; + } + } + + else if (is_known(t.cs(), known_if_3arg_commands)) { + // prevent misparsing of \usepackage if it is used + // as an argument (see e.g. our own output of + // \@ifundefined above) + string const arg1 = p.verbatim_item(); + string const arg2 = p.verbatim_item(); + string const arg3 = p.verbatim_item(); + // test case \@ifundefined{date}{}{\date{}} + if (t.cs() == "@ifundefined" && arg1 == "date" && + arg2.empty() && arg3 == "\\date{}") { + h_suppress_date = "true"; + // older tex2lyx versions did output + // \@ifundefined{definecolor}{\usepackage{color}}{} + } else if (t.cs() == "@ifundefined" && + arg1 == "definecolor" && + arg2 == "\\usepackage{color}" && + arg3.empty()) { + if (!in_lyx_preamble) + h_preamble << package_beg_sep + << "color" + << package_mid_sep + << "\\@ifundefined{definecolor}{color}{}" + << package_end_sep; + // test for case + //\@ifundefined{showcaptionsetup}{}{% + // \PassOptionsToPackage{caption=false}{subfig}} + // that LyX uses for subfloats + } else if (t.cs() == "@ifundefined" && + arg1 == "showcaptionsetup" && arg2.empty() + && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") { + ; // do nothing + } else if (!in_lyx_preamble) { + h_preamble << t.asInput() + << '{' << arg1 << '}' + << '{' << arg2 << '}' + << '{' << arg3 << '}'; + } + } + + else if (is_known(t.cs(), known_if_commands)) { + // must not parse anything in conditional code, since + // LyX would output the parsed contents unconditionally + if (!in_lyx_preamble) + h_preamble << t.asInput(); + handle_if(p, in_lyx_preamble); + } + + else if (!t.cs().empty() && !in_lyx_preamble) + h_preamble << '\\' << t.cs(); + } + + // remove the whitespace + p.skip_spaces(); + + // Force textclass if the user wanted it + if (!forceclass.empty()) + h_textclass = forceclass; + if (noweb_mode && !prefixIs(h_textclass, "literate-")) + h_textclass.insert(0, "literate-"); + tc.setName(h_textclass); + if (!tc.load()) { + cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl; + exit(EXIT_FAILURE); + } + if (h_papersides.empty()) { + ostringstream ss; + ss << tc.sides(); + h_papersides = ss.str(); + } +} + + +string babel2lyx(string const & language) +{ + char const * const * where = is_known(language, known_languages); + if (where) + return known_coded_languages[where - known_languages]; + return language; +} + + +string rgbcolor2code(string const & name) +{ + char const * const * where = is_known(name, known_basic_colors); + if (where) { + // "red", "green" etc + return known_basic_color_codes[where - known_basic_colors]; + } + // "255,0,0", "0,255,0" etc + RGBColor c(RGBColorFromLaTeX(name)); + return X11hexname(c); +} + +// }]) + + +} // namespace lyx diff --git a/src/tex2lyx/Preamble.h b/src/tex2lyx/Preamble.h new file mode 100644 index 0000000000..a4af4db931 --- /dev/null +++ b/src/tex2lyx/Preamble.h @@ -0,0 +1,178 @@ +/** + * \file Preamble.h + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author André Pönitz + * \author Uwe Stöhr + * + * Full author contact details are available in file CREDITS. + */ + +// {[( + +#ifndef LYX_PREAMBLE_H +#define LYX_PREAMBLE_H + +#include "Author.h" + +#include +#include +#include +#include +#include +#include + + +namespace lyx { + +class Parser; +class TeX2LyXDocClass; + +class Preamble +{ +public: + Preamble(); + + /// + std::string inputencoding() const { return h_inputencoding; } + /// + std::string notefontcolor() const { return h_notefontcolor; } + /// + std::string use_indices() const { return h_use_indices; } + /// + bool indentParagraphs() const; + /// + bool isPackageUsed(std::string const & package) const; + /// + std::vector + getPackageOptions(std::string const & package) const; + /// Tell that \p package will be loaded automatically by LyX. + /// This has only an effect if \p package is prepared for + /// autoloading in parse(). + void registerAutomaticallyLoadedPackage(std::string const & package); + /// + void addModule(std::string const & module); + /// + void suppressDate(bool suppress); + /// Register an author named \p name in the author list + void registerAuthor(std::string const & name); + /// Get author named \p name (must be registered first) + Author const & getAuthor(std::string const & name) const; + + + /// Parses the LaTeX preamble into internal data + void parse(Parser & p, std::string const & forceclass, + TeX2LyXDocClass & tc); + /// Writes the LyX file header from internal data + bool writeLyXHeader(std::ostream & os, bool subdoc); + +private: + /// + std::map > used_packages; + /// Packages that will be loaded automatically by LyX + std::set auto_packages; + /// + std::vector used_modules; + + /// needed to handle encodings with babel + bool one_language; + + std::ostringstream h_preamble; + std::string h_backgroundcolor; + std::string h_boxbgcolor; + std::string h_cite_engine; + std::string h_defskip; + std::string h_float_placement; + std::string h_fontcolor; + std::string h_fontencoding; + std::string h_font_roman; + std::string h_font_sans; + std::string h_font_typewriter; + std::string h_font_default_family; + std::string h_font_sc; + std::string h_font_osf; + std::string h_font_sf_scale; + std::string h_font_tt_scale; + std::string h_graphics; + std::string h_html_be_strict; + std::string h_html_css_as_file; + std::string h_html_math_output; + std::string h_inputencoding; + std::string h_language; + std::string h_language_package; + std::string h_listings_params; + std::string h_margins; + std::string h_notefontcolor; + std::string h_options; + std::string h_output_changes; + std::string h_papercolumns; + std::string h_paperfontsize; + std::string h_paperorientation; + std::string h_paperpagestyle; + std::string h_papersides; + std::string h_papersize; + std::string h_paragraph_indentation; + /// necessary to set the separation when \setlength is parsed + std::string h_paragraph_separation; + std::string h_pdf_title; + std::string h_pdf_author; + std::string h_pdf_subject; + std::string h_pdf_keywords; + std::string h_pdf_bookmarks; + std::string h_pdf_bookmarksnumbered; + std::string h_pdf_bookmarksopen; + std::string h_pdf_bookmarksopenlevel; + std::string h_pdf_breaklinks; + std::string h_pdf_pdfborder; + std::string h_pdf_colorlinks; + std::string h_pdf_backref; + std::string h_pdf_pdfusetitle; + std::string h_pdf_pagemode; + std::string h_pdf_quoted_options; + std::string h_quotes_language; + std::string h_secnumdepth; + std::string h_spacing; + std::string h_suppress_date; + std::string h_textclass; + std::string h_tocdepth; + std::string h_tracking_changes; + std::string h_use_bibtopic; + std::string h_use_indices; + std::string h_use_geometry; + std::string h_use_amsmath; + std::string h_use_default_options; + std::string h_use_esint; + std::string h_use_hyperref; + std::string h_use_mhchem; + std::string h_use_mathdots; + std::string h_use_refstyle; + + /*! + * Add package \p name with options \p options to used_packages. + * Remove options from \p options that we don't want to output. + */ + void add_package(std::string const & name, + std::vector & options); + /// + void handle_hyperref(std::vector & options); + /// + void handle_geometry(std::vector & options); + /// + void handle_package(Parser &p, std::string const & name, + std::string const & opts, bool in_lyx_preamble); + /// + void handle_if(Parser & p, bool in_lyx_preamble); + + AuthorList authors_; +}; + + +extern Preamble preamble; + +// }]) + + +} // namespace lyx + +#endif diff --git a/src/tex2lyx/TODO.txt b/src/tex2lyx/TODO.txt index 9fb7afc53c..7a4b2e30ce 100644 --- a/src/tex2lyx/TODO.txt +++ b/src/tex2lyx/TODO.txt @@ -10,15 +10,16 @@ LyX feature: LyX inset or document setting Format LaTeX feature LyX feature -222 change tracking change tracking 224 external insets defined in InsetExternal - lib/external_templates. This is - quite difficult to recognize. + lib/external_templates. + (Date and RasterImage cannot be supported + (Chess diagram and Spreadsheet are supported) + (Xfig figure, Lilypond, Dia diagram can be supported by looking at the file extension) + (for PDFpages work is in progress by uwestoehr) 226 nothing (impossible to import) InsetBranch, \branch...\end_branch 226 transformations InsetExternal 228 draft InsetExternal 232 bibtopic InsetBibTeX -248 booktabs.sty InsetTabular 254 esint.sty \use_esint 266 armenian \language, \lang 267 XeTeX utf8 encoding @@ -45,11 +46,8 @@ Format LaTeX feature LyX feature 363 horizontal longtable alignment InsetTabular 364 branch file name suffix \filename_suffix 366 relative lengths for parskip \defskip -367 relative lengths for h and v space InsetHSpace, InsetVSpace -368 glue lengths InsetHSpace -369 author id \author -370 \date{} \suppress_date - (partly supported, see bug #7844) +367 relative lengths for h and v space InsetSpace, InsetVSpace +368 glue lengths InsetSpace 371 automatic mhchem loading \use_mhchem 375 \includeonly \{begin,end}_includeonly 376 update .aux of unincluded children \maintain_unincluded_children @@ -66,9 +64,7 @@ Format LaTeX feature LyX feature 401 feyn.sty InsetMathDiagram 402 \addcontentsline InsetBibtex bibtotoc option 404 refstyle.sty InsetRef -405 author hash \author 407 vertical offset for multirows InsetTabular 409 XeTeX \use_non_tex_fonts 411 support for polyglossia \language_package (the cases of no package, of babel and of custom package is supported) -412 tabular* InsetTabular diff --git a/src/tex2lyx/math.cpp b/src/tex2lyx/math.cpp index f615926efa..d17d1d9017 100644 --- a/src/tex2lyx/math.cpp +++ b/src/tex2lyx/math.cpp @@ -27,6 +27,16 @@ bool is_math_env(string const & name) } +bool is_display_math_env(string const & name) +{ + CommandMap::const_iterator it = known_math_environments.find(name); + if (it != known_math_environments.end()) + if (!it->second.empty()) + return it->second.back() == displaymath; + return false; +} + + void parse_math(Parser & p, ostream & os, unsigned flags, const mode_type mode) { while (p.good()) { diff --git a/src/tex2lyx/preamble.cpp b/src/tex2lyx/preamble.cpp deleted file mode 100644 index 08c3235160..0000000000 --- a/src/tex2lyx/preamble.cpp +++ /dev/null @@ -1,1349 +0,0 @@ -/** - * \file preamble.cpp - * This file is part of LyX, the document processor. - * Licence details can be found in the file COPYING. - * - * \author André Pönitz - * \author Uwe Stöhr - * - * Full author contact details are available in file CREDITS. - */ - -// {[( - -#include - -#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 "support/regex.h" - -#include -#include -#include -#include -#include -#include - -using namespace std; -using namespace lyx::support; - - -namespace lyx { - -// special columntypes -extern map special_columns; - -map > used_packages; -const char * const modules_placeholder = "\001modules\001"; - -// needed to handle encodings with babel -bool one_language = true; -string h_inputencoding = "auto"; -string h_paragraph_separation = "indent"; - -namespace { - -//add this to known_languages when updating to lyxformat 266: -// "armenian" (needs special handling since not supported by standard babel) -//add these to known_languages when updating to lyxformat 268: -//"chinese-simplified", "chinese-traditional", "japanese", "korean" -// Both changes require first that support for non-babel languages (CJK, -// armtex) is added. -/** - * known babel language names (including synonyms) - * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai - * not yet supported by LyX: kurmanji - * please keep this in sync with known_coded_languages line by line! - */ -const char * const known_languages[] = {"acadian", "afrikaans", "albanian", -"american", "arabic", "arabtex", "austrian", "bahasa", "bahasai", "bahasam", -"basque", "belarusian", "brazil", "brazilian", "breton", "british", "bulgarian", -"canadian", "canadien", "catalan", "croatian", "czech", "danish", "dutch", -"english", "esperanto", "estonian", "farsi", "finnish", "francais", "french", -"frenchb", "frenchle", "frenchpro", "galician", "german", "germanb", "greek", -"hebrew", "hungarian", "icelandic", "indon", "indonesian", "interlingua", -"irish", "italian", "kazakh", "latin", "latvian", "lithuanian", "lowersorbian", -"lsorbian", "magyar", "malay", "meyalu", "mongolian", "naustrian", "newzealand", -"ngerman", "ngermanb", "norsk", "nynorsk", "polutonikogreek", "polish", -"portuges", "portuguese", "romanian", "russian", "russianb", "samin", -"scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish", -"swedish", "thai", "turkish", "turkmen", "ukraineb", "ukrainian", -"uppersorbian", "UKenglish", "USenglish", "usorbian", "vietnam", "welsh", -0}; - -/** - * the same as known_languages with .lyx names - * please keep this in sync with known_languages line by line! - */ -const char * const known_coded_languages[] = {"french", "afrikaans", "albanian", -"american", "arabic_arabi", "arabic_arabtex", "austrian", "bahasa", "bahasa", "bahasam", -"basque", "belarusian", "brazilian", "brazilian", "breton", "british", "bulgarian", -"canadian", "canadien", "catalan", "croatian", "czech", "danish", "dutch", -"english", "esperanto", "estonian", "farsi", "finnish", "french", "french", -"french", "french", "french", "galician", "german", "german", "greek", -"hebrew", "magyar", "icelandic", "bahasa", "bahasa", "interlingua", -"irish", "italian", "kazakh", "latin", "latvian", "lithuanian", "lowersorbian", -"lowersorbian", "magyar", "bahasam", "bahasam", "mongolian", "naustrian", "english", -"ngerman", "ngerman", "norsk", "nynorsk", "polutonikogreek", "polish", -"portuguese", "portuguese", "romanian", "russian", "russian", "samin", -"scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish", -"swedish", "thai", "turkish", "turkmen", "ukrainian", "ukrainian", -"uppersorbian", "uppersorbian", "english", "english", "vietnamese", "welsh", -0}; - -/// languages with english quotes (.lyx names) -const char * const known_english_quotes_languages[] = {"american", "bahasa", -"bahasam", "brazilian", "canadian", "chinese-simplified", "english", -"esperanto", "hebrew", "irish", "korean", "portuguese", "scottish", "thai", 0}; - -/// languages with french quotes (.lyx names) -const char * const known_french_quotes_languages[] = {"albanian", -"arabic_arabi", "arabic_arabtex", "basque", "canadien", "catalan", "french", -"galician", "greek", "italian", "norsk", "nynorsk", "polutonikogreek", -"russian", "spanish", "spanish-mexico", "turkish", "turkmen", "ukrainian", -"vietnamese", 0}; - -/// languages with german quotes (.lyx names) -const char * const known_german_quotes_languages[] = {"austrian", "bulgarian", -"czech", "german", "icelandic", "lithuanian", "lowersorbian", "naustrian", -"ngerman", "serbian", "serbian-latin", "slovak", "slovene", "uppersorbian", 0}; - -/// languages with polish quotes (.lyx names) -const char * const known_polish_quotes_languages[] = {"afrikaans", "croatian", -"dutch", "estonian", "magyar", "polish", "romanian", 0}; - -/// languages with swedish quotes (.lyx names) -const char * const known_swedish_quotes_languages[] = {"finnish", -"swedish", 0}; - -/// known language packages from the times before babel -const char * const known_old_language_packages[] = {"french", "frenchle", -"frenchpro", "german", "ngerman", "pmfrench", 0}; - -char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 }; - -const char * const known_roman_fonts[] = { "ae", "beraserif", "bookman", -"ccfonts", "chancery", "charter", "cmr", "fourier", "lmodern", "mathpazo", -"mathptmx", "newcent", "utopia", 0}; - -const char * const known_sans_fonts[] = { "avant", "berasans", "cmbr", "cmss", -"helvet", "lmss", 0}; - -const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt", -"courier", "lmtt", "luximono", "fourier", "lmodern", "mathpazo", "mathptmx", -"newcent", 0}; - -const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper", -"a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper", -"b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper", -"c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper", -"letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 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}; - -/// commands that can start an \if...\else...\endif sequence -const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket", -"ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf", -"ifsidecap", "ifupgreek", 0}; - -const char * const known_basic_colors[] = {"blue", "black", "cyan", "green", -"magenta", "red", "white", "yellow", 0}; - -const char * const known_basic_color_codes[] = {"#0000ff", "#000000", "#00ffff", "#00ff00", -"#ff00ff", "#ff0000", "#ffffff", "#ffff00", 0}; - -/// conditional commands with three arguments like \@ifundefined{}{}{} -const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists", -0}; - -// default settings -ostringstream h_preamble; -string h_textclass = "article"; -string h_use_default_options = "false"; -string h_options; -string h_language = "english"; -string h_language_package = "none"; -string h_fontencoding = "default"; -string h_font_roman = "default"; -string h_font_sans = "default"; -string h_font_typewriter = "default"; -string h_font_default_family = "default"; -string h_font_sc = "false"; -string h_font_osf = "false"; -string h_font_sf_scale = "100"; -string h_font_tt_scale = "100"; -string h_graphics = "default"; -string h_float_placement; -string h_paperfontsize = "default"; -string h_spacing = "single"; -string h_use_hyperref = "0"; -string h_pdf_title; -string h_pdf_author; -string h_pdf_subject; -string h_pdf_keywords; -string h_pdf_bookmarks = "1"; -string h_pdf_bookmarksnumbered = "0"; -string h_pdf_bookmarksopen = "0"; -string h_pdf_bookmarksopenlevel = "1"; -string h_pdf_breaklinks = "0"; -string h_pdf_pdfborder = "0"; -string h_pdf_colorlinks = "0"; -string h_pdf_backref = "section"; -string h_pdf_pdfusetitle = "1"; -string h_pdf_pagemode; -string h_pdf_quoted_options; -string h_papersize = "default"; -string h_use_geometry = "false"; -string h_use_amsmath = "1"; -string h_use_esint = "1"; -string h_use_mhchem = "0"; -string h_use_mathdots = "0"; -string h_cite_engine = "basic"; -string h_use_bibtopic = "false"; -string h_paperorientation = "portrait"; -string h_suppress_date = "false"; -string h_use_refstyle = "0"; -string h_backgroundcolor; -string h_boxbgcolor; -string h_fontcolor; -string h_notefontcolor; -string h_secnumdepth = "3"; -string h_tocdepth = "3"; -string h_defskip = "medskip"; -string h_paragraph_indentation = "default"; -string h_quotes_language = "english"; -string h_papercolumns = "1"; -string h_papersides; -string h_paperpagestyle = "default"; -string h_listings_params; -string h_tracking_changes = "false"; -string h_output_changes = "false"; -string h_html_math_output = "0"; -string h_html_css_as_file = "0"; -string h_html_be_strict = "false"; -string h_margins; - - -// returns true if at least one of the options in what has been found -bool handle_opt(vector & opts, char const * const * what, string & target) -{ - if (opts.empty()) - return false; - - bool found = false; - // the last language option is the document language (for babel and LyX) - // the last size option is the document font size - vector::iterator it; - vector::iterator position = opts.begin(); - for (; *what; ++what) { - it = find(opts.begin(), opts.end(), *what); - if (it != opts.end()) { - if (it >= position) { - found = true; - target = *what; - position = it; - } - } - } - return found; -} - - -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 - 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: - * authorformat=smallcaps, - * commabeforerest, - * titleformat=colonsep, - * bibformat={tabular,ibidem,numbered} - */ -vector split_options(string const & input) -{ - vector options; - string option; - Parser p(input); - while (p.good()) { - Token const & t = p.get_token(); - if (t.asInput() == ",") { - options.push_back(trim(option)); - option.erase(); - } else if (t.asInput() == "=") { - option += '='; - p.skip_spaces(true); - if (p.next_token().asInput() == "{") - option += '{' + p.getArg('{', '}') + '}'; - } else if (t.cat() != catSpace) - option += t.asInput(); - } - - if (!option.empty()) - options.push_back(trim(option)); - - return options; -} - - -/*! - * Retrieve a keyval option "name={value with=sign}" named \p name from - * \p options and return the value. - * The found option is also removed from \p options. - */ -string process_keyval_opt(vector & options, string name) -{ - for (size_t i = 0; i < options.size(); ++i) { - vector option; - split(options[i], option, '='); - if (option.size() < 2) - continue; - if (option[0] == name) { - options.erase(options.begin() + i); - option.erase(option.begin()); - return join(option, "="); - } - } - return ""; -} - - -/*! - * Add package \p name with options \p options to used_packages. - * Remove options from \p options that we don't want to output. - */ -void add_package(string const & name, vector & options) -{ - // every package inherits the global options - if (used_packages.find(name) == used_packages.end()) - used_packages[name] = split_options(h_options); - - vector & v = used_packages[name]; - v.insert(v.end(), options.begin(), options.end()); - if (name == "jurabib") { - // Don't output the order argument (see the cite command - // handling code in text.cpp). - vector::iterator end = - remove(options.begin(), options.end(), "natbiborder"); - end = remove(options.begin(), end, "jurabiborder"); - options.erase(end, options.end()); - } -} - - -// 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"; -} - - -string remove_braces(string const & value) -{ - if (value.empty()) - return value; - if (value[0] == '{' && value[value.length()-1] == '}') - return value.substr(1, value.length()-2); - return value; -} - - -void handle_hyperref(vector & options) -{ - // FIXME swallow inputencoding changes that might surround the - // hyperref setup if it was written by LyX - h_use_hyperref = "1"; - // swallow "unicode=true", since LyX does always write that - vector::iterator it = - find(options.begin(), options.end(), "unicode=true"); - if (it != options.end()) - options.erase(it); - it = find(options.begin(), options.end(), "pdfusetitle"); - if (it != options.end()) { - h_pdf_pdfusetitle = "1"; - options.erase(it); - } - string bookmarks = process_keyval_opt(options, "bookmarks"); - if (bookmarks == "true") - h_pdf_bookmarks = "1"; - else if (bookmarks == "false") - h_pdf_bookmarks = "0"; - if (h_pdf_bookmarks == "1") { - string bookmarksnumbered = - process_keyval_opt(options, "bookmarksnumbered"); - if (bookmarksnumbered == "true") - h_pdf_bookmarksnumbered = "1"; - else if (bookmarksnumbered == "false") - h_pdf_bookmarksnumbered = "0"; - string bookmarksopen = - process_keyval_opt(options, "bookmarksopen"); - if (bookmarksopen == "true") - h_pdf_bookmarksopen = "1"; - else if (bookmarksopen == "false") - h_pdf_bookmarksopen = "0"; - if (h_pdf_bookmarksopen == "1") { - string bookmarksopenlevel = - process_keyval_opt(options, "bookmarksopenlevel"); - if (!bookmarksopenlevel.empty()) - h_pdf_bookmarksopenlevel = bookmarksopenlevel; - } - } - string breaklinks = process_keyval_opt(options, "breaklinks"); - if (breaklinks == "true") - h_pdf_breaklinks = "1"; - else if (breaklinks == "false") - h_pdf_breaklinks = "0"; - string pdfborder = process_keyval_opt(options, "pdfborder"); - if (pdfborder == "{0 0 0}") - h_pdf_pdfborder = "1"; - else if (pdfborder == "{0 0 1}") - h_pdf_pdfborder = "0"; - string backref = process_keyval_opt(options, "backref"); - if (!backref.empty()) - h_pdf_backref = backref; - string colorlinks = process_keyval_opt(options, "colorlinks"); - if (colorlinks == "true") - h_pdf_colorlinks = "1"; - else if (colorlinks == "false") - h_pdf_colorlinks = "0"; - string pdfpagemode = process_keyval_opt(options, "pdfpagemode"); - if (!pdfpagemode.empty()) - h_pdf_pagemode = pdfpagemode; - string pdftitle = process_keyval_opt(options, "pdftitle"); - if (!pdftitle.empty()) { - h_pdf_title = remove_braces(pdftitle); - } - string pdfauthor = process_keyval_opt(options, "pdfauthor"); - if (!pdfauthor.empty()) { - h_pdf_author = remove_braces(pdfauthor); - } - string pdfsubject = process_keyval_opt(options, "pdfsubject"); - if (!pdfsubject.empty()) - h_pdf_subject = remove_braces(pdfsubject); - string pdfkeywords = process_keyval_opt(options, "pdfkeywords"); - if (!pdfkeywords.empty()) - h_pdf_keywords = remove_braces(pdfkeywords); - if (!options.empty()) { - if (!h_pdf_quoted_options.empty()) - h_pdf_quoted_options += ','; - h_pdf_quoted_options += join(options, ","); - options.clear(); - } -} - - -void handle_package(Parser &p, string const & name, string const & opts, - bool in_lyx_preamble) -{ - vector options = split_options(opts); - add_package(name, options); - string scale; - - // 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"; - } - - if (name == "mathpazo") - h_font_roman = "palatino"; - - if (name == "mathptmx") - h_font_roman = "times"; - - // sansserif fonts - if (is_known(name, known_sans_fonts)) { - h_font_sans = name; - if (!opts.empty()) { - scale = opts; - h_font_sf_scale = scale_as_percentage(scale); - } - } - - // typewriter fonts - if (is_known(name, known_typewriter_fonts)) { - // fourier can be set as roman font _only_ - // fourier as typewriter is handled in handling of \ttdefault - if (name != "fourier") { - h_font_typewriter = name; - if (!opts.empty()) { - scale = opts; - h_font_tt_scale = scale_as_percentage(scale); - } - } - } - - // font uses old-style figure - if (name == "eco") - h_font_osf = "true"; - - // after the detection and handling of special cases, we can remove the - // fonts, otherwise they would appear in the preamble, see bug #7856 - if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts) - || is_known(name, known_typewriter_fonts)) - ; - - else if (name == "amsmath" || name == "amssymb") - h_use_amsmath = "2"; - - else if (name == "esint") - h_use_esint = "2"; - - else if (name == "mhchem") - h_use_mhchem = "2"; - - else if (name == "mathdots") - h_use_mathdots = "2"; - - else if (name == "babel") { - h_language_package = "default"; - // One might think we would have to do nothing if babel is loaded - // without any options to prevent pollution of the preamble with this - // babel call in every roundtrip. - // But the user could have defined babel-specific things afterwards. So - // we need to keep it in the preamble to prevent cases like bug #7861. - if (!opts.empty()) { - // check if more than one option was used - used later for inputenc - if (options.begin() != options.end() - 1) - one_language = false; - // babel takes the last language of the option of its \usepackage - // call as document language. If there is no such language option, the - // last language in the documentclass options is used. - handle_opt(options, known_languages, h_language); - // If babel is called with options, LyX puts them by default into the - // document class options. This works for most languages, except - // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and - // perhaps in future others. - // Therefore keep the babel call as it is as the user might have - // reasons for it. - h_preamble << "\\usepackage[" << opts << "]{babel}\n"; - delete_opt(options, known_languages); - } - else - h_preamble << "\\usepackage{babel}\n"; - } - - else if (name == "fontenc") { - h_fontencoding = getStringFromVector(options, ","); - /* We could do the following for better round trip support, - * but this makes the document less portable, so I skip it: - if (h_fontencoding == lyxrc.fontenc) - h_fontencoding = "global"; - */ - options.clear(); - } - - else if (name == "inputenc" || name == "luainputenc") { - // h_inputencoding is only set when there is not more than one - // inputenc option because otherwise h_inputencoding must be - // set to "auto" (the default encoding of the document language) - // Therefore check for the "," character. - // It is also only set when there is not more than one babel - // language option. - if (opts.find(",") == string::npos && one_language == true) - h_inputencoding = opts; - if (!options.empty()) - p.setEncoding(options.back()); - options.clear(); - } - - else if (is_known(name, known_old_language_packages)) { - // known language packages from the times before babel - // if they are found and not also babel, they will be used as - // cutom language package - h_language_package = "\\usepackage{" + name + "}"; - } - - else if (name == "makeidx") - ; // ignore this - - else if (name == "prettyref") - ; // ignore this - - else if (name == "varioref") - ; // ignore this - - else if (name == "verbatim") - ; // ignore this - - else if (name == "nomencl") - ; // ignore this - - else if (name == "textcomp") - ; // ignore this - - else if (name == "url") - ; // ignore this - - else if (name == "subscript") - ; // 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"; - } - - else if (name == "graphicx") - ; // ignore this - - else if (name == "setspace") - ; // ignore this - -#if 0 - // do not ignore as long as we don't support all commands (e.g. \xout is missing) - else if (name == "ulem") - ; // ignore this -#endif - - else if (name == "geometry") - ; // Ignore this, the geometry settings are made by the \geometry - // command. This command is handled below. - - else if (name == "rotfloat") - ; // ignore this - - else if (name == "wrapfig") - ; // ignore this - - else if (name == "subfig") - ; // ignore this - - else if (is_known(name, known_languages)) - h_language = name; - - else if (name == "natbib") { - h_cite_engine = "natbib_authoryear"; - vector::iterator it = - find(options.begin(), options.end(), "authoryear"); - if (it != options.end()) - options.erase(it); - else { - it = find(options.begin(), options.end(), "numbers"); - if (it != options.end()) { - h_cite_engine = "natbib_numerical"; - options.erase(it); - } - } - } - - else if (name == "jurabib") - h_cite_engine = "jurabib"; - - else if (name == "hyperref") - handle_hyperref(options); - - 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... - if (!options.empty()) - cerr << "Ignoring options '" << join(options, ",") - << "' of package " << name << '.' << endl; - - // remove the whitespace - p.skip_spaces(); -} - - -void handle_if(Parser & p, bool in_lyx_preamble) -{ - while (p.good()) { - Token t = p.get_token(); - if (t.cat() == catEscape && - is_known(t.cs(), known_if_commands)) - handle_if(p, in_lyx_preamble); - else { - if (!in_lyx_preamble) - h_preamble << t.asInput(); - if (t.cat() == catEscape && t.cs() == "fi") - return; - } - } -} - - -void end_preamble(ostream & os, TextClass const & /*textclass*/) -{ - // translate from babel to LyX names - h_language = babel2lyx(h_language); - - // set the quote language - // LyX only knows the following quotes languages: - // english, swedish, german, polish, french and danish - // (quotes for "japanese" and "chinese-traditional" are missing because - // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383) - // conversion list taken from - // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage - // (quotes for kazakh and interlingua are unknown) - // danish - if (h_language == "danish") - h_quotes_language = "danish"; - // french - else if (is_known(h_language, known_french_quotes_languages)) - h_quotes_language = "french"; - // german - else if (is_known(h_language, known_german_quotes_languages)) - h_quotes_language = "german"; - // polish - else if (is_known(h_language, known_polish_quotes_languages)) - h_quotes_language = "polish"; - // swedish - else if (is_known(h_language, known_swedish_quotes_languages)) - h_quotes_language = "swedish"; - //english - else if (is_known(h_language, known_english_quotes_languages)) - h_quotes_language = "english"; - - // output the LyX file settings - os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n" - << "\\lyxformat " << LYX_FORMAT << '\n' - << "\\begin_document\n" - << "\\begin_header\n" - << "\\textclass " << h_textclass << "\n"; - if (!h_preamble.str().empty()) - os << "\\begin_preamble\n" << h_preamble.str() << "\n\\end_preamble\n"; - if (!h_options.empty()) - os << "\\options " << h_options << "\n"; - os << "\\use_default_options " << h_use_default_options << "\n" - << modules_placeholder - << "\\language " << h_language << "\n" - << "\\language_package " << h_language_package << "\n" - << "\\inputencoding " << h_inputencoding << "\n" - << "\\fontencoding " << h_fontencoding << "\n" - << "\\font_roman " << h_font_roman << "\n" - << "\\font_sans " << h_font_sans << "\n" - << "\\font_typewriter " << h_font_typewriter << "\n" - << "\\font_default_family " << h_font_default_family << "\n" - << "\\font_sc " << h_font_sc << "\n" - << "\\font_osf " << h_font_osf << "\n" - << "\\font_sf_scale " << h_font_sf_scale << "\n" - << "\\font_tt_scale " << h_font_tt_scale << "\n" - << "\\graphics " << h_graphics << "\n"; - if (!h_float_placement.empty()) - os << "\\float_placement " << h_float_placement << "\n"; - os << "\\paperfontsize " << h_paperfontsize << "\n" - << "\\spacing " << h_spacing << "\n" - << "\\use_hyperref " << h_use_hyperref << '\n'; - if (h_use_hyperref == "1") { - if (!h_pdf_title.empty()) - os << "\\pdf_title \"" << h_pdf_title << "\"\n"; - if (!h_pdf_author.empty()) - os << "\\pdf_author \"" << h_pdf_author << "\"\n"; - if (!h_pdf_subject.empty()) - os << "\\pdf_subject \"" << h_pdf_subject << "\"\n"; - if (!h_pdf_keywords.empty()) - os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n"; - os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n" - "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n" - "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n" - "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n" - "\\pdf_breaklinks " << h_pdf_breaklinks << "\n" - "\\pdf_pdfborder " << h_pdf_pdfborder << "\n" - "\\pdf_colorlinks " << h_pdf_colorlinks << "\n" - "\\pdf_backref " << h_pdf_backref << "\n" - "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n'; - if (!h_pdf_pagemode.empty()) - os << "\\pdf_pagemode " << h_pdf_pagemode << '\n'; - if (!h_pdf_quoted_options.empty()) - os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n"; - } - os << "\\papersize " << h_papersize << "\n" - << "\\use_geometry " << h_use_geometry << "\n" - << "\\use_amsmath " << h_use_amsmath << "\n" - << "\\use_esint " << h_use_esint << "\n" - << "\\use_mhchem " << h_use_mhchem << "\n" - << "\\use_mathdots " << h_use_mathdots << "\n" - << "\\cite_engine " << h_cite_engine << "\n" - << "\\use_bibtopic " << h_use_bibtopic << "\n" - << "\\paperorientation " << h_paperorientation << '\n' - << "\\suppress_date " << h_suppress_date << '\n' - << "\\use_refstyle " << h_use_refstyle << '\n'; - if (!h_fontcolor.empty()) - os << "\\fontcolor " << h_fontcolor << '\n'; - if (!h_notefontcolor.empty()) - os << "\\notefontcolor " << h_notefontcolor << '\n'; - if (!h_backgroundcolor.empty()) - os << "\\backgroundcolor " << h_backgroundcolor << '\n'; - if (!h_boxbgcolor.empty()) - os << "\\boxbgcolor " << h_boxbgcolor << '\n'; - os << h_margins - << "\\secnumdepth " << h_secnumdepth << "\n" - << "\\tocdepth " << h_tocdepth << "\n" - << "\\paragraph_separation " << h_paragraph_separation << "\n"; - if (h_paragraph_separation == "skip") - os << "\\defskip " << h_defskip << "\n"; - else - os << "\\paragraph_indentation " << h_paragraph_indentation << "\n"; - os << "\\quotes_language " << h_quotes_language << "\n" - << "\\papercolumns " << h_papercolumns << "\n" - << "\\papersides " << h_papersides << "\n" - << "\\paperpagestyle " << h_paperpagestyle << "\n"; - if (!h_listings_params.empty()) - os << "\\listings_params " << h_listings_params << "\n"; - os << "\\tracking_changes " << h_tracking_changes << "\n" - << "\\output_changes " << h_output_changes << "\n" - << "\\html_math_output " << h_html_math_output << "\n" - << "\\html_css_as_file " << h_html_css_as_file << "\n" - << "\\html_be_strict " << h_html_be_strict << "\n" - << "\\end_header\n\n" - << "\\begin_body\n"; - // clear preamble for subdocuments - h_preamble.str(""); -} - -} // anonymous namespace - - -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()) { - Token const & t = p.get_token(); - - if (t.cat() == catEscape && t.cs() == "documentclass") { - is_full_document = true; - break; - } - } - p.reset(); - - while (is_full_document && p.good()) { - Token const & t = p.get_token(); - -#ifdef FILEDEBUG - cerr << "t: " << t << "\n"; -#endif - - // - // cat codes - // - 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.cs(); - - else if (!in_lyx_preamble && - (t.cat() == catSpace || t.cat() == catNewline)) - h_preamble << t.asInput(); - - else if (t.cat() == catComment) { - 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() == "date") { - string argument = p.getArg('{', '}'); - if (argument.empty()) - h_suppress_date = "true"; - else - h_preamble << t.asInput() << '{' << argument << '}'; - } - - else if (t.cs() == "color") { - string argument = p.getArg('{', '}'); - // check the case that a standard color is used - if (is_known(argument, known_basic_colors)) - h_fontcolor = color2code(argument); - // check the case that LyX's document_fontcolor is defined - // but not used for \color - if (argument != "document_fontcolor" - && !is_known(argument, known_basic_colors)) { - h_preamble << t.asInput() << '{' << argument << '}'; - // the color might already be set because \definecolor - // is parsed before this - h_fontcolor = ""; - } - } - - else if (t.cs() == "pagecolor") { - string argument = p.getArg('{', '}'); - // check the case that a standard color is used - if (is_known(argument, known_basic_colors)) - h_backgroundcolor = color2code(argument); - // check the case that LyX's page_backgroundcolor is defined - // but not used for \pagecolor - if (argument != "page_backgroundcolor" - && !is_known(argument, known_basic_colors)) { - h_preamble << t.asInput() << '{' << argument << '}'; - // the color might already be set because \definecolor - // is parsed before this - h_backgroundcolor = ""; - } - } - - else if (t.cs() == "makeatletter") { - // LyX takes care of this - p.setCatCode('@', catLetter); - } - - else if (t.cs() == "makeatother") { - // LyX takes care of this - p.setCatCode('@', catOther); - } - - else if (t.cs() == "newcommand" || t.cs() == "newcommandx" - || t.cs() == "renewcommand" || t.cs() == "renewcommandx" - || t.cs() == "providecommand" || t.cs() == "providecommandx" - || t.cs() == "DeclareRobustCommand" - || t.cs() == "DeclareRobustCommandx" - || t.cs() == "ProvideTextCommandDefault" - || t.cs() == "DeclareMathAccent") { - bool star = false; - if (p.next_token().character() == '*') { - p.get_token(); - star = true; - } - string const name = p.verbatim_item(); - string const opt1 = p.getFullOpt(); - string const opt2 = p.getFullOpt(); - string const body = p.verbatim_item(); - // font settings - if (name == "\\rmdefault") - if (is_known(body, known_roman_fonts)) - h_font_roman = body; - if (name == "\\sfdefault") - if (is_known(body, known_sans_fonts)) - h_font_sans = body; - if (name == "\\ttdefault") - if (is_known(body, known_typewriter_fonts)) - h_font_typewriter = body; - if (name == "\\familydefault") { - string family = body; - // remove leading "\" - h_font_default_family = family.erase(0,1); - } - - // Add the command to the known commands - add_known_command(name, opt1, !opt2.empty(), from_utf8(body)); - - // only non-lyxspecific stuff - if (!in_lyx_preamble) { - ostringstream ss; - ss << '\\' << t.cs(); - if (star) - ss << '*'; - ss << '{' << name << '}' << opt1 << opt2 - << '{' << body << "}"; - h_preamble << ss.str(); -/* - ostream & out = in_preamble ? h_preamble : os; - out << "\\" << t.cs() << "{" << name << "}" - << opts << "{" << body << "}"; -*/ - } - } - - 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); - // The documentclass options are always parsed before the options - // of the babel call so that a language cannot overwrite the babel - // options. - handle_opt(opts, known_languages, h_language); - delete_opt(opts, known_languages); - - // 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, ","); - // FIXME This does not work for classes that have a - // different name in LyX than in LaTeX - h_textclass = p.getArg('{', '}'); - } - - else if (t.cs() == "usepackage") { - string const options = p.getArg('[', ']'); - string const name = p.getArg('{', '}'); - 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") { - string const name = p.getArg('{', '}'); - string const opt1 = p.getFullOpt(); - string const opt2 = p.getFullOpt(); - string const beg = p.verbatim_item(); - string const end = p.verbatim_item(); - if (!in_lyx_preamble) { - h_preamble << "\\newenvironment{" << name - << '}' << opt1 << opt2 << '{' - << beg << "}{" << end << '}'; - } - add_known_environment(name, opt1, !opt2.empty(), - from_utf8(beg), from_utf8(end)); - - } - - else if (t.cs() == "def") { - string name = p.get_token().cs(); - while (p.next_token().cat() != catBegin) - name += p.get_token().cs(); - if (!in_lyx_preamble) - h_preamble << "\\def\\" << name << '{' - << p.verbatim_item() << "}"; - } - - else if (t.cs() == "newcolumntype") { - string const name = p.getArg('{', '}'); - trim(name); - int nargs = 0; - string opts = p.getOpt(); - if (!opts.empty()) { - istringstream is(string(opts, 1)); - is >> nargs; - } - special_columns[name[0]] = nargs; - h_preamble << "\\newcolumntype{" << name << "}"; - if (nargs) - h_preamble << "[" << nargs << "]"; - h_preamble << "{" << p.verbatim_item() << "}"; - } - - else if (t.cs() == "setcounter") { - string const name = p.getArg('{', '}'); - string const content = p.getArg('{', '}'); - if (name == "secnumdepth") - h_secnumdepth = content; - else if (name == "tocdepth") - h_tocdepth = content; - else - h_preamble << "\\setcounter{" << name << "}{" << content << "}"; - } - - else if (t.cs() == "setlength") { - string const name = p.verbatim_item(); - string const content = p.verbatim_item(); - // 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 - h_paragraph_indentation = translate_len(content); - } 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") - break; - 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() == "definecolor") { - string const color = p.getArg('{', '}'); - string const space = p.getArg('{', '}'); - string const value = p.getArg('{', '}'); - if (color == "document_fontcolor" && space == "rgb") { - RGBColor c(RGBColorFromLaTeX(value)); - h_fontcolor = X11hexname(c); - } else if (color == "note_fontcolor" && space == "rgb") { - RGBColor c(RGBColorFromLaTeX(value)); - h_notefontcolor = X11hexname(c); - } else if (color == "page_backgroundcolor" && space == "rgb") { - RGBColor c(RGBColorFromLaTeX(value)); - h_backgroundcolor = X11hexname(c); - } else if (color == "shadecolor" && space == "rgb") { - RGBColor c(RGBColorFromLaTeX(value)); - h_boxbgcolor = X11hexname(c); - } else { - h_preamble << "\\definecolor{" << color - << "}{" << space << "}{" << value - << '}'; - } - } - - else if (t.cs() == "jurabibsetup") { - // FIXME p.getArg('{', '}') is most probably wrong (it - // does not handle nested braces). - // Use p.verbatim_item() instead. - vector jurabibsetup = - split_options(p.getArg('{', '}')); - // add jurabibsetup to the jurabib package options - add_package("jurabib", jurabibsetup); - if (!jurabibsetup.empty()) { - h_preamble << "\\jurabibsetup{" - << join(jurabibsetup, ",") << '}'; - } - } - - else if (t.cs() == "hypersetup") { - vector hypersetup = - split_options(p.verbatim_item()); - // add hypersetup to the hyperref package options - handle_hyperref(hypersetup); - if (!hypersetup.empty()) { - h_preamble << "\\hypersetup{" - << join(hypersetup, ",") << '}'; - } - } - - else if (is_known(t.cs(), known_if_3arg_commands)) { - // prevent misparsing of \usepackage if it is used - // as an argument (see e.g. our own output of - // \@ifundefined above) - string const arg1 = p.verbatim_item(); - string const arg2 = p.verbatim_item(); - string const arg3 = p.verbatim_item(); - // test case \@ifundefined{date}{}{\date{}} - if (arg1 == "date" && arg2.empty() && arg3 == "\\date{}") { - h_suppress_date = "true"; - // test for case - //\@ifundefined{showcaptionsetup}{}{% - // \PassOptionsToPackage{caption=false}{subfig}} - // that LyX uses for subfloats - } else if (arg1 == "showcaptionsetup" && arg2.empty() - && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") { - ; // do nothing - } else if (!in_lyx_preamble) { - h_preamble << t.asInput() - << '{' << arg1 << '}' - << '{' << arg2 << '}' - << '{' << arg3 << '}'; - } - } - - else if (is_known(t.cs(), known_if_commands)) { - // must not parse anything in conditional code, since - // LyX would output the parsed contents unconditionally - if (!in_lyx_preamble) - h_preamble << t.asInput(); - handle_if(p, in_lyx_preamble); - } - - else if (!t.cs().empty() && !in_lyx_preamble) - h_preamble << '\\' << t.cs(); - } - - // remove the whitespace - p.skip_spaces(); - - // Force textclass if the user wanted it - if (!forceclass.empty()) - h_textclass = forceclass; - if (noweb_mode && !prefixIs(h_textclass, "literate-")) - h_textclass.insert(0, "literate-"); - tc.setName(h_textclass); - if (!tc.load()) { - cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl; - exit(EXIT_FAILURE); - } - if (h_papersides.empty()) { - ostringstream ss; - ss << tc.sides(); - h_papersides = ss.str(); - } - end_preamble(os, tc); -} - - -/// translates a babel language name to a LyX language name -string babel2lyx(string const & language) -{ - char const * const * where = is_known(language, known_languages); - if (where) - return known_coded_languages[where - known_languages]; - return language; -} - - -/// translates a color name to a LyX color code -string color2code(string const & name) -{ - char const * const * where = is_known(name, known_basic_colors); - if (where) - return known_basic_color_codes[where - known_basic_colors]; - return name; -} - -// }]) - - -} // namespace lyx diff --git a/src/tex2lyx/table.cpp b/src/tex2lyx/table.cpp index 37a74509b6..588d10c1b9 100644 --- a/src/tex2lyx/table.cpp +++ b/src/tex2lyx/table.cpp @@ -16,6 +16,8 @@ #include "tex2lyx.h" +#include "Preamble.h" + #include "support/lassert.h" #include "support/convert.h" #include "support/lstrings.h" @@ -73,10 +75,23 @@ class RowInfo { public: RowInfo() : topline(false), bottomline(false), type(LT_NORMAL), caption(false), newpage(false) {} + /// Does this row have any special setting? + bool special() const + { + return topline || bottomline || !top_space.empty() || + !bottom_space.empty() || !interline_space.empty() || + type != LT_NORMAL || caption || newpage; + } /// horizontal line above bool topline; /// horizontal line below bool bottomline; + /// Extra space between the top line and this row + string top_space; + /// Extra space between this row and the bottom line + string bottom_space; + /// Extra space between the bottom line and the next top line + string interline_space; /// These are for longtabulars only /// row type (head, foot, firsthead etc.) LTRowType type; @@ -129,6 +144,23 @@ public: }; +class ltType { +public: + // constructor + ltType() : topDL(false), bottomDL(false), empty(false) {} + // we have this header type (is set in the getLT... functions) + bool set; + // double borders on top + bool topDL; + // double borders on bottom + bool bottomDL; + // used for FirstHeader & LastFooter and if this is true + // all the rows marked as FirstHeader or LastFooter are + // ignored in the output and it is set to be empty! + bool empty; +}; + + /// translate a horizontal alignment (as stored in ColInfo and CellInfo) to LyX inline char const * verbose_align(char c) { @@ -471,12 +503,35 @@ bool parse_hlines(Parser & p, Token const & t, string & hlines, { LASSERT(t.cat() == catEscape, return false); - if (t.cs() == "hline") - hlines += "\\hline"; + if (t.cs() == "hline" || t.cs() == "toprule" || t.cs() == "midrule" || + t.cs() == "bottomrule") + hlines += '\\' + t.cs(); else if (t.cs() == "cline") hlines += "\\cline{" + p.verbatim_item() + '}'; + else if (t.cs() == "cmidrule") { + // We cannot handle the \cmidrule(l){3-4} form + p.pushPosition(); + p.skip_spaces(true); + bool const hasParentheses(p.getFullArg('(', ')').first); + p.popPosition(); + if (hasParentheses) + return false; + hlines += "\\cmidrule{" + p.verbatim_item() + '}'; + } + + else if (t.cs() == "addlinespace") { + p.pushPosition(); + p.skip_spaces(true); + bool const hasArgument(p.getFullArg('{', '}').first); + p.popPosition(); + if (hasArgument) + hlines += "\\addlinespace{" + p.verbatim_item() + '}'; + else + hlines += "\\addlinespace"; + } + else if (is_long_tabular && t.cs() == "newpage") hlines += "\\newpage"; @@ -610,7 +665,6 @@ void parse_table(Parser & p, ostream & os, bool is_long_tabular, } continue; } - } // We need a HLINE separator if we either have no hline @@ -628,14 +682,20 @@ void parse_table(Parser & p, ostream & os, bool is_long_tabular, pos = IN_COLUMNS; break; case IN_HLINES_END: - // Oops, there is still cell content after hline - // stuff. This does not work in LaTeX, so we ignore - // the hlines. - cerr << "Ignoring '" << hlines << "' in a cell" - << endl; + // Oops, there is still cell content or unsupported + // booktabs commands after hline stuff. The latter are + // moved to the cell, and the first does not work in + // LaTeX, so we ignore the hlines. os << comments; - hlines.erase(); comments.erase(); + if (support::contains(hlines, "\\hline") || + support::contains(hlines, "\\cline") || + support::contains(hlines, "\\newpage")) + cerr << "Ignoring '" << hlines + << "' in a cell" << endl; + else + os << hlines; + hlines.erase(); pos = IN_COLUMNS; break; case IN_COLUMNS: @@ -770,9 +830,11 @@ void handle_hline_below(RowInfo & ri, vector & ci) } // anonymous namespace -void handle_tabular(Parser & p, ostream & os, bool is_long_tabular, - Context & context) +void handle_tabular(Parser & p, ostream & os, string const & name, + string const & tabularwidth, Context & context) { + bool const is_long_tabular(name == "longtable"); + bool booktabs = false; string tabularvalignment("middle"); string posopts = p.getOpt(); if (!posopts.empty()) { @@ -806,13 +868,18 @@ void handle_tabular(Parser & p, ostream & os, bool is_long_tabular, vector< vector > cellinfo(lines.size()); vector rowinfo(lines.size()); + ltType endfirsthead; + ltType endhead; + ltType endfoot; + ltType endlastfoot; // split into rows //cerr << "// split into rows\n"; - for (size_t row = 0; row < rowinfo.size(); ++row) { + for (size_t row = 0; row < rowinfo.size();) { // init row cellinfo[row].resize(colinfo.size()); + bool deletelastrow = false; // split row vector dummy; @@ -840,13 +907,18 @@ void handle_tabular(Parser & p, ostream & os, bool is_long_tabular, while (p1.good()) { Token t = p1.get_token(); //cerr << "read token: " << t << "\n"; - if (t.cs() == "hline") { + if (t.cs() == "hline" || t.cs() == "toprule" || + t.cs() == "midrule" || + t.cs() == "bottomrule") { + if (t.cs() != "hline") + booktabs = true; if (i == 0) { if (rowinfo[row].topline) { if (row > 0) // extra bottomline above handle_hline_below(rowinfo[row - 1], cellinfo[row - 1]); else - cerr << "dropping extra hline\n"; + cerr << "dropping extra " + << t.cs() << '\n'; //cerr << "below row: " << row-1 << endl; } else { handle_hline_above(rowinfo[row], cellinfo[row]); @@ -856,37 +928,39 @@ void handle_tabular(Parser & p, ostream & os, bool is_long_tabular, //cerr << "below row: " << row << endl; handle_hline_below(rowinfo[row], cellinfo[row]); } - } else if (t.cs() == "cline") { + } else if (t.cs() == "cline" || t.cs() == "cmidrule") { + if (t.cs() == "cmidrule") + booktabs = true; string arg = p1.verbatim_item(); - //cerr << "read cline arg: '" << arg << "'\n"; - vector t; - split(arg, t, '-'); - t.resize(2); - size_t from = convert(t[0]); + //cerr << "read " << t.cs() << " arg: '" << arg << "'\n"; + vector cols; + split(arg, cols, '-'); + cols.resize(2); + size_t from = convert(cols[0]); if (from == 0) cerr << "Could not parse " - "cline start column." + << t.cs() << " start column." << endl; else // 1 based index -> 0 based --from; if (from >= colinfo.size()) { - cerr << "cline starts at non " - "existing column " + cerr << t.cs() << " starts at " + "non existing column " << (from + 1) << endl; from = colinfo.size() - 1; } - size_t to = convert(t[1]); + size_t to = convert(cols[1]); if (to == 0) cerr << "Could not parse " - "cline end column." + << t.cs() << " end column." << endl; else // 1 based index -> 0 based --to; if (to >= colinfo.size()) { - cerr << "cline ends at non " - "existing column " + cerr << t.cs() << " ends at " + "non existing column " << (to + 1) << endl; to = colinfo.size() - 1; } @@ -900,38 +974,74 @@ void handle_tabular(Parser & p, ostream & os, bool is_long_tabular, cellinfo[row][col].bottomline = true; } } + } else if (t.cs() == "addlinespace") { + booktabs = true; + string const opt = p.next_token().cat() == catBegin ? + p.verbatim_item() : string(); + if (i == 0) { + if (opt.empty()) + rowinfo[row].top_space = "default"; + else + rowinfo[row].top_space = translate_len(opt); + } else if (rowinfo[row].bottomline) { + if (opt.empty()) + rowinfo[row].bottom_space = "default"; + else + rowinfo[row].bottom_space = translate_len(opt); + } else { + if (opt.empty()) + rowinfo[row].interline_space = "default"; + else + rowinfo[row].interline_space = translate_len(opt); + } } else if (t.cs() == "endhead") { - if (i > 0) + if (i == 0) + endhead.empty = true; + else rowinfo[row].type = LT_HEAD; for (int r = row - 1; r >= 0; --r) { if (rowinfo[r].type != LT_NORMAL) break; rowinfo[r].type = LT_HEAD; + endhead.empty = false; } + endhead.set = true; } else if (t.cs() == "endfirsthead") { - if (i > 0) + if (i == 0) + endfirsthead.empty = true; + else rowinfo[row].type = LT_FIRSTHEAD; for (int r = row - 1; r >= 0; --r) { if (rowinfo[r].type != LT_NORMAL) break; rowinfo[r].type = LT_FIRSTHEAD; + endfirsthead.empty = false; } + endfirsthead.set = true; } else if (t.cs() == "endfoot") { - if (i > 0) + if (i == 0) + endfoot.empty = true; + else rowinfo[row].type = LT_FOOT; for (int r = row - 1; r >= 0; --r) { if (rowinfo[r].type != LT_NORMAL) break; rowinfo[r].type = LT_FOOT; + endfoot.empty = false; } + endfoot.set = true; } else if (t.cs() == "endlastfoot") { - if (i > 0) + if (i == 0) + endlastfoot.empty = true; + else rowinfo[row].type = LT_LASTFOOT; for (int r = row - 1; r >= 0; --r) { if (rowinfo[r].type != LT_NORMAL) break; rowinfo[r].type = LT_LASTFOOT; + endlastfoot.empty = false; } + endlastfoot.set = true; } else if (t.cs() == "newpage") { if (i == 0) { if (row > 0) @@ -950,6 +1060,48 @@ void handle_tabular(Parser & p, ostream & os, bool is_long_tabular, } } + // LyX ends headers and footers always with \tabularnewline. + // This causes one additional row in the output. + // If the last row of a header/footer is empty, we can work + // around that by removing it. + if (row > 1) { + RowInfo test = rowinfo[row-1]; + test.type = LT_NORMAL; + if (lines[row-1].empty() && !test.special()) { + switch (rowinfo[row-1].type) { + case LT_FIRSTHEAD: + if (rowinfo[row].type != LT_FIRSTHEAD && + rowinfo[row-2].type == LT_FIRSTHEAD) + deletelastrow = true; + break; + case LT_HEAD: + if (rowinfo[row].type != LT_HEAD && + rowinfo[row-2].type == LT_HEAD) + deletelastrow = true; + break; + case LT_FOOT: + if (rowinfo[row].type != LT_FOOT && + rowinfo[row-2].type == LT_FOOT) + deletelastrow = true; + break; + case LT_LASTFOOT: + if (rowinfo[row].type != LT_LASTFOOT && + rowinfo[row-2].type == LT_LASTFOOT) + deletelastrow = true; + break; + case LT_NORMAL: + break; + } + } + } + + if (deletelastrow) { + lines.erase(lines.begin() + (row - 1)); + rowinfo.erase(rowinfo.begin() + (row - 1)); + cellinfo.erase(cellinfo.begin() + (row - 1)); + continue; + } + // split into cells vector cells; split(lines[row], cells, TAB); @@ -1010,7 +1162,8 @@ void handle_tabular(Parser & p, ostream & os, bool is_long_tabular, cellinfo[row][col].align = 'c'; } - } else if (col == 0 && is_long_tabular && + } else if (col == 0 && colinfo.size() > 1 && + is_long_tabular && p.next_token().cs() == "caption") { // longtable caption support in LyX is a hack: // Captions require a row of their own with @@ -1018,27 +1171,45 @@ void handle_tabular(Parser & p, ostream & os, bool is_long_tabular, // one multicolumn cell. The contents of that // cell must contain exactly one caption inset // and nothing else. - rowinfo[row].caption = true; - for (size_t c = 1; c < cells.size(); ++c) { - if (!cells[c].empty()) { - cerr << "Moving cell content '" - << cells[c] - << "' into the caption cell. " - "This will probably not work." - << endl; - cells[0] += cells[c]; + // LyX outputs all caption rows as first head, + // so we must not set the caption flag for + // captions not in the first head. + // Fortunately, the caption flag is only needed + // for tables with more than one column. + bool usecaption = (rowinfo[row].type == LT_NORMAL || + rowinfo[row].type == LT_FIRSTHEAD); + for (size_t r = 0; r < row && usecaption; ++r) + if (rowinfo[row].type != LT_NORMAL && + rowinfo[row].type != LT_FIRSTHEAD) + usecaption = false; + if (usecaption) { + rowinfo[row].caption = true; + for (size_t c = 1; c < cells.size(); ++c) { + if (!cells[c].empty()) { + cerr << "Moving cell content '" + << cells[c] + << "' into the caption cell. " + "This will probably not work." + << endl; + cells[0] += cells[c]; + } } + cells.resize(1); + cellinfo[row][col].align = colinfo[col].align; + cellinfo[row][col].multi = CELL_BEGIN_OF_MULTICOLUMN; + } else { + cellinfo[row][col].leftlines = colinfo[col].leftlines; + cellinfo[row][col].rightlines = colinfo[col].rightlines; + cellinfo[row][col].align = colinfo[col].align; } - cells.resize(1); - cellinfo[row][col].align = colinfo[col].align; - cellinfo[row][col].multi = CELL_BEGIN_OF_MULTICOLUMN; ostringstream os; parse_text_in_inset(p, os, FLAG_CELL, false, context); cellinfo[row][col].content += os.str(); - // add dummy multicolumn cells - for (size_t c = 1; c < colinfo.size(); ++c) - cellinfo[row][c].multi = CELL_PART_OF_MULTICOLUMN; - + if (usecaption) { + // add dummy multicolumn cells + for (size_t c = 1; c < colinfo.size(); ++c) + cellinfo[row][c].multi = CELL_PART_OF_MULTICOLUMN; + } } else { cellinfo[row][col].leftlines = colinfo[col].leftlines; cellinfo[row][col].rightlines = colinfo[col].rightlines; @@ -1060,6 +1231,8 @@ void handle_tabular(Parser & p, ostream & os, bool is_long_tabular, cellinfo[row - 1][col].bottomline = true; rowinfo.pop_back(); } + + ++row; } // Now we have the table structure and content in rowinfo, colinfo @@ -1106,15 +1279,33 @@ void handle_tabular(Parser & p, ostream & os, bool is_long_tabular, } } + if (booktabs) + preamble.registerAutomaticallyLoadedPackage("booktabs"); + if (is_long_tabular) + preamble.registerAutomaticallyLoadedPackage("longtable"); + //cerr << "// output what we have\n"; // output what we have os << "\n\n"; os << "\n"; //cerr << "// after header\n"; @@ -1131,6 +1322,9 @@ void handle_tabular(Parser & p, ostream & os, bool is_long_tabular, for (size_t row = 0; row < rowinfo.size(); ++row) { os << "Settings->LaTeX Preamble to see the result. .SS "What tex2lyx Can't Handle --- But it's \s-1OK\s0" .IP "\(bu" 4 -tabular* tables -.IP "\(bu" 4 some spacing commands (\f(CW\ehspace\fR, \f(CW\epagebreak\fR and \f(CW\elinebreak\fR) .IP "\(bu" 4 \f(CW\ecentering\fR, \f(CW\eraggedleft\fR, \f(CW\eraggedright\fR diff --git a/src/tex2lyx/tex2lyx.cpp b/src/tex2lyx/tex2lyx.cpp index 25f5955a68..5bc77d6d2e 100644 --- a/src/tex2lyx/tex2lyx.cpp +++ b/src/tex2lyx/tex2lyx.cpp @@ -21,6 +21,7 @@ #include "LayoutFile.h" #include "LayoutModuleList.h" #include "ModuleList.h" +#include "Preamble.h" #include "TextClass.h" #include "support/convert.h" @@ -342,6 +343,7 @@ bool checkModule(string const & name, bool command) bool noweb_mode = false; bool pdflatex = false; +bool xetex = false; bool roundtrip = false; @@ -364,13 +366,20 @@ void read_command(Parser & p, string command, CommandMap & commands) string const arg = p.getArg('{', '}'); if (arg == "translate") arguments.push_back(required); + else if (arg == "group") + arguments.push_back(req_group); else if (arg == "item") arguments.push_back(item); + else if (arg == "displaymath") + arguments.push_back(displaymath); else arguments.push_back(verbatim); } else { - p.getArg('[', ']'); - arguments.push_back(optional); + string const arg = p.getArg('[', ']'); + if (arg == "group") + arguments.push_back(opt_group); + else + arguments.push_back(optional); } } commands[command] = arguments; @@ -649,7 +658,7 @@ namespace { * You must ensure that \p parentFilePath is properly set before calling * this function! */ -void tex2lyx(idocstream & is, ostream & os, string encoding) +bool tex2lyx(idocstream & is, ostream & os, string encoding) { // Set a sensible default encoding. // This is used until an encoding command is found. @@ -657,18 +666,17 @@ void tex2lyx(idocstream & is, ostream & os, string encoding) // since latin1 does not cause an iconv error if the actual encoding // is different (bug 7509). if (encoding.empty()) { - if (h_inputencoding == "auto") + if (preamble.inputencoding() == "auto") encoding = "latin1"; else - encoding = h_inputencoding; + encoding = preamble.inputencoding(); } Parser p(is); p.setEncoding(encoding); //p.dump(); - ostringstream ps; - parse_preamble(p, ps, documentclass, textclass); + preamble.parse(p, documentclass, textclass); active_environments.push_back("document"); Context context(true, textclass); @@ -682,16 +690,16 @@ void tex2lyx(idocstream & is, ostream & os, string encoding) active_environments.pop_back(); // We know the used modules only after parsing the full text - ostringstream ms; if (!used_modules.empty()) { - ms << "\\begin_modules\n"; LayoutModuleList::const_iterator const end = used_modules.end(); LayoutModuleList::const_iterator it = used_modules.begin(); for (; it != end; it++) - ms << *it << '\n'; - ms << "\\end_modules\n"; + preamble.addModule(*it); + } + if (!preamble.writeLyXHeader(os, !active_environments.empty())) { + cerr << "Could write LyX file header." << endl; + return false; } - os << subst(ps.str(), modules_placeholder, ms.str()); ss.seekg(0); os << ss.str(); @@ -702,6 +710,7 @@ void tex2lyx(idocstream & is, ostream & os, string encoding) parsertest << p.get_token().asInput(); // and parsertest.tex should now have identical content #endif + return true; } @@ -719,9 +728,9 @@ bool tex2lyx(FileName const & infilename, ostream & os, string const & encoding) } string const oldParentFilePath = parentFilePath; parentFilePath = onlyPath(infilename.absFileName()); - tex2lyx(is, os, encoding); + bool retval = tex2lyx(is, os, encoding); parentFilePath = oldParentFilePath; - return true; + return retval; } } // anonymous namespace diff --git a/src/tex2lyx/tex2lyx.h b/src/tex2lyx/tex2lyx.h index 1a8317e56b..730013c03b 100644 --- a/src/tex2lyx/tex2lyx.h +++ b/src/tex2lyx/tex2lyx.h @@ -43,19 +43,10 @@ public: void setName(std::string const & name) { name_ = name; } }; -/// in preamble.cpp -void parse_preamble(Parser & p, std::ostream & os, - std::string const & forceclass, TeX2LyXDocClass & tc); /// Translate babel language name to LyX language name extern std::string babel2lyx(std::string const & language); -/// translate color name to LyX color code -extern std::string color2code(std::string const & name); - -/// used packages with options -extern std::map > used_packages; -extern const char * const modules_placeholder; -extern std::string h_inputencoding; -extern std::string h_paragraph_separation; +/// Translate basic color name or RGB color in LaTeX syntax to LyX color code +extern std::string rgbcolor2code(std::string const & name); /// in text.cpp std::string translate_len(std::string const &); @@ -80,8 +71,8 @@ void parse_math(Parser & p, std::ostream & os, unsigned flags, mode_type mode); /// in table.cpp -void handle_tabular(Parser & p, std::ostream & os, bool is_long_tabular, - Context & context); +void handle_tabular(Parser & p, std::ostream & os, std::string const & name, + std::string const & width, Context & context); /// in tex2lyx.cpp @@ -93,6 +84,7 @@ std::string join(std::vector const & input, char const * delim); bool is_math_env(std::string const & name); +bool is_display_math_env(std::string const & name); char const * const * is_known(std::string const &, char const * const *); /*! @@ -124,9 +116,12 @@ std::string active_environment(); enum ArgumentType { required, + req_group, verbatim, item, - optional + optional, + opt_group, + displaymath, }; class FullCommand { @@ -167,6 +162,8 @@ extern FullEnvironmentMap possible_textclass_environments; extern bool noweb_mode; /// Did we recognize any pdflatex-only construct? extern bool pdflatex; +/// Did we recognize any xetex-only construct? +extern bool xetex; /// LyX format that is created by tex2lyx int const LYX_FORMAT = 413; diff --git a/src/tex2lyx/text.cpp b/src/tex2lyx/text.cpp index bd7a0d203c..8a3d8fb943 100644 --- a/src/tex2lyx/text.cpp +++ b/src/tex2lyx/text.cpp @@ -19,14 +19,17 @@ #include "Context.h" #include "Encoding.h" #include "FloatList.h" +#include "LaTeXPackages.h" #include "Layout.h" #include "Length.h" +#include "Preamble.h" #include "support/lassert.h" #include "support/convert.h" #include "support/FileName.h" #include "support/filetools.h" #include "support/lstrings.h" +#include "support/lyxtime.h" #include #include @@ -160,7 +163,12 @@ char const * const known_old_font_families[] = { "rm", "sf", "tt", 0}; char const * const known_font_families[] = { "rmfamily", "sffamily", "ttfamily", 0}; -/// the same as known_old_font_families and known_font_families with .lyx names +/// LaTeX names for font family changing commands +char const * const known_text_font_families[] = { "textrm", "textsf", +"texttt", 0}; + +/// The same as known_old_font_families, known_font_families and +/// known_text_font_families with .lyx names char const * const known_coded_font_families[] = { "roman", "sans", "typewriter", 0}; @@ -170,7 +178,11 @@ char const * const known_old_font_series[] = { "bf", 0}; /// LaTeX names for font series char const * const known_font_series[] = { "bfseries", "mdseries", 0}; -/// the same as known_old_font_series and known_font_series with .lyx names +/// LaTeX names for font series changing commands +char const * const known_text_font_series[] = { "textbf", "textmd", 0}; + +/// The same as known_old_font_series, known_font_series and +/// known_text_font_series with .lyx names char const * const known_coded_font_series[] = { "bold", "medium", 0}; /// LaTeX 2.09 names for font shapes @@ -180,10 +192,23 @@ char const * const known_old_font_shapes[] = { "it", "sl", "sc", 0}; char const * const known_font_shapes[] = { "itshape", "slshape", "scshape", "upshape", 0}; -/// the same as known_old_font_shapes and known_font_shapes with .lyx names +/// LaTeX names for font shape changing commands +char const * const known_text_font_shapes[] = { "textit", "textsl", "textsc", +"textup", 0}; + +/// The same as known_old_font_shapes, known_font_shapes and +/// known_text_font_shapes with .lyx names char const * const known_coded_font_shapes[] = { "italic", "slanted", "smallcaps", "up", 0}; +/// Known special characters which need skip_spaces_braces() afterwards +char const * const known_special_chars[] = {"ldots", "lyxarrow", +"textcompwordmark", "slash", 0}; + +/// the same as known_special_chars with .lyx names +char const * const known_coded_special_chars[] = {"ldots{}", "menuseparator", +"textcompwordmark{}", "slash{}", 0}; + /*! * Graphics file extensions known by the dvips driver of the graphics package. * These extensions are used to complete the filename of an included @@ -668,10 +693,14 @@ void parse_arguments(string const & command, for (size_t i = 0; i < no_arguments; ++i) { switch (template_arguments[i]) { case required: + case req_group: // This argument contains regular LaTeX handle_ert(os, ert + '{', context); eat_whitespace(p, os, context, false); - parse_text(p, os, FLAG_ITEM, outer, context); + if (template_arguments[i] == required) + parse_text(p, os, FLAG_ITEM, outer, context); + else + parse_text_snippet(p, os, FLAG_ITEM, outer, context); ert = "}"; break; case item: @@ -683,11 +712,13 @@ void parse_arguments(string const & command, else ert += p.verbatim_item(); break; + case displaymath: case verbatim: // This argument may contain special characters ert += '{' + p.verbatim_item() + '}'; break; case optional: + case opt_group: // true because we must not eat whitespace // if an optional arg follows we must not strip the // brackets from this one @@ -792,7 +823,7 @@ void parse_box(Parser & p, ostream & os, unsigned outer_flags, latex_width = p.verbatim_item(); // if e.g. only \ovalbox{content} was used, set the width to 1\columnwidth // as this is LyX's standard for such cases (except for makebox) - // \framebox is special and handled below + // \framebox is more special and handled below if (latex_width.empty() && inner_type != "makebox" && outer_type != "framebox") latex_width = "1\\columnwidth"; @@ -927,6 +958,7 @@ void parse_box(Parser & p, ostream & os, unsigned outer_flags, (outer_type == "minipage" && inner_type == "shaded") || (outer_type == "parbox" && inner_type == "shaded")) { os << "Shaded\n"; + preamble.registerAutomaticallyLoadedPackage("color"); } else if (outer_type == "doublebox") os << "Doublebox\n"; else if (outer_type.empty()) @@ -1133,7 +1165,8 @@ void parse_unknown_environment(Parser & p, string const & name, ostream & os, void parse_environment(Parser & p, ostream & os, bool outer, - string & last_env, Context & parent_context) + string & last_env, bool & title_layout_found, + Context & parent_context) { Layout const * newlayout; InsetLayout const * newinsetlayout = 0; @@ -1149,13 +1182,24 @@ void parse_environment(Parser & p, ostream & os, bool outer, parse_math(p, os, FLAG_END, MATH_MODE); os << "\\end{" << name << "}"; end_inset(os); + if (is_display_math_env(name)) { + // Prevent the conversion of a line break to a space + // (bug 7668). This does not change the output, but + // looks ugly in LyX. + eat_whitespace(p, os, parent_context, false); + } } - else if (name == "tabular" || name == "longtable") { + else if (unstarred_name == "tabular" || name == "longtable") { eat_whitespace(p, os, parent_context, false); + string width = "0pt"; + if (name == "tabular*") { + width = lyx::translate_len(p.getArg('{', '}')); + eat_whitespace(p, os, parent_context, false); + } parent_context.check_layout(os); begin_inset(os, "Tabular "); - handle_tabular(p, os, name == "longtable", parent_context); + handle_tabular(p, os, name, width, parent_context); end_inset(os); p.skip_spaces(); } @@ -1176,6 +1220,15 @@ void parse_environment(Parser & p, ostream & os, bool outer, float_type = ""; if (!opt.empty()) os << "placement " << opt << '\n'; + if (contains(opt, "H")) + preamble.registerAutomaticallyLoadedPackage("float"); + else { + Floating const & fl = parent_context.textclass.floats() + .getType(unstarred_name); + if (!fl.floattype().empty() && fl.usesFloatPkg()) + preamble.registerAutomaticallyLoadedPackage("float"); + } + os << "wide " << convert(is_starred) << "\nsideways false" << "\nstatus open\n\n"; @@ -1287,6 +1340,8 @@ void parse_environment(Parser & p, ostream & os, bool outer, parse_text_in_inset(p, os, FLAG_END, outer, parent_context); end_inset(os); p.skip_spaces(); + if (!preamble.notefontcolor().empty()) + preamble.registerAutomaticallyLoadedPackage("color"); } else if (name == "framed" || name == "shaded") { @@ -1298,6 +1353,8 @@ void parse_environment(Parser & p, ostream & os, bool outer, else if (name == "lstlisting") { eat_whitespace(p, os, parent_context, false); // FIXME handle listings with parameters + // If this is added, don't forgot to handle the + // automatic color package loading if (p.hasOpt()) parse_unknown_environment(p, name, os, FLAG_END, outer, parent_context); @@ -1335,12 +1392,16 @@ void parse_environment(Parser & p, ostream & os, bool outer, parent_context.add_extra_stuff("\\align center\n"); else if (name == "singlespace") parent_context.add_extra_stuff("\\paragraph_spacing single\n"); - else if (name == "onehalfspace") + else if (name == "onehalfspace") { parent_context.add_extra_stuff("\\paragraph_spacing onehalf\n"); - else if (name == "doublespace") + preamble.registerAutomaticallyLoadedPackage("setspace"); + } else if (name == "doublespace") { parent_context.add_extra_stuff("\\paragraph_spacing double\n"); - else if (name == "spacing") + preamble.registerAutomaticallyLoadedPackage("setspace"); + } else if (name == "spacing") { parent_context.add_extra_stuff("\\paragraph_spacing other " + p.verbatim_item() + "\n"); + preamble.registerAutomaticallyLoadedPackage("setspace"); + } parse_text(p, os, FLAG_END, outer, parent_context); // Just in case the environment is empty parent_context.extra_stuff.erase(); @@ -1450,6 +1511,11 @@ void parse_environment(Parser & p, ostream & os, bool outer, context.check_end_deeper(os); parent_context.new_paragraph(os); p.skip_spaces(); + if (!title_layout_found) + title_layout_found = newlayout->intitle; + set const & req = newlayout->requires(); + for (set::const_iterator it = req.begin(); it != req.end(); it++) + preamble.registerAutomaticallyLoadedPackage(*it); } // The single '=' is meant here. @@ -1832,13 +1898,15 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, { Layout const * newlayout = 0; InsetLayout const * newinsetlayout = 0; + char const * const * where = 0; // Store the latest bibliographystyle and nocite{*} option // (needed for bibtex inset) string btprint; string bibliographystyle; - bool const use_natbib = used_packages.find("natbib") != used_packages.end(); - bool const use_jurabib = used_packages.find("jurabib") != used_packages.end(); + bool const use_natbib = preamble.isPackageUsed("natbib"); + bool const use_jurabib = preamble.isPackageUsed("jurabib"); string last_env; + bool title_layout_found = false; while (p.good()) { Token const & t = p.get_token(); @@ -1882,7 +1950,8 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, context.check_layout(os); begin_inset(os, "Formula "); Token const & n = p.get_token(); - if (n.cat() == catMath && outer) { + bool const display(n.cat() == catMath && outer); + if (display) { // TeX's $$...$$ syntax for displayed math os << "\\["; parse_math(p, os, FLAG_SIMPLE, MATH_MODE); @@ -1896,6 +1965,12 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, os << '$'; } end_inset(os); + if (display) { + // Prevent the conversion of a line break to a + // space (bug 7668). This does not change the + // output, but looks ugly in LyX. + eat_whitespace(p, os, context, false); + } } else if (t.cat() == catSuper || t.cat() == catSub) @@ -2017,8 +2092,10 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, os << t.cs(); } - else if (t.cat() == catBegin && - p.next_token().cat() == catEnd) { + else if (t.cat() == catBegin) { + Token const next = p.next_token(); + Token const end = p.next_next_token(); + if (next.cat() == catEnd) { // {} Token const prev = p.prev_token(); p.get_token(); @@ -2028,14 +2105,19 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, ; // ignore it in {}`` or -{}- else handle_ert(os, "{}", context); - - } - - else if (t.cat() == catBegin) { + } else if (next.cat() == catEscape && + is_known(next.cs(), known_quotes) && + end.cat() == catEnd) { + // Something like {\textquoteright} (e.g. + // from writer2latex). LyX writes + // \textquoteright{}, so we may skip the + // braces here for better readability. + parse_text_snippet(p, os, FLAG_BRACE_LAST, + outer, context); + } else { context.check_layout(os); // special handling of font attribute changes Token const prev = p.prev_token(); - Token const next = p.next_token(); TeXFont const oldFont = context.font; if (next.character() == '[' || next.character() == ']' || @@ -2108,6 +2190,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, parse_text_snippet(p, os, FLAG_BRACE_LAST, outer, context); handle_ert(os, "}", context); + } } } @@ -2142,10 +2225,15 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, parse_math(p, os, FLAG_EQUATION, MATH_MODE); os << "\\]"; end_inset(os); + // Prevent the conversion of a line break to a space + // (bug 7668). This does not change the output, but + // looks ugly in LyX. + eat_whitespace(p, os, context, false); } else if (t.cs() == "begin") - parse_environment(p, os, outer, last_env, context); + parse_environment(p, os, outer, last_env, + title_layout_found, context); else if (t.cs() == "end") { if (flags & FLAG_END) { @@ -2166,7 +2254,8 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, // FIXME: This swallows comments, but we cannot use // eat_whitespace() since we must not output // anything before the item. - s = p.getArg('[', ']'); + p.skip_spaces(true); + s = p.verbatimOption(); } else p.skip_spaces(false); context.set_item(); @@ -2216,7 +2305,8 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, else if (t.cs() == "bibitem") { context.set_item(); context.check_layout(os); - string label = convert_command_inset_arg(p.getArg('[', ']')); + eat_whitespace(p, os, context, false); + string label = convert_command_inset_arg(p.verbatimOption()); string key = convert_command_inset_arg(p.verbatim_item()); if (contains(label, '\\') || contains(key, '\\')) { // LyX can't handle LaTeX commands in labels or keys @@ -2231,8 +2321,53 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, } } - else if (is_macro(p)) - parse_macro(p, os, context); + else if (is_macro(p)) { + // catch the case of \def\inputGnumericTable + bool macro = true; + if (t.cs() == "def") { + Token second = p.next_token(); + if (second.cs() == "inputGnumericTable") { + p.pushPosition(); + p.get_token(); + skip_braces(p); + Token third = p.get_token(); + p.popPosition(); + if (third.cs() == "input") { + p.get_token(); + skip_braces(p); + p.get_token(); + string name = normalize_filename(p.verbatim_item()); + string const path = getMasterFilePath(); + // We want to preserve relative / absolute filenames, + // therefore path is only used for testing + if (!makeAbsPath(name, path).exists()) { + // The file extension is probably missing. + // Now try to find it out. + char const * const Gnumeric_formats[] = {"gnumeric" + "ods", "xls", 0}; + string const Gnumeric_name = + find_file(name, path, Gnumeric_formats); + if (!Gnumeric_name.empty()) + name = Gnumeric_name; + } + if (makeAbsPath(name, path).exists()) + fix_relative_filename(name); + else + cerr << "Warning: Could not find file '" + << name << "'." << endl; + context.check_layout(os); + begin_inset(os, "External\n\ttemplate "); + os << "GnumericSpreadsheet\n\tfilename " + << name << "\n"; + end_inset(os); + context.check_layout(os); + macro = false; + } + } + } + if (macro) + parse_macro(p, os, context); + } else if (t.cs() == "noindent") { p.skip_spaces(); @@ -2262,6 +2397,32 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, eat_whitespace(p, os, context, true); } + // Must catch empty dates before findLayout is called below + else if (t.cs() == "date") { + string const date = p.verbatim_item(); + if (date.empty()) + preamble.suppressDate(true); + else { + preamble.suppressDate(false); + if (context.new_layout_allowed && + (newlayout = findLayout(context.textclass, + t.cs(), true))) { + // write the layout + output_command_layout(os, p, outer, + context, newlayout); + p.skip_spaces(); + if (!title_layout_found) + title_layout_found = newlayout->intitle; + set const & req = newlayout->requires(); + for (set::const_iterator it = req.begin(); + it != req.end(); it++) + preamble.registerAutomaticallyLoadedPackage(*it); + } else + handle_ert(os, "\\date{" + date + '}', + context); + } + } + // Starred section headings // Must attempt to parse "Section*" before "Section". else if ((p.next_token().asInput() == "*") && @@ -2271,6 +2432,11 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, p.get_token(); output_command_layout(os, p, outer, context, newlayout); p.skip_spaces(); + if (!title_layout_found) + title_layout_found = newlayout->intitle; + set const & req = newlayout->requires(); + for (set::const_iterator it = req.begin(); it != req.end(); it++) + preamble.registerAutomaticallyLoadedPackage(*it); } // Section headings and the like @@ -2279,6 +2445,11 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, // write the layout output_command_layout(os, p, outer, context, newlayout); p.skip_spaces(); + if (!title_layout_found) + title_layout_found = newlayout->intitle; + set const & req = newlayout->requires(); + for (set::const_iterator it = req.begin(); it != req.end(); it++) + preamble.registerAutomaticallyLoadedPackage(*it); } else if (t.cs() == "caption") { @@ -2561,10 +2732,11 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, } else if (t.cs() == "makeindex" || t.cs() == "maketitle") { - // FIXME: Somehow prevent title layouts if - // "maketitle" was not found - // swallow this - skip_spaces_braces(p); + if (title_layout_found) { + // swallow this + skip_spaces_braces(p); + } else + handle_ert(os, t.asInput(), context); } else if (t.cs() == "tableofcontents") { @@ -2601,50 +2773,20 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, handle_ert(os, "\\listof{" + name + "}", context); } - else if (t.cs() == "textrm") - parse_text_attributes(p, os, FLAG_ITEM, outer, - context, "\\family", - context.font.family, "roman"); - - else if (t.cs() == "textsf") - parse_text_attributes(p, os, FLAG_ITEM, outer, - context, "\\family", - context.font.family, "sans"); - - else if (t.cs() == "texttt") - parse_text_attributes(p, os, FLAG_ITEM, outer, - context, "\\family", - context.font.family, "typewriter"); - - else if (t.cs() == "textmd") - parse_text_attributes(p, os, FLAG_ITEM, outer, - context, "\\series", - context.font.series, "medium"); - - else if (t.cs() == "textbf") - parse_text_attributes(p, os, FLAG_ITEM, outer, - context, "\\series", - context.font.series, "bold"); - - else if (t.cs() == "textup") - parse_text_attributes(p, os, FLAG_ITEM, outer, - context, "\\shape", - context.font.shape, "up"); - - else if (t.cs() == "textit") + else if ((where = is_known(t.cs(), known_text_font_families))) parse_text_attributes(p, os, FLAG_ITEM, outer, - context, "\\shape", - context.font.shape, "italic"); + context, "\\family", context.font.family, + known_coded_font_families[where - known_text_font_families]); - else if (t.cs() == "textsl") + else if ((where = is_known(t.cs(), known_text_font_series))) parse_text_attributes(p, os, FLAG_ITEM, outer, - context, "\\shape", - context.font.shape, "slanted"); + context, "\\series", context.font.series, + known_coded_font_series[where - known_text_font_series]); - else if (t.cs() == "textsc") + else if ((where = is_known(t.cs(), known_text_font_shapes))) parse_text_attributes(p, os, FLAG_ITEM, outer, - context, "\\shape", - context.font.shape, "smallcaps"); + context, "\\shape", context.font.shape, + known_coded_font_shapes[where - known_text_font_shapes]); else if (t.cs() == "textnormal" || t.cs() == "normalfont") { context.check_layout(os); @@ -2674,6 +2816,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, parse_text_snippet(p, os, FLAG_ITEM, outer, context); context.check_layout(os); os << "\n\\color inherit\n"; + preamble.registerAutomaticallyLoadedPackage("color"); } else // for custom defined colors handle_ert(os, t.asInput() + "{" + color + "}", context); @@ -2691,6 +2834,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, parse_text_snippet(p, os, FLAG_ITEM, outer, context); context.check_layout(os); os << "\n\\bar default\n"; + preamble.registerAutomaticallyLoadedPackage("ulem"); } else if (t.cs() == "sout") { @@ -2699,6 +2843,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, parse_text_snippet(p, os, FLAG_ITEM, outer, context); context.check_layout(os); os << "\n\\strikeout default\n"; + preamble.registerAutomaticallyLoadedPackage("ulem"); } else if (t.cs() == "uuline" || t.cs() == "uwave" || @@ -2708,6 +2853,52 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, parse_text_snippet(p, os, FLAG_ITEM, outer, context); context.check_layout(os); os << "\n\\" << t.cs() << " default\n"; + if (t.cs() == "uuline" || t.cs() == "uwave") + preamble.registerAutomaticallyLoadedPackage("ulem"); + } + + else if (t.cs() == "lyxadded" || t.cs() == "lyxdeleted") { + context.check_layout(os); + string name = p.getArg('{', '}'); + string localtime = p.getArg('{', '}'); + preamble.registerAuthor(name); + Author const & author = preamble.getAuthor(name); + // from_ctime() will fail if LyX decides to output the + // time in the text language. It might also use a wrong + // time zone (if the original LyX document was exported + // with a different time zone). + time_t ptime = from_ctime(localtime); + if (ptime == static_cast(-1)) { + cerr << "Warning: Could not parse time `" << localtime + << "´ for change tracking, using current time instead.\n"; + ptime = current_time(); + } + if (t.cs() == "lyxadded") + os << "\n\\change_inserted "; + else + os << "\n\\change_deleted "; + os << author.bufferId() << ' ' << ptime << '\n'; + parse_text_snippet(p, os, FLAG_ITEM, outer, context); + bool dvipost = LaTeXPackages::isAvailable("dvipost"); + bool xcolorulem = LaTeXPackages::isAvailable("ulem") && + LaTeXPackages::isAvailable("xcolor"); + // No need to test for luatex, since luatex comes in + // two flavours (dvi and pdf), like latex, and those + // are detected by pdflatex. + if (pdflatex || xetex) { + if (xcolorulem) { + preamble.registerAutomaticallyLoadedPackage("ulem"); + preamble.registerAutomaticallyLoadedPackage("xcolor"); + preamble.registerAutomaticallyLoadedPackage("pdfcolmk"); + } + } else { + if (dvipost) { + preamble.registerAutomaticallyLoadedPackage("dvipost"); + } else if (xcolorulem) { + preamble.registerAutomaticallyLoadedPackage("ulem"); + preamble.registerAutomaticallyLoadedPackage("xcolor"); + } + } } else if (t.cs() == "phantom" || t.cs() == "hphantom" || @@ -2764,7 +2955,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, // about the empty paragraph. context.new_paragraph(os); } - if (h_paragraph_separation == "indent") { + if (preamble.indentParagraphs()) { // we need to unindent, lest the line be too long context.add_par_extra_stuff("\\noindent\n"); } @@ -2795,7 +2986,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, is_known(p.next_token().cs(), known_phrases))) { // LyX sometimes puts a \protect in front, so we have to ignore it // FIXME: This needs to be changed when bug 4752 is fixed. - char const * const * where = is_known( + where = is_known( t.cs() == "protect" ? p.get_token().cs() : t.cs(), known_phrases); context.check_layout(os); @@ -2803,12 +2994,10 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, skip_spaces_braces(p); } - else if (is_known(t.cs(), known_ref_commands)) { + else if ((where = is_known(t.cs(), known_ref_commands))) { string const opt = p.getOpt(); if (opt.empty()) { context.check_layout(os); - char const * const * where = is_known(t.cs(), - known_ref_commands); begin_command_inset(os, "ref", known_coded_ref_commands[where - known_ref_commands]); os << "reference \"" @@ -2891,7 +3080,8 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, p.get_token(); } char argumentOrder = '\0'; - vector const & options = used_packages["jurabib"]; + vector const options = + preamble.getPackageOptions("jurabib"); if (find(options.begin(), options.end(), "natbiborder") != options.end()) argumentOrder = 'n'; @@ -2966,6 +3156,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, << convert_command_inset_arg(p.verbatim_item()) << "\"\n"; end_inset(os); + preamble.registerAutomaticallyLoadedPackage("nomencl"); } else if (t.cs() == "label") { @@ -2983,6 +3174,9 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, os << "type \"idx\"\n"; end_inset(os); skip_spaces_braces(p); + preamble.registerAutomaticallyLoadedPackage("makeidx"); + if (preamble.use_indices() == "true") + preamble.registerAutomaticallyLoadedPackage("splitidx"); } else if (t.cs() == "printnomenclature") { @@ -3009,6 +3203,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, os << "width \"" << width << '\"'; end_inset(os); skip_spaces_braces(p); + preamble.registerAutomaticallyLoadedPackage("nomencl"); } else if ((t.cs() == "textsuperscript" || t.cs() == "textsubscript")) { @@ -3017,10 +3212,11 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, os << t.cs().substr(4) << '\n'; parse_text_in_inset(p, os, FLAG_ITEM, false, context); end_inset(os); + if (t.cs() == "textsubscript") + preamble.registerAutomaticallyLoadedPackage("subscript"); } - else if (is_known(t.cs(), known_quotes)) { - char const * const * where = is_known(t.cs(), known_quotes); + else if ((where = is_known(t.cs(), known_quotes))) { context.check_layout(os); begin_inset(os, "Quotes "); os << known_coded_quotes[where - known_quotes]; @@ -3032,9 +3228,8 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, skip_braces(p); } - else if (is_known(t.cs(), known_sizes) && + else if ((where = is_known(t.cs(), known_sizes)) && context.new_layout_allowed) { - char const * const * where = is_known(t.cs(), known_sizes); context.check_layout(os); TeXFont const oldFont = context.font; context.font.size = known_coded_sizes[where - known_sizes]; @@ -3042,10 +3237,8 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, eat_whitespace(p, os, context, false); } - else if (is_known(t.cs(), known_font_families) && + else if ((where = is_known(t.cs(), known_font_families)) && context.new_layout_allowed) { - char const * const * where = - is_known(t.cs(), known_font_families); context.check_layout(os); TeXFont const oldFont = context.font; context.font.family = @@ -3054,10 +3247,8 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, eat_whitespace(p, os, context, false); } - else if (is_known(t.cs(), known_font_series) && + else if ((where = is_known(t.cs(), known_font_series)) && context.new_layout_allowed) { - char const * const * where = - is_known(t.cs(), known_font_series); context.check_layout(os); TeXFont const oldFont = context.font; context.font.series = @@ -3066,10 +3257,8 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, eat_whitespace(p, os, context, false); } - else if (is_known(t.cs(), known_font_shapes) && + else if ((where = is_known(t.cs(), known_font_shapes)) && context.new_layout_allowed) { - char const * const * where = - is_known(t.cs(), known_font_shapes); context.check_layout(os); TeXFont const oldFont = context.font; context.font.shape = @@ -3077,10 +3266,8 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, output_font_change(os, oldFont, context.font); eat_whitespace(p, os, context, false); } - else if (is_known(t.cs(), known_old_font_families) && + else if ((where = is_known(t.cs(), known_old_font_families)) && context.new_layout_allowed) { - char const * const * where = - is_known(t.cs(), known_old_font_families); context.check_layout(os); TeXFont const oldFont = context.font; context.font.init(); @@ -3091,10 +3278,8 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, eat_whitespace(p, os, context, false); } - else if (is_known(t.cs(), known_old_font_series) && + else if ((where = is_known(t.cs(), known_old_font_series)) && context.new_layout_allowed) { - char const * const * where = - is_known(t.cs(), known_old_font_series); context.check_layout(os); TeXFont const oldFont = context.font; context.font.init(); @@ -3105,10 +3290,8 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, eat_whitespace(p, os, context, false); } - else if (is_known(t.cs(), known_old_font_shapes) && + else if ((where = is_known(t.cs(), known_old_font_shapes)) && context.new_layout_allowed) { - char const * const * where = - is_known(t.cs(), known_old_font_shapes); context.check_layout(os); TeXFont const oldFont = context.font; context.font.init(); @@ -3141,27 +3324,11 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, p.setEncoding(enc); } - else if (t.cs() == "ldots") { + else if ((where = is_known(t.cs(), known_special_chars))) { context.check_layout(os); - os << "\\SpecialChar \\ldots{}\n"; - skip_spaces_braces(p); - } - - else if (t.cs() == "lyxarrow") { - context.check_layout(os); - os << "\\SpecialChar \\menuseparator\n"; - skip_spaces_braces(p); - } - - else if (t.cs() == "textcompwordmark") { - context.check_layout(os); - os << "\\SpecialChar \\textcompwordmark{}\n"; - skip_spaces_braces(p); - } - - else if (t.cs() == "slash") { - context.check_layout(os); - os << "\\SpecialChar \\slash{}\n"; + os << "\\SpecialChar \\" + << known_coded_special_chars[where - known_special_chars] + << '\n'; skip_spaces_braces(p); } @@ -3477,17 +3644,30 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, parse_outer_box(p, os, FLAG_ITEM, outer, context, t.cs(), ""); else if (t.cs() == "framebox") { - string special = p.getFullOpt(); - special += p.getOpt(); - parse_outer_box(p, os, FLAG_ITEM, outer, context, t.cs(), special); + if (p.next_token().character() == '(') { + //the syntax is: \framebox(x,y)[position]{content} + string arg = t.asInput(); + arg += p.getFullParentheseArg(); + arg += p.getFullOpt(); + eat_whitespace(p, os, context, false); + handle_ert(os, arg + '{', context); + eat_whitespace(p, os, context, false); + parse_text(p, os, FLAG_ITEM, outer, context); + handle_ert(os, "}", context); + } else { + string special = p.getFullOpt(); + special += p.getOpt(); + parse_outer_box(p, os, FLAG_ITEM, outer, + context, t.cs(), special); + } } //\makebox() is part of the picture environment and different from \makebox{} //\makebox{} will be parsed by parse_box else if (t.cs() == "makebox") { - string arg = t.asInput(); if (p.next_token().character() == '(') { //the syntax is: \makebox(x,y)[position]{content} + string arg = t.asInput(); arg += p.getFullParentheseArg(); arg += p.getFullOpt(); eat_whitespace(p, os, context, false); @@ -3512,8 +3692,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, skip_spaces_braces(p); } - else if (is_known(t.cs(), known_spaces)) { - char const * const * where = is_known(t.cs(), known_spaces); + else if ((where = is_known(t.cs(), known_spaces))) { context.check_layout(os); begin_inset(os, "space "); os << '\\' << known_coded_spaces[where - known_spaces] @@ -3547,7 +3726,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, t.cs() == "DeclareRobustCommandx" || t.cs() == "newcommand" || t.cs() == "newcommandx" || - t.cs() == "providecommand" || + t.cs() == "providecommand" || t.cs() == "providecommandx" || t.cs() == "renewcommand" || t.cs() == "renewcommandx") { @@ -3731,6 +3910,37 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, end_inset(os); } + else if (t.cs() == "loadgame") { + p.skip_spaces(); + string name = normalize_filename(p.verbatim_item()); + string const path = getMasterFilePath(); + // We want to preserve relative / absolute filenames, + // therefore path is only used for testing + if (!makeAbsPath(name, path).exists()) { + // The file extension is probably missing. + // Now try to find it out. + char const * const lyxskak_format[] = {"fen", 0}; + string const lyxskak_name = + find_file(name, path, lyxskak_format); + if (!lyxskak_name.empty()) + name = lyxskak_name; + } + if (makeAbsPath(name, path).exists()) + fix_relative_filename(name); + else + cerr << "Warning: Could not find file '" + << name << "'." << endl; + context.check_layout(os); + begin_inset(os, "External\n\ttemplate "); + os << "ChessDiagram\n\tfilename " + << name << "\n"; + end_inset(os); + context.check_layout(os); + // after a \loadgame follows a \showboard + if (p.get_token().asInput() == "showboard") + p.get_token(); + } + else { // try to see whether the string is in unicodesymbols // Only use text mode commands, since we are in text mode here, diff --git a/status.20x b/status.20x index 3da064f7ff..42b0b082d5 100644 --- a/status.20x +++ b/status.20x @@ -28,6 +28,10 @@ What's new * TEX2LYX IMPROVEMENTS +- Chess diagram and Spreadsheet external templates are imported + +- tabular* environments are imported + * USER INTERFACE @@ -61,6 +65,22 @@ What's new * TEX2LYX +- tex2lyx roundtips pollutes preamble with color code (bug 7845). + +- tex2lyx support for \date{} (bug 7844). + +- Latex import whitespace (bug 7668). + +- asme2e issues (bug 6449). + +- tex2lyx: problem with macros nested in \foreignlanguage (bug 5187). + +- tex2lyx booktabs support (bug 4553). + +- tex2lyx change tracking support (bug 4213). + +- tex2lyx problems with character style switches (bug 3036). + * USER INTERFACE