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