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