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