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