The attached patch implements export of math as images for XHTML.
I am morally certain that there are lots of things here that could be
done better. I do not really understand the code very well. I don't
think the patch breaks anything, but I'm guessing (and hoping) that
people will have design suggestions.
To test this, you need to take a LyX file with some math in it, open it
in a text editor, and change the "html_math_output" setting to 2. There
isn't yet a UI for this, since without this patch nothing happens. ;-)
As you will also see, at present the math images have the background
color of the LyX work area. I'm still thinking about how best to deal
with that.
Ultimately, the images will also work as a "fall back" for MathML and
HTML output. If we are trying to output MathML, e.g., and run into
something we can't handle, we'll issue an exception which will lead to
the math being output as an image.
Richard
Index: src/Buffer.cpp
===================================================================
--- src/Buffer.cpp (revision 34217)
+++ src/Buffer.cpp (working copy)
@@ -72,6 +72,7 @@
#include "insets/InsetInclude.h"
#include "insets/InsetText.h"
+#include "mathed/InsetMathHull.h"
#include "mathed/MacroTable.h"
#include "mathed/MathMacroTemplate.h"
#include "mathed/MathSupport.h"
@@ -163,7 +164,8 @@
/// Update macro table starting with position of it \param it in some
/// text inset.
- void updateMacros(DocIterator & it, DocIterator & scope);
+ void updateMacros(DocIterator & it, DocIterator & scope,
+ bool record_docits = false);
///
void setLabel(ParIterator & it, UpdateType utype) const;
///
@@ -403,7 +405,9 @@
}
// Remove any previewed LaTeX snippets associated with this buffer.
- thePreviews().removeLoader(*this);
+ // We need to leave them for use in export, though.
+ if (!isClone())
+ thePreviews().removeLoader(*this);
delete d;
}
@@ -1566,7 +1570,7 @@
updateBuffer(UpdateMaster, OutputUpdate);
checkBibInfoCache();
d->bibinfo_.makeCitationLabels(*this);
- updateMacros();
+ updateMacros(true);
updateMacroInstances();
if (!only_body) {
@@ -2644,7 +2648,8 @@
}
-void Buffer::Impl::updateMacros(DocIterator & it, DocIterator & scope)
+void Buffer::Impl::updateMacros(DocIterator & it, DocIterator & scope,
+ bool record_docits)
{
pit_type const lastpit = it.lastpit();
@@ -2698,6 +2703,14 @@
continue;
}
+ if (record_docits && iit->inset->asInsetMath()) {
+ InsetMath * im = static_cast<InsetMath *>(iit->inset);
+ if (im->asHullInset()) {
+ InsetMathHull * hull = static_cast<InsetMathHull *>(im);
+ hull->recordLocation(it);
+ }
+ }
+
if (iit->inset->lyxCode() != MATHMACRO_CODE)
continue;
@@ -2729,7 +2742,7 @@
}
-void Buffer::updateMacros() const
+void Buffer::updateMacros(bool record_docit) const
{
if (d->macro_lock)
return;
@@ -2748,7 +2761,7 @@
DocIterator it = par_iterator_begin();
DocIterator outerScope = it;
outerScope.pit() = outerScope.lastpit() + 2;
- d->updateMacros(it, outerScope);
+ d->updateMacros(it, outerScope, record_docit);
}
Index: src/Buffer.h
===================================================================
--- src/Buffer.h (revision 34217)
+++ src/Buffer.h (working copy)
@@ -427,7 +427,7 @@
// Macro handling
//
/// Collect macro definitions in paragraphs
- void updateMacros() const;
+ void updateMacros(bool record_docit = false) const;
/// Iterate through the whole buffer and try to resolve macros
void updateMacroInstances() const;
Index: src/graphics/PreviewImage.cpp
===================================================================
--- src/graphics/PreviewImage.cpp (revision 34217)
+++ src/graphics/PreviewImage.cpp (working copy)
@@ -70,6 +70,12 @@
}
+support::FileName const & PreviewImage::filename() const
+{
+ return pimpl_->iloader_.filename();
+}
+
+
Dimension PreviewImage::dim() const
{
Dimension dim;
Index: src/graphics/PreviewImage.h
===================================================================
--- src/graphics/PreviewImage.h (revision 34217)
+++ src/graphics/PreviewImage.h (working copy)
@@ -46,6 +46,8 @@
* triggers that.
*/
Image const * image() const;
+ ///
+ support::FileName const & filename() const;
private:
/// Use the Pimpl idiom to hide the internals.
Index: src/graphics/PreviewLoader.cpp
===================================================================
--- src/graphics/PreviewLoader.cpp (revision 34217)
+++ src/graphics/PreviewLoader.cpp (working copy)
@@ -204,7 +204,7 @@
///
void remove(string const & latex_snippet);
///
- void startLoading();
+ void startLoading(bool wait = false);
/// Emit this signal when an image is ready for display.
boost::signal<void(PreviewImage const &)> imageReady;
@@ -291,9 +291,9 @@
}
-void PreviewLoader::startLoading() const
+void PreviewLoader::startLoading(bool wait) const
{
- pimpl_->startLoading();
+ pimpl_->startLoading(wait);
}
@@ -520,7 +520,7 @@
}
-void PreviewLoader::Impl::startLoading()
+void PreviewLoader::Impl::startLoading(bool wait)
{
if (pending_.empty() || !pconverter_)
return;
@@ -587,6 +587,8 @@
// The conversion command.
ostringstream cs;
+ // FIXME
+ // These need updating for math output.
cs << pconverter_->command << ' ' << pconverter_->to << ' '
<< quoteName(latexfile.toFilesystemEncoding()) << ' '
<< int(font_scaling_factor) << ' '
@@ -597,6 +599,18 @@
string const command = libScriptSearch(cs.str());
+ if (wait) {
+ ForkedCall call;
+ int ret = call.startScript(ForkedProcess::Wait, command);
+ static int fake = (2^20) + 1;
+ int pid = fake++;
+ inprogress.pid = pid;
+ inprogress.command = command;
+ in_progress_[pid] = inprogress;
+ finishedGenerating(pid, ret);
+ return;
+ }
+
// Initiate the conversion from LaTeX to bitmap images files.
ForkedCall::SignalTypePtr
convert_ptr(new ForkedCall::SignalType);
Index: src/graphics/PreviewLoader.h
===================================================================
--- src/graphics/PreviewLoader.h (revision 34217)
+++ src/graphics/PreviewLoader.h (working copy)
@@ -68,7 +68,7 @@
/** We have accumulated several latex snippets with status "InQueue".
* Initiate their transformation into bitmap images.
*/
- void startLoading() const;
+ void startLoading(bool wait = false) const;
/** Connect and you'll be informed when the bitmap image file
* has been created and is ready for loading through
Index: src/insets/RenderPreview.cpp
===================================================================
--- src/insets/RenderPreview.cpp (revision 34217)
+++ src/insets/RenderPreview.cpp (working copy)
@@ -175,31 +175,31 @@
}
-void RenderPreview::startLoading(Buffer const & buffer) const
+void RenderPreview::startLoading(Buffer const & buffer, bool wait) const
{
- if (status() == LyXRC::PREVIEW_OFF || snippet_.empty())
+ if (!wait && (status() == LyXRC::PREVIEW_OFF || snippet_.empty()))
return;
graphics::PreviewLoader const & loader = getPreviewLoader(buffer);
- loader.startLoading();
+ loader.startLoading(wait);
}
void RenderPreview::addPreview(docstring const & latex_snippet,
- Buffer const & buffer)
+ Buffer const & buffer, bool force)
{
- if (status() == LyXRC::PREVIEW_OFF)
+ if (!force && status() == LyXRC::PREVIEW_OFF)
return;
graphics::PreviewLoader & loader = getPreviewLoader(buffer);
- addPreview(latex_snippet, loader);
+ addPreview(latex_snippet, loader, force);
}
void RenderPreview::addPreview(docstring const & latex_snippet,
- graphics::PreviewLoader & ploader)
+ graphics::PreviewLoader & ploader, bool force)
{
- if (status() == LyXRC::PREVIEW_OFF)
+ if (!force && status() == LyXRC::PREVIEW_OFF)
return;
// FIXME UNICODE
Index: src/insets/RenderPreview.h
===================================================================
--- src/insets/RenderPreview.h (revision 34217)
+++ src/insets/RenderPreview.h (working copy)
@@ -60,16 +60,17 @@
/** Find the PreviewLoader and add a LaTeX snippet to it.
* Do not start the loading process.
*/
- void addPreview(docstring const & latex_snippet, Buffer const &);
+ void addPreview(docstring const & latex_snippet, Buffer const &,
+ bool force = false);
/** Add a LaTeX snippet to the PreviewLoader.
* Do not start the loading process.
*/
void addPreview(docstring const & latex_snippet,
- graphics::PreviewLoader & ploader);
+ graphics::PreviewLoader & ploader, bool force = false);
/// Begin the loading process.
- void startLoading(Buffer const & buffer) const;
+ void startLoading(Buffer const & buffer, bool wait = false) const;
/** Remove a snippet from the cache of previews.
* Useful if previewing the contents of a file that has changed.
Index: src/mathed/InsetMathHull.cpp
===================================================================
--- src/mathed/InsetMathHull.cpp (revision 34217)
+++ src/mathed/InsetMathHull.cpp (working copy)
@@ -23,8 +23,12 @@
#include "BufferParams.h"
#include "BufferView.h"
#include "ColorSet.h"
+#include "Converter.h"
#include "CutAndPaste.h"
+#include "ErrorList.h"
#include "Encoding.h"
+#include "Exporter.h"
+#include "Format.h"
#include "FuncRequest.h"
#include "FuncStatus.h"
#include "LaTeXFeatures.h"
@@ -46,6 +50,7 @@
#include "support/lassert.h"
#include "support/debug.h"
+#include "support/filetools.h"
#include "support/gettext.h"
#include "support/lstrings.h"
@@ -495,34 +500,34 @@
}
-void InsetMathHull::preparePreview(DocIterator const & pos) const
+void InsetMathHull::preparePreview(DocIterator const & pos, bool force) const
{
Buffer const * buffer = pos.buffer();
// collect macros at this position
- MacroNameSet macros;
- buffer->listMacroNames(macros);
- MacroNameSet::iterator it = macros.begin();
- MacroNameSet::iterator end = macros.end();
- odocstringstream macro_preamble;
- for (; it != end; ++it) {
- MacroData const * data = buffer->getMacro(*it, pos, true);
- if (data) {
- data->write(macro_preamble, true);
- macro_preamble << endl;
+ MacroNameSet macros;
+ buffer->listMacroNames(macros);
+ MacroNameSet::iterator it = macros.begin();
+ MacroNameSet::iterator end = macros.end();
+ odocstringstream macro_preamble;
+ for (; it != end; ++it) {
+ MacroData const * data = buffer->getMacro(*it, pos, true);
+ if (data) {
+ data->write(macro_preamble, true);
+ macro_preamble << endl;
}
- }
+ }
- docstring const snippet = macro_preamble.str() + latexString(*this);
- LYXERR(Debug::MACROS, "Preview snippet: " << snippet);
- preview_->addPreview(snippet, *buffer);
+ docstring const snippet = macro_preamble.str() + latexString(*this);
+ LYXERR(Debug::MACROS, "Preview snippet: " << snippet);
+ preview_->addPreview(snippet, *buffer, force);
}
-void InsetMathHull::reloadPreview(DocIterator const & pos) const
+void InsetMathHull::reloadPreview(DocIterator const & pos, bool wait) const
{
- preparePreview(pos);
- preview_->startLoading(*pos.buffer());
+ preparePreview(pos, wait);
+ preview_->startLoading(*pos.buffer(), wait);
}
@@ -1819,8 +1824,69 @@
}
-docstring InsetMathHull::xhtml(XHTMLStream & xs, OutputParams const &) const
+string InsetMathHull::makeMathImage(OutputParams const & op) const
{
+ if (!docit_) {
+ LYXERR0("DocIterator invalid!");
+ return string();
+ }
+ reloadPreview(docit_, true);
+ graphics::PreviewImage const * pimage = preview_->getPreviewImage(buffer());
+ if (!pimage) {
+ LYXERR0("Unable to generate image. LaTeX follows.");
+ LYXERR0(latexString(*this));
+ return string();
+ }
+
+ // need to do a conversion to png, possibly.
+ FileName img(pimage->filename());
+ if (!img.isReadableFile())
+ return string();
+
+ string const from = formats.getFormatFromFile(img);
+ if (from.empty()) {
+ LYXERR0("Could not get file format.");
+ return string();
+ }
+
+ string output_file = onlyFilename(img.absFilename());
+ if (from == "png") {
+ // source and destination formats are the same
+ op.exportdata->addExternalFile("xhtml", img, output_file);
+ LYXERR0(img);
+ LYXERR0(output_file);
+ return output_file;
+ }
+
+ // so we need to convert
+ FileName const to_file = FileName(changeExtension(img.absFilename(), "png"));
+ string const output_to_file = changeExtension(output_file, "png");
+
+ // Do we need to perform the conversion?
+ // Yes if to_file does not exist or if temp_file is newer than to_file
+ if (compare_timestamps(img, to_file) < 0) {
+ op.exportdata->addExternalFile("xhtml", to_file, output_to_file);
+ LYXERR0(to_file);
+ LYXERR0(output_to_file);
+ return output_to_file;
+ }
+
+ // FIXME (Abdel 12/08/06): Is there a need to show these errors?
+ ErrorList el;
+ bool const success =
+ theConverters().convert(&buffer(), img, to_file, img,
+ from, "png", el, Converters::try_default | Converters::try_cache);
+ if (!success)
+ return string();
+ op.exportdata->addExternalFile("xhtml", to_file, output_to_file);
+ LYXERR0(to_file);
+ LYXERR0(output_to_file);
+ return output_to_file;
+}
+
+
+docstring InsetMathHull::xhtml(XHTMLStream & xs, OutputParams const & op) const
+{
BufferParams::MathOutput mathtype = buffer().params().html_math_output;
// FIXME Eventually we would like to do this inset by inset.
switch (mathtype) {
@@ -1845,7 +1911,11 @@
break;
}
case BufferParams::Images: {
- LYXERR0("Image output for math presently unsupported.");
+ string mathimg = makeMathImage(op);
+ if (mathimg.empty())
+ xs << "[[Math Image Error]]";
+ else
+ xs << html::CompTag("img", "src='" + mathimg + "'");
break;
}
case BufferParams::LaTeX: {
@@ -1869,4 +1939,9 @@
}
+void InsetMathHull::recordLocation(DocIterator const & di)
+{
+ docit_ = di;
+}
+
} // namespace lyx
Index: src/mathed/InsetMathHull.h
===================================================================
--- src/mathed/InsetMathHull.h (revision 34217)
+++ src/mathed/InsetMathHull.h (working copy)
@@ -14,6 +14,7 @@
#include "InsetMathGrid.h"
+#include "DocIterator.h"
#include "OutputEnums.h"
#include <boost/scoped_ptr.hpp>
@@ -139,9 +140,9 @@
void addPreview(DocIterator const & inset_pos,
graphics::PreviewLoader &) const;
/// Prepare the preview if preview is enabled.
- void preparePreview(DocIterator const & pos) const;
+ void preparePreview(DocIterator const & pos, bool force = false) const;
/// Recreates the preview if preview is enabled.
- void reloadPreview(DocIterator const & pos) const;
+ void reloadPreview(DocIterator const & pos, bool wait = false) const;
///
void initUnicodeMath() const;
@@ -150,6 +151,8 @@
/// Force inset into LTR environment if surroundings are RTL?
virtual bool forceLTR() const { return true; }
+ ///
+ void recordLocation(DocIterator const & di);
///
virtual docstring contextMenu(BufferView const &, int, int) const;
@@ -203,6 +206,8 @@
bool rowChangeOK() const;
/// can this change its number of cols?
bool colChangeOK() const;
+ ///
+ std::string makeMathImage(OutputParams const & op) const;
/// "none", "simple", "display", "eqnarray",...
HullType type_;
@@ -214,6 +219,8 @@
boost::scoped_ptr<RenderPreview> preview_;
///
mutable bool use_preview_;
+ ///
+ DocIterator docit_;
//
// Incorporate me
//