Create mechanism to handle packages for which we only know after parsing

the complete document whether LyX will load them or not.
Use the mechanism for the color package (fixes the color part of bug #7845).


git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@40095 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
Georg Baum 2011-10-30 18:12:49 +00:00
parent 3d3e1e58b1
commit f8cc30a1f2
5 changed files with 105 additions and 40 deletions

View File

@ -165,6 +165,12 @@ const char * const known_basic_color_codes[] = {"#0000ff", "#000000", "#00ffff",
const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
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)
@ -286,6 +292,12 @@ vector<string> Preamble::getPackageOptions(string const & package) const
}
void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
{
auto_packages.insert(package);
}
void Preamble::addModule(string const & module)
{
used_modules.push_back(module);
@ -349,7 +361,7 @@ string remove_braces(string const & value)
} // anonymous namespace
Preamble::Preamble() : one_language(true), ifundefined_color_set(false)
Preamble::Preamble() : one_language(true)
{
//h_backgroundcolor;
//h_boxbgcolor;
@ -663,11 +675,10 @@ void Preamble::handle_package(Parser &p, string const & name,
; // 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
// only add it if not yet added
if (!ifundefined_color_set)
h_preamble << "\\@ifundefined{definecolor}\n {\\usepackage{color}}{}\n";
if (!in_lyx_preamble)
h_preamble << package_beg_sep << name
<< package_mid_sep << "\\usepackage{"
<< name << '}' << package_end_sep;
}
else if (name == "graphicx")
@ -757,7 +768,7 @@ void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
}
void Preamble::writeLyXHeader(ostream & os)
bool Preamble::writeLyXHeader(ostream & os)
{
// translate from babel to LyX names
h_language = babel2lyx(h_language);
@ -795,8 +806,30 @@ void Preamble::writeLyXHeader(ostream & os)
<< "\\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";
string const raw = 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";
@ -892,6 +925,7 @@ void Preamble::writeLyXHeader(ostream & os)
<< "\\begin_body\n";
// clear preamble for subdocuments
h_preamble.str("");
return true;
}
@ -982,12 +1016,14 @@ void Preamble::parse(Parser & p, string const & forceclass,
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);
if (is_known(argument, known_basic_colors)) {
h_fontcolor = rgbcolor2code(argument);
preamble.registerAutomaticallyLoadedPackage("color");
} else if (argument == "document_fontcolor")
preamble.registerAutomaticallyLoadedPackage("color");
// 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)) {
else {
h_preamble << t.asInput() << '{' << argument << '}';
// the color might already be set because \definecolor
// is parsed before this
@ -998,12 +1034,13 @@ void Preamble::parse(Parser & p, string const & forceclass,
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);
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
if (argument != "page_backgroundcolor"
&& !is_known(argument, known_basic_colors)) {
else {
h_preamble << t.asInput() << '{' << argument << '}';
// the color might already be set because \definecolor
// is parsed before this
@ -1320,18 +1357,27 @@ void Preamble::parse(Parser & p, string const & forceclass,
string const arg2 = p.verbatim_item();
string const arg3 = p.verbatim_item();
// test case \@ifundefined{date}{}{\date{}}
if (arg1 == "date" && arg2.empty() && arg3 == "\\date{}") {
if (t.cs() == "@ifundefined" && arg1 == "date" &&
arg2.empty() && arg3 == "\\date{}") {
h_suppress_date = "true";
// test case \@ifundefined{definecolor}{\usepackage{color}}{}
// because we could pollute the preamble with it in roundtrips
} else if (arg1 == "definecolor" && arg2 == "\\usepackage{color}"
&& arg3.empty()) {
ifundefined_color_set = 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 (arg1 == "showcaptionsetup" && arg2.empty()
} else if (t.cs() == "@ifundefined" &&
arg1 == "showcaptionsetup" && arg2.empty()
&& arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
; // do nothing
} else if (!in_lyx_preamble) {
@ -1384,12 +1430,16 @@ string babel2lyx(string const & language)
}
string color2code(string const & name)
string rgbcolor2code(string const & name)
{
char const * const * where = is_known(name, known_basic_colors);
if (where)
if (where) {
// "red", "green" etc
return known_basic_color_codes[where - known_basic_colors];
return name;
}
// "255,0,0", "0,255,0" etc
RGBColor c(RGBColorFromLaTeX(name));
return X11hexname(c);
}
// }])

View File

@ -18,6 +18,7 @@
#include <string>
#include <vector>
#include <map>
#include <set>
namespace lyx {
@ -33,12 +34,18 @@ public:
///
std::string inputencoding() const { return h_inputencoding; }
///
std::string notefontcolor() const { return h_notefontcolor; }
///
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);
///
@ -49,21 +56,19 @@ public:
void parse(Parser & p, std::string const & forceclass,
TeX2LyXDocClass & tc);
/// Writes the LyX file header from internal data
void writeLyXHeader(std::ostream & os);
bool writeLyXHeader(std::ostream & os);
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;
/// necessary to avoid that our preamble stuff is added at each
/// tex2lyx run which would pollute the preamble when doing roundtrips
bool ifundefined_color_set;
std::ostringstream h_preamble;
std::string h_backgroundcolor;
std::string h_boxbgcolor;

View File

@ -639,7 +639,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.
@ -677,7 +677,10 @@ void tex2lyx(idocstream & is, ostream & os, string encoding)
for (; it != end; it++)
preamble.addModule(*it);
}
preamble.writeLyXHeader(os);
if (!preamble.writeLyXHeader(os)) {
cerr << "Could write LyX file header." << endl;
return false;
}
ss.seekg(0);
os << ss.str();
@ -688,6 +691,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;
}
@ -705,9 +709,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

View File

@ -45,8 +45,8 @@ public:
/// 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);
/// 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 &);

View File

@ -863,9 +863,10 @@ void parse_box(Parser & p, ostream & os, unsigned outer_flags,
os << "Boxed\n";
else if (outer_type == "shadowbox")
os << "Shadowbox\n";
else if (outer_type == "shaded")
else if (outer_type == "shaded") {
os << "Shaded\n";
else if (outer_type == "doublebox")
preamble.registerAutomaticallyLoadedPackage("color");
} else if (outer_type == "doublebox")
os << "Doublebox\n";
else if (outer_type.empty())
os << "Frameless\n";
@ -1166,6 +1167,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") {
@ -1177,6 +1180,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);
@ -2568,6 +2573,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);