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