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