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 
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 
80 /*
81   Forward declarations.
82 */
83 static MagickBooleanType
84   WritePSImage(const ImageInfo *,Image *,ExceptionInfo *);
85 
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)
PostscriptDelegateMessage(void * handle,const char * message,int length)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 
InvokePostscriptDelegate(const MagickBooleanType verbose,const char * command,char * message,ExceptionInfo * exception)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 
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 */
IsPS(const unsigned char * magick,const size_t length)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 
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 
IsPostscriptRendered(const char * path)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 
ProfileInteger(Image * image,short int * hex_digits)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 
ReadPSImage(const ImageInfo * image_info,ExceptionInfo * exception)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,MagickPathExtent);
984     (void) CopyMagickString(postscript_image->magick,image->magick,
985       MagickPathExtent);
986     if (columns != 0)
987       postscript_image->magick_columns=columns;
988     if (rows != 0)
989       postscript_image->magick_rows=rows;
990     postscript_image->page=page;
991     (void) CloneImageProfiles(postscript_image,image);
992     (void) CloneImageProperties(postscript_image,image);
993     next=SyncNextImageInList(postscript_image);
994     if (next != (Image *) NULL)
995       postscript_image=next;
996   } while (next != (Image *) NULL);
997   image=DestroyImageList(image);
998   scene=0;
999   for (next=GetFirstImageInList(postscript_image); next != (Image *) NULL; )
1000   {
1001     next->scene=scene++;
1002     next=GetNextImageInList(next);
1003   }
1004   return(GetFirstImageInList(postscript_image));
1005 }
1006 
1007 /*
1008 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1009 %                                                                             %
1010 %                                                                             %
1011 %                                                                             %
1012 %   R e g i s t e r P S I m a g e                                             %
1013 %                                                                             %
1014 %                                                                             %
1015 %                                                                             %
1016 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1017 %
1018 %  RegisterPSImage() adds properties for the PS image format to
1019 %  the list of supported formats.  The properties include the image format
1020 %  tag, a method to read and/or write the format, whether the format
1021 %  supports the saving of more than one frame to the same file or blob,
1022 %  whether the format supports native in-memory I/O, and a brief
1023 %  description of the format.
1024 %
1025 %  The format of the RegisterPSImage method is:
1026 %
1027 %      size_t RegisterPSImage(void)
1028 %
1029 */
RegisterPSImage(void)1030 ModuleExport size_t RegisterPSImage(void)
1031 {
1032   MagickInfo
1033     *entry;
1034 
1035   entry=AcquireMagickInfo("PS","EPI",
1036     "Encapsulated PostScript Interchange format");
1037   entry->decoder=(DecodeImageHandler *) ReadPSImage;
1038   entry->encoder=(EncodeImageHandler *) WritePSImage;
1039   entry->magick=(IsImageFormatHandler *) IsPS;
1040   entry->flags^=CoderAdjoinFlag;
1041   entry->flags^=CoderBlobSupportFlag;
1042   entry->flags|=CoderSeekableStreamFlag;
1043   entry->mime_type=ConstantString("application/postscript");
1044   (void) RegisterMagickInfo(entry);
1045   entry=AcquireMagickInfo("PS","EPS","Encapsulated PostScript");
1046   entry->decoder=(DecodeImageHandler *) ReadPSImage;
1047   entry->encoder=(EncodeImageHandler *) WritePSImage;
1048   entry->magick=(IsImageFormatHandler *) IsPS;
1049   entry->flags^=CoderAdjoinFlag;
1050   entry->flags^=CoderBlobSupportFlag;
1051   entry->flags|=CoderSeekableStreamFlag;
1052   entry->mime_type=ConstantString("application/postscript");
1053   (void) RegisterMagickInfo(entry);
1054   entry=AcquireMagickInfo("PS","EPSF","Encapsulated PostScript");
1055   entry->decoder=(DecodeImageHandler *) ReadPSImage;
1056   entry->encoder=(EncodeImageHandler *) WritePSImage;
1057   entry->magick=(IsImageFormatHandler *) IsPS;
1058   entry->flags^=CoderAdjoinFlag;
1059   entry->flags^=CoderBlobSupportFlag;
1060   entry->flags|=CoderSeekableStreamFlag;
1061   entry->mime_type=ConstantString("application/postscript");
1062   (void) RegisterMagickInfo(entry);
1063   entry=AcquireMagickInfo("PS","EPSI",
1064     "Encapsulated PostScript Interchange format");
1065   entry->decoder=(DecodeImageHandler *) ReadPSImage;
1066   entry->encoder=(EncodeImageHandler *) WritePSImage;
1067   entry->magick=(IsImageFormatHandler *) IsPS;
1068   entry->flags^=CoderAdjoinFlag;
1069   entry->flags^=CoderBlobSupportFlag;
1070   entry->flags|=CoderSeekableStreamFlag;
1071   entry->mime_type=ConstantString("application/postscript");
1072   (void) RegisterMagickInfo(entry);
1073   entry=AcquireMagickInfo("PS","PS","PostScript");
1074   entry->decoder=(DecodeImageHandler *) ReadPSImage;
1075   entry->encoder=(EncodeImageHandler *) WritePSImage;
1076   entry->magick=(IsImageFormatHandler *) IsPS;
1077   entry->mime_type=ConstantString("application/postscript");
1078   entry->flags^=CoderBlobSupportFlag;
1079   entry->flags|=CoderSeekableStreamFlag;
1080   (void) RegisterMagickInfo(entry);
1081   return(MagickImageCoderSignature);
1082 }
1083 
1084 /*
1085 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1086 %                                                                             %
1087 %                                                                             %
1088 %                                                                             %
1089 %   U n r e g i s t e r P S I m a g e                                         %
1090 %                                                                             %
1091 %                                                                             %
1092 %                                                                             %
1093 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1094 %
1095 %  UnregisterPSImage() removes format registrations made by the
1096 %  PS module from the list of supported formats.
1097 %
1098 %  The format of the UnregisterPSImage method is:
1099 %
1100 %      UnregisterPSImage(void)
1101 %
1102 */
UnregisterPSImage(void)1103 ModuleExport void UnregisterPSImage(void)
1104 {
1105   (void) UnregisterMagickInfo("EPI");
1106   (void) UnregisterMagickInfo("EPS");
1107   (void) UnregisterMagickInfo("EPSF");
1108   (void) UnregisterMagickInfo("EPSI");
1109   (void) UnregisterMagickInfo("PS");
1110 }
1111 
1112 /*
1113 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1114 %                                                                             %
1115 %                                                                             %
1116 %                                                                             %
1117 %   W r i t e P S I m a g e                                                   %
1118 %                                                                             %
1119 %                                                                             %
1120 %                                                                             %
1121 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1122 %
1123 %  WritePSImage translates an image to encapsulated Postscript
1124 %  Level I for printing.  If the supplied geometry is null, the image is
1125 %  centered on the Postscript page.  Otherwise, the image is positioned as
1126 %  specified by the geometry.
1127 %
1128 %  The format of the WritePSImage method is:
1129 %
1130 %      MagickBooleanType WritePSImage(const ImageInfo *image_info,
1131 %        Image *image,ExceptionInfo *exception)
1132 %
1133 %  A description of each parameter follows:
1134 %
1135 %    o image_info: the image info.
1136 %
1137 %    o image: the image.
1138 %
1139 %    o exception: return any errors or warnings in this structure.
1140 %
1141 */
1142 
PopHexPixel(const char * const * hex_digits,const size_t pixel,unsigned char * pixels)1143 static inline unsigned char *PopHexPixel(const char *const *hex_digits,
1144   const size_t pixel,unsigned char *pixels)
1145 {
1146   register const char
1147     *hex;
1148 
1149   hex=hex_digits[pixel];
1150   *pixels++=(unsigned char) (*hex++);
1151   *pixels++=(unsigned char) (*hex);
1152   return(pixels);
1153 }
1154 
WritePSImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)1155 static MagickBooleanType WritePSImage(const ImageInfo *image_info,Image *image,
1156   ExceptionInfo *exception)
1157 {
1158 #define WriteRunlengthPacket(image,pixel,length,p) \
1159 { \
1160   if ((image->alpha_trait != UndefinedPixelTrait) && \
1161       (GetPixelAlpha(image,p) == (Quantum) TransparentAlpha)) \
1162     { \
1163       q=PopHexPixel(hex_digits,0xff,q); \
1164       q=PopHexPixel(hex_digits,0xff,q); \
1165       q=PopHexPixel(hex_digits,0xff,q); \
1166     } \
1167   else \
1168     { \
1169       q=PopHexPixel(hex_digits,ScaleQuantumToChar(ClampToQuantum(pixel.red)),q); \
1170       q=PopHexPixel(hex_digits,ScaleQuantumToChar(ClampToQuantum(pixel.green)),q); \
1171       q=PopHexPixel(hex_digits,ScaleQuantumToChar(ClampToQuantum(pixel.blue)),q); \
1172     } \
1173   q=PopHexPixel(hex_digits,(size_t) MagickMin(length,0xff),q); \
1174 }
1175 
1176   static const char
1177     *const hex_digits[] =
1178     {
1179       "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0A", "0B",
1180       "0C", "0D", "0E", "0F", "10", "11", "12", "13", "14", "15", "16", "17",
1181       "18", "19", "1A", "1B", "1C", "1D", "1E", "1F", "20", "21", "22", "23",
1182       "24", "25", "26", "27", "28", "29", "2A", "2B", "2C", "2D", "2E", "2F",
1183       "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3A", "3B",
1184       "3C", "3D", "3E", "3F", "40", "41", "42", "43", "44", "45", "46", "47",
1185       "48", "49", "4A", "4B", "4C", "4D", "4E", "4F", "50", "51", "52", "53",
1186       "54", "55", "56", "57", "58", "59", "5A", "5B", "5C", "5D", "5E", "5F",
1187       "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6A", "6B",
1188       "6C", "6D", "6E", "6F", "70", "71", "72", "73", "74", "75", "76", "77",
1189       "78", "79", "7A", "7B", "7C", "7D", "7E", "7F", "80", "81", "82", "83",
1190       "84", "85", "86", "87", "88", "89", "8A", "8B", "8C", "8D", "8E", "8F",
1191       "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9A", "9B",
1192       "9C", "9D", "9E", "9F", "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7",
1193       "A8", "A9", "AA", "AB", "AC", "AD", "AE", "AF", "B0", "B1", "B2", "B3",
1194       "B4", "B5", "B6", "B7", "B8", "B9", "BA", "BB", "BC", "BD", "BE", "BF",
1195       "C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "CA", "CB",
1196       "CC", "CD", "CE", "CF", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
1197       "D8", "D9", "DA", "DB", "DC", "DD", "DE", "DF", "E0", "E1", "E2", "E3",
1198       "E4", "E5", "E6", "E7", "E8", "E9", "EA", "EB", "EC", "ED", "EE", "EF",
1199       "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "FA", "FB",
1200       "FC", "FD", "FE", "FF",  (const char *) NULL
1201     },
1202     *const PostscriptProlog[]=
1203     {
1204       "%%BeginProlog",
1205       "%",
1206       "% Display a color image.  The image is displayed in color on",
1207       "% Postscript viewers or printers that support color, otherwise",
1208       "% it is displayed as grayscale.",
1209       "%",
1210       "/DirectClassPacket",
1211       "{",
1212       "  %",
1213       "  % Get a DirectClass packet.",
1214       "  %",
1215       "  % Parameters:",
1216       "  %   red.",
1217       "  %   green.",
1218       "  %   blue.",
1219       "  %   length: number of pixels minus one of this color (optional).",
1220       "  %",
1221       "  currentfile color_packet readhexstring pop pop",
1222       "  compression 0 eq",
1223       "  {",
1224       "    /number_pixels 3 def",
1225       "  }",
1226       "  {",
1227       "    currentfile byte readhexstring pop 0 get",
1228       "    /number_pixels exch 1 add 3 mul def",
1229       "  } ifelse",
1230       "  0 3 number_pixels 1 sub",
1231       "  {",
1232       "    pixels exch color_packet putinterval",
1233       "  } for",
1234       "  pixels 0 number_pixels getinterval",
1235       "} bind def",
1236       "",
1237       "/DirectClassImage",
1238       "{",
1239       "  %",
1240       "  % Display a DirectClass image.",
1241       "  %",
1242       "  systemdict /colorimage known",
1243       "  {",
1244       "    columns rows 8",
1245       "    [",
1246       "      columns 0 0",
1247       "      rows neg 0 rows",
1248       "    ]",
1249       "    { DirectClassPacket } false 3 colorimage",
1250       "  }",
1251       "  {",
1252       "    %",
1253       "    % No colorimage operator;  convert to grayscale.",
1254       "    %",
1255       "    columns rows 8",
1256       "    [",
1257       "      columns 0 0",
1258       "      rows neg 0 rows",
1259       "    ]",
1260       "    { GrayDirectClassPacket } image",
1261       "  } ifelse",
1262       "} bind def",
1263       "",
1264       "/GrayDirectClassPacket",
1265       "{",
1266       "  %",
1267       "  % Get a DirectClass packet;  convert to grayscale.",
1268       "  %",
1269       "  % Parameters:",
1270       "  %   red",
1271       "  %   green",
1272       "  %   blue",
1273       "  %   length: number of pixels minus one of this color (optional).",
1274       "  %",
1275       "  currentfile color_packet readhexstring pop pop",
1276       "  color_packet 0 get 0.299 mul",
1277       "  color_packet 1 get 0.587 mul add",
1278       "  color_packet 2 get 0.114 mul add",
1279       "  cvi",
1280       "  /gray_packet exch def",
1281       "  compression 0 eq",
1282       "  {",
1283       "    /number_pixels 1 def",
1284       "  }",
1285       "  {",
1286       "    currentfile byte readhexstring pop 0 get",
1287       "    /number_pixels exch 1 add def",
1288       "  } ifelse",
1289       "  0 1 number_pixels 1 sub",
1290       "  {",
1291       "    pixels exch gray_packet put",
1292       "  } for",
1293       "  pixels 0 number_pixels getinterval",
1294       "} bind def",
1295       "",
1296       "/GrayPseudoClassPacket",
1297       "{",
1298       "  %",
1299       "  % Get a PseudoClass packet;  convert to grayscale.",
1300       "  %",
1301       "  % Parameters:",
1302       "  %   index: index into the colormap.",
1303       "  %   length: number of pixels minus one of this color (optional).",
1304       "  %",
1305       "  currentfile byte readhexstring pop 0 get",
1306       "  /offset exch 3 mul def",
1307       "  /color_packet colormap offset 3 getinterval def",
1308       "  color_packet 0 get 0.299 mul",
1309       "  color_packet 1 get 0.587 mul add",
1310       "  color_packet 2 get 0.114 mul add",
1311       "  cvi",
1312       "  /gray_packet exch def",
1313       "  compression 0 eq",
1314       "  {",
1315       "    /number_pixels 1 def",
1316       "  }",
1317       "  {",
1318       "    currentfile byte readhexstring pop 0 get",
1319       "    /number_pixels exch 1 add def",
1320       "  } ifelse",
1321       "  0 1 number_pixels 1 sub",
1322       "  {",
1323       "    pixels exch gray_packet put",
1324       "  } for",
1325       "  pixels 0 number_pixels getinterval",
1326       "} bind def",
1327       "",
1328       "/PseudoClassPacket",
1329       "{",
1330       "  %",
1331       "  % Get a PseudoClass packet.",
1332       "  %",
1333       "  % Parameters:",
1334       "  %   index: index into the colormap.",
1335       "  %   length: number of pixels minus one of this color (optional).",
1336       "  %",
1337       "  currentfile byte readhexstring pop 0 get",
1338       "  /offset exch 3 mul def",
1339       "  /color_packet colormap offset 3 getinterval def",
1340       "  compression 0 eq",
1341       "  {",
1342       "    /number_pixels 3 def",
1343       "  }",
1344       "  {",
1345       "    currentfile byte readhexstring pop 0 get",
1346       "    /number_pixels exch 1 add 3 mul def",
1347       "  } ifelse",
1348       "  0 3 number_pixels 1 sub",
1349       "  {",
1350       "    pixels exch color_packet putinterval",
1351       "  } for",
1352       "  pixels 0 number_pixels getinterval",
1353       "} bind def",
1354       "",
1355       "/PseudoClassImage",
1356       "{",
1357       "  %",
1358       "  % Display a PseudoClass image.",
1359       "  %",
1360       "  % Parameters:",
1361       "  %   class: 0-PseudoClass or 1-Grayscale.",
1362       "  %",
1363       "  currentfile buffer readline pop",
1364       "  token pop /class exch def pop",
1365       "  class 0 gt",
1366       "  {",
1367       "    currentfile buffer readline pop",
1368       "    token pop /depth exch def pop",
1369       "    /grays columns 8 add depth sub depth mul 8 idiv string def",
1370       "    columns rows depth",
1371       "    [",
1372       "      columns 0 0",
1373       "      rows neg 0 rows",
1374       "    ]",
1375       "    { currentfile grays readhexstring pop } image",
1376       "  }",
1377       "  {",
1378       "    %",
1379       "    % Parameters:",
1380       "    %   colors: number of colors in the colormap.",
1381       "    %   colormap: red, green, blue color packets.",
1382       "    %",
1383       "    currentfile buffer readline pop",
1384       "    token pop /colors exch def pop",
1385       "    /colors colors 3 mul def",
1386       "    /colormap colors string def",
1387       "    currentfile colormap readhexstring pop pop",
1388       "    systemdict /colorimage known",
1389       "    {",
1390       "      columns rows 8",
1391       "      [",
1392       "        columns 0 0",
1393       "        rows neg 0 rows",
1394       "      ]",
1395       "      { PseudoClassPacket } false 3 colorimage",
1396       "    }",
1397       "    {",
1398       "      %",
1399       "      % No colorimage operator;  convert to grayscale.",
1400       "      %",
1401       "      columns rows 8",
1402       "      [",
1403       "        columns 0 0",
1404       "        rows neg 0 rows",
1405       "      ]",
1406       "      { GrayPseudoClassPacket } image",
1407       "    } ifelse",
1408       "  } ifelse",
1409       "} bind def",
1410       "",
1411       "/DisplayImage",
1412       "{",
1413       "  %",
1414       "  % Display a DirectClass or PseudoClass image.",
1415       "  %",
1416       "  % Parameters:",
1417       "  %   x & y translation.",
1418       "  %   x & y scale.",
1419       "  %   label pointsize.",
1420       "  %   image label.",
1421       "  %   image columns & rows.",
1422       "  %   class: 0-DirectClass or 1-PseudoClass.",
1423       "  %   compression: 0-none or 1-RunlengthEncoded.",
1424       "  %   hex color packets.",
1425       "  %",
1426       "  gsave",
1427       "  /buffer 512 string def",
1428       "  /byte 1 string def",
1429       "  /color_packet 3 string def",
1430       "  /pixels 768 string def",
1431       "",
1432       "  currentfile buffer readline pop",
1433       "  token pop /x exch def",
1434       "  token pop /y exch def pop",
1435       "  x y translate",
1436       "  currentfile buffer readline pop",
1437       "  token pop /x exch def",
1438       "  token pop /y exch def pop",
1439       "  currentfile buffer readline pop",
1440       "  token pop /pointsize exch def pop",
1441       "  /Times-Roman findfont pointsize scalefont setfont",
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           for (j=(ssize_t) MultilineCensus(value)-1; j >= 0; j--)
1813           {
1814             (void) WriteBlobString(image,"  /label 512 string def\n");
1815             (void) WriteBlobString(image,"  currentfile label readline pop\n");
1816             (void) FormatLocaleString(buffer,MagickPathExtent,
1817               "  0 y %g add moveto label show pop\n",j*pointsize+12);
1818             (void) WriteBlobString(image,buffer);
1819           }
1820         for (s=PostscriptEpilog; *s != (char *) NULL; s++)
1821         {
1822           (void) FormatLocaleString(buffer,MagickPathExtent,"%s\n",*s);
1823           (void) WriteBlobString(image,buffer);
1824         }
1825         if (LocaleCompare(image_info->magick,"PS") == 0)
1826           (void) WriteBlobString(image,"  showpage\n");
1827         (void) WriteBlobString(image,"} bind def\n");
1828         (void) WriteBlobString(image,"%%EndProlog\n");
1829       }
1830     (void) FormatLocaleString(buffer,MagickPathExtent,"%%%%Page:  1 %.20g\n",
1831       (double) (page++));
1832     (void) WriteBlobString(image,buffer);
1833     (void) FormatLocaleString(buffer,MagickPathExtent,
1834       "%%%%PageBoundingBox: %.20g %.20g %.20g %.20g\n",(double) geometry.x,
1835       (double) geometry.y,geometry.x+(double) geometry.width,geometry.y+(double)
1836       (geometry.height+text_size));
1837     (void) WriteBlobString(image,buffer);
1838     if ((double) geometry.x < bounds.x1)
1839       bounds.x1=(double) geometry.x;
1840     if ((double) geometry.y < bounds.y1)
1841       bounds.y1=(double) geometry.y;
1842     if ((double) (geometry.x+geometry.width-1) > bounds.x2)
1843       bounds.x2=(double) geometry.x+geometry.width-1;
1844     if ((double) (geometry.y+(geometry.height+text_size)-1) > bounds.y2)
1845       bounds.y2=(double) geometry.y+(geometry.height+text_size)-1;
1846     value=GetImageProperty(image,"label",exception);
1847     if (value != (const char *) NULL)
1848       (void) WriteBlobString(image,"%%%%PageResources: font Times-Roman\n");
1849     if (LocaleCompare(image_info->magick,"PS") != 0)
1850       (void) WriteBlobString(image,"userdict begin\n");
1851     (void) WriteBlobString(image,"DisplayImage\n");
1852     /*
1853       Output image data.
1854     */
1855     (void) FormatLocaleString(buffer,MagickPathExtent,"%.20g %.20g\n%g %g\n%g\n",
1856       (double) geometry.x,(double) geometry.y,scale.x,scale.y,pointsize);
1857     (void) WriteBlobString(image,buffer);
1858     labels=(char **) NULL;
1859     value=GetImageProperty(image,"label",exception);
1860     if (value != (const char *) NULL)
1861       labels=StringToList(value);
1862     if (labels != (char **) NULL)
1863       {
1864         for (i=0; labels[i] != (char *) NULL; i++)
1865         {
1866           (void) FormatLocaleString(buffer,MagickPathExtent,"%s \n",
1867             labels[i]);
1868           (void) WriteBlobString(image,buffer);
1869           labels[i]=DestroyString(labels[i]);
1870         }
1871         labels=(char **) RelinquishMagickMemory(labels);
1872       }
1873     (void) ResetMagickMemory(&pixel,0,sizeof(pixel));
1874     pixel.alpha=(MagickRealType) TransparentAlpha;
1875     index=0;
1876     x=0;
1877     if ((image_info->type != TrueColorType) &&
1878         (SetImageGray(image,exception) != MagickFalse))
1879       {
1880         if (SetImageMonochrome(image,exception) == MagickFalse)
1881           {
1882             Quantum
1883               pixel;
1884 
1885             /*
1886               Dump image as grayscale.
1887             */
1888             (void) FormatLocaleString(buffer,MagickPathExtent,
1889               "%.20g %.20g\n1\n1\n1\n8\n",(double) image->columns,(double)
1890               image->rows);
1891             (void) WriteBlobString(image,buffer);
1892             q=pixels;
1893             for (y=0; y < (ssize_t) image->rows; y++)
1894             {
1895               p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1896               if (p == (const Quantum *) NULL)
1897                 break;
1898               for (x=0; x < (ssize_t) image->columns; x++)
1899               {
1900                 pixel=(Quantum) ScaleQuantumToChar(ClampToQuantum(GetPixelLuma(
1901                   image,p)));
1902                 q=PopHexPixel(hex_digits,(size_t) pixel,q);
1903                 if ((q-pixels+8) >= 80)
1904                   {
1905                     *q++='\n';
1906                     (void) WriteBlob(image,q-pixels,pixels);
1907                     q=pixels;
1908                   }
1909                 p+=GetPixelChannels(image);
1910               }
1911               if (image->previous == (Image *) NULL)
1912                 {
1913                   status=SetImageProgress(image,SaveImageTag,(MagickOffsetType)
1914                     y,image->rows);
1915                   if (status == MagickFalse)
1916                     break;
1917                 }
1918             }
1919             if (q != pixels)
1920               {
1921                 *q++='\n';
1922                 (void) WriteBlob(image,q-pixels,pixels);
1923               }
1924           }
1925         else
1926           {
1927             ssize_t
1928               y;
1929 
1930             Quantum
1931               pixel;
1932 
1933             /*
1934               Dump image as bitmap.
1935             */
1936             (void) FormatLocaleString(buffer,MagickPathExtent,
1937               "%.20g %.20g\n1\n1\n1\n1\n",(double) image->columns,(double)
1938               image->rows);
1939             (void) WriteBlobString(image,buffer);
1940             q=pixels;
1941             for (y=0; y < (ssize_t) image->rows; y++)
1942             {
1943               p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1944               if (p == (const Quantum *) NULL)
1945                 break;
1946               bit=0;
1947               byte=0;
1948               for (x=0; x < (ssize_t) image->columns; x++)
1949               {
1950                 byte<<=1;
1951                 pixel=ClampToQuantum(GetPixelLuma(image,p));
1952                 if (pixel >= (Quantum) (QuantumRange/2))
1953                   byte|=0x01;
1954                 bit++;
1955                 if (bit == 8)
1956                   {
1957                     q=PopHexPixel(hex_digits,byte,q);
1958                     if ((q-pixels+2) >= 80)
1959                       {
1960                         *q++='\n';
1961                         (void) WriteBlob(image,q-pixels,pixels);
1962                         q=pixels;
1963                       };
1964                     bit=0;
1965                     byte=0;
1966                   }
1967                 p+=GetPixelChannels(image);
1968               }
1969               if (bit != 0)
1970                 {
1971                   byte<<=(8-bit);
1972                   q=PopHexPixel(hex_digits,byte,q);
1973                   if ((q-pixels+2) >= 80)
1974                     {
1975                       *q++='\n';
1976                       (void) WriteBlob(image,q-pixels,pixels);
1977                       q=pixels;
1978                     }
1979                 };
1980               if (image->previous == (Image *) NULL)
1981                 {
1982                   status=SetImageProgress(image,SaveImageTag,(MagickOffsetType)
1983                     y,image->rows);
1984                   if (status == MagickFalse)
1985                     break;
1986                 }
1987             }
1988             if (q != pixels)
1989               {
1990                 *q++='\n';
1991                 (void) WriteBlob(image,q-pixels,pixels);
1992               }
1993           }
1994       }
1995     else
1996       if ((image->storage_class == DirectClass) ||
1997           (image->colors > 256) || (image->alpha_trait != UndefinedPixelTrait))
1998         {
1999           /*
2000             Dump DirectClass image.
2001           */
2002           (void) FormatLocaleString(buffer,MagickPathExtent,"%.20g %.20g\n0\n%d\n",
2003             (double) image->columns,(double) image->rows,
2004             compression == RLECompression ? 1 : 0);
2005           (void) WriteBlobString(image,buffer);
2006           switch (compression)
2007           {
2008             case RLECompression:
2009             {
2010               /*
2011                 Dump runlength-encoded DirectColor packets.
2012               */
2013               q=pixels;
2014               for (y=0; y < (ssize_t) image->rows; y++)
2015               {
2016                 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
2017                 if (p == (const Quantum *) NULL)
2018                   break;
2019                 GetPixelInfoPixel(image,p,&pixel);
2020                 length=255;
2021                 for (x=0; x < (ssize_t) image->columns; x++)
2022                 {
2023                   if ((GetPixelRed(image,p) == ClampToQuantum(pixel.red)) &&
2024                       (GetPixelGreen(image,p) == ClampToQuantum(pixel.green)) &&
2025                       (GetPixelBlue(image,p) == ClampToQuantum(pixel.blue)) &&
2026                       (GetPixelAlpha(image,p) == ClampToQuantum(pixel.alpha)) &&
2027                       (length < 255) && (x < (ssize_t) (image->columns-1)))
2028                     length++;
2029                   else
2030                     {
2031                       if (x > 0)
2032                         {
2033                           WriteRunlengthPacket(image,pixel,length,p);
2034                           if ((q-pixels+10) >= 80)
2035                             {
2036                               *q++='\n';
2037                               (void) WriteBlob(image,q-pixels,pixels);
2038                               q=pixels;
2039                             }
2040                         }
2041                       length=0;
2042                     }
2043                   GetPixelInfoPixel(image,p,&pixel);
2044                   p+=GetPixelChannels(image);
2045                 }
2046                 WriteRunlengthPacket(image,pixel,length,p);
2047                 if ((q-pixels+10) >= 80)
2048                   {
2049                     *q++='\n';
2050                     (void) WriteBlob(image,q-pixels,pixels);
2051                     q=pixels;
2052                   }
2053                 if (image->previous == (Image *) NULL)
2054                   {
2055                     status=SetImageProgress(image,SaveImageTag,
2056                       (MagickOffsetType) y,image->rows);
2057                     if (status == MagickFalse)
2058                       break;
2059                   }
2060               }
2061               if (q != pixels)
2062                 {
2063                   *q++='\n';
2064                   (void) WriteBlob(image,q-pixels,pixels);
2065                 }
2066               break;
2067             }
2068             case NoCompression:
2069             default:
2070             {
2071               /*
2072                 Dump uncompressed DirectColor packets.
2073               */
2074               q=pixels;
2075               for (y=0; y < (ssize_t) image->rows; y++)
2076               {
2077                 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
2078                 if (p == (const Quantum *) NULL)
2079                   break;
2080                 for (x=0; x < (ssize_t) image->columns; x++)
2081                 {
2082                   if ((image->alpha_trait != UndefinedPixelTrait) &&
2083                       (GetPixelAlpha(image,p) == (Quantum) TransparentAlpha))
2084                     {
2085                       q=PopHexPixel(hex_digits,0xff,q);
2086                       q=PopHexPixel(hex_digits,0xff,q);
2087                       q=PopHexPixel(hex_digits,0xff,q);
2088                     }
2089                   else
2090                     {
2091                       q=PopHexPixel(hex_digits,ScaleQuantumToChar(
2092                         GetPixelRed(image,p)),q);
2093                       q=PopHexPixel(hex_digits,ScaleQuantumToChar(
2094                         GetPixelGreen(image,p)),q);
2095                       q=PopHexPixel(hex_digits,ScaleQuantumToChar(
2096                         GetPixelBlue(image,p)),q);
2097                     }
2098                   if ((q-pixels+6) >= 80)
2099                     {
2100                       *q++='\n';
2101                       (void) WriteBlob(image,q-pixels,pixels);
2102                       q=pixels;
2103                     }
2104                   p+=GetPixelChannels(image);
2105                 }
2106                 if (image->previous == (Image *) NULL)
2107                   {
2108                     status=SetImageProgress(image,SaveImageTag,
2109                       (MagickOffsetType) y,image->rows);
2110                     if (status == MagickFalse)
2111                       break;
2112                   }
2113               }
2114               if (q != pixels)
2115                 {
2116                   *q++='\n';
2117                   (void) WriteBlob(image,q-pixels,pixels);
2118                 }
2119               break;
2120             }
2121           }
2122           (void) WriteBlobByte(image,'\n');
2123         }
2124       else
2125         {
2126           /*
2127             Dump PseudoClass image.
2128           */
2129           (void) FormatLocaleString(buffer,MagickPathExtent,
2130             "%.20g %.20g\n%d\n%d\n0\n",(double) image->columns,(double)
2131             image->rows,image->storage_class == PseudoClass ? 1 : 0,
2132             compression == RLECompression ? 1 : 0);
2133           (void) WriteBlobString(image,buffer);
2134           /*
2135             Dump number of colors and colormap.
2136           */
2137           (void) FormatLocaleString(buffer,MagickPathExtent,"%.20g\n",(double)
2138             image->colors);
2139           (void) WriteBlobString(image,buffer);
2140           for (i=0; i < (ssize_t) image->colors; i++)
2141           {
2142             (void) FormatLocaleString(buffer,MagickPathExtent,"%02X%02X%02X\n",
2143               ScaleQuantumToChar(ClampToQuantum(image->colormap[i].red)),
2144               ScaleQuantumToChar(ClampToQuantum(image->colormap[i].green)),
2145               ScaleQuantumToChar(ClampToQuantum(image->colormap[i].blue)));
2146             (void) WriteBlobString(image,buffer);
2147           }
2148           switch (compression)
2149           {
2150             case RLECompression:
2151             {
2152               /*
2153                 Dump runlength-encoded PseudoColor packets.
2154               */
2155               q=pixels;
2156               for (y=0; y < (ssize_t) image->rows; y++)
2157               {
2158                 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
2159                 if (p == (const Quantum *) NULL)
2160                   break;
2161                 index=GetPixelIndex(image,p);
2162                 length=255;
2163                 for (x=0; x < (ssize_t) image->columns; x++)
2164                 {
2165                   if ((index == GetPixelIndex(image,p)) &&
2166                       (length < 255) && (x < ((ssize_t) image->columns-1)))
2167                     length++;
2168                   else
2169                     {
2170                       if (x > 0)
2171                         {
2172                           q=PopHexPixel(hex_digits,(size_t) index,q);
2173                           q=PopHexPixel(hex_digits,(size_t)
2174                             MagickMin(length,0xff),q);
2175                           i++;
2176                           if ((q-pixels+6) >= 80)
2177                             {
2178                               *q++='\n';
2179                               (void) WriteBlob(image,q-pixels,pixels);
2180                               q=pixels;
2181                             }
2182                         }
2183                       length=0;
2184                     }
2185                   index=GetPixelIndex(image,p);
2186                   pixel.red=(MagickRealType) GetPixelRed(image,p);
2187                   pixel.green=(MagickRealType) GetPixelGreen(image,p);
2188                   pixel.blue=(MagickRealType) GetPixelBlue(image,p);
2189                   pixel.alpha=(MagickRealType) GetPixelAlpha(image,p);
2190                   p+=GetPixelChannels(image);
2191                 }
2192                 q=PopHexPixel(hex_digits,(size_t) index,q);
2193                 q=PopHexPixel(hex_digits,(size_t)
2194                   MagickMin(length,0xff),q);
2195                 if (image->previous == (Image *) NULL)
2196                   {
2197                     status=SetImageProgress(image,SaveImageTag,
2198                       (MagickOffsetType) y,image->rows);
2199                     if (status == MagickFalse)
2200                       break;
2201                   }
2202               }
2203               if (q != pixels)
2204                 {
2205                   *q++='\n';
2206                   (void) WriteBlob(image,q-pixels,pixels);
2207                 }
2208               break;
2209             }
2210             case NoCompression:
2211             default:
2212             {
2213               /*
2214                 Dump uncompressed PseudoColor packets.
2215               */
2216               q=pixels;
2217               for (y=0; y < (ssize_t) image->rows; y++)
2218               {
2219                 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
2220                 if (p == (const Quantum *) NULL)
2221                   break;
2222                 for (x=0; x < (ssize_t) image->columns; x++)
2223                 {
2224                   q=PopHexPixel(hex_digits,(size_t) GetPixelIndex(image,p),q);
2225                   if ((q-pixels+4) >= 80)
2226                     {
2227                       *q++='\n';
2228                       (void) WriteBlob(image,q-pixels,pixels);
2229                       q=pixels;
2230                     }
2231                   p+=GetPixelChannels(image);
2232                 }
2233                 if (image->previous == (Image *) NULL)
2234                   {
2235                     status=SetImageProgress(image,SaveImageTag,
2236                       (MagickOffsetType) y,image->rows);
2237                     if (status == MagickFalse)
2238                       break;
2239                   }
2240               }
2241               if (q != pixels)
2242                 {
2243                   *q++='\n';
2244                   (void) WriteBlob(image,q-pixels,pixels);
2245                 }
2246               break;
2247             }
2248           }
2249           (void) WriteBlobByte(image,'\n');
2250         }
2251     if (LocaleCompare(image_info->magick,"PS") != 0)
2252       (void) WriteBlobString(image,"end\n");
2253     (void) WriteBlobString(image,"%%PageTrailer\n");
2254     if (GetNextImageInList(image) == (Image *) NULL)
2255       break;
2256     image=SyncNextImageInList(image);
2257     status=SetImageProgress(image,SaveImagesTag,scene++,
2258       GetImageListLength(image));
2259     if (status == MagickFalse)
2260       break;
2261   } while (image_info->adjoin != MagickFalse);
2262   (void) WriteBlobString(image,"%%Trailer\n");
2263   if (page > 2)
2264     {
2265       (void) FormatLocaleString(buffer,MagickPathExtent,
2266         "%%%%BoundingBox: %.20g %.20g %.20g %.20g\n",ceil(bounds.x1-0.5),
2267         ceil(bounds.y1-0.5),floor(bounds.x2-0.5),floor(bounds.y2-0.5));
2268       (void) WriteBlobString(image,buffer);
2269       (void) FormatLocaleString(buffer,MagickPathExtent,
2270         "%%%%HiResBoundingBox: %g %g %g %g\n",bounds.x1,bounds.y1,bounds.x2,
2271         bounds.y2);
2272       (void) WriteBlobString(image,buffer);
2273     }
2274   (void) WriteBlobString(image,"%%EOF\n");
2275   (void) CloseBlob(image);
2276   return(MagickTrue);
2277 }
2278