blob: d204f5b389ef48be0b140a267ffad4ca141909ce [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
cristy3ed852e2009-09-05 21:47:34 +0000119#if defined(MAGICKCORE_GS_DELEGATE) || defined(__WINDOWS__)
120 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
133 register long
134 i;
135
136#if defined(__WINDOWS__)
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);
cristy3ed852e2009-09-05 21:47:34 +0000177#if defined(__WINDOWS__)
178 NTGhostscriptUnLoadDLL();
179#endif
180 for (i=0; i < (long) argc; i++)
181 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
290 register long
291 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;
307 value=(int) ((unsigned long) value << 4)+hex_digits[c];
308 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:"
cristy7789dc72010-03-17 12:24:47 +0000326#define ICCBased "ICCBased"
cristy3ed852e2009-09-05 21:47:34 +0000327#define HiResBoundingBox "HiResBoundingBox:"
328#define ImageData "ImageData:"
329#define PageBoundingBox "PageBoundingBox:"
330#define LanguageLevel "LanguageLevel:"
331#define PageMedia "PageMedia:"
332#define Pages "Pages:"
333#define PhotoshopProfile "BeginPhotoshop:"
334#define PostscriptLevel "!PS-"
335#define RenderPostscriptText " Rendering Postscript... "
336#define SpotColor "+ "
337
338 char
339 command[MaxTextExtent],
340 density[MaxTextExtent],
341 filename[MaxTextExtent],
342 geometry[MaxTextExtent],
343 input_filename[MaxTextExtent],
344 options[MaxTextExtent],
345 postscript_filename[MaxTextExtent],
346 translate_geometry[MaxTextExtent];
347
348 const char
349 *option;
350
351 const DelegateInfo
352 *delegate_info;
353
354 GeometryInfo
355 geometry_info;
356
357 Image
358 *image,
359 *next,
360 *postscript_image;
361
362 ImageInfo
363 *read_info;
364
365 int
366 c,
367 file;
368
369 MagickBooleanType
370 cmyk,
371 skip,
372 status;
373
374 MagickStatusType
375 flags;
376
377 PointInfo
378 delta;
379
380 RectangleInfo
381 page;
382
383 register char
384 *p;
385
386 register long
387 i;
388
389 SegmentInfo
390 bounds,
391 hires_bounds;
392
393 short int
394 hex_digits[256];
395
396 size_t
397 length;
398
399 ssize_t
400 count;
401
402 StringInfo
403 *profile;
404
405 unsigned long
406 columns,
407 extent,
408 language_level,
409 pages,
410 rows,
411 scene,
412 spotcolor;
413
414 /*
415 Open image file.
416 */
417 assert(image_info != (const ImageInfo *) NULL);
418 assert(image_info->signature == MagickSignature);
419 if (image_info->debug != MagickFalse)
420 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
421 image_info->filename);
422 assert(exception != (ExceptionInfo *) NULL);
423 assert(exception->signature == MagickSignature);
424 image=AcquireImage(image_info);
425 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
426 if (status == MagickFalse)
427 {
428 image=DestroyImageList(image);
429 return((Image *) NULL);
430 }
431 status=AcquireUniqueSymbolicLink(image_info->filename,input_filename);
432 if (status == MagickFalse)
433 {
434 ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
435 image_info->filename);
436 image=DestroyImageList(image);
437 return((Image *) NULL);
438 }
439 /*
440 Initialize hex values.
441 */
442 (void) ResetMagickMemory(hex_digits,0,sizeof(hex_digits));
443 hex_digits[(int) '0']=0;
444 hex_digits[(int) '1']=1;
445 hex_digits[(int) '2']=2;
446 hex_digits[(int) '3']=3;
447 hex_digits[(int) '4']=4;
448 hex_digits[(int) '5']=5;
449 hex_digits[(int) '6']=6;
450 hex_digits[(int) '7']=7;
451 hex_digits[(int) '8']=8;
452 hex_digits[(int) '9']=9;
453 hex_digits[(int) 'a']=10;
454 hex_digits[(int) 'b']=11;
455 hex_digits[(int) 'c']=12;
456 hex_digits[(int) 'd']=13;
457 hex_digits[(int) 'e']=14;
458 hex_digits[(int) 'f']=15;
459 hex_digits[(int) 'A']=10;
460 hex_digits[(int) 'B']=11;
461 hex_digits[(int) 'C']=12;
462 hex_digits[(int) 'D']=13;
463 hex_digits[(int) 'E']=14;
464 hex_digits[(int) 'F']=15;
465 /*
466 Set the page density.
467 */
468 delta.x=DefaultResolution;
469 delta.y=DefaultResolution;
470 if ((image->x_resolution == 0.0) || (image->y_resolution == 0.0))
471 {
472 flags=ParseGeometry(PSDensityGeometry,&geometry_info);
473 image->x_resolution=geometry_info.rho;
474 image->y_resolution=geometry_info.sigma;
475 if ((flags & SigmaValue) == 0)
476 image->y_resolution=image->x_resolution;
477 }
478 /*
479 Determine page geometry from the Postscript bounding box.
480 */
481 (void) ResetMagickMemory(&bounds,0,sizeof(bounds));
482 (void) ResetMagickMemory(&hires_bounds,0,sizeof(hires_bounds));
483 (void) ResetMagickMemory(&page,0,sizeof(page));
484 (void) ResetMagickMemory(command,0,sizeof(command));
485 hires_bounds.x2=0.0;
486 hires_bounds.y2=0.0;
487 columns=0;
488 rows=0;
489 extent=0;
490 spotcolor=0;
491 language_level=1;
492 skip=MagickFalse;
493 cmyk=image_info->colorspace == CMYKColorspace ? MagickTrue : MagickFalse;
494 pages=(~0UL);
495 p=command;
496 for (c=ReadBlobByte(image); c != EOF; c=ReadBlobByte(image))
497 {
498 /*
499 Note document structuring comments.
500 */
501 *p++=(char) c;
502 if ((strchr("\n\r%",c) == (char *) NULL) &&
503 ((size_t) (p-command) < (MaxTextExtent-1)))
504 continue;
505 *p='\0';
506 p=command;
507 /*
508 Skip %%BeginDocument thru %%EndDocument.
509 */
510 if (LocaleNCompare(BeginDocument,command,strlen(BeginDocument)) == 0)
511 skip=MagickTrue;
512 if (LocaleNCompare(EndDocument,command,strlen(EndDocument)) == 0)
513 skip=MagickFalse;
514 if (skip != MagickFalse)
515 continue;
516 if (LocaleNCompare(PostscriptLevel,command,strlen(PostscriptLevel)) == 0)
517 {
518 (void) SetImageProperty(image,"ps:Level",command+4);
519 if (GlobExpression(command,"*EPSF-*",MagickTrue) != MagickFalse)
520 pages=1;
521 }
522 if (LocaleNCompare(LanguageLevel,command,strlen(LanguageLevel)) == 0)
523 (void) sscanf(command,LanguageLevel " %lu",&language_level);
524 if (LocaleNCompare(Pages,command,strlen(Pages)) == 0)
525 (void) sscanf(command,Pages " %lu",&pages);
526 if (LocaleNCompare(ImageData,command,strlen(Pages)) == 0)
527 (void) sscanf(command,ImageData " %lu %lu",&columns,&rows);
528 if (LocaleNCompare(ICCProfile,command,strlen(PhotoshopProfile)) == 0)
529 {
530 unsigned char
531 *datum;
532
533 /*
534 Read ICC profile.
535 */
536 profile=AcquireStringInfo(65536);
537 for (i=0; (c=ProfileInteger(image,hex_digits)) != EOF; i++)
538 {
539 SetStringInfoLength(profile,(size_t) i+1);
540 datum=GetStringInfoDatum(profile);
541 datum[i]=(unsigned char) c;
542 }
543 (void) SetImageProfile(image,"icc",profile);
544 profile=DestroyStringInfo(profile);
545 continue;
546 }
547 if (LocaleNCompare(PhotoshopProfile,command,strlen(PhotoshopProfile)) == 0)
548 {
549 unsigned char
550 *p;
551
552 /*
553 Read Photoshop profile.
554 */
555 count=(ssize_t) sscanf(command,PhotoshopProfile " %lu",&extent);
556 if (count != 1)
557 continue;
558 length=extent;
559 profile=AcquireStringInfo(length);
560 p=GetStringInfoDatum(profile);
561 for (i=0; i < (long) length; i++)
562 *p++=(unsigned char) ProfileInteger(image,hex_digits);
563 (void) SetImageProfile(image,"8bim",profile);
564 profile=DestroyStringInfo(profile);
565 continue;
566 }
567 if (LocaleNCompare(BeginXMPPacket,command,strlen(BeginXMPPacket)) == 0)
568 {
569 register size_t
570 i;
571
572 /*
573 Read XMP profile.
574 */
575 p=command;
576 profile=StringToStringInfo(command);
577 for (i=GetStringInfoLength(profile)-1; c != EOF; i++)
578 {
579 SetStringInfoLength(profile,i+1);
580 c=ReadBlobByte(image);
581 GetStringInfoDatum(profile)[i]=(unsigned char) c;
582 *p++=(char) c;
583 if ((strchr("\n\r%",c) == (char *) NULL) &&
584 ((size_t) (p-command) < (MaxTextExtent-1)))
585 continue;
586 *p='\0';
587 p=command;
588 if (LocaleNCompare(EndXMPPacket,command,strlen(EndXMPPacket)) == 0)
589 break;
590 }
591 SetStringInfoLength(profile,i);
592 (void) SetImageProfile(image,"xmp",profile);
593 profile=DestroyStringInfo(profile);
594 continue;
595 }
596 /*
597 Is this a CMYK document?
598 */
599 length=strlen(DocumentProcessColors);
600 if (LocaleNCompare(DocumentProcessColors,command,length) == 0)
601 {
602 if ((GlobExpression(command,"*Cyan*",MagickTrue) != MagickFalse) ||
603 (GlobExpression(command,"*Magenta*",MagickTrue) != MagickFalse) ||
604 (GlobExpression(command,"*Yellow*",MagickTrue) != MagickFalse))
605 cmyk=MagickTrue;
606 }
607 if (LocaleNCompare(CMYKCustomColor,command,strlen(CMYKCustomColor)) == 0)
608 cmyk=MagickTrue;
cristy01ca8922010-03-17 12:20:37 +0000609 if (LocaleNCompare(CMYKProcessColor,command,strlen(CMYKProcessColor)) == 0)
610 cmyk=MagickTrue;
cristy7789dc72010-03-17 12:24:47 +0000611 if (LocaleNCompare(ICCBased,command,strlen(ICCBased)) == 0)
612 cmyk=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +0000613 length=strlen(DocumentCustomColors);
614 if ((LocaleNCompare(DocumentCustomColors,command,length) == 0) ||
615 (LocaleNCompare(CMYKCustomColor,command,strlen(CMYKCustomColor)) == 0) ||
616 (LocaleNCompare(SpotColor,command,strlen(SpotColor)) == 0))
617 {
618 char
619 property[MaxTextExtent],
620 *value;
621
622 register char
623 *p;
624
625 /*
626 Note spot names.
627 */
628 (void) FormatMagickString(property,MaxTextExtent,"ps:SpotColor-%lu",
629 spotcolor++);
630 for (p=command; *p != '\0'; p++)
631 if (isspace((int) (unsigned char) *p) != 0)
632 break;
633 value=AcquireString(p);
634 (void) SubstituteString(&value,"(","");
635 (void) SubstituteString(&value,")","");
636 (void) StripString(value);
637 (void) SetImageProperty(image,property,value);
638 value=DestroyString(value);
639 continue;
640 }
641 /*
642 Note region defined by bounding box.
643 */
644 count=0;
645 if (LocaleNCompare(BoundingBox,command,strlen(BoundingBox)) == 0)
646 count=(ssize_t) sscanf(command,BoundingBox " %lf %lf %lf %lf",&bounds.x1,
647 &bounds.y1,&bounds.x2,&bounds.y2);
648 if (LocaleNCompare(DocumentMedia,command,strlen(DocumentMedia)) == 0)
649 count=(ssize_t) sscanf(command,DocumentMedia " %*s %lf %lf",&bounds.x2,
650 &bounds.y2)+2;
651 if (LocaleNCompare(HiResBoundingBox,command,strlen(HiResBoundingBox)) == 0)
652 count=(ssize_t) sscanf(command,HiResBoundingBox " %lf %lf %lf %lf",
653 &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
654 if (LocaleNCompare(PageBoundingBox,command,strlen(PageBoundingBox)) == 0)
655 count=(ssize_t) sscanf(command,PageBoundingBox " %lf %lf %lf %lf",
656 &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
657 if (LocaleNCompare(PageMedia,command,strlen(PageMedia)) == 0)
658 count=(ssize_t) sscanf(command,PageMedia " %*s %lf %lf",&bounds.x2,
659 &bounds.y2)+2;
660 if (count != 4)
661 continue;
662 if (((bounds.x2 > hires_bounds.x2) && (bounds.y2 > hires_bounds.y2)) ||
663 ((hires_bounds.x2 == 0.0) && (hires_bounds.y2 == 0.0)))
664 {
665 /*
666 Set Postscript render geometry.
667 */
cristy8cd5b312010-01-07 01:10:24 +0000668 (void) FormatMagickString(geometry,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +0000669 "%gx%g%+.15g%+.15g",bounds.x2-bounds.x1,bounds.y2-bounds.y1,
cristy8cd5b312010-01-07 01:10:24 +0000670 bounds.x1,bounds.y1);
cristy3ed852e2009-09-05 21:47:34 +0000671 (void) SetImageProperty(image,"ps:HiResBoundingBox",geometry);
cristy06609ee2010-03-17 20:21:27 +0000672 page.width=(unsigned long) floor(bounds.x2-bounds.x1+0.5);
673 page.height=(unsigned long) floor(bounds.y2-bounds.y1+0.5);
cristy3ed852e2009-09-05 21:47:34 +0000674 hires_bounds=bounds;
675 }
676 }
677 (void) CloseBlob(image);
678 if (image_info->colorspace == RGBColorspace)
679 cmyk=MagickFalse;
680 /*
681 Create Ghostscript control file.
682 */
683 file=AcquireUniqueFileResource(postscript_filename);
684 if (file == -1)
685 {
686 ThrowFileException(&image->exception,FileOpenError,"UnableToOpenFile",
687 image_info->filename);
688 image=DestroyImageList(image);
689 return((Image *) NULL);
690 }
691 (void) CopyMagickString(command,"/setpagedevice {pop} bind 1 index where {"
692 "dup wcheck {3 1 roll put} {pop def} ifelse} {def} ifelse\n"
693 "<</UseCIEColor true>>setpagedevice\n",MaxTextExtent);
694 count=write(file,command,(unsigned int) strlen(command));
695 (void) FormatMagickString(translate_geometry,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +0000696 "%g %g translate\n",-bounds.x1,-bounds.y1);
cristy3ed852e2009-09-05 21:47:34 +0000697 count=write(file,translate_geometry,strlen(translate_geometry));
698 file=close(file)-1;
699 /*
700 Render Postscript with the Ghostscript delegate.
701 */
702 if (image_info->monochrome != MagickFalse)
703 delegate_info=GetDelegateInfo("ps:mono",(char *) NULL,exception);
704 else
705 if (cmyk != MagickFalse)
706 delegate_info=GetDelegateInfo("ps:cmyk",(char *) NULL,exception);
707 else
cristyf1849f82009-09-08 14:37:22 +0000708 if (pages == 1)
709 delegate_info=GetDelegateInfo("ps:alpha",(char *) NULL,exception);
710 else
711 delegate_info=GetDelegateInfo("ps:color",(char *) NULL,exception);
cristy3ed852e2009-09-05 21:47:34 +0000712 if (delegate_info == (const DelegateInfo *) NULL)
713 {
714 (void) RelinquishUniqueFileResource(postscript_filename);
715 image=DestroyImageList(image);
716 return((Image *) NULL);
717 }
718 *options='\0';
719 if ((page.width == 0) || (page.height == 0))
720 (void) ParseAbsoluteGeometry(PSPageGeometry,&page);
721 if (image_info->page != (char *) NULL)
722 (void) ParseAbsoluteGeometry(image_info->page,&page);
cristye7f51092010-01-17 00:39:37 +0000723 (void) FormatMagickString(density,MaxTextExtent,"%gx%g",
cristy8cd5b312010-01-07 01:10:24 +0000724 image->x_resolution,image->y_resolution);
cristy06609ee2010-03-17 20:21:27 +0000725 page.width=(unsigned long) floor(page.width*image->x_resolution/delta.x+0.5);
726 page.height=(unsigned long) floor(page.height*image->y_resolution/delta.y+
727 0.5);
cristy3ed852e2009-09-05 21:47:34 +0000728 (void) FormatMagickString(options,MaxTextExtent,"-g%lux%lu ",
729 page.width,page.height);
730 read_info=CloneImageInfo(image_info);
731 *read_info->magick='\0';
732 if (read_info->number_scenes != 0)
733 {
734 char
735 pages[MaxTextExtent];
736
737 (void) FormatMagickString(pages,MaxTextExtent,"-dFirstPage=%lu "
738 "-dLastPage=%lu",read_info->scene+1,read_info->scene+
739 read_info->number_scenes);
740 (void) ConcatenateMagickString(options,pages,MaxTextExtent);
741 read_info->number_scenes=0;
742 if (read_info->scenes != (char *) NULL)
743 *read_info->scenes='\0';
744 }
745 option=GetImageOption(image_info,"ps:use-cropbox");
746 if ((option != (const char *) NULL) && (IsMagickTrue(option) != MagickFalse))
747 (void) ConcatenateMagickString(options,"-dEPSCrop ",MaxTextExtent);
748 (void) CopyMagickString(filename,read_info->filename,MaxTextExtent);
749 (void) AcquireUniqueFilename(read_info->filename);
750 (void) FormatMagickString(command,MaxTextExtent,
751 GetDelegateCommands(delegate_info),
752 read_info->antialias != MagickFalse ? 4 : 1,
753 read_info->antialias != MagickFalse ? 4 : 1,density,options,
754 read_info->filename,postscript_filename,input_filename);
cristyb32b90a2009-09-07 21:45:48 +0000755 status=InvokePostscriptDelegate(read_info->verbose,command,exception);
cristy41083a42009-09-07 23:47:59 +0000756 if ((status == MagickFalse) ||
cristy3ed852e2009-09-05 21:47:34 +0000757 (IsPostscriptRendered(read_info->filename) == MagickFalse))
758 {
759 (void) ConcatenateMagickString(command," -c showpage",MaxTextExtent);
cristyb32b90a2009-09-07 21:45:48 +0000760 status=InvokePostscriptDelegate(read_info->verbose,command,exception);
cristy3ed852e2009-09-05 21:47:34 +0000761 }
762 postscript_image=(Image *) NULL;
cristy41083a42009-09-07 23:47:59 +0000763 if (status != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000764 postscript_image=ReadImage(read_info,exception);
765 (void) RelinquishUniqueFileResource(postscript_filename);
766 (void) RelinquishUniqueFileResource(read_info->filename);
767 (void) RelinquishUniqueFileResource(input_filename);
768 read_info=DestroyImageInfo(read_info);
769 if (postscript_image == (Image *) NULL)
770 {
771 image=DestroyImageList(image);
772 ThrowFileException(exception,DelegateError,"PostscriptDelegateFailed",
773 image_info->filename);
774 return((Image *) NULL);
775 }
776 if (LocaleCompare(postscript_image->magick,"BMP") == 0)
777 {
778 Image
779 *cmyk_image;
780
781 cmyk_image=ConsolidateCMYKImages(postscript_image,exception);
782 if (cmyk_image != (Image *) NULL)
783 {
784 postscript_image=DestroyImageList(postscript_image);
785 postscript_image=cmyk_image;
786 }
787 }
788 if (image_info->number_scenes != 0)
789 {
790 Image
791 *clone_image;
792
793 register long
794 i;
795
796 /*
797 Add place holder images to meet the subimage specification requirement.
798 */
799 for (i=0; i < (long) image_info->scene; i++)
800 {
801 clone_image=CloneImage(postscript_image,1,1,MagickTrue,exception);
802 if (clone_image != (Image *) NULL)
803 PrependImageToList(&postscript_image,clone_image);
804 }
805 }
806 do
807 {
808 (void) CopyMagickString(postscript_image->filename,filename,MaxTextExtent);
809 if (columns != 0)
810 postscript_image->magick_columns=columns;
811 if (rows != 0)
812 postscript_image->magick_rows=rows;
813 postscript_image->page=page;
814 (void) CloneImageProfiles(postscript_image,image);
815 (void) CloneImageProperties(postscript_image,image);
816 next=SyncNextImageInList(postscript_image);
817 if (next != (Image *) NULL)
818 postscript_image=next;
819 } while (next != (Image *) NULL);
820 image=DestroyImageList(image);
821 scene=0;
822 for (next=GetFirstImageInList(postscript_image); next != (Image *) NULL; )
823 {
824 next->scene=scene++;
825 next=GetNextImageInList(next);
826 }
827 return(GetFirstImageInList(postscript_image));
828}
829
830/*
831%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
832% %
833% %
834% %
835% R e g i s t e r P S I m a g e %
836% %
837% %
838% %
839%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
840%
841% RegisterPSImage() adds properties for the PS image format to
842% the list of supported formats. The properties include the image format
843% tag, a method to read and/or write the format, whether the format
844% supports the saving of more than one frame to the same file or blob,
845% whether the format supports native in-memory I/O, and a brief
846% description of the format.
847%
848% The format of the RegisterPSImage method is:
849%
850% unsigned long RegisterPSImage(void)
851%
852*/
853ModuleExport unsigned long RegisterPSImage(void)
854{
855 MagickInfo
856 *entry;
857
858 entry=SetMagickInfo("EPI");
859 entry->decoder=(DecodeImageHandler *) ReadPSImage;
860 entry->encoder=(EncodeImageHandler *) WritePSImage;
861 entry->magick=(IsImageFormatHandler *) IsPS;
862 entry->adjoin=MagickFalse;
863 entry->blob_support=MagickFalse;
864 entry->seekable_stream=MagickTrue;
865 entry->thread_support=EncoderThreadSupport;
866 entry->description=ConstantString(
867 "Encapsulated PostScript Interchange format");
868 entry->module=ConstantString("PS");
869 (void) RegisterMagickInfo(entry);
870 entry=SetMagickInfo("EPS");
871 entry->decoder=(DecodeImageHandler *) ReadPSImage;
872 entry->encoder=(EncodeImageHandler *) WritePSImage;
873 entry->magick=(IsImageFormatHandler *) IsPS;
874 entry->adjoin=MagickFalse;
875 entry->blob_support=MagickFalse;
876 entry->seekable_stream=MagickTrue;
877 entry->thread_support=EncoderThreadSupport;
878 entry->description=ConstantString("Encapsulated PostScript");
879 entry->module=ConstantString("PS");
880 (void) RegisterMagickInfo(entry);
881 entry=SetMagickInfo("EPSF");
882 entry->decoder=(DecodeImageHandler *) ReadPSImage;
883 entry->encoder=(EncodeImageHandler *) WritePSImage;
884 entry->magick=(IsImageFormatHandler *) IsPS;
885 entry->adjoin=MagickFalse;
886 entry->blob_support=MagickFalse;
887 entry->seekable_stream=MagickTrue;
888 entry->description=ConstantString("Encapsulated PostScript");
889 entry->module=ConstantString("PS");
890 (void) RegisterMagickInfo(entry);
891 entry=SetMagickInfo("EPSI");
892 entry->decoder=(DecodeImageHandler *) ReadPSImage;
893 entry->encoder=(EncodeImageHandler *) WritePSImage;
894 entry->magick=(IsImageFormatHandler *) IsPS;
895 entry->adjoin=MagickFalse;
896 entry->blob_support=MagickFalse;
897 entry->seekable_stream=MagickTrue;
898 entry->thread_support=EncoderThreadSupport;
899 entry->description=ConstantString(
900 "Encapsulated PostScript Interchange format");
901 entry->module=ConstantString("PS");
902 (void) RegisterMagickInfo(entry);
903 entry=SetMagickInfo("PS");
904 entry->decoder=(DecodeImageHandler *) ReadPSImage;
905 entry->encoder=(EncodeImageHandler *) WritePSImage;
906 entry->magick=(IsImageFormatHandler *) IsPS;
907 entry->module=ConstantString("PS");
908 entry->blob_support=MagickFalse;
909 entry->seekable_stream=MagickTrue;
910 entry->thread_support=EncoderThreadSupport;
911 entry->description=ConstantString("PostScript");
912 (void) RegisterMagickInfo(entry);
913 return(MagickImageCoderSignature);
914}
915
916/*
917%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
918% %
919% %
920% %
921% U n r e g i s t e r P S I m a g e %
922% %
923% %
924% %
925%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
926%
927% UnregisterPSImage() removes format registrations made by the
928% PS module from the list of supported formats.
929%
930% The format of the UnregisterPSImage method is:
931%
932% UnregisterPSImage(void)
933%
934*/
935ModuleExport void UnregisterPSImage(void)
936{
937 (void) UnregisterMagickInfo("EPI");
938 (void) UnregisterMagickInfo("EPS");
939 (void) UnregisterMagickInfo("EPSF");
940 (void) UnregisterMagickInfo("EPSI");
941 (void) UnregisterMagickInfo("PS");
942}
943
944/*
945%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
946% %
947% %
948% %
949% W r i t e P S I m a g e %
950% %
951% %
952% %
953%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
954%
955% WritePSImage translates an image to encapsulated Postscript
956% Level I for printing. If the supplied geometry is null, the image is
957% centered on the Postscript page. Otherwise, the image is positioned as
958% specified by the geometry.
959%
960% The format of the WritePSImage method is:
961%
962% MagickBooleanType WritePSImage(const ImageInfo *image_info,Image *image)
963%
964% A description of each parameter follows:
965%
966% o image_info: the image info.
967%
968% o image: the image.
969%
970*/
971
972static inline size_t MagickMin(const size_t x,const size_t y)
973{
974 if (x < y)
975 return(x);
976 return(y);
977}
978
979static inline unsigned char *PopHexPixel(const char **hex_digits,
980 const unsigned long pixel,unsigned char *pixels)
981{
982 register const char
983 *hex;
984
985 hex=hex_digits[pixel];
986 *pixels++=(unsigned char) (*hex++);
987 *pixels++=(unsigned char) (*hex);
988 return(pixels);
989}
990
991static MagickBooleanType WritePSImage(const ImageInfo *image_info,Image *image)
992{
993#define WriteRunlengthPacket(image,pixel,length,p) \
994{ \
995 if ((image->matte != MagickFalse) && \
996 (p->opacity == (Quantum) TransparentOpacity)) \
997 { \
998 q=PopHexPixel(hex_digits,0xff,q); \
999 q=PopHexPixel(hex_digits,0xff,q); \
1000 q=PopHexPixel(hex_digits,0xff,q); \
1001 } \
1002 else \
1003 { \
1004 q=PopHexPixel(hex_digits,ScaleQuantumToChar(pixel.red),q); \
1005 q=PopHexPixel(hex_digits,ScaleQuantumToChar(pixel.green),q); \
1006 q=PopHexPixel(hex_digits,ScaleQuantumToChar(pixel.blue),q); \
1007 } \
1008 q=PopHexPixel(hex_digits,(const unsigned long) MagickMin(length,0xff),q); \
1009}
1010
1011 static const char
1012 *hex_digits[] =
1013 {
1014 "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0A", "0B",
1015 "0C", "0D", "0E", "0F", "10", "11", "12", "13", "14", "15", "16", "17",
1016 "18", "19", "1A", "1B", "1C", "1D", "1E", "1F", "20", "21", "22", "23",
1017 "24", "25", "26", "27", "28", "29", "2A", "2B", "2C", "2D", "2E", "2F",
1018 "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3A", "3B",
1019 "3C", "3D", "3E", "3F", "40", "41", "42", "43", "44", "45", "46", "47",
1020 "48", "49", "4A", "4B", "4C", "4D", "4E", "4F", "50", "51", "52", "53",
1021 "54", "55", "56", "57", "58", "59", "5A", "5B", "5C", "5D", "5E", "5F",
1022 "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6A", "6B",
1023 "6C", "6D", "6E", "6F", "70", "71", "72", "73", "74", "75", "76", "77",
1024 "78", "79", "7A", "7B", "7C", "7D", "7E", "7F", "80", "81", "82", "83",
1025 "84", "85", "86", "87", "88", "89", "8A", "8B", "8C", "8D", "8E", "8F",
1026 "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9A", "9B",
1027 "9C", "9D", "9E", "9F", "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7",
1028 "A8", "A9", "AA", "AB", "AC", "AD", "AE", "AF", "B0", "B1", "B2", "B3",
1029 "B4", "B5", "B6", "B7", "B8", "B9", "BA", "BB", "BC", "BD", "BE", "BF",
1030 "C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "CA", "CB",
1031 "CC", "CD", "CE", "CF", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
1032 "D8", "D9", "DA", "DB", "DC", "DD", "DE", "DF", "E0", "E1", "E2", "E3",
1033 "E4", "E5", "E6", "E7", "E8", "E9", "EA", "EB", "EC", "ED", "EE", "EF",
1034 "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "FA", "FB",
1035 "FC", "FD", "FE", "FF", (char *) NULL
1036 },
1037 *PostscriptProlog[]=
1038 {
1039 "%%BeginProlog",
1040 "%",
1041 "% Display a color image. The image is displayed in color on",
1042 "% Postscript viewers or printers that support color, otherwise",
1043 "% it is displayed as grayscale.",
1044 "%",
1045 "/DirectClassPacket",
1046 "{",
1047 " %",
1048 " % Get a DirectClass packet.",
1049 " %",
1050 " % Parameters:",
1051 " % red.",
1052 " % green.",
1053 " % blue.",
1054 " % length: number of pixels minus one of this color (optional).",
1055 " %",
1056 " currentfile color_packet readhexstring pop pop",
1057 " compression 0 eq",
1058 " {",
1059 " /number_pixels 3 def",
1060 " }",
1061 " {",
1062 " currentfile byte readhexstring pop 0 get",
1063 " /number_pixels exch 1 add 3 mul def",
1064 " } ifelse",
1065 " 0 3 number_pixels 1 sub",
1066 " {",
1067 " pixels exch color_packet putinterval",
1068 " } for",
1069 " pixels 0 number_pixels getinterval",
1070 "} bind def",
1071 "",
1072 "/DirectClassImage",
1073 "{",
1074 " %",
1075 " % Display a DirectClass image.",
1076 " %",
1077 " systemdict /colorimage known",
1078 " {",
1079 " columns rows 8",
1080 " [",
1081 " columns 0 0",
1082 " rows neg 0 rows",
1083 " ]",
1084 " { DirectClassPacket } false 3 colorimage",
1085 " }",
1086 " {",
1087 " %",
1088 " % No colorimage operator; convert to grayscale.",
1089 " %",
1090 " columns rows 8",
1091 " [",
1092 " columns 0 0",
1093 " rows neg 0 rows",
1094 " ]",
1095 " { GrayDirectClassPacket } image",
1096 " } ifelse",
1097 "} bind def",
1098 "",
1099 "/GrayDirectClassPacket",
1100 "{",
1101 " %",
1102 " % Get a DirectClass packet; convert to grayscale.",
1103 " %",
1104 " % Parameters:",
1105 " % red",
1106 " % green",
1107 " % blue",
1108 " % length: number of pixels minus one of this color (optional).",
1109 " %",
1110 " currentfile color_packet readhexstring pop pop",
1111 " color_packet 0 get 0.299 mul",
1112 " color_packet 1 get 0.587 mul add",
1113 " color_packet 2 get 0.114 mul add",
1114 " cvi",
1115 " /gray_packet exch def",
1116 " compression 0 eq",
1117 " {",
1118 " /number_pixels 1 def",
1119 " }",
1120 " {",
1121 " currentfile byte readhexstring pop 0 get",
1122 " /number_pixels exch 1 add def",
1123 " } ifelse",
1124 " 0 1 number_pixels 1 sub",
1125 " {",
1126 " pixels exch gray_packet put",
1127 " } for",
1128 " pixels 0 number_pixels getinterval",
1129 "} bind def",
1130 "",
1131 "/GrayPseudoClassPacket",
1132 "{",
1133 " %",
1134 " % Get a PseudoClass packet; convert to grayscale.",
1135 " %",
1136 " % Parameters:",
1137 " % index: index into the colormap.",
1138 " % length: number of pixels minus one of this color (optional).",
1139 " %",
1140 " currentfile byte readhexstring pop 0 get",
1141 " /offset exch 3 mul def",
1142 " /color_packet colormap offset 3 getinterval def",
1143 " color_packet 0 get 0.299 mul",
1144 " color_packet 1 get 0.587 mul add",
1145 " color_packet 2 get 0.114 mul add",
1146 " cvi",
1147 " /gray_packet exch def",
1148 " compression 0 eq",
1149 " {",
1150 " /number_pixels 1 def",
1151 " }",
1152 " {",
1153 " currentfile byte readhexstring pop 0 get",
1154 " /number_pixels exch 1 add def",
1155 " } ifelse",
1156 " 0 1 number_pixels 1 sub",
1157 " {",
1158 " pixels exch gray_packet put",
1159 " } for",
1160 " pixels 0 number_pixels getinterval",
1161 "} bind def",
1162 "",
1163 "/PseudoClassPacket",
1164 "{",
1165 " %",
1166 " % Get a PseudoClass packet.",
1167 " %",
1168 " % Parameters:",
1169 " % index: index into the colormap.",
1170 " % length: number of pixels minus one of this color (optional).",
1171 " %",
1172 " currentfile byte readhexstring pop 0 get",
1173 " /offset exch 3 mul def",
1174 " /color_packet colormap offset 3 getinterval def",
1175 " compression 0 eq",
1176 " {",
1177 " /number_pixels 3 def",
1178 " }",
1179 " {",
1180 " currentfile byte readhexstring pop 0 get",
1181 " /number_pixels exch 1 add 3 mul def",
1182 " } ifelse",
1183 " 0 3 number_pixels 1 sub",
1184 " {",
1185 " pixels exch color_packet putinterval",
1186 " } for",
1187 " pixels 0 number_pixels getinterval",
1188 "} bind def",
1189 "",
1190 "/PseudoClassImage",
1191 "{",
1192 " %",
1193 " % Display a PseudoClass image.",
1194 " %",
1195 " % Parameters:",
1196 " % class: 0-PseudoClass or 1-Grayscale.",
1197 " %",
1198 " currentfile buffer readline pop",
1199 " token pop /class exch def pop",
1200 " class 0 gt",
1201 " {",
1202 " currentfile buffer readline pop",
1203 " token pop /depth exch def pop",
1204 " /grays columns 8 add depth sub depth mul 8 idiv string def",
1205 " columns rows depth",
1206 " [",
1207 " columns 0 0",
1208 " rows neg 0 rows",
1209 " ]",
1210 " { currentfile grays readhexstring pop } image",
1211 " }",
1212 " {",
1213 " %",
1214 " % Parameters:",
1215 " % colors: number of colors in the colormap.",
1216 " % colormap: red, green, blue color packets.",
1217 " %",
1218 " currentfile buffer readline pop",
1219 " token pop /colors exch def pop",
1220 " /colors colors 3 mul def",
1221 " /colormap colors string def",
1222 " currentfile colormap readhexstring pop pop",
1223 " systemdict /colorimage known",
1224 " {",
1225 " columns rows 8",
1226 " [",
1227 " columns 0 0",
1228 " rows neg 0 rows",
1229 " ]",
1230 " { PseudoClassPacket } false 3 colorimage",
1231 " }",
1232 " {",
1233 " %",
1234 " % No colorimage operator; convert to grayscale.",
1235 " %",
1236 " columns rows 8",
1237 " [",
1238 " columns 0 0",
1239 " rows neg 0 rows",
1240 " ]",
1241 " { GrayPseudoClassPacket } image",
1242 " } ifelse",
1243 " } ifelse",
1244 "} bind def",
1245 "",
1246 "/DisplayImage",
1247 "{",
1248 " %",
1249 " % Display a DirectClass or PseudoClass image.",
1250 " %",
1251 " % Parameters:",
1252 " % x & y translation.",
1253 " % x & y scale.",
1254 " % label pointsize.",
1255 " % image label.",
1256 " % image columns & rows.",
1257 " % class: 0-DirectClass or 1-PseudoClass.",
1258 " % compression: 0-none or 1-RunlengthEncoded.",
1259 " % hex color packets.",
1260 " %",
1261 " gsave",
1262 " /buffer 512 string def",
1263 " /byte 1 string def",
1264 " /color_packet 3 string def",
1265 " /pixels 768 string def",
1266 "",
1267 " currentfile buffer readline pop",
1268 " token pop /x exch def",
1269 " token pop /y exch def pop",
1270 " x y translate",
1271 " currentfile buffer readline pop",
1272 " token pop /x exch def",
1273 " token pop /y exch def pop",
1274 " currentfile buffer readline pop",
1275 " token pop /pointsize exch def pop",
1276 " /Times-Roman findfont pointsize scalefont setfont",
1277 (char *) NULL
1278 },
1279 *PostscriptEpilog[]=
1280 {
1281 " x y scale",
1282 " currentfile buffer readline pop",
1283 " token pop /columns exch def",
1284 " token pop /rows exch def pop",
1285 " currentfile buffer readline pop",
1286 " token pop /class exch def pop",
1287 " currentfile buffer readline pop",
1288 " token pop /compression exch def pop",
1289 " class 0 gt { PseudoClassImage } { DirectClassImage } ifelse",
1290 (char *) NULL
1291 };
1292
1293 char
1294 buffer[MaxTextExtent],
1295 date[MaxTextExtent],
1296 **labels,
1297 page_geometry[MaxTextExtent];
1298
1299 const char
1300 **s,
1301 *value;
1302
1303 const StringInfo
1304 *profile;
1305
1306 double
1307 pointsize;
1308
1309 GeometryInfo
1310 geometry_info;
1311
1312 IndexPacket
1313 index;
1314
1315 long
1316 j,
1317 y;
1318
1319 MagickBooleanType
1320 status;
1321
1322 MagickOffsetType
1323 scene;
1324
1325 MagickStatusType
1326 flags;
1327
1328 PixelPacket
1329 pixel;
1330
1331 PointInfo
1332 delta,
1333 resolution,
1334 scale;
1335
1336 RectangleInfo
1337 geometry,
1338 media_info,
1339 page_info;
1340
1341 register const IndexPacket
1342 *indexes;
1343
1344 register const PixelPacket
1345 *p;
1346
1347 register long
1348 i,
1349 x;
1350
1351 register unsigned char
1352 *q;
1353
1354 SegmentInfo
1355 bounds;
1356
1357 size_t
1358 length;
1359
1360 time_t
1361 timer;
1362
1363 unsigned char
1364 pixels[2048];
1365
1366 unsigned long
1367 bit,
1368 byte,
1369 page,
1370 text_size;
1371
1372 /*
1373 Open output image file.
1374 */
1375 assert(image_info != (const ImageInfo *) NULL);
1376 assert(image_info->signature == MagickSignature);
1377 assert(image != (Image *) NULL);
1378 assert(image->signature == MagickSignature);
1379 if (image->debug != MagickFalse)
1380 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1381 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
1382 if (status == MagickFalse)
1383 return(status);
1384 (void) ResetMagickMemory(&bounds,0,sizeof(bounds));
1385 page=1;
1386 scene=0;
1387 do
1388 {
1389 /*
1390 Scale relative to dots-per-inch.
1391 */
1392 if ((image->colorspace != RGBColorspace) &&
1393 (image->colorspace != CMYKColorspace))
1394 (void) TransformImageColorspace(image,RGBColorspace);
1395 delta.x=DefaultResolution;
1396 delta.y=DefaultResolution;
1397 resolution.x=image->x_resolution;
1398 resolution.y=image->y_resolution;
1399 if ((resolution.x == 0.0) || (resolution.y == 0.0))
1400 {
1401 flags=ParseGeometry(PSDensityGeometry,&geometry_info);
1402 resolution.x=geometry_info.rho;
1403 resolution.y=geometry_info.sigma;
1404 if ((flags & SigmaValue) == 0)
1405 resolution.y=resolution.x;
1406 }
1407 if (image_info->density != (char *) NULL)
1408 {
1409 flags=ParseGeometry(image_info->density,&geometry_info);
1410 resolution.x=geometry_info.rho;
1411 resolution.y=geometry_info.sigma;
1412 if ((flags & SigmaValue) == 0)
1413 resolution.y=resolution.x;
1414 }
1415 if (image->units == PixelsPerCentimeterResolution)
1416 {
1417 resolution.x*=2.54;
1418 resolution.y*=2.54;
1419 }
1420 SetGeometry(image,&geometry);
1421 (void) FormatMagickString(page_geometry,MaxTextExtent,"%lux%lu",
1422 image->columns,image->rows);
1423 if (image_info->page != (char *) NULL)
1424 (void) CopyMagickString(page_geometry,image_info->page,MaxTextExtent);
1425 else
1426 if ((image->page.width != 0) && (image->page.height != 0))
1427 (void) FormatMagickString(page_geometry,MaxTextExtent,"%lux%lu%+ld%+ld",
1428 image->page.width,image->page.height,image->page.x,image->page.y);
1429 else
1430 if ((image->gravity != UndefinedGravity) &&
1431 (LocaleCompare(image_info->magick,"PS") == 0))
1432 (void) CopyMagickString(page_geometry,PSPageGeometry,MaxTextExtent);
1433 (void) ConcatenateMagickString(page_geometry,">",MaxTextExtent);
1434 (void) ParseMetaGeometry(page_geometry,&geometry.x,&geometry.y,
1435 &geometry.width,&geometry.height);
1436 scale.x=(double) (geometry.width*delta.x)/resolution.x;
cristy06609ee2010-03-17 20:21:27 +00001437 geometry.width=(unsigned long) floor(scale.x+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001438 scale.y=(double) (geometry.height*delta.y)/resolution.y;
cristy06609ee2010-03-17 20:21:27 +00001439 geometry.height=(unsigned long) floor(scale.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001440 (void) ParseAbsoluteGeometry(page_geometry,&media_info);
1441 (void) ParseGravityGeometry(image,page_geometry,&page_info,
1442 &image->exception);
1443 if (image->gravity != UndefinedGravity)
1444 {
1445 geometry.x=(-page_info.x);
1446 geometry.y=(long) (media_info.height+page_info.y-image->rows);
1447 }
1448 pointsize=12.0;
1449 if (image_info->pointsize != 0.0)
1450 pointsize=image_info->pointsize;
1451 text_size=0;
1452 value=GetImageProperty(image,"label");
1453 if (value != (const char *) NULL)
1454 text_size=(unsigned long) (MultilineCensus(value)*pointsize+12);
1455 if (page == 1)
1456 {
1457 /*
1458 Output Postscript header.
1459 */
1460 if (LocaleCompare(image_info->magick,"PS") == 0)
1461 (void) CopyMagickString(buffer,"%!PS-Adobe-3.0\n",MaxTextExtent);
1462 else
1463 (void) CopyMagickString(buffer,"%!PS-Adobe-3.0 EPSF-3.0\n",
1464 MaxTextExtent);
1465 (void) WriteBlobString(image,buffer);
1466 (void) WriteBlobString(image,"%%Creator: (ImageMagick)\n");
1467 (void) FormatMagickString(buffer,MaxTextExtent,"%%%%Title: (%s)\n",
1468 image->filename);
1469 (void) WriteBlobString(image,buffer);
1470 timer=time((time_t *) NULL);
1471 (void) FormatMagickTime(timer,MaxTextExtent,date);
1472 (void) FormatMagickString(buffer,MaxTextExtent,
1473 "%%%%CreationDate: (%s)\n",date);
1474 (void) WriteBlobString(image,buffer);
1475 bounds.x1=(double) geometry.x;
1476 bounds.y1=(double) geometry.y;
1477 bounds.x2=(double) geometry.x+scale.x;
1478 bounds.y2=(double) geometry.y+(geometry.height+text_size);
1479 if ((image_info->adjoin != MagickFalse) &&
1480 (GetNextImageInList(image) != (Image *) NULL))
1481 (void) CopyMagickString(buffer,"%%%%BoundingBox: (atend)\n",
1482 MaxTextExtent);
1483 else
1484 {
1485 (void) FormatMagickString(buffer,MaxTextExtent,
cristy06609ee2010-03-17 20:21:27 +00001486 "%%%%BoundingBox: %ld %ld %ld %ld\n",(long) ceil(bounds.x1-0.5),
1487 (long) ceil(bounds.y1-0.5),(long) floor(bounds.x2+0.5),
1488 (long) floor(bounds.y2+0.5));
cristy3ed852e2009-09-05 21:47:34 +00001489 (void) WriteBlobString(image,buffer);
1490 (void) FormatMagickString(buffer,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001491 "%%%%HiResBoundingBox: %g %g %g %g\n",bounds.x1,
cristy8cd5b312010-01-07 01:10:24 +00001492 bounds.y1,bounds.x2,bounds.y2);
cristy3ed852e2009-09-05 21:47:34 +00001493 }
1494 (void) WriteBlobString(image,buffer);
1495 profile=GetImageProfile(image,"8bim");
1496 if (profile != (StringInfo *) NULL)
1497 {
1498 /*
1499 Embed Photoshop profile.
1500 */
1501 (void) FormatMagickString(buffer,MaxTextExtent,
1502 "%%BeginPhotoshop: %lu",(unsigned long) GetStringInfoLength(
1503 profile));
1504 (void) WriteBlobString(image,buffer);
1505 for (i=0; i < (long) GetStringInfoLength(profile); i++)
1506 {
1507 if ((i % 32) == 0)
1508 (void) WriteBlobString(image,"\n% ");
1509 (void) FormatMagickString(buffer,MaxTextExtent,"%02X",
1510 (unsigned int) (GetStringInfoDatum(profile)[i] & 0xff));
1511 (void) WriteBlobString(image,buffer);
1512 }
1513 (void) WriteBlobString(image,"\n%EndPhotoshop\n");
1514 }
1515 profile=GetImageProfile(image,"xmp");
cristy2b2eb912010-03-03 14:56:40 +00001516 if (0 && (profile != (StringInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001517 {
1518 /*
1519 Embed XML profile.
1520 */
1521 (void) WriteBlobString(image,"\n%begin_xml_code\n");
cristy2b2eb912010-03-03 14:56:40 +00001522 (void) FormatMagickString(buffer,MaxTextExtent,
cristyeec18db2010-03-03 21:15:45 +00001523 "\n%%begin_xml_packet: %lu\n",(unsigned long)
1524 GetStringInfoLength(profile));
cristy2b2eb912010-03-03 14:56:40 +00001525 (void) WriteBlobString(image,buffer);
cristy3ed852e2009-09-05 21:47:34 +00001526 for (i=0; i < (long) GetStringInfoLength(profile); i++)
1527 (void) WriteBlobByte(image,GetStringInfoDatum(profile)[i]);
cristy2b2eb912010-03-03 14:56:40 +00001528 (void) WriteBlobString(image,"\n%end_xml_packet\n%end_xml_code\n");
cristy3ed852e2009-09-05 21:47:34 +00001529 }
1530 value=GetImageProperty(image,"label");
1531 if (value != (const char *) NULL)
1532 (void) WriteBlobString(image,
1533 "%%DocumentNeededResources: font Times-Roman\n");
1534 (void) WriteBlobString(image,"%%DocumentData: Clean7Bit\n");
1535 (void) WriteBlobString(image,"%%LanguageLevel: 1\n");
1536 if (LocaleCompare(image_info->magick,"PS") != 0)
1537 (void) WriteBlobString(image,"%%Pages: 1\n");
1538 else
1539 {
1540 /*
1541 Compute the number of pages.
1542 */
1543 (void) WriteBlobString(image,"%%Orientation: Portrait\n");
1544 (void) WriteBlobString(image,"%%PageOrder: Ascend\n");
1545 (void) FormatMagickString(buffer,MaxTextExtent,"%%%%Pages: %lu\n",
1546 image_info->adjoin != MagickFalse ? (unsigned long)
1547 GetImageListLength(image) : 1UL);
1548 (void) WriteBlobString(image,buffer);
1549 }
1550 (void) WriteBlobString(image,"%%EndComments\n");
1551 (void) WriteBlobString(image,"\n%%BeginDefaults\n");
1552 (void) WriteBlobString(image,"%%EndDefaults\n\n");
1553 if ((LocaleCompare(image_info->magick,"EPI") == 0) ||
1554 (LocaleCompare(image_info->magick,"EPSI") == 0) ||
1555 (LocaleCompare(image_info->magick,"EPT") == 0))
1556 {
1557 Image
1558 *preview_image;
1559
1560 long
1561 y;
1562
1563 Quantum
1564 pixel;
1565
1566 register long
1567 x;
1568
1569 /*
1570 Create preview image.
1571 */
1572 preview_image=CloneImage(image,0,0,MagickTrue,&image->exception);
1573 if (preview_image == (Image *) NULL)
1574 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1575 /*
1576 Dump image as bitmap.
1577 */
1578 (void) FormatMagickString(buffer,MaxTextExtent,
1579 "%%%%BeginPreview: %lu %lu %lu %lu\n%% ",preview_image->columns,
1580 preview_image->rows,1L,(((preview_image->columns+7) >> 3)*
1581 preview_image->rows+35)/36);
1582 (void) WriteBlobString(image,buffer);
1583 q=pixels;
1584 for (y=0; y < (long) image->rows; y++)
1585 {
1586 p=GetVirtualPixels(preview_image,0,y,preview_image->columns,1,
1587 &preview_image->exception);
1588 if (p == (const PixelPacket *) NULL)
1589 break;
1590 indexes=GetVirtualIndexQueue(preview_image);
1591 bit=0;
1592 byte=0;
1593 for (x=0; x < (long) preview_image->columns; x++)
1594 {
1595 byte<<=1;
1596 pixel=PixelIntensityToQuantum(p);
1597 if (pixel >= (Quantum) (QuantumRange/2))
1598 byte|=0x01;
1599 bit++;
1600 if (bit == 8)
1601 {
1602 q=PopHexPixel(hex_digits,byte,q);
1603 if ((q-pixels+8) >= 80)
1604 {
1605 *q++='\n';
1606 (void) WriteBlob(image,q-pixels,pixels);
1607 q=pixels;
1608 (void) WriteBlobString(image,"% ");
1609 };
1610 bit=0;
1611 byte=0;
1612 }
1613 }
1614 if (bit != 0)
1615 {
1616 byte<<=(8-bit);
1617 q=PopHexPixel(hex_digits,byte,q);
1618 if ((q-pixels+8) >= 80)
1619 {
1620 *q++='\n';
1621 (void) WriteBlob(image,q-pixels,pixels);
1622 q=pixels;
1623 (void) WriteBlobString(image,"% ");
1624 };
1625 };
1626 }
1627 if (q != pixels)
1628 {
1629 *q++='\n';
1630 (void) WriteBlob(image,q-pixels,pixels);
1631 }
1632 (void) WriteBlobString(image,"\n%%EndPreview\n");
1633 preview_image=DestroyImage(preview_image);
1634 }
1635 /*
1636 Output Postscript commands.
1637 */
1638 for (s=PostscriptProlog; *s != (char *) NULL; s++)
1639 {
1640 (void) FormatMagickString(buffer,MaxTextExtent,"%s\n",*s);
1641 (void) WriteBlobString(image,buffer);
1642 }
1643 value=GetImageProperty(image,"label");
1644 if (value != (const char *) NULL)
1645 for (j=(long) MultilineCensus(value)-1; j >= 0; j--)
1646 {
1647 (void) WriteBlobString(image," /label 512 string def\n");
1648 (void) WriteBlobString(image," currentfile label readline pop\n");
1649 (void) FormatMagickString(buffer,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001650 " 0 y %g add moveto label show pop\n",j*pointsize+12);
cristy3ed852e2009-09-05 21:47:34 +00001651 (void) WriteBlobString(image,buffer);
1652 }
1653 for (s=PostscriptEpilog; *s != (char *) NULL; s++)
1654 {
1655 (void) FormatMagickString(buffer,MaxTextExtent,"%s\n",*s);
1656 (void) WriteBlobString(image,buffer);
1657 }
1658 if (LocaleCompare(image_info->magick,"PS") == 0)
1659 (void) WriteBlobString(image," showpage\n");
1660 (void) WriteBlobString(image,"} bind def\n");
1661 (void) WriteBlobString(image,"%%EndProlog\n");
1662 }
1663 (void) FormatMagickString(buffer,MaxTextExtent,"%%%%Page: 1 %lu\n",page++);
1664 (void) WriteBlobString(image,buffer);
1665 (void) FormatMagickString(buffer,MaxTextExtent,
1666 "%%%%PageBoundingBox: %ld %ld %ld %ld\n",geometry.x,geometry.y,
1667 geometry.x+(long) geometry.width,geometry.y+(long) (geometry.height+
1668 text_size));
1669 (void) WriteBlobString(image,buffer);
1670 if ((double) geometry.x < bounds.x1)
1671 bounds.x1=(double) geometry.x;
1672 if ((double) geometry.y < bounds.y1)
1673 bounds.y1=(double) geometry.y;
1674 if ((double) (geometry.x+geometry.width-1) > bounds.x2)
1675 bounds.x2=(double) geometry.x+geometry.width-1;
1676 if ((double) (geometry.y+(geometry.height+text_size)-1) > bounds.y2)
1677 bounds.y2=(double) geometry.y+(geometry.height+text_size)-1;
1678 value=GetImageProperty(image,"label");
1679 if (value != (const char *) NULL)
1680 (void) WriteBlobString(image,"%%%%PageResources: font Times-Roman\n");
1681 if (LocaleCompare(image_info->magick,"PS") != 0)
1682 (void) WriteBlobString(image,"userdict begin\n");
1683 (void) WriteBlobString(image,"DisplayImage\n");
1684 /*
1685 Output image data.
1686 */
cristy8cd5b312010-01-07 01:10:24 +00001687 (void) FormatMagickString(buffer,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001688 "%ld %ld\n%g %g\n%g\n",geometry.x,geometry.y,scale.x,scale.y,
cristy8cd5b312010-01-07 01:10:24 +00001689 pointsize);
cristy3ed852e2009-09-05 21:47:34 +00001690 (void) WriteBlobString(image,buffer);
1691 labels=(char **) NULL;
1692 value=GetImageProperty(image,"label");
1693 if (value != (const char *) NULL)
1694 labels=StringToList(value);
1695 if (labels != (char **) NULL)
1696 {
1697 for (i=0; labels[i] != (char *) NULL; i++)
1698 {
1699 (void) FormatMagickString(buffer,MaxTextExtent,"%s \n",
1700 labels[i]);
1701 (void) WriteBlobString(image,buffer);
1702 labels[i]=DestroyString(labels[i]);
1703 }
1704 labels=(char **) RelinquishMagickMemory(labels);
1705 }
1706 (void) ResetMagickMemory(&pixel,0,sizeof(pixel));
1707 pixel.opacity=(Quantum) TransparentOpacity;
1708 index=(IndexPacket) 0;
1709 x=0;
1710 if ((image_info->type != TrueColorType) &&
1711 (IsGrayImage(image,&image->exception) != MagickFalse))
1712 {
1713 if (IsMonochromeImage(image,&image->exception) == MagickFalse)
1714 {
1715 Quantum
1716 pixel;
1717
1718 /*
1719 Dump image as grayscale.
1720 */
1721 (void) FormatMagickString(buffer,MaxTextExtent,
1722 "%lu %lu\n1\n1\n1\n8\n",image->columns,image->rows);
1723 (void) WriteBlobString(image,buffer);
1724 q=pixels;
1725 for (y=0; y < (long) image->rows; y++)
1726 {
1727 p=GetVirtualPixels(image,0,y,image->columns,1,
1728 &image->exception);
1729 if (p == (const PixelPacket *) NULL)
1730 break;
1731 for (x=0; x < (long) image->columns; x++)
1732 {
1733 pixel=ScaleQuantumToChar(PixelIntensityToQuantum(p));
1734 q=PopHexPixel(hex_digits,pixel,q);
1735 i++;
1736 if ((q-pixels+8) >= 80)
1737 {
1738 *q++='\n';
1739 (void) WriteBlob(image,q-pixels,pixels);
1740 q=pixels;
1741 }
1742 p++;
1743 }
1744 if (image->previous == (Image *) NULL)
1745 {
1746 status=SetImageProgress(image,SaveImageTag,y,image->rows);
1747 if (status == MagickFalse)
1748 break;
1749 }
1750 }
1751 if (q != pixels)
1752 {
1753 *q++='\n';
1754 (void) WriteBlob(image,q-pixels,pixels);
1755 }
1756 }
1757 else
1758 {
1759 long
1760 y;
1761
1762 Quantum
1763 pixel;
1764
1765 /*
1766 Dump image as bitmap.
1767 */
1768 (void) FormatMagickString(buffer,MaxTextExtent,
1769 "%lu %lu\n1\n1\n1\n1\n",image->columns,image->rows);
1770 (void) WriteBlobString(image,buffer);
1771 q=pixels;
1772 for (y=0; y < (long) image->rows; y++)
1773 {
1774 p=GetVirtualPixels(image,0,y,image->columns,1,
1775 &image->exception);
1776 if (p == (const PixelPacket *) NULL)
1777 break;
1778 indexes=GetVirtualIndexQueue(image);
1779 bit=0;
1780 byte=0;
1781 for (x=0; x < (long) image->columns; x++)
1782 {
1783 byte<<=1;
1784 pixel=PixelIntensityToQuantum(p);
1785 if (pixel >= (Quantum) (QuantumRange/2))
1786 byte|=0x01;
1787 bit++;
1788 if (bit == 8)
1789 {
1790 q=PopHexPixel(hex_digits,byte,q);
1791 if ((q-pixels+2) >= 80)
1792 {
1793 *q++='\n';
1794 (void) WriteBlob(image,q-pixels,pixels);
1795 q=pixels;
1796 };
1797 bit=0;
1798 byte=0;
1799 }
1800 p++;
1801 }
1802 if (bit != 0)
1803 {
1804 byte<<=(8-bit);
1805 q=PopHexPixel(hex_digits,byte,q);
1806 if ((q-pixels+2) >= 80)
1807 {
1808 *q++='\n';
1809 (void) WriteBlob(image,q-pixels,pixels);
1810 q=pixels;
1811 }
1812 };
1813 if (image->previous == (Image *) NULL)
1814 {
1815 status=SetImageProgress(image,SaveImageTag,y,image->rows);
1816 if (status == MagickFalse)
1817 break;
1818 }
1819 }
1820 if (q != pixels)
1821 {
1822 *q++='\n';
1823 (void) WriteBlob(image,q-pixels,pixels);
1824 }
1825 }
1826 }
1827 else
1828 if ((image->storage_class == DirectClass) ||
1829 (image->colors > 256) || (image->matte != MagickFalse))
1830 {
1831 /*
1832 Dump DirectClass image.
1833 */
1834 (void) FormatMagickString(buffer,MaxTextExtent,"%lu %lu\n0\n%d\n",
1835 image->columns,image->rows,
1836 image_info->compression == RLECompression ? 1 : 0);
1837 (void) WriteBlobString(image,buffer);
1838 switch (image_info->compression)
1839 {
1840 case RLECompression:
1841 {
1842 /*
1843 Dump runlength-encoded DirectColor packets.
1844 */
1845 q=pixels;
1846 for (y=0; y < (long) image->rows; y++)
1847 {
1848 p=GetVirtualPixels(image,0,y,image->columns,1,
1849 &image->exception);
1850 if (p == (const PixelPacket *) NULL)
1851 break;
1852 pixel=(*p);
1853 length=255;
1854 for (x=0; x < (long) image->columns; x++)
1855 {
1856 if ((p->red == pixel.red) && (p->green == pixel.green) &&
1857 (p->blue == pixel.blue) &&
1858 (p->opacity == pixel.opacity) && (length < 255) &&
1859 (x < (long) (image->columns-1)))
1860 length++;
1861 else
1862 {
1863 if (x > 0)
1864 {
1865 WriteRunlengthPacket(image,pixel,length,p);
1866 if ((q-pixels+10) >= 80)
1867 {
1868 *q++='\n';
1869 (void) WriteBlob(image,q-pixels,pixels);
1870 q=pixels;
1871 }
1872 }
1873 length=0;
1874 }
1875 pixel=(*p);
1876 p++;
1877 }
1878 WriteRunlengthPacket(image,pixel,length,p);
1879 if ((q-pixels+10) >= 80)
1880 {
1881 *q++='\n';
1882 (void) WriteBlob(image,q-pixels,pixels);
1883 q=pixels;
1884 }
1885 if (image->previous == (Image *) NULL)
1886 {
1887 status=SetImageProgress(image,SaveImageTag,y,image->rows);
1888 if (status == MagickFalse)
1889 break;
1890 }
1891 }
1892 if (q != pixels)
1893 {
1894 *q++='\n';
1895 (void) WriteBlob(image,q-pixels,pixels);
1896 }
1897 break;
1898 }
1899 case NoCompression:
1900 default:
1901 {
1902 /*
1903 Dump uncompressed DirectColor packets.
1904 */
1905 q=pixels;
1906 for (y=0; y < (long) image->rows; y++)
1907 {
1908 p=GetVirtualPixels(image,0,y,image->columns,1,
1909 &image->exception);
1910 if (p == (const PixelPacket *) NULL)
1911 break;
1912 for (x=0; x < (long) image->columns; x++)
1913 {
1914 if ((image->matte != MagickFalse) &&
1915 (p->opacity == (Quantum) TransparentOpacity))
1916 {
1917 q=PopHexPixel(hex_digits,0xff,q);
1918 q=PopHexPixel(hex_digits,0xff,q);
1919 q=PopHexPixel(hex_digits,0xff,q);
1920 }
1921 else
1922 {
cristyce70c172010-01-07 17:15:30 +00001923 q=PopHexPixel(hex_digits,ScaleQuantumToChar(GetRedPixelComponent(p)),q);
1924 q=PopHexPixel(hex_digits,ScaleQuantumToChar(GetGreenPixelComponent(p)),q);
1925 q=PopHexPixel(hex_digits,ScaleQuantumToChar(GetBluePixelComponent(p)),q);
cristy3ed852e2009-09-05 21:47:34 +00001926 }
1927 if ((q-pixels+6) >= 80)
1928 {
1929 *q++='\n';
1930 (void) WriteBlob(image,q-pixels,pixels);
1931 q=pixels;
1932 }
1933 p++;
1934 }
1935 if (image->previous == (Image *) NULL)
1936 {
1937 status=SetImageProgress(image,SaveImageTag,y,image->rows);
1938 if (status == MagickFalse)
1939 break;
1940 }
1941 }
1942 if (q != pixels)
1943 {
1944 *q++='\n';
1945 (void) WriteBlob(image,q-pixels,pixels);
1946 }
1947 break;
1948 }
1949 }
1950 (void) WriteBlobByte(image,'\n');
1951 }
1952 else
1953 {
1954 /*
1955 Dump PseudoClass image.
1956 */
1957 (void) FormatMagickString(buffer,MaxTextExtent,"%lu %lu\n%d\n%d\n0\n",
1958 image->columns,image->rows,
1959 image->storage_class == PseudoClass ? 1 : 0,
1960 image_info->compression == RLECompression ? 1 : 0);
1961 (void) WriteBlobString(image,buffer);
1962 /*
1963 Dump number of colors and colormap.
1964 */
1965 (void) FormatMagickString(buffer,MaxTextExtent,"%lu\n",image->colors);
1966 (void) WriteBlobString(image,buffer);
1967 for (i=0; i < (long) image->colors; i++)
1968 {
1969 (void) FormatMagickString(buffer,MaxTextExtent,"%02X%02X%02X\n",
1970 ScaleQuantumToChar(image->colormap[i].red),
1971 ScaleQuantumToChar(image->colormap[i].green),
1972 ScaleQuantumToChar(image->colormap[i].blue));
1973 (void) WriteBlobString(image,buffer);
1974 }
1975 switch (image_info->compression)
1976 {
1977 case RLECompression:
1978 {
1979 /*
1980 Dump runlength-encoded PseudoColor packets.
1981 */
1982 q=pixels;
1983 for (y=0; y < (long) image->rows; y++)
1984 {
1985 p=GetVirtualPixels(image,0,y,image->columns,1,
1986 &image->exception);
1987 if (p == (const PixelPacket *) NULL)
1988 break;
1989 indexes=GetVirtualIndexQueue(image);
1990 index=(*indexes);
1991 length=255;
1992 for (x=0; x < (long) image->columns; x++)
1993 {
1994 if ((index == indexes[x]) && (length < 255) &&
1995 (x < ((long) image->columns-1)))
1996 length++;
1997 else
1998 {
1999 if (x > 0)
2000 {
2001 q=PopHexPixel(hex_digits,index,q);
2002 q=PopHexPixel(hex_digits,(unsigned long)
2003 MagickMin(length,0xff),q);
2004 i++;
2005 if ((q-pixels+6) >= 80)
2006 {
2007 *q++='\n';
2008 (void) WriteBlob(image,q-pixels,pixels);
2009 q=pixels;
2010 }
2011 }
2012 length=0;
2013 }
2014 index=indexes[x];
2015 pixel=(*p);
2016 p++;
2017 }
2018 q=PopHexPixel(hex_digits,index,q);
2019 q=PopHexPixel(hex_digits,(unsigned long)
2020 MagickMin(length,0xff),q);
2021 if (image->previous == (Image *) NULL)
2022 {
2023 status=SetImageProgress(image,SaveImageTag,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 break;
2034 }
2035 case NoCompression:
2036 default:
2037 {
2038 /*
2039 Dump uncompressed PseudoColor packets.
2040 */
2041 q=pixels;
2042 for (y=0; y < (long) image->rows; y++)
2043 {
2044 p=GetVirtualPixels(image,0,y,image->columns,1,
2045 &image->exception);
2046 if (p == (const PixelPacket *) NULL)
2047 break;
2048 indexes=GetVirtualIndexQueue(image);
2049 for (x=0; x < (long) image->columns; x++)
2050 {
2051 q=PopHexPixel(hex_digits,indexes[x],q);
2052 if ((q-pixels+4) >= 80)
2053 {
2054 *q++='\n';
2055 (void) WriteBlob(image,q-pixels,pixels);
2056 q=pixels;
2057 }
2058 p++;
2059 }
2060 if (image->previous == (Image *) NULL)
2061 {
2062 status=SetImageProgress(image,SaveImageTag,y,image->rows);
2063 if (status == MagickFalse)
2064 break;
2065 }
2066 }
2067 if (q != pixels)
2068 {
2069 *q++='\n';
2070 (void) WriteBlob(image,q-pixels,pixels);
2071 }
2072 break;
2073 }
2074 }
2075 (void) WriteBlobByte(image,'\n');
2076 }
2077 if (LocaleCompare(image_info->magick,"PS") != 0)
2078 (void) WriteBlobString(image,"end\n");
2079 (void) WriteBlobString(image,"%%PageTrailer\n");
2080 if (GetNextImageInList(image) == (Image *) NULL)
2081 break;
2082 image=SyncNextImageInList(image);
2083 status=SetImageProgress(image,SaveImagesTag,scene++,
2084 GetImageListLength(image));
2085 if (status == MagickFalse)
2086 break;
2087 } while (image_info->adjoin != MagickFalse);
2088 (void) WriteBlobString(image,"%%Trailer\n");
2089 if (page > 2)
2090 {
2091 (void) FormatMagickString(buffer,MaxTextExtent,
cristy06609ee2010-03-17 20:21:27 +00002092 "%%%%BoundingBox: %ld %ld %ld %ld\n",(long) ceil(bounds.x1-0.5),
2093 (long) ceil(bounds.y1-0.5),(long) floor(bounds.x2+0.5),(long)
2094 floor(bounds.y2+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002095 (void) WriteBlobString(image,buffer);
2096 (void) FormatMagickString(buffer,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00002097 "%%%%HiResBoundingBox: %g %g %g %g\n",bounds.x1,bounds.y1,
cristy8cd5b312010-01-07 01:10:24 +00002098 bounds.x2,bounds.y2);
cristy3ed852e2009-09-05 21:47:34 +00002099 (void) WriteBlobString(image,buffer);
2100 }
2101 (void) WriteBlobString(image,"%%EOF\n");
2102 (void) CloseBlob(image);
2103 return(MagickTrue);
2104}