]> git.lyx.org Git - features.git/commitdiff
Mass backport tex2lyx bug fixes.
authorGeorg Baum <georg.baum@post.rwth-aachen.de>
Sun, 4 Dec 2011 16:16:32 +0000 (16:16 +0000)
committerGeorg Baum <georg.baum@post.rwth-aachen.de>
Sun, 4 Dec 2011 16:16:32 +0000 (16:16 +0000)
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

31 files changed:
development/cmake/src/tex2lyx/CMakeLists.txt
development/scons/scons_manifest.py
lib/syntax.default
src/Changes.h
src/LaTeXFeatures.cpp
src/LaTeXFeatures.h
src/LaTeXPackages.cpp [new file with mode: 0644]
src/LaTeXPackages.h [new file with mode: 0644]
src/Makefile.am
src/SpellChecker.h
src/frontends/qt4/GuiApplication.cpp
src/frontends/qt4/GuiChanges.cpp
src/support/lyxtime.cpp
src/support/lyxtime.h
src/tex2lyx/Makefile.am
src/tex2lyx/Parser.cpp
src/tex2lyx/Parser.h
src/tex2lyx/Preamble.cpp [new file with mode: 0644]
src/tex2lyx/Preamble.h [new file with mode: 0644]
src/tex2lyx/TODO.txt
src/tex2lyx/math.cpp
src/tex2lyx/preamble.cpp [deleted file]
src/tex2lyx/table.cpp
src/tex2lyx/test/box-color-size-space-align.tex
src/tex2lyx/test/test-insets.tex
src/tex2lyx/test/test-structure.tex
src/tex2lyx/tex2lyx.1in
src/tex2lyx/tex2lyx.cpp
src/tex2lyx/tex2lyx.h
src/tex2lyx/text.cpp
status.20x

index 141d1b1182feffe621f732fbd19f44d79986e685..b6b0cd1118771b3405d7954c803b8ca70a17f022 100644 (file)
@@ -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)
index 266beabf28fd2942638cbc720a19be60d0056492..f938bb0a6f2bb30164bef1134ee9f28bb7f0db42 100644 (file)
@@ -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
index f713cfae8a5127aaa8081d85bb9a749ed614538e..9bfed3faba03172d762a65a589714c2eae75220a 100644 (file)
@@ -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}
 
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
index 435d44bca37d609a1404152272434e5181fdf086..84c4fea707e67d684287cb6e83cd36d3e0d368b4 100644 (file)
@@ -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?
index da17de0d4190f4f46656275f9683cef8fdfac675..d3a3fc2c0b726c1c1fa271efd82ffb2330dfe082 100644 (file)
@@ -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<string> 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);
 }
 
 
index a31ea3a2e5e2ab2f8f6323f0dd21090f5dcb614d..8cbe3dcb0f35f7f06510d1345213e864364bb408 100644 (file)
@@ -86,8 +86,6 @@ public:
        void require(std::string const & name);
        /// Add a set of feature names requirements
        void require(std::set<std::string> 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<std::string> SnippetList;
        ///
        SnippetList preamble_snippets_;
-       /// The available (required) packages
-       typedef std::set<std::string> Packages;
-       ///
-       static Packages packages_;
        ///
        typedef std::set<Language const *> LanguageList;
        /// used languages (only those that are supported by babel)
diff --git a/src/LaTeXPackages.cpp b/src/LaTeXPackages.cpp
new file mode 100644 (file)
index 0000000..a231a22
--- /dev/null
@@ -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 <config.h>
+
+#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 (file)
index 0000000..ff67e0a
--- /dev/null
@@ -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 <string>
+#include <set>
+
+
+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<std::string> Packages;
+       ///
+       static Packages packages_;
+};
+
+
+} // namespace lyx
+
+#endif
index b1f88697211ada1c464396e701e8cd3794a4e0cf..4c9e979886a92e07c9816c7a5d653bbdf8bc8db7 100644 (file)
@@ -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 \
index 55651e0231413f890ec7a31696ce9ee638b26b3d..f94f5c5f3fae0ab7c3f4cf60fa200c945d69d490 100644 (file)
@@ -14,7 +14,6 @@
 #define SPELL_BASE_H
 
 #include "support/strfwd.h"
-#include "support/lyxtime.h"
 
 
 namespace lyx {
index ed60ecc18b543cadfd085f9996adf024b2857f69..a2c60c9d29f8ee972c8da8604296eca0a3a6d247 100644 (file)
@@ -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"),
index 63268ae24e808d3dc3f89428c7649ffd3c5455b7..a27d24f5808bc96255a3d2a5b807c1ab59eed11f 100644 (file)
@@ -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"))
index 7f2530d41553b4b8649ac5920cf888307a60365d..c302531e2e0b0eb2104450bd24958d3a146ffef8 100644 (file)
 
 #include "support/lyxtime.h"
 
+#include "support/debug.h"
+#include "support/environment.h"
+#include "support/lstrings.h"
+#include "support/qstring_helpers.h"
+
+#include <QDateTime>
+#include <QLocale>
+
 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<time_t>(-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<time_t>(-1);
+       }
+       if (*remainder != '\0') {
+               LYXERR(Debug::LOCALE, "Could not parse `" << t
+                               << "´ (excess characters)");
+               return static_cast<time_t>(-1);
+       }
+       return mktime(&loc_tm);
+#endif
+}
+
+} // namespace support
 } // namespace lyx
index 6a8bd27682252bf0340ffc07dc926a66939d7463..620965aebc46ceb9d484275e385c363aa3985348 100644 (file)
@@ -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
index fc39503b90218ccaded5bedb47dfc2403590d778..04aa8ec3b6e9d9a6cd1e72cd47c16735e653bf29 100644 (file)
@@ -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 \
index 5fafef9944c6f7ebde6ba693bfdf8bd50f16483b..c48301207a447ec0e5593c64fea0e2b39881a2c0 100644 (file)
@@ -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;
index dbb202ddf008c17c7a1cef10af2fa6779e9c4c89..5e749e3019f40d8be6897664d0e68fed87ae94cb 100644 (file)
@@ -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 (file)
index 0000000..ff1d041
--- /dev/null
@@ -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 <config.h>
+
+#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 <algorithm>
+#include <iostream>
+
+using namespace std;
+using namespace lyx::support;
+
+
+namespace lyx {
+
+// special columntypes
+extern map<char, int> 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_sep<name>package_mid_sep<package loading code>package_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<string> & 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<string>::iterator it;
+       vector<string>::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<string> & 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<string>::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<string> split_options(string const & input)
+{
+       vector<string> 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<string> & options, string name)
+{
+       for (size_t i = 0; i < options.size(); ++i) {
+               vector<string> 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<string> Preamble::getPackageOptions(string const & package) const
+{
+       map<string, vector<string> >::const_iterator it = used_packages.find(package);
+       if (it != used_packages.end())
+               return it->second;
+       return vector<string>();
+}
+
+
+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<string> & options)
+{
+       // every package inherits the global options
+       if (used_packages.find(name) == used_packages.end())
+               used_packages[name] = split_options(h_options);
+
+       vector<string> & 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<string>::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<string>(100 * convert<double>(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<string> & 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<string>::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<string> & options)
+{
+       h_use_geometry = "true";
+       vector<string>::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<string> 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<string>::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<string>::const_iterator const end = used_modules.end();
+               vector<string>::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<string>::iterator it;
+                       vector<string> 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<string> vecnames;
+                       split(name, vecnames, ',');
+                       vector<string>::const_iterator it  = vecnames.begin();
+                       vector<string>::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<string> 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<string> 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<string> 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 (file)
index 0000000..a4af4db
--- /dev/null
@@ -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 <iosfwd>
+#include <sstream>
+#include <string>
+#include <vector>
+#include <map>
+#include <set>
+
+
+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<std::string>
+       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<std::string, std::vector<std::string> > used_packages;
+       /// Packages that will be loaded automatically by LyX
+       std::set<std::string> auto_packages;
+       ///
+       std::vector<std::string> 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<std::string> & options);
+       ///
+       void handle_hyperref(std::vector<std::string> & options);
+       ///
+       void handle_geometry(std::vector<std::string> & 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
index 9fb7afc53cdb00404449471554f0959cb2ce5829..7a4b2e30ce2c74472929d957ab0a1967f9b0eae0 100644 (file)
@@ -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
 
index f615926efa6e2d6daf0eae8902b58891e6025c18..d17d1d90174a5e11c01fd49a9318c14c5d7acbdd 100644 (file)
@@ -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 (file)
index 08c3235..0000000
+++ /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 <config.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 <algorithm>
-#include <iostream>
-#include <sstream>
-#include <string>
-#include <vector>
-#include <map>
-
-using namespace std;
-using namespace lyx::support;
-
-
-namespace lyx {
-
-// special columntypes
-extern map<char, int> special_columns;
-
-map<string, vector<string> > 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<string> & 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<string>::iterator it;
-       vector<string>::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<string> & 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<string>::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<string> split_options(string const & input)
-{
-       vector<string> 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<string> & options, string name)
-{
-       for (size_t i = 0; i < options.size(); ++i) {
-               vector<string> 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<string> & options)
-{
-       // every package inherits the global options
-       if (used_packages.find(name) == used_packages.end())
-               used_packages[name] = split_options(h_options);
-
-       vector<string> & 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<string>::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<string>(100 * convert<double>(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<string> & 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<string>::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<string> 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<string>::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<string>::iterator it;
-                       vector<string> 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<string> vecnames;
-                       split(name, vecnames, ',');
-                       vector<string>::const_iterator it  = vecnames.begin();
-                       vector<string>::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<string> opts = split_options(p.getArg('{', '}'));
-                       vector<string>::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<string> 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<string> 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
index 37a74509b62b04de9b241ca0988e416821613c62..588d10c1b99421a78e995440e718ea9e4407a044 100644 (file)
@@ -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<CellInfo> & 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> > cellinfo(lines.size());
        vector<RowInfo> 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<string> 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<string> t;
-                                       split(arg, t, '-');
-                                       t.resize(2);
-                                       size_t from = convert<unsigned int>(t[0]);
+                                       //cerr << "read " << t.cs() << " arg: '" << arg << "'\n";
+                                       vector<string> cols;
+                                       split(arg, cols, '-');
+                                       cols.resize(2);
+                                       size_t from = convert<unsigned int>(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<unsigned int>(t[1]);
+                                       size_t to = convert<unsigned int>(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<string> 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<lyxtabular version=\"3\" rows=\"" << rowinfo.size()
           << "\" columns=\"" << colinfo.size() << "\">\n";
        os << "<features"
           << write_attribute("rotate", false)
+          << write_attribute("booktabs", booktabs)
           << write_attribute("islongtable", is_long_tabular);
-       if (!is_long_tabular)
-               os << write_attribute("tabularvalignment", tabularvalignment);
+       if (is_long_tabular) {
+               os << write_attribute("firstHeadTopDL", endfirsthead.topDL)
+                  << write_attribute("firstHeadBottomDL", endfirsthead.bottomDL)
+                  << write_attribute("firstHeadEmpty", endfirsthead.empty)
+                  << write_attribute("headTopDL", endhead.topDL)
+                  << write_attribute("headBottomDL", endhead.bottomDL)
+                  << write_attribute("footTopDL", endfoot.topDL)
+                  << write_attribute("footBottomDL", endfoot.bottomDL)
+                  << write_attribute("lastFootTopDL", endlastfoot.topDL)
+                  << write_attribute("lastFootBottomDL", endlastfoot.bottomDL)
+                  << write_attribute("lastFootEmpty", endlastfoot.empty);
+       } else
+               os << write_attribute("tabularvalignment", tabularvalignment)
+                  << write_attribute("tabularwidth", tabularwidth);
        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 << "<row"
+                  << write_attribute("topspace", rowinfo[row].top_space)
+                  << write_attribute("bottomspace", rowinfo[row].bottom_space)
+                  << write_attribute("interlinespace", rowinfo[row].interline_space)
                   << write_attribute("endhead",
                                      rowinfo[row].type == LT_HEAD)
                   << write_attribute("endfirsthead",
index 4a6e4be0ead672a8e1729439f027cd8a9966ac04..278a5e1928c60aeac951b1a418adcbb59135d986 100644 (file)
@@ -87,6 +87,7 @@ blabla \makebox[3cm][l]{makebox 3} blabla
 \begin{picture}(8,6)
 \put(0,0){\makebox(0,0)[tr]{AAA}}
 \put(8,0){\makebox(0,0){BBB}}
+\put(0,8){\framebox(0,0){x}}
 \put(1,0){\line(1,0){6}}   
 \end{picture}
 
index f6b09391ccca2b491f7dc5422a8465f8d575fa16..8e086e7ec741a82ea86128b1ecf5b4b11f4e9893 100644 (file)
@@ -114,8 +114,19 @@ M., \& Rasio, F.~A. 2004, ApJ, 604, 632\end{thebibliography}
 \section{Input files\index{Input files}}
 
 We can input files too, like this \input{DummyDocument}, or with the include
-variant \include{DummyDocument} % unfortunately, including the doc twice
-% generates a multiply defined label
+variant \include{DummyDocument} % unfortunately, including the doc twice generates a multiply defined label
+
+We can also import chess diagrams:
+
+\loadgame{../../../lib/examples/iecc05}\showboard
+
+Spreadsheets:
+
+\def\inputGnumericTable{}\input{../../../lib/examples/longsheet.gnumeric}
+
+and PDF pages:
+
+\includepdf[pages=-,angle=22,origin=Bl,width=5cm,height=40mm,keepaspectratio]{../../../lib/examples/beamer-icsi-logo}
 
 If you prefer verbatim input, you can choose
 between~\verbatiminput{foo} or~\verbatiminput*{foo}.
@@ -258,6 +269,97 @@ Lots of lines& like this.\\
 Lots of lines& like this.
 \end{longtable}
 
+From bug 7412 another example with more captions (can currently not produced in LyX):
+\begin{longtable}{|l|l|}
+\caption{A long table}
+\endfirsthead
+\caption{A long table -- continued}
+\endhead
+\multicolumn{2}{r}{{Continued on next page}}
+\tabularnewline
+\endfoot
+\endlastfoot
+\hline
+\multicolumn{1}{|c|}{Something} & \multicolumn{1}{c|}{Description}\tabularnewline
+\hline Lots of lines& like this.\\
+\hline Lots of lines& like this.\\
+\hline Lots of lines& like this.\\
+\hline Lots of lines& like this.\\
+\hline Lots of lines& like this.\\
+\hline Lots of lines& like this.\\
+\hline Lots of lines& like this.\\
+\hline Lots of lines& like this.\\
+\hline Lots of lines& like this.\\
+\hline Lots of lines& like this.\\
+\hline Lots of lines& like this.\\
+\hline Lots of lines& like this.\\
+\hline Lots of lines& like this.\\
+\hline Lots of lines& like this.\\
+\hline Lots of lines& like this.\\
+\hline Lots of lines& like this.\\
+\hline Lots of lines& like this.\\
+\hline Lots of lines& like this.\\
+\hline Lots of lines& like this.\\
+\hline Lots of lines& like this.\\
+\hline Lots of lines& like this.\\
+\hline Lots of lines& like this.\\
+\hline Lots of lines& like this.\\
+\hline Lots of lines& like this.\\
+\hline Lots of lines& like this.\\
+\hline Lots of lines& like this.\\
+\hline Lots of lines& like this.\\
+\hline Lots of lines& like this.\\
+\hline Lots of lines& like this.\\
+\hline Lots of lines& like this.\\
+\hline Lots of lines& like this.\\
+\hline Lots of lines& like this.\\
+\hline Lots of lines& like this.\\
+\hline Lots of lines& like this.\\
+\hline Lots of lines& like this.\\
+\hline Lots of lines& like this.\\
+\hline Lots of lines& like this.\\
+\hline Lots of lines& like this.\\
+\hline Lots of lines& like this.\\
+\hline Lots of lines& like this.\\
+\hline Lots of lines& like this.\\
+\hline
+\end{longtable}
+
+A table*:
+
+\begin{tabular*} % some comment
+{0.8\columnwidth}[b]{lr}
+two\\
+lonely&lines
+\end{tabular*}
+
+A booktabs table:
+
+\begin{table}[h]
+\caption{\label{tab:Special-booktabs-table}Special booktabs-table}
+
+
+\centering{}%
+\begin{tabular}{cccc}
+\toprule 
+System & Chip\,1 & \multicolumn{2}{c}{Chip\,2}\tabularnewline
+\cmidrule(r){2-2}\cmidrule(l){3-4}\morecmidrules \cmidrule{2-4}Detector
+thickness in \textmu{}m & 300 & 300 & 700\tabularnewline
+\midrule 
+Edge angle in \textdegree{} & 3.55 & 2.71 & 7.99\tabularnewline
+\addlinespace
+Spatial resolution in \textmu{}m & 4.26 & 10.17 & 10.56\tabularnewline
+\addlinespace
+MTF at $f_{\mathrm{max}}$ & 0.53 & 0.37 & 0.39\tabularnewline
+\midrule 
+\morecmidrules \cmidrule{3-4}LSF-spatial resolution &  &  & \tabularnewline
+in \textmu{}m & 129.7 & 52.75 & 50.78\tabularnewline
+in \% of pixel size & 76.3 & 95.9 & 92.3\tabularnewline
+\bottomrule
+\end{tabular}
+\end{table}
+
+
 \section{Macros}
 
 LyX supports several kinds of macros:
index cc13a6e311419ef6c05390accf766e5e28ef2ca7..6ca87694a818c5abc3acacb261ee1cd66992da57 100644 (file)
@@ -16,6 +16,8 @@
 \newcommand{\noun}[1]{\textsc{#1}}
 %% Because html converters don't know tabularnewline
 \providecommand{\tabularnewline}{\\}
+\newcommand{\lyxadded}[3]{#3}
+\newcommand{\lyxdeleted}[3]{}
 
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Textclass specific LaTeX commands.
 \newenvironment{lyxlist}[1]
@@ -87,6 +89,12 @@ This causes the \strong{logikalmkup} module to be loaded.
 An environment
 \end{quote}
 
+We also support change tracking:
+\lyxadded{Hans Wurst}{Sun Nov  6 10:39:39 2011}{Added text}
+some parts remain
+\lyxdeleted{Hans Wurst}{Sun Nov  6 10:39:55 2011}{This was the original text}
+some parts remain
+
 \section*{A starred section for floats}
 
 \begin{figure}
@@ -278,6 +286,7 @@ labelings:
 \begin{lyxlist}{00.00.0000}
 \item [label~1] first item
 \item [label~2] second item
+\item [{$\left[\textrm{ }\right]^{x}$}] Label with space, math and ] in it
 \end{lyxlist}
 and bibliography:
 \begin{thebibliography}{9}
index 316ec0ce2021de1e14251b097612f0014c5ce8a5..028f10c9068ac58aceac9fdff22b8bb0cd45045a 100644 (file)
@@ -206,8 +206,6 @@ should thus be conserved in printed documents, although it will not of
 course show up in the LyX window. Check Document->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
index 25f5955a68f3d3c46ed434d7f10780dffd20ce25..5bc77d6d2eeef12399c889ea7707db30280c34e4 100644 (file)
@@ -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();
        // <origfile> 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
index 1a8317e56b7a4bfab20b987f6c90707c97d06a81..730013c03b64db442bd7545860f14ab6fce6496d 100644 (file)
@@ -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<std::string, std::vector<std::string> > 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<std::string> 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;
 
index bd7a0d203ccd31d0a98d2e9cfd1df6f4b6cda981..8a3d8fb943d59153e0adb01fad7eefef8d383790 100644 (file)
 #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 <algorithm>
 #include <iostream>
@@ -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<string>(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<string> const & req = newlayout->requires();
+               for (set<string>::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<string> const & req = newlayout->requires();
+                                       for (set<string>::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<string> const & req = newlayout->requires();
+                       for (set<string>::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<string> const & req = newlayout->requires();
+                       for (set<string>::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<time_t>(-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<string> const & options = used_packages["jurabib"];
+                       vector<string> 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,
index 3da064f7ff8dbbbcea8d37d11916c2499435d4c0..42b0b082d50a7bf3f423cf8fa89f04663cea67be 100644 (file)
@@ -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