]> granicus.if.org Git - imagemagick/blob - coders/ps.c
http://www.imagemagick.org/discourse-server/viewtopic.php?f=3&t=31210
[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-2017 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       (const char *) NULL
1443     },
1444     *const PostscriptEpilog[]=
1445     {
1446       "  x y scale",
1447       "  currentfile buffer readline pop",
1448       "  token pop /columns exch def",
1449       "  token pop /rows exch def pop",
1450       "  currentfile buffer readline pop",
1451       "  token pop /class exch def pop",
1452       "  currentfile buffer readline pop",
1453       "  token pop /compression exch def pop",
1454       "  class 0 gt { PseudoClassImage } { DirectClassImage } ifelse",
1455       "  grestore",
1456       (const char *) NULL
1457     };
1458
1459   char
1460     buffer[MagickPathExtent],
1461     date[MagickPathExtent],
1462     **labels,
1463     page_geometry[MagickPathExtent];
1464
1465   CompressionType
1466     compression;
1467
1468   const char
1469     *const *s,
1470     *value;
1471
1472   const StringInfo
1473     *profile;
1474
1475   double
1476     pointsize;
1477
1478   GeometryInfo
1479     geometry_info;
1480
1481   MagickBooleanType
1482     status;
1483
1484   MagickOffsetType
1485     scene;
1486
1487   MagickStatusType
1488     flags;
1489
1490   PixelInfo
1491     pixel;
1492
1493   PointInfo
1494     delta,
1495     resolution,
1496     scale;
1497
1498   Quantum
1499     index;
1500
1501   RectangleInfo
1502     geometry,
1503     media_info,
1504     page_info;
1505
1506   register const Quantum
1507     *p;
1508
1509   register ssize_t
1510     i,
1511     x;
1512
1513   register unsigned char
1514     *q;
1515
1516   SegmentInfo
1517     bounds;
1518
1519   size_t
1520     bit,
1521     byte,
1522     length,
1523     page,
1524     text_size;
1525
1526   ssize_t
1527     j,
1528     y;
1529
1530   time_t
1531     timer;
1532
1533   unsigned char
1534     pixels[2048];
1535
1536   /*
1537     Open output image file.
1538   */
1539   assert(image_info != (const ImageInfo *) NULL);
1540   assert(image_info->signature == MagickCoreSignature);
1541   assert(image != (Image *) NULL);
1542   assert(image->signature == MagickCoreSignature);
1543   if (image->debug != MagickFalse)
1544     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1545   assert(exception != (ExceptionInfo *) NULL);
1546   assert(exception->signature == MagickCoreSignature);
1547   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
1548   if (status == MagickFalse)
1549     return(status);
1550   (void) ResetMagickMemory(&bounds,0,sizeof(bounds));
1551   compression=image->compression;
1552   if (image_info->compression != UndefinedCompression)
1553     compression=image_info->compression;
1554   page=1;
1555   scene=0;
1556   do
1557   {
1558     /*
1559       Scale relative to dots-per-inch.
1560     */
1561     (void) TransformImageColorspace(image,sRGBColorspace,exception);
1562     delta.x=DefaultResolution;
1563     delta.y=DefaultResolution;
1564     resolution.x=image->resolution.x;
1565     resolution.y=image->resolution.y;
1566     if ((resolution.x == 0.0) || (resolution.y == 0.0))
1567       {
1568         flags=ParseGeometry(PSDensityGeometry,&geometry_info);
1569         resolution.x=geometry_info.rho;
1570         resolution.y=geometry_info.sigma;
1571         if ((flags & SigmaValue) == 0)
1572           resolution.y=resolution.x;
1573       }
1574     if (image_info->density != (char *) NULL)
1575       {
1576         flags=ParseGeometry(image_info->density,&geometry_info);
1577         resolution.x=geometry_info.rho;
1578         resolution.y=geometry_info.sigma;
1579         if ((flags & SigmaValue) == 0)
1580           resolution.y=resolution.x;
1581       }
1582     if (image->units == PixelsPerCentimeterResolution)
1583       {
1584         resolution.x=(double) ((size_t) (100.0*2.54*resolution.x+0.5)/100.0);
1585         resolution.y=(double) ((size_t) (100.0*2.54*resolution.y+0.5)/100.0);
1586       }
1587     SetGeometry(image,&geometry);
1588     (void) FormatLocaleString(page_geometry,MagickPathExtent,"%.20gx%.20g",
1589       (double) image->columns,(double) image->rows);
1590     if (image_info->page != (char *) NULL)
1591       (void) CopyMagickString(page_geometry,image_info->page,MagickPathExtent);
1592     else
1593       if ((image->page.width != 0) && (image->page.height != 0))
1594         (void) FormatLocaleString(page_geometry,MagickPathExtent,
1595           "%.20gx%.20g%+.20g%+.20g",(double) image->page.width,(double)
1596           image->page.height,(double) image->page.x,(double) image->page.y);
1597       else
1598         if ((image->gravity != UndefinedGravity) &&
1599             (LocaleCompare(image_info->magick,"PS") == 0))
1600           (void) CopyMagickString(page_geometry,PSPageGeometry,MagickPathExtent);
1601     (void) ConcatenateMagickString(page_geometry,">",MagickPathExtent);
1602     (void) ParseMetaGeometry(page_geometry,&geometry.x,&geometry.y,
1603       &geometry.width,&geometry.height);
1604     scale.x=(double) (geometry.width*delta.x)/resolution.x;
1605     geometry.width=(size_t) floor(scale.x+0.5);
1606     scale.y=(double) (geometry.height*delta.y)/resolution.y;
1607     geometry.height=(size_t) floor(scale.y+0.5);
1608     (void) ParseAbsoluteGeometry(page_geometry,&media_info);
1609     (void) ParseGravityGeometry(image,page_geometry,&page_info,exception);
1610     if (image->gravity != UndefinedGravity)
1611       {
1612         geometry.x=(-page_info.x);
1613         geometry.y=(ssize_t) (media_info.height+page_info.y-image->rows);
1614       }
1615     pointsize=12.0;
1616     if (image_info->pointsize != 0.0)
1617       pointsize=image_info->pointsize;
1618     text_size=0;
1619     value=GetImageProperty(image,"label",exception);
1620     if (value != (const char *) NULL)
1621       text_size=(size_t) (MultilineCensus(value)*pointsize+12);
1622     if (page == 1)
1623       {
1624         /*
1625           Output Postscript header.
1626         */
1627         if (LocaleCompare(image_info->magick,"PS") == 0)
1628           (void) CopyMagickString(buffer,"%!PS-Adobe-3.0\n",MagickPathExtent);
1629         else
1630           (void) CopyMagickString(buffer,"%!PS-Adobe-3.0 EPSF-3.0\n",
1631             MagickPathExtent);
1632         (void) WriteBlobString(image,buffer);
1633         (void) WriteBlobString(image,"%%Creator: (ImageMagick)\n");
1634         (void) FormatLocaleString(buffer,MagickPathExtent,"%%%%Title: (%s)\n",
1635           image->filename);
1636         (void) WriteBlobString(image,buffer);
1637         timer=time((time_t *) NULL);
1638         (void) FormatMagickTime(timer,MagickPathExtent,date);
1639         (void) FormatLocaleString(buffer,MagickPathExtent,
1640           "%%%%CreationDate: (%s)\n",date);
1641         (void) WriteBlobString(image,buffer);
1642         bounds.x1=(double) geometry.x;
1643         bounds.y1=(double) geometry.y;
1644         bounds.x2=(double) geometry.x+scale.x;
1645         bounds.y2=(double) geometry.y+(geometry.height+text_size);
1646         if ((image_info->adjoin != MagickFalse) &&
1647             (GetNextImageInList(image) != (Image *) NULL))
1648           (void) CopyMagickString(buffer,"%%%%BoundingBox: (atend)\n",
1649             MagickPathExtent);
1650         else
1651           {
1652             (void) FormatLocaleString(buffer,MagickPathExtent,
1653               "%%%%BoundingBox: %.20g %.20g %.20g %.20g\n",ceil(bounds.x1-0.5),
1654               ceil(bounds.y1-0.5),floor(bounds.x2+0.5),floor(bounds.y2+0.5));
1655             (void) WriteBlobString(image,buffer);
1656             (void) FormatLocaleString(buffer,MagickPathExtent,
1657               "%%%%HiResBoundingBox: %g %g %g %g\n",bounds.x1,
1658               bounds.y1,bounds.x2,bounds.y2);
1659           }
1660         (void) WriteBlobString(image,buffer);
1661         profile=GetImageProfile(image,"8bim");
1662         if (profile != (StringInfo *) NULL)
1663           {
1664             /*
1665               Embed Photoshop profile.
1666             */
1667             (void) FormatLocaleString(buffer,MagickPathExtent,
1668               "%%BeginPhotoshop: %.20g",(double) GetStringInfoLength(profile));
1669             (void) WriteBlobString(image,buffer);
1670             for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
1671             {
1672               if ((i % 32) == 0)
1673                 (void) WriteBlobString(image,"\n% ");
1674               (void) FormatLocaleString(buffer,MagickPathExtent,"%02X",
1675                 (unsigned int) (GetStringInfoDatum(profile)[i] & 0xff));
1676               (void) WriteBlobString(image,buffer);
1677             }
1678             (void) WriteBlobString(image,"\n%EndPhotoshop\n");
1679           }
1680         profile=GetImageProfile(image,"xmp");
1681 DisableMSCWarning(4127)
1682         if (0 && (profile != (StringInfo *) NULL))
1683 RestoreMSCWarning
1684           {
1685             /*
1686               Embed XML profile.
1687             */
1688             (void) WriteBlobString(image,"\n%begin_xml_code\n");
1689             (void) FormatLocaleString(buffer,MagickPathExtent,
1690                "\n%%begin_xml_packet: %.20g\n",(double)
1691                GetStringInfoLength(profile));
1692             (void) WriteBlobString(image,buffer);
1693             for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
1694               (void) WriteBlobByte(image,GetStringInfoDatum(profile)[i]);
1695             (void) WriteBlobString(image,"\n%end_xml_packet\n%end_xml_code\n");
1696           }
1697         value=GetImageProperty(image,"label",exception);
1698         if (value != (const char *) NULL)
1699           (void) WriteBlobString(image,
1700             "%%DocumentNeededResources: font Times-Roman\n");
1701         (void) WriteBlobString(image,"%%DocumentData: Clean7Bit\n");
1702         (void) WriteBlobString(image,"%%LanguageLevel: 1\n");
1703         if (LocaleCompare(image_info->magick,"PS") != 0)
1704           (void) WriteBlobString(image,"%%Pages: 1\n");
1705         else
1706           {
1707             /*
1708               Compute the number of pages.
1709             */
1710             (void) WriteBlobString(image,"%%Orientation: Portrait\n");
1711             (void) WriteBlobString(image,"%%PageOrder: Ascend\n");
1712             (void) FormatLocaleString(buffer,MagickPathExtent,"%%%%Pages: %.20g\n",
1713               image_info->adjoin != MagickFalse ? (double)
1714               GetImageListLength(image) : 1.0);
1715             (void) WriteBlobString(image,buffer);
1716           }
1717         (void) WriteBlobString(image,"%%EndComments\n");
1718         (void) WriteBlobString(image,"\n%%BeginDefaults\n");
1719         (void) WriteBlobString(image,"%%EndDefaults\n\n");
1720         if ((LocaleCompare(image_info->magick,"EPI") == 0) ||
1721             (LocaleCompare(image_info->magick,"EPSI") == 0) ||
1722             (LocaleCompare(image_info->magick,"EPT") == 0))
1723           {
1724             Image
1725               *preview_image;
1726
1727             Quantum
1728               pixel;
1729
1730             register ssize_t
1731               x;
1732
1733             ssize_t
1734               y;
1735
1736             /*
1737               Create preview image.
1738             */
1739             preview_image=CloneImage(image,0,0,MagickTrue,exception);
1740             if (preview_image == (Image *) NULL)
1741               ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1742             /*
1743               Dump image as bitmap.
1744             */
1745             (void) FormatLocaleString(buffer,MagickPathExtent,
1746               "%%%%BeginPreview: %.20g %.20g %.20g %.20g\n%%  ",(double)
1747               preview_image->columns,(double) preview_image->rows,1.0,
1748               (double) ((((preview_image->columns+7) >> 3)*preview_image->rows+
1749               35)/36));
1750             (void) WriteBlobString(image,buffer);
1751             q=pixels;
1752             for (y=0; y < (ssize_t) image->rows; y++)
1753             {
1754               p=GetVirtualPixels(preview_image,0,y,preview_image->columns,1,
1755                 exception);
1756               if (p == (const Quantum *) NULL)
1757                 break;
1758               bit=0;
1759               byte=0;
1760               for (x=0; x < (ssize_t) preview_image->columns; x++)
1761               {
1762                 byte<<=1;
1763                 pixel=ClampToQuantum(GetPixelLuma(preview_image,p));
1764                 if (pixel >= (Quantum) (QuantumRange/2))
1765                   byte|=0x01;
1766                 bit++;
1767                 if (bit == 8)
1768                   {
1769                     q=PopHexPixel(hex_digits,byte,q);
1770                     if ((q-pixels+8) >= 80)
1771                       {
1772                         *q++='\n';
1773                         (void) WriteBlob(image,q-pixels,pixels);
1774                         q=pixels;
1775                         (void) WriteBlobString(image,"%  ");
1776                       };
1777                     bit=0;
1778                     byte=0;
1779                   }
1780               }
1781               if (bit != 0)
1782                 {
1783                   byte<<=(8-bit);
1784                   q=PopHexPixel(hex_digits,byte,q);
1785                   if ((q-pixels+8) >= 80)
1786                     {
1787                       *q++='\n';
1788                       (void) WriteBlob(image,q-pixels,pixels);
1789                       q=pixels;
1790                       (void) WriteBlobString(image,"%  ");
1791                     };
1792                 };
1793             }
1794             if (q != pixels)
1795               {
1796                 *q++='\n';
1797                 (void) WriteBlob(image,q-pixels,pixels);
1798               }
1799             (void) WriteBlobString(image,"\n%%EndPreview\n");
1800             preview_image=DestroyImage(preview_image);
1801           }
1802         /*
1803           Output Postscript commands.
1804         */
1805         for (s=PostscriptProlog; *s != (char *) NULL; s++)
1806         {
1807           (void) FormatLocaleString(buffer,MagickPathExtent,"%s\n",*s);
1808           (void) WriteBlobString(image,buffer);
1809         }
1810         value=GetImageProperty(image,"label",exception);
1811         if (value != (const char *) NULL)
1812           {
1813             (void) WriteBlobString(image,
1814               "  /Times-Roman findfont pointsize scalefont setfont\n");
1815             for (j=(ssize_t) MultilineCensus(value)-1; j >= 0; j--)
1816             {
1817               (void) WriteBlobString(image,"  /label 512 string def\n");
1818               (void) WriteBlobString(image,
1819                 "  currentfile label readline pop\n");
1820               (void) FormatLocaleString(buffer,MagickPathExtent,
1821                 "  0 y %g add moveto label show pop\n",j*pointsize+12);
1822               (void) WriteBlobString(image,buffer);
1823             }
1824           }
1825         for (s=PostscriptEpilog; *s != (char *) NULL; s++)
1826         {
1827           (void) FormatLocaleString(buffer,MagickPathExtent,"%s\n",*s);
1828           (void) WriteBlobString(image,buffer);
1829         }
1830         if (LocaleCompare(image_info->magick,"PS") == 0)
1831           (void) WriteBlobString(image,"  showpage\n");
1832         (void) WriteBlobString(image,"} bind def\n");
1833         (void) WriteBlobString(image,"%%EndProlog\n");
1834       }
1835     (void) FormatLocaleString(buffer,MagickPathExtent,"%%%%Page:  1 %.20g\n",
1836       (double) (page++));
1837     (void) WriteBlobString(image,buffer);
1838     (void) FormatLocaleString(buffer,MagickPathExtent,
1839       "%%%%PageBoundingBox: %.20g %.20g %.20g %.20g\n",(double) geometry.x,
1840       (double) geometry.y,geometry.x+(double) geometry.width,geometry.y+(double)
1841       (geometry.height+text_size));
1842     (void) WriteBlobString(image,buffer);
1843     if ((double) geometry.x < bounds.x1)
1844       bounds.x1=(double) geometry.x;
1845     if ((double) geometry.y < bounds.y1)
1846       bounds.y1=(double) geometry.y;
1847     if ((double) (geometry.x+geometry.width-1) > bounds.x2)
1848       bounds.x2=(double) geometry.x+geometry.width-1;
1849     if ((double) (geometry.y+(geometry.height+text_size)-1) > bounds.y2)
1850       bounds.y2=(double) geometry.y+(geometry.height+text_size)-1;
1851     value=GetImageProperty(image,"label",exception);
1852     if (value != (const char *) NULL)
1853       (void) WriteBlobString(image,"%%%%PageResources: font Times-Roman\n");
1854     if (LocaleCompare(image_info->magick,"PS") != 0)
1855       (void) WriteBlobString(image,"userdict begin\n");
1856     (void) WriteBlobString(image,"DisplayImage\n");
1857     /*
1858       Output image data.
1859     */
1860     (void) FormatLocaleString(buffer,MagickPathExtent,"%.20g %.20g\n%g %g\n%g\n",
1861       (double) geometry.x,(double) geometry.y,scale.x,scale.y,pointsize);
1862     (void) WriteBlobString(image,buffer);
1863     labels=(char **) NULL;
1864     value=GetImageProperty(image,"label",exception);
1865     if (value != (const char *) NULL)
1866       labels=StringToList(value);
1867     if (labels != (char **) NULL)
1868       {
1869         for (i=0; labels[i] != (char *) NULL; i++)
1870         {
1871           (void) FormatLocaleString(buffer,MagickPathExtent,"%s \n",
1872             labels[i]);
1873           (void) WriteBlobString(image,buffer);
1874           labels[i]=DestroyString(labels[i]);
1875         }
1876         labels=(char **) RelinquishMagickMemory(labels);
1877       }
1878     (void) ResetMagickMemory(&pixel,0,sizeof(pixel));
1879     pixel.alpha=(MagickRealType) TransparentAlpha;
1880     index=0;
1881     x=0;
1882     if ((image_info->type != TrueColorType) &&
1883         (SetImageGray(image,exception) != MagickFalse))
1884       {
1885         if (SetImageMonochrome(image,exception) == MagickFalse)
1886           {
1887             Quantum
1888               pixel;
1889
1890             /*
1891               Dump image as grayscale.
1892             */
1893             (void) FormatLocaleString(buffer,MagickPathExtent,
1894               "%.20g %.20g\n1\n1\n1\n8\n",(double) image->columns,(double)
1895               image->rows);
1896             (void) WriteBlobString(image,buffer);
1897             q=pixels;
1898             for (y=0; y < (ssize_t) image->rows; y++)
1899             {
1900               p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1901               if (p == (const Quantum *) NULL)
1902                 break;
1903               for (x=0; x < (ssize_t) image->columns; x++)
1904               {
1905                 pixel=(Quantum) ScaleQuantumToChar(ClampToQuantum(GetPixelLuma(
1906                   image,p)));
1907                 q=PopHexPixel(hex_digits,(size_t) pixel,q);
1908                 if ((q-pixels+8) >= 80)
1909                   {
1910                     *q++='\n';
1911                     (void) WriteBlob(image,q-pixels,pixels);
1912                     q=pixels;
1913                   }
1914                 p+=GetPixelChannels(image);
1915               }
1916               if (image->previous == (Image *) NULL)
1917                 {
1918                   status=SetImageProgress(image,SaveImageTag,(MagickOffsetType)
1919                     y,image->rows);
1920                   if (status == MagickFalse)
1921                     break;
1922                 }
1923             }
1924             if (q != pixels)
1925               {
1926                 *q++='\n';
1927                 (void) WriteBlob(image,q-pixels,pixels);
1928               }
1929           }
1930         else
1931           {
1932             ssize_t
1933               y;
1934
1935             Quantum
1936               pixel;
1937
1938             /*
1939               Dump image as bitmap.
1940             */
1941             (void) FormatLocaleString(buffer,MagickPathExtent,
1942               "%.20g %.20g\n1\n1\n1\n1\n",(double) image->columns,(double)
1943               image->rows);
1944             (void) WriteBlobString(image,buffer);
1945             q=pixels;
1946             for (y=0; y < (ssize_t) image->rows; y++)
1947             {
1948               p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1949               if (p == (const Quantum *) NULL)
1950                 break;
1951               bit=0;
1952               byte=0;
1953               for (x=0; x < (ssize_t) image->columns; x++)
1954               {
1955                 byte<<=1;
1956                 pixel=ClampToQuantum(GetPixelLuma(image,p));
1957                 if (pixel >= (Quantum) (QuantumRange/2))
1958                   byte|=0x01;
1959                 bit++;
1960                 if (bit == 8)
1961                   {
1962                     q=PopHexPixel(hex_digits,byte,q);
1963                     if ((q-pixels+2) >= 80)
1964                       {
1965                         *q++='\n';
1966                         (void) WriteBlob(image,q-pixels,pixels);
1967                         q=pixels;
1968                       };
1969                     bit=0;
1970                     byte=0;
1971                   }
1972                 p+=GetPixelChannels(image);
1973               }
1974               if (bit != 0)
1975                 {
1976                   byte<<=(8-bit);
1977                   q=PopHexPixel(hex_digits,byte,q);
1978                   if ((q-pixels+2) >= 80)
1979                     {
1980                       *q++='\n';
1981                       (void) WriteBlob(image,q-pixels,pixels);
1982                       q=pixels;
1983                     }
1984                 };
1985               if (image->previous == (Image *) NULL)
1986                 {
1987                   status=SetImageProgress(image,SaveImageTag,(MagickOffsetType)
1988                     y,image->rows);
1989                   if (status == MagickFalse)
1990                     break;
1991                 }
1992             }
1993             if (q != pixels)
1994               {
1995                 *q++='\n';
1996                 (void) WriteBlob(image,q-pixels,pixels);
1997               }
1998           }
1999       }
2000     else
2001       if ((image->storage_class == DirectClass) ||
2002           (image->colors > 256) || (image->alpha_trait != UndefinedPixelTrait))
2003         {
2004           /*
2005             Dump DirectClass image.
2006           */
2007           (void) FormatLocaleString(buffer,MagickPathExtent,"%.20g %.20g\n0\n%d\n",
2008             (double) image->columns,(double) image->rows,
2009             compression == RLECompression ? 1 : 0);
2010           (void) WriteBlobString(image,buffer);
2011           switch (compression)
2012           {
2013             case RLECompression:
2014             {
2015               /*
2016                 Dump runlength-encoded DirectColor packets.
2017               */
2018               q=pixels;
2019               for (y=0; y < (ssize_t) image->rows; y++)
2020               {
2021                 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
2022                 if (p == (const Quantum *) NULL)
2023                   break;
2024                 GetPixelInfoPixel(image,p,&pixel);
2025                 length=255;
2026                 for (x=0; x < (ssize_t) image->columns; x++)
2027                 {
2028                   if ((GetPixelRed(image,p) == ClampToQuantum(pixel.red)) &&
2029                       (GetPixelGreen(image,p) == ClampToQuantum(pixel.green)) &&
2030                       (GetPixelBlue(image,p) == ClampToQuantum(pixel.blue)) &&
2031                       (GetPixelAlpha(image,p) == ClampToQuantum(pixel.alpha)) &&
2032                       (length < 255) && (x < (ssize_t) (image->columns-1)))
2033                     length++;
2034                   else
2035                     {
2036                       if (x > 0)
2037                         {
2038                           WriteRunlengthPacket(image,pixel,length,p);
2039                           if ((q-pixels+10) >= 80)
2040                             {
2041                               *q++='\n';
2042                               (void) WriteBlob(image,q-pixels,pixels);
2043                               q=pixels;
2044                             }
2045                         }
2046                       length=0;
2047                     }
2048                   GetPixelInfoPixel(image,p,&pixel);
2049                   p+=GetPixelChannels(image);
2050                 }
2051                 WriteRunlengthPacket(image,pixel,length,p);
2052                 if ((q-pixels+10) >= 80)
2053                   {
2054                     *q++='\n';
2055                     (void) WriteBlob(image,q-pixels,pixels);
2056                     q=pixels;
2057                   }
2058                 if (image->previous == (Image *) NULL)
2059                   {
2060                     status=SetImageProgress(image,SaveImageTag,
2061                       (MagickOffsetType) y,image->rows);
2062                     if (status == MagickFalse)
2063                       break;
2064                   }
2065               }
2066               if (q != pixels)
2067                 {
2068                   *q++='\n';
2069                   (void) WriteBlob(image,q-pixels,pixels);
2070                 }
2071               break;
2072             }
2073             case NoCompression:
2074             default:
2075             {
2076               /*
2077                 Dump uncompressed DirectColor packets.
2078               */
2079               q=pixels;
2080               for (y=0; y < (ssize_t) image->rows; y++)
2081               {
2082                 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
2083                 if (p == (const Quantum *) NULL)
2084                   break;
2085                 for (x=0; x < (ssize_t) image->columns; x++)
2086                 {
2087                   if ((image->alpha_trait != UndefinedPixelTrait) &&
2088                       (GetPixelAlpha(image,p) == (Quantum) TransparentAlpha))
2089                     {
2090                       q=PopHexPixel(hex_digits,0xff,q);
2091                       q=PopHexPixel(hex_digits,0xff,q);
2092                       q=PopHexPixel(hex_digits,0xff,q);
2093                     }
2094                   else
2095                     {
2096                       q=PopHexPixel(hex_digits,ScaleQuantumToChar(
2097                         GetPixelRed(image,p)),q);
2098                       q=PopHexPixel(hex_digits,ScaleQuantumToChar(
2099                         GetPixelGreen(image,p)),q);
2100                       q=PopHexPixel(hex_digits,ScaleQuantumToChar(
2101                         GetPixelBlue(image,p)),q);
2102                     }
2103                   if ((q-pixels+6) >= 80)
2104                     {
2105                       *q++='\n';
2106                       (void) WriteBlob(image,q-pixels,pixels);
2107                       q=pixels;
2108                     }
2109                   p+=GetPixelChannels(image);
2110                 }
2111                 if (image->previous == (Image *) NULL)
2112                   {
2113                     status=SetImageProgress(image,SaveImageTag,
2114                       (MagickOffsetType) y,image->rows);
2115                     if (status == MagickFalse)
2116                       break;
2117                   }
2118               }
2119               if (q != pixels)
2120                 {
2121                   *q++='\n';
2122                   (void) WriteBlob(image,q-pixels,pixels);
2123                 }
2124               break;
2125             }
2126           }
2127           (void) WriteBlobByte(image,'\n');
2128         }
2129       else
2130         {
2131           /*
2132             Dump PseudoClass image.
2133           */
2134           (void) FormatLocaleString(buffer,MagickPathExtent,
2135             "%.20g %.20g\n%d\n%d\n0\n",(double) image->columns,(double)
2136             image->rows,image->storage_class == PseudoClass ? 1 : 0,
2137             compression == RLECompression ? 1 : 0);
2138           (void) WriteBlobString(image,buffer);
2139           /*
2140             Dump number of colors and colormap.
2141           */
2142           (void) FormatLocaleString(buffer,MagickPathExtent,"%.20g\n",(double)
2143             image->colors);
2144           (void) WriteBlobString(image,buffer);
2145           for (i=0; i < (ssize_t) image->colors; i++)
2146           {
2147             (void) FormatLocaleString(buffer,MagickPathExtent,"%02X%02X%02X\n",
2148               ScaleQuantumToChar(ClampToQuantum(image->colormap[i].red)),
2149               ScaleQuantumToChar(ClampToQuantum(image->colormap[i].green)),
2150               ScaleQuantumToChar(ClampToQuantum(image->colormap[i].blue)));
2151             (void) WriteBlobString(image,buffer);
2152           }
2153           switch (compression)
2154           {
2155             case RLECompression:
2156             {
2157               /*
2158                 Dump runlength-encoded PseudoColor packets.
2159               */
2160               q=pixels;
2161               for (y=0; y < (ssize_t) image->rows; y++)
2162               {
2163                 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
2164                 if (p == (const Quantum *) NULL)
2165                   break;
2166                 index=GetPixelIndex(image,p);
2167                 length=255;
2168                 for (x=0; x < (ssize_t) image->columns; x++)
2169                 {
2170                   if ((index == GetPixelIndex(image,p)) &&
2171                       (length < 255) && (x < ((ssize_t) image->columns-1)))
2172                     length++;
2173                   else
2174                     {
2175                       if (x > 0)
2176                         {
2177                           q=PopHexPixel(hex_digits,(size_t) index,q);
2178                           q=PopHexPixel(hex_digits,(size_t)
2179                             MagickMin(length,0xff),q);
2180                           i++;
2181                           if ((q-pixels+6) >= 80)
2182                             {
2183                               *q++='\n';
2184                               (void) WriteBlob(image,q-pixels,pixels);
2185                               q=pixels;
2186                             }
2187                         }
2188                       length=0;
2189                     }
2190                   index=GetPixelIndex(image,p);
2191                   pixel.red=(MagickRealType) GetPixelRed(image,p);
2192                   pixel.green=(MagickRealType) GetPixelGreen(image,p);
2193                   pixel.blue=(MagickRealType) GetPixelBlue(image,p);
2194                   pixel.alpha=(MagickRealType) GetPixelAlpha(image,p);
2195                   p+=GetPixelChannels(image);
2196                 }
2197                 q=PopHexPixel(hex_digits,(size_t) index,q);
2198                 q=PopHexPixel(hex_digits,(size_t)
2199                   MagickMin(length,0xff),q);
2200                 if (image->previous == (Image *) NULL)
2201                   {
2202                     status=SetImageProgress(image,SaveImageTag,
2203                       (MagickOffsetType) y,image->rows);
2204                     if (status == MagickFalse)
2205                       break;
2206                   }
2207               }
2208               if (q != pixels)
2209                 {
2210                   *q++='\n';
2211                   (void) WriteBlob(image,q-pixels,pixels);
2212                 }
2213               break;
2214             }
2215             case NoCompression:
2216             default:
2217             {
2218               /*
2219                 Dump uncompressed PseudoColor packets.
2220               */
2221               q=pixels;
2222               for (y=0; y < (ssize_t) image->rows; y++)
2223               {
2224                 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
2225                 if (p == (const Quantum *) NULL)
2226                   break;
2227                 for (x=0; x < (ssize_t) image->columns; x++)
2228                 {
2229                   q=PopHexPixel(hex_digits,(size_t) GetPixelIndex(image,p),q);
2230                   if ((q-pixels+4) >= 80)
2231                     {
2232                       *q++='\n';
2233                       (void) WriteBlob(image,q-pixels,pixels);
2234                       q=pixels;
2235                     }
2236                   p+=GetPixelChannels(image);
2237                 }
2238                 if (image->previous == (Image *) NULL)
2239                   {
2240                     status=SetImageProgress(image,SaveImageTag,
2241                       (MagickOffsetType) y,image->rows);
2242                     if (status == MagickFalse)
2243                       break;
2244                   }
2245               }
2246               if (q != pixels)
2247                 {
2248                   *q++='\n';
2249                   (void) WriteBlob(image,q-pixels,pixels);
2250                 }
2251               break;
2252             }
2253           }
2254           (void) WriteBlobByte(image,'\n');
2255         }
2256     if (LocaleCompare(image_info->magick,"PS") != 0)
2257       (void) WriteBlobString(image,"end\n");
2258     (void) WriteBlobString(image,"%%PageTrailer\n");
2259     if (GetNextImageInList(image) == (Image *) NULL)
2260       break;
2261     image=SyncNextImageInList(image);
2262     status=SetImageProgress(image,SaveImagesTag,scene++,
2263       GetImageListLength(image));
2264     if (status == MagickFalse)
2265       break;
2266   } while (image_info->adjoin != MagickFalse);
2267   (void) WriteBlobString(image,"%%Trailer\n");
2268   if (page > 2)
2269     {
2270       (void) FormatLocaleString(buffer,MagickPathExtent,
2271         "%%%%BoundingBox: %.20g %.20g %.20g %.20g\n",ceil(bounds.x1-0.5),
2272         ceil(bounds.y1-0.5),floor(bounds.x2-0.5),floor(bounds.y2-0.5));
2273       (void) WriteBlobString(image,buffer);
2274       (void) FormatLocaleString(buffer,MagickPathExtent,
2275         "%%%%HiResBoundingBox: %g %g %g %g\n",bounds.x1,bounds.y1,bounds.x2,
2276         bounds.y2);
2277       (void) WriteBlobString(image,buffer);
2278     }
2279   (void) WriteBlobString(image,"%%EOF\n");
2280   (void) CloseBlob(image);
2281   return(MagickTrue);
2282 }