blob: 1e6d321014b7f44c692cbb8666fbe82710379925 [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% %
Cristyf6ff9ea2016-12-05 09:53:35 -050020% Copyright 1999-2017 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 {
Cristy23f956b2016-10-01 12:21:15 -0400983 (void) CopyMagickString(postscript_image->filename,filename,
984 MagickPathExtent);
cristy835b2462014-10-27 23:44:33 +0000985 (void) CopyMagickString(postscript_image->magick,image->magick,
cristy151b66d2015-04-15 10:50:31 +0000986 MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +0000987 if (columns != 0)
988 postscript_image->magick_columns=columns;
989 if (rows != 0)
990 postscript_image->magick_rows=rows;
991 postscript_image->page=page;
992 (void) CloneImageProfiles(postscript_image,image);
993 (void) CloneImageProperties(postscript_image,image);
994 next=SyncNextImageInList(postscript_image);
995 if (next != (Image *) NULL)
996 postscript_image=next;
997 } while (next != (Image *) NULL);
998 image=DestroyImageList(image);
999 scene=0;
1000 for (next=GetFirstImageInList(postscript_image); next != (Image *) NULL; )
1001 {
1002 next->scene=scene++;
1003 next=GetNextImageInList(next);
1004 }
1005 return(GetFirstImageInList(postscript_image));
1006}
1007
1008/*
1009%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1010% %
1011% %
1012% %
1013% R e g i s t e r P S I m a g e %
1014% %
1015% %
1016% %
1017%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1018%
1019% RegisterPSImage() adds properties for the PS image format to
1020% the list of supported formats. The properties include the image format
1021% tag, a method to read and/or write the format, whether the format
1022% supports the saving of more than one frame to the same file or blob,
1023% whether the format supports native in-memory I/O, and a brief
1024% description of the format.
1025%
1026% The format of the RegisterPSImage method is:
1027%
cristybb503372010-05-27 20:51:26 +00001028% size_t RegisterPSImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001029%
1030*/
cristybb503372010-05-27 20:51:26 +00001031ModuleExport size_t RegisterPSImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001032{
1033 MagickInfo
1034 *entry;
1035
dirk06b627a2015-04-06 18:59:17 +00001036 entry=AcquireMagickInfo("PS","EPI",
cristy5aefbeb2013-08-09 12:13:32 +00001037 "Encapsulated PostScript Interchange format");
cristy3ed852e2009-09-05 21:47:34 +00001038 entry->decoder=(DecodeImageHandler *) ReadPSImage;
1039 entry->encoder=(EncodeImageHandler *) WritePSImage;
1040 entry->magick=(IsImageFormatHandler *) IsPS;
dirk08e9a112015-02-22 01:51:41 +00001041 entry->flags^=CoderAdjoinFlag;
1042 entry->flags^=CoderBlobSupportFlag;
1043 entry->flags|=CoderSeekableStreamFlag;
cristy5aefbeb2013-08-09 12:13:32 +00001044 entry->mime_type=ConstantString("application/postscript");
cristy3ed852e2009-09-05 21:47:34 +00001045 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001046 entry=AcquireMagickInfo("PS","EPS","Encapsulated PostScript");
cristy3ed852e2009-09-05 21:47:34 +00001047 entry->decoder=(DecodeImageHandler *) ReadPSImage;
1048 entry->encoder=(EncodeImageHandler *) WritePSImage;
1049 entry->magick=(IsImageFormatHandler *) IsPS;
dirk08e9a112015-02-22 01:51:41 +00001050 entry->flags^=CoderAdjoinFlag;
1051 entry->flags^=CoderBlobSupportFlag;
1052 entry->flags|=CoderSeekableStreamFlag;
cristy5aefbeb2013-08-09 12:13:32 +00001053 entry->mime_type=ConstantString("application/postscript");
cristy3ed852e2009-09-05 21:47:34 +00001054 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001055 entry=AcquireMagickInfo("PS","EPSF","Encapsulated PostScript");
cristy3ed852e2009-09-05 21:47:34 +00001056 entry->decoder=(DecodeImageHandler *) ReadPSImage;
1057 entry->encoder=(EncodeImageHandler *) WritePSImage;
1058 entry->magick=(IsImageFormatHandler *) IsPS;
dirk08e9a112015-02-22 01:51:41 +00001059 entry->flags^=CoderAdjoinFlag;
1060 entry->flags^=CoderBlobSupportFlag;
1061 entry->flags|=CoderSeekableStreamFlag;
dirk06b627a2015-04-06 18:59:17 +00001062 entry->mime_type=ConstantString("application/postscript");
1063 (void) RegisterMagickInfo(entry);
1064 entry=AcquireMagickInfo("PS","EPSI",
cristy3ed852e2009-09-05 21:47:34 +00001065 "Encapsulated PostScript Interchange format");
dirk06b627a2015-04-06 18:59:17 +00001066 entry->decoder=(DecodeImageHandler *) ReadPSImage;
1067 entry->encoder=(EncodeImageHandler *) WritePSImage;
1068 entry->magick=(IsImageFormatHandler *) IsPS;
1069 entry->flags^=CoderAdjoinFlag;
1070 entry->flags^=CoderBlobSupportFlag;
1071 entry->flags|=CoderSeekableStreamFlag;
cristy5aefbeb2013-08-09 12:13:32 +00001072 entry->mime_type=ConstantString("application/postscript");
cristy3ed852e2009-09-05 21:47:34 +00001073 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001074 entry=AcquireMagickInfo("PS","PS","PostScript");
cristy3ed852e2009-09-05 21:47:34 +00001075 entry->decoder=(DecodeImageHandler *) ReadPSImage;
1076 entry->encoder=(EncodeImageHandler *) WritePSImage;
1077 entry->magick=(IsImageFormatHandler *) IsPS;
cristy5aefbeb2013-08-09 12:13:32 +00001078 entry->mime_type=ConstantString("application/postscript");
dirk08e9a112015-02-22 01:51:41 +00001079 entry->flags^=CoderBlobSupportFlag;
1080 entry->flags|=CoderSeekableStreamFlag;
cristy3ed852e2009-09-05 21:47:34 +00001081 (void) RegisterMagickInfo(entry);
1082 return(MagickImageCoderSignature);
1083}
1084
1085/*
1086%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1087% %
1088% %
1089% %
1090% U n r e g i s t e r P S I m a g e %
1091% %
1092% %
1093% %
1094%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1095%
1096% UnregisterPSImage() removes format registrations made by the
1097% PS module from the list of supported formats.
1098%
1099% The format of the UnregisterPSImage method is:
1100%
1101% UnregisterPSImage(void)
1102%
1103*/
1104ModuleExport void UnregisterPSImage(void)
1105{
1106 (void) UnregisterMagickInfo("EPI");
1107 (void) UnregisterMagickInfo("EPS");
1108 (void) UnregisterMagickInfo("EPSF");
1109 (void) UnregisterMagickInfo("EPSI");
1110 (void) UnregisterMagickInfo("PS");
1111}
1112
1113/*
1114%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1115% %
1116% %
1117% %
1118% W r i t e P S I m a g e %
1119% %
1120% %
1121% %
1122%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1123%
1124% WritePSImage translates an image to encapsulated Postscript
1125% Level I for printing. If the supplied geometry is null, the image is
1126% centered on the Postscript page. Otherwise, the image is positioned as
1127% specified by the geometry.
1128%
1129% The format of the WritePSImage method is:
1130%
cristy1e178e72011-08-28 19:44:34 +00001131% MagickBooleanType WritePSImage(const ImageInfo *image_info,
1132% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001133%
1134% A description of each parameter follows:
1135%
1136% o image_info: the image info.
1137%
1138% o image: the image.
1139%
cristy1e178e72011-08-28 19:44:34 +00001140% o exception: return any errors or warnings in this structure.
1141%
cristy3ed852e2009-09-05 21:47:34 +00001142*/
1143
Cristy629f8052015-09-14 07:48:08 -04001144static inline unsigned char *PopHexPixel(const char *const *hex_digits,
cristybb503372010-05-27 20:51:26 +00001145 const size_t pixel,unsigned char *pixels)
cristy3ed852e2009-09-05 21:47:34 +00001146{
1147 register const char
1148 *hex;
1149
1150 hex=hex_digits[pixel];
1151 *pixels++=(unsigned char) (*hex++);
1152 *pixels++=(unsigned char) (*hex);
1153 return(pixels);
1154}
1155
cristy1e178e72011-08-28 19:44:34 +00001156static MagickBooleanType WritePSImage(const ImageInfo *image_info,Image *image,
1157 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001158{
1159#define WriteRunlengthPacket(image,pixel,length,p) \
1160{ \
cristy17f11b02014-12-20 19:37:04 +00001161 if ((image->alpha_trait != UndefinedPixelTrait) && \
cristy4c08aed2011-07-01 19:47:50 +00001162 (GetPixelAlpha(image,p) == (Quantum) TransparentAlpha)) \
cristy3ed852e2009-09-05 21:47:34 +00001163 { \
1164 q=PopHexPixel(hex_digits,0xff,q); \
1165 q=PopHexPixel(hex_digits,0xff,q); \
1166 q=PopHexPixel(hex_digits,0xff,q); \
1167 } \
1168 else \
1169 { \
cristyd0323222013-04-07 16:13:21 +00001170 q=PopHexPixel(hex_digits,ScaleQuantumToChar(ClampToQuantum(pixel.red)),q); \
1171 q=PopHexPixel(hex_digits,ScaleQuantumToChar(ClampToQuantum(pixel.green)),q); \
1172 q=PopHexPixel(hex_digits,ScaleQuantumToChar(ClampToQuantum(pixel.blue)),q); \
cristy3ed852e2009-09-05 21:47:34 +00001173 } \
cristy9f027d12011-09-21 01:17:17 +00001174 q=PopHexPixel(hex_digits,(size_t) MagickMin(length,0xff),q); \
cristy3ed852e2009-09-05 21:47:34 +00001175}
1176
1177 static const char
Cristy629f8052015-09-14 07:48:08 -04001178 *const hex_digits[] =
cristy3ed852e2009-09-05 21:47:34 +00001179 {
1180 "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0A", "0B",
1181 "0C", "0D", "0E", "0F", "10", "11", "12", "13", "14", "15", "16", "17",
1182 "18", "19", "1A", "1B", "1C", "1D", "1E", "1F", "20", "21", "22", "23",
1183 "24", "25", "26", "27", "28", "29", "2A", "2B", "2C", "2D", "2E", "2F",
1184 "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3A", "3B",
1185 "3C", "3D", "3E", "3F", "40", "41", "42", "43", "44", "45", "46", "47",
1186 "48", "49", "4A", "4B", "4C", "4D", "4E", "4F", "50", "51", "52", "53",
1187 "54", "55", "56", "57", "58", "59", "5A", "5B", "5C", "5D", "5E", "5F",
1188 "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6A", "6B",
1189 "6C", "6D", "6E", "6F", "70", "71", "72", "73", "74", "75", "76", "77",
1190 "78", "79", "7A", "7B", "7C", "7D", "7E", "7F", "80", "81", "82", "83",
1191 "84", "85", "86", "87", "88", "89", "8A", "8B", "8C", "8D", "8E", "8F",
1192 "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9A", "9B",
1193 "9C", "9D", "9E", "9F", "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7",
1194 "A8", "A9", "AA", "AB", "AC", "AD", "AE", "AF", "B0", "B1", "B2", "B3",
1195 "B4", "B5", "B6", "B7", "B8", "B9", "BA", "BB", "BC", "BD", "BE", "BF",
1196 "C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "CA", "CB",
1197 "CC", "CD", "CE", "CF", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
1198 "D8", "D9", "DA", "DB", "DC", "DD", "DE", "DF", "E0", "E1", "E2", "E3",
1199 "E4", "E5", "E6", "E7", "E8", "E9", "EA", "EB", "EC", "ED", "EE", "EF",
1200 "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "FA", "FB",
Cristy629f8052015-09-14 07:48:08 -04001201 "FC", "FD", "FE", "FF", (const char *) NULL
cristy3ed852e2009-09-05 21:47:34 +00001202 },
Cristy629f8052015-09-14 07:48:08 -04001203 *const PostscriptProlog[]=
cristy3ed852e2009-09-05 21:47:34 +00001204 {
1205 "%%BeginProlog",
1206 "%",
1207 "% Display a color image. The image is displayed in color on",
1208 "% Postscript viewers or printers that support color, otherwise",
1209 "% it is displayed as grayscale.",
1210 "%",
1211 "/DirectClassPacket",
1212 "{",
1213 " %",
1214 " % Get a DirectClass packet.",
1215 " %",
1216 " % Parameters:",
1217 " % red.",
1218 " % green.",
1219 " % blue.",
1220 " % length: number of pixels minus one of this color (optional).",
1221 " %",
1222 " currentfile color_packet readhexstring pop pop",
1223 " compression 0 eq",
1224 " {",
1225 " /number_pixels 3 def",
1226 " }",
1227 " {",
1228 " currentfile byte readhexstring pop 0 get",
1229 " /number_pixels exch 1 add 3 mul def",
1230 " } ifelse",
1231 " 0 3 number_pixels 1 sub",
1232 " {",
1233 " pixels exch color_packet putinterval",
1234 " } for",
1235 " pixels 0 number_pixels getinterval",
1236 "} bind def",
1237 "",
1238 "/DirectClassImage",
1239 "{",
1240 " %",
1241 " % Display a DirectClass image.",
1242 " %",
1243 " systemdict /colorimage known",
1244 " {",
1245 " columns rows 8",
1246 " [",
1247 " columns 0 0",
1248 " rows neg 0 rows",
1249 " ]",
1250 " { DirectClassPacket } false 3 colorimage",
1251 " }",
1252 " {",
1253 " %",
1254 " % No colorimage operator; convert to grayscale.",
1255 " %",
1256 " columns rows 8",
1257 " [",
1258 " columns 0 0",
1259 " rows neg 0 rows",
1260 " ]",
1261 " { GrayDirectClassPacket } image",
1262 " } ifelse",
1263 "} bind def",
1264 "",
1265 "/GrayDirectClassPacket",
1266 "{",
1267 " %",
1268 " % Get a DirectClass packet; convert to grayscale.",
1269 " %",
1270 " % Parameters:",
1271 " % red",
1272 " % green",
1273 " % blue",
1274 " % length: number of pixels minus one of this color (optional).",
1275 " %",
1276 " currentfile color_packet readhexstring pop pop",
1277 " color_packet 0 get 0.299 mul",
1278 " color_packet 1 get 0.587 mul add",
1279 " color_packet 2 get 0.114 mul add",
1280 " cvi",
1281 " /gray_packet exch def",
1282 " compression 0 eq",
1283 " {",
1284 " /number_pixels 1 def",
1285 " }",
1286 " {",
1287 " currentfile byte readhexstring pop 0 get",
1288 " /number_pixels exch 1 add def",
1289 " } ifelse",
1290 " 0 1 number_pixels 1 sub",
1291 " {",
1292 " pixels exch gray_packet put",
1293 " } for",
1294 " pixels 0 number_pixels getinterval",
1295 "} bind def",
1296 "",
1297 "/GrayPseudoClassPacket",
1298 "{",
1299 " %",
1300 " % Get a PseudoClass packet; convert to grayscale.",
1301 " %",
1302 " % Parameters:",
1303 " % index: index into the colormap.",
1304 " % length: number of pixels minus one of this color (optional).",
1305 " %",
1306 " currentfile byte readhexstring pop 0 get",
1307 " /offset exch 3 mul def",
1308 " /color_packet colormap offset 3 getinterval def",
1309 " color_packet 0 get 0.299 mul",
1310 " color_packet 1 get 0.587 mul add",
1311 " color_packet 2 get 0.114 mul add",
1312 " cvi",
1313 " /gray_packet exch def",
1314 " compression 0 eq",
1315 " {",
1316 " /number_pixels 1 def",
1317 " }",
1318 " {",
1319 " currentfile byte readhexstring pop 0 get",
1320 " /number_pixels exch 1 add def",
1321 " } ifelse",
1322 " 0 1 number_pixels 1 sub",
1323 " {",
1324 " pixels exch gray_packet put",
1325 " } for",
1326 " pixels 0 number_pixels getinterval",
1327 "} bind def",
1328 "",
1329 "/PseudoClassPacket",
1330 "{",
1331 " %",
1332 " % Get a PseudoClass packet.",
1333 " %",
1334 " % Parameters:",
1335 " % index: index into the colormap.",
1336 " % length: number of pixels minus one of this color (optional).",
1337 " %",
1338 " currentfile byte readhexstring pop 0 get",
1339 " /offset exch 3 mul def",
1340 " /color_packet colormap offset 3 getinterval def",
1341 " compression 0 eq",
1342 " {",
1343 " /number_pixels 3 def",
1344 " }",
1345 " {",
1346 " currentfile byte readhexstring pop 0 get",
1347 " /number_pixels exch 1 add 3 mul def",
1348 " } ifelse",
1349 " 0 3 number_pixels 1 sub",
1350 " {",
1351 " pixels exch color_packet putinterval",
1352 " } for",
1353 " pixels 0 number_pixels getinterval",
1354 "} bind def",
1355 "",
1356 "/PseudoClassImage",
1357 "{",
1358 " %",
1359 " % Display a PseudoClass image.",
1360 " %",
1361 " % Parameters:",
1362 " % class: 0-PseudoClass or 1-Grayscale.",
1363 " %",
1364 " currentfile buffer readline pop",
1365 " token pop /class exch def pop",
1366 " class 0 gt",
1367 " {",
1368 " currentfile buffer readline pop",
1369 " token pop /depth exch def pop",
1370 " /grays columns 8 add depth sub depth mul 8 idiv string def",
1371 " columns rows depth",
1372 " [",
1373 " columns 0 0",
1374 " rows neg 0 rows",
1375 " ]",
1376 " { currentfile grays readhexstring pop } image",
1377 " }",
1378 " {",
1379 " %",
1380 " % Parameters:",
1381 " % colors: number of colors in the colormap.",
1382 " % colormap: red, green, blue color packets.",
1383 " %",
1384 " currentfile buffer readline pop",
1385 " token pop /colors exch def pop",
1386 " /colors colors 3 mul def",
1387 " /colormap colors string def",
1388 " currentfile colormap readhexstring pop pop",
1389 " systemdict /colorimage known",
1390 " {",
1391 " columns rows 8",
1392 " [",
1393 " columns 0 0",
1394 " rows neg 0 rows",
1395 " ]",
1396 " { PseudoClassPacket } false 3 colorimage",
1397 " }",
1398 " {",
1399 " %",
1400 " % No colorimage operator; convert to grayscale.",
1401 " %",
1402 " columns rows 8",
1403 " [",
1404 " columns 0 0",
1405 " rows neg 0 rows",
1406 " ]",
1407 " { GrayPseudoClassPacket } image",
1408 " } ifelse",
1409 " } ifelse",
1410 "} bind def",
1411 "",
1412 "/DisplayImage",
1413 "{",
1414 " %",
1415 " % Display a DirectClass or PseudoClass image.",
1416 " %",
1417 " % Parameters:",
1418 " % x & y translation.",
1419 " % x & y scale.",
1420 " % label pointsize.",
1421 " % image label.",
1422 " % image columns & rows.",
1423 " % class: 0-DirectClass or 1-PseudoClass.",
1424 " % compression: 0-none or 1-RunlengthEncoded.",
1425 " % hex color packets.",
1426 " %",
1427 " gsave",
1428 " /buffer 512 string def",
1429 " /byte 1 string def",
1430 " /color_packet 3 string def",
1431 " /pixels 768 string def",
1432 "",
1433 " currentfile buffer readline pop",
1434 " token pop /x exch def",
1435 " token pop /y exch def pop",
1436 " x y translate",
1437 " currentfile buffer readline pop",
1438 " token pop /x exch def",
1439 " token pop /y exch def pop",
1440 " currentfile buffer readline pop",
1441 " token pop /pointsize exch def pop",
1442 " /Times-Roman findfont pointsize scalefont setfont",
Cristy629f8052015-09-14 07:48:08 -04001443 (const char *) NULL
cristy3ed852e2009-09-05 21:47:34 +00001444 },
Cristy629f8052015-09-14 07:48:08 -04001445 *const PostscriptEpilog[]=
cristy3ed852e2009-09-05 21:47:34 +00001446 {
1447 " x y scale",
1448 " currentfile buffer readline pop",
1449 " token pop /columns exch def",
1450 " token pop /rows exch def pop",
1451 " currentfile buffer readline pop",
1452 " token pop /class exch def pop",
1453 " currentfile buffer readline pop",
1454 " token pop /compression exch def pop",
1455 " class 0 gt { PseudoClassImage } { DirectClassImage } ifelse",
cristy8b4ff222012-03-02 13:01:39 +00001456 " grestore",
Cristy629f8052015-09-14 07:48:08 -04001457 (const char *) NULL
cristy3ed852e2009-09-05 21:47:34 +00001458 };
1459
1460 char
cristy151b66d2015-04-15 10:50:31 +00001461 buffer[MagickPathExtent],
1462 date[MagickPathExtent],
cristy3ed852e2009-09-05 21:47:34 +00001463 **labels,
cristy151b66d2015-04-15 10:50:31 +00001464 page_geometry[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00001465
cristyaf8d3912014-02-21 14:50:33 +00001466 CompressionType
1467 compression;
1468
cristy3ed852e2009-09-05 21:47:34 +00001469 const char
Cristy629f8052015-09-14 07:48:08 -04001470 *const *s,
cristy3ed852e2009-09-05 21:47:34 +00001471 *value;
1472
1473 const StringInfo
1474 *profile;
1475
1476 double
1477 pointsize;
1478
1479 GeometryInfo
1480 geometry_info;
1481
cristy3ed852e2009-09-05 21:47:34 +00001482 MagickBooleanType
1483 status;
1484
1485 MagickOffsetType
1486 scene;
1487
1488 MagickStatusType
1489 flags;
1490
cristy101ab702011-10-13 13:06:32 +00001491 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001492 pixel;
1493
1494 PointInfo
1495 delta,
1496 resolution,
1497 scale;
1498
cristy4c08aed2011-07-01 19:47:50 +00001499 Quantum
1500 index;
1501
cristy3ed852e2009-09-05 21:47:34 +00001502 RectangleInfo
1503 geometry,
1504 media_info,
1505 page_info;
1506
cristy4c08aed2011-07-01 19:47:50 +00001507 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00001508 *p;
1509
cristybb503372010-05-27 20:51:26 +00001510 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001511 i,
1512 x;
1513
1514 register unsigned char
1515 *q;
1516
1517 SegmentInfo
1518 bounds;
1519
1520 size_t
cristy802d3642011-04-27 02:02:41 +00001521 bit,
1522 byte,
1523 length,
1524 page,
1525 text_size;
1526
1527 ssize_t
1528 j,
1529 y;
cristy3ed852e2009-09-05 21:47:34 +00001530
1531 time_t
1532 timer;
1533
1534 unsigned char
1535 pixels[2048];
1536
cristy3ed852e2009-09-05 21:47:34 +00001537 /*
1538 Open output image file.
1539 */
1540 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001541 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001542 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001543 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001544 if (image->debug != MagickFalse)
1545 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +00001546 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001547 assert(exception->signature == MagickCoreSignature);
cristy1e178e72011-08-28 19:44:34 +00001548 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +00001549 if (status == MagickFalse)
1550 return(status);
1551 (void) ResetMagickMemory(&bounds,0,sizeof(bounds));
cristyaf8d3912014-02-21 14:50:33 +00001552 compression=image->compression;
1553 if (image_info->compression != UndefinedCompression)
1554 compression=image_info->compression;
cristy3ed852e2009-09-05 21:47:34 +00001555 page=1;
1556 scene=0;
1557 do
1558 {
1559 /*
1560 Scale relative to dots-per-inch.
1561 */
cristyaf8d3912014-02-21 14:50:33 +00001562 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +00001563 delta.x=DefaultResolution;
1564 delta.y=DefaultResolution;
cristy2a11bef2011-10-28 18:33:11 +00001565 resolution.x=image->resolution.x;
1566 resolution.y=image->resolution.y;
cristy3ed852e2009-09-05 21:47:34 +00001567 if ((resolution.x == 0.0) || (resolution.y == 0.0))
1568 {
1569 flags=ParseGeometry(PSDensityGeometry,&geometry_info);
1570 resolution.x=geometry_info.rho;
1571 resolution.y=geometry_info.sigma;
1572 if ((flags & SigmaValue) == 0)
1573 resolution.y=resolution.x;
1574 }
1575 if (image_info->density != (char *) NULL)
1576 {
1577 flags=ParseGeometry(image_info->density,&geometry_info);
1578 resolution.x=geometry_info.rho;
1579 resolution.y=geometry_info.sigma;
1580 if ((flags & SigmaValue) == 0)
1581 resolution.y=resolution.x;
1582 }
1583 if (image->units == PixelsPerCentimeterResolution)
1584 {
cristya97426c2011-02-04 01:41:27 +00001585 resolution.x=(double) ((size_t) (100.0*2.54*resolution.x+0.5)/100.0);
1586 resolution.y=(double) ((size_t) (100.0*2.54*resolution.y+0.5)/100.0);
cristy3ed852e2009-09-05 21:47:34 +00001587 }
1588 SetGeometry(image,&geometry);
cristy151b66d2015-04-15 10:50:31 +00001589 (void) FormatLocaleString(page_geometry,MagickPathExtent,"%.20gx%.20g",
cristye8c25f92010-06-03 00:53:06 +00001590 (double) image->columns,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001591 if (image_info->page != (char *) NULL)
cristy151b66d2015-04-15 10:50:31 +00001592 (void) CopyMagickString(page_geometry,image_info->page,MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00001593 else
1594 if ((image->page.width != 0) && (image->page.height != 0))
cristy151b66d2015-04-15 10:50:31 +00001595 (void) FormatLocaleString(page_geometry,MagickPathExtent,
cristy6d8abba2010-06-03 01:10:47 +00001596 "%.20gx%.20g%+.20g%+.20g",(double) image->page.width,(double)
cristye8c25f92010-06-03 00:53:06 +00001597 image->page.height,(double) image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00001598 else
1599 if ((image->gravity != UndefinedGravity) &&
1600 (LocaleCompare(image_info->magick,"PS") == 0))
cristy151b66d2015-04-15 10:50:31 +00001601 (void) CopyMagickString(page_geometry,PSPageGeometry,MagickPathExtent);
1602 (void) ConcatenateMagickString(page_geometry,">",MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00001603 (void) ParseMetaGeometry(page_geometry,&geometry.x,&geometry.y,
1604 &geometry.width,&geometry.height);
1605 scale.x=(double) (geometry.width*delta.x)/resolution.x;
cristybb503372010-05-27 20:51:26 +00001606 geometry.width=(size_t) floor(scale.x+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001607 scale.y=(double) (geometry.height*delta.y)/resolution.y;
cristybb503372010-05-27 20:51:26 +00001608 geometry.height=(size_t) floor(scale.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001609 (void) ParseAbsoluteGeometry(page_geometry,&media_info);
cristy1e178e72011-08-28 19:44:34 +00001610 (void) ParseGravityGeometry(image,page_geometry,&page_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00001611 if (image->gravity != UndefinedGravity)
1612 {
1613 geometry.x=(-page_info.x);
cristybb503372010-05-27 20:51:26 +00001614 geometry.y=(ssize_t) (media_info.height+page_info.y-image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001615 }
1616 pointsize=12.0;
1617 if (image_info->pointsize != 0.0)
1618 pointsize=image_info->pointsize;
1619 text_size=0;
cristyd15e6592011-10-15 00:13:06 +00001620 value=GetImageProperty(image,"label",exception);
cristy3ed852e2009-09-05 21:47:34 +00001621 if (value != (const char *) NULL)
cristybb503372010-05-27 20:51:26 +00001622 text_size=(size_t) (MultilineCensus(value)*pointsize+12);
cristy3ed852e2009-09-05 21:47:34 +00001623 if (page == 1)
1624 {
1625 /*
1626 Output Postscript header.
1627 */
1628 if (LocaleCompare(image_info->magick,"PS") == 0)
cristy151b66d2015-04-15 10:50:31 +00001629 (void) CopyMagickString(buffer,"%!PS-Adobe-3.0\n",MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00001630 else
1631 (void) CopyMagickString(buffer,"%!PS-Adobe-3.0 EPSF-3.0\n",
cristy151b66d2015-04-15 10:50:31 +00001632 MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00001633 (void) WriteBlobString(image,buffer);
1634 (void) WriteBlobString(image,"%%Creator: (ImageMagick)\n");
cristy151b66d2015-04-15 10:50:31 +00001635 (void) FormatLocaleString(buffer,MagickPathExtent,"%%%%Title: (%s)\n",
cristy3ed852e2009-09-05 21:47:34 +00001636 image->filename);
1637 (void) WriteBlobString(image,buffer);
1638 timer=time((time_t *) NULL);
cristy151b66d2015-04-15 10:50:31 +00001639 (void) FormatMagickTime(timer,MagickPathExtent,date);
1640 (void) FormatLocaleString(buffer,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +00001641 "%%%%CreationDate: (%s)\n",date);
1642 (void) WriteBlobString(image,buffer);
1643 bounds.x1=(double) geometry.x;
1644 bounds.y1=(double) geometry.y;
1645 bounds.x2=(double) geometry.x+scale.x;
1646 bounds.y2=(double) geometry.y+(geometry.height+text_size);
1647 if ((image_info->adjoin != MagickFalse) &&
1648 (GetNextImageInList(image) != (Image *) NULL))
1649 (void) CopyMagickString(buffer,"%%%%BoundingBox: (atend)\n",
cristy151b66d2015-04-15 10:50:31 +00001650 MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00001651 else
1652 {
cristy151b66d2015-04-15 10:50:31 +00001653 (void) FormatLocaleString(buffer,MagickPathExtent,
cristye8c25f92010-06-03 00:53:06 +00001654 "%%%%BoundingBox: %.20g %.20g %.20g %.20g\n",ceil(bounds.x1-0.5),
cristy8071c472012-09-24 12:41:06 +00001655 ceil(bounds.y1-0.5),floor(bounds.x2+0.5),floor(bounds.y2+0.5));
cristy3ed852e2009-09-05 21:47:34 +00001656 (void) WriteBlobString(image,buffer);
cristy151b66d2015-04-15 10:50:31 +00001657 (void) FormatLocaleString(buffer,MagickPathExtent,
cristye7f51092010-01-17 00:39:37 +00001658 "%%%%HiResBoundingBox: %g %g %g %g\n",bounds.x1,
cristy8cd5b312010-01-07 01:10:24 +00001659 bounds.y1,bounds.x2,bounds.y2);
cristy3ed852e2009-09-05 21:47:34 +00001660 }
1661 (void) WriteBlobString(image,buffer);
1662 profile=GetImageProfile(image,"8bim");
1663 if (profile != (StringInfo *) NULL)
1664 {
1665 /*
1666 Embed Photoshop profile.
1667 */
cristy151b66d2015-04-15 10:50:31 +00001668 (void) FormatLocaleString(buffer,MagickPathExtent,
cristye8c25f92010-06-03 00:53:06 +00001669 "%%BeginPhotoshop: %.20g",(double) GetStringInfoLength(profile));
cristy3ed852e2009-09-05 21:47:34 +00001670 (void) WriteBlobString(image,buffer);
cristybb503372010-05-27 20:51:26 +00001671 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
cristy3ed852e2009-09-05 21:47:34 +00001672 {
1673 if ((i % 32) == 0)
1674 (void) WriteBlobString(image,"\n% ");
cristy151b66d2015-04-15 10:50:31 +00001675 (void) FormatLocaleString(buffer,MagickPathExtent,"%02X",
cristy3ed852e2009-09-05 21:47:34 +00001676 (unsigned int) (GetStringInfoDatum(profile)[i] & 0xff));
1677 (void) WriteBlobString(image,buffer);
1678 }
1679 (void) WriteBlobString(image,"\n%EndPhotoshop\n");
1680 }
1681 profile=GetImageProfile(image,"xmp");
dirk93b02b72013-11-16 16:03:36 +00001682DisableMSCWarning(4127)
cristy2b2eb912010-03-03 14:56:40 +00001683 if (0 && (profile != (StringInfo *) NULL))
dirk93b02b72013-11-16 16:03:36 +00001684RestoreMSCWarning
cristy3ed852e2009-09-05 21:47:34 +00001685 {
1686 /*
1687 Embed XML profile.
1688 */
1689 (void) WriteBlobString(image,"\n%begin_xml_code\n");
cristy151b66d2015-04-15 10:50:31 +00001690 (void) FormatLocaleString(buffer,MagickPathExtent,
cristye8c25f92010-06-03 00:53:06 +00001691 "\n%%begin_xml_packet: %.20g\n",(double)
cristyeec18db2010-03-03 21:15:45 +00001692 GetStringInfoLength(profile));
cristy2b2eb912010-03-03 14:56:40 +00001693 (void) WriteBlobString(image,buffer);
cristybb503372010-05-27 20:51:26 +00001694 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
cristy3ed852e2009-09-05 21:47:34 +00001695 (void) WriteBlobByte(image,GetStringInfoDatum(profile)[i]);
cristy2b2eb912010-03-03 14:56:40 +00001696 (void) WriteBlobString(image,"\n%end_xml_packet\n%end_xml_code\n");
cristy3ed852e2009-09-05 21:47:34 +00001697 }
cristyd15e6592011-10-15 00:13:06 +00001698 value=GetImageProperty(image,"label",exception);
cristy3ed852e2009-09-05 21:47:34 +00001699 if (value != (const char *) NULL)
1700 (void) WriteBlobString(image,
1701 "%%DocumentNeededResources: font Times-Roman\n");
1702 (void) WriteBlobString(image,"%%DocumentData: Clean7Bit\n");
1703 (void) WriteBlobString(image,"%%LanguageLevel: 1\n");
1704 if (LocaleCompare(image_info->magick,"PS") != 0)
1705 (void) WriteBlobString(image,"%%Pages: 1\n");
1706 else
1707 {
1708 /*
1709 Compute the number of pages.
1710 */
1711 (void) WriteBlobString(image,"%%Orientation: Portrait\n");
1712 (void) WriteBlobString(image,"%%PageOrder: Ascend\n");
cristy151b66d2015-04-15 10:50:31 +00001713 (void) FormatLocaleString(buffer,MagickPathExtent,"%%%%Pages: %.20g\n",
cristye8c25f92010-06-03 00:53:06 +00001714 image_info->adjoin != MagickFalse ? (double)
1715 GetImageListLength(image) : 1.0);
cristy3ed852e2009-09-05 21:47:34 +00001716 (void) WriteBlobString(image,buffer);
1717 }
1718 (void) WriteBlobString(image,"%%EndComments\n");
1719 (void) WriteBlobString(image,"\n%%BeginDefaults\n");
1720 (void) WriteBlobString(image,"%%EndDefaults\n\n");
1721 if ((LocaleCompare(image_info->magick,"EPI") == 0) ||
1722 (LocaleCompare(image_info->magick,"EPSI") == 0) ||
1723 (LocaleCompare(image_info->magick,"EPT") == 0))
1724 {
1725 Image
1726 *preview_image;
1727
cristy3ed852e2009-09-05 21:47:34 +00001728 Quantum
1729 pixel;
1730
cristybb503372010-05-27 20:51:26 +00001731 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001732 x;
1733
cristy802d3642011-04-27 02:02:41 +00001734 ssize_t
1735 y;
1736
cristy3ed852e2009-09-05 21:47:34 +00001737 /*
1738 Create preview image.
1739 */
cristy1e178e72011-08-28 19:44:34 +00001740 preview_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00001741 if (preview_image == (Image *) NULL)
1742 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1743 /*
1744 Dump image as bitmap.
1745 */
cristy151b66d2015-04-15 10:50:31 +00001746 (void) FormatLocaleString(buffer,MagickPathExtent,
cristye8c25f92010-06-03 00:53:06 +00001747 "%%%%BeginPreview: %.20g %.20g %.20g %.20g\n%% ",(double)
1748 preview_image->columns,(double) preview_image->rows,1.0,
1749 (double) ((((preview_image->columns+7) >> 3)*preview_image->rows+
1750 35)/36));
cristy3ed852e2009-09-05 21:47:34 +00001751 (void) WriteBlobString(image,buffer);
1752 q=pixels;
cristybb503372010-05-27 20:51:26 +00001753 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001754 {
1755 p=GetVirtualPixels(preview_image,0,y,preview_image->columns,1,
cristy1e178e72011-08-28 19:44:34 +00001756 exception);
cristy4c08aed2011-07-01 19:47:50 +00001757 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001758 break;
cristy3ed852e2009-09-05 21:47:34 +00001759 bit=0;
1760 byte=0;
cristybb503372010-05-27 20:51:26 +00001761 for (x=0; x < (ssize_t) preview_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001762 {
1763 byte<<=1;
cristyd0323222013-04-07 16:13:21 +00001764 pixel=ClampToQuantum(GetPixelLuma(preview_image,p));
cristy3ed852e2009-09-05 21:47:34 +00001765 if (pixel >= (Quantum) (QuantumRange/2))
1766 byte|=0x01;
1767 bit++;
1768 if (bit == 8)
1769 {
1770 q=PopHexPixel(hex_digits,byte,q);
1771 if ((q-pixels+8) >= 80)
1772 {
1773 *q++='\n';
1774 (void) WriteBlob(image,q-pixels,pixels);
1775 q=pixels;
1776 (void) WriteBlobString(image,"% ");
1777 };
1778 bit=0;
1779 byte=0;
1780 }
1781 }
1782 if (bit != 0)
1783 {
1784 byte<<=(8-bit);
1785 q=PopHexPixel(hex_digits,byte,q);
1786 if ((q-pixels+8) >= 80)
1787 {
1788 *q++='\n';
1789 (void) WriteBlob(image,q-pixels,pixels);
1790 q=pixels;
1791 (void) WriteBlobString(image,"% ");
1792 };
1793 };
1794 }
1795 if (q != pixels)
1796 {
1797 *q++='\n';
1798 (void) WriteBlob(image,q-pixels,pixels);
1799 }
1800 (void) WriteBlobString(image,"\n%%EndPreview\n");
1801 preview_image=DestroyImage(preview_image);
1802 }
1803 /*
1804 Output Postscript commands.
1805 */
1806 for (s=PostscriptProlog; *s != (char *) NULL; s++)
1807 {
cristy151b66d2015-04-15 10:50:31 +00001808 (void) FormatLocaleString(buffer,MagickPathExtent,"%s\n",*s);
cristy3ed852e2009-09-05 21:47:34 +00001809 (void) WriteBlobString(image,buffer);
1810 }
cristyd15e6592011-10-15 00:13:06 +00001811 value=GetImageProperty(image,"label",exception);
cristy3ed852e2009-09-05 21:47:34 +00001812 if (value != (const char *) NULL)
cristybb503372010-05-27 20:51:26 +00001813 for (j=(ssize_t) MultilineCensus(value)-1; j >= 0; j--)
cristy3ed852e2009-09-05 21:47:34 +00001814 {
1815 (void) WriteBlobString(image," /label 512 string def\n");
1816 (void) WriteBlobString(image," currentfile label readline pop\n");
cristy151b66d2015-04-15 10:50:31 +00001817 (void) FormatLocaleString(buffer,MagickPathExtent,
cristye7f51092010-01-17 00:39:37 +00001818 " 0 y %g add moveto label show pop\n",j*pointsize+12);
cristy3ed852e2009-09-05 21:47:34 +00001819 (void) WriteBlobString(image,buffer);
1820 }
1821 for (s=PostscriptEpilog; *s != (char *) NULL; s++)
1822 {
cristy151b66d2015-04-15 10:50:31 +00001823 (void) FormatLocaleString(buffer,MagickPathExtent,"%s\n",*s);
cristy3ed852e2009-09-05 21:47:34 +00001824 (void) WriteBlobString(image,buffer);
1825 }
1826 if (LocaleCompare(image_info->magick,"PS") == 0)
1827 (void) WriteBlobString(image," showpage\n");
1828 (void) WriteBlobString(image,"} bind def\n");
1829 (void) WriteBlobString(image,"%%EndProlog\n");
1830 }
cristy151b66d2015-04-15 10:50:31 +00001831 (void) FormatLocaleString(buffer,MagickPathExtent,"%%%%Page: 1 %.20g\n",
cristye8c25f92010-06-03 00:53:06 +00001832 (double) (page++));
cristy3ed852e2009-09-05 21:47:34 +00001833 (void) WriteBlobString(image,buffer);
cristy151b66d2015-04-15 10:50:31 +00001834 (void) FormatLocaleString(buffer,MagickPathExtent,
cristye8c25f92010-06-03 00:53:06 +00001835 "%%%%PageBoundingBox: %.20g %.20g %.20g %.20g\n",(double) geometry.x,
1836 (double) geometry.y,geometry.x+(double) geometry.width,geometry.y+(double)
cristyf2faecf2010-05-28 19:19:36 +00001837 (geometry.height+text_size));
cristy3ed852e2009-09-05 21:47:34 +00001838 (void) WriteBlobString(image,buffer);
1839 if ((double) geometry.x < bounds.x1)
1840 bounds.x1=(double) geometry.x;
1841 if ((double) geometry.y < bounds.y1)
1842 bounds.y1=(double) geometry.y;
1843 if ((double) (geometry.x+geometry.width-1) > bounds.x2)
1844 bounds.x2=(double) geometry.x+geometry.width-1;
1845 if ((double) (geometry.y+(geometry.height+text_size)-1) > bounds.y2)
1846 bounds.y2=(double) geometry.y+(geometry.height+text_size)-1;
cristyd15e6592011-10-15 00:13:06 +00001847 value=GetImageProperty(image,"label",exception);
cristy3ed852e2009-09-05 21:47:34 +00001848 if (value != (const char *) NULL)
1849 (void) WriteBlobString(image,"%%%%PageResources: font Times-Roman\n");
1850 if (LocaleCompare(image_info->magick,"PS") != 0)
1851 (void) WriteBlobString(image,"userdict begin\n");
1852 (void) WriteBlobString(image,"DisplayImage\n");
1853 /*
1854 Output image data.
1855 */
cristy151b66d2015-04-15 10:50:31 +00001856 (void) FormatLocaleString(buffer,MagickPathExtent,"%.20g %.20g\n%g %g\n%g\n",
cristye8c25f92010-06-03 00:53:06 +00001857 (double) geometry.x,(double) geometry.y,scale.x,scale.y,pointsize);
cristy3ed852e2009-09-05 21:47:34 +00001858 (void) WriteBlobString(image,buffer);
1859 labels=(char **) NULL;
cristyd15e6592011-10-15 00:13:06 +00001860 value=GetImageProperty(image,"label",exception);
cristy3ed852e2009-09-05 21:47:34 +00001861 if (value != (const char *) NULL)
1862 labels=StringToList(value);
1863 if (labels != (char **) NULL)
1864 {
1865 for (i=0; labels[i] != (char *) NULL; i++)
1866 {
cristy151b66d2015-04-15 10:50:31 +00001867 (void) FormatLocaleString(buffer,MagickPathExtent,"%s \n",
cristy3ed852e2009-09-05 21:47:34 +00001868 labels[i]);
1869 (void) WriteBlobString(image,buffer);
1870 labels[i]=DestroyString(labels[i]);
1871 }
1872 labels=(char **) RelinquishMagickMemory(labels);
1873 }
1874 (void) ResetMagickMemory(&pixel,0,sizeof(pixel));
cristyd0323222013-04-07 16:13:21 +00001875 pixel.alpha=(MagickRealType) TransparentAlpha;
cristy4c08aed2011-07-01 19:47:50 +00001876 index=0;
cristy3ed852e2009-09-05 21:47:34 +00001877 x=0;
1878 if ((image_info->type != TrueColorType) &&
dirkf1d85482015-04-06 00:36:00 +00001879 (SetImageGray(image,exception) != MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +00001880 {
dirkf1d85482015-04-06 00:36:00 +00001881 if (SetImageMonochrome(image,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001882 {
1883 Quantum
1884 pixel;
1885
1886 /*
1887 Dump image as grayscale.
1888 */
cristy151b66d2015-04-15 10:50:31 +00001889 (void) FormatLocaleString(buffer,MagickPathExtent,
cristye8c25f92010-06-03 00:53:06 +00001890 "%.20g %.20g\n1\n1\n1\n8\n",(double) image->columns,(double)
1891 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001892 (void) WriteBlobString(image,buffer);
1893 q=pixels;
cristybb503372010-05-27 20:51:26 +00001894 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001895 {
cristy1e178e72011-08-28 19:44:34 +00001896 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001897 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001898 break;
cristybb503372010-05-27 20:51:26 +00001899 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001900 {
cristyd0323222013-04-07 16:13:21 +00001901 pixel=(Quantum) ScaleQuantumToChar(ClampToQuantum(GetPixelLuma(
1902 image,p)));
cristya97426c2011-02-04 01:41:27 +00001903 q=PopHexPixel(hex_digits,(size_t) pixel,q);
cristy3ed852e2009-09-05 21:47:34 +00001904 if ((q-pixels+8) >= 80)
1905 {
1906 *q++='\n';
1907 (void) WriteBlob(image,q-pixels,pixels);
1908 q=pixels;
1909 }
cristyed231572011-07-14 02:18:59 +00001910 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001911 }
1912 if (image->previous == (Image *) NULL)
1913 {
cristya97426c2011-02-04 01:41:27 +00001914 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType)
1915 y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001916 if (status == MagickFalse)
1917 break;
1918 }
1919 }
1920 if (q != pixels)
1921 {
1922 *q++='\n';
1923 (void) WriteBlob(image,q-pixels,pixels);
1924 }
1925 }
1926 else
1927 {
cristybb503372010-05-27 20:51:26 +00001928 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001929 y;
1930
1931 Quantum
1932 pixel;
1933
1934 /*
1935 Dump image as bitmap.
1936 */
cristy151b66d2015-04-15 10:50:31 +00001937 (void) FormatLocaleString(buffer,MagickPathExtent,
cristye8c25f92010-06-03 00:53:06 +00001938 "%.20g %.20g\n1\n1\n1\n1\n",(double) image->columns,(double)
1939 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001940 (void) WriteBlobString(image,buffer);
1941 q=pixels;
cristybb503372010-05-27 20:51:26 +00001942 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001943 {
cristy1e178e72011-08-28 19:44:34 +00001944 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001945 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001946 break;
cristy3ed852e2009-09-05 21:47:34 +00001947 bit=0;
1948 byte=0;
cristybb503372010-05-27 20:51:26 +00001949 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001950 {
1951 byte<<=1;
cristyd0323222013-04-07 16:13:21 +00001952 pixel=ClampToQuantum(GetPixelLuma(image,p));
cristy3ed852e2009-09-05 21:47:34 +00001953 if (pixel >= (Quantum) (QuantumRange/2))
1954 byte|=0x01;
1955 bit++;
1956 if (bit == 8)
1957 {
1958 q=PopHexPixel(hex_digits,byte,q);
1959 if ((q-pixels+2) >= 80)
1960 {
1961 *q++='\n';
1962 (void) WriteBlob(image,q-pixels,pixels);
1963 q=pixels;
1964 };
1965 bit=0;
1966 byte=0;
1967 }
cristyed231572011-07-14 02:18:59 +00001968 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001969 }
1970 if (bit != 0)
1971 {
1972 byte<<=(8-bit);
1973 q=PopHexPixel(hex_digits,byte,q);
1974 if ((q-pixels+2) >= 80)
1975 {
1976 *q++='\n';
1977 (void) WriteBlob(image,q-pixels,pixels);
1978 q=pixels;
1979 }
1980 };
1981 if (image->previous == (Image *) NULL)
1982 {
cristy6cde06a2011-11-24 00:08:43 +00001983 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType)
cristy802d3642011-04-27 02:02:41 +00001984 y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001985 if (status == MagickFalse)
1986 break;
1987 }
1988 }
1989 if (q != pixels)
1990 {
1991 *q++='\n';
1992 (void) WriteBlob(image,q-pixels,pixels);
1993 }
1994 }
1995 }
1996 else
1997 if ((image->storage_class == DirectClass) ||
cristy17f11b02014-12-20 19:37:04 +00001998 (image->colors > 256) || (image->alpha_trait != UndefinedPixelTrait))
cristy3ed852e2009-09-05 21:47:34 +00001999 {
2000 /*
2001 Dump DirectClass image.
2002 */
cristy151b66d2015-04-15 10:50:31 +00002003 (void) FormatLocaleString(buffer,MagickPathExtent,"%.20g %.20g\n0\n%d\n",
cristye8c25f92010-06-03 00:53:06 +00002004 (double) image->columns,(double) image->rows,
cristyaf8d3912014-02-21 14:50:33 +00002005 compression == RLECompression ? 1 : 0);
cristy3ed852e2009-09-05 21:47:34 +00002006 (void) WriteBlobString(image,buffer);
cristyaf8d3912014-02-21 14:50:33 +00002007 switch (compression)
cristy3ed852e2009-09-05 21:47:34 +00002008 {
2009 case RLECompression:
2010 {
2011 /*
2012 Dump runlength-encoded DirectColor packets.
2013 */
2014 q=pixels;
cristybb503372010-05-27 20:51:26 +00002015 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002016 {
cristy1e178e72011-08-28 19:44:34 +00002017 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002018 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002019 break;
cristy101ab702011-10-13 13:06:32 +00002020 GetPixelInfoPixel(image,p,&pixel);
cristy3ed852e2009-09-05 21:47:34 +00002021 length=255;
cristybb503372010-05-27 20:51:26 +00002022 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002023 {
cristyd0323222013-04-07 16:13:21 +00002024 if ((GetPixelRed(image,p) == ClampToQuantum(pixel.red)) &&
2025 (GetPixelGreen(image,p) == ClampToQuantum(pixel.green)) &&
2026 (GetPixelBlue(image,p) == ClampToQuantum(pixel.blue)) &&
2027 (GetPixelAlpha(image,p) == ClampToQuantum(pixel.alpha)) &&
cristy802d3642011-04-27 02:02:41 +00002028 (length < 255) && (x < (ssize_t) (image->columns-1)))
cristy3ed852e2009-09-05 21:47:34 +00002029 length++;
2030 else
2031 {
2032 if (x > 0)
2033 {
2034 WriteRunlengthPacket(image,pixel,length,p);
2035 if ((q-pixels+10) >= 80)
2036 {
2037 *q++='\n';
2038 (void) WriteBlob(image,q-pixels,pixels);
2039 q=pixels;
2040 }
2041 }
2042 length=0;
2043 }
cristy101ab702011-10-13 13:06:32 +00002044 GetPixelInfoPixel(image,p,&pixel);
cristyed231572011-07-14 02:18:59 +00002045 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002046 }
2047 WriteRunlengthPacket(image,pixel,length,p);
2048 if ((q-pixels+10) >= 80)
2049 {
2050 *q++='\n';
2051 (void) WriteBlob(image,q-pixels,pixels);
2052 q=pixels;
2053 }
2054 if (image->previous == (Image *) NULL)
2055 {
cristy802d3642011-04-27 02:02:41 +00002056 status=SetImageProgress(image,SaveImageTag,
2057 (MagickOffsetType) y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002058 if (status == MagickFalse)
2059 break;
2060 }
2061 }
2062 if (q != pixels)
2063 {
2064 *q++='\n';
2065 (void) WriteBlob(image,q-pixels,pixels);
2066 }
2067 break;
2068 }
2069 case NoCompression:
2070 default:
2071 {
2072 /*
2073 Dump uncompressed DirectColor packets.
2074 */
2075 q=pixels;
cristybb503372010-05-27 20:51:26 +00002076 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002077 {
cristy1e178e72011-08-28 19:44:34 +00002078 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002079 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002080 break;
cristybb503372010-05-27 20:51:26 +00002081 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002082 {
cristy17f11b02014-12-20 19:37:04 +00002083 if ((image->alpha_trait != UndefinedPixelTrait) &&
cristy4c08aed2011-07-01 19:47:50 +00002084 (GetPixelAlpha(image,p) == (Quantum) TransparentAlpha))
cristy3ed852e2009-09-05 21:47:34 +00002085 {
2086 q=PopHexPixel(hex_digits,0xff,q);
2087 q=PopHexPixel(hex_digits,0xff,q);
2088 q=PopHexPixel(hex_digits,0xff,q);
2089 }
2090 else
2091 {
cristy802d3642011-04-27 02:02:41 +00002092 q=PopHexPixel(hex_digits,ScaleQuantumToChar(
cristy4c08aed2011-07-01 19:47:50 +00002093 GetPixelRed(image,p)),q);
cristy802d3642011-04-27 02:02:41 +00002094 q=PopHexPixel(hex_digits,ScaleQuantumToChar(
cristy4c08aed2011-07-01 19:47:50 +00002095 GetPixelGreen(image,p)),q);
cristy802d3642011-04-27 02:02:41 +00002096 q=PopHexPixel(hex_digits,ScaleQuantumToChar(
cristy4c08aed2011-07-01 19:47:50 +00002097 GetPixelBlue(image,p)),q);
cristy3ed852e2009-09-05 21:47:34 +00002098 }
2099 if ((q-pixels+6) >= 80)
2100 {
2101 *q++='\n';
2102 (void) WriteBlob(image,q-pixels,pixels);
2103 q=pixels;
2104 }
cristyed231572011-07-14 02:18:59 +00002105 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002106 }
2107 if (image->previous == (Image *) NULL)
2108 {
cristy802d3642011-04-27 02:02:41 +00002109 status=SetImageProgress(image,SaveImageTag,
2110 (MagickOffsetType) y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002111 if (status == MagickFalse)
2112 break;
2113 }
2114 }
2115 if (q != pixels)
2116 {
2117 *q++='\n';
2118 (void) WriteBlob(image,q-pixels,pixels);
2119 }
2120 break;
2121 }
2122 }
2123 (void) WriteBlobByte(image,'\n');
2124 }
2125 else
2126 {
2127 /*
2128 Dump PseudoClass image.
2129 */
cristy151b66d2015-04-15 10:50:31 +00002130 (void) FormatLocaleString(buffer,MagickPathExtent,
cristye8c25f92010-06-03 00:53:06 +00002131 "%.20g %.20g\n%d\n%d\n0\n",(double) image->columns,(double)
2132 image->rows,image->storage_class == PseudoClass ? 1 : 0,
cristyaf8d3912014-02-21 14:50:33 +00002133 compression == RLECompression ? 1 : 0);
cristy3ed852e2009-09-05 21:47:34 +00002134 (void) WriteBlobString(image,buffer);
2135 /*
2136 Dump number of colors and colormap.
2137 */
cristy151b66d2015-04-15 10:50:31 +00002138 (void) FormatLocaleString(buffer,MagickPathExtent,"%.20g\n",(double)
cristyf2faecf2010-05-28 19:19:36 +00002139 image->colors);
cristy3ed852e2009-09-05 21:47:34 +00002140 (void) WriteBlobString(image,buffer);
cristybb503372010-05-27 20:51:26 +00002141 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002142 {
cristy151b66d2015-04-15 10:50:31 +00002143 (void) FormatLocaleString(buffer,MagickPathExtent,"%02X%02X%02X\n",
cristyd0323222013-04-07 16:13:21 +00002144 ScaleQuantumToChar(ClampToQuantum(image->colormap[i].red)),
2145 ScaleQuantumToChar(ClampToQuantum(image->colormap[i].green)),
2146 ScaleQuantumToChar(ClampToQuantum(image->colormap[i].blue)));
cristy3ed852e2009-09-05 21:47:34 +00002147 (void) WriteBlobString(image,buffer);
2148 }
cristyaf8d3912014-02-21 14:50:33 +00002149 switch (compression)
cristy3ed852e2009-09-05 21:47:34 +00002150 {
2151 case RLECompression:
2152 {
2153 /*
2154 Dump runlength-encoded PseudoColor packets.
2155 */
2156 q=pixels;
cristybb503372010-05-27 20:51:26 +00002157 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002158 {
cristy1e178e72011-08-28 19:44:34 +00002159 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002160 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002161 break;
cristy4c08aed2011-07-01 19:47:50 +00002162 index=GetPixelIndex(image,p);
cristy3ed852e2009-09-05 21:47:34 +00002163 length=255;
cristybb503372010-05-27 20:51:26 +00002164 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002165 {
cristy4c08aed2011-07-01 19:47:50 +00002166 if ((index == GetPixelIndex(image,p)) &&
cristy802d3642011-04-27 02:02:41 +00002167 (length < 255) && (x < ((ssize_t) image->columns-1)))
cristy3ed852e2009-09-05 21:47:34 +00002168 length++;
2169 else
2170 {
2171 if (x > 0)
2172 {
cristya97426c2011-02-04 01:41:27 +00002173 q=PopHexPixel(hex_digits,(size_t) index,q);
cristybb503372010-05-27 20:51:26 +00002174 q=PopHexPixel(hex_digits,(size_t)
cristy3ed852e2009-09-05 21:47:34 +00002175 MagickMin(length,0xff),q);
2176 i++;
2177 if ((q-pixels+6) >= 80)
2178 {
2179 *q++='\n';
2180 (void) WriteBlob(image,q-pixels,pixels);
2181 q=pixels;
2182 }
2183 }
2184 length=0;
2185 }
cristy4c08aed2011-07-01 19:47:50 +00002186 index=GetPixelIndex(image,p);
cristyd0323222013-04-07 16:13:21 +00002187 pixel.red=(MagickRealType) GetPixelRed(image,p);
2188 pixel.green=(MagickRealType) GetPixelGreen(image,p);
2189 pixel.blue=(MagickRealType) GetPixelBlue(image,p);
2190 pixel.alpha=(MagickRealType) GetPixelAlpha(image,p);
cristyed231572011-07-14 02:18:59 +00002191 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002192 }
cristya97426c2011-02-04 01:41:27 +00002193 q=PopHexPixel(hex_digits,(size_t) index,q);
cristybb503372010-05-27 20:51:26 +00002194 q=PopHexPixel(hex_digits,(size_t)
cristy3ed852e2009-09-05 21:47:34 +00002195 MagickMin(length,0xff),q);
2196 if (image->previous == (Image *) NULL)
2197 {
cristya97426c2011-02-04 01:41:27 +00002198 status=SetImageProgress(image,SaveImageTag,
2199 (MagickOffsetType) y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002200 if (status == MagickFalse)
2201 break;
2202 }
2203 }
2204 if (q != pixels)
2205 {
2206 *q++='\n';
2207 (void) WriteBlob(image,q-pixels,pixels);
2208 }
2209 break;
2210 }
2211 case NoCompression:
2212 default:
2213 {
2214 /*
2215 Dump uncompressed PseudoColor packets.
2216 */
2217 q=pixels;
cristybb503372010-05-27 20:51:26 +00002218 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002219 {
cristy1e178e72011-08-28 19:44:34 +00002220 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002221 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002222 break;
cristybb503372010-05-27 20:51:26 +00002223 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002224 {
cristy4c08aed2011-07-01 19:47:50 +00002225 q=PopHexPixel(hex_digits,(size_t) GetPixelIndex(image,p),q);
cristy3ed852e2009-09-05 21:47:34 +00002226 if ((q-pixels+4) >= 80)
2227 {
2228 *q++='\n';
2229 (void) WriteBlob(image,q-pixels,pixels);
2230 q=pixels;
2231 }
cristyed231572011-07-14 02:18:59 +00002232 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002233 }
2234 if (image->previous == (Image *) NULL)
2235 {
cristya97426c2011-02-04 01:41:27 +00002236 status=SetImageProgress(image,SaveImageTag,
2237 (MagickOffsetType) y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002238 if (status == MagickFalse)
2239 break;
2240 }
2241 }
2242 if (q != pixels)
2243 {
2244 *q++='\n';
2245 (void) WriteBlob(image,q-pixels,pixels);
2246 }
2247 break;
2248 }
2249 }
2250 (void) WriteBlobByte(image,'\n');
2251 }
2252 if (LocaleCompare(image_info->magick,"PS") != 0)
2253 (void) WriteBlobString(image,"end\n");
2254 (void) WriteBlobString(image,"%%PageTrailer\n");
2255 if (GetNextImageInList(image) == (Image *) NULL)
2256 break;
2257 image=SyncNextImageInList(image);
2258 status=SetImageProgress(image,SaveImagesTag,scene++,
2259 GetImageListLength(image));
2260 if (status == MagickFalse)
2261 break;
2262 } while (image_info->adjoin != MagickFalse);
2263 (void) WriteBlobString(image,"%%Trailer\n");
2264 if (page > 2)
2265 {
cristy151b66d2015-04-15 10:50:31 +00002266 (void) FormatLocaleString(buffer,MagickPathExtent,
cristye8c25f92010-06-03 00:53:06 +00002267 "%%%%BoundingBox: %.20g %.20g %.20g %.20g\n",ceil(bounds.x1-0.5),
cristye4cc1a12012-09-03 23:04:46 +00002268 ceil(bounds.y1-0.5),floor(bounds.x2-0.5),floor(bounds.y2-0.5));
cristy3ed852e2009-09-05 21:47:34 +00002269 (void) WriteBlobString(image,buffer);
cristy151b66d2015-04-15 10:50:31 +00002270 (void) FormatLocaleString(buffer,MagickPathExtent,
cristye4cc1a12012-09-03 23:04:46 +00002271 "%%%%HiResBoundingBox: %g %g %g %g\n",bounds.x1,bounds.y1,bounds.x2,
2272 bounds.y2);
cristy3ed852e2009-09-05 21:47:34 +00002273 (void) WriteBlobString(image,buffer);
2274 }
2275 (void) WriteBlobString(image,"%%EOF\n");
2276 (void) CloseBlob(image);
2277 return(MagickTrue);
2278}