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