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