blob: 90cf6221884acaabc6b569e378d74b3ddddf22ad [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% %
Cristy7ce65e72015-12-12 18:03:16 -050020% Copyright 1999-2016 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
dirk6d23b5c2015-01-05 19:52:49 +0000190 gsapi_revision_t
191 revision;
192
cristy817f4cd2013-02-06 11:45:42 +0000193 int
194 argc,
195 code;
196
197 register ssize_t
198 i;
199
200#if defined(MAGICKCORE_WINDOWS_SUPPORT)
201 ghost_info=NTGhostscriptDLLVectors();
202#else
203 GhostInfo
204 ghost_info_struct;
205
206 ghost_info=(&ghost_info_struct);
dirkefd45a02014-05-03 14:00:09 +0000207 (void) ResetMagickMemory(&ghost_info_struct,0,sizeof(ghost_info_struct));
dirk52784992014-05-03 13:00:18 +0000208 ghost_info_struct.delete_instance=(void (*)(gs_main_instance *))
209 gsapi_delete_instance;
210 ghost_info_struct.exit=(int (*)(gs_main_instance *)) gsapi_exit;
cristy817f4cd2013-02-06 11:45:42 +0000211 ghost_info_struct.new_instance=(int (*)(gs_main_instance **,void *))
212 gsapi_new_instance;
213 ghost_info_struct.init_with_args=(int (*)(gs_main_instance *,int,char **))
214 gsapi_init_with_args;
215 ghost_info_struct.run_string=(int (*)(gs_main_instance *,const char *,int,
216 int *)) gsapi_run_string;
dirk52784992014-05-03 13:00:18 +0000217 ghost_info_struct.set_stdio=(int (*)(gs_main_instance *,int(*)(void *,char *,
218 int),int(*)(void *,const char *,int),int(*)(void *, const char *, int)))
219 gsapi_set_stdio;
dirk6d23b5c2015-01-05 19:52:49 +0000220 ghost_info_struct.revision=(int (*)(gsapi_revision_t *,int)) gsapi_revision;
cristy817f4cd2013-02-06 11:45:42 +0000221#endif
222 if (ghost_info == (GhostInfo *) NULL)
cristy26842022014-10-05 19:18:23 +0000223 ExecuteGhostscriptCommand(command,status);
dirk6d23b5c2015-01-05 19:52:49 +0000224 if ((ghost_info->revision)(&revision,sizeof(revision)) != 0)
225 revision.revision=0;
cristy817f4cd2013-02-06 11:45:42 +0000226 if (verbose != MagickFalse)
227 {
cristy7c443542015-01-05 23:19:48 +0000228 (void) fprintf(stdout,"[ghostscript library %.2f]",(double)
229 revision.revision/100.0);
cristy26842022014-10-05 19:18:23 +0000230 SetArgsStart(command,args_start);
dirk52784992014-05-03 13:00:18 +0000231 (void) fputs(args_start,stdout);
cristy817f4cd2013-02-06 11:45:42 +0000232 }
dirk52784992014-05-03 13:00:18 +0000233 errors=(char *) NULL;
234 status=(ghost_info->new_instance)(&interpreter,(void *) &errors);
cristy817f4cd2013-02-06 11:45:42 +0000235 if (status < 0)
cristy26842022014-10-05 19:18:23 +0000236 ExecuteGhostscriptCommand(command,status);
cristy817f4cd2013-02-06 11:45:42 +0000237 code=0;
238 argv=StringToArgv(command,&argc);
239 if (argv == (char **) NULL)
dirk85acc9f2015-11-23 21:05:33 +0100240 {
241 (ghost_info->delete_instance)(interpreter);
242 return(MagickFalse);
243 }
dirk52784992014-05-03 13:00:18 +0000244 (void) (ghost_info->set_stdio)(interpreter,(int(MagickDLLCall *)(void *,
245 char *,int)) NULL,PostscriptDelegateMessage,PostscriptDelegateMessage);
cristy817f4cd2013-02-06 11:45:42 +0000246 status=(ghost_info->init_with_args)(interpreter,argc-1,argv+1);
247 if (status == 0)
248 status=(ghost_info->run_string)(interpreter,"systemdict /start get exec\n",
249 0,&code);
250 (ghost_info->exit)(interpreter);
251 (ghost_info->delete_instance)(interpreter);
cristy817f4cd2013-02-06 11:45:42 +0000252 for (i=0; i < (ssize_t) argc; i++)
253 argv[i]=DestroyString(argv[i]);
254 argv=(char **) RelinquishMagickMemory(argv);
dirk4dd75672014-10-12 12:52:42 +0000255 if (status != 0)
cristy817f4cd2013-02-06 11:45:42 +0000256 {
cristy26842022014-10-05 19:18:23 +0000257 SetArgsStart(command,args_start);
dirk2267d492014-10-12 18:32:02 +0000258 if (status == -101) /* quit */
cristy151b66d2015-04-15 10:50:31 +0000259 (void) FormatLocaleString(message,MagickPathExtent,
dirk6d23b5c2015-01-05 19:52:49 +0000260 "[ghostscript library %.2f]%s: %s",(double)revision.revision / 100,
261 args_start,errors);
dirk4dd75672014-10-12 12:52:42 +0000262 else
263 {
264 (void) ThrowMagickException(exception,GetMagickModule(),
265 DelegateError,"PostscriptDelegateFailed",
dirk6d23b5c2015-01-05 19:52:49 +0000266 "`[ghostscript library %.2f]%s': %s",
267 (double)revision.revision / 100,args_start,errors);
dirk4dd75672014-10-12 12:52:42 +0000268 if (errors != (char *) NULL)
269 errors=DestroyString(errors);
270 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
271 "Ghostscript returns status %d, exit code %d",status,code);
272 return(MagickFalse);
273 }
cristy817f4cd2013-02-06 11:45:42 +0000274 }
dirk52784992014-05-03 13:00:18 +0000275 if (errors != (char *) NULL)
276 errors=DestroyString(errors);
cristy817f4cd2013-02-06 11:45:42 +0000277 return(MagickTrue);
278#else
cristy60575362014-10-18 13:22:50 +0000279 status=ExternalDelegateCommand(MagickFalse,verbose,command,message,exception);
cristy817f4cd2013-02-06 11:45:42 +0000280 return(status == 0 ? MagickTrue : MagickFalse);
281#endif
282}
283
284/*
285%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
286% %
287% %
288% %
cristy3ed852e2009-09-05 21:47:34 +0000289% I s P S %
290% %
291% %
292% %
293%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
294%
295% IsPS() returns MagickTrue if the image format type, identified by the
296% magick string, is PS.
297%
298% The format of the IsPS method is:
299%
300% MagickBooleanType IsPS(const unsigned char *magick,const size_t length)
301%
302% A description of each parameter follows:
303%
304% o magick: compare image format pattern against these bytes.
305%
306% o length: Specifies the length of the magick string.
307%
308*/
309static MagickBooleanType IsPS(const unsigned char *magick,const size_t length)
310{
311 if (length < 4)
312 return(MagickFalse);
313 if (memcmp(magick,"%!",2) == 0)
314 return(MagickTrue);
315 if (memcmp(magick,"\004%!",3) == 0)
316 return(MagickTrue);
317 return(MagickFalse);
318}
319
320/*
321%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
322% %
323% %
324% %
325% R e a d P S I m a g e %
326% %
327% %
328% %
329%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
330%
331% ReadPSImage() reads a Postscript image file and returns it. It allocates
332% the memory necessary for the new Image structure and returns a pointer
333% to the new image.
334%
335% The format of the ReadPSImage method is:
336%
337% Image *ReadPSImage(const ImageInfo *image_info,ExceptionInfo *exception)
338%
339% A description of each parameter follows:
340%
341% o image_info: the image info.
342%
343% o exception: return any errors or warnings in this structure.
344%
345*/
346
347static MagickBooleanType IsPostscriptRendered(const char *path)
348{
349 MagickBooleanType
350 status;
351
352 struct stat
353 attributes;
354
355 if ((path == (const char *) NULL) || (*path == '\0'))
356 return(MagickFalse);
357 status=GetPathAttributes(path,&attributes);
358 if ((status != MagickFalse) && S_ISREG(attributes.st_mode) &&
359 (attributes.st_size > 0))
360 return(MagickTrue);
361 return(MagickFalse);
362}
363
364static inline int ProfileInteger(Image *image,short int *hex_digits)
365{
366 int
367 c,
368 l,
369 value;
370
cristybb503372010-05-27 20:51:26 +0000371 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000372 i;
373
374 l=0;
375 value=0;
376 for (i=0; i < 2; )
377 {
378 c=ReadBlobByte(image);
379 if ((c == EOF) || ((c == '%') && (l == '%')))
380 {
381 value=(-1);
382 break;
383 }
384 l=c;
385 c&=0xff;
386 if (isxdigit(c) == MagickFalse)
387 continue;
cristybb503372010-05-27 20:51:26 +0000388 value=(int) ((size_t) value << 4)+hex_digits[c];
cristy3ed852e2009-09-05 21:47:34 +0000389 i++;
390 }
391 return(value);
392}
393
394static Image *ReadPSImage(const ImageInfo *image_info,ExceptionInfo *exception)
395{
396#define BoundingBox "BoundingBox:"
397#define BeginDocument "BeginDocument:"
398#define BeginXMPPacket "<?xpacket begin="
399#define EndXMPPacket "<?xpacket end="
400#define ICCProfile "BeginICCProfile:"
401#define CMYKCustomColor "CMYKCustomColor:"
cristy01ca8922010-03-17 12:20:37 +0000402#define CMYKProcessColor "CMYKProcessColor:"
cristy3ed852e2009-09-05 21:47:34 +0000403#define DocumentMedia "DocumentMedia:"
404#define DocumentCustomColors "DocumentCustomColors:"
405#define DocumentProcessColors "DocumentProcessColors:"
406#define EndDocument "EndDocument:"
407#define HiResBoundingBox "HiResBoundingBox:"
408#define ImageData "ImageData:"
409#define PageBoundingBox "PageBoundingBox:"
410#define LanguageLevel "LanguageLevel:"
411#define PageMedia "PageMedia:"
412#define Pages "Pages:"
413#define PhotoshopProfile "BeginPhotoshop:"
414#define PostscriptLevel "!PS-"
415#define RenderPostscriptText " Rendering Postscript... "
416#define SpotColor "+ "
417
418 char
cristy151b66d2015-04-15 10:50:31 +0000419 command[MagickPathExtent],
cristy51816562015-04-12 13:18:38 +0000420 *density,
cristy151b66d2015-04-15 10:50:31 +0000421 filename[MagickPathExtent],
422 geometry[MagickPathExtent],
423 input_filename[MagickPathExtent],
424 message[MagickPathExtent],
cristy51816562015-04-12 13:18:38 +0000425 *options,
cristy151b66d2015-04-15 10:50:31 +0000426 postscript_filename[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +0000427
cristy6dda0d32012-11-11 17:59:01 +0000428 const char
429 *option;
430
cristy3ed852e2009-09-05 21:47:34 +0000431 const DelegateInfo
432 *delegate_info;
433
434 GeometryInfo
435 geometry_info;
436
437 Image
438 *image,
439 *next,
440 *postscript_image;
441
442 ImageInfo
443 *read_info;
444
445 int
446 c,
447 file;
448
449 MagickBooleanType
450 cmyk,
dirka5eeacb2014-10-25 06:32:14 +0000451 fitPage,
cristy3ed852e2009-09-05 21:47:34 +0000452 skip,
453 status;
454
455 MagickStatusType
456 flags;
457
458 PointInfo
cristy6502b392012-04-11 00:05:33 +0000459 delta,
460 resolution;
cristy3ed852e2009-09-05 21:47:34 +0000461
462 RectangleInfo
463 page;
464
465 register char
466 *p;
467
cristybb503372010-05-27 20:51:26 +0000468 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000469 i;
470
471 SegmentInfo
472 bounds,
473 hires_bounds;
474
475 short int
476 hex_digits[256];
477
478 size_t
cristyd0323222013-04-07 16:13:21 +0000479 length;
cristy3ed852e2009-09-05 21:47:34 +0000480
481 ssize_t
cristyd0323222013-04-07 16:13:21 +0000482 count,
483 priority;
cristy3ed852e2009-09-05 21:47:34 +0000484
485 StringInfo
486 *profile;
487
cristyf2faecf2010-05-28 19:19:36 +0000488 unsigned long
cristy3ed852e2009-09-05 21:47:34 +0000489 columns,
490 extent,
491 language_level,
492 pages,
493 rows,
494 scene,
495 spotcolor;
496
497 /*
498 Open image file.
499 */
500 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000501 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000502 if (image_info->debug != MagickFalse)
503 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
504 image_info->filename);
505 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000506 assert(exception->signature == MagickCoreSignature);
cristy9950d572011-10-01 18:22:35 +0000507 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +0000508 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
509 if (status == MagickFalse)
510 {
511 image=DestroyImageList(image);
512 return((Image *) NULL);
513 }
514 status=AcquireUniqueSymbolicLink(image_info->filename,input_filename);
515 if (status == MagickFalse)
516 {
517 ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
518 image_info->filename);
519 image=DestroyImageList(image);
520 return((Image *) NULL);
521 }
522 /*
523 Initialize hex values.
524 */
525 (void) ResetMagickMemory(hex_digits,0,sizeof(hex_digits));
526 hex_digits[(int) '0']=0;
527 hex_digits[(int) '1']=1;
528 hex_digits[(int) '2']=2;
529 hex_digits[(int) '3']=3;
530 hex_digits[(int) '4']=4;
531 hex_digits[(int) '5']=5;
532 hex_digits[(int) '6']=6;
533 hex_digits[(int) '7']=7;
534 hex_digits[(int) '8']=8;
535 hex_digits[(int) '9']=9;
536 hex_digits[(int) 'a']=10;
537 hex_digits[(int) 'b']=11;
538 hex_digits[(int) 'c']=12;
539 hex_digits[(int) 'd']=13;
540 hex_digits[(int) 'e']=14;
541 hex_digits[(int) 'f']=15;
542 hex_digits[(int) 'A']=10;
543 hex_digits[(int) 'B']=11;
544 hex_digits[(int) 'C']=12;
545 hex_digits[(int) 'D']=13;
546 hex_digits[(int) 'E']=14;
547 hex_digits[(int) 'F']=15;
548 /*
549 Set the page density.
550 */
551 delta.x=DefaultResolution;
552 delta.y=DefaultResolution;
cristy2a11bef2011-10-28 18:33:11 +0000553 if ((image->resolution.x == 0.0) || (image->resolution.y == 0.0))
cristy3ed852e2009-09-05 21:47:34 +0000554 {
555 flags=ParseGeometry(PSDensityGeometry,&geometry_info);
cristy2a11bef2011-10-28 18:33:11 +0000556 image->resolution.x=geometry_info.rho;
557 image->resolution.y=geometry_info.sigma;
cristy3ed852e2009-09-05 21:47:34 +0000558 if ((flags & SigmaValue) == 0)
cristy2a11bef2011-10-28 18:33:11 +0000559 image->resolution.y=image->resolution.x;
cristy3ed852e2009-09-05 21:47:34 +0000560 }
cristy6cde06a2011-11-24 00:08:43 +0000561 if (image_info->density != (char *) NULL)
562 {
563 flags=ParseGeometry(image_info->density,&geometry_info);
564 image->resolution.x=geometry_info.rho;
565 image->resolution.y=geometry_info.sigma;
566 if ((flags & SigmaValue) == 0)
567 image->resolution.y=image->resolution.x;
568 }
569 (void) ParseAbsoluteGeometry(PSPageGeometry,&page);
570 if (image_info->page != (char *) NULL)
571 (void) ParseAbsoluteGeometry(image_info->page,&page);
cristy6502b392012-04-11 00:05:33 +0000572 resolution=image->resolution;
573 page.width=(size_t) ceil((double) (page.width*resolution.x/delta.x)-0.5);
574 page.height=(size_t) ceil((double) (page.height*resolution.y/delta.y)-0.5);
cristy3ed852e2009-09-05 21:47:34 +0000575 /*
576 Determine page geometry from the Postscript bounding box.
577 */
578 (void) ResetMagickMemory(&bounds,0,sizeof(bounds));
cristy3ed852e2009-09-05 21:47:34 +0000579 (void) ResetMagickMemory(command,0,sizeof(command));
cristy6cde06a2011-11-24 00:08:43 +0000580 cmyk=image_info->colorspace == CMYKColorspace ? MagickTrue : MagickFalse;
581 (void) ResetMagickMemory(&hires_bounds,0,sizeof(hires_bounds));
cristy4d246fc2014-01-15 22:33:44 +0000582 columns=0;
583 rows=0;
cristy4b1c78f2011-11-11 15:09:30 +0000584 priority=0;
cristy3ed852e2009-09-05 21:47:34 +0000585 rows=0;
586 extent=0;
587 spotcolor=0;
588 language_level=1;
cristy3ed852e2009-09-05 21:47:34 +0000589 pages=(~0UL);
cristy6cde06a2011-11-24 00:08:43 +0000590 skip=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +0000591 p=command;
592 for (c=ReadBlobByte(image); c != EOF; c=ReadBlobByte(image))
593 {
594 /*
595 Note document structuring comments.
596 */
597 *p++=(char) c;
598 if ((strchr("\n\r%",c) == (char *) NULL) &&
cristy151b66d2015-04-15 10:50:31 +0000599 ((size_t) (p-command) < (MagickPathExtent-1)))
cristy3ed852e2009-09-05 21:47:34 +0000600 continue;
601 *p='\0';
602 p=command;
603 /*
604 Skip %%BeginDocument thru %%EndDocument.
605 */
606 if (LocaleNCompare(BeginDocument,command,strlen(BeginDocument)) == 0)
607 skip=MagickTrue;
608 if (LocaleNCompare(EndDocument,command,strlen(EndDocument)) == 0)
609 skip=MagickFalse;
610 if (skip != MagickFalse)
611 continue;
612 if (LocaleNCompare(PostscriptLevel,command,strlen(PostscriptLevel)) == 0)
613 {
cristyd15e6592011-10-15 00:13:06 +0000614 (void) SetImageProperty(image,"ps:Level",command+4,exception);
cristy3ed852e2009-09-05 21:47:34 +0000615 if (GlobExpression(command,"*EPSF-*",MagickTrue) != MagickFalse)
616 pages=1;
617 }
618 if (LocaleNCompare(LanguageLevel,command,strlen(LanguageLevel)) == 0)
619 (void) sscanf(command,LanguageLevel " %lu",&language_level);
620 if (LocaleNCompare(Pages,command,strlen(Pages)) == 0)
621 (void) sscanf(command,Pages " %lu",&pages);
cristy97841ba2010-09-02 15:48:08 +0000622 if (LocaleNCompare(ImageData,command,strlen(ImageData)) == 0)
cristy3ed852e2009-09-05 21:47:34 +0000623 (void) sscanf(command,ImageData " %lu %lu",&columns,&rows);
cristy97841ba2010-09-02 15:48:08 +0000624 if (LocaleNCompare(ICCProfile,command,strlen(ICCProfile)) == 0)
cristy3ed852e2009-09-05 21:47:34 +0000625 {
626 unsigned char
627 *datum;
628
629 /*
630 Read ICC profile.
631 */
632 profile=AcquireStringInfo(65536);
633 for (i=0; (c=ProfileInteger(image,hex_digits)) != EOF; i++)
634 {
635 SetStringInfoLength(profile,(size_t) i+1);
636 datum=GetStringInfoDatum(profile);
637 datum[i]=(unsigned char) c;
638 }
cristyd15e6592011-10-15 00:13:06 +0000639 (void) SetImageProfile(image,"icc",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +0000640 profile=DestroyStringInfo(profile);
641 continue;
642 }
643 if (LocaleNCompare(PhotoshopProfile,command,strlen(PhotoshopProfile)) == 0)
644 {
645 unsigned char
dirkdb6b52c2015-11-25 23:44:01 +0100646 *q;
cristy3ed852e2009-09-05 21:47:34 +0000647
648 /*
649 Read Photoshop profile.
650 */
651 count=(ssize_t) sscanf(command,PhotoshopProfile " %lu",&extent);
652 if (count != 1)
653 continue;
654 length=extent;
cristy8723e4b2011-09-01 13:11:19 +0000655 profile=BlobToStringInfo((const void *) NULL,length);
cristyb231d582014-11-28 17:13:51 +0000656 if (profile != (StringInfo *) NULL)
657 {
dirkdb6b52c2015-11-25 23:44:01 +0100658 q=GetStringInfoDatum(profile);
cristyb231d582014-11-28 17:13:51 +0000659 for (i=0; i < (ssize_t) length; i++)
dirkdb6b52c2015-11-25 23:44:01 +0100660 *q++=(unsigned char) ProfileInteger(image,hex_digits);
cristyb231d582014-11-28 17:13:51 +0000661 (void) SetImageProfile(image,"8bim",profile,exception);
662 profile=DestroyStringInfo(profile);
663 }
cristy3ed852e2009-09-05 21:47:34 +0000664 continue;
665 }
666 if (LocaleNCompare(BeginXMPPacket,command,strlen(BeginXMPPacket)) == 0)
667 {
cristy3ed852e2009-09-05 21:47:34 +0000668 /*
669 Read XMP profile.
670 */
671 p=command;
672 profile=StringToStringInfo(command);
673 for (i=GetStringInfoLength(profile)-1; c != EOF; i++)
674 {
675 SetStringInfoLength(profile,i+1);
676 c=ReadBlobByte(image);
677 GetStringInfoDatum(profile)[i]=(unsigned char) c;
678 *p++=(char) c;
679 if ((strchr("\n\r%",c) == (char *) NULL) &&
cristy151b66d2015-04-15 10:50:31 +0000680 ((size_t) (p-command) < (MagickPathExtent-1)))
cristy3ed852e2009-09-05 21:47:34 +0000681 continue;
682 *p='\0';
683 p=command;
684 if (LocaleNCompare(EndXMPPacket,command,strlen(EndXMPPacket)) == 0)
685 break;
686 }
687 SetStringInfoLength(profile,i);
cristyd15e6592011-10-15 00:13:06 +0000688 (void) SetImageProfile(image,"xmp",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +0000689 profile=DestroyStringInfo(profile);
690 continue;
691 }
692 /*
693 Is this a CMYK document?
694 */
695 length=strlen(DocumentProcessColors);
696 if (LocaleNCompare(DocumentProcessColors,command,length) == 0)
697 {
698 if ((GlobExpression(command,"*Cyan*",MagickTrue) != MagickFalse) ||
699 (GlobExpression(command,"*Magenta*",MagickTrue) != MagickFalse) ||
700 (GlobExpression(command,"*Yellow*",MagickTrue) != MagickFalse))
701 cmyk=MagickTrue;
702 }
703 if (LocaleNCompare(CMYKCustomColor,command,strlen(CMYKCustomColor)) == 0)
704 cmyk=MagickTrue;
cristy01ca8922010-03-17 12:20:37 +0000705 if (LocaleNCompare(CMYKProcessColor,command,strlen(CMYKProcessColor)) == 0)
706 cmyk=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +0000707 length=strlen(DocumentCustomColors);
708 if ((LocaleNCompare(DocumentCustomColors,command,length) == 0) ||
709 (LocaleNCompare(CMYKCustomColor,command,strlen(CMYKCustomColor)) == 0) ||
710 (LocaleNCompare(SpotColor,command,strlen(SpotColor)) == 0))
711 {
712 char
cristy151b66d2015-04-15 10:50:31 +0000713 property[MagickPathExtent],
cristy3ed852e2009-09-05 21:47:34 +0000714 *value;
715
716 register char
dirkdb6b52c2015-11-25 23:44:01 +0100717 *q;
cristy3ed852e2009-09-05 21:47:34 +0000718
719 /*
720 Note spot names.
721 */
cristy151b66d2015-04-15 10:50:31 +0000722 (void) FormatLocaleString(property,MagickPathExtent,"ps:SpotColor-%.20g",
cristye8c25f92010-06-03 00:53:06 +0000723 (double) (spotcolor++));
dirkdb6b52c2015-11-25 23:44:01 +0100724 for (q=command; *q != '\0'; q++)
725 if (isspace((int) (unsigned char) *q) != 0)
cristy3ed852e2009-09-05 21:47:34 +0000726 break;
dirkdb6b52c2015-11-25 23:44:01 +0100727 value=AcquireString(q);
cristy3ed852e2009-09-05 21:47:34 +0000728 (void) SubstituteString(&value,"(","");
729 (void) SubstituteString(&value,")","");
730 (void) StripString(value);
cristyd15e6592011-10-15 00:13:06 +0000731 (void) SetImageProperty(image,property,value,exception);
cristy3ed852e2009-09-05 21:47:34 +0000732 value=DestroyString(value);
733 continue;
734 }
cristy6cde06a2011-11-24 00:08:43 +0000735 if (image_info->page != (char *) NULL)
736 continue;
cristy3ed852e2009-09-05 21:47:34 +0000737 /*
738 Note region defined by bounding box.
739 */
740 count=0;
cristy4b1c78f2011-11-11 15:09:30 +0000741 i=0;
cristy3ed852e2009-09-05 21:47:34 +0000742 if (LocaleNCompare(BoundingBox,command,strlen(BoundingBox)) == 0)
cristy3ed852e2009-09-05 21:47:34 +0000743 {
cristy4b1c78f2011-11-11 15:09:30 +0000744 count=(ssize_t) sscanf(command,BoundingBox " %lf %lf %lf %lf",
745 &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
746 i=2;
cristy3ed852e2009-09-05 21:47:34 +0000747 }
cristy4b1c78f2011-11-11 15:09:30 +0000748 if (LocaleNCompare(DocumentMedia,command,strlen(DocumentMedia)) == 0)
749 {
750 count=(ssize_t) sscanf(command,DocumentMedia " %lf %lf %lf %lf",
751 &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
752 i=1;
753 }
754 if (LocaleNCompare(HiResBoundingBox,command,strlen(HiResBoundingBox)) == 0)
755 {
756 count=(ssize_t) sscanf(command,HiResBoundingBox " %lf %lf %lf %lf",
757 &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
758 i=3;
759 }
760 if (LocaleNCompare(PageBoundingBox,command,strlen(PageBoundingBox)) == 0)
761 {
762 count=(ssize_t) sscanf(command,PageBoundingBox " %lf %lf %lf %lf",
763 &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
764 i=1;
765 }
766 if (LocaleNCompare(PageMedia,command,strlen(PageMedia)) == 0)
767 {
768 count=(ssize_t) sscanf(command,PageMedia " %lf %lf %lf %lf",
769 &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
770 i=1;
771 }
cristy3fac9ec2011-11-17 18:04:39 +0000772 if ((count != 4) || (i < (ssize_t) priority))
cristy4b1c78f2011-11-11 15:09:30 +0000773 continue;
cristyad29a8c2012-01-25 23:06:43 +0000774 if ((fabs(bounds.x2-bounds.x1) <= fabs(hires_bounds.x2-hires_bounds.x1)) ||
775 (fabs(bounds.y2-bounds.y1) <= fabs(hires_bounds.y2-hires_bounds.y1)))
cristye3c89012014-03-06 14:11:27 +0000776 if (i == (ssize_t) priority)
777 continue;
cristy4b1c78f2011-11-11 15:09:30 +0000778 hires_bounds=bounds;
779 priority=i;
cristy3ed852e2009-09-05 21:47:34 +0000780 }
cristy7b0fcf12011-11-24 00:24:07 +0000781 if ((fabs(hires_bounds.x2-hires_bounds.x1) >= MagickEpsilon) &&
cristy614bc082011-11-24 00:49:08 +0000782 (fabs(hires_bounds.y2-hires_bounds.y1) >= MagickEpsilon))
cristy4b1c78f2011-11-11 15:09:30 +0000783 {
784 /*
785 Set Postscript render geometry.
786 */
cristy151b66d2015-04-15 10:50:31 +0000787 (void) FormatLocaleString(geometry,MagickPathExtent,"%gx%g%+.15g%+.15g",
cristy4b1c78f2011-11-11 15:09:30 +0000788 hires_bounds.x2-hires_bounds.x1,hires_bounds.y2-hires_bounds.y1,
789 hires_bounds.x1,hires_bounds.y1);
790 (void) SetImageProperty(image,"ps:HiResBoundingBox",geometry,exception);
cristy6cde06a2011-11-24 00:08:43 +0000791 page.width=(size_t) ceil((double) ((hires_bounds.x2-hires_bounds.x1)*
cristy6502b392012-04-11 00:05:33 +0000792 resolution.x/delta.x)-0.5);
cristy6cde06a2011-11-24 00:08:43 +0000793 page.height=(size_t) ceil((double) ((hires_bounds.y2-hires_bounds.y1)*
cristy6502b392012-04-11 00:05:33 +0000794 resolution.y/delta.y)-0.5);
cristy4b1c78f2011-11-11 15:09:30 +0000795 }
dirka5eeacb2014-10-25 06:32:14 +0000796 fitPage=MagickFalse;
797 option=GetImageOption(image_info,"eps:fit-page");
798 if (option != (char *) NULL)
799 {
800 char
dirkdb6b52c2015-11-25 23:44:01 +0100801 *page_geometry;
dirka5eeacb2014-10-25 06:32:14 +0000802
dirkdb6b52c2015-11-25 23:44:01 +0100803 page_geometry=GetPageGeometry(option);
804 flags=ParseMetaGeometry(page_geometry,&page.x,&page.y,&page.width,
805 &page.height);
dirka5eeacb2014-10-25 06:32:14 +0000806 if (flags == NoValue)
807 {
808 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
809 "InvalidGeometry","`%s'",option);
810 image=DestroyImage(image);
811 return((Image *) NULL);
812 }
813 page.width=(size_t) ceil((double) (page.width*image->resolution.x/delta.x)
814 -0.5);
815 page.height=(size_t) ceil((double) (page.height*image->resolution.y/
816 delta.y) -0.5);
dirkdb6b52c2015-11-25 23:44:01 +0100817 page_geometry=DestroyString(page_geometry);
dirka5eeacb2014-10-25 06:32:14 +0000818 fitPage=MagickTrue;
819 }
cristy3ed852e2009-09-05 21:47:34 +0000820 (void) CloseBlob(image);
cristy3d9f5ba2012-06-26 13:37:31 +0000821 if (IssRGBCompatibleColorspace(image_info->colorspace) != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000822 cmyk=MagickFalse;
823 /*
824 Create Ghostscript control file.
825 */
826 file=AcquireUniqueFileResource(postscript_filename);
827 if (file == -1)
828 {
cristyc82a27b2011-10-21 01:07:16 +0000829 ThrowFileException(exception,FileOpenError,"UnableToOpenFile",
cristy3ed852e2009-09-05 21:47:34 +0000830 image_info->filename);
831 image=DestroyImageList(image);
832 return((Image *) NULL);
833 }
834 (void) CopyMagickString(command,"/setpagedevice {pop} bind 1 index where {"
835 "dup wcheck {3 1 roll put} {pop def} ifelse} {def} ifelse\n"
cristy151b66d2015-04-15 10:50:31 +0000836 "<</UseCIEColor true>>setpagedevice\n",MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +0000837 count=write(file,command,(unsigned int) strlen(command));
cristyc39e3d62010-10-14 16:52:01 +0000838 if (image_info->page == (char *) NULL)
839 {
840 char
cristy151b66d2015-04-15 10:50:31 +0000841 translate_geometry[MagickPathExtent];
cristyc39e3d62010-10-14 16:52:01 +0000842
cristy151b66d2015-04-15 10:50:31 +0000843 (void) FormatLocaleString(translate_geometry,MagickPathExtent,
cristyc39e3d62010-10-14 16:52:01 +0000844 "%g %g translate\n",-bounds.x1,-bounds.y1);
845 count=write(file,translate_geometry,(unsigned int)
846 strlen(translate_geometry));
847 }
cristy3ed852e2009-09-05 21:47:34 +0000848 file=close(file)-1;
849 /*
850 Render Postscript with the Ghostscript delegate.
851 */
852 if (image_info->monochrome != MagickFalse)
853 delegate_info=GetDelegateInfo("ps:mono",(char *) NULL,exception);
854 else
855 if (cmyk != MagickFalse)
856 delegate_info=GetDelegateInfo("ps:cmyk",(char *) NULL,exception);
857 else
cristya97426c2011-02-04 01:41:27 +0000858 delegate_info=GetDelegateInfo("ps:alpha",(char *) NULL,exception);
cristy3ed852e2009-09-05 21:47:34 +0000859 if (delegate_info == (const DelegateInfo *) NULL)
860 {
861 (void) RelinquishUniqueFileResource(postscript_filename);
862 image=DestroyImageList(image);
863 return((Image *) NULL);
864 }
cristy51816562015-04-12 13:18:38 +0000865 density=AcquireString("");
866 options=AcquireString("");
cristy151b66d2015-04-15 10:50:31 +0000867 (void) FormatLocaleString(density,MagickPathExtent,"%gx%g",resolution.x,
cristy6502b392012-04-11 00:05:33 +0000868 resolution.y);
cristy151b66d2015-04-15 10:50:31 +0000869 (void) FormatLocaleString(options,MagickPathExtent,"-g%.20gx%.20g ",(double)
cristyc8488582012-02-28 12:03:48 +0000870 page.width,(double) page.height);
cristy3ed852e2009-09-05 21:47:34 +0000871 read_info=CloneImageInfo(image_info);
872 *read_info->magick='\0';
873 if (read_info->number_scenes != 0)
874 {
875 char
cristy151b66d2015-04-15 10:50:31 +0000876 pages[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +0000877
cristy151b66d2015-04-15 10:50:31 +0000878 (void) FormatLocaleString(pages,MagickPathExtent,"-dFirstPage=%.20g "
cristyb167a142013-03-09 22:54:29 +0000879 "-dLastPage=%.20g ",(double) read_info->scene+1,(double)
cristyf2faecf2010-05-28 19:19:36 +0000880 (read_info->scene+read_info->number_scenes));
cristy151b66d2015-04-15 10:50:31 +0000881 (void) ConcatenateMagickString(options,pages,MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +0000882 read_info->number_scenes=0;
883 if (read_info->scenes != (char *) NULL)
884 *read_info->scenes='\0';
885 }
dirka5eeacb2014-10-25 06:32:14 +0000886 if (*image_info->magick == 'E')
887 {
888 option=GetImageOption(image_info,"eps:use-cropbox");
889 if ((option == (const char *) NULL) ||
890 (IsStringTrue(option) != MagickFalse))
cristy151b66d2015-04-15 10:50:31 +0000891 (void) ConcatenateMagickString(options,"-dEPSCrop ",MagickPathExtent);
dirka5eeacb2014-10-25 06:32:14 +0000892 if (fitPage != MagickFalse)
cristy151b66d2015-04-15 10:50:31 +0000893 (void) ConcatenateMagickString(options,"-dEPSFitPage ",MagickPathExtent);
dirka5eeacb2014-10-25 06:32:14 +0000894 }
cristy151b66d2015-04-15 10:50:31 +0000895 (void) CopyMagickString(filename,read_info->filename,MagickPathExtent);
cristya97426c2011-02-04 01:41:27 +0000896 (void) AcquireUniqueFilename(filename);
dirkfd826fc2014-06-04 21:11:30 +0000897 (void) RelinquishUniqueFileResource(filename);
cristy151b66d2015-04-15 10:50:31 +0000898 (void) ConcatenateMagickString(filename,"%d",MagickPathExtent);
899 (void) FormatLocaleString(command,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +0000900 GetDelegateCommands(delegate_info),
901 read_info->antialias != MagickFalse ? 4 : 1,
cristya97426c2011-02-04 01:41:27 +0000902 read_info->antialias != MagickFalse ? 4 : 1,density,options,filename,
903 postscript_filename,input_filename);
cristy51816562015-04-12 13:18:38 +0000904 options=DestroyString(options);
905 density=DestroyString(density);
cristy60575362014-10-18 13:22:50 +0000906 *message='\0';
907 status=InvokePostscriptDelegate(read_info->verbose,command,message,exception);
cristya97426c2011-02-04 01:41:27 +0000908 (void) InterpretImageFilename(image_info,image,filename,1,
cristy6fccee12011-10-20 18:43:18 +0000909 read_info->filename,exception);
cristy41083a42009-09-07 23:47:59 +0000910 if ((status == MagickFalse) ||
cristy3ed852e2009-09-05 21:47:34 +0000911 (IsPostscriptRendered(read_info->filename) == MagickFalse))
912 {
cristy151b66d2015-04-15 10:50:31 +0000913 (void) ConcatenateMagickString(command," -c showpage",MagickPathExtent);
cristy60575362014-10-18 13:22:50 +0000914 status=InvokePostscriptDelegate(read_info->verbose,command,message,
dirk4dd75672014-10-12 12:52:42 +0000915 exception);
cristy3ed852e2009-09-05 21:47:34 +0000916 }
cristy3ed852e2009-09-05 21:47:34 +0000917 (void) RelinquishUniqueFileResource(postscript_filename);
cristy3ed852e2009-09-05 21:47:34 +0000918 (void) RelinquishUniqueFileResource(input_filename);
cristya97426c2011-02-04 01:41:27 +0000919 postscript_image=(Image *) NULL;
920 if (status == MagickFalse)
921 for (i=1; ; i++)
922 {
cristya97426c2011-02-04 01:41:27 +0000923 (void) InterpretImageFilename(image_info,image,filename,(int) i,
cristy6fccee12011-10-20 18:43:18 +0000924 read_info->filename,exception);
cristya97426c2011-02-04 01:41:27 +0000925 if (IsPostscriptRendered(read_info->filename) == MagickFalse)
926 break;
927 (void) RelinquishUniqueFileResource(read_info->filename);
928 }
929 else
930 for (i=1; ; i++)
931 {
cristya97426c2011-02-04 01:41:27 +0000932 (void) InterpretImageFilename(image_info,image,filename,(int) i,
cristy6fccee12011-10-20 18:43:18 +0000933 read_info->filename,exception);
cristya97426c2011-02-04 01:41:27 +0000934 if (IsPostscriptRendered(read_info->filename) == MagickFalse)
935 break;
cristyb0ffea22012-06-17 20:22:01 +0000936 read_info->blob=NULL;
937 read_info->length=0;
cristya97426c2011-02-04 01:41:27 +0000938 next=ReadImage(read_info,exception);
939 (void) RelinquishUniqueFileResource(read_info->filename);
940 if (next == (Image *) NULL)
941 break;
942 AppendImageToList(&postscript_image,next);
943 }
944 (void) RelinquishUniqueFileResource(read_info->filename);
cristy3ed852e2009-09-05 21:47:34 +0000945 read_info=DestroyImageInfo(read_info);
946 if (postscript_image == (Image *) NULL)
dirk947430c2014-10-12 18:53:34 +0000947 {
cristy60575362014-10-18 13:22:50 +0000948 if (*message != '\0')
dirk947430c2014-10-12 18:53:34 +0000949 (void) ThrowMagickException(exception,GetMagickModule(),
cristy60575362014-10-18 13:22:50 +0000950 DelegateError,"PostscriptDelegateFailed","`%s'",message);
cristy3ed852e2009-09-05 21:47:34 +0000951 image=DestroyImageList(image);
cristy3ed852e2009-09-05 21:47:34 +0000952 return((Image *) NULL);
953 }
954 if (LocaleCompare(postscript_image->magick,"BMP") == 0)
955 {
956 Image
957 *cmyk_image;
958
959 cmyk_image=ConsolidateCMYKImages(postscript_image,exception);
960 if (cmyk_image != (Image *) NULL)
961 {
962 postscript_image=DestroyImageList(postscript_image);
963 postscript_image=cmyk_image;
964 }
965 }
966 if (image_info->number_scenes != 0)
967 {
968 Image
969 *clone_image;
970
cristy3ed852e2009-09-05 21:47:34 +0000971 /*
972 Add place holder images to meet the subimage specification requirement.
973 */
cristybb503372010-05-27 20:51:26 +0000974 for (i=0; i < (ssize_t) image_info->scene; i++)
cristy3ed852e2009-09-05 21:47:34 +0000975 {
976 clone_image=CloneImage(postscript_image,1,1,MagickTrue,exception);
977 if (clone_image != (Image *) NULL)
978 PrependImageToList(&postscript_image,clone_image);
979 }
980 }
981 do
982 {
cristy151b66d2015-04-15 10:50:31 +0000983 (void) CopyMagickString(postscript_image->filename,filename,MagickPathExtent);
cristy835b2462014-10-27 23:44:33 +0000984 (void) CopyMagickString(postscript_image->magick,image->magick,
cristy151b66d2015-04-15 10:50:31 +0000985 MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +0000986 if (columns != 0)
987 postscript_image->magick_columns=columns;
988 if (rows != 0)
989 postscript_image->magick_rows=rows;
990 postscript_image->page=page;
991 (void) CloneImageProfiles(postscript_image,image);
992 (void) CloneImageProperties(postscript_image,image);
993 next=SyncNextImageInList(postscript_image);
994 if (next != (Image *) NULL)
995 postscript_image=next;
996 } while (next != (Image *) NULL);
997 image=DestroyImageList(image);
998 scene=0;
999 for (next=GetFirstImageInList(postscript_image); next != (Image *) NULL; )
1000 {
1001 next->scene=scene++;
1002 next=GetNextImageInList(next);
1003 }
1004 return(GetFirstImageInList(postscript_image));
1005}
1006
1007/*
1008%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1009% %
1010% %
1011% %
1012% R e g i s t e r P S I m a g e %
1013% %
1014% %
1015% %
1016%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1017%
1018% RegisterPSImage() adds properties for the PS image format to
1019% the list of supported formats. The properties include the image format
1020% tag, a method to read and/or write the format, whether the format
1021% supports the saving of more than one frame to the same file or blob,
1022% whether the format supports native in-memory I/O, and a brief
1023% description of the format.
1024%
1025% The format of the RegisterPSImage method is:
1026%
cristybb503372010-05-27 20:51:26 +00001027% size_t RegisterPSImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001028%
1029*/
cristybb503372010-05-27 20:51:26 +00001030ModuleExport size_t RegisterPSImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001031{
1032 MagickInfo
1033 *entry;
1034
dirk06b627a2015-04-06 18:59:17 +00001035 entry=AcquireMagickInfo("PS","EPI",
cristy5aefbeb2013-08-09 12:13:32 +00001036 "Encapsulated PostScript Interchange format");
cristy3ed852e2009-09-05 21:47:34 +00001037 entry->decoder=(DecodeImageHandler *) ReadPSImage;
1038 entry->encoder=(EncodeImageHandler *) WritePSImage;
1039 entry->magick=(IsImageFormatHandler *) IsPS;
dirk08e9a112015-02-22 01:51:41 +00001040 entry->flags^=CoderAdjoinFlag;
1041 entry->flags^=CoderBlobSupportFlag;
1042 entry->flags|=CoderSeekableStreamFlag;
cristy5aefbeb2013-08-09 12:13:32 +00001043 entry->mime_type=ConstantString("application/postscript");
cristy3ed852e2009-09-05 21:47:34 +00001044 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001045 entry=AcquireMagickInfo("PS","EPS","Encapsulated PostScript");
cristy3ed852e2009-09-05 21:47:34 +00001046 entry->decoder=(DecodeImageHandler *) ReadPSImage;
1047 entry->encoder=(EncodeImageHandler *) WritePSImage;
1048 entry->magick=(IsImageFormatHandler *) IsPS;
dirk08e9a112015-02-22 01:51:41 +00001049 entry->flags^=CoderAdjoinFlag;
1050 entry->flags^=CoderBlobSupportFlag;
1051 entry->flags|=CoderSeekableStreamFlag;
cristy5aefbeb2013-08-09 12:13:32 +00001052 entry->mime_type=ConstantString("application/postscript");
cristy3ed852e2009-09-05 21:47:34 +00001053 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001054 entry=AcquireMagickInfo("PS","EPSF","Encapsulated PostScript");
cristy3ed852e2009-09-05 21:47:34 +00001055 entry->decoder=(DecodeImageHandler *) ReadPSImage;
1056 entry->encoder=(EncodeImageHandler *) WritePSImage;
1057 entry->magick=(IsImageFormatHandler *) IsPS;
dirk08e9a112015-02-22 01:51:41 +00001058 entry->flags^=CoderAdjoinFlag;
1059 entry->flags^=CoderBlobSupportFlag;
1060 entry->flags|=CoderSeekableStreamFlag;
dirk06b627a2015-04-06 18:59:17 +00001061 entry->mime_type=ConstantString("application/postscript");
1062 (void) RegisterMagickInfo(entry);
1063 entry=AcquireMagickInfo("PS","EPSI",
cristy3ed852e2009-09-05 21:47:34 +00001064 "Encapsulated PostScript Interchange format");
dirk06b627a2015-04-06 18:59:17 +00001065 entry->decoder=(DecodeImageHandler *) ReadPSImage;
1066 entry->encoder=(EncodeImageHandler *) WritePSImage;
1067 entry->magick=(IsImageFormatHandler *) IsPS;
1068 entry->flags^=CoderAdjoinFlag;
1069 entry->flags^=CoderBlobSupportFlag;
1070 entry->flags|=CoderSeekableStreamFlag;
cristy5aefbeb2013-08-09 12:13:32 +00001071 entry->mime_type=ConstantString("application/postscript");
cristy3ed852e2009-09-05 21:47:34 +00001072 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001073 entry=AcquireMagickInfo("PS","PS","PostScript");
cristy3ed852e2009-09-05 21:47:34 +00001074 entry->decoder=(DecodeImageHandler *) ReadPSImage;
1075 entry->encoder=(EncodeImageHandler *) WritePSImage;
1076 entry->magick=(IsImageFormatHandler *) IsPS;
cristy5aefbeb2013-08-09 12:13:32 +00001077 entry->mime_type=ConstantString("application/postscript");
dirk08e9a112015-02-22 01:51:41 +00001078 entry->flags^=CoderBlobSupportFlag;
1079 entry->flags|=CoderSeekableStreamFlag;
cristy3ed852e2009-09-05 21:47:34 +00001080 (void) RegisterMagickInfo(entry);
1081 return(MagickImageCoderSignature);
1082}
1083
1084/*
1085%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1086% %
1087% %
1088% %
1089% U n r e g i s t e r P S I m a g e %
1090% %
1091% %
1092% %
1093%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1094%
1095% UnregisterPSImage() removes format registrations made by the
1096% PS module from the list of supported formats.
1097%
1098% The format of the UnregisterPSImage method is:
1099%
1100% UnregisterPSImage(void)
1101%
1102*/
1103ModuleExport void UnregisterPSImage(void)
1104{
1105 (void) UnregisterMagickInfo("EPI");
1106 (void) UnregisterMagickInfo("EPS");
1107 (void) UnregisterMagickInfo("EPSF");
1108 (void) UnregisterMagickInfo("EPSI");
1109 (void) UnregisterMagickInfo("PS");
1110}
1111
1112/*
1113%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1114% %
1115% %
1116% %
1117% W r i t e P S I m a g e %
1118% %
1119% %
1120% %
1121%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1122%
1123% WritePSImage translates an image to encapsulated Postscript
1124% Level I for printing. If the supplied geometry is null, the image is
1125% centered on the Postscript page. Otherwise, the image is positioned as
1126% specified by the geometry.
1127%
1128% The format of the WritePSImage method is:
1129%
cristy1e178e72011-08-28 19:44:34 +00001130% MagickBooleanType WritePSImage(const ImageInfo *image_info,
1131% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001132%
1133% A description of each parameter follows:
1134%
1135% o image_info: the image info.
1136%
1137% o image: the image.
1138%
cristy1e178e72011-08-28 19:44:34 +00001139% o exception: return any errors or warnings in this structure.
1140%
cristy3ed852e2009-09-05 21:47:34 +00001141*/
1142
Cristy629f8052015-09-14 07:48:08 -04001143static inline unsigned char *PopHexPixel(const char *const *hex_digits,
cristybb503372010-05-27 20:51:26 +00001144 const size_t pixel,unsigned char *pixels)
cristy3ed852e2009-09-05 21:47:34 +00001145{
1146 register const char
1147 *hex;
1148
1149 hex=hex_digits[pixel];
1150 *pixels++=(unsigned char) (*hex++);
1151 *pixels++=(unsigned char) (*hex);
1152 return(pixels);
1153}
1154
cristy1e178e72011-08-28 19:44:34 +00001155static MagickBooleanType WritePSImage(const ImageInfo *image_info,Image *image,
1156 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001157{
1158#define WriteRunlengthPacket(image,pixel,length,p) \
1159{ \
cristy17f11b02014-12-20 19:37:04 +00001160 if ((image->alpha_trait != UndefinedPixelTrait) && \
cristy4c08aed2011-07-01 19:47:50 +00001161 (GetPixelAlpha(image,p) == (Quantum) TransparentAlpha)) \
cristy3ed852e2009-09-05 21:47:34 +00001162 { \
1163 q=PopHexPixel(hex_digits,0xff,q); \
1164 q=PopHexPixel(hex_digits,0xff,q); \
1165 q=PopHexPixel(hex_digits,0xff,q); \
1166 } \
1167 else \
1168 { \
cristyd0323222013-04-07 16:13:21 +00001169 q=PopHexPixel(hex_digits,ScaleQuantumToChar(ClampToQuantum(pixel.red)),q); \
1170 q=PopHexPixel(hex_digits,ScaleQuantumToChar(ClampToQuantum(pixel.green)),q); \
1171 q=PopHexPixel(hex_digits,ScaleQuantumToChar(ClampToQuantum(pixel.blue)),q); \
cristy3ed852e2009-09-05 21:47:34 +00001172 } \
cristy9f027d12011-09-21 01:17:17 +00001173 q=PopHexPixel(hex_digits,(size_t) MagickMin(length,0xff),q); \
cristy3ed852e2009-09-05 21:47:34 +00001174}
1175
1176 static const char
Cristy629f8052015-09-14 07:48:08 -04001177 *const hex_digits[] =
cristy3ed852e2009-09-05 21:47:34 +00001178 {
1179 "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0A", "0B",
1180 "0C", "0D", "0E", "0F", "10", "11", "12", "13", "14", "15", "16", "17",
1181 "18", "19", "1A", "1B", "1C", "1D", "1E", "1F", "20", "21", "22", "23",
1182 "24", "25", "26", "27", "28", "29", "2A", "2B", "2C", "2D", "2E", "2F",
1183 "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3A", "3B",
1184 "3C", "3D", "3E", "3F", "40", "41", "42", "43", "44", "45", "46", "47",
1185 "48", "49", "4A", "4B", "4C", "4D", "4E", "4F", "50", "51", "52", "53",
1186 "54", "55", "56", "57", "58", "59", "5A", "5B", "5C", "5D", "5E", "5F",
1187 "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6A", "6B",
1188 "6C", "6D", "6E", "6F", "70", "71", "72", "73", "74", "75", "76", "77",
1189 "78", "79", "7A", "7B", "7C", "7D", "7E", "7F", "80", "81", "82", "83",
1190 "84", "85", "86", "87", "88", "89", "8A", "8B", "8C", "8D", "8E", "8F",
1191 "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9A", "9B",
1192 "9C", "9D", "9E", "9F", "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7",
1193 "A8", "A9", "AA", "AB", "AC", "AD", "AE", "AF", "B0", "B1", "B2", "B3",
1194 "B4", "B5", "B6", "B7", "B8", "B9", "BA", "BB", "BC", "BD", "BE", "BF",
1195 "C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "CA", "CB",
1196 "CC", "CD", "CE", "CF", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
1197 "D8", "D9", "DA", "DB", "DC", "DD", "DE", "DF", "E0", "E1", "E2", "E3",
1198 "E4", "E5", "E6", "E7", "E8", "E9", "EA", "EB", "EC", "ED", "EE", "EF",
1199 "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "FA", "FB",
Cristy629f8052015-09-14 07:48:08 -04001200 "FC", "FD", "FE", "FF", (const char *) NULL
cristy3ed852e2009-09-05 21:47:34 +00001201 },
Cristy629f8052015-09-14 07:48:08 -04001202 *const PostscriptProlog[]=
cristy3ed852e2009-09-05 21:47:34 +00001203 {
1204 "%%BeginProlog",
1205 "%",
1206 "% Display a color image. The image is displayed in color on",
1207 "% Postscript viewers or printers that support color, otherwise",
1208 "% it is displayed as grayscale.",
1209 "%",
1210 "/DirectClassPacket",
1211 "{",
1212 " %",
1213 " % Get a DirectClass packet.",
1214 " %",
1215 " % Parameters:",
1216 " % red.",
1217 " % green.",
1218 " % blue.",
1219 " % length: number of pixels minus one of this color (optional).",
1220 " %",
1221 " currentfile color_packet readhexstring pop pop",
1222 " compression 0 eq",
1223 " {",
1224 " /number_pixels 3 def",
1225 " }",
1226 " {",
1227 " currentfile byte readhexstring pop 0 get",
1228 " /number_pixels exch 1 add 3 mul def",
1229 " } ifelse",
1230 " 0 3 number_pixels 1 sub",
1231 " {",
1232 " pixels exch color_packet putinterval",
1233 " } for",
1234 " pixels 0 number_pixels getinterval",
1235 "} bind def",
1236 "",
1237 "/DirectClassImage",
1238 "{",
1239 " %",
1240 " % Display a DirectClass image.",
1241 " %",
1242 " systemdict /colorimage known",
1243 " {",
1244 " columns rows 8",
1245 " [",
1246 " columns 0 0",
1247 " rows neg 0 rows",
1248 " ]",
1249 " { DirectClassPacket } false 3 colorimage",
1250 " }",
1251 " {",
1252 " %",
1253 " % No colorimage operator; convert to grayscale.",
1254 " %",
1255 " columns rows 8",
1256 " [",
1257 " columns 0 0",
1258 " rows neg 0 rows",
1259 " ]",
1260 " { GrayDirectClassPacket } image",
1261 " } ifelse",
1262 "} bind def",
1263 "",
1264 "/GrayDirectClassPacket",
1265 "{",
1266 " %",
1267 " % Get a DirectClass packet; convert to grayscale.",
1268 " %",
1269 " % Parameters:",
1270 " % red",
1271 " % green",
1272 " % blue",
1273 " % length: number of pixels minus one of this color (optional).",
1274 " %",
1275 " currentfile color_packet readhexstring pop pop",
1276 " color_packet 0 get 0.299 mul",
1277 " color_packet 1 get 0.587 mul add",
1278 " color_packet 2 get 0.114 mul add",
1279 " cvi",
1280 " /gray_packet exch def",
1281 " compression 0 eq",
1282 " {",
1283 " /number_pixels 1 def",
1284 " }",
1285 " {",
1286 " currentfile byte readhexstring pop 0 get",
1287 " /number_pixels exch 1 add def",
1288 " } ifelse",
1289 " 0 1 number_pixels 1 sub",
1290 " {",
1291 " pixels exch gray_packet put",
1292 " } for",
1293 " pixels 0 number_pixels getinterval",
1294 "} bind def",
1295 "",
1296 "/GrayPseudoClassPacket",
1297 "{",
1298 " %",
1299 " % Get a PseudoClass packet; convert to grayscale.",
1300 " %",
1301 " % Parameters:",
1302 " % index: index into the colormap.",
1303 " % length: number of pixels minus one of this color (optional).",
1304 " %",
1305 " currentfile byte readhexstring pop 0 get",
1306 " /offset exch 3 mul def",
1307 " /color_packet colormap offset 3 getinterval def",
1308 " color_packet 0 get 0.299 mul",
1309 " color_packet 1 get 0.587 mul add",
1310 " color_packet 2 get 0.114 mul add",
1311 " cvi",
1312 " /gray_packet exch def",
1313 " compression 0 eq",
1314 " {",
1315 " /number_pixels 1 def",
1316 " }",
1317 " {",
1318 " currentfile byte readhexstring pop 0 get",
1319 " /number_pixels exch 1 add def",
1320 " } ifelse",
1321 " 0 1 number_pixels 1 sub",
1322 " {",
1323 " pixels exch gray_packet put",
1324 " } for",
1325 " pixels 0 number_pixels getinterval",
1326 "} bind def",
1327 "",
1328 "/PseudoClassPacket",
1329 "{",
1330 " %",
1331 " % Get a PseudoClass packet.",
1332 " %",
1333 " % Parameters:",
1334 " % index: index into the colormap.",
1335 " % length: number of pixels minus one of this color (optional).",
1336 " %",
1337 " currentfile byte readhexstring pop 0 get",
1338 " /offset exch 3 mul def",
1339 " /color_packet colormap offset 3 getinterval def",
1340 " compression 0 eq",
1341 " {",
1342 " /number_pixels 3 def",
1343 " }",
1344 " {",
1345 " currentfile byte readhexstring pop 0 get",
1346 " /number_pixels exch 1 add 3 mul def",
1347 " } ifelse",
1348 " 0 3 number_pixels 1 sub",
1349 " {",
1350 " pixels exch color_packet putinterval",
1351 " } for",
1352 " pixels 0 number_pixels getinterval",
1353 "} bind def",
1354 "",
1355 "/PseudoClassImage",
1356 "{",
1357 " %",
1358 " % Display a PseudoClass image.",
1359 " %",
1360 " % Parameters:",
1361 " % class: 0-PseudoClass or 1-Grayscale.",
1362 " %",
1363 " currentfile buffer readline pop",
1364 " token pop /class exch def pop",
1365 " class 0 gt",
1366 " {",
1367 " currentfile buffer readline pop",
1368 " token pop /depth exch def pop",
1369 " /grays columns 8 add depth sub depth mul 8 idiv string def",
1370 " columns rows depth",
1371 " [",
1372 " columns 0 0",
1373 " rows neg 0 rows",
1374 " ]",
1375 " { currentfile grays readhexstring pop } image",
1376 " }",
1377 " {",
1378 " %",
1379 " % Parameters:",
1380 " % colors: number of colors in the colormap.",
1381 " % colormap: red, green, blue color packets.",
1382 " %",
1383 " currentfile buffer readline pop",
1384 " token pop /colors exch def pop",
1385 " /colors colors 3 mul def",
1386 " /colormap colors string def",
1387 " currentfile colormap readhexstring pop pop",
1388 " systemdict /colorimage known",
1389 " {",
1390 " columns rows 8",
1391 " [",
1392 " columns 0 0",
1393 " rows neg 0 rows",
1394 " ]",
1395 " { PseudoClassPacket } false 3 colorimage",
1396 " }",
1397 " {",
1398 " %",
1399 " % No colorimage operator; convert to grayscale.",
1400 " %",
1401 " columns rows 8",
1402 " [",
1403 " columns 0 0",
1404 " rows neg 0 rows",
1405 " ]",
1406 " { GrayPseudoClassPacket } image",
1407 " } ifelse",
1408 " } ifelse",
1409 "} bind def",
1410 "",
1411 "/DisplayImage",
1412 "{",
1413 " %",
1414 " % Display a DirectClass or PseudoClass image.",
1415 " %",
1416 " % Parameters:",
1417 " % x & y translation.",
1418 " % x & y scale.",
1419 " % label pointsize.",
1420 " % image label.",
1421 " % image columns & rows.",
1422 " % class: 0-DirectClass or 1-PseudoClass.",
1423 " % compression: 0-none or 1-RunlengthEncoded.",
1424 " % hex color packets.",
1425 " %",
1426 " gsave",
1427 " /buffer 512 string def",
1428 " /byte 1 string def",
1429 " /color_packet 3 string def",
1430 " /pixels 768 string def",
1431 "",
1432 " currentfile buffer readline pop",
1433 " token pop /x exch def",
1434 " token pop /y exch def pop",
1435 " x y translate",
1436 " currentfile buffer readline pop",
1437 " token pop /x exch def",
1438 " token pop /y exch def pop",
1439 " currentfile buffer readline pop",
1440 " token pop /pointsize exch def pop",
1441 " /Times-Roman findfont pointsize scalefont setfont",
Cristy629f8052015-09-14 07:48:08 -04001442 (const char *) NULL
cristy3ed852e2009-09-05 21:47:34 +00001443 },
Cristy629f8052015-09-14 07:48:08 -04001444 *const PostscriptEpilog[]=
cristy3ed852e2009-09-05 21:47:34 +00001445 {
1446 " x y scale",
1447 " currentfile buffer readline pop",
1448 " token pop /columns exch def",
1449 " token pop /rows exch def pop",
1450 " currentfile buffer readline pop",
1451 " token pop /class exch def pop",
1452 " currentfile buffer readline pop",
1453 " token pop /compression exch def pop",
1454 " class 0 gt { PseudoClassImage } { DirectClassImage } ifelse",
cristy8b4ff222012-03-02 13:01:39 +00001455 " grestore",
Cristy629f8052015-09-14 07:48:08 -04001456 (const char *) NULL
cristy3ed852e2009-09-05 21:47:34 +00001457 };
1458
1459 char
cristy151b66d2015-04-15 10:50:31 +00001460 buffer[MagickPathExtent],
1461 date[MagickPathExtent],
cristy3ed852e2009-09-05 21:47:34 +00001462 **labels,
cristy151b66d2015-04-15 10:50:31 +00001463 page_geometry[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00001464
cristyaf8d3912014-02-21 14:50:33 +00001465 CompressionType
1466 compression;
1467
cristy3ed852e2009-09-05 21:47:34 +00001468 const char
Cristy629f8052015-09-14 07:48:08 -04001469 *const *s,
cristy3ed852e2009-09-05 21:47:34 +00001470 *value;
1471
1472 const StringInfo
1473 *profile;
1474
1475 double
1476 pointsize;
1477
1478 GeometryInfo
1479 geometry_info;
1480
cristy3ed852e2009-09-05 21:47:34 +00001481 MagickBooleanType
1482 status;
1483
1484 MagickOffsetType
1485 scene;
1486
1487 MagickStatusType
1488 flags;
1489
cristy101ab702011-10-13 13:06:32 +00001490 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001491 pixel;
1492
1493 PointInfo
1494 delta,
1495 resolution,
1496 scale;
1497
cristy4c08aed2011-07-01 19:47:50 +00001498 Quantum
1499 index;
1500
cristy3ed852e2009-09-05 21:47:34 +00001501 RectangleInfo
1502 geometry,
1503 media_info,
1504 page_info;
1505
cristy4c08aed2011-07-01 19:47:50 +00001506 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00001507 *p;
1508
cristybb503372010-05-27 20:51:26 +00001509 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001510 i,
1511 x;
1512
1513 register unsigned char
1514 *q;
1515
1516 SegmentInfo
1517 bounds;
1518
1519 size_t
cristy802d3642011-04-27 02:02:41 +00001520 bit,
1521 byte,
1522 length,
1523 page,
1524 text_size;
1525
1526 ssize_t
1527 j,
1528 y;
cristy3ed852e2009-09-05 21:47:34 +00001529
1530 time_t
1531 timer;
1532
1533 unsigned char
1534 pixels[2048];
1535
cristy3ed852e2009-09-05 21:47:34 +00001536 /*
1537 Open output image file.
1538 */
1539 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001540 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001541 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001542 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001543 if (image->debug != MagickFalse)
1544 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +00001545 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001546 assert(exception->signature == MagickCoreSignature);
cristy1e178e72011-08-28 19:44:34 +00001547 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +00001548 if (status == MagickFalse)
1549 return(status);
1550 (void) ResetMagickMemory(&bounds,0,sizeof(bounds));
cristyaf8d3912014-02-21 14:50:33 +00001551 compression=image->compression;
1552 if (image_info->compression != UndefinedCompression)
1553 compression=image_info->compression;
cristy3ed852e2009-09-05 21:47:34 +00001554 page=1;
1555 scene=0;
1556 do
1557 {
1558 /*
1559 Scale relative to dots-per-inch.
1560 */
cristyaf8d3912014-02-21 14:50:33 +00001561 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +00001562 delta.x=DefaultResolution;
1563 delta.y=DefaultResolution;
cristy2a11bef2011-10-28 18:33:11 +00001564 resolution.x=image->resolution.x;
1565 resolution.y=image->resolution.y;
cristy3ed852e2009-09-05 21:47:34 +00001566 if ((resolution.x == 0.0) || (resolution.y == 0.0))
1567 {
1568 flags=ParseGeometry(PSDensityGeometry,&geometry_info);
1569 resolution.x=geometry_info.rho;
1570 resolution.y=geometry_info.sigma;
1571 if ((flags & SigmaValue) == 0)
1572 resolution.y=resolution.x;
1573 }
1574 if (image_info->density != (char *) NULL)
1575 {
1576 flags=ParseGeometry(image_info->density,&geometry_info);
1577 resolution.x=geometry_info.rho;
1578 resolution.y=geometry_info.sigma;
1579 if ((flags & SigmaValue) == 0)
1580 resolution.y=resolution.x;
1581 }
1582 if (image->units == PixelsPerCentimeterResolution)
1583 {
cristya97426c2011-02-04 01:41:27 +00001584 resolution.x=(double) ((size_t) (100.0*2.54*resolution.x+0.5)/100.0);
1585 resolution.y=(double) ((size_t) (100.0*2.54*resolution.y+0.5)/100.0);
cristy3ed852e2009-09-05 21:47:34 +00001586 }
1587 SetGeometry(image,&geometry);
cristy151b66d2015-04-15 10:50:31 +00001588 (void) FormatLocaleString(page_geometry,MagickPathExtent,"%.20gx%.20g",
cristye8c25f92010-06-03 00:53:06 +00001589 (double) image->columns,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001590 if (image_info->page != (char *) NULL)
cristy151b66d2015-04-15 10:50:31 +00001591 (void) CopyMagickString(page_geometry,image_info->page,MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00001592 else
1593 if ((image->page.width != 0) && (image->page.height != 0))
cristy151b66d2015-04-15 10:50:31 +00001594 (void) FormatLocaleString(page_geometry,MagickPathExtent,
cristy6d8abba2010-06-03 01:10:47 +00001595 "%.20gx%.20g%+.20g%+.20g",(double) image->page.width,(double)
cristye8c25f92010-06-03 00:53:06 +00001596 image->page.height,(double) image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00001597 else
1598 if ((image->gravity != UndefinedGravity) &&
1599 (LocaleCompare(image_info->magick,"PS") == 0))
cristy151b66d2015-04-15 10:50:31 +00001600 (void) CopyMagickString(page_geometry,PSPageGeometry,MagickPathExtent);
1601 (void) ConcatenateMagickString(page_geometry,">",MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00001602 (void) ParseMetaGeometry(page_geometry,&geometry.x,&geometry.y,
1603 &geometry.width,&geometry.height);
1604 scale.x=(double) (geometry.width*delta.x)/resolution.x;
cristybb503372010-05-27 20:51:26 +00001605 geometry.width=(size_t) floor(scale.x+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001606 scale.y=(double) (geometry.height*delta.y)/resolution.y;
cristybb503372010-05-27 20:51:26 +00001607 geometry.height=(size_t) floor(scale.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001608 (void) ParseAbsoluteGeometry(page_geometry,&media_info);
cristy1e178e72011-08-28 19:44:34 +00001609 (void) ParseGravityGeometry(image,page_geometry,&page_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00001610 if (image->gravity != UndefinedGravity)
1611 {
1612 geometry.x=(-page_info.x);
cristybb503372010-05-27 20:51:26 +00001613 geometry.y=(ssize_t) (media_info.height+page_info.y-image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001614 }
1615 pointsize=12.0;
1616 if (image_info->pointsize != 0.0)
1617 pointsize=image_info->pointsize;
1618 text_size=0;
cristyd15e6592011-10-15 00:13:06 +00001619 value=GetImageProperty(image,"label",exception);
cristy3ed852e2009-09-05 21:47:34 +00001620 if (value != (const char *) NULL)
cristybb503372010-05-27 20:51:26 +00001621 text_size=(size_t) (MultilineCensus(value)*pointsize+12);
cristy3ed852e2009-09-05 21:47:34 +00001622 if (page == 1)
1623 {
1624 /*
1625 Output Postscript header.
1626 */
1627 if (LocaleCompare(image_info->magick,"PS") == 0)
cristy151b66d2015-04-15 10:50:31 +00001628 (void) CopyMagickString(buffer,"%!PS-Adobe-3.0\n",MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00001629 else
1630 (void) CopyMagickString(buffer,"%!PS-Adobe-3.0 EPSF-3.0\n",
cristy151b66d2015-04-15 10:50:31 +00001631 MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00001632 (void) WriteBlobString(image,buffer);
1633 (void) WriteBlobString(image,"%%Creator: (ImageMagick)\n");
cristy151b66d2015-04-15 10:50:31 +00001634 (void) FormatLocaleString(buffer,MagickPathExtent,"%%%%Title: (%s)\n",
cristy3ed852e2009-09-05 21:47:34 +00001635 image->filename);
1636 (void) WriteBlobString(image,buffer);
1637 timer=time((time_t *) NULL);
cristy151b66d2015-04-15 10:50:31 +00001638 (void) FormatMagickTime(timer,MagickPathExtent,date);
1639 (void) FormatLocaleString(buffer,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +00001640 "%%%%CreationDate: (%s)\n",date);
1641 (void) WriteBlobString(image,buffer);
1642 bounds.x1=(double) geometry.x;
1643 bounds.y1=(double) geometry.y;
1644 bounds.x2=(double) geometry.x+scale.x;
1645 bounds.y2=(double) geometry.y+(geometry.height+text_size);
1646 if ((image_info->adjoin != MagickFalse) &&
1647 (GetNextImageInList(image) != (Image *) NULL))
1648 (void) CopyMagickString(buffer,"%%%%BoundingBox: (atend)\n",
cristy151b66d2015-04-15 10:50:31 +00001649 MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00001650 else
1651 {
cristy151b66d2015-04-15 10:50:31 +00001652 (void) FormatLocaleString(buffer,MagickPathExtent,
cristye8c25f92010-06-03 00:53:06 +00001653 "%%%%BoundingBox: %.20g %.20g %.20g %.20g\n",ceil(bounds.x1-0.5),
cristy8071c472012-09-24 12:41:06 +00001654 ceil(bounds.y1-0.5),floor(bounds.x2+0.5),floor(bounds.y2+0.5));
cristy3ed852e2009-09-05 21:47:34 +00001655 (void) WriteBlobString(image,buffer);
cristy151b66d2015-04-15 10:50:31 +00001656 (void) FormatLocaleString(buffer,MagickPathExtent,
cristye7f51092010-01-17 00:39:37 +00001657 "%%%%HiResBoundingBox: %g %g %g %g\n",bounds.x1,
cristy8cd5b312010-01-07 01:10:24 +00001658 bounds.y1,bounds.x2,bounds.y2);
cristy3ed852e2009-09-05 21:47:34 +00001659 }
1660 (void) WriteBlobString(image,buffer);
1661 profile=GetImageProfile(image,"8bim");
1662 if (profile != (StringInfo *) NULL)
1663 {
1664 /*
1665 Embed Photoshop profile.
1666 */
cristy151b66d2015-04-15 10:50:31 +00001667 (void) FormatLocaleString(buffer,MagickPathExtent,
cristye8c25f92010-06-03 00:53:06 +00001668 "%%BeginPhotoshop: %.20g",(double) GetStringInfoLength(profile));
cristy3ed852e2009-09-05 21:47:34 +00001669 (void) WriteBlobString(image,buffer);
cristybb503372010-05-27 20:51:26 +00001670 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
cristy3ed852e2009-09-05 21:47:34 +00001671 {
1672 if ((i % 32) == 0)
1673 (void) WriteBlobString(image,"\n% ");
cristy151b66d2015-04-15 10:50:31 +00001674 (void) FormatLocaleString(buffer,MagickPathExtent,"%02X",
cristy3ed852e2009-09-05 21:47:34 +00001675 (unsigned int) (GetStringInfoDatum(profile)[i] & 0xff));
1676 (void) WriteBlobString(image,buffer);
1677 }
1678 (void) WriteBlobString(image,"\n%EndPhotoshop\n");
1679 }
1680 profile=GetImageProfile(image,"xmp");
dirk93b02b72013-11-16 16:03:36 +00001681DisableMSCWarning(4127)
cristy2b2eb912010-03-03 14:56:40 +00001682 if (0 && (profile != (StringInfo *) NULL))
dirk93b02b72013-11-16 16:03:36 +00001683RestoreMSCWarning
cristy3ed852e2009-09-05 21:47:34 +00001684 {
1685 /*
1686 Embed XML profile.
1687 */
1688 (void) WriteBlobString(image,"\n%begin_xml_code\n");
cristy151b66d2015-04-15 10:50:31 +00001689 (void) FormatLocaleString(buffer,MagickPathExtent,
cristye8c25f92010-06-03 00:53:06 +00001690 "\n%%begin_xml_packet: %.20g\n",(double)
cristyeec18db2010-03-03 21:15:45 +00001691 GetStringInfoLength(profile));
cristy2b2eb912010-03-03 14:56:40 +00001692 (void) WriteBlobString(image,buffer);
cristybb503372010-05-27 20:51:26 +00001693 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
cristy3ed852e2009-09-05 21:47:34 +00001694 (void) WriteBlobByte(image,GetStringInfoDatum(profile)[i]);
cristy2b2eb912010-03-03 14:56:40 +00001695 (void) WriteBlobString(image,"\n%end_xml_packet\n%end_xml_code\n");
cristy3ed852e2009-09-05 21:47:34 +00001696 }
cristyd15e6592011-10-15 00:13:06 +00001697 value=GetImageProperty(image,"label",exception);
cristy3ed852e2009-09-05 21:47:34 +00001698 if (value != (const char *) NULL)
1699 (void) WriteBlobString(image,
1700 "%%DocumentNeededResources: font Times-Roman\n");
1701 (void) WriteBlobString(image,"%%DocumentData: Clean7Bit\n");
1702 (void) WriteBlobString(image,"%%LanguageLevel: 1\n");
1703 if (LocaleCompare(image_info->magick,"PS") != 0)
1704 (void) WriteBlobString(image,"%%Pages: 1\n");
1705 else
1706 {
1707 /*
1708 Compute the number of pages.
1709 */
1710 (void) WriteBlobString(image,"%%Orientation: Portrait\n");
1711 (void) WriteBlobString(image,"%%PageOrder: Ascend\n");
cristy151b66d2015-04-15 10:50:31 +00001712 (void) FormatLocaleString(buffer,MagickPathExtent,"%%%%Pages: %.20g\n",
cristye8c25f92010-06-03 00:53:06 +00001713 image_info->adjoin != MagickFalse ? (double)
1714 GetImageListLength(image) : 1.0);
cristy3ed852e2009-09-05 21:47:34 +00001715 (void) WriteBlobString(image,buffer);
1716 }
1717 (void) WriteBlobString(image,"%%EndComments\n");
1718 (void) WriteBlobString(image,"\n%%BeginDefaults\n");
1719 (void) WriteBlobString(image,"%%EndDefaults\n\n");
1720 if ((LocaleCompare(image_info->magick,"EPI") == 0) ||
1721 (LocaleCompare(image_info->magick,"EPSI") == 0) ||
1722 (LocaleCompare(image_info->magick,"EPT") == 0))
1723 {
1724 Image
1725 *preview_image;
1726
cristy3ed852e2009-09-05 21:47:34 +00001727 Quantum
1728 pixel;
1729
cristybb503372010-05-27 20:51:26 +00001730 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001731 x;
1732
cristy802d3642011-04-27 02:02:41 +00001733 ssize_t
1734 y;
1735
cristy3ed852e2009-09-05 21:47:34 +00001736 /*
1737 Create preview image.
1738 */
cristy1e178e72011-08-28 19:44:34 +00001739 preview_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00001740 if (preview_image == (Image *) NULL)
1741 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1742 /*
1743 Dump image as bitmap.
1744 */
cristy151b66d2015-04-15 10:50:31 +00001745 (void) FormatLocaleString(buffer,MagickPathExtent,
cristye8c25f92010-06-03 00:53:06 +00001746 "%%%%BeginPreview: %.20g %.20g %.20g %.20g\n%% ",(double)
1747 preview_image->columns,(double) preview_image->rows,1.0,
1748 (double) ((((preview_image->columns+7) >> 3)*preview_image->rows+
1749 35)/36));
cristy3ed852e2009-09-05 21:47:34 +00001750 (void) WriteBlobString(image,buffer);
1751 q=pixels;
cristybb503372010-05-27 20:51:26 +00001752 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001753 {
1754 p=GetVirtualPixels(preview_image,0,y,preview_image->columns,1,
cristy1e178e72011-08-28 19:44:34 +00001755 exception);
cristy4c08aed2011-07-01 19:47:50 +00001756 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001757 break;
cristy3ed852e2009-09-05 21:47:34 +00001758 bit=0;
1759 byte=0;
cristybb503372010-05-27 20:51:26 +00001760 for (x=0; x < (ssize_t) preview_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001761 {
1762 byte<<=1;
cristyd0323222013-04-07 16:13:21 +00001763 pixel=ClampToQuantum(GetPixelLuma(preview_image,p));
cristy3ed852e2009-09-05 21:47:34 +00001764 if (pixel >= (Quantum) (QuantumRange/2))
1765 byte|=0x01;
1766 bit++;
1767 if (bit == 8)
1768 {
1769 q=PopHexPixel(hex_digits,byte,q);
1770 if ((q-pixels+8) >= 80)
1771 {
1772 *q++='\n';
1773 (void) WriteBlob(image,q-pixels,pixels);
1774 q=pixels;
1775 (void) WriteBlobString(image,"% ");
1776 };
1777 bit=0;
1778 byte=0;
1779 }
1780 }
1781 if (bit != 0)
1782 {
1783 byte<<=(8-bit);
1784 q=PopHexPixel(hex_digits,byte,q);
1785 if ((q-pixels+8) >= 80)
1786 {
1787 *q++='\n';
1788 (void) WriteBlob(image,q-pixels,pixels);
1789 q=pixels;
1790 (void) WriteBlobString(image,"% ");
1791 };
1792 };
1793 }
1794 if (q != pixels)
1795 {
1796 *q++='\n';
1797 (void) WriteBlob(image,q-pixels,pixels);
1798 }
1799 (void) WriteBlobString(image,"\n%%EndPreview\n");
1800 preview_image=DestroyImage(preview_image);
1801 }
1802 /*
1803 Output Postscript commands.
1804 */
1805 for (s=PostscriptProlog; *s != (char *) NULL; s++)
1806 {
cristy151b66d2015-04-15 10:50:31 +00001807 (void) FormatLocaleString(buffer,MagickPathExtent,"%s\n",*s);
cristy3ed852e2009-09-05 21:47:34 +00001808 (void) WriteBlobString(image,buffer);
1809 }
cristyd15e6592011-10-15 00:13:06 +00001810 value=GetImageProperty(image,"label",exception);
cristy3ed852e2009-09-05 21:47:34 +00001811 if (value != (const char *) NULL)
cristybb503372010-05-27 20:51:26 +00001812 for (j=(ssize_t) MultilineCensus(value)-1; j >= 0; j--)
cristy3ed852e2009-09-05 21:47:34 +00001813 {
1814 (void) WriteBlobString(image," /label 512 string def\n");
1815 (void) WriteBlobString(image," currentfile label readline pop\n");
cristy151b66d2015-04-15 10:50:31 +00001816 (void) FormatLocaleString(buffer,MagickPathExtent,
cristye7f51092010-01-17 00:39:37 +00001817 " 0 y %g add moveto label show pop\n",j*pointsize+12);
cristy3ed852e2009-09-05 21:47:34 +00001818 (void) WriteBlobString(image,buffer);
1819 }
1820 for (s=PostscriptEpilog; *s != (char *) NULL; s++)
1821 {
cristy151b66d2015-04-15 10:50:31 +00001822 (void) FormatLocaleString(buffer,MagickPathExtent,"%s\n",*s);
cristy3ed852e2009-09-05 21:47:34 +00001823 (void) WriteBlobString(image,buffer);
1824 }
1825 if (LocaleCompare(image_info->magick,"PS") == 0)
1826 (void) WriteBlobString(image," showpage\n");
1827 (void) WriteBlobString(image,"} bind def\n");
1828 (void) WriteBlobString(image,"%%EndProlog\n");
1829 }
cristy151b66d2015-04-15 10:50:31 +00001830 (void) FormatLocaleString(buffer,MagickPathExtent,"%%%%Page: 1 %.20g\n",
cristye8c25f92010-06-03 00:53:06 +00001831 (double) (page++));
cristy3ed852e2009-09-05 21:47:34 +00001832 (void) WriteBlobString(image,buffer);
cristy151b66d2015-04-15 10:50:31 +00001833 (void) FormatLocaleString(buffer,MagickPathExtent,
cristye8c25f92010-06-03 00:53:06 +00001834 "%%%%PageBoundingBox: %.20g %.20g %.20g %.20g\n",(double) geometry.x,
1835 (double) geometry.y,geometry.x+(double) geometry.width,geometry.y+(double)
cristyf2faecf2010-05-28 19:19:36 +00001836 (geometry.height+text_size));
cristy3ed852e2009-09-05 21:47:34 +00001837 (void) WriteBlobString(image,buffer);
1838 if ((double) geometry.x < bounds.x1)
1839 bounds.x1=(double) geometry.x;
1840 if ((double) geometry.y < bounds.y1)
1841 bounds.y1=(double) geometry.y;
1842 if ((double) (geometry.x+geometry.width-1) > bounds.x2)
1843 bounds.x2=(double) geometry.x+geometry.width-1;
1844 if ((double) (geometry.y+(geometry.height+text_size)-1) > bounds.y2)
1845 bounds.y2=(double) geometry.y+(geometry.height+text_size)-1;
cristyd15e6592011-10-15 00:13:06 +00001846 value=GetImageProperty(image,"label",exception);
cristy3ed852e2009-09-05 21:47:34 +00001847 if (value != (const char *) NULL)
1848 (void) WriteBlobString(image,"%%%%PageResources: font Times-Roman\n");
1849 if (LocaleCompare(image_info->magick,"PS") != 0)
1850 (void) WriteBlobString(image,"userdict begin\n");
1851 (void) WriteBlobString(image,"DisplayImage\n");
1852 /*
1853 Output image data.
1854 */
cristy151b66d2015-04-15 10:50:31 +00001855 (void) FormatLocaleString(buffer,MagickPathExtent,"%.20g %.20g\n%g %g\n%g\n",
cristye8c25f92010-06-03 00:53:06 +00001856 (double) geometry.x,(double) geometry.y,scale.x,scale.y,pointsize);
cristy3ed852e2009-09-05 21:47:34 +00001857 (void) WriteBlobString(image,buffer);
1858 labels=(char **) NULL;
cristyd15e6592011-10-15 00:13:06 +00001859 value=GetImageProperty(image,"label",exception);
cristy3ed852e2009-09-05 21:47:34 +00001860 if (value != (const char *) NULL)
1861 labels=StringToList(value);
1862 if (labels != (char **) NULL)
1863 {
1864 for (i=0; labels[i] != (char *) NULL; i++)
1865 {
cristy151b66d2015-04-15 10:50:31 +00001866 (void) FormatLocaleString(buffer,MagickPathExtent,"%s \n",
cristy3ed852e2009-09-05 21:47:34 +00001867 labels[i]);
1868 (void) WriteBlobString(image,buffer);
1869 labels[i]=DestroyString(labels[i]);
1870 }
1871 labels=(char **) RelinquishMagickMemory(labels);
1872 }
1873 (void) ResetMagickMemory(&pixel,0,sizeof(pixel));
cristyd0323222013-04-07 16:13:21 +00001874 pixel.alpha=(MagickRealType) TransparentAlpha;
cristy4c08aed2011-07-01 19:47:50 +00001875 index=0;
cristy3ed852e2009-09-05 21:47:34 +00001876 x=0;
1877 if ((image_info->type != TrueColorType) &&
dirkf1d85482015-04-06 00:36:00 +00001878 (SetImageGray(image,exception) != MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +00001879 {
dirkf1d85482015-04-06 00:36:00 +00001880 if (SetImageMonochrome(image,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001881 {
1882 Quantum
1883 pixel;
1884
1885 /*
1886 Dump image as grayscale.
1887 */
cristy151b66d2015-04-15 10:50:31 +00001888 (void) FormatLocaleString(buffer,MagickPathExtent,
cristye8c25f92010-06-03 00:53:06 +00001889 "%.20g %.20g\n1\n1\n1\n8\n",(double) image->columns,(double)
1890 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001891 (void) WriteBlobString(image,buffer);
1892 q=pixels;
cristybb503372010-05-27 20:51:26 +00001893 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001894 {
cristy1e178e72011-08-28 19:44:34 +00001895 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001896 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001897 break;
cristybb503372010-05-27 20:51:26 +00001898 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001899 {
cristyd0323222013-04-07 16:13:21 +00001900 pixel=(Quantum) ScaleQuantumToChar(ClampToQuantum(GetPixelLuma(
1901 image,p)));
cristya97426c2011-02-04 01:41:27 +00001902 q=PopHexPixel(hex_digits,(size_t) pixel,q);
cristy3ed852e2009-09-05 21:47:34 +00001903 if ((q-pixels+8) >= 80)
1904 {
1905 *q++='\n';
1906 (void) WriteBlob(image,q-pixels,pixels);
1907 q=pixels;
1908 }
cristyed231572011-07-14 02:18:59 +00001909 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001910 }
1911 if (image->previous == (Image *) NULL)
1912 {
cristya97426c2011-02-04 01:41:27 +00001913 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType)
1914 y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001915 if (status == MagickFalse)
1916 break;
1917 }
1918 }
1919 if (q != pixels)
1920 {
1921 *q++='\n';
1922 (void) WriteBlob(image,q-pixels,pixels);
1923 }
1924 }
1925 else
1926 {
cristybb503372010-05-27 20:51:26 +00001927 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001928 y;
1929
1930 Quantum
1931 pixel;
1932
1933 /*
1934 Dump image as bitmap.
1935 */
cristy151b66d2015-04-15 10:50:31 +00001936 (void) FormatLocaleString(buffer,MagickPathExtent,
cristye8c25f92010-06-03 00:53:06 +00001937 "%.20g %.20g\n1\n1\n1\n1\n",(double) image->columns,(double)
1938 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001939 (void) WriteBlobString(image,buffer);
1940 q=pixels;
cristybb503372010-05-27 20:51:26 +00001941 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001942 {
cristy1e178e72011-08-28 19:44:34 +00001943 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001944 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001945 break;
cristy3ed852e2009-09-05 21:47:34 +00001946 bit=0;
1947 byte=0;
cristybb503372010-05-27 20:51:26 +00001948 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001949 {
1950 byte<<=1;
cristyd0323222013-04-07 16:13:21 +00001951 pixel=ClampToQuantum(GetPixelLuma(image,p));
cristy3ed852e2009-09-05 21:47:34 +00001952 if (pixel >= (Quantum) (QuantumRange/2))
1953 byte|=0x01;
1954 bit++;
1955 if (bit == 8)
1956 {
1957 q=PopHexPixel(hex_digits,byte,q);
1958 if ((q-pixels+2) >= 80)
1959 {
1960 *q++='\n';
1961 (void) WriteBlob(image,q-pixels,pixels);
1962 q=pixels;
1963 };
1964 bit=0;
1965 byte=0;
1966 }
cristyed231572011-07-14 02:18:59 +00001967 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001968 }
1969 if (bit != 0)
1970 {
1971 byte<<=(8-bit);
1972 q=PopHexPixel(hex_digits,byte,q);
1973 if ((q-pixels+2) >= 80)
1974 {
1975 *q++='\n';
1976 (void) WriteBlob(image,q-pixels,pixels);
1977 q=pixels;
1978 }
1979 };
1980 if (image->previous == (Image *) NULL)
1981 {
cristy6cde06a2011-11-24 00:08:43 +00001982 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType)
cristy802d3642011-04-27 02:02:41 +00001983 y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001984 if (status == MagickFalse)
1985 break;
1986 }
1987 }
1988 if (q != pixels)
1989 {
1990 *q++='\n';
1991 (void) WriteBlob(image,q-pixels,pixels);
1992 }
1993 }
1994 }
1995 else
1996 if ((image->storage_class == DirectClass) ||
cristy17f11b02014-12-20 19:37:04 +00001997 (image->colors > 256) || (image->alpha_trait != UndefinedPixelTrait))
cristy3ed852e2009-09-05 21:47:34 +00001998 {
1999 /*
2000 Dump DirectClass image.
2001 */
cristy151b66d2015-04-15 10:50:31 +00002002 (void) FormatLocaleString(buffer,MagickPathExtent,"%.20g %.20g\n0\n%d\n",
cristye8c25f92010-06-03 00:53:06 +00002003 (double) image->columns,(double) image->rows,
cristyaf8d3912014-02-21 14:50:33 +00002004 compression == RLECompression ? 1 : 0);
cristy3ed852e2009-09-05 21:47:34 +00002005 (void) WriteBlobString(image,buffer);
cristyaf8d3912014-02-21 14:50:33 +00002006 switch (compression)
cristy3ed852e2009-09-05 21:47:34 +00002007 {
2008 case RLECompression:
2009 {
2010 /*
2011 Dump runlength-encoded DirectColor packets.
2012 */
2013 q=pixels;
cristybb503372010-05-27 20:51:26 +00002014 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002015 {
cristy1e178e72011-08-28 19:44:34 +00002016 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002017 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002018 break;
cristy101ab702011-10-13 13:06:32 +00002019 GetPixelInfoPixel(image,p,&pixel);
cristy3ed852e2009-09-05 21:47:34 +00002020 length=255;
cristybb503372010-05-27 20:51:26 +00002021 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002022 {
cristyd0323222013-04-07 16:13:21 +00002023 if ((GetPixelRed(image,p) == ClampToQuantum(pixel.red)) &&
2024 (GetPixelGreen(image,p) == ClampToQuantum(pixel.green)) &&
2025 (GetPixelBlue(image,p) == ClampToQuantum(pixel.blue)) &&
2026 (GetPixelAlpha(image,p) == ClampToQuantum(pixel.alpha)) &&
cristy802d3642011-04-27 02:02:41 +00002027 (length < 255) && (x < (ssize_t) (image->columns-1)))
cristy3ed852e2009-09-05 21:47:34 +00002028 length++;
2029 else
2030 {
2031 if (x > 0)
2032 {
2033 WriteRunlengthPacket(image,pixel,length,p);
2034 if ((q-pixels+10) >= 80)
2035 {
2036 *q++='\n';
2037 (void) WriteBlob(image,q-pixels,pixels);
2038 q=pixels;
2039 }
2040 }
2041 length=0;
2042 }
cristy101ab702011-10-13 13:06:32 +00002043 GetPixelInfoPixel(image,p,&pixel);
cristyed231572011-07-14 02:18:59 +00002044 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002045 }
2046 WriteRunlengthPacket(image,pixel,length,p);
2047 if ((q-pixels+10) >= 80)
2048 {
2049 *q++='\n';
2050 (void) WriteBlob(image,q-pixels,pixels);
2051 q=pixels;
2052 }
2053 if (image->previous == (Image *) NULL)
2054 {
cristy802d3642011-04-27 02:02:41 +00002055 status=SetImageProgress(image,SaveImageTag,
2056 (MagickOffsetType) y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002057 if (status == MagickFalse)
2058 break;
2059 }
2060 }
2061 if (q != pixels)
2062 {
2063 *q++='\n';
2064 (void) WriteBlob(image,q-pixels,pixels);
2065 }
2066 break;
2067 }
2068 case NoCompression:
2069 default:
2070 {
2071 /*
2072 Dump uncompressed DirectColor packets.
2073 */
2074 q=pixels;
cristybb503372010-05-27 20:51:26 +00002075 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002076 {
cristy1e178e72011-08-28 19:44:34 +00002077 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002078 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002079 break;
cristybb503372010-05-27 20:51:26 +00002080 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002081 {
cristy17f11b02014-12-20 19:37:04 +00002082 if ((image->alpha_trait != UndefinedPixelTrait) &&
cristy4c08aed2011-07-01 19:47:50 +00002083 (GetPixelAlpha(image,p) == (Quantum) TransparentAlpha))
cristy3ed852e2009-09-05 21:47:34 +00002084 {
2085 q=PopHexPixel(hex_digits,0xff,q);
2086 q=PopHexPixel(hex_digits,0xff,q);
2087 q=PopHexPixel(hex_digits,0xff,q);
2088 }
2089 else
2090 {
cristy802d3642011-04-27 02:02:41 +00002091 q=PopHexPixel(hex_digits,ScaleQuantumToChar(
cristy4c08aed2011-07-01 19:47:50 +00002092 GetPixelRed(image,p)),q);
cristy802d3642011-04-27 02:02:41 +00002093 q=PopHexPixel(hex_digits,ScaleQuantumToChar(
cristy4c08aed2011-07-01 19:47:50 +00002094 GetPixelGreen(image,p)),q);
cristy802d3642011-04-27 02:02:41 +00002095 q=PopHexPixel(hex_digits,ScaleQuantumToChar(
cristy4c08aed2011-07-01 19:47:50 +00002096 GetPixelBlue(image,p)),q);
cristy3ed852e2009-09-05 21:47:34 +00002097 }
2098 if ((q-pixels+6) >= 80)
2099 {
2100 *q++='\n';
2101 (void) WriteBlob(image,q-pixels,pixels);
2102 q=pixels;
2103 }
cristyed231572011-07-14 02:18:59 +00002104 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002105 }
2106 if (image->previous == (Image *) NULL)
2107 {
cristy802d3642011-04-27 02:02:41 +00002108 status=SetImageProgress(image,SaveImageTag,
2109 (MagickOffsetType) y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002110 if (status == MagickFalse)
2111 break;
2112 }
2113 }
2114 if (q != pixels)
2115 {
2116 *q++='\n';
2117 (void) WriteBlob(image,q-pixels,pixels);
2118 }
2119 break;
2120 }
2121 }
2122 (void) WriteBlobByte(image,'\n');
2123 }
2124 else
2125 {
2126 /*
2127 Dump PseudoClass image.
2128 */
cristy151b66d2015-04-15 10:50:31 +00002129 (void) FormatLocaleString(buffer,MagickPathExtent,
cristye8c25f92010-06-03 00:53:06 +00002130 "%.20g %.20g\n%d\n%d\n0\n",(double) image->columns,(double)
2131 image->rows,image->storage_class == PseudoClass ? 1 : 0,
cristyaf8d3912014-02-21 14:50:33 +00002132 compression == RLECompression ? 1 : 0);
cristy3ed852e2009-09-05 21:47:34 +00002133 (void) WriteBlobString(image,buffer);
2134 /*
2135 Dump number of colors and colormap.
2136 */
cristy151b66d2015-04-15 10:50:31 +00002137 (void) FormatLocaleString(buffer,MagickPathExtent,"%.20g\n",(double)
cristyf2faecf2010-05-28 19:19:36 +00002138 image->colors);
cristy3ed852e2009-09-05 21:47:34 +00002139 (void) WriteBlobString(image,buffer);
cristybb503372010-05-27 20:51:26 +00002140 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002141 {
cristy151b66d2015-04-15 10:50:31 +00002142 (void) FormatLocaleString(buffer,MagickPathExtent,"%02X%02X%02X\n",
cristyd0323222013-04-07 16:13:21 +00002143 ScaleQuantumToChar(ClampToQuantum(image->colormap[i].red)),
2144 ScaleQuantumToChar(ClampToQuantum(image->colormap[i].green)),
2145 ScaleQuantumToChar(ClampToQuantum(image->colormap[i].blue)));
cristy3ed852e2009-09-05 21:47:34 +00002146 (void) WriteBlobString(image,buffer);
2147 }
cristyaf8d3912014-02-21 14:50:33 +00002148 switch (compression)
cristy3ed852e2009-09-05 21:47:34 +00002149 {
2150 case RLECompression:
2151 {
2152 /*
2153 Dump runlength-encoded PseudoColor packets.
2154 */
2155 q=pixels;
cristybb503372010-05-27 20:51:26 +00002156 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002157 {
cristy1e178e72011-08-28 19:44:34 +00002158 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002159 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002160 break;
cristy4c08aed2011-07-01 19:47:50 +00002161 index=GetPixelIndex(image,p);
cristy3ed852e2009-09-05 21:47:34 +00002162 length=255;
cristybb503372010-05-27 20:51:26 +00002163 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002164 {
cristy4c08aed2011-07-01 19:47:50 +00002165 if ((index == GetPixelIndex(image,p)) &&
cristy802d3642011-04-27 02:02:41 +00002166 (length < 255) && (x < ((ssize_t) image->columns-1)))
cristy3ed852e2009-09-05 21:47:34 +00002167 length++;
2168 else
2169 {
2170 if (x > 0)
2171 {
cristya97426c2011-02-04 01:41:27 +00002172 q=PopHexPixel(hex_digits,(size_t) index,q);
cristybb503372010-05-27 20:51:26 +00002173 q=PopHexPixel(hex_digits,(size_t)
cristy3ed852e2009-09-05 21:47:34 +00002174 MagickMin(length,0xff),q);
2175 i++;
2176 if ((q-pixels+6) >= 80)
2177 {
2178 *q++='\n';
2179 (void) WriteBlob(image,q-pixels,pixels);
2180 q=pixels;
2181 }
2182 }
2183 length=0;
2184 }
cristy4c08aed2011-07-01 19:47:50 +00002185 index=GetPixelIndex(image,p);
cristyd0323222013-04-07 16:13:21 +00002186 pixel.red=(MagickRealType) GetPixelRed(image,p);
2187 pixel.green=(MagickRealType) GetPixelGreen(image,p);
2188 pixel.blue=(MagickRealType) GetPixelBlue(image,p);
2189 pixel.alpha=(MagickRealType) GetPixelAlpha(image,p);
cristyed231572011-07-14 02:18:59 +00002190 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002191 }
cristya97426c2011-02-04 01:41:27 +00002192 q=PopHexPixel(hex_digits,(size_t) index,q);
cristybb503372010-05-27 20:51:26 +00002193 q=PopHexPixel(hex_digits,(size_t)
cristy3ed852e2009-09-05 21:47:34 +00002194 MagickMin(length,0xff),q);
2195 if (image->previous == (Image *) NULL)
2196 {
cristya97426c2011-02-04 01:41:27 +00002197 status=SetImageProgress(image,SaveImageTag,
2198 (MagickOffsetType) y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002199 if (status == MagickFalse)
2200 break;
2201 }
2202 }
2203 if (q != pixels)
2204 {
2205 *q++='\n';
2206 (void) WriteBlob(image,q-pixels,pixels);
2207 }
2208 break;
2209 }
2210 case NoCompression:
2211 default:
2212 {
2213 /*
2214 Dump uncompressed PseudoColor packets.
2215 */
2216 q=pixels;
cristybb503372010-05-27 20:51:26 +00002217 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002218 {
cristy1e178e72011-08-28 19:44:34 +00002219 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002220 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002221 break;
cristybb503372010-05-27 20:51:26 +00002222 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002223 {
cristy4c08aed2011-07-01 19:47:50 +00002224 q=PopHexPixel(hex_digits,(size_t) GetPixelIndex(image,p),q);
cristy3ed852e2009-09-05 21:47:34 +00002225 if ((q-pixels+4) >= 80)
2226 {
2227 *q++='\n';
2228 (void) WriteBlob(image,q-pixels,pixels);
2229 q=pixels;
2230 }
cristyed231572011-07-14 02:18:59 +00002231 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002232 }
2233 if (image->previous == (Image *) NULL)
2234 {
cristya97426c2011-02-04 01:41:27 +00002235 status=SetImageProgress(image,SaveImageTag,
2236 (MagickOffsetType) y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002237 if (status == MagickFalse)
2238 break;
2239 }
2240 }
2241 if (q != pixels)
2242 {
2243 *q++='\n';
2244 (void) WriteBlob(image,q-pixels,pixels);
2245 }
2246 break;
2247 }
2248 }
2249 (void) WriteBlobByte(image,'\n');
2250 }
2251 if (LocaleCompare(image_info->magick,"PS") != 0)
2252 (void) WriteBlobString(image,"end\n");
2253 (void) WriteBlobString(image,"%%PageTrailer\n");
2254 if (GetNextImageInList(image) == (Image *) NULL)
2255 break;
2256 image=SyncNextImageInList(image);
2257 status=SetImageProgress(image,SaveImagesTag,scene++,
2258 GetImageListLength(image));
2259 if (status == MagickFalse)
2260 break;
2261 } while (image_info->adjoin != MagickFalse);
2262 (void) WriteBlobString(image,"%%Trailer\n");
2263 if (page > 2)
2264 {
cristy151b66d2015-04-15 10:50:31 +00002265 (void) FormatLocaleString(buffer,MagickPathExtent,
cristye8c25f92010-06-03 00:53:06 +00002266 "%%%%BoundingBox: %.20g %.20g %.20g %.20g\n",ceil(bounds.x1-0.5),
cristye4cc1a12012-09-03 23:04:46 +00002267 ceil(bounds.y1-0.5),floor(bounds.x2-0.5),floor(bounds.y2-0.5));
cristy3ed852e2009-09-05 21:47:34 +00002268 (void) WriteBlobString(image,buffer);
cristy151b66d2015-04-15 10:50:31 +00002269 (void) FormatLocaleString(buffer,MagickPathExtent,
cristye4cc1a12012-09-03 23:04:46 +00002270 "%%%%HiResBoundingBox: %g %g %g %g\n",bounds.x1,bounds.y1,bounds.x2,
2271 bounds.y2);
cristy3ed852e2009-09-05 21:47:34 +00002272 (void) WriteBlobString(image,buffer);
2273 }
2274 (void) WriteBlobString(image,"%%EOF\n");
2275 (void) CloseBlob(image);
2276 return(MagickTrue);
2277}