blob: 01f0203cea23ac0a165cc4cae62c2f83f3aa772a [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% PPPP SSSSS %
7% P P SS %
8% PPPP SSS %
9% P SS %
10% P SSSSS %
11% %
12% %
13% Read/Write Postscript Format %
14% %
15% Software Design %
16% John Cristy %
17% July 1992 %
18% %
19% %
cristy1454be72011-12-19 01:52:48 +000020% Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
40 Include declarations.
41*/
cristy4c08aed2011-07-01 19:47:50 +000042#include "MagickCore/studio.h"
43#include "MagickCore/attribute.h"
44#include "MagickCore/blob.h"
45#include "MagickCore/blob-private.h"
46#include "MagickCore/cache.h"
47#include "MagickCore/color.h"
48#include "MagickCore/color-private.h"
49#include "MagickCore/colorspace.h"
cristy510d06a2011-07-06 23:43:54 +000050#include "MagickCore/colorspace-private.h"
cristy4c08aed2011-07-01 19:47:50 +000051#include "MagickCore/constitute.h"
52#include "MagickCore/delegate.h"
53#include "MagickCore/delegate-private.h"
54#include "MagickCore/draw.h"
55#include "MagickCore/exception.h"
56#include "MagickCore/exception-private.h"
57#include "MagickCore/geometry.h"
58#include "MagickCore/image.h"
59#include "MagickCore/image-private.h"
60#include "MagickCore/list.h"
61#include "MagickCore/magick.h"
62#include "MagickCore/memory_.h"
63#include "MagickCore/monitor.h"
64#include "MagickCore/monitor-private.h"
65#include "MagickCore/option.h"
66#include "MagickCore/profile.h"
67#include "MagickCore/resource_.h"
68#include "MagickCore/pixel-accessor.h"
69#include "MagickCore/property.h"
70#include "MagickCore/quantum-private.h"
71#include "MagickCore/static.h"
72#include "MagickCore/string_.h"
73#include "MagickCore/module.h"
74#include "MagickCore/token.h"
75#include "MagickCore/transform.h"
76#include "MagickCore/utility.h"
cristy3ed852e2009-09-05 21:47:34 +000077
78/*
79 Forward declarations.
80*/
81static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +000082 WritePSImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +000083
84/*
85%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
86% %
87% %
88% %
89% I n v o k e P o s t s r i p t D e l e g a t e %
90% %
91% %
92% %
93%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
94%
cristydefb3f02009-09-10 02:18:35 +000095% InvokePostscriptDelegate() executes the Postscript interpreter with the
cristy3ed852e2009-09-05 21:47:34 +000096% specified command.
97%
98% The format of the InvokePostscriptDelegate method is:
99%
100% MagickBooleanType InvokePostscriptDelegate(
cristyb32b90a2009-09-07 21:45:48 +0000101% const MagickBooleanType verbose,const char *command,
102% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000103%
104% A description of each parameter follows:
105%
106% o verbose: A value other than zero displays the command prior to
107% executing it.
108%
109% o command: the address of a character string containing the command to
110% execute.
111%
cristyb32b90a2009-09-07 21:45:48 +0000112% o exception: return any errors or warnings in this structure.
113%
cristy3ed852e2009-09-05 21:47:34 +0000114*/
115static MagickBooleanType InvokePostscriptDelegate(
cristyb32b90a2009-09-07 21:45:48 +0000116 const MagickBooleanType verbose,const char *command,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000117{
cristyb32b90a2009-09-07 21:45:48 +0000118 int
119 status;
120
cristy0157aea2010-04-24 21:12:18 +0000121#if defined(MAGICKCORE_GS_DELEGATE) || defined(MAGICKCORE_WINDOWS_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000122 char
123 **argv;
124
cristydefb3f02009-09-10 02:18:35 +0000125 const GhostInfo
126 *ghost_info;
cristy3ed852e2009-09-05 21:47:34 +0000127
128 gs_main_instance
129 *interpreter;
130
131 int
132 argc,
cristyb32b90a2009-09-07 21:45:48 +0000133 code;
cristy3ed852e2009-09-05 21:47:34 +0000134
cristybb503372010-05-27 20:51:26 +0000135 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000136 i;
137
cristy0157aea2010-04-24 21:12:18 +0000138#if defined(MAGICKCORE_WINDOWS_SUPPORT)
cristydefb3f02009-09-10 02:18:35 +0000139 ghost_info=NTGhostscriptDLLVectors();
cristy3ed852e2009-09-05 21:47:34 +0000140#else
cristydefb3f02009-09-10 02:18:35 +0000141 GhostInfo
142 ghost_info_struct;
cristy3ed852e2009-09-05 21:47:34 +0000143
cristydefb3f02009-09-10 02:18:35 +0000144 ghost_info=(&ghost_info_struct);
145 (void) ResetMagickMemory(&ghost_info,0,sizeof(ghost_info));
146 ghost_info_struct.new_instance=(int (*)(gs_main_instance **,void *))
cristy3ed852e2009-09-05 21:47:34 +0000147 gsapi_new_instance;
cristydefb3f02009-09-10 02:18:35 +0000148 ghost_info_struct.init_with_args=(int (*)(gs_main_instance *,int,char **))
cristy3ed852e2009-09-05 21:47:34 +0000149 gsapi_init_with_args;
cristydefb3f02009-09-10 02:18:35 +0000150 ghost_info_struct.run_string=(int (*)(gs_main_instance *,const char *,int,
151 int *)) gsapi_run_string;
152 ghost_info_struct.delete_instance=(void (*)(gs_main_instance *))
cristy3ed852e2009-09-05 21:47:34 +0000153 gsapi_delete_instance;
cristydefb3f02009-09-10 02:18:35 +0000154 ghost_info_struct.exit=(int (*)(gs_main_instance *)) gsapi_exit;
cristy3ed852e2009-09-05 21:47:34 +0000155#endif
cristydefb3f02009-09-10 02:18:35 +0000156 if (ghost_info == (GhostInfo *) NULL)
cristyb32b90a2009-09-07 21:45:48 +0000157 {
cristy6de4bc22010-01-12 17:10:35 +0000158 status=SystemCommand(MagickFalse,verbose,command,exception);
cristy41083a42009-09-07 23:47:59 +0000159 return(status == 0 ? MagickTrue : MagickFalse);
cristyb32b90a2009-09-07 21:45:48 +0000160 }
cristy3ed852e2009-09-05 21:47:34 +0000161 if (verbose != MagickFalse)
162 {
163 (void) fputs("[ghostscript library]",stdout);
164 (void) fputs(strchr(command,' '),stdout);
165 }
cristydefb3f02009-09-10 02:18:35 +0000166 status=(ghost_info->new_instance)(&interpreter,(void *) NULL);
cristy3ed852e2009-09-05 21:47:34 +0000167 if (status < 0)
cristyb32b90a2009-09-07 21:45:48 +0000168 {
cristy6de4bc22010-01-12 17:10:35 +0000169 status=SystemCommand(MagickFalse,verbose,command,exception);
cristy41083a42009-09-07 23:47:59 +0000170 return(status == 0 ? MagickTrue : MagickFalse);
cristyb32b90a2009-09-07 21:45:48 +0000171 }
cristya73c0f82010-12-20 15:57:05 +0000172 code=0;
cristy3ed852e2009-09-05 21:47:34 +0000173 argv=StringToArgv(command,&argc);
cristydefb3f02009-09-10 02:18:35 +0000174 status=(ghost_info->init_with_args)(interpreter,argc-1,argv+1);
cristy3ed852e2009-09-05 21:47:34 +0000175 if (status == 0)
cristydefb3f02009-09-10 02:18:35 +0000176 status=(ghost_info->run_string)(interpreter,"systemdict /start get exec\n",
177 0,&code);
178 (ghost_info->exit)(interpreter);
179 (ghost_info->delete_instance)(interpreter);
cristy0157aea2010-04-24 21:12:18 +0000180#if defined(MAGICKCORE_WINDOWS_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000181 NTGhostscriptUnLoadDLL();
182#endif
cristybb503372010-05-27 20:51:26 +0000183 for (i=0; i < (ssize_t) argc; i++)
cristy3ed852e2009-09-05 21:47:34 +0000184 argv[i]=DestroyString(argv[i]);
185 argv=(char **) RelinquishMagickMemory(argv);
cristy41083a42009-09-07 23:47:59 +0000186 if ((status != 0) && (status != -101))
187 {
188 char
189 *message;
cristyb32b90a2009-09-07 21:45:48 +0000190
cristy41083a42009-09-07 23:47:59 +0000191 message=GetExceptionMessage(errno);
192 (void) ThrowMagickException(exception,GetMagickModule(),DelegateError,
193 "`%s': %s",command,message);
194 message=DestroyString(message);
195 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
196 "Ghostscript returns status %d, exit code %d",status,code);
197 return(MagickFalse);
198 }
cristy3ed852e2009-09-05 21:47:34 +0000199 return(MagickTrue);
200#else
cristy6de4bc22010-01-12 17:10:35 +0000201 status=SystemCommand(MagickFalse,verbose,command,exception);
cristy41083a42009-09-07 23:47:59 +0000202 return(status == 0 ? MagickTrue : MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +0000203#endif
204}
205
206/*
207%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
208% %
209% %
210% %
211% I s P S %
212% %
213% %
214% %
215%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
216%
217% IsPS() returns MagickTrue if the image format type, identified by the
218% magick string, is PS.
219%
220% The format of the IsPS method is:
221%
222% MagickBooleanType IsPS(const unsigned char *magick,const size_t length)
223%
224% A description of each parameter follows:
225%
226% o magick: compare image format pattern against these bytes.
227%
228% o length: Specifies the length of the magick string.
229%
230*/
231static MagickBooleanType IsPS(const unsigned char *magick,const size_t length)
232{
233 if (length < 4)
234 return(MagickFalse);
235 if (memcmp(magick,"%!",2) == 0)
236 return(MagickTrue);
237 if (memcmp(magick,"\004%!",3) == 0)
238 return(MagickTrue);
239 return(MagickFalse);
240}
241
242/*
243%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
244% %
245% %
246% %
247% R e a d P S I m a g e %
248% %
249% %
250% %
251%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
252%
253% ReadPSImage() reads a Postscript image file and returns it. It allocates
254% the memory necessary for the new Image structure and returns a pointer
255% to the new image.
256%
257% The format of the ReadPSImage method is:
258%
259% Image *ReadPSImage(const ImageInfo *image_info,ExceptionInfo *exception)
260%
261% A description of each parameter follows:
262%
263% o image_info: the image info.
264%
265% o exception: return any errors or warnings in this structure.
266%
267*/
268
269static MagickBooleanType IsPostscriptRendered(const char *path)
270{
271 MagickBooleanType
272 status;
273
274 struct stat
275 attributes;
276
277 if ((path == (const char *) NULL) || (*path == '\0'))
278 return(MagickFalse);
279 status=GetPathAttributes(path,&attributes);
280 if ((status != MagickFalse) && S_ISREG(attributes.st_mode) &&
281 (attributes.st_size > 0))
282 return(MagickTrue);
283 return(MagickFalse);
284}
285
286static inline int ProfileInteger(Image *image,short int *hex_digits)
287{
288 int
289 c,
290 l,
291 value;
292
cristybb503372010-05-27 20:51:26 +0000293 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000294 i;
295
296 l=0;
297 value=0;
298 for (i=0; i < 2; )
299 {
300 c=ReadBlobByte(image);
301 if ((c == EOF) || ((c == '%') && (l == '%')))
302 {
303 value=(-1);
304 break;
305 }
306 l=c;
307 c&=0xff;
308 if (isxdigit(c) == MagickFalse)
309 continue;
cristybb503372010-05-27 20:51:26 +0000310 value=(int) ((size_t) value << 4)+hex_digits[c];
cristy3ed852e2009-09-05 21:47:34 +0000311 i++;
312 }
313 return(value);
314}
315
316static Image *ReadPSImage(const ImageInfo *image_info,ExceptionInfo *exception)
317{
318#define BoundingBox "BoundingBox:"
319#define BeginDocument "BeginDocument:"
320#define BeginXMPPacket "<?xpacket begin="
321#define EndXMPPacket "<?xpacket end="
322#define ICCProfile "BeginICCProfile:"
323#define CMYKCustomColor "CMYKCustomColor:"
cristy01ca8922010-03-17 12:20:37 +0000324#define CMYKProcessColor "CMYKProcessColor:"
cristy3ed852e2009-09-05 21:47:34 +0000325#define DocumentMedia "DocumentMedia:"
326#define DocumentCustomColors "DocumentCustomColors:"
327#define DocumentProcessColors "DocumentProcessColors:"
328#define EndDocument "EndDocument:"
329#define HiResBoundingBox "HiResBoundingBox:"
330#define ImageData "ImageData:"
331#define PageBoundingBox "PageBoundingBox:"
332#define LanguageLevel "LanguageLevel:"
333#define PageMedia "PageMedia:"
334#define Pages "Pages:"
335#define PhotoshopProfile "BeginPhotoshop:"
336#define PostscriptLevel "!PS-"
337#define RenderPostscriptText " Rendering Postscript... "
338#define SpotColor "+ "
339
340 char
341 command[MaxTextExtent],
342 density[MaxTextExtent],
343 filename[MaxTextExtent],
344 geometry[MaxTextExtent],
345 input_filename[MaxTextExtent],
346 options[MaxTextExtent],
cristyc39e3d62010-10-14 16:52:01 +0000347 postscript_filename[MaxTextExtent];
cristy3ed852e2009-09-05 21:47:34 +0000348
349 const char
350 *option;
351
352 const DelegateInfo
353 *delegate_info;
354
355 GeometryInfo
356 geometry_info;
357
358 Image
359 *image,
360 *next,
361 *postscript_image;
362
363 ImageInfo
364 *read_info;
365
366 int
367 c,
368 file;
369
370 MagickBooleanType
371 cmyk,
372 skip,
373 status;
374
375 MagickStatusType
376 flags;
377
378 PointInfo
379 delta;
380
381 RectangleInfo
382 page;
383
384 register char
385 *p;
386
cristybb503372010-05-27 20:51:26 +0000387 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000388 i;
389
390 SegmentInfo
391 bounds,
392 hires_bounds;
393
394 short int
395 hex_digits[256];
396
397 size_t
cristy4b1c78f2011-11-11 15:09:30 +0000398 length,
399 priority;
cristy3ed852e2009-09-05 21:47:34 +0000400
401 ssize_t
402 count;
403
404 StringInfo
405 *profile;
406
cristyf2faecf2010-05-28 19:19:36 +0000407 unsigned long
cristy3ed852e2009-09-05 21:47:34 +0000408 columns,
409 extent,
410 language_level,
411 pages,
412 rows,
413 scene,
414 spotcolor;
415
416 /*
417 Open image file.
418 */
419 assert(image_info != (const ImageInfo *) NULL);
420 assert(image_info->signature == MagickSignature);
421 if (image_info->debug != MagickFalse)
422 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
423 image_info->filename);
424 assert(exception != (ExceptionInfo *) NULL);
425 assert(exception->signature == MagickSignature);
cristy9950d572011-10-01 18:22:35 +0000426 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +0000427 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
428 if (status == MagickFalse)
429 {
430 image=DestroyImageList(image);
431 return((Image *) NULL);
432 }
433 status=AcquireUniqueSymbolicLink(image_info->filename,input_filename);
434 if (status == MagickFalse)
435 {
436 ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
437 image_info->filename);
438 image=DestroyImageList(image);
439 return((Image *) NULL);
440 }
441 /*
442 Initialize hex values.
443 */
444 (void) ResetMagickMemory(hex_digits,0,sizeof(hex_digits));
445 hex_digits[(int) '0']=0;
446 hex_digits[(int) '1']=1;
447 hex_digits[(int) '2']=2;
448 hex_digits[(int) '3']=3;
449 hex_digits[(int) '4']=4;
450 hex_digits[(int) '5']=5;
451 hex_digits[(int) '6']=6;
452 hex_digits[(int) '7']=7;
453 hex_digits[(int) '8']=8;
454 hex_digits[(int) '9']=9;
455 hex_digits[(int) 'a']=10;
456 hex_digits[(int) 'b']=11;
457 hex_digits[(int) 'c']=12;
458 hex_digits[(int) 'd']=13;
459 hex_digits[(int) 'e']=14;
460 hex_digits[(int) 'f']=15;
461 hex_digits[(int) 'A']=10;
462 hex_digits[(int) 'B']=11;
463 hex_digits[(int) 'C']=12;
464 hex_digits[(int) 'D']=13;
465 hex_digits[(int) 'E']=14;
466 hex_digits[(int) 'F']=15;
467 /*
468 Set the page density.
469 */
470 delta.x=DefaultResolution;
471 delta.y=DefaultResolution;
cristy2a11bef2011-10-28 18:33:11 +0000472 if ((image->resolution.x == 0.0) || (image->resolution.y == 0.0))
cristy3ed852e2009-09-05 21:47:34 +0000473 {
474 flags=ParseGeometry(PSDensityGeometry,&geometry_info);
cristy2a11bef2011-10-28 18:33:11 +0000475 image->resolution.x=geometry_info.rho;
476 image->resolution.y=geometry_info.sigma;
cristy3ed852e2009-09-05 21:47:34 +0000477 if ((flags & SigmaValue) == 0)
cristy2a11bef2011-10-28 18:33:11 +0000478 image->resolution.y=image->resolution.x;
cristy3ed852e2009-09-05 21:47:34 +0000479 }
cristy6cde06a2011-11-24 00:08:43 +0000480 if (image_info->density != (char *) NULL)
481 {
482 flags=ParseGeometry(image_info->density,&geometry_info);
483 image->resolution.x=geometry_info.rho;
484 image->resolution.y=geometry_info.sigma;
485 if ((flags & SigmaValue) == 0)
486 image->resolution.y=image->resolution.x;
487 }
488 (void) ParseAbsoluteGeometry(PSPageGeometry,&page);
489 if (image_info->page != (char *) NULL)
490 (void) ParseAbsoluteGeometry(image_info->page,&page);
491 page.width=(size_t) ceil((double) (page.width*image->resolution.x/delta.x)-
492 0.5);
493 page.height=(size_t) ceil((double) (page.height*image->resolution.y/delta.y)-
494 0.5);
cristy3ed852e2009-09-05 21:47:34 +0000495 /*
496 Determine page geometry from the Postscript bounding box.
497 */
498 (void) ResetMagickMemory(&bounds,0,sizeof(bounds));
cristy3ed852e2009-09-05 21:47:34 +0000499 (void) ResetMagickMemory(command,0,sizeof(command));
cristy6cde06a2011-11-24 00:08:43 +0000500 cmyk=image_info->colorspace == CMYKColorspace ? MagickTrue : MagickFalse;
501 (void) ResetMagickMemory(&hires_bounds,0,sizeof(hires_bounds));
cristy4b1c78f2011-11-11 15:09:30 +0000502 priority=0;
cristy3ed852e2009-09-05 21:47:34 +0000503 rows=0;
504 extent=0;
505 spotcolor=0;
506 language_level=1;
cristy3ed852e2009-09-05 21:47:34 +0000507 pages=(~0UL);
cristy6cde06a2011-11-24 00:08:43 +0000508 skip=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +0000509 p=command;
510 for (c=ReadBlobByte(image); c != EOF; c=ReadBlobByte(image))
511 {
512 /*
513 Note document structuring comments.
514 */
515 *p++=(char) c;
516 if ((strchr("\n\r%",c) == (char *) NULL) &&
517 ((size_t) (p-command) < (MaxTextExtent-1)))
518 continue;
519 *p='\0';
520 p=command;
521 /*
522 Skip %%BeginDocument thru %%EndDocument.
523 */
524 if (LocaleNCompare(BeginDocument,command,strlen(BeginDocument)) == 0)
525 skip=MagickTrue;
526 if (LocaleNCompare(EndDocument,command,strlen(EndDocument)) == 0)
527 skip=MagickFalse;
528 if (skip != MagickFalse)
529 continue;
530 if (LocaleNCompare(PostscriptLevel,command,strlen(PostscriptLevel)) == 0)
531 {
cristyd15e6592011-10-15 00:13:06 +0000532 (void) SetImageProperty(image,"ps:Level",command+4,exception);
cristy3ed852e2009-09-05 21:47:34 +0000533 if (GlobExpression(command,"*EPSF-*",MagickTrue) != MagickFalse)
534 pages=1;
535 }
536 if (LocaleNCompare(LanguageLevel,command,strlen(LanguageLevel)) == 0)
537 (void) sscanf(command,LanguageLevel " %lu",&language_level);
538 if (LocaleNCompare(Pages,command,strlen(Pages)) == 0)
539 (void) sscanf(command,Pages " %lu",&pages);
cristy97841ba2010-09-02 15:48:08 +0000540 if (LocaleNCompare(ImageData,command,strlen(ImageData)) == 0)
cristy3ed852e2009-09-05 21:47:34 +0000541 (void) sscanf(command,ImageData " %lu %lu",&columns,&rows);
cristy97841ba2010-09-02 15:48:08 +0000542 if (LocaleNCompare(ICCProfile,command,strlen(ICCProfile)) == 0)
cristy3ed852e2009-09-05 21:47:34 +0000543 {
544 unsigned char
545 *datum;
546
547 /*
548 Read ICC profile.
549 */
550 profile=AcquireStringInfo(65536);
551 for (i=0; (c=ProfileInteger(image,hex_digits)) != EOF; i++)
552 {
553 SetStringInfoLength(profile,(size_t) i+1);
554 datum=GetStringInfoDatum(profile);
555 datum[i]=(unsigned char) c;
556 }
cristyd15e6592011-10-15 00:13:06 +0000557 (void) SetImageProfile(image,"icc",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +0000558 profile=DestroyStringInfo(profile);
559 continue;
560 }
561 if (LocaleNCompare(PhotoshopProfile,command,strlen(PhotoshopProfile)) == 0)
562 {
563 unsigned char
564 *p;
565
566 /*
567 Read Photoshop profile.
568 */
569 count=(ssize_t) sscanf(command,PhotoshopProfile " %lu",&extent);
570 if (count != 1)
571 continue;
572 length=extent;
cristy8723e4b2011-09-01 13:11:19 +0000573 profile=BlobToStringInfo((const void *) NULL,length);
cristy3ed852e2009-09-05 21:47:34 +0000574 p=GetStringInfoDatum(profile);
cristybb503372010-05-27 20:51:26 +0000575 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +0000576 *p++=(unsigned char) ProfileInteger(image,hex_digits);
cristyd15e6592011-10-15 00:13:06 +0000577 (void) SetImageProfile(image,"8bim",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +0000578 profile=DestroyStringInfo(profile);
579 continue;
580 }
581 if (LocaleNCompare(BeginXMPPacket,command,strlen(BeginXMPPacket)) == 0)
582 {
583 register size_t
584 i;
585
586 /*
587 Read XMP profile.
588 */
589 p=command;
590 profile=StringToStringInfo(command);
591 for (i=GetStringInfoLength(profile)-1; c != EOF; i++)
592 {
593 SetStringInfoLength(profile,i+1);
594 c=ReadBlobByte(image);
595 GetStringInfoDatum(profile)[i]=(unsigned char) c;
596 *p++=(char) c;
597 if ((strchr("\n\r%",c) == (char *) NULL) &&
598 ((size_t) (p-command) < (MaxTextExtent-1)))
599 continue;
600 *p='\0';
601 p=command;
602 if (LocaleNCompare(EndXMPPacket,command,strlen(EndXMPPacket)) == 0)
603 break;
604 }
605 SetStringInfoLength(profile,i);
cristyd15e6592011-10-15 00:13:06 +0000606 (void) SetImageProfile(image,"xmp",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +0000607 profile=DestroyStringInfo(profile);
608 continue;
609 }
610 /*
611 Is this a CMYK document?
612 */
613 length=strlen(DocumentProcessColors);
614 if (LocaleNCompare(DocumentProcessColors,command,length) == 0)
615 {
616 if ((GlobExpression(command,"*Cyan*",MagickTrue) != MagickFalse) ||
617 (GlobExpression(command,"*Magenta*",MagickTrue) != MagickFalse) ||
618 (GlobExpression(command,"*Yellow*",MagickTrue) != MagickFalse))
619 cmyk=MagickTrue;
620 }
621 if (LocaleNCompare(CMYKCustomColor,command,strlen(CMYKCustomColor)) == 0)
622 cmyk=MagickTrue;
cristy01ca8922010-03-17 12:20:37 +0000623 if (LocaleNCompare(CMYKProcessColor,command,strlen(CMYKProcessColor)) == 0)
624 cmyk=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +0000625 length=strlen(DocumentCustomColors);
626 if ((LocaleNCompare(DocumentCustomColors,command,length) == 0) ||
627 (LocaleNCompare(CMYKCustomColor,command,strlen(CMYKCustomColor)) == 0) ||
628 (LocaleNCompare(SpotColor,command,strlen(SpotColor)) == 0))
629 {
630 char
631 property[MaxTextExtent],
632 *value;
633
634 register char
635 *p;
636
637 /*
638 Note spot names.
639 */
cristyb51dff52011-05-19 16:55:47 +0000640 (void) FormatLocaleString(property,MaxTextExtent,"ps:SpotColor-%.20g",
cristye8c25f92010-06-03 00:53:06 +0000641 (double) (spotcolor++));
cristy3ed852e2009-09-05 21:47:34 +0000642 for (p=command; *p != '\0'; p++)
643 if (isspace((int) (unsigned char) *p) != 0)
644 break;
645 value=AcquireString(p);
646 (void) SubstituteString(&value,"(","");
647 (void) SubstituteString(&value,")","");
648 (void) StripString(value);
cristyd15e6592011-10-15 00:13:06 +0000649 (void) SetImageProperty(image,property,value,exception);
cristy3ed852e2009-09-05 21:47:34 +0000650 value=DestroyString(value);
651 continue;
652 }
cristy6cde06a2011-11-24 00:08:43 +0000653 if (image_info->page != (char *) NULL)
654 continue;
cristy3ed852e2009-09-05 21:47:34 +0000655 /*
656 Note region defined by bounding box.
657 */
658 count=0;
cristy4b1c78f2011-11-11 15:09:30 +0000659 i=0;
cristy3ed852e2009-09-05 21:47:34 +0000660 if (LocaleNCompare(BoundingBox,command,strlen(BoundingBox)) == 0)
cristy3ed852e2009-09-05 21:47:34 +0000661 {
cristy4b1c78f2011-11-11 15:09:30 +0000662 count=(ssize_t) sscanf(command,BoundingBox " %lf %lf %lf %lf",
663 &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
664 i=2;
cristy3ed852e2009-09-05 21:47:34 +0000665 }
cristy4b1c78f2011-11-11 15:09:30 +0000666 if (LocaleNCompare(DocumentMedia,command,strlen(DocumentMedia)) == 0)
667 {
668 count=(ssize_t) sscanf(command,DocumentMedia " %lf %lf %lf %lf",
669 &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
670 i=1;
671 }
672 if (LocaleNCompare(HiResBoundingBox,command,strlen(HiResBoundingBox)) == 0)
673 {
674 count=(ssize_t) sscanf(command,HiResBoundingBox " %lf %lf %lf %lf",
675 &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
676 i=3;
677 }
678 if (LocaleNCompare(PageBoundingBox,command,strlen(PageBoundingBox)) == 0)
679 {
680 count=(ssize_t) sscanf(command,PageBoundingBox " %lf %lf %lf %lf",
681 &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
682 i=1;
683 }
684 if (LocaleNCompare(PageMedia,command,strlen(PageMedia)) == 0)
685 {
686 count=(ssize_t) sscanf(command,PageMedia " %lf %lf %lf %lf",
687 &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
688 i=1;
689 }
cristy3fac9ec2011-11-17 18:04:39 +0000690 if ((count != 4) || (i < (ssize_t) priority))
cristy4b1c78f2011-11-11 15:09:30 +0000691 continue;
cristyad29a8c2012-01-25 23:06:43 +0000692 if ((fabs(bounds.x2-bounds.x1) <= fabs(hires_bounds.x2-hires_bounds.x1)) ||
693 (fabs(bounds.y2-bounds.y1) <= fabs(hires_bounds.y2-hires_bounds.y1)))
694 continue;
cristy4b1c78f2011-11-11 15:09:30 +0000695 hires_bounds=bounds;
696 priority=i;
cristy3ed852e2009-09-05 21:47:34 +0000697 }
cristy7b0fcf12011-11-24 00:24:07 +0000698 if ((fabs(hires_bounds.x2-hires_bounds.x1) >= MagickEpsilon) &&
cristy614bc082011-11-24 00:49:08 +0000699 (fabs(hires_bounds.y2-hires_bounds.y1) >= MagickEpsilon))
cristy4b1c78f2011-11-11 15:09:30 +0000700 {
701 /*
702 Set Postscript render geometry.
703 */
704 (void) FormatLocaleString(geometry,MaxTextExtent,"%gx%g%+.15g%+.15g",
705 hires_bounds.x2-hires_bounds.x1,hires_bounds.y2-hires_bounds.y1,
706 hires_bounds.x1,hires_bounds.y1);
707 (void) SetImageProperty(image,"ps:HiResBoundingBox",geometry,exception);
cristy6cde06a2011-11-24 00:08:43 +0000708 page.width=(size_t) ceil((double) ((hires_bounds.x2-hires_bounds.x1)*
709 image->resolution.x/delta.x)-0.5);
710 page.height=(size_t) ceil((double) ((hires_bounds.y2-hires_bounds.y1)*
711 image->resolution.y/delta.y)-0.5);
cristy4b1c78f2011-11-11 15:09:30 +0000712 }
cristy3ed852e2009-09-05 21:47:34 +0000713 (void) CloseBlob(image);
cristy510d06a2011-07-06 23:43:54 +0000714 if (IsRGBColorspace(image_info->colorspace) != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000715 cmyk=MagickFalse;
716 /*
717 Create Ghostscript control file.
718 */
719 file=AcquireUniqueFileResource(postscript_filename);
720 if (file == -1)
721 {
cristyc82a27b2011-10-21 01:07:16 +0000722 ThrowFileException(exception,FileOpenError,"UnableToOpenFile",
cristy3ed852e2009-09-05 21:47:34 +0000723 image_info->filename);
724 image=DestroyImageList(image);
725 return((Image *) NULL);
726 }
727 (void) CopyMagickString(command,"/setpagedevice {pop} bind 1 index where {"
728 "dup wcheck {3 1 roll put} {pop def} ifelse} {def} ifelse\n"
729 "<</UseCIEColor true>>setpagedevice\n",MaxTextExtent);
730 count=write(file,command,(unsigned int) strlen(command));
cristyc39e3d62010-10-14 16:52:01 +0000731 if (image_info->page == (char *) NULL)
732 {
733 char
734 translate_geometry[MaxTextExtent];
735
cristyb51dff52011-05-19 16:55:47 +0000736 (void) FormatLocaleString(translate_geometry,MaxTextExtent,
cristyc39e3d62010-10-14 16:52:01 +0000737 "%g %g translate\n",-bounds.x1,-bounds.y1);
738 count=write(file,translate_geometry,(unsigned int)
739 strlen(translate_geometry));
740 }
cristy3ed852e2009-09-05 21:47:34 +0000741 file=close(file)-1;
742 /*
743 Render Postscript with the Ghostscript delegate.
744 */
745 if (image_info->monochrome != MagickFalse)
746 delegate_info=GetDelegateInfo("ps:mono",(char *) NULL,exception);
747 else
748 if (cmyk != MagickFalse)
749 delegate_info=GetDelegateInfo("ps:cmyk",(char *) NULL,exception);
750 else
cristya97426c2011-02-04 01:41:27 +0000751 delegate_info=GetDelegateInfo("ps:alpha",(char *) NULL,exception);
cristy3ed852e2009-09-05 21:47:34 +0000752 if (delegate_info == (const DelegateInfo *) NULL)
753 {
754 (void) RelinquishUniqueFileResource(postscript_filename);
755 image=DestroyImageList(image);
756 return((Image *) NULL);
757 }
758 *options='\0';
cristyb51dff52011-05-19 16:55:47 +0000759 (void) FormatLocaleString(density,MaxTextExtent,"%gx%g",
cristy2a11bef2011-10-28 18:33:11 +0000760 image->resolution.x,image->resolution.y);
cristyc8488582012-02-28 12:03:48 +0000761 (void) FormatLocaleString(options,MaxTextExtent,"-g%.20gx%.20g ",(double)
762 page.width,(double) page.height);
cristy3ed852e2009-09-05 21:47:34 +0000763 read_info=CloneImageInfo(image_info);
764 *read_info->magick='\0';
765 if (read_info->number_scenes != 0)
766 {
767 char
768 pages[MaxTextExtent];
769
cristyb51dff52011-05-19 16:55:47 +0000770 (void) FormatLocaleString(pages,MaxTextExtent,"-dFirstPage=%.20g "
cristye8c25f92010-06-03 00:53:06 +0000771 "-dLastPage=%.20g",(double) read_info->scene+1,(double)
cristyf2faecf2010-05-28 19:19:36 +0000772 (read_info->scene+read_info->number_scenes));
cristy3ed852e2009-09-05 21:47:34 +0000773 (void) ConcatenateMagickString(options,pages,MaxTextExtent);
774 read_info->number_scenes=0;
775 if (read_info->scenes != (char *) NULL)
776 *read_info->scenes='\0';
777 }
778 option=GetImageOption(image_info,"ps:use-cropbox");
779 if ((option != (const char *) NULL) && (IsMagickTrue(option) != MagickFalse))
780 (void) ConcatenateMagickString(options,"-dEPSCrop ",MaxTextExtent);
781 (void) CopyMagickString(filename,read_info->filename,MaxTextExtent);
cristya97426c2011-02-04 01:41:27 +0000782 (void) AcquireUniqueFilename(filename);
783 (void) ConcatenateMagickString(filename,"-%08d",MaxTextExtent);
cristyb51dff52011-05-19 16:55:47 +0000784 (void) FormatLocaleString(command,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +0000785 GetDelegateCommands(delegate_info),
786 read_info->antialias != MagickFalse ? 4 : 1,
cristya97426c2011-02-04 01:41:27 +0000787 read_info->antialias != MagickFalse ? 4 : 1,density,options,filename,
788 postscript_filename,input_filename);
cristyb32b90a2009-09-07 21:45:48 +0000789 status=InvokePostscriptDelegate(read_info->verbose,command,exception);
cristya97426c2011-02-04 01:41:27 +0000790 (void) InterpretImageFilename(image_info,image,filename,1,
cristy6fccee12011-10-20 18:43:18 +0000791 read_info->filename,exception);
cristy41083a42009-09-07 23:47:59 +0000792 if ((status == MagickFalse) ||
cristy3ed852e2009-09-05 21:47:34 +0000793 (IsPostscriptRendered(read_info->filename) == MagickFalse))
794 {
795 (void) ConcatenateMagickString(command," -c showpage",MaxTextExtent);
cristyb32b90a2009-09-07 21:45:48 +0000796 status=InvokePostscriptDelegate(read_info->verbose,command,exception);
cristy3ed852e2009-09-05 21:47:34 +0000797 }
cristy3ed852e2009-09-05 21:47:34 +0000798 (void) RelinquishUniqueFileResource(postscript_filename);
cristy3ed852e2009-09-05 21:47:34 +0000799 (void) RelinquishUniqueFileResource(input_filename);
cristya97426c2011-02-04 01:41:27 +0000800 postscript_image=(Image *) NULL;
801 if (status == MagickFalse)
802 for (i=1; ; i++)
803 {
cristya97426c2011-02-04 01:41:27 +0000804 (void) InterpretImageFilename(image_info,image,filename,(int) i,
cristy6fccee12011-10-20 18:43:18 +0000805 read_info->filename,exception);
cristya97426c2011-02-04 01:41:27 +0000806 if (IsPostscriptRendered(read_info->filename) == MagickFalse)
807 break;
808 (void) RelinquishUniqueFileResource(read_info->filename);
809 }
810 else
811 for (i=1; ; i++)
812 {
cristya97426c2011-02-04 01:41:27 +0000813 (void) InterpretImageFilename(image_info,image,filename,(int) i,
cristy6fccee12011-10-20 18:43:18 +0000814 read_info->filename,exception);
cristya97426c2011-02-04 01:41:27 +0000815 if (IsPostscriptRendered(read_info->filename) == MagickFalse)
816 break;
817 next=ReadImage(read_info,exception);
818 (void) RelinquishUniqueFileResource(read_info->filename);
819 if (next == (Image *) NULL)
820 break;
821 AppendImageToList(&postscript_image,next);
822 }
823 (void) RelinquishUniqueFileResource(read_info->filename);
cristy3ed852e2009-09-05 21:47:34 +0000824 read_info=DestroyImageInfo(read_info);
825 if (postscript_image == (Image *) NULL)
826 {
827 image=DestroyImageList(image);
828 ThrowFileException(exception,DelegateError,"PostscriptDelegateFailed",
829 image_info->filename);
830 return((Image *) NULL);
831 }
832 if (LocaleCompare(postscript_image->magick,"BMP") == 0)
833 {
834 Image
835 *cmyk_image;
836
837 cmyk_image=ConsolidateCMYKImages(postscript_image,exception);
838 if (cmyk_image != (Image *) NULL)
839 {
840 postscript_image=DestroyImageList(postscript_image);
841 postscript_image=cmyk_image;
842 }
843 }
844 if (image_info->number_scenes != 0)
845 {
846 Image
847 *clone_image;
848
cristybb503372010-05-27 20:51:26 +0000849 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000850 i;
851
852 /*
853 Add place holder images to meet the subimage specification requirement.
854 */
cristybb503372010-05-27 20:51:26 +0000855 for (i=0; i < (ssize_t) image_info->scene; i++)
cristy3ed852e2009-09-05 21:47:34 +0000856 {
857 clone_image=CloneImage(postscript_image,1,1,MagickTrue,exception);
858 if (clone_image != (Image *) NULL)
859 PrependImageToList(&postscript_image,clone_image);
860 }
861 }
862 do
863 {
864 (void) CopyMagickString(postscript_image->filename,filename,MaxTextExtent);
865 if (columns != 0)
866 postscript_image->magick_columns=columns;
867 if (rows != 0)
868 postscript_image->magick_rows=rows;
869 postscript_image->page=page;
870 (void) CloneImageProfiles(postscript_image,image);
871 (void) CloneImageProperties(postscript_image,image);
872 next=SyncNextImageInList(postscript_image);
873 if (next != (Image *) NULL)
874 postscript_image=next;
875 } while (next != (Image *) NULL);
876 image=DestroyImageList(image);
877 scene=0;
878 for (next=GetFirstImageInList(postscript_image); next != (Image *) NULL; )
879 {
880 next->scene=scene++;
881 next=GetNextImageInList(next);
882 }
883 return(GetFirstImageInList(postscript_image));
884}
885
886/*
887%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
888% %
889% %
890% %
891% R e g i s t e r P S I m a g e %
892% %
893% %
894% %
895%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
896%
897% RegisterPSImage() adds properties for the PS image format to
898% the list of supported formats. The properties include the image format
899% tag, a method to read and/or write the format, whether the format
900% supports the saving of more than one frame to the same file or blob,
901% whether the format supports native in-memory I/O, and a brief
902% description of the format.
903%
904% The format of the RegisterPSImage method is:
905%
cristybb503372010-05-27 20:51:26 +0000906% size_t RegisterPSImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000907%
908*/
cristybb503372010-05-27 20:51:26 +0000909ModuleExport size_t RegisterPSImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000910{
911 MagickInfo
912 *entry;
913
914 entry=SetMagickInfo("EPI");
915 entry->decoder=(DecodeImageHandler *) ReadPSImage;
916 entry->encoder=(EncodeImageHandler *) WritePSImage;
917 entry->magick=(IsImageFormatHandler *) IsPS;
918 entry->adjoin=MagickFalse;
919 entry->blob_support=MagickFalse;
920 entry->seekable_stream=MagickTrue;
921 entry->thread_support=EncoderThreadSupport;
922 entry->description=ConstantString(
923 "Encapsulated PostScript Interchange format");
924 entry->module=ConstantString("PS");
925 (void) RegisterMagickInfo(entry);
926 entry=SetMagickInfo("EPS");
927 entry->decoder=(DecodeImageHandler *) ReadPSImage;
928 entry->encoder=(EncodeImageHandler *) WritePSImage;
929 entry->magick=(IsImageFormatHandler *) IsPS;
930 entry->adjoin=MagickFalse;
931 entry->blob_support=MagickFalse;
932 entry->seekable_stream=MagickTrue;
933 entry->thread_support=EncoderThreadSupport;
934 entry->description=ConstantString("Encapsulated PostScript");
935 entry->module=ConstantString("PS");
936 (void) RegisterMagickInfo(entry);
937 entry=SetMagickInfo("EPSF");
938 entry->decoder=(DecodeImageHandler *) ReadPSImage;
939 entry->encoder=(EncodeImageHandler *) WritePSImage;
940 entry->magick=(IsImageFormatHandler *) IsPS;
941 entry->adjoin=MagickFalse;
942 entry->blob_support=MagickFalse;
943 entry->seekable_stream=MagickTrue;
944 entry->description=ConstantString("Encapsulated PostScript");
945 entry->module=ConstantString("PS");
946 (void) RegisterMagickInfo(entry);
947 entry=SetMagickInfo("EPSI");
948 entry->decoder=(DecodeImageHandler *) ReadPSImage;
949 entry->encoder=(EncodeImageHandler *) WritePSImage;
950 entry->magick=(IsImageFormatHandler *) IsPS;
951 entry->adjoin=MagickFalse;
952 entry->blob_support=MagickFalse;
953 entry->seekable_stream=MagickTrue;
954 entry->thread_support=EncoderThreadSupport;
955 entry->description=ConstantString(
956 "Encapsulated PostScript Interchange format");
957 entry->module=ConstantString("PS");
958 (void) RegisterMagickInfo(entry);
959 entry=SetMagickInfo("PS");
960 entry->decoder=(DecodeImageHandler *) ReadPSImage;
961 entry->encoder=(EncodeImageHandler *) WritePSImage;
962 entry->magick=(IsImageFormatHandler *) IsPS;
963 entry->module=ConstantString("PS");
964 entry->blob_support=MagickFalse;
965 entry->seekable_stream=MagickTrue;
966 entry->thread_support=EncoderThreadSupport;
967 entry->description=ConstantString("PostScript");
968 (void) RegisterMagickInfo(entry);
969 return(MagickImageCoderSignature);
970}
971
972/*
973%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
974% %
975% %
976% %
977% U n r e g i s t e r P S I m a g e %
978% %
979% %
980% %
981%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
982%
983% UnregisterPSImage() removes format registrations made by the
984% PS module from the list of supported formats.
985%
986% The format of the UnregisterPSImage method is:
987%
988% UnregisterPSImage(void)
989%
990*/
991ModuleExport void UnregisterPSImage(void)
992{
993 (void) UnregisterMagickInfo("EPI");
994 (void) UnregisterMagickInfo("EPS");
995 (void) UnregisterMagickInfo("EPSF");
996 (void) UnregisterMagickInfo("EPSI");
997 (void) UnregisterMagickInfo("PS");
998}
999
1000/*
1001%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1002% %
1003% %
1004% %
1005% W r i t e P S I m a g e %
1006% %
1007% %
1008% %
1009%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1010%
1011% WritePSImage translates an image to encapsulated Postscript
1012% Level I for printing. If the supplied geometry is null, the image is
1013% centered on the Postscript page. Otherwise, the image is positioned as
1014% specified by the geometry.
1015%
1016% The format of the WritePSImage method is:
1017%
cristy1e178e72011-08-28 19:44:34 +00001018% MagickBooleanType WritePSImage(const ImageInfo *image_info,
1019% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001020%
1021% A description of each parameter follows:
1022%
1023% o image_info: the image info.
1024%
1025% o image: the image.
1026%
cristy1e178e72011-08-28 19:44:34 +00001027% o exception: return any errors or warnings in this structure.
1028%
cristy3ed852e2009-09-05 21:47:34 +00001029*/
1030
1031static inline size_t MagickMin(const size_t x,const size_t y)
1032{
1033 if (x < y)
1034 return(x);
1035 return(y);
1036}
1037
1038static inline unsigned char *PopHexPixel(const char **hex_digits,
cristybb503372010-05-27 20:51:26 +00001039 const size_t pixel,unsigned char *pixels)
cristy3ed852e2009-09-05 21:47:34 +00001040{
1041 register const char
1042 *hex;
1043
1044 hex=hex_digits[pixel];
1045 *pixels++=(unsigned char) (*hex++);
1046 *pixels++=(unsigned char) (*hex);
1047 return(pixels);
1048}
1049
cristy1e178e72011-08-28 19:44:34 +00001050static MagickBooleanType WritePSImage(const ImageInfo *image_info,Image *image,
1051 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001052{
1053#define WriteRunlengthPacket(image,pixel,length,p) \
1054{ \
1055 if ((image->matte != MagickFalse) && \
cristy4c08aed2011-07-01 19:47:50 +00001056 (GetPixelAlpha(image,p) == (Quantum) TransparentAlpha)) \
cristy3ed852e2009-09-05 21:47:34 +00001057 { \
1058 q=PopHexPixel(hex_digits,0xff,q); \
1059 q=PopHexPixel(hex_digits,0xff,q); \
1060 q=PopHexPixel(hex_digits,0xff,q); \
1061 } \
1062 else \
1063 { \
1064 q=PopHexPixel(hex_digits,ScaleQuantumToChar(pixel.red),q); \
1065 q=PopHexPixel(hex_digits,ScaleQuantumToChar(pixel.green),q); \
1066 q=PopHexPixel(hex_digits,ScaleQuantumToChar(pixel.blue),q); \
1067 } \
cristy9f027d12011-09-21 01:17:17 +00001068 q=PopHexPixel(hex_digits,(size_t) MagickMin(length,0xff),q); \
cristy3ed852e2009-09-05 21:47:34 +00001069}
1070
1071 static const char
1072 *hex_digits[] =
1073 {
1074 "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0A", "0B",
1075 "0C", "0D", "0E", "0F", "10", "11", "12", "13", "14", "15", "16", "17",
1076 "18", "19", "1A", "1B", "1C", "1D", "1E", "1F", "20", "21", "22", "23",
1077 "24", "25", "26", "27", "28", "29", "2A", "2B", "2C", "2D", "2E", "2F",
1078 "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3A", "3B",
1079 "3C", "3D", "3E", "3F", "40", "41", "42", "43", "44", "45", "46", "47",
1080 "48", "49", "4A", "4B", "4C", "4D", "4E", "4F", "50", "51", "52", "53",
1081 "54", "55", "56", "57", "58", "59", "5A", "5B", "5C", "5D", "5E", "5F",
1082 "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6A", "6B",
1083 "6C", "6D", "6E", "6F", "70", "71", "72", "73", "74", "75", "76", "77",
1084 "78", "79", "7A", "7B", "7C", "7D", "7E", "7F", "80", "81", "82", "83",
1085 "84", "85", "86", "87", "88", "89", "8A", "8B", "8C", "8D", "8E", "8F",
1086 "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9A", "9B",
1087 "9C", "9D", "9E", "9F", "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7",
1088 "A8", "A9", "AA", "AB", "AC", "AD", "AE", "AF", "B0", "B1", "B2", "B3",
1089 "B4", "B5", "B6", "B7", "B8", "B9", "BA", "BB", "BC", "BD", "BE", "BF",
1090 "C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "CA", "CB",
1091 "CC", "CD", "CE", "CF", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
1092 "D8", "D9", "DA", "DB", "DC", "DD", "DE", "DF", "E0", "E1", "E2", "E3",
1093 "E4", "E5", "E6", "E7", "E8", "E9", "EA", "EB", "EC", "ED", "EE", "EF",
1094 "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "FA", "FB",
1095 "FC", "FD", "FE", "FF", (char *) NULL
1096 },
1097 *PostscriptProlog[]=
1098 {
1099 "%%BeginProlog",
1100 "%",
1101 "% Display a color image. The image is displayed in color on",
1102 "% Postscript viewers or printers that support color, otherwise",
1103 "% it is displayed as grayscale.",
1104 "%",
1105 "/DirectClassPacket",
1106 "{",
1107 " %",
1108 " % Get a DirectClass packet.",
1109 " %",
1110 " % Parameters:",
1111 " % red.",
1112 " % green.",
1113 " % blue.",
1114 " % length: number of pixels minus one of this color (optional).",
1115 " %",
1116 " currentfile color_packet readhexstring pop pop",
1117 " compression 0 eq",
1118 " {",
1119 " /number_pixels 3 def",
1120 " }",
1121 " {",
1122 " currentfile byte readhexstring pop 0 get",
1123 " /number_pixels exch 1 add 3 mul def",
1124 " } ifelse",
1125 " 0 3 number_pixels 1 sub",
1126 " {",
1127 " pixels exch color_packet putinterval",
1128 " } for",
1129 " pixels 0 number_pixels getinterval",
1130 "} bind def",
1131 "",
1132 "/DirectClassImage",
1133 "{",
1134 " %",
1135 " % Display a DirectClass image.",
1136 " %",
1137 " systemdict /colorimage known",
1138 " {",
1139 " columns rows 8",
1140 " [",
1141 " columns 0 0",
1142 " rows neg 0 rows",
1143 " ]",
1144 " { DirectClassPacket } false 3 colorimage",
1145 " }",
1146 " {",
1147 " %",
1148 " % No colorimage operator; convert to grayscale.",
1149 " %",
1150 " columns rows 8",
1151 " [",
1152 " columns 0 0",
1153 " rows neg 0 rows",
1154 " ]",
1155 " { GrayDirectClassPacket } image",
1156 " } ifelse",
1157 "} bind def",
1158 "",
1159 "/GrayDirectClassPacket",
1160 "{",
1161 " %",
1162 " % Get a DirectClass packet; convert to grayscale.",
1163 " %",
1164 " % Parameters:",
1165 " % red",
1166 " % green",
1167 " % blue",
1168 " % length: number of pixels minus one of this color (optional).",
1169 " %",
1170 " currentfile color_packet readhexstring pop pop",
1171 " color_packet 0 get 0.299 mul",
1172 " color_packet 1 get 0.587 mul add",
1173 " color_packet 2 get 0.114 mul add",
1174 " cvi",
1175 " /gray_packet exch def",
1176 " compression 0 eq",
1177 " {",
1178 " /number_pixels 1 def",
1179 " }",
1180 " {",
1181 " currentfile byte readhexstring pop 0 get",
1182 " /number_pixels exch 1 add def",
1183 " } ifelse",
1184 " 0 1 number_pixels 1 sub",
1185 " {",
1186 " pixels exch gray_packet put",
1187 " } for",
1188 " pixels 0 number_pixels getinterval",
1189 "} bind def",
1190 "",
1191 "/GrayPseudoClassPacket",
1192 "{",
1193 " %",
1194 " % Get a PseudoClass packet; convert to grayscale.",
1195 " %",
1196 " % Parameters:",
1197 " % index: index into the colormap.",
1198 " % length: number of pixels minus one of this color (optional).",
1199 " %",
1200 " currentfile byte readhexstring pop 0 get",
1201 " /offset exch 3 mul def",
1202 " /color_packet colormap offset 3 getinterval def",
1203 " color_packet 0 get 0.299 mul",
1204 " color_packet 1 get 0.587 mul add",
1205 " color_packet 2 get 0.114 mul add",
1206 " cvi",
1207 " /gray_packet exch def",
1208 " compression 0 eq",
1209 " {",
1210 " /number_pixels 1 def",
1211 " }",
1212 " {",
1213 " currentfile byte readhexstring pop 0 get",
1214 " /number_pixels exch 1 add def",
1215 " } ifelse",
1216 " 0 1 number_pixels 1 sub",
1217 " {",
1218 " pixels exch gray_packet put",
1219 " } for",
1220 " pixels 0 number_pixels getinterval",
1221 "} bind def",
1222 "",
1223 "/PseudoClassPacket",
1224 "{",
1225 " %",
1226 " % Get a PseudoClass packet.",
1227 " %",
1228 " % Parameters:",
1229 " % index: index into the colormap.",
1230 " % length: number of pixels minus one of this color (optional).",
1231 " %",
1232 " currentfile byte readhexstring pop 0 get",
1233 " /offset exch 3 mul def",
1234 " /color_packet colormap offset 3 getinterval def",
1235 " compression 0 eq",
1236 " {",
1237 " /number_pixels 3 def",
1238 " }",
1239 " {",
1240 " currentfile byte readhexstring pop 0 get",
1241 " /number_pixels exch 1 add 3 mul def",
1242 " } ifelse",
1243 " 0 3 number_pixels 1 sub",
1244 " {",
1245 " pixels exch color_packet putinterval",
1246 " } for",
1247 " pixels 0 number_pixels getinterval",
1248 "} bind def",
1249 "",
1250 "/PseudoClassImage",
1251 "{",
1252 " %",
1253 " % Display a PseudoClass image.",
1254 " %",
1255 " % Parameters:",
1256 " % class: 0-PseudoClass or 1-Grayscale.",
1257 " %",
1258 " currentfile buffer readline pop",
1259 " token pop /class exch def pop",
1260 " class 0 gt",
1261 " {",
1262 " currentfile buffer readline pop",
1263 " token pop /depth exch def pop",
1264 " /grays columns 8 add depth sub depth mul 8 idiv string def",
1265 " columns rows depth",
1266 " [",
1267 " columns 0 0",
1268 " rows neg 0 rows",
1269 " ]",
1270 " { currentfile grays readhexstring pop } image",
1271 " }",
1272 " {",
1273 " %",
1274 " % Parameters:",
1275 " % colors: number of colors in the colormap.",
1276 " % colormap: red, green, blue color packets.",
1277 " %",
1278 " currentfile buffer readline pop",
1279 " token pop /colors exch def pop",
1280 " /colors colors 3 mul def",
1281 " /colormap colors string def",
1282 " currentfile colormap readhexstring pop pop",
1283 " systemdict /colorimage known",
1284 " {",
1285 " columns rows 8",
1286 " [",
1287 " columns 0 0",
1288 " rows neg 0 rows",
1289 " ]",
1290 " { PseudoClassPacket } false 3 colorimage",
1291 " }",
1292 " {",
1293 " %",
1294 " % No colorimage operator; convert to grayscale.",
1295 " %",
1296 " columns rows 8",
1297 " [",
1298 " columns 0 0",
1299 " rows neg 0 rows",
1300 " ]",
1301 " { GrayPseudoClassPacket } image",
1302 " } ifelse",
1303 " } ifelse",
1304 "} bind def",
1305 "",
1306 "/DisplayImage",
1307 "{",
1308 " %",
1309 " % Display a DirectClass or PseudoClass image.",
1310 " %",
1311 " % Parameters:",
1312 " % x & y translation.",
1313 " % x & y scale.",
1314 " % label pointsize.",
1315 " % image label.",
1316 " % image columns & rows.",
1317 " % class: 0-DirectClass or 1-PseudoClass.",
1318 " % compression: 0-none or 1-RunlengthEncoded.",
1319 " % hex color packets.",
1320 " %",
1321 " gsave",
1322 " /buffer 512 string def",
1323 " /byte 1 string def",
1324 " /color_packet 3 string def",
1325 " /pixels 768 string def",
1326 "",
1327 " currentfile buffer readline pop",
1328 " token pop /x exch def",
1329 " token pop /y exch def pop",
1330 " x y translate",
1331 " currentfile buffer readline pop",
1332 " token pop /x exch def",
1333 " token pop /y exch def pop",
1334 " currentfile buffer readline pop",
1335 " token pop /pointsize exch def pop",
1336 " /Times-Roman findfont pointsize scalefont setfont",
1337 (char *) NULL
1338 },
1339 *PostscriptEpilog[]=
1340 {
1341 " x y scale",
1342 " currentfile buffer readline pop",
1343 " token pop /columns exch def",
1344 " token pop /rows exch def pop",
1345 " currentfile buffer readline pop",
1346 " token pop /class exch def pop",
1347 " currentfile buffer readline pop",
1348 " token pop /compression exch def pop",
1349 " class 0 gt { PseudoClassImage } { DirectClassImage } ifelse",
cristy8b4ff222012-03-02 13:01:39 +00001350 " grestore",
cristy3ed852e2009-09-05 21:47:34 +00001351 (char *) NULL
1352 };
1353
1354 char
1355 buffer[MaxTextExtent],
1356 date[MaxTextExtent],
1357 **labels,
1358 page_geometry[MaxTextExtent];
1359
1360 const char
1361 **s,
1362 *value;
1363
1364 const StringInfo
1365 *profile;
1366
1367 double
1368 pointsize;
1369
1370 GeometryInfo
1371 geometry_info;
1372
cristy3ed852e2009-09-05 21:47:34 +00001373 MagickBooleanType
1374 status;
1375
1376 MagickOffsetType
1377 scene;
1378
1379 MagickStatusType
1380 flags;
1381
cristy101ab702011-10-13 13:06:32 +00001382 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001383 pixel;
1384
1385 PointInfo
1386 delta,
1387 resolution,
1388 scale;
1389
cristy4c08aed2011-07-01 19:47:50 +00001390 Quantum
1391 index;
1392
cristy3ed852e2009-09-05 21:47:34 +00001393 RectangleInfo
1394 geometry,
1395 media_info,
1396 page_info;
1397
cristy4c08aed2011-07-01 19:47:50 +00001398 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00001399 *p;
1400
cristybb503372010-05-27 20:51:26 +00001401 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001402 i,
1403 x;
1404
1405 register unsigned char
1406 *q;
1407
1408 SegmentInfo
1409 bounds;
1410
1411 size_t
cristy802d3642011-04-27 02:02:41 +00001412 bit,
1413 byte,
1414 length,
1415 page,
1416 text_size;
1417
1418 ssize_t
1419 j,
1420 y;
cristy3ed852e2009-09-05 21:47:34 +00001421
1422 time_t
1423 timer;
1424
1425 unsigned char
1426 pixels[2048];
1427
cristy3ed852e2009-09-05 21:47:34 +00001428 /*
1429 Open output image file.
1430 */
1431 assert(image_info != (const ImageInfo *) NULL);
1432 assert(image_info->signature == MagickSignature);
1433 assert(image != (Image *) NULL);
1434 assert(image->signature == MagickSignature);
1435 if (image->debug != MagickFalse)
1436 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +00001437 assert(exception != (ExceptionInfo *) NULL);
1438 assert(exception->signature == MagickSignature);
cristy1e178e72011-08-28 19:44:34 +00001439 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +00001440 if (status == MagickFalse)
1441 return(status);
1442 (void) ResetMagickMemory(&bounds,0,sizeof(bounds));
1443 page=1;
1444 scene=0;
1445 do
1446 {
1447 /*
1448 Scale relative to dots-per-inch.
1449 */
cristy510d06a2011-07-06 23:43:54 +00001450 if ((IsRGBColorspace(image->colorspace) == MagickFalse) &&
cristy3ed852e2009-09-05 21:47:34 +00001451 (image->colorspace != CMYKColorspace))
cristy8d951092012-02-08 18:54:56 +00001452 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +00001453 delta.x=DefaultResolution;
1454 delta.y=DefaultResolution;
cristy2a11bef2011-10-28 18:33:11 +00001455 resolution.x=image->resolution.x;
1456 resolution.y=image->resolution.y;
cristy3ed852e2009-09-05 21:47:34 +00001457 if ((resolution.x == 0.0) || (resolution.y == 0.0))
1458 {
1459 flags=ParseGeometry(PSDensityGeometry,&geometry_info);
1460 resolution.x=geometry_info.rho;
1461 resolution.y=geometry_info.sigma;
1462 if ((flags & SigmaValue) == 0)
1463 resolution.y=resolution.x;
1464 }
1465 if (image_info->density != (char *) NULL)
1466 {
1467 flags=ParseGeometry(image_info->density,&geometry_info);
1468 resolution.x=geometry_info.rho;
1469 resolution.y=geometry_info.sigma;
1470 if ((flags & SigmaValue) == 0)
1471 resolution.y=resolution.x;
1472 }
1473 if (image->units == PixelsPerCentimeterResolution)
1474 {
cristya97426c2011-02-04 01:41:27 +00001475 resolution.x=(double) ((size_t) (100.0*2.54*resolution.x+0.5)/100.0);
1476 resolution.y=(double) ((size_t) (100.0*2.54*resolution.y+0.5)/100.0);
cristy3ed852e2009-09-05 21:47:34 +00001477 }
1478 SetGeometry(image,&geometry);
cristyb51dff52011-05-19 16:55:47 +00001479 (void) FormatLocaleString(page_geometry,MaxTextExtent,"%.20gx%.20g",
cristye8c25f92010-06-03 00:53:06 +00001480 (double) image->columns,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001481 if (image_info->page != (char *) NULL)
1482 (void) CopyMagickString(page_geometry,image_info->page,MaxTextExtent);
1483 else
1484 if ((image->page.width != 0) && (image->page.height != 0))
cristyb51dff52011-05-19 16:55:47 +00001485 (void) FormatLocaleString(page_geometry,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +00001486 "%.20gx%.20g%+.20g%+.20g",(double) image->page.width,(double)
cristye8c25f92010-06-03 00:53:06 +00001487 image->page.height,(double) image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00001488 else
1489 if ((image->gravity != UndefinedGravity) &&
1490 (LocaleCompare(image_info->magick,"PS") == 0))
1491 (void) CopyMagickString(page_geometry,PSPageGeometry,MaxTextExtent);
1492 (void) ConcatenateMagickString(page_geometry,">",MaxTextExtent);
1493 (void) ParseMetaGeometry(page_geometry,&geometry.x,&geometry.y,
1494 &geometry.width,&geometry.height);
1495 scale.x=(double) (geometry.width*delta.x)/resolution.x;
cristybb503372010-05-27 20:51:26 +00001496 geometry.width=(size_t) floor(scale.x+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001497 scale.y=(double) (geometry.height*delta.y)/resolution.y;
cristybb503372010-05-27 20:51:26 +00001498 geometry.height=(size_t) floor(scale.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001499 (void) ParseAbsoluteGeometry(page_geometry,&media_info);
cristy1e178e72011-08-28 19:44:34 +00001500 (void) ParseGravityGeometry(image,page_geometry,&page_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00001501 if (image->gravity != UndefinedGravity)
1502 {
1503 geometry.x=(-page_info.x);
cristybb503372010-05-27 20:51:26 +00001504 geometry.y=(ssize_t) (media_info.height+page_info.y-image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001505 }
1506 pointsize=12.0;
1507 if (image_info->pointsize != 0.0)
1508 pointsize=image_info->pointsize;
1509 text_size=0;
cristyd15e6592011-10-15 00:13:06 +00001510 value=GetImageProperty(image,"label",exception);
cristy3ed852e2009-09-05 21:47:34 +00001511 if (value != (const char *) NULL)
cristybb503372010-05-27 20:51:26 +00001512 text_size=(size_t) (MultilineCensus(value)*pointsize+12);
cristy3ed852e2009-09-05 21:47:34 +00001513 if (page == 1)
1514 {
1515 /*
1516 Output Postscript header.
1517 */
1518 if (LocaleCompare(image_info->magick,"PS") == 0)
1519 (void) CopyMagickString(buffer,"%!PS-Adobe-3.0\n",MaxTextExtent);
1520 else
1521 (void) CopyMagickString(buffer,"%!PS-Adobe-3.0 EPSF-3.0\n",
1522 MaxTextExtent);
1523 (void) WriteBlobString(image,buffer);
1524 (void) WriteBlobString(image,"%%Creator: (ImageMagick)\n");
cristyb51dff52011-05-19 16:55:47 +00001525 (void) FormatLocaleString(buffer,MaxTextExtent,"%%%%Title: (%s)\n",
cristy3ed852e2009-09-05 21:47:34 +00001526 image->filename);
1527 (void) WriteBlobString(image,buffer);
1528 timer=time((time_t *) NULL);
1529 (void) FormatMagickTime(timer,MaxTextExtent,date);
cristyb51dff52011-05-19 16:55:47 +00001530 (void) FormatLocaleString(buffer,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00001531 "%%%%CreationDate: (%s)\n",date);
1532 (void) WriteBlobString(image,buffer);
1533 bounds.x1=(double) geometry.x;
1534 bounds.y1=(double) geometry.y;
1535 bounds.x2=(double) geometry.x+scale.x;
1536 bounds.y2=(double) geometry.y+(geometry.height+text_size);
1537 if ((image_info->adjoin != MagickFalse) &&
1538 (GetNextImageInList(image) != (Image *) NULL))
1539 (void) CopyMagickString(buffer,"%%%%BoundingBox: (atend)\n",
1540 MaxTextExtent);
1541 else
1542 {
cristyb51dff52011-05-19 16:55:47 +00001543 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001544 "%%%%BoundingBox: %.20g %.20g %.20g %.20g\n",ceil(bounds.x1-0.5),
1545 ceil(bounds.y1-0.5),floor(bounds.x2+0.5),floor(bounds.y2+0.5));
cristy3ed852e2009-09-05 21:47:34 +00001546 (void) WriteBlobString(image,buffer);
cristyb51dff52011-05-19 16:55:47 +00001547 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001548 "%%%%HiResBoundingBox: %g %g %g %g\n",bounds.x1,
cristy8cd5b312010-01-07 01:10:24 +00001549 bounds.y1,bounds.x2,bounds.y2);
cristy3ed852e2009-09-05 21:47:34 +00001550 }
1551 (void) WriteBlobString(image,buffer);
1552 profile=GetImageProfile(image,"8bim");
1553 if (profile != (StringInfo *) NULL)
1554 {
1555 /*
1556 Embed Photoshop profile.
1557 */
cristyb51dff52011-05-19 16:55:47 +00001558 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001559 "%%BeginPhotoshop: %.20g",(double) GetStringInfoLength(profile));
cristy3ed852e2009-09-05 21:47:34 +00001560 (void) WriteBlobString(image,buffer);
cristybb503372010-05-27 20:51:26 +00001561 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
cristy3ed852e2009-09-05 21:47:34 +00001562 {
1563 if ((i % 32) == 0)
1564 (void) WriteBlobString(image,"\n% ");
cristyb51dff52011-05-19 16:55:47 +00001565 (void) FormatLocaleString(buffer,MaxTextExtent,"%02X",
cristy3ed852e2009-09-05 21:47:34 +00001566 (unsigned int) (GetStringInfoDatum(profile)[i] & 0xff));
1567 (void) WriteBlobString(image,buffer);
1568 }
1569 (void) WriteBlobString(image,"\n%EndPhotoshop\n");
1570 }
1571 profile=GetImageProfile(image,"xmp");
cristy2b2eb912010-03-03 14:56:40 +00001572 if (0 && (profile != (StringInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001573 {
1574 /*
1575 Embed XML profile.
1576 */
1577 (void) WriteBlobString(image,"\n%begin_xml_code\n");
cristyb51dff52011-05-19 16:55:47 +00001578 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001579 "\n%%begin_xml_packet: %.20g\n",(double)
cristyeec18db2010-03-03 21:15:45 +00001580 GetStringInfoLength(profile));
cristy2b2eb912010-03-03 14:56:40 +00001581 (void) WriteBlobString(image,buffer);
cristybb503372010-05-27 20:51:26 +00001582 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
cristy3ed852e2009-09-05 21:47:34 +00001583 (void) WriteBlobByte(image,GetStringInfoDatum(profile)[i]);
cristy2b2eb912010-03-03 14:56:40 +00001584 (void) WriteBlobString(image,"\n%end_xml_packet\n%end_xml_code\n");
cristy3ed852e2009-09-05 21:47:34 +00001585 }
cristyd15e6592011-10-15 00:13:06 +00001586 value=GetImageProperty(image,"label",exception);
cristy3ed852e2009-09-05 21:47:34 +00001587 if (value != (const char *) NULL)
1588 (void) WriteBlobString(image,
1589 "%%DocumentNeededResources: font Times-Roman\n");
1590 (void) WriteBlobString(image,"%%DocumentData: Clean7Bit\n");
1591 (void) WriteBlobString(image,"%%LanguageLevel: 1\n");
1592 if (LocaleCompare(image_info->magick,"PS") != 0)
1593 (void) WriteBlobString(image,"%%Pages: 1\n");
1594 else
1595 {
1596 /*
1597 Compute the number of pages.
1598 */
1599 (void) WriteBlobString(image,"%%Orientation: Portrait\n");
1600 (void) WriteBlobString(image,"%%PageOrder: Ascend\n");
cristyb51dff52011-05-19 16:55:47 +00001601 (void) FormatLocaleString(buffer,MaxTextExtent,"%%%%Pages: %.20g\n",
cristye8c25f92010-06-03 00:53:06 +00001602 image_info->adjoin != MagickFalse ? (double)
1603 GetImageListLength(image) : 1.0);
cristy3ed852e2009-09-05 21:47:34 +00001604 (void) WriteBlobString(image,buffer);
1605 }
1606 (void) WriteBlobString(image,"%%EndComments\n");
1607 (void) WriteBlobString(image,"\n%%BeginDefaults\n");
1608 (void) WriteBlobString(image,"%%EndDefaults\n\n");
1609 if ((LocaleCompare(image_info->magick,"EPI") == 0) ||
1610 (LocaleCompare(image_info->magick,"EPSI") == 0) ||
1611 (LocaleCompare(image_info->magick,"EPT") == 0))
1612 {
1613 Image
1614 *preview_image;
1615
cristy3ed852e2009-09-05 21:47:34 +00001616 Quantum
1617 pixel;
1618
cristybb503372010-05-27 20:51:26 +00001619 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001620 x;
1621
cristy802d3642011-04-27 02:02:41 +00001622 ssize_t
1623 y;
1624
cristy3ed852e2009-09-05 21:47:34 +00001625 /*
1626 Create preview image.
1627 */
cristy1e178e72011-08-28 19:44:34 +00001628 preview_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00001629 if (preview_image == (Image *) NULL)
1630 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1631 /*
1632 Dump image as bitmap.
1633 */
cristyb51dff52011-05-19 16:55:47 +00001634 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001635 "%%%%BeginPreview: %.20g %.20g %.20g %.20g\n%% ",(double)
1636 preview_image->columns,(double) preview_image->rows,1.0,
1637 (double) ((((preview_image->columns+7) >> 3)*preview_image->rows+
1638 35)/36));
cristy3ed852e2009-09-05 21:47:34 +00001639 (void) WriteBlobString(image,buffer);
1640 q=pixels;
cristybb503372010-05-27 20:51:26 +00001641 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001642 {
1643 p=GetVirtualPixels(preview_image,0,y,preview_image->columns,1,
cristy1e178e72011-08-28 19:44:34 +00001644 exception);
cristy4c08aed2011-07-01 19:47:50 +00001645 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001646 break;
cristy3ed852e2009-09-05 21:47:34 +00001647 bit=0;
1648 byte=0;
cristybb503372010-05-27 20:51:26 +00001649 for (x=0; x < (ssize_t) preview_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001650 {
1651 byte<<=1;
cristy4c08aed2011-07-01 19:47:50 +00001652 pixel=GetPixelIntensity(preview_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001653 if (pixel >= (Quantum) (QuantumRange/2))
1654 byte|=0x01;
1655 bit++;
1656 if (bit == 8)
1657 {
1658 q=PopHexPixel(hex_digits,byte,q);
1659 if ((q-pixels+8) >= 80)
1660 {
1661 *q++='\n';
1662 (void) WriteBlob(image,q-pixels,pixels);
1663 q=pixels;
1664 (void) WriteBlobString(image,"% ");
1665 };
1666 bit=0;
1667 byte=0;
1668 }
1669 }
1670 if (bit != 0)
1671 {
1672 byte<<=(8-bit);
1673 q=PopHexPixel(hex_digits,byte,q);
1674 if ((q-pixels+8) >= 80)
1675 {
1676 *q++='\n';
1677 (void) WriteBlob(image,q-pixels,pixels);
1678 q=pixels;
1679 (void) WriteBlobString(image,"% ");
1680 };
1681 };
1682 }
1683 if (q != pixels)
1684 {
1685 *q++='\n';
1686 (void) WriteBlob(image,q-pixels,pixels);
1687 }
1688 (void) WriteBlobString(image,"\n%%EndPreview\n");
1689 preview_image=DestroyImage(preview_image);
1690 }
1691 /*
1692 Output Postscript commands.
1693 */
1694 for (s=PostscriptProlog; *s != (char *) NULL; s++)
1695 {
cristyb51dff52011-05-19 16:55:47 +00001696 (void) FormatLocaleString(buffer,MaxTextExtent,"%s\n",*s);
cristy3ed852e2009-09-05 21:47:34 +00001697 (void) WriteBlobString(image,buffer);
1698 }
cristyd15e6592011-10-15 00:13:06 +00001699 value=GetImageProperty(image,"label",exception);
cristy3ed852e2009-09-05 21:47:34 +00001700 if (value != (const char *) NULL)
cristybb503372010-05-27 20:51:26 +00001701 for (j=(ssize_t) MultilineCensus(value)-1; j >= 0; j--)
cristy3ed852e2009-09-05 21:47:34 +00001702 {
1703 (void) WriteBlobString(image," /label 512 string def\n");
1704 (void) WriteBlobString(image," currentfile label readline pop\n");
cristyb51dff52011-05-19 16:55:47 +00001705 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001706 " 0 y %g add moveto label show pop\n",j*pointsize+12);
cristy3ed852e2009-09-05 21:47:34 +00001707 (void) WriteBlobString(image,buffer);
1708 }
1709 for (s=PostscriptEpilog; *s != (char *) NULL; s++)
1710 {
cristyb51dff52011-05-19 16:55:47 +00001711 (void) FormatLocaleString(buffer,MaxTextExtent,"%s\n",*s);
cristy3ed852e2009-09-05 21:47:34 +00001712 (void) WriteBlobString(image,buffer);
1713 }
1714 if (LocaleCompare(image_info->magick,"PS") == 0)
1715 (void) WriteBlobString(image," showpage\n");
1716 (void) WriteBlobString(image,"} bind def\n");
1717 (void) WriteBlobString(image,"%%EndProlog\n");
1718 }
cristyb51dff52011-05-19 16:55:47 +00001719 (void) FormatLocaleString(buffer,MaxTextExtent,"%%%%Page: 1 %.20g\n",
cristye8c25f92010-06-03 00:53:06 +00001720 (double) (page++));
cristy3ed852e2009-09-05 21:47:34 +00001721 (void) WriteBlobString(image,buffer);
cristyb51dff52011-05-19 16:55:47 +00001722 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001723 "%%%%PageBoundingBox: %.20g %.20g %.20g %.20g\n",(double) geometry.x,
1724 (double) geometry.y,geometry.x+(double) geometry.width,geometry.y+(double)
cristyf2faecf2010-05-28 19:19:36 +00001725 (geometry.height+text_size));
cristy3ed852e2009-09-05 21:47:34 +00001726 (void) WriteBlobString(image,buffer);
1727 if ((double) geometry.x < bounds.x1)
1728 bounds.x1=(double) geometry.x;
1729 if ((double) geometry.y < bounds.y1)
1730 bounds.y1=(double) geometry.y;
1731 if ((double) (geometry.x+geometry.width-1) > bounds.x2)
1732 bounds.x2=(double) geometry.x+geometry.width-1;
1733 if ((double) (geometry.y+(geometry.height+text_size)-1) > bounds.y2)
1734 bounds.y2=(double) geometry.y+(geometry.height+text_size)-1;
cristyd15e6592011-10-15 00:13:06 +00001735 value=GetImageProperty(image,"label",exception);
cristy3ed852e2009-09-05 21:47:34 +00001736 if (value != (const char *) NULL)
1737 (void) WriteBlobString(image,"%%%%PageResources: font Times-Roman\n");
1738 if (LocaleCompare(image_info->magick,"PS") != 0)
1739 (void) WriteBlobString(image,"userdict begin\n");
1740 (void) WriteBlobString(image,"DisplayImage\n");
1741 /*
1742 Output image data.
1743 */
cristyb51dff52011-05-19 16:55:47 +00001744 (void) FormatLocaleString(buffer,MaxTextExtent,"%.20g %.20g\n%g %g\n%g\n",
cristye8c25f92010-06-03 00:53:06 +00001745 (double) geometry.x,(double) geometry.y,scale.x,scale.y,pointsize);
cristy3ed852e2009-09-05 21:47:34 +00001746 (void) WriteBlobString(image,buffer);
1747 labels=(char **) NULL;
cristyd15e6592011-10-15 00:13:06 +00001748 value=GetImageProperty(image,"label",exception);
cristy3ed852e2009-09-05 21:47:34 +00001749 if (value != (const char *) NULL)
1750 labels=StringToList(value);
1751 if (labels != (char **) NULL)
1752 {
1753 for (i=0; labels[i] != (char *) NULL; i++)
1754 {
cristyb51dff52011-05-19 16:55:47 +00001755 (void) FormatLocaleString(buffer,MaxTextExtent,"%s \n",
cristy3ed852e2009-09-05 21:47:34 +00001756 labels[i]);
1757 (void) WriteBlobString(image,buffer);
1758 labels[i]=DestroyString(labels[i]);
1759 }
1760 labels=(char **) RelinquishMagickMemory(labels);
1761 }
1762 (void) ResetMagickMemory(&pixel,0,sizeof(pixel));
cristy4c08aed2011-07-01 19:47:50 +00001763 pixel.alpha=(Quantum) TransparentAlpha;
1764 index=0;
cristy3ed852e2009-09-05 21:47:34 +00001765 x=0;
1766 if ((image_info->type != TrueColorType) &&
cristy1e178e72011-08-28 19:44:34 +00001767 (IsImageGray(image,exception) != MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +00001768 {
cristy1e178e72011-08-28 19:44:34 +00001769 if (IsImageMonochrome(image,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001770 {
1771 Quantum
1772 pixel;
1773
1774 /*
1775 Dump image as grayscale.
1776 */
cristyb51dff52011-05-19 16:55:47 +00001777 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001778 "%.20g %.20g\n1\n1\n1\n8\n",(double) image->columns,(double)
1779 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001780 (void) WriteBlobString(image,buffer);
1781 q=pixels;
cristybb503372010-05-27 20:51:26 +00001782 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001783 {
cristy1e178e72011-08-28 19:44:34 +00001784 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001785 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001786 break;
cristybb503372010-05-27 20:51:26 +00001787 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001788 {
cristy4c08aed2011-07-01 19:47:50 +00001789 pixel=(Quantum) ScaleQuantumToChar(GetPixelIntensity(image,p));
cristya97426c2011-02-04 01:41:27 +00001790 q=PopHexPixel(hex_digits,(size_t) pixel,q);
cristy3ed852e2009-09-05 21:47:34 +00001791 i++;
1792 if ((q-pixels+8) >= 80)
1793 {
1794 *q++='\n';
1795 (void) WriteBlob(image,q-pixels,pixels);
1796 q=pixels;
1797 }
cristyed231572011-07-14 02:18:59 +00001798 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001799 }
1800 if (image->previous == (Image *) NULL)
1801 {
cristya97426c2011-02-04 01:41:27 +00001802 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType)
1803 y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001804 if (status == MagickFalse)
1805 break;
1806 }
1807 }
1808 if (q != pixels)
1809 {
1810 *q++='\n';
1811 (void) WriteBlob(image,q-pixels,pixels);
1812 }
1813 }
1814 else
1815 {
cristybb503372010-05-27 20:51:26 +00001816 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001817 y;
1818
1819 Quantum
1820 pixel;
1821
1822 /*
1823 Dump image as bitmap.
1824 */
cristyb51dff52011-05-19 16:55:47 +00001825 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001826 "%.20g %.20g\n1\n1\n1\n1\n",(double) image->columns,(double)
1827 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001828 (void) WriteBlobString(image,buffer);
1829 q=pixels;
cristybb503372010-05-27 20:51:26 +00001830 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001831 {
cristy1e178e72011-08-28 19:44:34 +00001832 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001833 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001834 break;
cristy3ed852e2009-09-05 21:47:34 +00001835 bit=0;
1836 byte=0;
cristybb503372010-05-27 20:51:26 +00001837 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001838 {
1839 byte<<=1;
cristy4c08aed2011-07-01 19:47:50 +00001840 pixel=GetPixelIntensity(image,p);
cristy3ed852e2009-09-05 21:47:34 +00001841 if (pixel >= (Quantum) (QuantumRange/2))
1842 byte|=0x01;
1843 bit++;
1844 if (bit == 8)
1845 {
1846 q=PopHexPixel(hex_digits,byte,q);
1847 if ((q-pixels+2) >= 80)
1848 {
1849 *q++='\n';
1850 (void) WriteBlob(image,q-pixels,pixels);
1851 q=pixels;
1852 };
1853 bit=0;
1854 byte=0;
1855 }
cristyed231572011-07-14 02:18:59 +00001856 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001857 }
1858 if (bit != 0)
1859 {
1860 byte<<=(8-bit);
1861 q=PopHexPixel(hex_digits,byte,q);
1862 if ((q-pixels+2) >= 80)
1863 {
1864 *q++='\n';
1865 (void) WriteBlob(image,q-pixels,pixels);
1866 q=pixels;
1867 }
1868 };
1869 if (image->previous == (Image *) NULL)
1870 {
cristy6cde06a2011-11-24 00:08:43 +00001871 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType)
cristy802d3642011-04-27 02:02:41 +00001872 y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001873 if (status == MagickFalse)
1874 break;
1875 }
1876 }
1877 if (q != pixels)
1878 {
1879 *q++='\n';
1880 (void) WriteBlob(image,q-pixels,pixels);
1881 }
1882 }
1883 }
1884 else
1885 if ((image->storage_class == DirectClass) ||
1886 (image->colors > 256) || (image->matte != MagickFalse))
1887 {
1888 /*
1889 Dump DirectClass image.
1890 */
cristyb51dff52011-05-19 16:55:47 +00001891 (void) FormatLocaleString(buffer,MaxTextExtent,"%.20g %.20g\n0\n%d\n",
cristye8c25f92010-06-03 00:53:06 +00001892 (double) image->columns,(double) image->rows,
cristy3ed852e2009-09-05 21:47:34 +00001893 image_info->compression == RLECompression ? 1 : 0);
1894 (void) WriteBlobString(image,buffer);
1895 switch (image_info->compression)
1896 {
1897 case RLECompression:
1898 {
1899 /*
1900 Dump runlength-encoded DirectColor packets.
1901 */
1902 q=pixels;
cristybb503372010-05-27 20:51:26 +00001903 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001904 {
cristy1e178e72011-08-28 19:44:34 +00001905 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001906 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001907 break;
cristy101ab702011-10-13 13:06:32 +00001908 GetPixelInfoPixel(image,p,&pixel);
cristy3ed852e2009-09-05 21:47:34 +00001909 length=255;
cristybb503372010-05-27 20:51:26 +00001910 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001911 {
cristy4c08aed2011-07-01 19:47:50 +00001912 if ((GetPixelRed(image,p) == pixel.red) &&
1913 (GetPixelGreen(image,p) == pixel.green) &&
1914 (GetPixelBlue(image,p) == pixel.blue) &&
1915 (GetPixelAlpha(image,p) == pixel.alpha) &&
cristy802d3642011-04-27 02:02:41 +00001916 (length < 255) && (x < (ssize_t) (image->columns-1)))
cristy3ed852e2009-09-05 21:47:34 +00001917 length++;
1918 else
1919 {
1920 if (x > 0)
1921 {
1922 WriteRunlengthPacket(image,pixel,length,p);
1923 if ((q-pixels+10) >= 80)
1924 {
1925 *q++='\n';
1926 (void) WriteBlob(image,q-pixels,pixels);
1927 q=pixels;
1928 }
1929 }
1930 length=0;
1931 }
cristy101ab702011-10-13 13:06:32 +00001932 GetPixelInfoPixel(image,p,&pixel);
cristyed231572011-07-14 02:18:59 +00001933 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001934 }
1935 WriteRunlengthPacket(image,pixel,length,p);
1936 if ((q-pixels+10) >= 80)
1937 {
1938 *q++='\n';
1939 (void) WriteBlob(image,q-pixels,pixels);
1940 q=pixels;
1941 }
1942 if (image->previous == (Image *) NULL)
1943 {
cristy802d3642011-04-27 02:02:41 +00001944 status=SetImageProgress(image,SaveImageTag,
1945 (MagickOffsetType) y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001946 if (status == MagickFalse)
1947 break;
1948 }
1949 }
1950 if (q != pixels)
1951 {
1952 *q++='\n';
1953 (void) WriteBlob(image,q-pixels,pixels);
1954 }
1955 break;
1956 }
1957 case NoCompression:
1958 default:
1959 {
1960 /*
1961 Dump uncompressed DirectColor packets.
1962 */
1963 q=pixels;
cristybb503372010-05-27 20:51:26 +00001964 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001965 {
cristy1e178e72011-08-28 19:44:34 +00001966 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001967 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001968 break;
cristybb503372010-05-27 20:51:26 +00001969 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001970 {
1971 if ((image->matte != MagickFalse) &&
cristy4c08aed2011-07-01 19:47:50 +00001972 (GetPixelAlpha(image,p) == (Quantum) TransparentAlpha))
cristy3ed852e2009-09-05 21:47:34 +00001973 {
1974 q=PopHexPixel(hex_digits,0xff,q);
1975 q=PopHexPixel(hex_digits,0xff,q);
1976 q=PopHexPixel(hex_digits,0xff,q);
1977 }
1978 else
1979 {
cristy802d3642011-04-27 02:02:41 +00001980 q=PopHexPixel(hex_digits,ScaleQuantumToChar(
cristy4c08aed2011-07-01 19:47:50 +00001981 GetPixelRed(image,p)),q);
cristy802d3642011-04-27 02:02:41 +00001982 q=PopHexPixel(hex_digits,ScaleQuantumToChar(
cristy4c08aed2011-07-01 19:47:50 +00001983 GetPixelGreen(image,p)),q);
cristy802d3642011-04-27 02:02:41 +00001984 q=PopHexPixel(hex_digits,ScaleQuantumToChar(
cristy4c08aed2011-07-01 19:47:50 +00001985 GetPixelBlue(image,p)),q);
cristy3ed852e2009-09-05 21:47:34 +00001986 }
1987 if ((q-pixels+6) >= 80)
1988 {
1989 *q++='\n';
1990 (void) WriteBlob(image,q-pixels,pixels);
1991 q=pixels;
1992 }
cristyed231572011-07-14 02:18:59 +00001993 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001994 }
1995 if (image->previous == (Image *) NULL)
1996 {
cristy802d3642011-04-27 02:02:41 +00001997 status=SetImageProgress(image,SaveImageTag,
1998 (MagickOffsetType) y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001999 if (status == MagickFalse)
2000 break;
2001 }
2002 }
2003 if (q != pixels)
2004 {
2005 *q++='\n';
2006 (void) WriteBlob(image,q-pixels,pixels);
2007 }
2008 break;
2009 }
2010 }
2011 (void) WriteBlobByte(image,'\n');
2012 }
2013 else
2014 {
2015 /*
2016 Dump PseudoClass image.
2017 */
cristyb51dff52011-05-19 16:55:47 +00002018 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00002019 "%.20g %.20g\n%d\n%d\n0\n",(double) image->columns,(double)
2020 image->rows,image->storage_class == PseudoClass ? 1 : 0,
cristy3ed852e2009-09-05 21:47:34 +00002021 image_info->compression == RLECompression ? 1 : 0);
2022 (void) WriteBlobString(image,buffer);
2023 /*
2024 Dump number of colors and colormap.
2025 */
cristyb51dff52011-05-19 16:55:47 +00002026 (void) FormatLocaleString(buffer,MaxTextExtent,"%.20g\n",(double)
cristyf2faecf2010-05-28 19:19:36 +00002027 image->colors);
cristy3ed852e2009-09-05 21:47:34 +00002028 (void) WriteBlobString(image,buffer);
cristybb503372010-05-27 20:51:26 +00002029 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002030 {
cristyb51dff52011-05-19 16:55:47 +00002031 (void) FormatLocaleString(buffer,MaxTextExtent,"%02X%02X%02X\n",
cristy3ed852e2009-09-05 21:47:34 +00002032 ScaleQuantumToChar(image->colormap[i].red),
2033 ScaleQuantumToChar(image->colormap[i].green),
2034 ScaleQuantumToChar(image->colormap[i].blue));
2035 (void) WriteBlobString(image,buffer);
2036 }
2037 switch (image_info->compression)
2038 {
2039 case RLECompression:
2040 {
2041 /*
2042 Dump runlength-encoded PseudoColor packets.
2043 */
2044 q=pixels;
cristybb503372010-05-27 20:51:26 +00002045 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002046 {
cristy1e178e72011-08-28 19:44:34 +00002047 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002048 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002049 break;
cristy4c08aed2011-07-01 19:47:50 +00002050 index=GetPixelIndex(image,p);
cristy3ed852e2009-09-05 21:47:34 +00002051 length=255;
cristybb503372010-05-27 20:51:26 +00002052 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002053 {
cristy4c08aed2011-07-01 19:47:50 +00002054 if ((index == GetPixelIndex(image,p)) &&
cristy802d3642011-04-27 02:02:41 +00002055 (length < 255) && (x < ((ssize_t) image->columns-1)))
cristy3ed852e2009-09-05 21:47:34 +00002056 length++;
2057 else
2058 {
2059 if (x > 0)
2060 {
cristya97426c2011-02-04 01:41:27 +00002061 q=PopHexPixel(hex_digits,(size_t) index,q);
cristybb503372010-05-27 20:51:26 +00002062 q=PopHexPixel(hex_digits,(size_t)
cristy3ed852e2009-09-05 21:47:34 +00002063 MagickMin(length,0xff),q);
2064 i++;
2065 if ((q-pixels+6) >= 80)
2066 {
2067 *q++='\n';
2068 (void) WriteBlob(image,q-pixels,pixels);
2069 q=pixels;
2070 }
2071 }
2072 length=0;
2073 }
cristy4c08aed2011-07-01 19:47:50 +00002074 index=GetPixelIndex(image,p);
2075 pixel.red=GetPixelRed(image,p);
2076 pixel.green=GetPixelGreen(image,p);
2077 pixel.blue=GetPixelBlue(image,p);
2078 pixel.alpha=GetPixelAlpha(image,p);
cristyed231572011-07-14 02:18:59 +00002079 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002080 }
cristya97426c2011-02-04 01:41:27 +00002081 q=PopHexPixel(hex_digits,(size_t) index,q);
cristybb503372010-05-27 20:51:26 +00002082 q=PopHexPixel(hex_digits,(size_t)
cristy3ed852e2009-09-05 21:47:34 +00002083 MagickMin(length,0xff),q);
2084 if (image->previous == (Image *) NULL)
2085 {
cristya97426c2011-02-04 01:41:27 +00002086 status=SetImageProgress(image,SaveImageTag,
2087 (MagickOffsetType) y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002088 if (status == MagickFalse)
2089 break;
2090 }
2091 }
2092 if (q != pixels)
2093 {
2094 *q++='\n';
2095 (void) WriteBlob(image,q-pixels,pixels);
2096 }
2097 break;
2098 }
2099 case NoCompression:
2100 default:
2101 {
2102 /*
2103 Dump uncompressed PseudoColor packets.
2104 */
2105 q=pixels;
cristybb503372010-05-27 20:51:26 +00002106 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002107 {
cristy1e178e72011-08-28 19:44:34 +00002108 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002109 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002110 break;
cristybb503372010-05-27 20:51:26 +00002111 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002112 {
cristy4c08aed2011-07-01 19:47:50 +00002113 q=PopHexPixel(hex_digits,(size_t) GetPixelIndex(image,p),q);
cristy3ed852e2009-09-05 21:47:34 +00002114 if ((q-pixels+4) >= 80)
2115 {
2116 *q++='\n';
2117 (void) WriteBlob(image,q-pixels,pixels);
2118 q=pixels;
2119 }
cristyed231572011-07-14 02:18:59 +00002120 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002121 }
2122 if (image->previous == (Image *) NULL)
2123 {
cristya97426c2011-02-04 01:41:27 +00002124 status=SetImageProgress(image,SaveImageTag,
2125 (MagickOffsetType) y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002126 if (status == MagickFalse)
2127 break;
2128 }
2129 }
2130 if (q != pixels)
2131 {
2132 *q++='\n';
2133 (void) WriteBlob(image,q-pixels,pixels);
2134 }
2135 break;
2136 }
2137 }
2138 (void) WriteBlobByte(image,'\n');
2139 }
2140 if (LocaleCompare(image_info->magick,"PS") != 0)
2141 (void) WriteBlobString(image,"end\n");
2142 (void) WriteBlobString(image,"%%PageTrailer\n");
2143 if (GetNextImageInList(image) == (Image *) NULL)
2144 break;
2145 image=SyncNextImageInList(image);
2146 status=SetImageProgress(image,SaveImagesTag,scene++,
2147 GetImageListLength(image));
2148 if (status == MagickFalse)
2149 break;
2150 } while (image_info->adjoin != MagickFalse);
2151 (void) WriteBlobString(image,"%%Trailer\n");
2152 if (page > 2)
2153 {
cristyb51dff52011-05-19 16:55:47 +00002154 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00002155 "%%%%BoundingBox: %.20g %.20g %.20g %.20g\n",ceil(bounds.x1-0.5),
2156 ceil(bounds.y1-0.5),floor(bounds.x2+0.5),floor(bounds.y2+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002157 (void) WriteBlobString(image,buffer);
cristyb51dff52011-05-19 16:55:47 +00002158 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00002159 "%%%%HiResBoundingBox: %g %g %g %g\n",bounds.x1,bounds.y1,
cristy8cd5b312010-01-07 01:10:24 +00002160 bounds.x2,bounds.y2);
cristy3ed852e2009-09-05 21:47:34 +00002161 (void) WriteBlobString(image,buffer);
2162 }
2163 (void) WriteBlobString(image,"%%EOF\n");
2164 (void) CloseBlob(image);
2165 return(MagickTrue);
2166}