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