]> granicus.if.org Git - imagemagick/blob - coders/ps.c
(no commit message)
[imagemagick] / coders / ps.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                               PPPP   SSSSS                                  %
7 %                               P   P  SS                                     %
8 %                               PPPP    SSS                                   %
9 %                               P         SS                                  %
10 %                               P      SSSSS                                  %
11 %                                                                             %
12 %                                                                             %
13 %                         Read/Write Postscript Format                        %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                John Cristy                                  %
17 %                                 July 1992                                   %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2013 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    http://www.imagemagick.org/script/license.php                            %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38 \f
39 /*
40   Include declarations.
41 */
42 #include "MagickCore/studio.h"
43 #include "MagickCore/attribute.h"
44 #include "MagickCore/blob.h"
45 #include "MagickCore/blob-private.h"
46 #include "MagickCore/cache.h"
47 #include "MagickCore/color.h"
48 #include "MagickCore/color-private.h"
49 #include "MagickCore/colorspace.h"
50 #include "MagickCore/colorspace-private.h"
51 #include "MagickCore/constitute.h"
52 #include "MagickCore/delegate.h"
53 #include "MagickCore/delegate-private.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/profile.h"
67 #include "MagickCore/resource_.h"
68 #include "MagickCore/pixel-accessor.h"
69 #include "MagickCore/property.h"
70 #include "MagickCore/quantum-private.h"
71 #include "MagickCore/static.h"
72 #include "MagickCore/string_.h"
73 #include "MagickCore/module.h"
74 #include "MagickCore/token.h"
75 #include "MagickCore/transform.h"
76 #include "MagickCore/utility.h"
77 \f
78 /*
79   Forward declarations.
80 */
81 static MagickBooleanType
82   WritePSImage(const ImageInfo *,Image *,ExceptionInfo *);
83 \f
84 /*
85 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
86 %                                                                             %
87 %                                                                             %
88 %                                                                             %
89 %   I s P S                                                                   %
90 %                                                                             %
91 %                                                                             %
92 %                                                                             %
93 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
94 %
95 %  IsPS() returns MagickTrue if the image format type, identified by the
96 %  magick string, is PS.
97 %
98 %  The format of the IsPS method is:
99 %
100 %      MagickBooleanType IsPS(const unsigned char *magick,const size_t length)
101 %
102 %  A description of each parameter follows:
103 %
104 %    o magick: compare image format pattern against these bytes.
105 %
106 %    o length: Specifies the length of the magick string.
107 %
108 */
109 static MagickBooleanType IsPS(const unsigned char *magick,const size_t length)
110 {
111   if (length < 4)
112     return(MagickFalse);
113   if (memcmp(magick,"%!",2) == 0)
114     return(MagickTrue);
115   if (memcmp(magick,"\004%!",3) == 0)
116     return(MagickTrue);
117   return(MagickFalse);
118 }
119 \f
120 /*
121 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
122 %                                                                             %
123 %                                                                             %
124 %                                                                             %
125 %   R e a d P S I m a g e                                                     %
126 %                                                                             %
127 %                                                                             %
128 %                                                                             %
129 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
130 %
131 %  ReadPSImage() reads a Postscript image file and returns it.  It allocates
132 %  the memory necessary for the new Image structure and returns a pointer
133 %  to the new image.
134 %
135 %  The format of the ReadPSImage method is:
136 %
137 %      Image *ReadPSImage(const ImageInfo *image_info,ExceptionInfo *exception)
138 %
139 %  A description of each parameter follows:
140 %
141 %    o image_info: the image info.
142 %
143 %    o exception: return any errors or warnings in this structure.
144 %
145 */
146
147 static MagickBooleanType IsPostscriptRendered(const char *path)
148 {
149   MagickBooleanType
150     status;
151
152   struct stat
153     attributes;
154
155   if ((path == (const char *) NULL) || (*path == '\0'))
156     return(MagickFalse);
157   status=GetPathAttributes(path,&attributes);
158   if ((status != MagickFalse) && S_ISREG(attributes.st_mode) &&
159       (attributes.st_size > 0))
160     return(MagickTrue);
161   return(MagickFalse);
162 }
163
164 static inline int ProfileInteger(Image *image,short int *hex_digits)
165 {
166   int
167     c,
168     l,
169     value;
170
171   register ssize_t
172     i;
173
174   l=0;
175   value=0;
176   for (i=0; i < 2; )
177   {
178     c=ReadBlobByte(image);
179     if ((c == EOF) || ((c == '%') && (l == '%')))
180       {
181         value=(-1);
182         break;
183       }
184     l=c;
185     c&=0xff;
186     if (isxdigit(c) == MagickFalse)
187       continue;
188     value=(int) ((size_t) value << 4)+hex_digits[c];
189     i++;
190   }
191   return(value);
192 }
193
194 static Image *ReadPSImage(const ImageInfo *image_info,ExceptionInfo *exception)
195 {
196 #define BoundingBox  "BoundingBox:"
197 #define BeginDocument  "BeginDocument:"
198 #define BeginXMPPacket  "<?xpacket begin="
199 #define EndXMPPacket  "<?xpacket end="
200 #define ICCProfile "BeginICCProfile:"
201 #define CMYKCustomColor  "CMYKCustomColor:"
202 #define CMYKProcessColor  "CMYKProcessColor:"
203 #define DocumentMedia  "DocumentMedia:"
204 #define DocumentCustomColors  "DocumentCustomColors:"
205 #define DocumentProcessColors  "DocumentProcessColors:"
206 #define EndDocument  "EndDocument:"
207 #define HiResBoundingBox  "HiResBoundingBox:"
208 #define ImageData  "ImageData:"
209 #define PageBoundingBox  "PageBoundingBox:"
210 #define LanguageLevel  "LanguageLevel:"
211 #define PageMedia  "PageMedia:"
212 #define Pages  "Pages:"
213 #define PhotoshopProfile  "BeginPhotoshop:"
214 #define PostscriptLevel  "!PS-"
215 #define RenderPostscriptText  "  Rendering Postscript...  "
216 #define SpotColor  "+ "
217
218   char
219     command[MaxTextExtent],
220     density[MaxTextExtent],
221     filename[MaxTextExtent],
222     geometry[MaxTextExtent],
223     input_filename[MaxTextExtent],
224     options[MaxTextExtent],
225     postscript_filename[MaxTextExtent];
226
227   const char
228     *option;
229
230   const DelegateInfo
231     *delegate_info;
232
233   GeometryInfo
234     geometry_info;
235
236   Image
237     *image,
238     *next,
239     *postscript_image;
240
241   ImageInfo
242     *read_info;
243
244   int
245     c,
246     file;
247
248   MagickBooleanType
249     cmyk,
250     skip,
251     status;
252
253   MagickStatusType
254     flags;
255
256   PointInfo
257     delta,
258     resolution;
259
260   RectangleInfo
261     page;
262
263   register char
264     *p;
265
266   register ssize_t
267     i;
268
269   SegmentInfo
270     bounds,
271     hires_bounds;
272
273   short int
274     hex_digits[256];
275
276   size_t
277     length,
278     priority;
279
280   ssize_t
281     count;
282
283   StringInfo
284     *profile;
285
286   unsigned long
287     columns,
288     extent,
289     language_level,
290     pages,
291     rows,
292     scene,
293     spotcolor;
294
295   /*
296     Open image file.
297   */
298   assert(image_info != (const ImageInfo *) NULL);
299   assert(image_info->signature == MagickSignature);
300   if (image_info->debug != MagickFalse)
301     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
302       image_info->filename);
303   assert(exception != (ExceptionInfo *) NULL);
304   assert(exception->signature == MagickSignature);
305   image=AcquireImage(image_info,exception);
306   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
307   if (status == MagickFalse)
308     {
309       image=DestroyImageList(image);
310       return((Image *) NULL);
311     }
312   status=AcquireUniqueSymbolicLink(image_info->filename,input_filename);
313   if (status == MagickFalse)
314     {
315       ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
316         image_info->filename);
317       image=DestroyImageList(image);
318       return((Image *) NULL);
319     }
320   /*
321     Initialize hex values.
322   */
323   (void) ResetMagickMemory(hex_digits,0,sizeof(hex_digits));
324   hex_digits[(int) '0']=0;
325   hex_digits[(int) '1']=1;
326   hex_digits[(int) '2']=2;
327   hex_digits[(int) '3']=3;
328   hex_digits[(int) '4']=4;
329   hex_digits[(int) '5']=5;
330   hex_digits[(int) '6']=6;
331   hex_digits[(int) '7']=7;
332   hex_digits[(int) '8']=8;
333   hex_digits[(int) '9']=9;
334   hex_digits[(int) 'a']=10;
335   hex_digits[(int) 'b']=11;
336   hex_digits[(int) 'c']=12;
337   hex_digits[(int) 'd']=13;
338   hex_digits[(int) 'e']=14;
339   hex_digits[(int) 'f']=15;
340   hex_digits[(int) 'A']=10;
341   hex_digits[(int) 'B']=11;
342   hex_digits[(int) 'C']=12;
343   hex_digits[(int) 'D']=13;
344   hex_digits[(int) 'E']=14;
345   hex_digits[(int) 'F']=15;
346   /*
347     Set the page density.
348   */
349   delta.x=DefaultResolution;
350   delta.y=DefaultResolution;
351   if ((image->resolution.x == 0.0) || (image->resolution.y == 0.0))
352     {
353       flags=ParseGeometry(PSDensityGeometry,&geometry_info);
354       image->resolution.x=geometry_info.rho;
355       image->resolution.y=geometry_info.sigma;
356       if ((flags & SigmaValue) == 0)
357         image->resolution.y=image->resolution.x;
358     }
359   if (image_info->density != (char *) NULL)
360     {
361       flags=ParseGeometry(image_info->density,&geometry_info);
362       image->resolution.x=geometry_info.rho;
363       image->resolution.y=geometry_info.sigma;
364       if ((flags & SigmaValue) == 0)
365         image->resolution.y=image->resolution.x;
366     }
367   (void) ParseAbsoluteGeometry(PSPageGeometry,&page);
368   if (image_info->page != (char *) NULL)
369     (void) ParseAbsoluteGeometry(image_info->page,&page);
370   resolution=image->resolution;
371   page.width=(size_t) ceil((double) (page.width*resolution.x/delta.x)-0.5);
372   page.height=(size_t) ceil((double) (page.height*resolution.y/delta.y)-0.5);
373   /*
374     Determine page geometry from the Postscript bounding box.
375   */
376   (void) ResetMagickMemory(&bounds,0,sizeof(bounds));
377   (void) ResetMagickMemory(command,0,sizeof(command));
378   cmyk=image_info->colorspace == CMYKColorspace ? MagickTrue : MagickFalse;
379   (void) ResetMagickMemory(&hires_bounds,0,sizeof(hires_bounds));
380   priority=0;
381   rows=0;
382   extent=0;
383   spotcolor=0;
384   language_level=1;
385   pages=(~0UL);
386   skip=MagickFalse;
387   p=command;
388   for (c=ReadBlobByte(image); c != EOF; c=ReadBlobByte(image))
389   {
390     /*
391       Note document structuring comments.
392     */
393     *p++=(char) c;
394     if ((strchr("\n\r%",c) == (char *) NULL) &&
395         ((size_t) (p-command) < (MaxTextExtent-1)))
396       continue;
397     *p='\0';
398     p=command;
399     /*
400       Skip %%BeginDocument thru %%EndDocument.
401     */
402     if (LocaleNCompare(BeginDocument,command,strlen(BeginDocument)) == 0)
403       skip=MagickTrue;
404     if (LocaleNCompare(EndDocument,command,strlen(EndDocument)) == 0)
405       skip=MagickFalse;
406     if (skip != MagickFalse)
407       continue;
408     if (LocaleNCompare(PostscriptLevel,command,strlen(PostscriptLevel)) == 0)
409       {
410         (void) SetImageProperty(image,"ps:Level",command+4,exception);
411         if (GlobExpression(command,"*EPSF-*",MagickTrue) != MagickFalse)
412           pages=1;
413       }
414     if (LocaleNCompare(LanguageLevel,command,strlen(LanguageLevel)) == 0)
415       (void) sscanf(command,LanguageLevel " %lu",&language_level);
416     if (LocaleNCompare(Pages,command,strlen(Pages)) == 0)
417       (void) sscanf(command,Pages " %lu",&pages);
418     if (LocaleNCompare(ImageData,command,strlen(ImageData)) == 0)
419       (void) sscanf(command,ImageData " %lu %lu",&columns,&rows);
420     if (LocaleNCompare(ICCProfile,command,strlen(ICCProfile)) == 0)
421       {
422         unsigned char
423           *datum;
424
425         /*
426           Read ICC profile.
427         */
428         profile=AcquireStringInfo(65536);
429         for (i=0; (c=ProfileInteger(image,hex_digits)) != EOF; i++)
430         {
431           SetStringInfoLength(profile,(size_t) i+1);
432           datum=GetStringInfoDatum(profile);
433           datum[i]=(unsigned char) c;
434         }
435         (void) SetImageProfile(image,"icc",profile,exception);
436         profile=DestroyStringInfo(profile);
437         continue;
438       }
439     if (LocaleNCompare(PhotoshopProfile,command,strlen(PhotoshopProfile)) == 0)
440       {
441         unsigned char
442           *p;
443
444         /*
445           Read Photoshop profile.
446         */
447         count=(ssize_t) sscanf(command,PhotoshopProfile " %lu",&extent);
448         if (count != 1)
449           continue;
450         length=extent;
451         profile=BlobToStringInfo((const void *) NULL,length);
452         p=GetStringInfoDatum(profile);
453         for (i=0; i < (ssize_t) length; i++)
454           *p++=(unsigned char) ProfileInteger(image,hex_digits);
455         (void) SetImageProfile(image,"8bim",profile,exception);
456         profile=DestroyStringInfo(profile);
457         continue;
458       }
459     if (LocaleNCompare(BeginXMPPacket,command,strlen(BeginXMPPacket)) == 0)
460       {
461         register size_t
462           i;
463
464         /*
465           Read XMP profile.
466         */
467         p=command;
468         profile=StringToStringInfo(command);
469         for (i=GetStringInfoLength(profile)-1; c != EOF; i++)
470         {
471           SetStringInfoLength(profile,i+1);
472           c=ReadBlobByte(image);
473           GetStringInfoDatum(profile)[i]=(unsigned char) c;
474           *p++=(char) c;
475           if ((strchr("\n\r%",c) == (char *) NULL) &&
476               ((size_t) (p-command) < (MaxTextExtent-1)))
477             continue;
478           *p='\0';
479           p=command;
480           if (LocaleNCompare(EndXMPPacket,command,strlen(EndXMPPacket)) == 0)
481             break;
482         }
483         SetStringInfoLength(profile,i);
484         (void) SetImageProfile(image,"xmp",profile,exception);
485         profile=DestroyStringInfo(profile);
486         continue;
487       }
488     /*
489       Is this a CMYK document?
490     */
491     length=strlen(DocumentProcessColors);
492     if (LocaleNCompare(DocumentProcessColors,command,length) == 0)
493       {
494         if ((GlobExpression(command,"*Cyan*",MagickTrue) != MagickFalse) ||
495             (GlobExpression(command,"*Magenta*",MagickTrue) != MagickFalse) ||
496             (GlobExpression(command,"*Yellow*",MagickTrue) != MagickFalse))
497           cmyk=MagickTrue;
498       }
499     if (LocaleNCompare(CMYKCustomColor,command,strlen(CMYKCustomColor)) == 0)
500       cmyk=MagickTrue;
501     if (LocaleNCompare(CMYKProcessColor,command,strlen(CMYKProcessColor)) == 0)
502       cmyk=MagickTrue;
503     length=strlen(DocumentCustomColors);
504     if ((LocaleNCompare(DocumentCustomColors,command,length) == 0) ||
505         (LocaleNCompare(CMYKCustomColor,command,strlen(CMYKCustomColor)) == 0) ||
506         (LocaleNCompare(SpotColor,command,strlen(SpotColor)) == 0))
507       {
508         char
509           property[MaxTextExtent],
510           *value;
511
512         register char
513           *p;
514
515         /*
516           Note spot names.
517         */
518         (void) FormatLocaleString(property,MaxTextExtent,"ps:SpotColor-%.20g",
519           (double) (spotcolor++));
520         for (p=command; *p != '\0'; p++)
521           if (isspace((int) (unsigned char) *p) != 0)
522             break;
523         value=AcquireString(p);
524         (void) SubstituteString(&value,"(","");
525         (void) SubstituteString(&value,")","");
526         (void) StripString(value);
527         (void) SetImageProperty(image,property,value,exception);
528         value=DestroyString(value);
529         continue;
530       }
531     if (image_info->page != (char *) NULL)
532       continue;
533     /*
534       Note region defined by bounding box.
535     */
536     count=0;
537     i=0;
538     if (LocaleNCompare(BoundingBox,command,strlen(BoundingBox)) == 0)
539       {
540         count=(ssize_t) sscanf(command,BoundingBox " %lf %lf %lf %lf",
541           &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
542         i=2;
543       }
544     if (LocaleNCompare(DocumentMedia,command,strlen(DocumentMedia)) == 0)
545       {
546         count=(ssize_t) sscanf(command,DocumentMedia " %lf %lf %lf %lf",
547           &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
548         i=1;
549       }
550     if (LocaleNCompare(HiResBoundingBox,command,strlen(HiResBoundingBox)) == 0)
551       {
552         count=(ssize_t) sscanf(command,HiResBoundingBox " %lf %lf %lf %lf",
553           &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
554         i=3;
555       }
556     if (LocaleNCompare(PageBoundingBox,command,strlen(PageBoundingBox)) == 0)
557       {
558         count=(ssize_t) sscanf(command,PageBoundingBox " %lf %lf %lf %lf",
559           &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
560         i=1;
561       }
562     if (LocaleNCompare(PageMedia,command,strlen(PageMedia)) == 0)
563       {
564         count=(ssize_t) sscanf(command,PageMedia " %lf %lf %lf %lf",
565           &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
566         i=1;
567       }
568     if ((count != 4) || (i < (ssize_t) priority))
569       continue;
570     if ((fabs(bounds.x2-bounds.x1) <= fabs(hires_bounds.x2-hires_bounds.x1)) ||
571         (fabs(bounds.y2-bounds.y1) <= fabs(hires_bounds.y2-hires_bounds.y1)))
572       continue;
573     hires_bounds=bounds;
574     priority=i;
575   }
576   if ((fabs(hires_bounds.x2-hires_bounds.x1) >= MagickEpsilon) && 
577       (fabs(hires_bounds.y2-hires_bounds.y1) >= MagickEpsilon))
578     {
579       /*
580         Set Postscript render geometry.
581       */
582       (void) FormatLocaleString(geometry,MaxTextExtent,"%gx%g%+.15g%+.15g",
583         hires_bounds.x2-hires_bounds.x1,hires_bounds.y2-hires_bounds.y1,
584         hires_bounds.x1,hires_bounds.y1);
585       (void) SetImageProperty(image,"ps:HiResBoundingBox",geometry,exception);
586       page.width=(size_t) ceil((double) ((hires_bounds.x2-hires_bounds.x1)*
587         resolution.x/delta.x)-0.5);
588       page.height=(size_t) ceil((double) ((hires_bounds.y2-hires_bounds.y1)*
589         resolution.y/delta.y)-0.5);
590     }
591   (void) CloseBlob(image);
592   if (IssRGBCompatibleColorspace(image_info->colorspace) != MagickFalse)
593     cmyk=MagickFalse;
594   /*
595     Create Ghostscript control file.
596   */
597   file=AcquireUniqueFileResource(postscript_filename);
598   if (file == -1)
599     {
600       ThrowFileException(exception,FileOpenError,"UnableToOpenFile",
601         image_info->filename);
602       image=DestroyImageList(image);
603       return((Image *) NULL);
604     }
605   (void) CopyMagickString(command,"/setpagedevice {pop} bind 1 index where {"
606     "dup wcheck {3 1 roll put} {pop def} ifelse} {def} ifelse\n"
607     "<</UseCIEColor true>>setpagedevice\n",MaxTextExtent);
608   count=write(file,command,(unsigned int) strlen(command));
609   if (image_info->page == (char *) NULL)
610     {
611       char
612         translate_geometry[MaxTextExtent];
613
614       (void) FormatLocaleString(translate_geometry,MaxTextExtent,
615         "%g %g translate\n",-bounds.x1,-bounds.y1);
616       count=write(file,translate_geometry,(unsigned int)
617         strlen(translate_geometry));
618     }
619   file=close(file)-1;
620   /*
621     Render Postscript with the Ghostscript delegate.
622   */
623   if (image_info->monochrome != MagickFalse)
624     delegate_info=GetDelegateInfo("ps:mono",(char *) NULL,exception);
625   else
626     if (cmyk != MagickFalse)
627       delegate_info=GetDelegateInfo("ps:cmyk",(char *) NULL,exception);
628     else
629       delegate_info=GetDelegateInfo("ps:alpha",(char *) NULL,exception);
630   if (delegate_info == (const DelegateInfo *) NULL)
631     {
632       (void) RelinquishUniqueFileResource(postscript_filename);
633       image=DestroyImageList(image);
634       return((Image *) NULL);
635     }
636   *options='\0';
637   (void) FormatLocaleString(density,MaxTextExtent,"%gx%g",resolution.x,
638     resolution.y);
639   (void) FormatLocaleString(options,MaxTextExtent,"-g%.20gx%.20g ",(double)
640     page.width,(double) page.height);
641   read_info=CloneImageInfo(image_info);
642   *read_info->magick='\0';
643   if (read_info->number_scenes != 0)
644     {
645       char
646         pages[MaxTextExtent];
647
648       (void) FormatLocaleString(pages,MaxTextExtent,"-dFirstPage=%.20g "
649         "-dLastPage=%.20g",(double) read_info->scene+1,(double)
650         (read_info->scene+read_info->number_scenes));
651       (void) ConcatenateMagickString(options,pages,MaxTextExtent);
652       read_info->number_scenes=0;
653       if (read_info->scenes != (char *) NULL)
654         *read_info->scenes='\0';
655     }
656   option=GetImageOption(image_info,"eps:use-cropbox");
657   if ((*image_info->magick == 'E') && ((option == (const char *) NULL) || 
658       (IsStringTrue(option) != MagickFalse)))
659     (void) ConcatenateMagickString(options,"-dEPSCrop ",MaxTextExtent);
660   (void) CopyMagickString(filename,read_info->filename,MaxTextExtent);
661   (void) AcquireUniqueFilename(filename);
662   (void) ConcatenateMagickString(filename,"%d",MaxTextExtent);
663   (void) FormatLocaleString(command,MaxTextExtent,
664     GetDelegateCommands(delegate_info),
665     read_info->antialias != MagickFalse ? 4 : 1,
666     read_info->antialias != MagickFalse ? 4 : 1,density,options,filename,
667     postscript_filename,input_filename);
668   status=InvokePostscriptDelegate(read_info->verbose,command,exception);
669   (void) InterpretImageFilename(image_info,image,filename,1,
670     read_info->filename,exception);
671   if ((status == MagickFalse) ||
672       (IsPostscriptRendered(read_info->filename) == MagickFalse))
673     {
674       (void) ConcatenateMagickString(command," -c showpage",MaxTextExtent);
675       status=InvokePostscriptDelegate(read_info->verbose,command,exception);
676     }
677   (void) RelinquishUniqueFileResource(postscript_filename);
678   (void) RelinquishUniqueFileResource(input_filename);
679   postscript_image=(Image *) NULL;
680   if (status == MagickFalse)
681     for (i=1; ; i++)
682     {
683       (void) InterpretImageFilename(image_info,image,filename,(int) i,
684         read_info->filename,exception);
685       if (IsPostscriptRendered(read_info->filename) == MagickFalse)
686         break;
687       (void) RelinquishUniqueFileResource(read_info->filename);
688     }
689   else
690     for (i=1; ; i++)
691     {
692       (void) InterpretImageFilename(image_info,image,filename,(int) i,
693         read_info->filename,exception);
694       if (IsPostscriptRendered(read_info->filename) == MagickFalse)
695         break;
696       read_info->blob=NULL;
697       read_info->length=0;
698       next=ReadImage(read_info,exception);
699       (void) RelinquishUniqueFileResource(read_info->filename);
700       if (next == (Image *) NULL)
701         break;
702       AppendImageToList(&postscript_image,next);
703     }
704   (void) RelinquishUniqueFileResource(read_info->filename);
705   read_info=DestroyImageInfo(read_info);
706   if (postscript_image == (Image *) NULL)
707     {
708       image=DestroyImageList(image);
709       ThrowFileException(exception,DelegateError,"PostscriptDelegateFailed",
710         image_info->filename);
711       return((Image *) NULL);
712     }
713   if (LocaleCompare(postscript_image->magick,"BMP") == 0)
714     {
715       Image
716         *cmyk_image;
717
718       cmyk_image=ConsolidateCMYKImages(postscript_image,exception);
719       if (cmyk_image != (Image *) NULL)
720         {
721           postscript_image=DestroyImageList(postscript_image);
722           postscript_image=cmyk_image;
723         }
724     }
725   if (image_info->number_scenes != 0)
726     {
727       Image
728         *clone_image;
729
730       register ssize_t
731         i;
732
733       /*
734         Add place holder images to meet the subimage specification requirement.
735       */
736       for (i=0; i < (ssize_t) image_info->scene; i++)
737       {
738         clone_image=CloneImage(postscript_image,1,1,MagickTrue,exception);
739         if (clone_image != (Image *) NULL)
740           PrependImageToList(&postscript_image,clone_image);
741       }
742     }
743   do
744   {
745     (void) CopyMagickString(postscript_image->filename,filename,MaxTextExtent);
746     if (columns != 0)
747       postscript_image->magick_columns=columns;
748     if (rows != 0)
749       postscript_image->magick_rows=rows;
750     postscript_image->page=page;
751     (void) CloneImageProfiles(postscript_image,image);
752     (void) CloneImageProperties(postscript_image,image);
753     next=SyncNextImageInList(postscript_image);
754     if (next != (Image *) NULL)
755       postscript_image=next;
756   } while (next != (Image *) NULL);
757   image=DestroyImageList(image);
758   scene=0;
759   for (next=GetFirstImageInList(postscript_image); next != (Image *) NULL; )
760   {
761     next->scene=scene++;
762     next=GetNextImageInList(next);
763   }
764   return(GetFirstImageInList(postscript_image));
765 }
766 \f
767 /*
768 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
769 %                                                                             %
770 %                                                                             %
771 %                                                                             %
772 %   R e g i s t e r P S I m a g e                                             %
773 %                                                                             %
774 %                                                                             %
775 %                                                                             %
776 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
777 %
778 %  RegisterPSImage() adds properties for the PS image format to
779 %  the list of supported formats.  The properties include the image format
780 %  tag, a method to read and/or write the format, whether the format
781 %  supports the saving of more than one frame to the same file or blob,
782 %  whether the format supports native in-memory I/O, and a brief
783 %  description of the format.
784 %
785 %  The format of the RegisterPSImage method is:
786 %
787 %      size_t RegisterPSImage(void)
788 %
789 */
790 ModuleExport size_t RegisterPSImage(void)
791 {
792   MagickInfo
793     *entry;
794
795   entry=SetMagickInfo("EPI");
796   entry->decoder=(DecodeImageHandler *) ReadPSImage;
797   entry->encoder=(EncodeImageHandler *) WritePSImage;
798   entry->magick=(IsImageFormatHandler *) IsPS;
799   entry->adjoin=MagickFalse;
800   entry->blob_support=MagickFalse;
801   entry->seekable_stream=MagickTrue;
802   entry->description=ConstantString(
803    "Encapsulated PostScript Interchange format");
804   entry->module=ConstantString("PS");
805   (void) RegisterMagickInfo(entry);
806   entry=SetMagickInfo("EPS");
807   entry->decoder=(DecodeImageHandler *) ReadPSImage;
808   entry->encoder=(EncodeImageHandler *) WritePSImage;
809   entry->magick=(IsImageFormatHandler *) IsPS;
810   entry->adjoin=MagickFalse;
811   entry->blob_support=MagickFalse;
812   entry->seekable_stream=MagickTrue;
813   entry->description=ConstantString("Encapsulated PostScript");
814   entry->module=ConstantString("PS");
815   (void) RegisterMagickInfo(entry);
816   entry=SetMagickInfo("EPSF");
817   entry->decoder=(DecodeImageHandler *) ReadPSImage;
818   entry->encoder=(EncodeImageHandler *) WritePSImage;
819   entry->magick=(IsImageFormatHandler *) IsPS;
820   entry->adjoin=MagickFalse;
821   entry->blob_support=MagickFalse;
822   entry->seekable_stream=MagickTrue;
823   entry->description=ConstantString("Encapsulated PostScript");
824   entry->module=ConstantString("PS");
825   (void) RegisterMagickInfo(entry);
826   entry=SetMagickInfo("EPSI");
827   entry->decoder=(DecodeImageHandler *) ReadPSImage;
828   entry->encoder=(EncodeImageHandler *) WritePSImage;
829   entry->magick=(IsImageFormatHandler *) IsPS;
830   entry->adjoin=MagickFalse;
831   entry->blob_support=MagickFalse;
832   entry->seekable_stream=MagickTrue;
833   entry->description=ConstantString(
834     "Encapsulated PostScript Interchange format");
835   entry->module=ConstantString("PS");
836   (void) RegisterMagickInfo(entry);
837   entry=SetMagickInfo("PS");
838   entry->decoder=(DecodeImageHandler *) ReadPSImage;
839   entry->encoder=(EncodeImageHandler *) WritePSImage;
840   entry->magick=(IsImageFormatHandler *) IsPS;
841   entry->module=ConstantString("PS");
842   entry->blob_support=MagickFalse;
843   entry->seekable_stream=MagickTrue;
844   entry->description=ConstantString("PostScript");
845   (void) RegisterMagickInfo(entry);
846   return(MagickImageCoderSignature);
847 }
848 \f
849 /*
850 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
851 %                                                                             %
852 %                                                                             %
853 %                                                                             %
854 %   U n r e g i s t e r P S I m a g e                                         %
855 %                                                                             %
856 %                                                                             %
857 %                                                                             %
858 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
859 %
860 %  UnregisterPSImage() removes format registrations made by the
861 %  PS module from the list of supported formats.
862 %
863 %  The format of the UnregisterPSImage method is:
864 %
865 %      UnregisterPSImage(void)
866 %
867 */
868 ModuleExport void UnregisterPSImage(void)
869 {
870   (void) UnregisterMagickInfo("EPI");
871   (void) UnregisterMagickInfo("EPS");
872   (void) UnregisterMagickInfo("EPSF");
873   (void) UnregisterMagickInfo("EPSI");
874   (void) UnregisterMagickInfo("PS");
875 }
876 \f
877 /*
878 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
879 %                                                                             %
880 %                                                                             %
881 %                                                                             %
882 %   W r i t e P S I m a g e                                                   %
883 %                                                                             %
884 %                                                                             %
885 %                                                                             %
886 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
887 %
888 %  WritePSImage translates an image to encapsulated Postscript
889 %  Level I for printing.  If the supplied geometry is null, the image is
890 %  centered on the Postscript page.  Otherwise, the image is positioned as
891 %  specified by the geometry.
892 %
893 %  The format of the WritePSImage method is:
894 %
895 %      MagickBooleanType WritePSImage(const ImageInfo *image_info,
896 %        Image *image,ExceptionInfo *exception)
897 %
898 %  A description of each parameter follows:
899 %
900 %    o image_info: the image info.
901 %
902 %    o image: the image.
903 %
904 %    o exception: return any errors or warnings in this structure.
905 %
906 */
907
908 static inline size_t MagickMin(const size_t x,const size_t y)
909 {
910   if (x < y)
911     return(x);
912   return(y);
913 }
914
915 static inline unsigned char *PopHexPixel(const char **hex_digits,
916   const size_t pixel,unsigned char *pixels)
917 {
918   register const char
919     *hex;
920
921   hex=hex_digits[pixel];
922   *pixels++=(unsigned char) (*hex++);
923   *pixels++=(unsigned char) (*hex);
924   return(pixels);
925 }
926
927 static MagickBooleanType WritePSImage(const ImageInfo *image_info,Image *image,
928   ExceptionInfo *exception)
929 {
930 #define WriteRunlengthPacket(image,pixel,length,p) \
931 { \
932   if ((image->alpha_trait == BlendPixelTrait) && \
933       (GetPixelAlpha(image,p) == (Quantum) TransparentAlpha)) \
934     { \
935       q=PopHexPixel(hex_digits,0xff,q); \
936       q=PopHexPixel(hex_digits,0xff,q); \
937       q=PopHexPixel(hex_digits,0xff,q); \
938     } \
939   else \
940     { \
941       q=PopHexPixel(hex_digits,ScaleQuantumToChar(pixel.red),q); \
942       q=PopHexPixel(hex_digits,ScaleQuantumToChar(pixel.green),q); \
943       q=PopHexPixel(hex_digits,ScaleQuantumToChar(pixel.blue),q); \
944     } \
945   q=PopHexPixel(hex_digits,(size_t) MagickMin(length,0xff),q); \
946 }
947
948   static const char
949     *hex_digits[] =
950     {
951       "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0A", "0B",
952       "0C", "0D", "0E", "0F", "10", "11", "12", "13", "14", "15", "16", "17",
953       "18", "19", "1A", "1B", "1C", "1D", "1E", "1F", "20", "21", "22", "23",
954       "24", "25", "26", "27", "28", "29", "2A", "2B", "2C", "2D", "2E", "2F",
955       "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3A", "3B",
956       "3C", "3D", "3E", "3F", "40", "41", "42", "43", "44", "45", "46", "47",
957       "48", "49", "4A", "4B", "4C", "4D", "4E", "4F", "50", "51", "52", "53",
958       "54", "55", "56", "57", "58", "59", "5A", "5B", "5C", "5D", "5E", "5F",
959       "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6A", "6B",
960       "6C", "6D", "6E", "6F", "70", "71", "72", "73", "74", "75", "76", "77",
961       "78", "79", "7A", "7B", "7C", "7D", "7E", "7F", "80", "81", "82", "83",
962       "84", "85", "86", "87", "88", "89", "8A", "8B", "8C", "8D", "8E", "8F",
963       "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9A", "9B",
964       "9C", "9D", "9E", "9F", "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7",
965       "A8", "A9", "AA", "AB", "AC", "AD", "AE", "AF", "B0", "B1", "B2", "B3",
966       "B4", "B5", "B6", "B7", "B8", "B9", "BA", "BB", "BC", "BD", "BE", "BF",
967       "C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "CA", "CB",
968       "CC", "CD", "CE", "CF", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
969       "D8", "D9", "DA", "DB", "DC", "DD", "DE", "DF", "E0", "E1", "E2", "E3",
970       "E4", "E5", "E6", "E7", "E8", "E9", "EA", "EB", "EC", "ED", "EE", "EF",
971       "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "FA", "FB",
972       "FC", "FD", "FE", "FF",  (char *) NULL
973     },
974     *PostscriptProlog[]=
975     {
976       "%%BeginProlog",
977       "%",
978       "% Display a color image.  The image is displayed in color on",
979       "% Postscript viewers or printers that support color, otherwise",
980       "% it is displayed as grayscale.",
981       "%",
982       "/DirectClassPacket",
983       "{",
984       "  %",
985       "  % Get a DirectClass packet.",
986       "  %",
987       "  % Parameters:",
988       "  %   red.",
989       "  %   green.",
990       "  %   blue.",
991       "  %   length: number of pixels minus one of this color (optional).",
992       "  %",
993       "  currentfile color_packet readhexstring pop pop",
994       "  compression 0 eq",
995       "  {",
996       "    /number_pixels 3 def",
997       "  }",
998       "  {",
999       "    currentfile byte readhexstring pop 0 get",
1000       "    /number_pixels exch 1 add 3 mul def",
1001       "  } ifelse",
1002       "  0 3 number_pixels 1 sub",
1003       "  {",
1004       "    pixels exch color_packet putinterval",
1005       "  } for",
1006       "  pixels 0 number_pixels getinterval",
1007       "} bind def",
1008       "",
1009       "/DirectClassImage",
1010       "{",
1011       "  %",
1012       "  % Display a DirectClass image.",
1013       "  %",
1014       "  systemdict /colorimage known",
1015       "  {",
1016       "    columns rows 8",
1017       "    [",
1018       "      columns 0 0",
1019       "      rows neg 0 rows",
1020       "    ]",
1021       "    { DirectClassPacket } false 3 colorimage",
1022       "  }",
1023       "  {",
1024       "    %",
1025       "    % No colorimage operator;  convert to grayscale.",
1026       "    %",
1027       "    columns rows 8",
1028       "    [",
1029       "      columns 0 0",
1030       "      rows neg 0 rows",
1031       "    ]",
1032       "    { GrayDirectClassPacket } image",
1033       "  } ifelse",
1034       "} bind def",
1035       "",
1036       "/GrayDirectClassPacket",
1037       "{",
1038       "  %",
1039       "  % Get a DirectClass packet;  convert to grayscale.",
1040       "  %",
1041       "  % Parameters:",
1042       "  %   red",
1043       "  %   green",
1044       "  %   blue",
1045       "  %   length: number of pixels minus one of this color (optional).",
1046       "  %",
1047       "  currentfile color_packet readhexstring pop pop",
1048       "  color_packet 0 get 0.299 mul",
1049       "  color_packet 1 get 0.587 mul add",
1050       "  color_packet 2 get 0.114 mul add",
1051       "  cvi",
1052       "  /gray_packet exch def",
1053       "  compression 0 eq",
1054       "  {",
1055       "    /number_pixels 1 def",
1056       "  }",
1057       "  {",
1058       "    currentfile byte readhexstring pop 0 get",
1059       "    /number_pixels exch 1 add def",
1060       "  } ifelse",
1061       "  0 1 number_pixels 1 sub",
1062       "  {",
1063       "    pixels exch gray_packet put",
1064       "  } for",
1065       "  pixels 0 number_pixels getinterval",
1066       "} bind def",
1067       "",
1068       "/GrayPseudoClassPacket",
1069       "{",
1070       "  %",
1071       "  % Get a PseudoClass packet;  convert to grayscale.",
1072       "  %",
1073       "  % Parameters:",
1074       "  %   index: index into the colormap.",
1075       "  %   length: number of pixels minus one of this color (optional).",
1076       "  %",
1077       "  currentfile byte readhexstring pop 0 get",
1078       "  /offset exch 3 mul def",
1079       "  /color_packet colormap offset 3 getinterval def",
1080       "  color_packet 0 get 0.299 mul",
1081       "  color_packet 1 get 0.587 mul add",
1082       "  color_packet 2 get 0.114 mul add",
1083       "  cvi",
1084       "  /gray_packet exch def",
1085       "  compression 0 eq",
1086       "  {",
1087       "    /number_pixels 1 def",
1088       "  }",
1089       "  {",
1090       "    currentfile byte readhexstring pop 0 get",
1091       "    /number_pixels exch 1 add def",
1092       "  } ifelse",
1093       "  0 1 number_pixels 1 sub",
1094       "  {",
1095       "    pixels exch gray_packet put",
1096       "  } for",
1097       "  pixels 0 number_pixels getinterval",
1098       "} bind def",
1099       "",
1100       "/PseudoClassPacket",
1101       "{",
1102       "  %",
1103       "  % Get a PseudoClass packet.",
1104       "  %",
1105       "  % Parameters:",
1106       "  %   index: index into the colormap.",
1107       "  %   length: number of pixels minus one of this color (optional).",
1108       "  %",
1109       "  currentfile byte readhexstring pop 0 get",
1110       "  /offset exch 3 mul def",
1111       "  /color_packet colormap offset 3 getinterval def",
1112       "  compression 0 eq",
1113       "  {",
1114       "    /number_pixels 3 def",
1115       "  }",
1116       "  {",
1117       "    currentfile byte readhexstring pop 0 get",
1118       "    /number_pixels exch 1 add 3 mul def",
1119       "  } ifelse",
1120       "  0 3 number_pixels 1 sub",
1121       "  {",
1122       "    pixels exch color_packet putinterval",
1123       "  } for",
1124       "  pixels 0 number_pixels getinterval",
1125       "} bind def",
1126       "",
1127       "/PseudoClassImage",
1128       "{",
1129       "  %",
1130       "  % Display a PseudoClass image.",
1131       "  %",
1132       "  % Parameters:",
1133       "  %   class: 0-PseudoClass or 1-Grayscale.",
1134       "  %",
1135       "  currentfile buffer readline pop",
1136       "  token pop /class exch def pop",
1137       "  class 0 gt",
1138       "  {",
1139       "    currentfile buffer readline pop",
1140       "    token pop /depth exch def pop",
1141       "    /grays columns 8 add depth sub depth mul 8 idiv string def",
1142       "    columns rows depth",
1143       "    [",
1144       "      columns 0 0",
1145       "      rows neg 0 rows",
1146       "    ]",
1147       "    { currentfile grays readhexstring pop } image",
1148       "  }",
1149       "  {",
1150       "    %",
1151       "    % Parameters:",
1152       "    %   colors: number of colors in the colormap.",
1153       "    %   colormap: red, green, blue color packets.",
1154       "    %",
1155       "    currentfile buffer readline pop",
1156       "    token pop /colors exch def pop",
1157       "    /colors colors 3 mul def",
1158       "    /colormap colors string def",
1159       "    currentfile colormap readhexstring pop pop",
1160       "    systemdict /colorimage known",
1161       "    {",
1162       "      columns rows 8",
1163       "      [",
1164       "        columns 0 0",
1165       "        rows neg 0 rows",
1166       "      ]",
1167       "      { PseudoClassPacket } false 3 colorimage",
1168       "    }",
1169       "    {",
1170       "      %",
1171       "      % No colorimage operator;  convert to grayscale.",
1172       "      %",
1173       "      columns rows 8",
1174       "      [",
1175       "        columns 0 0",
1176       "        rows neg 0 rows",
1177       "      ]",
1178       "      { GrayPseudoClassPacket } image",
1179       "    } ifelse",
1180       "  } ifelse",
1181       "} bind def",
1182       "",
1183       "/DisplayImage",
1184       "{",
1185       "  %",
1186       "  % Display a DirectClass or PseudoClass image.",
1187       "  %",
1188       "  % Parameters:",
1189       "  %   x & y translation.",
1190       "  %   x & y scale.",
1191       "  %   label pointsize.",
1192       "  %   image label.",
1193       "  %   image columns & rows.",
1194       "  %   class: 0-DirectClass or 1-PseudoClass.",
1195       "  %   compression: 0-none or 1-RunlengthEncoded.",
1196       "  %   hex color packets.",
1197       "  %",
1198       "  gsave",
1199       "  /buffer 512 string def",
1200       "  /byte 1 string def",
1201       "  /color_packet 3 string def",
1202       "  /pixels 768 string def",
1203       "",
1204       "  currentfile buffer readline pop",
1205       "  token pop /x exch def",
1206       "  token pop /y exch def pop",
1207       "  x y translate",
1208       "  currentfile buffer readline pop",
1209       "  token pop /x exch def",
1210       "  token pop /y exch def pop",
1211       "  currentfile buffer readline pop",
1212       "  token pop /pointsize exch def pop",
1213       "  /Times-Roman findfont pointsize scalefont setfont",
1214       (char *) NULL
1215     },
1216     *PostscriptEpilog[]=
1217     {
1218       "  x y scale",
1219       "  currentfile buffer readline pop",
1220       "  token pop /columns exch def",
1221       "  token pop /rows exch def pop",
1222       "  currentfile buffer readline pop",
1223       "  token pop /class exch def pop",
1224       "  currentfile buffer readline pop",
1225       "  token pop /compression exch def pop",
1226       "  class 0 gt { PseudoClassImage } { DirectClassImage } ifelse",
1227       "  grestore",
1228       (char *) NULL
1229     };
1230
1231   char
1232     buffer[MaxTextExtent],
1233     date[MaxTextExtent],
1234     **labels,
1235     page_geometry[MaxTextExtent];
1236
1237   const char
1238     **s,
1239     *value;
1240
1241   const StringInfo
1242     *profile;
1243
1244   double
1245     pointsize;
1246
1247   GeometryInfo
1248     geometry_info;
1249
1250   MagickBooleanType
1251     status;
1252
1253   MagickOffsetType
1254     scene;
1255
1256   MagickStatusType
1257     flags;
1258
1259   PixelInfo
1260     pixel;
1261
1262   PointInfo
1263     delta,
1264     resolution,
1265     scale;
1266
1267   Quantum
1268     index;
1269
1270   RectangleInfo
1271     geometry,
1272     media_info,
1273     page_info;
1274
1275   register const Quantum
1276     *p;
1277
1278   register ssize_t
1279     i,
1280     x;
1281
1282   register unsigned char
1283     *q;
1284
1285   SegmentInfo
1286     bounds;
1287
1288   size_t
1289     bit,
1290     byte,
1291     length,
1292     page,
1293     text_size;
1294
1295   ssize_t
1296     j,
1297     y;
1298
1299   time_t
1300     timer;
1301
1302   unsigned char
1303     pixels[2048];
1304
1305   /*
1306     Open output image file.
1307   */
1308   assert(image_info != (const ImageInfo *) NULL);
1309   assert(image_info->signature == MagickSignature);
1310   assert(image != (Image *) NULL);
1311   assert(image->signature == MagickSignature);
1312   if (image->debug != MagickFalse)
1313     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1314   assert(exception != (ExceptionInfo *) NULL);
1315   assert(exception->signature == MagickSignature);
1316   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
1317   if (status == MagickFalse)
1318     return(status);
1319   (void) ResetMagickMemory(&bounds,0,sizeof(bounds));
1320   page=1;
1321   scene=0;
1322   do
1323   {
1324     /*
1325       Scale relative to dots-per-inch.
1326     */
1327     if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
1328       (void) TransformImageColorspace(image,sRGBColorspace,exception);
1329     delta.x=DefaultResolution;
1330     delta.y=DefaultResolution;
1331     resolution.x=image->resolution.x;
1332     resolution.y=image->resolution.y;
1333     if ((resolution.x == 0.0) || (resolution.y == 0.0))
1334       {
1335         flags=ParseGeometry(PSDensityGeometry,&geometry_info);
1336         resolution.x=geometry_info.rho;
1337         resolution.y=geometry_info.sigma;
1338         if ((flags & SigmaValue) == 0)
1339           resolution.y=resolution.x;
1340       }
1341     if (image_info->density != (char *) NULL)
1342       {
1343         flags=ParseGeometry(image_info->density,&geometry_info);
1344         resolution.x=geometry_info.rho;
1345         resolution.y=geometry_info.sigma;
1346         if ((flags & SigmaValue) == 0)
1347           resolution.y=resolution.x;
1348       }
1349     if (image->units == PixelsPerCentimeterResolution)
1350       {
1351         resolution.x=(double) ((size_t) (100.0*2.54*resolution.x+0.5)/100.0);
1352         resolution.y=(double) ((size_t) (100.0*2.54*resolution.y+0.5)/100.0);
1353       }
1354     SetGeometry(image,&geometry);
1355     (void) FormatLocaleString(page_geometry,MaxTextExtent,"%.20gx%.20g",
1356       (double) image->columns,(double) image->rows);
1357     if (image_info->page != (char *) NULL)
1358       (void) CopyMagickString(page_geometry,image_info->page,MaxTextExtent);
1359     else
1360       if ((image->page.width != 0) && (image->page.height != 0))
1361         (void) FormatLocaleString(page_geometry,MaxTextExtent,
1362           "%.20gx%.20g%+.20g%+.20g",(double) image->page.width,(double)
1363           image->page.height,(double) image->page.x,(double) image->page.y);
1364       else
1365         if ((image->gravity != UndefinedGravity) &&
1366             (LocaleCompare(image_info->magick,"PS") == 0))
1367           (void) CopyMagickString(page_geometry,PSPageGeometry,MaxTextExtent);
1368     (void) ConcatenateMagickString(page_geometry,">",MaxTextExtent);
1369     (void) ParseMetaGeometry(page_geometry,&geometry.x,&geometry.y,
1370       &geometry.width,&geometry.height);
1371     scale.x=(double) (geometry.width*delta.x)/resolution.x;
1372     geometry.width=(size_t) floor(scale.x+0.5);
1373     scale.y=(double) (geometry.height*delta.y)/resolution.y;
1374     geometry.height=(size_t) floor(scale.y+0.5);
1375     (void) ParseAbsoluteGeometry(page_geometry,&media_info);
1376     (void) ParseGravityGeometry(image,page_geometry,&page_info,exception);
1377     if (image->gravity != UndefinedGravity)
1378       {
1379         geometry.x=(-page_info.x);
1380         geometry.y=(ssize_t) (media_info.height+page_info.y-image->rows);
1381       }
1382     pointsize=12.0;
1383     if (image_info->pointsize != 0.0)
1384       pointsize=image_info->pointsize;
1385     text_size=0;
1386     value=GetImageProperty(image,"label",exception);
1387     if (value != (const char *) NULL)
1388       text_size=(size_t) (MultilineCensus(value)*pointsize+12);
1389     if (page == 1)
1390       {
1391         /*
1392           Output Postscript header.
1393         */
1394         if (LocaleCompare(image_info->magick,"PS") == 0)
1395           (void) CopyMagickString(buffer,"%!PS-Adobe-3.0\n",MaxTextExtent);
1396         else
1397           (void) CopyMagickString(buffer,"%!PS-Adobe-3.0 EPSF-3.0\n",
1398             MaxTextExtent);
1399         (void) WriteBlobString(image,buffer);
1400         (void) WriteBlobString(image,"%%Creator: (ImageMagick)\n");
1401         (void) FormatLocaleString(buffer,MaxTextExtent,"%%%%Title: (%s)\n",
1402           image->filename);
1403         (void) WriteBlobString(image,buffer);
1404         timer=time((time_t *) NULL);
1405         (void) FormatMagickTime(timer,MaxTextExtent,date);
1406         (void) FormatLocaleString(buffer,MaxTextExtent,
1407           "%%%%CreationDate: (%s)\n",date);
1408         (void) WriteBlobString(image,buffer);
1409         bounds.x1=(double) geometry.x;
1410         bounds.y1=(double) geometry.y;
1411         bounds.x2=(double) geometry.x+scale.x;
1412         bounds.y2=(double) geometry.y+(geometry.height+text_size);
1413         if ((image_info->adjoin != MagickFalse) &&
1414             (GetNextImageInList(image) != (Image *) NULL))
1415           (void) CopyMagickString(buffer,"%%%%BoundingBox: (atend)\n",
1416             MaxTextExtent);
1417         else
1418           {
1419             (void) FormatLocaleString(buffer,MaxTextExtent,
1420               "%%%%BoundingBox: %.20g %.20g %.20g %.20g\n",ceil(bounds.x1-0.5),
1421               ceil(bounds.y1-0.5),floor(bounds.x2+0.5),floor(bounds.y2+0.5));
1422             (void) WriteBlobString(image,buffer);
1423             (void) FormatLocaleString(buffer,MaxTextExtent,
1424               "%%%%HiResBoundingBox: %g %g %g %g\n",bounds.x1,
1425               bounds.y1,bounds.x2,bounds.y2);
1426           }
1427         (void) WriteBlobString(image,buffer);
1428         profile=GetImageProfile(image,"8bim");
1429         if (profile != (StringInfo *) NULL)
1430           {
1431             /*
1432               Embed Photoshop profile.
1433             */
1434             (void) FormatLocaleString(buffer,MaxTextExtent,
1435               "%%BeginPhotoshop: %.20g",(double) GetStringInfoLength(profile));
1436             (void) WriteBlobString(image,buffer);
1437             for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
1438             {
1439               if ((i % 32) == 0)
1440                 (void) WriteBlobString(image,"\n% ");
1441               (void) FormatLocaleString(buffer,MaxTextExtent,"%02X",
1442                 (unsigned int) (GetStringInfoDatum(profile)[i] & 0xff));
1443               (void) WriteBlobString(image,buffer);
1444             }
1445             (void) WriteBlobString(image,"\n%EndPhotoshop\n");
1446           }
1447         profile=GetImageProfile(image,"xmp");
1448         if (0 && (profile != (StringInfo *) NULL))
1449           {
1450             /*
1451               Embed XML profile.
1452             */
1453             (void) WriteBlobString(image,"\n%begin_xml_code\n");
1454             (void) FormatLocaleString(buffer,MaxTextExtent,
1455                "\n%%begin_xml_packet: %.20g\n",(double)
1456                GetStringInfoLength(profile));
1457             (void) WriteBlobString(image,buffer);
1458             for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
1459               (void) WriteBlobByte(image,GetStringInfoDatum(profile)[i]);
1460             (void) WriteBlobString(image,"\n%end_xml_packet\n%end_xml_code\n");
1461           }
1462         value=GetImageProperty(image,"label",exception);
1463         if (value != (const char *) NULL)
1464           (void) WriteBlobString(image,
1465             "%%DocumentNeededResources: font Times-Roman\n");
1466         (void) WriteBlobString(image,"%%DocumentData: Clean7Bit\n");
1467         (void) WriteBlobString(image,"%%LanguageLevel: 1\n");
1468         if (LocaleCompare(image_info->magick,"PS") != 0)
1469           (void) WriteBlobString(image,"%%Pages: 1\n");
1470         else
1471           {
1472             /*
1473               Compute the number of pages.
1474             */
1475             (void) WriteBlobString(image,"%%Orientation: Portrait\n");
1476             (void) WriteBlobString(image,"%%PageOrder: Ascend\n");
1477             (void) FormatLocaleString(buffer,MaxTextExtent,"%%%%Pages: %.20g\n",
1478               image_info->adjoin != MagickFalse ? (double)
1479               GetImageListLength(image) : 1.0);
1480             (void) WriteBlobString(image,buffer);
1481           }
1482         (void) WriteBlobString(image,"%%EndComments\n");
1483         (void) WriteBlobString(image,"\n%%BeginDefaults\n");
1484         (void) WriteBlobString(image,"%%EndDefaults\n\n");
1485         if ((LocaleCompare(image_info->magick,"EPI") == 0) ||
1486             (LocaleCompare(image_info->magick,"EPSI") == 0) ||
1487             (LocaleCompare(image_info->magick,"EPT") == 0))
1488           {
1489             Image
1490               *preview_image;
1491
1492             Quantum
1493               pixel;
1494
1495             register ssize_t
1496               x;
1497
1498             ssize_t
1499               y;
1500
1501             /*
1502               Create preview image.
1503             */
1504             preview_image=CloneImage(image,0,0,MagickTrue,exception);
1505             if (preview_image == (Image *) NULL)
1506               ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1507             /*
1508               Dump image as bitmap.
1509             */
1510             (void) FormatLocaleString(buffer,MaxTextExtent,
1511               "%%%%BeginPreview: %.20g %.20g %.20g %.20g\n%%  ",(double)
1512               preview_image->columns,(double) preview_image->rows,1.0,
1513               (double) ((((preview_image->columns+7) >> 3)*preview_image->rows+
1514               35)/36));
1515             (void) WriteBlobString(image,buffer);
1516             q=pixels;
1517             for (y=0; y < (ssize_t) image->rows; y++)
1518             {
1519               p=GetVirtualPixels(preview_image,0,y,preview_image->columns,1,
1520                 exception);
1521               if (p == (const Quantum *) NULL)
1522                 break;
1523               bit=0;
1524               byte=0;
1525               for (x=0; x < (ssize_t) preview_image->columns; x++)
1526               {
1527                 byte<<=1;
1528                 pixel=GetPixelIntensity(preview_image,p);
1529                 if (pixel >= (Quantum) (QuantumRange/2))
1530                   byte|=0x01;
1531                 bit++;
1532                 if (bit == 8)
1533                   {
1534                     q=PopHexPixel(hex_digits,byte,q);
1535                     if ((q-pixels+8) >= 80)
1536                       {
1537                         *q++='\n';
1538                         (void) WriteBlob(image,q-pixels,pixels);
1539                         q=pixels;
1540                         (void) WriteBlobString(image,"%  ");
1541                       };
1542                     bit=0;
1543                     byte=0;
1544                   }
1545               }
1546               if (bit != 0)
1547                 {
1548                   byte<<=(8-bit);
1549                   q=PopHexPixel(hex_digits,byte,q);
1550                   if ((q-pixels+8) >= 80)
1551                     {
1552                       *q++='\n';
1553                       (void) WriteBlob(image,q-pixels,pixels);
1554                       q=pixels;
1555                       (void) WriteBlobString(image,"%  ");
1556                     };
1557                 };
1558             }
1559             if (q != pixels)
1560               {
1561                 *q++='\n';
1562                 (void) WriteBlob(image,q-pixels,pixels);
1563               }
1564             (void) WriteBlobString(image,"\n%%EndPreview\n");
1565             preview_image=DestroyImage(preview_image);
1566           }
1567         /*
1568           Output Postscript commands.
1569         */
1570         for (s=PostscriptProlog; *s != (char *) NULL; s++)
1571         {
1572           (void) FormatLocaleString(buffer,MaxTextExtent,"%s\n",*s);
1573           (void) WriteBlobString(image,buffer);
1574         }
1575         value=GetImageProperty(image,"label",exception);
1576         if (value != (const char *) NULL)
1577           for (j=(ssize_t) MultilineCensus(value)-1; j >= 0; j--)
1578           {
1579             (void) WriteBlobString(image,"  /label 512 string def\n");
1580             (void) WriteBlobString(image,"  currentfile label readline pop\n");
1581             (void) FormatLocaleString(buffer,MaxTextExtent,
1582               "  0 y %g add moveto label show pop\n",j*pointsize+12);
1583             (void) WriteBlobString(image,buffer);
1584           }
1585         for (s=PostscriptEpilog; *s != (char *) NULL; s++)
1586         {
1587           (void) FormatLocaleString(buffer,MaxTextExtent,"%s\n",*s);
1588           (void) WriteBlobString(image,buffer);
1589         }
1590         if (LocaleCompare(image_info->magick,"PS") == 0)
1591           (void) WriteBlobString(image,"  showpage\n");
1592         (void) WriteBlobString(image,"} bind def\n");
1593         (void) WriteBlobString(image,"%%EndProlog\n");
1594       }
1595     (void) FormatLocaleString(buffer,MaxTextExtent,"%%%%Page:  1 %.20g\n",
1596       (double) (page++));
1597     (void) WriteBlobString(image,buffer);
1598     (void) FormatLocaleString(buffer,MaxTextExtent,
1599       "%%%%PageBoundingBox: %.20g %.20g %.20g %.20g\n",(double) geometry.x,
1600       (double) geometry.y,geometry.x+(double) geometry.width,geometry.y+(double)
1601       (geometry.height+text_size));
1602     (void) WriteBlobString(image,buffer);
1603     if ((double) geometry.x < bounds.x1)
1604       bounds.x1=(double) geometry.x;
1605     if ((double) geometry.y < bounds.y1)
1606       bounds.y1=(double) geometry.y;
1607     if ((double) (geometry.x+geometry.width-1) > bounds.x2)
1608       bounds.x2=(double) geometry.x+geometry.width-1;
1609     if ((double) (geometry.y+(geometry.height+text_size)-1) > bounds.y2)
1610       bounds.y2=(double) geometry.y+(geometry.height+text_size)-1;
1611     value=GetImageProperty(image,"label",exception);
1612     if (value != (const char *) NULL)
1613       (void) WriteBlobString(image,"%%%%PageResources: font Times-Roman\n");
1614     if (LocaleCompare(image_info->magick,"PS") != 0)
1615       (void) WriteBlobString(image,"userdict begin\n");
1616     (void) WriteBlobString(image,"DisplayImage\n");
1617     /*
1618       Output image data.
1619     */
1620     (void) FormatLocaleString(buffer,MaxTextExtent,"%.20g %.20g\n%g %g\n%g\n",
1621       (double) geometry.x,(double) geometry.y,scale.x,scale.y,pointsize);
1622     (void) WriteBlobString(image,buffer);
1623     labels=(char **) NULL;
1624     value=GetImageProperty(image,"label",exception);
1625     if (value != (const char *) NULL)
1626       labels=StringToList(value);
1627     if (labels != (char **) NULL)
1628       {
1629         for (i=0; labels[i] != (char *) NULL; i++)
1630         {
1631           (void) FormatLocaleString(buffer,MaxTextExtent,"%s \n",
1632             labels[i]);
1633           (void) WriteBlobString(image,buffer);
1634           labels[i]=DestroyString(labels[i]);
1635         }
1636         labels=(char **) RelinquishMagickMemory(labels);
1637       }
1638     (void) ResetMagickMemory(&pixel,0,sizeof(pixel));
1639     pixel.alpha=(Quantum) TransparentAlpha;
1640     index=0;
1641     x=0;
1642     if ((image_info->type != TrueColorType) &&
1643         (IsImageGray(image,exception) != MagickFalse))
1644       {
1645         if (IsImageMonochrome(image,exception) == MagickFalse)
1646           {
1647             Quantum
1648               pixel;
1649
1650             /*
1651               Dump image as grayscale.
1652             */
1653             (void) FormatLocaleString(buffer,MaxTextExtent,
1654               "%.20g %.20g\n1\n1\n1\n8\n",(double) image->columns,(double)
1655               image->rows);
1656             (void) WriteBlobString(image,buffer);
1657             q=pixels;
1658             for (y=0; y < (ssize_t) image->rows; y++)
1659             {
1660               p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1661               if (p == (const Quantum *) NULL)
1662                 break;
1663               for (x=0; x < (ssize_t) image->columns; x++)
1664               {
1665                 pixel=(Quantum) ScaleQuantumToChar(GetPixelIntensity(image,p));
1666                 q=PopHexPixel(hex_digits,(size_t) pixel,q);
1667                 i++;
1668                 if ((q-pixels+8) >= 80)
1669                   {
1670                     *q++='\n';
1671                     (void) WriteBlob(image,q-pixels,pixels);
1672                     q=pixels;
1673                   }
1674                 p+=GetPixelChannels(image);
1675               }
1676               if (image->previous == (Image *) NULL)
1677                 {
1678                   status=SetImageProgress(image,SaveImageTag,(MagickOffsetType)
1679                     y,image->rows);
1680                   if (status == MagickFalse)
1681                     break;
1682                 }
1683             }
1684             if (q != pixels)
1685               {
1686                 *q++='\n';
1687                 (void) WriteBlob(image,q-pixels,pixels);
1688               }
1689           }
1690         else
1691           {
1692             ssize_t
1693               y;
1694
1695             Quantum
1696               pixel;
1697
1698             /*
1699               Dump image as bitmap.
1700             */
1701             (void) FormatLocaleString(buffer,MaxTextExtent,
1702               "%.20g %.20g\n1\n1\n1\n1\n",(double) image->columns,(double)
1703               image->rows);
1704             (void) WriteBlobString(image,buffer);
1705             q=pixels;
1706             for (y=0; y < (ssize_t) image->rows; y++)
1707             {
1708               p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1709               if (p == (const Quantum *) NULL)
1710                 break;
1711               bit=0;
1712               byte=0;
1713               for (x=0; x < (ssize_t) image->columns; x++)
1714               {
1715                 byte<<=1;
1716                 pixel=GetPixelIntensity(image,p);
1717                 if (pixel >= (Quantum) (QuantumRange/2))
1718                   byte|=0x01;
1719                 bit++;
1720                 if (bit == 8)
1721                   {
1722                     q=PopHexPixel(hex_digits,byte,q);
1723                     if ((q-pixels+2) >= 80)
1724                       {
1725                         *q++='\n';
1726                         (void) WriteBlob(image,q-pixels,pixels);
1727                         q=pixels;
1728                       };
1729                     bit=0;
1730                     byte=0;
1731                   }
1732                 p+=GetPixelChannels(image);
1733               }
1734               if (bit != 0)
1735                 {
1736                   byte<<=(8-bit);
1737                   q=PopHexPixel(hex_digits,byte,q);
1738                   if ((q-pixels+2) >= 80)
1739                     {
1740                       *q++='\n';
1741                       (void) WriteBlob(image,q-pixels,pixels);
1742                       q=pixels;
1743                     }
1744                 };
1745               if (image->previous == (Image *) NULL)
1746                 {
1747                   status=SetImageProgress(image,SaveImageTag,(MagickOffsetType)
1748                     y,image->rows);
1749                   if (status == MagickFalse)
1750                     break;
1751                 }
1752             }
1753             if (q != pixels)
1754               {
1755                 *q++='\n';
1756                 (void) WriteBlob(image,q-pixels,pixels);
1757               }
1758           }
1759       }
1760     else
1761       if ((image->storage_class == DirectClass) ||
1762           (image->colors > 256) || (image->alpha_trait == BlendPixelTrait))
1763         {
1764           /*
1765             Dump DirectClass image.
1766           */
1767           (void) FormatLocaleString(buffer,MaxTextExtent,"%.20g %.20g\n0\n%d\n",
1768             (double) image->columns,(double) image->rows,
1769             image_info->compression == RLECompression ? 1 : 0);
1770           (void) WriteBlobString(image,buffer);
1771           switch (image_info->compression)
1772           {
1773             case RLECompression:
1774             {
1775               /*
1776                 Dump runlength-encoded DirectColor packets.
1777               */
1778               q=pixels;
1779               for (y=0; y < (ssize_t) image->rows; y++)
1780               {
1781                 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1782                 if (p == (const Quantum *) NULL)
1783                   break;
1784                 GetPixelInfoPixel(image,p,&pixel);
1785                 length=255;
1786                 for (x=0; x < (ssize_t) image->columns; x++)
1787                 {
1788                   if ((GetPixelRed(image,p) == pixel.red) &&
1789                       (GetPixelGreen(image,p) == pixel.green) &&
1790                       (GetPixelBlue(image,p) == pixel.blue) &&
1791                       (GetPixelAlpha(image,p) == pixel.alpha) &&
1792                       (length < 255) && (x < (ssize_t) (image->columns-1)))
1793                     length++;
1794                   else
1795                     {
1796                       if (x > 0)
1797                         {
1798                           WriteRunlengthPacket(image,pixel,length,p);
1799                           if ((q-pixels+10) >= 80)
1800                             {
1801                               *q++='\n';
1802                               (void) WriteBlob(image,q-pixels,pixels);
1803                               q=pixels;
1804                             }
1805                         }
1806                       length=0;
1807                     }
1808                   GetPixelInfoPixel(image,p,&pixel);
1809                   p+=GetPixelChannels(image);
1810                 }
1811                 WriteRunlengthPacket(image,pixel,length,p);
1812                 if ((q-pixels+10) >= 80)
1813                   {
1814                     *q++='\n';
1815                     (void) WriteBlob(image,q-pixels,pixels);
1816                     q=pixels;
1817                   }
1818                 if (image->previous == (Image *) NULL)
1819                   {
1820                     status=SetImageProgress(image,SaveImageTag,
1821                       (MagickOffsetType) y,image->rows);
1822                     if (status == MagickFalse)
1823                       break;
1824                   }
1825               }
1826               if (q != pixels)
1827                 {
1828                   *q++='\n';
1829                   (void) WriteBlob(image,q-pixels,pixels);
1830                 }
1831               break;
1832             }
1833             case NoCompression:
1834             default:
1835             {
1836               /*
1837                 Dump uncompressed DirectColor packets.
1838               */
1839               q=pixels;
1840               for (y=0; y < (ssize_t) image->rows; y++)
1841               {
1842                 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1843                 if (p == (const Quantum *) NULL)
1844                   break;
1845                 for (x=0; x < (ssize_t) image->columns; x++)
1846                 {
1847                   if ((image->alpha_trait == BlendPixelTrait) &&
1848                       (GetPixelAlpha(image,p) == (Quantum) TransparentAlpha))
1849                     {
1850                       q=PopHexPixel(hex_digits,0xff,q);
1851                       q=PopHexPixel(hex_digits,0xff,q);
1852                       q=PopHexPixel(hex_digits,0xff,q);
1853                     }
1854                   else
1855                     {
1856                       q=PopHexPixel(hex_digits,ScaleQuantumToChar(
1857                         GetPixelRed(image,p)),q);
1858                       q=PopHexPixel(hex_digits,ScaleQuantumToChar(
1859                         GetPixelGreen(image,p)),q);
1860                       q=PopHexPixel(hex_digits,ScaleQuantumToChar(
1861                         GetPixelBlue(image,p)),q);
1862                     }
1863                   if ((q-pixels+6) >= 80)
1864                     {
1865                       *q++='\n';
1866                       (void) WriteBlob(image,q-pixels,pixels);
1867                       q=pixels;
1868                     }
1869                   p+=GetPixelChannels(image);
1870                 }
1871                 if (image->previous == (Image *) NULL)
1872                   {
1873                     status=SetImageProgress(image,SaveImageTag,
1874                       (MagickOffsetType) y,image->rows);
1875                     if (status == MagickFalse)
1876                       break;
1877                   }
1878               }
1879               if (q != pixels)
1880                 {
1881                   *q++='\n';
1882                   (void) WriteBlob(image,q-pixels,pixels);
1883                 }
1884               break;
1885             }
1886           }
1887           (void) WriteBlobByte(image,'\n');
1888         }
1889       else
1890         {
1891           /*
1892             Dump PseudoClass image.
1893           */
1894           (void) FormatLocaleString(buffer,MaxTextExtent,
1895             "%.20g %.20g\n%d\n%d\n0\n",(double) image->columns,(double)
1896             image->rows,image->storage_class == PseudoClass ? 1 : 0,
1897             image_info->compression == RLECompression ? 1 : 0);
1898           (void) WriteBlobString(image,buffer);
1899           /*
1900             Dump number of colors and colormap.
1901           */
1902           (void) FormatLocaleString(buffer,MaxTextExtent,"%.20g\n",(double)
1903             image->colors);
1904           (void) WriteBlobString(image,buffer);
1905           for (i=0; i < (ssize_t) image->colors; i++)
1906           {
1907             (void) FormatLocaleString(buffer,MaxTextExtent,"%02X%02X%02X\n",
1908               ScaleQuantumToChar(image->colormap[i].red),
1909               ScaleQuantumToChar(image->colormap[i].green),
1910               ScaleQuantumToChar(image->colormap[i].blue));
1911             (void) WriteBlobString(image,buffer);
1912           }
1913           switch (image_info->compression)
1914           {
1915             case RLECompression:
1916             {
1917               /*
1918                 Dump runlength-encoded PseudoColor packets.
1919               */
1920               q=pixels;
1921               for (y=0; y < (ssize_t) image->rows; y++)
1922               {
1923                 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1924                 if (p == (const Quantum *) NULL)
1925                   break;
1926                 index=GetPixelIndex(image,p);
1927                 length=255;
1928                 for (x=0; x < (ssize_t) image->columns; x++)
1929                 {
1930                   if ((index == GetPixelIndex(image,p)) &&
1931                       (length < 255) && (x < ((ssize_t) image->columns-1)))
1932                     length++;
1933                   else
1934                     {
1935                       if (x > 0)
1936                         {
1937                           q=PopHexPixel(hex_digits,(size_t) index,q);
1938                           q=PopHexPixel(hex_digits,(size_t)
1939                             MagickMin(length,0xff),q);
1940                           i++;
1941                           if ((q-pixels+6) >= 80)
1942                             {
1943                               *q++='\n';
1944                               (void) WriteBlob(image,q-pixels,pixels);
1945                               q=pixels;
1946                             }
1947                         }
1948                       length=0;
1949                     }
1950                   index=GetPixelIndex(image,p);
1951                   pixel.red=GetPixelRed(image,p);
1952                   pixel.green=GetPixelGreen(image,p);
1953                   pixel.blue=GetPixelBlue(image,p);
1954                   pixel.alpha=GetPixelAlpha(image,p);
1955                   p+=GetPixelChannels(image);
1956                 }
1957                 q=PopHexPixel(hex_digits,(size_t) index,q);
1958                 q=PopHexPixel(hex_digits,(size_t)
1959                   MagickMin(length,0xff),q);
1960                 if (image->previous == (Image *) NULL)
1961                   {
1962                     status=SetImageProgress(image,SaveImageTag,
1963                       (MagickOffsetType) y,image->rows);
1964                     if (status == MagickFalse)
1965                       break;
1966                   }
1967               }
1968               if (q != pixels)
1969                 {
1970                   *q++='\n';
1971                   (void) WriteBlob(image,q-pixels,pixels);
1972                 }
1973               break;
1974             }
1975             case NoCompression:
1976             default:
1977             {
1978               /*
1979                 Dump uncompressed PseudoColor packets.
1980               */
1981               q=pixels;
1982               for (y=0; y < (ssize_t) image->rows; y++)
1983               {
1984                 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1985                 if (p == (const Quantum *) NULL)
1986                   break;
1987                 for (x=0; x < (ssize_t) image->columns; x++)
1988                 {
1989                   q=PopHexPixel(hex_digits,(size_t) GetPixelIndex(image,p),q);
1990                   if ((q-pixels+4) >= 80)
1991                     {
1992                       *q++='\n';
1993                       (void) WriteBlob(image,q-pixels,pixels);
1994                       q=pixels;
1995                     }
1996                   p+=GetPixelChannels(image);
1997                 }
1998                 if (image->previous == (Image *) NULL)
1999                   {
2000                     status=SetImageProgress(image,SaveImageTag,
2001                       (MagickOffsetType) y,image->rows);
2002                     if (status == MagickFalse)
2003                       break;
2004                   }
2005               }
2006               if (q != pixels)
2007                 {
2008                   *q++='\n';
2009                   (void) WriteBlob(image,q-pixels,pixels);
2010                 }
2011               break;
2012             }
2013           }
2014           (void) WriteBlobByte(image,'\n');
2015         }
2016     if (LocaleCompare(image_info->magick,"PS") != 0)
2017       (void) WriteBlobString(image,"end\n");
2018     (void) WriteBlobString(image,"%%PageTrailer\n");
2019     if (GetNextImageInList(image) == (Image *) NULL)
2020       break;
2021     image=SyncNextImageInList(image);
2022     status=SetImageProgress(image,SaveImagesTag,scene++,
2023       GetImageListLength(image));
2024     if (status == MagickFalse)
2025       break;
2026   } while (image_info->adjoin != MagickFalse);
2027   (void) WriteBlobString(image,"%%Trailer\n");
2028   if (page > 2)
2029     {
2030       (void) FormatLocaleString(buffer,MaxTextExtent,
2031         "%%%%BoundingBox: %.20g %.20g %.20g %.20g\n",ceil(bounds.x1-0.5),
2032         ceil(bounds.y1-0.5),floor(bounds.x2-0.5),floor(bounds.y2-0.5));
2033       (void) WriteBlobString(image,buffer);
2034       (void) FormatLocaleString(buffer,MaxTextExtent,
2035         "%%%%HiResBoundingBox: %g %g %g %g\n",bounds.x1,bounds.y1,bounds.x2,
2036         bounds.y2);
2037       (void) WriteBlobString(image,buffer);
2038     }
2039   (void) WriteBlobString(image,"%%EOF\n");
2040   (void) CloseBlob(image);
2041   return(MagickTrue);
2042 }