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