]> git.lyx.org Git - lyx.git/blobdiff - src/tex2lyx/text.cpp
tex2lyx: improve CJK handling
[lyx.git] / src / tex2lyx / text.cpp
index 0a2fc0756d56a579275827c06ec6f76083228976..62d5e8df9ebfaf80c6d3458a94bf37d5ffdba6af 100644 (file)
@@ -24,6 +24,8 @@
 #include "Length.h"
 #include "Preamble.h"
 
+#include "insets/ExternalTemplate.h"
+
 #include "support/lassert.h"
 #include "support/convert.h"
 #include "support/FileName.h"
@@ -115,6 +117,30 @@ char const * const known_ref_commands[] = { "ref", "pageref", "vref",
 char const * const known_coded_ref_commands[] = { "ref", "pageref", "vref",
  "vpageref", "formatted", "eqref", 0 };
 
+/**
+ * supported CJK encodings
+ * SJIS anf Bg5 cannot be supported as this is not
+ * supported by iconv
+ * JIS does not work with LyX's encoding conversion
+ */
+const char * const supported_CJK_encodings[] = {
+"EUC-JP", "KS", "GB", "UTF8", 0};
+
+/**
+ * the same as supported_CJK_encodings with their corresponding LyX language name
+ * please keep this in sync with supported_CJK_encodings line by line!
+ */
+const char * const coded_supported_CJK_encodings[] = {
+"japanese-cjk", "korean", "chinese-simplified", "chinese-traditional", 0};
+
+string CJK2lyx(string const & encoding)
+{
+       char const * const * where = is_known(encoding, supported_CJK_encodings);
+       if (where)
+               return coded_supported_CJK_encodings[where - supported_CJK_encodings];
+       return encoding;
+}
+
 /*!
  * natbib commands.
  * The starred forms are also known except for "citefullauthor",
@@ -454,9 +480,15 @@ docstring convert_unicodesymbols(docstring s)
                        continue;
                }
                s = s.substr(i);
+               bool termination;
                docstring rem;
-               docstring parsed = encodings.fromLaTeXCommand(s, rem,
-                               Encodings::TEXT_CMD);
+               set<string> req;
+               docstring parsed = encodings.fromLaTeXCommand(s,
+                               Encodings::TEXT_CMD, termination, rem, &req);
+               set<string>::const_iterator it = req.begin();
+               set<string>::const_iterator en = req.end();
+               for (; it != en; ++it)
+                       preamble.registerAutomaticallyLoadedPackage(*it);
                os << parsed;
                s = rem;
                if (s.empty() || s[0] != '\\')
@@ -564,7 +596,7 @@ void skip_spaces_braces(Parser & p, bool keepws = false)
           should be handled by this function:
           - abc \j{} xyz
           - abc \j {} xyz
-          - abc \j 
+          - abc \j
             {} xyz
           - abc \j %comment
             {} xyz
@@ -608,7 +640,7 @@ void output_command_layout(ostream & os, Parser & p, bool outer,
        while (optargs < context.layout->optargs) {
                eat_whitespace(p, os, context, false);
                if (p.next_token().cat() == catEscape ||
-                   p.next_token().character() != '[') 
+                   p.next_token().character() != '[')
                        break;
                p.get_token(); // eat '['
                begin_inset(os, "Argument\n");
@@ -925,7 +957,7 @@ void parse_box(Parser & p, ostream & os, unsigned outer_flags,
                        // the inner env
                        if (!inner_type.empty() && (inner_flags & FLAG_END))
                                active_environments.pop_back();
-       
+
                        // Ensure that the end of the outer box is parsed correctly:
                        // The opening brace has been eaten by parse_outer_box()
                        if (!outer_type.empty() && (outer_flags & FLAG_ITEM)) {
@@ -1113,16 +1145,28 @@ void parse_outer_box(Parser & p, ostream & os, unsigned flags, bool outer,
 }
 
 
-void parse_listings(Parser & p, ostream & os, Context & parent_context)
+void parse_listings(Parser & p, ostream & os, Context & parent_context, bool in_line)
 {
        parent_context.check_layout(os);
        begin_inset(os, "listings\n");
-       os << "inline false\n"
-          << "status collapsed\n";
+       if (p.hasOpt()) {
+               string arg = p.verbatimOption();
+               os << "lstparams " << '"' << arg << '"' << '\n';
+       }
+       if (in_line)
+               os << "inline true\n";
+       else
+               os << "inline false\n";
+       os << "status collapsed\n";
        Context context(true, parent_context.textclass);
        context.layout = &parent_context.textclass.plainLayout();
-       context.check_layout(os);
-       string const s = p.verbatimEnvironment("lstlisting");
+       string s;
+       if (in_line) {
+               s = p.plainCommand('!', '!', "lstinline");
+               context.new_paragraph(os);
+               context.check_layout(os);
+       } else
+               s = p.plainEnvironment("lstlisting");
        for (string::const_iterator it = s.begin(), et = s.end(); it != et; ++it) {
                if (*it == '\\')
                        os << "\n\\backslash\n";
@@ -1168,8 +1212,7 @@ void parse_unknown_environment(Parser & p, string const & name, ostream & os,
 
 
 void parse_environment(Parser & p, ostream & os, bool outer,
-                       string & last_env, bool & title_layout_found,
-                       Context & parent_context)
+                       string & last_env, Context & parent_context)
 {
        Layout const * newlayout;
        InsetLayout const * newinsetlayout = 0;
@@ -1193,6 +1236,23 @@ void parse_environment(Parser & p, ostream & os, bool outer,
                }
        }
 
+       else if (is_known(name, preamble.polyglossia_languages)) {
+               // We must begin a new paragraph if not already done
+               if (! parent_context.atParagraphStart()) {
+                       parent_context.check_end_layout(os);
+                       parent_context.new_paragraph(os);
+               }
+               // save the language in the context so that it is
+               // handled by parse_text
+               parent_context.font.language = preamble.polyglossia2lyx(name);
+               parse_text(p, os, FLAG_END, outer, parent_context);
+               // Just in case the environment is empty
+               parent_context.extra_stuff.erase();
+               // We must begin a new paragraph to reset the language
+               parent_context.new_paragraph(os);
+               p.skip_spaces();
+       }
+
        else if (unstarred_name == "tabular" || name == "longtable") {
                eat_whitespace(p, os, parent_context, false);
                string width = "0pt";
@@ -1223,6 +1283,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";
@@ -1253,6 +1322,7 @@ void parse_environment(Parser & p, ostream & os, bool outer,
                // we must make sure that the next item gets a \begin_layout.
                parent_context.new_paragraph(os);
                p.skip_spaces();
+               preamble.registerAutomaticallyLoadedPackage("rotfloat");
        }
 
        else if (name == "wrapfigure" || name == "wraptable") {
@@ -1285,6 +1355,7 @@ void parse_environment(Parser & p, ostream & os, bool outer,
                // we must make sure that the next item gets a \begin_layout.
                parent_context.new_paragraph(os);
                p.skip_spaces();
+               preamble.registerAutomaticallyLoadedPackage("wrapfig");
        }
 
        else if (name == "minipage") {
@@ -1324,6 +1395,80 @@ void parse_environment(Parser & p, ostream & os, bool outer,
                end_inset(os);
                p.skip_spaces();
                skip_braces(p); // eat {} that might by set by LyX behind comments
+               preamble.registerAutomaticallyLoadedPackage("verbatim");
+       }
+
+       else if (name == "verbatim") {
+               os << "\n\\end_layout\n\n\\begin_layout Verbatim\n";
+               string const s = p.plainEnvironment("verbatim");
+               string::const_iterator it2 = s.begin();
+               for (string::const_iterator it = s.begin(), et = s.end(); it != et; ++it) {
+                       if (*it == '\\')
+                               os << "\\backslash ";
+                       else if (*it == '\n') {
+                               it2 = it + 1;
+                               // avoid adding an empty paragraph at the end
+                               // FIXME: if there are 2 consecutive spaces at the end ignore it
+                               // because LyX will re-add a \n
+                               // This hack must be removed once bug 8049 is fixed!
+                               if ((it + 1 != et) && (it + 2 != et || *it2 != '\n'))
+                                       os << "\n\\end_layout\n\\begin_layout Verbatim\n";
+                       } else 
+                               os << *it;
+               }
+               os << "\n\\end_layout\n\n";
+               p.skip_spaces();
+               // reset to Standard layout
+               os << "\n\\begin_layout Standard\n";
+       }
+
+       else if (name == "CJK") {
+               // the scheme is \begin{CJK}{encoding}{mapping}{text}
+               // It is impossible to decide if a CJK environment was in its own paragraph or within
+               // a line. We therefore always assume a paragraph since the latter is a rare case.
+               eat_whitespace(p, os, parent_context, false);
+               parent_context.check_end_layout(os);
+               // store the encoding to be able to reset it
+               string const encoding_old = p.getEncoding();
+               string const encoding = p.getArg('{', '}');
+               // SJIS and Bg5 cammopt be handled by iconv
+               // JIS does not work with LyX's encoding conversion
+               if (encoding != "Bg5" && encoding != "JIS" && encoding != "SJIS")
+                       p.setEncoding(encoding);
+               else
+                       p.setEncoding("utf8");
+               // LyX doesn't support the second argument so if
+               // this is used we need to output everything as ERT
+               string const mapping = p.getArg('{', '}');
+               if ((!mapping.empty() && mapping != " ")
+                       || (!is_known(encoding, supported_CJK_encodings))) {
+                       parent_context.check_layout(os);
+                       handle_ert(os, "\\begin{" + name + "}{" + encoding + "}{" + mapping + "}",
+                                      parent_context);
+                       // we must parse the content as verbatim because e.g. JIS can contain
+                       // normally invalid characters
+                       string const s = p.plainEnvironment("CJK");
+                       for (string::const_iterator it = s.begin(), et = s.end(); it != et; ++it) {
+                               if (*it == '\\')
+                                       handle_ert(os, "\\", parent_context);
+                               else if (*it == '$')
+                                       handle_ert(os, "$", parent_context);
+                               else 
+                                       os << *it;
+                       }
+                       handle_ert(os, "\\end{" + name + "}",
+                                      parent_context);
+               } else {
+                       string const lang = CJK2lyx(encoding);
+                       // store the language because we must reset it at the end
+                       string const lang_old = parent_context.font.language;
+                       parent_context.font.language = lang;
+                       parse_text_in_inset(p, os, FLAG_END, outer, parent_context);
+                       parent_context.font.language = lang_old;
+                       parent_context.new_paragraph(os);
+               }
+               p.setEncoding(encoding_old);
+               p.skip_spaces();
        }
 
        else if (name == "lyxgreyedout") {
@@ -1346,14 +1491,9 @@ 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);
-               else
-                       parse_listings(p, os, parent_context);
+               // FIXME handle the automatic color package loading
+               // uwestoehr asks: In what case color is loaded?
+               parse_listings(p, os, parent_context, false);
                p.skip_spaces();
        }
 
@@ -1386,12 +1526,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();
@@ -1459,7 +1603,7 @@ void parse_environment(Parser & p, ostream & os, bool outer,
                        while (optargs < context.layout->optargs) {
                                eat_whitespace(p, os, context, false);
                                if (p.next_token().cat() == catEscape ||
-                                   p.next_token().character() != '[') 
+                                   p.next_token().character() != '[')
                                        break;
                                p.get_token(); // eat '['
                                if (need_layout) {
@@ -1501,8 +1645,13 @@ 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;
+               if (!preamble.titleLayoutFound())
+                       preamble.titleLayoutFound(newlayout->intitle);
+               set<string> const & req = newlayout->requires();
+               set<string>::const_iterator it = req.begin();
+               set<string>::const_iterator en = req.end();
+               for (; it != en; ++it)
+                       preamble.registerAutomaticallyLoadedPackage(*it);
        }
 
        // The single '=' is meant here.
@@ -1877,6 +2026,28 @@ void parse_macro(Parser & p, ostream & os, Context & context)
                handle_ert(os, command + ert, context);
 }
 
+
+void registerExternalTemplatePackages(string const & name)
+{
+       external::TemplateManager const & etm = external::TemplateManager::get();
+       external::Template const * const et = etm.getTemplateByName(name);
+       if (!et)
+               return;
+       external::Template::Formats::const_iterator cit = et->formats.end();
+       if (pdflatex)
+               cit = et->formats.find("PDFLaTeX");
+       if (cit == et->formats.end())
+               // If the template has not specified a PDFLaTeX output,
+               // we try the LaTeX format.
+               cit = et->formats.find("LaTeX");
+       if (cit == et->formats.end())
+               return;
+       vector<string>::const_iterator qit = cit->second.requirements.begin();
+       vector<string>::const_iterator qend = cit->second.requirements.end();
+       for (; qit != qend; ++qit)
+               preamble.registerAutomaticallyLoadedPackage(*qit);
+}
+
 } // anonymous namespace
 
 
@@ -1889,14 +2060,50 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
        // Store the latest bibliographystyle and nocite{*} option
        // (needed for bibtex inset)
        string btprint;
-       string bibliographystyle;
+       string bibliographystyle = "default";
        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();
 
+       // it is impossible to determine the correct document language if CJK is used.
+       // Therefore write a note at the beginning of the document
+       if (have_CJK) {
+               context.check_layout(os);
+               begin_inset(os, "Note Note\n");
+               os << "status open\n\\begin_layout Plain Layout\n"
+                  << "\\series bold\n"
+                  << "Important information:\n"
+                  << "\\end_layout\n\n"
+                  << "\\begin_layout Plain Layout\n"
+                  << "This document contains text in Chinese, Japanese or Korean.\n"
+                  << " It was therefore impossible for tex2lyx to set the correct document langue for your document."
+                  << " Please set the language manually in the document settings.\n"
+                  << "\\end_layout\n";
+               end_inset(os);
+               have_CJK = false;
+       }
+
+       // it is impossible to determine the correct encoding for non-CJK Japanese.
+       // Therefore write a note at the beginning of the document
+       if (is_nonCJKJapanese) {
+               context.check_layout(os);
+               begin_inset(os, "Note Note\n");
+               os << "status open\n\\begin_layout Plain Layout\n"
+                  << "\\series bold\n"
+                  << "Important information:\n"
+                  << "\\end_layout\n\n"
+                  << "\\begin_layout Plain Layout\n"
+                  << "This document is in Japanese (non-CJK).\n"
+                  << " It was therefore impossible for tex2lyx to determine the correct encoding."
+                  << " The encoding EUC-JP was assumed. If this is incorrect, please set the correct"
+                  << " encoding in the document settings.\n"
+                  << "\\end_layout\n";
+               end_inset(os);
+               is_nonCJKJapanese = false;
+       }
+
 #ifdef FILEDEBUG
                debugToken(cerr, t, flags);
 #endif
@@ -2079,8 +2286,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();
@@ -2090,14 +2299,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() == ']' ||
@@ -2170,6 +2384,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);
+                               }
                        }
                }
 
@@ -2212,7 +2427,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
 
                else if (t.cs() == "begin")
                        parse_environment(p, os, outer, last_env,
-                                         title_layout_found, context);
+                                         context);
 
                else if (t.cs() == "end") {
                        if (flags & FLAG_END) {
@@ -2233,7 +2448,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();
@@ -2283,7 +2499,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
@@ -2298,8 +2515,58 @@ 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
+                                               // The file extension is in every case ".tex".
+                                               // So we need to remove this extension and check for
+                                               // the original one.
+                                               name = removeExtension(name);
+                                               if (!makeAbsPath(name, path).exists()) {
+                                                       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;
+                                               // register the packages that are automatically reloaded
+                                               // by the Gnumeric template
+                                               registerExternalTemplatePackages("GnumericSpreadsheet");
+                                       }
+                               }
+                       }
+                       if (macro)
+                               parse_macro(p, os, context);
+               }
 
                else if (t.cs() == "noindent") {
                        p.skip_spaces();
@@ -2331,10 +2598,14 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
 
                // Must catch empty dates before findLayout is called below
                else if (t.cs() == "date") {
+                       eat_whitespace(p, os, context, false);
+                       p.pushPosition();
                        string const date = p.verbatim_item();
-                       if (date.empty())
+                       p.popPosition();
+                       if (date.empty()) {
                                preamble.suppressDate(true);
-                       else {
+                               p.verbatim_item();
+                       } else {
                                preamble.suppressDate(false);
                                if (context.new_layout_allowed &&
                                    (newlayout = findLayout(context.textclass,
@@ -2342,12 +2613,18 @@ 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;
+                                       parse_text_snippet(p, os, FLAG_ITEM, outer, context);
+                                       if (!preamble.titleLayoutFound())
+                                               preamble.titleLayoutFound(newlayout->intitle);
+                                       set<string> const & req = newlayout->requires();
+                                       set<string>::const_iterator it = req.begin();
+                                       set<string>::const_iterator en = req.end();
+                                       for (; it != en; ++it)
+                                               preamble.registerAutomaticallyLoadedPackage(*it);
                                } else
-                                       handle_ert(os, "\\date{" + date + '}',
-                                                       context);
+                                       handle_ert(os,
+                                               "\\date{" + p.verbatim_item() + '}',
+                                               context);
                        }
                }
 
@@ -2360,8 +2637,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;
+                       if (!preamble.titleLayoutFound())
+                               preamble.titleLayoutFound(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
@@ -2370,8 +2650,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;
+                       if (!preamble.titleLayoutFound())
+                               preamble.titleLayoutFound(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") {
@@ -2620,6 +2903,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        // Warn about invalid options.
                        // Check whether some option was given twice.
                        end_inset(os);
+                       preamble.registerAutomaticallyLoadedPackage("graphicx");
                }
 
                else if (t.cs() == "footnote" ||
@@ -2641,6 +2925,11 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        end_inset(os);
                }
 
+               else if (t.cs() == "lstinline") {
+                       p.skip_spaces();
+                       parse_listings(p, os, context, true);
+               }
+
                else if (t.cs() == "ensuremath") {
                        p.skip_spaces();
                        context.check_layout(os);
@@ -2654,18 +2943,20 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                }
 
                else if (t.cs() == "makeindex" || t.cs() == "maketitle") {
-                       if (title_layout_found) {
+                       if (preamble.titleLayoutFound()) {
                                // swallow this
                                skip_spaces_braces(p);
                        } else
                                handle_ert(os, t.asInput(), context);
                }
 
-               else if (t.cs() == "tableofcontents") {
+               else if (t.cs() == "tableofcontents" || t.cs() == "lstlistoflistings") {
                        context.check_layout(os);
-                       begin_command_inset(os, "toc", "tableofcontents");
+                       begin_command_inset(os, "toc", t.cs());
                        end_inset(os);
                        skip_spaces_braces(p);
+                       if (t.cs() == "lstlistoflistings")
+                               preamble.registerAutomaticallyLoadedPackage("listings");
                }
 
                else if (t.cs() == "listoffigures") {
@@ -2861,7 +3152,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        end_inset(os);
                        skip_spaces_braces(p);
                }
-               
+
                else if (t.cs() == "lyxline") {
                        // swallow size argument (it is not used anyway)
                        p.getArg('{', '}');
@@ -2926,6 +3217,9 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                   << convert_command_inset_arg(p.verbatim_item())
                                   << "\"\n";
                                end_inset(os);
+                               if (t.cs() == "vref" || t.cs() == "vpageref")
+                                       preamble.registerAutomaticallyLoadedPackage("varioref");
+
                        } else {
                                // LyX does not support optional arguments of ref commands
                                handle_ert(os, t.asInput() + '[' + opt + "]{" +
@@ -3058,12 +3352,18 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                btprint = key;
                }
 
-               else if (t.cs() == "index") {
+               else if (t.cs() == "index" ||
+                        (t.cs() == "sindex" && preamble.use_indices() == "true")) {
                        context.check_layout(os);
-                       begin_inset(os, "Index idx\n");
-                       os << "status collapsed\n";
+                       string const arg = (t.cs() == "sindex" && p.hasOpt()) ?
+                               p.getArg('[', ']') : "";
+                       string const kind = arg.empty() ? "idx" : arg;
+                       begin_inset(os, "Index ");
+                       os << kind << "\nstatus collapsed\n";
                        parse_text_in_inset(p, os, FLAG_ITEM, false, context, "Index");
                        end_inset(os);
+                       if (kind != "idx")
+                               preamble.registerAutomaticallyLoadedPackage("splitidx");
                }
 
                else if (t.cs() == "nomenclature") {
@@ -3078,8 +3378,9 @@ 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") {
                        context.check_layout(os);
                        begin_command_inset(os, "label", "label");
@@ -3095,6 +3396,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") {
@@ -3121,6 +3425,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")) {
@@ -3222,8 +3527,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                else if (t.cs() == "selectlanguage") {
                        context.check_layout(os);
                        // save the language for the case that a
-                       // \foreignlanguage is used 
-
+                       // \foreignlanguage is used
                        context.font.language = babel2lyx(p.verbatim_item());
                        os << "\n\\lang " << context.font.language << "\n";
                }
@@ -3234,6 +3538,36 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                              context, "\\lang",
                                              context.font.language, lang);
                }
+               
+               else if (prefixIs(t.cs(), "text") 
+                        && is_known(t.cs().substr(4), preamble.polyglossia_languages)) {
+                       // scheme is \textLANGUAGE{text} where LANGUAGE is in polyglossia_languages[]
+                       string lang;
+                       // We have to output the whole command if it has an option
+                       // because LyX doesn't support this yet, see bug #8214,
+                       // only if there is a single option specifying a variant, we can handle it.
+                       if (p.hasOpt()) {
+                               string langopts = p.getOpt();
+                               // check if the option contains a variant, if yes, extract it
+                               string::size_type pos_var = langopts.find("variant");
+                               string::size_type i = langopts.find(',');
+                               string::size_type k = langopts.find('=', pos_var);
+                               if (pos_var != string::npos && i == string::npos) {
+                                       string variant;
+                                       variant = langopts.substr(k + 1, langopts.length() - k - 2);
+                                       lang = preamble.polyglossia2lyx(variant);
+                                       parse_text_attributes(p, os, FLAG_ITEM, outer,
+                                                                 context, "\\lang",
+                                                                 context.font.language, lang);
+                               } else
+                                       handle_ert(os, t.asInput() + langopts, context);
+                       } else {
+                               lang = preamble.polyglossia2lyx(t.cs().substr(4, string::npos));
+                               parse_text_attributes(p, os, FLAG_ITEM, outer,
+                                                         context, "\\lang",
+                                                         context.font.language, lang);
+                       }
+               }
 
                else if (t.cs() == "inputencoding") {
                        // nothing to write here
@@ -3329,21 +3663,27 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        handle_ert(os, t.asInput(), context);
 
                // accents (see Table 6 in Comprehensive LaTeX Symbol List)
-               else if (t.cs().size() == 1 
+               else if (t.cs().size() == 1
                         && contains("\"'.=^`bcdHkrtuv~", t.cs())) {
                        context.check_layout(os);
                        // try to see whether the string is in unicodesymbols
+                       bool termination;
                        docstring rem;
-                       string command = t.asInput() + "{" 
+                       string command = t.asInput() + "{"
                                + trimSpaceAndEol(p.verbatim_item())
                                + "}";
-                       docstring s = encodings.fromLaTeXCommand(from_utf8(command), rem);
+                       set<string> req;
+                       docstring s = encodings.fromLaTeXCommand(from_utf8(command),
+                               Encodings::TEXT_CMD | Encodings::MATH_CMD,
+                               termination, rem, &req);
                        if (!s.empty()) {
                                if (!rem.empty())
-                                       cerr << "When parsing " << command 
+                                       cerr << "When parsing " << command
                                             << ", result is " << to_utf8(s)
                                             << "+" << to_utf8(rem) << endl;
                                os << to_utf8(s);
+                               for (set<string>::const_iterator it = req.begin(); it != req.end(); ++it)
+                                       preamble.registerAutomaticallyLoadedPackage(*it);
                        } else
                                // we did not find a non-ert version
                                handle_ert(os, command, context);
@@ -3473,10 +3813,13 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                begin_inset(os, "External\n");
                                os << "\ttemplate XFig\n"
                                   << "\tfilename " << outname << '\n';
+                               registerExternalTemplatePackages("XFig");
                        } else {
                                begin_command_inset(os, "include", name);
                                os << "preview false\n"
                                      "filename \"" << outname << "\"\n";
+                               if (t.cs() == "verbatiminput")
+                                       preamble.registerAutomaticallyLoadedPackage("verbatim");
                        }
                        end_inset(os);
                }
@@ -3561,22 +3904,41 @@ 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);
+                               parse_text(p, os, FLAG_ITEM, outer, context);
+                               handle_ert(os, "}", context);
+                       } else {
+                               string special = p.getFullOpt();
+                               special += p.getOpt();
+                               // LyX does not yet support \framebox without any option
+                               if (!special.empty())
+                                       parse_outer_box(p, os, FLAG_ITEM, outer,
+                                                       context, t.cs(), special);
+                               else {
+                                       eat_whitespace(p, os, context, false);
+                                       handle_ert(os, "\\framebox{", context);
+                                       parse_text(p, os, FLAG_ITEM, outer, context);
+                                       handle_ert(os, "}", context);
+                               }
+                       }
                }
 
                //\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);
                                handle_ert(os, arg + '{', context);
-                               eat_whitespace(p, os, context, false);
                                parse_text(p, os, FLAG_ITEM, outer, context);
                                handle_ert(os, "}", context);
                        } else
@@ -3630,7 +3992,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") {
@@ -3814,21 +4176,127 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        end_inset(os);
                }
 
+               else if (t.cs() == "includepdf") {
+                       p.skip_spaces();
+                       string const arg = p.getArg('[', ']');
+                       map<string, string> opts;
+                       vector<string> keys;
+                       split_map(arg, opts, keys);
+                       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 pdfpages_format[] = {"pdf", 0};
+                               string const pdftex_name =
+                                       find_file(name, path, pdfpages_format);
+                               if (!pdftex_name.empty()) {
+                                       name = pdftex_name;
+                                       pdflatex = true;
+                               }
+                       }
+                       if (makeAbsPath(name, path).exists())
+                               fix_relative_filename(name);
+                       else
+                               cerr << "Warning: Could not find file '"
+                                    << name << "'." << endl;
+                       // write output
+                       context.check_layout(os);
+                       begin_inset(os, "External\n\ttemplate ");
+                       os << "PDFPages\n\tfilename "
+                          << name << "\n";
+                       // parse the options
+                       if (opts.find("pages") != opts.end())
+                               os << "\textra LaTeX \"pages="
+                                  << opts["pages"] << "\"\n";
+                       if (opts.find("angle") != opts.end())
+                               os << "\trotateAngle "
+                                  << opts["angle"] << '\n';
+                       if (opts.find("origin") != opts.end()) {
+                               ostringstream ss;
+                               string const opt = opts["origin"];
+                               if (opt == "tl") ss << "topleft";
+                               if (opt == "bl") ss << "bottomleft";
+                               if (opt == "Bl") ss << "baselineleft";
+                               if (opt == "c") ss << "center";
+                               if (opt == "tc") ss << "topcenter";
+                               if (opt == "bc") ss << "bottomcenter";
+                               if (opt == "Bc") ss << "baselinecenter";
+                               if (opt == "tr") ss << "topright";
+                               if (opt == "br") ss << "bottomright";
+                               if (opt == "Br") ss << "baselineright";
+                               if (!ss.str().empty())
+                                       os << "\trotateOrigin " << ss.str() << '\n';
+                               else
+                                       cerr << "Warning: Ignoring unknown includegraphics origin argument '" << opt << "'\n";
+                       }
+                       if (opts.find("width") != opts.end())
+                               os << "\twidth "
+                                  << translate_len(opts["width"]) << '\n';
+                       if (opts.find("height") != opts.end())
+                               os << "\theight "
+                                  << translate_len(opts["height"]) << '\n';
+                       if (opts.find("keepaspectratio") != opts.end())
+                               os << "\tkeepAspectRatio\n";
+                       end_inset(os);
+                       context.check_layout(os);
+                       registerExternalTemplatePackages("PDFPages");
+               }
+
+               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();
+                       registerExternalTemplatePackages("ChessDiagram");
+               }
+
                else {
                        // try to see whether the string is in unicodesymbols
                        // Only use text mode commands, since we are in text mode here,
                        // and math commands may be invalid (bug 6797)
+                       bool termination;
                        docstring rem;
+                       set<string> req;
                        docstring s = encodings.fromLaTeXCommand(from_utf8(t.asInput()),
-                                                                rem, Encodings::TEXT_CMD);
+                                       Encodings::TEXT_CMD, termination, rem, &req);
                        if (!s.empty()) {
                                if (!rem.empty())
-                                       cerr << "When parsing " << t.cs() 
+                                       cerr << "When parsing " << t.cs()
                                             << ", result is " << to_utf8(s)
                                             << "+" << to_utf8(rem) << endl;
                                context.check_layout(os);
                                os << to_utf8(s);
-                               skip_spaces_braces(p);
+                               if (termination)
+                                       skip_spaces_braces(p);
+                               for (set<string>::const_iterator it = req.begin(); it != req.end(); ++it)
+                                       preamble.registerAutomaticallyLoadedPackage(*it);
                        }
                        //cerr << "#: " << t << " mode: " << mode << endl;
                        // heuristic: read up to next non-nested space