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