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