blob: 95834c3797896770e5537b8c24c6ba3f681e62d9 [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% %
Cristyf19d4142017-04-24 11:34:30 -040026% https://www.imagemagick.org/script/license.php %
cristy3ed852e2009-09-05 21:47:34 +000027% %
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 */
Cristy60877182017-08-11 15:10:52 -0400632 profile=AcquireStringInfo(MagickPathExtent);
633 datum=GetStringInfoDatum(profile);
cristy3ed852e2009-09-05 21:47:34 +0000634 for (i=0; (c=ProfileInteger(image,hex_digits)) != EOF; i++)
635 {
Cristy60877182017-08-11 15:10:52 -0400636 if (i >= GetStringInfoLength(profile))
637 {
638 SetStringInfoLength(profile,(size_t) i << 1);
639 datum=GetStringInfoDatum(profile);
640 }
cristy3ed852e2009-09-05 21:47:34 +0000641 datum[i]=(unsigned char) c;
642 }
Cristy60877182017-08-11 15:10:52 -0400643 SetStringInfoLength(profile,(size_t) i+1);
cristyd15e6592011-10-15 00:13:06 +0000644 (void) SetImageProfile(image,"icc",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +0000645 profile=DestroyStringInfo(profile);
646 continue;
647 }
648 if (LocaleNCompare(PhotoshopProfile,command,strlen(PhotoshopProfile)) == 0)
649 {
650 unsigned char
dirkdb6b52c2015-11-25 23:44:01 +0100651 *q;
cristy3ed852e2009-09-05 21:47:34 +0000652
653 /*
654 Read Photoshop profile.
655 */
656 count=(ssize_t) sscanf(command,PhotoshopProfile " %lu",&extent);
657 if (count != 1)
658 continue;
659 length=extent;
cristy8723e4b2011-09-01 13:11:19 +0000660 profile=BlobToStringInfo((const void *) NULL,length);
cristyb231d582014-11-28 17:13:51 +0000661 if (profile != (StringInfo *) NULL)
662 {
dirkdb6b52c2015-11-25 23:44:01 +0100663 q=GetStringInfoDatum(profile);
cristyb231d582014-11-28 17:13:51 +0000664 for (i=0; i < (ssize_t) length; i++)
dirkdb6b52c2015-11-25 23:44:01 +0100665 *q++=(unsigned char) ProfileInteger(image,hex_digits);
cristyb231d582014-11-28 17:13:51 +0000666 (void) SetImageProfile(image,"8bim",profile,exception);
667 profile=DestroyStringInfo(profile);
668 }
cristy3ed852e2009-09-05 21:47:34 +0000669 continue;
670 }
671 if (LocaleNCompare(BeginXMPPacket,command,strlen(BeginXMPPacket)) == 0)
672 {
cristy3ed852e2009-09-05 21:47:34 +0000673 /*
674 Read XMP profile.
675 */
676 p=command;
677 profile=StringToStringInfo(command);
678 for (i=GetStringInfoLength(profile)-1; c != EOF; i++)
679 {
680 SetStringInfoLength(profile,i+1);
681 c=ReadBlobByte(image);
682 GetStringInfoDatum(profile)[i]=(unsigned char) c;
683 *p++=(char) c;
684 if ((strchr("\n\r%",c) == (char *) NULL) &&
cristy151b66d2015-04-15 10:50:31 +0000685 ((size_t) (p-command) < (MagickPathExtent-1)))
cristy3ed852e2009-09-05 21:47:34 +0000686 continue;
687 *p='\0';
688 p=command;
689 if (LocaleNCompare(EndXMPPacket,command,strlen(EndXMPPacket)) == 0)
690 break;
691 }
692 SetStringInfoLength(profile,i);
cristyd15e6592011-10-15 00:13:06 +0000693 (void) SetImageProfile(image,"xmp",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +0000694 profile=DestroyStringInfo(profile);
695 continue;
696 }
697 /*
698 Is this a CMYK document?
699 */
700 length=strlen(DocumentProcessColors);
701 if (LocaleNCompare(DocumentProcessColors,command,length) == 0)
702 {
703 if ((GlobExpression(command,"*Cyan*",MagickTrue) != MagickFalse) ||
704 (GlobExpression(command,"*Magenta*",MagickTrue) != MagickFalse) ||
705 (GlobExpression(command,"*Yellow*",MagickTrue) != MagickFalse))
706 cmyk=MagickTrue;
707 }
708 if (LocaleNCompare(CMYKCustomColor,command,strlen(CMYKCustomColor)) == 0)
709 cmyk=MagickTrue;
cristy01ca8922010-03-17 12:20:37 +0000710 if (LocaleNCompare(CMYKProcessColor,command,strlen(CMYKProcessColor)) == 0)
711 cmyk=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +0000712 length=strlen(DocumentCustomColors);
713 if ((LocaleNCompare(DocumentCustomColors,command,length) == 0) ||
714 (LocaleNCompare(CMYKCustomColor,command,strlen(CMYKCustomColor)) == 0) ||
715 (LocaleNCompare(SpotColor,command,strlen(SpotColor)) == 0))
716 {
717 char
cristy151b66d2015-04-15 10:50:31 +0000718 property[MagickPathExtent],
cristy3ed852e2009-09-05 21:47:34 +0000719 *value;
720
721 register char
dirkdb6b52c2015-11-25 23:44:01 +0100722 *q;
cristy3ed852e2009-09-05 21:47:34 +0000723
724 /*
725 Note spot names.
726 */
cristy151b66d2015-04-15 10:50:31 +0000727 (void) FormatLocaleString(property,MagickPathExtent,"ps:SpotColor-%.20g",
cristye8c25f92010-06-03 00:53:06 +0000728 (double) (spotcolor++));
dirkdb6b52c2015-11-25 23:44:01 +0100729 for (q=command; *q != '\0'; q++)
730 if (isspace((int) (unsigned char) *q) != 0)
cristy3ed852e2009-09-05 21:47:34 +0000731 break;
dirkdb6b52c2015-11-25 23:44:01 +0100732 value=AcquireString(q);
cristy3ed852e2009-09-05 21:47:34 +0000733 (void) SubstituteString(&value,"(","");
734 (void) SubstituteString(&value,")","");
735 (void) StripString(value);
cristyd15e6592011-10-15 00:13:06 +0000736 (void) SetImageProperty(image,property,value,exception);
cristy3ed852e2009-09-05 21:47:34 +0000737 value=DestroyString(value);
738 continue;
739 }
cristy6cde06a2011-11-24 00:08:43 +0000740 if (image_info->page != (char *) NULL)
741 continue;
cristy3ed852e2009-09-05 21:47:34 +0000742 /*
743 Note region defined by bounding box.
744 */
745 count=0;
cristy4b1c78f2011-11-11 15:09:30 +0000746 i=0;
cristy3ed852e2009-09-05 21:47:34 +0000747 if (LocaleNCompare(BoundingBox,command,strlen(BoundingBox)) == 0)
cristy3ed852e2009-09-05 21:47:34 +0000748 {
cristy4b1c78f2011-11-11 15:09:30 +0000749 count=(ssize_t) sscanf(command,BoundingBox " %lf %lf %lf %lf",
750 &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
751 i=2;
cristy3ed852e2009-09-05 21:47:34 +0000752 }
cristy4b1c78f2011-11-11 15:09:30 +0000753 if (LocaleNCompare(DocumentMedia,command,strlen(DocumentMedia)) == 0)
754 {
755 count=(ssize_t) sscanf(command,DocumentMedia " %lf %lf %lf %lf",
756 &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
757 i=1;
758 }
759 if (LocaleNCompare(HiResBoundingBox,command,strlen(HiResBoundingBox)) == 0)
760 {
761 count=(ssize_t) sscanf(command,HiResBoundingBox " %lf %lf %lf %lf",
762 &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
763 i=3;
764 }
765 if (LocaleNCompare(PageBoundingBox,command,strlen(PageBoundingBox)) == 0)
766 {
767 count=(ssize_t) sscanf(command,PageBoundingBox " %lf %lf %lf %lf",
768 &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
769 i=1;
770 }
771 if (LocaleNCompare(PageMedia,command,strlen(PageMedia)) == 0)
772 {
773 count=(ssize_t) sscanf(command,PageMedia " %lf %lf %lf %lf",
774 &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
775 i=1;
776 }
cristy3fac9ec2011-11-17 18:04:39 +0000777 if ((count != 4) || (i < (ssize_t) priority))
cristy4b1c78f2011-11-11 15:09:30 +0000778 continue;
cristyad29a8c2012-01-25 23:06:43 +0000779 if ((fabs(bounds.x2-bounds.x1) <= fabs(hires_bounds.x2-hires_bounds.x1)) ||
780 (fabs(bounds.y2-bounds.y1) <= fabs(hires_bounds.y2-hires_bounds.y1)))
cristye3c89012014-03-06 14:11:27 +0000781 if (i == (ssize_t) priority)
782 continue;
cristy4b1c78f2011-11-11 15:09:30 +0000783 hires_bounds=bounds;
784 priority=i;
cristy3ed852e2009-09-05 21:47:34 +0000785 }
cristy7b0fcf12011-11-24 00:24:07 +0000786 if ((fabs(hires_bounds.x2-hires_bounds.x1) >= MagickEpsilon) &&
cristy614bc082011-11-24 00:49:08 +0000787 (fabs(hires_bounds.y2-hires_bounds.y1) >= MagickEpsilon))
cristy4b1c78f2011-11-11 15:09:30 +0000788 {
789 /*
790 Set Postscript render geometry.
791 */
cristy151b66d2015-04-15 10:50:31 +0000792 (void) FormatLocaleString(geometry,MagickPathExtent,"%gx%g%+.15g%+.15g",
cristy4b1c78f2011-11-11 15:09:30 +0000793 hires_bounds.x2-hires_bounds.x1,hires_bounds.y2-hires_bounds.y1,
794 hires_bounds.x1,hires_bounds.y1);
795 (void) SetImageProperty(image,"ps:HiResBoundingBox",geometry,exception);
cristy6cde06a2011-11-24 00:08:43 +0000796 page.width=(size_t) ceil((double) ((hires_bounds.x2-hires_bounds.x1)*
cristy6502b392012-04-11 00:05:33 +0000797 resolution.x/delta.x)-0.5);
cristy6cde06a2011-11-24 00:08:43 +0000798 page.height=(size_t) ceil((double) ((hires_bounds.y2-hires_bounds.y1)*
cristy6502b392012-04-11 00:05:33 +0000799 resolution.y/delta.y)-0.5);
cristy4b1c78f2011-11-11 15:09:30 +0000800 }
dirka5eeacb2014-10-25 06:32:14 +0000801 fitPage=MagickFalse;
802 option=GetImageOption(image_info,"eps:fit-page");
803 if (option != (char *) NULL)
804 {
805 char
dirkdb6b52c2015-11-25 23:44:01 +0100806 *page_geometry;
dirka5eeacb2014-10-25 06:32:14 +0000807
dirkdb6b52c2015-11-25 23:44:01 +0100808 page_geometry=GetPageGeometry(option);
809 flags=ParseMetaGeometry(page_geometry,&page.x,&page.y,&page.width,
810 &page.height);
dirka5eeacb2014-10-25 06:32:14 +0000811 if (flags == NoValue)
812 {
813 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
814 "InvalidGeometry","`%s'",option);
815 image=DestroyImage(image);
816 return((Image *) NULL);
817 }
818 page.width=(size_t) ceil((double) (page.width*image->resolution.x/delta.x)
819 -0.5);
820 page.height=(size_t) ceil((double) (page.height*image->resolution.y/
821 delta.y) -0.5);
dirkdb6b52c2015-11-25 23:44:01 +0100822 page_geometry=DestroyString(page_geometry);
dirka5eeacb2014-10-25 06:32:14 +0000823 fitPage=MagickTrue;
824 }
cristy3ed852e2009-09-05 21:47:34 +0000825 (void) CloseBlob(image);
cristy3d9f5ba2012-06-26 13:37:31 +0000826 if (IssRGBCompatibleColorspace(image_info->colorspace) != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000827 cmyk=MagickFalse;
828 /*
829 Create Ghostscript control file.
830 */
831 file=AcquireUniqueFileResource(postscript_filename);
832 if (file == -1)
833 {
cristyc82a27b2011-10-21 01:07:16 +0000834 ThrowFileException(exception,FileOpenError,"UnableToOpenFile",
cristy3ed852e2009-09-05 21:47:34 +0000835 image_info->filename);
836 image=DestroyImageList(image);
837 return((Image *) NULL);
838 }
839 (void) CopyMagickString(command,"/setpagedevice {pop} bind 1 index where {"
840 "dup wcheck {3 1 roll put} {pop def} ifelse} {def} ifelse\n"
cristy151b66d2015-04-15 10:50:31 +0000841 "<</UseCIEColor true>>setpagedevice\n",MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +0000842 count=write(file,command,(unsigned int) strlen(command));
cristyc39e3d62010-10-14 16:52:01 +0000843 if (image_info->page == (char *) NULL)
844 {
845 char
cristy151b66d2015-04-15 10:50:31 +0000846 translate_geometry[MagickPathExtent];
cristyc39e3d62010-10-14 16:52:01 +0000847
cristy151b66d2015-04-15 10:50:31 +0000848 (void) FormatLocaleString(translate_geometry,MagickPathExtent,
cristyc39e3d62010-10-14 16:52:01 +0000849 "%g %g translate\n",-bounds.x1,-bounds.y1);
850 count=write(file,translate_geometry,(unsigned int)
851 strlen(translate_geometry));
852 }
cristy3ed852e2009-09-05 21:47:34 +0000853 file=close(file)-1;
854 /*
855 Render Postscript with the Ghostscript delegate.
856 */
857 if (image_info->monochrome != MagickFalse)
858 delegate_info=GetDelegateInfo("ps:mono",(char *) NULL,exception);
859 else
860 if (cmyk != MagickFalse)
861 delegate_info=GetDelegateInfo("ps:cmyk",(char *) NULL,exception);
862 else
cristya97426c2011-02-04 01:41:27 +0000863 delegate_info=GetDelegateInfo("ps:alpha",(char *) NULL,exception);
cristy3ed852e2009-09-05 21:47:34 +0000864 if (delegate_info == (const DelegateInfo *) NULL)
865 {
866 (void) RelinquishUniqueFileResource(postscript_filename);
867 image=DestroyImageList(image);
868 return((Image *) NULL);
869 }
cristy51816562015-04-12 13:18:38 +0000870 density=AcquireString("");
871 options=AcquireString("");
cristy151b66d2015-04-15 10:50:31 +0000872 (void) FormatLocaleString(density,MagickPathExtent,"%gx%g",resolution.x,
cristy6502b392012-04-11 00:05:33 +0000873 resolution.y);
cristy151b66d2015-04-15 10:50:31 +0000874 (void) FormatLocaleString(options,MagickPathExtent,"-g%.20gx%.20g ",(double)
cristyc8488582012-02-28 12:03:48 +0000875 page.width,(double) page.height);
cristy3ed852e2009-09-05 21:47:34 +0000876 read_info=CloneImageInfo(image_info);
877 *read_info->magick='\0';
878 if (read_info->number_scenes != 0)
879 {
880 char
cristy151b66d2015-04-15 10:50:31 +0000881 pages[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +0000882
cristy151b66d2015-04-15 10:50:31 +0000883 (void) FormatLocaleString(pages,MagickPathExtent,"-dFirstPage=%.20g "
cristyb167a142013-03-09 22:54:29 +0000884 "-dLastPage=%.20g ",(double) read_info->scene+1,(double)
cristyf2faecf2010-05-28 19:19:36 +0000885 (read_info->scene+read_info->number_scenes));
cristy151b66d2015-04-15 10:50:31 +0000886 (void) ConcatenateMagickString(options,pages,MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +0000887 read_info->number_scenes=0;
888 if (read_info->scenes != (char *) NULL)
889 *read_info->scenes='\0';
890 }
dirka5eeacb2014-10-25 06:32:14 +0000891 if (*image_info->magick == 'E')
892 {
893 option=GetImageOption(image_info,"eps:use-cropbox");
894 if ((option == (const char *) NULL) ||
895 (IsStringTrue(option) != MagickFalse))
cristy151b66d2015-04-15 10:50:31 +0000896 (void) ConcatenateMagickString(options,"-dEPSCrop ",MagickPathExtent);
dirka5eeacb2014-10-25 06:32:14 +0000897 if (fitPage != MagickFalse)
cristy151b66d2015-04-15 10:50:31 +0000898 (void) ConcatenateMagickString(options,"-dEPSFitPage ",MagickPathExtent);
dirka5eeacb2014-10-25 06:32:14 +0000899 }
cristy151b66d2015-04-15 10:50:31 +0000900 (void) CopyMagickString(filename,read_info->filename,MagickPathExtent);
cristya97426c2011-02-04 01:41:27 +0000901 (void) AcquireUniqueFilename(filename);
dirkfd826fc2014-06-04 21:11:30 +0000902 (void) RelinquishUniqueFileResource(filename);
cristy151b66d2015-04-15 10:50:31 +0000903 (void) ConcatenateMagickString(filename,"%d",MagickPathExtent);
904 (void) FormatLocaleString(command,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +0000905 GetDelegateCommands(delegate_info),
906 read_info->antialias != MagickFalse ? 4 : 1,
cristya97426c2011-02-04 01:41:27 +0000907 read_info->antialias != MagickFalse ? 4 : 1,density,options,filename,
908 postscript_filename,input_filename);
cristy51816562015-04-12 13:18:38 +0000909 options=DestroyString(options);
910 density=DestroyString(density);
cristy60575362014-10-18 13:22:50 +0000911 *message='\0';
912 status=InvokePostscriptDelegate(read_info->verbose,command,message,exception);
cristya97426c2011-02-04 01:41:27 +0000913 (void) InterpretImageFilename(image_info,image,filename,1,
cristy6fccee12011-10-20 18:43:18 +0000914 read_info->filename,exception);
cristy41083a42009-09-07 23:47:59 +0000915 if ((status == MagickFalse) ||
cristy3ed852e2009-09-05 21:47:34 +0000916 (IsPostscriptRendered(read_info->filename) == MagickFalse))
917 {
cristy151b66d2015-04-15 10:50:31 +0000918 (void) ConcatenateMagickString(command," -c showpage",MagickPathExtent);
cristy60575362014-10-18 13:22:50 +0000919 status=InvokePostscriptDelegate(read_info->verbose,command,message,
dirk4dd75672014-10-12 12:52:42 +0000920 exception);
cristy3ed852e2009-09-05 21:47:34 +0000921 }
cristy3ed852e2009-09-05 21:47:34 +0000922 (void) RelinquishUniqueFileResource(postscript_filename);
cristy3ed852e2009-09-05 21:47:34 +0000923 (void) RelinquishUniqueFileResource(input_filename);
cristya97426c2011-02-04 01:41:27 +0000924 postscript_image=(Image *) NULL;
925 if (status == MagickFalse)
926 for (i=1; ; i++)
927 {
cristya97426c2011-02-04 01:41:27 +0000928 (void) InterpretImageFilename(image_info,image,filename,(int) i,
cristy6fccee12011-10-20 18:43:18 +0000929 read_info->filename,exception);
cristya97426c2011-02-04 01:41:27 +0000930 if (IsPostscriptRendered(read_info->filename) == MagickFalse)
931 break;
932 (void) RelinquishUniqueFileResource(read_info->filename);
933 }
934 else
935 for (i=1; ; i++)
936 {
cristya97426c2011-02-04 01:41:27 +0000937 (void) InterpretImageFilename(image_info,image,filename,(int) i,
cristy6fccee12011-10-20 18:43:18 +0000938 read_info->filename,exception);
cristya97426c2011-02-04 01:41:27 +0000939 if (IsPostscriptRendered(read_info->filename) == MagickFalse)
940 break;
cristyb0ffea22012-06-17 20:22:01 +0000941 read_info->blob=NULL;
942 read_info->length=0;
cristya97426c2011-02-04 01:41:27 +0000943 next=ReadImage(read_info,exception);
944 (void) RelinquishUniqueFileResource(read_info->filename);
945 if (next == (Image *) NULL)
946 break;
947 AppendImageToList(&postscript_image,next);
948 }
949 (void) RelinquishUniqueFileResource(read_info->filename);
cristy3ed852e2009-09-05 21:47:34 +0000950 read_info=DestroyImageInfo(read_info);
951 if (postscript_image == (Image *) NULL)
dirk947430c2014-10-12 18:53:34 +0000952 {
cristy60575362014-10-18 13:22:50 +0000953 if (*message != '\0')
dirk947430c2014-10-12 18:53:34 +0000954 (void) ThrowMagickException(exception,GetMagickModule(),
cristy60575362014-10-18 13:22:50 +0000955 DelegateError,"PostscriptDelegateFailed","`%s'",message);
cristy3ed852e2009-09-05 21:47:34 +0000956 image=DestroyImageList(image);
cristy3ed852e2009-09-05 21:47:34 +0000957 return((Image *) NULL);
958 }
959 if (LocaleCompare(postscript_image->magick,"BMP") == 0)
960 {
961 Image
962 *cmyk_image;
963
964 cmyk_image=ConsolidateCMYKImages(postscript_image,exception);
965 if (cmyk_image != (Image *) NULL)
966 {
967 postscript_image=DestroyImageList(postscript_image);
968 postscript_image=cmyk_image;
969 }
970 }
971 if (image_info->number_scenes != 0)
972 {
973 Image
974 *clone_image;
975
cristy3ed852e2009-09-05 21:47:34 +0000976 /*
977 Add place holder images to meet the subimage specification requirement.
978 */
cristybb503372010-05-27 20:51:26 +0000979 for (i=0; i < (ssize_t) image_info->scene; i++)
cristy3ed852e2009-09-05 21:47:34 +0000980 {
981 clone_image=CloneImage(postscript_image,1,1,MagickTrue,exception);
982 if (clone_image != (Image *) NULL)
983 PrependImageToList(&postscript_image,clone_image);
984 }
985 }
986 do
987 {
Cristy23f956b2016-10-01 12:21:15 -0400988 (void) CopyMagickString(postscript_image->filename,filename,
989 MagickPathExtent);
cristy835b2462014-10-27 23:44:33 +0000990 (void) CopyMagickString(postscript_image->magick,image->magick,
cristy151b66d2015-04-15 10:50:31 +0000991 MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +0000992 if (columns != 0)
993 postscript_image->magick_columns=columns;
994 if (rows != 0)
995 postscript_image->magick_rows=rows;
996 postscript_image->page=page;
997 (void) CloneImageProfiles(postscript_image,image);
998 (void) CloneImageProperties(postscript_image,image);
999 next=SyncNextImageInList(postscript_image);
1000 if (next != (Image *) NULL)
1001 postscript_image=next;
1002 } while (next != (Image *) NULL);
1003 image=DestroyImageList(image);
1004 scene=0;
1005 for (next=GetFirstImageInList(postscript_image); next != (Image *) NULL; )
1006 {
1007 next->scene=scene++;
1008 next=GetNextImageInList(next);
1009 }
1010 return(GetFirstImageInList(postscript_image));
1011}
1012
1013/*
1014%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1015% %
1016% %
1017% %
1018% R e g i s t e r P S I m a g e %
1019% %
1020% %
1021% %
1022%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1023%
1024% RegisterPSImage() adds properties for the PS image format to
1025% the list of supported formats. The properties include the image format
1026% tag, a method to read and/or write the format, whether the format
1027% supports the saving of more than one frame to the same file or blob,
1028% whether the format supports native in-memory I/O, and a brief
1029% description of the format.
1030%
1031% The format of the RegisterPSImage method is:
1032%
cristybb503372010-05-27 20:51:26 +00001033% size_t RegisterPSImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001034%
1035*/
cristybb503372010-05-27 20:51:26 +00001036ModuleExport size_t RegisterPSImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001037{
1038 MagickInfo
1039 *entry;
1040
dirk06b627a2015-04-06 18:59:17 +00001041 entry=AcquireMagickInfo("PS","EPI",
cristy5aefbeb2013-08-09 12:13:32 +00001042 "Encapsulated PostScript Interchange format");
cristy3ed852e2009-09-05 21:47:34 +00001043 entry->decoder=(DecodeImageHandler *) ReadPSImage;
1044 entry->encoder=(EncodeImageHandler *) WritePSImage;
1045 entry->magick=(IsImageFormatHandler *) IsPS;
dirk08e9a112015-02-22 01:51:41 +00001046 entry->flags^=CoderAdjoinFlag;
1047 entry->flags^=CoderBlobSupportFlag;
cristy5aefbeb2013-08-09 12:13:32 +00001048 entry->mime_type=ConstantString("application/postscript");
cristy3ed852e2009-09-05 21:47:34 +00001049 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001050 entry=AcquireMagickInfo("PS","EPS","Encapsulated PostScript");
cristy3ed852e2009-09-05 21:47:34 +00001051 entry->decoder=(DecodeImageHandler *) ReadPSImage;
1052 entry->encoder=(EncodeImageHandler *) WritePSImage;
1053 entry->magick=(IsImageFormatHandler *) IsPS;
dirk08e9a112015-02-22 01:51:41 +00001054 entry->flags^=CoderAdjoinFlag;
1055 entry->flags^=CoderBlobSupportFlag;
cristy5aefbeb2013-08-09 12:13:32 +00001056 entry->mime_type=ConstantString("application/postscript");
cristy3ed852e2009-09-05 21:47:34 +00001057 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001058 entry=AcquireMagickInfo("PS","EPSF","Encapsulated PostScript");
cristy3ed852e2009-09-05 21:47:34 +00001059 entry->decoder=(DecodeImageHandler *) ReadPSImage;
1060 entry->encoder=(EncodeImageHandler *) WritePSImage;
1061 entry->magick=(IsImageFormatHandler *) IsPS;
dirk08e9a112015-02-22 01:51:41 +00001062 entry->flags^=CoderAdjoinFlag;
1063 entry->flags^=CoderBlobSupportFlag;
dirk06b627a2015-04-06 18:59:17 +00001064 entry->mime_type=ConstantString("application/postscript");
1065 (void) RegisterMagickInfo(entry);
1066 entry=AcquireMagickInfo("PS","EPSI",
cristy3ed852e2009-09-05 21:47:34 +00001067 "Encapsulated PostScript Interchange format");
dirk06b627a2015-04-06 18:59:17 +00001068 entry->decoder=(DecodeImageHandler *) ReadPSImage;
1069 entry->encoder=(EncodeImageHandler *) WritePSImage;
1070 entry->magick=(IsImageFormatHandler *) IsPS;
1071 entry->flags^=CoderAdjoinFlag;
1072 entry->flags^=CoderBlobSupportFlag;
cristy5aefbeb2013-08-09 12:13:32 +00001073 entry->mime_type=ConstantString("application/postscript");
cristy3ed852e2009-09-05 21:47:34 +00001074 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001075 entry=AcquireMagickInfo("PS","PS","PostScript");
cristy3ed852e2009-09-05 21:47:34 +00001076 entry->decoder=(DecodeImageHandler *) ReadPSImage;
1077 entry->encoder=(EncodeImageHandler *) WritePSImage;
1078 entry->magick=(IsImageFormatHandler *) IsPS;
cristy5aefbeb2013-08-09 12:13:32 +00001079 entry->mime_type=ConstantString("application/postscript");
dirk08e9a112015-02-22 01:51:41 +00001080 entry->flags^=CoderBlobSupportFlag;
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{ \
Cristyb8647f12017-07-16 11:18:07 -04001161 if ((image->alpha_trait != UndefinedPixelTrait) && (length != 0) && \
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",
Cristy629f8052015-09-14 07:48:08 -04001442 (const char *) NULL
cristy3ed852e2009-09-05 21:47:34 +00001443 },
Cristy629f8052015-09-14 07:48:08 -04001444 *const PostscriptEpilog[]=
cristy3ed852e2009-09-05 21:47:34 +00001445 {
1446 " x y scale",
1447 " currentfile buffer readline pop",
1448 " token pop /columns exch def",
1449 " token pop /rows exch def pop",
1450 " currentfile buffer readline pop",
1451 " token pop /class exch def pop",
1452 " currentfile buffer readline pop",
1453 " token pop /compression exch def pop",
1454 " class 0 gt { PseudoClassImage } { DirectClassImage } ifelse",
cristy8b4ff222012-03-02 13:01:39 +00001455 " grestore",
Cristy629f8052015-09-14 07:48:08 -04001456 (const char *) NULL
cristy3ed852e2009-09-05 21:47:34 +00001457 };
1458
1459 char
cristy151b66d2015-04-15 10:50:31 +00001460 buffer[MagickPathExtent],
1461 date[MagickPathExtent],
cristy3ed852e2009-09-05 21:47:34 +00001462 **labels,
cristy151b66d2015-04-15 10:50:31 +00001463 page_geometry[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00001464
cristyaf8d3912014-02-21 14:50:33 +00001465 CompressionType
1466 compression;
1467
cristy3ed852e2009-09-05 21:47:34 +00001468 const char
Cristy629f8052015-09-14 07:48:08 -04001469 *const *s,
cristy3ed852e2009-09-05 21:47:34 +00001470 *value;
1471
1472 const StringInfo
1473 *profile;
1474
1475 double
1476 pointsize;
1477
1478 GeometryInfo
1479 geometry_info;
1480
cristy3ed852e2009-09-05 21:47:34 +00001481 MagickBooleanType
1482 status;
1483
1484 MagickOffsetType
1485 scene;
1486
1487 MagickStatusType
1488 flags;
1489
cristy101ab702011-10-13 13:06:32 +00001490 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001491 pixel;
1492
1493 PointInfo
1494 delta,
1495 resolution,
1496 scale;
1497
cristy4c08aed2011-07-01 19:47:50 +00001498 Quantum
1499 index;
1500
cristy3ed852e2009-09-05 21:47:34 +00001501 RectangleInfo
1502 geometry,
1503 media_info,
1504 page_info;
1505
cristy4c08aed2011-07-01 19:47:50 +00001506 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00001507 *p;
1508
cristybb503372010-05-27 20:51:26 +00001509 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001510 i,
1511 x;
1512
1513 register unsigned char
1514 *q;
1515
1516 SegmentInfo
1517 bounds;
1518
1519 size_t
cristy802d3642011-04-27 02:02:41 +00001520 bit,
1521 byte,
1522 length,
1523 page,
1524 text_size;
1525
1526 ssize_t
1527 j,
1528 y;
cristy3ed852e2009-09-05 21:47:34 +00001529
1530 time_t
1531 timer;
1532
1533 unsigned char
1534 pixels[2048];
1535
cristy3ed852e2009-09-05 21:47:34 +00001536 /*
1537 Open output image file.
1538 */
1539 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001540 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001541 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001542 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001543 if (image->debug != MagickFalse)
1544 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +00001545 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001546 assert(exception->signature == MagickCoreSignature);
cristy1e178e72011-08-28 19:44:34 +00001547 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +00001548 if (status == MagickFalse)
1549 return(status);
1550 (void) ResetMagickMemory(&bounds,0,sizeof(bounds));
cristyaf8d3912014-02-21 14:50:33 +00001551 compression=image->compression;
1552 if (image_info->compression != UndefinedCompression)
1553 compression=image_info->compression;
cristy3ed852e2009-09-05 21:47:34 +00001554 page=1;
1555 scene=0;
1556 do
1557 {
1558 /*
1559 Scale relative to dots-per-inch.
1560 */
cristyaf8d3912014-02-21 14:50:33 +00001561 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +00001562 delta.x=DefaultResolution;
1563 delta.y=DefaultResolution;
cristy2a11bef2011-10-28 18:33:11 +00001564 resolution.x=image->resolution.x;
1565 resolution.y=image->resolution.y;
cristy3ed852e2009-09-05 21:47:34 +00001566 if ((resolution.x == 0.0) || (resolution.y == 0.0))
1567 {
1568 flags=ParseGeometry(PSDensityGeometry,&geometry_info);
1569 resolution.x=geometry_info.rho;
1570 resolution.y=geometry_info.sigma;
1571 if ((flags & SigmaValue) == 0)
1572 resolution.y=resolution.x;
1573 }
1574 if (image_info->density != (char *) NULL)
1575 {
1576 flags=ParseGeometry(image_info->density,&geometry_info);
1577 resolution.x=geometry_info.rho;
1578 resolution.y=geometry_info.sigma;
1579 if ((flags & SigmaValue) == 0)
1580 resolution.y=resolution.x;
1581 }
1582 if (image->units == PixelsPerCentimeterResolution)
1583 {
cristya97426c2011-02-04 01:41:27 +00001584 resolution.x=(double) ((size_t) (100.0*2.54*resolution.x+0.5)/100.0);
1585 resolution.y=(double) ((size_t) (100.0*2.54*resolution.y+0.5)/100.0);
cristy3ed852e2009-09-05 21:47:34 +00001586 }
1587 SetGeometry(image,&geometry);
cristy151b66d2015-04-15 10:50:31 +00001588 (void) FormatLocaleString(page_geometry,MagickPathExtent,"%.20gx%.20g",
cristye8c25f92010-06-03 00:53:06 +00001589 (double) image->columns,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001590 if (image_info->page != (char *) NULL)
cristy151b66d2015-04-15 10:50:31 +00001591 (void) CopyMagickString(page_geometry,image_info->page,MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00001592 else
1593 if ((image->page.width != 0) && (image->page.height != 0))
cristy151b66d2015-04-15 10:50:31 +00001594 (void) FormatLocaleString(page_geometry,MagickPathExtent,
cristy6d8abba2010-06-03 01:10:47 +00001595 "%.20gx%.20g%+.20g%+.20g",(double) image->page.width,(double)
cristye8c25f92010-06-03 00:53:06 +00001596 image->page.height,(double) image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00001597 else
1598 if ((image->gravity != UndefinedGravity) &&
1599 (LocaleCompare(image_info->magick,"PS") == 0))
cristy151b66d2015-04-15 10:50:31 +00001600 (void) CopyMagickString(page_geometry,PSPageGeometry,MagickPathExtent);
1601 (void) ConcatenateMagickString(page_geometry,">",MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00001602 (void) ParseMetaGeometry(page_geometry,&geometry.x,&geometry.y,
1603 &geometry.width,&geometry.height);
1604 scale.x=(double) (geometry.width*delta.x)/resolution.x;
cristybb503372010-05-27 20:51:26 +00001605 geometry.width=(size_t) floor(scale.x+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001606 scale.y=(double) (geometry.height*delta.y)/resolution.y;
cristybb503372010-05-27 20:51:26 +00001607 geometry.height=(size_t) floor(scale.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001608 (void) ParseAbsoluteGeometry(page_geometry,&media_info);
cristy1e178e72011-08-28 19:44:34 +00001609 (void) ParseGravityGeometry(image,page_geometry,&page_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00001610 if (image->gravity != UndefinedGravity)
1611 {
1612 geometry.x=(-page_info.x);
cristybb503372010-05-27 20:51:26 +00001613 geometry.y=(ssize_t) (media_info.height+page_info.y-image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001614 }
1615 pointsize=12.0;
1616 if (image_info->pointsize != 0.0)
1617 pointsize=image_info->pointsize;
1618 text_size=0;
cristyd15e6592011-10-15 00:13:06 +00001619 value=GetImageProperty(image,"label",exception);
cristy3ed852e2009-09-05 21:47:34 +00001620 if (value != (const char *) NULL)
cristybb503372010-05-27 20:51:26 +00001621 text_size=(size_t) (MultilineCensus(value)*pointsize+12);
cristy3ed852e2009-09-05 21:47:34 +00001622 if (page == 1)
1623 {
1624 /*
1625 Output Postscript header.
1626 */
1627 if (LocaleCompare(image_info->magick,"PS") == 0)
cristy151b66d2015-04-15 10:50:31 +00001628 (void) CopyMagickString(buffer,"%!PS-Adobe-3.0\n",MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00001629 else
1630 (void) CopyMagickString(buffer,"%!PS-Adobe-3.0 EPSF-3.0\n",
cristy151b66d2015-04-15 10:50:31 +00001631 MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00001632 (void) WriteBlobString(image,buffer);
1633 (void) WriteBlobString(image,"%%Creator: (ImageMagick)\n");
cristy151b66d2015-04-15 10:50:31 +00001634 (void) FormatLocaleString(buffer,MagickPathExtent,"%%%%Title: (%s)\n",
cristy3ed852e2009-09-05 21:47:34 +00001635 image->filename);
1636 (void) WriteBlobString(image,buffer);
1637 timer=time((time_t *) NULL);
cristy151b66d2015-04-15 10:50:31 +00001638 (void) FormatMagickTime(timer,MagickPathExtent,date);
1639 (void) FormatLocaleString(buffer,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +00001640 "%%%%CreationDate: (%s)\n",date);
1641 (void) WriteBlobString(image,buffer);
1642 bounds.x1=(double) geometry.x;
1643 bounds.y1=(double) geometry.y;
1644 bounds.x2=(double) geometry.x+scale.x;
1645 bounds.y2=(double) geometry.y+(geometry.height+text_size);
1646 if ((image_info->adjoin != MagickFalse) &&
1647 (GetNextImageInList(image) != (Image *) NULL))
1648 (void) CopyMagickString(buffer,"%%%%BoundingBox: (atend)\n",
cristy151b66d2015-04-15 10:50:31 +00001649 MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00001650 else
1651 {
cristy151b66d2015-04-15 10:50:31 +00001652 (void) FormatLocaleString(buffer,MagickPathExtent,
cristye8c25f92010-06-03 00:53:06 +00001653 "%%%%BoundingBox: %.20g %.20g %.20g %.20g\n",ceil(bounds.x1-0.5),
cristy8071c472012-09-24 12:41:06 +00001654 ceil(bounds.y1-0.5),floor(bounds.x2+0.5),floor(bounds.y2+0.5));
cristy3ed852e2009-09-05 21:47:34 +00001655 (void) WriteBlobString(image,buffer);
cristy151b66d2015-04-15 10:50:31 +00001656 (void) FormatLocaleString(buffer,MagickPathExtent,
cristye7f51092010-01-17 00:39:37 +00001657 "%%%%HiResBoundingBox: %g %g %g %g\n",bounds.x1,
cristy8cd5b312010-01-07 01:10:24 +00001658 bounds.y1,bounds.x2,bounds.y2);
cristy3ed852e2009-09-05 21:47:34 +00001659 }
1660 (void) WriteBlobString(image,buffer);
1661 profile=GetImageProfile(image,"8bim");
1662 if (profile != (StringInfo *) NULL)
1663 {
1664 /*
1665 Embed Photoshop profile.
1666 */
cristy151b66d2015-04-15 10:50:31 +00001667 (void) FormatLocaleString(buffer,MagickPathExtent,
cristye8c25f92010-06-03 00:53:06 +00001668 "%%BeginPhotoshop: %.20g",(double) GetStringInfoLength(profile));
cristy3ed852e2009-09-05 21:47:34 +00001669 (void) WriteBlobString(image,buffer);
cristybb503372010-05-27 20:51:26 +00001670 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
cristy3ed852e2009-09-05 21:47:34 +00001671 {
1672 if ((i % 32) == 0)
1673 (void) WriteBlobString(image,"\n% ");
cristy151b66d2015-04-15 10:50:31 +00001674 (void) FormatLocaleString(buffer,MagickPathExtent,"%02X",
cristy3ed852e2009-09-05 21:47:34 +00001675 (unsigned int) (GetStringInfoDatum(profile)[i] & 0xff));
1676 (void) WriteBlobString(image,buffer);
1677 }
1678 (void) WriteBlobString(image,"\n%EndPhotoshop\n");
1679 }
1680 profile=GetImageProfile(image,"xmp");
dirk93b02b72013-11-16 16:03:36 +00001681DisableMSCWarning(4127)
cristy2b2eb912010-03-03 14:56:40 +00001682 if (0 && (profile != (StringInfo *) NULL))
dirk93b02b72013-11-16 16:03:36 +00001683RestoreMSCWarning
cristy3ed852e2009-09-05 21:47:34 +00001684 {
1685 /*
1686 Embed XML profile.
1687 */
1688 (void) WriteBlobString(image,"\n%begin_xml_code\n");
cristy151b66d2015-04-15 10:50:31 +00001689 (void) FormatLocaleString(buffer,MagickPathExtent,
cristye8c25f92010-06-03 00:53:06 +00001690 "\n%%begin_xml_packet: %.20g\n",(double)
cristyeec18db2010-03-03 21:15:45 +00001691 GetStringInfoLength(profile));
cristy2b2eb912010-03-03 14:56:40 +00001692 (void) WriteBlobString(image,buffer);
cristybb503372010-05-27 20:51:26 +00001693 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
cristy3ed852e2009-09-05 21:47:34 +00001694 (void) WriteBlobByte(image,GetStringInfoDatum(profile)[i]);
cristy2b2eb912010-03-03 14:56:40 +00001695 (void) WriteBlobString(image,"\n%end_xml_packet\n%end_xml_code\n");
cristy3ed852e2009-09-05 21:47:34 +00001696 }
cristyd15e6592011-10-15 00:13:06 +00001697 value=GetImageProperty(image,"label",exception);
cristy3ed852e2009-09-05 21:47:34 +00001698 if (value != (const char *) NULL)
1699 (void) WriteBlobString(image,
1700 "%%DocumentNeededResources: font Times-Roman\n");
1701 (void) WriteBlobString(image,"%%DocumentData: Clean7Bit\n");
1702 (void) WriteBlobString(image,"%%LanguageLevel: 1\n");
1703 if (LocaleCompare(image_info->magick,"PS") != 0)
1704 (void) WriteBlobString(image,"%%Pages: 1\n");
1705 else
1706 {
1707 /*
1708 Compute the number of pages.
1709 */
1710 (void) WriteBlobString(image,"%%Orientation: Portrait\n");
1711 (void) WriteBlobString(image,"%%PageOrder: Ascend\n");
cristy151b66d2015-04-15 10:50:31 +00001712 (void) FormatLocaleString(buffer,MagickPathExtent,"%%%%Pages: %.20g\n",
cristye8c25f92010-06-03 00:53:06 +00001713 image_info->adjoin != MagickFalse ? (double)
1714 GetImageListLength(image) : 1.0);
cristy3ed852e2009-09-05 21:47:34 +00001715 (void) WriteBlobString(image,buffer);
1716 }
1717 (void) WriteBlobString(image,"%%EndComments\n");
1718 (void) WriteBlobString(image,"\n%%BeginDefaults\n");
1719 (void) WriteBlobString(image,"%%EndDefaults\n\n");
1720 if ((LocaleCompare(image_info->magick,"EPI") == 0) ||
1721 (LocaleCompare(image_info->magick,"EPSI") == 0) ||
1722 (LocaleCompare(image_info->magick,"EPT") == 0))
1723 {
1724 Image
1725 *preview_image;
1726
cristy3ed852e2009-09-05 21:47:34 +00001727 Quantum
1728 pixel;
1729
cristybb503372010-05-27 20:51:26 +00001730 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001731 x;
1732
cristy802d3642011-04-27 02:02:41 +00001733 ssize_t
1734 y;
1735
cristy3ed852e2009-09-05 21:47:34 +00001736 /*
1737 Create preview image.
1738 */
cristy1e178e72011-08-28 19:44:34 +00001739 preview_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00001740 if (preview_image == (Image *) NULL)
1741 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1742 /*
1743 Dump image as bitmap.
1744 */
cristy151b66d2015-04-15 10:50:31 +00001745 (void) FormatLocaleString(buffer,MagickPathExtent,
cristye8c25f92010-06-03 00:53:06 +00001746 "%%%%BeginPreview: %.20g %.20g %.20g %.20g\n%% ",(double)
1747 preview_image->columns,(double) preview_image->rows,1.0,
1748 (double) ((((preview_image->columns+7) >> 3)*preview_image->rows+
1749 35)/36));
cristy3ed852e2009-09-05 21:47:34 +00001750 (void) WriteBlobString(image,buffer);
1751 q=pixels;
cristybb503372010-05-27 20:51:26 +00001752 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001753 {
1754 p=GetVirtualPixels(preview_image,0,y,preview_image->columns,1,
cristy1e178e72011-08-28 19:44:34 +00001755 exception);
cristy4c08aed2011-07-01 19:47:50 +00001756 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001757 break;
cristy3ed852e2009-09-05 21:47:34 +00001758 bit=0;
1759 byte=0;
cristybb503372010-05-27 20:51:26 +00001760 for (x=0; x < (ssize_t) preview_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001761 {
1762 byte<<=1;
cristyd0323222013-04-07 16:13:21 +00001763 pixel=ClampToQuantum(GetPixelLuma(preview_image,p));
cristy3ed852e2009-09-05 21:47:34 +00001764 if (pixel >= (Quantum) (QuantumRange/2))
1765 byte|=0x01;
1766 bit++;
1767 if (bit == 8)
1768 {
1769 q=PopHexPixel(hex_digits,byte,q);
1770 if ((q-pixels+8) >= 80)
1771 {
1772 *q++='\n';
1773 (void) WriteBlob(image,q-pixels,pixels);
1774 q=pixels;
1775 (void) WriteBlobString(image,"% ");
1776 };
1777 bit=0;
1778 byte=0;
1779 }
1780 }
1781 if (bit != 0)
1782 {
1783 byte<<=(8-bit);
1784 q=PopHexPixel(hex_digits,byte,q);
1785 if ((q-pixels+8) >= 80)
1786 {
1787 *q++='\n';
1788 (void) WriteBlob(image,q-pixels,pixels);
1789 q=pixels;
1790 (void) WriteBlobString(image,"% ");
1791 };
1792 };
1793 }
1794 if (q != pixels)
1795 {
1796 *q++='\n';
1797 (void) WriteBlob(image,q-pixels,pixels);
1798 }
1799 (void) WriteBlobString(image,"\n%%EndPreview\n");
1800 preview_image=DestroyImage(preview_image);
1801 }
1802 /*
1803 Output Postscript commands.
1804 */
1805 for (s=PostscriptProlog; *s != (char *) NULL; s++)
1806 {
cristy151b66d2015-04-15 10:50:31 +00001807 (void) FormatLocaleString(buffer,MagickPathExtent,"%s\n",*s);
cristy3ed852e2009-09-05 21:47:34 +00001808 (void) WriteBlobString(image,buffer);
1809 }
cristyd15e6592011-10-15 00:13:06 +00001810 value=GetImageProperty(image,"label",exception);
cristy3ed852e2009-09-05 21:47:34 +00001811 if (value != (const char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001812 {
Cristyd3bb6e52016-12-15 08:09:35 -05001813 (void) WriteBlobString(image,
1814 " /Times-Roman findfont pointsize scalefont setfont\n");
1815 for (j=(ssize_t) MultilineCensus(value)-1; j >= 0; j--)
1816 {
1817 (void) WriteBlobString(image," /label 512 string def\n");
1818 (void) WriteBlobString(image,
1819 " currentfile label readline pop\n");
1820 (void) FormatLocaleString(buffer,MagickPathExtent,
1821 " 0 y %g add moveto label show pop\n",j*pointsize+12);
1822 (void) WriteBlobString(image,buffer);
1823 }
cristy3ed852e2009-09-05 21:47:34 +00001824 }
1825 for (s=PostscriptEpilog; *s != (char *) NULL; s++)
1826 {
cristy151b66d2015-04-15 10:50:31 +00001827 (void) FormatLocaleString(buffer,MagickPathExtent,"%s\n",*s);
cristy3ed852e2009-09-05 21:47:34 +00001828 (void) WriteBlobString(image,buffer);
1829 }
1830 if (LocaleCompare(image_info->magick,"PS") == 0)
1831 (void) WriteBlobString(image," showpage\n");
1832 (void) WriteBlobString(image,"} bind def\n");
1833 (void) WriteBlobString(image,"%%EndProlog\n");
1834 }
cristy151b66d2015-04-15 10:50:31 +00001835 (void) FormatLocaleString(buffer,MagickPathExtent,"%%%%Page: 1 %.20g\n",
cristye8c25f92010-06-03 00:53:06 +00001836 (double) (page++));
cristy3ed852e2009-09-05 21:47:34 +00001837 (void) WriteBlobString(image,buffer);
cristy151b66d2015-04-15 10:50:31 +00001838 (void) FormatLocaleString(buffer,MagickPathExtent,
cristye8c25f92010-06-03 00:53:06 +00001839 "%%%%PageBoundingBox: %.20g %.20g %.20g %.20g\n",(double) geometry.x,
1840 (double) geometry.y,geometry.x+(double) geometry.width,geometry.y+(double)
cristyf2faecf2010-05-28 19:19:36 +00001841 (geometry.height+text_size));
cristy3ed852e2009-09-05 21:47:34 +00001842 (void) WriteBlobString(image,buffer);
1843 if ((double) geometry.x < bounds.x1)
1844 bounds.x1=(double) geometry.x;
1845 if ((double) geometry.y < bounds.y1)
1846 bounds.y1=(double) geometry.y;
1847 if ((double) (geometry.x+geometry.width-1) > bounds.x2)
1848 bounds.x2=(double) geometry.x+geometry.width-1;
1849 if ((double) (geometry.y+(geometry.height+text_size)-1) > bounds.y2)
1850 bounds.y2=(double) geometry.y+(geometry.height+text_size)-1;
cristyd15e6592011-10-15 00:13:06 +00001851 value=GetImageProperty(image,"label",exception);
cristy3ed852e2009-09-05 21:47:34 +00001852 if (value != (const char *) NULL)
1853 (void) WriteBlobString(image,"%%%%PageResources: font Times-Roman\n");
1854 if (LocaleCompare(image_info->magick,"PS") != 0)
1855 (void) WriteBlobString(image,"userdict begin\n");
1856 (void) WriteBlobString(image,"DisplayImage\n");
1857 /*
1858 Output image data.
1859 */
cristy151b66d2015-04-15 10:50:31 +00001860 (void) FormatLocaleString(buffer,MagickPathExtent,"%.20g %.20g\n%g %g\n%g\n",
cristye8c25f92010-06-03 00:53:06 +00001861 (double) geometry.x,(double) geometry.y,scale.x,scale.y,pointsize);
cristy3ed852e2009-09-05 21:47:34 +00001862 (void) WriteBlobString(image,buffer);
1863 labels=(char **) NULL;
cristyd15e6592011-10-15 00:13:06 +00001864 value=GetImageProperty(image,"label",exception);
cristy3ed852e2009-09-05 21:47:34 +00001865 if (value != (const char *) NULL)
1866 labels=StringToList(value);
1867 if (labels != (char **) NULL)
1868 {
1869 for (i=0; labels[i] != (char *) NULL; i++)
1870 {
cristy151b66d2015-04-15 10:50:31 +00001871 (void) FormatLocaleString(buffer,MagickPathExtent,"%s \n",
cristy3ed852e2009-09-05 21:47:34 +00001872 labels[i]);
1873 (void) WriteBlobString(image,buffer);
1874 labels[i]=DestroyString(labels[i]);
1875 }
1876 labels=(char **) RelinquishMagickMemory(labels);
1877 }
1878 (void) ResetMagickMemory(&pixel,0,sizeof(pixel));
cristyd0323222013-04-07 16:13:21 +00001879 pixel.alpha=(MagickRealType) TransparentAlpha;
cristy4c08aed2011-07-01 19:47:50 +00001880 index=0;
cristy3ed852e2009-09-05 21:47:34 +00001881 x=0;
1882 if ((image_info->type != TrueColorType) &&
dirkf1d85482015-04-06 00:36:00 +00001883 (SetImageGray(image,exception) != MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +00001884 {
dirkf1d85482015-04-06 00:36:00 +00001885 if (SetImageMonochrome(image,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001886 {
1887 Quantum
1888 pixel;
1889
1890 /*
1891 Dump image as grayscale.
1892 */
cristy151b66d2015-04-15 10:50:31 +00001893 (void) FormatLocaleString(buffer,MagickPathExtent,
cristye8c25f92010-06-03 00:53:06 +00001894 "%.20g %.20g\n1\n1\n1\n8\n",(double) image->columns,(double)
1895 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001896 (void) WriteBlobString(image,buffer);
1897 q=pixels;
cristybb503372010-05-27 20:51:26 +00001898 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001899 {
cristy1e178e72011-08-28 19:44:34 +00001900 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001901 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001902 break;
cristybb503372010-05-27 20:51:26 +00001903 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001904 {
cristyd0323222013-04-07 16:13:21 +00001905 pixel=(Quantum) ScaleQuantumToChar(ClampToQuantum(GetPixelLuma(
1906 image,p)));
cristya97426c2011-02-04 01:41:27 +00001907 q=PopHexPixel(hex_digits,(size_t) pixel,q);
cristy3ed852e2009-09-05 21:47:34 +00001908 if ((q-pixels+8) >= 80)
1909 {
1910 *q++='\n';
1911 (void) WriteBlob(image,q-pixels,pixels);
1912 q=pixels;
1913 }
cristyed231572011-07-14 02:18:59 +00001914 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001915 }
1916 if (image->previous == (Image *) NULL)
1917 {
cristya97426c2011-02-04 01:41:27 +00001918 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType)
1919 y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001920 if (status == MagickFalse)
1921 break;
1922 }
1923 }
1924 if (q != pixels)
1925 {
1926 *q++='\n';
1927 (void) WriteBlob(image,q-pixels,pixels);
1928 }
1929 }
1930 else
1931 {
cristybb503372010-05-27 20:51:26 +00001932 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001933 y;
1934
1935 Quantum
1936 pixel;
1937
1938 /*
1939 Dump image as bitmap.
1940 */
cristy151b66d2015-04-15 10:50:31 +00001941 (void) FormatLocaleString(buffer,MagickPathExtent,
cristye8c25f92010-06-03 00:53:06 +00001942 "%.20g %.20g\n1\n1\n1\n1\n",(double) image->columns,(double)
1943 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001944 (void) WriteBlobString(image,buffer);
1945 q=pixels;
cristybb503372010-05-27 20:51:26 +00001946 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001947 {
cristy1e178e72011-08-28 19:44:34 +00001948 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001949 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001950 break;
cristy3ed852e2009-09-05 21:47:34 +00001951 bit=0;
1952 byte=0;
cristybb503372010-05-27 20:51:26 +00001953 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001954 {
1955 byte<<=1;
cristyd0323222013-04-07 16:13:21 +00001956 pixel=ClampToQuantum(GetPixelLuma(image,p));
cristy3ed852e2009-09-05 21:47:34 +00001957 if (pixel >= (Quantum) (QuantumRange/2))
1958 byte|=0x01;
1959 bit++;
1960 if (bit == 8)
1961 {
1962 q=PopHexPixel(hex_digits,byte,q);
1963 if ((q-pixels+2) >= 80)
1964 {
1965 *q++='\n';
1966 (void) WriteBlob(image,q-pixels,pixels);
1967 q=pixels;
1968 };
1969 bit=0;
1970 byte=0;
1971 }
cristyed231572011-07-14 02:18:59 +00001972 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001973 }
1974 if (bit != 0)
1975 {
1976 byte<<=(8-bit);
1977 q=PopHexPixel(hex_digits,byte,q);
1978 if ((q-pixels+2) >= 80)
1979 {
1980 *q++='\n';
1981 (void) WriteBlob(image,q-pixels,pixels);
1982 q=pixels;
1983 }
1984 };
1985 if (image->previous == (Image *) NULL)
1986 {
cristy6cde06a2011-11-24 00:08:43 +00001987 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType)
cristy802d3642011-04-27 02:02:41 +00001988 y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001989 if (status == MagickFalse)
1990 break;
1991 }
1992 }
1993 if (q != pixels)
1994 {
1995 *q++='\n';
1996 (void) WriteBlob(image,q-pixels,pixels);
1997 }
1998 }
1999 }
2000 else
2001 if ((image->storage_class == DirectClass) ||
cristy17f11b02014-12-20 19:37:04 +00002002 (image->colors > 256) || (image->alpha_trait != UndefinedPixelTrait))
cristy3ed852e2009-09-05 21:47:34 +00002003 {
2004 /*
2005 Dump DirectClass image.
2006 */
Cristyb8647f12017-07-16 11:18:07 -04002007 (void) FormatLocaleString(buffer,MagickPathExtent,
2008 "%.20g %.20g\n0\n%d\n",(double) image->columns,(double) image->rows,
cristyaf8d3912014-02-21 14:50:33 +00002009 compression == RLECompression ? 1 : 0);
cristy3ed852e2009-09-05 21:47:34 +00002010 (void) WriteBlobString(image,buffer);
cristyaf8d3912014-02-21 14:50:33 +00002011 switch (compression)
cristy3ed852e2009-09-05 21:47:34 +00002012 {
2013 case RLECompression:
2014 {
2015 /*
2016 Dump runlength-encoded DirectColor packets.
2017 */
2018 q=pixels;
cristybb503372010-05-27 20:51:26 +00002019 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002020 {
cristy1e178e72011-08-28 19:44:34 +00002021 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002022 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002023 break;
cristy101ab702011-10-13 13:06:32 +00002024 GetPixelInfoPixel(image,p,&pixel);
cristy3ed852e2009-09-05 21:47:34 +00002025 length=255;
cristybb503372010-05-27 20:51:26 +00002026 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002027 {
cristyd0323222013-04-07 16:13:21 +00002028 if ((GetPixelRed(image,p) == ClampToQuantum(pixel.red)) &&
2029 (GetPixelGreen(image,p) == ClampToQuantum(pixel.green)) &&
2030 (GetPixelBlue(image,p) == ClampToQuantum(pixel.blue)) &&
2031 (GetPixelAlpha(image,p) == ClampToQuantum(pixel.alpha)) &&
cristy802d3642011-04-27 02:02:41 +00002032 (length < 255) && (x < (ssize_t) (image->columns-1)))
cristy3ed852e2009-09-05 21:47:34 +00002033 length++;
2034 else
2035 {
2036 if (x > 0)
2037 {
2038 WriteRunlengthPacket(image,pixel,length,p);
2039 if ((q-pixels+10) >= 80)
2040 {
2041 *q++='\n';
2042 (void) WriteBlob(image,q-pixels,pixels);
2043 q=pixels;
2044 }
2045 }
2046 length=0;
2047 }
cristy101ab702011-10-13 13:06:32 +00002048 GetPixelInfoPixel(image,p,&pixel);
cristyed231572011-07-14 02:18:59 +00002049 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002050 }
2051 WriteRunlengthPacket(image,pixel,length,p);
2052 if ((q-pixels+10) >= 80)
2053 {
2054 *q++='\n';
2055 (void) WriteBlob(image,q-pixels,pixels);
2056 q=pixels;
2057 }
2058 if (image->previous == (Image *) NULL)
2059 {
cristy802d3642011-04-27 02:02:41 +00002060 status=SetImageProgress(image,SaveImageTag,
2061 (MagickOffsetType) y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002062 if (status == MagickFalse)
2063 break;
2064 }
2065 }
2066 if (q != pixels)
2067 {
2068 *q++='\n';
2069 (void) WriteBlob(image,q-pixels,pixels);
2070 }
2071 break;
2072 }
2073 case NoCompression:
2074 default:
2075 {
2076 /*
2077 Dump uncompressed DirectColor packets.
2078 */
2079 q=pixels;
cristybb503372010-05-27 20:51:26 +00002080 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002081 {
cristy1e178e72011-08-28 19:44:34 +00002082 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002083 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002084 break;
cristybb503372010-05-27 20:51:26 +00002085 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002086 {
cristy17f11b02014-12-20 19:37:04 +00002087 if ((image->alpha_trait != UndefinedPixelTrait) &&
cristy4c08aed2011-07-01 19:47:50 +00002088 (GetPixelAlpha(image,p) == (Quantum) TransparentAlpha))
cristy3ed852e2009-09-05 21:47:34 +00002089 {
2090 q=PopHexPixel(hex_digits,0xff,q);
2091 q=PopHexPixel(hex_digits,0xff,q);
2092 q=PopHexPixel(hex_digits,0xff,q);
2093 }
2094 else
2095 {
cristy802d3642011-04-27 02:02:41 +00002096 q=PopHexPixel(hex_digits,ScaleQuantumToChar(
cristy4c08aed2011-07-01 19:47:50 +00002097 GetPixelRed(image,p)),q);
cristy802d3642011-04-27 02:02:41 +00002098 q=PopHexPixel(hex_digits,ScaleQuantumToChar(
cristy4c08aed2011-07-01 19:47:50 +00002099 GetPixelGreen(image,p)),q);
cristy802d3642011-04-27 02:02:41 +00002100 q=PopHexPixel(hex_digits,ScaleQuantumToChar(
cristy4c08aed2011-07-01 19:47:50 +00002101 GetPixelBlue(image,p)),q);
cristy3ed852e2009-09-05 21:47:34 +00002102 }
2103 if ((q-pixels+6) >= 80)
2104 {
2105 *q++='\n';
2106 (void) WriteBlob(image,q-pixels,pixels);
2107 q=pixels;
2108 }
cristyed231572011-07-14 02:18:59 +00002109 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002110 }
2111 if (image->previous == (Image *) NULL)
2112 {
cristy802d3642011-04-27 02:02:41 +00002113 status=SetImageProgress(image,SaveImageTag,
2114 (MagickOffsetType) y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002115 if (status == MagickFalse)
2116 break;
2117 }
2118 }
2119 if (q != pixels)
2120 {
2121 *q++='\n';
2122 (void) WriteBlob(image,q-pixels,pixels);
2123 }
2124 break;
2125 }
2126 }
2127 (void) WriteBlobByte(image,'\n');
2128 }
2129 else
2130 {
2131 /*
2132 Dump PseudoClass image.
2133 */
cristy151b66d2015-04-15 10:50:31 +00002134 (void) FormatLocaleString(buffer,MagickPathExtent,
cristye8c25f92010-06-03 00:53:06 +00002135 "%.20g %.20g\n%d\n%d\n0\n",(double) image->columns,(double)
2136 image->rows,image->storage_class == PseudoClass ? 1 : 0,
cristyaf8d3912014-02-21 14:50:33 +00002137 compression == RLECompression ? 1 : 0);
cristy3ed852e2009-09-05 21:47:34 +00002138 (void) WriteBlobString(image,buffer);
2139 /*
2140 Dump number of colors and colormap.
2141 */
cristy151b66d2015-04-15 10:50:31 +00002142 (void) FormatLocaleString(buffer,MagickPathExtent,"%.20g\n",(double)
cristyf2faecf2010-05-28 19:19:36 +00002143 image->colors);
cristy3ed852e2009-09-05 21:47:34 +00002144 (void) WriteBlobString(image,buffer);
cristybb503372010-05-27 20:51:26 +00002145 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002146 {
cristy151b66d2015-04-15 10:50:31 +00002147 (void) FormatLocaleString(buffer,MagickPathExtent,"%02X%02X%02X\n",
cristyd0323222013-04-07 16:13:21 +00002148 ScaleQuantumToChar(ClampToQuantum(image->colormap[i].red)),
2149 ScaleQuantumToChar(ClampToQuantum(image->colormap[i].green)),
2150 ScaleQuantumToChar(ClampToQuantum(image->colormap[i].blue)));
cristy3ed852e2009-09-05 21:47:34 +00002151 (void) WriteBlobString(image,buffer);
2152 }
cristyaf8d3912014-02-21 14:50:33 +00002153 switch (compression)
cristy3ed852e2009-09-05 21:47:34 +00002154 {
2155 case RLECompression:
2156 {
2157 /*
2158 Dump runlength-encoded PseudoColor packets.
2159 */
2160 q=pixels;
cristybb503372010-05-27 20:51:26 +00002161 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002162 {
cristy1e178e72011-08-28 19:44:34 +00002163 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002164 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002165 break;
cristy4c08aed2011-07-01 19:47:50 +00002166 index=GetPixelIndex(image,p);
cristy3ed852e2009-09-05 21:47:34 +00002167 length=255;
cristybb503372010-05-27 20:51:26 +00002168 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002169 {
cristy4c08aed2011-07-01 19:47:50 +00002170 if ((index == GetPixelIndex(image,p)) &&
cristy802d3642011-04-27 02:02:41 +00002171 (length < 255) && (x < ((ssize_t) image->columns-1)))
cristy3ed852e2009-09-05 21:47:34 +00002172 length++;
2173 else
2174 {
2175 if (x > 0)
2176 {
cristya97426c2011-02-04 01:41:27 +00002177 q=PopHexPixel(hex_digits,(size_t) index,q);
cristybb503372010-05-27 20:51:26 +00002178 q=PopHexPixel(hex_digits,(size_t)
cristy3ed852e2009-09-05 21:47:34 +00002179 MagickMin(length,0xff),q);
2180 i++;
2181 if ((q-pixels+6) >= 80)
2182 {
2183 *q++='\n';
2184 (void) WriteBlob(image,q-pixels,pixels);
2185 q=pixels;
2186 }
2187 }
2188 length=0;
2189 }
cristy4c08aed2011-07-01 19:47:50 +00002190 index=GetPixelIndex(image,p);
cristyd0323222013-04-07 16:13:21 +00002191 pixel.red=(MagickRealType) GetPixelRed(image,p);
2192 pixel.green=(MagickRealType) GetPixelGreen(image,p);
2193 pixel.blue=(MagickRealType) GetPixelBlue(image,p);
2194 pixel.alpha=(MagickRealType) GetPixelAlpha(image,p);
cristyed231572011-07-14 02:18:59 +00002195 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002196 }
cristya97426c2011-02-04 01:41:27 +00002197 q=PopHexPixel(hex_digits,(size_t) index,q);
cristybb503372010-05-27 20:51:26 +00002198 q=PopHexPixel(hex_digits,(size_t)
cristy3ed852e2009-09-05 21:47:34 +00002199 MagickMin(length,0xff),q);
2200 if (image->previous == (Image *) NULL)
2201 {
cristya97426c2011-02-04 01:41:27 +00002202 status=SetImageProgress(image,SaveImageTag,
2203 (MagickOffsetType) y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002204 if (status == MagickFalse)
2205 break;
2206 }
2207 }
2208 if (q != pixels)
2209 {
2210 *q++='\n';
2211 (void) WriteBlob(image,q-pixels,pixels);
2212 }
2213 break;
2214 }
2215 case NoCompression:
2216 default:
2217 {
2218 /*
2219 Dump uncompressed PseudoColor packets.
2220 */
2221 q=pixels;
cristybb503372010-05-27 20:51:26 +00002222 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002223 {
cristy1e178e72011-08-28 19:44:34 +00002224 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002225 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002226 break;
cristybb503372010-05-27 20:51:26 +00002227 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002228 {
cristy4c08aed2011-07-01 19:47:50 +00002229 q=PopHexPixel(hex_digits,(size_t) GetPixelIndex(image,p),q);
cristy3ed852e2009-09-05 21:47:34 +00002230 if ((q-pixels+4) >= 80)
2231 {
2232 *q++='\n';
2233 (void) WriteBlob(image,q-pixels,pixels);
2234 q=pixels;
2235 }
cristyed231572011-07-14 02:18:59 +00002236 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002237 }
2238 if (image->previous == (Image *) NULL)
2239 {
cristya97426c2011-02-04 01:41:27 +00002240 status=SetImageProgress(image,SaveImageTag,
2241 (MagickOffsetType) y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002242 if (status == MagickFalse)
2243 break;
2244 }
2245 }
2246 if (q != pixels)
2247 {
2248 *q++='\n';
2249 (void) WriteBlob(image,q-pixels,pixels);
2250 }
2251 break;
2252 }
2253 }
2254 (void) WriteBlobByte(image,'\n');
2255 }
2256 if (LocaleCompare(image_info->magick,"PS") != 0)
2257 (void) WriteBlobString(image,"end\n");
2258 (void) WriteBlobString(image,"%%PageTrailer\n");
2259 if (GetNextImageInList(image) == (Image *) NULL)
2260 break;
2261 image=SyncNextImageInList(image);
2262 status=SetImageProgress(image,SaveImagesTag,scene++,
2263 GetImageListLength(image));
2264 if (status == MagickFalse)
2265 break;
2266 } while (image_info->adjoin != MagickFalse);
2267 (void) WriteBlobString(image,"%%Trailer\n");
2268 if (page > 2)
2269 {
cristy151b66d2015-04-15 10:50:31 +00002270 (void) FormatLocaleString(buffer,MagickPathExtent,
cristye8c25f92010-06-03 00:53:06 +00002271 "%%%%BoundingBox: %.20g %.20g %.20g %.20g\n",ceil(bounds.x1-0.5),
cristye4cc1a12012-09-03 23:04:46 +00002272 ceil(bounds.y1-0.5),floor(bounds.x2-0.5),floor(bounds.y2-0.5));
cristy3ed852e2009-09-05 21:47:34 +00002273 (void) WriteBlobString(image,buffer);
cristy151b66d2015-04-15 10:50:31 +00002274 (void) FormatLocaleString(buffer,MagickPathExtent,
cristye4cc1a12012-09-03 23:04:46 +00002275 "%%%%HiResBoundingBox: %g %g %g %g\n",bounds.x1,bounds.y1,bounds.x2,
2276 bounds.y2);
cristy3ed852e2009-09-05 21:47:34 +00002277 (void) WriteBlobString(image,buffer);
2278 }
2279 (void) WriteBlobString(image,"%%EOF\n");
2280 (void) CloseBlob(image);
2281 return(MagickTrue);
2282}