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

PostScriptDriver.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 ********************************/

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

    Started: March 2004

*/

#include <PostScriptDriver.h>
#include <Polyline.h>
#include <Text.h>
#include <Image.h>
#include <System.h>
#include <iomanip>

/*! \brief function to convert between PS ISO encoding and Unicode

See <a href="http://www.w3.org/TR/REC-html40/sgml/entities.html">entities.html</a> for
a description of the characters.
\sa TextNode.cc
\note C O P I E D   F R O M  TEXTNODE.CC
*/
00043 string specialPS(const string& sp)
{
      static map<string, string> specialsPS;
      if ( specialsPS.empty() )
      {
            specialsPS["169"] = "251"; //copyright
            specialsPS["176"] = "260"; //degres
            specialsPS["956"] = "265"; //mu
            specialsPS["188"] = "274"; //vulgar fraction one quarter
            specialsPS["189"] = "275"; //vulgar fraction one half
            specialsPS["190"] = "276"; //vulgar fraction three quarters
            specialsPS["191"] = "277"; //inverted question mark
            specialsPS["192"] = "300"; //latin capital letter A with grave
            specialsPS["193"] = "301"; //latin capital letter A with acute

            specialsPS["195"] = "303"; //latin capital letter A with tilde
            specialsPS["196"] = "304"; //latin capital letter A with  diaeresis
            specialsPS["197"] = "305"; //latin capital letter A with ring above
            specialsPS["198"] = "306"; //latin capital letter AE
            specialsPS["199"] = "307"; //latin capital letter C with cedilla

            specialsPS["200"] = "310"; //latin capital letter E with grave
            specialsPS["201"] = "311"; //latin capital letter E with acute
            specialsPS["202"] = "312"; //latin capital letter E with circumflex
            specialsPS["203"] = "313"; //latin capital letter E with  diaeresis

            specialsPS["204"] = "314"; //latin capital letter I with grave
            specialsPS["205"] = "315"; //latin capital letter I with acute
            specialsPS["206"] = "316"; //latin capital letter I with circumflex
            specialsPS["207"] = "317"; //latin capital letter I with  diaeresis

            specialsPS["209"] = "321"; //latin capital letter N with tilde

            specialsPS["210"] = "322"; //latin capital letter O with grave
            specialsPS["211"] = "323"; //latin capital letter O with acute
            specialsPS["214"] = "325"; //latin capital letter O with  diaeresis
            specialsPS["216"] = "330"; //latin capital letter O slash

            specialsPS["217"] = "331"; //latin capital letter U with grave
            specialsPS["218"] = "332"; //latin capital letter U with acute
            specialsPS["219"] = "333"; //latin capital letter U with circumflex
            specialsPS["220"] = "334"; //latin capital letter U with  diaeresis

            specialsPS["221"] = "335"; //latin capital letter Y with acute

            specialsPS["222"] = "336"; //latin capital letter THORN
            specialsPS["223"] = "337"; //latin small letter sharp s = ess-zed

            specialsPS["224"] = "340"; //latin small letter a with grave
            specialsPS["225"] = "341"; //latin small letter a with cute
            specialsPS["226"] = "342"; //latin small letter a with circumflex
            specialsPS["227"] = "343"; //latin small letter a with tilde
            specialsPS["228"] = "344"; //latin small letter a with  diaeresis
            specialsPS["229"] = "345"; //latin small letter a with ring above
            specialsPS["230"] = "346"; //latin small letter ae
            specialsPS["231"] = "347"; //latin small letter c with cedilla

            specialsPS["232"] = "350"; //latin small letter e with grave
            specialsPS["233"] = "351"; //latin small letter e with cute
            specialsPS["234"] = "352"; //latin small letter e with circumflex
            specialsPS["235"] = "353"; //latin small letter e with ring above

            specialsPS["236"] = "354"; //latin small letter i with grave
            specialsPS["237"] = "355"; //latin small letter i with cute
            specialsPS["238"] = "356"; //latin small letter i with circumflex
            specialsPS["239"] = "357"; //latin small letter i with diaeresis

            specialsPS["241"] = "361"; //latin small letter n with tilde

            specialsPS["242"] = "362"; //latin small letter o with grave
            specialsPS["243"] = "363"; //latin small letter o with cute
            specialsPS["244"] = "364"; //latin small letter o with diaeresis
            specialsPS["245"] = "365"; //latin small letter o with tilde

            specialsPS["248"] = "370"; //latin small letter o slash

            specialsPS["249"] = "371"; //latin small letter u with grave
            specialsPS["250"] = "372"; //latin small letter u with cute
            specialsPS["251"] = "373"; //latin small letter u with circumflex
            specialsPS["252"] = "374"; //latin small letter u with diaeresis

            specialsPS["253"] = "375"; //latin small letter y with acute

            specialsPS["254"] = "376"; //latin small letter THORN
            specialsPS["255"] = "377"; //latin small letter y with diaeresis
      }

      const map<string, string>::iterator f = specialsPS.find(sp);
      return( f == specialsPS.end() ) ? sp : f->second;
}


using namespace magics;

/*!
  \brief Constructor

The PostScript driver produces one or more (if split is activated) text files
in PostScript format.

*/
00144 PostScriptDriver::PostScriptDriver() : ps_(true),pdf_(false),eps_(false),old_(true),
                        maxPathSize_(200),deviceColourModel_(1)
{
}

/*!
  \brief Destructor
*/
00152 PostScriptDriver::~PostScriptDriver()
{
}

/*!
  \brief Opening the driver
*/
00159 void PostScriptDriver::open()
{
      currentPage_ = 0;
      setCMscale(getResolution()/2.54);// cm -> pixel
      if(!isSplit()) openFile();
}

/*!
  \brief Closing the driver
*/
00169 void PostScriptDriver::close()
{
      if(!isSplit()) closeFile();

      currentPage_ = 0; // reset values for new pages ...
      coordRatioX_ = 1;
      coordRatioY_ = 1;
}

/*!
  \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. Strongly
  depends on what output was selected (split or eps)
*/
00185 MAGICS_NO_EXPORT void PostScriptDriver::startPage() const
{
      dimensionX_ = convertCM(getXDeviceLength()); // 72   = points / inch
      dimensionY_ = convertCM(getYDeviceLength()); // 2.54 = cm / inch

      newPage_ = true;
//    if(currentPage_ > 0) endPage();
      if(isSplit()) openFile();
      currentPage_++;
      fstream *ps = getStream();

      if(!isSplit()) *ps << "%%Page: " << currentPage_ << " " << currentPage_ << "\n";
      else *ps << "%%Page: 1 1\n";

      // Here the whole page gets scaled to the resolution!
      *ps << "gs " << 72./getResolution() << " dup s ";
if(old_)
{
      if( ( isEPS() && (getXDeviceLength() < getYDeviceLength()) ) ||
          (!isEPS() && (getXDeviceLength() > getYDeviceLength()) ) )
            *ps << static_cast<int>(dimensionY_) << " 0 t 90 ro ";
}
      *ps << "1 lw [] 0 sd ";

      setDeviceColourModel(getColour_model());

        *ps << "2 setlinejoin 0 1 SUP 0 10 SF 0 SHA 0 SVA\n"; // MITER is now always on
      currentColour_ = Colour("none");
}

/*!
  \brief ending a page

  This method has to take care that for formats with multiple output
  files are closed.
*/
00221 MAGICS_NO_EXPORT void PostScriptDriver::endPage() const
{
      fstream *ps = getStream();
      *ps << "S\n";
      debugOutput("End of page");
      if(isSplit()) closeFile();
}

/*!
  \brief project to a new Layout

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

  \sa Layout
*/
00236 MAGICS_NO_EXPORT void PostScriptDriver::project(const magics::Layout& layout) const
{
      debugOutput("Begin layout "+layout.name());

      Log::debug() << "---> PROJECT (PS) " << layout.name() <<  endl;

      float scale = 1.0;
      if(newPage_) {scale = getScale();newPage_=false;}

      dimensionStack_.push(dimensionX_);
      dimensionStack_.push(dimensionY_);
      scalesX_.push(coordRatioX_);
      scalesY_.push(coordRatioY_);

      coordRatioX_ *= scale;
      coordRatioY_ *= scale;

      float offsetX_ = layout.x()     * 0.01 * dimensionX_;
      float offsetY_ = layout.y()     * 0.01 * dimensionY_;
      dimensionX_ =    layout.width() * 0.01 * dimensionX_;
      dimensionY_ =    layout.height()* 0.01 * dimensionY_;

      const float sumX = layout.maxX() - layout.minX();
      const float sumY = layout.maxY() - layout.minY();

//    const float sc = (1.-scale) * .5;
      if( sumX!=0 && sumY!=0 )
      {
            coordRatioX_ = (dimensionX_/sumX);
            coordRatioY_ = (dimensionY_/sumY);
      }

      const float X_ = offsetX_+projectX( -layout.minX());
      const float Y_ = offsetY_+projectY( -layout.minY());

      fstream *ps = getStream();
      *ps   << "gs";
//    if(fabs(X_) > 0.0001 && fabs(Y_) > 0.0001 )
            *ps<<" "<< X_ <<" "<< Y_ <<" t";
      *ps   <<"\n";

}

/*!
  \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.

*/
00286 MAGICS_NO_EXPORT void PostScriptDriver::unproject() const
{
      dimensionY_ = dimensionStack_.top();dimensionStack_.pop();
      dimensionX_ = dimensionStack_.top();dimensionStack_.pop();
      coordRatioX_  = scalesX_.top();scalesX_.pop();
      coordRatioY_  = scalesY_.top();scalesY_.pop();
      
      fstream *ps = getStream();
      *ps << "gr\n";
      setLineParameters(M_SOLID, 1);
      debugOutput("End layout");
}


/*!
  \brief sets a new colour

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

  \sa Colour
*/
00308 MAGICS_NO_EXPORT void PostScriptDriver::setNewColour(const Colour &colour) const
{
      if(currentColour_ == colour) return;
      currentColour_ = colour;

      float c,m,y,k,gray;
      const float r = colour.red();
      const float g = colour.green();
      const float b = colour.blue();

      fstream *ps = getStream();
      streamsize ss = ps->precision(2);
      switch(getDeviceColourModel())
      {
            case 0:    // rgb
                  *ps << r << " " << g << " " << b << " C\n";
                  break;
            case 1:    // CMYK
                  c = 1. - r;
                  m = 1. - g;
                  y = 1. - b;
                  k = (c < m) ? c : m;
                  k = (y < k) ? y : k;
                  if( k == 1. )
                        *ps << "0 0 0 1 Y\n";
                  else
                  {
                        c = (c - k) / (1.-k);
                        m = (m - k) / (1.-k);
                        y = (y - k) / (1.-k);
                        *ps << c << " " << m << " " << y << " " << k << " Y\n";
                  }
                  break;
            case 2 :     // monochrome - RGB
                  if ( (r ==1.) && (g == 1.) && ( b ==1. ))
                        *ps << "1 1 1 C\n";
                  else
                        *ps << "0 0 0 C\n";
                  break;
            case 3:     // RGB gray
                  gray = 0.3*r + 0.59*g + 0.11*b;
                  *ps << gray << " " << gray << " " << gray << " C\n";
                  break;
            case 4 :    // monochrome - CMYK
                  if ( (r ==1.) && (g == 1.) && ( b ==1. ))
                        *ps << "0 0 0 0 Y\n";
                  else
                        *ps << "0 0 0 1 Y\n";
                  break;
            case 5:    // CMYK gray
                  gray = 0.3*r + 0.59*g + 0.11*b;
                  *ps << "0 0 0 " << gray << " Y\n";
                  break;
            default:   // RGB
                  *ps << r << " " << g << " " << b << " C\n";
                  break;
      }// end switch
      ps->precision(ss);
}

/*!
  \brief sets a new line width

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

  \sa setLineParameters()
*/
00376 MAGICS_NO_EXPORT void PostScriptDriver::setNewLineWidth(const float width) const
{
      if(currentLineWidth_ == width) return;
      currentLineWidth_ = width;

      fstream *ps = getStream();
      *ps << currentLineWidth_<< " lw\n";
}

/*!
  \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

*/
00397 MAGICS_NO_EXPORT int PostScriptDriver::setLineParameters(const LineStyle linestyle, const float w) const
{
      setNewLineWidth(w);
      if(currentLineType_ == linestyle) return 0;
      currentLineType_ = linestyle;

      fstream *ps = getStream();

      const int width = (int)(currentLineWidth_+.5);
      const int sw = (currentLineWidth_ > 2.) ? 1 : 0;

      switch(currentLineType_)
      {
            case M_SOLID:
                  *ps << "[] 0 sd\n";
                  break;
            case M_DASH:
                  *ps << "["  <<
                        (( sw ) ?  4*width : 16 ) << " " <<
                        (( sw ) ?  1*width :  8 ) << "] 8 sd\n";
                  break;
            case M_DOT:
                  *ps << "[" <<
                        (( sw ) ? width : 4 ) << " " <<
                        (( sw ) ? width : 8 ) << "] 4 sd\n";
                  break;
            case M_CHAIN_DASH:
                  *ps << "[" <<
                        ( (sw) ? width*4 : 16 ) << " " <<
                        ( (sw) ? width*1 :  8 ) << " " <<
                        ( (sw) ? width*1 :  4 ) << " " <<
                        ( (sw) ? width*1 :  8 ) << " "
                        << "] 0 sd\n";
                  break;
            case M_CHAIN_DOT:
                  *ps << "[" <<
                        ( (sw) ? width*4 : 12 ) << " " <<
                        ( (sw) ? width*1 :  8 ) << " " <<
                        ( (sw) ? width*1 :  4 ) << " " <<
                        ( (sw) ? width*1 :  8 ) << " " <<
                        ( (sw) ? width*1 :  4 ) << " " <<
                        ( (sw) ? width*1 :  8 ) << " "
                        << "] 0 sd\n";
                  break;
            default:
                  *ps << "[] 0 sd\n";
                  break;
      }// end switch
      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
*/
00461 MAGICS_NO_EXPORT void PostScriptDriver::renderPolyline(const int n, float *x, float *y) const
{
      if(n < 2 || (currentColour_==Colour("NONE"))) return;

      float *xx = x;
      float *yy = y;

      std::fstream *ps = getStream();

      if(n == 2)
      {
            const float ix0 = projectX(*xx);
            xx++;
            const float iy0 = projectY(*yy);
            yy++;
            const float dx0 = projectX(*xx)-ix0;
            const float dy0 = projectY(*yy)-iy0;
            if( zero(dx0)&&zero(dy0) ) return;
            *ps << dx0 << " " << dy0 << " " << ix0 << " " << iy0 <<" B\n";
      }
      else
      {
            int nn = n;
            float *dx,*dy;
            streamsize ss = ps->precision(2);

            while(nn>1)
            {
                  unsigned int p = ( nn > int(maxPathSize_)) ? maxPathSize_ : nn;
                  dx = new float[p+1];
                  dy = new float[p+1];

                  float kx = projectX(*xx);
                  xx++;
                  float ky = projectY(*yy);
                  yy++;

                  *(dx++) = kx;
                  *(dy++) = ky;

                  unsigned int i;
                  for(i=1; i<p; i++)
                  {
                        const float cx = projectX(*xx);xx++;
                        *(dx++) = cx - kx;
                        kx = cx;
                        const float cy = projectY(*yy);yy++;
                        *(dy++) = cy - ky;
                        ky = cy;
                  }

                  int counter = 0; // to avoid to long lines
                  for(i=p-1; i>0; i--)
                  {
                        const float ddx = *(--dx);
                        const float ddy = *(--dy);

                        if( !(zero(ddx) && zero(ddy)) )
                              *ps << ddx << " " << ddy<< " ";
                        else
                              p--;

                        if(counter>15) {*ps << "\n";counter=0;}
                        else counter++;
                  }
                  --dx;--dy;

                  if(p>1) *ps << p-1 << " " << *dx << " " << *dy << " p\n";

                  nn = nn - maxPathSize_;
                  if(++nn>1)
                  {
                        // Compensate for additional point (last becomes first)
                        --xx;--yy;
                  }
                  delete [] dx;
                  delete [] dy;
            }// end while
            ps->precision(ss);
      }
}


/*!
  \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
*/
00555 MAGICS_NO_EXPORT void PostScriptDriver::renderPolyline2(const int n, float* x, float* y) const
{
      if(n != 2 || (currentColour_==Colour("NONE"))) return;

      float *xx = x;
      float *yy = y;

      std::fstream *ps = getStream();

      const float ix0 = *xx;
      xx++;
      const float iy0 = *yy;
      yy++;
      const float dx0 = *xx-ix0;
      const float dy0 = *yy-iy0;
      if( zero(dx0)&&zero(dy0) ) return;
      *ps << dx0 << " " << dy0 << " " << ix0 << " " << iy0 <<" B\n";
}

/*!
  \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
*/
00585 MAGICS_NO_EXPORT void PostScriptDriver::renderSimplePolygon(const int n, float* x, float* y) const
{
      if(n<3 || (currentColour_==Colour("NONE"))) return;

//Log::dev()<< "PS_SIMPLE " << n<<" points   col: "<< currentColour_<< endl;

      int nn = n;
      if ( (x[nn-1] == x[0]) && (y[nn-1] == y[0]) ) nn--;

      const int N = nn+1;
      float *rx = new float[N];
      float *ry = new float[N];
      float *dx = rx; *(dx++);
      float *dy = ry; *(dy++);
      float *xx = x, *yy = y;
      float fx = projectX(*(xx++));
      float fy = projectY(*(yy++));

      int i;
      for( i = 1; i<nn; i++)
      {
            const float pxx = projectX(*xx);
            const float pyy = projectY(*yy);
            *(dx++) = pxx - fx; fx = pxx; xx++;
            *(dy++) = pyy - fy; fy = pyy; yy++;
      }

      std::fstream *ps = getStream();
      *ps << "gs\n";

      if (currentShading_==M_SH_DOT)
      {
            const DotShadingProperties *pro = (DotShadingProperties*)currentShadingProperties_;
            const int density = (int)(pro->density_*.4);
            if(density<0) return;

            int s = (int)pro->size_*100;
            if (s<2) s = 2;

            const float r = currentColour_.red();
            const float g = currentColour_.green();
            const float b = currentColour_.blue();

            float c = 1. - r;
            float m = 1. - g;
            float z = 1. - b;
            float k = (c < m) ? c : m;
                  k = (z < k) ? z : k;

            *ps
                << "/Pat {\n gs 0 0 " << s+density << " " << s+density << " rectclip gr gs ";
                if( k == 1. )
                  *ps << "0 0 0 1";
                else
                {
                 c = (c - k) / (1.-k);
                 m = (m - k) / (1.-k);
                 z = (z - k) / (1.-k);
                 *ps << c << " " << m << " " << z << " " << k;
                }
            *ps << " setcmykcolor 1 1 m 0 "<<s<<" rl "<<s<<" 0 rl 0 -"<<s<<" rl cp fill gr "
                << "} bind def\n"
                << "<< /PatternType 1 /PaintType 1 /TilingType 1\n"
                << "/BBox [0 0 "<<s+2<<" "<<s+2<<"] /XStep "<<s+2+density<<" /YStep "<<s+2+density<<"\n"
                << "/PaintProc { Pat }\n"
                << ">>\n"
                << "matrix makepattern setpattern\n";
      }
      else if (currentShading_==M_SH_HATCH)
      {
            const HatchShadingProperties *pro = (HatchShadingProperties*)currentShadingProperties_;
            indexHatch_ = pro->index_;
            const int s = (int)(pro->density_);

            const float r = currentColour_.red();
            const float g = currentColour_.green();
            const float b = currentColour_.blue();

            float c = 1. - r;
            float m = 1. - g;
            float z = 1. - b;
            float k = (c < m) ? c : m;
                  k = (z < k) ? z : k;

            *ps
                << "/Pat {\n gs 0 0 " << s << " " << s << " rectclip gr gs ";
                if( k == 1. )
                  *ps << "0 0 0 1";
                else
                {
                 c = (c - k) / (1.-k);
                 m = (m - k) / (1.-k);
                 z = (z - k) / (1.-k);
                 *ps << c << " " << m << " " << z << " " << k;
                }

            *ps << " setcmykcolor";

            if(indexHatch_==1 || indexHatch_==3) // horizontal
            {
                  *ps  << " 0 "<<s*.5<<" m "<<s<<" 0 rl st";
            }
            if(indexHatch_==2 || indexHatch_==3)
            {
                  *ps  << " "<<s*.5<<" 0 m 0 "<<s<<" rl st";
            }
            if(indexHatch_==4 || indexHatch_==6)
            {
                  *ps  << " 0 0 m "<<s<<" "<<s<<" rl st";
            }
            if(indexHatch_==5 || indexHatch_==6)
            {
                  *ps  << " 0 "<<s<<" m "<<s<<" -"<<s<<" rl st";
            }

            *ps << " gr } bind def\n"
                << "<< /PatternType 1 /PaintType 1 /TilingType 1\n"
                << "/BBox [0 0 "<<s<<" "<<s<<"] /XStep "<<s<<" /YStep "<<s<<"\n"
                << "/PaintProc { Pat }\n"
                << ">>\n"
                << "matrix makepattern setpattern\n";
      }// end hatch

      *dx = projectX(x[0]) - projectX(x[nn-1]);
      *dy = projectY(y[0]) - projectY(y[nn-1]);
      rx[0] = projectX(x[0]);
      ry[0] = projectY(y[0]);

      for ( i=nn; i>0; i--)
      {
            *ps << (*dx--) << " " << *(dy--)<< " ";
      }
      *ps <<nn << " " << rx[0] << " " << ry[0] << " e gr\n";

      delete [] rx;
      delete [] ry;
//    currentShading_=M_SH_SOLID;
}

/*!
  \brief renders text strings

  This method renders given text strings.

  \note As of version 2.0 there are two forms of describing text in Text.
  \todo Underlining of text

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

      fstream *ps = getStream();
      streamsize ss = ps->precision(2);

      *ps << text.getJustification() << " SHA " << text.getVerticalAlign() << " SVA ";

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

      int u=0;
      ostringstream all_text;
      for(;niceText<niceTextEnd;)
      {
            all_text << (*niceText).text();
            niceText++;
            u++;
      }

      niceText = text.textBegin();
      int count = 0;

      for(;niceText<niceTextEnd;)
      {
            const MagFont magfont = (*niceText).font();

            const std::set<string>& styles = magfont.styles();
            string style = "";
            if(styles.find("bold") != styles.end()) style = "bold";
            if(styles.find("italic") != styles.end()) style += "italic";
            if(style == "") style = "normal";
            const string lowFont = lowerCase(magfont.name()+"_"+style);
            fontMapIter iter = FontMap_.find(lowFont);

            const bool underlined = (styles.find("underlined") != styles.end()) ? true : false;

            int font;
            if(iter!=FontMap_.end())
                  font = iter->second.id;
            else
            {
                  font = 0; // if not found get default
                  Log::warning() << "PostScriptDriver: Font "<< lowFont << " is not registered! Default font is used."<< endl;
            }

            const float dheight = convertCM(magfont.size());

            setNewColour(magfont.colour());

            float offset = 0;
            float height = dheight;
//          if((*niceText).elevation()==NORMAL)           {offset = 0;             height = dheight;}
//          else if((*niceText).elevation()==SUPERSCRIPT) {offset = (dheight*0.6); height = dheight*0.5;}
//          else if((*niceText).elevation()==SUBSCRIPT)   {offset = (dheight*0.6); height = dheight*0.5;}

            *ps << font << " " << static_cast<int>(height) << " SF ";

            // plot strings
            string textCommand = (text.getBlanking()) ? "TB" : "T";
            if(underlined) textCommand = "TU";

            const int len = (*niceText).text().length()+1;
            char pp[len];
            strcpy(pp, (*niceText).text().c_str());
            char *p = pp;
            bool specialChar = false;
            ostringstream tmp;
            ostringstream special;

            while(*p)
            {
              if(specialChar)
              {
                  if( *p == ';')
                  {
                        tmp << specialPS(special.str());
                        specialChar=false;
                  }
                  else
                  {
                        special << *p;
                  }
              }
              else if ( *p == '(')    {tmp << "\\(";}
              else if ( *p == ')')    {tmp << "\\)";}
              else if ( *p == '\\')   {tmp << "\\\\";}
              else if ( *p & 0x80)    {p++;tmp << *p;}   // temp fix for multibyte char (degree sign)
              else if ( *p == '&')    {p++;
                                       if(*p)
                                 {
                                    if(*p=='#')
                                    {
                                          tmp << "\\";
                                          specialChar=true;
                                    }
                                    else tmp<<"&"<<*p;
                                 }
                                       else tmp<<"&";}
              else                    {tmp << *p;}
              p++;
            }

        const string showCommand = (underlined) ? "ushow" : "show";
        unsigned int noTexts = text.size();
        for(unsigned int nT=0;nT<noTexts;nT++)  // for all string CO-ORDINATES
        {
            if(niceText == text.textBegin())
            {
                  const float x0 = projectX(text[nT].x());
                  const float y0 = projectY(text[nT].y()) + offset;

                  if(u>1)
                  {
                        *ps <<"gs "<< x0 << " " << y0 << " t ("<< all_text.str() << ") stringwidth pop HA mul VA Height mul moveto "
                            << "("<<tmp.str()<< ") "<<showCommand<<"\n";
                  }
                  else
                  {

      /*
      Log::dev()<< "  PS TEXT >>> " << (*niceText).text()<<" " <<text.getAngle()<< "  ver:";
      if (text.getVerticalAlign()==MBASE)      Log::dev()<< "MBASE";
      else if (text.getVerticalAlign()==MTOP)  Log::dev()<< "MTOP";
      else if (text.getVerticalAlign()==MHALF) Log::dev()<< "MHALF";
      else if (text.getVerticalAlign()==MBOTTOM) Log::dev()<< "MBOTTOM";
      else Log::dev()<< "???";
      cout<<"  x: "<< text[nT].x()<< "   y: "<< text[nT].y()<< endl;
      */
                        const float an = 360.-(text.getAngle()*57.29577951);
                        if(an==0 || an==360)
                              *ps <<"gs "<< x0 << " " << y0 << " t ("<<tmp.str()<< ") 0 0 "<<textCommand<<"\n";
                        else
                              *ps <<"gs "<< x0 << " " << y0 << " t "<<an<< " ro ("<<tmp.str()<< ") 0 0 "<<textCommand<<"\n";
                  }
            }
            else
            {
                  if(offset!=0)
                  {
                        *ps << "gs 0 "<<offset<<" t\n";
                  }
                  *ps << "("<<tmp.str()<< ") "<<showCommand<<"\n";
                  if(offset!=0)
                  {
                        *ps << "gr\n";
                  }
            }
            count++;
            if (niceText+1 == text.textEnd()) *ps <<"gr\n";
         }
         niceText++;
      } // endfor all nicetexts
      ps->precision(ss);
      currentColour_ = Colour("none");
}

/*!
  \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.

  \param x X Position
  \param y Y Position
  \param r Radius of circle
  \param s Style which determines how the circle is shaded
*/
00909 MAGICS_NO_EXPORT void PostScriptDriver::circle(const float x, const float y, const float r, const int s) const
{
      std::fstream *ps = getStream();
      const float cx = projectX(x);
      const float cy = projectY(y);

      if(s < 8)
      {
            *ps << "n " << cx << " " << cy << " " << r << " 0 360 arc st\n";
            if(s > 0)
                  *ps <<"n "<<cx<<" "<<cy<<" m "<<cx<<" "<<cy<<" "<<r<<" 90 "<<90-(s*45)<<" arn\n";
      }
      else *ps << "n " << cx << " " << cy << " " << r << " 0 360 ar\n";
      if(s == 9)
      {
            *ps << "1 1 1 C n "<<cx<<" "<<cy+r-1<<" m 0 "<<-r-r+2<<" rl st\n";
            const Colour col = currentColour_;
            currentColour_ = Colour("white");
            setNewColour(col);
      }
}

/*!
  \brief render pixmaps

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

  \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 landscape says if contents is landscape

*/
00948 MAGICS_NO_EXPORT bool PostScriptDriver::renderPixmap(float x0,float y0,float x1,float y1,
                                            int width,int height,unsigned char* pixmap,int landscape, bool alpha) const
{
      unsigned char *p = pixmap;
      char *t = new char[9];
      std::fstream *ps = getStream();
      const int col_model = getDeviceColourModel();
      const float dx = x1 - x0 + 1;
      const float dy = y1 - y0 + 1;

      if(landscape)
      {      //swop w/h
             const int x = width;
             width = height;
             height = x;
      }

      if(height==0 || width==0)  return false;

      *ps << "gs /pic " << width*(( col_model == 1 ) ? 4 : 3) << " string def " << x0 << " " << y0
          << " t " << dx << " " << dy << " s " << width
          << " " << height << " 8\n"
          << "[" << width << " 0 0 " << height << " 0 0] "
          << "{currentfile pic readhexstring pop}"
          << " false " << (( col_model == 1 ) ? 4 : 3) <<" colorimage\n";

      int nl = 0;
      for(int j=height-1;j>=0;j--)
      {
        for(int i=width-1;i>=0;i--)
        {
            // Get image left-right and bottom-up
            const int n = ( landscape ) ? (height*i+j)*3 : (j*width + width-1-i )*3;
            unsigned char *p2 = p+n;
            unsigned char r = *(p2++);
            unsigned char g = *(p2++);
            unsigned char b = *(p2++);
            if(alpha) *(p2++);                 // ignore alpha values ... :-(
            short kr,kg,kb,kc,km,ky,kk;
            float cc,cm,cy,ck;

            switch ( col_model )
            {
                   case 0:
                         sprintf(t,"%02hx%02hx%02hx",r,g,b);
                         break;
                   case 1:
                         cc = 1. - (r*0.00392156); cm = 1. - (g*0.00392156); cy = 1. - (b*0.00392156);
                         ck = ( cc < cm ) ?  cc : cm;
                         if ( cy < ck ) ck = cy;
                         if( ck == 1. )
                         { kc = 0; km= 0; ky = 0; kk = 255;}
                         else
                         {
                               kc = int( ((cc - ck) / (1.-ck)) * 255.);
                               km = int( ((cm - ck) / (1.-ck)) * 255.);
                               ky = int( ((cy - ck) / (1.-ck)) * 255.);
                               kk = int( ck * 255.);
                         }
                         sprintf(t,"%02hx%02hx%02hx%02hx",kc,km,ky,kk);
                         break;
                   case 2:
                         if ( (r ==255) && (g == 255) && ( b ==255 ))
                          { kr = 255; kg = 255; kb = 255;}
                         else
                          { kr = 0; kg = 0; kb = 0;}
                         sprintf(t,"%02hx%02hx%02hx",kr,kg,kb);
                         break;
                   case 3:
                         kr = int(0.3*r + 0.59*g + 0.11*b);
                         sprintf(t,"%02hx%02hx%02hx",kr,kr,kr);
                         break;
                   default:
                         sprintf(t,"%02hx%02hx%02hx",r,g,b);
                         break;
            }
            *ps << t;
            if ( (++nl)%12 == 0) *ps << "\n";
         }
       }
       *ps << "gr" <<endl;
       delete [] t;
       return true;
}

/*!
  \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
*/
01043 MAGICS_NO_EXPORT bool PostScriptDriver::renderCellArray(const Image& image) const
{
   ColourTable &lt  = image.getColourTable();
   const int width  = image.getNumberOfColumns();
   const int height = image.getNumberOfRows();

//Log::dev()<<" PostScriptDriver::renderCellArray "<< width << endl;

   if(width > 0 && height > 0)
   {
      const int col_model = getDeviceColourModel();
      
      const float x0 = projectX(image.getOrigin().x());
      const float y0 = projectY(image.getOrigin().y()-image.getHeight());
      const float x1 = projectX(image.getOrigin().x()+image.getWidth());
      const float y1 = projectY(image.getOrigin().y());
      const float dx = x1 - x0 + 1;
      const float dy = y1 - y0 + 1;
//    bool mask = false;

      fstream *ps = getStream();
      *ps << "gs /pic " << width*(( col_model == 1 ) ? 4 : 3) << " string def " << x0 << " " << y0
          << " t " << dx << " " << dy << " s " << width
          << " " << height << " 8\n"
          << "[" << width << " 0 0 " << height << " 0 0] "
          << "{currentfile pic readhexstring pop}"
          << " false " << (( col_model == 1 ) ? 4 : 3) <<" colorimage\n";

      char *t = new char[9];
      int nl = 0;
      for (int i=height-1;i>=0;i--)
      {
            for (int j=0;j<width;j++)
            {
                  short kr,kg,kb,kc,km,ky,kk;
                  float cc,cm,cy,ck;
                  const int in = width*i+j;
                  const short c = image[in];

                  float r = lt[c].red();
                  float g = lt[c].green();
                  float b = lt[c].blue();

                  if( (r*g*b>1) || (r*g*b<0) )
                  {
                        r = 1.;
                        g = 1.;
                        b = 1.;
//                      Log::info()<< "PostScriptDriver-> Cellshading colour not defined in table! Colour index: "<<c<<endl;
//    PostScript will always 'overpaint' anything below missing data!!!!
//
                  }

                  switch ( col_model )
                  {
                   case 0:
                         kr = short(r*255.);
                         kg = short(g*255.);
                         kb = short(b*255.);
                         sprintf(t,"%02hx%02hx%02hx",kr,kg,kb);
                         break;
                   case 1:
                         cc = 1.0 - r; cm = 1.0 - g; cy = 1. - b;
                         ck = ( cc < cm ) ?  cc : cm;
                         if ( cy < ck ) ck = cy;
                         if( ck == 1. )
                          { kc = 0; km = 0; ky = 0; kk = 255;}
                         else
                         {
                               kc = short(((cc - ck) / (1.-ck)) * 255.);
                               km = short(((cm - ck) / (1.-ck)) * 255.);
                               ky = short(((cy - ck) / (1.-ck)) * 255.);
                               kk = short(ck* 255.);
                         }
                         sprintf(t,"%02hx%02hx%02hx%02hx",kc,km,ky,kk);
                         break;
                   case 2:
                         if ( (r ==1.) && (g == 1.) && ( b ==1. ))
                          { kr = 255; kg = 255; kb = 255;}
                         else
                          { kr = 0; kg = 0; kb = 0;}
                         sprintf(t,"%02hx%02hx%02hx",kr,kg,kb);
                         break;
                   case 3:
                         ck = 0.3*r + 0.59*g + 0.11*b;
                         kr = short(ck*255.);
                         sprintf(t,"%02hx%02hx%02hx",kr,kr,kr);
                         break;
                  }
                  *ps << t;
                  if ( (++nl)%12 == 0) *ps << "\n";
            }
      }
      *ps << "gr" <<endl;
      delete [] t;
   }
   else
   {
      Log::warning() << "PostScriptDriver: failed to plot CellArray with wrong dimensions! Width: "<<width<<" Height: "<<height <<endl;
   }
   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
*/
01156 MAGICS_NO_EXPORT void PostScriptDriver::debugOutput(const string &s) const
{
      if(getDebug()) PSOut_ << "%% "<<s<<"\n";
}

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

//! Method to plot symbols
/*!

*/
01174 MAGICS_NO_EXPORT void PostScriptDriver::renderSymbols(const Symbol& symbol) const
{
      currentShading_=M_SH_SOLID;
      BaseDriver::renderSymbols(symbol);
}






/*!
      \note The file header can only be written after the Fonts have been read

      \sa open() startPage()
*/
01190 MAGICS_NO_EXPORT void PostScriptDriver::openFile() const
{
      if(!isSplit()) fileName_ = getFileName("ps");
      else
      {
            if(!isEPS()) fileName_ = getFileName("ps" ,currentPage_+1);
            else         fileName_ = getFileName("eps",currentPage_+1);
      }
      if(isPDF())
      {
            const string::size_type pos = fileName_.rfind(".pdf");
            if(pos != string::npos) fileName_.replace(pos,4,".ps");
      }

      if(PSOut_.is_open()) PSOut_.close();
      PSOut_.clear();
      PSOut_.open(fileName_.c_str(),std::ios::out);

      PSOut_.setf(ios_base::fixed);
      PSOut_.unsetf(ios::showpoint);
      PSOut_.precision(2);
      writePSFileHeader();

      // copy temp file into output file
      if(!PSOut_){
            Log::fatal() << "PostScriptDriver::close() --> Cannot write PostScript file! " << fileName_ << "\n";
            exit(1);
      }
}

/*!
      \brief Method to close the PostScript output file.

      \sa close() endPage()
*/
01225 MAGICS_NO_EXPORT void PostScriptDriver::closeFile() const
{
      // write end of file
      writePSFileEnd();

      // close + remove files
      PSOut_.close();

      const string fps = fileName_;

      if(isPDF())
      {
            const string::size_type pos = fileName_.rfind(".ps");
            if(pos != string::npos) fileName_.replace(pos,3,".pdf");
            printOutputName("PS pdf "+fileName_);

            // the -q option means no output - warnings may not show up!!! (fonts)
            string cmd = "( gs -q -dNOPAUSE -dBATCH -dSAFER -sDEVICE=pdfwrite -sOutputFile=";
            cmd.append(fileName_);
            cmd.append(" -c .setpdfwrite -f ");
            cmd.append(fps);
            cmd.append(" )");

            int status = system(cmd.c_str());
            if(status)
            {
                  Log::error() << "\nPostScriptDriver: Command exit not zero - NO PDF produced!\n"
                               << " COMMAND: "<<cmd<<"\n"<< endl;
                  setPS(true);
            }
      }
      if(!isPS() && !isEPS() ) remove(fps.c_str());
      else 
      {
            if(isPS()) printOutputName("PS ps "+fps);
            else printOutputName("PS eps "+fps);
      }
}


/*!
   \brief Method writing the PostScript file header.
*/
01268 MAGICS_NO_EXPORT void PostScriptDriver::writePSFileHeader() const
{
      fstream *ps = getStream();
      const SystemInfo info;

      *ps << "%!PS-Adobe-3.0";
      if(isEPS()) *ps << " EPSF-3.0";
      *ps << "\n%%Title: "<< getTitle()
          << "\n%%Creator: "<< getMagicsVersionString() <<"\n%%CreationDate: " << info.getTime()
          << "\n%%For: " << info.getUserID() << "@" << info.getHostName() << " " << info.getUserName()<<"\n";

      const float dimensionX = getXDeviceLength() * 72. / 2.54; // 72   = points / inch
      const float dimensionY = getYDeviceLength() * 72. / 2.54; // 2.54 = cm / inch

      const string orientation = (getXDeviceLength() < getYDeviceLength()) ? "Portrait" : "Landscape";

      if(isEPS())
      {
            *ps << "%%Pages: 1\n%%Orientation: "<<orientation<<"\n"
                << "%%BoundingBox: 0 0 " << static_cast<int>(dimensionX) << " " << static_cast<int>(dimensionY)+1 << "\n";
      }
      else
      {
            if(old_)
            {
                  if(isPDF() && !isPS())
                  {
                        float big, small;
                        if(dimensionX>dimensionY) { big = dimensionX;small = dimensionY;}

                        else { big = dimensionY;small = dimensionX;}

                        *ps << "%%Orientation: "<<orientation<<"\n%%LanguageLevel: 2\n%%Pages: 1\n";
      //                    << "%%BoundingBox: 0 0 " << static_cast<int>(small)+1 << " " << static_cast<int>(big)+1<< "\n";
                  }
                  else
                  {
                        float big, small;
                        if(dimensionX>dimensionY) { big = dimensionX;small = dimensionY;}
                        else { big = dimensionY;small = dimensionX;}

                        *ps << "%%Orientation: "<<orientation<<"\n%%LanguageLevel: 2\n%%Pages: (atend)\n"
                            << "%%BoundingBox: 0 0 " << static_cast<int>(small)+1 << " " << static_cast<int>(big)+1<< "\n";
                  }
            }
            else
            {
                  *ps << "%%Pages: (atend)\n"
                      << "%%BoundingBox: 0 0 " << static_cast<int>(dimensionX)+1 << " " << static_cast<int>(dimensionY)+1<< "\n";
            }
      }

      *ps << "%%EndComments\n%%BeginProlog\n";
      if(!isEPS()) *ps << "/S { gr showpage } def\n";
      else *ps << "/S {gr} def\n"; // define "showpage" empty for EPS files

      copyMacro(ps,"PostScriptMacro1.ps");

      fontMapIter mapit;

      for(mapit = FontMap_.begin();mapit != FontMap_.end(); mapit++)
            *ps << "Font " << (*mapit).second.id << " eq { /"<< (*mapit).second.ps_name<< " } if\n";

      copyMacro(ps,"PostScriptMacro2.ps");
      if(getenv("MAGPLUS_PS_DEBUG")) *ps << "("<<getEnvVariable("MAGPLUS_HOME") + MAGPLUS_PATH_TO_SHARE_<<"EHANDLER.PS) run\n";
}

/*!
   \brief Method copying macro code in the PostScript file header.
*/
01338 MAGICS_NO_EXPORT void PostScriptDriver::copyMacro(fstream *ps, const string &file) const
{
      const string s = getEnvVariable("MAGPLUS_HOME") + MAGPLUS_PATH_TO_SHARE_ + file;
      ifstream psfile(s.c_str());

      if(!psfile){
            Log::fatal() << "PostScriptDriver::copyMacro() --> Cannot open PostScript Macro file! " << s <<
             " Is MAGPLUS_HOME set correctly?\n";
            exit(1);
      }
      char ch;
      while (psfile.get(ch)){ps->put(ch);}
      psfile.close();
}


MAGICS_NO_EXPORT void PostScriptDriver::writePSFileEnd() const
{
      if(!isEPS())
      {
            fstream *ps = getStream();
            const int realpagenumber = (isSplit()) ? 1 : currentPage_;

            *ps << "%%Trailer\n";
            *ps << "%%Pages: " << realpagenumber << "\n";
            *ps << "%%EOF\n";
            ps->close();
      }
}

MAGICS_NO_EXPORT void PostScriptDriver::setDeviceColourModel(const string &m) const
{
      if(m.empty()) deviceColourModel_ = 1; // use default
      else if(magCompare(m,"RGB"))             deviceColourModel_ = 0;
      else if(magCompare(m,"CMYK"))            deviceColourModel_ = 1;
      else if(magCompare(m,"MONOCHROME"))      deviceColourModel_ = 2;
      else if(magCompare(m,"GRAY"))            deviceColourModel_ = 3;
      else if(magCompare(m,"CMYK_MONOCHROME")) deviceColourModel_ = 4;
      else if(magCompare(m,"CMYK_GRAY"))       deviceColourModel_ = 5;
      else
      {
            Log::warning() << "PostScriptDriver::setDeviceColourModel() -> "<< m
                           << " is unknown model! CMYK model is used." << endl;
            deviceColourModel_ = 1;
      }
}

static SimpleObjectMaker<PostScriptDriver, BaseDriver> PostScript_driver("PostScript");

Generated by  Doxygen 1.6.0   Back to index