/********************************************************************************
*                                                                               *
*                        P o s t S c r i p t   O u t p u t                      *
*                                                                               *
*********************************************************************************
* Copyright (C) 2003,2026 by Jeroen van der Zijp.   All Rights Reserved.        *
*********************************************************************************
* This library is free software; you can redistribute it and/or modify          *
* it under the terms of the GNU Lesser General Public License as published by   *
* the Free Software Foundation; either version 3 of the License, or             *
* (at your option) any later version.                                           *
*                                                                               *
* This library is distributed in the hope that it will be useful,               *
* but WITHOUT ANY WARRANTY; without even the implied warranty of                *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                 *
* GNU Lesser General Public License for more details.                           *
*                                                                               *
* You should have received a copy of the GNU Lesser General Public License      *
* along with this program.  If not, see <http://www.gnu.org/licenses/>          *
********************************************************************************/
#include "xincs.h"
#include "fxver.h"
#include "fxdefs.h"
#include "fxmath.h"
#include "FXArray.h"
#include "FXHash.h"
#include "FXStream.h"



/*
  Notes:
  - Generates EPS file to a stream; you can pass in the paper
    dimensions in points.
  - There is no PostScript input.
*/

using namespace FX;

/*******************************************************************************/

namespace FX {


#ifndef FXLOADXBM
extern FXAPI FXbool fxsavePS(FXStream& store,const FXColor *data,FXint width,FXint height,FXint paperw=612,FXint paperh=792,FXint margin=35,FXbool color=true);
#endif

// Furnish our own version
extern FXAPI FXint __vsnprintf(FXchar* string,FXint length,const FXchar* format,va_list args);


// Spit output to stream
static void output(FXStream& store,const char* fmt,...){
  FXchar buffer[1024]; FXint len;
  va_list args;
  va_start(args,fmt);
  len=__vsnprintf(buffer,sizeof(buffer),fmt,args);
  va_end(args);
  store.save(buffer,len);
  }


// Save image to PostScript file
FXbool fxsavePS(FXStream& store,const FXColor* data,FXint width,FXint height,FXint paperw,FXint paperh,FXint margin,FXbool color){
  if(store.direction()==FXStreamSave){

    // Must make sense
    if(data && 0<width && 0<height && 0<paperh && 0<paperw && 0<margin){
      FXint bx,by,bxx,byy,x,y;
      const FXuchar *p;

      // Figure out scale; maximize for the paper size
      bxx=paperw-margin*2;
      byy=(height*bxx)/width;
      if((paperh-margin*2)<byy){
        byy=paperh-margin*2;
        bxx=(width*byy)/height;
        }

      bx=margin+(paperw-margin*2-bxx)/2;
      by=margin+(paperh-margin*2-byy)/2;

      // Output header
      output(store,"%%!PS-Adobe-2.0 EPSF-2.0\n");
      output(store,"%%%%Title: Image\n");
      output(store,"%%%%Creator: FOX Toolkit\n");
      output(store,"%%%%BoundingBox: %i %i %i %i\n",bx,by,bx+bxx,by+byy);
      output(store,"%%%%Pages: 1\n");
      output(store,"%%%%DocumentFonts:\n");
      output(store,"%%%%EndComments\n");
      output(store,"%%%%EndProlog\n");
      output(store,"%%%%Page: 1 1\n");
      output(store,"/origstate save def\n");
      output(store,"20 dict begin\n");

      // Color
      if(color){

        // When colorimage not available, use image instead
        output(store,"/bwproc\n");
        output(store," {  rgbproc\n");
        output(store,"    dup length 3 idiv string 0 3 0\n");
        output(store,"    5 -1 roll\n");
        output(store,"    { add 2 1 roll 1 sub dup 0 eq\n");
        output(store,"      { pop 3 idiv 3 -1 roll dup 4 -1 roll dup\n");
        output(store,"        3 1 roll 5 -1 roll put 1 add 3 0 }\n");
        output(store,"      { 2 1 roll } ifelse\n");
        output(store,"    } forall\n");
        output(store,"    pop pop pop\n");
        output(store,"} def\n");
        output(store,"systemdict /colorimage known not\n");
        output(store," { /colorimage\n");
        output(store,"     { pop pop /rgbproc exch def\n");
        output(store,"     { bwproc } image\n");
        output(store," } def\n");
        output(store,"} if\n");

        // Set up color image operator
        output(store,"/pix %i string def\n",width*3);
        output(store,"%i %i translate\n",bx,by);
        output(store,"%i %i scale\n",bxx,byy);
        output(store,"%i %i 8\n",width,height);
        output(store,"[%i 0 0 -%i 0 %i]\n",width,height,height);
        output(store,"{currentfile pix readhexstring pop}\n");
        output(store,"false 3 colorimage\n");
        output(store,"\n");

        // Output pixels
        p=(const FXuchar*)data;
        for(y=0; y<height; y++){
          for(x=0; x<width; x++,p+=4){
            output(store,"%02x",p[2]);
            output(store,"%02x",p[1]);
            output(store,"%02x",p[0]);
            }
          output(store, "\n");
          }
        }

      // Gray scale
      else{

        // Set up grayscale image operator
        output(store,"/pix %i string def\n",width);
        output(store,"%i %i translate\n",bx,by);
        output(store,"%i %i scale\n",bxx,byy);
        output(store,"%i %i 8\n",width,height);
        output(store,"[%i 0 0 -%i 0 %i]\n",width,height,height);
        output(store,"{currentfile pix readhexstring pop}\n");
        output(store,"image\n");
        output(store,"\n");

        // Output pixels
        p=(const FXuchar*)data;
        for(y=0; y<height; y++){
          for(x=0; x<width; x++,p+=4){
            output(store,"%02x",(77*(FXuint)p[2]+151*(FXuint)p[1]+28*(FXuint)p[0])/256);
            }
          output(store,"\n");
          }
        }

      // Output trailer
      output(store,"\n");
      output(store,"showpage\n");
      output(store,"end\n");
      output(store,"origstate restore\n");
      output(store,"%%%%Trailer\n");
      return true;
      }
    }
  return false;
  }

}
