//
// "$Id$"
//
// PostScript device support for the Fast Light Tool Kit (FLTK).
//
// Copyright 2010-2015 by Bill Spitzak and others.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file.  If this
// file is missing or damaged, see the license at:
//
//     http://www.fltk.org/COPYING.php
//
// Please report all bugs and problems to:
//
//     http://www.fltk.org/str.php
//

#include <FL/Fl_Printer.H>
#include <config.h>
#include <FL/Fl.H>
#include <FL/fl_ask.H>
#include <FL/fl_draw.H>
#include <stdio.h>
#include <FL/Fl_PostScript.H>
#include <FL/Fl_Native_File_Chooser.H>
#include <stdarg.h>
#if defined(USE_X11)
#include "Fl_Font.H"
#if USE_XFT
#include <X11/Xft/Xft.h>
#endif
#endif

const char *Fl_PostScript_Graphics_Driver::class_id = "Fl_PostScript_Graphics_Driver";
const char *Fl_PostScript_File_Device::class_id = "Fl_PostScript_File_Device";
/** \brief Label of the PostScript file chooser window */
const char *Fl_PostScript_File_Device::file_chooser_title = "Select a .ps file";

/**
 @brief The constructor.
 */
Fl_PostScript_Graphics_Driver::Fl_PostScript_Graphics_Driver(void)
{
  close_cmd_ = 0;
  //lang_level_ = 3;
  lang_level_ = 2;
  mask = 0;
  ps_filename_ = NULL;
  scale_x = scale_y = 1.;
  bg_r = bg_g = bg_b = 255;
}

/** \brief The destructor. */
Fl_PostScript_Graphics_Driver::~Fl_PostScript_Graphics_Driver() {
  if(ps_filename_) free(ps_filename_);
}

/**
 @brief The constructor.
 */
Fl_PostScript_File_Device::Fl_PostScript_File_Device(void)
{
#ifdef __APPLE__
  gc = fl_gc; // the display context is used by fl_text_extents()
#endif
  Fl_Surface_Device::driver( new Fl_PostScript_Graphics_Driver() );
}

/**
 \brief Returns the PostScript driver of this drawing surface.
 */
Fl_PostScript_Graphics_Driver *Fl_PostScript_File_Device::driver()
{
  return (Fl_PostScript_Graphics_Driver*)Fl_Surface_Device::driver();
}


/**
 @brief Begins the session where all graphics requests will go to a local PostScript file.
 *
 Opens a file dialog entitled with Fl_PostScript_File_Device::file_chooser_title to select an output PostScript file.
 @param pagecount The total number of pages to be created. Use 0 if this number is unknown when this function is called.
 @param format Desired page format.
 @param layout Desired page layout.
 @return 0 if OK, 1 if user cancelled the file dialog, 2 if fopen failed on user-selected output file.
 */
int Fl_PostScript_File_Device::start_job (int pagecount, enum Fl_Paged_Device::Page_Format format, 
					  enum Fl_Paged_Device::Page_Layout layout)
{
  Fl_Native_File_Chooser fnfc;
  fnfc.title(Fl_PostScript_File_Device::file_chooser_title);
  fnfc.type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE);
  fnfc.options(Fl_Native_File_Chooser::SAVEAS_CONFIRM | Fl_Native_File_Chooser::USE_FILTER_EXT);
  fnfc.filter("PostScript\t*.ps\n");
  // Show native chooser
  if ( fnfc.show() ) return 1;
  Fl_PostScript_Graphics_Driver *ps = driver();
  ps->output = fl_fopen(fnfc.filename(), "w");
  if(ps->output == NULL) return 2;
  ps->ps_filename_ = strdup(fnfc.filename());
  ps->start_postscript(pagecount, format, layout);
  this->set_current();
  return 0;
}

extern "C" {
  static int dont_close(FILE *f)
  {
    return 0;
  }
}

/**
 @brief Begins the session where all graphics requests will go to FILE pointer.
 *
 @param ps_output A writable FILE pointer that will receive PostScript output and that should not be closed
 until after end_job() has been called.
 @param pagecount The total number of pages to be created. Use 0 if this number is unknown when this function is called.
 @param format Desired page format.
 @param layout Desired page layout.
 @return always 0.
 */
int Fl_PostScript_File_Device::start_job (FILE *ps_output, int pagecount, 
    enum Fl_Paged_Device::Page_Format format, enum Fl_Paged_Device::Page_Layout layout)
{
  Fl_PostScript_Graphics_Driver *ps = driver();
  ps->output = ps_output;
  ps->ps_filename_ = NULL;
  ps->start_postscript(pagecount, format, layout);
  ps->close_command(dont_close); // so that end_job() doesn't close the file
  this->set_current();
  return 0;
}

/** Don't use with this class. */
int Fl_PostScript_File_Device::start_job(int pagecount, int* from, int* to)
{
  return 1;
}

/**
 @brief The destructor.
 */
Fl_PostScript_File_Device::~Fl_PostScript_File_Device() {
  Fl_PostScript_Graphics_Driver *ps = driver();
  if (ps) delete ps;
}

/** Shields output PostScript data from modifications of the current locale.
 It typically avoids PostScript errors caused if the current locale uses comma instead of dot
 as "decimal point".
 \param format  directives controlling output PostScript data
 \return value returned by vfprintf() call
 */
int Fl_PostScript_Graphics_Driver::clocale_printf(const char *format, ...)
{
  char *saved_locale = setlocale(LC_NUMERIC, NULL);
  setlocale(LC_NUMERIC, "C");
  va_list args;
  va_start(args, format);
  int retval = vfprintf(output, format, args);
  va_end(args);
  setlocale(LC_NUMERIC, saved_locale);
  return retval;
}

#ifndef FL_DOXYGEN

#if ! (defined(__APPLE__) || defined(WIN32) )
#  include "print_panel.cxx"
#endif

//  Prolog string 

static const char * prolog =
"%%BeginProlog\n"
"/L { /y2 exch def\n"
"/x2 exch def\n"
"/y1 exch def\n"
"/x1 exch def\n"
"newpath   x1 y1 moveto x2 y2 lineto\n"
"stroke}\n"
"bind def\n"


"/R { /dy exch def\n"
"/dx exch def\n"
"/y exch def\n"
"/x exch def\n"
"newpath\n"
"x y moveto\n"
"dx 0 rlineto\n"
"0 dy rlineto\n"
"dx neg 0 rlineto\n"
"closepath stroke\n"
"} bind def\n"

"/CL {\n"
"/dy exch def\n"
"/dx exch def\n"
"/y exch def\n"
"/x exch def\n"
"newpath\n"
"x y moveto\n"
"dx 0 rlineto\n"
"0 dy rlineto\n"
"dx neg 0 rlineto\n"
"closepath\n"
"clip\n"
"} bind def\n"

"/FR { /dy exch def\n"
"/dx exch def\n"
"/y exch def\n"
"/x exch def\n"
"currentlinewidth 0 setlinewidth newpath\n"
"x y moveto\n"
"dx 0 rlineto\n"
"0 dy rlineto\n"
"dx neg 0 rlineto\n"
"closepath fill setlinewidth\n"
"} bind def\n"

"/GS { gsave } bind  def\n"
"/GR { grestore } bind def\n"

"/SP { showpage } bind def\n"
"/LW { setlinewidth } bind def\n"
"/CF /Courier def\n"
"/SF { /CF exch def } bind def\n"
"/fsize 12 def\n"
"/FS { /fsize exch def fsize CF findfont exch scalefont setfont }def \n"


"/GL { setgray } bind def\n"
"/SRGB { setrgbcolor } bind def\n"

"/A85RLE { /ASCII85Decode filter /RunLengthDecode filter } bind def\n" // ASCII85Decode followed by RunLengthDecode filters

//  color images 

"/CI { GS /py exch def /px exch def /sy exch def /sx exch def\n"
"translate \n"
"sx sy scale px py 8 \n"
"[ px 0 0 py neg 0 py ]\n"
"currentfile A85RLE\n false 3"
" colorimage GR\n"
"} bind def\n"

//  gray images 

"/GI { GS /py exch def /px exch def /sy exch def /sx exch def \n"
"translate \n"
"sx sy scale px py 8 \n"


"[ px 0 0 py neg 0 py ]\n"
"currentfile A85RLE\n"
"image GR\n"
"} bind def\n"

// single-color bitmask 

"/MI { GS /py exch def /px exch def /sy exch def /sx exch def \n"
"translate \n"
"sx sy scale px py true \n"
"[ px 0 0 py neg 0 py ]\n"
"currentfile A85RLE\n"
"imagemask GR\n"
"} bind def\n"


//  path 

"/BFP { newpath moveto }  def\n"
"/BP { newpath } bind def \n"
"/PL { lineto } bind def \n"
"/PM { moveto } bind def \n"
"/MT { moveto } bind def \n"
"/LT { lineto } bind def \n"
"/EFP { closepath fill } bind def\n"  //was:stroke
"/ELP { stroke } bind def\n"  
"/ECP { closepath stroke } bind def\n"  // Closed (loop)
"/LW { setlinewidth } bind def\n"

// ////////////////////////// misc ////////////////
"/TR { translate } bind def\n"
"/CT { concat } bind def\n"
"/RCT { matrix invertmatrix concat} bind def\n"
"/SC { scale } bind def\n"
//"/GPD { currentpagedevice /PageSize get} def\n"

// show at position with desired width
// usage:
// width (string) x y show_pos_width
"/show_pos_width {GS moveto dup dup stringwidth pop exch length 2 div dup 2 le {pop 9999} if "
"1 sub exch 3 index exch sub exch "
"div 0 2 index 1 -1 scale ashow pop pop GR} bind def\n" // spacing altered to match desired width
//"/show_pos_width {GS moveto dup stringwidth pop 3 2 roll exch div -1 matrix scale concat "
//"show GR } bind def\n" // horizontally scaled text to match desired width

;


static const char * prolog_2 =  // prolog relevant only if lang_level >1

// color image dictionaries
"/CII {GS /inter exch def /py exch def /px exch def /sy exch def /sx exch def \n"
"translate \n"
"sx sy scale\n"
"/DeviceRGB setcolorspace\n"
"/IDD 8 dict def\n"
"IDD begin\n"
"/ImageType 1 def\n"
"/Width px def\n"
"/Height py def\n"
"/BitsPerComponent 8 def\n"
"/Interpolate inter def\n"
"/DataSource currentfile A85RLE def\n"
"/MultipleDataSources false def\n"
"/ImageMatrix [ px 0 0 py neg 0 py ] def\n"
"/Decode [ 0 1 0 1 0 1 ] def\n"
"end\n"
"IDD image GR} bind def\n"

// gray image dict 
"/GII {GS /inter exch def /py exch def /px exch def /sy exch def /sx exch def \n"
"translate \n"
"sx sy scale\n"
"/DeviceGray setcolorspace\n"
"/IDD 8 dict def\n"
"IDD begin\n"
"/ImageType 1 def\n"
"/Width px def\n"
"/Height py def\n"
"/BitsPerComponent 8 def\n"

"/Interpolate inter def\n"
"/DataSource currentfile A85RLE def\n"
"/MultipleDataSources false def\n"
"/ImageMatrix [ px 0 0 py neg 0 py ] def\n"
"/Decode [ 0 1 ] def\n"
"end\n"
"IDD image GR} bind def\n"

// Create a custom PostScript font derived from PostScript standard text fonts
// The encoding of this custom font is as follows:
// 0000-00FF  coincides with Unicode, that is to ASCII + Latin-1
// 0100-017F  coincides with Unicode, that is to Latin Extended-A
// 0180-01A6  encodes miscellaneous characters present in PostScript standard text fonts

// use ISOLatin1Encoding for all text fonts
"/ToISO { dup findfont dup length dict copy begin /Encoding ISOLatin1Encoding def currentdict end definefont pop } def\n"
"/Helvetica ToISO /Helvetica-Bold ToISO /Helvetica-Oblique ToISO /Helvetica-BoldOblique ToISO \n"
"/Courier ToISO /Courier-Bold ToISO /Courier-Oblique ToISO /Courier-BoldOblique ToISO \n"
"/Times-Roman ToISO /Times-Bold ToISO /Times-Italic ToISO /Times-BoldItalic ToISO \n"

// define LatinExtA, the encoding of Latin-extended-A + some additional characters
// see http://partners.adobe.com/public/developer/en/opentype/glyphlist.txt for their names
"/LatinExtA \n"
"[ "
" /Amacron /amacron /Abreve /abreve /Aogonek /aogonek\n" // begin of Latin Extended-A code page
" /Cacute  /cacute  /Ccircumflex  /ccircumflex  /Cdotaccent  /cdotaccent  /Ccaron  /ccaron \n"
" /Dcaron  /dcaron   /Dcroat  /dcroat\n"
" /Emacron  /emacron  /Ebreve  /ebreve  /Edotaccent  /edotaccent  /Eogonek  /eogonek  /Ecaron  /ecaron\n"
" /Gcircumflex  /gcircumflex  /Gbreve  /gbreve  /Gdotaccent  /gdotaccent  /Gcommaaccent  /gcommaaccent \n"
" /Hcircumflex /hcircumflex  /Hbar  /hbar  \n"
" /Itilde  /itilde  /Imacron  /imacron  /Ibreve  /ibreve  /Iogonek  /iogonek /Idotaccent  /dotlessi  \n"
" /IJ  /ij  /Jcircumflex  /jcircumflex\n"
" /Kcommaaccent  /kcommaaccent  /kgreenlandic  \n"
" /Lacute  /lacute  /Lcommaaccent  /lcommaaccent   /Lcaron  /lcaron  /Ldotaccent /ldotaccent   /Lslash  /lslash \n"
" /Nacute  /nacute  /Ncommaaccent  /ncommaaccent  /Ncaron  /ncaron  /napostrophe  /Eng  /eng  \n"
" /Omacron  /omacron /Obreve  /obreve  /Ohungarumlaut  /ohungarumlaut  /OE  /oe \n"
" /Racute  /racute  /Rcommaaccent  /rcommaaccent  /Rcaron  /rcaron \n"
" /Sacute /sacute  /Scircumflex  /scircumflex  /Scedilla /scedilla /Scaron  /scaron \n"
" /Tcommaaccent  /tcommaaccent  /Tcaron  /tcaron  /Tbar  /tbar \n"
" /Utilde  /utilde /Umacron /umacron  /Ubreve  /ubreve  /Uring  /uring  /Uhungarumlaut  /uhungarumlaut  /Uogonek /uogonek \n"
" /Wcircumflex  /wcircumflex  /Ycircumflex  /ycircumflex  /Ydieresis \n"
" /Zacute /zacute /Zdotaccent /zdotaccent /Zcaron /zcaron \n"
" /longs \n" // end of Latin Extended-A code page
" /florin  /circumflex  /caron  /breve  /dotaccent  /ring \n" // remaining characters from PostScript standard text fonts
" /ogonek  /tilde  /hungarumlaut  /endash /emdash \n"
" /quoteleft  /quoteright  /quotesinglbase  /quotedblleft  /quotedblright \n"
" /quotedblbase  /dagger  /daggerdbl  /bullet  /ellipsis \n"
" /perthousand  /guilsinglleft  /guilsinglright  /fraction  /Euro \n"
" /trademark /partialdiff  /Delta /summation  /radical \n"
" /infinity /notequal /lessequal /greaterequal /lozenge \n"
" /fi /fl /apple \n"
" ] def \n"
// deal with alternative PostScript names of some characters
" /mycharstrings /Helvetica findfont /CharStrings get def\n"
" /PSname2 { dup mycharstrings exch known {LatinExtA 3 -1 roll 3 -1 roll put}{pop pop} ifelse } def \n"
" 16#20 /Gdot PSname2 16#21 /gdot PSname2 16#30 /Idot PSname2 16#3F /Ldot PSname2 16#40 /ldot PSname2 16#7F /slong PSname2 \n"

// proc that gives LatinExtA encoding to a font
"/ToLatinExtA { findfont dup length dict copy begin /Encoding LatinExtA def currentdict end definefont pop } def\n"
// create Ext-versions of standard fonts that use LatinExtA encoding \n"
"/HelveticaExt /Helvetica ToLatinExtA \n"
"/Helvetica-BoldExt /Helvetica-Bold ToLatinExtA /Helvetica-ObliqueExt /Helvetica-Oblique ToLatinExtA  \n"
"/Helvetica-BoldObliqueExt /Helvetica-BoldOblique ToLatinExtA  \n"
"/CourierExt /Courier ToLatinExtA /Courier-BoldExt /Courier-Bold ToLatinExtA  \n"
"/Courier-ObliqueExt /Courier-Oblique ToLatinExtA /Courier-BoldObliqueExt /Courier-BoldOblique ToLatinExtA \n"
"/Times-RomanExt /Times-Roman ToLatinExtA /Times-BoldExt /Times-Bold ToLatinExtA  \n"
"/Times-ItalicExt /Times-Italic ToLatinExtA /Times-BoldItalicExt /Times-BoldItalic ToLatinExtA \n"

// proc to create a Type 0 font with 2-byte encoding 
// that merges a text font with ISO encoding + same font with LatinExtA encoding
"/To2byte { 6 dict begin /FontType 0 def \n"
"/FDepVector 3 1 roll findfont exch findfont 2 array astore def \n"
"/FontMatrix [1  0  0  1  0  0] def /FMapType 6 def /Encoding [ 0 1 0 ] def\n"
// 100: Hexa count of ISO array; A7: hexa count of LatinExtA array
"/SubsVector < 01 0100 00A7 > def\n" 
"currentdict end definefont pop } def\n"
// create Type 0 versions of standard fonts
"/Helvetica2B /HelveticaExt /Helvetica To2byte \n"
"/Helvetica-Bold2B /Helvetica-BoldExt /Helvetica-Bold To2byte \n"
"/Helvetica-Oblique2B /Helvetica-ObliqueExt /Helvetica-Oblique To2byte \n"
"/Helvetica-BoldOblique2B /Helvetica-BoldObliqueExt /Helvetica-BoldOblique To2byte \n"
"/Courier2B /CourierExt /Courier To2byte \n"
"/Courier-Bold2B /Courier-BoldExt /Courier-Bold To2byte \n"
"/Courier-Oblique2B /Courier-ObliqueExt /Courier-Oblique To2byte \n"
"/Courier-BoldOblique2B /Courier-BoldObliqueExt /Courier-BoldOblique To2byte \n"
"/Times-Roman2B /Times-RomanExt /Times-Roman To2byte \n"
"/Times-Bold2B /Times-BoldExt /Times-Bold To2byte \n"
"/Times-Italic2B /Times-ItalicExt /Times-Italic To2byte \n"
"/Times-BoldItalic2B /Times-BoldItalicExt /Times-BoldItalic To2byte \n"
;

static const char * prolog_2_pixmap =  // prolog relevant only if lang_level == 2 for pixmaps/masked color images
"/pixmap_mat {[ pixmap_sx 0 0 pixmap_sy neg 0 pixmap_sy ]}  bind def\n"

"/pixmap_dict {"
"<< /PatternType 1 "
"/PaintType 1 "
"/TilingType 2 "
"/BBox [0  0  pixmap_sx  pixmap_sy] "
"/XStep pixmap_sx "
"/YStep pixmap_sy\n"
"/PaintProc "
"{ begin "
"pixmap_w pixmap_h scale "
"pixmap_sx pixmap_sy 8 "
"pixmap_mat "
"currentfile A85RLE "
"false 3 "
"colorimage "
"end "
"} bind "
">>\n"
"} bind def\n"

"/pixmap_plot {"
"GS "
"/pixmap_sy exch def /pixmap_sx exch def\n"
"/pixmap_h exch def /pixmap_w exch def\n"
"translate\n"
"pixmap_dict matrix makepattern setpattern\n"
"pixmap_w pixmap_h scale\n"
"pixmap_sx pixmap_sy\n"
"true\n"
"pixmap_mat\n"
"currentfile A85RLE\n"
"imagemask\n"
"GR\n"
"} bind def\n"
;

static const char * prolog_3 = // prolog relevant only if lang_level >2

// masked color images 
"/CIM {GS /inter exch def /my exch def /mx exch def /py exch def /px exch def /sy exch def /sx exch def \n"
"translate \n"
"sx sy scale\n"
"/DeviceRGB setcolorspace\n"

"/IDD 8 dict def\n"

"IDD begin\n"
"/ImageType 1 def\n"
"/Width px def\n"
"/Height py def\n"
"/BitsPerComponent 8 def\n"
"/Interpolate inter def\n"
"/DataSource currentfile A85RLE def\n"
"/MultipleDataSources false def\n"
"/ImageMatrix [ px 0 0 py neg 0 py ] def\n"

"/Decode [ 0 1 0 1 0 1 ] def\n"
"end\n"

"/IMD 8 dict def\n"
"IMD begin\n"
"/ImageType 1 def\n"
"/Width mx def\n"           
"/Height my def\n"
"/BitsPerComponent 1 def\n"
//  "/Interpolate inter def\n"
"/ImageMatrix [ mx 0 0 my neg 0 my ] def\n"
"/Decode [ 1 0 ] def\n"
"end\n"

"<<\n"
"/ImageType 3\n"
"/InterleaveType 2\n"
"/MaskDict IMD\n"
"/DataDict IDD\n"
">> image GR\n"
"} bind def\n"


//  masked gray images 
"/GIM {GS /inter exch def /my exch def /mx exch def /py exch def /px exch def /sy exch def /sx exch def \n"
"translate \n"
"sx sy scale\n"
"/DeviceGray setcolorspace\n"

"/IDD 8 dict def\n"

"IDD begin\n"
"/ImageType 1 def\n"
"/Width px def\n"
"/Height py def\n"
"/BitsPerComponent 8 def\n"
"/Interpolate inter def\n"
"/DataSource currentfile A85RLE def\n"
"/MultipleDataSources false def\n"
"/ImageMatrix [ px 0 0 py neg 0 py ] def\n"

"/Decode [ 0 1 ] def\n"
"end\n"

"/IMD 8 dict def\n"

"IMD begin\n"
"/ImageType 1 def\n"
"/Width mx def\n"           
"/Height my def\n"
"/BitsPerComponent 1 def\n"
"/ImageMatrix [ mx 0 0 my neg 0 my ] def\n"
"/Decode [ 1 0 ] def\n"
"end\n"

"<<\n"
"/ImageType 3\n"
"/InterleaveType 2\n"
"/MaskDict IMD\n"
"/DataDict IDD\n"
">> image GR\n"
"} bind def\n"


"\n"
;

// end prolog 

int Fl_PostScript_Graphics_Driver::start_postscript (int pagecount, 
    enum Fl_Paged_Device::Page_Format format, enum Fl_Paged_Device::Page_Layout layout)
//returns 0 iff OK
{
  int w, h, x;
  if (format == Fl_Paged_Device::A4) {
    left_margin = 18;
    top_margin = 18;
  }
  else {
    left_margin = 12;
    top_margin = 12;
  }
  page_format_ = (enum Fl_Paged_Device::Page_Format)(format | layout);
  if (layout & Fl_Paged_Device::LANDSCAPE){
    ph_ = Fl_Paged_Device::page_formats[format].width;
    pw_ = Fl_Paged_Device::page_formats[format].height;
  } else {
    pw_ = Fl_Paged_Device::page_formats[format].width;
    ph_ = Fl_Paged_Device::page_formats[format].height;
  }
  
  fputs("%!PS-Adobe-3.0\n", output);
  fputs("%%Creator: FLTK\n", output);
  if (lang_level_>1)
    fprintf(output, "%%%%LanguageLevel: %i\n" , lang_level_);
  if ((pages_ = pagecount))
    fprintf(output, "%%%%Pages: %i\n", pagecount);
  else
    fputs("%%Pages: (atend)\n", output);
  fprintf(output, "%%%%BeginFeature: *PageSize %s\n", Fl_Paged_Device::page_formats[format].name );
  w = Fl_Paged_Device::page_formats[format].width;
  h = Fl_Paged_Device::page_formats[format].height;
  if (lang_level_ == 3 && (layout & Fl_Paged_Device::LANDSCAPE) ) { x = w; w = h; h = x; }
  fprintf(output, "<</PageSize[%d %d]>>setpagedevice\n", w, h );
  fputs("%%EndFeature\n", output);
  fputs("%%EndComments\n", output);
  fputs(prolog, output);
  if (lang_level_ > 1) {
    fputs(prolog_2, output);
    }
  if (lang_level_ == 2) {
    fputs(prolog_2_pixmap, output);
    }
  if (lang_level_ > 2)
    fputs(prolog_3, output);
  if (lang_level_ >= 3) {
    fputs("/CS { clipsave } bind def\n", output);
    fputs("/CR { cliprestore } bind def\n", output);
  } else {
    fputs("/CS { GS } bind def\n", output);
    fputs("/CR { GR } bind def\n", output);
  }
  page_policy_ = 1;
  
  
  fputs("%%EndProlog\n",output);
  if (lang_level_ >= 2)
    fprintf(output,"<< /Policies << /Pagesize 1 >> >> setpagedevice\n");
  
  reset();
  nPages=0;
  return 0;
}

void Fl_PostScript_Graphics_Driver::recover(){
  color(cr_,cg_,cb_);
  line_style(linestyle_,linewidth_,linedash_);
  font(Fl_Graphics_Driver::font(), Fl_Graphics_Driver::size());
}

void Fl_PostScript_Graphics_Driver::reset(){
  gap_=1;
  clip_=0;
  cr_=cg_=cb_=0;
  Fl_Graphics_Driver::font(FL_HELVETICA, 12);
  linewidth_=0;
  linestyle_=FL_SOLID;
  strcpy(linedash_,"");
  Clip *c=clip_;   ////just not to have memory leaks for badly writen code (forgotten clip popping)
  
  while(c){
    clip_=clip_->prev;
    delete c;
    c=clip_;
  }
  
}

void Fl_PostScript_Graphics_Driver::page_policy(int p){
  page_policy_ = p;
  if(lang_level_>=2)
    fprintf(output,"<< /Policies << /Pagesize %i >> >> setpagedevice\n", p);
}

// //////////////////// paging //////////////////////////////////////////



void Fl_PostScript_Graphics_Driver::page(double pw, double ph, int media) {
  
  if (nPages){
    fprintf(output, "CR\nGR\nGR\nGR\nSP\nrestore\n");
  }
  ++nPages;
  fprintf(output, "%%%%Page: %i %i\n" , nPages , nPages);
  fprintf(output, "%%%%PageBoundingBox: 0 0 %d %d\n", pw > ph ? (int)ph : (int)pw , pw > ph ? (int)pw : (int)ph);
  if (pw>ph){
    fprintf(output, "%%%%PageOrientation: Landscape\n");
  }else{
    fprintf(output, "%%%%PageOrientation: Portrait\n");
  }
  
  fprintf(output, "%%%%BeginPageSetup\n");
  if((media & Fl_Paged_Device::MEDIA) &&(lang_level_>1)){
    int r = media & Fl_Paged_Device::REVERSED;
    if(r) r = 2;
    fprintf(output, "<< /PageSize [%i %i] /Orientation %i>> setpagedevice\n", (int)(pw+.5), (int)(ph+.5), r);
  }
  fprintf(output, "%%%%EndPageSetup\n");
  
/*  pw_ = pw;
  ph_ = ph;*/
  reset();
  
  fprintf(output, "save\n");
  fprintf(output, "GS\n");
  clocale_printf( "%g %g TR\n", (double)0 /*lm_*/ , ph_ /* - tm_*/);
  fprintf(output, "1 -1 SC\n");
  line_style(0);
  fprintf(output, "GS\n");
  
  if (!((media & Fl_Paged_Device::MEDIA) &&(lang_level_>1))){
    if (pw > ph) {
      if(media & Fl_Paged_Device::REVERSED) {
        fprintf(output, "-90 rotate %i 0 translate\n", int(-pw));
	}
      else {
        fprintf(output, "90 rotate -%i -%i translate\n", (lang_level_ == 2 ? int(pw - ph) : 0), int(ph));
	}
      }
      else {
	if(media & Fl_Paged_Device::REVERSED)
	  fprintf(output, "180 rotate %i %i translate\n", int(-pw), int(-ph));
	}
  }
  fprintf(output, "GS\nCS\n");
}

void Fl_PostScript_Graphics_Driver::page(int format){
/*  if(format &  Fl_Paged_Device::LANDSCAPE){
    ph_=Fl_Paged_Device::page_formats[format & 0xFF].width;
    pw_=Fl_Paged_Device::page_formats[format & 0xFF].height;
  }else{
    pw_=Fl_Paged_Device::page_formats[format & 0xFF].width;
    ph_=Fl_Paged_Device::page_formats[format & 0xFF].height;
  }*/
  page(pw_,ph_,format & 0xFF00);//,orientation only;
}

void Fl_PostScript_Graphics_Driver::rect(int x, int y, int w, int h) {
  // Commented code does not work, i can't find the bug ;-(
  // fprintf(output, "GS\n");
  //  fprintf(output, "%i, %i, %i, %i R\n", x , y , w, h);
  //  fprintf(output, "GR\n");
  fprintf(output, "GS\n");
  fprintf(output,"BP\n");
  fprintf(output, "%i %i MT\n", x , y);
  fprintf(output, "%i %i LT\n", x+w-1 , y);
  fprintf(output, "%i %i LT\n", x+w-1 , y+h-1);
  fprintf(output, "%i %i LT\n", x , y+h-1);
  fprintf(output, "ECP\n");
  fprintf(output, "GR\n");
}

void Fl_PostScript_Graphics_Driver::rectf(int x, int y, int w, int h) {
  clocale_printf( "%g %g %i %i FR\n", x-0.5, y-0.5, w, h);
}

void Fl_PostScript_Graphics_Driver::line(int x1, int y1, int x2, int y2) {
  fprintf(output, "GS\n");
  fprintf(output, "%i %i %i %i L\n", x1 , y1, x2 ,y2);
  fprintf(output, "GR\n");
}

void Fl_PostScript_Graphics_Driver::line(int x0, int y0, int x1, int y1, int x2, int y2) {
  fprintf(output, "GS\n");
  fprintf(output,"BP\n");
  fprintf(output, "%i %i MT\n", x0 , y0);
  fprintf(output, "%i %i LT\n", x1 , y1);
  fprintf(output, "%i %i LT\n", x2 , y2);
  fprintf(output, "ELP\n");
  fprintf(output, "GR\n");
}

void Fl_PostScript_Graphics_Driver::xyline(int x, int y, int x1, int y2, int x3){
  fprintf(output, "GS\n");
  fprintf(output,"BP\n");
  fprintf(output, "%i %i MT\n", x , y );
  fprintf(output, "%i %i LT\n", x1 , y );
  fprintf(output, "%i %i LT\n", x1 , y2);
  fprintf(output,"%i %i LT\n", x3 , y2);
  fprintf(output, "ELP\n");
  fprintf(output, "GR\n");
}


void Fl_PostScript_Graphics_Driver::xyline(int x, int y, int x1, int y2){
  
  fprintf(output, "GS\n");
  fprintf(output,"BP\n");
  fprintf(output, "%i %i MT\n", x , y);
  fprintf(output,"%i %i LT\n", x1 , y);
  fprintf(output, "%i %i LT\n", x1 , y2 );
  fprintf(output, "ELP\n");
  fprintf(output, "GR\n");
}

void Fl_PostScript_Graphics_Driver::xyline(int x, int y, int x1){
  fprintf(output, "GS\n");
  fprintf(output,"BP\n");
  fprintf(output, "%i %i MT\n", x , y);
  fprintf(output, "%i %i LT\n", x1 , y );
  fprintf(output, "ELP\n");
  
  fprintf(output, "GR\n");
}

void Fl_PostScript_Graphics_Driver::yxline(int x, int y, int y1, int x2, int y3){
  fprintf(output, "GS\n");
  
  fprintf(output,"BP\n");
  fprintf(output,"%i %i MT\n", x , y);
  fprintf(output, "%i %i LT\n", x , y1 );
  fprintf(output, "%i %i LT\n", x2 , y1 );
  fprintf(output , "%i %i LT\n", x2 , y3);
  fprintf(output, "ELP\n");
  fprintf(output, "GR\n");
}

void Fl_PostScript_Graphics_Driver::yxline(int x, int y, int y1, int x2){
  fprintf(output, "GS\n");
  fprintf(output,"BP\n");
  fprintf(output, "%i %i MT\n", x , y);
  fprintf(output, "%i %i LT\n", x , y1);
  fprintf(output, "%i %i LT\n", x2 , y1);
  fprintf(output, "ELP\n");
  fprintf(output, "GR\n");
}

void Fl_PostScript_Graphics_Driver::yxline(int x, int y, int y1){
  fprintf(output, "GS\n");
  fprintf(output,"BP\n");
  fprintf(output, "%i %i MT\n", x , y);
  fprintf(output, "%i %i LT\n", x , y1);
  fprintf(output, "ELP\n");
  fprintf(output, "GR\n");
}

void Fl_PostScript_Graphics_Driver::loop(int x0, int y0, int x1, int y1, int x2, int y2) {
  fprintf(output, "GS\n");
  fprintf(output,"BP\n");
  fprintf(output, "%i %i MT\n", x0 , y0);
  fprintf(output, "%i %i LT\n", x1 , y1);
  fprintf(output, "%i %i LT\n", x2 , y2);
  fprintf(output, "ECP\n");
  fprintf(output, "GR\n");
}

void Fl_PostScript_Graphics_Driver::loop(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3) {
  fprintf(output, "GS\n");
  fprintf(output,"BP\n");
  fprintf(output, "%i %i MT\n", x0 , y0);
  fprintf(output, "%i %i LT\n", x1 , y1);
  fprintf(output, "%i %i LT\n", x2 , y2);
  fprintf(output, "%i %i LT\n", x3 , y3);
  fprintf(output, "ECP\n");
  fprintf(output, "GR\n");
}

void Fl_PostScript_Graphics_Driver::polygon(int x0, int y0, int x1, int y1, int x2, int y2) {
  fprintf(output, "GS\n");
  fprintf(output,"BP\n");
  fprintf(output, "%i %i MT\n", x0 , y0);
  fprintf(output,"%i %i LT\n", x1 , y1);
  fprintf(output, "%i %i LT\n", x2 , y2);
  fprintf(output, "EFP\n");
  fprintf(output, "GR\n");
}

void Fl_PostScript_Graphics_Driver::polygon(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3) {
  fprintf(output, "GS\n");
  fprintf(output,"BP\n");
  fprintf(output, "%i %i MT\n", x0 , y0 );
  fprintf(output, "%i %i LT\n", x1 , y1 );
  fprintf(output, "%i %i LT\n", x2 , y2 );
  fprintf(output, "%i %i LT\n", x3 , y3 );
  
  fprintf(output, "EFP\n");
  fprintf(output, "GR\n");
}

void Fl_PostScript_Graphics_Driver::point(int x, int y){
  rectf(x,y,1,1);
}

static const int dashes_flat[5][7]={
{-1,0,0,0,0,0,0},
{3,1,-1,0,0,0,0},
{1,1,-1,0,0,0,0},
{3,1,1,1,-1,0,0},
{3,1,1,1,1,1,-1}
};


//yeah, hack...
static const double dashes_cap[5][7]={
{-1,0,0,0,0,0,0},
{2,2,-1,0,0,0,0},
{0.01,1.99,-1,0,0,0,0},
{2,2,0.01,1.99,-1,0,0},
{2,2,0.01,1.99,0.01,1.99,-1}
};


void Fl_PostScript_Graphics_Driver::line_style(int style, int width, char* dashes){
  //line_styled_=1;
  
  linewidth_=width;
  linestyle_=style;
  //dashes_= dashes;
  if(dashes){
    if(dashes != linedash_)
      strcpy(linedash_,dashes);
    
  }else
    linedash_[0]=0;
  char width0 = 0;
  if(!width){
    width=1; //for screen drawing compatibility
    width0=1;
  }
  
  fprintf(output, "%i setlinewidth\n", width);
  
  if(!style && (!dashes || !(*dashes)) && width0) //system lines
    style = FL_CAP_SQUARE;
  
  int cap = (style &0xf00) >> 8;
  if(cap) cap--;
  fprintf(output,"%i setlinecap\n", cap);
  
  int join = (style & 0xf000) >> 12;
  
  if(join) join--;
  fprintf(output,"%i setlinejoin\n", join);
  
  
  fprintf(output, "[");
  if(dashes && *dashes){
    while(*dashes){
      fprintf(output, "%i ", *dashes);
      dashes++;
    }
  }else{
    if(style & 0x200){ // round and square caps, dash length need to be adjusted
      const double *dt = dashes_cap[style & 0xff];
      while (*dt >= 0){
        clocale_printf("%g ",width * (*dt));
        dt++;
      }
    }else{
      
      const int *ds = dashes_flat[style & 0xff];
      while (*ds >= 0){
	fprintf(output, "%i ",width * (*ds));
        ds++;
      }
    }
  }
  fprintf(output, "] 0 setdash\n");
}

static const char *_fontNames[] = {
"Helvetica2B", 
"Helvetica-Bold2B",
"Helvetica-Oblique2B",
"Helvetica-BoldOblique2B",
"Courier2B",
"Courier-Bold2B",
"Courier-Oblique2B",
"Courier-BoldOblique2B",
"Times-Roman2B",
"Times-Bold2B",
"Times-Italic2B",
"Times-BoldItalic2B",
"Symbol",
"Courier2B",
"Courier-Bold2B",
"ZapfDingbats"
};

void Fl_PostScript_Graphics_Driver::font(int f, int s) {
  Fl_Graphics_Driver *driver = Fl_Display_Device::display_device()->driver();
  driver->font(f,s); // Use display fonts for font measurement
  Fl_Graphics_Driver::font(f, s);
  Fl_Font_Descriptor *desc = driver->font_descriptor();
  this->font_descriptor(desc);
  if (f < FL_FREE_FONT) {
    float ps_size = (float) s;
    fprintf(output, "/%s SF\n" , _fontNames[f]);
#if defined(USE_X11) 
#if USE_XFT
    // Xft font height is sometimes larger than the required size (see STR 2566).
    // Increase the PostScript font size by 15% without exceeding the display font height 
    int max = desc->font->height;
    ps_size = s * 1.15;
    if (ps_size > max) ps_size = max;
#else
    // Non-Xft fonts can be smaller than required.
    // Set the PostScript font size to the display font height 
    char *name = desc->font->font_name_list[0];
    char *p = strstr(name, "--");
    if (p) {
      sscanf(p + 2, "%f", &ps_size);
    }
#endif // USE_XFT
#endif // USE_X11
    clocale_printf("%.1f FS\n", ps_size);
  }
}

double Fl_PostScript_Graphics_Driver::width(const char *s, int n) {
  return Fl_Display_Device::display_device()->driver()->width(s, n);
}

double Fl_PostScript_Graphics_Driver::width(unsigned u) {
  return Fl_Display_Device::display_device()->driver()->width(u);
}

int Fl_PostScript_Graphics_Driver::height() {
  return Fl_Display_Device::display_device()->driver()->height();
}

int Fl_PostScript_Graphics_Driver::descent() {
  return Fl_Display_Device::display_device()->driver()->descent();
}

void Fl_PostScript_Graphics_Driver::text_extents(const char *c, int n, int &dx, int &dy, int &w, int &h) {
  Fl_Display_Device::display_device()->driver()->text_extents(c, n, dx, dy, w, h);
}


void Fl_PostScript_Graphics_Driver::color(Fl_Color c) {
  Fl::get_color(c, cr_, cg_, cb_);
  color(cr_, cg_, cb_);
}

void Fl_PostScript_Graphics_Driver::color(unsigned char r, unsigned char g, unsigned char b) {
  Fl_Graphics_Driver::color( fl_rgb_color(r, g, b) );
  cr_ = r; cg_ = g; cb_ = b;
  if (r == g && g == b) {
    double gray = r/255.0;
    clocale_printf("%g GL\n", gray);
  } else {
    double fr, fg, fb;
    fr = r/255.0;
    fg = g/255.0;
    fb = b/255.0;
    clocale_printf("%g %g %g SRGB\n", fr , fg , fb);
  }
}

void Fl_PostScript_Graphics_Driver::draw(int angle, const char *str, int n, int x, int y)
{
  fprintf(output, "GS %d %d translate %d rotate\n", x, y, - angle);
  this->transformed_draw(str, n, 0, 0);
  fprintf(output, "GR\n");
}


// computes the mask for the RGB image img of all pixels with color != bg
static uchar *calc_mask(uchar *img, int w, int h, Fl_Color bg)
{
  uchar red, green, blue, r, g, b;
  uchar bit, byte, *q;
  Fl::get_color(bg, red, green, blue);
  int W = (w+7)/8; // width of mask
  uchar* mask = new uchar[W * h];
  q = mask;
  while (h-- > 0) { // for each row
    bit = 0x80; // byte with last bit set
    byte = 0; // next mask byte to compute
    for (int j = 0; j < w; j++) { // for each column
      r = *img++; // the pixel color components
      g = *img++;
      b = *img++;
      // if pixel doesn't have bg color, put it in mask
      if (r != red || g != green || b != blue) byte |= bit;
      bit = bit>>1; // shift bit one step to the right
      if (bit == 0) { // single set bit has fallen out
	*q++ = byte; // enter byte in mask
	byte = 0; // reset next mask byte to zero
	bit = 0x80; // and this byte
	}
      }
    if (bit != 0x80) *q++ = byte; // enter last columns' byte in mask
    }
  return mask;
}

// write to PostScript a bitmap image of a UTF8 string
void Fl_PostScript_Graphics_Driver::transformed_draw_extra(const char* str, int n, double x, double y, int w, bool rtl)
{
  // scale for bitmask computation
#if defined(USE_X11) && !USE_XFT
  float scale = 1; // don't scale because we can't expect to have scalable fonts
#else
  float scale = 2;
#endif
  Fl_Fontsize old_size = size();
  Fl_Font fontnum = Fl_Graphics_Driver::font();
  int w_scaled =  (int)(w * (scale + 0.5));
  int h = (int)(height() * scale);
  // create an offscreen image of the string
  Fl_Color text_color = Fl_Graphics_Driver::color();
  Fl_Color bg_color = fl_contrast(FL_WHITE, text_color);
  Fl_Offscreen off = fl_create_offscreen(w_scaled, (int)(h+3*scale) );
  fl_begin_offscreen(off);
  fl_color(bg_color);
  // color offscreen background with a shade contrasting with the text color
  fl_rectf(0, 0, w_scaled, (int)(h+3*scale) );
  fl_color(text_color);
#if defined(USE_X11) && !USE_XFT
  // force seeing this font as new so it's applied to the offscreen graphics context
  fl_graphics_driver->font_descriptor(NULL);
  fl_font(fontnum, 0);
#endif
  fl_font(fontnum, (Fl_Fontsize)(scale * old_size) );
  int w2 = (int)fl_width(str, n);
  // draw string in offscreen
  if (rtl) fl_rtl_draw(str, n, w2, (int)(h * 0.8) );
  else fl_draw(str, n, 1, (int)(h * 0.8) );
  // read (most of) the offscreen image
  uchar *img = fl_read_image(NULL, 1, 1, w2, h, 0);
  fl_end_offscreen();
  font(fontnum, old_size);
  fl_delete_offscreen(off);
  // compute the mask of what is not the background
  uchar *mask = calc_mask(img, w2, h, bg_color);
  delete[] img;
  // write the string image to PostScript as a scaled bitmask
  scale = w2 / float(w);
  clocale_printf("%g %g %g %g %d %d MI\n", x, y - h*0.77/scale, w2/scale, h/scale, w2, h);
  uchar *di;
  int wmask = (w2+7)/8;
  void *rle85 = prepare_rle85();
  for (int j = h - 1; j >= 0; j--){
    di = mask + j * wmask;
    for (int i = 0; i < wmask; i++){
      write_rle85(*di, rle85);
      di++;
    }
  }
  close_rle85(rle85); fputc('\n', output);
  delete[] mask;
}

static int is_in_table(unsigned utf) {
  unsigned i;
  static unsigned extra_table_roman[] = { // unicodes/*names*/ of other characters from PostScript standard fonts
    0x192/*florin*/, 0x2C6/*circumflex*/, 0x2C7/*caron*/, 
    0x2D8/*breve*/, 0x2D9/*dotaccent*/, 0x2DA/*ring*/, 0x2DB/*ogonek*/, 0x2DC/*tilde*/, 0x2DD/*hungarumlaut*/,
    0x2013/*endash*/, 0x2014/*emdash*/, 0x2018/*quoteleft*/, 0x2019/*quoteright*/, 
    0x201A/*quotesinglbase*/, 0x201C/*quotedblleft*/, 0x201D/*quotedblright*/, 0x201E/*quotedblbase*/, 
    0x2020/*dagger*/, 0x2021/*daggerdbl*/, 0x2022/*bullet*/,
    0x2026/*ellipsis*/, 0x2030/*perthousand*/, 0x2039/*guilsinglleft*/, 0x203A/*guilsinglright*/, 
    0x2044/*fraction*/, 0x20AC/*Euro*/, 0x2122/*trademark*/, 
    0x2202/*partialdiff*/, 0x2206/*Delta*/, 0x2211/*summation*/, 0x221A/*radical*/,
    0x221E/*infinity*/, 0x2260/*notequal*/, 0x2264/*lessequal*/, 
    0x2265/*greaterequal*/, 
    0x25CA/*lozenge*/, 0xFB01/*fi*/, 0xFB02/*fl*/,
    0xF8FF/*apple*/
  };
  for ( i = 0; i < sizeof(extra_table_roman)/sizeof(int); i++) {
    if (extra_table_roman[i] == utf) return i + 0x180;
  }
  return 0;
}

// outputs in PostScript a UTF8 string using the same width in points as on display
void Fl_PostScript_Graphics_Driver::transformed_draw(const char* str, int n, double x, double y) {
  int len, code;
  if (!n || !str || !*str) return;
  // compute display width of string
  int w = (int)width(str, n);
  if (w == 0) return;
  if (Fl_Graphics_Driver::font() >= FL_FREE_FONT) {
    transformed_draw_extra(str, n, x, y, w, false);
    return;
    }
  fprintf(output, "%d <~", w);
  void *data = prepare85();
  // transforms UTF8 encoding to our custom PostScript encoding as follows:
  // extract each unicode character
  // if unicode <= 0x17F, unicode and PostScript codes are identical
  // if unicode is one of the values listed in extra_table_roman above
  //    its PostScript code is 0x180 + the character's rank in extra_table_roman
  // if unicode is something else, draw all string as bitmap image

  const char *last = str + n;
  const char *str2 = str;
  while (str2 < last) {
    // Extract each unicode character of string.
    unsigned utf = fl_utf8decode(str2, last, &len);
    str2 += len;
    if (utf <= 0x17F) { // until Latin Extended-A
      ;
      }
    else if ( (code = is_in_table(utf)) != 0) { // other handled characters
      utf = code;
      }
    else { // unhandled character: draw all string as bitmap image
      fprintf(output, "~> pop pop\n"); // close and ignore the opened hex string
      transformed_draw_extra(str, n, x, y, w, false);
      return;
    }
    // 2 bytes per character, high-order byte first, encode that to ASCII85
    uchar c[2]; c[1] = utf & 0xFF; c[0] = (utf & 0xFF00)>>8; write85(data, c, 2);
  }
  close85(data);
  clocale_printf(" %g %g show_pos_width\n", x, y);
}

void Fl_PostScript_Graphics_Driver::rtl_draw(const char* str, int n, int x, int y) {
  int w = (int)width(str, n);
  transformed_draw_extra(str, n, x - w, y, w, true);
}

void Fl_PostScript_Graphics_Driver::concat(){
  clocale_printf("[%g %g %g %g %g %g] CT\n", fl_matrix->a , fl_matrix->b , fl_matrix->c , fl_matrix->d , fl_matrix->x , fl_matrix->y);
}

void Fl_PostScript_Graphics_Driver::reconcat(){
  clocale_printf("[%g %g %g %g %g %g] RCT\n" , fl_matrix->a , fl_matrix->b , fl_matrix->c , fl_matrix->d , fl_matrix->x , fl_matrix->y);
}

/////////////////  transformed (double) drawings ////////////////////////////////


void Fl_PostScript_Graphics_Driver::begin_points(){
  fprintf(output, "GS\n");
  concat();
  
  fprintf(output, "BP\n");
  gap_=1;
  shape_=POINTS;
}

void Fl_PostScript_Graphics_Driver::begin_line(){
  fprintf(output, "GS\n");
  concat();
  fprintf(output, "BP\n");
  gap_=1;
  shape_=LINE;
}

void Fl_PostScript_Graphics_Driver::begin_loop(){
  fprintf(output, "GS\n");
  concat();
  fprintf(output, "BP\n");
  gap_=1;
  shape_=LOOP;
}

void Fl_PostScript_Graphics_Driver::begin_polygon(){
  fprintf(output, "GS\n");
  concat();
  fprintf(output, "BP\n");
  gap_=1;
  shape_=POLYGON;
}

void Fl_PostScript_Graphics_Driver::vertex(double x, double y){
  if(shape_==POINTS){
    clocale_printf("%g %g MT\n", x , y);
    gap_=1;
    return;
  }
  if(gap_){
    clocale_printf("%g %g MT\n", x , y);
    gap_=0;
  }else
    clocale_printf("%g %g LT\n", x , y);
}

void Fl_PostScript_Graphics_Driver::curve(double x, double y, double x1, double y1, double x2, double y2, double x3, double y3){
  if(shape_==NONE) return;
  if(gap_)
    clocale_printf("%g %g MT\n", x , y);
  else
    clocale_printf("%g %g LT\n", x , y);
  gap_=0;
  
  clocale_printf("%g %g %g %g %g %g curveto \n", x1 , y1 , x2 , y2 , x3 , y3);
}


void Fl_PostScript_Graphics_Driver::circle(double x, double y, double r){
  if(shape_==NONE){
    fprintf(output, "GS\n");
    concat();
    //    fprintf(output, "BP\n");
    clocale_printf("%g %g %g 0 360 arc\n", x , y , r);
    reconcat();
    //    fprintf(output, "ELP\n");
    fprintf(output, "GR\n");
  }else
    
    clocale_printf("%g %g %g 0 360 arc\n", x , y , r);
  
}

void Fl_PostScript_Graphics_Driver::arc(double x, double y, double r, double start, double a){
  if(shape_==NONE) return;
  gap_=0;
  if(start>a)
    clocale_printf("%g %g %g %g %g arc\n", x , y , r , -start, -a);
  else
    clocale_printf("%g %g %g %g %g arcn\n", x , y , r , -start, -a);
  
}

void Fl_PostScript_Graphics_Driver::arc(int x, int y, int w, int h, double a1, double a2) {
  if (w <= 1 || h <= 1) return;
  fprintf(output, "GS\n");
  //fprintf(output, "BP\n");
  begin_line();
  clocale_printf("%g %g TR\n", x + w/2.0 -0.5 , y + h/2.0 - 0.5);
  clocale_printf("%g %g SC\n", (w-1)/2.0 , (h-1)/2.0 );
  arc(0,0,1,a2,a1);
  //  fprintf(output, "0 0 1 %g %g arc\n" , -a1 , -a2);
  clocale_printf("%g %g SC\n", 2.0/(w-1) , 2.0/(h-1) );
  clocale_printf("%g %g TR\n", -x - w/2.0 +0.5 , -y - h/2.0 +0.5);
  end_line();
  
  //  fprintf(output, "%g setlinewidth\n",  2/sqrt(w*h));
  //  fprintf(output, "ELP\n");
  //  fprintf(output, 2.0/w , 2.0/w , " SC\n";
  //  fprintf(output, (-x - w/2.0) , (-y - h/2)  , " TR\n";
  fprintf(output, "GR\n");
}

void Fl_PostScript_Graphics_Driver::pie(int x, int y, int w, int h, double a1, double a2) {
  fprintf(output, "GS\n");
  begin_polygon();
  clocale_printf("%g %g TR\n", x + w/2.0 -0.5 , y + h/2.0 - 0.5);
  clocale_printf("%g %g SC\n", (w-1)/2.0 , (h-1)/2.0 );
  vertex(0,0);
  arc(0.0,0.0, 1, a2, a1);
  end_polygon();
  fprintf(output, "GR\n");
}

void Fl_PostScript_Graphics_Driver::end_points(){
  gap_=1;
  reconcat();
  fprintf(output, "ELP\n"); //??
  fprintf(output, "GR\n");
  shape_=NONE;
}

void Fl_PostScript_Graphics_Driver::end_line(){
  gap_=1;
  reconcat();
  fprintf(output, "ELP\n");
  fprintf(output, "GR\n");
  shape_=NONE;
}
void Fl_PostScript_Graphics_Driver::end_loop(){
  gap_=1;
  reconcat();
  fprintf(output, "ECP\n");
  fprintf(output, "GR\n");
  shape_=NONE;
}

void Fl_PostScript_Graphics_Driver::end_polygon(){
  
  gap_=1;
  reconcat();
  fprintf(output, "EFP\n");
  fprintf(output, "GR\n");
  shape_=NONE;
}

void Fl_PostScript_Graphics_Driver::transformed_vertex(double x, double y){
  reconcat();
  if(gap_){
    clocale_printf("%g %g MT\n", x , y);
    gap_=0;
  }else
    clocale_printf("%g %g LT\n", x , y);
  concat();
}

/////////////////////////////   Clipping /////////////////////////////////////////////

void Fl_PostScript_Graphics_Driver::push_clip(int x, int y, int w, int h) {
  Clip * c=new Clip();
  clip_box(x,y,w,h,c->x,c->y,c->w,c->h);
  c->prev=clip_;
  clip_=c;
  fprintf(output, "CR\nCS\n");
  if(lang_level_<3)
    recover();
  clocale_printf("%g %g %i %i CL\n", clip_->x-0.5 , clip_->y-0.5 , clip_->w  , clip_->h);
  
}

void Fl_PostScript_Graphics_Driver::push_no_clip() {
  Clip * c = new Clip();
  c->prev=clip_;
  clip_=c;
  clip_->x = clip_->y = clip_->w = clip_->h = -1;
  fprintf(output, "CR\nCS\n");
  if(lang_level_<3)
    recover();
}

void Fl_PostScript_Graphics_Driver::pop_clip() {
  if(!clip_)return;
  Clip * c=clip_;
  clip_=clip_->prev;
  delete c;
  fprintf(output, "CR\nCS\n");
  if(clip_ && clip_->w >0)
    clocale_printf("%g %g %i %i CL\n", clip_->x - 0.5, clip_->y - 0.5, clip_->w  , clip_->h);
  // uh, -0.5 is to match screen clipping, for floats there should be something beter
  if(lang_level_<3)
    recover();
}

int Fl_PostScript_Graphics_Driver::clip_box(int x, int y, int w, int h, int &X, int &Y, int &W, int &H){
  if(!clip_){
    X=x;Y=y;W=w;H=h;
    return 1;
  }
  if(clip_->w < 0){
    X=x;Y=y;W=w;H=h;
    return 1;
  }
  int ret=0;
  if (x > (X=clip_->x)) {X=x; ret=1;}
  if (y > (Y=clip_->y)) {Y=y; ret=1;}
  if ((x+w) < (clip_->x+clip_->w)) {
    W=x+w-X;
    
    ret=1;
    
  }else
    W = clip_->x + clip_->w - X;
  if(W<0){
    W=0;
    return 1;
  }
  if ((y+h) < (clip_->y+clip_->h)) {
    H=y+h-Y;
    ret=1;
  }else
    H = clip_->y + clip_->h - Y;
  if(H<0){
    W=0;
    H=0;
    return 1;
  }
  return ret;
}

int Fl_PostScript_Graphics_Driver::not_clipped(int x, int y, int w, int h){
  if(!clip_) return 1;
  if(clip_->w < 0) return 1;
  int X, Y, W, H;
  clip_box(x, y, w, h, X, Y, W, H);
  if(W) return 1;
  return 0;
}

void Fl_PostScript_File_Device::margins(int *left, int *top, int *right, int *bottom) // to implement
{
  Fl_PostScript_Graphics_Driver *ps = driver();
  if(left) *left = (int)(ps->left_margin / ps->scale_x + .5);
  if(right) *right = (int)(ps->left_margin / ps->scale_x + .5);
  if(top) *top = (int)(ps->top_margin / ps->scale_y + .5);
  if(bottom) *bottom = (int)(ps->top_margin / ps->scale_y + .5);
}

int Fl_PostScript_File_Device::printable_rect(int *w, int *h)
//returns 0 iff OK
{
  Fl_PostScript_Graphics_Driver *ps = driver();
  if(w) *w = (int)((ps->pw_ - 2 * ps->left_margin) / ps->scale_x + .5);
  if(h) *h = (int)((ps->ph_ - 2 * ps->top_margin) / ps->scale_y + .5);
  return 0;
}

void Fl_PostScript_File_Device::origin(int *x, int *y)
{
  Fl_Paged_Device::origin(x, y);
}

void Fl_PostScript_File_Device::origin(int x, int y)
{
  x_offset = x;
  y_offset = y;
  Fl_PostScript_Graphics_Driver *ps = driver();
  ps->clocale_printf("GR GR GS %d %d TR  %f %f SC %d %d TR %f rotate GS\n",
	  ps->left_margin, ps->top_margin, ps->scale_x, ps->scale_y, x, y, ps->angle);
}

void Fl_PostScript_File_Device::scale (float s_x, float s_y)
{
  if (s_y == 0.) s_y = s_x;
  Fl_PostScript_Graphics_Driver *ps = driver();
  ps->scale_x = s_x;
  ps->scale_y = s_y;
  ps->clocale_printf("GR GR GS %d %d TR  %f %f SC %f rotate GS\n",
	  ps->left_margin, ps->top_margin, ps->scale_x, ps->scale_y, ps->angle);
}

void Fl_PostScript_File_Device::rotate (float rot_angle)
{
  Fl_PostScript_Graphics_Driver *ps = driver();
  ps->angle = - rot_angle;
  ps->clocale_printf("GR GR GS %d %d TR  %f %f SC %d %d TR %f rotate GS\n",
	  ps->left_margin, ps->top_margin, ps->scale_x, ps->scale_y, x_offset, y_offset, ps->angle);
}

void Fl_PostScript_File_Device::translate(int x, int y)
{
  fprintf(driver()->output, "GS %d %d translate GS\n", x, y);
}

void Fl_PostScript_File_Device::untranslate(void)
{
  fprintf(driver()->output, "GR GR\n");
}

int Fl_PostScript_File_Device::start_page (void)
{
  Fl_PostScript_Graphics_Driver *ps = driver();
  ps->page(ps->page_format_);
  x_offset = 0;
  y_offset = 0;
  ps->scale_x = ps->scale_y = 1.;
  ps->angle = 0;
  fprintf(ps->output, "GR GR GS %d %d translate GS\n", ps->left_margin, ps->top_margin);
  return 0;
}

int Fl_PostScript_File_Device::end_page (void)
{
  return 0;
}

void Fl_PostScript_File_Device::end_job (void)
// finishes PostScript & closes file
{
  Fl_PostScript_Graphics_Driver *ps = driver();
  if (ps->nPages) {  // for eps nPages is 0 so it is fine ....
    fprintf(ps->output, "CR\nGR\nGR\nGR\nSP\n restore\n");
    if (!ps->pages_){
      fprintf(ps->output, "%%%%Trailer\n");
      fprintf(ps->output, "%%%%Pages: %i\n" , ps->nPages);
    };
  } else
    fprintf(ps->output, "GR\n restore\n");
  fputs("%%EOF",ps->output);
  ps->reset();
  fflush(ps->output);
  if(ferror(ps->output)) {
    fl_alert ("Error during PostScript data output.");
    }
  if (ps->close_cmd_) {
    (*ps->close_cmd_)(ps->output);
  } else {
    fclose(ps->output);
    }
  while (ps->clip_){
    Fl_PostScript_Graphics_Driver::Clip * c= ps->clip_;
    ps->clip_= ps->clip_->prev;
    delete c;
  }
  Fl_Display_Device::display_device()->set_current();
}

#endif // FL_DOXYGEN

#if ! (defined(__APPLE__) || defined(WIN32) )
/** Starts a print job. */
int Fl_PostScript_Printer::start_job(int pages, int *firstpage, int *lastpage) {
  enum Fl_Paged_Device::Page_Format format;
  enum Fl_Paged_Device::Page_Layout layout;

  // first test version for print dialog
  if (!print_panel) make_print_panel();
  printing_style style = print_load();
  print_selection->deactivate();
  print_all->setonly();
  print_all->do_callback();
  print_from->value("1");
  { char tmp[10]; snprintf(tmp, sizeof(tmp), "%d", pages); print_to->value(tmp); }
  print_panel->show(); // this is modal
  while (print_panel->shown()) Fl::wait();
  
  if (!print_start) // user clicked cancel
    return 1;

  // get options

  switch (print_page_size->value()) {
    case 0:
      format = Fl_Paged_Device::LETTER;
      break;
    case 2:
      format = Fl_Paged_Device::LEGAL;
      break;
    case 3:
      format = Fl_Paged_Device::EXECUTIVE;
      break;
    case 4:
      format = Fl_Paged_Device::A3;
      break;
    case 5:
      format = Fl_Paged_Device::A5;
      break;
    case 6:
      format = Fl_Paged_Device::B5;
      break;
    case 7:
      format = Fl_Paged_Device::ENVELOPE;
      break;
    case 8:
      format = Fl_Paged_Device::DLE;
      break;
    default:
      format = Fl_Paged_Device::A4;
      }
      
  { // page range choice
    int from = 1, to = pages;
    if (print_pages->value()) {
      sscanf(print_from->value(), "%d", &from);
      sscanf(print_to->value(), "%d", &to);
    }
    if (from < 1) from = 1;
    if (to > pages) to = pages;
    if (to < from) to = from;
    if (firstpage) *firstpage = from;
    if (lastpage) *lastpage = to;
    if (pages > 0) pages = to - from + 1;
  }
  
  if (print_output_mode[0]->value()) layout = Fl_Paged_Device::PORTRAIT;
  else if (print_output_mode[1]->value()) layout = Fl_Paged_Device::LANDSCAPE;
  else if (print_output_mode[2]->value()) layout = Fl_Paged_Device::PORTRAIT;
  else layout = Fl_Paged_Device::LANDSCAPE;

  int print_pipe = print_choice->value();	// 0 = print to file, >0 = printer (pipe)

  const char *media = print_page_size->text(print_page_size->value());
  const char *printer = (const char *)print_choice->menu()[print_choice->value()].user_data();
  if (!print_pipe) printer = "<File>";

  if (!print_pipe) // fall back to file printing
    return Fl_PostScript_File_Device::start_job (pages, format, layout);

  // Print: pipe the output into the lp command...

  char command[1024];
  if (style == SystemV) snprintf(command, sizeof(command), "lp -s -d %s -n %d -t '%s' -o media=%s",
        printer, print_collate_button->value() ? 1 : (int)(print_copies->value() + 0.5), "FLTK", media);
  else snprintf(command, sizeof(command), "lpr -h -P%s -#%d -T FLTK ",
                printer, print_collate_button->value() ? 1 : (int)(print_copies->value() + 0.5));

  Fl_PostScript_Graphics_Driver *ps = driver();
  ps->output = popen(command, "w");
  if (!ps->output) {
    fl_alert("could not run command: %s\n",command);
    return 1;
  }
  ps->close_command(pclose);
  this->set_current();
  return ps->start_postscript(pages, format, layout); // start printing
}

#endif // ! (defined(__APPLE__) || defined(WIN32) )


//
// End of "$Id$".
//
