You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1614 lines
53 KiB

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% PPPP SSSSS 33333 %
% P P SS 33 %
% PPPP SSS 333 %
% P SS 33 %
% P SSSSS 33333 %
% %
% %
% Write Postscript Level III Format %
% %
% Software Design %
% Cristy %
% Lars Ruben Skyum %
% July 1992 %
% %
% %
% Copyright 1999-2021 ImageMagick Studio LLC, a non-profit organization %
% dedicated to making software imaging solutions freely available. %
% %
% You may not use this file except in compliance with the License. You may %
% obtain a copy of the License at %
% %
% https://imagemagick.org/script/license.php %
% %
% Unless required by applicable law or agreed to in writing, software %
% distributed under the License is distributed on an "AS IS" BASIS, %
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
% See the License for the specific language governing permissions and %
% limitations under the License. %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%
*/
/*
Include declarations.
*/
#include "MagickCore/studio.h"
#include "MagickCore/artifact.h"
#include "MagickCore/attribute.h"
#include "MagickCore/blob.h"
#include "MagickCore/blob-private.h"
#include "MagickCore/cache.h"
#include "MagickCore/channel.h"
#include "MagickCore/color.h"
#include "MagickCore/color-private.h"
#include "MagickCore/compress.h"
#include "MagickCore/constitute.h"
#include "MagickCore/draw.h"
#include "MagickCore/exception.h"
#include "MagickCore/exception-private.h"
#include "MagickCore/geometry.h"
#include "MagickCore/image.h"
#include "MagickCore/image-private.h"
#include "MagickCore/list.h"
#include "MagickCore/magick.h"
#include "MagickCore/memory_.h"
#include "MagickCore/module.h"
#include "MagickCore/monitor.h"
#include "MagickCore/monitor-private.h"
#include "MagickCore/option.h"
#include "MagickCore/pixel-accessor.h"
#include "MagickCore/property.h"
#include "MagickCore/quantum-private.h"
#include "MagickCore/resource_.h"
#include "MagickCore/static.h"
#include "MagickCore/string_.h"
#include "MagickCore/module.h"
#include "MagickCore/timer-private.h"
#include "MagickCore/token.h"
#include "MagickCore/utility.h"
/*
Define declarations.
*/
#define PS3_NoCompression "0"
#define PS3_FaxCompression "1"
#define PS3_JPEGCompression "2"
#define PS3_LZWCompression "3"
#define PS3_RLECompression "4"
#define PS3_ZipCompression "5"
#define PS3_RGBColorspace "0"
#define PS3_CMYKColorspace "1"
#define PS3_DirectClass "0"
#define PS3_PseudoClass "1"
#if defined(MAGICKCORE_TIFF_DELEGATE)
#define CCITTParam "-1"
#else
#define CCITTParam "0"
#endif
/*
Forward declarations.
*/
static MagickBooleanType
WritePS3Image(const ImageInfo *,Image *,ExceptionInfo *);
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% R e g i s t e r P S 3 I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% RegisterPS3Image() adds properties for the PS3 image format to the list of
% supported formats. The properties 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:
%
% size_t RegisterPS3Image(void)
%
*/
ModuleExport size_t RegisterPS3Image(void)
{
MagickInfo
*entry;
entry=AcquireMagickInfo("PS3","EPS3","Level III Encapsulated PostScript");
entry->encoder=(EncodeImageHandler *) WritePS3Image;
entry->mime_type=ConstantString("application/postscript");
entry->flags|=CoderEncoderSeekableStreamFlag;
entry->flags^=CoderBlobSupportFlag;
(void) RegisterMagickInfo(entry);
entry=AcquireMagickInfo("PS3","PS3","Level III PostScript");
entry->encoder=(EncodeImageHandler *) WritePS3Image;
entry->mime_type=ConstantString("application/postscript");
entry->flags|=CoderEncoderSeekableStreamFlag;
entry->flags^=CoderBlobSupportFlag;
(void) RegisterMagickInfo(entry);
return(MagickImageCoderSignature);
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% U n r e g i s t e r P S 3 I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% 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 I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% 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:
%
% MagickBooleanType WritePS3Image(const ImageInfo *image_info,
% Image *image,ExceptionInfo *exception)
%
% A description of each parameter follows:
%
% o image_info: Specifies a pointer to a ImageInfo structure.
%
% o image: the image.
%
% o exception: return any errors or warnings in this structure.
%
*/
static MagickBooleanType Huffman2DEncodeImage(const ImageInfo *image_info,
Image *image,Image *inject_image,ExceptionInfo *exception)
{
Image
*group4_image;
ImageInfo
*write_info;
MagickBooleanType
status;
size_t
length;
unsigned char
*group4;
group4_image=CloneImage(inject_image,0,0,MagickTrue,exception);
if (group4_image == (Image *) NULL)
return(MagickFalse);
status=MagickTrue;
write_info=CloneImageInfo(image_info);
(void) CopyMagickString(write_info->filename,"GROUP4:",MagickPathExtent);
(void) CopyMagickString(write_info->magick,"GROUP4",MagickPathExtent);
group4=(unsigned char *) ImageToBlob(write_info,group4_image,&length,
exception);
group4_image=DestroyImage(group4_image);
write_info=DestroyImageInfo(write_info);
if (group4 == (unsigned char *) NULL)
return(MagickFalse);
if (WriteBlob(image,length,group4) != (ssize_t) length)
status=MagickFalse;
group4=(unsigned char *) RelinquishMagickMemory(group4);
return(status);
}
static MagickBooleanType SerializeImage(const ImageInfo *image_info,
Image *image,MemoryInfo **pixel_info,size_t *length,ExceptionInfo *exception)
{
MagickBooleanType
status;
const Quantum
*p;
ssize_t
x;
unsigned char
*q;
ssize_t
y;
assert(image != (Image *) NULL);
assert(image->signature == MagickCoreSignature);
if (image->debug != MagickFalse)
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
status=MagickTrue;
*length=(image->colorspace == CMYKColorspace ? 4 : 3)*(size_t)
image->columns*image->rows;
*pixel_info=AcquireVirtualMemory(*length,sizeof(*q));
if (*pixel_info == (MemoryInfo *) NULL)
ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
q=(unsigned char *) GetVirtualMemoryBlob(*pixel_info);
(void) memset(q,0,*length*sizeof(*q));
for (y=0; y < (ssize_t) image->rows; y++)
{
p=GetVirtualPixels(image,0,y,image->columns,1,exception);
if (p == (const Quantum *) NULL)
break;
if (image->colorspace != CMYKColorspace)
for (x=0; x < (ssize_t) image->columns; x++)
{
*q++=ScaleQuantumToChar(GetPixelRed(image,p));
*q++=ScaleQuantumToChar(GetPixelGreen(image,p));
*q++=ScaleQuantumToChar(GetPixelBlue(image,p));
p+=GetPixelChannels(image);
}
else
for (x=0; x < (ssize_t) image->columns; x++)
{
*q++=ScaleQuantumToChar(GetPixelRed(image,p));
*q++=ScaleQuantumToChar(GetPixelGreen(image,p));
*q++=ScaleQuantumToChar(GetPixelBlue(image,p));
*q++=ScaleQuantumToChar(GetPixelBlack(image,p));
p+=GetPixelChannels(image);
}
if (image->previous == (Image *) NULL)
{
status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
image->rows);
if (status == MagickFalse)
break;
}
}
if (status == MagickFalse)
*pixel_info=RelinquishVirtualMemory(*pixel_info);
return(status);
}
static MagickBooleanType SerializeImageChannel(const ImageInfo *image_info,
Image *image,MemoryInfo **pixel_info,size_t *length,ExceptionInfo *exception)
{
MagickBooleanType
status;
const Quantum
*p;
ssize_t
x;
unsigned char
*q;
size_t
pack,
padded_columns;
ssize_t
y;
unsigned char
code,
bit;
assert(image != (Image *) NULL);
assert(image->signature == MagickCoreSignature);
if (image->debug != MagickFalse)
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
status=MagickTrue;
pack=SetImageMonochrome(image,exception) == MagickFalse ? 1UL : 8UL;
padded_columns=((image->columns+pack-1)/pack)*pack;
*length=(size_t) padded_columns*image->rows/pack;
*pixel_info=AcquireVirtualMemory(*length,sizeof(*q));
if (*pixel_info == (MemoryInfo *) NULL)
ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
q=(unsigned char *) GetVirtualMemoryBlob(*pixel_info);
for (y=0; y < (ssize_t) image->rows; y++)
{
p=GetVirtualPixels(image,0,y,image->columns,1,exception);
if (p == (const Quantum *) NULL)
break;
if (pack == 1)
for (x=0; x < (ssize_t) image->columns; x++)
{
*q++=ScaleQuantumToChar(ClampToQuantum(GetPixelLuma(image,p)));
p+=GetPixelChannels(image);
}
else
{
code='\0';
for (x=0; x < (ssize_t) padded_columns; x++)
{
bit=(unsigned char) 0x00;
if (x < (ssize_t) image->columns)
bit=(unsigned char) (GetPixelLuma(image,p) == TransparentAlpha ?
0x01 : 0x00);
code=(code << 1)+bit;
if (((x+1) % pack) == 0)
{
*q++=code;
code='\0';
}
p+=GetPixelChannels(image);
}
}
status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
image->rows);
if (status == MagickFalse)
break;
}
if (status == MagickFalse)
*pixel_info=RelinquishVirtualMemory(*pixel_info);
return(status);
}
static MagickBooleanType SerializeImageIndexes(const ImageInfo *image_info,
Image *image,MemoryInfo **pixel_info,size_t *length,ExceptionInfo *exception)
{
MagickBooleanType
status;
const Quantum
*p;
ssize_t
x;
unsigned char
*q;
ssize_t
y;
assert(image != (Image *) NULL);
assert(image->signature == MagickCoreSignature);
if (image->debug != MagickFalse)
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
status=MagickTrue;
*length=(size_t) image->columns*image->rows;
*pixel_info=AcquireVirtualMemory(*length,sizeof(*q));
if (*pixel_info == (MemoryInfo *) NULL)
ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
q=(unsigned char *) GetVirtualMemoryBlob(*pixel_info);
for (y=0; y < (ssize_t) image->rows; y++)
{
p=GetVirtualPixels(image,0,y,image->columns,1,exception);
if (p == (const Quantum *) NULL)
break;
for (x=0; x < (ssize_t) image->columns; x++)
{
*q++=(unsigned char) ((ssize_t) GetPixelIndex(image,p));
p+=GetPixelChannels(image);
}
if (image->previous == (Image *) NULL)
{
status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
image->rows);
if (status == MagickFalse)
break;
}
}
if (status == MagickFalse)
*pixel_info=RelinquishVirtualMemory(*pixel_info);
return(status);
}
static MagickBooleanType WritePS3MaskImage(const ImageInfo *image_info,
Image *image,const CompressionType compression,ExceptionInfo *exception)
{
char
buffer[MagickPathExtent];
Image
*mask_image;
MagickBooleanType
status;
MagickOffsetType
offset,
start,
stop;
MemoryInfo
*pixel_info;
ssize_t
i;
size_t
length;
unsigned char
*pixels;
assert(image_info != (ImageInfo *) NULL);
assert(image_info->signature == MagickCoreSignature);
assert(image != (Image *) NULL);
assert(image->signature == MagickCoreSignature);
if (image->debug != MagickFalse)
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
assert(image->alpha_trait != UndefinedPixelTrait);
status=MagickTrue;
/*
Note BeginData DSC comment for update later.
*/
start=TellBlob(image);
if (start < 0)
ThrowWriterException(CorruptImageError,"ImproperImageHeader");
(void) FormatLocaleString(buffer,MagickPathExtent,
"%%%%BeginData:%13ld %s Bytes\n",0L,compression == NoCompression ?
"ASCII" : "BINARY");
(void) WriteBlobString(image,buffer);
stop=TellBlob(image);
if (stop < 0)
ThrowWriterException(CorruptImageError,"ImproperImageHeader");
/*
Only lossless compressions for the mask.
*/
switch (compression)
{
case NoCompression:
default:
{
(void) FormatLocaleString(buffer,MagickPathExtent,
"currentfile %.20g %.20g " PS3_NoCompression
" ByteStreamDecodeFilter\n",(double) image->columns,(double)
image->rows);
break;
}
case FaxCompression:
case Group4Compression:
{
(void) FormatLocaleString(buffer,MagickPathExtent,
"currentfile %.20g %.20g " PS3_FaxCompression
" ByteStreamDecodeFilter\n",(double) image->columns,(double)
image->rows);
break;
}
case LZWCompression:
{
(void) FormatLocaleString(buffer,MagickPathExtent,
"currentfile %.20g %.20g " PS3_LZWCompression
" ByteStreamDecodeFilter\n",(double) image->columns,(double)
image->rows);
break;
}
case RLECompression:
{
(void) FormatLocaleString(buffer,MagickPathExtent,
"currentfile %.20g %.20g " PS3_RLECompression
" ByteStreamDecodeFilter\n",(double) image->columns,(double)
image->rows);
break;
}
case ZipCompression:
{
(void) FormatLocaleString(buffer,MagickPathExtent,
"currentfile %.20g %.20g " PS3_ZipCompression
" ByteStreamDecodeFilter\n",(double) image->columns,(double)
image->rows);
break;
}
}
(void) WriteBlobString(image,buffer);
(void) WriteBlobString(image,"/ReusableStreamDecode filter\n");
mask_image=SeparateImage(image,AlphaChannel,exception);
if (mask_image == (Image *) NULL)
ThrowWriterException(CoderError,exception->reason);
(void) SetImageType(mask_image,BilevelType,exception);
(void) SetImageType(mask_image,PaletteType,exception);
mask_image->alpha_trait=UndefinedPixelTrait;
pixels=(unsigned char *) NULL;
length=0;
switch (compression)
{
case NoCompression:
default:
{
status=SerializeImageChannel(image_info,mask_image,&pixel_info,&length,
exception);
if (status == MagickFalse)
break;
Ascii85Initialize(image);
pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
for (i=0; i < (ssize_t) length; i++)
Ascii85Encode(image,pixels[i]);
Ascii85Flush(image);
pixel_info=RelinquishVirtualMemory(pixel_info);
break;
}
case FaxCompression:
case Group4Compression:
{
if ((compression == FaxCompression) ||
(LocaleCompare(CCITTParam,"0") == 0))
status=HuffmanEncodeImage(image_info,image,mask_image,exception);
else
status=Huffman2DEncodeImage(image_info,image,mask_image,exception);
break;
}
case LZWCompression:
{
status=SerializeImageChannel(image_info,mask_image,&pixel_info,&length,
exception);
if (status == MagickFalse)
break;
pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
status=LZWEncodeImage(image,length,pixels,exception);
pixel_info=RelinquishVirtualMemory(pixel_info);
break;
}
case RLECompression:
{
status=SerializeImageChannel(image_info,mask_image,&pixel_info,&length,
exception);
if (status == MagickFalse)
break;
pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
status=PackbitsEncodeImage(image,length,pixels,exception);
pixel_info=RelinquishVirtualMemory(pixel_info);
break;
}
case ZipCompression:
{
status=SerializeImageChannel(image_info,mask_image,&pixel_info,&length,
exception);
if (status == MagickFalse)
break;
pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
status=ZLIBEncodeImage(image,length,pixels,exception);
pixel_info=RelinquishVirtualMemory(pixel_info);
break;
}
}
mask_image=DestroyImage(mask_image);
(void) WriteBlobByte(image,'\n');
length=(size_t) (TellBlob(image)-stop);
stop=TellBlob(image);
if (stop < 0)
ThrowWriterException(CorruptImageError,"ImproperImageHeader");
offset=SeekBlob(image,start,SEEK_SET);
if (offset < 0)
ThrowWriterException(CorruptImageError,"ImproperImageHeader");
(void) FormatLocaleString(buffer,MagickPathExtent,
"%%%%BeginData:%13ld %s Bytes\n",(long) length,
compression == NoCompression ? "ASCII" : "BINARY");
(void) WriteBlobString(image,buffer);
offset=SeekBlob(image,stop,SEEK_SET);
if (offset < 0)
ThrowWriterException(CorruptImageError,"ImproperImageHeader");
(void) WriteBlobString(image,"%%EndData\n");
(void) WriteBlobString(image, "/mask_stream exch def\n");
return(status);
}
static MagickBooleanType WritePS3Image(const ImageInfo *image_info,Image *image,
ExceptionInfo *exception)
{
static const char
PostscriptProlog[] =
"/ByteStreamDecodeFilter\n"
"{\n"
" /z exch def\n"
" /r exch def\n"
" /c exch def\n"
" z " PS3_NoCompression " eq { /ASCII85Decode filter } if\n"
" z " PS3_FaxCompression " eq\n"
" {\n"
" <<\n"
" /K " CCITTParam "\n"
" /Columns c\n"
" /Rows r\n"
" >>\n"
" /CCITTFaxDecode filter\n"
" } if\n"
" z " PS3_JPEGCompression " eq { /DCTDecode filter } if\n"
" z " PS3_LZWCompression " eq { /LZWDecode filter } if\n"
" z " PS3_RLECompression " eq { /RunLengthDecode filter } if\n"
" z " PS3_ZipCompression " eq { /FlateDecode filter } if\n"
"} bind def\n"
"\n"
"/DirectClassImageDict\n"
"{\n"
" colorspace " PS3_RGBColorspace " eq\n"
" {\n"
" /DeviceRGB setcolorspace\n"
" <<\n"
" /ImageType 1\n"
" /Width columns\n"
" /Height rows\n"
" /BitsPerComponent 8\n"
" /DataSource pixel_stream\n"
" /MultipleDataSources false\n"
" /ImageMatrix [columns 0 0 rows neg 0 rows]\n"
" /Decode [0 1 0 1 0 1]\n"
" >>\n"
" }\n"
" {\n"
" /DeviceCMYK setcolorspace\n"
" <<\n"
" /ImageType 1\n"
" /Width columns\n"
" /Height rows\n"
" /BitsPerComponent 8\n"
" /DataSource pixel_stream\n"
" /MultipleDataSources false\n"
" /ImageMatrix [columns 0 0 rows neg 0 rows]\n"
" /Decode\n"
" compression " PS3_JPEGCompression " eq\n"
" { [1 0 1 0 1 0 1 0] }\n"
" { [0 1 0 1 0 1 0 1] }\n"
" ifelse\n"
" >>\n"
" }\n"
" ifelse\n"
"} bind def\n"
"\n"
"/PseudoClassImageDict\n"
"{\n"
" % Colors in colormap image.\n"
" currentfile buffer readline pop\n"
" token pop /colors exch def pop\n"
" colors 0 eq\n"
" {\n"
" % Depth of grayscale image.\n"
" currentfile buffer readline pop\n"
" token pop /bits exch def pop\n"
" /DeviceGray setcolorspace\n"
" <<\n"
" /ImageType 1\n"
" /Width columns\n"
" /Height rows\n"
" /BitsPerComponent bits\n"
" /Decode [0 1]\n"
" /ImageMatrix [columns 0 0 rows neg 0 rows]\n"
" /DataSource pixel_stream\n"
" >>\n"
" }\n"
" {\n"
" % RGB colormap.\n"
" /colormap colors 3 mul string def\n"
" compression " PS3_NoCompression " eq\n"
" { currentfile /ASCII85Decode filter colormap readstring pop pop }\n"
" { currentfile colormap readstring pop pop }\n"
" ifelse\n"
" [ /Indexed /DeviceRGB colors 1 sub colormap ] setcolorspace\n"
" <<\n"
" /ImageType 1\n"
" /Width columns\n"
" /Height rows\n"
" /BitsPerComponent 8\n"
" /Decode [0 255]\n"
" /ImageMatrix [columns 0 0 rows neg 0 rows]\n"
" /DataSource pixel_stream\n"
" >>\n"
" }\n"
" ifelse\n"
"} bind def\n"
"\n"
"/NonMaskedImageDict\n"
"{\n"
" class " PS3_PseudoClass " eq\n"
" { PseudoClassImageDict }\n"
" { DirectClassImageDict }\n"
" ifelse\n"
"} bind def\n"
"\n"
"/MaskedImageDict\n"
"{\n"
" <<\n"
" /ImageType 3\n"
" /InterleaveType 3\n"
" /DataDict NonMaskedImageDict\n"
" /MaskDict\n"
" <<\n"
" /ImageType 1\n"
" /Width columns\n"
" /Height rows\n"
" /BitsPerComponent 1\n"
" /DataSource mask_stream\n"
" /MultipleDataSources false\n"
" /ImageMatrix [ columns 0 0 rows neg 0 rows]\n"
" /Decode [ 0 1 ]\n"
" >>\n"
" >>\n"
"} bind def\n"
"\n"
"/ClipImage\n"
"{} def\n"
"\n"
"/DisplayImage\n"
"{\n"
" gsave\n"
" /buffer 512 string def\n"
" % Translation.\n"
" currentfile buffer readline pop\n"
" token pop /x exch def\n"
" token pop /y exch def pop\n"
" x y translate\n"
" % Image size and font size.\n"
" currentfile buffer readline pop\n"
" token pop /x exch def\n"
" token pop /y exch def pop\n"
" currentfile buffer readline pop\n"
" token pop /pointsize exch def pop\n";
static const char
PostscriptEpilog[] =
" x y scale\n"
" % Clipping path.\n"
" currentfile buffer readline pop\n"
" token pop /clipped exch def pop\n"
" % Showpage.\n"
" currentfile buffer readline pop\n"
" token pop /sp exch def pop\n"
" % Image pixel size.\n"
" currentfile buffer readline pop\n"
" token pop /columns exch def\n"
" token pop /rows exch def pop\n"
" % Colorspace (RGB/CMYK).\n"
" currentfile buffer readline pop\n"
" token pop /colorspace exch def pop\n"
" % Transparency.\n"
" currentfile buffer readline pop\n"
" token pop /alpha exch def pop\n"
" % Stencil mask?\n"
" currentfile buffer readline pop\n"
" token pop /stencil exch def pop\n"
" % Image class (direct/pseudo).\n"
" currentfile buffer readline pop\n"
" token pop /class exch def pop\n"
" % Compression type.\n"
" currentfile buffer readline pop\n"
" token pop /compression exch def pop\n"
" % Clip and render.\n"
" /pixel_stream currentfile columns rows compression ByteStreamDecodeFilter def\n"
" clipped { ClipImage } if\n"
" alpha stencil not and\n"
" { MaskedImageDict mask_stream resetfile }\n"
" { NonMaskedImageDict }\n"
" ifelse\n"
" stencil { 0 setgray imagemask } { image } ifelse\n"
" grestore\n"
" sp { showpage } if\n"
"} bind def\n";
char
buffer[MagickPathExtent],
date[MagickTimeExtent],
**labels,
page_geometry[MagickPathExtent];
CompressionType
compression;
const char
*option,
*value;
double
pointsize;
GeometryInfo
geometry_info;
MagickBooleanType
status;
MagickOffsetType
offset,
scene,
start,
stop;
MagickStatusType
flags;
MemoryInfo
*pixel_info;
PointInfo
delta,
resolution,
scale;
RectangleInfo
geometry,
media_info,
page_info;
ssize_t
i;
SegmentInfo
bounds;
size_t
imageListLength,
length,
page,
pixel,
text_size;
ssize_t
j;
time_t
timer;
unsigned char
*pixels;
/*
Open output image file.
*/
assert(image_info != (const ImageInfo *) NULL);
assert(image_info->signature == MagickCoreSignature);
assert(image != (Image *) NULL);
assert(image->signature == MagickCoreSignature);
if (image->debug != MagickFalse)
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
assert(exception != (ExceptionInfo *) NULL);
assert(exception->signature == MagickCoreSignature);
status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
if (status == MagickFalse)
return(MagickFalse);
compression=image->compression;
if (image_info->compression != UndefinedCompression)
compression=image_info->compression;
switch (compression)
{
case FaxCompression:
case Group4Compression:
{
if ((SetImageMonochrome(image,exception) == MagickFalse) ||
(image->alpha_trait != UndefinedPixelTrait))
compression=RLECompression;
break;
}
#if !defined(MAGICKCORE_JPEG_DELEGATE)
case JPEGCompression:
{
compression=RLECompression;
(void) ThrowMagickException(exception,GetMagickModule(),
MissingDelegateError,"DelegateLibrarySupportNotBuiltIn","`%s' (JPEG)",
image->filename);
break;
}
#endif
#if !defined(MAGICKCORE_ZLIB_DELEGATE)
case ZipCompression:
{
compression=RLECompression;
(void) ThrowMagickException(exception,GetMagickModule(),
MissingDelegateError,"DelegateLibrarySupportNotBuiltIn","`%s' (ZLIB)",
image->filename);
break;
}
#endif
default:
break;
}
(void) memset(&bounds,0,sizeof(bounds));
page=0;
scene=0;
imageListLength=GetImageListLength(image);
do
{
/*
Scale relative to dots-per-inch.
*/
delta.x=DefaultResolution;
delta.y=DefaultResolution;
resolution.x=image->resolution.x;
resolution.y=image->resolution.y;
if ((resolution.x == 0.0) || (resolution.y == 0.0))
{
flags=ParseGeometry(PSDensityGeometry,&geometry_info);
resolution.x=geometry_info.rho;
resolution.y=geometry_info.sigma;
if ((flags & SigmaValue) == 0)
resolution.y=resolution.x;
}
if (image_info->density != (char *) NULL)
{
flags=ParseGeometry(image_info->density,&geometry_info);
resolution.x=geometry_info.rho;
resolution.y=geometry_info.sigma;
if ((flags & SigmaValue) == 0)
resolution.y=resolution.x;
}
if (image->units == PixelsPerCentimeterResolution)
{
resolution.x=(100.0*2.54*resolution.x+0.5)/100.0;
resolution.y=(100.0*2.54*resolution.y+0.5)/100.0;
}
SetGeometry(image,&geometry);
(void) FormatLocaleString(page_geometry,MagickPathExtent,"%.20gx%.20g",
(double) image->columns,(double) image->rows);
if (image_info->page != (char *) NULL)
(void) CopyMagickString(page_geometry,image_info->page,MagickPathExtent);
else
if ((image->page.width != 0) && (image->page.height != 0))
(void) FormatLocaleString(page_geometry,MagickPathExtent,
"%.20gx%.20g%+.20g%+.20g",(double) image->page.width,(double)
image->page.height,(double) image->page.x,(double) image->page.y);
else
if ((image->gravity != UndefinedGravity) &&
(LocaleCompare(image_info->magick,"PS") == 0))
(void) CopyMagickString(page_geometry,PSPageGeometry,
MagickPathExtent);
(void) ConcatenateMagickString(page_geometry,">",MagickPathExtent);
(void) ParseMetaGeometry(page_geometry,&geometry.x,&geometry.y,
&geometry.width,&geometry.height);
scale.x=PerceptibleReciprocal(resolution.x)*geometry.width*delta.x;
geometry.width=(size_t) floor(scale.x+0.5);
scale.y=PerceptibleReciprocal(resolution.y)*geometry.height*delta.y;
geometry.height=(size_t) floor(scale.y+0.5);
(void) ParseAbsoluteGeometry(page_geometry,&media_info);
(void) ParseGravityGeometry(image,page_geometry,&page_info,exception);
if (image->gravity != UndefinedGravity)
{
geometry.x=(-page_info.x);
geometry.y=(ssize_t) (media_info.height+page_info.y-image->rows);
}
pointsize=12.0;
if (image_info->pointsize != 0.0)
pointsize=image_info->pointsize;
text_size=0;
value=GetImageProperty(image,"label",exception);
if (value != (const char *) NULL)
text_size=(size_t) (MultilineCensus(value)*pointsize+12);
page++;
if (page == 1)
{
/*
Postscript header on the first page.
*/
if (LocaleCompare(image_info->magick,"PS3") == 0)
(void) CopyMagickString(buffer,"%!PS-Adobe-3.0\n",MagickPathExtent);
else
(void) CopyMagickString(buffer,"%!PS-Adobe-3.0 EPSF-3.0\n",
MagickPathExtent);
(void) WriteBlobString(image,buffer);
(void) FormatLocaleString(buffer,MagickPathExtent,
"%%%%Creator: ImageMagick %s\n",MagickLibVersionText);
(void) WriteBlobString(image,buffer);
(void) FormatLocaleString(buffer,MagickPathExtent,"%%%%Title: %s\n",
image->filename);
(void) WriteBlobString(image,buffer);
timer=GetMagickTime();
(void) FormatMagickTime(timer,sizeof(date),date);
(void) FormatLocaleString(buffer,MagickPathExtent,
"%%%%CreationDate: %s\n",date);
(void) WriteBlobString(image,buffer);
bounds.x1=(double) geometry.x;
bounds.y1=(double) geometry.y;
bounds.x2=(double) geometry.x+scale.x;
bounds.y2=(double) geometry.y+scale.y+text_size;
if ((image_info->adjoin != MagickFalse) &&
(GetNextImageInList(image) != (Image *) NULL))
{
(void) WriteBlobString(image,"%%BoundingBox: (atend)\n");
(void) WriteBlobString(image,"%%HiResBoundingBox: (atend)\n");
}
else
{
(void) FormatLocaleString(buffer,MagickPathExtent,
"%%%%BoundingBox: %g %g %g %g\n",ceil(bounds.x1-0.5),
ceil(bounds.y1-0.5),floor(bounds.x2+0.5),floor(bounds.y2+0.5));
(void) WriteBlobString(image,buffer);
(void) FormatLocaleString(buffer,MagickPathExtent,
"%%%%HiResBoundingBox: %g %g %g %g\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 (SetImageGray(image,exception) != MagickFalse)
(void) WriteBlobString(image,
"%%DocumentProcessColors: Black\n");
}
/*
Font resources
*/
value=GetImageProperty(image,"label",exception);
if (value != (const char *) NULL)
(void) WriteBlobString(image,
"%%DocumentNeededResources: font Helvetica\n");
(void) WriteBlobString(image,"%%LanguageLevel: 3\n");
/*
Pages, orientation and 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 == MagickFalse)
(void) CopyMagickString(buffer,"%%Pages: 1\n",MagickPathExtent);
else
(void) FormatLocaleString(buffer,MagickPathExtent,
"%%%%Pages: %.20g\n",(double) imageListLength);
(void) WriteBlobString(image,buffer);
}
if (image->colorspace == CMYKColorspace)
(void) WriteBlobString(image,
"%%DocumentProcessColors: Cyan Magenta Yellow Black\n");
(void) WriteBlobString(image,"%%EndComments\n");
/*
The static postscript procedures prolog.
*/
(void) WriteBlobString(image,"%%BeginProlog\n");
(void) WriteBlob(image,sizeof(PostscriptProlog)-1,PostscriptProlog);
/*
One label line for each line in label string.
*/
value=GetImageProperty(image,"label",exception);
if (value != (const char *) NULL)
{
(void) WriteBlobString(image,"\n %% Labels.\n /Helvetica "
" findfont pointsize scalefont setfont\n");
for (i=(ssize_t) MultilineCensus(value)-1; i >= 0; i--)
{
(void) WriteBlobString(image,
" currentfile buffer readline pop token pop\n");
(void) FormatLocaleString(buffer,MagickPathExtent,
" 0 y %g add moveto show pop\n",i*pointsize+12);
(void) WriteBlobString(image,buffer);
}
}
/*
The static postscript procedures epilog.
*/
(void) WriteBlob(image,sizeof(PostscriptEpilog)-1,PostscriptEpilog);
(void) WriteBlobString(image,"%%EndProlog\n");
}
(void) FormatLocaleString(buffer,MagickPathExtent,"%%%%Page: 1 %.20g\n",
(double) page);
(void) WriteBlobString(image,buffer);
/*
Page bounding box.
*/
(void) FormatLocaleString(buffer,MagickPathExtent,
"%%%%PageBoundingBox: %.20g %.20g %.20g %.20g\n",(double) geometry.x,
(double) geometry.y,geometry.x+(double) geometry.width,geometry.y+
(double) (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 (SetImageGray(image,exception) != MagickFalse)
(void) WriteBlobString(image,"%%PageProcessColors: Black\n");
/*
Adjust document bounding box to bound page bounding box.
*/
if ((double) geometry.x < bounds.x1)
bounds.x1=(double) geometry.x;
if ((double) geometry.y < bounds.y1)
bounds.y1=(double) geometry.y;
if ((double) (geometry.x+scale.x) > bounds.x2)
bounds.x2=(double) geometry.x+scale.x;
if ((double) (geometry.y+scale.y+text_size) > bounds.y2)
bounds.y2=(double) geometry.y+scale.y+text_size;
/*
Page font resource if there's a label.
*/
value=GetImageProperty(image,"label",exception);
if (value != (const char *) NULL)
(void) WriteBlobString(image,"%%PageResources: font Helvetica\n");
/*
PS clipping path from Photoshop clipping path.
*/
if (((image->channels & WriteMaskChannel) != 0) ||
(LocaleNCompare("8BIM:",image->magick_filename,5) != 0))
(void) WriteBlobString(image,"/ClipImage {} def\n");
else
{
const char
*value;
value=GetImageProperty(image,image->magick_filename,exception);
if (value == (const char *) NULL)
return(MagickFalse);
(void) WriteBlobString(image,value);
(void) WriteBlobByte(image,'\n');
}
/*
Push a dictionary for our own def's if this an EPS.
*/
if (LocaleCompare(image_info->magick,"PS3") != 0)
(void) WriteBlobString(image,"userdict begin\n");
/*
Image mask.
*/
if ((image->alpha_trait != UndefinedPixelTrait) &&
(WritePS3MaskImage(image_info,image,compression,exception) == MagickFalse))
{
(void) CloseBlob(image);
return(MagickFalse);
}
/*
Remember position of BeginData comment so we can update it.
*/
start=TellBlob(image);
if (start < 0)
ThrowWriterException(CorruptImageError,"ImproperImageHeader");
(void) FormatLocaleString(buffer,MagickPathExtent,
"%%%%BeginData:%13ld %s Bytes\n",0L,
compression == NoCompression ? "ASCII" : "BINARY");
(void) WriteBlobString(image,buffer);
stop=TellBlob(image);
if (stop < 0)
ThrowWriterException(CorruptImageError,"ImproperImageHeader");
(void) WriteBlobString(image,"DisplayImage\n");
/*
Translate, scale, and font point size.
*/
(void) FormatLocaleString(buffer,MagickPathExtent,"%.20g %.20g\n%g %g\n%g\n",
(double) geometry.x,(double) geometry.y,scale.x,scale.y,pointsize);
(void) WriteBlobString(image,buffer);
/*
Output labels.
*/
labels=(char **) NULL;
value=GetImageProperty(image,"label",exception);
if (value != (const char *) NULL)
labels=StringToList(value);
if (labels != (char **) NULL)
{
for (i=0; labels[i] != (char *) NULL; i++)
{
if (compression != NoCompression)
{
for (j=0; labels[i][j] != '\0'; j++)
(void) WriteBlobByte(image,(unsigned char) labels[i][j]);
(void) WriteBlobByte(image,'\n');
}
else
{
(void) WriteBlobString(image,"<~");
Ascii85Initialize(image);
for (j=0; labels[i][j] != '\0'; j++)
Ascii85Encode(image,(unsigned char) labels[i][j]);
Ascii85Flush(image);
}
labels[i]=DestroyString(labels[i]);
}
labels=(char **) RelinquishMagickMemory(labels);
}
/*
Photoshop clipping path active?
*/
if (((image->channels & WriteMaskChannel) != 0) &&
(LocaleNCompare("8BIM:",image->magick_filename,5) == 0))
(void) WriteBlobString(image,"true\n");
else
(void) WriteBlobString(image,"false\n");
/*
Showpage for non-EPS.
*/
(void) WriteBlobString(image, LocaleCompare(image_info->magick,"PS3") == 0 ?
"true\n" : "false\n");
/*
Image columns, rows, and color space.
*/
(void) FormatLocaleString(buffer,MagickPathExtent,"%.20g %.20g\n%s\n",
(double) image->columns,(double) image->rows,image->colorspace ==
CMYKColorspace ? PS3_CMYKColorspace : PS3_RGBColorspace);
(void) WriteBlobString(image,buffer);
/*
Masked image?
*/
(void) WriteBlobString(image,image->alpha_trait != UndefinedPixelTrait ?
"true\n" : "false\n");
/*
Render with imagemask operator?
*/
option=GetImageOption(image_info,"ps3:imagemask");
(void) WriteBlobString(image,((option != (const char *) NULL) &&
(SetImageMonochrome(image,exception) != MagickFalse)) ?
"true\n" : "false\n");
/*
Output pixel data.
*/
pixels=(unsigned char *) NULL;
length=0;
if ((image_info->type != TrueColorType) &&
(image_info->type != TrueColorAlphaType) &&
(image_info->type != ColorSeparationType) &&
(image_info->type != ColorSeparationAlphaType) &&
(image->colorspace != CMYKColorspace) &&
((SetImageGray(image,exception) != MagickFalse) ||
(SetImageMonochrome(image,exception) != MagickFalse)))
{
/*
Gray images.
*/
(void) WriteBlobString(image,PS3_PseudoClass"\n");
switch (compression)
{
case NoCompression:
default:
{
(void) WriteBlobString(image,PS3_NoCompression"\n");
break;
}
case FaxCompression:
case Group4Compression:
{
(void) WriteBlobString(image,PS3_FaxCompression"\n");
break;
}
case JPEGCompression:
{
(void) WriteBlobString(image,PS3_JPEGCompression"\n");
break;
}
case LZWCompression:
{
(void) WriteBlobString(image,PS3_LZWCompression"\n");
break;
}
case RLECompression:
{
(void) WriteBlobString(image,PS3_RLECompression"\n");
break;
}
case ZipCompression:
{
(void) WriteBlobString(image,PS3_ZipCompression"\n");
break;
}
}
/*
Number of colors -- 0 for single component non-color mapped data.
*/
(void) WriteBlobString(image,"0\n");
/*
1 bit or 8 bit components?
*/
(void) FormatLocaleString(buffer,MagickPathExtent,"%d\n",
SetImageMonochrome(image,exception) != MagickFalse ? 1 : 8);
(void) WriteBlobString(image,buffer);
/*
Image data.
*/
if (compression == JPEGCompression)
status=InjectImageBlob(image_info,image,image,"jpeg",exception);
else
if ((compression == FaxCompression) ||
(compression == Group4Compression))
{
if (LocaleCompare(CCITTParam,"0") == 0)
status=HuffmanEncodeImage(image_info,image,image,exception);
else
status=Huffman2DEncodeImage(image_info,image,image,exception);
}
else
{
status=SerializeImageChannel(image_info,image,&pixel_info,&length,
exception);
if (status == MagickFalse)
{
(void) CloseBlob(image);
return(MagickFalse);
}
pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
switch (compression)
{
case NoCompression:
default:
{
Ascii85Initialize(image);
for (i=0; i < (ssize_t) length; i++)
Ascii85Encode(image,pixels[i]);
Ascii85Flush(image);
status=MagickTrue;
break;
}
case LZWCompression:
{
status=LZWEncodeImage(image,length,pixels,exception);
break;
}
case RLECompression:
{
status=PackbitsEncodeImage(image,length,pixels,exception);
break;
}
case ZipCompression:
{
status=ZLIBEncodeImage(image,length,pixels,exception);
break;
}
}
pixel_info=RelinquishVirtualMemory(pixel_info);
}
}
else
if ((image->storage_class == DirectClass) || (image->colors > 256) ||
(compression == JPEGCompression))
{
/*
Truecolor image.
*/
(void) WriteBlobString(image,PS3_DirectClass"\n");
switch (compression)
{
case NoCompression:
default:
{
(void) WriteBlobString(image,PS3_NoCompression"\n");
break;
}
case RLECompression:
{
(void) WriteBlobString(image,PS3_RLECompression"\n");
break;
}
case JPEGCompression:
{
(void) WriteBlobString(image,PS3_JPEGCompression"\n");
break;
}
case LZWCompression:
{
(void) WriteBlobString(image,PS3_LZWCompression"\n");
break;
}
case ZipCompression:
{
(void) WriteBlobString(image,PS3_ZipCompression"\n");
break;
}
}
/*
Image data.
*/
if (compression == JPEGCompression)
status=InjectImageBlob(image_info,image,image,"jpeg",exception);
else
{
/*
Stream based compressions.
*/
status=SerializeImage(image_info,image,&pixel_info,&length,
exception);
if (status == MagickFalse)
{
(void) CloseBlob(image);
return(MagickFalse);
}
pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
switch (compression)
{
case NoCompression:
default:
{
Ascii85Initialize(image);
for (i=0; i < (ssize_t) length; i++)
Ascii85Encode(image,pixels[i]);
Ascii85Flush(image);
status=MagickTrue;
break;
}
case RLECompression:
{
status=PackbitsEncodeImage(image,length,pixels,exception);
break;
}
case LZWCompression:
{
status=LZWEncodeImage(image,length,pixels,exception);
break;
}
case ZipCompression:
{
status=ZLIBEncodeImage(image,length,pixels,exception);
break;
}
}
pixel_info=RelinquishVirtualMemory(pixel_info);
}
}
else
{
/*
Colormapped images.
*/
(void) WriteBlobString(image,PS3_PseudoClass"\n");
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.
*/
(void) FormatLocaleString(buffer,MagickPathExtent,"%.20g\n",
(double) image->colors);
(void) WriteBlobString(image,buffer);
/*
Color map - uncompressed.
*/
if ((compression != NoCompression) &&
(compression != UndefinedCompression))
{
for (i=0; i < (ssize_t) image->colors; i++)
{
pixel=ScaleQuantumToChar(image->colormap[i].red);
(void) WriteBlobByte(image,(unsigned char) pixel);
pixel=ScaleQuantumToChar(image->colormap[i].green);
(void) WriteBlobByte(image,(unsigned char) pixel);
pixel=ScaleQuantumToChar(image->colormap[i].blue);
(void) WriteBlobByte(image,(unsigned char) pixel);
}
}
else
{
Ascii85Initialize(image);
for (i=0; i < (ssize_t) image->colors; i++)
{
pixel=ScaleQuantumToChar(image->colormap[i].red);
Ascii85Encode(image,(unsigned char) pixel);
pixel=ScaleQuantumToChar(image->colormap[i].green);
Ascii85Encode(image,(unsigned char) pixel);
pixel=ScaleQuantumToChar(image->colormap[i].blue);
Ascii85Encode(image,(unsigned char) pixel);
}
Ascii85Flush(image);
}
status=SerializeImageIndexes(image_info,image,&pixel_info,&length,
exception);
if (status == MagickFalse)
{
(void) CloseBlob(image);
return(MagickFalse);
}
pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
switch (compression)
{
case NoCompression:
default:
{
Ascii85Initialize(image);
for (i=0; i < (ssize_t) length; i++)
Ascii85Encode(image,pixels[i]);
Ascii85Flush(image);
status=MagickTrue;
break;
}
case RLECompression:
{
status=PackbitsEncodeImage(image,length,pixels,exception);
break;
}
case LZWCompression:
{
status=LZWEncodeImage(image,length,pixels,exception);
break;
}
case ZipCompression:
{
status=ZLIBEncodeImage(image,length,pixels,exception);
break;
}
}
pixel_info=RelinquishVirtualMemory(pixel_info);
}
(void) WriteBlobByte(image,'\n');
if (status == MagickFalse)
{
(void) CloseBlob(image);
return(MagickFalse);
}
/*
Update BeginData now that we know the data size.
*/
length=(size_t) (TellBlob(image)-stop);
stop=TellBlob(image);
if (stop < 0)
ThrowWriterException(CorruptImageError,"ImproperImageHeader");
offset=SeekBlob(image,start,SEEK_SET);
if (offset < 0)
ThrowWriterException(CorruptImageError,"ImproperImageHeader");
(void) FormatLocaleString(buffer,MagickPathExtent,
"%%%%BeginData:%13ld %s Bytes\n",(long) length,
compression == NoCompression ? "ASCII" : "BINARY");
(void) WriteBlobString(image,buffer);
offset=SeekBlob(image,stop,SEEK_SET);
(void) WriteBlobString(image,"%%EndData\n");
/*
End private dictionary if this an EPS.
*/
if (LocaleCompare(image_info->magick,"PS3") != 0)
(void) WriteBlobString(image,"end\n");
(void) WriteBlobString(image,"%%PageTrailer\n");
if (GetNextImageInList(image) == (Image *) NULL)
break;
image=SyncNextImageInList(image);
status=SetImageProgress(image,SaveImagesTag,scene++,imageListLength);
if (status == MagickFalse)
break;
} while (image_info->adjoin != MagickFalse);
(void) WriteBlobString(image,"%%Trailer\n");
if (page > 1)
{
(void) FormatLocaleString(buffer,MagickPathExtent,
"%%%%BoundingBox: %g %g %g %g\n",ceil(bounds.x1-0.5),
ceil(bounds.y1-0.5),floor(bounds.x2+0.5),floor(bounds.y2+0.5));
(void) WriteBlobString(image,buffer);
(void) FormatLocaleString(buffer,MagickPathExtent,
"%%%%HiResBoundingBox: %g %g %g %g\n",bounds.x1,bounds.y1,bounds.x2,
bounds.y2);
(void) WriteBlobString(image,buffer);
}
(void) WriteBlobString(image,"%%EOF\n");
(void) CloseBlob(image);
return(MagickTrue);
}