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