/* $Xorg: psout.c,v 1.9 2001/03/26 15:25:12 coskrey Exp $ */
/*

Copyright 1996, 1998  The Open Group

Permission to use, copy, modify, distribute, and sell this software and its
documentation for any purpose is hereby granted without fee, provided that
the above copyright notice appear in all copies and that both that
copyright notice and this permission notice appear in supporting
documentation.

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Except as contained in this notice, the name of The Open Group shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from The Open Group.

*/
/*
 * (c) Copyright 1996 Hewlett-Packard Company
 * (c) Copyright 1996 International Business Machines Corp.
 * (c) Copyright 1996, 2000 Sun Microsystems, Inc.  All Rights Reserved.
 * (c) Copyright 1996 Novell, Inc.
 * (c) Copyright 1996 Digital Equipment Corp.
 * (c) Copyright 1996 Fujitsu Limited
 * (c) Copyright 1996 Hitachi, Ltd.
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject
 * to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * Except as contained in this notice, the names of the copyright holders
 * shall not be used in advertising or otherwise to promote the sale, use
 * or other dealings in this Software without prior written authorization
 * from said copyright holders.
 */

/*******************************************************************
**
**    *********************************************************
**    *
**    *  File:          psout.c
**    *
**    *  Contents:      Code to output PostScript to file
**    *
**    *  Created By:    Roger Helmendach (Liberty Systems)
**    *
**    *  Copyright:     Copyright 1996 The Open Group, Inc.
**    *
**    *********************************************************
**
********************************************************************/

#ifdef HAVE_DIX_CONFIG_H
#include <dix-config.h>
#endif

#include <stdlib.h>
#include <stdio.h>
#include "os.h"
#define USE_PSOUT_PRIVATE 1
#include "psout.h"
#ifdef XP_USE_FREETYPE
#include <ft2build.h>
#include FT_FREETYPE_H
#endif /* XP_USE_FREETYPE */
/* For VENDOR_STRING and VENDOR_RELEASE */
#include "site.h"

extern PsElmPtr PsCloneFillElementList(int nElms, PsElmPtr elms);
extern void PsDestroyFillElementList(int nElms, PsElmPtr elms);

/*
 *  Standard definitions
 */

static char *S_StandardDefs = "\
/d{def}bind def\
/b{bind}bind d\
/bd{b d}b d\
/x{exch}bd\
/xd{x d}bd\
/dp{dup}bd\
/t{true}bd\
/f{false}bd\
/p{pop}bd\
/r{roll}bd\
/c{copy}bd\
/i{index}bd\
/rp{repeat}bd\
/n{newpath}bd\
/w{setlinewidth}bd\
/lc{setlinecap}bd\
/lj{setlinejoin}bd\
/sml{setmiterlimit}bd\
/ds{setdash}bd\
/ie{ifelse}bd\
/len{length}bd\
/m{moveto}bd\
/rm{rmoveto}bd\
/l{lineto}bd\
/rl{rlineto}bd\
/a{arc}bd\
/an{arcn}bd\
/st{stroke}bd\
/fl{fill}bd\
/ef{eofill}bd\
/sp{showpage}bd\
/cp{closepath}bd\
/clp{clippath}bd\
/cl{clip}bd\
/pb{pathbbox}bd\
/tr{translate}bd\
/rt{rotate}bd\
/dv{div}bd\
/ml{mul}bd\
/ad{add}bd\
/ng{neg}bd\
/scl{scale}bd\
/sc{setrgbcolor}bd\
/g{setgray}bd\
/gs{gsave}bd\
/gr{grestore}bd\
/sv{save}bd\
/rs{restore}bd\
/mx{matrix}bd\
/cm{currentmatrix}bd\
/sm{setmatrix}bd\
/ccm{concatmatrix}bd\
/cc{concat}bd\
/ff{findfont}bd\
/mf{makefont}bd\
/sf{setfont}bd\
/cft{currentfont}bd\
/fd{FontDirectory}bd\
/sh{show}bd\
/stw{stringwidth}bd\
/ci{colorimage}bd\
/ig{image}bd\
/im{imagemask}bd\
/cf{currentfile}bd\
/rh{readhexstring}bd\
/str{string}bd\
/al{aload}bd\
/wh{where}bd\
/kn{known}bd\
/stp{stopped}bd\
/bg{begin}bd\
/ed{end}bd\
/fa{forall}bd\
/pi{putinterval}bd\
/mk{mark}bd\
/ctm{cleartomark}bd\
/df{definefont}bd\
/cd{currentdict}bd\
/db{20 dict dp bg}bd\
/de{ed}bd\
/languagelevel wh{p languagelevel}{1}ie\
 1 eq{/makepattern{p}bd/setpattern{p}bd/setpagedevice{p}bd}if\
/mp{makepattern}bd\
/spt{setpattern}bd\
/spd{setpagedevice}bd\
"
#ifdef XP_USE_FREETYPE
"/trmoveto{currentfont /FontMatrix get transform rm}d"
#endif /* XP_USE_FREETYPE */
;

/*
 *  Composite definitions
 *
 *
 *    XYr  -  Return X/Y dpi for device
 *
 *      XYr <xdpi> <ydpi>
 *
 *    Cs  -  Coordinate setup (for origin upper left)
 *
 *      <orient(0,1,2,3)> Cs
 *
 *    P  -  Draw a point
 *
 *      <x> <y> P
 *
 *    R  -  Add rectangle to path
 *
 *      <x> <y> <w> <h> R
 *
 *    Ac  -  Add arc to path
 *
 *      <x> <y> <w> <h> <ang1> <ang2> Ac
 *
 *    An  -  Add arc to path (counterclockwise)
 *
 *      <x> <y> <w> <h> <ang1> <ang2> An
 *
 *    Tf  -  Set font
 *
 *      <font_name> <size> <iso> Tf
 *
 *    Tfm  -  Set font with matrix
 *
 *      <font_name> <matrix> <iso> Tfm
 *
 *    T  -  Draw text
 *
 *      <text> <x> <y> T
 *
 *    Tb  -  Draw text with background color
 *
 *      <text> <x> <y> <bg_red> <bg_green> <bg_blue> Tb
 *
 *    Im1  -  Image 1 bit monochrome imagemask
 *
 *      <x> <y> <w> <h> <sw> <sh> Im1
 *
 *    Im24  -  Image 24 bit RGB color
 *
 *      <x> <y> <w> <h> <sw> <sh> Im24
 *
 *    Im1t  -  Image 1 bit monochrome imagemask (in tile)
 *
 *      <data> <x> <y> <w> <h> <sw> <sh> Im1t
 *
 *    Im24t  -  Image 24 bit RGB color (in tile)
 *
 *      <data> <x> <y> <w> <h> <sw> <sh> Im24t
 */

static char *S_CompositeDefs = "\
/XYr{/currentpagedevice wh\
  {p currentpagedevice dp /HWResolution kn\
    {/HWResolution get al p}{p 300 300}ie}{300 300}ie}bd\
/Cs{dp 0 eq{0 pHt tr XYr -1 x dv 72 ml x 1 x dv 72 ml x scl}if\
  dp 1 eq{90 rt XYr -1 x dv 72 ml x 1 x dv 72 ml x scl}if\
  dp 2 eq{pWd 0 tr XYr 1 x dv 72 ml x -1 x dv 72 ml x scl}if\
  3 eq{pHt pWd tr 90 rt XYr 1 x dv 72 ml x -1 x dv 72 ml x scl}if}bd\
/P{gs 1 w [] 0 ds 2 c m .1 ad x .1 ad x l st gr}bd\
/R{4 2 r m 1 i 0 rl 0 x rl ng 0 rl cp}bd\
/Ac{mx_ cm p 6 -2 r tr 4 2 r ng scl 0 0 .5 5 3 r a mx_ sm}bd\
/An{mx_ cm p 6 -2 r tr 4 2 r ng scl 0 0 .5 5 3 r an mx_ sm}bd\
/ISO{dp len dict bg{1 i/FID ne{d}{p p}ie}fa\
  /Encoding ISOLatin1Encoding d cd ed df}bd\
/iN{dp len str cvs dp len x 1 i 3 ad str 2 c c p x p dp 3 -1 r(ISO)pi}bd\
/Tp{{x dp iN dp fd x kn{x p dp/f_ x d ff}{dp/f_ x d x ff ISO}ie x}\
  {x dp/f_ x d ff x}ie}bd\
/Tf{Tp[x 0 0 2 i ng 0 0] dp/fm_ x d mf sf}bd\
/Tfm{Tp 1 -1 tm1_ scl tm2_ ccm dp/fm_ x d mf sf}bd\
/T{m sh}bd\
/Tb{gs sc f_ ff sf cft/FontMatrix get 3 get\
  cft/FontBBox get dp 1 get x 3 get 2 i ml 3 1 r ml\
  0 0 m 4 i stw p 4 i 4 i m fm_ cc\
  0 2 i rl dp 0 rl 0 2 i ng rl 0 3 i rl ng 0 rl cp fl p p\
  gr T}bd\
/Im1{6 4 r tr scl t [3 i 0 0 5 i 0 0]{cf str1 rh p} im}bd\
/Im1rev{6 4 r tr scl f [3 i 0 0 5 i 0 0]{cf str1 rh p} im}bd\
/Im24{gs 6 4 r tr scl 8 [3 i 0 0 5 i 0 0]{cf str3 rh p} f 3 ci}bd\
/Im1t{6 4 r tr scl t [3 i 0 0 5 i 0 0]{} im}bd\
/Im24t{gs 6 4 r tr scl 8 [3 i 0 0 5 i 0 0]{} f 3 ci}bd\
/ck2{/currentpagedevice wh \
{p dp currentpagedevice dp 3 -1 r kn \
{x get al p 3 -1 r eq 3 1 r eq and } \
{p p p p t}ie} \
{p p p t}ie}bd \
/ck1{/currentpagedevice wh \
{p dp currentpagedevice dp 3 -1 r kn \
{x get eq} {p p p t}ie} \
{p p t}ie}bd \
/mtx{scl t [3 i 0 0 5 i 0 0]}bd \
";

char *pg_orient[] = {"Portrait","Landscape","Reverse Portrait","Reverse Landscape"};
/*
 *  Setup definitions
 */

static char *S_SetupDefs = "\
 /mx_ mx d\
 /im_ mx d\
 /tm1_ mx d\
 /tm2_ mx d\
 /str3 3 str d\
 /str1 1 str d\
";

/*******************************************************************
 *                       PRIVATE FUNCTIONS                         *
 *******************************************************************/

void
S_Flush(PsOutPtr self)
{
  int len;
  
  if( self->Buf[0] == '\0' )
    return;
  
  len = strlen(self->Buf);

  /* Append a newline char ('\n') if there isn't one there already */
  if( self->Buf[len-1] != '\n' )
  {
    self->Buf[len++] = '\n';
    self->Buf[len]   = '\0';
  }

  (void)fwrite(self->Buf, len, 1, self->Fp);

  self->Buf[0] = '\0';
}

static void
S_Comment(PsOutPtr self, char *comment)
{
  S_Flush(self);
  strcpy(self->Buf, comment);
  S_Flush(self);
}

static void
S_OutDefs(PsOutPtr self, char *defs)
{
  int  i, k=0;
  S_Flush(self);
  memset(self->Buf, 0, sizeof(self->Buf));
  for( i=0 ; defs[i]!='\0' ;)
  {
    if( k>70 && (i==0 || (i && defs[i-1]!='/')) &&
        (defs[i]==' ' || defs[i]=='/' || defs[i]=='{') )
    {
      S_Flush(self);
      k = 0;
      memset(self->Buf, 0, sizeof(self->Buf));
    }
    if( k && self->Buf[k-1]==' ' && defs[i]==' ' ) { i++; continue; }
    self->Buf[k] = defs[i];
    k++; i++;
  }
  S_Flush(self);
}

void
S_OutNum(PsOutPtr self, float num)
{
  int  i;
  char buf[64];
  int  len;

  sprintf(buf, "%.3f", num);

  /* Remove any zeros at the end */
  for( i=strlen(buf)-1 ; buf[i]=='0' ; i-- ); buf[i+1] = '\0';
  /* Remove '.' if it is the last character */
  i = strlen(buf)-1; if( buf[i]=='.' ) buf[i] = '\0';

  len = strlen(self->Buf);
  if( len > 0 )
  {
    self->Buf[len++] = ' ';
    self->Buf[len]   = '\0';
  } 
  strcpy(&self->Buf[len], buf);
  if( (len+i)>70 ) S_Flush(self);
}

static void
S_OutStr(PsOutPtr self, char *txt, int txtl)
{
  int  i, k;
  char buf[1024];
  for( i=0,k=0 ; i<txtl ; i++ )
  {
    if( (txt[i]>=' ' && txt[i]<='~') &&
        txt[i]!='(' && txt[i]!=')' && txt[i]!='\\' )
      { buf[k] = txt[i]; k++; continue; }
    buf[k] = '\\'; k++;
    sprintf(&buf[k], "%03o", txt[i]&0xFF);
    /* Skip to the end of the buffer */
    while( buf[k] != '\0' )
      k++;
  }
  strcat(self->Buf, "(");
  i = strlen(self->Buf);
  memcpy(&self->Buf[i], buf, k);
  self->Buf[i+k] = '\0';
  strcat(self->Buf, ")");
  if( strlen(self->Buf)>70 ) S_Flush(self);
}

/* Same as S_OutStr() but takes |short *| instead of |char *| */
static void
S_OutStr16(PsOutPtr self, unsigned short *txt, int txtl)
{
  int  i, k;
  char buf[2048];
  for( i=0,k=0 ; i<txtl ; i++ )
  {
    if( (txt[i]>=' ' && txt[i]<='~') &&
        txt[i]!='(' && txt[i]!=')' && txt[i]!='\\' )
      { buf[k] = txt[i]; k++; continue; }
    buf[k] = '\\'; k++;
    sprintf(&buf[k], "%03o", txt[i]&0xFFFF);
    /* Skip to the end of the buffer */
    while( buf[k] != '\0' )
      k++;
  }
  strcat(self->Buf, "(");
  i = strlen(self->Buf);
  memcpy(&self->Buf[i], buf, k);
  self->Buf[i+k] = '\0';
  strcat(self->Buf, ")");
  if( strlen(self->Buf)>70 ) S_Flush(self);
}

void
S_OutTok(PsOutPtr self, char *tok, int cr)
{
  int len = strlen(self->Buf);
  if( len > 0 )
  {
    self->Buf[len++] = ' ';
    self->Buf[len]   = '\0';
  } 
  strcpy(&self->Buf[len], tok);
  if( cr ) S_Flush(self);
}

static void
S_Color(PsOutPtr self, PsOutColor clr)
{
  int   ir, ig, ib;
  ir = PSOUTCOLOR_TO_REDBITS(clr);
  ig = PSOUTCOLOR_TO_GREENBITS(clr);
  ib = PSOUTCOLOR_TO_BLUEBITS(clr);
  if( ir==ig && ig==ib )
    { S_OutNum(self, PSOUTCOLOR_BITS_TO_PSFLOAT(ir)); S_OutTok(self, "g", 1); }
  else
  {
    S_OutNum(self, PSOUTCOLOR_BITS_TO_PSFLOAT(ir));
    S_OutNum(self, PSOUTCOLOR_BITS_TO_PSFLOAT(ig));
    S_OutNum(self, PSOUTCOLOR_BITS_TO_PSFLOAT(ib));
    S_OutTok(self, "sc", 1);
  }
}

static void
S_SetPageDevice(PsOutPtr self, int orient, int count, int plex, int res,
                int wd, int ht, int isPage)
{
    float fwd = ((float)wd/(float)res)*72.;
    float fht = ((float)ht/(float)res)*72.;

#define USE_WORKAROUND_COPY_COUNT_BUG 1

#ifdef USE_WORKAROUND_COPY_COUNT_BUG
    /* Workaround (see http://xprint.mozdev.org/bugs/show_bug.cgi?id=1861 -
     * 'Need workaround for bug 1378 ...') to avoid that we print n^2 copies
     * instead of n copies.
     * The problem is that we use both /NumCopies here but pass the
     * %copy-count% to the spooler, too.
     * But we only have to use _one_ way...
     *
     * The final fix for bug 1378 (http://xprint.mozdev.org/bugs/show_bug.cgi?id=1378 -
     * "PS DDX creates n^2 copies of a job instead of n copies") will back this
     * workaround out and replace it with a better solution.
     * (see mozilla.org bug 140030
     * (http://bugzilla.mozilla.org/show_bug.cgi?id=140030 - "Setting number
     * of copies causes too many copies to print") for the initial report for
     * this issue...)
     */
    count = 1;
#endif /* USE_WORKAROUND_COPY_COUNT_BUG */

    S_OutTok(self, "/pWd", 0);
    S_OutNum(self, fwd);
    S_OutTok(self, "d /pHt", 0);
    S_OutNum(self, fht);
    S_OutTok(self, "d", 1);

  /*
   * if these are page attributes, have PostScript check to see if they
   * have changed.  If not, don't do setpagedevice, since it will cause
   * a page flush and screw up duplex printing.  Having PostScript check
   * means we don't have to keep track ourselves.
   */
    if(isPage) {
      S_OutNum(self, (float) orient);
      S_OutTok(self, "/Orientation ck1", 0);
      S_OutTok(self, "pWd pHt /PageSize ck2 and not {", 1);
    }
    S_OutTok(self, "{db", 0);

    S_OutTok(self, "/Orientation", 0);
    S_OutNum(self, (float) orient);
    S_OutTok(self, " d ", 0);
    S_OutTok(self, "/PageSize [pWd pHt] d", 0);

    S_OutTok(self, " de spd", 0);
    /*
   * save a flag to show if we failed to set orientation... determined
   * by both/either Orientation and/or PageSize, use this
   * later to set/not set orientation using Cs command.
   */
    S_OutTok(self,"}stp /orientationFailed x d", 1);
    /*
   * if these are page attributes, have PostScript check to see if they
   * have changed.  If not, don't do setpagedevice, since it will cause
   * a page flush and screw up duplex printing.  Having PostScript check
   * means we don't have to keep track ourselves.
   */
    if(isPage)
    {
      S_OutTok(self,"}if",1);

      S_OutTok(self, (plex==0)?"f":"t", 0);
      S_OutTok(self, "/Duplex ck1 ", 0);

      S_OutTok(self, (plex==2)?"t":"f", 0);
      S_OutTok(self, "/Tumble ck1 and ", 0);


      S_OutNum(self, (float)res);
      S_OutNum(self, (float)res);
      S_OutTok(self, " /HWResolution ck2 and", 0);
      
      if( count>1 )
      {
          S_OutNum(self, (float)count);
          S_OutTok(self, " /NumCopies", 0);
          S_OutTok(self, " ck1 and ", 0);
      }
      S_OutTok(self," not {",1);
    }
    S_OutTok(self, "{db", 0);

    S_OutTok(self, "/Duplex ", 0);
    S_OutTok(self, (plex==0)?"f":"t", 0);
    S_OutTok(self, " d ", 0);

    S_OutTok(self, "/Tumble ", 0);
    S_OutTok(self, (plex==2)?"t":"f", 0);
    S_OutTok(self, " d ", 0);
  
    S_OutTok(self, " /HWResolution [", 0);
    S_OutNum(self, (float)res);
    S_OutNum(self, (float)res);
    S_OutTok(self, "] d ", 0);

    if( count>1 )
    {
      S_OutTok(self, " /NumCopies", 0);
      S_OutNum(self, (float)count);
      S_OutTok(self, " d ", 0);
    }
    S_OutTok(self, " de spd}stp p", 1);

    if(isPage)
    {
      S_OutTok(self, "}if", 1);
    }
}

/*******************************************************************
 *                        PUBLIC FUNCTIONS                         *
 *******************************************************************/

FILE *
PsOut_ChangeFile(PsOutPtr self, FILE *fp)
{
  FILE *nfp;

  nfp = self->Fp;

  self->Fp = fp;

  return nfp;
}

PsOutPtr
PsOut_BeginFile(FILE *fp, char *title, int orient, int count, int plex, int res,
                int wd, int ht, Bool raw)
{
  int  i;
  char buffer[256+32]; /* enougth space for a title with 256 chars... */
/*
 *  Get ready to output PostScript header
 */
  PsOutPtr psout;
  psout = (PsOutPtr)xalloc(sizeof(PsOutRec));
  memset(psout, 0, sizeof(PsOutRec));
  psout->Fp = fp;
  psout->isRaw = raw;
  psout->pagenum = 0;

  if (!raw) {
/*
 *  Output PostScript header
 */
      /* GhostScript will rant about the missing BoundingBox if we use
       * "%!PS-Adobe-3.0 EPSF-3.0" here... */
      S_Comment(psout, "%!PS-Adobe-3.0");
#ifdef XP_USE_FREETYPE
      {
        FT_Int ftmajor = 0,
               ftminor = 0,
               ftpatch = 0; 
        extern FT_Library ftypeLibrary; /* defined in xc/lib/font/FreeType/ftfuncs.c */

        FT_Library_Version(ftypeLibrary, &ftmajor, &ftminor, &ftpatch);
        sprintf(buffer, 
                "%%%%Creator: The X Print Server's PostScript DDX "
                "(%s, release %d, FreeType version %d.%d.%d)",
                VENDOR_STRING, VENDOR_RELEASE,
                (int)ftmajor, (int)ftminor, (int)ftpatch);
      }
#else
      sprintf(buffer, 
              "%%%%Creator: The X Print Server's PostScript DDX (%s, release %d)",
              VENDOR_STRING, VENDOR_RELEASE);
#endif /* XP_USE_FREETYPE */
      S_Comment(psout, buffer);

      if (title)
      {
        sprintf(buffer, "%%%%Title: %.256s", title);
        S_Comment(psout, buffer);
      }
      S_Comment(psout, "%%EndComments");
      S_Comment(psout, "%%BeginProlog");
      S_Comment(psout, "%%BeginProcSet: XServer_PS_Functions");
      S_OutDefs(psout, S_StandardDefs);
      S_OutDefs(psout, S_CompositeDefs);
      S_Comment(psout, "%%EndProcSet");
      S_Comment(psout, "%%EndProlog");
      S_Comment(psout, "%%BeginSetup");
      /* set document level page attributes */
      S_SetPageDevice(psout, orient, count, plex, res, wd, ht, 0);
      S_Comment(psout, "%%Pages: atend");
      S_OutDefs(psout, S_SetupDefs);
      S_Comment(psout, "%%EndSetup");
  }
/*
 *  Initialize the structure
 */
  psout->CurColor    = PSOUTCOLOR_NOCOLOR;
  psout->LineWidth   = 1;
  psout->LineCap     = PsCButt;
  psout->LineJoin    = PsJMiter;
  psout->NDashes     = 0;
  psout->Dashes      = (int *)0;
  psout->FontName    = (char *)0;
  psout->FontSize    = 0;
  psout->start_image = 0;
  for( i=0 ; i<4 ; i++ ) psout->FontMtx[i] = 0.;
  psout->ImageFormat = 0;
  return(psout);
}

void
PsOut_EndFile(PsOutPtr self, int closeFile)
{
  char coms[50];
  int  i;
  
  if (!self)
    return;

  if (!self->isRaw) {
      S_Comment(self,"%%Trailer");
      sprintf(coms,"%%%%Pages: %d", self->pagenum);
      S_Comment(self, coms);
      S_Comment(self, "%%EOF");
  }
  if( self->NDashes && self->Dashes ) xfree(self->Dashes);
  if( self->FontName ) xfree(self->FontName);
  if( self->Patterns ) xfree(self->Patterns);
  if( self->Clip.rects ) xfree(self->Clip.rects);
  if( closeFile ) fclose(self->Fp);
  xfree(self);
}

void
PsOut_BeginPage(PsOutPtr self, int orient, int count, int plex, int res,
                int wd, int ht)
{
  char coms[50];

/*** comment for pagenumbers *****/

  S_Comment(self,"%%PageHeader");
  self->pagenum++;
  sprintf(coms,"%%%%Page: %d %d", self->pagenum, self->pagenum);
  S_Comment(self, coms);
  sprintf(coms,"%%%%PageOrientation: %s",pg_orient[orient]);
  S_Comment(self, coms);

/*** end comment *****************/

  /* set page level page attributes */
  S_SetPageDevice(self, orient, count, plex, res, wd, ht, 1);
  
  S_OutTok(self, "gs ", 0);
  /*
   * check to see if we set orientation already; if it wasn't set,
   * use Cs to set orientation here.
   */
  S_OutNum(self, (float)orient);
  S_OutTok(self, "orientationFailed { ", 0);
  S_OutNum(self, (float)orient);
  S_OutTok(self, " } { 0 }ie Cs 100 sml gs", 1);
}

void
PsOut_EndPage(PsOutPtr self)
{
  S_OutTok(self, "gr gr sp", 1);

  /* did grestore: mark attributes 'dirty' so they will be re-sent */
  PsOut_DirtyAttributes(self);

/*** comment for pagenumbers *****/

  S_Comment(self,"%%PageTrailer");

/*** end comment *****************/
}

void
PsOut_DirtyAttributes(PsOutPtr self)
{
  int  i;
  self->CurColor    = PSOUTCOLOR_NOCOLOR;
  self->LineWidth   = -1;
  self->LineCap     = (PsCapEnum)-1;
  self->LineJoin    = (PsJoinEnum)-1;
  self->NDashes     = -1;
  self->FontSize    = -1;
  for( i=0 ; i<4 ; i++ ) self->FontMtx[i] = -1.;
  if( self->Dashes   ) { xfree(self->Dashes);   self->Dashes   = (int *)0;  }
  if( self->FontName ) { xfree(self->FontName); self->FontName = (char *)0; }
}

void
PsOut_Comment(PsOutPtr self, char *comment)
{
  S_Comment(self, comment);
}

void
PsOut_Offset(PsOutPtr self, int x, int y)
{
  self->XOff = x;
  self->YOff = y;
}

void
PsOut_Clip(PsOutPtr self, int clpTyp, PsClipPtr clpinf)
{
  int  i, k;
  int  changed = 0;
  int  xo = self->XOff;
  int  yo = self->YOff;

  if( self->InTile ) return;
  if( self->InFrame ) xo = yo = 0;
  if( clpTyp!=self->ClipType ) changed = 1;
  else
  {
    if( clpinf->nRects!=self->Clip.nRects ) changed = 1;
    else
    {
      if( clpinf->nOutterClips!=self->Clip.nOutterClips ) changed = 1;
      else
      {
        for( i=0 ; i<clpinf->nOutterClips ; i++ )
        {
          if( memcmp(&clpinf->outterClips[i], &self->Clip.outterClips[i],
                     sizeof(PsRectRec))!=0 ) break;
        }
        if( i<clpinf->nOutterClips ) changed = 1;
        else
        {
          for( i=0 ; i<clpinf->nRects ; i++ )
          {
            if( memcmp(&clpinf->rects[i], &self->Clip.rects[i],
                       sizeof(PsRectRec))!=0 ) { changed = 1; break; }
          }
        }
      }
    }
    if( clpinf->nElms!=self->Clip.nElms ) changed = 1;
    else
    {
      for( i=0 ; i<clpinf->nElms ; i++ )
      {
        if( clpinf->elms[i].type!=PSOUT_POINTS )
        {
          if( memcmp(&clpinf->elms[i], &self->Clip.elms[i],
                     sizeof(PsElmRec))!=0 ) { changed = 1; break; }
        }
        else
        {
          if( clpinf->elms[i].type!=self->Clip.elms[i].type ||
              clpinf->elms[i].nPoints!=self->Clip.elms[i].nPoints )
            { changed = 1; break; }
          else
          {
            for( k=0 ; k<clpinf->elms[i].nPoints ; k++ )
            {
              if( memcmp(&clpinf->elms[i].c.points[k],
                         &self->Clip.elms[i].c.points[k], sizeof(PsPointRec)) )
                { changed = 1; break; }
            }
            if( changed ) break;
          }
        }
      }
    }
  }

  if( self->Clip.rects )       xfree(self->Clip.rects);
  if( self->Clip.outterClips ) xfree(self->Clip.outterClips);
  if( self->Clip.elms )
    PsDestroyFillElementList(self->Clip.nElms, self->Clip.elms);
  self->ClipType          = clpTyp;
  self->Clip.nRects       = clpinf->nRects;
  self->Clip.nElms        = clpinf->nElms;
  self->Clip.nOutterClips = clpinf->nOutterClips;
  if( clpinf->nRects )
  {
    self->Clip.rects = (PsRectPtr)xalloc(clpinf->nRects*sizeof(PsRectRec));
    memcpy(self->Clip.rects, clpinf->rects, clpinf->nRects*sizeof(PsRectRec));
  }
  else self->Clip.rects = 0;
  if( clpinf->nOutterClips )
  {
    self->Clip.outterClips = (PsRectPtr)xalloc(clpinf->nOutterClips*
					       sizeof(PsRectRec));
    memcpy(self->Clip.outterClips, clpinf->outterClips,
           clpinf->nOutterClips*sizeof(PsRectRec));
  }
  else self->Clip.outterClips = 0;
  if( clpinf->nElms )
    self->Clip.elms = PsCloneFillElementList(clpinf->nElms, clpinf->elms);
  else self->Clip.elms = 0;

  PsOut_DirtyAttributes(self);
  S_OutTok(self, "gr gs", 1);
  if( self->Clip.nOutterClips )
  {
    for( i=0 ; i<self->Clip.nOutterClips ; i++ )
    {
      S_OutNum(self, (float)(self->Clip.outterClips[i].x));
      S_OutNum(self, (float)(self->Clip.outterClips[i].y));
      S_OutNum(self, (float)self->Clip.outterClips[i].w);
      S_OutNum(self, (float)self->Clip.outterClips[i].h);
      S_OutTok(self, "R", 1);
    }
    S_OutTok(self, "cl n", 1);
  }
  if( self->Clip.nRects )
  {
    for( i=0 ; i<self->Clip.nRects ; i++ )
    {
      S_OutNum(self, (float)(self->Clip.rects[i].x+xo));
      S_OutNum(self, (float)(self->Clip.rects[i].y+yo));
      S_OutNum(self, (float)self->Clip.rects[i].w);
      S_OutNum(self, (float)self->Clip.rects[i].h);
      S_OutTok(self, "R", 1);
    }
    S_OutTok(self, "cl n", 1);
  }
  if( self->Clip.nElms )
  {
    PsElmPtr elm = self->Clip.elms;
    for( i=0 ; i<self->Clip.nElms ; i++,elm++ )
    {
      switch(elm->type)
      {
        case PSOUT_POINTS:
          for( k=0 ; k<elm->nPoints ; k++ )
          {
            S_OutNum(self, (float)elm->c.points[k].x+xo);
            S_OutNum(self, (float)elm->c.points[k].y+yo);
            if( k==0 ) S_OutTok(self, "m", 0);
            else       S_OutTok(self, "l", 0);
          }
          S_OutTok(self, "cp", 1);
          break;
        case PSOUT_RECT:
          S_OutNum(self, (float)elm->c.rect.x+xo);
          S_OutNum(self, (float)elm->c.rect.y+yo);
          S_OutNum(self, (float)elm->c.rect.w);
          S_OutNum(self, (float)elm->c.rect.h);
          S_OutTok(self, "R", 1);
          break;
        case PSOUT_ARC:
          if( elm->c.arc.style==PsPieSlice )
          {
            S_OutNum(self, (float)elm->c.arc.x+xo+(float)elm->c.arc.w/2.);
            S_OutNum(self, (float)elm->c.arc.y+yo+(float)elm->c.arc.h/2.);
            S_OutTok(self, "m", 0);
          }
          S_OutNum(self, (float)elm->c.arc.x+xo+(float)elm->c.arc.w/2.);
          S_OutNum(self, (float)elm->c.arc.y+yo+(float)elm->c.arc.h/2.);
          S_OutNum(self, (float)elm->c.arc.w);
          S_OutNum(self, (float)elm->c.arc.h);
          S_OutNum(self, (float)elm->c.arc.a1/64.);
          S_OutNum(self, (float)elm->c.arc.a1/64.+(float)elm->c.arc.a2/64.);
          if( elm->c.arc.a2<0 ) S_OutTok(self, "An cp", 1);
          else                  S_OutTok(self, "Ac cp", 1);
          break;
      }
    }
    S_OutTok(self, "cl n", 1);
  }
}

void
PsOut_Color(PsOutPtr self, PsOutColor clr)
{
  if( clr==self->CurColor || self->InTile>=PsStip ) return;
  self->CurColor = clr;
  S_Color(self, clr);
}

void
PsOut_FillRule(PsOutPtr self, PsRuleEnum rule)
{
  self->FillRule = rule;
}

void
PsOut_LineAttrs(PsOutPtr self, int wd, PsCapEnum cap, PsJoinEnum join,
                int nDsh, int *dsh, int dshOff, PsOutColor bclr)
{
  int         i;
  int         same = 1;

  if( wd!=self->LineWidth && wd>=0 )
  {
    if( wd==0 ) wd = 1;
    self->LineWidth = wd;
    S_OutNum(self, (float)wd); S_OutTok(self, "w", 1);
  }
  if( cap!=self->LineCap )
  {
    self->LineCap = cap;
    S_OutNum(self, (float)cap); S_OutTok(self, "lc", 1);
  }
  if( join!=self->LineJoin )
  {
    self->LineJoin = join;
    S_OutNum(self, (float)join); S_OutTok(self, "lj", 1);
  }
  if( nDsh!=self->NDashes ) same = 0;
  else if( dshOff!=self->DashOffset ) same = 0;
  else if( nDsh )
  {
    for( i=0 ; i<nDsh ; i++ )
      { if( dsh[i]!=self->Dashes[i] ) break; }
    if( i<nDsh ) same = 0;
  }
  if( !same )
  {
    if( self->NDashes && self->Dashes )
      { xfree(self->Dashes); self->Dashes = (int *)0; }
    self->NDashes    = nDsh;
    self->DashOffset = dshOff;
    if( nDsh ) self->Dashes = (int *)xalloc(sizeof(int)*nDsh);
    S_OutTok(self, "[", 0);
    for( i=0 ; i<nDsh ; i++ )
    {
      self->Dashes[i] = dsh[i];
      S_OutNum(self, (float)dsh[i]);
    }
    S_OutTok(self, "]", 0);
    S_OutNum(self, (float)dshOff);
    S_OutTok(self, "ds", 1);
  }

  if( nDsh )
    self->LineBClr = bclr;
  else
    bclr = PSOUTCOLOR_NOCOLOR;
}

void
PsOut_TextAttrs(PsOutPtr self, char *fnam, int siz, int iso)
{
  int       i;
  char      buf[256];
  if( self->FontName && strcmp(fnam, self->FontName)==0 &&
      siz==self->FontSize ) return;
  if( self->FontName ) xfree(self->FontName);
  self->FontName = (char *)xalloc(strlen(fnam)+1);
  strcpy(self->FontName, fnam);
  self->FontSize = siz;
  for( i=0 ; i<4 ; i++ ) self->FontMtx[i] = -1.;
  strcpy(buf, "/"); strcat(buf, fnam);
  S_OutTok(self, buf, 0);
  S_OutNum(self, (float)siz);
  if( iso ) S_OutTok(self, "t", 0);
  else      S_OutTok(self, "f", 0);
  S_OutTok(self, "Tf", 1);
}

void
PsOut_TextAttrsMtx(PsOutPtr self, char *fnam, float *mtx, int iso)
{
  int       i;
  char      buf[256];
  if( self->FontName && strcmp(fnam, self->FontName)==0 &&
      mtx[0]==self->FontMtx[0] && mtx[1]==self->FontMtx[1] &&
      mtx[2]==self->FontMtx[2] && mtx[3]==self->FontMtx[3] ) return;
  if( self->FontName ) xfree(self->FontName);
  self->FontName = (char *)xalloc(strlen(fnam)+1);
  strcpy(self->FontName, fnam);
  for( i=0 ; i<4 ; i++ ) self->FontMtx[i] = mtx[i];
  self->FontSize = -1;
  strcpy(buf, "/"); strcat(buf, fnam); strcat(buf, " [");
  S_OutTok(self, buf, 0);
  for( i=0 ; i<4 ; i++ ) S_OutNum(self, mtx[i]);
  S_OutTok(self, "0 0]", 0);
  if( iso ) S_OutTok(self, "t", 0);
  else      S_OutTok(self, "f", 0);
  S_OutTok(self, "Tfm", 1);
}

void
PsOut_Polygon(PsOutPtr self, int nPts, PsPointPtr pts)
{
  int  i;
  int  xo = self->XOff;
  int  yo = self->YOff;

  if( self->InFrame || self->InTile ) xo = yo = 0;
  if( nPts<=2 ) return;
  for( i=0 ; i<nPts ; i++ )
  {
    S_OutNum(self, (float)(pts[i].x+xo));
    S_OutNum(self, (float)(pts[i].y+yo));
    if( i==0 ) S_OutTok(self, "m", 0);
    else       S_OutTok(self, "l", 0);
  }
  if( self->FillRule==PsEvenOdd ) S_OutTok(self, "cp ef", 1);
  else                            S_OutTok(self, "cp fl", 1);
}

void
PsOut_FillRect(PsOutPtr self, int x, int y, int w, int h)
{
  int  xo = self->XOff;
  int  yo = self->YOff;

  if( self->InFrame || self->InTile ) xo = yo = 0;
  x += xo; y += yo;
  S_OutNum(self, (float)x);
  S_OutNum(self, (float)y);
  S_OutNum(self, (float)w);
  S_OutNum(self, (float)h);
  S_OutTok(self, "R fl", 1);
}

void
PsOut_FillArc(PsOutPtr self, int x, int y, int w, int h,
	      float ang1, float ang2, PsArcEnum style)
{
  int  xo = self->XOff;
  int  yo = self->YOff;

  if( self->InFrame || self->InTile ) xo = yo = 0;
  x += xo; y += yo;
  if( style==PsPieSlice )
  {
    S_OutNum(self, (float)x+(float)w/2.);
    S_OutNum(self, (float)y+(float)h/2.);
    S_OutTok(self, "m", 0);
  }
  S_OutNum(self, (float)x+(float)w/2.);
  S_OutNum(self, (float)y+(float)h/2.);
  S_OutNum(self, (float)w);
  S_OutNum(self, (float)h);
  S_OutNum(self, ang1);
  S_OutNum(self, ang1+ang2);
  if( ang2<0 ) S_OutTok(self, "An cp fl", 1);
  else         S_OutTok(self, "Ac cp fl", 1);
}

void
PsOut_Lines(PsOutPtr self, int nPts, PsPointPtr pts)
{
  int  i;
  int  xo = self->XOff;
  int  yo = self->YOff;

  if( self->InFrame || self->InTile ) xo = yo = 0;
  if( nPts<1 ) return;
  for( i=0 ; i<nPts ; i++ )
  {
    S_OutNum(self, (float)(pts[i].x+xo));
    S_OutNum(self, (float)(pts[i].y+yo));
    if( i==0 ) S_OutTok(self, "m", 0);
    else       S_OutTok(self, "l", 0);
  }
  if( self->LineBClr != PSOUTCOLOR_NOCOLOR )
  {
    S_OutTok(self, "gs", 0);
    S_Color(self, self->LineBClr);
    S_OutTok(self, "[] 0 ds st gr", 0);
  }
  S_OutTok(self, "st", 1);
}

void
PsOut_Points(PsOutPtr self, int nPts, PsPointPtr pts)
{
  int  i;
  int  xo = self->XOff;
  int  yo = self->YOff;

  if( self->InFrame || self->InTile ) xo = yo = 0;
  if( nPts<1 ) return;
  for( i=0 ; i<nPts ; i++ )
  {
    S_OutNum(self, (float)(pts[i].x+xo));
    S_OutNum(self, (float)(pts[i].y+yo));
    S_OutTok(self, "P", 1);
  }
}

void
PsOut_DrawRect(PsOutPtr self, int x, int y, int w, int h)
{
  int  xo = self->XOff;
  int  yo = self->YOff;

  if( self->InFrame || self->InTile ) xo = yo = 0;
  x += xo; y += yo;
  S_OutNum(self, (float)x);
  S_OutNum(self, (float)y);
  S_OutNum(self, (float)w);
  S_OutNum(self, (float)h);
  S_OutTok(self, "R", 0);
  if( self->LineBClr != PSOUTCOLOR_NOCOLOR )
  {
    S_OutTok(self, "gs", 0);
    S_Color(self, self->LineBClr);
    S_OutTok(self, "[] 0 ds st gr", 0);
  }
  S_OutTok(self, "st", 1);
}

void
PsOut_DrawArc(PsOutPtr self, int x, int y, int w, int h, 
	      float ang1, float ang2)
{
  int  xo = self->XOff;
  int  yo = self->YOff;

  if( self->InFrame || self->InTile ) xo = yo = 0;
  x += xo; y += yo;
  S_OutNum(self, (float)x+(float)w/2.);
  S_OutNum(self, (float)y+(float)h/2.);
  S_OutNum(self, (float)w);
  S_OutNum(self, (float)h);
  S_OutNum(self, ang1);
  S_OutNum(self, ang1+ang2);
  if( ang2<0 ) S_OutTok(self, "An", 0);
  else         S_OutTok(self, "Ac", 0);
  if( self->LineBClr != PSOUTCOLOR_NOCOLOR )
  {
    S_OutTok(self, "gs", 0);
    S_Color(self, self->LineBClr);
    S_OutTok(self, "[] 0 ds st gr", 0);
  }
  S_OutTok(self, "st", 1);
}

void
PsOut_Text(PsOutPtr self, int x, int y, char *text, int textl, PsOutColor bclr)
{
  int  xo = self->XOff;
  int  yo = self->YOff;

  if( self->InFrame || self->InTile ) xo = yo = 0;
  x += xo; y += yo;
  S_OutStr(self, text, textl);
  S_OutNum(self, (float)x);
  S_OutNum(self, (float)y);
  if( bclr == PSOUTCOLOR_NOCOLOR )
    S_OutTok(self, "T", 1);
  else
  {
    int ir = PSOUTCOLOR_TO_REDBITS(bclr);
    int ig = PSOUTCOLOR_TO_GREENBITS(bclr);
    int ib = PSOUTCOLOR_TO_BLUEBITS(bclr);

    S_OutNum(self, PSOUTCOLOR_BITS_TO_PSFLOAT(ir));
    S_OutNum(self, PSOUTCOLOR_BITS_TO_PSFLOAT(ig));
    S_OutNum(self, PSOUTCOLOR_BITS_TO_PSFLOAT(ib));
    S_OutTok(self, "Tb", 1);
  }
}

void
PsOut_Text16(PsOutPtr self, int x, int y, unsigned short *text, int textl, PsOutColor bclr)
{
  int  xo = self->XOff;
  int  yo = self->YOff;

  if( self->InFrame || self->InTile ) xo = yo = 0;
  x += xo; y += yo;
  S_OutStr16(self, text, textl);
  S_OutNum(self, (float)x);
  S_OutNum(self, (float)y);
  if( bclr == PSOUTCOLOR_NOCOLOR )
    S_OutTok(self, "T", 1);
  else
  {
    int ir = PSOUTCOLOR_TO_REDBITS(bclr);
    int ig = PSOUTCOLOR_TO_GREENBITS(bclr);
    int ib = PSOUTCOLOR_TO_BLUEBITS(bclr);
    S_OutNum(self, PSOUTCOLOR_BITS_TO_PSFLOAT(ir));
    S_OutNum(self, PSOUTCOLOR_BITS_TO_PSFLOAT(ig));
    S_OutNum(self, PSOUTCOLOR_BITS_TO_PSFLOAT(ib));
    S_OutTok(self, "Tb", 1);
  }
}

#ifdef BM_CACHE
void  /* new */
PsOut_ImageCache(PsOutPtr self, int x, int y, long cache_id, PsOutColor bclr, PsOutColor fclr)
{
  char cacheID[10];
  int xo = self->XOff;
  int yo = self->YOff;

  if( self->InFrame || self->InTile ) xo = yo = 0;
  x += xo; y += yo;
  sprintf(cacheID, "c%di", cache_id);

  S_OutNum(self, (float)x);
  S_OutNum(self, (float)y);

  if( fclr==PSOUTCOLOR_WHITE )
  {
    int ir = PSOUTCOLOR_TO_REDBITS(bclr);
    int ig = PSOUTCOLOR_TO_GREENBITS(bclr);
    int ib = PSOUTCOLOR_TO_BLUEBITS(bclr);

    if( ir==ig && ig==ib )
      S_OutNum(self, PSOUTCOLOR_BITS_TO_PSFLOAT(ir));
    else
      S_OutNum(self, (float)0);
      self->RevImage = 1;
    }
  else
  {
    int ir = PSOUTCOLOR_TO_REDBITS(fclr);
    int ig = PSOUTCOLOR_TO_GREENBITS(fclr);
    int ib = PSOUTCOLOR_TO_BLUEBITS(fclr);

    if( ir==ig && ig==ib )
      S_OutNum(self, PSOUTCOLOR_BITS_TO_PSFLOAT(ir));
    else
      S_OutNum(self, (float)0);
  }

  S_OutTok(self, cacheID, 1);
}     /* new */

void  /* new */
PsOut_BeginImageCache(PsOutPtr self, long cache_id)
{
  char cacheID[10];

  sprintf(cacheID, "/c%di {", cache_id);

  S_OutTok(self, cacheID, 0);
}     /* new */

void  /* new */
PsOut_EndImageCache(PsOutPtr self)
{
  S_OutTok(self, "}bd", 1);
}     /* new */
#endif

void
PsOut_BeginImage(PsOutPtr self, PsOutColor bclr, PsOutColor fclr, int x, int y,
                 int w, int h, int sw, int sh, int format)
{
  PsOutColor savClr = self->CurColor;
  int xo = self->XOff;
  int yo = self->YOff;

  if( self->InFrame || self->InTile ) xo = yo = 0;
  x += xo; y += yo;
  if( self->InTile )
  {
    if( self->InTile>=PsStip && format!=1 ) { self->ImgSkip = 1; return; }
    self->ImgBClr = bclr; self->ImgFClr = fclr;
    self->ImgX    = x;    self->ImgY    = y;
    self->ImgW    = w;    self->ImgH    = h;
    self->SclW    = sw;   self->SclH    = sh;
    S_OutTok(self, "<", 0);
    self->ImageFormat = format;
    self->RevImage = 0;
    if( self->InTile==PsTile && format==1 && fclr==PSOUTCOLOR_WHITE )
      self->RevImage = 1;
    return;
  }

  self->RevImage = 0;
  if( format==1 )
  {
    S_OutTok(self, "gs", 0);
    if( fclr==PSOUTCOLOR_WHITE )
    {
      PsOut_Color(self, fclr);
      PsOut_FillRect(self, x, y, sw, sh);
      PsOut_Color(self, bclr);
      self->RevImage = 1;
    }
    else
    {
      PsOut_Color(self, bclr);
      PsOut_FillRect(self, x, y, sw, sh);
      PsOut_Color(self, fclr);
    }
  }
  S_OutNum(self, (float)x);
  S_OutNum(self, (float)y);
  S_OutNum(self, (float)w);
  S_OutNum(self, (float)h);
  S_OutNum(self, (float)sw);
  S_OutNum(self, (float)sh);
  if( format==1 ) {
	if(self->RevImage) 
	    S_OutTok(self, "Im1rev", 1);
	else
	    S_OutTok(self, "Im1", 1);
  }
  else            S_OutTok(self, "Im24", 1);
  self->ImageFormat = format;
  self->CurColor    = savClr;
}

void
PsOut_BeginImageIM(PsOutPtr self, PsOutColor bclr, PsOutColor fclr, int x, int y,
                 int w, int h, int sw, int sh, int format)
{
  PsOutColor savClr = self->CurColor;
  int xo = self->XOff;
  int yo = self->YOff;

  if( self->InFrame || self->InTile ) xo = yo = 0;
  x += xo; y += yo;
  if( self->InTile )
  {
    if( self->InTile>=PsStip && format!=1 ) { self->ImgSkip = 1; return; }
    self->ImgBClr = bclr; self->ImgFClr = fclr;
    self->ImgX    = x;    self->ImgY    = y;
    self->ImgW    = w;    self->ImgH    = h;
    self->SclW    = sw;   self->SclH    = sh;
    S_OutTok(self, "<", 0);
    self->ImageFormat = format;
    self->RevImage = 0;
    if( self->InTile==PsTile && format==1 && fclr==PSOUTCOLOR_WHITE )
      self->RevImage = 1;
    return;
  }

  self->RevImage = 0;
  if( format==1 )
  {
    S_OutTok(self, "gs", 0);
#ifdef BM_CACHE
    S_OutTok(self, "g", 1);
#else
    if( fclr==PSOUTCOLOR_WHITE )
    {
      PsOut_Color(self, bclr);
      self->RevImage = 1;
    }
    else
    {
      PsOut_Color(self, fclr);
    }
#endif
  }

#ifdef BM_CACHE
  S_OutTok(self, "tr", 0);    /* new */
#else
  S_OutNum(self, (float)x);
  S_OutNum(self, (float)y);
#endif
  S_OutNum(self, (float)w);
  S_OutNum(self, (float)h);
  S_OutNum(self, (float)sw);
  S_OutNum(self, (float)sh);
#ifdef BM_CACHE
  S_OutTok(self, "mtx", 1);   /* new */
  S_OutTok(self, "<", 0);     /* new */
  self->start_image = 1;
#else
  if( format==1 ){
        if(self->RevImage)
            S_OutTok(self, "Im1rev", 1);
        else
            S_OutTok(self, "Im1", 1);
  }
  else      S_OutTok(self, "Im24", 1);
#endif
  self->ImageFormat = format;
  self->CurColor    = savClr;
}

void
PsOut_EndImage(PsOutPtr  self)
{
  if( self->ImgSkip ) { self->ImgSkip = 0; return; }
  if( self->InTile )
  {
    S_OutTok(self, ">", 1);
    if( self->ImageFormat==1 && self->InTile==PsTile )
    {
      if( self->ImgFClr==PSOUTCOLOR_WHITE )
      {
        PsOut_Color(self, self->ImgFClr);
        PsOut_FillRect(self, self->ImgX, self->ImgY, self->SclW, self->SclH);
        PsOut_Color(self, self->ImgBClr);
      }
      else
      {
        PsOut_Color(self, self->ImgBClr);
        PsOut_FillRect(self, self->ImgX, self->ImgY, self->SclW, self->SclH);
        PsOut_Color(self, self->ImgFClr);
      }
    }
    S_OutNum(self, (float)self->ImgX);
    S_OutNum(self, (float)self->ImgY);
    S_OutNum(self, (float)self->ImgW);
    S_OutNum(self, (float)self->ImgH);
    S_OutNum(self, (float)self->SclW);
    S_OutNum(self, (float)self->SclH);
    if( self->ImageFormat==1 ) S_OutTok(self, "Im1t", 1);
    else                       S_OutTok(self, "Im24t", 1);
    self->ImageFormat = 0;
    self->RevImage    = 0;
    return;
  }
  /*
   * Bug 4639307: Move flush before "> im" to get all of bitmap into ps file.
   */
  S_Flush(self);
#ifdef BM_CACHE
  if(self->start_image)
    S_OutTok(self, "> im", 1);       /* new */
#endif
  self->ImageFormat = 0;
  self->RevImage    = 0;
#ifdef BM_CACHE
  if(self->start_image)
  {
    self->start_image = 0;
    S_OutTok(self, "gr", 0);
  }
  else
    S_OutTok(self, "gr", 1);
#else
  S_OutTok(self, "gr", 1);
#endif
}

void
PsOut_OutImageBytes(PsOutPtr self, int nBytes, char *bytes)
{
  int   i;
  int   b;
  int   len;
  const char hex[] = { '0', '1', '2', '3', '4', '5', '6', '7',
                       '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };

  if( (!self->ImageFormat) || self->ImgSkip ) return;

  len = strlen(self->Buf);

  for( i=0 ; i<nBytes ; i++ )
  {
    if( self->RevImage ) b = (int)((bytes[i]^0xFF)&0xFF);
    else                 b = (int)(bytes[i]&0xFF);
    
    self->Buf[len++] = hex[(b&0xF0) >> 4];
    self->Buf[len++] = hex[(b&0x0F)];
    self->Buf[len] = '\0';

    if( len>70 ) 
    {
      S_Flush(self);
      len = 0;
    }
  }
}

void
PsOut_BeginFrame(PsOutPtr self, int xoff, int yoff, int x, int y,
                 int w, int h)
{
  int  xo = self->XOff;
  int  yo = self->YOff;

  if( self->InFrame ) xo = yo = 0;
  S_OutTok(self, "gs", 0);
  S_OutNum(self, (float)(x+xo));
  S_OutNum(self, (float)(y+yo));
  S_OutNum(self, (float)w);
  S_OutNum(self, (float)h);
  S_OutTok(self, "R cl n", 0);
  xoff += xo; yoff += yo;
  if( xoff || yoff )
  {
    S_OutNum(self, (float)xoff);
    S_OutNum(self, (float)yoff);
    S_OutTok(self, "tr", 0);
  }
  S_OutTok(self, "gs", 1);
  self->InFrame += 1;
}

void
PsOut_EndFrame(PsOutPtr self)
{
  self->InFrame -= 1;
  if( self->InFrame<0 ) self->InFrame = 0;
  S_OutTok(self, "gr gr", 1);
  PsOut_DirtyAttributes(self);
}

int
PsOut_BeginPattern(PsOutPtr self, void *tag, int w, int h, PsFillEnum type,
                   PsOutColor bclr, PsOutColor fclr)
{
  int   i;
  char  key[64];

  for( i=0 ; i<self->NPatterns ; i++ )
    { if( self->Patterns[i].tag==tag && self->Patterns[i].type==type ) break; }
  if( i<self->NPatterns ) return(1);
  if( (self->NPatterns+1)>self->MxPatterns )
  {
    if( self->Patterns )
    {
      self->MxPatterns *= 2;
      self->Patterns =
        (PsPatPtr)xrealloc(self->Patterns, sizeof(PsPatRec)*self->MxPatterns);
    }
    else
    {
      self->MxPatterns = 64;
      self->Patterns = (PsPatPtr)xalloc(sizeof(PsPatRec)*self->MxPatterns);
    }
  }
  self->Patterns[self->NPatterns].tag  = tag;
  self->Patterns[self->NPatterns].type = type;
  sprintf(key, "/ %d", (int)tag);
  switch(type) {
    case PsTile:   key[1] = 't'; break;
    case PsStip:   key[1] = 's'; break;
    case PsOpStip: key[1] = 'o'; break; }
  S_OutTok(self, key, 0);
  S_OutTok(self, "db/PatternType 1 d/PaintType 1 d", 0);
  S_OutTok(self, "/TilingType 1 d/BBox[0 0", 0);
  S_OutNum(self, (float)w);
  S_OutNum(self, (float)h);
  S_OutTok(self, "]d/XStep", 0);
  S_OutNum(self, (float)w);
  S_OutTok(self, "d/YStep", 0);
  S_OutNum(self, (float)h);
  S_OutTok(self, "d/PaintProc{bg sv", 1);
  if( type==PsOpStip )
  {
    S_Color(self, bclr);
    S_OutTok(self, "0 0", 0);
    S_OutNum(self, (float)w);
    S_OutNum(self, (float)h);
    S_OutTok(self, "R fl", 1);
  }
  if( type!=PsTile ) S_Color(self, fclr);
  self->NPatterns += 1;
  self->InTile = type;
  return(0);
}

void
PsOut_EndPattern(PsOutPtr self)
{
  self->InTile = PsSolid;
  S_OutTok(self, "rs ed}d de im_ mp d", 1);
}

void
PsOut_SetPattern(PsOutPtr self, void *tag, PsFillEnum type)
{
  int   i;
  char  key[64];

  for( i=0 ; i<self->NPatterns ; i++ )
    { if( tag==self->Patterns[i].tag && type==self->Patterns[i].type ) break; }
  if( i>=self->NPatterns ) return;
  sprintf(key, " %d", (int)tag);
  switch(type) {
    case PsTile:   key[0] = 't'; break;
    case PsStip:   key[0] = 's'; break;
    case PsOpStip: key[0] = 'o'; break; }
  S_OutTok(self, key, 0);
  S_OutTok(self, "spt", 1);
  self->CurColor = PSOUTCOLOR_NOCOLOR;
}

void
PsOut_RawData(PsOutPtr self, char *data, int len)
{
    S_Flush(self);
    if (!ferror(self->Fp)) {
	(void) fwrite(data, 1, len, self->Fp);
    }
}

typedef enum PsDownfontFontType_  
{ 
  PsDFT_Type1PFA=0,
  PsDFT_Type1PFB,
  PsDFT_TrueType /* not implemented yet */
} PsDownfontFontType;

/* Download a PS Type1 font */
int
PsOut_DownloadType1(PsOutPtr self, const char *auditmsg, const char *name, const char *fname)
{
  int     i;
  int     stt;
  char    buf[256];
  FILE   *fp;
  PsDownfontFontType type;

  fp = fopen(fname, "r");
  if( !fp )
    return 0;

#ifdef DEBUG_gisburn
  /* This should be log-able! */
  fprintf(stderr, "PsOut_DownloadType1: %s: Downloading '%s' from '%s'\n", auditmsg, name, fname);
#endif /* DEBUG_gisburn */

  fread(buf, 32, 1, fp);
  fseek(fp, (long)0, 0);

  /* Is this a Adobe PostScript Type 1 binary font (PFB) ? */
  if( (buf[0]&0xFF)==0x80 && (buf[1]&0xFF)==0x01 )
  {
    type = PsDFT_Type1PFB;
  }  
  /* Is this a Adobe PostScript ASCII font (PFA) ? */
  else if (!strncmp(buf, "%!PS-AdobeFont", 14))
  {
    type = PsDFT_Type1PFA;
  }
  else
  {
    /* This should be log-able! */
    fprintf(stderr, "PsOut_DownloadType1: Unknown font type for '%s'\n", fname);
    return 0;
  }      

  S_Flush(self);
  sprintf(buf, "%%%%BeginFont: %s", name);
  S_Comment(self, buf);

  if( type == PsDFT_Type1PFB )
  {
    char *buf, 
         *pt;
    int   len, 
          ch,
          stype;

    ch = fgetc(fp);  
    /* Strip out the binary headers and de-binary it */
    while( (ch&0xFF) == 0x80 ) 
    {
      stype = fgetc(fp);
      if( stype==3 ) /* eof mark */
        break;
      len = fgetc(fp);
      len |= fgetc(fp)<<8;
      len |= fgetc(fp)<<16;
      len |= fgetc(fp)<<24;
      buf = (char *)xalloc(len+1);
      if( stype==1 ) 
      {
        /* Process ASCII section */
        len = fread(buf, 1, len, fp);
        /* convert any lone CRs (ie Mac eol) to LFs */
        for( pt = buf ; (pt = memchr(pt, '\r', len-(pt-buf))) != NULL ; pt++ ) 
        {
          if ( pt[1]!='\n' ) 
            *pt = '\n';
        }
        fwrite(buf, 1, len, self->Fp);
      } 
      else if( stype==2 ) 
      {
        int i;
        
        /* Process binary section */
        len = fread(buf, 1, len, fp);
        for( i=0 ; i<len ; i++ ) 
        {
          ch = buf[i];
          if( ((ch>>4)&0xf) <= 9 )
            fputc('0'+((ch>>4)&0xf), self->Fp);
          else
            fputc('A'-10+((ch>>4)&0xf), self->Fp);
          
          if( (ch&0xf) <= 9 )
            fputc('0'+(ch&0xf), self->Fp);
          else
            fputc('A'-10+(ch&0xf), self->Fp);
          
          if( (i&0x1f)==0x1f )
              fputc('\n', self->Fp);
        }
      }
      xfree(buf);
      
      /* Next block... */
      ch = fgetc(fp);
    }
  }
  /* Is this a Adobe PostScript ASCII font (PFA) ? */
  else if (type == PsDFT_Type1PFA)
  {
    for(;;)
    {
      stt = fread(buf, 1, 256, fp);
      if( stt<=0 ) break;
      if (!ferror(self->Fp)) {
        (void) fwrite(buf, 1, stt, self->Fp);
      }
      if( stt<256 )
        break;
    }
  }
  fclose(fp);
  S_Flush(self);
  S_Comment(self, "%%EndFont");
  
  /* Success... */
  return 1;
}






