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