blob: 06dae208298ed5c3235b5855064694bd06559e9e [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 %
cristyde984cd2013-12-01 14:49:27 +000016% Cristy %
cristy3ed852e2009-09-05 21:47:34 +000017% July 1992 %
18% %
19% %
cristyb56bb242014-11-25 17:12:48 +000020% Copyright 1999-2015 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"
cristy76ce6e12013-04-05 14:33:38 +000043#include "MagickCore/artifact.h"
cristy4c08aed2011-07-01 19:47:50 +000044#include "MagickCore/attribute.h"
45#include "MagickCore/blob.h"
46#include "MagickCore/blob-private.h"
47#include "MagickCore/cache.h"
48#include "MagickCore/color.h"
49#include "MagickCore/color-private.h"
50#include "MagickCore/colorspace.h"
cristy510d06a2011-07-06 23:43:54 +000051#include "MagickCore/colorspace-private.h"
cristy4c08aed2011-07-01 19:47:50 +000052#include "MagickCore/constitute.h"
53#include "MagickCore/delegate.h"
54#include "MagickCore/delegate-private.h"
55#include "MagickCore/draw.h"
56#include "MagickCore/exception.h"
57#include "MagickCore/exception-private.h"
58#include "MagickCore/geometry.h"
59#include "MagickCore/image.h"
60#include "MagickCore/image-private.h"
61#include "MagickCore/list.h"
62#include "MagickCore/magick.h"
63#include "MagickCore/memory_.h"
64#include "MagickCore/monitor.h"
65#include "MagickCore/monitor-private.h"
cristy1e37e8f2014-02-21 17:05:37 +000066#include "MagickCore/nt-base-private.h"
cristy4c08aed2011-07-01 19:47:50 +000067#include "MagickCore/option.h"
68#include "MagickCore/profile.h"
69#include "MagickCore/resource_.h"
70#include "MagickCore/pixel-accessor.h"
71#include "MagickCore/property.h"
72#include "MagickCore/quantum-private.h"
73#include "MagickCore/static.h"
74#include "MagickCore/string_.h"
75#include "MagickCore/module.h"
76#include "MagickCore/token.h"
77#include "MagickCore/transform.h"
78#include "MagickCore/utility.h"
cristy3ed852e2009-09-05 21:47:34 +000079
80/*
81 Forward declarations.
82*/
83static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +000084 WritePSImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +000085
86/*
87%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
88% %
89% %
90% %
cristy817f4cd2013-02-06 11:45:42 +000091% I n v o k e P o s t s r i p t D e l e g a t e %
92% %
93% %
94% %
95%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
96%
97% InvokePostscriptDelegate() executes the Postscript interpreter with the
98% specified command.
99%
100% The format of the InvokePostscriptDelegate method is:
101%
102% MagickBooleanType InvokePostscriptDelegate(
103% const MagickBooleanType verbose,const char *command,
104% ExceptionInfo *exception)
105%
106% A description of each parameter follows:
107%
108% o verbose: A value other than zero displays the command prior to
109% executing it.
110%
111% o command: the address of a character string containing the command to
112% execute.
113%
114% o exception: return any errors or warnings in this structure.
115%
116*/
dirkfdec0e72014-05-09 18:45:41 +0000117#if defined(MAGICKCORE_GS_DELEGATE) || defined(MAGICKCORE_WINDOWS_SUPPORT)
dirk52784992014-05-03 13:00:18 +0000118static int MagickDLLCall PostscriptDelegateMessage(void *handle,
cristy26842022014-10-05 19:18:23 +0000119 const char *message,int length)
dirk52784992014-05-03 13:00:18 +0000120{
121 char
122 **messages;
123
cristy26842022014-10-05 19:18:23 +0000124 ssize_t
125 offset;
dirk52784992014-05-03 13:00:18 +0000126
127 offset=0;
cristy26842022014-10-05 19:18:23 +0000128 messages=(char **) handle;
dirk52784992014-05-03 13:00:18 +0000129 if (*messages == (char *) NULL)
cristy26842022014-10-05 19:18:23 +0000130 *messages=(char *) AcquireQuantumMemory(length+1,sizeof(char *));
dirk52784992014-05-03 13:00:18 +0000131 else
132 {
133 offset=strlen(*messages);
cristy26842022014-10-05 19:18:23 +0000134 *messages=(char *) ResizeQuantumMemory(*messages,offset+length+1,
dirk52784992014-05-03 13:00:18 +0000135 sizeof(char *));
136 }
cristy26842022014-10-05 19:18:23 +0000137 (void) memcpy(*messages+offset,message,length);
138 (*messages)[length+offset] ='\0';
139 return(length);
dirk52784992014-05-03 13:00:18 +0000140}
dirkfdec0e72014-05-09 18:45:41 +0000141#endif
dirk52784992014-05-03 13:00:18 +0000142
cristy817f4cd2013-02-06 11:45:42 +0000143static MagickBooleanType InvokePostscriptDelegate(
cristy60575362014-10-18 13:22:50 +0000144 const MagickBooleanType verbose,const char *command,char *message,
dirk4dd75672014-10-12 12:52:42 +0000145 ExceptionInfo *exception)
cristy817f4cd2013-02-06 11:45:42 +0000146{
dirkfdec0e72014-05-09 18:45:41 +0000147 int
148 status;
149
150#if defined(MAGICKCORE_GS_DELEGATE) || defined(MAGICKCORE_WINDOWS_SUPPORT)
cristy26842022014-10-05 19:18:23 +0000151#define SetArgsStart(command,args_start) \
dirk52784992014-05-03 13:00:18 +0000152 if (args_start == (const char *) NULL) \
153 { \
cristy26842022014-10-05 19:18:23 +0000154 if (*command != '"') \
dirk52784992014-05-03 13:00:18 +0000155 args_start=strchr(command,' '); \
156 else \
157 { \
158 args_start=strchr(command+1,'"'); \
159 if (args_start != (const char *) NULL) \
160 args_start++; \
161 } \
162 }
163
cristy26842022014-10-05 19:18:23 +0000164#define ExecuteGhostscriptCommand(command,status) \
165{ \
cristy60575362014-10-18 13:22:50 +0000166 status=ExternalDelegateCommand(MagickFalse,verbose,command,message, \
cristydfc19b62014-10-17 22:52:24 +0000167 exception); \
cristy26842022014-10-05 19:18:23 +0000168 if (status == 0) \
169 return(MagickTrue); \
170 if (status < 0) \
dirkf3682e92014-05-03 16:00:28 +0000171 return(MagickFalse); \
cristy26842022014-10-05 19:18:23 +0000172 (void) ThrowMagickException(exception,GetMagickModule(),DelegateError, \
173 "FailedToExecuteCommand","`%s' (%d)",command,status); \
174 return(MagickFalse); \
175}
dirkf3682e92014-05-03 16:00:28 +0000176
cristy817f4cd2013-02-06 11:45:42 +0000177 char
dirk52784992014-05-03 13:00:18 +0000178 **argv,
179 *errors;
cristy817f4cd2013-02-06 11:45:42 +0000180
dirkfdec0e72014-05-09 18:45:41 +0000181 const char
cristy26842022014-10-05 19:18:23 +0000182 *args_start = (const char *) NULL;
dirkfdec0e72014-05-09 18:45:41 +0000183
cristy817f4cd2013-02-06 11:45:42 +0000184 const GhostInfo
185 *ghost_info;
186
187 gs_main_instance
188 *interpreter;
189
190 int
191 argc,
192 code;
193
194 register ssize_t
195 i;
196
197#if defined(MAGICKCORE_WINDOWS_SUPPORT)
198 ghost_info=NTGhostscriptDLLVectors();
199#else
200 GhostInfo
201 ghost_info_struct;
202
203 ghost_info=(&ghost_info_struct);
dirkefd45a02014-05-03 14:00:09 +0000204 (void) ResetMagickMemory(&ghost_info_struct,0,sizeof(ghost_info_struct));
dirk52784992014-05-03 13:00:18 +0000205 ghost_info_struct.delete_instance=(void (*)(gs_main_instance *))
206 gsapi_delete_instance;
207 ghost_info_struct.exit=(int (*)(gs_main_instance *)) gsapi_exit;
cristy817f4cd2013-02-06 11:45:42 +0000208 ghost_info_struct.new_instance=(int (*)(gs_main_instance **,void *))
209 gsapi_new_instance;
210 ghost_info_struct.init_with_args=(int (*)(gs_main_instance *,int,char **))
211 gsapi_init_with_args;
212 ghost_info_struct.run_string=(int (*)(gs_main_instance *,const char *,int,
213 int *)) gsapi_run_string;
dirk52784992014-05-03 13:00:18 +0000214 ghost_info_struct.set_stdio=(int (*)(gs_main_instance *,int(*)(void *,char *,
215 int),int(*)(void *,const char *,int),int(*)(void *, const char *, int)))
216 gsapi_set_stdio;
cristy817f4cd2013-02-06 11:45:42 +0000217#endif
218 if (ghost_info == (GhostInfo *) NULL)
cristy26842022014-10-05 19:18:23 +0000219 ExecuteGhostscriptCommand(command,status);
cristy817f4cd2013-02-06 11:45:42 +0000220 if (verbose != MagickFalse)
221 {
222 (void) fputs("[ghostscript library]",stdout);
cristy26842022014-10-05 19:18:23 +0000223 SetArgsStart(command,args_start);
dirk52784992014-05-03 13:00:18 +0000224 (void) fputs(args_start,stdout);
cristy817f4cd2013-02-06 11:45:42 +0000225 }
dirk52784992014-05-03 13:00:18 +0000226 errors=(char *) NULL;
227 status=(ghost_info->new_instance)(&interpreter,(void *) &errors);
cristy817f4cd2013-02-06 11:45:42 +0000228 if (status < 0)
cristy26842022014-10-05 19:18:23 +0000229 ExecuteGhostscriptCommand(command,status);
cristy817f4cd2013-02-06 11:45:42 +0000230 code=0;
231 argv=StringToArgv(command,&argc);
232 if (argv == (char **) NULL)
233 return(MagickFalse);
dirk52784992014-05-03 13:00:18 +0000234 (void) (ghost_info->set_stdio)(interpreter,(int(MagickDLLCall *)(void *,
235 char *,int)) NULL,PostscriptDelegateMessage,PostscriptDelegateMessage);
cristy817f4cd2013-02-06 11:45:42 +0000236 status=(ghost_info->init_with_args)(interpreter,argc-1,argv+1);
237 if (status == 0)
238 status=(ghost_info->run_string)(interpreter,"systemdict /start get exec\n",
239 0,&code);
240 (ghost_info->exit)(interpreter);
241 (ghost_info->delete_instance)(interpreter);
cristy817f4cd2013-02-06 11:45:42 +0000242 for (i=0; i < (ssize_t) argc; i++)
243 argv[i]=DestroyString(argv[i]);
244 argv=(char **) RelinquishMagickMemory(argv);
dirk4dd75672014-10-12 12:52:42 +0000245 if (status != 0)
cristy817f4cd2013-02-06 11:45:42 +0000246 {
cristy26842022014-10-05 19:18:23 +0000247 SetArgsStart(command,args_start);
dirk2267d492014-10-12 18:32:02 +0000248 if (status == -101) /* quit */
cristy60575362014-10-18 13:22:50 +0000249 (void) FormatLocaleString(message,MaxTextExtent,
dirk4dd75672014-10-12 12:52:42 +0000250 "[ghostscript library]%s: %s",args_start,errors);
251 else
252 {
253 (void) ThrowMagickException(exception,GetMagickModule(),
254 DelegateError,"PostscriptDelegateFailed",
255 "`[ghostscript library]%s': %s",args_start,errors);
256 if (errors != (char *) NULL)
257 errors=DestroyString(errors);
258 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
259 "Ghostscript returns status %d, exit code %d",status,code);
260 return(MagickFalse);
261 }
cristy817f4cd2013-02-06 11:45:42 +0000262 }
dirk52784992014-05-03 13:00:18 +0000263 if (errors != (char *) NULL)
264 errors=DestroyString(errors);
cristy817f4cd2013-02-06 11:45:42 +0000265 return(MagickTrue);
266#else
cristy60575362014-10-18 13:22:50 +0000267 status=ExternalDelegateCommand(MagickFalse,verbose,command,message,exception);
cristy817f4cd2013-02-06 11:45:42 +0000268 return(status == 0 ? MagickTrue : MagickFalse);
269#endif
270}
271
272/*
273%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
274% %
275% %
276% %
cristy3ed852e2009-09-05 21:47:34 +0000277% I s P S %
278% %
279% %
280% %
281%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
282%
283% IsPS() returns MagickTrue if the image format type, identified by the
284% magick string, is PS.
285%
286% The format of the IsPS method is:
287%
288% MagickBooleanType IsPS(const unsigned char *magick,const size_t length)
289%
290% A description of each parameter follows:
291%
292% o magick: compare image format pattern against these bytes.
293%
294% o length: Specifies the length of the magick string.
295%
296*/
297static MagickBooleanType IsPS(const unsigned char *magick,const size_t length)
298{
299 if (length < 4)
300 return(MagickFalse);
301 if (memcmp(magick,"%!",2) == 0)
302 return(MagickTrue);
303 if (memcmp(magick,"\004%!",3) == 0)
304 return(MagickTrue);
305 return(MagickFalse);
306}
307
308/*
309%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
310% %
311% %
312% %
313% R e a d P S I m a g e %
314% %
315% %
316% %
317%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
318%
319% ReadPSImage() reads a Postscript image file and returns it. It allocates
320% the memory necessary for the new Image structure and returns a pointer
321% to the new image.
322%
323% The format of the ReadPSImage method is:
324%
325% Image *ReadPSImage(const ImageInfo *image_info,ExceptionInfo *exception)
326%
327% A description of each parameter follows:
328%
329% o image_info: the image info.
330%
331% o exception: return any errors or warnings in this structure.
332%
333*/
334
335static MagickBooleanType IsPostscriptRendered(const char *path)
336{
337 MagickBooleanType
338 status;
339
340 struct stat
341 attributes;
342
343 if ((path == (const char *) NULL) || (*path == '\0'))
344 return(MagickFalse);
345 status=GetPathAttributes(path,&attributes);
346 if ((status != MagickFalse) && S_ISREG(attributes.st_mode) &&
347 (attributes.st_size > 0))
348 return(MagickTrue);
349 return(MagickFalse);
350}
351
352static inline int ProfileInteger(Image *image,short int *hex_digits)
353{
354 int
355 c,
356 l,
357 value;
358
cristybb503372010-05-27 20:51:26 +0000359 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000360 i;
361
362 l=0;
363 value=0;
364 for (i=0; i < 2; )
365 {
366 c=ReadBlobByte(image);
367 if ((c == EOF) || ((c == '%') && (l == '%')))
368 {
369 value=(-1);
370 break;
371 }
372 l=c;
373 c&=0xff;
374 if (isxdigit(c) == MagickFalse)
375 continue;
cristybb503372010-05-27 20:51:26 +0000376 value=(int) ((size_t) value << 4)+hex_digits[c];
cristy3ed852e2009-09-05 21:47:34 +0000377 i++;
378 }
379 return(value);
380}
381
382static Image *ReadPSImage(const ImageInfo *image_info,ExceptionInfo *exception)
383{
384#define BoundingBox "BoundingBox:"
385#define BeginDocument "BeginDocument:"
386#define BeginXMPPacket "<?xpacket begin="
387#define EndXMPPacket "<?xpacket end="
388#define ICCProfile "BeginICCProfile:"
389#define CMYKCustomColor "CMYKCustomColor:"
cristy01ca8922010-03-17 12:20:37 +0000390#define CMYKProcessColor "CMYKProcessColor:"
cristy3ed852e2009-09-05 21:47:34 +0000391#define DocumentMedia "DocumentMedia:"
392#define DocumentCustomColors "DocumentCustomColors:"
393#define DocumentProcessColors "DocumentProcessColors:"
394#define EndDocument "EndDocument:"
395#define HiResBoundingBox "HiResBoundingBox:"
396#define ImageData "ImageData:"
397#define PageBoundingBox "PageBoundingBox:"
398#define LanguageLevel "LanguageLevel:"
399#define PageMedia "PageMedia:"
400#define Pages "Pages:"
401#define PhotoshopProfile "BeginPhotoshop:"
402#define PostscriptLevel "!PS-"
403#define RenderPostscriptText " Rendering Postscript... "
404#define SpotColor "+ "
405
406 char
407 command[MaxTextExtent],
408 density[MaxTextExtent],
409 filename[MaxTextExtent],
410 geometry[MaxTextExtent],
411 input_filename[MaxTextExtent],
cristy60575362014-10-18 13:22:50 +0000412 message[MaxTextExtent],
cristy3ed852e2009-09-05 21:47:34 +0000413 options[MaxTextExtent],
cristyc39e3d62010-10-14 16:52:01 +0000414 postscript_filename[MaxTextExtent];
cristy3ed852e2009-09-05 21:47:34 +0000415
cristy6dda0d32012-11-11 17:59:01 +0000416 const char
417 *option;
418
cristy3ed852e2009-09-05 21:47:34 +0000419 const DelegateInfo
420 *delegate_info;
421
422 GeometryInfo
423 geometry_info;
424
425 Image
426 *image,
427 *next,
428 *postscript_image;
429
430 ImageInfo
431 *read_info;
432
433 int
434 c,
435 file;
436
437 MagickBooleanType
438 cmyk,
dirka5eeacb2014-10-25 06:32:14 +0000439 fitPage,
cristy3ed852e2009-09-05 21:47:34 +0000440 skip,
441 status;
442
443 MagickStatusType
444 flags;
445
446 PointInfo
cristy6502b392012-04-11 00:05:33 +0000447 delta,
448 resolution;
cristy3ed852e2009-09-05 21:47:34 +0000449
450 RectangleInfo
451 page;
452
453 register char
454 *p;
455
cristybb503372010-05-27 20:51:26 +0000456 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000457 i;
458
459 SegmentInfo
460 bounds,
461 hires_bounds;
462
463 short int
464 hex_digits[256];
465
466 size_t
cristyd0323222013-04-07 16:13:21 +0000467 length;
cristy3ed852e2009-09-05 21:47:34 +0000468
469 ssize_t
cristyd0323222013-04-07 16:13:21 +0000470 count,
471 priority;
cristy3ed852e2009-09-05 21:47:34 +0000472
473 StringInfo
474 *profile;
475
cristyf2faecf2010-05-28 19:19:36 +0000476 unsigned long
cristy3ed852e2009-09-05 21:47:34 +0000477 columns,
478 extent,
479 language_level,
480 pages,
481 rows,
482 scene,
483 spotcolor;
484
485 /*
486 Open image file.
487 */
488 assert(image_info != (const ImageInfo *) NULL);
489 assert(image_info->signature == MagickSignature);
490 if (image_info->debug != MagickFalse)
491 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
492 image_info->filename);
493 assert(exception != (ExceptionInfo *) NULL);
494 assert(exception->signature == MagickSignature);
cristy9950d572011-10-01 18:22:35 +0000495 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +0000496 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
497 if (status == MagickFalse)
498 {
499 image=DestroyImageList(image);
500 return((Image *) NULL);
501 }
502 status=AcquireUniqueSymbolicLink(image_info->filename,input_filename);
503 if (status == MagickFalse)
504 {
505 ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
506 image_info->filename);
507 image=DestroyImageList(image);
508 return((Image *) NULL);
509 }
510 /*
511 Initialize hex values.
512 */
513 (void) ResetMagickMemory(hex_digits,0,sizeof(hex_digits));
514 hex_digits[(int) '0']=0;
515 hex_digits[(int) '1']=1;
516 hex_digits[(int) '2']=2;
517 hex_digits[(int) '3']=3;
518 hex_digits[(int) '4']=4;
519 hex_digits[(int) '5']=5;
520 hex_digits[(int) '6']=6;
521 hex_digits[(int) '7']=7;
522 hex_digits[(int) '8']=8;
523 hex_digits[(int) '9']=9;
524 hex_digits[(int) 'a']=10;
525 hex_digits[(int) 'b']=11;
526 hex_digits[(int) 'c']=12;
527 hex_digits[(int) 'd']=13;
528 hex_digits[(int) 'e']=14;
529 hex_digits[(int) 'f']=15;
530 hex_digits[(int) 'A']=10;
531 hex_digits[(int) 'B']=11;
532 hex_digits[(int) 'C']=12;
533 hex_digits[(int) 'D']=13;
534 hex_digits[(int) 'E']=14;
535 hex_digits[(int) 'F']=15;
536 /*
537 Set the page density.
538 */
539 delta.x=DefaultResolution;
540 delta.y=DefaultResolution;
cristy2a11bef2011-10-28 18:33:11 +0000541 if ((image->resolution.x == 0.0) || (image->resolution.y == 0.0))
cristy3ed852e2009-09-05 21:47:34 +0000542 {
543 flags=ParseGeometry(PSDensityGeometry,&geometry_info);
cristy2a11bef2011-10-28 18:33:11 +0000544 image->resolution.x=geometry_info.rho;
545 image->resolution.y=geometry_info.sigma;
cristy3ed852e2009-09-05 21:47:34 +0000546 if ((flags & SigmaValue) == 0)
cristy2a11bef2011-10-28 18:33:11 +0000547 image->resolution.y=image->resolution.x;
cristy3ed852e2009-09-05 21:47:34 +0000548 }
cristy6cde06a2011-11-24 00:08:43 +0000549 if (image_info->density != (char *) NULL)
550 {
551 flags=ParseGeometry(image_info->density,&geometry_info);
552 image->resolution.x=geometry_info.rho;
553 image->resolution.y=geometry_info.sigma;
554 if ((flags & SigmaValue) == 0)
555 image->resolution.y=image->resolution.x;
556 }
557 (void) ParseAbsoluteGeometry(PSPageGeometry,&page);
558 if (image_info->page != (char *) NULL)
559 (void) ParseAbsoluteGeometry(image_info->page,&page);
cristy6502b392012-04-11 00:05:33 +0000560 resolution=image->resolution;
561 page.width=(size_t) ceil((double) (page.width*resolution.x/delta.x)-0.5);
562 page.height=(size_t) ceil((double) (page.height*resolution.y/delta.y)-0.5);
cristy3ed852e2009-09-05 21:47:34 +0000563 /*
564 Determine page geometry from the Postscript bounding box.
565 */
566 (void) ResetMagickMemory(&bounds,0,sizeof(bounds));
cristy3ed852e2009-09-05 21:47:34 +0000567 (void) ResetMagickMemory(command,0,sizeof(command));
cristy6cde06a2011-11-24 00:08:43 +0000568 cmyk=image_info->colorspace == CMYKColorspace ? MagickTrue : MagickFalse;
569 (void) ResetMagickMemory(&hires_bounds,0,sizeof(hires_bounds));
cristy4d246fc2014-01-15 22:33:44 +0000570 columns=0;
571 rows=0;
cristy4b1c78f2011-11-11 15:09:30 +0000572 priority=0;
cristy3ed852e2009-09-05 21:47:34 +0000573 rows=0;
574 extent=0;
575 spotcolor=0;
576 language_level=1;
cristy3ed852e2009-09-05 21:47:34 +0000577 pages=(~0UL);
cristy6cde06a2011-11-24 00:08:43 +0000578 skip=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +0000579 p=command;
580 for (c=ReadBlobByte(image); c != EOF; c=ReadBlobByte(image))
581 {
582 /*
583 Note document structuring comments.
584 */
585 *p++=(char) c;
586 if ((strchr("\n\r%",c) == (char *) NULL) &&
587 ((size_t) (p-command) < (MaxTextExtent-1)))
588 continue;
589 *p='\0';
590 p=command;
591 /*
592 Skip %%BeginDocument thru %%EndDocument.
593 */
594 if (LocaleNCompare(BeginDocument,command,strlen(BeginDocument)) == 0)
595 skip=MagickTrue;
596 if (LocaleNCompare(EndDocument,command,strlen(EndDocument)) == 0)
597 skip=MagickFalse;
598 if (skip != MagickFalse)
599 continue;
600 if (LocaleNCompare(PostscriptLevel,command,strlen(PostscriptLevel)) == 0)
601 {
cristyd15e6592011-10-15 00:13:06 +0000602 (void) SetImageProperty(image,"ps:Level",command+4,exception);
cristy3ed852e2009-09-05 21:47:34 +0000603 if (GlobExpression(command,"*EPSF-*",MagickTrue) != MagickFalse)
604 pages=1;
605 }
606 if (LocaleNCompare(LanguageLevel,command,strlen(LanguageLevel)) == 0)
607 (void) sscanf(command,LanguageLevel " %lu",&language_level);
608 if (LocaleNCompare(Pages,command,strlen(Pages)) == 0)
609 (void) sscanf(command,Pages " %lu",&pages);
cristy97841ba2010-09-02 15:48:08 +0000610 if (LocaleNCompare(ImageData,command,strlen(ImageData)) == 0)
cristy3ed852e2009-09-05 21:47:34 +0000611 (void) sscanf(command,ImageData " %lu %lu",&columns,&rows);
cristy97841ba2010-09-02 15:48:08 +0000612 if (LocaleNCompare(ICCProfile,command,strlen(ICCProfile)) == 0)
cristy3ed852e2009-09-05 21:47:34 +0000613 {
614 unsigned char
615 *datum;
616
617 /*
618 Read ICC profile.
619 */
620 profile=AcquireStringInfo(65536);
621 for (i=0; (c=ProfileInteger(image,hex_digits)) != EOF; i++)
622 {
623 SetStringInfoLength(profile,(size_t) i+1);
624 datum=GetStringInfoDatum(profile);
625 datum[i]=(unsigned char) c;
626 }
cristyd15e6592011-10-15 00:13:06 +0000627 (void) SetImageProfile(image,"icc",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +0000628 profile=DestroyStringInfo(profile);
629 continue;
630 }
631 if (LocaleNCompare(PhotoshopProfile,command,strlen(PhotoshopProfile)) == 0)
632 {
633 unsigned char
634 *p;
635
636 /*
637 Read Photoshop profile.
638 */
639 count=(ssize_t) sscanf(command,PhotoshopProfile " %lu",&extent);
640 if (count != 1)
641 continue;
642 length=extent;
cristy8723e4b2011-09-01 13:11:19 +0000643 profile=BlobToStringInfo((const void *) NULL,length);
cristyb231d582014-11-28 17:13:51 +0000644 if (profile != (StringInfo *) NULL)
645 {
646 p=GetStringInfoDatum(profile);
647 for (i=0; i < (ssize_t) length; i++)
648 *p++=(unsigned char) ProfileInteger(image,hex_digits);
649 (void) SetImageProfile(image,"8bim",profile,exception);
650 profile=DestroyStringInfo(profile);
651 }
cristy3ed852e2009-09-05 21:47:34 +0000652 continue;
653 }
654 if (LocaleNCompare(BeginXMPPacket,command,strlen(BeginXMPPacket)) == 0)
655 {
656 register size_t
657 i;
658
659 /*
660 Read XMP profile.
661 */
662 p=command;
663 profile=StringToStringInfo(command);
664 for (i=GetStringInfoLength(profile)-1; c != EOF; i++)
665 {
666 SetStringInfoLength(profile,i+1);
667 c=ReadBlobByte(image);
668 GetStringInfoDatum(profile)[i]=(unsigned char) c;
669 *p++=(char) c;
670 if ((strchr("\n\r%",c) == (char *) NULL) &&
671 ((size_t) (p-command) < (MaxTextExtent-1)))
672 continue;
673 *p='\0';
674 p=command;
675 if (LocaleNCompare(EndXMPPacket,command,strlen(EndXMPPacket)) == 0)
676 break;
677 }
678 SetStringInfoLength(profile,i);
cristyd15e6592011-10-15 00:13:06 +0000679 (void) SetImageProfile(image,"xmp",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +0000680 profile=DestroyStringInfo(profile);
681 continue;
682 }
683 /*
684 Is this a CMYK document?
685 */
686 length=strlen(DocumentProcessColors);
687 if (LocaleNCompare(DocumentProcessColors,command,length) == 0)
688 {
689 if ((GlobExpression(command,"*Cyan*",MagickTrue) != MagickFalse) ||
690 (GlobExpression(command,"*Magenta*",MagickTrue) != MagickFalse) ||
691 (GlobExpression(command,"*Yellow*",MagickTrue) != MagickFalse))
692 cmyk=MagickTrue;
693 }
694 if (LocaleNCompare(CMYKCustomColor,command,strlen(CMYKCustomColor)) == 0)
695 cmyk=MagickTrue;
cristy01ca8922010-03-17 12:20:37 +0000696 if (LocaleNCompare(CMYKProcessColor,command,strlen(CMYKProcessColor)) == 0)
697 cmyk=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +0000698 length=strlen(DocumentCustomColors);
699 if ((LocaleNCompare(DocumentCustomColors,command,length) == 0) ||
700 (LocaleNCompare(CMYKCustomColor,command,strlen(CMYKCustomColor)) == 0) ||
701 (LocaleNCompare(SpotColor,command,strlen(SpotColor)) == 0))
702 {
703 char
704 property[MaxTextExtent],
705 *value;
706
707 register char
708 *p;
709
710 /*
711 Note spot names.
712 */
cristyb51dff52011-05-19 16:55:47 +0000713 (void) FormatLocaleString(property,MaxTextExtent,"ps:SpotColor-%.20g",
cristye8c25f92010-06-03 00:53:06 +0000714 (double) (spotcolor++));
cristy3ed852e2009-09-05 21:47:34 +0000715 for (p=command; *p != '\0'; p++)
716 if (isspace((int) (unsigned char) *p) != 0)
717 break;
718 value=AcquireString(p);
719 (void) SubstituteString(&value,"(","");
720 (void) SubstituteString(&value,")","");
721 (void) StripString(value);
cristyd15e6592011-10-15 00:13:06 +0000722 (void) SetImageProperty(image,property,value,exception);
cristy3ed852e2009-09-05 21:47:34 +0000723 value=DestroyString(value);
724 continue;
725 }
cristy6cde06a2011-11-24 00:08:43 +0000726 if (image_info->page != (char *) NULL)
727 continue;
cristy3ed852e2009-09-05 21:47:34 +0000728 /*
729 Note region defined by bounding box.
730 */
731 count=0;
cristy4b1c78f2011-11-11 15:09:30 +0000732 i=0;
cristy3ed852e2009-09-05 21:47:34 +0000733 if (LocaleNCompare(BoundingBox,command,strlen(BoundingBox)) == 0)
cristy3ed852e2009-09-05 21:47:34 +0000734 {
cristy4b1c78f2011-11-11 15:09:30 +0000735 count=(ssize_t) sscanf(command,BoundingBox " %lf %lf %lf %lf",
736 &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
737 i=2;
cristy3ed852e2009-09-05 21:47:34 +0000738 }
cristy4b1c78f2011-11-11 15:09:30 +0000739 if (LocaleNCompare(DocumentMedia,command,strlen(DocumentMedia)) == 0)
740 {
741 count=(ssize_t) sscanf(command,DocumentMedia " %lf %lf %lf %lf",
742 &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
743 i=1;
744 }
745 if (LocaleNCompare(HiResBoundingBox,command,strlen(HiResBoundingBox)) == 0)
746 {
747 count=(ssize_t) sscanf(command,HiResBoundingBox " %lf %lf %lf %lf",
748 &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
749 i=3;
750 }
751 if (LocaleNCompare(PageBoundingBox,command,strlen(PageBoundingBox)) == 0)
752 {
753 count=(ssize_t) sscanf(command,PageBoundingBox " %lf %lf %lf %lf",
754 &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
755 i=1;
756 }
757 if (LocaleNCompare(PageMedia,command,strlen(PageMedia)) == 0)
758 {
759 count=(ssize_t) sscanf(command,PageMedia " %lf %lf %lf %lf",
760 &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
761 i=1;
762 }
cristy3fac9ec2011-11-17 18:04:39 +0000763 if ((count != 4) || (i < (ssize_t) priority))
cristy4b1c78f2011-11-11 15:09:30 +0000764 continue;
cristyad29a8c2012-01-25 23:06:43 +0000765 if ((fabs(bounds.x2-bounds.x1) <= fabs(hires_bounds.x2-hires_bounds.x1)) ||
766 (fabs(bounds.y2-bounds.y1) <= fabs(hires_bounds.y2-hires_bounds.y1)))
cristye3c89012014-03-06 14:11:27 +0000767 if (i == (ssize_t) priority)
768 continue;
cristy4b1c78f2011-11-11 15:09:30 +0000769 hires_bounds=bounds;
770 priority=i;
cristy3ed852e2009-09-05 21:47:34 +0000771 }
cristy7b0fcf12011-11-24 00:24:07 +0000772 if ((fabs(hires_bounds.x2-hires_bounds.x1) >= MagickEpsilon) &&
cristy614bc082011-11-24 00:49:08 +0000773 (fabs(hires_bounds.y2-hires_bounds.y1) >= MagickEpsilon))
cristy4b1c78f2011-11-11 15:09:30 +0000774 {
775 /*
776 Set Postscript render geometry.
777 */
778 (void) FormatLocaleString(geometry,MaxTextExtent,"%gx%g%+.15g%+.15g",
779 hires_bounds.x2-hires_bounds.x1,hires_bounds.y2-hires_bounds.y1,
780 hires_bounds.x1,hires_bounds.y1);
781 (void) SetImageProperty(image,"ps:HiResBoundingBox",geometry,exception);
cristy6cde06a2011-11-24 00:08:43 +0000782 page.width=(size_t) ceil((double) ((hires_bounds.x2-hires_bounds.x1)*
cristy6502b392012-04-11 00:05:33 +0000783 resolution.x/delta.x)-0.5);
cristy6cde06a2011-11-24 00:08:43 +0000784 page.height=(size_t) ceil((double) ((hires_bounds.y2-hires_bounds.y1)*
cristy6502b392012-04-11 00:05:33 +0000785 resolution.y/delta.y)-0.5);
cristy4b1c78f2011-11-11 15:09:30 +0000786 }
dirka5eeacb2014-10-25 06:32:14 +0000787 fitPage=MagickFalse;
788 option=GetImageOption(image_info,"eps:fit-page");
789 if (option != (char *) NULL)
790 {
791 char
792 *geometry;
793
794 MagickStatusType
795 flags;
796
797 geometry=GetPageGeometry(option);
798 flags=ParseMetaGeometry(geometry,&page.x,&page.y,&page.width,&page.height);
799 if (flags == NoValue)
800 {
801 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
802 "InvalidGeometry","`%s'",option);
803 image=DestroyImage(image);
804 return((Image *) NULL);
805 }
806 page.width=(size_t) ceil((double) (page.width*image->resolution.x/delta.x)
807 -0.5);
808 page.height=(size_t) ceil((double) (page.height*image->resolution.y/
809 delta.y) -0.5);
810 geometry=DestroyString(geometry);
811 fitPage=MagickTrue;
812 }
cristy3ed852e2009-09-05 21:47:34 +0000813 (void) CloseBlob(image);
cristy3d9f5ba2012-06-26 13:37:31 +0000814 if (IssRGBCompatibleColorspace(image_info->colorspace) != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000815 cmyk=MagickFalse;
816 /*
817 Create Ghostscript control file.
818 */
819 file=AcquireUniqueFileResource(postscript_filename);
820 if (file == -1)
821 {
cristyc82a27b2011-10-21 01:07:16 +0000822 ThrowFileException(exception,FileOpenError,"UnableToOpenFile",
cristy3ed852e2009-09-05 21:47:34 +0000823 image_info->filename);
824 image=DestroyImageList(image);
825 return((Image *) NULL);
826 }
827 (void) CopyMagickString(command,"/setpagedevice {pop} bind 1 index where {"
828 "dup wcheck {3 1 roll put} {pop def} ifelse} {def} ifelse\n"
829 "<</UseCIEColor true>>setpagedevice\n",MaxTextExtent);
830 count=write(file,command,(unsigned int) strlen(command));
cristyc39e3d62010-10-14 16:52:01 +0000831 if (image_info->page == (char *) NULL)
832 {
833 char
834 translate_geometry[MaxTextExtent];
835
cristyb51dff52011-05-19 16:55:47 +0000836 (void) FormatLocaleString(translate_geometry,MaxTextExtent,
cristyc39e3d62010-10-14 16:52:01 +0000837 "%g %g translate\n",-bounds.x1,-bounds.y1);
838 count=write(file,translate_geometry,(unsigned int)
839 strlen(translate_geometry));
840 }
cristy3ed852e2009-09-05 21:47:34 +0000841 file=close(file)-1;
842 /*
843 Render Postscript with the Ghostscript delegate.
844 */
845 if (image_info->monochrome != MagickFalse)
846 delegate_info=GetDelegateInfo("ps:mono",(char *) NULL,exception);
847 else
848 if (cmyk != MagickFalse)
849 delegate_info=GetDelegateInfo("ps:cmyk",(char *) NULL,exception);
850 else
cristya97426c2011-02-04 01:41:27 +0000851 delegate_info=GetDelegateInfo("ps:alpha",(char *) NULL,exception);
cristy3ed852e2009-09-05 21:47:34 +0000852 if (delegate_info == (const DelegateInfo *) NULL)
853 {
854 (void) RelinquishUniqueFileResource(postscript_filename);
855 image=DestroyImageList(image);
856 return((Image *) NULL);
857 }
858 *options='\0';
cristy6502b392012-04-11 00:05:33 +0000859 (void) FormatLocaleString(density,MaxTextExtent,"%gx%g",resolution.x,
860 resolution.y);
cristyc8488582012-02-28 12:03:48 +0000861 (void) FormatLocaleString(options,MaxTextExtent,"-g%.20gx%.20g ",(double)
862 page.width,(double) page.height);
cristy3ed852e2009-09-05 21:47:34 +0000863 read_info=CloneImageInfo(image_info);
864 *read_info->magick='\0';
865 if (read_info->number_scenes != 0)
866 {
867 char
868 pages[MaxTextExtent];
869
cristyb51dff52011-05-19 16:55:47 +0000870 (void) FormatLocaleString(pages,MaxTextExtent,"-dFirstPage=%.20g "
cristyb167a142013-03-09 22:54:29 +0000871 "-dLastPage=%.20g ",(double) read_info->scene+1,(double)
cristyf2faecf2010-05-28 19:19:36 +0000872 (read_info->scene+read_info->number_scenes));
cristy3ed852e2009-09-05 21:47:34 +0000873 (void) ConcatenateMagickString(options,pages,MaxTextExtent);
874 read_info->number_scenes=0;
875 if (read_info->scenes != (char *) NULL)
876 *read_info->scenes='\0';
877 }
dirka5eeacb2014-10-25 06:32:14 +0000878 if (*image_info->magick == 'E')
879 {
880 option=GetImageOption(image_info,"eps:use-cropbox");
881 if ((option == (const char *) NULL) ||
882 (IsStringTrue(option) != MagickFalse))
883 (void) ConcatenateMagickString(options,"-dEPSCrop ",MaxTextExtent);
884 if (fitPage != MagickFalse)
885 (void) ConcatenateMagickString(options,"-dEPSFitPage ",MaxTextExtent);
886 }
cristy3ed852e2009-09-05 21:47:34 +0000887 (void) CopyMagickString(filename,read_info->filename,MaxTextExtent);
cristya97426c2011-02-04 01:41:27 +0000888 (void) AcquireUniqueFilename(filename);
dirkfd826fc2014-06-04 21:11:30 +0000889 (void) RelinquishUniqueFileResource(filename);
cristybe0063d2013-02-01 01:33:47 +0000890 (void) ConcatenateMagickString(filename,"%d",MaxTextExtent);
cristyb51dff52011-05-19 16:55:47 +0000891 (void) FormatLocaleString(command,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +0000892 GetDelegateCommands(delegate_info),
893 read_info->antialias != MagickFalse ? 4 : 1,
cristya97426c2011-02-04 01:41:27 +0000894 read_info->antialias != MagickFalse ? 4 : 1,density,options,filename,
895 postscript_filename,input_filename);
cristy60575362014-10-18 13:22:50 +0000896 *message='\0';
897 status=InvokePostscriptDelegate(read_info->verbose,command,message,exception);
cristya97426c2011-02-04 01:41:27 +0000898 (void) InterpretImageFilename(image_info,image,filename,1,
cristy6fccee12011-10-20 18:43:18 +0000899 read_info->filename,exception);
cristy41083a42009-09-07 23:47:59 +0000900 if ((status == MagickFalse) ||
cristy3ed852e2009-09-05 21:47:34 +0000901 (IsPostscriptRendered(read_info->filename) == MagickFalse))
902 {
903 (void) ConcatenateMagickString(command," -c showpage",MaxTextExtent);
cristy60575362014-10-18 13:22:50 +0000904 status=InvokePostscriptDelegate(read_info->verbose,command,message,
dirk4dd75672014-10-12 12:52:42 +0000905 exception);
cristy3ed852e2009-09-05 21:47:34 +0000906 }
cristy3ed852e2009-09-05 21:47:34 +0000907 (void) RelinquishUniqueFileResource(postscript_filename);
cristy3ed852e2009-09-05 21:47:34 +0000908 (void) RelinquishUniqueFileResource(input_filename);
cristya97426c2011-02-04 01:41:27 +0000909 postscript_image=(Image *) NULL;
910 if (status == MagickFalse)
911 for (i=1; ; i++)
912 {
cristya97426c2011-02-04 01:41:27 +0000913 (void) InterpretImageFilename(image_info,image,filename,(int) i,
cristy6fccee12011-10-20 18:43:18 +0000914 read_info->filename,exception);
cristya97426c2011-02-04 01:41:27 +0000915 if (IsPostscriptRendered(read_info->filename) == MagickFalse)
916 break;
917 (void) RelinquishUniqueFileResource(read_info->filename);
918 }
919 else
920 for (i=1; ; i++)
921 {
cristya97426c2011-02-04 01:41:27 +0000922 (void) InterpretImageFilename(image_info,image,filename,(int) i,
cristy6fccee12011-10-20 18:43:18 +0000923 read_info->filename,exception);
cristya97426c2011-02-04 01:41:27 +0000924 if (IsPostscriptRendered(read_info->filename) == MagickFalse)
925 break;
cristyb0ffea22012-06-17 20:22:01 +0000926 read_info->blob=NULL;
927 read_info->length=0;
cristya97426c2011-02-04 01:41:27 +0000928 next=ReadImage(read_info,exception);
929 (void) RelinquishUniqueFileResource(read_info->filename);
930 if (next == (Image *) NULL)
931 break;
932 AppendImageToList(&postscript_image,next);
933 }
934 (void) RelinquishUniqueFileResource(read_info->filename);
cristy3ed852e2009-09-05 21:47:34 +0000935 read_info=DestroyImageInfo(read_info);
936 if (postscript_image == (Image *) NULL)
dirk947430c2014-10-12 18:53:34 +0000937 {
cristy60575362014-10-18 13:22:50 +0000938 if (*message != '\0')
dirk947430c2014-10-12 18:53:34 +0000939 (void) ThrowMagickException(exception,GetMagickModule(),
cristy60575362014-10-18 13:22:50 +0000940 DelegateError,"PostscriptDelegateFailed","`%s'",message);
cristy3ed852e2009-09-05 21:47:34 +0000941 image=DestroyImageList(image);
cristy3ed852e2009-09-05 21:47:34 +0000942 return((Image *) NULL);
943 }
944 if (LocaleCompare(postscript_image->magick,"BMP") == 0)
945 {
946 Image
947 *cmyk_image;
948
949 cmyk_image=ConsolidateCMYKImages(postscript_image,exception);
950 if (cmyk_image != (Image *) NULL)
951 {
952 postscript_image=DestroyImageList(postscript_image);
953 postscript_image=cmyk_image;
954 }
955 }
956 if (image_info->number_scenes != 0)
957 {
958 Image
959 *clone_image;
960
cristybb503372010-05-27 20:51:26 +0000961 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000962 i;
963
964 /*
965 Add place holder images to meet the subimage specification requirement.
966 */
cristybb503372010-05-27 20:51:26 +0000967 for (i=0; i < (ssize_t) image_info->scene; i++)
cristy3ed852e2009-09-05 21:47:34 +0000968 {
969 clone_image=CloneImage(postscript_image,1,1,MagickTrue,exception);
970 if (clone_image != (Image *) NULL)
971 PrependImageToList(&postscript_image,clone_image);
972 }
973 }
974 do
975 {
976 (void) CopyMagickString(postscript_image->filename,filename,MaxTextExtent);
cristy835b2462014-10-27 23:44:33 +0000977 (void) CopyMagickString(postscript_image->magick,image->magick,
978 MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +0000979 if (columns != 0)
980 postscript_image->magick_columns=columns;
981 if (rows != 0)
982 postscript_image->magick_rows=rows;
983 postscript_image->page=page;
984 (void) CloneImageProfiles(postscript_image,image);
985 (void) CloneImageProperties(postscript_image,image);
986 next=SyncNextImageInList(postscript_image);
987 if (next != (Image *) NULL)
988 postscript_image=next;
989 } while (next != (Image *) NULL);
990 image=DestroyImageList(image);
991 scene=0;
992 for (next=GetFirstImageInList(postscript_image); next != (Image *) NULL; )
993 {
994 next->scene=scene++;
995 next=GetNextImageInList(next);
996 }
997 return(GetFirstImageInList(postscript_image));
998}
999
1000/*
1001%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1002% %
1003% %
1004% %
1005% R e g i s t e r P S I m a g e %
1006% %
1007% %
1008% %
1009%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1010%
1011% RegisterPSImage() adds properties for the PS image format to
1012% the list of supported formats. The properties include the image format
1013% tag, a method to read and/or write the format, whether the format
1014% supports the saving of more than one frame to the same file or blob,
1015% whether the format supports native in-memory I/O, and a brief
1016% description of the format.
1017%
1018% The format of the RegisterPSImage method is:
1019%
cristybb503372010-05-27 20:51:26 +00001020% size_t RegisterPSImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001021%
1022*/
cristybb503372010-05-27 20:51:26 +00001023ModuleExport size_t RegisterPSImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001024{
1025 MagickInfo
1026 *entry;
1027
1028 entry=SetMagickInfo("EPI");
1029 entry->decoder=(DecodeImageHandler *) ReadPSImage;
1030 entry->encoder=(EncodeImageHandler *) WritePSImage;
1031 entry->magick=(IsImageFormatHandler *) IsPS;
1032 entry->adjoin=MagickFalse;
1033 entry->blob_support=MagickFalse;
1034 entry->seekable_stream=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00001035 entry->description=ConstantString(
cristy5aefbeb2013-08-09 12:13:32 +00001036 "Encapsulated PostScript Interchange format");
1037 entry->mime_type=ConstantString("application/postscript");
cristy3ed852e2009-09-05 21:47:34 +00001038 entry->module=ConstantString("PS");
1039 (void) RegisterMagickInfo(entry);
1040 entry=SetMagickInfo("EPS");
1041 entry->decoder=(DecodeImageHandler *) ReadPSImage;
1042 entry->encoder=(EncodeImageHandler *) WritePSImage;
1043 entry->magick=(IsImageFormatHandler *) IsPS;
1044 entry->adjoin=MagickFalse;
1045 entry->blob_support=MagickFalse;
1046 entry->seekable_stream=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00001047 entry->description=ConstantString("Encapsulated PostScript");
cristy5aefbeb2013-08-09 12:13:32 +00001048 entry->mime_type=ConstantString("application/postscript");
cristy3ed852e2009-09-05 21:47:34 +00001049 entry->module=ConstantString("PS");
1050 (void) RegisterMagickInfo(entry);
1051 entry=SetMagickInfo("EPSF");
1052 entry->decoder=(DecodeImageHandler *) ReadPSImage;
1053 entry->encoder=(EncodeImageHandler *) WritePSImage;
1054 entry->magick=(IsImageFormatHandler *) IsPS;
1055 entry->adjoin=MagickFalse;
1056 entry->blob_support=MagickFalse;
1057 entry->seekable_stream=MagickTrue;
1058 entry->description=ConstantString("Encapsulated PostScript");
cristy5aefbeb2013-08-09 12:13:32 +00001059 entry->mime_type=ConstantString("application/postscript");
cristy3ed852e2009-09-05 21:47:34 +00001060 entry->module=ConstantString("PS");
1061 (void) RegisterMagickInfo(entry);
1062 entry=SetMagickInfo("EPSI");
1063 entry->decoder=(DecodeImageHandler *) ReadPSImage;
1064 entry->encoder=(EncodeImageHandler *) WritePSImage;
1065 entry->magick=(IsImageFormatHandler *) IsPS;
1066 entry->adjoin=MagickFalse;
1067 entry->blob_support=MagickFalse;
1068 entry->seekable_stream=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00001069 entry->description=ConstantString(
1070 "Encapsulated PostScript Interchange format");
cristy5aefbeb2013-08-09 12:13:32 +00001071 entry->mime_type=ConstantString("application/postscript");
cristy3ed852e2009-09-05 21:47:34 +00001072 entry->module=ConstantString("PS");
1073 (void) RegisterMagickInfo(entry);
1074 entry=SetMagickInfo("PS");
1075 entry->decoder=(DecodeImageHandler *) ReadPSImage;
1076 entry->encoder=(EncodeImageHandler *) WritePSImage;
1077 entry->magick=(IsImageFormatHandler *) IsPS;
cristy5aefbeb2013-08-09 12:13:32 +00001078 entry->mime_type=ConstantString("application/postscript");
cristy3ed852e2009-09-05 21:47:34 +00001079 entry->module=ConstantString("PS");
1080 entry->blob_support=MagickFalse;
1081 entry->seekable_stream=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00001082 entry->description=ConstantString("PostScript");
1083 (void) RegisterMagickInfo(entry);
1084 return(MagickImageCoderSignature);
1085}
1086
1087/*
1088%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1089% %
1090% %
1091% %
1092% U n r e g i s t e r P S I m a g e %
1093% %
1094% %
1095% %
1096%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1097%
1098% UnregisterPSImage() removes format registrations made by the
1099% PS module from the list of supported formats.
1100%
1101% The format of the UnregisterPSImage method is:
1102%
1103% UnregisterPSImage(void)
1104%
1105*/
1106ModuleExport void UnregisterPSImage(void)
1107{
1108 (void) UnregisterMagickInfo("EPI");
1109 (void) UnregisterMagickInfo("EPS");
1110 (void) UnregisterMagickInfo("EPSF");
1111 (void) UnregisterMagickInfo("EPSI");
1112 (void) UnregisterMagickInfo("PS");
1113}
1114
1115/*
1116%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1117% %
1118% %
1119% %
1120% W r i t e P S I m a g e %
1121% %
1122% %
1123% %
1124%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1125%
1126% WritePSImage translates an image to encapsulated Postscript
1127% Level I for printing. If the supplied geometry is null, the image is
1128% centered on the Postscript page. Otherwise, the image is positioned as
1129% specified by the geometry.
1130%
1131% The format of the WritePSImage method is:
1132%
cristy1e178e72011-08-28 19:44:34 +00001133% MagickBooleanType WritePSImage(const ImageInfo *image_info,
1134% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001135%
1136% A description of each parameter follows:
1137%
1138% o image_info: the image info.
1139%
1140% o image: the image.
1141%
cristy1e178e72011-08-28 19:44:34 +00001142% o exception: return any errors or warnings in this structure.
1143%
cristy3ed852e2009-09-05 21:47:34 +00001144*/
1145
1146static inline size_t MagickMin(const size_t x,const size_t y)
1147{
1148 if (x < y)
1149 return(x);
1150 return(y);
1151}
1152
1153static inline unsigned char *PopHexPixel(const char **hex_digits,
cristybb503372010-05-27 20:51:26 +00001154 const size_t pixel,unsigned char *pixels)
cristy3ed852e2009-09-05 21:47:34 +00001155{
1156 register const char
1157 *hex;
1158
1159 hex=hex_digits[pixel];
1160 *pixels++=(unsigned char) (*hex++);
1161 *pixels++=(unsigned char) (*hex);
1162 return(pixels);
1163}
1164
cristy1e178e72011-08-28 19:44:34 +00001165static MagickBooleanType WritePSImage(const ImageInfo *image_info,Image *image,
1166 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001167{
1168#define WriteRunlengthPacket(image,pixel,length,p) \
1169{ \
cristy35553db2014-11-23 15:43:29 +00001170 if ((image->alpha_trait == BlendPixelTrait) && \
cristy4c08aed2011-07-01 19:47:50 +00001171 (GetPixelAlpha(image,p) == (Quantum) TransparentAlpha)) \
cristy3ed852e2009-09-05 21:47:34 +00001172 { \
1173 q=PopHexPixel(hex_digits,0xff,q); \
1174 q=PopHexPixel(hex_digits,0xff,q); \
1175 q=PopHexPixel(hex_digits,0xff,q); \
1176 } \
1177 else \
1178 { \
cristyd0323222013-04-07 16:13:21 +00001179 q=PopHexPixel(hex_digits,ScaleQuantumToChar(ClampToQuantum(pixel.red)),q); \
1180 q=PopHexPixel(hex_digits,ScaleQuantumToChar(ClampToQuantum(pixel.green)),q); \
1181 q=PopHexPixel(hex_digits,ScaleQuantumToChar(ClampToQuantum(pixel.blue)),q); \
cristy3ed852e2009-09-05 21:47:34 +00001182 } \
cristy9f027d12011-09-21 01:17:17 +00001183 q=PopHexPixel(hex_digits,(size_t) MagickMin(length,0xff),q); \
cristy3ed852e2009-09-05 21:47:34 +00001184}
1185
1186 static const char
1187 *hex_digits[] =
1188 {
1189 "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0A", "0B",
1190 "0C", "0D", "0E", "0F", "10", "11", "12", "13", "14", "15", "16", "17",
1191 "18", "19", "1A", "1B", "1C", "1D", "1E", "1F", "20", "21", "22", "23",
1192 "24", "25", "26", "27", "28", "29", "2A", "2B", "2C", "2D", "2E", "2F",
1193 "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3A", "3B",
1194 "3C", "3D", "3E", "3F", "40", "41", "42", "43", "44", "45", "46", "47",
1195 "48", "49", "4A", "4B", "4C", "4D", "4E", "4F", "50", "51", "52", "53",
1196 "54", "55", "56", "57", "58", "59", "5A", "5B", "5C", "5D", "5E", "5F",
1197 "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6A", "6B",
1198 "6C", "6D", "6E", "6F", "70", "71", "72", "73", "74", "75", "76", "77",
1199 "78", "79", "7A", "7B", "7C", "7D", "7E", "7F", "80", "81", "82", "83",
1200 "84", "85", "86", "87", "88", "89", "8A", "8B", "8C", "8D", "8E", "8F",
1201 "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9A", "9B",
1202 "9C", "9D", "9E", "9F", "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7",
1203 "A8", "A9", "AA", "AB", "AC", "AD", "AE", "AF", "B0", "B1", "B2", "B3",
1204 "B4", "B5", "B6", "B7", "B8", "B9", "BA", "BB", "BC", "BD", "BE", "BF",
1205 "C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "CA", "CB",
1206 "CC", "CD", "CE", "CF", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
1207 "D8", "D9", "DA", "DB", "DC", "DD", "DE", "DF", "E0", "E1", "E2", "E3",
1208 "E4", "E5", "E6", "E7", "E8", "E9", "EA", "EB", "EC", "ED", "EE", "EF",
1209 "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "FA", "FB",
1210 "FC", "FD", "FE", "FF", (char *) NULL
1211 },
1212 *PostscriptProlog[]=
1213 {
1214 "%%BeginProlog",
1215 "%",
1216 "% Display a color image. The image is displayed in color on",
1217 "% Postscript viewers or printers that support color, otherwise",
1218 "% it is displayed as grayscale.",
1219 "%",
1220 "/DirectClassPacket",
1221 "{",
1222 " %",
1223 " % Get a DirectClass packet.",
1224 " %",
1225 " % Parameters:",
1226 " % red.",
1227 " % green.",
1228 " % blue.",
1229 " % length: number of pixels minus one of this color (optional).",
1230 " %",
1231 " currentfile color_packet readhexstring pop pop",
1232 " compression 0 eq",
1233 " {",
1234 " /number_pixels 3 def",
1235 " }",
1236 " {",
1237 " currentfile byte readhexstring pop 0 get",
1238 " /number_pixels exch 1 add 3 mul def",
1239 " } ifelse",
1240 " 0 3 number_pixels 1 sub",
1241 " {",
1242 " pixels exch color_packet putinterval",
1243 " } for",
1244 " pixels 0 number_pixels getinterval",
1245 "} bind def",
1246 "",
1247 "/DirectClassImage",
1248 "{",
1249 " %",
1250 " % Display a DirectClass image.",
1251 " %",
1252 " systemdict /colorimage known",
1253 " {",
1254 " columns rows 8",
1255 " [",
1256 " columns 0 0",
1257 " rows neg 0 rows",
1258 " ]",
1259 " { DirectClassPacket } false 3 colorimage",
1260 " }",
1261 " {",
1262 " %",
1263 " % No colorimage operator; convert to grayscale.",
1264 " %",
1265 " columns rows 8",
1266 " [",
1267 " columns 0 0",
1268 " rows neg 0 rows",
1269 " ]",
1270 " { GrayDirectClassPacket } image",
1271 " } ifelse",
1272 "} bind def",
1273 "",
1274 "/GrayDirectClassPacket",
1275 "{",
1276 " %",
1277 " % Get a DirectClass packet; convert to grayscale.",
1278 " %",
1279 " % Parameters:",
1280 " % red",
1281 " % green",
1282 " % blue",
1283 " % length: number of pixels minus one of this color (optional).",
1284 " %",
1285 " currentfile color_packet readhexstring pop pop",
1286 " color_packet 0 get 0.299 mul",
1287 " color_packet 1 get 0.587 mul add",
1288 " color_packet 2 get 0.114 mul add",
1289 " cvi",
1290 " /gray_packet exch def",
1291 " compression 0 eq",
1292 " {",
1293 " /number_pixels 1 def",
1294 " }",
1295 " {",
1296 " currentfile byte readhexstring pop 0 get",
1297 " /number_pixels exch 1 add def",
1298 " } ifelse",
1299 " 0 1 number_pixels 1 sub",
1300 " {",
1301 " pixels exch gray_packet put",
1302 " } for",
1303 " pixels 0 number_pixels getinterval",
1304 "} bind def",
1305 "",
1306 "/GrayPseudoClassPacket",
1307 "{",
1308 " %",
1309 " % Get a PseudoClass packet; convert to grayscale.",
1310 " %",
1311 " % Parameters:",
1312 " % index: index into the colormap.",
1313 " % length: number of pixels minus one of this color (optional).",
1314 " %",
1315 " currentfile byte readhexstring pop 0 get",
1316 " /offset exch 3 mul def",
1317 " /color_packet colormap offset 3 getinterval def",
1318 " color_packet 0 get 0.299 mul",
1319 " color_packet 1 get 0.587 mul add",
1320 " color_packet 2 get 0.114 mul add",
1321 " cvi",
1322 " /gray_packet exch def",
1323 " compression 0 eq",
1324 " {",
1325 " /number_pixels 1 def",
1326 " }",
1327 " {",
1328 " currentfile byte readhexstring pop 0 get",
1329 " /number_pixels exch 1 add def",
1330 " } ifelse",
1331 " 0 1 number_pixels 1 sub",
1332 " {",
1333 " pixels exch gray_packet put",
1334 " } for",
1335 " pixels 0 number_pixels getinterval",
1336 "} bind def",
1337 "",
1338 "/PseudoClassPacket",
1339 "{",
1340 " %",
1341 " % Get a PseudoClass packet.",
1342 " %",
1343 " % Parameters:",
1344 " % index: index into the colormap.",
1345 " % length: number of pixels minus one of this color (optional).",
1346 " %",
1347 " currentfile byte readhexstring pop 0 get",
1348 " /offset exch 3 mul def",
1349 " /color_packet colormap offset 3 getinterval def",
1350 " compression 0 eq",
1351 " {",
1352 " /number_pixels 3 def",
1353 " }",
1354 " {",
1355 " currentfile byte readhexstring pop 0 get",
1356 " /number_pixels exch 1 add 3 mul def",
1357 " } ifelse",
1358 " 0 3 number_pixels 1 sub",
1359 " {",
1360 " pixels exch color_packet putinterval",
1361 " } for",
1362 " pixels 0 number_pixels getinterval",
1363 "} bind def",
1364 "",
1365 "/PseudoClassImage",
1366 "{",
1367 " %",
1368 " % Display a PseudoClass image.",
1369 " %",
1370 " % Parameters:",
1371 " % class: 0-PseudoClass or 1-Grayscale.",
1372 " %",
1373 " currentfile buffer readline pop",
1374 " token pop /class exch def pop",
1375 " class 0 gt",
1376 " {",
1377 " currentfile buffer readline pop",
1378 " token pop /depth exch def pop",
1379 " /grays columns 8 add depth sub depth mul 8 idiv string def",
1380 " columns rows depth",
1381 " [",
1382 " columns 0 0",
1383 " rows neg 0 rows",
1384 " ]",
1385 " { currentfile grays readhexstring pop } image",
1386 " }",
1387 " {",
1388 " %",
1389 " % Parameters:",
1390 " % colors: number of colors in the colormap.",
1391 " % colormap: red, green, blue color packets.",
1392 " %",
1393 " currentfile buffer readline pop",
1394 " token pop /colors exch def pop",
1395 " /colors colors 3 mul def",
1396 " /colormap colors string def",
1397 " currentfile colormap readhexstring pop pop",
1398 " systemdict /colorimage known",
1399 " {",
1400 " columns rows 8",
1401 " [",
1402 " columns 0 0",
1403 " rows neg 0 rows",
1404 " ]",
1405 " { PseudoClassPacket } false 3 colorimage",
1406 " }",
1407 " {",
1408 " %",
1409 " % No colorimage operator; convert to grayscale.",
1410 " %",
1411 " columns rows 8",
1412 " [",
1413 " columns 0 0",
1414 " rows neg 0 rows",
1415 " ]",
1416 " { GrayPseudoClassPacket } image",
1417 " } ifelse",
1418 " } ifelse",
1419 "} bind def",
1420 "",
1421 "/DisplayImage",
1422 "{",
1423 " %",
1424 " % Display a DirectClass or PseudoClass image.",
1425 " %",
1426 " % Parameters:",
1427 " % x & y translation.",
1428 " % x & y scale.",
1429 " % label pointsize.",
1430 " % image label.",
1431 " % image columns & rows.",
1432 " % class: 0-DirectClass or 1-PseudoClass.",
1433 " % compression: 0-none or 1-RunlengthEncoded.",
1434 " % hex color packets.",
1435 " %",
1436 " gsave",
1437 " /buffer 512 string def",
1438 " /byte 1 string def",
1439 " /color_packet 3 string def",
1440 " /pixels 768 string def",
1441 "",
1442 " currentfile buffer readline pop",
1443 " token pop /x exch def",
1444 " token pop /y exch def pop",
1445 " x y translate",
1446 " currentfile buffer readline pop",
1447 " token pop /x exch def",
1448 " token pop /y exch def pop",
1449 " currentfile buffer readline pop",
1450 " token pop /pointsize exch def pop",
1451 " /Times-Roman findfont pointsize scalefont setfont",
1452 (char *) NULL
1453 },
1454 *PostscriptEpilog[]=
1455 {
1456 " x y scale",
1457 " currentfile buffer readline pop",
1458 " token pop /columns exch def",
1459 " token pop /rows exch def pop",
1460 " currentfile buffer readline pop",
1461 " token pop /class exch def pop",
1462 " currentfile buffer readline pop",
1463 " token pop /compression exch def pop",
1464 " class 0 gt { PseudoClassImage } { DirectClassImage } ifelse",
cristy8b4ff222012-03-02 13:01:39 +00001465 " grestore",
cristy3ed852e2009-09-05 21:47:34 +00001466 (char *) NULL
1467 };
1468
1469 char
1470 buffer[MaxTextExtent],
1471 date[MaxTextExtent],
1472 **labels,
1473 page_geometry[MaxTextExtent];
1474
cristyaf8d3912014-02-21 14:50:33 +00001475 CompressionType
1476 compression;
1477
cristy3ed852e2009-09-05 21:47:34 +00001478 const char
1479 **s,
1480 *value;
1481
1482 const StringInfo
1483 *profile;
1484
1485 double
1486 pointsize;
1487
1488 GeometryInfo
1489 geometry_info;
1490
cristy3ed852e2009-09-05 21:47:34 +00001491 MagickBooleanType
1492 status;
1493
1494 MagickOffsetType
1495 scene;
1496
1497 MagickStatusType
1498 flags;
1499
cristy101ab702011-10-13 13:06:32 +00001500 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001501 pixel;
1502
1503 PointInfo
1504 delta,
1505 resolution,
1506 scale;
1507
cristy4c08aed2011-07-01 19:47:50 +00001508 Quantum
1509 index;
1510
cristy3ed852e2009-09-05 21:47:34 +00001511 RectangleInfo
1512 geometry,
1513 media_info,
1514 page_info;
1515
cristy4c08aed2011-07-01 19:47:50 +00001516 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00001517 *p;
1518
cristybb503372010-05-27 20:51:26 +00001519 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001520 i,
1521 x;
1522
1523 register unsigned char
1524 *q;
1525
1526 SegmentInfo
1527 bounds;
1528
1529 size_t
cristy802d3642011-04-27 02:02:41 +00001530 bit,
1531 byte,
1532 length,
1533 page,
1534 text_size;
1535
1536 ssize_t
1537 j,
1538 y;
cristy3ed852e2009-09-05 21:47:34 +00001539
1540 time_t
1541 timer;
1542
1543 unsigned char
1544 pixels[2048];
1545
cristy3ed852e2009-09-05 21:47:34 +00001546 /*
1547 Open output image file.
1548 */
1549 assert(image_info != (const ImageInfo *) NULL);
1550 assert(image_info->signature == MagickSignature);
1551 assert(image != (Image *) NULL);
1552 assert(image->signature == MagickSignature);
1553 if (image->debug != MagickFalse)
1554 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +00001555 assert(exception != (ExceptionInfo *) NULL);
1556 assert(exception->signature == MagickSignature);
cristy1e178e72011-08-28 19:44:34 +00001557 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +00001558 if (status == MagickFalse)
1559 return(status);
1560 (void) ResetMagickMemory(&bounds,0,sizeof(bounds));
cristyaf8d3912014-02-21 14:50:33 +00001561 compression=image->compression;
1562 if (image_info->compression != UndefinedCompression)
1563 compression=image_info->compression;
cristy3ed852e2009-09-05 21:47:34 +00001564 page=1;
1565 scene=0;
1566 do
1567 {
1568 /*
1569 Scale relative to dots-per-inch.
1570 */
cristyaf8d3912014-02-21 14:50:33 +00001571 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +00001572 delta.x=DefaultResolution;
1573 delta.y=DefaultResolution;
cristy2a11bef2011-10-28 18:33:11 +00001574 resolution.x=image->resolution.x;
1575 resolution.y=image->resolution.y;
cristy3ed852e2009-09-05 21:47:34 +00001576 if ((resolution.x == 0.0) || (resolution.y == 0.0))
1577 {
1578 flags=ParseGeometry(PSDensityGeometry,&geometry_info);
1579 resolution.x=geometry_info.rho;
1580 resolution.y=geometry_info.sigma;
1581 if ((flags & SigmaValue) == 0)
1582 resolution.y=resolution.x;
1583 }
1584 if (image_info->density != (char *) NULL)
1585 {
1586 flags=ParseGeometry(image_info->density,&geometry_info);
1587 resolution.x=geometry_info.rho;
1588 resolution.y=geometry_info.sigma;
1589 if ((flags & SigmaValue) == 0)
1590 resolution.y=resolution.x;
1591 }
1592 if (image->units == PixelsPerCentimeterResolution)
1593 {
cristya97426c2011-02-04 01:41:27 +00001594 resolution.x=(double) ((size_t) (100.0*2.54*resolution.x+0.5)/100.0);
1595 resolution.y=(double) ((size_t) (100.0*2.54*resolution.y+0.5)/100.0);
cristy3ed852e2009-09-05 21:47:34 +00001596 }
1597 SetGeometry(image,&geometry);
cristyb51dff52011-05-19 16:55:47 +00001598 (void) FormatLocaleString(page_geometry,MaxTextExtent,"%.20gx%.20g",
cristye8c25f92010-06-03 00:53:06 +00001599 (double) image->columns,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001600 if (image_info->page != (char *) NULL)
1601 (void) CopyMagickString(page_geometry,image_info->page,MaxTextExtent);
1602 else
1603 if ((image->page.width != 0) && (image->page.height != 0))
cristyb51dff52011-05-19 16:55:47 +00001604 (void) FormatLocaleString(page_geometry,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +00001605 "%.20gx%.20g%+.20g%+.20g",(double) image->page.width,(double)
cristye8c25f92010-06-03 00:53:06 +00001606 image->page.height,(double) image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00001607 else
1608 if ((image->gravity != UndefinedGravity) &&
1609 (LocaleCompare(image_info->magick,"PS") == 0))
1610 (void) CopyMagickString(page_geometry,PSPageGeometry,MaxTextExtent);
1611 (void) ConcatenateMagickString(page_geometry,">",MaxTextExtent);
1612 (void) ParseMetaGeometry(page_geometry,&geometry.x,&geometry.y,
1613 &geometry.width,&geometry.height);
1614 scale.x=(double) (geometry.width*delta.x)/resolution.x;
cristybb503372010-05-27 20:51:26 +00001615 geometry.width=(size_t) floor(scale.x+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001616 scale.y=(double) (geometry.height*delta.y)/resolution.y;
cristybb503372010-05-27 20:51:26 +00001617 geometry.height=(size_t) floor(scale.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001618 (void) ParseAbsoluteGeometry(page_geometry,&media_info);
cristy1e178e72011-08-28 19:44:34 +00001619 (void) ParseGravityGeometry(image,page_geometry,&page_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00001620 if (image->gravity != UndefinedGravity)
1621 {
1622 geometry.x=(-page_info.x);
cristybb503372010-05-27 20:51:26 +00001623 geometry.y=(ssize_t) (media_info.height+page_info.y-image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001624 }
1625 pointsize=12.0;
1626 if (image_info->pointsize != 0.0)
1627 pointsize=image_info->pointsize;
1628 text_size=0;
cristyd15e6592011-10-15 00:13:06 +00001629 value=GetImageProperty(image,"label",exception);
cristy3ed852e2009-09-05 21:47:34 +00001630 if (value != (const char *) NULL)
cristybb503372010-05-27 20:51:26 +00001631 text_size=(size_t) (MultilineCensus(value)*pointsize+12);
cristy3ed852e2009-09-05 21:47:34 +00001632 if (page == 1)
1633 {
1634 /*
1635 Output Postscript header.
1636 */
1637 if (LocaleCompare(image_info->magick,"PS") == 0)
1638 (void) CopyMagickString(buffer,"%!PS-Adobe-3.0\n",MaxTextExtent);
1639 else
1640 (void) CopyMagickString(buffer,"%!PS-Adobe-3.0 EPSF-3.0\n",
1641 MaxTextExtent);
1642 (void) WriteBlobString(image,buffer);
1643 (void) WriteBlobString(image,"%%Creator: (ImageMagick)\n");
cristyb51dff52011-05-19 16:55:47 +00001644 (void) FormatLocaleString(buffer,MaxTextExtent,"%%%%Title: (%s)\n",
cristy3ed852e2009-09-05 21:47:34 +00001645 image->filename);
1646 (void) WriteBlobString(image,buffer);
1647 timer=time((time_t *) NULL);
1648 (void) FormatMagickTime(timer,MaxTextExtent,date);
cristyb51dff52011-05-19 16:55:47 +00001649 (void) FormatLocaleString(buffer,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00001650 "%%%%CreationDate: (%s)\n",date);
1651 (void) WriteBlobString(image,buffer);
1652 bounds.x1=(double) geometry.x;
1653 bounds.y1=(double) geometry.y;
1654 bounds.x2=(double) geometry.x+scale.x;
1655 bounds.y2=(double) geometry.y+(geometry.height+text_size);
1656 if ((image_info->adjoin != MagickFalse) &&
1657 (GetNextImageInList(image) != (Image *) NULL))
1658 (void) CopyMagickString(buffer,"%%%%BoundingBox: (atend)\n",
1659 MaxTextExtent);
1660 else
1661 {
cristyb51dff52011-05-19 16:55:47 +00001662 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001663 "%%%%BoundingBox: %.20g %.20g %.20g %.20g\n",ceil(bounds.x1-0.5),
cristy8071c472012-09-24 12:41:06 +00001664 ceil(bounds.y1-0.5),floor(bounds.x2+0.5),floor(bounds.y2+0.5));
cristy3ed852e2009-09-05 21:47:34 +00001665 (void) WriteBlobString(image,buffer);
cristyb51dff52011-05-19 16:55:47 +00001666 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001667 "%%%%HiResBoundingBox: %g %g %g %g\n",bounds.x1,
cristy8cd5b312010-01-07 01:10:24 +00001668 bounds.y1,bounds.x2,bounds.y2);
cristy3ed852e2009-09-05 21:47:34 +00001669 }
1670 (void) WriteBlobString(image,buffer);
1671 profile=GetImageProfile(image,"8bim");
1672 if (profile != (StringInfo *) NULL)
1673 {
1674 /*
1675 Embed Photoshop profile.
1676 */
cristyb51dff52011-05-19 16:55:47 +00001677 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001678 "%%BeginPhotoshop: %.20g",(double) GetStringInfoLength(profile));
cristy3ed852e2009-09-05 21:47:34 +00001679 (void) WriteBlobString(image,buffer);
cristybb503372010-05-27 20:51:26 +00001680 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
cristy3ed852e2009-09-05 21:47:34 +00001681 {
1682 if ((i % 32) == 0)
1683 (void) WriteBlobString(image,"\n% ");
cristyb51dff52011-05-19 16:55:47 +00001684 (void) FormatLocaleString(buffer,MaxTextExtent,"%02X",
cristy3ed852e2009-09-05 21:47:34 +00001685 (unsigned int) (GetStringInfoDatum(profile)[i] & 0xff));
1686 (void) WriteBlobString(image,buffer);
1687 }
1688 (void) WriteBlobString(image,"\n%EndPhotoshop\n");
1689 }
1690 profile=GetImageProfile(image,"xmp");
dirk93b02b72013-11-16 16:03:36 +00001691DisableMSCWarning(4127)
cristy2b2eb912010-03-03 14:56:40 +00001692 if (0 && (profile != (StringInfo *) NULL))
dirk93b02b72013-11-16 16:03:36 +00001693RestoreMSCWarning
cristy3ed852e2009-09-05 21:47:34 +00001694 {
1695 /*
1696 Embed XML profile.
1697 */
1698 (void) WriteBlobString(image,"\n%begin_xml_code\n");
cristyb51dff52011-05-19 16:55:47 +00001699 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001700 "\n%%begin_xml_packet: %.20g\n",(double)
cristyeec18db2010-03-03 21:15:45 +00001701 GetStringInfoLength(profile));
cristy2b2eb912010-03-03 14:56:40 +00001702 (void) WriteBlobString(image,buffer);
cristybb503372010-05-27 20:51:26 +00001703 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
cristy3ed852e2009-09-05 21:47:34 +00001704 (void) WriteBlobByte(image,GetStringInfoDatum(profile)[i]);
cristy2b2eb912010-03-03 14:56:40 +00001705 (void) WriteBlobString(image,"\n%end_xml_packet\n%end_xml_code\n");
cristy3ed852e2009-09-05 21:47:34 +00001706 }
cristyd15e6592011-10-15 00:13:06 +00001707 value=GetImageProperty(image,"label",exception);
cristy3ed852e2009-09-05 21:47:34 +00001708 if (value != (const char *) NULL)
1709 (void) WriteBlobString(image,
1710 "%%DocumentNeededResources: font Times-Roman\n");
1711 (void) WriteBlobString(image,"%%DocumentData: Clean7Bit\n");
1712 (void) WriteBlobString(image,"%%LanguageLevel: 1\n");
1713 if (LocaleCompare(image_info->magick,"PS") != 0)
1714 (void) WriteBlobString(image,"%%Pages: 1\n");
1715 else
1716 {
1717 /*
1718 Compute the number of pages.
1719 */
1720 (void) WriteBlobString(image,"%%Orientation: Portrait\n");
1721 (void) WriteBlobString(image,"%%PageOrder: Ascend\n");
cristyb51dff52011-05-19 16:55:47 +00001722 (void) FormatLocaleString(buffer,MaxTextExtent,"%%%%Pages: %.20g\n",
cristye8c25f92010-06-03 00:53:06 +00001723 image_info->adjoin != MagickFalse ? (double)
1724 GetImageListLength(image) : 1.0);
cristy3ed852e2009-09-05 21:47:34 +00001725 (void) WriteBlobString(image,buffer);
1726 }
1727 (void) WriteBlobString(image,"%%EndComments\n");
1728 (void) WriteBlobString(image,"\n%%BeginDefaults\n");
1729 (void) WriteBlobString(image,"%%EndDefaults\n\n");
1730 if ((LocaleCompare(image_info->magick,"EPI") == 0) ||
1731 (LocaleCompare(image_info->magick,"EPSI") == 0) ||
1732 (LocaleCompare(image_info->magick,"EPT") == 0))
1733 {
1734 Image
1735 *preview_image;
1736
cristy3ed852e2009-09-05 21:47:34 +00001737 Quantum
1738 pixel;
1739
cristybb503372010-05-27 20:51:26 +00001740 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001741 x;
1742
cristy802d3642011-04-27 02:02:41 +00001743 ssize_t
1744 y;
1745
cristy3ed852e2009-09-05 21:47:34 +00001746 /*
1747 Create preview image.
1748 */
cristy1e178e72011-08-28 19:44:34 +00001749 preview_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00001750 if (preview_image == (Image *) NULL)
1751 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1752 /*
1753 Dump image as bitmap.
1754 */
cristyb51dff52011-05-19 16:55:47 +00001755 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001756 "%%%%BeginPreview: %.20g %.20g %.20g %.20g\n%% ",(double)
1757 preview_image->columns,(double) preview_image->rows,1.0,
1758 (double) ((((preview_image->columns+7) >> 3)*preview_image->rows+
1759 35)/36));
cristy3ed852e2009-09-05 21:47:34 +00001760 (void) WriteBlobString(image,buffer);
1761 q=pixels;
cristybb503372010-05-27 20:51:26 +00001762 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001763 {
1764 p=GetVirtualPixels(preview_image,0,y,preview_image->columns,1,
cristy1e178e72011-08-28 19:44:34 +00001765 exception);
cristy4c08aed2011-07-01 19:47:50 +00001766 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001767 break;
cristy3ed852e2009-09-05 21:47:34 +00001768 bit=0;
1769 byte=0;
cristybb503372010-05-27 20:51:26 +00001770 for (x=0; x < (ssize_t) preview_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001771 {
1772 byte<<=1;
cristyd0323222013-04-07 16:13:21 +00001773 pixel=ClampToQuantum(GetPixelLuma(preview_image,p));
cristy3ed852e2009-09-05 21:47:34 +00001774 if (pixel >= (Quantum) (QuantumRange/2))
1775 byte|=0x01;
1776 bit++;
1777 if (bit == 8)
1778 {
1779 q=PopHexPixel(hex_digits,byte,q);
1780 if ((q-pixels+8) >= 80)
1781 {
1782 *q++='\n';
1783 (void) WriteBlob(image,q-pixels,pixels);
1784 q=pixels;
1785 (void) WriteBlobString(image,"% ");
1786 };
1787 bit=0;
1788 byte=0;
1789 }
1790 }
1791 if (bit != 0)
1792 {
1793 byte<<=(8-bit);
1794 q=PopHexPixel(hex_digits,byte,q);
1795 if ((q-pixels+8) >= 80)
1796 {
1797 *q++='\n';
1798 (void) WriteBlob(image,q-pixels,pixels);
1799 q=pixels;
1800 (void) WriteBlobString(image,"% ");
1801 };
1802 };
1803 }
1804 if (q != pixels)
1805 {
1806 *q++='\n';
1807 (void) WriteBlob(image,q-pixels,pixels);
1808 }
1809 (void) WriteBlobString(image,"\n%%EndPreview\n");
1810 preview_image=DestroyImage(preview_image);
1811 }
1812 /*
1813 Output Postscript commands.
1814 */
1815 for (s=PostscriptProlog; *s != (char *) NULL; s++)
1816 {
cristyb51dff52011-05-19 16:55:47 +00001817 (void) FormatLocaleString(buffer,MaxTextExtent,"%s\n",*s);
cristy3ed852e2009-09-05 21:47:34 +00001818 (void) WriteBlobString(image,buffer);
1819 }
cristyd15e6592011-10-15 00:13:06 +00001820 value=GetImageProperty(image,"label",exception);
cristy3ed852e2009-09-05 21:47:34 +00001821 if (value != (const char *) NULL)
cristybb503372010-05-27 20:51:26 +00001822 for (j=(ssize_t) MultilineCensus(value)-1; j >= 0; j--)
cristy3ed852e2009-09-05 21:47:34 +00001823 {
1824 (void) WriteBlobString(image," /label 512 string def\n");
1825 (void) WriteBlobString(image," currentfile label readline pop\n");
cristyb51dff52011-05-19 16:55:47 +00001826 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001827 " 0 y %g add moveto label show pop\n",j*pointsize+12);
cristy3ed852e2009-09-05 21:47:34 +00001828 (void) WriteBlobString(image,buffer);
1829 }
1830 for (s=PostscriptEpilog; *s != (char *) NULL; s++)
1831 {
cristyb51dff52011-05-19 16:55:47 +00001832 (void) FormatLocaleString(buffer,MaxTextExtent,"%s\n",*s);
cristy3ed852e2009-09-05 21:47:34 +00001833 (void) WriteBlobString(image,buffer);
1834 }
1835 if (LocaleCompare(image_info->magick,"PS") == 0)
1836 (void) WriteBlobString(image," showpage\n");
1837 (void) WriteBlobString(image,"} bind def\n");
1838 (void) WriteBlobString(image,"%%EndProlog\n");
1839 }
cristyb51dff52011-05-19 16:55:47 +00001840 (void) FormatLocaleString(buffer,MaxTextExtent,"%%%%Page: 1 %.20g\n",
cristye8c25f92010-06-03 00:53:06 +00001841 (double) (page++));
cristy3ed852e2009-09-05 21:47:34 +00001842 (void) WriteBlobString(image,buffer);
cristyb51dff52011-05-19 16:55:47 +00001843 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001844 "%%%%PageBoundingBox: %.20g %.20g %.20g %.20g\n",(double) geometry.x,
1845 (double) geometry.y,geometry.x+(double) geometry.width,geometry.y+(double)
cristyf2faecf2010-05-28 19:19:36 +00001846 (geometry.height+text_size));
cristy3ed852e2009-09-05 21:47:34 +00001847 (void) WriteBlobString(image,buffer);
1848 if ((double) geometry.x < bounds.x1)
1849 bounds.x1=(double) geometry.x;
1850 if ((double) geometry.y < bounds.y1)
1851 bounds.y1=(double) geometry.y;
1852 if ((double) (geometry.x+geometry.width-1) > bounds.x2)
1853 bounds.x2=(double) geometry.x+geometry.width-1;
1854 if ((double) (geometry.y+(geometry.height+text_size)-1) > bounds.y2)
1855 bounds.y2=(double) geometry.y+(geometry.height+text_size)-1;
cristyd15e6592011-10-15 00:13:06 +00001856 value=GetImageProperty(image,"label",exception);
cristy3ed852e2009-09-05 21:47:34 +00001857 if (value != (const char *) NULL)
1858 (void) WriteBlobString(image,"%%%%PageResources: font Times-Roman\n");
1859 if (LocaleCompare(image_info->magick,"PS") != 0)
1860 (void) WriteBlobString(image,"userdict begin\n");
1861 (void) WriteBlobString(image,"DisplayImage\n");
1862 /*
1863 Output image data.
1864 */
cristyb51dff52011-05-19 16:55:47 +00001865 (void) FormatLocaleString(buffer,MaxTextExtent,"%.20g %.20g\n%g %g\n%g\n",
cristye8c25f92010-06-03 00:53:06 +00001866 (double) geometry.x,(double) geometry.y,scale.x,scale.y,pointsize);
cristy3ed852e2009-09-05 21:47:34 +00001867 (void) WriteBlobString(image,buffer);
1868 labels=(char **) NULL;
cristyd15e6592011-10-15 00:13:06 +00001869 value=GetImageProperty(image,"label",exception);
cristy3ed852e2009-09-05 21:47:34 +00001870 if (value != (const char *) NULL)
1871 labels=StringToList(value);
1872 if (labels != (char **) NULL)
1873 {
1874 for (i=0; labels[i] != (char *) NULL; i++)
1875 {
cristyb51dff52011-05-19 16:55:47 +00001876 (void) FormatLocaleString(buffer,MaxTextExtent,"%s \n",
cristy3ed852e2009-09-05 21:47:34 +00001877 labels[i]);
1878 (void) WriteBlobString(image,buffer);
1879 labels[i]=DestroyString(labels[i]);
1880 }
1881 labels=(char **) RelinquishMagickMemory(labels);
1882 }
1883 (void) ResetMagickMemory(&pixel,0,sizeof(pixel));
cristyd0323222013-04-07 16:13:21 +00001884 pixel.alpha=(MagickRealType) TransparentAlpha;
cristy4c08aed2011-07-01 19:47:50 +00001885 index=0;
cristy3ed852e2009-09-05 21:47:34 +00001886 x=0;
1887 if ((image_info->type != TrueColorType) &&
cristy1e178e72011-08-28 19:44:34 +00001888 (IsImageGray(image,exception) != MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +00001889 {
cristy1e178e72011-08-28 19:44:34 +00001890 if (IsImageMonochrome(image,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001891 {
1892 Quantum
1893 pixel;
1894
1895 /*
1896 Dump image as grayscale.
1897 */
cristyb51dff52011-05-19 16:55:47 +00001898 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001899 "%.20g %.20g\n1\n1\n1\n8\n",(double) image->columns,(double)
1900 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001901 (void) WriteBlobString(image,buffer);
1902 q=pixels;
cristybb503372010-05-27 20:51:26 +00001903 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001904 {
cristy1e178e72011-08-28 19:44:34 +00001905 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001906 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001907 break;
cristybb503372010-05-27 20:51:26 +00001908 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001909 {
cristyd0323222013-04-07 16:13:21 +00001910 pixel=(Quantum) ScaleQuantumToChar(ClampToQuantum(GetPixelLuma(
1911 image,p)));
cristya97426c2011-02-04 01:41:27 +00001912 q=PopHexPixel(hex_digits,(size_t) pixel,q);
cristy3ed852e2009-09-05 21:47:34 +00001913 i++;
1914 if ((q-pixels+8) >= 80)
1915 {
1916 *q++='\n';
1917 (void) WriteBlob(image,q-pixels,pixels);
1918 q=pixels;
1919 }
cristyed231572011-07-14 02:18:59 +00001920 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001921 }
1922 if (image->previous == (Image *) NULL)
1923 {
cristya97426c2011-02-04 01:41:27 +00001924 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType)
1925 y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001926 if (status == MagickFalse)
1927 break;
1928 }
1929 }
1930 if (q != pixels)
1931 {
1932 *q++='\n';
1933 (void) WriteBlob(image,q-pixels,pixels);
1934 }
1935 }
1936 else
1937 {
cristybb503372010-05-27 20:51:26 +00001938 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001939 y;
1940
1941 Quantum
1942 pixel;
1943
1944 /*
1945 Dump image as bitmap.
1946 */
cristyb51dff52011-05-19 16:55:47 +00001947 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001948 "%.20g %.20g\n1\n1\n1\n1\n",(double) image->columns,(double)
1949 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001950 (void) WriteBlobString(image,buffer);
1951 q=pixels;
cristybb503372010-05-27 20:51:26 +00001952 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001953 {
cristy1e178e72011-08-28 19:44:34 +00001954 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001955 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001956 break;
cristy3ed852e2009-09-05 21:47:34 +00001957 bit=0;
1958 byte=0;
cristybb503372010-05-27 20:51:26 +00001959 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001960 {
1961 byte<<=1;
cristyd0323222013-04-07 16:13:21 +00001962 pixel=ClampToQuantum(GetPixelLuma(image,p));
cristy3ed852e2009-09-05 21:47:34 +00001963 if (pixel >= (Quantum) (QuantumRange/2))
1964 byte|=0x01;
1965 bit++;
1966 if (bit == 8)
1967 {
1968 q=PopHexPixel(hex_digits,byte,q);
1969 if ((q-pixels+2) >= 80)
1970 {
1971 *q++='\n';
1972 (void) WriteBlob(image,q-pixels,pixels);
1973 q=pixels;
1974 };
1975 bit=0;
1976 byte=0;
1977 }
cristyed231572011-07-14 02:18:59 +00001978 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001979 }
1980 if (bit != 0)
1981 {
1982 byte<<=(8-bit);
1983 q=PopHexPixel(hex_digits,byte,q);
1984 if ((q-pixels+2) >= 80)
1985 {
1986 *q++='\n';
1987 (void) WriteBlob(image,q-pixels,pixels);
1988 q=pixels;
1989 }
1990 };
1991 if (image->previous == (Image *) NULL)
1992 {
cristy6cde06a2011-11-24 00:08:43 +00001993 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType)
cristy802d3642011-04-27 02:02:41 +00001994 y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001995 if (status == MagickFalse)
1996 break;
1997 }
1998 }
1999 if (q != pixels)
2000 {
2001 *q++='\n';
2002 (void) WriteBlob(image,q-pixels,pixels);
2003 }
2004 }
2005 }
2006 else
2007 if ((image->storage_class == DirectClass) ||
cristy35553db2014-11-23 15:43:29 +00002008 (image->colors > 256) || (image->alpha_trait == BlendPixelTrait))
cristy3ed852e2009-09-05 21:47:34 +00002009 {
2010 /*
2011 Dump DirectClass image.
2012 */
cristyb51dff52011-05-19 16:55:47 +00002013 (void) FormatLocaleString(buffer,MaxTextExtent,"%.20g %.20g\n0\n%d\n",
cristye8c25f92010-06-03 00:53:06 +00002014 (double) image->columns,(double) image->rows,
cristyaf8d3912014-02-21 14:50:33 +00002015 compression == RLECompression ? 1 : 0);
cristy3ed852e2009-09-05 21:47:34 +00002016 (void) WriteBlobString(image,buffer);
cristyaf8d3912014-02-21 14:50:33 +00002017 switch (compression)
cristy3ed852e2009-09-05 21:47:34 +00002018 {
2019 case RLECompression:
2020 {
2021 /*
2022 Dump runlength-encoded DirectColor packets.
2023 */
2024 q=pixels;
cristybb503372010-05-27 20:51:26 +00002025 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002026 {
cristy1e178e72011-08-28 19:44:34 +00002027 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002028 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002029 break;
cristy101ab702011-10-13 13:06:32 +00002030 GetPixelInfoPixel(image,p,&pixel);
cristy3ed852e2009-09-05 21:47:34 +00002031 length=255;
cristybb503372010-05-27 20:51:26 +00002032 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002033 {
cristyd0323222013-04-07 16:13:21 +00002034 if ((GetPixelRed(image,p) == ClampToQuantum(pixel.red)) &&
2035 (GetPixelGreen(image,p) == ClampToQuantum(pixel.green)) &&
2036 (GetPixelBlue(image,p) == ClampToQuantum(pixel.blue)) &&
2037 (GetPixelAlpha(image,p) == ClampToQuantum(pixel.alpha)) &&
cristy802d3642011-04-27 02:02:41 +00002038 (length < 255) && (x < (ssize_t) (image->columns-1)))
cristy3ed852e2009-09-05 21:47:34 +00002039 length++;
2040 else
2041 {
2042 if (x > 0)
2043 {
2044 WriteRunlengthPacket(image,pixel,length,p);
2045 if ((q-pixels+10) >= 80)
2046 {
2047 *q++='\n';
2048 (void) WriteBlob(image,q-pixels,pixels);
2049 q=pixels;
2050 }
2051 }
2052 length=0;
2053 }
cristy101ab702011-10-13 13:06:32 +00002054 GetPixelInfoPixel(image,p,&pixel);
cristyed231572011-07-14 02:18:59 +00002055 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002056 }
2057 WriteRunlengthPacket(image,pixel,length,p);
2058 if ((q-pixels+10) >= 80)
2059 {
2060 *q++='\n';
2061 (void) WriteBlob(image,q-pixels,pixels);
2062 q=pixels;
2063 }
2064 if (image->previous == (Image *) NULL)
2065 {
cristy802d3642011-04-27 02:02:41 +00002066 status=SetImageProgress(image,SaveImageTag,
2067 (MagickOffsetType) y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002068 if (status == MagickFalse)
2069 break;
2070 }
2071 }
2072 if (q != pixels)
2073 {
2074 *q++='\n';
2075 (void) WriteBlob(image,q-pixels,pixels);
2076 }
2077 break;
2078 }
2079 case NoCompression:
2080 default:
2081 {
2082 /*
2083 Dump uncompressed DirectColor packets.
2084 */
2085 q=pixels;
cristybb503372010-05-27 20:51:26 +00002086 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002087 {
cristy1e178e72011-08-28 19:44:34 +00002088 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002089 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002090 break;
cristybb503372010-05-27 20:51:26 +00002091 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002092 {
cristy35553db2014-11-23 15:43:29 +00002093 if ((image->alpha_trait == BlendPixelTrait) &&
cristy4c08aed2011-07-01 19:47:50 +00002094 (GetPixelAlpha(image,p) == (Quantum) TransparentAlpha))
cristy3ed852e2009-09-05 21:47:34 +00002095 {
2096 q=PopHexPixel(hex_digits,0xff,q);
2097 q=PopHexPixel(hex_digits,0xff,q);
2098 q=PopHexPixel(hex_digits,0xff,q);
2099 }
2100 else
2101 {
cristy802d3642011-04-27 02:02:41 +00002102 q=PopHexPixel(hex_digits,ScaleQuantumToChar(
cristy4c08aed2011-07-01 19:47:50 +00002103 GetPixelRed(image,p)),q);
cristy802d3642011-04-27 02:02:41 +00002104 q=PopHexPixel(hex_digits,ScaleQuantumToChar(
cristy4c08aed2011-07-01 19:47:50 +00002105 GetPixelGreen(image,p)),q);
cristy802d3642011-04-27 02:02:41 +00002106 q=PopHexPixel(hex_digits,ScaleQuantumToChar(
cristy4c08aed2011-07-01 19:47:50 +00002107 GetPixelBlue(image,p)),q);
cristy3ed852e2009-09-05 21:47:34 +00002108 }
2109 if ((q-pixels+6) >= 80)
2110 {
2111 *q++='\n';
2112 (void) WriteBlob(image,q-pixels,pixels);
2113 q=pixels;
2114 }
cristyed231572011-07-14 02:18:59 +00002115 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002116 }
2117 if (image->previous == (Image *) NULL)
2118 {
cristy802d3642011-04-27 02:02:41 +00002119 status=SetImageProgress(image,SaveImageTag,
2120 (MagickOffsetType) y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002121 if (status == MagickFalse)
2122 break;
2123 }
2124 }
2125 if (q != pixels)
2126 {
2127 *q++='\n';
2128 (void) WriteBlob(image,q-pixels,pixels);
2129 }
2130 break;
2131 }
2132 }
2133 (void) WriteBlobByte(image,'\n');
2134 }
2135 else
2136 {
2137 /*
2138 Dump PseudoClass image.
2139 */
cristyb51dff52011-05-19 16:55:47 +00002140 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00002141 "%.20g %.20g\n%d\n%d\n0\n",(double) image->columns,(double)
2142 image->rows,image->storage_class == PseudoClass ? 1 : 0,
cristyaf8d3912014-02-21 14:50:33 +00002143 compression == RLECompression ? 1 : 0);
cristy3ed852e2009-09-05 21:47:34 +00002144 (void) WriteBlobString(image,buffer);
2145 /*
2146 Dump number of colors and colormap.
2147 */
cristyb51dff52011-05-19 16:55:47 +00002148 (void) FormatLocaleString(buffer,MaxTextExtent,"%.20g\n",(double)
cristyf2faecf2010-05-28 19:19:36 +00002149 image->colors);
cristy3ed852e2009-09-05 21:47:34 +00002150 (void) WriteBlobString(image,buffer);
cristybb503372010-05-27 20:51:26 +00002151 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002152 {
cristyb51dff52011-05-19 16:55:47 +00002153 (void) FormatLocaleString(buffer,MaxTextExtent,"%02X%02X%02X\n",
cristyd0323222013-04-07 16:13:21 +00002154 ScaleQuantumToChar(ClampToQuantum(image->colormap[i].red)),
2155 ScaleQuantumToChar(ClampToQuantum(image->colormap[i].green)),
2156 ScaleQuantumToChar(ClampToQuantum(image->colormap[i].blue)));
cristy3ed852e2009-09-05 21:47:34 +00002157 (void) WriteBlobString(image,buffer);
2158 }
cristyaf8d3912014-02-21 14:50:33 +00002159 switch (compression)
cristy3ed852e2009-09-05 21:47:34 +00002160 {
2161 case RLECompression:
2162 {
2163 /*
2164 Dump runlength-encoded PseudoColor packets.
2165 */
2166 q=pixels;
cristybb503372010-05-27 20:51:26 +00002167 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002168 {
cristy1e178e72011-08-28 19:44:34 +00002169 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002170 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002171 break;
cristy4c08aed2011-07-01 19:47:50 +00002172 index=GetPixelIndex(image,p);
cristy3ed852e2009-09-05 21:47:34 +00002173 length=255;
cristybb503372010-05-27 20:51:26 +00002174 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002175 {
cristy4c08aed2011-07-01 19:47:50 +00002176 if ((index == GetPixelIndex(image,p)) &&
cristy802d3642011-04-27 02:02:41 +00002177 (length < 255) && (x < ((ssize_t) image->columns-1)))
cristy3ed852e2009-09-05 21:47:34 +00002178 length++;
2179 else
2180 {
2181 if (x > 0)
2182 {
cristya97426c2011-02-04 01:41:27 +00002183 q=PopHexPixel(hex_digits,(size_t) index,q);
cristybb503372010-05-27 20:51:26 +00002184 q=PopHexPixel(hex_digits,(size_t)
cristy3ed852e2009-09-05 21:47:34 +00002185 MagickMin(length,0xff),q);
2186 i++;
2187 if ((q-pixels+6) >= 80)
2188 {
2189 *q++='\n';
2190 (void) WriteBlob(image,q-pixels,pixels);
2191 q=pixels;
2192 }
2193 }
2194 length=0;
2195 }
cristy4c08aed2011-07-01 19:47:50 +00002196 index=GetPixelIndex(image,p);
cristyd0323222013-04-07 16:13:21 +00002197 pixel.red=(MagickRealType) GetPixelRed(image,p);
2198 pixel.green=(MagickRealType) GetPixelGreen(image,p);
2199 pixel.blue=(MagickRealType) GetPixelBlue(image,p);
2200 pixel.alpha=(MagickRealType) GetPixelAlpha(image,p);
cristyed231572011-07-14 02:18:59 +00002201 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002202 }
cristya97426c2011-02-04 01:41:27 +00002203 q=PopHexPixel(hex_digits,(size_t) index,q);
cristybb503372010-05-27 20:51:26 +00002204 q=PopHexPixel(hex_digits,(size_t)
cristy3ed852e2009-09-05 21:47:34 +00002205 MagickMin(length,0xff),q);
2206 if (image->previous == (Image *) NULL)
2207 {
cristya97426c2011-02-04 01:41:27 +00002208 status=SetImageProgress(image,SaveImageTag,
2209 (MagickOffsetType) y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002210 if (status == MagickFalse)
2211 break;
2212 }
2213 }
2214 if (q != pixels)
2215 {
2216 *q++='\n';
2217 (void) WriteBlob(image,q-pixels,pixels);
2218 }
2219 break;
2220 }
2221 case NoCompression:
2222 default:
2223 {
2224 /*
2225 Dump uncompressed PseudoColor packets.
2226 */
2227 q=pixels;
cristybb503372010-05-27 20:51:26 +00002228 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002229 {
cristy1e178e72011-08-28 19:44:34 +00002230 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002231 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002232 break;
cristybb503372010-05-27 20:51:26 +00002233 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002234 {
cristy4c08aed2011-07-01 19:47:50 +00002235 q=PopHexPixel(hex_digits,(size_t) GetPixelIndex(image,p),q);
cristy3ed852e2009-09-05 21:47:34 +00002236 if ((q-pixels+4) >= 80)
2237 {
2238 *q++='\n';
2239 (void) WriteBlob(image,q-pixels,pixels);
2240 q=pixels;
2241 }
cristyed231572011-07-14 02:18:59 +00002242 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002243 }
2244 if (image->previous == (Image *) NULL)
2245 {
cristya97426c2011-02-04 01:41:27 +00002246 status=SetImageProgress(image,SaveImageTag,
2247 (MagickOffsetType) y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002248 if (status == MagickFalse)
2249 break;
2250 }
2251 }
2252 if (q != pixels)
2253 {
2254 *q++='\n';
2255 (void) WriteBlob(image,q-pixels,pixels);
2256 }
2257 break;
2258 }
2259 }
2260 (void) WriteBlobByte(image,'\n');
2261 }
2262 if (LocaleCompare(image_info->magick,"PS") != 0)
2263 (void) WriteBlobString(image,"end\n");
2264 (void) WriteBlobString(image,"%%PageTrailer\n");
2265 if (GetNextImageInList(image) == (Image *) NULL)
2266 break;
2267 image=SyncNextImageInList(image);
2268 status=SetImageProgress(image,SaveImagesTag,scene++,
2269 GetImageListLength(image));
2270 if (status == MagickFalse)
2271 break;
2272 } while (image_info->adjoin != MagickFalse);
2273 (void) WriteBlobString(image,"%%Trailer\n");
2274 if (page > 2)
2275 {
cristyb51dff52011-05-19 16:55:47 +00002276 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00002277 "%%%%BoundingBox: %.20g %.20g %.20g %.20g\n",ceil(bounds.x1-0.5),
cristye4cc1a12012-09-03 23:04:46 +00002278 ceil(bounds.y1-0.5),floor(bounds.x2-0.5),floor(bounds.y2-0.5));
cristy3ed852e2009-09-05 21:47:34 +00002279 (void) WriteBlobString(image,buffer);
cristyb51dff52011-05-19 16:55:47 +00002280 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye4cc1a12012-09-03 23:04:46 +00002281 "%%%%HiResBoundingBox: %g %g %g %g\n",bounds.x1,bounds.y1,bounds.x2,
2282 bounds.y2);
cristy3ed852e2009-09-05 21:47:34 +00002283 (void) WriteBlobString(image,buffer);
2284 }
2285 (void) WriteBlobString(image,"%%EOF\n");
2286 (void) CloseBlob(image);
2287 return(MagickTrue);
2288}