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