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