blob: 5796321011ce1fb32c85956a72a536b7d07bb383 [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% %
cristy7e41fe82010-12-04 23:12:08 +000020% Copyright 1999-2011 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*/
cristy4c08aed2011-07-01 19:47:50 +000042#include "MagickCore/studio.h"
43#include "MagickCore/attribute.h"
44#include "MagickCore/blob.h"
45#include "MagickCore/blob-private.h"
46#include "MagickCore/cache.h"
47#include "MagickCore/color.h"
48#include "MagickCore/color-private.h"
49#include "MagickCore/colorspace.h"
50#include "MagickCore/constitute.h"
51#include "MagickCore/delegate.h"
52#include "MagickCore/delegate-private.h"
53#include "MagickCore/draw.h"
54#include "MagickCore/exception.h"
55#include "MagickCore/exception-private.h"
56#include "MagickCore/geometry.h"
57#include "MagickCore/image.h"
58#include "MagickCore/image-private.h"
59#include "MagickCore/list.h"
60#include "MagickCore/magick.h"
61#include "MagickCore/memory_.h"
62#include "MagickCore/monitor.h"
63#include "MagickCore/monitor-private.h"
64#include "MagickCore/option.h"
65#include "MagickCore/profile.h"
66#include "MagickCore/resource_.h"
67#include "MagickCore/pixel-accessor.h"
68#include "MagickCore/property.h"
69#include "MagickCore/quantum-private.h"
70#include "MagickCore/static.h"
71#include "MagickCore/string_.h"
72#include "MagickCore/module.h"
73#include "MagickCore/token.h"
74#include "MagickCore/transform.h"
75#include "MagickCore/utility.h"
cristy3ed852e2009-09-05 21:47:34 +000076
77/*
78 Forward declarations.
79*/
80static MagickBooleanType
81 WritePSImage(const ImageInfo *,Image *);
82
83/*
84%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
85% %
86% %
87% %
88% I n v o k e P o s t s r i p t D e l e g a t e %
89% %
90% %
91% %
92%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
93%
cristydefb3f02009-09-10 02:18:35 +000094% InvokePostscriptDelegate() executes the Postscript interpreter with the
cristy3ed852e2009-09-05 21:47:34 +000095% specified command.
96%
97% The format of the InvokePostscriptDelegate method is:
98%
99% MagickBooleanType InvokePostscriptDelegate(
cristyb32b90a2009-09-07 21:45:48 +0000100% const MagickBooleanType verbose,const char *command,
101% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000102%
103% A description of each parameter follows:
104%
105% o verbose: A value other than zero displays the command prior to
106% executing it.
107%
108% o command: the address of a character string containing the command to
109% execute.
110%
cristyb32b90a2009-09-07 21:45:48 +0000111% o exception: return any errors or warnings in this structure.
112%
cristy3ed852e2009-09-05 21:47:34 +0000113*/
114static MagickBooleanType InvokePostscriptDelegate(
cristyb32b90a2009-09-07 21:45:48 +0000115 const MagickBooleanType verbose,const char *command,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000116{
cristyb32b90a2009-09-07 21:45:48 +0000117 int
118 status;
119
cristy0157aea2010-04-24 21:12:18 +0000120#if defined(MAGICKCORE_GS_DELEGATE) || defined(MAGICKCORE_WINDOWS_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000121 char
122 **argv;
123
cristydefb3f02009-09-10 02:18:35 +0000124 const GhostInfo
125 *ghost_info;
cristy3ed852e2009-09-05 21:47:34 +0000126
127 gs_main_instance
128 *interpreter;
129
130 int
131 argc,
cristyb32b90a2009-09-07 21:45:48 +0000132 code;
cristy3ed852e2009-09-05 21:47:34 +0000133
cristybb503372010-05-27 20:51:26 +0000134 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000135 i;
136
cristy0157aea2010-04-24 21:12:18 +0000137#if defined(MAGICKCORE_WINDOWS_SUPPORT)
cristydefb3f02009-09-10 02:18:35 +0000138 ghost_info=NTGhostscriptDLLVectors();
cristy3ed852e2009-09-05 21:47:34 +0000139#else
cristydefb3f02009-09-10 02:18:35 +0000140 GhostInfo
141 ghost_info_struct;
cristy3ed852e2009-09-05 21:47:34 +0000142
cristydefb3f02009-09-10 02:18:35 +0000143 ghost_info=(&ghost_info_struct);
144 (void) ResetMagickMemory(&ghost_info,0,sizeof(ghost_info));
145 ghost_info_struct.new_instance=(int (*)(gs_main_instance **,void *))
cristy3ed852e2009-09-05 21:47:34 +0000146 gsapi_new_instance;
cristydefb3f02009-09-10 02:18:35 +0000147 ghost_info_struct.init_with_args=(int (*)(gs_main_instance *,int,char **))
cristy3ed852e2009-09-05 21:47:34 +0000148 gsapi_init_with_args;
cristydefb3f02009-09-10 02:18:35 +0000149 ghost_info_struct.run_string=(int (*)(gs_main_instance *,const char *,int,
150 int *)) gsapi_run_string;
151 ghost_info_struct.delete_instance=(void (*)(gs_main_instance *))
cristy3ed852e2009-09-05 21:47:34 +0000152 gsapi_delete_instance;
cristydefb3f02009-09-10 02:18:35 +0000153 ghost_info_struct.exit=(int (*)(gs_main_instance *)) gsapi_exit;
cristy3ed852e2009-09-05 21:47:34 +0000154#endif
cristydefb3f02009-09-10 02:18:35 +0000155 if (ghost_info == (GhostInfo *) NULL)
cristyb32b90a2009-09-07 21:45:48 +0000156 {
cristy6de4bc22010-01-12 17:10:35 +0000157 status=SystemCommand(MagickFalse,verbose,command,exception);
cristy41083a42009-09-07 23:47:59 +0000158 return(status == 0 ? MagickTrue : MagickFalse);
cristyb32b90a2009-09-07 21:45:48 +0000159 }
cristy3ed852e2009-09-05 21:47:34 +0000160 if (verbose != MagickFalse)
161 {
162 (void) fputs("[ghostscript library]",stdout);
163 (void) fputs(strchr(command,' '),stdout);
164 }
cristydefb3f02009-09-10 02:18:35 +0000165 status=(ghost_info->new_instance)(&interpreter,(void *) NULL);
cristy3ed852e2009-09-05 21:47:34 +0000166 if (status < 0)
cristyb32b90a2009-09-07 21:45:48 +0000167 {
cristy6de4bc22010-01-12 17:10:35 +0000168 status=SystemCommand(MagickFalse,verbose,command,exception);
cristy41083a42009-09-07 23:47:59 +0000169 return(status == 0 ? MagickTrue : MagickFalse);
cristyb32b90a2009-09-07 21:45:48 +0000170 }
cristya73c0f82010-12-20 15:57:05 +0000171 code=0;
cristy3ed852e2009-09-05 21:47:34 +0000172 argv=StringToArgv(command,&argc);
cristydefb3f02009-09-10 02:18:35 +0000173 status=(ghost_info->init_with_args)(interpreter,argc-1,argv+1);
cristy3ed852e2009-09-05 21:47:34 +0000174 if (status == 0)
cristydefb3f02009-09-10 02:18:35 +0000175 status=(ghost_info->run_string)(interpreter,"systemdict /start get exec\n",
176 0,&code);
177 (ghost_info->exit)(interpreter);
178 (ghost_info->delete_instance)(interpreter);
cristy0157aea2010-04-24 21:12:18 +0000179#if defined(MAGICKCORE_WINDOWS_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000180 NTGhostscriptUnLoadDLL();
181#endif
cristybb503372010-05-27 20:51:26 +0000182 for (i=0; i < (ssize_t) argc; i++)
cristy3ed852e2009-09-05 21:47:34 +0000183 argv[i]=DestroyString(argv[i]);
184 argv=(char **) RelinquishMagickMemory(argv);
cristy41083a42009-09-07 23:47:59 +0000185 if ((status != 0) && (status != -101))
186 {
187 char
188 *message;
cristyb32b90a2009-09-07 21:45:48 +0000189
cristy41083a42009-09-07 23:47:59 +0000190 message=GetExceptionMessage(errno);
191 (void) ThrowMagickException(exception,GetMagickModule(),DelegateError,
192 "`%s': %s",command,message);
193 message=DestroyString(message);
194 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
195 "Ghostscript returns status %d, exit code %d",status,code);
196 return(MagickFalse);
197 }
cristy3ed852e2009-09-05 21:47:34 +0000198 return(MagickTrue);
199#else
cristy6de4bc22010-01-12 17:10:35 +0000200 status=SystemCommand(MagickFalse,verbose,command,exception);
cristy41083a42009-09-07 23:47:59 +0000201 return(status == 0 ? MagickTrue : MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +0000202#endif
203}
204
205/*
206%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
207% %
208% %
209% %
210% I s P S %
211% %
212% %
213% %
214%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
215%
216% IsPS() returns MagickTrue if the image format type, identified by the
217% magick string, is PS.
218%
219% The format of the IsPS method is:
220%
221% MagickBooleanType IsPS(const unsigned char *magick,const size_t length)
222%
223% A description of each parameter follows:
224%
225% o magick: compare image format pattern against these bytes.
226%
227% o length: Specifies the length of the magick string.
228%
229*/
230static MagickBooleanType IsPS(const unsigned char *magick,const size_t length)
231{
232 if (length < 4)
233 return(MagickFalse);
234 if (memcmp(magick,"%!",2) == 0)
235 return(MagickTrue);
236 if (memcmp(magick,"\004%!",3) == 0)
237 return(MagickTrue);
238 return(MagickFalse);
239}
240
241/*
242%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
243% %
244% %
245% %
246% R e a d P S I m a g e %
247% %
248% %
249% %
250%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
251%
252% ReadPSImage() reads a Postscript image file and returns it. It allocates
253% the memory necessary for the new Image structure and returns a pointer
254% to the new image.
255%
256% The format of the ReadPSImage method is:
257%
258% Image *ReadPSImage(const ImageInfo *image_info,ExceptionInfo *exception)
259%
260% A description of each parameter follows:
261%
262% o image_info: the image info.
263%
264% o exception: return any errors or warnings in this structure.
265%
266*/
267
268static MagickBooleanType IsPostscriptRendered(const char *path)
269{
270 MagickBooleanType
271 status;
272
273 struct stat
274 attributes;
275
276 if ((path == (const char *) NULL) || (*path == '\0'))
277 return(MagickFalse);
278 status=GetPathAttributes(path,&attributes);
279 if ((status != MagickFalse) && S_ISREG(attributes.st_mode) &&
280 (attributes.st_size > 0))
281 return(MagickTrue);
282 return(MagickFalse);
283}
284
285static inline int ProfileInteger(Image *image,short int *hex_digits)
286{
287 int
288 c,
289 l,
290 value;
291
cristybb503372010-05-27 20:51:26 +0000292 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000293 i;
294
295 l=0;
296 value=0;
297 for (i=0; i < 2; )
298 {
299 c=ReadBlobByte(image);
300 if ((c == EOF) || ((c == '%') && (l == '%')))
301 {
302 value=(-1);
303 break;
304 }
305 l=c;
306 c&=0xff;
307 if (isxdigit(c) == MagickFalse)
308 continue;
cristybb503372010-05-27 20:51:26 +0000309 value=(int) ((size_t) value << 4)+hex_digits[c];
cristy3ed852e2009-09-05 21:47:34 +0000310 i++;
311 }
312 return(value);
313}
314
315static Image *ReadPSImage(const ImageInfo *image_info,ExceptionInfo *exception)
316{
317#define BoundingBox "BoundingBox:"
318#define BeginDocument "BeginDocument:"
319#define BeginXMPPacket "<?xpacket begin="
320#define EndXMPPacket "<?xpacket end="
321#define ICCProfile "BeginICCProfile:"
322#define CMYKCustomColor "CMYKCustomColor:"
cristy01ca8922010-03-17 12:20:37 +0000323#define CMYKProcessColor "CMYKProcessColor:"
cristy3ed852e2009-09-05 21:47:34 +0000324#define DocumentMedia "DocumentMedia:"
325#define DocumentCustomColors "DocumentCustomColors:"
326#define DocumentProcessColors "DocumentProcessColors:"
327#define EndDocument "EndDocument:"
328#define HiResBoundingBox "HiResBoundingBox:"
329#define ImageData "ImageData:"
330#define PageBoundingBox "PageBoundingBox:"
331#define LanguageLevel "LanguageLevel:"
332#define PageMedia "PageMedia:"
333#define Pages "Pages:"
334#define PhotoshopProfile "BeginPhotoshop:"
335#define PostscriptLevel "!PS-"
336#define RenderPostscriptText " Rendering Postscript... "
337#define SpotColor "+ "
338
339 char
340 command[MaxTextExtent],
341 density[MaxTextExtent],
342 filename[MaxTextExtent],
343 geometry[MaxTextExtent],
344 input_filename[MaxTextExtent],
345 options[MaxTextExtent],
cristyc39e3d62010-10-14 16:52:01 +0000346 postscript_filename[MaxTextExtent];
cristy3ed852e2009-09-05 21:47:34 +0000347
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
cristybb503372010-05-27 20:51:26 +0000386 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000387 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
cristyf2faecf2010-05-28 19:19:36 +0000405 unsigned long
cristy3ed852e2009-09-05 21:47:34 +0000406 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);
cristy97841ba2010-09-02 15:48:08 +0000526 if (LocaleNCompare(ImageData,command,strlen(ImageData)) == 0)
cristy3ed852e2009-09-05 21:47:34 +0000527 (void) sscanf(command,ImageData " %lu %lu",&columns,&rows);
cristy97841ba2010-09-02 15:48:08 +0000528 if (LocaleNCompare(ICCProfile,command,strlen(ICCProfile)) == 0)
cristy3ed852e2009-09-05 21:47:34 +0000529 {
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);
cristybb503372010-05-27 20:51:26 +0000561 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +0000562 *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;
cristy3ed852e2009-09-05 21:47:34 +0000611 length=strlen(DocumentCustomColors);
612 if ((LocaleNCompare(DocumentCustomColors,command,length) == 0) ||
613 (LocaleNCompare(CMYKCustomColor,command,strlen(CMYKCustomColor)) == 0) ||
614 (LocaleNCompare(SpotColor,command,strlen(SpotColor)) == 0))
615 {
616 char
617 property[MaxTextExtent],
618 *value;
619
620 register char
621 *p;
622
623 /*
624 Note spot names.
625 */
cristyb51dff52011-05-19 16:55:47 +0000626 (void) FormatLocaleString(property,MaxTextExtent,"ps:SpotColor-%.20g",
cristye8c25f92010-06-03 00:53:06 +0000627 (double) (spotcolor++));
cristy3ed852e2009-09-05 21:47:34 +0000628 for (p=command; *p != '\0'; p++)
629 if (isspace((int) (unsigned char) *p) != 0)
630 break;
631 value=AcquireString(p);
632 (void) SubstituteString(&value,"(","");
633 (void) SubstituteString(&value,")","");
634 (void) StripString(value);
635 (void) SetImageProperty(image,property,value);
636 value=DestroyString(value);
637 continue;
638 }
639 /*
640 Note region defined by bounding box.
641 */
642 count=0;
643 if (LocaleNCompare(BoundingBox,command,strlen(BoundingBox)) == 0)
644 count=(ssize_t) sscanf(command,BoundingBox " %lf %lf %lf %lf",&bounds.x1,
645 &bounds.y1,&bounds.x2,&bounds.y2);
646 if (LocaleNCompare(DocumentMedia,command,strlen(DocumentMedia)) == 0)
647 count=(ssize_t) sscanf(command,DocumentMedia " %*s %lf %lf",&bounds.x2,
648 &bounds.y2)+2;
649 if (LocaleNCompare(HiResBoundingBox,command,strlen(HiResBoundingBox)) == 0)
650 count=(ssize_t) sscanf(command,HiResBoundingBox " %lf %lf %lf %lf",
651 &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
652 if (LocaleNCompare(PageBoundingBox,command,strlen(PageBoundingBox)) == 0)
653 count=(ssize_t) sscanf(command,PageBoundingBox " %lf %lf %lf %lf",
654 &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
655 if (LocaleNCompare(PageMedia,command,strlen(PageMedia)) == 0)
656 count=(ssize_t) sscanf(command,PageMedia " %*s %lf %lf",&bounds.x2,
657 &bounds.y2)+2;
658 if (count != 4)
659 continue;
660 if (((bounds.x2 > hires_bounds.x2) && (bounds.y2 > hires_bounds.y2)) ||
661 ((hires_bounds.x2 == 0.0) && (hires_bounds.y2 == 0.0)))
662 {
663 /*
664 Set Postscript render geometry.
665 */
cristyb51dff52011-05-19 16:55:47 +0000666 (void) FormatLocaleString(geometry,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +0000667 "%gx%g%+.15g%+.15g",bounds.x2-bounds.x1,bounds.y2-bounds.y1,
cristy8cd5b312010-01-07 01:10:24 +0000668 bounds.x1,bounds.y1);
cristy3ed852e2009-09-05 21:47:34 +0000669 (void) SetImageProperty(image,"ps:HiResBoundingBox",geometry);
cristybb503372010-05-27 20:51:26 +0000670 page.width=(size_t) floor(bounds.x2-bounds.x1+0.5);
671 page.height=(size_t) floor(bounds.y2-bounds.y1+0.5);
cristy3ed852e2009-09-05 21:47:34 +0000672 hires_bounds=bounds;
673 }
674 }
675 (void) CloseBlob(image);
676 if (image_info->colorspace == RGBColorspace)
677 cmyk=MagickFalse;
678 /*
679 Create Ghostscript control file.
680 */
681 file=AcquireUniqueFileResource(postscript_filename);
682 if (file == -1)
683 {
684 ThrowFileException(&image->exception,FileOpenError,"UnableToOpenFile",
685 image_info->filename);
686 image=DestroyImageList(image);
687 return((Image *) NULL);
688 }
689 (void) CopyMagickString(command,"/setpagedevice {pop} bind 1 index where {"
690 "dup wcheck {3 1 roll put} {pop def} ifelse} {def} ifelse\n"
691 "<</UseCIEColor true>>setpagedevice\n",MaxTextExtent);
692 count=write(file,command,(unsigned int) strlen(command));
cristyc39e3d62010-10-14 16:52:01 +0000693 if (image_info->page == (char *) NULL)
694 {
695 char
696 translate_geometry[MaxTextExtent];
697
cristyb51dff52011-05-19 16:55:47 +0000698 (void) FormatLocaleString(translate_geometry,MaxTextExtent,
cristyc39e3d62010-10-14 16:52:01 +0000699 "%g %g translate\n",-bounds.x1,-bounds.y1);
700 count=write(file,translate_geometry,(unsigned int)
701 strlen(translate_geometry));
702 }
cristy3ed852e2009-09-05 21:47:34 +0000703 file=close(file)-1;
704 /*
705 Render Postscript with the Ghostscript delegate.
706 */
707 if (image_info->monochrome != MagickFalse)
708 delegate_info=GetDelegateInfo("ps:mono",(char *) NULL,exception);
709 else
710 if (cmyk != MagickFalse)
711 delegate_info=GetDelegateInfo("ps:cmyk",(char *) NULL,exception);
712 else
cristya97426c2011-02-04 01:41:27 +0000713 delegate_info=GetDelegateInfo("ps:alpha",(char *) NULL,exception);
cristy3ed852e2009-09-05 21:47:34 +0000714 if (delegate_info == (const DelegateInfo *) NULL)
715 {
716 (void) RelinquishUniqueFileResource(postscript_filename);
717 image=DestroyImageList(image);
718 return((Image *) NULL);
719 }
720 *options='\0';
721 if ((page.width == 0) || (page.height == 0))
722 (void) ParseAbsoluteGeometry(PSPageGeometry,&page);
cristydba40d42010-03-25 18:31:50 +0000723 if (image_info->density != (char *) NULL)
724 {
725 flags=ParseGeometry(image_info->density,&geometry_info);
726 image->x_resolution=geometry_info.rho;
727 image->y_resolution=geometry_info.sigma;
728 if ((flags & SigmaValue) == 0)
729 image->y_resolution=image->x_resolution;
730 }
cristyb51dff52011-05-19 16:55:47 +0000731 (void) FormatLocaleString(density,MaxTextExtent,"%gx%g",
cristy8cd5b312010-01-07 01:10:24 +0000732 image->x_resolution,image->y_resolution);
cristydba40d42010-03-25 18:31:50 +0000733 if (image_info->page != (char *) NULL)
734 (void) ParseAbsoluteGeometry(image_info->page,&page);
cristya97426c2011-02-04 01:41:27 +0000735 page.width=(size_t) floor((double) (page.width*image->x_resolution/delta.x)+
736 0.5);
737 page.height=(size_t) floor((double) (page.height*image->y_resolution/delta.y)+
cristy06609ee2010-03-17 20:21:27 +0000738 0.5);
cristyb51dff52011-05-19 16:55:47 +0000739 (void) FormatLocaleString(options,MaxTextExtent,"-g%.20gx%.20g ",(double)
cristye8c25f92010-06-03 00:53:06 +0000740 page.width,(double) page.height);
cristy3ed852e2009-09-05 21:47:34 +0000741 read_info=CloneImageInfo(image_info);
742 *read_info->magick='\0';
743 if (read_info->number_scenes != 0)
744 {
745 char
746 pages[MaxTextExtent];
747
cristyb51dff52011-05-19 16:55:47 +0000748 (void) FormatLocaleString(pages,MaxTextExtent,"-dFirstPage=%.20g "
cristye8c25f92010-06-03 00:53:06 +0000749 "-dLastPage=%.20g",(double) read_info->scene+1,(double)
cristyf2faecf2010-05-28 19:19:36 +0000750 (read_info->scene+read_info->number_scenes));
cristy3ed852e2009-09-05 21:47:34 +0000751 (void) ConcatenateMagickString(options,pages,MaxTextExtent);
752 read_info->number_scenes=0;
753 if (read_info->scenes != (char *) NULL)
754 *read_info->scenes='\0';
755 }
756 option=GetImageOption(image_info,"ps:use-cropbox");
757 if ((option != (const char *) NULL) && (IsMagickTrue(option) != MagickFalse))
758 (void) ConcatenateMagickString(options,"-dEPSCrop ",MaxTextExtent);
759 (void) CopyMagickString(filename,read_info->filename,MaxTextExtent);
cristya97426c2011-02-04 01:41:27 +0000760 (void) AcquireUniqueFilename(filename);
761 (void) ConcatenateMagickString(filename,"-%08d",MaxTextExtent);
cristyb51dff52011-05-19 16:55:47 +0000762 (void) FormatLocaleString(command,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +0000763 GetDelegateCommands(delegate_info),
764 read_info->antialias != MagickFalse ? 4 : 1,
cristya97426c2011-02-04 01:41:27 +0000765 read_info->antialias != MagickFalse ? 4 : 1,density,options,filename,
766 postscript_filename,input_filename);
cristyb32b90a2009-09-07 21:45:48 +0000767 status=InvokePostscriptDelegate(read_info->verbose,command,exception);
cristya97426c2011-02-04 01:41:27 +0000768 (void) InterpretImageFilename(image_info,image,filename,1,
769 read_info->filename);
cristy41083a42009-09-07 23:47:59 +0000770 if ((status == MagickFalse) ||
cristy3ed852e2009-09-05 21:47:34 +0000771 (IsPostscriptRendered(read_info->filename) == MagickFalse))
772 {
773 (void) ConcatenateMagickString(command," -c showpage",MaxTextExtent);
cristyb32b90a2009-09-07 21:45:48 +0000774 status=InvokePostscriptDelegate(read_info->verbose,command,exception);
cristy3ed852e2009-09-05 21:47:34 +0000775 }
cristy3ed852e2009-09-05 21:47:34 +0000776 (void) RelinquishUniqueFileResource(postscript_filename);
cristy3ed852e2009-09-05 21:47:34 +0000777 (void) RelinquishUniqueFileResource(input_filename);
cristya97426c2011-02-04 01:41:27 +0000778 postscript_image=(Image *) NULL;
779 if (status == MagickFalse)
780 for (i=1; ; i++)
781 {
cristya97426c2011-02-04 01:41:27 +0000782 (void) InterpretImageFilename(image_info,image,filename,(int) i,
783 read_info->filename);
784 if (IsPostscriptRendered(read_info->filename) == MagickFalse)
785 break;
786 (void) RelinquishUniqueFileResource(read_info->filename);
787 }
788 else
789 for (i=1; ; i++)
790 {
cristya97426c2011-02-04 01:41:27 +0000791 (void) InterpretImageFilename(image_info,image,filename,(int) i,
792 read_info->filename);
793 if (IsPostscriptRendered(read_info->filename) == MagickFalse)
794 break;
795 next=ReadImage(read_info,exception);
796 (void) RelinquishUniqueFileResource(read_info->filename);
797 if (next == (Image *) NULL)
798 break;
799 AppendImageToList(&postscript_image,next);
800 }
801 (void) RelinquishUniqueFileResource(read_info->filename);
cristy3ed852e2009-09-05 21:47:34 +0000802 read_info=DestroyImageInfo(read_info);
803 if (postscript_image == (Image *) NULL)
804 {
805 image=DestroyImageList(image);
806 ThrowFileException(exception,DelegateError,"PostscriptDelegateFailed",
807 image_info->filename);
808 return((Image *) NULL);
809 }
810 if (LocaleCompare(postscript_image->magick,"BMP") == 0)
811 {
812 Image
813 *cmyk_image;
814
815 cmyk_image=ConsolidateCMYKImages(postscript_image,exception);
816 if (cmyk_image != (Image *) NULL)
817 {
818 postscript_image=DestroyImageList(postscript_image);
819 postscript_image=cmyk_image;
820 }
821 }
822 if (image_info->number_scenes != 0)
823 {
824 Image
825 *clone_image;
826
cristybb503372010-05-27 20:51:26 +0000827 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000828 i;
829
830 /*
831 Add place holder images to meet the subimage specification requirement.
832 */
cristybb503372010-05-27 20:51:26 +0000833 for (i=0; i < (ssize_t) image_info->scene; i++)
cristy3ed852e2009-09-05 21:47:34 +0000834 {
835 clone_image=CloneImage(postscript_image,1,1,MagickTrue,exception);
836 if (clone_image != (Image *) NULL)
837 PrependImageToList(&postscript_image,clone_image);
838 }
839 }
840 do
841 {
842 (void) CopyMagickString(postscript_image->filename,filename,MaxTextExtent);
843 if (columns != 0)
844 postscript_image->magick_columns=columns;
845 if (rows != 0)
846 postscript_image->magick_rows=rows;
847 postscript_image->page=page;
848 (void) CloneImageProfiles(postscript_image,image);
849 (void) CloneImageProperties(postscript_image,image);
850 next=SyncNextImageInList(postscript_image);
851 if (next != (Image *) NULL)
852 postscript_image=next;
853 } while (next != (Image *) NULL);
854 image=DestroyImageList(image);
855 scene=0;
856 for (next=GetFirstImageInList(postscript_image); next != (Image *) NULL; )
857 {
858 next->scene=scene++;
859 next=GetNextImageInList(next);
860 }
861 return(GetFirstImageInList(postscript_image));
862}
863
864/*
865%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
866% %
867% %
868% %
869% R e g i s t e r P S I m a g e %
870% %
871% %
872% %
873%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
874%
875% RegisterPSImage() adds properties for the PS image format to
876% the list of supported formats. The properties include the image format
877% tag, a method to read and/or write the format, whether the format
878% supports the saving of more than one frame to the same file or blob,
879% whether the format supports native in-memory I/O, and a brief
880% description of the format.
881%
882% The format of the RegisterPSImage method is:
883%
cristybb503372010-05-27 20:51:26 +0000884% size_t RegisterPSImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000885%
886*/
cristybb503372010-05-27 20:51:26 +0000887ModuleExport size_t RegisterPSImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000888{
889 MagickInfo
890 *entry;
891
892 entry=SetMagickInfo("EPI");
893 entry->decoder=(DecodeImageHandler *) ReadPSImage;
894 entry->encoder=(EncodeImageHandler *) WritePSImage;
895 entry->magick=(IsImageFormatHandler *) IsPS;
896 entry->adjoin=MagickFalse;
897 entry->blob_support=MagickFalse;
898 entry->seekable_stream=MagickTrue;
899 entry->thread_support=EncoderThreadSupport;
900 entry->description=ConstantString(
901 "Encapsulated PostScript Interchange format");
902 entry->module=ConstantString("PS");
903 (void) RegisterMagickInfo(entry);
904 entry=SetMagickInfo("EPS");
905 entry->decoder=(DecodeImageHandler *) ReadPSImage;
906 entry->encoder=(EncodeImageHandler *) WritePSImage;
907 entry->magick=(IsImageFormatHandler *) IsPS;
908 entry->adjoin=MagickFalse;
909 entry->blob_support=MagickFalse;
910 entry->seekable_stream=MagickTrue;
911 entry->thread_support=EncoderThreadSupport;
912 entry->description=ConstantString("Encapsulated PostScript");
913 entry->module=ConstantString("PS");
914 (void) RegisterMagickInfo(entry);
915 entry=SetMagickInfo("EPSF");
916 entry->decoder=(DecodeImageHandler *) ReadPSImage;
917 entry->encoder=(EncodeImageHandler *) WritePSImage;
918 entry->magick=(IsImageFormatHandler *) IsPS;
919 entry->adjoin=MagickFalse;
920 entry->blob_support=MagickFalse;
921 entry->seekable_stream=MagickTrue;
922 entry->description=ConstantString("Encapsulated PostScript");
923 entry->module=ConstantString("PS");
924 (void) RegisterMagickInfo(entry);
925 entry=SetMagickInfo("EPSI");
926 entry->decoder=(DecodeImageHandler *) ReadPSImage;
927 entry->encoder=(EncodeImageHandler *) WritePSImage;
928 entry->magick=(IsImageFormatHandler *) IsPS;
929 entry->adjoin=MagickFalse;
930 entry->blob_support=MagickFalse;
931 entry->seekable_stream=MagickTrue;
932 entry->thread_support=EncoderThreadSupport;
933 entry->description=ConstantString(
934 "Encapsulated PostScript Interchange format");
935 entry->module=ConstantString("PS");
936 (void) RegisterMagickInfo(entry);
937 entry=SetMagickInfo("PS");
938 entry->decoder=(DecodeImageHandler *) ReadPSImage;
939 entry->encoder=(EncodeImageHandler *) WritePSImage;
940 entry->magick=(IsImageFormatHandler *) IsPS;
941 entry->module=ConstantString("PS");
942 entry->blob_support=MagickFalse;
943 entry->seekable_stream=MagickTrue;
944 entry->thread_support=EncoderThreadSupport;
945 entry->description=ConstantString("PostScript");
946 (void) RegisterMagickInfo(entry);
947 return(MagickImageCoderSignature);
948}
949
950/*
951%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
952% %
953% %
954% %
955% U n r e g i s t e r P S I m a g e %
956% %
957% %
958% %
959%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
960%
961% UnregisterPSImage() removes format registrations made by the
962% PS module from the list of supported formats.
963%
964% The format of the UnregisterPSImage method is:
965%
966% UnregisterPSImage(void)
967%
968*/
969ModuleExport void UnregisterPSImage(void)
970{
971 (void) UnregisterMagickInfo("EPI");
972 (void) UnregisterMagickInfo("EPS");
973 (void) UnregisterMagickInfo("EPSF");
974 (void) UnregisterMagickInfo("EPSI");
975 (void) UnregisterMagickInfo("PS");
976}
977
978/*
979%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
980% %
981% %
982% %
983% W r i t e P S I m a g e %
984% %
985% %
986% %
987%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
988%
989% WritePSImage translates an image to encapsulated Postscript
990% Level I for printing. If the supplied geometry is null, the image is
991% centered on the Postscript page. Otherwise, the image is positioned as
992% specified by the geometry.
993%
994% The format of the WritePSImage method is:
995%
996% MagickBooleanType WritePSImage(const ImageInfo *image_info,Image *image)
997%
998% A description of each parameter follows:
999%
1000% o image_info: the image info.
1001%
1002% o image: the image.
1003%
1004*/
1005
1006static inline size_t MagickMin(const size_t x,const size_t y)
1007{
1008 if (x < y)
1009 return(x);
1010 return(y);
1011}
1012
1013static inline unsigned char *PopHexPixel(const char **hex_digits,
cristybb503372010-05-27 20:51:26 +00001014 const size_t pixel,unsigned char *pixels)
cristy3ed852e2009-09-05 21:47:34 +00001015{
1016 register const char
1017 *hex;
1018
1019 hex=hex_digits[pixel];
1020 *pixels++=(unsigned char) (*hex++);
1021 *pixels++=(unsigned char) (*hex);
1022 return(pixels);
1023}
1024
1025static MagickBooleanType WritePSImage(const ImageInfo *image_info,Image *image)
1026{
1027#define WriteRunlengthPacket(image,pixel,length,p) \
1028{ \
1029 if ((image->matte != MagickFalse) && \
cristy4c08aed2011-07-01 19:47:50 +00001030 (GetPixelAlpha(image,p) == (Quantum) TransparentAlpha)) \
cristy3ed852e2009-09-05 21:47:34 +00001031 { \
1032 q=PopHexPixel(hex_digits,0xff,q); \
1033 q=PopHexPixel(hex_digits,0xff,q); \
1034 q=PopHexPixel(hex_digits,0xff,q); \
1035 } \
1036 else \
1037 { \
1038 q=PopHexPixel(hex_digits,ScaleQuantumToChar(pixel.red),q); \
1039 q=PopHexPixel(hex_digits,ScaleQuantumToChar(pixel.green),q); \
1040 q=PopHexPixel(hex_digits,ScaleQuantumToChar(pixel.blue),q); \
1041 } \
cristybb503372010-05-27 20:51:26 +00001042 q=PopHexPixel(hex_digits,(const size_t) MagickMin(length,0xff),q); \
cristy3ed852e2009-09-05 21:47:34 +00001043}
1044
1045 static const char
1046 *hex_digits[] =
1047 {
1048 "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0A", "0B",
1049 "0C", "0D", "0E", "0F", "10", "11", "12", "13", "14", "15", "16", "17",
1050 "18", "19", "1A", "1B", "1C", "1D", "1E", "1F", "20", "21", "22", "23",
1051 "24", "25", "26", "27", "28", "29", "2A", "2B", "2C", "2D", "2E", "2F",
1052 "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3A", "3B",
1053 "3C", "3D", "3E", "3F", "40", "41", "42", "43", "44", "45", "46", "47",
1054 "48", "49", "4A", "4B", "4C", "4D", "4E", "4F", "50", "51", "52", "53",
1055 "54", "55", "56", "57", "58", "59", "5A", "5B", "5C", "5D", "5E", "5F",
1056 "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6A", "6B",
1057 "6C", "6D", "6E", "6F", "70", "71", "72", "73", "74", "75", "76", "77",
1058 "78", "79", "7A", "7B", "7C", "7D", "7E", "7F", "80", "81", "82", "83",
1059 "84", "85", "86", "87", "88", "89", "8A", "8B", "8C", "8D", "8E", "8F",
1060 "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9A", "9B",
1061 "9C", "9D", "9E", "9F", "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7",
1062 "A8", "A9", "AA", "AB", "AC", "AD", "AE", "AF", "B0", "B1", "B2", "B3",
1063 "B4", "B5", "B6", "B7", "B8", "B9", "BA", "BB", "BC", "BD", "BE", "BF",
1064 "C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "CA", "CB",
1065 "CC", "CD", "CE", "CF", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
1066 "D8", "D9", "DA", "DB", "DC", "DD", "DE", "DF", "E0", "E1", "E2", "E3",
1067 "E4", "E5", "E6", "E7", "E8", "E9", "EA", "EB", "EC", "ED", "EE", "EF",
1068 "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "FA", "FB",
1069 "FC", "FD", "FE", "FF", (char *) NULL
1070 },
1071 *PostscriptProlog[]=
1072 {
1073 "%%BeginProlog",
1074 "%",
1075 "% Display a color image. The image is displayed in color on",
1076 "% Postscript viewers or printers that support color, otherwise",
1077 "% it is displayed as grayscale.",
1078 "%",
1079 "/DirectClassPacket",
1080 "{",
1081 " %",
1082 " % Get a DirectClass packet.",
1083 " %",
1084 " % Parameters:",
1085 " % red.",
1086 " % green.",
1087 " % blue.",
1088 " % length: number of pixels minus one of this color (optional).",
1089 " %",
1090 " currentfile color_packet readhexstring pop pop",
1091 " compression 0 eq",
1092 " {",
1093 " /number_pixels 3 def",
1094 " }",
1095 " {",
1096 " currentfile byte readhexstring pop 0 get",
1097 " /number_pixels exch 1 add 3 mul def",
1098 " } ifelse",
1099 " 0 3 number_pixels 1 sub",
1100 " {",
1101 " pixels exch color_packet putinterval",
1102 " } for",
1103 " pixels 0 number_pixels getinterval",
1104 "} bind def",
1105 "",
1106 "/DirectClassImage",
1107 "{",
1108 " %",
1109 " % Display a DirectClass image.",
1110 " %",
1111 " systemdict /colorimage known",
1112 " {",
1113 " columns rows 8",
1114 " [",
1115 " columns 0 0",
1116 " rows neg 0 rows",
1117 " ]",
1118 " { DirectClassPacket } false 3 colorimage",
1119 " }",
1120 " {",
1121 " %",
1122 " % No colorimage operator; convert to grayscale.",
1123 " %",
1124 " columns rows 8",
1125 " [",
1126 " columns 0 0",
1127 " rows neg 0 rows",
1128 " ]",
1129 " { GrayDirectClassPacket } image",
1130 " } ifelse",
1131 "} bind def",
1132 "",
1133 "/GrayDirectClassPacket",
1134 "{",
1135 " %",
1136 " % Get a DirectClass packet; convert to grayscale.",
1137 " %",
1138 " % Parameters:",
1139 " % red",
1140 " % green",
1141 " % blue",
1142 " % length: number of pixels minus one of this color (optional).",
1143 " %",
1144 " currentfile color_packet readhexstring pop pop",
1145 " color_packet 0 get 0.299 mul",
1146 " color_packet 1 get 0.587 mul add",
1147 " color_packet 2 get 0.114 mul add",
1148 " cvi",
1149 " /gray_packet exch def",
1150 " compression 0 eq",
1151 " {",
1152 " /number_pixels 1 def",
1153 " }",
1154 " {",
1155 " currentfile byte readhexstring pop 0 get",
1156 " /number_pixels exch 1 add def",
1157 " } ifelse",
1158 " 0 1 number_pixels 1 sub",
1159 " {",
1160 " pixels exch gray_packet put",
1161 " } for",
1162 " pixels 0 number_pixels getinterval",
1163 "} bind def",
1164 "",
1165 "/GrayPseudoClassPacket",
1166 "{",
1167 " %",
1168 " % Get a PseudoClass packet; convert to grayscale.",
1169 " %",
1170 " % Parameters:",
1171 " % index: index into the colormap.",
1172 " % length: number of pixels minus one of this color (optional).",
1173 " %",
1174 " currentfile byte readhexstring pop 0 get",
1175 " /offset exch 3 mul def",
1176 " /color_packet colormap offset 3 getinterval def",
1177 " color_packet 0 get 0.299 mul",
1178 " color_packet 1 get 0.587 mul add",
1179 " color_packet 2 get 0.114 mul add",
1180 " cvi",
1181 " /gray_packet exch def",
1182 " compression 0 eq",
1183 " {",
1184 " /number_pixels 1 def",
1185 " }",
1186 " {",
1187 " currentfile byte readhexstring pop 0 get",
1188 " /number_pixels exch 1 add def",
1189 " } ifelse",
1190 " 0 1 number_pixels 1 sub",
1191 " {",
1192 " pixels exch gray_packet put",
1193 " } for",
1194 " pixels 0 number_pixels getinterval",
1195 "} bind def",
1196 "",
1197 "/PseudoClassPacket",
1198 "{",
1199 " %",
1200 " % Get a PseudoClass packet.",
1201 " %",
1202 " % Parameters:",
1203 " % index: index into the colormap.",
1204 " % length: number of pixels minus one of this color (optional).",
1205 " %",
1206 " currentfile byte readhexstring pop 0 get",
1207 " /offset exch 3 mul def",
1208 " /color_packet colormap offset 3 getinterval def",
1209 " compression 0 eq",
1210 " {",
1211 " /number_pixels 3 def",
1212 " }",
1213 " {",
1214 " currentfile byte readhexstring pop 0 get",
1215 " /number_pixels exch 1 add 3 mul def",
1216 " } ifelse",
1217 " 0 3 number_pixels 1 sub",
1218 " {",
1219 " pixels exch color_packet putinterval",
1220 " } for",
1221 " pixels 0 number_pixels getinterval",
1222 "} bind def",
1223 "",
1224 "/PseudoClassImage",
1225 "{",
1226 " %",
1227 " % Display a PseudoClass image.",
1228 " %",
1229 " % Parameters:",
1230 " % class: 0-PseudoClass or 1-Grayscale.",
1231 " %",
1232 " currentfile buffer readline pop",
1233 " token pop /class exch def pop",
1234 " class 0 gt",
1235 " {",
1236 " currentfile buffer readline pop",
1237 " token pop /depth exch def pop",
1238 " /grays columns 8 add depth sub depth mul 8 idiv string def",
1239 " columns rows depth",
1240 " [",
1241 " columns 0 0",
1242 " rows neg 0 rows",
1243 " ]",
1244 " { currentfile grays readhexstring pop } image",
1245 " }",
1246 " {",
1247 " %",
1248 " % Parameters:",
1249 " % colors: number of colors in the colormap.",
1250 " % colormap: red, green, blue color packets.",
1251 " %",
1252 " currentfile buffer readline pop",
1253 " token pop /colors exch def pop",
1254 " /colors colors 3 mul def",
1255 " /colormap colors string def",
1256 " currentfile colormap readhexstring pop pop",
1257 " systemdict /colorimage known",
1258 " {",
1259 " columns rows 8",
1260 " [",
1261 " columns 0 0",
1262 " rows neg 0 rows",
1263 " ]",
1264 " { PseudoClassPacket } false 3 colorimage",
1265 " }",
1266 " {",
1267 " %",
1268 " % No colorimage operator; convert to grayscale.",
1269 " %",
1270 " columns rows 8",
1271 " [",
1272 " columns 0 0",
1273 " rows neg 0 rows",
1274 " ]",
1275 " { GrayPseudoClassPacket } image",
1276 " } ifelse",
1277 " } ifelse",
1278 "} bind def",
1279 "",
1280 "/DisplayImage",
1281 "{",
1282 " %",
1283 " % Display a DirectClass or PseudoClass image.",
1284 " %",
1285 " % Parameters:",
1286 " % x & y translation.",
1287 " % x & y scale.",
1288 " % label pointsize.",
1289 " % image label.",
1290 " % image columns & rows.",
1291 " % class: 0-DirectClass or 1-PseudoClass.",
1292 " % compression: 0-none or 1-RunlengthEncoded.",
1293 " % hex color packets.",
1294 " %",
1295 " gsave",
1296 " /buffer 512 string def",
1297 " /byte 1 string def",
1298 " /color_packet 3 string def",
1299 " /pixels 768 string def",
1300 "",
1301 " currentfile buffer readline pop",
1302 " token pop /x exch def",
1303 " token pop /y exch def pop",
1304 " x y translate",
1305 " currentfile buffer readline pop",
1306 " token pop /x exch def",
1307 " token pop /y exch def pop",
1308 " currentfile buffer readline pop",
1309 " token pop /pointsize exch def pop",
1310 " /Times-Roman findfont pointsize scalefont setfont",
1311 (char *) NULL
1312 },
1313 *PostscriptEpilog[]=
1314 {
1315 " x y scale",
1316 " currentfile buffer readline pop",
1317 " token pop /columns exch def",
1318 " token pop /rows exch def pop",
1319 " currentfile buffer readline pop",
1320 " token pop /class exch def pop",
1321 " currentfile buffer readline pop",
1322 " token pop /compression exch def pop",
1323 " class 0 gt { PseudoClassImage } { DirectClassImage } ifelse",
1324 (char *) NULL
1325 };
1326
1327 char
1328 buffer[MaxTextExtent],
1329 date[MaxTextExtent],
1330 **labels,
1331 page_geometry[MaxTextExtent];
1332
1333 const char
1334 **s,
1335 *value;
1336
1337 const StringInfo
1338 *profile;
1339
1340 double
1341 pointsize;
1342
1343 GeometryInfo
1344 geometry_info;
1345
cristy3ed852e2009-09-05 21:47:34 +00001346 MagickBooleanType
1347 status;
1348
1349 MagickOffsetType
1350 scene;
1351
1352 MagickStatusType
1353 flags;
1354
1355 PixelPacket
1356 pixel;
1357
1358 PointInfo
1359 delta,
1360 resolution,
1361 scale;
1362
cristy4c08aed2011-07-01 19:47:50 +00001363 Quantum
1364 index;
1365
cristy3ed852e2009-09-05 21:47:34 +00001366 RectangleInfo
1367 geometry,
1368 media_info,
1369 page_info;
1370
cristy4c08aed2011-07-01 19:47:50 +00001371 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00001372 *p;
1373
cristybb503372010-05-27 20:51:26 +00001374 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001375 i,
1376 x;
1377
1378 register unsigned char
1379 *q;
1380
1381 SegmentInfo
1382 bounds;
1383
1384 size_t
cristy802d3642011-04-27 02:02:41 +00001385 bit,
1386 byte,
1387 length,
1388 page,
1389 text_size;
1390
1391 ssize_t
1392 j,
1393 y;
cristy3ed852e2009-09-05 21:47:34 +00001394
1395 time_t
1396 timer;
1397
1398 unsigned char
1399 pixels[2048];
1400
cristy3ed852e2009-09-05 21:47:34 +00001401 /*
1402 Open output image file.
1403 */
1404 assert(image_info != (const ImageInfo *) NULL);
1405 assert(image_info->signature == MagickSignature);
1406 assert(image != (Image *) NULL);
1407 assert(image->signature == MagickSignature);
1408 if (image->debug != MagickFalse)
1409 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1410 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
1411 if (status == MagickFalse)
1412 return(status);
1413 (void) ResetMagickMemory(&bounds,0,sizeof(bounds));
1414 page=1;
1415 scene=0;
1416 do
1417 {
1418 /*
1419 Scale relative to dots-per-inch.
1420 */
1421 if ((image->colorspace != RGBColorspace) &&
1422 (image->colorspace != CMYKColorspace))
1423 (void) TransformImageColorspace(image,RGBColorspace);
1424 delta.x=DefaultResolution;
1425 delta.y=DefaultResolution;
1426 resolution.x=image->x_resolution;
1427 resolution.y=image->y_resolution;
1428 if ((resolution.x == 0.0) || (resolution.y == 0.0))
1429 {
1430 flags=ParseGeometry(PSDensityGeometry,&geometry_info);
1431 resolution.x=geometry_info.rho;
1432 resolution.y=geometry_info.sigma;
1433 if ((flags & SigmaValue) == 0)
1434 resolution.y=resolution.x;
1435 }
1436 if (image_info->density != (char *) NULL)
1437 {
1438 flags=ParseGeometry(image_info->density,&geometry_info);
1439 resolution.x=geometry_info.rho;
1440 resolution.y=geometry_info.sigma;
1441 if ((flags & SigmaValue) == 0)
1442 resolution.y=resolution.x;
1443 }
1444 if (image->units == PixelsPerCentimeterResolution)
1445 {
cristya97426c2011-02-04 01:41:27 +00001446 resolution.x=(double) ((size_t) (100.0*2.54*resolution.x+0.5)/100.0);
1447 resolution.y=(double) ((size_t) (100.0*2.54*resolution.y+0.5)/100.0);
cristy3ed852e2009-09-05 21:47:34 +00001448 }
1449 SetGeometry(image,&geometry);
cristyb51dff52011-05-19 16:55:47 +00001450 (void) FormatLocaleString(page_geometry,MaxTextExtent,"%.20gx%.20g",
cristye8c25f92010-06-03 00:53:06 +00001451 (double) image->columns,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001452 if (image_info->page != (char *) NULL)
1453 (void) CopyMagickString(page_geometry,image_info->page,MaxTextExtent);
1454 else
1455 if ((image->page.width != 0) && (image->page.height != 0))
cristyb51dff52011-05-19 16:55:47 +00001456 (void) FormatLocaleString(page_geometry,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +00001457 "%.20gx%.20g%+.20g%+.20g",(double) image->page.width,(double)
cristye8c25f92010-06-03 00:53:06 +00001458 image->page.height,(double) image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00001459 else
1460 if ((image->gravity != UndefinedGravity) &&
1461 (LocaleCompare(image_info->magick,"PS") == 0))
1462 (void) CopyMagickString(page_geometry,PSPageGeometry,MaxTextExtent);
1463 (void) ConcatenateMagickString(page_geometry,">",MaxTextExtent);
1464 (void) ParseMetaGeometry(page_geometry,&geometry.x,&geometry.y,
1465 &geometry.width,&geometry.height);
1466 scale.x=(double) (geometry.width*delta.x)/resolution.x;
cristybb503372010-05-27 20:51:26 +00001467 geometry.width=(size_t) floor(scale.x+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001468 scale.y=(double) (geometry.height*delta.y)/resolution.y;
cristybb503372010-05-27 20:51:26 +00001469 geometry.height=(size_t) floor(scale.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001470 (void) ParseAbsoluteGeometry(page_geometry,&media_info);
1471 (void) ParseGravityGeometry(image,page_geometry,&page_info,
1472 &image->exception);
1473 if (image->gravity != UndefinedGravity)
1474 {
1475 geometry.x=(-page_info.x);
cristybb503372010-05-27 20:51:26 +00001476 geometry.y=(ssize_t) (media_info.height+page_info.y-image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001477 }
1478 pointsize=12.0;
1479 if (image_info->pointsize != 0.0)
1480 pointsize=image_info->pointsize;
1481 text_size=0;
1482 value=GetImageProperty(image,"label");
1483 if (value != (const char *) NULL)
cristybb503372010-05-27 20:51:26 +00001484 text_size=(size_t) (MultilineCensus(value)*pointsize+12);
cristy3ed852e2009-09-05 21:47:34 +00001485 if (page == 1)
1486 {
1487 /*
1488 Output Postscript header.
1489 */
1490 if (LocaleCompare(image_info->magick,"PS") == 0)
1491 (void) CopyMagickString(buffer,"%!PS-Adobe-3.0\n",MaxTextExtent);
1492 else
1493 (void) CopyMagickString(buffer,"%!PS-Adobe-3.0 EPSF-3.0\n",
1494 MaxTextExtent);
1495 (void) WriteBlobString(image,buffer);
1496 (void) WriteBlobString(image,"%%Creator: (ImageMagick)\n");
cristyb51dff52011-05-19 16:55:47 +00001497 (void) FormatLocaleString(buffer,MaxTextExtent,"%%%%Title: (%s)\n",
cristy3ed852e2009-09-05 21:47:34 +00001498 image->filename);
1499 (void) WriteBlobString(image,buffer);
1500 timer=time((time_t *) NULL);
1501 (void) FormatMagickTime(timer,MaxTextExtent,date);
cristyb51dff52011-05-19 16:55:47 +00001502 (void) FormatLocaleString(buffer,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00001503 "%%%%CreationDate: (%s)\n",date);
1504 (void) WriteBlobString(image,buffer);
1505 bounds.x1=(double) geometry.x;
1506 bounds.y1=(double) geometry.y;
1507 bounds.x2=(double) geometry.x+scale.x;
1508 bounds.y2=(double) geometry.y+(geometry.height+text_size);
1509 if ((image_info->adjoin != MagickFalse) &&
1510 (GetNextImageInList(image) != (Image *) NULL))
1511 (void) CopyMagickString(buffer,"%%%%BoundingBox: (atend)\n",
1512 MaxTextExtent);
1513 else
1514 {
cristyb51dff52011-05-19 16:55:47 +00001515 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001516 "%%%%BoundingBox: %.20g %.20g %.20g %.20g\n",ceil(bounds.x1-0.5),
1517 ceil(bounds.y1-0.5),floor(bounds.x2+0.5),floor(bounds.y2+0.5));
cristy3ed852e2009-09-05 21:47:34 +00001518 (void) WriteBlobString(image,buffer);
cristyb51dff52011-05-19 16:55:47 +00001519 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001520 "%%%%HiResBoundingBox: %g %g %g %g\n",bounds.x1,
cristy8cd5b312010-01-07 01:10:24 +00001521 bounds.y1,bounds.x2,bounds.y2);
cristy3ed852e2009-09-05 21:47:34 +00001522 }
1523 (void) WriteBlobString(image,buffer);
1524 profile=GetImageProfile(image,"8bim");
1525 if (profile != (StringInfo *) NULL)
1526 {
1527 /*
1528 Embed Photoshop profile.
1529 */
cristyb51dff52011-05-19 16:55:47 +00001530 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001531 "%%BeginPhotoshop: %.20g",(double) GetStringInfoLength(profile));
cristy3ed852e2009-09-05 21:47:34 +00001532 (void) WriteBlobString(image,buffer);
cristybb503372010-05-27 20:51:26 +00001533 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
cristy3ed852e2009-09-05 21:47:34 +00001534 {
1535 if ((i % 32) == 0)
1536 (void) WriteBlobString(image,"\n% ");
cristyb51dff52011-05-19 16:55:47 +00001537 (void) FormatLocaleString(buffer,MaxTextExtent,"%02X",
cristy3ed852e2009-09-05 21:47:34 +00001538 (unsigned int) (GetStringInfoDatum(profile)[i] & 0xff));
1539 (void) WriteBlobString(image,buffer);
1540 }
1541 (void) WriteBlobString(image,"\n%EndPhotoshop\n");
1542 }
1543 profile=GetImageProfile(image,"xmp");
cristy2b2eb912010-03-03 14:56:40 +00001544 if (0 && (profile != (StringInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001545 {
1546 /*
1547 Embed XML profile.
1548 */
1549 (void) WriteBlobString(image,"\n%begin_xml_code\n");
cristyb51dff52011-05-19 16:55:47 +00001550 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001551 "\n%%begin_xml_packet: %.20g\n",(double)
cristyeec18db2010-03-03 21:15:45 +00001552 GetStringInfoLength(profile));
cristy2b2eb912010-03-03 14:56:40 +00001553 (void) WriteBlobString(image,buffer);
cristybb503372010-05-27 20:51:26 +00001554 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
cristy3ed852e2009-09-05 21:47:34 +00001555 (void) WriteBlobByte(image,GetStringInfoDatum(profile)[i]);
cristy2b2eb912010-03-03 14:56:40 +00001556 (void) WriteBlobString(image,"\n%end_xml_packet\n%end_xml_code\n");
cristy3ed852e2009-09-05 21:47:34 +00001557 }
1558 value=GetImageProperty(image,"label");
1559 if (value != (const char *) NULL)
1560 (void) WriteBlobString(image,
1561 "%%DocumentNeededResources: font Times-Roman\n");
1562 (void) WriteBlobString(image,"%%DocumentData: Clean7Bit\n");
1563 (void) WriteBlobString(image,"%%LanguageLevel: 1\n");
1564 if (LocaleCompare(image_info->magick,"PS") != 0)
1565 (void) WriteBlobString(image,"%%Pages: 1\n");
1566 else
1567 {
1568 /*
1569 Compute the number of pages.
1570 */
1571 (void) WriteBlobString(image,"%%Orientation: Portrait\n");
1572 (void) WriteBlobString(image,"%%PageOrder: Ascend\n");
cristyb51dff52011-05-19 16:55:47 +00001573 (void) FormatLocaleString(buffer,MaxTextExtent,"%%%%Pages: %.20g\n",
cristye8c25f92010-06-03 00:53:06 +00001574 image_info->adjoin != MagickFalse ? (double)
1575 GetImageListLength(image) : 1.0);
cristy3ed852e2009-09-05 21:47:34 +00001576 (void) WriteBlobString(image,buffer);
1577 }
1578 (void) WriteBlobString(image,"%%EndComments\n");
1579 (void) WriteBlobString(image,"\n%%BeginDefaults\n");
1580 (void) WriteBlobString(image,"%%EndDefaults\n\n");
1581 if ((LocaleCompare(image_info->magick,"EPI") == 0) ||
1582 (LocaleCompare(image_info->magick,"EPSI") == 0) ||
1583 (LocaleCompare(image_info->magick,"EPT") == 0))
1584 {
1585 Image
1586 *preview_image;
1587
cristy3ed852e2009-09-05 21:47:34 +00001588 Quantum
1589 pixel;
1590
cristybb503372010-05-27 20:51:26 +00001591 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001592 x;
1593
cristy802d3642011-04-27 02:02:41 +00001594 ssize_t
1595 y;
1596
cristy3ed852e2009-09-05 21:47:34 +00001597 /*
1598 Create preview image.
1599 */
1600 preview_image=CloneImage(image,0,0,MagickTrue,&image->exception);
1601 if (preview_image == (Image *) NULL)
1602 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1603 /*
1604 Dump image as bitmap.
1605 */
cristyb51dff52011-05-19 16:55:47 +00001606 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001607 "%%%%BeginPreview: %.20g %.20g %.20g %.20g\n%% ",(double)
1608 preview_image->columns,(double) preview_image->rows,1.0,
1609 (double) ((((preview_image->columns+7) >> 3)*preview_image->rows+
1610 35)/36));
cristy3ed852e2009-09-05 21:47:34 +00001611 (void) WriteBlobString(image,buffer);
1612 q=pixels;
cristybb503372010-05-27 20:51:26 +00001613 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001614 {
1615 p=GetVirtualPixels(preview_image,0,y,preview_image->columns,1,
1616 &preview_image->exception);
cristy4c08aed2011-07-01 19:47:50 +00001617 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001618 break;
cristy3ed852e2009-09-05 21:47:34 +00001619 bit=0;
1620 byte=0;
cristybb503372010-05-27 20:51:26 +00001621 for (x=0; x < (ssize_t) preview_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001622 {
1623 byte<<=1;
cristy4c08aed2011-07-01 19:47:50 +00001624 pixel=GetPixelIntensity(preview_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001625 if (pixel >= (Quantum) (QuantumRange/2))
1626 byte|=0x01;
1627 bit++;
1628 if (bit == 8)
1629 {
1630 q=PopHexPixel(hex_digits,byte,q);
1631 if ((q-pixels+8) >= 80)
1632 {
1633 *q++='\n';
1634 (void) WriteBlob(image,q-pixels,pixels);
1635 q=pixels;
1636 (void) WriteBlobString(image,"% ");
1637 };
1638 bit=0;
1639 byte=0;
1640 }
1641 }
1642 if (bit != 0)
1643 {
1644 byte<<=(8-bit);
1645 q=PopHexPixel(hex_digits,byte,q);
1646 if ((q-pixels+8) >= 80)
1647 {
1648 *q++='\n';
1649 (void) WriteBlob(image,q-pixels,pixels);
1650 q=pixels;
1651 (void) WriteBlobString(image,"% ");
1652 };
1653 };
1654 }
1655 if (q != pixels)
1656 {
1657 *q++='\n';
1658 (void) WriteBlob(image,q-pixels,pixels);
1659 }
1660 (void) WriteBlobString(image,"\n%%EndPreview\n");
1661 preview_image=DestroyImage(preview_image);
1662 }
1663 /*
1664 Output Postscript commands.
1665 */
1666 for (s=PostscriptProlog; *s != (char *) NULL; s++)
1667 {
cristyb51dff52011-05-19 16:55:47 +00001668 (void) FormatLocaleString(buffer,MaxTextExtent,"%s\n",*s);
cristy3ed852e2009-09-05 21:47:34 +00001669 (void) WriteBlobString(image,buffer);
1670 }
1671 value=GetImageProperty(image,"label");
1672 if (value != (const char *) NULL)
cristybb503372010-05-27 20:51:26 +00001673 for (j=(ssize_t) MultilineCensus(value)-1; j >= 0; j--)
cristy3ed852e2009-09-05 21:47:34 +00001674 {
1675 (void) WriteBlobString(image," /label 512 string def\n");
1676 (void) WriteBlobString(image," currentfile label readline pop\n");
cristyb51dff52011-05-19 16:55:47 +00001677 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001678 " 0 y %g add moveto label show pop\n",j*pointsize+12);
cristy3ed852e2009-09-05 21:47:34 +00001679 (void) WriteBlobString(image,buffer);
1680 }
1681 for (s=PostscriptEpilog; *s != (char *) NULL; s++)
1682 {
cristyb51dff52011-05-19 16:55:47 +00001683 (void) FormatLocaleString(buffer,MaxTextExtent,"%s\n",*s);
cristy3ed852e2009-09-05 21:47:34 +00001684 (void) WriteBlobString(image,buffer);
1685 }
1686 if (LocaleCompare(image_info->magick,"PS") == 0)
1687 (void) WriteBlobString(image," showpage\n");
1688 (void) WriteBlobString(image,"} bind def\n");
1689 (void) WriteBlobString(image,"%%EndProlog\n");
1690 }
cristyb51dff52011-05-19 16:55:47 +00001691 (void) FormatLocaleString(buffer,MaxTextExtent,"%%%%Page: 1 %.20g\n",
cristye8c25f92010-06-03 00:53:06 +00001692 (double) (page++));
cristy3ed852e2009-09-05 21:47:34 +00001693 (void) WriteBlobString(image,buffer);
cristyb51dff52011-05-19 16:55:47 +00001694 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001695 "%%%%PageBoundingBox: %.20g %.20g %.20g %.20g\n",(double) geometry.x,
1696 (double) geometry.y,geometry.x+(double) geometry.width,geometry.y+(double)
cristyf2faecf2010-05-28 19:19:36 +00001697 (geometry.height+text_size));
cristy3ed852e2009-09-05 21:47:34 +00001698 (void) WriteBlobString(image,buffer);
1699 if ((double) geometry.x < bounds.x1)
1700 bounds.x1=(double) geometry.x;
1701 if ((double) geometry.y < bounds.y1)
1702 bounds.y1=(double) geometry.y;
1703 if ((double) (geometry.x+geometry.width-1) > bounds.x2)
1704 bounds.x2=(double) geometry.x+geometry.width-1;
1705 if ((double) (geometry.y+(geometry.height+text_size)-1) > bounds.y2)
1706 bounds.y2=(double) geometry.y+(geometry.height+text_size)-1;
1707 value=GetImageProperty(image,"label");
1708 if (value != (const char *) NULL)
1709 (void) WriteBlobString(image,"%%%%PageResources: font Times-Roman\n");
1710 if (LocaleCompare(image_info->magick,"PS") != 0)
1711 (void) WriteBlobString(image,"userdict begin\n");
1712 (void) WriteBlobString(image,"DisplayImage\n");
1713 /*
1714 Output image data.
1715 */
cristyb51dff52011-05-19 16:55:47 +00001716 (void) FormatLocaleString(buffer,MaxTextExtent,"%.20g %.20g\n%g %g\n%g\n",
cristye8c25f92010-06-03 00:53:06 +00001717 (double) geometry.x,(double) geometry.y,scale.x,scale.y,pointsize);
cristy3ed852e2009-09-05 21:47:34 +00001718 (void) WriteBlobString(image,buffer);
1719 labels=(char **) NULL;
1720 value=GetImageProperty(image,"label");
1721 if (value != (const char *) NULL)
1722 labels=StringToList(value);
1723 if (labels != (char **) NULL)
1724 {
1725 for (i=0; labels[i] != (char *) NULL; i++)
1726 {
cristyb51dff52011-05-19 16:55:47 +00001727 (void) FormatLocaleString(buffer,MaxTextExtent,"%s \n",
cristy3ed852e2009-09-05 21:47:34 +00001728 labels[i]);
1729 (void) WriteBlobString(image,buffer);
1730 labels[i]=DestroyString(labels[i]);
1731 }
1732 labels=(char **) RelinquishMagickMemory(labels);
1733 }
1734 (void) ResetMagickMemory(&pixel,0,sizeof(pixel));
cristy4c08aed2011-07-01 19:47:50 +00001735 pixel.alpha=(Quantum) TransparentAlpha;
1736 index=0;
cristy3ed852e2009-09-05 21:47:34 +00001737 x=0;
1738 if ((image_info->type != TrueColorType) &&
cristy4c08aed2011-07-01 19:47:50 +00001739 (IsImageGray(image,&image->exception) != MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +00001740 {
cristy4c08aed2011-07-01 19:47:50 +00001741 if (IsImageMonochrome(image,&image->exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001742 {
1743 Quantum
1744 pixel;
1745
1746 /*
1747 Dump image as grayscale.
1748 */
cristyb51dff52011-05-19 16:55:47 +00001749 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001750 "%.20g %.20g\n1\n1\n1\n8\n",(double) image->columns,(double)
1751 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001752 (void) WriteBlobString(image,buffer);
1753 q=pixels;
cristybb503372010-05-27 20:51:26 +00001754 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001755 {
1756 p=GetVirtualPixels(image,0,y,image->columns,1,
1757 &image->exception);
cristy4c08aed2011-07-01 19:47:50 +00001758 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001759 break;
cristybb503372010-05-27 20:51:26 +00001760 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001761 {
cristy4c08aed2011-07-01 19:47:50 +00001762 pixel=(Quantum) ScaleQuantumToChar(GetPixelIntensity(image,p));
cristya97426c2011-02-04 01:41:27 +00001763 q=PopHexPixel(hex_digits,(size_t) pixel,q);
cristy3ed852e2009-09-05 21:47:34 +00001764 i++;
1765 if ((q-pixels+8) >= 80)
1766 {
1767 *q++='\n';
1768 (void) WriteBlob(image,q-pixels,pixels);
1769 q=pixels;
1770 }
cristy4c08aed2011-07-01 19:47:50 +00001771 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001772 }
1773 if (image->previous == (Image *) NULL)
1774 {
cristya97426c2011-02-04 01:41:27 +00001775 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType)
1776 y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001777 if (status == MagickFalse)
1778 break;
1779 }
1780 }
1781 if (q != pixels)
1782 {
1783 *q++='\n';
1784 (void) WriteBlob(image,q-pixels,pixels);
1785 }
1786 }
1787 else
1788 {
cristybb503372010-05-27 20:51:26 +00001789 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001790 y;
1791
1792 Quantum
1793 pixel;
1794
1795 /*
1796 Dump image as bitmap.
1797 */
cristyb51dff52011-05-19 16:55:47 +00001798 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001799 "%.20g %.20g\n1\n1\n1\n1\n",(double) image->columns,(double)
1800 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001801 (void) WriteBlobString(image,buffer);
1802 q=pixels;
cristybb503372010-05-27 20:51:26 +00001803 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001804 {
1805 p=GetVirtualPixels(image,0,y,image->columns,1,
1806 &image->exception);
cristy4c08aed2011-07-01 19:47:50 +00001807 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001808 break;
cristy3ed852e2009-09-05 21:47:34 +00001809 bit=0;
1810 byte=0;
cristybb503372010-05-27 20:51:26 +00001811 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001812 {
1813 byte<<=1;
cristy4c08aed2011-07-01 19:47:50 +00001814 pixel=GetPixelIntensity(image,p);
cristy3ed852e2009-09-05 21:47:34 +00001815 if (pixel >= (Quantum) (QuantumRange/2))
1816 byte|=0x01;
1817 bit++;
1818 if (bit == 8)
1819 {
1820 q=PopHexPixel(hex_digits,byte,q);
1821 if ((q-pixels+2) >= 80)
1822 {
1823 *q++='\n';
1824 (void) WriteBlob(image,q-pixels,pixels);
1825 q=pixels;
1826 };
1827 bit=0;
1828 byte=0;
1829 }
cristy4c08aed2011-07-01 19:47:50 +00001830 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001831 }
1832 if (bit != 0)
1833 {
1834 byte<<=(8-bit);
1835 q=PopHexPixel(hex_digits,byte,q);
1836 if ((q-pixels+2) >= 80)
1837 {
1838 *q++='\n';
1839 (void) WriteBlob(image,q-pixels,pixels);
1840 q=pixels;
1841 }
1842 };
1843 if (image->previous == (Image *) NULL)
1844 {
cristy802d3642011-04-27 02:02:41 +00001845 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType)
1846 y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001847 if (status == MagickFalse)
1848 break;
1849 }
1850 }
1851 if (q != pixels)
1852 {
1853 *q++='\n';
1854 (void) WriteBlob(image,q-pixels,pixels);
1855 }
1856 }
1857 }
1858 else
1859 if ((image->storage_class == DirectClass) ||
1860 (image->colors > 256) || (image->matte != MagickFalse))
1861 {
1862 /*
1863 Dump DirectClass image.
1864 */
cristyb51dff52011-05-19 16:55:47 +00001865 (void) FormatLocaleString(buffer,MaxTextExtent,"%.20g %.20g\n0\n%d\n",
cristye8c25f92010-06-03 00:53:06 +00001866 (double) image->columns,(double) image->rows,
cristy3ed852e2009-09-05 21:47:34 +00001867 image_info->compression == RLECompression ? 1 : 0);
1868 (void) WriteBlobString(image,buffer);
1869 switch (image_info->compression)
1870 {
1871 case RLECompression:
1872 {
1873 /*
1874 Dump runlength-encoded DirectColor packets.
1875 */
1876 q=pixels;
cristybb503372010-05-27 20:51:26 +00001877 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001878 {
1879 p=GetVirtualPixels(image,0,y,image->columns,1,
1880 &image->exception);
cristy4c08aed2011-07-01 19:47:50 +00001881 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001882 break;
cristy4c08aed2011-07-01 19:47:50 +00001883 GetPixelPacket(image,p,&pixel);
cristy3ed852e2009-09-05 21:47:34 +00001884 length=255;
cristybb503372010-05-27 20:51:26 +00001885 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001886 {
cristy4c08aed2011-07-01 19:47:50 +00001887 if ((GetPixelRed(image,p) == pixel.red) &&
1888 (GetPixelGreen(image,p) == pixel.green) &&
1889 (GetPixelBlue(image,p) == pixel.blue) &&
1890 (GetPixelAlpha(image,p) == pixel.alpha) &&
cristy802d3642011-04-27 02:02:41 +00001891 (length < 255) && (x < (ssize_t) (image->columns-1)))
cristy3ed852e2009-09-05 21:47:34 +00001892 length++;
1893 else
1894 {
1895 if (x > 0)
1896 {
1897 WriteRunlengthPacket(image,pixel,length,p);
1898 if ((q-pixels+10) >= 80)
1899 {
1900 *q++='\n';
1901 (void) WriteBlob(image,q-pixels,pixels);
1902 q=pixels;
1903 }
1904 }
1905 length=0;
1906 }
cristy4c08aed2011-07-01 19:47:50 +00001907 GetPixelPacket(image,p,&pixel);
1908 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001909 }
1910 WriteRunlengthPacket(image,pixel,length,p);
1911 if ((q-pixels+10) >= 80)
1912 {
1913 *q++='\n';
1914 (void) WriteBlob(image,q-pixels,pixels);
1915 q=pixels;
1916 }
1917 if (image->previous == (Image *) NULL)
1918 {
cristy802d3642011-04-27 02:02:41 +00001919 status=SetImageProgress(image,SaveImageTag,
1920 (MagickOffsetType) y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001921 if (status == MagickFalse)
1922 break;
1923 }
1924 }
1925 if (q != pixels)
1926 {
1927 *q++='\n';
1928 (void) WriteBlob(image,q-pixels,pixels);
1929 }
1930 break;
1931 }
1932 case NoCompression:
1933 default:
1934 {
1935 /*
1936 Dump uncompressed DirectColor packets.
1937 */
1938 q=pixels;
cristybb503372010-05-27 20:51:26 +00001939 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001940 {
1941 p=GetVirtualPixels(image,0,y,image->columns,1,
1942 &image->exception);
cristy4c08aed2011-07-01 19:47:50 +00001943 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001944 break;
cristybb503372010-05-27 20:51:26 +00001945 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001946 {
1947 if ((image->matte != MagickFalse) &&
cristy4c08aed2011-07-01 19:47:50 +00001948 (GetPixelAlpha(image,p) == (Quantum) TransparentAlpha))
cristy3ed852e2009-09-05 21:47:34 +00001949 {
1950 q=PopHexPixel(hex_digits,0xff,q);
1951 q=PopHexPixel(hex_digits,0xff,q);
1952 q=PopHexPixel(hex_digits,0xff,q);
1953 }
1954 else
1955 {
cristy802d3642011-04-27 02:02:41 +00001956 q=PopHexPixel(hex_digits,ScaleQuantumToChar(
cristy4c08aed2011-07-01 19:47:50 +00001957 GetPixelRed(image,p)),q);
cristy802d3642011-04-27 02:02:41 +00001958 q=PopHexPixel(hex_digits,ScaleQuantumToChar(
cristy4c08aed2011-07-01 19:47:50 +00001959 GetPixelGreen(image,p)),q);
cristy802d3642011-04-27 02:02:41 +00001960 q=PopHexPixel(hex_digits,ScaleQuantumToChar(
cristy4c08aed2011-07-01 19:47:50 +00001961 GetPixelBlue(image,p)),q);
cristy3ed852e2009-09-05 21:47:34 +00001962 }
1963 if ((q-pixels+6) >= 80)
1964 {
1965 *q++='\n';
1966 (void) WriteBlob(image,q-pixels,pixels);
1967 q=pixels;
1968 }
cristy4c08aed2011-07-01 19:47:50 +00001969 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001970 }
1971 if (image->previous == (Image *) NULL)
1972 {
cristy802d3642011-04-27 02:02:41 +00001973 status=SetImageProgress(image,SaveImageTag,
1974 (MagickOffsetType) y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001975 if (status == MagickFalse)
1976 break;
1977 }
1978 }
1979 if (q != pixels)
1980 {
1981 *q++='\n';
1982 (void) WriteBlob(image,q-pixels,pixels);
1983 }
1984 break;
1985 }
1986 }
1987 (void) WriteBlobByte(image,'\n');
1988 }
1989 else
1990 {
1991 /*
1992 Dump PseudoClass image.
1993 */
cristyb51dff52011-05-19 16:55:47 +00001994 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001995 "%.20g %.20g\n%d\n%d\n0\n",(double) image->columns,(double)
1996 image->rows,image->storage_class == PseudoClass ? 1 : 0,
cristy3ed852e2009-09-05 21:47:34 +00001997 image_info->compression == RLECompression ? 1 : 0);
1998 (void) WriteBlobString(image,buffer);
1999 /*
2000 Dump number of colors and colormap.
2001 */
cristyb51dff52011-05-19 16:55:47 +00002002 (void) FormatLocaleString(buffer,MaxTextExtent,"%.20g\n",(double)
cristyf2faecf2010-05-28 19:19:36 +00002003 image->colors);
cristy3ed852e2009-09-05 21:47:34 +00002004 (void) WriteBlobString(image,buffer);
cristybb503372010-05-27 20:51:26 +00002005 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002006 {
cristyb51dff52011-05-19 16:55:47 +00002007 (void) FormatLocaleString(buffer,MaxTextExtent,"%02X%02X%02X\n",
cristy3ed852e2009-09-05 21:47:34 +00002008 ScaleQuantumToChar(image->colormap[i].red),
2009 ScaleQuantumToChar(image->colormap[i].green),
2010 ScaleQuantumToChar(image->colormap[i].blue));
2011 (void) WriteBlobString(image,buffer);
2012 }
2013 switch (image_info->compression)
2014 {
2015 case RLECompression:
2016 {
2017 /*
2018 Dump runlength-encoded PseudoColor packets.
2019 */
2020 q=pixels;
cristybb503372010-05-27 20:51:26 +00002021 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002022 {
2023 p=GetVirtualPixels(image,0,y,image->columns,1,
2024 &image->exception);
cristy4c08aed2011-07-01 19:47:50 +00002025 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002026 break;
cristy4c08aed2011-07-01 19:47:50 +00002027 index=GetPixelIndex(image,p);
cristy3ed852e2009-09-05 21:47:34 +00002028 length=255;
cristybb503372010-05-27 20:51:26 +00002029 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002030 {
cristy4c08aed2011-07-01 19:47:50 +00002031 if ((index == GetPixelIndex(image,p)) &&
cristy802d3642011-04-27 02:02:41 +00002032 (length < 255) && (x < ((ssize_t) image->columns-1)))
cristy3ed852e2009-09-05 21:47:34 +00002033 length++;
2034 else
2035 {
2036 if (x > 0)
2037 {
cristya97426c2011-02-04 01:41:27 +00002038 q=PopHexPixel(hex_digits,(size_t) index,q);
cristybb503372010-05-27 20:51:26 +00002039 q=PopHexPixel(hex_digits,(size_t)
cristy3ed852e2009-09-05 21:47:34 +00002040 MagickMin(length,0xff),q);
2041 i++;
2042 if ((q-pixels+6) >= 80)
2043 {
2044 *q++='\n';
2045 (void) WriteBlob(image,q-pixels,pixels);
2046 q=pixels;
2047 }
2048 }
2049 length=0;
2050 }
cristy4c08aed2011-07-01 19:47:50 +00002051 index=GetPixelIndex(image,p);
2052 pixel.red=GetPixelRed(image,p);
2053 pixel.green=GetPixelGreen(image,p);
2054 pixel.blue=GetPixelBlue(image,p);
2055 pixel.alpha=GetPixelAlpha(image,p);
2056 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002057 }
cristya97426c2011-02-04 01:41:27 +00002058 q=PopHexPixel(hex_digits,(size_t) index,q);
cristybb503372010-05-27 20:51:26 +00002059 q=PopHexPixel(hex_digits,(size_t)
cristy3ed852e2009-09-05 21:47:34 +00002060 MagickMin(length,0xff),q);
2061 if (image->previous == (Image *) NULL)
2062 {
cristya97426c2011-02-04 01:41:27 +00002063 status=SetImageProgress(image,SaveImageTag,
2064 (MagickOffsetType) y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002065 if (status == MagickFalse)
2066 break;
2067 }
2068 }
2069 if (q != pixels)
2070 {
2071 *q++='\n';
2072 (void) WriteBlob(image,q-pixels,pixels);
2073 }
2074 break;
2075 }
2076 case NoCompression:
2077 default:
2078 {
2079 /*
2080 Dump uncompressed PseudoColor packets.
2081 */
2082 q=pixels;
cristybb503372010-05-27 20:51:26 +00002083 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002084 {
2085 p=GetVirtualPixels(image,0,y,image->columns,1,
2086 &image->exception);
cristy4c08aed2011-07-01 19:47:50 +00002087 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002088 break;
cristybb503372010-05-27 20:51:26 +00002089 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002090 {
cristy4c08aed2011-07-01 19:47:50 +00002091 q=PopHexPixel(hex_digits,(size_t) GetPixelIndex(image,p),q);
cristy3ed852e2009-09-05 21:47:34 +00002092 if ((q-pixels+4) >= 80)
2093 {
2094 *q++='\n';
2095 (void) WriteBlob(image,q-pixels,pixels);
2096 q=pixels;
2097 }
cristy4c08aed2011-07-01 19:47:50 +00002098 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002099 }
2100 if (image->previous == (Image *) NULL)
2101 {
cristya97426c2011-02-04 01:41:27 +00002102 status=SetImageProgress(image,SaveImageTag,
2103 (MagickOffsetType) y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002104 if (status == MagickFalse)
2105 break;
2106 }
2107 }
2108 if (q != pixels)
2109 {
2110 *q++='\n';
2111 (void) WriteBlob(image,q-pixels,pixels);
2112 }
2113 break;
2114 }
2115 }
2116 (void) WriteBlobByte(image,'\n');
2117 }
2118 if (LocaleCompare(image_info->magick,"PS") != 0)
2119 (void) WriteBlobString(image,"end\n");
2120 (void) WriteBlobString(image,"%%PageTrailer\n");
2121 if (GetNextImageInList(image) == (Image *) NULL)
2122 break;
2123 image=SyncNextImageInList(image);
2124 status=SetImageProgress(image,SaveImagesTag,scene++,
2125 GetImageListLength(image));
2126 if (status == MagickFalse)
2127 break;
2128 } while (image_info->adjoin != MagickFalse);
2129 (void) WriteBlobString(image,"%%Trailer\n");
2130 if (page > 2)
2131 {
cristyb51dff52011-05-19 16:55:47 +00002132 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00002133 "%%%%BoundingBox: %.20g %.20g %.20g %.20g\n",ceil(bounds.x1-0.5),
2134 ceil(bounds.y1-0.5),floor(bounds.x2+0.5),floor(bounds.y2+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002135 (void) WriteBlobString(image,buffer);
cristyb51dff52011-05-19 16:55:47 +00002136 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00002137 "%%%%HiResBoundingBox: %g %g %g %g\n",bounds.x1,bounds.y1,
cristy8cd5b312010-01-07 01:10:24 +00002138 bounds.x2,bounds.y2);
cristy3ed852e2009-09-05 21:47:34 +00002139 (void) WriteBlobString(image,buffer);
2140 }
2141 (void) WriteBlobString(image,"%%EOF\n");
2142 (void) CloseBlob(image);
2143 return(MagickTrue);
2144}