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