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