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