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