root/coders/ps3.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. SerializeHuffman2DImage
  2. Huffman2DEncodeImage
  3. SerializeHuffman2DImage
  4. Huffman2DEncodeImage
  5. JPEGEncodeImage
  6. RegisterPS3Image
  7. SerializePseudoClassImage
  8. SerializeMultiChannelImage
  9. SerializeSingleChannelImage
  10. UnregisterPS3Image
  11. MaskWriteByteHook
  12. WritePS3MaskImage
  13. WritePS3Image
  14. ZLIBAllocFunc
  15. ZLIBFreeFunc
  16. ZLIBEncode2Image
  17. ZLIBEncode2Image

/*
% Copyright (C) 2003 GraphicsMagick Group
% Copyright (C) 2002 ImageMagick Studio
% Copyright 1991-1999 E. I. du Pont de Nemours and Company
%
% This program is covered by multiple licenses, which are described in
% Copyright.txt. You should have received a copy of Copyright.txt with this
% package; otherwise see http://www.graphicsmagick.org/www/Copyright.html.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%                            PPPP   SSSSS  33333                              %
%                            P   P  SS        33                              %
%                            PPPP    SSS    333                               %
%                            P         SS     33                              %
%                            P      SSSSS  33333                              %
%                                                                             %
%                                                                             %
%                    Write Postscript Level III Format.                       %
%                                                                             %
%                                                                             %
%                              Software Design                                %
%                                John Cristy                                  %
%                                 July 1992                                   %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%
*/

/*
  Include declarations.
*/
#include "magick/studio.h"
#include "magick/analyze.h"
#include "magick/attribute.h"
#include "magick/blob.h"
#include "magick/pixel_cache.h"
#include "magick/channel.h"
#include "magick/color.h"
#include "magick/compress.h"
#include "magick/constitute.h"
#include "magick/magick.h"
#include "magick/map.h"
#include "magick/monitor.h"
#include "magick/tempfile.h"
#include "magick/utility.h"
#include "magick/version.h"
#if defined(HasTIFF)
#define CCITTParam  "-1"
#else
#define CCITTParam  "0"
#endif

/* For consistent PS and C code usage */
#define PS3_NoCompression "0"
#define PS3_FaxCompression "1"
#define PS3_RLECompression "2"
#define PS3_LZWCompression "3"
#define PS3_ZipCompression "4"
#define PS3_JPEGCompression "5"

#define PS3_RGBColorspace "0"
#define PS3_CMYKColorspace "1"

#define PS3_DirectClass "0"
#define PS3_PseudoClass "1"

/*
  Forward declarations.
*/
static unsigned int
  WritePS3Image(const ImageInfo *,Image *),
  ZLIBEncode2Image(Image *,const size_t,const unsigned long,unsigned char *,
    WriteByteHook,void *);

#if defined(HasTIFF)
#if defined(HAVE_TIFFCONF_H)
#include "tiffconf.h"
#endif
#include "tiffio.h"
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   H u f f m a n 2 D E n c o d e I m a g e                                   %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Method Huffman2DEncodeImage compresses an image via two-dimensional
%  Huffman-coding.
%
%  The format of the Huffman2DEncodeImage method is:
%
%      unsigned int Huffman2DEncodeImage(const ImageInfo *image_info,
%        Image *image)
%
%  A description of each parameter follows:
%
%    o status:  Method Huffman2DEncodeImage returns True if all the pixels are
%      compressed without error, otherwise False.
%
%    o image_info: The image info..
%
%    o image: The image.
%
*/
static unsigned int SerializeHuffman2DImage(const ImageInfo *image_info,
  Image *image, unsigned char **pixels, size_t *length)
{
  char
    filename[MaxTextExtent];

  Image
    *huffman_image;

  ImageInfo
    *clone_info;

  long
    count,
    j;

  register long
    i;

  TIFF
    *tiff;

  uint16
    fillorder;

  unsigned char
    *buffer,
    *p;

  unsigned int
    status;

  unsigned long
    *byte_count,
    strip_size;

  /*
    Write image as CCITTFax4 TIFF image to a temporary file.
  */
  assert(image_info != (ImageInfo *) NULL);
  assert(image_info->signature == MagickSignature);
  assert(image != (Image *) NULL);
  assert(image->signature == MagickSignature);
  if(!AcquireTemporaryFileName(filename))
    {
      ThrowBinaryException(FileOpenError,UnableToCreateTemporaryFile,
        filename)
    }
  huffman_image=CloneImage(image,0,0,True,&image->exception);
  if (huffman_image == (Image *) NULL)
    return(False);
  /* 
    TODO: If (image, then) huffman_image->compression is JPEG, huffman_image
    is changed to DirectClass in WriteTIFFImage and the huffman_image ends up
    broken. Change WriteTIFFIMage to not alter bilevel image to directclass
    when clone_info is G4 compression. Until then, set
    huffman_image->compression here:
  */
  huffman_image->compression=Group4Compression;

  (void) SetImageType(huffman_image,BilevelType);
  FormatString(huffman_image->filename,"tiff:%s",filename);
  clone_info=CloneImageInfo(image_info);
  clone_info->compression=Group4Compression;
  clone_info->type=BilevelType;
  (void) AddDefinitions(clone_info,"tiff:fill-order=msb2lsb",
                        &image->exception);
  status=WriteImage(clone_info,huffman_image);
  DestroyImageInfo(clone_info);
  DestroyImage(huffman_image);
  if (status == False)
    {
      (void) LiberateTemporaryFile(filename);
      return(False);
    }
  tiff=TIFFOpen(filename,"rb");
  if (tiff == (TIFF *) NULL)
    {
      (void) LiberateTemporaryFile(filename);
      ThrowBinaryException(FileOpenError,UnableToOpenFile,
        image_info->filename)
    }
  /*
    Allocate raw strip buffer.
  */
  (void) TIFFGetField(tiff,TIFFTAG_STRIPBYTECOUNTS,&byte_count);
  *length=strip_size=byte_count[0];
  for (i=1; i < (long) TIFFNumberOfStrips(tiff); i++)
  {
    if (byte_count[i] > strip_size)
      strip_size=byte_count[i];
    *length+=byte_count[i];
  }
  buffer=MagickAllocateMemory(unsigned char *,strip_size);
  if (buffer == (unsigned char *) NULL)
    {
      TIFFClose(tiff);
      (void) LiberateTemporaryFile(filename);
      ThrowBinaryException(ResourceLimitError,MemoryAllocationFailed,
        (char *) NULL)
    }
  *pixels=MagickAllocateMemory(unsigned char *,*length);
  if (*pixels == (unsigned char *) NULL)
    {
      MagickFreeMemory(buffer);
      TIFFClose(tiff);
      (void) LiberateTemporaryFile(filename);
      ThrowBinaryException(ResourceLimitError,MemoryAllocationFailed,
        (char *) NULL)
    }

  (void) TIFFGetFieldDefaulted(tiff,TIFFTAG_FILLORDER,&fillorder);
  p=(*pixels);
  for (i=0; i < (long) TIFFNumberOfStrips(tiff); i++)
  {
    count=TIFFReadRawStrip(tiff,(uint32) i,buffer,(long) byte_count[i]);
    if (fillorder == FILLORDER_LSB2MSB)
      TIFFReverseBits(buffer,count);
    for (j=0; j < count; j++)
      *p++=buffer[j];
  }
  MagickFreeMemory(buffer);
  TIFFClose(tiff);
  (void) LiberateTemporaryFile(filename);
  return(True);
}

static unsigned int Huffman2DEncodeImage(const ImageInfo *image_info,
  Image *image)
{
  size_t
    length;

  register unsigned long
    i;

  unsigned char
    *pixels;

  unsigned int
    status;

  assert(image_info != (ImageInfo *) NULL);
  assert(image_info->signature == MagickSignature);
  assert(image != (Image *) NULL);
  assert(image->signature == MagickSignature);

  status=SerializeHuffman2DImage(image_info,image,&pixels,&length);
  if (!status)
    return False;

  Ascii85Initialize(image);
  for (i=0; i < length; i++)
    Ascii85Encode(image,(unsigned long) pixels[i]);
  Ascii85Flush(image);
  MagickFreeMemory(pixels);
  return(True);
}

#else

static unsigned int SerializeHuffman2DImage(const ImageInfo *image_info,
  Image *image, unsigned char **pixels, size_t *length)
{
  assert(image != (Image *) NULL);
  assert(image->signature == MagickSignature);
  ThrowBinaryException(MissingDelegateError,TIFFLibraryIsNotAvailable,image->filename)
}
static unsigned int Huffman2DEncodeImage(const ImageInfo *image_info,
  Image *image)
{
  assert(image != (Image *) NULL);
  assert(image->signature == MagickSignature);
  ThrowBinaryException(MissingDelegateError,TIFFLibraryIsNotAvailable,image->filename)
}
#endif

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   J P E G E n c o d e I m a g e                                             %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Method JPEGEncodeImage compresses an image via JPEG compression.
%
%  The format of the JPEGEncodeImage method is:
%
%      unsigned int JPEGEncodeImage(const ImageInfo *image_info,
%        Image *image)
%
%  A description of each parameter follows:
%
%    o status:  Method JPEGEncodeImage returns True if all the pixels are
%      compressed without error, otherwise False.
%
%    o image_info: The image info.
%
%    o image: The image.
%
*/
static MagickPassFail JPEGEncodeImage(const ImageInfo *image_info,
  Image *image)
{
  unsigned char
    *blob;

  size_t
    length;

  MagickPassFail
    status=MagickFail;
  
  blob=ImageToJPEGBlob(image,image_info,&length,&image->exception);
  if (blob != (unsigned char *) NULL)
    {
      register const unsigned char
        *p;

      register size_t
        i;

        Ascii85Initialize(image);
      for (p=(const unsigned char*) blob,i=0; i < length; i++)
        Ascii85Encode(image,(unsigned long) p[i]);
      Ascii85Flush(image);
      MagickFreeMemory(blob);
      status=MagickPass;
    }
  return status;
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   R e g i s t e r P S 3 I m a g e                                           %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Method RegisterPS3Image adds attributes for the PS3 image format to
%  the list of supported formats.  The attributes include the image format
%  tag, a method to read and/or write the format, whether the format
%  supports the saving of more than one frame to the same file or blob,
%  whether the format supports native in-memory I/O, and a brief
%  description of the format.
%
%  The format of the RegisterPS3Image method is:
%
%      RegisterPS3Image(void)
%
*/
ModuleExport void RegisterPS3Image(void)
{
  MagickInfo
    *entry;

  entry=SetMagickInfo("EPS3");
  entry->encoder=(EncoderHandler) WritePS3Image;
  entry->description="Adobe Level III Encapsulated PostScript";
  entry->module="PS3";
  entry->coder_class=PrimaryCoderClass;
  (void) RegisterMagickInfo(entry);

  entry=SetMagickInfo("PS3");
  entry->encoder=(EncoderHandler) WritePS3Image;
  entry->description="Adobe Level III PostScript";
  entry->module="PS3";
  entry->coder_class=PrimaryCoderClass;
  (void) RegisterMagickInfo(entry);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   S e r i a l i z e P s e u d o C l a s s I m a g e                         %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Convert the indexes of a color mapped image to a stream of bytes.
%
%  The format of the SerializePseudoClassImage method is:
%
%       unsigned int SerializePseudoClassImage(const ImageInfo *image_info,
%         Image *image, unsigned char **pixels, size_t *length)
%
%  A description of each parameter follows:
%
%    o image_info: Specifies a pointer to a ImageInfo structure. required by
%                  exception handlers.
%
%    o image: The image for which the color map indexes should be serialized.
%
%    o pixels: the serialized indexes.
%
%    o length: the length of the pixels mamory area.
%
*/
static unsigned int SerializePseudoClassImage(const ImageInfo *image_info,
  Image *image, unsigned char **pixels, size_t *length)
{
  long
    x,
    y;

  register unsigned char
    *q;

  const PixelPacket
    *p;

  int
    status;

  register const IndexPacket
    *indexes;

  assert(image != (Image *) NULL);
  assert(image->signature == MagickSignature);
  status=True;
  *length=image->columns*image->rows;
  *pixels=MagickAllocateMemory(unsigned char *, *length);
  if (*pixels == (unsigned char *) NULL)
    ThrowWriterException(ResourceLimitError,MemoryAllocationFailed,image);
  q=(*pixels);
  for (y=0; y < (long) image->rows; y++)
  {
    p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception);
    if (p == (const PixelPacket *) NULL)
      break;
    indexes=AccessImmutableIndexes(image);
    for (x=0; x < (long) image->columns; x++)
      *q++=indexes[x];
    if (image->previous == (Image *) NULL)
      if (QuantumTick(y,image->rows))
        {
          status=MagickMonitorFormatted(y,image->rows,&image->exception,
                                        SaveImageText,image->filename,
                                        image->columns,image->rows);
          if (status == False)
            break;
        }
  }
  if (status == False)
    MagickFreeMemory(*pixels);
  return(status);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   S e r i a l i z e M u l t i C h a n n e l I m a g e                       %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Convert three and four channel images to a stream of bytes.
%
%  The format of the SerializeMultiChannelImage method is:
%
%       unsigned int SerializeMultiChannelImage(const ImageInfo *image_info,
%         Image *image, unsigned char **pixels, size_t *length)
%
%  A description of each parameter follows:
%
%    o image_info: Specifies a pointer to a ImageInfo structure. required by
%                  exception handlers.
%
%    o image: The image for which the RGB or CMYK channels should be
%             serialized.
%
%    o pixels: the serialized image channels.
%
%    o length: the length of the pixels mamory area.
%
*/
static unsigned int SerializeMultiChannelImage(const ImageInfo *image_info,
  Image *image, unsigned char **pixels, size_t *length)
{
  long
    x,
    y;

  register unsigned char
    *q;

  const PixelPacket
    *p;

  int
    status;

  assert(image != (Image *) NULL);
  assert(image->signature == MagickSignature);
  status=True;
  *length=(image->colorspace == CMYKColorspace ? 4 : 3)*image->columns*image->rows;
  *pixels=MagickAllocateMemory(unsigned char *, *length);
  if (*pixels == (unsigned char *) NULL)
    ThrowWriterException(ResourceLimitError,MemoryAllocationFailed,image);

  q=(*pixels);
  for (y=0; y < (long) image->rows; y++)
  {
    p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception);
    if (p == (const PixelPacket *) NULL)
      break;
    if (image->colorspace == CMYKColorspace)
      for (x=0; x < (long) image->columns; x++)
      {
        *q++=ScaleQuantumToChar(p->red);
        *q++=ScaleQuantumToChar(p->green);
        *q++=ScaleQuantumToChar(p->blue);
        *q++=ScaleQuantumToChar(p->opacity);
        p++;
      }
    else
      for (x=0; x < (long) image->columns; x++)
      {
        *q++=ScaleQuantumToChar(p->red);
        *q++=ScaleQuantumToChar(p->green);
        *q++=ScaleQuantumToChar(p->blue);
        p++;
      }
    if (image->previous == (Image *) NULL)
      if (QuantumTick(y,image->rows))
        {
          status=MagickMonitorFormatted(y,image->rows,&image->exception,
                                        SaveImageText,image->filename,
                                        image->columns,image->rows);
          if (status == False)
            break;
        }
  }
  if (status == False)
    MagickFreeMemory(*pixels);
  return(status);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   S e r i a l i z e S i n g l e C h a n n e l I m a g e                     %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Convert 1 and 8 bit single channel images to a stream of bytes. Bilevel
%  image pixels are packed 8 pixels in a byte.
%
%  The format of the SerializeSingleChannelImage method is:
%
%       unsigned int SerializeSingleChannelImage(const ImageInfo *image_info,
%         Image *image, unsigned char **pixels, size_t *length)
%
%  A description of each parameter follows:
%
%    o image_info: Specifies a pointer to an ImageInfo structure. Required by
%                  exception handlers.
%
%    o image: The image for which the RGB or CMYK channels should be
%             serialized.
%
%    o characteristics: Already populated image characteristics.
%
%    o pixels: the serialized image channels.
%
%    o length: the length of the pixels mamory area.
%
*/
static unsigned int SerializeSingleChannelImage(const ImageInfo *image_info,
  Image *image,unsigned char **pixels, size_t *length)
{
  unsigned long
    x,
    y;

  register unsigned char
    *q;

  const PixelPacket
    *p;

  unsigned long
    pack,
    padded_columns;

  unsigned char
    code,
    bit;

  int
    status;

  assert(image != (Image *) NULL);
  assert(image->signature == MagickSignature);
  status=True;
  pack=IsMonochromeImage(image,&image->exception) ? 8 : 1;
  /* Padded columns are padded to byte boundary */
  padded_columns=((image->columns+pack-1)/pack)*pack;
  *length=padded_columns*image->rows/pack;
  *pixels=MagickAllocateMemory(unsigned char *, *length);
  if (*pixels == (unsigned char *) NULL)
    ThrowWriterException(ResourceLimitError,MemoryAllocationFailed,
      image);

  q=(*pixels);
  for (y=0; y < image->rows; y++)
  {
    p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception);
    if (p == (const PixelPacket *) NULL)
      break;
    if (pack == 1)
    {
      for (x=0; x < image->columns; x++)
      {
        *q++=ScaleQuantumToChar(PixelIntensityToQuantum(p));
        p++;
      }
    }
    else
    {
      code=0;
      for (x=0; x < padded_columns; x++)
      {
        bit=0x00;
        if (x < image->columns)
          bit=PixelIntensityToQuantum(p) == TransparentOpacity ? 0x01 : 0x00;
        code=(code << 1)+bit;
        if ((x+1) % pack == 0)
        {
          *q++=code;
          code=0;
        }
        p++;
      }
    }
    if (image->previous == (Image *) NULL)
      if (QuantumTick(y,image->rows))
        {
          status=MagickMonitorFormatted(y,image->rows,
                                        &image->exception,SaveImageText,
                                        image->filename,
                                        image->columns,image->rows);
          if (status == False)
            break;
        }
  }
  if (status == False)
    MagickFreeMemory(*pixels);
  return(status);
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   U n r e g i s t e r P S 3 I m a g e                                       %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Method UnregisterPS3Image removes format registrations made by the
%  PS3 module from the list of supported formats.
%
%  The format of the UnregisterPS3Image method is:
%
%      UnregisterPS3Image(void)
%
*/
ModuleExport void UnregisterPS3Image(void)
{
  (void) UnregisterMagickInfo("EPS3");
  (void) UnregisterMagickInfo("PS3");
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   W r i t e P S 3 M a s k I m a g e                                         %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Method WritePS3ImageMask writes the alpha channel of an image as an
%  Encapsulated Postscript Level III image mask. The mask is written as a
%  bilevel image, converted from the grayscale alpha channel using dithering
%  options supplied in image_info.
%
%  The format of the WritePS3MaskImage method is:
%
%      unsigned int WritePS3MaskImage(const ImageInfo *image_info,Image *image)
%
%  A description of each parameter follows:
%
%    o status: Method WritePS3MaskImage return True if the mask is printed.
%      False is returned on errors like out-of-memory.
%
%    o image_info: Specifies a pointer to a ImageInfo structure.
%
%    o image: The address of a structure of type Image.
%
%
*/
typedef struct WriteByteHookInfo_
{
  Image
    *image;
} WriteByteHookInfo;

static unsigned int MaskWriteByteHook(Image *ARGUNUSED(mask_image),
  const magick_uint8_t code, void *info)
{
  Ascii85Encode(((WriteByteHookInfo *)info)->image, (unsigned long)code);
  return(True);
}

static unsigned int WritePS3MaskImage(const ImageInfo *image_info,Image *image)
{
  char
    buffer[MaxTextExtent];

  Image
    *mask_image;

  size_t
    i,
    length;

  CompressionType
    compression;

  unsigned char
    *pixels;

  int
    status;

  ExtendedSignedIntegralType
    start,
    stop;

  assert(image_info != (ImageInfo *) NULL);
  assert(image_info->signature == MagickSignature);
  assert(image != (Image *) NULL);
  assert(image->signature == MagickSignature);
  assert(image->matte);
  status=True;
  compression=image->compression;
  if (image_info->compression != UndefinedCompression)
    compression=image_info->compression;

  /* Keep position of BeginData DSC comment for update later */
  start=TellBlob(image);
  FormatString(buffer,"%%%%BeginData:%13ld ASCII Bytes\n",0L);
  (void) WriteBlobString(image,buffer);
  stop=TellBlob(image);

  /* Only lossless compressions for the mask */
  switch (compression)
  {
    case NoCompression:
      FormatString(buffer,
        "currentfile %lu %lu "PS3_NoCompression" ByteStreamDecodeFilter\n",
        image->columns, image->rows);
    case FaxCompression:
    default:
      FormatString(buffer,
        "currentfile %lu %lu "PS3_FaxCompression" ByteStreamDecodeFilter\n",
        image->columns, image->rows);
      break;
    case RLECompression:
      FormatString(buffer,
        "currentfile %lu %lu "PS3_RLECompression" ByteStreamDecodeFilter\n",
        image->columns, image->rows);
      break;
    case LZWCompression:
      FormatString(buffer,
        "currentfile %lu %lu "PS3_LZWCompression" ByteStreamDecodeFilter\n",
        image->columns, image->rows);
      break;
    case ZipCompression:
      FormatString(buffer,
        "currentfile %lu %lu "PS3_ZipCompression" ByteStreamDecodeFilter\n",
        image->columns, image->rows);
      break;
  }
  (void)WriteBlobString(image, buffer);
  (void)WriteBlobString(image, "/ReusableStreamDecode filter\n");

  mask_image=CloneImage(image,0,0,True,&image->exception);
  if (mask_image == (Image *) NULL)
    ThrowWriterException2(CoderError,image->exception.reason,image);
  status=ChannelImage(mask_image, OpacityChannel);
  if (!status)
  {
    DestroyImage(mask_image);
    return(False);
  }
  (void) SetImageType(mask_image, BilevelType);
  mask_image->matte=False;

  /* Only lossless compressions for the mask */
  switch (compression)
  {
    case NoCompression:
      status=SerializeSingleChannelImage(image_info,mask_image,&pixels,&length);
      if (status)
      {
        Ascii85Initialize(image);
        for (i=0; i < length; i++)
          Ascii85Encode(image, (unsigned long)pixels[i]);
        Ascii85Flush(image);
        MagickFreeMemory(pixels);
      }
      break;
    case FaxCompression:
    default:
      if (LocaleCompare(CCITTParam,"0") == 0)
      {
        WriteByteHookInfo
          info;
        info.image=image;
        /* Read bytes from mask and write them ASCII85 encoded to image */
        Ascii85Initialize(image);
        status=HuffmanEncode2Image(image_info,mask_image,MaskWriteByteHook,(void *)&info);
        Ascii85Flush(image);
      }
      else
      {
        status=SerializeHuffman2DImage(image_info,mask_image,&pixels,&length);
        if (status)
        {
          Ascii85Initialize(image);
          for (i=0; i < length; i++)
            Ascii85Encode(image,(unsigned long) pixels[i]);
          Ascii85Flush(image);
          MagickFreeMemory(pixels);
        }
      }
      break;
    case RLECompression:
      status=SerializeSingleChannelImage(image_info,mask_image,&pixels,&length);
      if (status)
      {
        Ascii85Initialize(image);
        status=PackbitsEncode2Image(image,length,pixels,
          Ascii85WriteByteHook,(void *)NULL);
        Ascii85Flush(image);
        MagickFreeMemory(pixels);
      }
      break;
    case LZWCompression:
      status=SerializeSingleChannelImage(image_info,mask_image,&pixels,&length);
      if (status)
      {
        Ascii85Initialize(image);
        status=LZWEncode2Image(image,length,pixels,Ascii85WriteByteHook,(void*)NULL);
        Ascii85Flush(image);
        MagickFreeMemory(pixels);
      }
      break;
    case ZipCompression:
      status=SerializeSingleChannelImage(image_info,mask_image,&pixels,&length);
      if (status)
      {
        Ascii85Initialize(image);
        status=ZLIBEncode2Image(image,length,image_info->quality,pixels,
          Ascii85WriteByteHook,(void*)NULL);
        Ascii85Flush(image);
        MagickFreeMemory(pixels);
      }
      break;
  }
  DestroyImage(mask_image);
  length=TellBlob(image)-stop;
  stop=TellBlob(image);
  (void) SeekBlob(image,start,SEEK_SET);
  FormatString(buffer,"%%%%BeginData:%13ld ASCII Bytes\n",(long) length);
  (void) WriteBlobString(image,buffer);
  (void) SeekBlob(image,stop,SEEK_SET);
  (void) WriteBlobString(image,"%%EndData\n");
  (void)WriteBlobString(image, "/mask_stream exch def\n");
  return(status);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   W r i t e P S 3 I m a g e                                                 %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Method WritePS3Image translates an image to encapsulated Postscript
%  Level III for printing.  If the supplied geometry is null, the image is
%  centered on the Postscript page.  Otherwise, the image is positioned as
%  specified by the geometry.
%
%  The format of the WritePS3Image method is:
%
%      unsigned int WritePS3Image(const ImageInfo *image_info,Image *image)
%
%  A description of each parameter follows:
%
%    o status: Method WritePS3Image return True if the image is printed.
%      False is returned if the image file cannot be opened for printing.
%
%    o image_info: Specifies a pointer to a ImageInfo structure.
%
%    o image: The address of a structure of type Image;  returned from
%      ReadImage.
%
%
*/
static unsigned int WritePS3Image(const ImageInfo *image_info,Image *image)
{
  static const char
    *PostscriptProlog[]=
    {
      "/ByteStreamDecodeFilter",
      "{",
      "  /z exch def",
      "  /r exch def",
      "  /c exch def",
      "  /ASCII85Decode filter",
      "  z "PS3_FaxCompression" eq",
      "  {",
      "    <<",
      "      /K "CCITTParam,
      "      /Columns c",
      "      /Rows r",
      "    >>",
      "    /CCITTFaxDecode filter",
      "  } if",
      "  z "PS3_RLECompression" eq {/RunLengthDecode filter} if",
      "  z "PS3_LZWCompression" eq {/LZWDecode filter} if",
      "  z "PS3_ZipCompression" eq {/FlateDecode filter} if",
      "  z "PS3_JPEGCompression" eq {/DCTDecode filter} if",
      "} bind def",
      "",
      "/DirectClassImageDict",
      "{",
      "  colorspace "PS3_RGBColorspace" eq",
      "  {",
      "    /DeviceRGB setcolorspace",
      "    <<",
      "      /ImageType 1",
      "      /Width columns",
      "      /Height rows",
      "      /BitsPerComponent 8",
      "      /DataSource pixel_stream",
      "      /MultipleDataSources false",
      "      /ImageMatrix [columns 0 0 rows neg 0 rows]",
      "      /Decode [0 1 0 1 0 1]",
      "    >>",
      "  }",
      "  {",
      "    /DeviceCMYK setcolorspace",
      "    <<",
      "      /ImageType 1",
      "      /Width columns",
      "      /Height rows",
      "      /BitsPerComponent 8",
      "      /DataSource pixel_stream",
      "      /MultipleDataSources false",
      "      /ImageMatrix [columns 0 0 rows neg 0 rows]",
      "      /Decode",
      /*
        JPEG coder is used for JPEG compression. It compensates
        for inverted CMYK, so use an inverted decode matrix for
        JPEG compressed CMYK images.
      */
      "        compression "PS3_JPEGCompression" eq",
      "        {[1 0 1 0 1 0 1 0]}",
      "        {[0 1 0 1 0 1 0 1]}",
      "        ifelse",
      "    >>",
      "  }",
      "  ifelse",
      "} bind def",
      "",
      "/PseudoClassImageDict",
      "{",
      "  % COLORS IN PSEUDO CLASS IMAGE",
      "  currentfile buffer readline pop",
      "  token pop /colors exch def pop",
      "  colors 0 eq",
      "  {",
      "    % DEPTH OF GRAYSCALE",
      "    currentfile buffer readline pop",
      "    token pop /bits exch def pop",
      "    /DeviceGray setcolorspace",
      "    <<",
      "      /ImageType 1",
      "      /Width columns",
      "      /Height rows",
      "      /BitsPerComponent bits",
      "      /Decode [0 1]",
      "      /ImageMatrix [columns 0 0 rows neg 0 rows]",
      "      /DataSource pixel_stream",
      "    >>",
      "  }",
      "  {",
      "    % RGB COLORMAP",
      "    /colormap colors 3 mul string def",
      "    % INDEXES",
      "    currentfile /ASCII85Decode filter colormap readstring pop pop",
      "    [ /Indexed /DeviceRGB colors 1 sub colormap ] setcolorspace",
      "    <<",
      "      /ImageType 1",
      "      /Width columns",
      "      /Height rows",
      "      /BitsPerComponent 8",
      "      /Decode [0 255]",
      "      /ImageMatrix [columns 0 0 rows neg 0 rows]",
      "      /DataSource pixel_stream",
      "    >>",
      "  }",
      "  ifelse",
      "} bind def",
      "",
      "/NonMaskedImageDict",
      "{",
      "  class "PS3_PseudoClass" eq",
      "  { PseudoClassImageDict }",
      "  { DirectClassImageDict }",
      "  ifelse",
      "} bind def",
      "",
      "/MaskedImageDict",
      "{",
      "  <<",
      "    /ImageType 3",
      "    /InterleaveType 3",
      "    /DataDict NonMaskedImageDict",
      "    /MaskDict",
      "    <<",
      "      /ImageType 1",
      "      /Width columns",
      "      /Height rows",
      "      /BitsPerComponent 1",
      "      /DataSource mask_stream",
      "      /MultipleDataSources false",
      "      /ImageMatrix [ columns 0 0 rows neg 0 rows]",
      "      /Decode [ 0 1 ]",
      "    >>",
      "  >>",
      "} bind def",
      "",
      /*
        Default procedure for image clipping does nothing. Image
        will provide overriding ClipPath procdure if relevant.
      */
      "/ClipImage",
      "{} def",
      "",
      "/DisplayImage",
      "{",
      "  /buffer 512 string def",
      "",
      "  % TRANSLATION",
      "  currentfile buffer readline pop",
      "  token pop /x exch def",
      "  token pop /y exch def pop",
      "  x y translate",
      "",
      "  % IMAGE SIZE AND FONT SIZE",
      "  currentfile buffer readline pop",
      "  token pop /x exch def",
      "  token pop /y exch def pop",
      "  currentfile buffer readline pop",
      "  token pop /pointsize exch def pop",
      (char *) NULL
    },
    /*
      This hole in the PS prolog is for labels.
    */
    *PostscriptEpilog[]=
    {
      "  x y scale",
      "",
      "  % CLIPPING PATH",
      "  currentfile buffer readline pop",
      "  token pop /clipped exch def pop",
      "",
      "  % EPS",
      "  currentfile buffer readline pop",
      "  token pop /sp exch def pop",
      "",
      "  % IMAGE PIXEL SIZE",
      "  currentfile buffer readline pop",
      "  token pop /columns exch def",
      "  token pop /rows exch def pop",
      "",
      "  % COLORSPACE (RGB/CMYK)",
      "  currentfile buffer readline pop",
      "  token pop /colorspace exch def pop",
      "",
      "  % TRANSPARENCY",
      "  currentfile buffer readline pop",
      "  token pop /alpha exch def pop",
      "",
      "  % STENCIL MASK?",
      "  currentfile buffer readline pop",
      "  token pop /stencil exch def pop",
      "",
      "  % IMAGE CLASS (DIRECT/PSEUDO)",
      "  currentfile buffer readline pop",
      "  token pop /class exch def pop",
      "",
      "  % COMPRESSION",
      "  currentfile buffer readline pop",
      "  token pop /compression exch def pop",
      "",
      "  % CLIP AND RENDER",
      "  /pixel_stream currentfile columns rows compression",
      "    ByteStreamDecodeFilter def",
      "  clipped {ClipImage} if",
      "  alpha stencil not and",
      "  { MaskedImageDict mask_stream resetfile }",
      "  { NonMaskedImageDict }",
      "  ifelse",
      "  stencil {0 setgray imagemask} {image} ifelse",
      "  grestore",
      "  sp {showpage} if",
      "} bind def",
      (char *) NULL
    };

  char
    buffer[MaxTextExtent],
    date[MaxTextExtent],
    density[MaxTextExtent],
    page_geometry[MaxTextExtent],
    **labels;

  const char
    **q;

  CompressionType
    compression;

  const ImageAttribute
    *attribute;

  double
    dx_resolution,
    dy_resolution,
    x_resolution,
    x_scale,
    y_resolution,
    y_scale;

  ExtendedSignedIntegralType
    start,
    stop;

  int
    count,
    status;

  RectangleInfo
    geometry;

  register unsigned int
    i,
    j;

  SegmentInfo
    bounds={0.0,0.0,0.0,0.0};

  size_t
    length;

  time_t
    timer;

  unsigned char
    *pixels;

  unsigned long
    page,
    scene,
    text_size;

  /*
    Open output image file.
  */
  assert(image_info != (const ImageInfo *) NULL);
  assert(image_info->signature == MagickSignature);
  assert(image != (Image *) NULL);
  assert(image->signature == MagickSignature);
  status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
  if (status == False)
    ThrowWriterException(FileOpenError,UnableToOpenFile,image);
  compression=image->compression;
  if (image_info->compression != UndefinedCompression)
    compression=image_info->compression;
  switch (compression)
  {
#if !defined(HasJPEG)
    case JPEGCompression:
    {
      compression=RLECompression;
      ThrowException(&image->exception,MissingDelegateError,JPEGLibraryIsNotAvailable,image->filename);
      break;
    }
#endif
#if !defined(HasZLIB)
    case ZipCompression:
    {
      compression=RLECompression;
      ThrowException(&image->exception,MissingDelegateError,ZipLibraryIsNotAvailable,image->filename);
      break;
    }
#endif
    default:
      break;
  }
  page=0;
  scene=0;
  do
  {
    page++;
    /*
      Scale image to size of Postscript page.
    */
    text_size=0;
    attribute=GetImageAttribute(image,"label");
    if (attribute != (const ImageAttribute *) NULL)
      text_size=(unsigned int)(MultilineCensus(attribute->value)*
        image_info->pointsize+12);
    SetGeometry(image,&geometry);
    geometry.y=(long) text_size;
    FormatString(page_geometry,"%lux%lu",image->columns,image->rows);
    if (image_info->page != (char *) NULL)
      {
        (void) strlcpy(page_geometry,image_info->page,MaxTextExtent);
      }
    else
      {
        if ((image->page.width != 0) && (image->page.height != 0))
          (void) FormatString(page_geometry,"%lux%lu%+ld%+ld",
            image->page.width,image->page.height,image->page.x,
              image->page.y);
        else
          if (LocaleCompare(image_info->magick,"PS3") == 0)
            (void) strcpy(page_geometry,PSPageGeometry);
      }
    (void) GetMagickGeometry(page_geometry,&geometry.x,&geometry.y,
       &geometry.width,&geometry.height);
    /*
      Scale relative to dots-per-inch. First whatever resolution passed
      in image_info. Then resolution from the image. Last default PS
      resolution.
    */
    dx_resolution=72.0;
    dy_resolution=72.0;
    x_resolution=72.0;
    (void) strcpy(density,PSDensityGeometry);
    count=GetMagickDimension(density,&x_resolution,&y_resolution,NULL,NULL);
    if (count != 2)
      y_resolution=x_resolution;
    if (image_info->density != (char *) NULL)
      {
        double 
          unit_conversion;

        unit_conversion=
          image_info->units == PixelsPerCentimeterResolution ? 2.54 : 1.0;
        count=GetMagickDimension(image_info->density,&x_resolution,
          &y_resolution,NULL,NULL);
        x_resolution*=unit_conversion;
        if (count != 2)
          y_resolution=x_resolution;
        else
          y_resolution*=unit_conversion;
      }
    else
      {
        double 
          unit_conversion;

        unit_conversion=
          image->units == PixelsPerCentimeterResolution ? 2.54 : 1.0;
        if (image->x_resolution > 0.0)
          x_resolution=image->x_resolution*unit_conversion;
        if (image->y_resolution > 0.0)
          y_resolution=image->y_resolution*unit_conversion;
      }
    x_scale=(geometry.width*dx_resolution)/x_resolution;
    geometry.width=(unsigned long) (x_scale+0.5);
    y_scale=(geometry.height*dy_resolution)/y_resolution;
    geometry.height=(unsigned long) (y_scale+0.5);

    /*
      Postscript header on the first page
    */
    if (page == 1)
      {
        /* Postscript magic */
        if (LocaleCompare(image_info->magick,"PS3") == 0)
          (void) strcpy(buffer,"%!PS-Adobe-3.0\n");
        else
          (void) strcpy(buffer,"%!PS-Adobe-3.0 EPSF-3.0\n");
        (void) WriteBlobString(image,buffer);

        /* Creator */
        FormatString(buffer,"%%%%Creator: GraphicsMagick %s\n",
          MagickLibVersionText);
        (void) WriteBlobString(image,buffer);

        /* Title */
        FormatString(buffer,"%%%%Title: %.1024s\n",image->filename);
        (void) WriteBlobString(image,buffer);

        /* File creation timestamp */
        timer=time((time_t *) NULL);
        (void) localtime(&timer);
        (void) strlcpy(date,ctime(&timer),MaxTextExtent);
        date[strlen(date)-1]='\0';
        FormatString(buffer,"%%%%CreationDate: %.1024s\n",date);
        (void) WriteBlobString(image,buffer);

        /* Bounding box and process colors if not RGB */
        bounds.x1=geometry.x;
        bounds.y1=geometry.y;
        bounds.x2=geometry.x+x_scale;
        bounds.y2=geometry.y+y_scale+text_size;
        if (image_info->adjoin && (image->next != (Image *) NULL))
          {
            (void) WriteBlobString(image,"%%BoundingBox: (atend)\n");
            (void) WriteBlobString(image,"%%HiResBoundingBox: (atend)\n");
            /* No document process colors if there's more than one page */
          }
        else
          {
            FormatString(buffer,"%%%%BoundingBox: %g %g %g %g\n",
              floor(bounds.x1+0.5),floor(bounds.y1+0.5),ceil(bounds.x2-0.5),
              ceil(bounds.y2-0.5));
            (void) WriteBlobString(image,buffer);
            FormatString(buffer,"%%%%HiResBoundingBox: %.7g %.7g %.7g %.7g\n",
              bounds.x1,bounds.y1,bounds.x2,bounds.y2);
            (void) WriteBlobString(image,buffer);

            if (image->colorspace == CMYKColorspace)
              (void) WriteBlobString(image,
                "%%DocumentProcessColors: Cyan Magenta Yellow Black\n");
            else
              if (IsGrayImage(image,&image->exception))
                (void) WriteBlobString(image,
                  "%%DocumentProcessColors: Black\n");
          }

        /* Font resources */
        attribute=GetImageAttribute(image,"label");
        if (attribute != (const ImageAttribute *) NULL)
          (void) WriteBlobString(image,
            "%%DocumentNeededResources: font Helvetica\n");

        /* Language level */
        (void) WriteBlobString(image,"%%LanguageLevel: 3\n");

        /* Pages, orientation, and page order */
        if (LocaleCompare(image_info->magick,"PS3") != 0)
          {
            (void) WriteBlobString(image,"%%Pages: 1\n");
          }
        else
          {
            (void) WriteBlobString(image,"%%Orientation: Portrait\n");
            (void) WriteBlobString(image,"%%PageOrder: Ascend\n");
            if (!image_info->adjoin)
              (void) strcpy(buffer,"%%Pages: 1\n");
            else
              FormatString(buffer,"%%%%Pages: %lu\n",(unsigned long)
                GetImageListLength(image));
            (void) WriteBlobString(image,buffer);
          }
        (void) WriteBlobString(image,"%%EndComments\n");

        /* The static postscript procedures prolog. */
        (void)WriteBlobString(image,"%%BeginProlog\n");
        for (q=PostscriptProlog; *q; q++)
          {
            (void) WriteBlobString(image,*q);
            (void) WriteBlobByte(image,'\n');
          }

        /* One label line for each line in label string */
        attribute=GetImageAttribute(image,"label");
        if (attribute != (const ImageAttribute *) NULL)
          {
            long
              si;

            (void) WriteBlobString(image,"\n  % LABELS\n  /Helvetica "
              "findfont pointsize scalefont setfont\n");
            for (si=(long) MultilineCensus(attribute->value)-1; si >= 0; si--)
              {
                (void) WriteBlobString(image,
                  "  currentfile buffer readline pop token pop\n");
                FormatString(buffer,"  0 y %g add moveto show pop\n",
                  si*image_info->pointsize+12);
                (void) WriteBlobString(image,buffer);
              }
          }

        /* The static postscript procedures epilog. */
        for (q=PostscriptEpilog; *q; q++)
          {
            (void) WriteBlobString(image,*q);
            (void) WriteBlobByte(image,'\n');
          }
        (void)WriteBlobString(image,"%%EndProlog\n");
      }

    /* Page number */
    FormatString(buffer,"%%%%Page: 1 %lu\n",page);
    (void) WriteBlobString(image,buffer);

    /* Page bounding box */
    FormatString(buffer,"%%%%PageBoundingBox: %ld %ld %ld %ld\n",geometry.x,
      geometry.y,geometry.x+(long) geometry.width,geometry.y+(long)
      (geometry.height+text_size));
    (void) WriteBlobString(image,buffer);

    /* Page process colors if not RGB */
    if (image->colorspace == CMYKColorspace)
      (void) WriteBlobString(image,
        "%%PageProcessColors: Cyan Magenta Yellow Black\n");
    else
      if (IsGrayImage(image,&image->exception))
        (void) WriteBlobString(image,"%%PageProcessColors: Black\n");

    /*
      Adjust document bounding box to bound page bounding
      box if page bounding box is the larger
    */
    if (geometry.x < bounds.x1)
      bounds.x1=geometry.x;
    if (geometry.y < bounds.y1)
      bounds.y1=geometry.y;
    if ((geometry.x+x_scale) > bounds.x2)
      bounds.x2=geometry.x+x_scale;
    if ((geometry.y+y_scale+text_size) > bounds.y2)
      bounds.y2=geometry.y+y_scale+text_size;

    /* Page font resource if there's a label */
    attribute=GetImageAttribute(image,"label");
    if (attribute != (const ImageAttribute *) NULL)
      (void) WriteBlobString(image,"%%PageResources: font Helvetica\n");

    /* PS clipping path from Photoshop clipping path */
    if ((image->clip_mask != (Image *) NULL) && 
      (LocaleNCompare("8BIM:",image->clip_mask->magick_filename,5) == 0))
      {
        const ImageAttribute
          *attribute;

        attribute=GetImageAttribute(image,image->clip_mask->magick_filename);
        if (attribute == (const ImageAttribute *) NULL)
          return(False);
        (void) WriteBlobString(image,attribute->value);
        (void) WriteBlobByte(image,'\n');
      }
    else
      {
        /* Reset definition in case previous image had a clipping path.*/
        (void)WriteBlobString(image,"/ClipImage {} def\n");
      }

    /* Push a dictionary for our own def's if this is an EPS */
    if (LocaleCompare(image_info->magick,"PS3") != 0)
      (void) WriteBlobString(image,"userdict begin\n");

    /* Image mask */
    if (image->matte)
      if (!WritePS3MaskImage(image_info, image))
      {
        CloseBlob(image);
        return(False);
      }

    /*
      Remember position of BeginData comment so we can update
      it when we know how many ascii85 encoded bytes we have
      in the blob.
    */
    start=TellBlob(image);
    FormatString(buffer,"%%%%BeginData:%13ld ASCII Bytes\n",0L);
    (void) WriteBlobString(image,buffer);
    stop=TellBlob(image);

    /* Call to image display procedure */
    (void) WriteBlobString(image,"DisplayImage\n");

    /* Translate, scale, and font point size */
    FormatString(buffer,"%ld %ld\n%.7g %.7g\n%f\n",geometry.x,geometry.y,
      x_scale,y_scale,image_info->pointsize);
    (void) WriteBlobString(image,buffer);

    /* Output labels */
    labels=(char **) NULL;
    attribute=GetImageAttribute(image,"label");
    if (attribute != (const ImageAttribute *) NULL)
      labels=StringToList(attribute->value);
    if (labels != (char **) NULL)
      {
        for (i=0; labels[i] != (char *) NULL; i++)
          {
            Ascii85Initialize(image);
            (void) WriteBlobString(image,"<~");
            for (j=0; labels[i][j] != '\0'; j++)
              Ascii85Encode(image, (unsigned long)labels[i][j]);
            Ascii85Flush(image);
            MagickFreeMemory(labels[i]);
          }
        MagickFreeMemory(labels);
      }

    /* Photoshop clipping path active? */
    if ((image->clip_mask != (Image *) NULL) &&
        (LocaleNCompare("8BIM:",image->clip_mask->magick_filename,5) == 0))
      (void) WriteBlobString(image,"true\n");
    else
      (void) WriteBlobString(image,"false\n");
  
    /* Compression seems to take precedence over anyting */
    if (compression == FaxCompression)
      (void) SetImageType(image, BilevelType);

    /* Showpage for non-EPS. */
    (void) WriteBlobString(image,
      LocaleCompare(image_info->magick,"PS3") == 0 ? "true\n" : "false\n");

    /* Image columns and rows; color space */
    FormatString(buffer,"%lu %lu\n%.1024s\n",
      image->columns,image->rows,image->colorspace == CMYKColorspace ?
        PS3_CMYKColorspace : PS3_RGBColorspace);
    (void) WriteBlobString(image,buffer);

    /* Masked image? */
    (void) WriteBlobString(image,image->matte ? "true\n" : "false\n");

    /* Render with imagemask operator? */
    if (((AccessDefinition(image_info,"ps","imagemask")) != 0) &&
        IsMonochromeImage(image,&image->exception))
      (void) WriteBlobString(image,"true\n");
    else
      (void) WriteBlobString(image,"false\n");

    /*
      Output data in one of three ways:
      1) 1 bit and 8 bit single channel direct color image data.
      2) 3 and 4 channel direct color image data
      3) 8 bit color mapped image data
    */

    /*
      Output 1 bit and 8 bit single channel image data. IsGray and IsMono
      may return true for both direct class RGB and CMYK colors, so we need
      to test that these were not requested explicitly.
    */
    if ((image_info->type != TrueColorType) &&
         (image_info->type != TrueColorMatteType) &&
         (image_info->type != ColorSeparationType) &&
         (image_info->type != ColorSeparationMatteType) &&
         (image->colorspace != CMYKColorspace) &&
         (IsGrayImage(image,&image->exception) ||
          IsMonochromeImage(image,&image->exception)))
    {
      /* Image class */
      (void) WriteBlobString(image,PS3_PseudoClass"\n");

      /* Compression scheme - any */
      switch (compression)
      {
        case NoCompression:
        default:
          (void) WriteBlobString(image,PS3_NoCompression"\n");
          break;
        case FaxCompression:
          (void) WriteBlobString(image,PS3_FaxCompression"\n");
          break;
        case RLECompression:
          (void) WriteBlobString(image,PS3_RLECompression"\n");
          break;
        case LZWCompression:
          (void) WriteBlobString(image,PS3_LZWCompression"\n");
          break;
        case ZipCompression:
          (void) WriteBlobString(image,PS3_ZipCompression"\n");
          break;
        case JPEGCompression:
          (void) WriteBlobString(image,PS3_JPEGCompression"\n");
          break;
      }
      
      /* Number of colors -- 0 for single component non-color mapped data */
      (void) WriteBlobString(image,"0\n");

      /* 1 bit or 8 bit components? */
      FormatString(buffer,"%d\n", 
        IsMonochromeImage(image,&image->exception) ? 1 : 8);
      (void) WriteBlobString(image,buffer);

      /* Image data. Always ASCII85 encoded. */
      if (compression == JPEGCompression)
        {
          status=JPEGEncodeImage(image_info,image);
        }
      else
        if (compression == FaxCompression)
          {
            if (LocaleCompare(CCITTParam,"0") == 0)
              status=HuffmanEncodeImage(image_info,image);
            else
              status=Huffman2DEncodeImage(image_info,image);
          }
        else
          {
            status=SerializeSingleChannelImage(image_info,image,&pixels,
              &length);
            if (!status)
              {
                CloseBlob(image);
                return(False);
              }
            Ascii85Initialize(image);
            switch (compression)
            {
              case NoCompression:
              default:
                for (i=0; i < length; i++)
                  Ascii85Encode(image, (unsigned long)pixels[i]);
                break;
              case RLECompression:
                status=PackbitsEncode2Image(image,length,pixels,
                  Ascii85WriteByteHook,(void*)NULL);
                break;
              case LZWCompression:
                status=LZWEncode2Image(image,length,pixels,
                  Ascii85WriteByteHook,(void*)NULL);
                break;
              case ZipCompression:
                status=ZLIBEncode2Image(image,length,image_info->quality,
                  pixels,Ascii85WriteByteHook,(void*)NULL);
                break;
            }
            Ascii85Flush(image);
            MagickFreeMemory(pixels);
          }
    }
    else
      if ((image->storage_class == DirectClass) || (image->colors > 256) ||
          (compression == JPEGCompression))
        {
          /* Image class */
          (void) WriteBlobString(image,PS3_DirectClass"\n");

          /* Compression scheme - fax is only for bilevel images */
          switch (compression)
          {
            case NoCompression:
            default:
              (void) WriteBlobString(image,PS3_NoCompression"\n");
              break;
            case RLECompression:
              (void) WriteBlobString(image,PS3_RLECompression"\n");
              break;
            case LZWCompression:
              (void) WriteBlobString(image,PS3_LZWCompression"\n");
              break;
            case ZipCompression:
              (void) WriteBlobString(image,PS3_ZipCompression"\n");
              break;
            case JPEGCompression:
              (void) WriteBlobString(image,PS3_JPEGCompression"\n");
              break;
          }

          /* Image data. Always ASCII85 encoded. */
          if (compression == JPEGCompression)
            {
                status=JPEGEncodeImage(image_info,image);
            }
          else
            {
              /* Stream based compressions */
              status=SerializeMultiChannelImage(image_info,image,&pixels,
                &length);
              if (!status)
                {
                  CloseBlob(image);
                  return(False);
                }
              Ascii85Initialize(image);
              switch (compression)
              {
                case NoCompression:
                default:
                  for (i=0; i < length; i++)
                    Ascii85Encode(image, (unsigned long)pixels[i]);
                  status=True;
                  break;
                case RLECompression:
                  status=PackbitsEncode2Image(image,length,pixels,
                    Ascii85WriteByteHook,(void*)NULL);
                  break;
                case LZWCompression:
                  status=LZWEncode2Image(image,length,pixels,
                    Ascii85WriteByteHook,(void*)NULL);
                  break;
                case ZipCompression:
                  status=ZLIBEncode2Image(image,length,image_info->quality,
                    pixels,Ascii85WriteByteHook,(void*)NULL);
                  break;
              }
              Ascii85Flush(image);
              MagickFreeMemory(pixels);
            }
        }
      else
        {
          /*
            Color mapped images.
            
            Image class.
          */
          (void) WriteBlobString(image,PS3_PseudoClass"\n");

          /*
            Compression scheme - fax is only for bilevel images,
            JPEG for true color (single channel) images.
          */
          switch (compression)
          {
            case NoCompression:
            default:
              (void) WriteBlobString(image,PS3_NoCompression"\n");
              break;
            case RLECompression:
              (void) WriteBlobString(image,PS3_RLECompression"\n");
              break;
            case LZWCompression:
              (void) WriteBlobString(image,PS3_LZWCompression"\n");
              break;
            case ZipCompression:
              (void) WriteBlobString(image,PS3_ZipCompression"\n");
              break;
          }
          
          /* Number of colors in color map */
          FormatString(buffer,"%u\n",image->colors);
          (void) WriteBlobString(image,buffer);

          /* Color map - uncompressed, ascii85 encoded */
          Ascii85Initialize(image);
          for (i=0; i < image->colors; i++)
          {
            Ascii85Encode(image, (unsigned long)image->colormap[i].red);
            Ascii85Encode(image, (unsigned long)image->colormap[i].green);
            Ascii85Encode(image, (unsigned long)image->colormap[i].blue);
          }
          Ascii85Flush(image);

          status=SerializePseudoClassImage(image_info,image,&pixels,&length);
          if (!status)
            {
              CloseBlob(image);
              return(False);
            }
          Ascii85Initialize(image);
          switch (compression)
          {
            case NoCompression:
            default:
              for (i=0; i < length; i++)
                Ascii85Encode(image, (unsigned long)pixels[i]);
              status=True;
              break;
            case RLECompression:
              status=PackbitsEncode2Image(image,length,pixels,
                Ascii85WriteByteHook,(void *)NULL);
              break;
            case LZWCompression:
              status=LZWEncode2Image(image,length,pixels,Ascii85WriteByteHook,
                (void *)NULL);
              break;
            case ZipCompression:
              status=ZLIBEncode2Image(image,length,image_info->quality,pixels,
                Ascii85WriteByteHook,(void *)NULL);
              break;
          }
          Ascii85Flush(image);
          MagickFreeMemory(pixels);
        }

    if (!status)
      {
        CloseBlob(image);
        return(False);
      }

    /* Update BeginData now that we know the data size */
    length=TellBlob(image)-stop;
    stop=TellBlob(image);
    (void) SeekBlob(image,start,SEEK_SET);
    FormatString(buffer,"%%%%BeginData:%13ld ASCII Bytes\n",(long) length);
    (void) WriteBlobString(image,buffer);
    (void) SeekBlob(image,stop,SEEK_SET);
    (void) WriteBlobString(image,"%%EndData\n");

    /* End private dictionary if this is an EPS */
    if (LocaleCompare(image_info->magick,"PS3") != 0)
      (void) WriteBlobString(image,"end\n");

    (void) WriteBlobString(image,"%%PageTrailer\n");
    if (image->next == (Image *) NULL)
      break;
    image=SyncNextImageInList(image);
    status=MagickMonitorFormatted(scene,GetImageListLength(image),
                                  &image->exception,SaveImagesText,
                                  image->filename);
    if (status == False)
      break;
    scene++;
  } while (image_info->adjoin);
  if (image_info->adjoin)
    while (image->previous != (Image *) NULL)
      image=image->previous;
  (void) WriteBlobString(image,"%%Trailer\n");
  if (page > 1)
    {
      FormatString(buffer,"%%%%BoundingBox: %g %g %g %g\n",
        floor(bounds.x1+0.5),floor(bounds.y1+0.5),ceil(bounds.x2-0.5),
        ceil(bounds.y2-0.5));
      (void) WriteBlobString(image,buffer);
      FormatString(buffer,"%%%%HiResBoundingBox: %.7g %.7g %.7g %.7g\n",
        bounds.x1,bounds.y1,bounds.x2,bounds.y2);
      (void) WriteBlobString(image,buffer);
    }
  (void) WriteBlobString(image,"%%EOF\n");
  CloseBlob(image);
  return(True);
}

#if defined(HasZLIB)
#include "zlib.h"
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   Z L I B E n c o d e 2 I m a g e                                           %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Method ZLIBEncode2Image compresses an image via ZLIB-coding specific to
%  Postscript Level II or Portable Document Format.  To ensure portability, the
%  binary ZLIB bytes are encoded as ASCII base-85.
%
%  The format of the ZLIBEncode2Image method is:
%
%      unsigned int ZLIBEncode2Image(Image *image,const size_t length,
%        const unsigned long quality,unsigned char *pixels,
%        WriteByteHook write_byte,void *info)
%
%  A description of each parameter follows:
%
%    o image: The address of a structure of type Image;  returned from
%      ReadImage.
%
%    o length:  A value that specifies the number of pixels to compress.
%
%    o quality: the compression level (0-100).
%
%    o pixels: The address of an unsigned array of characters containing the
%      pixels to compress.
%
%    o write_byte: function (hook) to call for writing each byte of compressed
%      data.
%
%    o info: information block to pass along when calling write_byte
%      function.
%
%
*/

static voidpf ZLIBAllocFunc(voidpf opaque, uInt items, uInt size)
{
  ARG_NOT_USED(opaque);
  return MagickMallocCleared((size_t) items*size);
}
static void ZLIBFreeFunc(voidpf opaque, voidpf address)
{
  ARG_NOT_USED(opaque);
  MagickFree(address);
}

static unsigned int ZLIBEncode2Image(Image *image,const size_t length,
  const unsigned long quality,unsigned char *pixels,WriteByteHook write_byte,
  void *info)
{
  int
    status;

  register long
    i;

  unsigned char
    *compressed_pixels;

  unsigned long
    compressed_packets;

  z_stream
    stream;

  assert(image != (Image *) NULL);
  assert(image->signature == MagickSignature);
  compressed_packets=(unsigned long) (1.001*length+12);
  compressed_pixels=MagickAllocateMemory(unsigned char *,compressed_packets);
  if (compressed_pixels == (unsigned char *) NULL)
    ThrowBinaryException(ResourceLimitError,MemoryAllocationFailed,
      (char *) NULL);
  stream.next_in=pixels;
  stream.avail_in=(unsigned int) length;
  stream.next_out=compressed_pixels;
  stream.avail_out=(unsigned int) compressed_packets;
  stream.zalloc=ZLIBAllocFunc;
  stream.zfree=ZLIBFreeFunc;
  stream.opaque=(voidpf) NULL;
  status=deflateInit(&stream,(int) Min(quality/10,9));
  if (status == Z_OK)
    {
      status=deflate(&stream,Z_FINISH);
      if (status == Z_STREAM_END)
        status=deflateEnd(&stream);
      else
        (void) deflateEnd(&stream);
      compressed_packets=stream.total_out;
    }
  if (status)
    ThrowBinaryException(CoderError,UnableToZipCompressImage,(char *) NULL)
  else
    for (i=0; i < (long) compressed_packets; i++)
      (void) (*write_byte)(image,(magick_uint8_t)compressed_pixels[i],info);
  MagickFreeMemory(compressed_pixels);
  return(!status);
}
#else
static unsigned int ZLIBEncode2Image(Image *image,const size_t length,
  const unsigned long quality,unsigned char *pixels,WriteByteHook write_byte,
  void *info)
{
  assert(image != (Image *) NULL);
  assert(image->signature == MagickSignature);
  ThrowBinaryException(MissingDelegateError,ZipLibraryIsNotAvailable,image->filename);
  return(False);
}
#endif


/* [<][>][^][v][top][bottom][index][help] */