blob: 6e11c933ccd6d42670238adf045e6bbcf0d28ec4 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
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% John Cristy %
17% July 1992 %
18% %
19% %
cristy16af1cb2009-12-11 21:38:29 +000020% Copyright 1999-2010 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% 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 "magick/studio.h"
43#include "magick/blob.h"
44#include "magick/blob-private.h"
45#include "magick/cache.h"
46#include "magick/color.h"
47#include "magick/color-private.h"
48#include "magick/colorspace.h"
49#include "magick/constitute.h"
50#include "magick/delegate.h"
51#include "magick/delegate-private.h"
52#include "magick/draw.h"
53#include "magick/exception.h"
54#include "magick/exception-private.h"
55#include "magick/geometry.h"
56#include "magick/image.h"
57#include "magick/image-private.h"
58#include "magick/list.h"
59#include "magick/magick.h"
60#include "magick/memory_.h"
61#include "magick/monitor.h"
62#include "magick/monitor-private.h"
63#include "magick/option.h"
64#include "magick/profile.h"
65#include "magick/resource_.h"
66#include "magick/pixel-private.h"
67#include "magick/property.h"
68#include "magick/quantum-private.h"
69#include "magick/static.h"
70#include "magick/string_.h"
71#include "magick/module.h"
72#include "magick/token.h"
73#include "magick/transform.h"
74#include "magick/utility.h"
75
76/*
77 Forward declarations.
78*/
79static MagickBooleanType
80 WritePSImage(const ImageInfo *,Image *);
81
82/*
83%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
84% %
85% %
86% %
87% I n v o k e P o s t s r i p t D e l e g a t e %
88% %
89% %
90% %
91%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
92%
cristydefb3f02009-09-10 02:18:35 +000093% InvokePostscriptDelegate() executes the Postscript interpreter with the
cristy3ed852e2009-09-05 21:47:34 +000094% specified command.
95%
96% The format of the InvokePostscriptDelegate method is:
97%
98% MagickBooleanType InvokePostscriptDelegate(
cristyb32b90a2009-09-07 21:45:48 +000099% const MagickBooleanType verbose,const char *command,
100% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000101%
102% A description of each parameter follows:
103%
104% o verbose: A value other than zero displays the command prior to
105% executing it.
106%
107% o command: the address of a character string containing the command to
108% execute.
109%
cristyb32b90a2009-09-07 21:45:48 +0000110% o exception: return any errors or warnings in this structure.
111%
cristy3ed852e2009-09-05 21:47:34 +0000112*/
113static MagickBooleanType InvokePostscriptDelegate(
cristyb32b90a2009-09-07 21:45:48 +0000114 const MagickBooleanType verbose,const char *command,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000115{
cristyb32b90a2009-09-07 21:45:48 +0000116 int
117 status;
118
cristy0157aea2010-04-24 21:12:18 +0000119#if defined(MAGICKCORE_GS_DELEGATE) || defined(MAGICKCORE_WINDOWS_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000120 char
121 **argv;
122
cristydefb3f02009-09-10 02:18:35 +0000123 const GhostInfo
124 *ghost_info;
cristy3ed852e2009-09-05 21:47:34 +0000125
126 gs_main_instance
127 *interpreter;
128
129 int
130 argc,
cristyb32b90a2009-09-07 21:45:48 +0000131 code;
cristy3ed852e2009-09-05 21:47:34 +0000132
cristybb503372010-05-27 20:51:26 +0000133 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000134 i;
135
cristy0157aea2010-04-24 21:12:18 +0000136#if defined(MAGICKCORE_WINDOWS_SUPPORT)
cristydefb3f02009-09-10 02:18:35 +0000137 ghost_info=NTGhostscriptDLLVectors();
cristy3ed852e2009-09-05 21:47:34 +0000138#else
cristydefb3f02009-09-10 02:18:35 +0000139 GhostInfo
140 ghost_info_struct;
cristy3ed852e2009-09-05 21:47:34 +0000141
cristydefb3f02009-09-10 02:18:35 +0000142 ghost_info=(&ghost_info_struct);
143 (void) ResetMagickMemory(&ghost_info,0,sizeof(ghost_info));
144 ghost_info_struct.new_instance=(int (*)(gs_main_instance **,void *))
cristy3ed852e2009-09-05 21:47:34 +0000145 gsapi_new_instance;
cristydefb3f02009-09-10 02:18:35 +0000146 ghost_info_struct.init_with_args=(int (*)(gs_main_instance *,int,char **))
cristy3ed852e2009-09-05 21:47:34 +0000147 gsapi_init_with_args;
cristydefb3f02009-09-10 02:18:35 +0000148 ghost_info_struct.run_string=(int (*)(gs_main_instance *,const char *,int,
149 int *)) gsapi_run_string;
150 ghost_info_struct.delete_instance=(void (*)(gs_main_instance *))
cristy3ed852e2009-09-05 21:47:34 +0000151 gsapi_delete_instance;
cristydefb3f02009-09-10 02:18:35 +0000152 ghost_info_struct.exit=(int (*)(gs_main_instance *)) gsapi_exit;
cristy3ed852e2009-09-05 21:47:34 +0000153#endif
cristydefb3f02009-09-10 02:18:35 +0000154 if (ghost_info == (GhostInfo *) NULL)
cristyb32b90a2009-09-07 21:45:48 +0000155 {
cristy6de4bc22010-01-12 17:10:35 +0000156 status=SystemCommand(MagickFalse,verbose,command,exception);
cristy41083a42009-09-07 23:47:59 +0000157 return(status == 0 ? MagickTrue : MagickFalse);
cristyb32b90a2009-09-07 21:45:48 +0000158 }
cristy3ed852e2009-09-05 21:47:34 +0000159 if (verbose != MagickFalse)
160 {
161 (void) fputs("[ghostscript library]",stdout);
162 (void) fputs(strchr(command,' '),stdout);
163 }
cristydefb3f02009-09-10 02:18:35 +0000164 status=(ghost_info->new_instance)(&interpreter,(void *) NULL);
cristy3ed852e2009-09-05 21:47:34 +0000165 if (status < 0)
cristyb32b90a2009-09-07 21:45:48 +0000166 {
cristy6de4bc22010-01-12 17:10:35 +0000167 status=SystemCommand(MagickFalse,verbose,command,exception);
cristy41083a42009-09-07 23:47:59 +0000168 return(status == 0 ? MagickTrue : MagickFalse);
cristyb32b90a2009-09-07 21:45:48 +0000169 }
cristy3ed852e2009-09-05 21:47:34 +0000170 argv=StringToArgv(command,&argc);
cristydefb3f02009-09-10 02:18:35 +0000171 status=(ghost_info->init_with_args)(interpreter,argc-1,argv+1);
cristy3ed852e2009-09-05 21:47:34 +0000172 if (status == 0)
cristydefb3f02009-09-10 02:18:35 +0000173 status=(ghost_info->run_string)(interpreter,"systemdict /start get exec\n",
174 0,&code);
175 (ghost_info->exit)(interpreter);
176 (ghost_info->delete_instance)(interpreter);
cristy0157aea2010-04-24 21:12:18 +0000177#if defined(MAGICKCORE_WINDOWS_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000178 NTGhostscriptUnLoadDLL();
179#endif
cristybb503372010-05-27 20:51:26 +0000180 for (i=0; i < (ssize_t) argc; i++)
cristy3ed852e2009-09-05 21:47:34 +0000181 argv[i]=DestroyString(argv[i]);
182 argv=(char **) RelinquishMagickMemory(argv);
cristy41083a42009-09-07 23:47:59 +0000183 if ((status != 0) && (status != -101))
184 {
185 char
186 *message;
cristyb32b90a2009-09-07 21:45:48 +0000187
cristy41083a42009-09-07 23:47:59 +0000188 message=GetExceptionMessage(errno);
189 (void) ThrowMagickException(exception,GetMagickModule(),DelegateError,
190 "`%s': %s",command,message);
191 message=DestroyString(message);
192 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
193 "Ghostscript returns status %d, exit code %d",status,code);
194 return(MagickFalse);
195 }
cristy3ed852e2009-09-05 21:47:34 +0000196 return(MagickTrue);
197#else
cristy6de4bc22010-01-12 17:10:35 +0000198 status=SystemCommand(MagickFalse,verbose,command,exception);
cristy41083a42009-09-07 23:47:59 +0000199 return(status == 0 ? MagickTrue : MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +0000200#endif
201}
202
203/*
204%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
205% %
206% %
207% %
208% I s P S %
209% %
210% %
211% %
212%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
213%
214% IsPS() returns MagickTrue if the image format type, identified by the
215% magick string, is PS.
216%
217% The format of the IsPS method is:
218%
219% MagickBooleanType IsPS(const unsigned char *magick,const size_t length)
220%
221% A description of each parameter follows:
222%
223% o magick: compare image format pattern against these bytes.
224%
225% o length: Specifies the length of the magick string.
226%
227*/
228static MagickBooleanType IsPS(const unsigned char *magick,const size_t length)
229{
230 if (length < 4)
231 return(MagickFalse);
232 if (memcmp(magick,"%!",2) == 0)
233 return(MagickTrue);
234 if (memcmp(magick,"\004%!",3) == 0)
235 return(MagickTrue);
236 return(MagickFalse);
237}
238
239/*
240%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
241% %
242% %
243% %
244% R e a d P S I m a g e %
245% %
246% %
247% %
248%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
249%
250% ReadPSImage() reads a Postscript image file and returns it. It allocates
251% the memory necessary for the new Image structure and returns a pointer
252% to the new image.
253%
254% The format of the ReadPSImage method is:
255%
256% Image *ReadPSImage(const ImageInfo *image_info,ExceptionInfo *exception)
257%
258% A description of each parameter follows:
259%
260% o image_info: the image info.
261%
262% o exception: return any errors or warnings in this structure.
263%
264*/
265
266static MagickBooleanType IsPostscriptRendered(const char *path)
267{
268 MagickBooleanType
269 status;
270
271 struct stat
272 attributes;
273
274 if ((path == (const char *) NULL) || (*path == '\0'))
275 return(MagickFalse);
276 status=GetPathAttributes(path,&attributes);
277 if ((status != MagickFalse) && S_ISREG(attributes.st_mode) &&
278 (attributes.st_size > 0))
279 return(MagickTrue);
280 return(MagickFalse);
281}
282
283static inline int ProfileInteger(Image *image,short int *hex_digits)
284{
285 int
286 c,
287 l,
288 value;
289
cristybb503372010-05-27 20:51:26 +0000290 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000291 i;
292
293 l=0;
294 value=0;
295 for (i=0; i < 2; )
296 {
297 c=ReadBlobByte(image);
298 if ((c == EOF) || ((c == '%') && (l == '%')))
299 {
300 value=(-1);
301 break;
302 }
303 l=c;
304 c&=0xff;
305 if (isxdigit(c) == MagickFalse)
306 continue;
cristybb503372010-05-27 20:51:26 +0000307 value=(int) ((size_t) value << 4)+hex_digits[c];
cristy3ed852e2009-09-05 21:47:34 +0000308 i++;
309 }
310 return(value);
311}
312
313static Image *ReadPSImage(const ImageInfo *image_info,ExceptionInfo *exception)
314{
315#define BoundingBox "BoundingBox:"
316#define BeginDocument "BeginDocument:"
317#define BeginXMPPacket "<?xpacket begin="
318#define EndXMPPacket "<?xpacket end="
319#define ICCProfile "BeginICCProfile:"
320#define CMYKCustomColor "CMYKCustomColor:"
cristy01ca8922010-03-17 12:20:37 +0000321#define CMYKProcessColor "CMYKProcessColor:"
cristy3ed852e2009-09-05 21:47:34 +0000322#define DocumentMedia "DocumentMedia:"
323#define DocumentCustomColors "DocumentCustomColors:"
324#define DocumentProcessColors "DocumentProcessColors:"
325#define EndDocument "EndDocument:"
326#define HiResBoundingBox "HiResBoundingBox:"
327#define ImageData "ImageData:"
328#define PageBoundingBox "PageBoundingBox:"
329#define LanguageLevel "LanguageLevel:"
330#define PageMedia "PageMedia:"
331#define Pages "Pages:"
332#define PhotoshopProfile "BeginPhotoshop:"
333#define PostscriptLevel "!PS-"
334#define RenderPostscriptText " Rendering Postscript... "
335#define SpotColor "+ "
336
337 char
338 command[MaxTextExtent],
339 density[MaxTextExtent],
340 filename[MaxTextExtent],
341 geometry[MaxTextExtent],
342 input_filename[MaxTextExtent],
343 options[MaxTextExtent],
344 postscript_filename[MaxTextExtent],
345 translate_geometry[MaxTextExtent];
346
347 const char
348 *option;
349
350 const DelegateInfo
351 *delegate_info;
352
353 GeometryInfo
354 geometry_info;
355
356 Image
357 *image,
358 *next,
359 *postscript_image;
360
361 ImageInfo
362 *read_info;
363
364 int
365 c,
366 file;
367
368 MagickBooleanType
369 cmyk,
370 skip,
371 status;
372
373 MagickStatusType
374 flags;
375
376 PointInfo
377 delta;
378
379 RectangleInfo
380 page;
381
382 register char
383 *p;
384
cristybb503372010-05-27 20:51:26 +0000385 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000386 i;
387
388 SegmentInfo
389 bounds,
390 hires_bounds;
391
392 short int
393 hex_digits[256];
394
395 size_t
396 length;
397
398 ssize_t
399 count;
400
401 StringInfo
402 *profile;
403
cristyf2faecf2010-05-28 19:19:36 +0000404 unsigned long
cristy3ed852e2009-09-05 21:47:34 +0000405 columns,
406 extent,
407 language_level,
408 pages,
409 rows,
410 scene,
411 spotcolor;
412
413 /*
414 Open image file.
415 */
416 assert(image_info != (const ImageInfo *) NULL);
417 assert(image_info->signature == MagickSignature);
418 if (image_info->debug != MagickFalse)
419 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
420 image_info->filename);
421 assert(exception != (ExceptionInfo *) NULL);
422 assert(exception->signature == MagickSignature);
423 image=AcquireImage(image_info);
424 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
425 if (status == MagickFalse)
426 {
427 image=DestroyImageList(image);
428 return((Image *) NULL);
429 }
430 status=AcquireUniqueSymbolicLink(image_info->filename,input_filename);
431 if (status == MagickFalse)
432 {
433 ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
434 image_info->filename);
435 image=DestroyImageList(image);
436 return((Image *) NULL);
437 }
438 /*
439 Initialize hex values.
440 */
441 (void) ResetMagickMemory(hex_digits,0,sizeof(hex_digits));
442 hex_digits[(int) '0']=0;
443 hex_digits[(int) '1']=1;
444 hex_digits[(int) '2']=2;
445 hex_digits[(int) '3']=3;
446 hex_digits[(int) '4']=4;
447 hex_digits[(int) '5']=5;
448 hex_digits[(int) '6']=6;
449 hex_digits[(int) '7']=7;
450 hex_digits[(int) '8']=8;
451 hex_digits[(int) '9']=9;
452 hex_digits[(int) 'a']=10;
453 hex_digits[(int) 'b']=11;
454 hex_digits[(int) 'c']=12;
455 hex_digits[(int) 'd']=13;
456 hex_digits[(int) 'e']=14;
457 hex_digits[(int) 'f']=15;
458 hex_digits[(int) 'A']=10;
459 hex_digits[(int) 'B']=11;
460 hex_digits[(int) 'C']=12;
461 hex_digits[(int) 'D']=13;
462 hex_digits[(int) 'E']=14;
463 hex_digits[(int) 'F']=15;
464 /*
465 Set the page density.
466 */
467 delta.x=DefaultResolution;
468 delta.y=DefaultResolution;
469 if ((image->x_resolution == 0.0) || (image->y_resolution == 0.0))
470 {
471 flags=ParseGeometry(PSDensityGeometry,&geometry_info);
472 image->x_resolution=geometry_info.rho;
473 image->y_resolution=geometry_info.sigma;
474 if ((flags & SigmaValue) == 0)
475 image->y_resolution=image->x_resolution;
476 }
477 /*
478 Determine page geometry from the Postscript bounding box.
479 */
480 (void) ResetMagickMemory(&bounds,0,sizeof(bounds));
481 (void) ResetMagickMemory(&hires_bounds,0,sizeof(hires_bounds));
482 (void) ResetMagickMemory(&page,0,sizeof(page));
483 (void) ResetMagickMemory(command,0,sizeof(command));
484 hires_bounds.x2=0.0;
485 hires_bounds.y2=0.0;
486 columns=0;
487 rows=0;
488 extent=0;
489 spotcolor=0;
490 language_level=1;
491 skip=MagickFalse;
492 cmyk=image_info->colorspace == CMYKColorspace ? MagickTrue : MagickFalse;
493 pages=(~0UL);
494 p=command;
495 for (c=ReadBlobByte(image); c != EOF; c=ReadBlobByte(image))
496 {
497 /*
498 Note document structuring comments.
499 */
500 *p++=(char) c;
501 if ((strchr("\n\r%",c) == (char *) NULL) &&
502 ((size_t) (p-command) < (MaxTextExtent-1)))
503 continue;
504 *p='\0';
505 p=command;
506 /*
507 Skip %%BeginDocument thru %%EndDocument.
508 */
509 if (LocaleNCompare(BeginDocument,command,strlen(BeginDocument)) == 0)
510 skip=MagickTrue;
511 if (LocaleNCompare(EndDocument,command,strlen(EndDocument)) == 0)
512 skip=MagickFalse;
513 if (skip != MagickFalse)
514 continue;
515 if (LocaleNCompare(PostscriptLevel,command,strlen(PostscriptLevel)) == 0)
516 {
517 (void) SetImageProperty(image,"ps:Level",command+4);
518 if (GlobExpression(command,"*EPSF-*",MagickTrue) != MagickFalse)
519 pages=1;
520 }
521 if (LocaleNCompare(LanguageLevel,command,strlen(LanguageLevel)) == 0)
522 (void) sscanf(command,LanguageLevel " %lu",&language_level);
523 if (LocaleNCompare(Pages,command,strlen(Pages)) == 0)
524 (void) sscanf(command,Pages " %lu",&pages);
525 if (LocaleNCompare(ImageData,command,strlen(Pages)) == 0)
526 (void) sscanf(command,ImageData " %lu %lu",&columns,&rows);
527 if (LocaleNCompare(ICCProfile,command,strlen(PhotoshopProfile)) == 0)
528 {
529 unsigned char
530 *datum;
531
532 /*
533 Read ICC profile.
534 */
535 profile=AcquireStringInfo(65536);
536 for (i=0; (c=ProfileInteger(image,hex_digits)) != EOF; i++)
537 {
538 SetStringInfoLength(profile,(size_t) i+1);
539 datum=GetStringInfoDatum(profile);
540 datum[i]=(unsigned char) c;
541 }
542 (void) SetImageProfile(image,"icc",profile);
543 profile=DestroyStringInfo(profile);
544 continue;
545 }
546 if (LocaleNCompare(PhotoshopProfile,command,strlen(PhotoshopProfile)) == 0)
547 {
548 unsigned char
549 *p;
550
551 /*
552 Read Photoshop profile.
553 */
554 count=(ssize_t) sscanf(command,PhotoshopProfile " %lu",&extent);
555 if (count != 1)
556 continue;
557 length=extent;
558 profile=AcquireStringInfo(length);
559 p=GetStringInfoDatum(profile);
cristybb503372010-05-27 20:51:26 +0000560 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +0000561 *p++=(unsigned char) ProfileInteger(image,hex_digits);
562 (void) SetImageProfile(image,"8bim",profile);
563 profile=DestroyStringInfo(profile);
564 continue;
565 }
566 if (LocaleNCompare(BeginXMPPacket,command,strlen(BeginXMPPacket)) == 0)
567 {
568 register size_t
569 i;
570
571 /*
572 Read XMP profile.
573 */
574 p=command;
575 profile=StringToStringInfo(command);
576 for (i=GetStringInfoLength(profile)-1; c != EOF; i++)
577 {
578 SetStringInfoLength(profile,i+1);
579 c=ReadBlobByte(image);
580 GetStringInfoDatum(profile)[i]=(unsigned char) c;
581 *p++=(char) c;
582 if ((strchr("\n\r%",c) == (char *) NULL) &&
583 ((size_t) (p-command) < (MaxTextExtent-1)))
584 continue;
585 *p='\0';
586 p=command;
587 if (LocaleNCompare(EndXMPPacket,command,strlen(EndXMPPacket)) == 0)
588 break;
589 }
590 SetStringInfoLength(profile,i);
591 (void) SetImageProfile(image,"xmp",profile);
592 profile=DestroyStringInfo(profile);
593 continue;
594 }
595 /*
596 Is this a CMYK document?
597 */
598 length=strlen(DocumentProcessColors);
599 if (LocaleNCompare(DocumentProcessColors,command,length) == 0)
600 {
601 if ((GlobExpression(command,"*Cyan*",MagickTrue) != MagickFalse) ||
602 (GlobExpression(command,"*Magenta*",MagickTrue) != MagickFalse) ||
603 (GlobExpression(command,"*Yellow*",MagickTrue) != MagickFalse))
604 cmyk=MagickTrue;
605 }
606 if (LocaleNCompare(CMYKCustomColor,command,strlen(CMYKCustomColor)) == 0)
607 cmyk=MagickTrue;
cristy01ca8922010-03-17 12:20:37 +0000608 if (LocaleNCompare(CMYKProcessColor,command,strlen(CMYKProcessColor)) == 0)
609 cmyk=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +0000610 length=strlen(DocumentCustomColors);
611 if ((LocaleNCompare(DocumentCustomColors,command,length) == 0) ||
612 (LocaleNCompare(CMYKCustomColor,command,strlen(CMYKCustomColor)) == 0) ||
613 (LocaleNCompare(SpotColor,command,strlen(SpotColor)) == 0))
614 {
615 char
616 property[MaxTextExtent],
617 *value;
618
619 register char
620 *p;
621
622 /*
623 Note spot names.
624 */
cristye8c25f92010-06-03 00:53:06 +0000625 (void) FormatMagickString(property,MaxTextExtent,"ps:SpotColor-%.20g",
626 (double) (spotcolor++));
cristy3ed852e2009-09-05 21:47:34 +0000627 for (p=command; *p != '\0'; p++)
628 if (isspace((int) (unsigned char) *p) != 0)
629 break;
630 value=AcquireString(p);
631 (void) SubstituteString(&value,"(","");
632 (void) SubstituteString(&value,")","");
633 (void) StripString(value);
634 (void) SetImageProperty(image,property,value);
635 value=DestroyString(value);
636 continue;
637 }
638 /*
639 Note region defined by bounding box.
640 */
641 count=0;
642 if (LocaleNCompare(BoundingBox,command,strlen(BoundingBox)) == 0)
643 count=(ssize_t) sscanf(command,BoundingBox " %lf %lf %lf %lf",&bounds.x1,
644 &bounds.y1,&bounds.x2,&bounds.y2);
645 if (LocaleNCompare(DocumentMedia,command,strlen(DocumentMedia)) == 0)
646 count=(ssize_t) sscanf(command,DocumentMedia " %*s %lf %lf",&bounds.x2,
647 &bounds.y2)+2;
648 if (LocaleNCompare(HiResBoundingBox,command,strlen(HiResBoundingBox)) == 0)
649 count=(ssize_t) sscanf(command,HiResBoundingBox " %lf %lf %lf %lf",
650 &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
651 if (LocaleNCompare(PageBoundingBox,command,strlen(PageBoundingBox)) == 0)
652 count=(ssize_t) sscanf(command,PageBoundingBox " %lf %lf %lf %lf",
653 &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
654 if (LocaleNCompare(PageMedia,command,strlen(PageMedia)) == 0)
655 count=(ssize_t) sscanf(command,PageMedia " %*s %lf %lf",&bounds.x2,
656 &bounds.y2)+2;
657 if (count != 4)
658 continue;
659 if (((bounds.x2 > hires_bounds.x2) && (bounds.y2 > hires_bounds.y2)) ||
660 ((hires_bounds.x2 == 0.0) && (hires_bounds.y2 == 0.0)))
661 {
662 /*
663 Set Postscript render geometry.
664 */
cristy8cd5b312010-01-07 01:10:24 +0000665 (void) FormatMagickString(geometry,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +0000666 "%gx%g%+.15g%+.15g",bounds.x2-bounds.x1,bounds.y2-bounds.y1,
cristy8cd5b312010-01-07 01:10:24 +0000667 bounds.x1,bounds.y1);
cristy3ed852e2009-09-05 21:47:34 +0000668 (void) SetImageProperty(image,"ps:HiResBoundingBox",geometry);
cristybb503372010-05-27 20:51:26 +0000669 page.width=(size_t) floor(bounds.x2-bounds.x1+0.5);
670 page.height=(size_t) floor(bounds.y2-bounds.y1+0.5);
cristy3ed852e2009-09-05 21:47:34 +0000671 hires_bounds=bounds;
672 }
673 }
674 (void) CloseBlob(image);
675 if (image_info->colorspace == RGBColorspace)
676 cmyk=MagickFalse;
677 /*
678 Create Ghostscript control file.
679 */
680 file=AcquireUniqueFileResource(postscript_filename);
681 if (file == -1)
682 {
683 ThrowFileException(&image->exception,FileOpenError,"UnableToOpenFile",
684 image_info->filename);
685 image=DestroyImageList(image);
686 return((Image *) NULL);
687 }
688 (void) CopyMagickString(command,"/setpagedevice {pop} bind 1 index where {"
689 "dup wcheck {3 1 roll put} {pop def} ifelse} {def} ifelse\n"
690 "<</UseCIEColor true>>setpagedevice\n",MaxTextExtent);
691 count=write(file,command,(unsigned int) strlen(command));
692 (void) FormatMagickString(translate_geometry,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +0000693 "%g %g translate\n",-bounds.x1,-bounds.y1);
cristy0b29b252010-05-30 01:59:46 +0000694 count=write(file,translate_geometry,(unsigned int)
695 strlen(translate_geometry));
cristy3ed852e2009-09-05 21:47:34 +0000696 file=close(file)-1;
697 /*
698 Render Postscript with the Ghostscript delegate.
699 */
700 if (image_info->monochrome != MagickFalse)
701 delegate_info=GetDelegateInfo("ps:mono",(char *) NULL,exception);
702 else
703 if (cmyk != MagickFalse)
704 delegate_info=GetDelegateInfo("ps:cmyk",(char *) NULL,exception);
705 else
cristyf1849f82009-09-08 14:37:22 +0000706 if (pages == 1)
707 delegate_info=GetDelegateInfo("ps:alpha",(char *) NULL,exception);
708 else
709 delegate_info=GetDelegateInfo("ps:color",(char *) NULL,exception);
cristy3ed852e2009-09-05 21:47:34 +0000710 if (delegate_info == (const DelegateInfo *) NULL)
711 {
712 (void) RelinquishUniqueFileResource(postscript_filename);
713 image=DestroyImageList(image);
714 return((Image *) NULL);
715 }
716 *options='\0';
717 if ((page.width == 0) || (page.height == 0))
718 (void) ParseAbsoluteGeometry(PSPageGeometry,&page);
cristydba40d42010-03-25 18:31:50 +0000719 if (image_info->density != (char *) NULL)
720 {
721 flags=ParseGeometry(image_info->density,&geometry_info);
722 image->x_resolution=geometry_info.rho;
723 image->y_resolution=geometry_info.sigma;
724 if ((flags & SigmaValue) == 0)
725 image->y_resolution=image->x_resolution;
726 }
cristye7f51092010-01-17 00:39:37 +0000727 (void) FormatMagickString(density,MaxTextExtent,"%gx%g",
cristy8cd5b312010-01-07 01:10:24 +0000728 image->x_resolution,image->y_resolution);
cristydba40d42010-03-25 18:31:50 +0000729 if (image_info->page != (char *) NULL)
730 (void) ParseAbsoluteGeometry(image_info->page,&page);
cristybb503372010-05-27 20:51:26 +0000731 page.width=(size_t) floor(page.width*image->x_resolution/delta.x+0.5);
732 page.height=(size_t) floor(page.height*image->y_resolution/delta.y+
cristy06609ee2010-03-17 20:21:27 +0000733 0.5);
cristye8c25f92010-06-03 00:53:06 +0000734 (void) FormatMagickString(options,MaxTextExtent,"-g%.20gx%.20g ",(double)
735 page.width,(double) page.height);
cristy3ed852e2009-09-05 21:47:34 +0000736 read_info=CloneImageInfo(image_info);
737 *read_info->magick='\0';
738 if (read_info->number_scenes != 0)
739 {
740 char
741 pages[MaxTextExtent];
742
cristye8c25f92010-06-03 00:53:06 +0000743 (void) FormatMagickString(pages,MaxTextExtent,"-dFirstPage=%.20g "
744 "-dLastPage=%.20g",(double) read_info->scene+1,(double)
cristyf2faecf2010-05-28 19:19:36 +0000745 (read_info->scene+read_info->number_scenes));
cristy3ed852e2009-09-05 21:47:34 +0000746 (void) ConcatenateMagickString(options,pages,MaxTextExtent);
747 read_info->number_scenes=0;
748 if (read_info->scenes != (char *) NULL)
749 *read_info->scenes='\0';
750 }
751 option=GetImageOption(image_info,"ps:use-cropbox");
752 if ((option != (const char *) NULL) && (IsMagickTrue(option) != MagickFalse))
753 (void) ConcatenateMagickString(options,"-dEPSCrop ",MaxTextExtent);
754 (void) CopyMagickString(filename,read_info->filename,MaxTextExtent);
755 (void) AcquireUniqueFilename(read_info->filename);
756 (void) FormatMagickString(command,MaxTextExtent,
757 GetDelegateCommands(delegate_info),
758 read_info->antialias != MagickFalse ? 4 : 1,
759 read_info->antialias != MagickFalse ? 4 : 1,density,options,
760 read_info->filename,postscript_filename,input_filename);
cristyb32b90a2009-09-07 21:45:48 +0000761 status=InvokePostscriptDelegate(read_info->verbose,command,exception);
cristy41083a42009-09-07 23:47:59 +0000762 if ((status == MagickFalse) ||
cristy3ed852e2009-09-05 21:47:34 +0000763 (IsPostscriptRendered(read_info->filename) == MagickFalse))
764 {
765 (void) ConcatenateMagickString(command," -c showpage",MaxTextExtent);
cristyb32b90a2009-09-07 21:45:48 +0000766 status=InvokePostscriptDelegate(read_info->verbose,command,exception);
cristy3ed852e2009-09-05 21:47:34 +0000767 }
768 postscript_image=(Image *) NULL;
cristy41083a42009-09-07 23:47:59 +0000769 if (status != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000770 postscript_image=ReadImage(read_info,exception);
771 (void) RelinquishUniqueFileResource(postscript_filename);
772 (void) RelinquishUniqueFileResource(read_info->filename);
773 (void) RelinquishUniqueFileResource(input_filename);
774 read_info=DestroyImageInfo(read_info);
775 if (postscript_image == (Image *) NULL)
776 {
777 image=DestroyImageList(image);
778 ThrowFileException(exception,DelegateError,"PostscriptDelegateFailed",
779 image_info->filename);
780 return((Image *) NULL);
781 }
782 if (LocaleCompare(postscript_image->magick,"BMP") == 0)
783 {
784 Image
785 *cmyk_image;
786
787 cmyk_image=ConsolidateCMYKImages(postscript_image,exception);
788 if (cmyk_image != (Image *) NULL)
789 {
790 postscript_image=DestroyImageList(postscript_image);
791 postscript_image=cmyk_image;
792 }
793 }
794 if (image_info->number_scenes != 0)
795 {
796 Image
797 *clone_image;
798
cristybb503372010-05-27 20:51:26 +0000799 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000800 i;
801
802 /*
803 Add place holder images to meet the subimage specification requirement.
804 */
cristybb503372010-05-27 20:51:26 +0000805 for (i=0; i < (ssize_t) image_info->scene; i++)
cristy3ed852e2009-09-05 21:47:34 +0000806 {
807 clone_image=CloneImage(postscript_image,1,1,MagickTrue,exception);
808 if (clone_image != (Image *) NULL)
809 PrependImageToList(&postscript_image,clone_image);
810 }
811 }
812 do
813 {
814 (void) CopyMagickString(postscript_image->filename,filename,MaxTextExtent);
815 if (columns != 0)
816 postscript_image->magick_columns=columns;
817 if (rows != 0)
818 postscript_image->magick_rows=rows;
819 postscript_image->page=page;
820 (void) CloneImageProfiles(postscript_image,image);
821 (void) CloneImageProperties(postscript_image,image);
822 next=SyncNextImageInList(postscript_image);
823 if (next != (Image *) NULL)
824 postscript_image=next;
825 } while (next != (Image *) NULL);
826 image=DestroyImageList(image);
827 scene=0;
828 for (next=GetFirstImageInList(postscript_image); next != (Image *) NULL; )
829 {
830 next->scene=scene++;
831 next=GetNextImageInList(next);
832 }
833 return(GetFirstImageInList(postscript_image));
834}
835
836/*
837%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
838% %
839% %
840% %
841% R e g i s t e r P S I m a g e %
842% %
843% %
844% %
845%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
846%
847% RegisterPSImage() adds properties for the PS image format to
848% the list of supported formats. The properties include the image format
849% tag, a method to read and/or write the format, whether the format
850% supports the saving of more than one frame to the same file or blob,
851% whether the format supports native in-memory I/O, and a brief
852% description of the format.
853%
854% The format of the RegisterPSImage method is:
855%
cristybb503372010-05-27 20:51:26 +0000856% size_t RegisterPSImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000857%
858*/
cristybb503372010-05-27 20:51:26 +0000859ModuleExport size_t RegisterPSImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000860{
861 MagickInfo
862 *entry;
863
864 entry=SetMagickInfo("EPI");
865 entry->decoder=(DecodeImageHandler *) ReadPSImage;
866 entry->encoder=(EncodeImageHandler *) WritePSImage;
867 entry->magick=(IsImageFormatHandler *) IsPS;
868 entry->adjoin=MagickFalse;
869 entry->blob_support=MagickFalse;
870 entry->seekable_stream=MagickTrue;
871 entry->thread_support=EncoderThreadSupport;
872 entry->description=ConstantString(
873 "Encapsulated PostScript Interchange format");
874 entry->module=ConstantString("PS");
875 (void) RegisterMagickInfo(entry);
876 entry=SetMagickInfo("EPS");
877 entry->decoder=(DecodeImageHandler *) ReadPSImage;
878 entry->encoder=(EncodeImageHandler *) WritePSImage;
879 entry->magick=(IsImageFormatHandler *) IsPS;
880 entry->adjoin=MagickFalse;
881 entry->blob_support=MagickFalse;
882 entry->seekable_stream=MagickTrue;
883 entry->thread_support=EncoderThreadSupport;
884 entry->description=ConstantString("Encapsulated PostScript");
885 entry->module=ConstantString("PS");
886 (void) RegisterMagickInfo(entry);
887 entry=SetMagickInfo("EPSF");
888 entry->decoder=(DecodeImageHandler *) ReadPSImage;
889 entry->encoder=(EncodeImageHandler *) WritePSImage;
890 entry->magick=(IsImageFormatHandler *) IsPS;
891 entry->adjoin=MagickFalse;
892 entry->blob_support=MagickFalse;
893 entry->seekable_stream=MagickTrue;
894 entry->description=ConstantString("Encapsulated PostScript");
895 entry->module=ConstantString("PS");
896 (void) RegisterMagickInfo(entry);
897 entry=SetMagickInfo("EPSI");
898 entry->decoder=(DecodeImageHandler *) ReadPSImage;
899 entry->encoder=(EncodeImageHandler *) WritePSImage;
900 entry->magick=(IsImageFormatHandler *) IsPS;
901 entry->adjoin=MagickFalse;
902 entry->blob_support=MagickFalse;
903 entry->seekable_stream=MagickTrue;
904 entry->thread_support=EncoderThreadSupport;
905 entry->description=ConstantString(
906 "Encapsulated PostScript Interchange format");
907 entry->module=ConstantString("PS");
908 (void) RegisterMagickInfo(entry);
909 entry=SetMagickInfo("PS");
910 entry->decoder=(DecodeImageHandler *) ReadPSImage;
911 entry->encoder=(EncodeImageHandler *) WritePSImage;
912 entry->magick=(IsImageFormatHandler *) IsPS;
913 entry->module=ConstantString("PS");
914 entry->blob_support=MagickFalse;
915 entry->seekable_stream=MagickTrue;
916 entry->thread_support=EncoderThreadSupport;
917 entry->description=ConstantString("PostScript");
918 (void) RegisterMagickInfo(entry);
919 return(MagickImageCoderSignature);
920}
921
922/*
923%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
924% %
925% %
926% %
927% U n r e g i s t e r P S I m a g e %
928% %
929% %
930% %
931%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
932%
933% UnregisterPSImage() removes format registrations made by the
934% PS module from the list of supported formats.
935%
936% The format of the UnregisterPSImage method is:
937%
938% UnregisterPSImage(void)
939%
940*/
941ModuleExport void UnregisterPSImage(void)
942{
943 (void) UnregisterMagickInfo("EPI");
944 (void) UnregisterMagickInfo("EPS");
945 (void) UnregisterMagickInfo("EPSF");
946 (void) UnregisterMagickInfo("EPSI");
947 (void) UnregisterMagickInfo("PS");
948}
949
950/*
951%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
952% %
953% %
954% %
955% W r i t e P S I m a g e %
956% %
957% %
958% %
959%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
960%
961% WritePSImage translates an image to encapsulated Postscript
962% Level I for printing. If the supplied geometry is null, the image is
963% centered on the Postscript page. Otherwise, the image is positioned as
964% specified by the geometry.
965%
966% The format of the WritePSImage method is:
967%
968% MagickBooleanType WritePSImage(const ImageInfo *image_info,Image *image)
969%
970% A description of each parameter follows:
971%
972% o image_info: the image info.
973%
974% o image: the image.
975%
976*/
977
978static inline size_t MagickMin(const size_t x,const size_t y)
979{
980 if (x < y)
981 return(x);
982 return(y);
983}
984
985static inline unsigned char *PopHexPixel(const char **hex_digits,
cristybb503372010-05-27 20:51:26 +0000986 const size_t pixel,unsigned char *pixels)
cristy3ed852e2009-09-05 21:47:34 +0000987{
988 register const char
989 *hex;
990
991 hex=hex_digits[pixel];
992 *pixels++=(unsigned char) (*hex++);
993 *pixels++=(unsigned char) (*hex);
994 return(pixels);
995}
996
997static MagickBooleanType WritePSImage(const ImageInfo *image_info,Image *image)
998{
999#define WriteRunlengthPacket(image,pixel,length,p) \
1000{ \
1001 if ((image->matte != MagickFalse) && \
1002 (p->opacity == (Quantum) TransparentOpacity)) \
1003 { \
1004 q=PopHexPixel(hex_digits,0xff,q); \
1005 q=PopHexPixel(hex_digits,0xff,q); \
1006 q=PopHexPixel(hex_digits,0xff,q); \
1007 } \
1008 else \
1009 { \
1010 q=PopHexPixel(hex_digits,ScaleQuantumToChar(pixel.red),q); \
1011 q=PopHexPixel(hex_digits,ScaleQuantumToChar(pixel.green),q); \
1012 q=PopHexPixel(hex_digits,ScaleQuantumToChar(pixel.blue),q); \
1013 } \
cristybb503372010-05-27 20:51:26 +00001014 q=PopHexPixel(hex_digits,(const size_t) MagickMin(length,0xff),q); \
cristy3ed852e2009-09-05 21:47:34 +00001015}
1016
1017 static const char
1018 *hex_digits[] =
1019 {
1020 "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0A", "0B",
1021 "0C", "0D", "0E", "0F", "10", "11", "12", "13", "14", "15", "16", "17",
1022 "18", "19", "1A", "1B", "1C", "1D", "1E", "1F", "20", "21", "22", "23",
1023 "24", "25", "26", "27", "28", "29", "2A", "2B", "2C", "2D", "2E", "2F",
1024 "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3A", "3B",
1025 "3C", "3D", "3E", "3F", "40", "41", "42", "43", "44", "45", "46", "47",
1026 "48", "49", "4A", "4B", "4C", "4D", "4E", "4F", "50", "51", "52", "53",
1027 "54", "55", "56", "57", "58", "59", "5A", "5B", "5C", "5D", "5E", "5F",
1028 "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6A", "6B",
1029 "6C", "6D", "6E", "6F", "70", "71", "72", "73", "74", "75", "76", "77",
1030 "78", "79", "7A", "7B", "7C", "7D", "7E", "7F", "80", "81", "82", "83",
1031 "84", "85", "86", "87", "88", "89", "8A", "8B", "8C", "8D", "8E", "8F",
1032 "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9A", "9B",
1033 "9C", "9D", "9E", "9F", "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7",
1034 "A8", "A9", "AA", "AB", "AC", "AD", "AE", "AF", "B0", "B1", "B2", "B3",
1035 "B4", "B5", "B6", "B7", "B8", "B9", "BA", "BB", "BC", "BD", "BE", "BF",
1036 "C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "CA", "CB",
1037 "CC", "CD", "CE", "CF", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
1038 "D8", "D9", "DA", "DB", "DC", "DD", "DE", "DF", "E0", "E1", "E2", "E3",
1039 "E4", "E5", "E6", "E7", "E8", "E9", "EA", "EB", "EC", "ED", "EE", "EF",
1040 "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "FA", "FB",
1041 "FC", "FD", "FE", "FF", (char *) NULL
1042 },
1043 *PostscriptProlog[]=
1044 {
1045 "%%BeginProlog",
1046 "%",
1047 "% Display a color image. The image is displayed in color on",
1048 "% Postscript viewers or printers that support color, otherwise",
1049 "% it is displayed as grayscale.",
1050 "%",
1051 "/DirectClassPacket",
1052 "{",
1053 " %",
1054 " % Get a DirectClass packet.",
1055 " %",
1056 " % Parameters:",
1057 " % red.",
1058 " % green.",
1059 " % blue.",
1060 " % length: number of pixels minus one of this color (optional).",
1061 " %",
1062 " currentfile color_packet readhexstring pop pop",
1063 " compression 0 eq",
1064 " {",
1065 " /number_pixels 3 def",
1066 " }",
1067 " {",
1068 " currentfile byte readhexstring pop 0 get",
1069 " /number_pixels exch 1 add 3 mul def",
1070 " } ifelse",
1071 " 0 3 number_pixels 1 sub",
1072 " {",
1073 " pixels exch color_packet putinterval",
1074 " } for",
1075 " pixels 0 number_pixels getinterval",
1076 "} bind def",
1077 "",
1078 "/DirectClassImage",
1079 "{",
1080 " %",
1081 " % Display a DirectClass image.",
1082 " %",
1083 " systemdict /colorimage known",
1084 " {",
1085 " columns rows 8",
1086 " [",
1087 " columns 0 0",
1088 " rows neg 0 rows",
1089 " ]",
1090 " { DirectClassPacket } false 3 colorimage",
1091 " }",
1092 " {",
1093 " %",
1094 " % No colorimage operator; convert to grayscale.",
1095 " %",
1096 " columns rows 8",
1097 " [",
1098 " columns 0 0",
1099 " rows neg 0 rows",
1100 " ]",
1101 " { GrayDirectClassPacket } image",
1102 " } ifelse",
1103 "} bind def",
1104 "",
1105 "/GrayDirectClassPacket",
1106 "{",
1107 " %",
1108 " % Get a DirectClass packet; convert to grayscale.",
1109 " %",
1110 " % Parameters:",
1111 " % red",
1112 " % green",
1113 " % blue",
1114 " % length: number of pixels minus one of this color (optional).",
1115 " %",
1116 " currentfile color_packet readhexstring pop pop",
1117 " color_packet 0 get 0.299 mul",
1118 " color_packet 1 get 0.587 mul add",
1119 " color_packet 2 get 0.114 mul add",
1120 " cvi",
1121 " /gray_packet exch def",
1122 " compression 0 eq",
1123 " {",
1124 " /number_pixels 1 def",
1125 " }",
1126 " {",
1127 " currentfile byte readhexstring pop 0 get",
1128 " /number_pixels exch 1 add def",
1129 " } ifelse",
1130 " 0 1 number_pixels 1 sub",
1131 " {",
1132 " pixels exch gray_packet put",
1133 " } for",
1134 " pixels 0 number_pixels getinterval",
1135 "} bind def",
1136 "",
1137 "/GrayPseudoClassPacket",
1138 "{",
1139 " %",
1140 " % Get a PseudoClass packet; convert to grayscale.",
1141 " %",
1142 " % Parameters:",
1143 " % index: index into the colormap.",
1144 " % length: number of pixels minus one of this color (optional).",
1145 " %",
1146 " currentfile byte readhexstring pop 0 get",
1147 " /offset exch 3 mul def",
1148 " /color_packet colormap offset 3 getinterval def",
1149 " color_packet 0 get 0.299 mul",
1150 " color_packet 1 get 0.587 mul add",
1151 " color_packet 2 get 0.114 mul add",
1152 " cvi",
1153 " /gray_packet exch def",
1154 " compression 0 eq",
1155 " {",
1156 " /number_pixels 1 def",
1157 " }",
1158 " {",
1159 " currentfile byte readhexstring pop 0 get",
1160 " /number_pixels exch 1 add def",
1161 " } ifelse",
1162 " 0 1 number_pixels 1 sub",
1163 " {",
1164 " pixels exch gray_packet put",
1165 " } for",
1166 " pixels 0 number_pixels getinterval",
1167 "} bind def",
1168 "",
1169 "/PseudoClassPacket",
1170 "{",
1171 " %",
1172 " % Get a PseudoClass packet.",
1173 " %",
1174 " % Parameters:",
1175 " % index: index into the colormap.",
1176 " % length: number of pixels minus one of this color (optional).",
1177 " %",
1178 " currentfile byte readhexstring pop 0 get",
1179 " /offset exch 3 mul def",
1180 " /color_packet colormap offset 3 getinterval def",
1181 " compression 0 eq",
1182 " {",
1183 " /number_pixels 3 def",
1184 " }",
1185 " {",
1186 " currentfile byte readhexstring pop 0 get",
1187 " /number_pixels exch 1 add 3 mul def",
1188 " } ifelse",
1189 " 0 3 number_pixels 1 sub",
1190 " {",
1191 " pixels exch color_packet putinterval",
1192 " } for",
1193 " pixels 0 number_pixels getinterval",
1194 "} bind def",
1195 "",
1196 "/PseudoClassImage",
1197 "{",
1198 " %",
1199 " % Display a PseudoClass image.",
1200 " %",
1201 " % Parameters:",
1202 " % class: 0-PseudoClass or 1-Grayscale.",
1203 " %",
1204 " currentfile buffer readline pop",
1205 " token pop /class exch def pop",
1206 " class 0 gt",
1207 " {",
1208 " currentfile buffer readline pop",
1209 " token pop /depth exch def pop",
1210 " /grays columns 8 add depth sub depth mul 8 idiv string def",
1211 " columns rows depth",
1212 " [",
1213 " columns 0 0",
1214 " rows neg 0 rows",
1215 " ]",
1216 " { currentfile grays readhexstring pop } image",
1217 " }",
1218 " {",
1219 " %",
1220 " % Parameters:",
1221 " % colors: number of colors in the colormap.",
1222 " % colormap: red, green, blue color packets.",
1223 " %",
1224 " currentfile buffer readline pop",
1225 " token pop /colors exch def pop",
1226 " /colors colors 3 mul def",
1227 " /colormap colors string def",
1228 " currentfile colormap readhexstring pop pop",
1229 " systemdict /colorimage known",
1230 " {",
1231 " columns rows 8",
1232 " [",
1233 " columns 0 0",
1234 " rows neg 0 rows",
1235 " ]",
1236 " { PseudoClassPacket } false 3 colorimage",
1237 " }",
1238 " {",
1239 " %",
1240 " % No colorimage operator; convert to grayscale.",
1241 " %",
1242 " columns rows 8",
1243 " [",
1244 " columns 0 0",
1245 " rows neg 0 rows",
1246 " ]",
1247 " { GrayPseudoClassPacket } image",
1248 " } ifelse",
1249 " } ifelse",
1250 "} bind def",
1251 "",
1252 "/DisplayImage",
1253 "{",
1254 " %",
1255 " % Display a DirectClass or PseudoClass image.",
1256 " %",
1257 " % Parameters:",
1258 " % x & y translation.",
1259 " % x & y scale.",
1260 " % label pointsize.",
1261 " % image label.",
1262 " % image columns & rows.",
1263 " % class: 0-DirectClass or 1-PseudoClass.",
1264 " % compression: 0-none or 1-RunlengthEncoded.",
1265 " % hex color packets.",
1266 " %",
1267 " gsave",
1268 " /buffer 512 string def",
1269 " /byte 1 string def",
1270 " /color_packet 3 string def",
1271 " /pixels 768 string def",
1272 "",
1273 " currentfile buffer readline pop",
1274 " token pop /x exch def",
1275 " token pop /y exch def pop",
1276 " x y translate",
1277 " currentfile buffer readline pop",
1278 " token pop /x exch def",
1279 " token pop /y exch def pop",
1280 " currentfile buffer readline pop",
1281 " token pop /pointsize exch def pop",
1282 " /Times-Roman findfont pointsize scalefont setfont",
1283 (char *) NULL
1284 },
1285 *PostscriptEpilog[]=
1286 {
1287 " x y scale",
1288 " currentfile buffer readline pop",
1289 " token pop /columns exch def",
1290 " token pop /rows exch def pop",
1291 " currentfile buffer readline pop",
1292 " token pop /class exch def pop",
1293 " currentfile buffer readline pop",
1294 " token pop /compression exch def pop",
1295 " class 0 gt { PseudoClassImage } { DirectClassImage } ifelse",
1296 (char *) NULL
1297 };
1298
1299 char
1300 buffer[MaxTextExtent],
1301 date[MaxTextExtent],
1302 **labels,
1303 page_geometry[MaxTextExtent];
1304
1305 const char
1306 **s,
1307 *value;
1308
1309 const StringInfo
1310 *profile;
1311
1312 double
1313 pointsize;
1314
1315 GeometryInfo
1316 geometry_info;
1317
1318 IndexPacket
1319 index;
1320
cristybb503372010-05-27 20:51:26 +00001321 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001322 j,
1323 y;
1324
1325 MagickBooleanType
1326 status;
1327
1328 MagickOffsetType
1329 scene;
1330
1331 MagickStatusType
1332 flags;
1333
1334 PixelPacket
1335 pixel;
1336
1337 PointInfo
1338 delta,
1339 resolution,
1340 scale;
1341
1342 RectangleInfo
1343 geometry,
1344 media_info,
1345 page_info;
1346
1347 register const IndexPacket
1348 *indexes;
1349
1350 register const PixelPacket
1351 *p;
1352
cristybb503372010-05-27 20:51:26 +00001353 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001354 i,
1355 x;
1356
1357 register unsigned char
1358 *q;
1359
1360 SegmentInfo
1361 bounds;
1362
1363 size_t
1364 length;
1365
1366 time_t
1367 timer;
1368
1369 unsigned char
1370 pixels[2048];
1371
cristybb503372010-05-27 20:51:26 +00001372 size_t
cristy3ed852e2009-09-05 21:47:34 +00001373 bit,
1374 byte,
1375 page,
1376 text_size;
1377
1378 /*
1379 Open output image file.
1380 */
1381 assert(image_info != (const ImageInfo *) NULL);
1382 assert(image_info->signature == MagickSignature);
1383 assert(image != (Image *) NULL);
1384 assert(image->signature == MagickSignature);
1385 if (image->debug != MagickFalse)
1386 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1387 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
1388 if (status == MagickFalse)
1389 return(status);
1390 (void) ResetMagickMemory(&bounds,0,sizeof(bounds));
1391 page=1;
1392 scene=0;
1393 do
1394 {
1395 /*
1396 Scale relative to dots-per-inch.
1397 */
1398 if ((image->colorspace != RGBColorspace) &&
1399 (image->colorspace != CMYKColorspace))
1400 (void) TransformImageColorspace(image,RGBColorspace);
1401 delta.x=DefaultResolution;
1402 delta.y=DefaultResolution;
1403 resolution.x=image->x_resolution;
1404 resolution.y=image->y_resolution;
1405 if ((resolution.x == 0.0) || (resolution.y == 0.0))
1406 {
1407 flags=ParseGeometry(PSDensityGeometry,&geometry_info);
1408 resolution.x=geometry_info.rho;
1409 resolution.y=geometry_info.sigma;
1410 if ((flags & SigmaValue) == 0)
1411 resolution.y=resolution.x;
1412 }
1413 if (image_info->density != (char *) NULL)
1414 {
1415 flags=ParseGeometry(image_info->density,&geometry_info);
1416 resolution.x=geometry_info.rho;
1417 resolution.y=geometry_info.sigma;
1418 if ((flags & SigmaValue) == 0)
1419 resolution.y=resolution.x;
1420 }
1421 if (image->units == PixelsPerCentimeterResolution)
1422 {
cristybb503372010-05-27 20:51:26 +00001423 resolution.x=(size_t) (100.0*2.54*resolution.x+0.5)/100.0;
1424 resolution.y=(size_t) (100.0*2.54*resolution.y+0.5)/100.0;
cristy3ed852e2009-09-05 21:47:34 +00001425 }
1426 SetGeometry(image,&geometry);
cristye8c25f92010-06-03 00:53:06 +00001427 (void) FormatMagickString(page_geometry,MaxTextExtent,"%.20gx%.20g",
1428 (double) image->columns,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001429 if (image_info->page != (char *) NULL)
1430 (void) CopyMagickString(page_geometry,image_info->page,MaxTextExtent);
1431 else
1432 if ((image->page.width != 0) && (image->page.height != 0))
cristye8c25f92010-06-03 00:53:06 +00001433 (void) FormatMagickString(page_geometry,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +00001434 "%.20gx%.20g%+.20g%+.20g",(double) image->page.width,(double)
cristye8c25f92010-06-03 00:53:06 +00001435 image->page.height,(double) image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00001436 else
1437 if ((image->gravity != UndefinedGravity) &&
1438 (LocaleCompare(image_info->magick,"PS") == 0))
1439 (void) CopyMagickString(page_geometry,PSPageGeometry,MaxTextExtent);
1440 (void) ConcatenateMagickString(page_geometry,">",MaxTextExtent);
1441 (void) ParseMetaGeometry(page_geometry,&geometry.x,&geometry.y,
1442 &geometry.width,&geometry.height);
1443 scale.x=(double) (geometry.width*delta.x)/resolution.x;
cristybb503372010-05-27 20:51:26 +00001444 geometry.width=(size_t) floor(scale.x+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001445 scale.y=(double) (geometry.height*delta.y)/resolution.y;
cristybb503372010-05-27 20:51:26 +00001446 geometry.height=(size_t) floor(scale.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001447 (void) ParseAbsoluteGeometry(page_geometry,&media_info);
1448 (void) ParseGravityGeometry(image,page_geometry,&page_info,
1449 &image->exception);
1450 if (image->gravity != UndefinedGravity)
1451 {
1452 geometry.x=(-page_info.x);
cristybb503372010-05-27 20:51:26 +00001453 geometry.y=(ssize_t) (media_info.height+page_info.y-image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001454 }
1455 pointsize=12.0;
1456 if (image_info->pointsize != 0.0)
1457 pointsize=image_info->pointsize;
1458 text_size=0;
1459 value=GetImageProperty(image,"label");
1460 if (value != (const char *) NULL)
cristybb503372010-05-27 20:51:26 +00001461 text_size=(size_t) (MultilineCensus(value)*pointsize+12);
cristy3ed852e2009-09-05 21:47:34 +00001462 if (page == 1)
1463 {
1464 /*
1465 Output Postscript header.
1466 */
1467 if (LocaleCompare(image_info->magick,"PS") == 0)
1468 (void) CopyMagickString(buffer,"%!PS-Adobe-3.0\n",MaxTextExtent);
1469 else
1470 (void) CopyMagickString(buffer,"%!PS-Adobe-3.0 EPSF-3.0\n",
1471 MaxTextExtent);
1472 (void) WriteBlobString(image,buffer);
1473 (void) WriteBlobString(image,"%%Creator: (ImageMagick)\n");
1474 (void) FormatMagickString(buffer,MaxTextExtent,"%%%%Title: (%s)\n",
1475 image->filename);
1476 (void) WriteBlobString(image,buffer);
1477 timer=time((time_t *) NULL);
1478 (void) FormatMagickTime(timer,MaxTextExtent,date);
1479 (void) FormatMagickString(buffer,MaxTextExtent,
1480 "%%%%CreationDate: (%s)\n",date);
1481 (void) WriteBlobString(image,buffer);
1482 bounds.x1=(double) geometry.x;
1483 bounds.y1=(double) geometry.y;
1484 bounds.x2=(double) geometry.x+scale.x;
1485 bounds.y2=(double) geometry.y+(geometry.height+text_size);
1486 if ((image_info->adjoin != MagickFalse) &&
1487 (GetNextImageInList(image) != (Image *) NULL))
1488 (void) CopyMagickString(buffer,"%%%%BoundingBox: (atend)\n",
1489 MaxTextExtent);
1490 else
1491 {
1492 (void) FormatMagickString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001493 "%%%%BoundingBox: %.20g %.20g %.20g %.20g\n",ceil(bounds.x1-0.5),
1494 ceil(bounds.y1-0.5),floor(bounds.x2+0.5),floor(bounds.y2+0.5));
cristy3ed852e2009-09-05 21:47:34 +00001495 (void) WriteBlobString(image,buffer);
1496 (void) FormatMagickString(buffer,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001497 "%%%%HiResBoundingBox: %g %g %g %g\n",bounds.x1,
cristy8cd5b312010-01-07 01:10:24 +00001498 bounds.y1,bounds.x2,bounds.y2);
cristy3ed852e2009-09-05 21:47:34 +00001499 }
1500 (void) WriteBlobString(image,buffer);
1501 profile=GetImageProfile(image,"8bim");
1502 if (profile != (StringInfo *) NULL)
1503 {
1504 /*
1505 Embed Photoshop profile.
1506 */
1507 (void) FormatMagickString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001508 "%%BeginPhotoshop: %.20g",(double) GetStringInfoLength(profile));
cristy3ed852e2009-09-05 21:47:34 +00001509 (void) WriteBlobString(image,buffer);
cristybb503372010-05-27 20:51:26 +00001510 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
cristy3ed852e2009-09-05 21:47:34 +00001511 {
1512 if ((i % 32) == 0)
1513 (void) WriteBlobString(image,"\n% ");
1514 (void) FormatMagickString(buffer,MaxTextExtent,"%02X",
1515 (unsigned int) (GetStringInfoDatum(profile)[i] & 0xff));
1516 (void) WriteBlobString(image,buffer);
1517 }
1518 (void) WriteBlobString(image,"\n%EndPhotoshop\n");
1519 }
1520 profile=GetImageProfile(image,"xmp");
cristy2b2eb912010-03-03 14:56:40 +00001521 if (0 && (profile != (StringInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001522 {
1523 /*
1524 Embed XML profile.
1525 */
1526 (void) WriteBlobString(image,"\n%begin_xml_code\n");
cristy2b2eb912010-03-03 14:56:40 +00001527 (void) FormatMagickString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001528 "\n%%begin_xml_packet: %.20g\n",(double)
cristyeec18db2010-03-03 21:15:45 +00001529 GetStringInfoLength(profile));
cristy2b2eb912010-03-03 14:56:40 +00001530 (void) WriteBlobString(image,buffer);
cristybb503372010-05-27 20:51:26 +00001531 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
cristy3ed852e2009-09-05 21:47:34 +00001532 (void) WriteBlobByte(image,GetStringInfoDatum(profile)[i]);
cristy2b2eb912010-03-03 14:56:40 +00001533 (void) WriteBlobString(image,"\n%end_xml_packet\n%end_xml_code\n");
cristy3ed852e2009-09-05 21:47:34 +00001534 }
1535 value=GetImageProperty(image,"label");
1536 if (value != (const char *) NULL)
1537 (void) WriteBlobString(image,
1538 "%%DocumentNeededResources: font Times-Roman\n");
1539 (void) WriteBlobString(image,"%%DocumentData: Clean7Bit\n");
1540 (void) WriteBlobString(image,"%%LanguageLevel: 1\n");
1541 if (LocaleCompare(image_info->magick,"PS") != 0)
1542 (void) WriteBlobString(image,"%%Pages: 1\n");
1543 else
1544 {
1545 /*
1546 Compute the number of pages.
1547 */
1548 (void) WriteBlobString(image,"%%Orientation: Portrait\n");
1549 (void) WriteBlobString(image,"%%PageOrder: Ascend\n");
cristye8c25f92010-06-03 00:53:06 +00001550 (void) FormatMagickString(buffer,MaxTextExtent,"%%%%Pages: %.20g\n",
1551 image_info->adjoin != MagickFalse ? (double)
1552 GetImageListLength(image) : 1.0);
cristy3ed852e2009-09-05 21:47:34 +00001553 (void) WriteBlobString(image,buffer);
1554 }
1555 (void) WriteBlobString(image,"%%EndComments\n");
1556 (void) WriteBlobString(image,"\n%%BeginDefaults\n");
1557 (void) WriteBlobString(image,"%%EndDefaults\n\n");
1558 if ((LocaleCompare(image_info->magick,"EPI") == 0) ||
1559 (LocaleCompare(image_info->magick,"EPSI") == 0) ||
1560 (LocaleCompare(image_info->magick,"EPT") == 0))
1561 {
1562 Image
1563 *preview_image;
1564
cristybb503372010-05-27 20:51:26 +00001565 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001566 y;
1567
1568 Quantum
1569 pixel;
1570
cristybb503372010-05-27 20:51:26 +00001571 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001572 x;
1573
1574 /*
1575 Create preview image.
1576 */
1577 preview_image=CloneImage(image,0,0,MagickTrue,&image->exception);
1578 if (preview_image == (Image *) NULL)
1579 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1580 /*
1581 Dump image as bitmap.
1582 */
1583 (void) FormatMagickString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001584 "%%%%BeginPreview: %.20g %.20g %.20g %.20g\n%% ",(double)
1585 preview_image->columns,(double) preview_image->rows,1.0,
1586 (double) ((((preview_image->columns+7) >> 3)*preview_image->rows+
1587 35)/36));
cristy3ed852e2009-09-05 21:47:34 +00001588 (void) WriteBlobString(image,buffer);
1589 q=pixels;
cristybb503372010-05-27 20:51:26 +00001590 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001591 {
1592 p=GetVirtualPixels(preview_image,0,y,preview_image->columns,1,
1593 &preview_image->exception);
1594 if (p == (const PixelPacket *) NULL)
1595 break;
1596 indexes=GetVirtualIndexQueue(preview_image);
1597 bit=0;
1598 byte=0;
cristybb503372010-05-27 20:51:26 +00001599 for (x=0; x < (ssize_t) preview_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001600 {
1601 byte<<=1;
1602 pixel=PixelIntensityToQuantum(p);
1603 if (pixel >= (Quantum) (QuantumRange/2))
1604 byte|=0x01;
1605 bit++;
1606 if (bit == 8)
1607 {
1608 q=PopHexPixel(hex_digits,byte,q);
1609 if ((q-pixels+8) >= 80)
1610 {
1611 *q++='\n';
1612 (void) WriteBlob(image,q-pixels,pixels);
1613 q=pixels;
1614 (void) WriteBlobString(image,"% ");
1615 };
1616 bit=0;
1617 byte=0;
1618 }
1619 }
1620 if (bit != 0)
1621 {
1622 byte<<=(8-bit);
1623 q=PopHexPixel(hex_digits,byte,q);
1624 if ((q-pixels+8) >= 80)
1625 {
1626 *q++='\n';
1627 (void) WriteBlob(image,q-pixels,pixels);
1628 q=pixels;
1629 (void) WriteBlobString(image,"% ");
1630 };
1631 };
1632 }
1633 if (q != pixels)
1634 {
1635 *q++='\n';
1636 (void) WriteBlob(image,q-pixels,pixels);
1637 }
1638 (void) WriteBlobString(image,"\n%%EndPreview\n");
1639 preview_image=DestroyImage(preview_image);
1640 }
1641 /*
1642 Output Postscript commands.
1643 */
1644 for (s=PostscriptProlog; *s != (char *) NULL; s++)
1645 {
1646 (void) FormatMagickString(buffer,MaxTextExtent,"%s\n",*s);
1647 (void) WriteBlobString(image,buffer);
1648 }
1649 value=GetImageProperty(image,"label");
1650 if (value != (const char *) NULL)
cristybb503372010-05-27 20:51:26 +00001651 for (j=(ssize_t) MultilineCensus(value)-1; j >= 0; j--)
cristy3ed852e2009-09-05 21:47:34 +00001652 {
1653 (void) WriteBlobString(image," /label 512 string def\n");
1654 (void) WriteBlobString(image," currentfile label readline pop\n");
1655 (void) FormatMagickString(buffer,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001656 " 0 y %g add moveto label show pop\n",j*pointsize+12);
cristy3ed852e2009-09-05 21:47:34 +00001657 (void) WriteBlobString(image,buffer);
1658 }
1659 for (s=PostscriptEpilog; *s != (char *) NULL; s++)
1660 {
1661 (void) FormatMagickString(buffer,MaxTextExtent,"%s\n",*s);
1662 (void) WriteBlobString(image,buffer);
1663 }
1664 if (LocaleCompare(image_info->magick,"PS") == 0)
1665 (void) WriteBlobString(image," showpage\n");
1666 (void) WriteBlobString(image,"} bind def\n");
1667 (void) WriteBlobString(image,"%%EndProlog\n");
1668 }
cristye8c25f92010-06-03 00:53:06 +00001669 (void) FormatMagickString(buffer,MaxTextExtent,"%%%%Page: 1 %.20g\n",
1670 (double) (page++));
cristy3ed852e2009-09-05 21:47:34 +00001671 (void) WriteBlobString(image,buffer);
1672 (void) FormatMagickString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001673 "%%%%PageBoundingBox: %.20g %.20g %.20g %.20g\n",(double) geometry.x,
1674 (double) geometry.y,geometry.x+(double) geometry.width,geometry.y+(double)
cristyf2faecf2010-05-28 19:19:36 +00001675 (geometry.height+text_size));
cristy3ed852e2009-09-05 21:47:34 +00001676 (void) WriteBlobString(image,buffer);
1677 if ((double) geometry.x < bounds.x1)
1678 bounds.x1=(double) geometry.x;
1679 if ((double) geometry.y < bounds.y1)
1680 bounds.y1=(double) geometry.y;
1681 if ((double) (geometry.x+geometry.width-1) > bounds.x2)
1682 bounds.x2=(double) geometry.x+geometry.width-1;
1683 if ((double) (geometry.y+(geometry.height+text_size)-1) > bounds.y2)
1684 bounds.y2=(double) geometry.y+(geometry.height+text_size)-1;
1685 value=GetImageProperty(image,"label");
1686 if (value != (const char *) NULL)
1687 (void) WriteBlobString(image,"%%%%PageResources: font Times-Roman\n");
1688 if (LocaleCompare(image_info->magick,"PS") != 0)
1689 (void) WriteBlobString(image,"userdict begin\n");
1690 (void) WriteBlobString(image,"DisplayImage\n");
1691 /*
1692 Output image data.
1693 */
cristye8c25f92010-06-03 00:53:06 +00001694 (void) FormatMagickString(buffer,MaxTextExtent,"%.20g %.20g\n%g %g\n%g\n",
1695 (double) geometry.x,(double) geometry.y,scale.x,scale.y,pointsize);
cristy3ed852e2009-09-05 21:47:34 +00001696 (void) WriteBlobString(image,buffer);
1697 labels=(char **) NULL;
1698 value=GetImageProperty(image,"label");
1699 if (value != (const char *) NULL)
1700 labels=StringToList(value);
1701 if (labels != (char **) NULL)
1702 {
1703 for (i=0; labels[i] != (char *) NULL; i++)
1704 {
1705 (void) FormatMagickString(buffer,MaxTextExtent,"%s \n",
1706 labels[i]);
1707 (void) WriteBlobString(image,buffer);
1708 labels[i]=DestroyString(labels[i]);
1709 }
1710 labels=(char **) RelinquishMagickMemory(labels);
1711 }
1712 (void) ResetMagickMemory(&pixel,0,sizeof(pixel));
1713 pixel.opacity=(Quantum) TransparentOpacity;
1714 index=(IndexPacket) 0;
1715 x=0;
1716 if ((image_info->type != TrueColorType) &&
1717 (IsGrayImage(image,&image->exception) != MagickFalse))
1718 {
1719 if (IsMonochromeImage(image,&image->exception) == MagickFalse)
1720 {
1721 Quantum
1722 pixel;
1723
1724 /*
1725 Dump image as grayscale.
1726 */
1727 (void) FormatMagickString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001728 "%.20g %.20g\n1\n1\n1\n8\n",(double) image->columns,(double)
1729 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001730 (void) WriteBlobString(image,buffer);
1731 q=pixels;
cristybb503372010-05-27 20:51:26 +00001732 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001733 {
1734 p=GetVirtualPixels(image,0,y,image->columns,1,
1735 &image->exception);
1736 if (p == (const PixelPacket *) NULL)
1737 break;
cristybb503372010-05-27 20:51:26 +00001738 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001739 {
1740 pixel=ScaleQuantumToChar(PixelIntensityToQuantum(p));
1741 q=PopHexPixel(hex_digits,pixel,q);
1742 i++;
1743 if ((q-pixels+8) >= 80)
1744 {
1745 *q++='\n';
1746 (void) WriteBlob(image,q-pixels,pixels);
1747 q=pixels;
1748 }
1749 p++;
1750 }
1751 if (image->previous == (Image *) NULL)
1752 {
cristycee97112010-05-28 00:44:52 +00001753 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1754 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001755 if (status == MagickFalse)
1756 break;
1757 }
1758 }
1759 if (q != pixels)
1760 {
1761 *q++='\n';
1762 (void) WriteBlob(image,q-pixels,pixels);
1763 }
1764 }
1765 else
1766 {
cristybb503372010-05-27 20:51:26 +00001767 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001768 y;
1769
1770 Quantum
1771 pixel;
1772
1773 /*
1774 Dump image as bitmap.
1775 */
1776 (void) FormatMagickString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001777 "%.20g %.20g\n1\n1\n1\n1\n",(double) image->columns,(double)
1778 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001779 (void) WriteBlobString(image,buffer);
1780 q=pixels;
cristybb503372010-05-27 20:51:26 +00001781 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001782 {
1783 p=GetVirtualPixels(image,0,y,image->columns,1,
1784 &image->exception);
1785 if (p == (const PixelPacket *) NULL)
1786 break;
1787 indexes=GetVirtualIndexQueue(image);
1788 bit=0;
1789 byte=0;
cristybb503372010-05-27 20:51:26 +00001790 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001791 {
1792 byte<<=1;
1793 pixel=PixelIntensityToQuantum(p);
1794 if (pixel >= (Quantum) (QuantumRange/2))
1795 byte|=0x01;
1796 bit++;
1797 if (bit == 8)
1798 {
1799 q=PopHexPixel(hex_digits,byte,q);
1800 if ((q-pixels+2) >= 80)
1801 {
1802 *q++='\n';
1803 (void) WriteBlob(image,q-pixels,pixels);
1804 q=pixels;
1805 };
1806 bit=0;
1807 byte=0;
1808 }
1809 p++;
1810 }
1811 if (bit != 0)
1812 {
1813 byte<<=(8-bit);
1814 q=PopHexPixel(hex_digits,byte,q);
1815 if ((q-pixels+2) >= 80)
1816 {
1817 *q++='\n';
1818 (void) WriteBlob(image,q-pixels,pixels);
1819 q=pixels;
1820 }
1821 };
1822 if (image->previous == (Image *) NULL)
1823 {
cristycee97112010-05-28 00:44:52 +00001824 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1825 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001826 if (status == MagickFalse)
1827 break;
1828 }
1829 }
1830 if (q != pixels)
1831 {
1832 *q++='\n';
1833 (void) WriteBlob(image,q-pixels,pixels);
1834 }
1835 }
1836 }
1837 else
1838 if ((image->storage_class == DirectClass) ||
1839 (image->colors > 256) || (image->matte != MagickFalse))
1840 {
1841 /*
1842 Dump DirectClass image.
1843 */
cristye8c25f92010-06-03 00:53:06 +00001844 (void) FormatMagickString(buffer,MaxTextExtent,"%.20g %.20g\n0\n%d\n",
1845 (double) image->columns,(double) image->rows,
cristy3ed852e2009-09-05 21:47:34 +00001846 image_info->compression == RLECompression ? 1 : 0);
1847 (void) WriteBlobString(image,buffer);
1848 switch (image_info->compression)
1849 {
1850 case RLECompression:
1851 {
1852 /*
1853 Dump runlength-encoded DirectColor packets.
1854 */
1855 q=pixels;
cristybb503372010-05-27 20:51:26 +00001856 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001857 {
1858 p=GetVirtualPixels(image,0,y,image->columns,1,
1859 &image->exception);
1860 if (p == (const PixelPacket *) NULL)
1861 break;
1862 pixel=(*p);
1863 length=255;
cristybb503372010-05-27 20:51:26 +00001864 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001865 {
1866 if ((p->red == pixel.red) && (p->green == pixel.green) &&
1867 (p->blue == pixel.blue) &&
1868 (p->opacity == pixel.opacity) && (length < 255) &&
cristybb503372010-05-27 20:51:26 +00001869 (x < (ssize_t) (image->columns-1)))
cristy3ed852e2009-09-05 21:47:34 +00001870 length++;
1871 else
1872 {
1873 if (x > 0)
1874 {
1875 WriteRunlengthPacket(image,pixel,length,p);
1876 if ((q-pixels+10) >= 80)
1877 {
1878 *q++='\n';
1879 (void) WriteBlob(image,q-pixels,pixels);
1880 q=pixels;
1881 }
1882 }
1883 length=0;
1884 }
1885 pixel=(*p);
1886 p++;
1887 }
1888 WriteRunlengthPacket(image,pixel,length,p);
1889 if ((q-pixels+10) >= 80)
1890 {
1891 *q++='\n';
1892 (void) WriteBlob(image,q-pixels,pixels);
1893 q=pixels;
1894 }
1895 if (image->previous == (Image *) NULL)
1896 {
cristycee97112010-05-28 00:44:52 +00001897 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1898 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001899 if (status == MagickFalse)
1900 break;
1901 }
1902 }
1903 if (q != pixels)
1904 {
1905 *q++='\n';
1906 (void) WriteBlob(image,q-pixels,pixels);
1907 }
1908 break;
1909 }
1910 case NoCompression:
1911 default:
1912 {
1913 /*
1914 Dump uncompressed DirectColor packets.
1915 */
1916 q=pixels;
cristybb503372010-05-27 20:51:26 +00001917 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001918 {
1919 p=GetVirtualPixels(image,0,y,image->columns,1,
1920 &image->exception);
1921 if (p == (const PixelPacket *) NULL)
1922 break;
cristybb503372010-05-27 20:51:26 +00001923 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001924 {
1925 if ((image->matte != MagickFalse) &&
1926 (p->opacity == (Quantum) TransparentOpacity))
1927 {
1928 q=PopHexPixel(hex_digits,0xff,q);
1929 q=PopHexPixel(hex_digits,0xff,q);
1930 q=PopHexPixel(hex_digits,0xff,q);
1931 }
1932 else
1933 {
cristyce70c172010-01-07 17:15:30 +00001934 q=PopHexPixel(hex_digits,ScaleQuantumToChar(GetRedPixelComponent(p)),q);
1935 q=PopHexPixel(hex_digits,ScaleQuantumToChar(GetGreenPixelComponent(p)),q);
1936 q=PopHexPixel(hex_digits,ScaleQuantumToChar(GetBluePixelComponent(p)),q);
cristy3ed852e2009-09-05 21:47:34 +00001937 }
1938 if ((q-pixels+6) >= 80)
1939 {
1940 *q++='\n';
1941 (void) WriteBlob(image,q-pixels,pixels);
1942 q=pixels;
1943 }
1944 p++;
1945 }
1946 if (image->previous == (Image *) NULL)
1947 {
cristycee97112010-05-28 00:44:52 +00001948 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1949 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001950 if (status == MagickFalse)
1951 break;
1952 }
1953 }
1954 if (q != pixels)
1955 {
1956 *q++='\n';
1957 (void) WriteBlob(image,q-pixels,pixels);
1958 }
1959 break;
1960 }
1961 }
1962 (void) WriteBlobByte(image,'\n');
1963 }
1964 else
1965 {
1966 /*
1967 Dump PseudoClass image.
1968 */
cristye8c25f92010-06-03 00:53:06 +00001969 (void) FormatMagickString(buffer,MaxTextExtent,
1970 "%.20g %.20g\n%d\n%d\n0\n",(double) image->columns,(double)
1971 image->rows,image->storage_class == PseudoClass ? 1 : 0,
cristy3ed852e2009-09-05 21:47:34 +00001972 image_info->compression == RLECompression ? 1 : 0);
1973 (void) WriteBlobString(image,buffer);
1974 /*
1975 Dump number of colors and colormap.
1976 */
cristye8c25f92010-06-03 00:53:06 +00001977 (void) FormatMagickString(buffer,MaxTextExtent,"%.20g\n",(double)
cristyf2faecf2010-05-28 19:19:36 +00001978 image->colors);
cristy3ed852e2009-09-05 21:47:34 +00001979 (void) WriteBlobString(image,buffer);
cristybb503372010-05-27 20:51:26 +00001980 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001981 {
1982 (void) FormatMagickString(buffer,MaxTextExtent,"%02X%02X%02X\n",
1983 ScaleQuantumToChar(image->colormap[i].red),
1984 ScaleQuantumToChar(image->colormap[i].green),
1985 ScaleQuantumToChar(image->colormap[i].blue));
1986 (void) WriteBlobString(image,buffer);
1987 }
1988 switch (image_info->compression)
1989 {
1990 case RLECompression:
1991 {
1992 /*
1993 Dump runlength-encoded PseudoColor packets.
1994 */
1995 q=pixels;
cristybb503372010-05-27 20:51:26 +00001996 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001997 {
1998 p=GetVirtualPixels(image,0,y,image->columns,1,
1999 &image->exception);
2000 if (p == (const PixelPacket *) NULL)
2001 break;
2002 indexes=GetVirtualIndexQueue(image);
2003 index=(*indexes);
2004 length=255;
cristybb503372010-05-27 20:51:26 +00002005 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002006 {
2007 if ((index == indexes[x]) && (length < 255) &&
cristybb503372010-05-27 20:51:26 +00002008 (x < ((ssize_t) image->columns-1)))
cristy3ed852e2009-09-05 21:47:34 +00002009 length++;
2010 else
2011 {
2012 if (x > 0)
2013 {
2014 q=PopHexPixel(hex_digits,index,q);
cristybb503372010-05-27 20:51:26 +00002015 q=PopHexPixel(hex_digits,(size_t)
cristy3ed852e2009-09-05 21:47:34 +00002016 MagickMin(length,0xff),q);
2017 i++;
2018 if ((q-pixels+6) >= 80)
2019 {
2020 *q++='\n';
2021 (void) WriteBlob(image,q-pixels,pixels);
2022 q=pixels;
2023 }
2024 }
2025 length=0;
2026 }
2027 index=indexes[x];
2028 pixel=(*p);
2029 p++;
2030 }
2031 q=PopHexPixel(hex_digits,index,q);
cristybb503372010-05-27 20:51:26 +00002032 q=PopHexPixel(hex_digits,(size_t)
cristy3ed852e2009-09-05 21:47:34 +00002033 MagickMin(length,0xff),q);
2034 if (image->previous == (Image *) NULL)
2035 {
cristycee97112010-05-28 00:44:52 +00002036 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2037 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002038 if (status == MagickFalse)
2039 break;
2040 }
2041 }
2042 if (q != pixels)
2043 {
2044 *q++='\n';
2045 (void) WriteBlob(image,q-pixels,pixels);
2046 }
2047 break;
2048 }
2049 case NoCompression:
2050 default:
2051 {
2052 /*
2053 Dump uncompressed PseudoColor packets.
2054 */
2055 q=pixels;
cristybb503372010-05-27 20:51:26 +00002056 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002057 {
2058 p=GetVirtualPixels(image,0,y,image->columns,1,
2059 &image->exception);
2060 if (p == (const PixelPacket *) NULL)
2061 break;
2062 indexes=GetVirtualIndexQueue(image);
cristybb503372010-05-27 20:51:26 +00002063 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002064 {
2065 q=PopHexPixel(hex_digits,indexes[x],q);
2066 if ((q-pixels+4) >= 80)
2067 {
2068 *q++='\n';
2069 (void) WriteBlob(image,q-pixels,pixels);
2070 q=pixels;
2071 }
2072 p++;
2073 }
2074 if (image->previous == (Image *) NULL)
2075 {
cristycee97112010-05-28 00:44:52 +00002076 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2077 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002078 if (status == MagickFalse)
2079 break;
2080 }
2081 }
2082 if (q != pixels)
2083 {
2084 *q++='\n';
2085 (void) WriteBlob(image,q-pixels,pixels);
2086 }
2087 break;
2088 }
2089 }
2090 (void) WriteBlobByte(image,'\n');
2091 }
2092 if (LocaleCompare(image_info->magick,"PS") != 0)
2093 (void) WriteBlobString(image,"end\n");
2094 (void) WriteBlobString(image,"%%PageTrailer\n");
2095 if (GetNextImageInList(image) == (Image *) NULL)
2096 break;
2097 image=SyncNextImageInList(image);
2098 status=SetImageProgress(image,SaveImagesTag,scene++,
2099 GetImageListLength(image));
2100 if (status == MagickFalse)
2101 break;
2102 } while (image_info->adjoin != MagickFalse);
2103 (void) WriteBlobString(image,"%%Trailer\n");
2104 if (page > 2)
2105 {
2106 (void) FormatMagickString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00002107 "%%%%BoundingBox: %.20g %.20g %.20g %.20g\n",ceil(bounds.x1-0.5),
2108 ceil(bounds.y1-0.5),floor(bounds.x2+0.5),floor(bounds.y2+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002109 (void) WriteBlobString(image,buffer);
2110 (void) FormatMagickString(buffer,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00002111 "%%%%HiResBoundingBox: %g %g %g %g\n",bounds.x1,bounds.y1,
cristy8cd5b312010-01-07 01:10:24 +00002112 bounds.x2,bounds.y2);
cristy3ed852e2009-09-05 21:47:34 +00002113 (void) WriteBlobString(image,buffer);
2114 }
2115 (void) WriteBlobString(image,"%%EOF\n");
2116 (void) CloseBlob(image);
2117 return(MagickTrue);
2118}