X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Fgraphics%2FPreviewLoader.cpp;h=ac14fbb6a904591e9b8a0182bef5b230eb7ae3e6;hb=8663a371f31dc7bb888071c15a0b2cd4c3669c60;hp=2f6206f1272fe52e978ade3e5ef0811bfa7167e9;hpb=9439b6e6e0cf950aafe8ce8d76d380c543110db7;p=lyx.git diff --git a/src/graphics/PreviewLoader.cpp b/src/graphics/PreviewLoader.cpp index 2f6206f127..ac14fbb6a9 100644 --- a/src/graphics/PreviewLoader.cpp +++ b/src/graphics/PreviewLoader.cpp @@ -25,6 +25,7 @@ #include "output.h" #include "OutputParams.h" #include "TexRow.h" +#include "texstream.h" #include "frontends/Application.h" // hexName @@ -37,16 +38,21 @@ #include "support/ForkedCalls.h" #include "support/lstrings.h" -#include +#include "support/TempFile.h" -#include +#include #include #include +#include +#include +#include + +#include using namespace std; using namespace lyx::support; -using boost::bind; + namespace { @@ -59,41 +65,11 @@ typedef list PendingSnippets; typedef vector BitmapFile; -string const unique_filename(string const & bufferpath) +FileName const unique_tex_filename(FileName const & bufferpath) { - static int theCounter = 0; - string const filename = lyx::convert(theCounter++) + "lyxpreview"; - return addName(bufferpath, filename); -} - - -lyx::Converter const * setConverter() -{ - string const from = "lyxpreview"; - - typedef vector FmtList; - typedef lyx::graphics::Cache GCache; - FmtList const loadableFormats = GCache::get().loadableFormats(); - FmtList::const_iterator it = loadableFormats.begin(); - FmtList::const_iterator const end = loadableFormats.end(); - - for (; it != end; ++it) { - string const to = *it; - if (from == to) - continue; - - lyx::Converter const * ptr = lyx::theConverters().getConverter(from, to); - if (ptr) - return ptr; - } - - static bool first = true; - if (first) { - first = false; - LYXERR0("PreviewLoader::startLoading()\n" - << "No converter from \"lyxpreview\" format has been defined."); - } - return 0; + TempFile tempfile(bufferpath, "lyxpreviewXXXXXX.tex"); + tempfile.setAutoRemove(false); + return tempfile.name(); } @@ -184,14 +160,13 @@ typedef map InProgressProcesses; typedef InProgressProcesses::value_type InProgressProcess; -} // namespace anon - +} // namespace namespace lyx { namespace graphics { -class PreviewLoader::Impl : public boost::signals::trackable { +class PreviewLoader::Impl { public: /// Impl(PreviewLoader & p, Buffer const & b); @@ -205,26 +180,31 @@ public: void add(string const & latex_snippet); /// void remove(string const & latex_snippet); + /// \p wait whether to wait for the process to complete or, instead, + /// to do it in the background. + void startLoading(bool wait = false); /// - void startLoading(); + void refreshPreviews(); /// Emit this signal when an image is ready for display. - boost::signal imageReady; + signals2::signal imageReady; Buffer const & buffer() const { return buffer_; } + lyx::Converter const * setConverter(string const & from); + private: /// Called by the ForkedCall process that generated the bitmap files. void finishedGenerating(pid_t, int); /// - void dumpPreamble(odocstream &) const; + void dumpPreamble(otexstream &, OutputParams::FLAVOR) const; /// void dumpData(odocstream &, BitmapFile const &) const; /** cache_ allows easy retrieval of already-generated images * using the LaTeX snippet as the identifier. */ - typedef boost::shared_ptr PreviewImagePtr; + typedef std::shared_ptr PreviewImagePtr; /// typedef map Cache; /// @@ -246,10 +226,20 @@ private: /// Buffer const & buffer_; /// - double font_scaling_factor_; + mutable int font_scaling_factor_; + /// + mutable int fg_color_; + /// + mutable int bg_color_; + /// + QTimer * delay_refresh_; + /// + bool finished_generating_; /// We don't own this static lyx::Converter const * pconverter_; + + Trackable trackable_; }; @@ -295,13 +285,19 @@ void PreviewLoader::remove(string const & latex_snippet) const } -void PreviewLoader::startLoading() const +void PreviewLoader::startLoading(bool wait) const { - pimpl_->startLoading(); + pimpl_->startLoading(wait); } -boost::signals::connection PreviewLoader::connect(slot_type const & slot) const +void PreviewLoader::refreshPreviews() +{ + pimpl_->refreshPreviews(); +} + + +signals2::connection PreviewLoader::connect(slot const & slot) const { return pimpl_->imageReady.connect(slot); } @@ -354,7 +350,7 @@ InProgress::InProgress(string const & filename_base, PendingSnippets const & pending, string const & to_format) : pid(0), - metrics_file(FileName(filename_base + ".metrics")), + metrics_file(filename_base + ".metrics"), snippets(pending.size()) { PendingSnippets::const_iterator pit = pending.begin(); @@ -382,28 +378,66 @@ void InProgress::stop() const } } -} // namespace anon +} // namespace namespace lyx { namespace graphics { PreviewLoader::Impl::Impl(PreviewLoader & p, Buffer const & b) - : parent_(p), buffer_(b), font_scaling_factor_(0.0) + : parent_(p), buffer_(b), finished_generating_(true) { - font_scaling_factor_ = 0.01 * lyxrc.dpi * lyxrc.zoom * - convert(lyxrc.preview_scale_factor); + font_scaling_factor_ = int(buffer_.fontScalingFactor()); + if (theApp()) { + fg_color_ = strtol(theApp()->hexName(foregroundColor()).c_str(), 0, 16); + bg_color_ = strtol(theApp()->hexName(backgroundColor()).c_str(), 0, 16); + } else { + fg_color_ = 0x0; + bg_color_ = 0xffffff; + } + if (!pconverter_) + pconverter_ = setConverter("lyxpreview"); - LYXERR(Debug::GRAPHICS, "The font scaling factor is " - << font_scaling_factor_); + delay_refresh_ = new QTimer(&parent_); + delay_refresh_->setSingleShot(true); + QObject::connect(delay_refresh_, SIGNAL(timeout()), + &parent_, SLOT(refreshPreviews())); +} - if (!pconverter_) - pconverter_ = setConverter(); + +lyx::Converter const * PreviewLoader::Impl::setConverter(string const & from) +{ + typedef vector FmtList; + FmtList const & loadableFormats = graphics::Cache::get().loadableFormats(); + FmtList::const_iterator it = loadableFormats.begin(); + FmtList::const_iterator const end = loadableFormats.end(); + + for (; it != end; ++it) { + string const to = *it; + if (from == to) + continue; + + lyx::Converter const * ptr = lyx::theConverters().getConverter(from, to); + if (ptr) + return ptr; + } + + // Show the error only once. This is thread-safe. + static nullptr_t no_conv = [&]{ + LYXERR0("PreviewLoader::startLoading()\n" + << "No converter from \"" << from + << "\" format has been defined."); + return nullptr; + } (); + + return no_conv; } PreviewLoader::Impl::~Impl() { + delete delay_refresh_; + InProgressProcesses::iterator ipit = in_progress_.begin(); InProgressProcesses::iterator ipend = in_progress_.end(); @@ -415,11 +449,46 @@ PreviewLoader::Impl::~Impl() PreviewImage const * PreviewLoader::Impl::preview(string const & latex_snippet) const { + int fs = int(buffer_.fontScalingFactor()); + int fg = 0x0; + int bg = 0xffffff; + if (theApp()) { + fg = strtol(theApp()->hexName(foregroundColor()).c_str(), 0, 16); + bg = strtol(theApp()->hexName(backgroundColor()).c_str(), 0, 16); + } + if (font_scaling_factor_ != fs || fg_color_ != fg || bg_color_ != bg) { + // Schedule refresh of all previews on zoom or color changes. + // The previews are regenerated only after the zoom factor + // has not been changed for about 1 second. + fg_color_ = fg; + bg_color_ = bg; + delay_refresh_->start(1000); + } + // Don't try to access the cache until we are done. + if (delay_refresh_->isActive() || !finished_generating_) + return 0; Cache::const_iterator it = cache_.find(latex_snippet); return (it == cache_.end()) ? 0 : it->second.get(); } +void PreviewLoader::Impl::refreshPreviews() +{ + font_scaling_factor_ = int(buffer_.fontScalingFactor()); + // Reschedule refresh until the previous process completed. + if (!finished_generating_) { + delay_refresh_->start(1000); + return; + } + Cache::const_iterator cit = cache_.begin(); + Cache::const_iterator cend = cache_.end(); + while (cit != cend) + parent_.remove((cit++)->first); + finished_generating_ = false; + buffer_.updatePreviews(); +} + + namespace { class FindSnippet { @@ -437,7 +506,7 @@ private: string const snippet_; }; -} // namespace anon +} // namespace PreviewLoader::Status PreviewLoader::Impl::status(string const & latex_snippet) const @@ -499,7 +568,7 @@ private: string const & snippet_; }; -} // namespace anon +} // namespace void PreviewLoader::Impl::remove(string const & latex_snippet) @@ -526,7 +595,7 @@ void PreviewLoader::Impl::remove(string const & latex_snippet) } -void PreviewLoader::Impl::startLoading() +void PreviewLoader::Impl::startLoading(bool wait) { if (pending_.empty() || !pconverter_) return; @@ -538,24 +607,30 @@ void PreviewLoader::Impl::startLoading() LYXERR(Debug::GRAPHICS, "PreviewLoader::startLoading()"); // As used by the LaTeX file and by the resulting image files - string const directory = buffer_.temppath(); + FileName const directory(buffer_.temppath()); - string const filename_base = unique_filename(directory); + FileName const latexfile = unique_tex_filename(directory); + string const filename_base = removeExtension(latexfile.absFileName()); // Create an InProgress instance to place in the map of all // such processes if it starts correctly. - InProgress inprogress(filename_base, pending_, pconverter_->to); + InProgress inprogress(filename_base, pending_, pconverter_->to()); // clear pending_, so we're ready to start afresh. pending_.clear(); // Output the LaTeX file. - FileName const latexfile(filename_base + ".tex"); - // we use the encoding of the buffer Encoding const & enc = buffer_.params().encoding(); - odocfstream of(enc.iconvName()); - TexRow texrow; + ofdocstream of; + try { of.reset(enc.iconvName()); } + catch (iconv_codecvt_facet_exception const & e) { + LYXERR0("Caught iconv exception: " << e.what() + << "\nUnable to create LaTeX file: " << latexfile); + return; + } + + otexstream os(of); OutputParams runparams(&enc); LaTeXFeatures features(buffer_, buffer_.params(), runparams); @@ -568,9 +643,55 @@ void PreviewLoader::Impl::startLoading() return; } of << "\\batchmode\n"; - dumpPreamble(of); + + // Set \jobname of previews to the document name (see bug 9627) + of << "\\def\\jobname{" + << from_utf8(changeExtension(buffer_.latexName(true), "")) + << "}\n"; + + LYXERR(Debug::LATEX, "Format = " << buffer_.params().getDefaultOutputFormat()); + string latexparam = ""; + bool docformat = !buffer_.params().default_output_format.empty() + && buffer_.params().default_output_format != "default"; + // Use LATEX flavor if the document does not specify a specific + // output format (see bug 9371). + OutputParams::FLAVOR flavor = docformat + ? buffer_.params().getOutputFlavor() + : OutputParams::LATEX; + if (buffer_.params().encoding().package() == Encoding::japanese) { + latexparam = " --latex=platex"; + flavor = OutputParams::LATEX; + } + else if (buffer_.params().useNonTeXFonts) { + if (flavor == OutputParams::LUATEX) + latexparam = " --latex=lualatex"; + else { + flavor = OutputParams::XETEX; + latexparam = " --latex=xelatex"; + } + } + else { + switch (flavor) { + case OutputParams::PDFLATEX: + latexparam = " --latex=pdflatex"; + break; + case OutputParams::XETEX: + latexparam = " --latex=xelatex"; + break; + case OutputParams::LUATEX: + latexparam = " --latex=lualatex"; + break; + case OutputParams::DVILUATEX: + latexparam = " --latex=dvilualatex"; + break; + default: + flavor = OutputParams::LATEX; + } + } + dumpPreamble(os, flavor); // handle inputenc etc. - buffer_.params().writeEncodingPreamble(of, features, texrow); + // I think, this is already hadled by dumpPreamble(): Kornel + // buffer_.params().writeEncodingPreamble(os, features); of << "\n\\begin{document}\n"; dumpData(of, inprogress.snippets); of << "\n\\end{document}\n"; @@ -583,20 +704,45 @@ void PreviewLoader::Impl::startLoading() // The conversion command. ostringstream cs; - cs << pconverter_->command << ' ' << pconverter_->to << ' ' - << quoteName(latexfile.toFilesystemEncoding()) << ' ' - << int(font_scaling_factor_) << ' ' - << theApp()->hexName(Color_preview) << ' ' - << theApp()->hexName(Color_background); + cs << pconverter_->command() + << " " << quoteName(latexfile.toFilesystemEncoding()) + << " --dpi " << font_scaling_factor_; + + // FIXME XHTML + // The colors should be customizable. + if (!buffer_.isExporting()) { + ColorCode const fg = PreviewLoader::foregroundColor(); + ColorCode const bg = PreviewLoader::backgroundColor(); + cs << " --fg " << theApp()->hexName(fg) + << " --bg " << theApp()->hexName(bg); + } - string const command = libScriptSearch(cs.str()); + cs << latexparam; + cs << " --bibtex=" << quoteName(buffer_.params().bibtexCommand()); + if (buffer_.params().bufferFormat() == "lilypond-book") + cs << " --lilypond"; + + string const command = cs.str(); + + if (wait) { + ForkedCall call(buffer_.filePath(), buffer_.layoutPos()); + int ret = call.startScript(ForkedProcess::Wait, command); + static atomic_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); - convert_ptr->connect(bind(&Impl::finishedGenerating, this, _1, _2)); + ForkedCall::sigPtr convert_ptr = make_shared(); + convert_ptr->connect(ForkedProcess::slot([this](pid_t pid, int retval){ + finishedGenerating(pid, retval); + }).track_foreign(trackable_.p())); - ForkedCall call; + ForkedCall call(buffer_.filePath()); int ret = call.startScript(command, convert_ptr); if (ret != 0) { @@ -612,6 +758,11 @@ void PreviewLoader::Impl::startLoading() } +double PreviewLoader::displayPixelRatio() const +{ + return buffer().params().display_pixel_ratio; +} + void PreviewLoader::Impl::finishedGenerating(pid_t pid, int retval) { // Paranoia check! @@ -619,6 +770,7 @@ void PreviewLoader::Impl::finishedGenerating(pid_t pid, int retval) if (git == in_progress_.end()) { lyxerr << "PreviewLoader::finishedGenerating(): unable to find " "data for PID " << pid << endl; + finished_generating_ = true; return; } @@ -627,8 +779,11 @@ void PreviewLoader::Impl::finishedGenerating(pid_t pid, int retval) LYXERR(Debug::GRAPHICS, "PreviewLoader::finishedInProgress(" << retval << "): processing " << status << " for " << command); - if (retval > 0) + if (retval > 0) { + in_progress_.erase(git); + finished_generating_ = true; return; + } // Read the metrics file, if it exists vector ascent_fractions(git->second.snippets.size()); @@ -647,10 +802,15 @@ void PreviewLoader::Impl::finishedGenerating(pid_t pid, int retval) FileName const & file = it->second; double af = ascent_fractions[metrics_counter]; - PreviewImagePtr ptr(new PreviewImage(parent_, snip, file, af)); - cache_[snip] = ptr; + // Add the image to the cache only if it's actually present + // and not empty (an empty image is signaled by af < 0) + if (af >= 0 && file.isReadableFile()) { + PreviewImagePtr ptr(new PreviewImage(parent_, snip, file, af)); + cache_[snip] = ptr; + + newimages.push_back(ptr); + } - newimages.push_back(ptr); } // Remove the item from the list of still-executing processes. @@ -664,22 +824,21 @@ void PreviewLoader::Impl::finishedGenerating(pid_t pid, int retval) for (; nit != nend; ++nit) { imageReady(*nit->get()); } + finished_generating_ = true; } -void PreviewLoader::Impl::dumpPreamble(odocstream & os) const +void PreviewLoader::Impl::dumpPreamble(otexstream & os, OutputParams::FLAVOR flavor) const { - // Why on earth is Buffer::makeLaTeXFile a non-const method? - Buffer & tmp = const_cast(buffer_); // Dump the preamble only. - // We don't need an encoding for runparams since it is not used by - // the preamble. - OutputParams runparams(0); - runparams.flavor = OutputParams::LATEX; + LYXERR(Debug::LATEX, "dumpPreamble, flavor == " << flavor); + OutputParams runparams(&buffer_.params().encoding()); + runparams.flavor = flavor; runparams.nice = true; runparams.moving_arg = true; runparams.free_spacing = true; - tmp.writeLaTeXSource(os, buffer_.filePath(), runparams, true, false); + runparams.is_child = buffer_.parent(); + buffer_.writeLaTeXSource(os, buffer_.filePath(), runparams, Buffer::OnlyPreamble); // FIXME! This is a HACK! The proper fix is to control the 'true' // passed to WriteStream below: @@ -694,15 +853,6 @@ void PreviewLoader::Impl::dumpPreamble(odocstream & os) const << "\\def\\lyxlock{}\n" << "\n"; - // Loop over the insets in the buffer and dump all the math-macros. - Inset & inset = buffer_.inset(); - InsetIterator it = inset_iterator_begin(inset); - InsetIterator const end = inset_iterator_end(inset); - - for (; it != end; ++it) - if (it->lyxCode() == MATHMACRO_CODE) - it->latex(buffer_, os, runparams); - // All equation labels appear as "(#)" + preview.sty's rendering of // the label name if (lyxrc.preview_hashed_labels) @@ -710,8 +860,10 @@ void PreviewLoader::Impl::dumpPreamble(odocstream & os) const // Use the preview style file to ensure that each snippet appears on a // fresh page. + // Also support PDF output (automatically generated e.g. when + // \usepackage[pdftex]{hyperref} is used and XeTeX. os << "\n" - << "\\usepackage[active,delayed,dvips,showlabels,lyx]{preview}\n" + << "\\usepackage[active,delayed,showlabels,lyx]{preview}\n" << "\n"; } @@ -735,3 +887,5 @@ void PreviewLoader::Impl::dumpData(odocstream & os, } // namespace graphics } // namespace lyx + +#include "moc_PreviewLoader.cpp"