Hi Angus, Hi José!

-- I guess this is for you! :-)

I implemented a basic version for generating bitmap files for equations
in DocBook output.  You can now export to Docbook or Docbook-xml and
dozens of "eqn-XYZ.png" files will appear.

Here is what I changed:

* refactored PreviewLoader to use a new class SnippetConversion
* added functions generateBitmaps() and bitmaps() in sgml.[hC]
* changed Buffer::makeDocbookFile() to call createBitmaps()
* changed math_hullsinset::docbook() to look up a bitmap file for the equation
and create a <graphics> element to an external file, which is also added to
runparams.exportdata
* changed lyxpreview2bitmap.py and legacy_lyxpreview2ppm to happily create
PNG files even without dvipng

So it works, but there are some details about filehandling, architecture and the
"Right Way of Docbooking" where I would appreciate some feedback (see below)

Patch attached.
Ciao
/Andreas

TODO:

- find out about sporadic "child timeout" errors
- test other bitmap formats (eps)
- improve naming of bitmap files
- create bitmaps in extra directory, not at the same level as the master file
- some DocBook bells and whistles
- move the SnippetConversion* from sgml.C to exportdata
- Ask for overwrite of bitmap files (appears that eporter doesn't do that yet)



Attachment: snippetconversion.diff
Description: Binary data

/**
 * \file PreviewLoader.C
 * This file is part of LyX, the document processor.
 * Licence details can be found in the file COPYING.
 *
 * \author Angus Leeming
 *
 * Full author contact details are available in file CREDITS.
 */

#include <config.h>

#include "GraphicsCache.h"
#include "SnippetConversion.h"
#include "PreviewLoader.h"
#include "PreviewImage.h"

#include "buffer.h"
#include "converter.h"
#include "debug.h"
#include "format.h"
#include "LColor.h"
#include "lyxrc.h"
#include "outputparams.h"

#include "frontends/lyx_gui.h" // hexname

#include "support/filetools.h"
#include "support/lstrings.h"
#include "support/lyxlib.h"
#include "support/convert.h"

#include <boost/bind.hpp>

#include <sstream>
#include <fstream>
#include <iomanip>

namespace support = lyx::support;

using std::endl;
using std::find;
using std::fill;
using std::find_if;
using std::make_pair;

using boost::bind;

using std::ifstream;
using std::list;
using std::map;
using std::ofstream;
using std::ostream;
using std::ostringstream;
using std::pair;
using std::vector;
using std::string;

namespace {

typedef lyx::graphics::SnippetConversion::SnippetList SnippetList;


string const unique_filename(string const bufferpath);

Converter const * setConverter();


typedef map<pid_t, lyx::graphics::SnippetConversion>  InProgressProcesses;

typedef InProgressProcesses::value_type InProgressProcess;

} // namespace anon


namespace lyx {
namespace graphics {

class PreviewLoader::Impl : public boost::signals::trackable {
public:
        ///
        Impl(PreviewLoader & p, Buffer const & b);
        /// Stop any SnippetConversion items still executing.
        ~Impl();
        ///
        PreviewImage const * preview(string const & latex_snippet) const;
        ///
        PreviewLoader::Status status(string const & latex_snippet) const;
        ///
        void add(string const & latex_snippet);
        ///
        void remove(string const & latex_snippet);
        ///
        void startLoading();

        /// Emit this signal when an image is ready for display.
        boost::signal<void(PreviewImage const &)> imageReady;

        Buffer const & buffer() const { return buffer_; }

private:
        /// Called by the Forkedcall process that generated the bitmap files.
        void finishedGenerating(pid_t, int);

        /** cache_ allows easy retrieval of already-generated images
         *  using the LaTeX snippet as the identifier.
         */
        typedef boost::shared_ptr<PreviewImage> PreviewImagePtr;
        ///
        typedef map<string, PreviewImagePtr> Cache;
        ///
        Cache cache_;

        /** pending_ stores the LaTeX snippets in anticipation of them being
         *  sent to the converter.
         */
        SnippetList pending_;

        /** in_progress_ stores all forked processes so that we can proceed
         *  thereafter.
            The map uses the conversion commands as its identifiers.
         */
        InProgressProcesses in_progress_;

        ///
        PreviewLoader & parent_;
        ///
        Buffer const & buffer_;
        ///
        double font_scaling_factor_;

        /// We don't own this
        static Converter const * pconverter_;
};


Converter const * PreviewLoader::Impl::pconverter_;


// The public interface, defined in PreviewLoader.h
// ================================================
PreviewLoader::PreviewLoader(Buffer const & b)
        : pimpl_(new Impl(*this, b))
{}


PreviewLoader::~PreviewLoader()
{}


PreviewImage const * PreviewLoader::preview(string const & latex_snippet) const
{
        return pimpl_->preview(latex_snippet);
}


PreviewLoader::Status PreviewLoader::status(string const & latex_snippet) const
{
        return pimpl_->status(latex_snippet);
}


void PreviewLoader::add(string const & latex_snippet) const
{
        pimpl_->add(latex_snippet);
}


void PreviewLoader::remove(string const & latex_snippet) const
{
        pimpl_->remove(latex_snippet);
}


void PreviewLoader::startLoading() const
{
        pimpl_->startLoading();
}


boost::signals::connection PreviewLoader::connect(slot_type const & slot) const
{
        return pimpl_->imageReady.connect(slot);
}


void PreviewLoader::emitSignal(PreviewImage const & pimage) const
{
        pimpl_->imageReady(pimage);
}


Buffer const & PreviewLoader::buffer() const
{
        return pimpl_->buffer();
}

} // namespace graphics
} // namespace lyx


// The details of the Impl
// =======================


namespace lyx {
namespace graphics {

PreviewLoader::Impl::Impl(PreviewLoader & p, Buffer const & b)
        : parent_(p), buffer_(b), font_scaling_factor_(0.0)
{
        font_scaling_factor_ = 0.01 * lyxrc.dpi * lyxrc.zoom *
                support::strToDbl(lyxrc.preview_scale_factor);

        lyxerr[Debug::GRAPHICS] << "The font scaling factor is "
                                << font_scaling_factor_ << endl;

        if (!pconverter_)
                pconverter_ = defaultConverter();
}


PreviewLoader::Impl::~Impl()
{
        InProgressProcesses::iterator ipit  = in_progress_.begin();
        InProgressProcesses::iterator ipend = in_progress_.end();

        for (; ipit != ipend; ++ipit) {
                ipit->second.stop();
        }
}


PreviewImage const *
PreviewLoader::Impl::preview(string const & latex_snippet) const
{
        Cache::const_iterator it = cache_.find(latex_snippet);
        return (it == cache_.end()) ? 0 : it->second.get();
}


namespace {

typedef SnippetConversion::SnippetList SnippetList;


class FindSnippet : public std::unary_function<InProgressProcess, bool> {
public:
        FindSnippet(string const & s) : snippet_(s) {}
        bool operator()(InProgressProcess const & process) const
        {
                return ! (process.second.findSnippet(snippet_).empty());
        }

private:
        string const snippet_;
};

} // namespace anon

PreviewLoader::Status
PreviewLoader::Impl::status(string const & latex_snippet) const
{
        Cache::const_iterator cit = cache_.find(latex_snippet);
        if (cit != cache_.end())
                return Ready;

        SnippetList::const_iterator pit  = pending_.begin();
        SnippetList::const_iterator pend = pending_.end();

        pit = find(pit, pend, latex_snippet);
        if (pit != pend)
                return InQueue;

        InProgressProcesses::const_iterator ipit  = in_progress_.begin();
        InProgressProcesses::const_iterator ipend = in_progress_.end();

        ipit = find_if(ipit, ipend, FindSnippet(latex_snippet));
        if (ipit != ipend)
                return Processing;

        return NotFound;
}


void PreviewLoader::Impl::add(string const & latex_snippet)
{
        if (!pconverter_ || status(latex_snippet) != NotFound)
                return;

        string const snippet = support::trim(latex_snippet);
        if (snippet.empty())
                return;

        lyxerr[Debug::GRAPHICS] << "adding snippet:\n" << snippet << endl;

        pending_.push_back(snippet);
}


namespace {

class EraseSnippet {
public:
        EraseSnippet(string const & s) : snippet_(s) {}
        void operator()(InProgressProcess & process)
        {
                process.second.eraseSnippet(snippet_);
        }

private:
        string const & snippet_;
};

} // namespace anon


void PreviewLoader::Impl::remove(string const & latex_snippet)
{
        Cache::iterator cit = cache_.find(latex_snippet);
        if (cit != cache_.end())
                cache_.erase(cit);

        SnippetList::iterator pit  = pending_.begin();
        SnippetList::iterator pend = pending_.end();

        pending_.erase(std::remove(pit, pend, latex_snippet), pend);

        InProgressProcesses::iterator ipit  = in_progress_.begin();
        InProgressProcesses::iterator ipend = in_progress_.end();

        std::for_each(ipit, ipend, EraseSnippet(latex_snippet));

        while (ipit != ipend) {
                InProgressProcesses::iterator curr = ipit++;
                if (curr->second.empty())
                        in_progress_.erase(curr);
        }
}


void PreviewLoader::Impl::startLoading()
{
        if (pending_.empty() || !pconverter_)
                return;

        // Only start the process off after the buffer is loaded from file.
        if (!buffer_.fully_loaded())
                return;

        lyxerr[Debug::GRAPHICS] << "PreviewLoader::startLoading()" << endl;

        // As used by the LaTeX file and by the resulting image files
        string const directory = buffer_.temppath();

        string const filename_base(unique_filename(directory));

        // Create an SnippetConversion instance to place in the map of all
        // such processes if it starts correctly.
        SnippetConversion inprogress(filename_base, pconverter_, pending_);

        // clear pending_, so we're ready to start afresh.
        pending_.clear();

        // Initiate the conversion from LaTeX to bitmap images files.
        support::Forkedcall::SignalTypePtr convert_ptr(new 
support::Forkedcall::SignalType);
        convert_ptr->connect(bind(&Impl::finishedGenerating, this, _1, _2));

        // Store the generation process in a list of all such processes
        inprogress.start(buffer_, 
                         lyx_gui::hexname(LColor::preview),
                         lyx_gui::hexname(LColor::background),
                         font_scaling_factor_, convert_ptr);
        in_progress_[inprogress.pid()] = inprogress;
}


void PreviewLoader::Impl::finishedGenerating(pid_t pid, int retval)
{
        // Paranoia check!
        InProgressProcesses::iterator git = in_progress_.find(pid);
        if (git == in_progress_.end()) {
                lyxerr << "PreviewLoader::finishedGenerating(): unable to find "
                        "data for PID " << pid << endl;
                return;
        }

        string const command = git->second.command();
        string const status = retval > 0 ? "failed" : "succeeded";
        lyxerr[Debug::GRAPHICS] << "PreviewLoader::finishedInProgress("
                                << retval << "): processing " << status
                                << " for " << command << endl;
        if (retval > 0)
                return;

        // Read the metrics file, if it exists
        git->second.setAscentFractions();

        std::list<PreviewImagePtr> newimages;

        // Add these newly generated bitmap files to the cache and
        // start loading them into LyX.
        vector<SnippetConversion::Entry>::const_iterator it  = 
git->second.begin();
        vector<SnippetConversion::Entry>::const_iterator end = 
git->second.end();

        for (; it != end; ++it) {
                string const & snip = it->latex;
                string const & file = it->filename;
                double af = it->ascentfraction;

                PreviewImagePtr ptr(new PreviewImage(parent_, snip, file, af));
                cache_[snip] = ptr;

                newimages.push_back(ptr);
        }

        // Remove the item from the list of still-executing processes.
        in_progress_.erase(git);

        // Tell the outside world
        std::list<PreviewImagePtr>::const_reverse_iterator
                nit  = newimages.rbegin();
        std::list<PreviewImagePtr>::const_reverse_iterator
                nend = newimages.rend();
        for (; nit != nend; ++nit) {
                imageReady(*nit->get());
        }
}


Converter const * defaultConverter()
{
        string const from = "lyxpreview";

        typedef vector<string> 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;

                Converter const * ptr = converters.getConverter(from, to);
                if (ptr)
                        return ptr;
        }

        static bool first = true;
        if (first) {
                first = false;
                lyxerr << "PreviewLoader::startLoading()\n"
                       << "No converter from \"lyxpreview\" format has been "
                        "defined."
                       << endl;
        }

        return 0;
}


} // namespace graphics
} // namespace lyx

namespace {

string const unique_filename(string const bufferpath)
{
        static int theCounter = 0;
        string const filename = convert<string>(theCounter++) + "lyxpreview";
        return support::AddName(bufferpath, filename);
}




} // namespace anon
// -*- C++ -*-
/**
 * \file SnippetConversion.h
 * This file is part of LyX, the document processor.
 * Licence details can be found in the file COPYING.
 *
 * \author Andreas Vox
 *
 * Full author contact details are available in file CREDITS.
 *
 * lyx::graphics::SnippetConversion converts latex snippets to bitmap
 * files. 
 * snippets are collected one by one. Then, on start converting, these 
 * are dumped to file and processed, converting each snippet to a 
 * separate bitmap image file. 
 */

#ifndef SNIPPETCONVERSION_H
#define SNIPPETCONVERSION_H

#include "converter.h"

#include "support/forkedcall.h"

#include <boost/utility.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/signal.hpp>
#include <boost/bind.hpp>

#include <sstream>
#include <fstream>


namespace lyx {
namespace graphics {

/// Converts a set of LaTeX snippets to graphic files using yxpreview2bitmap
class SnippetConversion {
public:

        /// Holds one snippet and its associated graphics file
        struct Entry {
            std::string latex;
            std::string filename;
            double ascentfraction;
        };

        // A list of all snippets to be converted to previews
        typedef std::list<std::string> SnippetList;

        ///
        SnippetConversion() : pid_(0) {}
        ///
        SnippetConversion(std::string const & filename_base,
                   Converter const * converter, SnippetList const & pending);
                   
        /// Dynamically forget about a snippet
        void eraseSnippet(std::string const & snippet_);

        /// return start of entry list
        std::vector<Entry>::const_iterator begin() const;

        /// return end of entry list
        std::vector<Entry>::const_iterator end() const;

        /// is the entry list empty?
        bool empty() const;

        /// how many entries are in the list?
        int size() const;

        /// Find a snippet in the entry list. Return its associated graphics 
filename
        std::string const findSnippet(std::string const & snippet_) const;

        /// Start the conversion with a forked call
        void start(Buffer const & buffer, 
                   std::string const & hex_foreground, 
                   std::string const & hex_background,
                   double font_scaling_factor_,
                   lyx::support::Forkedcall::SignalTypePtr convert_ptr);

        /// Start the conversion with a execvp call
        int startAndWait(Buffer const & buffer, 
                   std::string const & hex_foreground, 
                   std::string const & hex_background,
                   double font_scaling_factor_);

        /// Remove any files left lying around and kill the forked process.
        void stop();

        /// Read the metrics file and set ascentfractions in entries
        void setAscentFractions();

        ///     The external command which gets forked
        std::string const & command() const { return command_; }
        /// The PID of the forked process
        pid_t pid() const { return pid_; }

private:
        ///
        void dumpPreamble(std::ostream &, Buffer const & buffer) const;
        ///
        void dumpData(std::ostream &, std::vector<Entry> const &) const;
        ///
        std::string prepareCommand(Buffer const & buffer, 
                                   std::string const & hex_foreground, 
                                   std::string const & hex_background,
                                   double font_scaling_factor_);

        ///
        std::string command_;
        ///
        pid_t pid_;
        ///
        std::string filename_base;
        ///
        std::string metrics_file;
        ///
        std::vector<Entry> snippets;
 
        Converter const * converter_;
 };


} // namespace graphics
} // namespace lyx

#endif // SNIPPETCONVERSION_H
// -*- C++ -*-
/**
 * \file SnippetConversion.C
 * This file is part of LyX, the document processor.
 * Licence details can be found in the file COPYING.
 *
 * \author Andreas Vox
 *
 * Full author contact details are available in file CREDITS.
 *
 * lyx::graphics::SnippetConversion converts latex snippets to bitmap
 * files. 
 * snippets are collected one by one. Then, on start converting, these 
 * are dumped to file and processed, converting each snippet to a 
 * separate bitmap image file. 
 */

#include "SnippetConversion.h"

#include "buffer.h"
#include "debug.h"
#include "insetiterator.h"
#include "lyxrc.h"
#include "paragraph.h"

#include "insets/inset.h"

#include "support/filetools.h"
#include "support/forkedcontr.h"
#include "support/lyxlib.h"



#include <boost/utility.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/signal.hpp>

namespace support = lyx::support;

using std::vector;
using std::string;

using std::ostream;
using std::ostringstream;
using std::ofstream;
using std::ifstream;
using std::endl;


// some helpers:

namespace {
        
typedef vector<lyx::graphics::SnippetConversion::Entry> BitmapFileList;

typedef lyx::graphics::SnippetConversion::SnippetList SnippetList;

typedef lyx::graphics::SnippetConversion::Entry Entry;

class IncrementedFileName {
public:
        IncrementedFileName(string const & to_format,
                            string const & filename_base)
                : to_format_(to_format), base_(filename_base), counter_(1)
        {}

        Entry const operator()(string const & snippet)
        {
                ostringstream os;
                os << base_ << counter_++ << '.' << to_format_;
                
                Entry result;
                result.filename = os.str();
                result.latex = snippet;
                return result;
        }

private:
        string const & to_format_;
        string const & base_;
        int counter_;
};

class FindFirst : public std::unary_function<Entry, bool> {
public:
        FindFirst(string const & comp) : comp_(comp) {}
        bool operator()(Entry const & sp) const
        {
                return sp.latex == comp_;
        }
private:
        string const comp_;
};

        
        
} // namespace anon

namespace lyx {
namespace graphics {



SnippetConversion::SnippetConversion(string const & filename_base,
                Converter const * converter, SnippetList const & pending)       
  
                : pid_(0),
                  filename_base(filename_base),
                  metrics_file(filename_base + ".metrics"),
                  snippets(pending.size()),
                  converter_(converter) 
{                  
        SnippetList::const_iterator pit  = pending.begin();
        SnippetList::const_iterator pend = pending.end();
        BitmapFileList::iterator sit = snippets.begin();

        std::transform(pit, pend, sit,
                       IncrementedFileName(converter->to, filename_base));      
           
}


bool SnippetConversion::empty() const 
{
        return snippets.empty();
}


int SnippetConversion::size() const 
{
        return snippets.size();
}

BitmapFileList::const_iterator SnippetConversion::begin() const 
{
        return snippets.begin();
}

BitmapFileList::const_iterator SnippetConversion::end() const 
{
        return snippets.end();
}


string const SnippetConversion::findSnippet(string const & snippet_) const
{
                BitmapFileList::const_iterator beg  = snippets.begin();
                BitmapFileList::const_iterator end = snippets.end();
                BitmapFileList::const_iterator res = find_if(beg, end, 
FindFirst(snippet_));
                if (res != end) {
                        return res-> filename;
                }
                else {
                        return "";
                }
}

void SnippetConversion::eraseSnippet(string const & snippet_)
{
                BitmapFileList::iterator it  = snippets.begin();
                BitmapFileList::iterator end = snippets.end();

                it = find_if(it, end, FindFirst(snippet_));
                if (it != end)
                        snippets.erase(it, it+1);
}


string SnippetConversion::prepareCommand(Buffer const & buffer, 
                              string const & hex_foreground, 
                              string const & hex_background,
                              double font_scaling_factor_)
{
                // Output the LaTeX file.
        string const latexfile = filename_base + ".tex";

        lyxerr << "preview snippets in " << latexfile << endl;
        
        ofstream of(latexfile.c_str());
        if (!of) {
                lyxerr[Debug::GRAPHICS] << "SnippetConversion::start()\n"
                                        << "Unable to create LaTeX file\n"
                                        << latexfile << endl;
                return "";
        }
        
        lyxerr << "writing preview snippets now" << endl;

        of << "\\batchmode\n";
        dumpPreamble(of, buffer);
        of << "\n\\begin{document}\n";
        dumpData(of, snippets);
        of << "\n\\end{document}\n";
        of.close();

        lyxerr << "done writing preview snippets" << endl;

        
        // The conversion command.
        ostringstream cs;
        cs << converter_->command << ' ' << converter_->to << ' '
           << latexfile << ' ' << int(font_scaling_factor_) << ' '
           << hex_foreground << ' ' << hex_background;
           
        lyxerr << "preview command is " << cs.str() << endl;
        
        return support::LibScriptSearch(cs.str());
}

void SnippetConversion::start(Buffer const & buffer, 
                              string const & hex_foreground, 
                              string const & hex_background,
                              double font_scaling_factor,
                              support::Forkedcall::SignalTypePtr convert_ptr) 
{
        command_ = prepareCommand(buffer, hex_foreground, hex_background, 
font_scaling_factor);
        support::Forkedcall call;
        int ret = call.startscript(command_, convert_ptr);

        if (ret != 0) {
                lyxerr[Debug::GRAPHICS] << "SnippetConversion::start()\n"
                                        << "Unable to start process\n"
                                        << command_ << endl;
                return;
        }
        pid_ = call.pid();
           
}


int SnippetConversion::startAndWait(Buffer const & buffer, 
                              string const & hex_foreground, 
                              string const & hex_background,
                              double font_scaling_factor) 
{
        command_ = prepareCommand(buffer, hex_foreground, hex_background, 
font_scaling_factor);
        support::Forkedcall call;
        int ret = call.startscript(support::ForkedProcess::Wait, command_);
        
        if (ret != 0) {
                lyxerr[Debug::GRAPHICS] << "SnippetConversion::startAndWait()\n"
                                        << "Unable to start process\n"
                                        << command_ << endl;
        }
        else {
                pid_ = call.pid();
        }
        return ret;
           
}

void SnippetConversion::stop()
{
        if (!metrics_file.empty())
                support::unlink(metrics_file);

        BitmapFileList::const_iterator vit  = snippets.begin();
        BitmapFileList::const_iterator vend = snippets.end();
        for (; vit != vend; ++vit) {
                if (!vit->filename.empty())
                        support::unlink(vit->filename);
        }

        if (pid_)
                support::ForkedcallsController::get().kill(pid_, 0);

}



void SnippetConversion::dumpPreamble(ostream & os, Buffer const & buffer) const
{
        // Why on earth is Buffer::makeLaTeXFile a non-const method?
        Buffer & tmp = const_cast<Buffer &>(buffer);
        // Dump the preamble only.
        OutputParams runparams;
        runparams.flavor = OutputParams::LATEX;
        runparams.nice = true;
        runparams.moving_arg = true;
        runparams.free_spacing = true;
        tmp.makeLaTeXFile(os, buffer.filePath(), runparams, true, false);

        // FIXME! This is a HACK! The proper fix is to control the 'true'
        // passed to WriteStream below:
        // int InsetFormula::latex(Buffer const &, ostream & os,
        //                         OutputParams const & runparams) const
        // {
        //      WriteStream wi(os, runparams.moving_arg, true);
        //      par_->write(wi);
        //      return wi.line();
        // }
        os << "\n"
           << "\\def\\lyxlock{}\n"
           << "\n";

        // Loop over the insets in the buffer and dump all the math-macros.
        InsetBase & inset = buffer.inset();
        InsetIterator it = inset_iterator_begin(inset);
        InsetIterator const end = inset_iterator_end(inset);

        for (; it != end; ++it)
                if (it->lyxCode() == InsetBase::MATHMACRO_CODE)
                        it->latex(buffer, os, runparams);

        // All equation lables appear as "(#)" + preview.sty's rendering of
        // the label name
        if (lyxrc.preview_hashed_labels)
                os << "\\renewcommand{\\theequation}{\\#}\n";

        // Use the preview style file to ensure that each snippet appears on a
        // fresh page.
        os << "\n"
           << "\\usepackage[active,delayed,dvips,showlabels,lyx]{preview}\n"
           << "\n";
}


void SnippetConversion::dumpData(ostream & os,
                                   BitmapFileList const & vec) const
{
        if (vec.empty())
                return;

        BitmapFileList::const_iterator it  = vec.begin();
        BitmapFileList::const_iterator end = vec.end();

        for (; it != end; ++it) {
                os << "\\begin{preview}\n"
                   << it->latex
                   << "\n\\end{preview}\n\n";
        }
}


void SnippetConversion::setAscentFractions() 
{
        BitmapFileList::iterator it  = snippets.begin();
        BitmapFileList::iterator end = snippets.end();

        bool error = false;

        ifstream in(metrics_file.c_str());
        if (!in.good()) {
                lyxerr[Debug::GRAPHICS]
                        << "setAscentFractions(" << metrics_file << ")\n"
                        << "Unable to open file!" << endl;
        }
        else {
                int snippet_counter = 1;
                while (!in.eof() && it != end) {
                        string snippet;
                        int id;
                        double ascent_fraction;
        
                        in >> snippet >> id >> ascent_fraction;
        
                        if (!in.good())
                                // eof after all
                                break;
        
                        error = snippet != "Snippet";
                        if (error)
                                break;
        
                        error = id != snippet_counter;
                        if (error)
                                break;
        
                        it->ascentfraction = ascent_fraction;
        
                        ++snippet_counter;
                        ++it;
                }
        }
        
        // If all else fails, then the images will have equal ascents and
        // descents.
        while (it != end) {
                it->ascentfraction = 0.5;
                ++it;
        }
        
        if (error) {
                lyxerr[Debug::GRAPHICS]
                        << "setAscentFractions(" << metrics_file << ")\n"
                        << "Error reading file!\n" << endl;
        }
}

} // namespace
} // lyx::graphics

Reply via email to