//========================================================================
//
// PSOutputDev.h
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

#ifndef PSOUTPUTDEV_H
#define PSOUTPUTDEV_H

#include <aconf.h>

#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif

#include <stddef.h>
#include "config.h"
#include "Object.h"
#include "GlobalParams.h"
#include "OutputDev.h"

class GHash;
class PDFDoc;
class XRef;
class Function;
class GfxPath;
class GfxFont;
class GfxColorSpace;
class GfxDeviceGrayColorSpace;
class GfxCalGrayColorSpace;
class GfxDeviceRGBColorSpace;
class GfxCalRGBColorSpace;
class GfxDeviceCMYKColorSpace;
class GfxLabColorSpace;
class GfxICCBasedColorSpace;
class GfxIndexedColorSpace;
class GfxSeparationColorSpace;
class GfxDeviceNColorSpace;
class GfxFunctionShading;
class GfxAxialShading;
class GfxRadialShading;
class PDFRectangle;
class PSOutCustomColor;
class PSOutputDev;
class PSFontFileInfo;

//------------------------------------------------------------------------
// PSOutputDev
//------------------------------------------------------------------------

enum PSOutMode {
  psModePS,
  psModeEPS,
  psModeForm
};

enum PSFileType {
  psFile,			// write to file
  psPipe,			// write to pipe
  psStdout,			// write to stdout
  psGeneric			// write to a generic stream
};

enum PSOutCustomCodeLocation {
  psOutCustomDocSetup,
  psOutCustomPageSetup
};

typedef void (*PSOutputFunc)(void *stream, const char *data, int len);

typedef GString *(*PSOutCustomCodeCbk)(PSOutputDev *psOut,
				       PSOutCustomCodeLocation loc, int n, 
				       void *data);

class PSOutputDev: public OutputDev {
public:

  // Open a PostScript output file, and write the prolog.
  PSOutputDev(char *fileName, PDFDoc *docA,
	      int firstPageA, int lastPageA, PSOutMode modeA,
	      int imgLLXA = 0, int imgLLYA = 0,
	      int imgURXA = 0, int imgURYA = 0,
	      GBool manualCtrlA = gFalse,
	      PSOutCustomCodeCbk customCodeCbkA = NULL,
	      void *customCodeCbkDataA = NULL,
	      GBool honorUserUnitA = gFalse,
	      GBool fileNameIsUTF8 = gFalse);

  // Open a PSOutputDev that will write to a generic stream.
  PSOutputDev(PSOutputFunc outputFuncA, void *outputStreamA,
	      PDFDoc *docA,
	      int firstPageA, int lastPageA, PSOutMode modeA,
	      int imgLLXA = 0, int imgLLYA = 0,
	      int imgURXA = 0, int imgURYA = 0,
	      GBool manualCtrlA = gFalse,
	      PSOutCustomCodeCbk customCodeCbkA = NULL,
	      void *customCodeCbkDataA = NULL,
	      GBool honorUserUnitA = gFalse);

  // Destructor -- writes the trailer and closes the file.
  virtual ~PSOutputDev();

  // Check if file was successfully created.
  virtual GBool isOk() { return ok; }

  // Returns false if there have been any errors on the output stream.
  GBool checkIO();

  //---- get info about output device

  // Does this device use upside-down coordinates?
  // (Upside-down means (0,0) is the top left corner of the page.)
  virtual GBool upsideDown() { return gFalse; }

  // Does this device use drawChar() or drawString()?
  virtual GBool useDrawChar() { return gFalse; }

  // Does this device use tilingPatternFill()?  If this returns false,
  // tiling pattern fills will be reduced to a series of other drawing
  // operations.
  virtual GBool useTilingPatternFill() { return gTrue; }

  // Does this device use drawForm()?  If this returns false,
  // form-type XObjects will be interpreted (i.e., unrolled).
  virtual GBool useDrawForm() { return preload; }

  // Does this device use beginType3Char/endType3Char?  Otherwise,
  // text in Type 3 fonts will be drawn with drawChar/drawString.
  virtual GBool interpretType3Chars() { return gFalse; }

  //----- header/trailer (used only if manualCtrl is true)

  // Write the document-level header.
  void writeHeader(PDFRectangle *mediaBox, PDFRectangle *cropBox,
		   int pageRotate);

  // Write the Xpdf procset.
  void writeXpdfProcset();

  // Write the document-level setup.
  void writeDocSetup(Catalog *catalog);

  // Write the trailer for the current page.
  void writePageTrailer();

  // Write the document trailer.
  void writeTrailer();

  //----- initialization and control

  // Check to see if a page slice should be displayed.  If this
  // returns false, the page display is aborted.  Typically, an
  // OutputDev will use some alternate means to display the page
  // before returning false.
  virtual GBool checkPageSlice(Page *page, double hDPI, double vDPI,
			       int rotate, GBool useMediaBox, GBool crop,
			       int sliceX, int sliceY, int sliceW, int sliceH,
			       GBool printing,
			       GBool (*abortCheckCbk)(void *data) = NULL,
			       void *abortCheckCbkData = NULL);

  // Start a page.
  virtual void startPage(int pageNum, GfxState *state);

  // End a page.
  virtual void endPage();

  //----- save/restore graphics state
  virtual void saveState(GfxState *state);
  virtual void restoreState(GfxState *state);

  //----- update graphics state
  virtual void updateCTM(GfxState *state, double m11, double m12,
			 double m21, double m22, double m31, double m32);
  virtual void updateLineDash(GfxState *state);
  virtual void updateFlatness(GfxState *state);
  virtual void updateLineJoin(GfxState *state);
  virtual void updateLineCap(GfxState *state);
  virtual void updateMiterLimit(GfxState *state);
  virtual void updateLineWidth(GfxState *state);
  virtual void updateFillColorSpace(GfxState *state);
  virtual void updateStrokeColorSpace(GfxState *state);
  virtual void updateFillColor(GfxState *state);
  virtual void updateStrokeColor(GfxState *state);
  virtual void updateFillOverprint(GfxState *state);
  virtual void updateStrokeOverprint(GfxState *state);
  virtual void updateOverprintMode(GfxState *state);
  virtual void updateTransfer(GfxState *state);

  //----- update text state
  virtual void updateFont(GfxState *state);
  virtual void updateTextMat(GfxState *state);
  virtual void updateCharSpace(GfxState *state);
  virtual void updateRender(GfxState *state);
  virtual void updateRise(GfxState *state);
  virtual void updateWordSpace(GfxState *state);
  virtual void updateHorizScaling(GfxState *state);
  virtual void updateTextPos(GfxState *state);
  virtual void updateTextShift(GfxState *state, double shift);
  virtual void saveTextPos(GfxState *state);
  virtual void restoreTextPos(GfxState *state);

  //----- path painting
  virtual void stroke(GfxState *state);
  virtual void fill(GfxState *state);
  virtual void eoFill(GfxState *state);
  virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *strRef,
				 int paintType, int tilingType, Dict *resDict,
				 double *mat, double *bbox,
				 int x0, int y0, int x1, int y1,
				 double xStep, double yStep);
  virtual GBool shadedFill(GfxState *state, GfxShading *shading);

  //----- path clipping
  virtual void clip(GfxState *state);
  virtual void eoClip(GfxState *state);
  virtual void clipToStrokePath(GfxState *state);

  //----- text drawing
  virtual void drawString(GfxState *state, GString *s);
  virtual void endTextObject(GfxState *state);

  //----- image drawing
  virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
			     int width, int height, GBool invert,
			     GBool inlineImg, GBool interpolate);
  virtual void drawImage(GfxState *state, Object *ref, Stream *str,
			 int width, int height, GfxImageColorMap *colorMap,
			 int *maskColors, GBool inlineImg, GBool interpolate);
  virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,
			       int width, int height,
			       GfxImageColorMap *colorMap,
			       Object *maskRef, Stream *maskStr,
			       int maskWidth, int maskHeight,
			       GBool maskInvert, GBool interpolate);

#if OPI_SUPPORT
  //----- OPI functions
  virtual void opiBegin(GfxState *state, Dict *opiDict);
  virtual void opiEnd(GfxState *state, Dict *opiDict);
#endif

  //----- Type 3 font operators
  virtual void type3D0(GfxState *state, double wx, double wy);
  virtual void type3D1(GfxState *state, double wx, double wy,
		       double llx, double lly, double urx, double ury);

  //----- form XObjects
  virtual void drawForm(Ref ref);

  //----- PostScript XObjects
  virtual void psXObject(Stream *psStream, Stream *level1Stream);

  //----- miscellaneous
  void setImageableArea(int imgLLXA, int imgLLYA, int imgURXA, int imgURYA)
    { imgLLX = imgLLXA; imgLLY = imgLLYA; imgURX = imgURXA; imgURY = imgURYA; }
  void setOffset(double x, double y)
    { tx0 = x; ty0 = y; }
  void setScale(double x, double y)
    { xScale0 = x; yScale0 = y; }
  void setRotate(int rotateA)
    { rotate0 = rotateA; }
  void setClip(double llx, double lly, double urx, double ury)
    { clipLLX0 = llx; clipLLY0 = lly; clipURX0 = urx; clipURY0 = ury; }
  void setUnderlayCbk(void (*cbk)(PSOutputDev *psOut, void *data),
		      void *data)
    { underlayCbk = cbk; underlayCbkData = data; }
  void setOverlayCbk(void (*cbk)(PSOutputDev *psOut, void *data),
		     void *data)
    { overlayCbk = cbk; overlayCbkData = data; }

  void writePSChar(char c);
  void writePSBlock(char *s, int len);
  void writePS(const char *s);
  void writePSFmt(const char *fmt, ...);
  void writePSString(GString *s);
  void writePSName(const char *s);

private:

  void init(PSOutputFunc outputFuncA, void *outputStreamA,
	    PSFileType fileTypeA, PDFDoc *docA,
	    int firstPageA, int lastPageA, PSOutMode modeA,
	    int imgLLXA, int imgLLYA, int imgURXA, int imgURYA,
	    GBool manualCtrlA, GBool honorUserUnitA);
  GBool checkIfPageNeedsToBeRasterized(int pg);
  void setupResources(Dict *resDict);
  void setupFonts(Dict *resDict);
  void setupFont(GfxFont *font, Dict *parentResDict);
  PSFontFileInfo *setupEmbeddedType1Font(GfxFont *font, Ref *id);
  PSFontFileInfo *setupExternalType1Font(GfxFont *font, GString *fileName);
  PSFontFileInfo *setupEmbeddedType1CFont(GfxFont *font, Ref *id);
  PSFontFileInfo *setupEmbeddedOpenTypeT1CFont(GfxFont *font, Ref *id);
  PSFontFileInfo *setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id);
  PSFontFileInfo *setupExternalTrueTypeFont(GfxFont *font, GString *fileName,
					    int fontNum);
  PSFontFileInfo *setupEmbeddedCIDType0Font(GfxFont *font, Ref *id);
  PSFontFileInfo *setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id,
					       GBool needVerticalMetrics);
  PSFontFileInfo *setupExternalCIDTrueTypeFont(GfxFont *font,
					       GString *fileName,
					       int fontNum,
					       GBool needVerticalMetrics);
  PSFontFileInfo *setupEmbeddedOpenTypeCFFFont(GfxFont *font, Ref *id);
  PSFontFileInfo *setupExternalOpenTypeCFFFont(GfxFont *font,
					       GString *fileName);
  PSFontFileInfo *setupType3Font(GfxFont *font, Dict *parentResDict);
  GString *makePSFontName(GfxFont *font, Ref *id);
  GString *fixType1Font(GString *font, int length1, int length2);
  GBool splitType1PFA(Guchar *font, int fontSize,
		      int length1, int length2,
		      GString *textSection, GString *binSection);
  GBool splitType1PFB(Guchar *font, int fontSize,
		      GString *textSection, GString *binSection);
  GString *asciiHexDecodeType1EexecSection(GString *in);
  GBool fixType1EexecSection(GString *binSection, GString *out);
  GString *copyType1PFA(Guchar *font, int fontSize);
  GString *copyType1PFB(Guchar *font, int fontSize);
  void renameType1Font(GString *font, GString *name);
  void setupDefaultFont();
  void setupImages(Dict *resDict);
  void setupImage(Ref id, Stream *str, GBool mask, Array *colorKeyMask);
  void setupForms(Dict *resDict);
  void setupForm(Object *strRef, Object *strObj);
  void addProcessColor(double c, double m, double y, double k);
  void addCustomColor(GfxState *state, GfxSeparationColorSpace *sepCS);
  void addCustomColors(GfxState *state, GfxDeviceNColorSpace *devnCS);
  void tilingPatternFillL1(GfxState *state, Gfx *gfx, Object *strRef,
			   int paintType, int tilingType, Dict *resDict,
			   double *mat, double *bbox,
			   int x0, int y0, int x1, int y1,
			   double xStep, double yStep);
  void tilingPatternFillL2(GfxState *state, Gfx *gfx, Object *strRef,
			   int paintType, int tilingType, Dict *resDict,
			   double *mat, double *bbox,
			   int x0, int y0, int x1, int y1,
			   double xStep, double yStep);
  GBool functionShadedFill(GfxState *state,
  			   GfxFunctionShading *shading);
  GBool axialShadedFill(GfxState *state, GfxAxialShading *shading);
  GBool radialShadedFill(GfxState *state, GfxRadialShading *shading);
  void doPath(GfxPath *path);
  void doImageL1(Object *ref, GfxState *state,
		 GfxImageColorMap *colorMap,
		 GBool invert, GBool inlineImg,
		 Stream *str, int width, int height, int len);
  void doImageL1Sep(GfxState *state, GfxImageColorMap *colorMap,
		    GBool invert, GBool inlineImg,
		    Stream *str, int width, int height, int len);
  void doImageL2(Object *ref, GfxState *state,
		 GfxImageColorMap *colorMap,
		 GBool invert, GBool inlineImg,
		 Stream *str, int width, int height, int len,
		 int *maskColors, Stream *maskStr,
		 int maskWidth, int maskHeight, GBool maskInvert);
  void convertColorKeyMaskToClipRects(GfxImageColorMap *colorMap,
				      Stream *str,
				      int width, int height,
				      int *maskColors);
  void convertExplicitMaskToClipRects(Stream *maskStr,
				      int maskWidth, int maskHeight,
				      GBool maskInvert);
  void doImageL3(Object *ref, GfxState *state,
		 GfxImageColorMap *colorMap,
		 GBool invert, GBool inlineImg,
		 Stream *str, int width, int height, int len,
		 int *maskColors, Stream *maskStr,
		 int maskWidth, int maskHeight, GBool maskInvert);
  void dumpColorSpaceL2(GfxState *state, GfxColorSpace *colorSpace,
			GBool genXform, GBool updateColors,
			GBool map01);
  void dumpDeviceGrayColorSpace(GfxDeviceGrayColorSpace *cs,
				GBool genXform, GBool updateColors,
				GBool map01);
  void dumpCalGrayColorSpace(GfxCalGrayColorSpace *cs,
			     GBool genXform, GBool updateColors,
			     GBool map01);
  void dumpDeviceRGBColorSpace(GfxDeviceRGBColorSpace *cs,
			       GBool genXform, GBool updateColors,
			       GBool map01);
  void dumpCalRGBColorSpace(GfxCalRGBColorSpace *cs,
			    GBool genXform, GBool updateColors,
			    GBool map01);
  void dumpDeviceCMYKColorSpace(GfxDeviceCMYKColorSpace *cs,
				GBool genXform, GBool updateColors,
				GBool map01);
  void dumpLabColorSpace(GfxLabColorSpace *cs,
			 GBool genXform, GBool updateColors,
			 GBool map01);
  void dumpICCBasedColorSpace(GfxState *state, GfxICCBasedColorSpace *cs,
			      GBool genXform, GBool updateColors,
			      GBool map01);
  void dumpIndexedColorSpace(GfxState *state,
			     GfxIndexedColorSpace *cs,
			     GBool genXform, GBool updateColors,
			     GBool map01);
  void dumpSeparationColorSpace(GfxState *state,
				GfxSeparationColorSpace *cs,
				GBool genXform, GBool updateColors,
				GBool map01);
  void dumpDeviceNColorSpaceL2(GfxState *state, GfxDeviceNColorSpace *cs,
			       GBool genXform, GBool updateColors,
			       GBool map01);
  void dumpDeviceNColorSpaceL3(GfxState *state, GfxDeviceNColorSpace *cs,
			       GBool genXform, GBool updateColors,
			       GBool map01);
  GString *createDeviceNTintFunc(GfxDeviceNColorSpace *cs);
#if OPI_SUPPORT
  void opiBegin20(GfxState *state, Dict *dict);
  void opiBegin13(GfxState *state, Dict *dict);
  void opiTransform(GfxState *state, double x0, double y0,
		    double *x1, double *y1);
  GBool getFileSpec(Object *fileSpec, Object *fileName);
#endif
  void cvtFunction(Function *func);
  GString *filterPSName(GString *name);
  void writePSTextLine(GString *s);

  PSLevel level;		// PostScript level
  PSOutMode mode;		// PostScript mode (PS, EPS, form)
  int paperWidth;		// width of paper, in pts
  int paperHeight;		// height of paper, in pts
  GBool paperMatch;		// true if paper size is set to match each page
  int imgLLX, imgLLY,		// imageable area, in pts
      imgURX, imgURY;
  GBool preload;		// load all images into memory, and
				//   predefine forms

  PSOutputFunc outputFunc;
  void *outputStream;
  PSFileType fileType;		// file / pipe / stdout
  GBool manualCtrl;
  int seqPage;			// current sequential page number
  void (*underlayCbk)(PSOutputDev *psOut, void *data);
  void *underlayCbkData;
  void (*overlayCbk)(PSOutputDev *psOut, void *data);
  void *overlayCbkData;
  GString *(*customCodeCbk)(PSOutputDev *psOut,
			    PSOutCustomCodeLocation loc, int n, 
			    void *data);
  void *customCodeCbkData;
  GBool honorUserUnit;

  PDFDoc *doc;
  XRef *xref;			// the xref table for this PDF file

  int firstPage;		// first output page
  int lastPage;			// last output page
  char *rasterizePage;		// boolean for each page - true if page
				//   needs to be rasterized

  GList *fontInfo;		// info for each font [PSFontInfo]
  GHash *fontFileInfo;		// info for each font file [PSFontFileInfo]
  Ref *imgIDs;			// list of image IDs for in-memory images
  int imgIDLen;			// number of entries in imgIDs array
  int imgIDSize;		// size of imgIDs array
  Ref *formIDs;			// list of IDs for predefined forms
  int formIDLen;		// number of entries in formIDs array
  int formIDSize;		// size of formIDs array
  char *visitedResources;	// vector of resource objects already visited
  GBool noStateChanges;		// true if there have been no state changes
				//   since the last save
  GList *saveStack;		// "no state changes" flag for each
				//   pending save
  int numTilingPatterns;	// current number of nested tiling patterns
  int nextFunc;			// next unique number to use for a function

  GList *paperSizes;		// list of used paper sizes, if paperMatch
				//   is true [PSOutPaperSize]
  double tx0, ty0;		// global translation
  double xScale0, yScale0;	// global scaling
  int rotate0;			// rotation angle (0, 90, 180, 270)
  double clipLLX0, clipLLY0,
         clipURX0, clipURY0;
  double tx, ty;		// global translation for current page
  double xScale, yScale;	// global scaling for current page
  int rotate;			// rotation angle for current page
  double epsX1, epsY1,		// EPS bounding box (unrotated)
         epsX2, epsY2;

  GString *embFontList;		// resource comments for embedded fonts

  int processColors;		// used process colors
  PSOutCustomColor		// used custom colors
    *customColors;

  GBool haveTextClip;		// set if text has been drawn with a
				//   clipping render mode

  GBool inType3Char;		// inside a Type 3 CharProc
  GString *t3String;		// Type 3 content string
  double t3WX, t3WY,		// Type 3 character parameters
         t3LLX, t3LLY, t3URX, t3URY;
  GBool t3FillColorOnly;	// operators should only use the fill color
  GBool t3Cacheable;		// cleared if char is not cacheable
  GBool t3NeedsRestore;		// set if a 'q' operator was issued

#if OPI_SUPPORT
  int opi13Nest;		// nesting level of OPI 1.3 objects
  int opi20Nest;		// nesting level of OPI 2.0 objects
#endif

  GBool ok;			// set up ok?

  friend class WinPDFPrinter;
};

#endif
