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