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