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