1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3%                                                                             %
4%                                                                             %
5%                                                                             %
6%                            PPPP   SSSSS  33333                              %
7%                            P   P  SS        33                              %
8%                            PPPP    SSS    333                               %
9%                            P         SS     33                              %
10%                            P      SSSSS  33333                              %
11%                                                                             %
12%                                                                             %
13%                     Write Postscript Level III Format                       %
14%                                                                             %
15%                              Software Design                                %
16%                                   Cristy                                    %
17%                              Lars Ruben Skyum                               %
18%                                 July 1992                                   %
19%                                                                             %
20%                                                                             %
21%  Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization      %
22%  dedicated to making software imaging solutions freely available.           %
23%                                                                             %
24%  You may not use this file except in compliance with the License.  You may  %
25%  obtain a copy of the License at                                            %
26%                                                                             %
27%    http://www.imagemagick.org/script/license.php                            %
28%                                                                             %
29%  Unless required by applicable law or agreed to in writing, software        %
30%  distributed under the License is distributed on an "AS IS" BASIS,          %
31%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
32%  See the License for the specific language governing permissions and        %
33%  limitations under the License.                                             %
34%                                                                             %
35%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36%
37%
38*/
39
40/*
41  Include declarations.
42*/
43#include "MagickCore/studio.h"
44#include "MagickCore/artifact.h"
45#include "MagickCore/attribute.h"
46#include "MagickCore/blob.h"
47#include "MagickCore/blob-private.h"
48#include "MagickCore/cache.h"
49#include "MagickCore/channel.h"
50#include "MagickCore/color.h"
51#include "MagickCore/color-private.h"
52#include "MagickCore/compress.h"
53#include "MagickCore/constitute.h"
54#include "MagickCore/draw.h"
55#include "MagickCore/exception.h"
56#include "MagickCore/exception-private.h"
57#include "MagickCore/geometry.h"
58#include "MagickCore/image.h"
59#include "MagickCore/image-private.h"
60#include "MagickCore/list.h"
61#include "MagickCore/magick.h"
62#include "MagickCore/memory_.h"
63#include "MagickCore/monitor.h"
64#include "MagickCore/monitor-private.h"
65#include "MagickCore/option.h"
66#include "MagickCore/pixel-accessor.h"
67#include "MagickCore/property.h"
68#include "MagickCore/quantum-private.h"
69#include "MagickCore/resource_.h"
70#include "MagickCore/static.h"
71#include "MagickCore/string_.h"
72#include "MagickCore/module.h"
73#include "MagickCore/token.h"
74#include "MagickCore/utility.h"
75#include "MagickCore/module.h"
76
77/*
78  Define declarations.
79*/
80#define PS3_NoCompression "0"
81#define PS3_FaxCompression "1"
82#define PS3_JPEGCompression "2"
83#define PS3_LZWCompression "3"
84#define PS3_RLECompression "4"
85#define PS3_ZipCompression "5"
86
87#define PS3_RGBColorspace "0"
88#define PS3_CMYKColorspace "1"
89
90#define PS3_DirectClass "0"
91#define PS3_PseudoClass "1"
92
93#if defined(MAGICKCORE_TIFF_DELEGATE)
94#define CCITTParam  "-1"
95#else
96#define CCITTParam  "0"
97#endif
98
99/*
100  Forward declarations.
101*/
102static MagickBooleanType
103  WritePS3Image(const ImageInfo *,Image *,ExceptionInfo *);
104
105/*
106%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
107%                                                                             %
108%                                                                             %
109%                                                                             %
110%   R e g i s t e r P S 3 I m a g e                                           %
111%                                                                             %
112%                                                                             %
113%                                                                             %
114%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
115%
116%  RegisterPS3Image() adds properties for the PS3 image format to the list of
117%  supported formats.  The properties include the image format tag, a method to
118%  read and/or write the format, whether the format supports the saving of more
119%  than one frame to the same file or blob, whether the format supports native
120%  in-memory I/O, and a brief description of the format.
121%
122%  The format of the RegisterPS3Image method is:
123%
124%      size_t RegisterPS3Image(void)
125%
126*/
127ModuleExport size_t RegisterPS3Image(void)
128{
129  MagickInfo
130    *entry;
131
132  entry=AcquireMagickInfo("PS3","EPS3","Level III Encapsulated PostScript");
133  entry->encoder=(EncodeImageHandler *) WritePS3Image;
134  entry->mime_type=ConstantString("application/postscript");
135  entry->flags|=CoderSeekableStreamFlag;
136  (void) RegisterMagickInfo(entry);
137  entry=AcquireMagickInfo("PS3","PS3","Level III PostScript");
138  entry->encoder=(EncodeImageHandler *) WritePS3Image;
139  entry->mime_type=ConstantString("application/postscript");
140  entry->flags|=CoderSeekableStreamFlag;
141  (void) RegisterMagickInfo(entry);
142  return(MagickImageCoderSignature);
143}
144
145/*
146%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
147%                                                                             %
148%                                                                             %
149%                                                                             %
150%   U n r e g i s t e r P S 3 I m a g e                                       %
151%                                                                             %
152%                                                                             %
153%                                                                             %
154%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
155%
156%  UnregisterPS3Image() removes format registrations made by the PS3 module
157%  from the list of supported formats.
158%
159%  The format of the UnregisterPS3Image method is:
160%
161%      UnregisterPS3Image(void)
162%
163*/
164ModuleExport void UnregisterPS3Image(void)
165{
166  (void) UnregisterMagickInfo("EPS3");
167  (void) UnregisterMagickInfo("PS3");
168}
169
170/*
171%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
172%                                                                             %
173%                                                                             %
174%                                                                             %
175%   W r i t e P S 3 I m a g e                                                 %
176%                                                                             %
177%                                                                             %
178%                                                                             %
179%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
180%
181%  WritePS3Image() translates an image to encapsulated Postscript Level III
182%  for printing.  If the supplied geometry is null, the image is centered on
183%  the Postscript page.  Otherwise, the image is positioned as specified by the
184%  geometry.
185%
186%  The format of the WritePS3Image method is:
187%
188%      MagickBooleanType WritePS3Image(const ImageInfo *image_info,
189%        Image *image,ExceptionInfo *exception)
190%
191%  A description of each parameter follows:
192%
193%    o image_info: Specifies a pointer to a ImageInfo structure.
194%
195%    o image: the image.
196%
197%    o exception: return any errors or warnings in this structure.
198%
199*/
200
201static MagickBooleanType Huffman2DEncodeImage(const ImageInfo *image_info,
202  Image *image,Image *inject_image,ExceptionInfo *exception)
203{
204  Image
205    *group4_image;
206
207  ImageInfo
208    *write_info;
209
210  MagickBooleanType
211    status;
212
213  size_t
214    length;
215
216  unsigned char
217    *group4;
218
219  status=MagickTrue;
220  write_info=CloneImageInfo(image_info);
221  (void) CopyMagickString(write_info->filename,"GROUP4:",MagickPathExtent);
222  (void) CopyMagickString(write_info->magick,"GROUP4",MagickPathExtent);
223  group4_image=CloneImage(inject_image,0,0,MagickTrue,exception);
224  if (group4_image == (Image *) NULL)
225    return(MagickFalse);
226  group4=(unsigned char *) ImageToBlob(write_info,group4_image,&length,
227    exception);
228  group4_image=DestroyImage(group4_image);
229  if (group4 == (unsigned char *) NULL)
230    return(MagickFalse);
231  write_info=DestroyImageInfo(write_info);
232  if (WriteBlob(image,length,group4) != (ssize_t) length)
233    status=MagickFalse;
234  group4=(unsigned char *) RelinquishMagickMemory(group4);
235  return(status);
236}
237
238static MagickBooleanType SerializeImage(const ImageInfo *image_info,
239  Image *image,MemoryInfo **pixel_info,size_t *length,ExceptionInfo *exception)
240{
241  MagickBooleanType
242    status;
243
244  register const Quantum
245    *p;
246
247  register ssize_t
248    x;
249
250  register unsigned char
251    *q;
252
253  ssize_t
254    y;
255
256  assert(image != (Image *) NULL);
257  assert(image->signature == MagickCoreSignature);
258  if (image->debug != MagickFalse)
259    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
260  status=MagickTrue;
261  *length=(image->colorspace == CMYKColorspace ? 4 : 3)*(size_t)
262    image->columns*image->rows;
263  *pixel_info=AcquireVirtualMemory(*length,sizeof(*q));
264  if (*pixel_info == (MemoryInfo *) NULL)
265    ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
266  q=(unsigned char *) GetVirtualMemoryBlob(*pixel_info);
267  for (y=0; y < (ssize_t) image->rows; y++)
268  {
269    p=GetVirtualPixels(image,0,y,image->columns,1,exception);
270    if (p == (const Quantum *) NULL)
271      break;
272    if (image->colorspace != CMYKColorspace)
273      for (x=0; x < (ssize_t) image->columns; x++)
274      {
275        *q++=ScaleQuantumToChar(GetPixelRed(image,p));
276        *q++=ScaleQuantumToChar(GetPixelGreen(image,p));
277        *q++=ScaleQuantumToChar(GetPixelBlue(image,p));
278        p+=GetPixelChannels(image);
279      }
280    else
281      for (x=0; x < (ssize_t) image->columns; x++)
282      {
283        *q++=ScaleQuantumToChar(GetPixelRed(image,p));
284        *q++=ScaleQuantumToChar(GetPixelGreen(image,p));
285        *q++=ScaleQuantumToChar(GetPixelBlue(image,p));
286        *q++=ScaleQuantumToChar(GetPixelBlack(image,p));
287        p+=GetPixelChannels(image);
288      }
289    if (image->previous == (Image *) NULL)
290      {
291        status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
292          image->rows);
293        if (status == MagickFalse)
294          break;
295      }
296  }
297  if (status == MagickFalse)
298    *pixel_info=RelinquishVirtualMemory(*pixel_info);
299  return(status);
300}
301
302static MagickBooleanType SerializeImageChannel(const ImageInfo *image_info,
303  Image *image,MemoryInfo **pixel_info,size_t *length,ExceptionInfo *exception)
304{
305  MagickBooleanType
306    status;
307
308  register const Quantum
309    *p;
310
311  register ssize_t
312    x;
313
314  register unsigned char
315    *q;
316
317  size_t
318    pack,
319    padded_columns;
320
321  ssize_t
322    y;
323
324  unsigned char
325    code,
326    bit;
327
328  assert(image != (Image *) NULL);
329  assert(image->signature == MagickCoreSignature);
330  if (image->debug != MagickFalse)
331    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
332  status=MagickTrue;
333  pack=SetImageMonochrome(image,exception) == MagickFalse ? 1UL : 8UL;
334  padded_columns=((image->columns+pack-1)/pack)*pack;
335  *length=(size_t) padded_columns*image->rows/pack;
336  *pixel_info=AcquireVirtualMemory(*length,sizeof(*q));
337  if (*pixel_info == (MemoryInfo *) NULL)
338    ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
339  q=(unsigned char *) GetVirtualMemoryBlob(*pixel_info);
340  for (y=0; y < (ssize_t) image->rows; y++)
341  {
342    p=GetVirtualPixels(image,0,y,image->columns,1,exception);
343    if (p == (const Quantum *) NULL)
344      break;
345    if (pack == 1)
346      for (x=0; x < (ssize_t) image->columns; x++)
347      {
348        *q++=ScaleQuantumToChar(ClampToQuantum(GetPixelLuma(image,p)));
349        p+=GetPixelChannels(image);
350      }
351    else
352      {
353        code='\0';
354        for (x=0; x < (ssize_t) padded_columns; x++)
355        {
356          bit=(unsigned char) 0x00;
357          if (x < (ssize_t) image->columns)
358            bit=(unsigned char) (GetPixelLuma(image,p) == TransparentAlpha ?
359              0x01 : 0x00);
360          code=(code << 1)+bit;
361          if (((x+1) % pack) == 0)
362            {
363              *q++=code;
364              code='\0';
365            }
366          p+=GetPixelChannels(image);
367        }
368      }
369    status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
370      image->rows);
371    if (status == MagickFalse)
372      break;
373  }
374  if (status == MagickFalse)
375    *pixel_info=RelinquishVirtualMemory(*pixel_info);
376  return(status);
377}
378
379static MagickBooleanType SerializeImageIndexes(const ImageInfo *image_info,
380  Image *image,MemoryInfo **pixel_info,size_t *length,ExceptionInfo *exception)
381{
382  MagickBooleanType
383    status;
384
385  register const Quantum
386    *p;
387
388  register ssize_t
389    x;
390
391  register unsigned char
392    *q;
393
394  ssize_t
395    y;
396
397  assert(image != (Image *) NULL);
398  assert(image->signature == MagickCoreSignature);
399  if (image->debug != MagickFalse)
400    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
401  status=MagickTrue;
402  *length=(size_t) image->columns*image->rows;
403  *pixel_info=AcquireVirtualMemory(*length,sizeof(*q));
404  if (*pixel_info == (MemoryInfo *) NULL)
405    ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
406  q=(unsigned char *) GetVirtualMemoryBlob(*pixel_info);
407  for (y=0; y < (ssize_t) image->rows; y++)
408  {
409    p=GetVirtualPixels(image,0,y,image->columns,1,exception);
410    if (p == (const Quantum *) NULL)
411      break;
412    for (x=0; x < (ssize_t) image->columns; x++)
413    {
414      *q++=(unsigned char) GetPixelIndex(image,p);
415      p+=GetPixelChannels(image);
416    }
417    if (image->previous == (Image *) NULL)
418      {
419        status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
420          image->rows);
421        if (status == MagickFalse)
422          break;
423      }
424  }
425  if (status == MagickFalse)
426    *pixel_info=RelinquishVirtualMemory(*pixel_info);
427  return(status);
428}
429
430static MagickBooleanType WritePS3MaskImage(const ImageInfo *image_info,
431  Image *image,const CompressionType compression,ExceptionInfo *exception)
432{
433  char
434    buffer[MagickPathExtent];
435
436  Image
437    *mask_image;
438
439  MagickBooleanType
440    status;
441
442  MagickOffsetType
443    offset,
444    start,
445    stop;
446
447  MemoryInfo
448    *pixel_info;
449
450  register ssize_t
451    i;
452
453  size_t
454    length;
455
456  unsigned char
457    *pixels;
458
459  assert(image_info != (ImageInfo *) NULL);
460  assert(image_info->signature == MagickCoreSignature);
461  assert(image != (Image *) NULL);
462  assert(image->signature == MagickCoreSignature);
463  if (image->debug != MagickFalse)
464    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
465  assert(image->alpha_trait != UndefinedPixelTrait);
466  status=MagickTrue;
467  /*
468    Note BeginData DSC comment for update later.
469  */
470  start=TellBlob(image);
471  if (start < 0)
472    ThrowWriterException(CorruptImageError,"ImproperImageHeader");
473  (void) FormatLocaleString(buffer,MagickPathExtent,
474    "%%%%BeginData:%13ld %s Bytes\n",0L,compression == NoCompression ?
475    "ASCII" : "BINARY");
476  (void) WriteBlobString(image,buffer);
477  stop=TellBlob(image);
478  if (stop < 0)
479    ThrowWriterException(CorruptImageError,"ImproperImageHeader");
480  /*
481    Only lossless compressions for the mask.
482  */
483  switch (compression)
484  {
485    case NoCompression:
486    default:
487    {
488      (void) FormatLocaleString(buffer,MagickPathExtent,
489        "currentfile %.20g %.20g "PS3_NoCompression" ByteStreamDecodeFilter\n",
490        (double) image->columns,(double) image->rows);
491      break;
492    }
493    case FaxCompression:
494    case Group4Compression:
495    {
496      (void) FormatLocaleString(buffer,MagickPathExtent,
497        "currentfile %.20g %.20g "PS3_FaxCompression" ByteStreamDecodeFilter\n",
498        (double) image->columns,(double) image->rows);
499      break;
500    }
501    case LZWCompression:
502    {
503      (void) FormatLocaleString(buffer,MagickPathExtent,
504        "currentfile %.20g %.20g "PS3_LZWCompression" ByteStreamDecodeFilter\n",
505        (double) image->columns,(double) image->rows);
506      break;
507    }
508    case RLECompression:
509    {
510      (void) FormatLocaleString(buffer,MagickPathExtent,
511        "currentfile %.20g %.20g "PS3_RLECompression" ByteStreamDecodeFilter\n",
512        (double) image->columns,(double) image->rows);
513      break;
514    }
515    case ZipCompression:
516    {
517      (void) FormatLocaleString(buffer,MagickPathExtent,
518        "currentfile %.20g %.20g "PS3_ZipCompression" ByteStreamDecodeFilter\n",
519        (double) image->columns,(double) image->rows);
520      break;
521    }
522  }
523  (void) WriteBlobString(image,buffer);
524  (void) WriteBlobString(image,"/ReusableStreamDecode filter\n");
525  mask_image=SeparateImage(image,AlphaChannel,exception);
526  if (mask_image == (Image *) NULL)
527    ThrowWriterException(CoderError,exception->reason);
528  (void) SetImageType(mask_image,BilevelType,exception);
529  (void) SetImageType(mask_image,PaletteType,exception);
530  mask_image->alpha_trait=UndefinedPixelTrait;
531  pixels=(unsigned char *) NULL;
532  length=0;
533  switch (compression)
534  {
535    case NoCompression:
536    default:
537    {
538      status=SerializeImageChannel(image_info,mask_image,&pixel_info,&length,
539        exception);
540      if (status == MagickFalse)
541        break;
542      Ascii85Initialize(image);
543      pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
544      for (i=0; i < (ssize_t) length; i++)
545        Ascii85Encode(image,pixels[i]);
546      Ascii85Flush(image);
547      pixel_info=RelinquishVirtualMemory(pixel_info);
548      break;
549    }
550    case FaxCompression:
551    case Group4Compression:
552    {
553      if ((compression == FaxCompression) ||
554          (LocaleCompare(CCITTParam,"0") == 0))
555        status=HuffmanEncodeImage(image_info,image,mask_image,exception);
556      else
557        status=Huffman2DEncodeImage(image_info,image,mask_image,exception);
558      break;
559    }
560    case LZWCompression:
561    {
562      status=SerializeImageChannel(image_info,mask_image,&pixel_info,&length,
563        exception);
564      if (status == MagickFalse)
565        break;
566      pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
567      status=LZWEncodeImage(image,length,pixels,exception);
568      pixel_info=RelinquishVirtualMemory(pixel_info);
569      break;
570    }
571    case RLECompression:
572    {
573      status=SerializeImageChannel(image_info,mask_image,&pixel_info,&length,
574        exception);
575      if (status == MagickFalse)
576        break;
577      pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
578      status=PackbitsEncodeImage(image,length,pixels,exception);
579      pixel_info=RelinquishVirtualMemory(pixel_info);
580      break;
581    }
582    case ZipCompression:
583    {
584      status=SerializeImageChannel(image_info,mask_image,&pixel_info,&length,
585        exception);
586      if (status == MagickFalse)
587        break;
588      pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
589      status=ZLIBEncodeImage(image,length,pixels,exception);
590      pixel_info=RelinquishVirtualMemory(pixel_info);
591      break;
592    }
593  }
594  mask_image=DestroyImage(mask_image);
595  (void) WriteBlobByte(image,'\n');
596  length=(size_t) (TellBlob(image)-stop);
597  stop=TellBlob(image);
598  if (stop < 0)
599    ThrowWriterException(CorruptImageError,"ImproperImageHeader");
600  offset=SeekBlob(image,start,SEEK_SET);
601  if (offset < 0)
602    ThrowWriterException(CorruptImageError,"ImproperImageHeader");
603  (void) FormatLocaleString(buffer,MagickPathExtent,
604    "%%%%BeginData:%13ld %s Bytes\n",(long) length,
605    compression == NoCompression ? "ASCII" : "BINARY");
606  (void) WriteBlobString(image,buffer);
607  offset=SeekBlob(image,stop,SEEK_SET);
608  if (offset < 0)
609    ThrowWriterException(CorruptImageError,"ImproperImageHeader");
610  (void) WriteBlobString(image,"%%EndData\n");
611  (void) WriteBlobString(image, "/mask_stream exch def\n");
612  return(status);
613}
614
615static MagickBooleanType WritePS3Image(const ImageInfo *image_info,Image *image,
616  ExceptionInfo *exception)
617{
618  static const char
619    *const PostscriptProlog[]=
620    {
621      "/ByteStreamDecodeFilter",
622      "{",
623      "  /z exch def",
624      "  /r exch def",
625      "  /c exch def",
626      "  z "PS3_NoCompression" eq { /ASCII85Decode filter } if",
627      "  z "PS3_FaxCompression" eq",
628      "  {",
629      "    <<",
630      "      /K "CCITTParam,
631      "      /Columns c",
632      "      /Rows r",
633      "    >>",
634      "    /CCITTFaxDecode filter",
635      "  } if",
636      "  z "PS3_JPEGCompression" eq { /DCTDecode filter } if",
637      "  z "PS3_LZWCompression" eq { /LZWDecode filter } if",
638      "  z "PS3_RLECompression" eq { /RunLengthDecode filter } if",
639      "  z "PS3_ZipCompression" eq { /FlateDecode filter } if",
640      "} bind def",
641      "",
642      "/DirectClassImageDict",
643      "{",
644      "  colorspace "PS3_RGBColorspace" eq",
645      "  {",
646      "    /DeviceRGB setcolorspace",
647      "    <<",
648      "      /ImageType 1",
649      "      /Width columns",
650      "      /Height rows",
651      "      /BitsPerComponent 8",
652      "      /DataSource pixel_stream",
653      "      /MultipleDataSources false",
654      "      /ImageMatrix [columns 0 0 rows neg 0 rows]",
655      "      /Decode [0 1 0 1 0 1]",
656      "    >>",
657      "  }",
658      "  {",
659      "    /DeviceCMYK setcolorspace",
660      "    <<",
661      "      /ImageType 1",
662      "      /Width columns",
663      "      /Height rows",
664      "      /BitsPerComponent 8",
665      "      /DataSource pixel_stream",
666      "      /MultipleDataSources false",
667      "      /ImageMatrix [columns 0 0 rows neg 0 rows]",
668      "      /Decode",
669      "        compression "PS3_JPEGCompression" eq",
670      "        { [1 0 1 0 1 0 1 0] }",
671      "        { [0 1 0 1 0 1 0 1] }",
672      "        ifelse",
673      "    >>",
674      "  }",
675      "  ifelse",
676      "} bind def",
677      "",
678      "/PseudoClassImageDict",
679      "{",
680      "  % Colors in colormap image.",
681      "  currentfile buffer readline pop",
682      "  token pop /colors exch def pop",
683      "  colors 0 eq",
684      "  {",
685      "    % Depth of grayscale image.",
686      "    currentfile buffer readline pop",
687      "    token pop /bits exch def pop",
688      "    /DeviceGray setcolorspace",
689      "    <<",
690      "      /ImageType 1",
691      "      /Width columns",
692      "      /Height rows",
693      "      /BitsPerComponent bits",
694      "      /Decode [0 1]",
695      "      /ImageMatrix [columns 0 0 rows neg 0 rows]",
696      "      /DataSource pixel_stream",
697      "    >>",
698      "  }",
699      "  {",
700      "    % RGB colormap.",
701      "    /colormap colors 3 mul string def",
702      "    compression "PS3_NoCompression" eq",
703      "    { currentfile /ASCII85Decode filter colormap readstring pop pop }",
704      "    { currentfile colormap readstring pop pop }",
705      "    ifelse",
706      "    [ /Indexed /DeviceRGB colors 1 sub colormap ] setcolorspace",
707      "    <<",
708      "      /ImageType 1",
709      "      /Width columns",
710      "      /Height rows",
711      "      /BitsPerComponent 8",
712      "      /Decode [0 255]",
713      "      /ImageMatrix [columns 0 0 rows neg 0 rows]",
714      "      /DataSource pixel_stream",
715      "    >>",
716      "  }",
717      "  ifelse",
718      "} bind def",
719      "",
720      "/NonMaskedImageDict",
721      "{",
722      "  class "PS3_PseudoClass" eq",
723      "  { PseudoClassImageDict }",
724      "  { DirectClassImageDict }",
725      "  ifelse",
726      "} bind def",
727      "",
728      "/MaskedImageDict",
729      "{",
730      "  <<",
731      "    /ImageType 3",
732      "    /InterleaveType 3",
733      "    /DataDict NonMaskedImageDict",
734      "    /MaskDict",
735      "    <<",
736      "      /ImageType 1",
737      "      /Width columns",
738      "      /Height rows",
739      "      /BitsPerComponent 1",
740      "      /DataSource mask_stream",
741      "      /MultipleDataSources false",
742      "      /ImageMatrix [ columns 0 0 rows neg 0 rows]",
743      "      /Decode [ 0 1 ]",
744      "    >>",
745      "  >>",
746      "} bind def",
747      "",
748      "/ClipImage",
749      "{} def",
750      "",
751      "/DisplayImage",
752      "{",
753      "  gsave",
754      "  /buffer 512 string def",
755      "  % Translation.",
756      "  currentfile buffer readline pop",
757      "  token pop /x exch def",
758      "  token pop /y exch def pop",
759      "  x y translate",
760      "  % Image size and font size.",
761      "  currentfile buffer readline pop",
762      "  token pop /x exch def",
763      "  token pop /y exch def pop",
764      "  currentfile buffer readline pop",
765      "  token pop /pointsize exch def pop",
766      (const char *) NULL
767    },
768    *const PostscriptEpilog[]=
769    {
770      "  x y scale",
771      "  % Clipping path.",
772      "  currentfile buffer readline pop",
773      "  token pop /clipped exch def pop",
774      "  % Showpage.",
775      "  currentfile buffer readline pop",
776      "  token pop /sp exch def pop",
777      "  % Image pixel size.",
778      "  currentfile buffer readline pop",
779      "  token pop /columns exch def",
780      "  token pop /rows exch def pop",
781      "  % Colorspace (RGB/CMYK).",
782      "  currentfile buffer readline pop",
783      "  token pop /colorspace exch def pop",
784      "  % Transparency.",
785      "  currentfile buffer readline pop",
786      "  token pop /alpha exch def pop",
787      "  % Stencil mask?",
788      "  currentfile buffer readline pop",
789      "  token pop /stencil exch def pop",
790      "  % Image class (direct/pseudo).",
791      "  currentfile buffer readline pop",
792      "  token pop /class exch def pop",
793      "  % Compression type.",
794      "  currentfile buffer readline pop",
795      "  token pop /compression exch def pop",
796      "  % Clip and render.",
797      "  /pixel_stream currentfile columns rows compression ByteStreamDecodeFilter def",
798      "  clipped { ClipImage } if",
799      "  alpha stencil not and",
800      "  { MaskedImageDict mask_stream resetfile }",
801      "  { NonMaskedImageDict }",
802      "  ifelse",
803      "  stencil { 0 setgray imagemask } { image } ifelse",
804      "  grestore",
805      "  sp { showpage } if",
806      "} bind def",
807      (const char *) NULL
808    };
809
810  char
811    buffer[MagickPathExtent],
812    date[MagickPathExtent],
813    **labels,
814    page_geometry[MagickPathExtent];
815
816  CompressionType
817    compression;
818
819  const char
820    *option,
821    *const *q,
822    *value;
823
824  double
825    pointsize;
826
827  GeometryInfo
828    geometry_info;
829
830  MagickBooleanType
831    status;
832
833  MagickOffsetType
834    offset,
835    scene,
836    start,
837    stop;
838
839  MagickStatusType
840    flags;
841
842  MemoryInfo
843    *pixel_info;
844
845  PointInfo
846    delta,
847    resolution,
848    scale;
849
850  RectangleInfo
851    geometry,
852    media_info,
853    page_info;
854
855  register ssize_t
856    i;
857
858  SegmentInfo
859    bounds;
860
861  size_t
862    length,
863    page,
864    pixel,
865    text_size;
866
867  ssize_t
868    j;
869
870  time_t
871    timer;
872
873  unsigned char
874    *pixels;
875
876  /*
877    Open output image file.
878  */
879  assert(image_info != (const ImageInfo *) NULL);
880  assert(image_info->signature == MagickCoreSignature);
881  assert(image != (Image *) NULL);
882  assert(image->signature == MagickCoreSignature);
883  if (image->debug != MagickFalse)
884    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
885  assert(exception != (ExceptionInfo *) NULL);
886  assert(exception->signature == MagickCoreSignature);
887  status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
888  if (status == MagickFalse)
889    return(MagickFalse);
890  compression=image->compression;
891  if (image_info->compression != UndefinedCompression)
892    compression=image_info->compression;
893  switch (compression)
894  {
895    case FaxCompression:
896    case Group4Compression:
897    {
898      if ((SetImageMonochrome(image,exception) == MagickFalse) ||
899          (image->alpha_trait != UndefinedPixelTrait))
900        compression=RLECompression;
901      break;
902    }
903#if !defined(MAGICKCORE_JPEG_DELEGATE)
904    case JPEGCompression:
905    {
906      compression=RLECompression;
907      (void) ThrowMagickException(exception,GetMagickModule(),
908        MissingDelegateError,"DelegateLibrarySupportNotBuiltIn","`%s' (JPEG)",
909        image->filename);
910      break;
911    }
912#endif
913#if !defined(MAGICKCORE_ZLIB_DELEGATE)
914    case ZipCompression:
915    {
916      compression=RLECompression;
917      (void) ThrowMagickException(exception,GetMagickModule(),
918        MissingDelegateError,"DelegateLibrarySupportNotBuiltIn","`%s' (ZLIB)",
919        image->filename);
920      break;
921    }
922#endif
923    default:
924      break;
925  }
926  (void) ResetMagickMemory(&bounds,0,sizeof(bounds));
927  page=0;
928  scene=0;
929  do
930  {
931    /*
932      Scale relative to dots-per-inch.
933    */
934    delta.x=DefaultResolution;
935    delta.y=DefaultResolution;
936    resolution.x=image->resolution.x;
937    resolution.y=image->resolution.y;
938    if ((resolution.x == 0.0) || (resolution.y == 0.0))
939      {
940        flags=ParseGeometry(PSDensityGeometry,&geometry_info);
941        resolution.x=geometry_info.rho;
942        resolution.y=geometry_info.sigma;
943        if ((flags & SigmaValue) == 0)
944          resolution.y=resolution.x;
945      }
946    if (image_info->density != (char *) NULL)
947      {
948        flags=ParseGeometry(image_info->density,&geometry_info);
949        resolution.x=geometry_info.rho;
950        resolution.y=geometry_info.sigma;
951        if ((flags & SigmaValue) == 0)
952          resolution.y=resolution.x;
953      }
954    if (image->units == PixelsPerCentimeterResolution)
955      {
956        resolution.x=(size_t) (100.0*2.54*resolution.x+0.5)/100.0;
957        resolution.y=(size_t) (100.0*2.54*resolution.y+0.5)/100.0;
958      }
959    SetGeometry(image,&geometry);
960    (void) FormatLocaleString(page_geometry,MagickPathExtent,"%.20gx%.20g",
961      (double) image->columns,(double) image->rows);
962    if (image_info->page != (char *) NULL)
963      (void) CopyMagickString(page_geometry,image_info->page,MagickPathExtent);
964    else
965      if ((image->page.width != 0) && (image->page.height != 0))
966        (void) FormatLocaleString(page_geometry,MagickPathExtent,
967          "%.20gx%.20g%+.20g%+.20g",(double) image->page.width,(double)
968          image->page.height,(double) image->page.x,(double) image->page.y);
969      else
970        if ((image->gravity != UndefinedGravity) &&
971            (LocaleCompare(image_info->magick,"PS") == 0))
972          (void) CopyMagickString(page_geometry,PSPageGeometry,MagickPathExtent);
973    (void) ConcatenateMagickString(page_geometry,">",MagickPathExtent);
974    (void) ParseMetaGeometry(page_geometry,&geometry.x,&geometry.y,
975      &geometry.width,&geometry.height);
976    scale.x=(double) (geometry.width*delta.x)/resolution.x;
977    geometry.width=(size_t) floor(scale.x+0.5);
978    scale.y=(double) (geometry.height*delta.y)/resolution.y;
979    geometry.height=(size_t) floor(scale.y+0.5);
980    (void) ParseAbsoluteGeometry(page_geometry,&media_info);
981    (void) ParseGravityGeometry(image,page_geometry,&page_info,exception);
982    if (image->gravity != UndefinedGravity)
983      {
984        geometry.x=(-page_info.x);
985        geometry.y=(ssize_t) (media_info.height+page_info.y-image->rows);
986      }
987    pointsize=12.0;
988    if (image_info->pointsize != 0.0)
989      pointsize=image_info->pointsize;
990    text_size=0;
991    value=GetImageProperty(image,"label",exception);
992    if (value != (const char *) NULL)
993      text_size=(size_t) (MultilineCensus(value)*pointsize+12);
994    page++;
995    if (page == 1)
996      {
997        /*
998          Postscript header on the first page.
999        */
1000        if (LocaleCompare(image_info->magick,"PS3") == 0)
1001          (void) CopyMagickString(buffer,"%!PS-Adobe-3.0\n",MagickPathExtent);
1002        else
1003          (void) CopyMagickString(buffer,"%!PS-Adobe-3.0 EPSF-3.0\n",
1004            MagickPathExtent);
1005        (void) WriteBlobString(image,buffer);
1006        (void) FormatLocaleString(buffer,MagickPathExtent,
1007          "%%%%Creator: ImageMagick %s\n",MagickLibVersionText);
1008        (void) WriteBlobString(image,buffer);
1009        (void) FormatLocaleString(buffer,MagickPathExtent,"%%%%Title: %s\n",
1010          image->filename);
1011        (void) WriteBlobString(image,buffer);
1012        timer=time((time_t *) NULL);
1013        (void) FormatMagickTime(timer,MagickPathExtent,date);
1014        (void) FormatLocaleString(buffer,MagickPathExtent,
1015          "%%%%CreationDate: %s\n",date);
1016        (void) WriteBlobString(image,buffer);
1017        bounds.x1=(double) geometry.x;
1018        bounds.y1=(double) geometry.y;
1019        bounds.x2=(double) geometry.x+scale.x;
1020        bounds.y2=(double) geometry.y+scale.y+text_size;
1021        if ((image_info->adjoin != MagickFalse) &&
1022            (GetNextImageInList(image) != (Image *) NULL))
1023          {
1024            (void) WriteBlobString(image,"%%BoundingBox: (atend)\n");
1025            (void) WriteBlobString(image,"%%HiResBoundingBox: (atend)\n");
1026          }
1027        else
1028          {
1029            (void) FormatLocaleString(buffer,MagickPathExtent,
1030              "%%%%BoundingBox: %g %g %g %g\n",ceil(bounds.x1-0.5),
1031              ceil(bounds.y1-0.5),floor(bounds.x2+0.5),floor(bounds.y2+0.5));
1032            (void) WriteBlobString(image,buffer);
1033            (void) FormatLocaleString(buffer,MagickPathExtent,
1034              "%%%%HiResBoundingBox: %g %g %g %g\n",bounds.x1,
1035              bounds.y1,bounds.x2,bounds.y2);
1036            (void) WriteBlobString(image,buffer);
1037            if (image->colorspace == CMYKColorspace)
1038              (void) WriteBlobString(image,
1039                "%%DocumentProcessColors: Cyan Magenta Yellow Black\n");
1040            else
1041              if (SetImageGray(image,exception) != MagickFalse)
1042                (void) WriteBlobString(image,
1043                  "%%DocumentProcessColors: Black\n");
1044          }
1045        /*
1046          Font resources
1047        */
1048        value=GetImageProperty(image,"label",exception);
1049        if (value != (const char *) NULL)
1050          (void) WriteBlobString(image,
1051            "%%DocumentNeededResources: font Helvetica\n");
1052        (void) WriteBlobString(image,"%%LanguageLevel: 3\n");
1053        /*
1054          Pages, orientation and order.
1055        */
1056        if (LocaleCompare(image_info->magick,"PS3") != 0)
1057          (void) WriteBlobString(image,"%%Pages: 1\n");
1058        else
1059          {
1060            (void) WriteBlobString(image,"%%Orientation: Portrait\n");
1061            (void) WriteBlobString(image,"%%PageOrder: Ascend\n");
1062            if (image_info->adjoin == MagickFalse)
1063              (void) CopyMagickString(buffer,"%%Pages: 1\n",MagickPathExtent);
1064            else
1065              (void) FormatLocaleString(buffer,MagickPathExtent,
1066                "%%%%Pages: %.20g\n",(double) GetImageListLength(image));
1067            (void) WriteBlobString(image,buffer);
1068          }
1069        if (image->colorspace == CMYKColorspace)
1070          (void) WriteBlobString(image,
1071            "%%DocumentProcessColors: Cyan Magenta Yellow Black\n");
1072        (void) WriteBlobString(image,"%%EndComments\n");
1073        /*
1074          The static postscript procedures prolog.
1075        */
1076        (void)WriteBlobString(image,"%%BeginProlog\n");
1077        for (q=PostscriptProlog; *q; q++)
1078        {
1079          (void) WriteBlobString(image,*q);
1080          (void) WriteBlobByte(image,'\n');
1081        }
1082        /*
1083          One label line for each line in label string.
1084        */
1085        value=GetImageProperty(image,"label",exception);
1086        if (value != (const char *) NULL)
1087          {
1088              (void) WriteBlobString(image,"\n  %% Labels.\n  /Helvetica "
1089              " findfont pointsize scalefont setfont\n");
1090            for (i=(ssize_t) MultilineCensus(value)-1; i >= 0; i--)
1091            {
1092              (void) WriteBlobString(image,
1093                "  currentfile buffer readline pop token pop\n");
1094              (void) FormatLocaleString(buffer,MagickPathExtent,
1095                "  0 y %g add moveto show pop\n",i*pointsize+12);
1096              (void) WriteBlobString(image,buffer);
1097            }
1098          }
1099        /*
1100          The static postscript procedures epilog.
1101        */
1102        for (q=PostscriptEpilog; *q; q++)
1103        {
1104          (void) WriteBlobString(image,*q);
1105          (void) WriteBlobByte(image,'\n');
1106        }
1107        (void)WriteBlobString(image,"%%EndProlog\n");
1108      }
1109    (void) FormatLocaleString(buffer,MagickPathExtent,"%%%%Page: 1 %.20g\n",
1110      (double) page);
1111    (void) WriteBlobString(image,buffer);
1112    /*
1113      Page bounding box.
1114    */
1115    (void) FormatLocaleString(buffer,MagickPathExtent,
1116      "%%%%PageBoundingBox: %.20g %.20g %.20g %.20g\n",(double) geometry.x,
1117       (double) geometry.y,geometry.x+(double) geometry.width,geometry.y+
1118       (double) (geometry.height+text_size));
1119    (void) WriteBlobString(image,buffer);
1120    /*
1121      Page process colors if not RGB.
1122    */
1123    if (image->colorspace == CMYKColorspace)
1124      (void) WriteBlobString(image,
1125        "%%PageProcessColors: Cyan Magenta Yellow Black\n");
1126    else
1127      if (SetImageGray(image,exception) != MagickFalse)
1128        (void) WriteBlobString(image,"%%PageProcessColors: Black\n");
1129    /*
1130      Adjust document bounding box to bound page bounding box.
1131    */
1132    if ((double) geometry.x < bounds.x1)
1133      bounds.x1=(double) geometry.x;
1134    if ((double) geometry.y < bounds.y1)
1135      bounds.y1=(double) geometry.y;
1136    if ((double) (geometry.x+scale.x) > bounds.x2)
1137      bounds.x2=(double) geometry.x+scale.x;
1138    if ((double) (geometry.y+scale.y+text_size) > bounds.y2)
1139      bounds.y2=(double) geometry.y+scale.y+text_size;
1140    /*
1141      Page font resource if there's a label.
1142    */
1143    value=GetImageProperty(image,"label",exception);
1144    if (value != (const char *) NULL)
1145      (void) WriteBlobString(image,"%%PageResources: font Helvetica\n");
1146    /*
1147      PS clipping path from Photoshop clipping path.
1148    */
1149    if ((image->read_mask != MagickFalse) ||
1150        (LocaleNCompare("8BIM:",image->magick_filename,5) != 0))
1151      (void) WriteBlobString(image,"/ClipImage {} def\n");
1152    else
1153      {
1154        const char
1155          *value;
1156
1157        value=GetImageProperty(image,image->magick_filename,exception);
1158        if (value == (const char *) NULL)
1159          return(MagickFalse);
1160        (void) WriteBlobString(image,value);
1161        (void) WriteBlobByte(image,'\n');
1162      }
1163    /*
1164      Push a dictionary for our own def's if this an EPS.
1165    */
1166    if (LocaleCompare(image_info->magick,"PS3") != 0)
1167      (void) WriteBlobString(image,"userdict begin\n");
1168    /*
1169      Image mask.
1170    */
1171    if ((image->alpha_trait != UndefinedPixelTrait) &&
1172        (WritePS3MaskImage(image_info,image,compression,exception) == MagickFalse))
1173      {
1174        (void) CloseBlob(image);
1175        return(MagickFalse);
1176      }
1177    /*
1178      Remember position of BeginData comment so we can update it.
1179    */
1180    start=TellBlob(image);
1181    if (start < 0)
1182      ThrowWriterException(CorruptImageError,"ImproperImageHeader");
1183    (void) FormatLocaleString(buffer,MagickPathExtent,
1184      "%%%%BeginData:%13ld %s Bytes\n",0L,
1185      compression == NoCompression ? "ASCII" : "BINARY");
1186    (void) WriteBlobString(image,buffer);
1187    stop=TellBlob(image);
1188    if (stop < 0)
1189      ThrowWriterException(CorruptImageError,"ImproperImageHeader");
1190    (void) WriteBlobString(image,"DisplayImage\n");
1191    /*
1192      Translate, scale, and font point size.
1193    */
1194    (void) FormatLocaleString(buffer,MagickPathExtent,"%.20g %.20g\n%g %g\n%g\n",
1195      (double) geometry.x,(double) geometry.y,scale.x,scale.y,pointsize);
1196    (void) WriteBlobString(image,buffer);
1197    /*
1198      Output labels.
1199    */
1200    labels=(char **) NULL;
1201    value=GetImageProperty(image,"label",exception);
1202    if (value != (const char *) NULL)
1203      labels=StringToList(value);
1204    if (labels != (char **) NULL)
1205      {
1206        for (i=0; labels[i] != (char *) NULL; i++)
1207        {
1208          if (compression != NoCompression)
1209            {
1210              for (j=0; labels[i][j] != '\0'; j++)
1211                (void) WriteBlobByte(image,(unsigned char) labels[i][j]);
1212              (void) WriteBlobByte(image,'\n');
1213            }
1214          else
1215            {
1216              (void) WriteBlobString(image,"<~");
1217              Ascii85Initialize(image);
1218              for (j=0; labels[i][j] != '\0'; j++)
1219                Ascii85Encode(image,(unsigned char) labels[i][j]);
1220              Ascii85Flush(image);
1221            }
1222          labels[i]=DestroyString(labels[i]);
1223        }
1224        labels=(char **) RelinquishMagickMemory(labels);
1225      }
1226    /*
1227      Photoshop clipping path active?
1228    */
1229    if ((image->read_mask != MagickFalse) &&
1230        (LocaleNCompare("8BIM:",image->magick_filename,5) == 0))
1231        (void) WriteBlobString(image,"true\n");
1232      else
1233        (void) WriteBlobString(image,"false\n");
1234    /*
1235      Showpage for non-EPS.
1236    */
1237    (void) WriteBlobString(image, LocaleCompare(image_info->magick,"PS3") == 0 ?
1238      "true\n" : "false\n");
1239    /*
1240      Image columns, rows, and color space.
1241    */
1242    (void) FormatLocaleString(buffer,MagickPathExtent,"%.20g %.20g\n%s\n",
1243      (double) image->columns,(double) image->rows,image->colorspace ==
1244      CMYKColorspace ? PS3_CMYKColorspace : PS3_RGBColorspace);
1245    (void) WriteBlobString(image,buffer);
1246    /*
1247      Masked image?
1248    */
1249    (void) WriteBlobString(image,image->alpha_trait != UndefinedPixelTrait ?
1250      "true\n" : "false\n");
1251    /*
1252      Render with imagemask operator?
1253    */
1254    option=GetImageOption(image_info,"ps3:imagemask");
1255    (void) WriteBlobString(image,((option != (const char *) NULL) &&
1256      (SetImageMonochrome(image,exception) != MagickFalse)) ?
1257      "true\n" : "false\n");
1258    /*
1259      Output pixel data.
1260    */
1261    pixels=(unsigned char *) NULL;
1262    length=0;
1263    if ((image_info->type != TrueColorType) &&
1264        (image_info->type != TrueColorAlphaType) &&
1265        (image_info->type != ColorSeparationType) &&
1266        (image_info->type != ColorSeparationAlphaType) &&
1267        (image->colorspace != CMYKColorspace) &&
1268        ((SetImageGray(image,exception) != MagickFalse) ||
1269         (SetImageMonochrome(image,exception) != MagickFalse)))
1270      {
1271        /*
1272          Gray images.
1273        */
1274        (void) WriteBlobString(image,PS3_PseudoClass"\n");
1275        switch (compression)
1276        {
1277          case NoCompression:
1278          default:
1279          {
1280            (void) WriteBlobString(image,PS3_NoCompression"\n");
1281            break;
1282          }
1283          case FaxCompression:
1284          case Group4Compression:
1285          {
1286            (void) WriteBlobString(image,PS3_FaxCompression"\n");
1287            break;
1288          }
1289          case JPEGCompression:
1290          {
1291            (void) WriteBlobString(image,PS3_JPEGCompression"\n");
1292            break;
1293          }
1294          case LZWCompression:
1295          {
1296            (void) WriteBlobString(image,PS3_LZWCompression"\n");
1297            break;
1298          }
1299          case RLECompression:
1300          {
1301            (void) WriteBlobString(image,PS3_RLECompression"\n");
1302            break;
1303          }
1304          case ZipCompression:
1305          {
1306            (void) WriteBlobString(image,PS3_ZipCompression"\n");
1307            break;
1308          }
1309        }
1310        /*
1311          Number of colors -- 0 for single component non-color mapped data.
1312        */
1313        (void) WriteBlobString(image,"0\n");
1314        /*
1315          1 bit or 8 bit components?
1316        */
1317        (void) FormatLocaleString(buffer,MagickPathExtent,"%d\n",
1318          SetImageMonochrome(image,exception) != MagickFalse ? 1 : 8);
1319        (void) WriteBlobString(image,buffer);
1320        /*
1321          Image data.
1322        */
1323        if (compression == JPEGCompression)
1324          status=InjectImageBlob(image_info,image,image,"jpeg",exception);
1325        else
1326          if ((compression == FaxCompression) ||
1327              (compression == Group4Compression))
1328            {
1329              if (LocaleCompare(CCITTParam,"0") == 0)
1330                status=HuffmanEncodeImage(image_info,image,image,exception);
1331              else
1332                status=Huffman2DEncodeImage(image_info,image,image,exception);
1333            }
1334          else
1335            {
1336              status=SerializeImageChannel(image_info,image,&pixel_info,&length,
1337                exception);
1338              if (status == MagickFalse)
1339                {
1340                  (void) CloseBlob(image);
1341                  return(MagickFalse);
1342                }
1343              pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
1344              switch (compression)
1345              {
1346                case NoCompression:
1347                default:
1348                {
1349                  Ascii85Initialize(image);
1350                  for (i=0; i < (ssize_t) length; i++)
1351                    Ascii85Encode(image,pixels[i]);
1352                  Ascii85Flush(image);
1353                  status=MagickTrue;
1354                  break;
1355                }
1356                case LZWCompression:
1357                {
1358                  status=LZWEncodeImage(image,length,pixels,exception);
1359                  break;
1360                }
1361                case RLECompression:
1362                {
1363                  status=PackbitsEncodeImage(image,length,pixels,exception);
1364                  break;
1365                }
1366                case ZipCompression:
1367                {
1368                  status=ZLIBEncodeImage(image,length,pixels,exception);
1369                  break;
1370                }
1371              }
1372              pixel_info=RelinquishVirtualMemory(pixel_info);
1373            }
1374      }
1375    else
1376      if ((image->storage_class == DirectClass) || (image->colors > 256) ||
1377          (compression == JPEGCompression))
1378        {
1379          /*
1380            Truecolor image.
1381          */
1382          (void) WriteBlobString(image,PS3_DirectClass"\n");
1383          switch (compression)
1384          {
1385            case NoCompression:
1386            default:
1387            {
1388              (void) WriteBlobString(image,PS3_NoCompression"\n");
1389              break;
1390            }
1391            case RLECompression:
1392            {
1393              (void) WriteBlobString(image,PS3_RLECompression"\n");
1394              break;
1395            }
1396            case JPEGCompression:
1397            {
1398              (void) WriteBlobString(image,PS3_JPEGCompression"\n");
1399              break;
1400            }
1401            case LZWCompression:
1402            {
1403              (void) WriteBlobString(image,PS3_LZWCompression"\n");
1404              break;
1405            }
1406            case ZipCompression:
1407            {
1408              (void) WriteBlobString(image,PS3_ZipCompression"\n");
1409              break;
1410            }
1411          }
1412          /*
1413            Image data.
1414          */
1415          if (compression == JPEGCompression)
1416            status=InjectImageBlob(image_info,image,image,"jpeg",exception);
1417          else
1418            {
1419              /*
1420                Stream based compressions.
1421              */
1422              status=SerializeImage(image_info,image,&pixel_info,&length,
1423                exception);
1424              if (status == MagickFalse)
1425                {
1426                  (void) CloseBlob(image);
1427                  return(MagickFalse);
1428                }
1429              pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
1430              switch (compression)
1431              {
1432                case NoCompression:
1433                default:
1434                {
1435                  Ascii85Initialize(image);
1436                  for (i=0; i < (ssize_t) length; i++)
1437                    Ascii85Encode(image,pixels[i]);
1438                  Ascii85Flush(image);
1439                  status=MagickTrue;
1440                  break;
1441                }
1442                case RLECompression:
1443                {
1444                  status=PackbitsEncodeImage(image,length,pixels,exception);
1445                  break;
1446                }
1447                case LZWCompression:
1448                {
1449                  status=LZWEncodeImage(image,length,pixels,exception);
1450                  break;
1451                }
1452                case ZipCompression:
1453                {
1454                  status=ZLIBEncodeImage(image,length,pixels,exception);
1455                  break;
1456                }
1457              }
1458              pixel_info=RelinquishVirtualMemory(pixel_info);
1459            }
1460          }
1461        else
1462          {
1463            /*
1464              Colormapped images.
1465            */
1466            (void) WriteBlobString(image,PS3_PseudoClass"\n");
1467            switch (compression)
1468            {
1469              case NoCompression:
1470              default:
1471              {
1472                (void) WriteBlobString(image,PS3_NoCompression"\n");
1473                break;
1474              }
1475              case RLECompression:
1476              {
1477                (void) WriteBlobString(image,PS3_RLECompression"\n");
1478                break;
1479              }
1480              case LZWCompression:
1481              {
1482                (void) WriteBlobString(image,PS3_LZWCompression"\n");
1483                break;
1484              }
1485              case ZipCompression:
1486              {
1487                (void) WriteBlobString(image,PS3_ZipCompression"\n");
1488                break;
1489              }
1490            }
1491            /*
1492              Number of colors in color map.
1493            */
1494            (void) FormatLocaleString(buffer,MagickPathExtent,"%.20g\n",
1495              (double) image->colors);
1496            (void) WriteBlobString(image,buffer);
1497            /*
1498              Color map - uncompressed.
1499            */
1500            if ((compression != NoCompression) &&
1501                (compression != UndefinedCompression))
1502              {
1503                for (i=0; i < (ssize_t) image->colors; i++)
1504                {
1505                  pixel=ScaleQuantumToChar(image->colormap[i].red);
1506                  (void) WriteBlobByte(image,(unsigned char) pixel);
1507                  pixel=ScaleQuantumToChar(image->colormap[i].green);
1508                  (void) WriteBlobByte(image,(unsigned char) pixel);
1509                  pixel=ScaleQuantumToChar(image->colormap[i].blue);
1510                  (void) WriteBlobByte(image,(unsigned char) pixel);
1511                }
1512              }
1513            else
1514              {
1515                Ascii85Initialize(image);
1516                for (i=0; i < (ssize_t) image->colors; i++)
1517                {
1518                  pixel=ScaleQuantumToChar(image->colormap[i].red);
1519                  Ascii85Encode(image,(unsigned char) pixel);
1520                  pixel=ScaleQuantumToChar(image->colormap[i].green);
1521                  Ascii85Encode(image,(unsigned char) pixel);
1522                  pixel=ScaleQuantumToChar(image->colormap[i].blue);
1523                  Ascii85Encode(image,(unsigned char) pixel);
1524                }
1525                Ascii85Flush(image);
1526              }
1527            status=SerializeImageIndexes(image_info,image,&pixel_info,&length,
1528              exception);
1529            if (status == MagickFalse)
1530              {
1531                (void) CloseBlob(image);
1532                return(MagickFalse);
1533              }
1534            pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
1535            switch (compression)
1536            {
1537              case NoCompression:
1538              default:
1539              {
1540                Ascii85Initialize(image);
1541                for (i=0; i < (ssize_t) length; i++)
1542                  Ascii85Encode(image,pixels[i]);
1543                Ascii85Flush(image);
1544                status=MagickTrue;
1545                break;
1546              }
1547              case RLECompression:
1548              {
1549                status=PackbitsEncodeImage(image,length,pixels,exception);
1550                break;
1551              }
1552              case LZWCompression:
1553              {
1554                status=LZWEncodeImage(image,length,pixels,exception);
1555                break;
1556              }
1557              case ZipCompression:
1558              {
1559                status=ZLIBEncodeImage(image,length,pixels,exception);
1560                break;
1561              }
1562            }
1563            pixel_info=RelinquishVirtualMemory(pixel_info);
1564          }
1565    (void) WriteBlobByte(image,'\n');
1566    if (status == MagickFalse)
1567      {
1568        (void) CloseBlob(image);
1569        return(MagickFalse);
1570      }
1571    /*
1572      Update BeginData now that we know the data size.
1573    */
1574    length=(size_t) (TellBlob(image)-stop);
1575    stop=TellBlob(image);
1576    if (stop < 0)
1577      ThrowWriterException(CorruptImageError,"ImproperImageHeader");
1578    offset=SeekBlob(image,start,SEEK_SET);
1579    if (offset < 0)
1580      ThrowWriterException(CorruptImageError,"ImproperImageHeader");
1581    (void) FormatLocaleString(buffer,MagickPathExtent,
1582      "%%%%BeginData:%13ld %s Bytes\n",(long) length,
1583      compression == NoCompression ? "ASCII" : "BINARY");
1584    (void) WriteBlobString(image,buffer);
1585    offset=SeekBlob(image,stop,SEEK_SET);
1586    (void) WriteBlobString(image,"%%EndData\n");
1587    /*
1588      End private dictionary if this an EPS.
1589    */
1590    if (LocaleCompare(image_info->magick,"PS3") != 0)
1591      (void) WriteBlobString(image,"end\n");
1592    (void) WriteBlobString(image,"%%PageTrailer\n");
1593    if (GetNextImageInList(image) == (Image *) NULL)
1594      break;
1595    image=SyncNextImageInList(image);
1596    status=SetImageProgress(image,SaveImagesTag,scene++,
1597      GetImageListLength(image));
1598    if (status == MagickFalse)
1599      break;
1600  } while (image_info->adjoin != MagickFalse);
1601  (void) WriteBlobString(image,"%%Trailer\n");
1602  if (page > 1)
1603    {
1604      (void) FormatLocaleString(buffer,MagickPathExtent,
1605        "%%%%BoundingBox: %g %g %g %g\n",ceil(bounds.x1-0.5),
1606        ceil(bounds.y1-0.5),floor(bounds.x2+0.5),floor(bounds.y2+0.5));
1607      (void) WriteBlobString(image,buffer);
1608      (void) FormatLocaleString(buffer,MagickPathExtent,
1609        "%%%%HiResBoundingBox: %g %g %g %g\n",bounds.x1,bounds.y1,bounds.x2,
1610        bounds.y2);
1611      (void) WriteBlobString(image,buffer);
1612    }
1613  (void) WriteBlobString(image,"%%EOF\n");
1614  (void) CloseBlob(image);
1615  return(MagickTrue);
1616}
1617