Logo Search packages:      
Sourcecode: magics++ version File versions  Download package

CairoDriver.cc

Go to the documentation of this file.
/******************************** LICENSE ********************************


  Copyright 2007 European Centre for Medium-Range Weather Forecasts (ECMWF)

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.


 ******************************** LICENSE ********************************/
/*

 I currently have a function that accepts a cairo context (with a path already set)
 and strokes the path with a pre-set width and color. The context can have any arbitrary
 transformation already applied to it. So given this situation, how would I ensure
 that I get a uniform line width from the stroke even when they may have applied a
 deforming scale to the context? Any ideas would be appreciated!

Something like:

  cairo_save (cr);
  cairo_identity_matrix (cr);
  cairo_set_line_width (cr, width);
  cairo_stroke (cr);
  cairo_restore (cr);

*/

/*! \file CairoDriver.cc
    \brief Implementation of CairoDriver.
    \author Graphics Section, ECMWF

    Started: Mon Oct 15 20:49:32 2007

   \todo Fix 'convert' dependency
   \todo Check how much drivers are dependent on writing temp files in local directory (thread safety)
*/
#include <cairo.h>

#include <CairoDriver.h>
#include <Polyline.h>
#include <Text.h>
#include <Image.h>
#include <Symbol.h>
#include <ImportObject.h>
#include <System.h>


#include <pango/pangocairo.h>
#include <iconv.h>             // Only for AIX?

#if CAIRO_HAS_PDF_SURFACE
#include <cairo-pdf.h>
#endif

#if CAIRO_HAS_PS_SURFACE
#include <cairo-ps.h>
#endif

#if CAIRO_HAS_SVG_SURFACE
#include <cairo-svg.h>
#endif

#if CAIRO_HAS_XLIB_SURFACE
#include <cairo-xlib.h>
Display *dpy;
#endif

#define FONT_SCALE 25*.7  //! \todo clean-up!!!

using namespace magics;

/*!
  \brief Constructor
*/
00085 CairoDriver::CairoDriver() : filename_(""), offsetX_(0), offsetY_(0), backend_("PDF")
{
        cr_ = 0;
}

/*!
  \brief Destructor
*/
00093 CairoDriver::~CairoDriver()
{
}

/*!
  \brief Opening the driver
*/
00100 void CairoDriver::open()
{
        Log::info() << "Cairo version used is: "<<cairo_version_string()<< " backend: "<<backend_ <<endl;

        const float ratio = getYDeviceLength() / getXDeviceLength();
      const int width = maground(getWidth());

      setCMscale(float(width)/getXDeviceLength());
      dimensionXglobal_ = width;
      Log::dev() << "width -->" << width << endl;
      Log::dev() << "ratio -->" << ratio*width << endl;
      Log::dev() << "ratio -->" << maground(ratio*width) << endl;
      Log::dev() << "ratio -->" << static_cast<int>(ratio*width) << endl;
      dimensionYglobal_ = maground(ratio*width);

      coordRatioY_ = -1;

      if(context_)
      {
#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 2, 0)
            cr_ = (cairo_t*)context_;
            surface_ = cairo_get_group_target(cr_);
            dimensionXglobal_ = cairo_image_surface_get_width (surface_);
                dimensionYglobal_ = cairo_image_surface_get_height(surface_);
#else
            Log::error() << "CairoDriver: For contexts you need at least Cairo 1.2!" <<endl;
#endif
      }
      else
            setupNewSurface();
}


void CairoDriver::setupNewSurface() const
{
      if(magCompare(backend_,"png"))
      {
          surface_ = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, dimensionXglobal_, dimensionYglobal_);
      }
      else if(magCompare(backend_,"pdf"))
      {
#if CAIRO_HAS_PDF_SURFACE
          filename_ = getFileName("pdf");
            dimensionXglobal_ = static_cast<int>(getXDeviceLength()*72/2.54);
            dimensionYglobal_ = static_cast<int>(getYDeviceLength()*72/2.54);

          surface_ = cairo_pdf_surface_create(filename_.c_str(), dimensionXglobal_, dimensionYglobal_);
#else
          Log::error() << "CairoDriver: PDF output NOT supported! Enable PDF support in your Cairo installation." <<endl;
#endif
      }
      else if(magCompare(backend_,"ps"))
      {
#if CAIRO_HAS_PS_SURFACE
          filename_ = getFileName("ps"); 

          const int dimensionXglobal = static_cast<int>(getXDeviceLength()*72/2.54);
          const int dimensionYglobal = static_cast<int>(getYDeviceLength()*72/2.54);

          if(dimensionXglobal>dimensionYglobal)   // landscape
          {
                surface_ = cairo_ps_surface_create(filename_.c_str(), dimensionYglobal,dimensionXglobal);
            }
            else
            {
                surface_ = cairo_ps_surface_create(filename_.c_str(), dimensionXglobal,dimensionYglobal);
            }
#else
          Log::error() << "CairoDriver: PS output NOT supported! Enable PS support in your Cairo installation." <<endl;
#endif
      }
      else if(magCompare(backend_,"eps"))
      {
#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 5, 2)
          filename_ = getFileName("eps");
          surface_ = cairo_ps_surface_create(filename_.c_str(), dimensionXglobal_, dimensionYglobal_);
          cairo_ps_surface_set_eps (surface_,true);
#else
          Log::error() << "CairoDriver: EPS output NOT supported! You need at least version Cairo 1.5.2.\n"
                       << "PostScript is generated instead." <<endl;
          filename_ = getFileName("ps");
          surface_ = cairo_ps_surface_create(filename_.c_str(), dimensionXglobal_, dimensionYglobal_);
#endif
      }
      else if(magCompare(backend_,"svg"))
      {
#if CAIRO_HAS_SVG_SURFACE
          filename_ = getFileName("svg",currentPage_);
          surface_ = cairo_svg_surface_create (filename_.c_str(), dimensionXglobal_, dimensionYglobal_);
//        cairo_svg_surface_restrict_to_version (surface_, CAIRO_SVG_VERSION_1_2);
#else
          Log::error() << "CairoDriver: SVG output NOT supported! Enable SVG support in your Cairo installation." <<endl;
#endif
      }
      else if(magCompare(backend_,"x"))
      {
#if CAIRO_HAS_XLIB_SURFACE
            Window rootwin;
            Window win;

            if(!(dpy=XOpenDisplay(NULL)))
            {
                  Log::error() << "CairoDriver: Could not open display for Xlib!"<<endl;
                  exit(1);
            }

            int scr=DefaultScreen(dpy);
            rootwin=RootWindow(dpy, scr);

            win=XCreateSimpleWindow(dpy, rootwin, 1, 1, dimensionXglobal_, dimensionYglobal_, 0,
                        BlackPixel(dpy, scr), BlackPixel(dpy, scr));

            XStoreName(dpy, win, "Magics++");
            XSelectInput(dpy, win, ExposureMask|ButtonPressMask);
            XMapWindow(dpy, win);

            surface_ = cairo_xlib_surface_create(dpy, win, DefaultVisual(dpy, 0), dimensionXglobal_, dimensionYglobal_);
#else
          Log::error() << "CairoDriver: Xlib output NOT supported! Enable Xlib support in your Cairo installation." <<endl;
#endif
      }
      else
      {
            Log::error() << "CairoDriver: The backend "<< backend_ <<" is NOT supported!" <<endl;
      }

      cairo_status_t status = cairo_surface_status(surface_);
      if (status)
      {
            Log::error()      << "CairoDriver: the surface ("<<backend_<<") could not be generated!\n"
                        << " -> "<<cairo_status_to_string(status)<<endl;
      }

      if ( !cr_) cr_ = cairo_create (surface_);

      // set PS META information
      if(magCompare(backend_,"ps") )
      {
#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 8, 0)
        const SystemInfo info;
        const string s1 = "%%Title: " + getTitle();
        cairo_ps_surface_dsc_comment (surface_, s1.c_str());
        const string s2 = "%%Creator2: " + getMagicsVersionString();
        cairo_ps_surface_dsc_comment (surface_, s2.c_str());
        const string s3 = "%%For: " + info.getUserID() + "@" + info.getHostName() + " " + info.getUserName();
        cairo_ps_surface_dsc_comment (surface_, s3.c_str());

          if(dimensionXglobal_>dimensionYglobal_)   // landscape
          {
                dimensionYglobal_ = static_cast<int>(getXDeviceLength()*72/2.54);
                dimensionXglobal_ = static_cast<int>(getYDeviceLength()*72/2.54);
                cairo_translate (cr_, 0, dimensionYglobal_);
                cairo_matrix_t matrix;
                cairo_matrix_init (&matrix, 0, -1, 1, 0, 0,  0);
            cairo_transform (cr_, &matrix);
            cairo_ps_surface_dsc_comment (surface_, "%%PageOrientation: Landscape");
          }
            else
            {
                dimensionXglobal_ = static_cast<int>(getXDeviceLength()*72/2.54);
                dimensionYglobal_ = static_cast<int>(getYDeviceLength()*72/2.54);
            cairo_ps_surface_dsc_comment (surface_, "%%PageOrientation: Portrait");
            }

        if(magCompare(MAGICS_SITE,"ecmwf"))
          cairo_ps_surface_dsc_comment (surface_, "%%Copyright: Copyright (C) 2009 ECMWF");
#endif
      }

#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 2, 0)
      cairo_surface_set_fallback_resolution (surface_, getResolution(), getResolution());
#endif
      if(magCompare(getTransparent(),"off") || !magCompare(backend_,"png"))
      {
            cairo_set_source_rgb (cr_, 1.0, 1.0, 1.0); /* white */
      }
      else
      {
            cairo_set_source_rgba (cr_, 1.0, 1.0, 1.0,0.0); /* white transparent */
      }
      cairo_paint (cr_);
      cairo_set_line_join(cr_,CAIRO_LINE_JOIN_BEVEL);

      dimensionX_ = static_cast<float>(dimensionXglobal_);
      if(!context_)
         dimensionY_ = static_cast<float>(dimensionYglobal_);
      else
      {
         const float ratio = getYDeviceLength() / getXDeviceLength();
         dimensionY_ =  static_cast<int>(ratio*dimensionXglobal_);
      }
      currentPage_ = 0;
}

/*!
  \brief Closing the driver
*/
00297 void CairoDriver::close()
{
      currentPage_ = 0;

      if(magCompare(backend_,"pdf") && !filename_.empty()) printOutputName("CAIRO pdf "+filename_);
      if(magCompare(backend_,"ps") && !filename_.empty()) printOutputName("CAIRO ps "+filename_);

      if ( context_ == 0 ) {
            cairo_destroy (cr_);
            cairo_surface_destroy (surface_);
      }
#if CAIRO_HAS_XLIB_SURFACE
      if(magCompare(backend_,"x"))
      {
            XEvent event;
            while(1)
            {
                  XNextEvent(dpy, &event);
                  if(event.type==Expose && event.xexpose.count<1)
                  {}
                  else if(event.type==ButtonPress) break;
            }
            XCloseDisplay(dpy);
      }
#endif
}


/*!
  \brief starting a new page

  This method has to take care that previous pages are closed and that
  for formats with multiple output files a new file is set up.
*/
00331 MAGICS_NO_EXPORT void CairoDriver::startPage() const
{
      if(currentPage_ > 0)
      {
            if (magCompare(backend_,"png") )
            {
                  cairo_destroy (cr_);
                  cairo_surface_destroy (surface_);

                  surface_ = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, dimensionXglobal_, dimensionYglobal_);
                  cr_ = cairo_create(surface_);
                  if(magCompare(getTransparent(),"off"))
                  {
                        cairo_set_source_rgb (cr_, 1.0, 1.0, 1.0); /* white */
                  }
                  else
                  {
                        cairo_set_source_rgba (cr_, 1.0, 1.0, 1.0, 0.0); /* white */
                  }
                  cairo_paint (cr_);
            }
#if CAIRO_HAS_SVG_SURFACE
            else if (magCompare(backend_,"svg") )
            {
                  cairo_destroy (cr_);
                  cairo_surface_destroy (surface_);

                  filename_ = getFileName("svg",currentPage_+1);
                  surface_ = cairo_svg_surface_create(filename_.c_str(), dimensionXglobal_, dimensionYglobal_);
                  cr_ = cairo_create (surface_);
#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 4, 0)
                  cairo_svg_surface_restrict_to_version (surface_, CAIRO_SVG_VERSION_1_1);
#endif
            }
#endif
            else if(magCompare(backend_,"eps"))
            {
#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 5, 2)
                  cairo_destroy (cr_);
                  cairo_surface_destroy (surface_);

                  filename_ = getFileName("eps",currentPage_+1);
                  surface_ = cairo_ps_surface_create(filename_.c_str(), dimensionXglobal_, dimensionYglobal_);
                  cairo_ps_surface_set_eps (surface_,true);
                  cr_ = cairo_create (surface_);
#endif
            }
      }

      if( currentPage_ == 0 || (!magCompare(backend_,"pdf") && !magCompare(backend_,"ps")) )
      {
            cairo_translate(cr_,0,static_cast<float>(dimensionYglobal_));
      }
//    cairo_scale(cr_,1,-1);

      currentPage_++;
      newPage_ = true;
}

MAGICS_NO_EXPORT void CairoDriver::startAdditionalPage(const StandaloneLayout& lay) const
{
      if (magCompare(backend_,"png") )
      {
            tmp_cr_ = cr_;
            tmp_surface_ = surface_;

            surface_ = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, lay.outputWidth(),lay.outputHeight());
            cr_ = cairo_create(surface_);

            if(magCompare(getTransparent(),"off"))
            {
                  cairo_set_source_rgb (cr_, 1.0, 1.0, 1.0); /* white */
            }
            else
            {
                  cairo_set_source_rgba (cr_, 1.0, 1.0, 1.0, 0.0); /* white */
            }
            cairo_paint (cr_);
      }
}

MAGICS_NO_EXPORT void CairoDriver::endAdditionalPage() const
{
      filename_ = getFileName("png" ,-2);
      cairo_surface_write_to_png(surface_, filename_.c_str());
      if(!filename_.empty()) printOutputName("CAIRO png "+filename_);
      cairo_destroy (cr_);
      cairo_surface_destroy (surface_);
      surface_=tmp_surface_;
      cr_=tmp_cr_;
}



/*!
  \brief ending a new page

  This method has to take care that for formats with multiple output
  files are closed.
*/
00431 MAGICS_NO_EXPORT void CairoDriver::endPage() const
{
      cairo_show_page(cr_);

      if(magCompare(backend_,"eps"))
      {
            if(!filename_.empty()) printOutputName("CAIRO eps "+filename_);
      }
      else if(magCompare(backend_,"svg"))
      {
            if(!filename_.empty()) printOutputName("CAIRO svg "+filename_);
      }
      else if (magCompare(backend_,"png") )
      {
            filename_ = getFileName("png" ,currentPage_);
            cairo_surface_write_to_png(surface_, filename_.c_str());
            if(!filename_.empty()) printOutputName("CAIRO png "+filename_);
      }
}


/*!
  \brief project to a new Layout

  This method will update the offset and scale according to the new Layout given.

  \sa Layout
*/
00459 MAGICS_NO_EXPORT void CairoDriver::project(const Layout& layout) const
{
      cairo_save (cr_);

      // push current state
      dimensionStack_.push(dimensionX_);
      dimensionStack_.push(dimensionY_);
      offsetsX_.push(offsetX_);
      offsetsY_.push(offsetY_);
      scalesX_.push(coordRatioX_);
      scalesY_.push(coordRatioY_);

      offsetX_    += layout.x()     * 0.01 * dimensionX_;
      offsetY_    -= layout.y()     * 0.01 * dimensionY_;
      dimensionX_ =  layout.width() * 0.01 * dimensionX_;
      dimensionY_ =  layout.height()* 0.01 * dimensionY_;
/*
        if (magCompare(backend_,"png") )
      {
                layout.pushDriverInfo(offsetX_, dimensionYglobal_ - dimensionY_ + offsetY_, dimensionX_, dimensionY_);
      }
*/
      const float sumX = layout.maxX() - layout.minX();
      const float sumY = layout.maxY() - layout.minY();

      if( sumX!=0 && sumY!=0 )
      {
            coordRatioX_ = dimensionX_/sumX;
            coordRatioY_ = -dimensionY_/sumY;
      }

      offsetX_ = projectX( -layout.minX());
      offsetY_ = projectY( -layout.minY());

/*
      if(box->getClip())
      {
//          cairo_set_source_rgb(cr_, 1,0,0);
            cairo_rectangle (cr_, projectX(Xmin),projectY(Ymin),projectX(Xmax)-projectX(Xmin),projectY(Ymax)-projectY(Ymin) );
            cairo_clip(cr_);
//          cairo_stroke(cr_);
      }
      if(box->centered())
      {
            Xoff += Xlength*0.5;
            Yoff += Ylength*0.5;
            obsBox_=true;
      }
      else obsBox_=false;
*/
      // write meta info
      if (magCompare(backend_,"png") )
      {
                layout.pushDriverInfo(offsetX_, dimensionYglobal_ - offsetY_, dimensionX_, dimensionY_);
      }
}

/*!
  \brief reproject out of the last Layout

  This method will update the offset and scale to the state they were before the
  last Layout was received.

  \sa UnLayout
*/
00524 MAGICS_NO_EXPORT void CairoDriver::unproject() const
{
      dimensionY_ = dimensionStack_.top();dimensionStack_.pop();
      dimensionX_ = dimensionStack_.top();dimensionStack_.pop();
      offsetX_ = offsetsX_.top();offsetsX_.pop();
      offsetY_ = offsetsY_.top();offsetsY_.pop();
      coordRatioX_  = scalesX_.top(); scalesX_.pop();
      coordRatioY_  = scalesY_.top(); scalesY_.pop();

//    cairo_reset_clip(cr_);
      cairo_restore(cr_);
}


/*!
  \brief sets a new colour

  This colour stays the default drawing colour until the painting in the
  current box is finished.

  \sa Colour setColour
*/
00546 MAGICS_NO_EXPORT void CairoDriver::setNewColour(const Colour &colour) const
{
      currentColour_ = colour;
}

/*!
  \brief sets a colour

  This colour stays the default drawing colour until the painting in the
  current box is finished.

  \sa Colour setNewColour
*/
00559 MAGICS_NO_EXPORT void CairoDriver::setColour(cairo_t* cr, const Colour &colour) const
{
      if(!(colour=="undefined") && ( (colour.red()*colour.green()*colour.blue()) >= 0 ) )
      {
        if(colour.alpha()<1.) cairo_set_source_rgb (cr,colour.red(),colour.green(),colour.blue());
        else                  cairo_set_source_rgba(cr,colour.red(),colour.green(),colour.blue(),colour.alpha());
      }
      else cairo_set_source_rgba(cr,1.,1.,1.,0.);
}


/*!
  \brief sets a new line width

  This line width stays the default width until the painting in the
  current box is finished.

  \sa setLineParameters()
  \todo Find a better way than multiple by 0.6
*/
00579 MAGICS_NO_EXPORT void CairoDriver::setNewLineWidth(const float width) const
{
        currentLineWidth_ = (width > 0.01 ? width : 0.01) * .6;
}

/*!
  \brief sets new properties of how lines are drawn

  These properties stay the default until the painting in the
  current box is finished.

  \sa LineStyle

  \param linestyle Object describing the line style
  \param w width of the line
*/
00595 MAGICS_NO_EXPORT int CairoDriver::setLineParameters(const LineStyle linestyle, const float width) const
{
      setNewLineWidth(width);

#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 4, 0)
      if(cairo_get_dash_count(cr_)==0 && linestyle==M_SOLID) return 0;
#endif
      switch(linestyle)
      {
            case M_DASH: // 6 on - 2 off
                  cairo_set_line_cap (cr_, CAIRO_LINE_CAP_SQUARE);
                  static double dash_line[] = { 4. };
                  cairo_set_dash(cr_,dash_line,1,0.);
                  break;
            case M_DOT: // 1 on - 2 off
                        setNewLineWidth(2*width);
                  static double dotted_line[] = { 0., 6. };
                  cairo_set_line_cap (cr_, CAIRO_LINE_CAP_ROUND);
                  cairo_set_dash(cr_,dotted_line,2,0.);
                  break;
            case M_CHAIN_DASH: // 4 on - 2 off -  1 on - 2 off
                  static double chain_dash_line[] = { 4., 4., 0., 6. };
                  cairo_set_line_cap (cr_, CAIRO_LINE_CAP_SQUARE);
                  cairo_set_dash(cr_,chain_dash_line,4,0.);
                  break;
            case M_CHAIN_DOT: // 4 on - 2 off -  1 on - 2 off - 1 on - 2 off
                  static double chain_dot_line[] = { 4., 4., 0., 6., 0., 6. };
                  cairo_set_line_cap (cr_, CAIRO_LINE_CAP_SQUARE);
                  cairo_set_dash(cr_,chain_dot_line,6,0.);
                  break;
            default:  // SOLID
                  cairo_set_line_cap (cr_, CAIRO_LINE_CAP_SQUARE);
                  static double solid_line[] = { 4., 0. };
                  cairo_set_dash(cr_,solid_line,2,0.);
                  break;
      }
      return 0;
}

/*!
  \brief renders polylines

  This method renders a polyline given as two float arrays. The two
  arrays given as X and Y values have to be at least the length of
  <i>n</i>. All values beyond <i>n</i> will be ignored. The style is
  determined by what is described in the current LineStyle.

  \sa setLineParameters()
  \param n number of points
  \param x array of x values
  \param y array of y values
*/
00647 MAGICS_NO_EXPORT void CairoDriver::renderPolyline(const int n, float *x, float *y) const
{
      if(n<2) return;
      cairo_save(cr_);

      float xx = projectX(x[0]);
      float yy = projectY(y[0]);
      cairo_move_to (cr_, xx, yy);

      for(int l = 1; l<n; l++)
      {
            xx = projectX(x[l]);
            yy = projectY(y[l]);
            cairo_line_to (cr_, xx, yy);
      }

      cairo_identity_matrix (cr_);
        cairo_set_line_width (cr_, currentLineWidth_);
      setColour(cr_,currentColour_);
      cairo_stroke (cr_);
      cairo_restore(cr_);
}

/*!
  \brief renders a single line

  This method renders a polyline with two points.The style is
  determined by what is described in the current LineStyle.

  \sa setLineParameters()
  \param n number of points
  \param x array of x values
  \param y array of y values
*/
00681 MAGICS_NO_EXPORT void CairoDriver::renderPolyline2(const int n, float* x, float* y) const
{
      if(n!=2) return;

      cairo_save(cr_);
      cairo_move_to (cr_, x[0], y[0]);
      cairo_line_to (cr_, x[1], y[1]);
      cairo_identity_matrix (cr_);
      cairo_set_line_width (cr_, currentLineWidth_);

      setColour(cr_, currentColour_);
      cairo_stroke (cr_);
      cairo_restore(cr_);
}

/*!
  \brief renders a filled polygon

  This method renders a filled polygon. The style is
  determined by what is described in the current LineStyle.

  \sa setLineParameters()
  \param n number of points
  \param x array of x values
  \param y array of y values
*/
00707 MAGICS_NO_EXPORT void CairoDriver::renderSimplePolygon(const int n, float* x, float* y) const
{
      if(n<3) return;
      cairo_save(cr_);
      setColour(cr_, currentColour_);

      float xx = projectX(x[0]);
      float yy = projectY(y[0]);
      cairo_move_to (cr_, xx, yy);

      for(int l = 1; l<n; l++)
      {
            xx = projectX(x[l]);
            yy = projectY(y[l]);
            cairo_line_to (cr_, xx, yy);
      }

      cairo_close_path (cr_);

#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 2, 0)
      if (currentShading_==M_SH_DOT)
      {
            const DotShadingProperties *pro = (DotShadingProperties*)currentShadingProperties_;
            const int density = (int)(100./pro->density_);
            int s = (int)pro->size_*3;

            cairo_surface_t *pat_surface;
            cairo_pattern_t *pattern;
            cairo_t *cr2;

            pat_surface = cairo_surface_create_similar(cairo_get_group_target(cr_),CAIRO_CONTENT_COLOR_ALPHA, density, density);
            cr2 = cairo_create (pat_surface);
            cairo_surface_destroy (pat_surface);

            setColour(cr2, currentColour_);
            const float off = (density-s)*.5;
            cairo_rectangle (cr2, off, off, off+s, off+s);
            cairo_fill (cr2);

            pattern = cairo_pattern_create_for_surface (cairo_get_target (cr2));
            cairo_destroy (cr2);

            cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);

            cairo_set_source (cr_, pattern);
            cairo_fill (cr_);

            cairo_pattern_destroy(pattern);
      }
      else if (currentShading_==M_SH_HATCH)
      {
            const HatchShadingProperties *pro = (HatchShadingProperties*)currentShadingProperties_;
            indexHatch_ = pro->index_;
            const int density = (int)(1./pro->density_*150);

            cairo_surface_t *pat_surface;
            cairo_pattern_t *pattern;
            cairo_t *cr2;

            pat_surface = cairo_surface_create_similar(cairo_get_group_target(cr_),CAIRO_CONTENT_COLOR_ALPHA, density, density);
            cr2 = cairo_create (pat_surface);
            cairo_surface_destroy (pat_surface);

            setColour(cr2, currentColour_);
            if(indexHatch_==1 || indexHatch_==3) // horizontal
            {
                  cairo_move_to(cr2,       0, density*.5+.5);
                  cairo_line_to(cr2, density+.5, density*.5+.5);
            }
            if(indexHatch_==2 || indexHatch_==3) // vertical
            {
                  cairo_move_to(cr2, density+.5*.5, 0);
                  cairo_line_to(cr2, density+.5*.5, density+.5);
            }
            if(indexHatch_==4 || indexHatch_==6) 
            {
                  cairo_move_to(cr2,       0, 0);
                  cairo_line_to(cr2, density+.5, density+.5);
            }
            if(indexHatch_==5 || indexHatch_==6)
            {
                  cairo_move_to(cr2, density+.5, 0);
                  cairo_line_to(cr2,       0, density+.5);
            }
                cairo_identity_matrix (cr_);
            cairo_set_line_width (cr_,pro->thickness_);
            cairo_stroke(cr2);

            pattern = cairo_pattern_create_for_surface (cairo_get_target (cr2));
            cairo_destroy (cr2);

            cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);

            cairo_set_source (cr_, pattern);
            cairo_fill (cr_);

            cairo_pattern_destroy(pattern);
      }
      else
#else
                if (currentShading_==M_SH_HATCH || currentShading_==M_SH_DOT)
                    Log::error() << "CairoDriver: For hatch and dot shading you need at least Cairo 1.2!\n"
                                 << "             Solid shading used instead."<<endl;
#endif
      {
            cairo_set_line_width(cr_, 0);  // otherwise shading is too thick!!!
            cairo_fill(cr_);
      }
      cairo_restore(cr_);
}

/*!
  \brief renders text strings

  Cairo expects a string as a char array, where each character is expressed as
  16 bit Unicode. Expat however delivers Multi-Byte encoding!

  \sa Text
  \param text object containing the strings and their description
*/
00827 MAGICS_NO_EXPORT void CairoDriver::renderText(const Text& text) const
{
      if(text.empty()) return;
      const vector<NiceText>& niceT = text.getNiceText();
      if(niceT.empty()) return;

//    cairo_save(cr_);

      double xxx = projectX(text[0].x());
      double yyy = projectY(text[0].y());

      enum Justification horizontal = text.getJustification();
      const enum VerticalAlign vertical = text.getVerticalAlign();

      vector<NiceText>::const_iterator niceText = text.textBegin();
      vector<NiceText>::const_iterator niceTextEnd = text.textEnd();

      float textXoffset = 0.;
      ostringstream alltext;

      for(;niceText<niceTextEnd;niceText++)
      {
            MagFont magfont = (*niceText).font();
            const std::set<string>& styles = magfont.styles();

            setNewColour(magfont.colour());
            const int r = (int)(currentColour_.red()*255.);
            const int g = (int)(currentColour_.green()*255.);
            const int b = (int)(currentColour_.blue()*255.);
            ostringstream col;
                  col <<hex <<"#";
                  if(r>15)    col <<r;
                  else        col <<"0"<< r;
                  if(g>15)    col <<g;
                  else        col <<"0"<< g;
                  if(b>15)    col <<b;
                  else        col <<"0"<< b;
            const string t=(*niceText).text();
            alltext << "<span";
            alltext << " color=\""<<col.str()<<"\" font_family=\""<<magfont.name()<<"\" size=\""<<int(magfont.size()*FONT_SCALE*1024)<<"\"";
                if(styles.find("bold")    != styles.end())  alltext << " weight=\"bold\"";
                if(styles.find("italic")  != styles.end())  alltext << " style=\"italic\"";
                if(text.getBlanking())                      alltext << " background=\"#FFFFFF\"";
                if(styles.find("underlined") != styles.end()) alltext << " underline=\"single\"";
            if((*niceText).elevation()==SUPERSCRIPT)    alltext << "><sup";
            else if((*niceText).elevation()==SUBSCRIPT) alltext << "><sub";

            alltext << ">"<<t;

            if((*niceText).elevation()==SUPERSCRIPT)    alltext << "</sup>";
            else if((*niceText).elevation()==SUBSCRIPT) alltext << "</sub>";

            alltext <<"</span>";
      }

      const string alltextstring = alltext.str();

      const char *glyphs = alltextstring.c_str();
      const size_t len   = alltextstring.length();

      GError* pError = 0;
      PangoAttrList *pAttrList;
      char * pText = 0;

      pango_parse_markup(glyphs, len, 0, &pAttrList, &pText, NULL, &pError);
      if(pError)
      {
        Log::warning() << "PANGO: " <<pError->message<<"<"<< endl;
//      free(pError);
      }

      PangoLayout *layout = pango_cairo_create_layout(cr_);
      pango_layout_set_text (layout, pText, -1);
      pango_layout_set_attributes(layout, pAttrList);

//    PangoFontDescription *desc = pango_font_description_from_string(font_string.str().c_str());
//    pango_layout_set_font_description (layout, desc);
//    pango_font_description_free(desc);

      int w, h;
      pango_layout_get_size (layout, &w, &h);
      double width  = w / PANGO_SCALE;
      double height = h / PANGO_SCALE;

      float x = 0;
      if(horizontal == MCENTRE)     x = 0.5 + width*.5;
      else if(horizontal == MRIGHT) x = width;

      float y = 0.;
      if (vertical==MBASE)        { y = height*.15;}
      else if (vertical==MTOP)    { y = 0;         }
      else if (vertical==MHALF)   { y = height*.5; }
      else if (vertical==MBOTTOM) { y = height;    }

      //  T E X T co-ordinates
      //  --> start always bottom left
      //

      unsigned int noTexts = text.size();
      for(unsigned int nT=0;nT<noTexts;nT++)  // for all string COORDINATES
      {
        cairo_save(cr_);
        xxx = projectX(text[nT].x());
        yyy = projectY(text[nT].y());

        cairo_move_to (cr_, xxx-x+textXoffset, yyy-y);
          cairo_rotate (cr_, text.getAngle());
        pango_cairo_update_layout (cr_, layout);
        pango_cairo_show_layout (cr_, layout);
        cairo_restore(cr_);
      }

//    cairo_restore(cr_);
}

/*!
  \brief drawing a circle

  This method renders given text strings.

  The meaning of the last parameter <i>s</i> is as follows:
     - 0-8 determines how many quarters of the circle are filled. Starting from the top clock-wise.
     - 9 fills the whole circle but leaves a vertical bar empty in the middle of the circle.

  \todo check if this is right and correct colour in other drivers for case fill = 9!!!

  \param x X Position
  \param y Y Position
  \param r Radius of circle
  \param s Style which determines how the circle is shaded
*/
00958 MAGICS_NO_EXPORT void CairoDriver::circle(const float x, const float y, const float r, const int s) const
{
#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 2, 0)
      const float xx = projectX(x);
      const float yy = projectY(y);

      cairo_save(cr_);
      cairo_new_sub_path(cr_);

//    cairo_identity_matrix (cr_);
      cairo_set_line_width (cr_, currentLineWidth_);
      setColour(cr_, currentColour_);

      int fill = s;

      if(s > 8) fill = 8;
      if(s > 0)
      {
            cairo_arc (cr_, xx, yy, r, -M_PI * .5, M_PI * ((0.25 * fill)-.5) );
            cairo_fill(cr_);
      }

      if(fill == 9)
      {
            cairo_set_source_rgb(cr_,1,1,1);
            cairo_move_to (cr_, xx, yy+r-1);
            cairo_line_to (cr_, xx, yy-r+1);
              cairo_stroke(cr_);
            setColour(cr_, currentColour_);
      }

      cairo_arc (cr_, xx, yy, r, 0., M_PI * 2.);
        cairo_stroke(cr_);
      cairo_restore(cr_);
#else
        Log::warning() << "CairoDriver::circle requires at least cairo version 1.2!" << endl;
#endif
}

/*!
  \brief render pixmaps

  This method renders pixmaps. These are used for cell shading and raster input (GIFs and PNGs).

  \note Normally implemented in BaseDriver
  \sa renderCellArray()

  \param x0 x of lower corner
  \param y0 y of lower corner
  \param x1 x of higher corner
  \param y1 y of higher corner
  \param w width of pixmap
  \param h height of pixmap
  \param pixmap contents
  \param alpha transparency
*/
01014 MAGICS_NO_EXPORT bool CairoDriver::renderPixmap(float x0,float y0,float x1,float y1,
                  int w,int h,unsigned char* pixmap,int,bool alpha) const
{
#ifdef DRIVERS_DEBUG_
      cout << "CD:renderPixmap> "<<w<<"x"<<h << endl;
#endif
      unsigned char *p = pixmap;
      const float dx =  (x1 - x0)/w;
      const float dy = -(y1 - y0)/h;   // Minus needed for Y axis correction

      const float X0 = x0;
      const float Y0 = y0;
      float a=0;

      cairo_save(cr_);
      cairo_antialias_t t = cairo_get_antialias(cr_);
      cairo_set_antialias(cr_, CAIRO_ANTIALIAS_NONE);
      for(int i=h-1;i>=0;i--)
      {
            for(int j=0;j<w; x0+=dx,j++)
            {
                  const float r = *(p++);
                  const float g = *(p++);
                  const float b = *(p++);
                  if(alpha) a = *(p++);

                  if( (r*g*b) > 0)
                  {
                        if (!alpha) cairo_set_source_rgb(cr_,r,g,b);
                        else        cairo_set_source_rgba(cr_,r,g,b,a);
                  }
                  else
                        cairo_set_source_rgba(cr_,1,1,1,0);


                  const float x0 = X0+(j*dx);
                  const float y0 = Y0+(i*dy);
                  cairo_rectangle (cr_, x0,y0,dx,-dy);
                  cairo_stroke_preserve(cr_);
                  cairo_fill (cr_);
            }
            x0 = X0;
            y0 += dy;
      }
      cairo_restore(cr_);
      cairo_set_antialias(cr_, t);
      return true;
}

/*!
  \brief Image render method for ALL drivers.

  This method should be used by all Magics++ drivers to render image objects.
*/
01068 MAGICS_NO_EXPORT void CairoDriver::renderImage(const ImportObject& obj) const
{
      std::string file = obj.getPath();

      if(!magCompare(obj.getFormat(),"png"))
      {
            std::string cmd = "convert "+file+"[1] ___magics_cairo_temp.png";
            Log::info() <<"CairoDriver::renderImage calling convert ... with: " <<cmd << endl;
            int status = system(cmd.c_str());
            if(status)
            {
                  Log::error() << "\nCairoDriver: Command exit not zero - NO PNG produced!\n"
                               << " COMMAND: "<<cmd<<"\n"<< endl;
                  return;
            }
            file="___magics_cairo_temp.png";
      }

      cairo_surface_t *image = cairo_image_surface_create_from_png(file.c_str());

      if(image)
      {
            cairo_save(cr_);
            int w = cairo_image_surface_get_width(image);
            int h = cairo_image_surface_get_height(image);

            const float x = projectX(obj.getOrigin().x());
            const float y = projectY(obj.getOrigin().y());
            const float oh = projectY(obj.getOrigin().y()+obj.getHeight()) - y;
            const float ow = projectX(obj.getOrigin().x()+obj.getWidth())  - x;

            cairo_translate (cr_, x, y+oh);
            cairo_scale (cr_, ow/w, -oh/h);

            cairo_set_source_surface(cr_, image, 0, 0);
            cairo_paint(cr_);

            cairo_surface_destroy (image);
            cairo_restore(cr_);
            if(magCompare(file,"___magics_cairo_temp.png")) remove("___magics_cairo_temp.png");
      }
      else Log::warning() << "CairoDriver-> Could NOT read the image file "<< file << " !" << endl;
}


/*!
  \brief render cell arrays

  This method renders cell arrays, also called images in Magics language. These are
  mainly used for satellite data.

  \sa renderPixmap()

  \param image Object containing an image
*/
01123 MAGICS_NO_EXPORT bool CairoDriver::renderCellArray(const Image& image) const
{
#ifdef DRIVERS_DEBUG_
      cout << "CD:renderCellArray> "<<image.getWidth()<<"x"<<image.getHeight() << endl;
#endif

      ColourTable &lt = image.getColourTable();
      const int width  = image.getNumberOfColumns();
      const int height = image.getNumberOfRows();
      const float x0 = projectX(image.getOrigin().x());
      const float y0 = projectY(image.getOrigin().y());
      const float x1 = projectX(image.getOrigin().x()+image.getWidth());
      const float y1 = projectY(image.getOrigin().y()+image.getHeight());
      const float dx = (x1-x0)/width;
      const float dy = -(y1-y0)/height;

      cairo_save(cr_);
      cairo_antialias_t t = cairo_get_antialias(cr_);
      cairo_set_antialias(cr_, CAIRO_ANTIALIAS_NONE);
      for (int i=height-1;i>=0;i--)
      {
            for(int j=0;j<width; j++)
            {
              const long in = width*i+j;
              const short c = image[in];

              setColour(cr_,Colour(lt[c].red(),lt[c].green(),lt[c].blue(),lt[c].alpha()));
              const float wx = x0+(j*dx);
              const float wy = y0+(i*dy);
              cairo_rectangle (cr_,wx,wy,dx,dy);
              cairo_stroke_preserve(cr_);
              cairo_fill (cr_);
            }
      }
      cairo_restore(cr_);
      cairo_set_antialias(cr_, t);
      return true;
}


/*!
  \brief prints debug output

  When Magics++ is compiled in debug mode these extra strings are printed.

  \note This can increase file and log file sizes if you run Magics++ in debug mode!

  \param s string to be printed
*/
01172 MAGICS_NO_EXPORT void CairoDriver::debugOutput(const string &s) const
{
      Log::debug() << s << endl;
}

/*!
  \brief class information are given to the output-stream
*/
01180 void CairoDriver::print(ostream& out)  const
{
      out << "CairoDriver[";
      out << "]";
}

//! Method to plot symbols
/*!
 Needs special treatment of Logo.
*/
01190 MAGICS_NO_EXPORT void CairoDriver::renderSymbols(const Symbol& symbol) const
{
      debugOutput("Start CairoDriver Symbols");
/*
      if(symbol.getSymbol()=="logo_ecmwf")
      {
            const string logofile = getEnvVariable("MAGPLUS_HOME") + MAGPLUS_PATH_TO_SHARE_ + "ecmwf_logo.png";
            cairo_surface_t *image = cairo_image_surface_create_from_png(logofile.c_str());

            if(image)
            {
                  cairo_save(cr_);
                  int w = cairo_image_surface_get_width(image);
                  int h = cairo_image_surface_get_height(image);

                  cairo_translate (cr_, projectX(symbol[0].x()), projectY(symbol[0].y()));
                  const float scaling = convertCM(symbol.getHeight()*.5) / coordRatioY_;
//                cairo_scale (cr_, 0.3, 0.3);
                  cairo_set_source_surface(cr_, image, w*scaling, h*scaling);
                  cairo_paint(cr_);

                  cairo_surface_destroy (image);
                  cairo_restore(cr_);
            }
            else Log::warning() << "CairoDriver-> Could NOT read the logo file "<< logofile << " !" << endl;
      }
//    else if(symbol.getSymbol().compare(0,7,"magics_")==0 )
      else
*/    {
            BaseDriver::renderSymbols(symbol);
      }
}

01223 MAGICS_NO_EXPORT bool CairoDriver::convertToPixmap(const string &fname, const GraphicsFormat format, const int reso,
                   const float wx0, const float wy0,const float wx1,const float wy1) const
{
      if(format==PNG)
      {
            cairo_save(cr_);
            cairo_surface_t *image = cairo_image_surface_create_from_png (fname.c_str());
            int w = cairo_image_surface_get_width (image);
            int h = cairo_image_surface_get_height (image);

            cairo_translate (cr_, wx0, wy0);
            cairo_scale  (cr_, (wx1-wx0)/w, -(wy1-wy0)/h);
//          cairo_translate (cr_, -0.5*w, -0.5*h);

            cairo_set_source_surface (cr_, image, 0, 0);
            cairo_paint (cr_);

            cairo_surface_destroy (image);
            cairo_restore(cr_);
            return true;
      }
      else
            return BaseDriver::convertToPixmap(fname, format, reso, wx0, wy0, wx1, wy1);
}

static SimpleObjectMaker<CairoDriver, BaseDriver> Cairo_driver("Cairo");

Generated by  Doxygen 1.6.0   Back to index