Logo Search packages:      
Sourcecode: passepartout version File versions  Download package

postscriptviewent.cc

///
// Copyright (C) 2003, 2004, Fredrik Arnerup & Rasmus Kaj, See COPYING
///
#include "postscriptviewent.h"
#include "config.h"
#include "document/basicframe.h" // For Gen_Pic_Error; Todo: remove!
#include "util/tempfile.h"
#include "util/processman.h"
#include "util/os.h"
#include "util/filesys.h"
#include "util/warning.h"
#include "util/stringutil.h" // tostr
#include "fonts/fontmanager.hh" // getFontPaths
#include <sstream>

/// \todo Remote the following line!
typedef BasicFrame::GenPicError GenPicError;

namespace {
  enum SpecialPid { P_SHUTDOWN = -3, P_STARTUP = -2, P_IDLE = -1 };

  const string& preferredDeviceName() {
    static string devicename;
    if(devicename.empty()) {
      verbose << "Deciding which ghostscript device to use ..." << std::endl;

      const std::string &psinterpreter = config.PSInterpreter.values.front();
      if(psinterpreter.empty())
      throw GenPicError(BasicFrame::GENERATION,
                    "No PostScript interpreter specified");
      
      if(ProcessManager::instance()
       .system(psinterpreter + " -q -dBATCH -sDEVICE=pngalpha") == 0)
      devicename = "pngalpha";
      else
      devicename = "ppmraw";
      
      verbose << "... using the " << devicename << " device." << std::endl;
    }
    return devicename;
  }
}

PostscriptViewent::PostscriptViewent(View& view, const FrameRef psframe,
                             int _scale_factor)
  : Viewent(view, psframe), frame(psframe), 
    pid(P_IDLE), tmp_file(Tempfile::find_new_name()),
    scale_factor(_scale_factor), redraw_requested(false)
{
  if(preferredDeviceName() == "pngalpha")
    scale_factor = 1;
  
  ProcessManager::instance().process_stopped.connect
    (slot(*this, &PostscriptViewent::process_stopped));
  /// \todo  Only regenerate when necessary, i.e. not when just moving ...
  frame->object_changed_signal.connect
    (SigC::hide<Pagent*>(slot(*this, &PostscriptViewent::regenerate)));
  view.connect_zoom_change
    (SigC::hide<float>(slot(*this, &PostscriptViewent::regenerate)));

  canvas_pixbuf.reset(new Gnome::Canvas::Pixbuf(*content_group));
  canvas_pixbuf->property_anchor() = Gtk::ANCHOR_SOUTH_WEST;
  canvas_pixbuf->property_x() = 0; canvas_pixbuf->property_y() = 0;
  canvas_pixbuf->property_width_set() = true;
  canvas_pixbuf->property_height_set() = true;
  canvas_pixbuf->property_width_in_pixels() = false;
  canvas_pixbuf->property_height_in_pixels() = false;
  on_geometry_changed(psframe);
  regenerate();
}

PostscriptViewent::~PostscriptViewent() {
  redraw_requested = false;
  if(pid == P_STARTUP) {
    cerr << "PostscriptViewent terminating while in startup.  Bad."
       << std::endl;
  } else if(pid > P_IDLE) {
    stop_process();
  }
  try { unlink(tmp_file); }
  catch(std::exception& err) {
    warning << "Failed to delete postscript view render file." << err.what()
          << std::endl;
  }
}

void PostscriptViewent::set_state(State state) {
  Viewent::set_state(state);
  // make sure old images don't linger
  if(state == BROKEN || state == MISSING) {
    canvas_pixbuf->property_pixbuf() = Glib::RefPtr<Gdk::Pixbuf>();
  }
}

void PostscriptViewent::on_geometry_changed(Pagent *pagent) {
  Viewent::on_geometry_changed(pagent);

  Vector size = frame->get_inherent_size();

  canvas_pixbuf->property_width() = size.x;
  canvas_pixbuf->property_height() = size.y;
}

void PostscriptViewent::regenerate() {
  if(pid != P_IDLE) {
    //verbose << "PostscriptViewent allready running gs " << pid << std::endl;
    redraw_requested = true;
    return;
  } else {
    pid = P_STARTUP;
    
    Glib::signal_timeout().connect
      (SigC::slot(*this, &PostscriptViewent::regenerate_bg),
       200, Glib::PRIORITY_DEFAULT_IDLE);
  }
}

/** This method is run as a separate thread by regenerate(). */
bool PostscriptViewent::regenerate_bg() {
  try {
    set_state(WAIT);
    redraw_requested = false;
    
    std::string &psinterpreter = config.PSInterpreter.values.front();
    if(psinterpreter.empty())
      throw GenPicError(BasicFrame::GENERATION,
                        "No PostScript interpreter specified");
    
    const Matrix &m = frame->get_matrix();
    const Vector framesize = frame->get_inherent_size();
    
    // Size of frame as pixels in this view.
    const Gdk::Point pv(int(view.pt2scr(framesize.x * m.sc_x())),
                        int(view.pt2scr(framesize.y * m.sc_y())));
    if(pv.get_x() == 0 || pv.get_y() == 0) 
      throw GenPicError(BasicFrame::ZEROSIZE, "Picture has zero size");
    
    std::ostringstream tmp;
    tmp << psinterpreter //assuming it is ghostscript
      << " -q -dSAFER -dNOPAUSE -dBATCH"
      << " -sDEVICE=" << preferredDeviceName();

    // gs doesn't use fontconfig, so give font paths as parameters
    using font::FontManager;
    const FontManager::FontPaths &fontpaths =
      FontManager::instance().getFontPaths();
    if(!fontpaths.empty()) {
      tmp << " -sFONTPATH=";
      FontManager::FontPaths::const_iterator i;
      for(i = fontpaths.begin(); i != fontpaths.end(); i++) {
        if(i != fontpaths.begin())
          tmp << ":"; // colon is path separator for unix
        tmp << *i;
      }
    }

    tmp     << " -r" << view.get_scrres() * scale_factor
        << " -g" << scale_factor * pv.get_x() << 'x' 
        << scale_factor * pv.get_y()
      << " -sOutputFile=" << tmp_file << " -";
    Process proc = ProcessManager::instance().run(tmp.str());
    
    if((pid = proc->get_pid()) == -1)
      throw GenPicError(BasicFrame::GENERATION,
                        "Failed to run " + psinterpreter);
    
    verbose << pid << ": " << tmp.str() << std::endl;
    
    frame->generate_picture(proc->get_cin());
    
  } catch(const GenPicError &e) {
    stop_process();
    if(e.type == BasicFrame::NOTREADY)
      redraw_requested = true;      // simply try again
    else {
      set_state(e.type == BasicFrame::ASSOCIATION ? MISSING : BROKEN);
      warning << frame->get_name() << ": " << e.what() << std::endl;
    }
    pid = P_IDLE;
  } catch(const std::exception& e) {
    stop_process();
    warning << frame->get_name() << ": " << e.what() << std::endl;
    set_state(BROKEN);
    pid = P_IDLE;
  } catch(...) {
    stop_process();
    warning << frame->get_name() << ": Unknown error" << std::endl;
    set_state(BROKEN);
    pid = P_IDLE;
  }
  
  // Try again if needed
  return (pid == P_IDLE && redraw_requested);
}

void PostscriptViewent::process_stopped(pid_t _pid, bool exited_normally, 
                              int exit_code)
{
  if(_pid != pid)
    return;

  try {
    debug << _pid << ", exit code: " << exit_code << std::endl;

    if(!exited_normally)
      throw std::runtime_error("Process " + tostr(_pid)
                               + " exited abnormally");

    if(!exists(tmp_file)) 
      throw std::runtime_error("\"" + tmp_file + "\" does not exist");

    PixbufRef pixbuf = Gdk::Pixbuf::create_from_file(tmp_file);

    // Make all white pixels transparent.
    if(config.FakeTransparency.values.front()
       && pixbuf && !pixbuf->get_has_alpha())
      pixbuf = pixbuf->add_alpha(true, 255, 255, 255); 
    // had some trouble with some images becoming totally transparent

    // scale
    if(scale_factor != 1)
      pixbuf = pixbuf->scale_simple(pixbuf->get_width() / scale_factor,
                                    pixbuf->get_height() / scale_factor,
                                    Gdk::INTERP_BILINEAR);

    canvas_pixbuf->property_pixbuf() = pixbuf;
    on_geometry_changed(frame);
    set_state(NORMAL);
  
  } catch(const std::exception& e) {
    set_state(BROKEN);
    warning << frame->get_name() << ": " << e.what() << std::endl; 
  } catch(const Glib::Error& e) {
    set_state(BROKEN);
    warning << frame->get_name() << ": " << e.what() << std::endl; 
  }
  pid = P_IDLE;

  if(redraw_requested)
    regenerate();
}

void PostscriptViewent::stop_process() {
  if(pid > 0) {
    // We don't want the process termination hook to run.
    pid_t stopping = pid;
    pid = P_SHUTDOWN;
    ProcessManager::instance().stop(stopping);
    
  } else {
    warning << frame->get_name() << ": " << " no process to stop" << std::endl;
  }
}

Generated by  Doxygen 1.6.0   Back to index