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