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