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