blob: 4448b3b7a8523f3765af2e0b71d39401771deebb [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;
692 hires_bounds=bounds;
693 priority=i;
cristy3ed852e2009-09-05 21:47:34 +0000694 }
cristy7b0fcf12011-11-24 00:24:07 +0000695 if ((fabs(hires_bounds.x2-hires_bounds.x1) >= MagickEpsilon) &&
cristy614bc082011-11-24 00:49:08 +0000696 (fabs(hires_bounds.y2-hires_bounds.y1) >= MagickEpsilon))
cristy4b1c78f2011-11-11 15:09:30 +0000697 {
698 /*
699 Set Postscript render geometry.
700 */
701 (void) FormatLocaleString(geometry,MaxTextExtent,"%gx%g%+.15g%+.15g",
702 hires_bounds.x2-hires_bounds.x1,hires_bounds.y2-hires_bounds.y1,
703 hires_bounds.x1,hires_bounds.y1);
704 (void) SetImageProperty(image,"ps:HiResBoundingBox",geometry,exception);
cristy6cde06a2011-11-24 00:08:43 +0000705 page.width=(size_t) ceil((double) ((hires_bounds.x2-hires_bounds.x1)*
706 image->resolution.x/delta.x)-0.5);
707 page.height=(size_t) ceil((double) ((hires_bounds.y2-hires_bounds.y1)*
708 image->resolution.y/delta.y)-0.5);
cristy4b1c78f2011-11-11 15:09:30 +0000709 }
cristy3ed852e2009-09-05 21:47:34 +0000710 (void) CloseBlob(image);
cristy510d06a2011-07-06 23:43:54 +0000711 if (IsRGBColorspace(image_info->colorspace) != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000712 cmyk=MagickFalse;
713 /*
714 Create Ghostscript control file.
715 */
716 file=AcquireUniqueFileResource(postscript_filename);
717 if (file == -1)
718 {
cristyc82a27b2011-10-21 01:07:16 +0000719 ThrowFileException(exception,FileOpenError,"UnableToOpenFile",
cristy3ed852e2009-09-05 21:47:34 +0000720 image_info->filename);
721 image=DestroyImageList(image);
722 return((Image *) NULL);
723 }
724 (void) CopyMagickString(command,"/setpagedevice {pop} bind 1 index where {"
725 "dup wcheck {3 1 roll put} {pop def} ifelse} {def} ifelse\n"
726 "<</UseCIEColor true>>setpagedevice\n",MaxTextExtent);
727 count=write(file,command,(unsigned int) strlen(command));
cristyc39e3d62010-10-14 16:52:01 +0000728 if (image_info->page == (char *) NULL)
729 {
730 char
731 translate_geometry[MaxTextExtent];
732
cristyb51dff52011-05-19 16:55:47 +0000733 (void) FormatLocaleString(translate_geometry,MaxTextExtent,
cristyc39e3d62010-10-14 16:52:01 +0000734 "%g %g translate\n",-bounds.x1,-bounds.y1);
735 count=write(file,translate_geometry,(unsigned int)
736 strlen(translate_geometry));
737 }
cristy3ed852e2009-09-05 21:47:34 +0000738 file=close(file)-1;
739 /*
740 Render Postscript with the Ghostscript delegate.
741 */
742 if (image_info->monochrome != MagickFalse)
743 delegate_info=GetDelegateInfo("ps:mono",(char *) NULL,exception);
744 else
745 if (cmyk != MagickFalse)
746 delegate_info=GetDelegateInfo("ps:cmyk",(char *) NULL,exception);
747 else
cristya97426c2011-02-04 01:41:27 +0000748 delegate_info=GetDelegateInfo("ps:alpha",(char *) NULL,exception);
cristy3ed852e2009-09-05 21:47:34 +0000749 if (delegate_info == (const DelegateInfo *) NULL)
750 {
751 (void) RelinquishUniqueFileResource(postscript_filename);
752 image=DestroyImageList(image);
753 return((Image *) NULL);
754 }
755 *options='\0';
cristyb51dff52011-05-19 16:55:47 +0000756 (void) FormatLocaleString(density,MaxTextExtent,"%gx%g",
cristy2a11bef2011-10-28 18:33:11 +0000757 image->resolution.x,image->resolution.y);
cristyb51dff52011-05-19 16:55:47 +0000758 (void) FormatLocaleString(options,MaxTextExtent,"-g%.20gx%.20g ",(double)
cristye8c25f92010-06-03 00:53:06 +0000759 page.width,(double) page.height);
cristy3ed852e2009-09-05 21:47:34 +0000760 read_info=CloneImageInfo(image_info);
761 *read_info->magick='\0';
762 if (read_info->number_scenes != 0)
763 {
764 char
765 pages[MaxTextExtent];
766
cristyb51dff52011-05-19 16:55:47 +0000767 (void) FormatLocaleString(pages,MaxTextExtent,"-dFirstPage=%.20g "
cristye8c25f92010-06-03 00:53:06 +0000768 "-dLastPage=%.20g",(double) read_info->scene+1,(double)
cristyf2faecf2010-05-28 19:19:36 +0000769 (read_info->scene+read_info->number_scenes));
cristy3ed852e2009-09-05 21:47:34 +0000770 (void) ConcatenateMagickString(options,pages,MaxTextExtent);
771 read_info->number_scenes=0;
772 if (read_info->scenes != (char *) NULL)
773 *read_info->scenes='\0';
774 }
775 option=GetImageOption(image_info,"ps:use-cropbox");
776 if ((option != (const char *) NULL) && (IsMagickTrue(option) != MagickFalse))
777 (void) ConcatenateMagickString(options,"-dEPSCrop ",MaxTextExtent);
778 (void) CopyMagickString(filename,read_info->filename,MaxTextExtent);
cristya97426c2011-02-04 01:41:27 +0000779 (void) AcquireUniqueFilename(filename);
780 (void) ConcatenateMagickString(filename,"-%08d",MaxTextExtent);
cristyb51dff52011-05-19 16:55:47 +0000781 (void) FormatLocaleString(command,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +0000782 GetDelegateCommands(delegate_info),
783 read_info->antialias != MagickFalse ? 4 : 1,
cristya97426c2011-02-04 01:41:27 +0000784 read_info->antialias != MagickFalse ? 4 : 1,density,options,filename,
785 postscript_filename,input_filename);
cristyb32b90a2009-09-07 21:45:48 +0000786 status=InvokePostscriptDelegate(read_info->verbose,command,exception);
cristya97426c2011-02-04 01:41:27 +0000787 (void) InterpretImageFilename(image_info,image,filename,1,
cristy6fccee12011-10-20 18:43:18 +0000788 read_info->filename,exception);
cristy41083a42009-09-07 23:47:59 +0000789 if ((status == MagickFalse) ||
cristy3ed852e2009-09-05 21:47:34 +0000790 (IsPostscriptRendered(read_info->filename) == MagickFalse))
791 {
792 (void) ConcatenateMagickString(command," -c showpage",MaxTextExtent);
cristyb32b90a2009-09-07 21:45:48 +0000793 status=InvokePostscriptDelegate(read_info->verbose,command,exception);
cristy3ed852e2009-09-05 21:47:34 +0000794 }
cristy3ed852e2009-09-05 21:47:34 +0000795 (void) RelinquishUniqueFileResource(postscript_filename);
cristy3ed852e2009-09-05 21:47:34 +0000796 (void) RelinquishUniqueFileResource(input_filename);
cristya97426c2011-02-04 01:41:27 +0000797 postscript_image=(Image *) NULL;
798 if (status == MagickFalse)
799 for (i=1; ; i++)
800 {
cristya97426c2011-02-04 01:41:27 +0000801 (void) InterpretImageFilename(image_info,image,filename,(int) i,
cristy6fccee12011-10-20 18:43:18 +0000802 read_info->filename,exception);
cristya97426c2011-02-04 01:41:27 +0000803 if (IsPostscriptRendered(read_info->filename) == MagickFalse)
804 break;
805 (void) RelinquishUniqueFileResource(read_info->filename);
806 }
807 else
808 for (i=1; ; i++)
809 {
cristya97426c2011-02-04 01:41:27 +0000810 (void) InterpretImageFilename(image_info,image,filename,(int) i,
cristy6fccee12011-10-20 18:43:18 +0000811 read_info->filename,exception);
cristya97426c2011-02-04 01:41:27 +0000812 if (IsPostscriptRendered(read_info->filename) == MagickFalse)
813 break;
814 next=ReadImage(read_info,exception);
815 (void) RelinquishUniqueFileResource(read_info->filename);
816 if (next == (Image *) NULL)
817 break;
818 AppendImageToList(&postscript_image,next);
819 }
820 (void) RelinquishUniqueFileResource(read_info->filename);
cristy3ed852e2009-09-05 21:47:34 +0000821 read_info=DestroyImageInfo(read_info);
822 if (postscript_image == (Image *) NULL)
823 {
824 image=DestroyImageList(image);
825 ThrowFileException(exception,DelegateError,"PostscriptDelegateFailed",
826 image_info->filename);
827 return((Image *) NULL);
828 }
829 if (LocaleCompare(postscript_image->magick,"BMP") == 0)
830 {
831 Image
832 *cmyk_image;
833
834 cmyk_image=ConsolidateCMYKImages(postscript_image,exception);
835 if (cmyk_image != (Image *) NULL)
836 {
837 postscript_image=DestroyImageList(postscript_image);
838 postscript_image=cmyk_image;
839 }
840 }
841 if (image_info->number_scenes != 0)
842 {
843 Image
844 *clone_image;
845
cristybb503372010-05-27 20:51:26 +0000846 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000847 i;
848
849 /*
850 Add place holder images to meet the subimage specification requirement.
851 */
cristybb503372010-05-27 20:51:26 +0000852 for (i=0; i < (ssize_t) image_info->scene; i++)
cristy3ed852e2009-09-05 21:47:34 +0000853 {
854 clone_image=CloneImage(postscript_image,1,1,MagickTrue,exception);
855 if (clone_image != (Image *) NULL)
856 PrependImageToList(&postscript_image,clone_image);
857 }
858 }
859 do
860 {
861 (void) CopyMagickString(postscript_image->filename,filename,MaxTextExtent);
862 if (columns != 0)
863 postscript_image->magick_columns=columns;
864 if (rows != 0)
865 postscript_image->magick_rows=rows;
866 postscript_image->page=page;
867 (void) CloneImageProfiles(postscript_image,image);
868 (void) CloneImageProperties(postscript_image,image);
869 next=SyncNextImageInList(postscript_image);
870 if (next != (Image *) NULL)
871 postscript_image=next;
872 } while (next != (Image *) NULL);
873 image=DestroyImageList(image);
874 scene=0;
875 for (next=GetFirstImageInList(postscript_image); next != (Image *) NULL; )
876 {
877 next->scene=scene++;
878 next=GetNextImageInList(next);
879 }
880 return(GetFirstImageInList(postscript_image));
881}
882
883/*
884%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
885% %
886% %
887% %
888% R e g i s t e r P S I m a g e %
889% %
890% %
891% %
892%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
893%
894% RegisterPSImage() adds properties for the PS image format to
895% the list of supported formats. The properties include the image format
896% tag, a method to read and/or write the format, whether the format
897% supports the saving of more than one frame to the same file or blob,
898% whether the format supports native in-memory I/O, and a brief
899% description of the format.
900%
901% The format of the RegisterPSImage method is:
902%
cristybb503372010-05-27 20:51:26 +0000903% size_t RegisterPSImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000904%
905*/
cristybb503372010-05-27 20:51:26 +0000906ModuleExport size_t RegisterPSImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000907{
908 MagickInfo
909 *entry;
910
911 entry=SetMagickInfo("EPI");
912 entry->decoder=(DecodeImageHandler *) ReadPSImage;
913 entry->encoder=(EncodeImageHandler *) WritePSImage;
914 entry->magick=(IsImageFormatHandler *) IsPS;
915 entry->adjoin=MagickFalse;
916 entry->blob_support=MagickFalse;
917 entry->seekable_stream=MagickTrue;
918 entry->thread_support=EncoderThreadSupport;
919 entry->description=ConstantString(
920 "Encapsulated PostScript Interchange format");
921 entry->module=ConstantString("PS");
922 (void) RegisterMagickInfo(entry);
923 entry=SetMagickInfo("EPS");
924 entry->decoder=(DecodeImageHandler *) ReadPSImage;
925 entry->encoder=(EncodeImageHandler *) WritePSImage;
926 entry->magick=(IsImageFormatHandler *) IsPS;
927 entry->adjoin=MagickFalse;
928 entry->blob_support=MagickFalse;
929 entry->seekable_stream=MagickTrue;
930 entry->thread_support=EncoderThreadSupport;
931 entry->description=ConstantString("Encapsulated PostScript");
932 entry->module=ConstantString("PS");
933 (void) RegisterMagickInfo(entry);
934 entry=SetMagickInfo("EPSF");
935 entry->decoder=(DecodeImageHandler *) ReadPSImage;
936 entry->encoder=(EncodeImageHandler *) WritePSImage;
937 entry->magick=(IsImageFormatHandler *) IsPS;
938 entry->adjoin=MagickFalse;
939 entry->blob_support=MagickFalse;
940 entry->seekable_stream=MagickTrue;
941 entry->description=ConstantString("Encapsulated PostScript");
942 entry->module=ConstantString("PS");
943 (void) RegisterMagickInfo(entry);
944 entry=SetMagickInfo("EPSI");
945 entry->decoder=(DecodeImageHandler *) ReadPSImage;
946 entry->encoder=(EncodeImageHandler *) WritePSImage;
947 entry->magick=(IsImageFormatHandler *) IsPS;
948 entry->adjoin=MagickFalse;
949 entry->blob_support=MagickFalse;
950 entry->seekable_stream=MagickTrue;
951 entry->thread_support=EncoderThreadSupport;
952 entry->description=ConstantString(
953 "Encapsulated PostScript Interchange format");
954 entry->module=ConstantString("PS");
955 (void) RegisterMagickInfo(entry);
956 entry=SetMagickInfo("PS");
957 entry->decoder=(DecodeImageHandler *) ReadPSImage;
958 entry->encoder=(EncodeImageHandler *) WritePSImage;
959 entry->magick=(IsImageFormatHandler *) IsPS;
960 entry->module=ConstantString("PS");
961 entry->blob_support=MagickFalse;
962 entry->seekable_stream=MagickTrue;
963 entry->thread_support=EncoderThreadSupport;
964 entry->description=ConstantString("PostScript");
965 (void) RegisterMagickInfo(entry);
966 return(MagickImageCoderSignature);
967}
968
969/*
970%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
971% %
972% %
973% %
974% U n r e g i s t e r P S I m a g e %
975% %
976% %
977% %
978%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
979%
980% UnregisterPSImage() removes format registrations made by the
981% PS module from the list of supported formats.
982%
983% The format of the UnregisterPSImage method is:
984%
985% UnregisterPSImage(void)
986%
987*/
988ModuleExport void UnregisterPSImage(void)
989{
990 (void) UnregisterMagickInfo("EPI");
991 (void) UnregisterMagickInfo("EPS");
992 (void) UnregisterMagickInfo("EPSF");
993 (void) UnregisterMagickInfo("EPSI");
994 (void) UnregisterMagickInfo("PS");
995}
996
997/*
998%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
999% %
1000% %
1001% %
1002% W r i t e P S I m a g e %
1003% %
1004% %
1005% %
1006%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1007%
1008% WritePSImage translates an image to encapsulated Postscript
1009% Level I for printing. If the supplied geometry is null, the image is
1010% centered on the Postscript page. Otherwise, the image is positioned as
1011% specified by the geometry.
1012%
1013% The format of the WritePSImage method is:
1014%
cristy1e178e72011-08-28 19:44:34 +00001015% MagickBooleanType WritePSImage(const ImageInfo *image_info,
1016% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001017%
1018% A description of each parameter follows:
1019%
1020% o image_info: the image info.
1021%
1022% o image: the image.
1023%
cristy1e178e72011-08-28 19:44:34 +00001024% o exception: return any errors or warnings in this structure.
1025%
cristy3ed852e2009-09-05 21:47:34 +00001026*/
1027
1028static inline size_t MagickMin(const size_t x,const size_t y)
1029{
1030 if (x < y)
1031 return(x);
1032 return(y);
1033}
1034
1035static inline unsigned char *PopHexPixel(const char **hex_digits,
cristybb503372010-05-27 20:51:26 +00001036 const size_t pixel,unsigned char *pixels)
cristy3ed852e2009-09-05 21:47:34 +00001037{
1038 register const char
1039 *hex;
1040
1041 hex=hex_digits[pixel];
1042 *pixels++=(unsigned char) (*hex++);
1043 *pixels++=(unsigned char) (*hex);
1044 return(pixels);
1045}
1046
cristy1e178e72011-08-28 19:44:34 +00001047static MagickBooleanType WritePSImage(const ImageInfo *image_info,Image *image,
1048 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001049{
1050#define WriteRunlengthPacket(image,pixel,length,p) \
1051{ \
1052 if ((image->matte != MagickFalse) && \
cristy4c08aed2011-07-01 19:47:50 +00001053 (GetPixelAlpha(image,p) == (Quantum) TransparentAlpha)) \
cristy3ed852e2009-09-05 21:47:34 +00001054 { \
1055 q=PopHexPixel(hex_digits,0xff,q); \
1056 q=PopHexPixel(hex_digits,0xff,q); \
1057 q=PopHexPixel(hex_digits,0xff,q); \
1058 } \
1059 else \
1060 { \
1061 q=PopHexPixel(hex_digits,ScaleQuantumToChar(pixel.red),q); \
1062 q=PopHexPixel(hex_digits,ScaleQuantumToChar(pixel.green),q); \
1063 q=PopHexPixel(hex_digits,ScaleQuantumToChar(pixel.blue),q); \
1064 } \
cristy9f027d12011-09-21 01:17:17 +00001065 q=PopHexPixel(hex_digits,(size_t) MagickMin(length,0xff),q); \
cristy3ed852e2009-09-05 21:47:34 +00001066}
1067
1068 static const char
1069 *hex_digits[] =
1070 {
1071 "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0A", "0B",
1072 "0C", "0D", "0E", "0F", "10", "11", "12", "13", "14", "15", "16", "17",
1073 "18", "19", "1A", "1B", "1C", "1D", "1E", "1F", "20", "21", "22", "23",
1074 "24", "25", "26", "27", "28", "29", "2A", "2B", "2C", "2D", "2E", "2F",
1075 "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3A", "3B",
1076 "3C", "3D", "3E", "3F", "40", "41", "42", "43", "44", "45", "46", "47",
1077 "48", "49", "4A", "4B", "4C", "4D", "4E", "4F", "50", "51", "52", "53",
1078 "54", "55", "56", "57", "58", "59", "5A", "5B", "5C", "5D", "5E", "5F",
1079 "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6A", "6B",
1080 "6C", "6D", "6E", "6F", "70", "71", "72", "73", "74", "75", "76", "77",
1081 "78", "79", "7A", "7B", "7C", "7D", "7E", "7F", "80", "81", "82", "83",
1082 "84", "85", "86", "87", "88", "89", "8A", "8B", "8C", "8D", "8E", "8F",
1083 "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9A", "9B",
1084 "9C", "9D", "9E", "9F", "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7",
1085 "A8", "A9", "AA", "AB", "AC", "AD", "AE", "AF", "B0", "B1", "B2", "B3",
1086 "B4", "B5", "B6", "B7", "B8", "B9", "BA", "BB", "BC", "BD", "BE", "BF",
1087 "C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "CA", "CB",
1088 "CC", "CD", "CE", "CF", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
1089 "D8", "D9", "DA", "DB", "DC", "DD", "DE", "DF", "E0", "E1", "E2", "E3",
1090 "E4", "E5", "E6", "E7", "E8", "E9", "EA", "EB", "EC", "ED", "EE", "EF",
1091 "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "FA", "FB",
1092 "FC", "FD", "FE", "FF", (char *) NULL
1093 },
1094 *PostscriptProlog[]=
1095 {
1096 "%%BeginProlog",
1097 "%",
1098 "% Display a color image. The image is displayed in color on",
1099 "% Postscript viewers or printers that support color, otherwise",
1100 "% it is displayed as grayscale.",
1101 "%",
1102 "/DirectClassPacket",
1103 "{",
1104 " %",
1105 " % Get a DirectClass packet.",
1106 " %",
1107 " % Parameters:",
1108 " % red.",
1109 " % green.",
1110 " % blue.",
1111 " % length: number of pixels minus one of this color (optional).",
1112 " %",
1113 " currentfile color_packet readhexstring pop pop",
1114 " compression 0 eq",
1115 " {",
1116 " /number_pixels 3 def",
1117 " }",
1118 " {",
1119 " currentfile byte readhexstring pop 0 get",
1120 " /number_pixels exch 1 add 3 mul def",
1121 " } ifelse",
1122 " 0 3 number_pixels 1 sub",
1123 " {",
1124 " pixels exch color_packet putinterval",
1125 " } for",
1126 " pixels 0 number_pixels getinterval",
1127 "} bind def",
1128 "",
1129 "/DirectClassImage",
1130 "{",
1131 " %",
1132 " % Display a DirectClass image.",
1133 " %",
1134 " systemdict /colorimage known",
1135 " {",
1136 " columns rows 8",
1137 " [",
1138 " columns 0 0",
1139 " rows neg 0 rows",
1140 " ]",
1141 " { DirectClassPacket } false 3 colorimage",
1142 " }",
1143 " {",
1144 " %",
1145 " % No colorimage operator; convert to grayscale.",
1146 " %",
1147 " columns rows 8",
1148 " [",
1149 " columns 0 0",
1150 " rows neg 0 rows",
1151 " ]",
1152 " { GrayDirectClassPacket } image",
1153 " } ifelse",
1154 "} bind def",
1155 "",
1156 "/GrayDirectClassPacket",
1157 "{",
1158 " %",
1159 " % Get a DirectClass packet; convert to grayscale.",
1160 " %",
1161 " % Parameters:",
1162 " % red",
1163 " % green",
1164 " % blue",
1165 " % length: number of pixels minus one of this color (optional).",
1166 " %",
1167 " currentfile color_packet readhexstring pop pop",
1168 " color_packet 0 get 0.299 mul",
1169 " color_packet 1 get 0.587 mul add",
1170 " color_packet 2 get 0.114 mul add",
1171 " cvi",
1172 " /gray_packet exch def",
1173 " compression 0 eq",
1174 " {",
1175 " /number_pixels 1 def",
1176 " }",
1177 " {",
1178 " currentfile byte readhexstring pop 0 get",
1179 " /number_pixels exch 1 add def",
1180 " } ifelse",
1181 " 0 1 number_pixels 1 sub",
1182 " {",
1183 " pixels exch gray_packet put",
1184 " } for",
1185 " pixels 0 number_pixels getinterval",
1186 "} bind def",
1187 "",
1188 "/GrayPseudoClassPacket",
1189 "{",
1190 " %",
1191 " % Get a PseudoClass packet; convert to grayscale.",
1192 " %",
1193 " % Parameters:",
1194 " % index: index into the colormap.",
1195 " % length: number of pixels minus one of this color (optional).",
1196 " %",
1197 " currentfile byte readhexstring pop 0 get",
1198 " /offset exch 3 mul def",
1199 " /color_packet colormap offset 3 getinterval def",
1200 " color_packet 0 get 0.299 mul",
1201 " color_packet 1 get 0.587 mul add",
1202 " color_packet 2 get 0.114 mul add",
1203 " cvi",
1204 " /gray_packet exch def",
1205 " compression 0 eq",
1206 " {",
1207 " /number_pixels 1 def",
1208 " }",
1209 " {",
1210 " currentfile byte readhexstring pop 0 get",
1211 " /number_pixels exch 1 add def",
1212 " } ifelse",
1213 " 0 1 number_pixels 1 sub",
1214 " {",
1215 " pixels exch gray_packet put",
1216 " } for",
1217 " pixels 0 number_pixels getinterval",
1218 "} bind def",
1219 "",
1220 "/PseudoClassPacket",
1221 "{",
1222 " %",
1223 " % Get a PseudoClass packet.",
1224 " %",
1225 " % Parameters:",
1226 " % index: index into the colormap.",
1227 " % length: number of pixels minus one of this color (optional).",
1228 " %",
1229 " currentfile byte readhexstring pop 0 get",
1230 " /offset exch 3 mul def",
1231 " /color_packet colormap offset 3 getinterval def",
1232 " compression 0 eq",
1233 " {",
1234 " /number_pixels 3 def",
1235 " }",
1236 " {",
1237 " currentfile byte readhexstring pop 0 get",
1238 " /number_pixels exch 1 add 3 mul def",
1239 " } ifelse",
1240 " 0 3 number_pixels 1 sub",
1241 " {",
1242 " pixels exch color_packet putinterval",
1243 " } for",
1244 " pixels 0 number_pixels getinterval",
1245 "} bind def",
1246 "",
1247 "/PseudoClassImage",
1248 "{",
1249 " %",
1250 " % Display a PseudoClass image.",
1251 " %",
1252 " % Parameters:",
1253 " % class: 0-PseudoClass or 1-Grayscale.",
1254 " %",
1255 " currentfile buffer readline pop",
1256 " token pop /class exch def pop",
1257 " class 0 gt",
1258 " {",
1259 " currentfile buffer readline pop",
1260 " token pop /depth exch def pop",
1261 " /grays columns 8 add depth sub depth mul 8 idiv string def",
1262 " columns rows depth",
1263 " [",
1264 " columns 0 0",
1265 " rows neg 0 rows",
1266 " ]",
1267 " { currentfile grays readhexstring pop } image",
1268 " }",
1269 " {",
1270 " %",
1271 " % Parameters:",
1272 " % colors: number of colors in the colormap.",
1273 " % colormap: red, green, blue color packets.",
1274 " %",
1275 " currentfile buffer readline pop",
1276 " token pop /colors exch def pop",
1277 " /colors colors 3 mul def",
1278 " /colormap colors string def",
1279 " currentfile colormap readhexstring pop pop",
1280 " systemdict /colorimage known",
1281 " {",
1282 " columns rows 8",
1283 " [",
1284 " columns 0 0",
1285 " rows neg 0 rows",
1286 " ]",
1287 " { PseudoClassPacket } false 3 colorimage",
1288 " }",
1289 " {",
1290 " %",
1291 " % No colorimage operator; convert to grayscale.",
1292 " %",
1293 " columns rows 8",
1294 " [",
1295 " columns 0 0",
1296 " rows neg 0 rows",
1297 " ]",
1298 " { GrayPseudoClassPacket } image",
1299 " } ifelse",
1300 " } ifelse",
1301 "} bind def",
1302 "",
1303 "/DisplayImage",
1304 "{",
1305 " %",
1306 " % Display a DirectClass or PseudoClass image.",
1307 " %",
1308 " % Parameters:",
1309 " % x & y translation.",
1310 " % x & y scale.",
1311 " % label pointsize.",
1312 " % image label.",
1313 " % image columns & rows.",
1314 " % class: 0-DirectClass or 1-PseudoClass.",
1315 " % compression: 0-none or 1-RunlengthEncoded.",
1316 " % hex color packets.",
1317 " %",
1318 " gsave",
1319 " /buffer 512 string def",
1320 " /byte 1 string def",
1321 " /color_packet 3 string def",
1322 " /pixels 768 string def",
1323 "",
1324 " currentfile buffer readline pop",
1325 " token pop /x exch def",
1326 " token pop /y exch def pop",
1327 " x y translate",
1328 " currentfile buffer readline pop",
1329 " token pop /x exch def",
1330 " token pop /y exch def pop",
1331 " currentfile buffer readline pop",
1332 " token pop /pointsize exch def pop",
1333 " /Times-Roman findfont pointsize scalefont setfont",
1334 (char *) NULL
1335 },
1336 *PostscriptEpilog[]=
1337 {
1338 " x y scale",
1339 " currentfile buffer readline pop",
1340 " token pop /columns exch def",
1341 " token pop /rows exch def pop",
1342 " currentfile buffer readline pop",
1343 " token pop /class exch def pop",
1344 " currentfile buffer readline pop",
1345 " token pop /compression exch def pop",
1346 " class 0 gt { PseudoClassImage } { DirectClassImage } ifelse",
1347 (char *) NULL
1348 };
1349
1350 char
1351 buffer[MaxTextExtent],
1352 date[MaxTextExtent],
1353 **labels,
1354 page_geometry[MaxTextExtent];
1355
1356 const char
1357 **s,
1358 *value;
1359
1360 const StringInfo
1361 *profile;
1362
1363 double
1364 pointsize;
1365
1366 GeometryInfo
1367 geometry_info;
1368
cristy3ed852e2009-09-05 21:47:34 +00001369 MagickBooleanType
1370 status;
1371
1372 MagickOffsetType
1373 scene;
1374
1375 MagickStatusType
1376 flags;
1377
cristy101ab702011-10-13 13:06:32 +00001378 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001379 pixel;
1380
1381 PointInfo
1382 delta,
1383 resolution,
1384 scale;
1385
cristy4c08aed2011-07-01 19:47:50 +00001386 Quantum
1387 index;
1388
cristy3ed852e2009-09-05 21:47:34 +00001389 RectangleInfo
1390 geometry,
1391 media_info,
1392 page_info;
1393
cristy4c08aed2011-07-01 19:47:50 +00001394 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00001395 *p;
1396
cristybb503372010-05-27 20:51:26 +00001397 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001398 i,
1399 x;
1400
1401 register unsigned char
1402 *q;
1403
1404 SegmentInfo
1405 bounds;
1406
1407 size_t
cristy802d3642011-04-27 02:02:41 +00001408 bit,
1409 byte,
1410 length,
1411 page,
1412 text_size;
1413
1414 ssize_t
1415 j,
1416 y;
cristy3ed852e2009-09-05 21:47:34 +00001417
1418 time_t
1419 timer;
1420
1421 unsigned char
1422 pixels[2048];
1423
cristy3ed852e2009-09-05 21:47:34 +00001424 /*
1425 Open output image file.
1426 */
1427 assert(image_info != (const ImageInfo *) NULL);
1428 assert(image_info->signature == MagickSignature);
1429 assert(image != (Image *) NULL);
1430 assert(image->signature == MagickSignature);
1431 if (image->debug != MagickFalse)
1432 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +00001433 assert(exception != (ExceptionInfo *) NULL);
1434 assert(exception->signature == MagickSignature);
cristy1e178e72011-08-28 19:44:34 +00001435 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +00001436 if (status == MagickFalse)
1437 return(status);
1438 (void) ResetMagickMemory(&bounds,0,sizeof(bounds));
1439 page=1;
1440 scene=0;
1441 do
1442 {
1443 /*
1444 Scale relative to dots-per-inch.
1445 */
cristy510d06a2011-07-06 23:43:54 +00001446 if ((IsRGBColorspace(image->colorspace) == MagickFalse) &&
cristy3ed852e2009-09-05 21:47:34 +00001447 (image->colorspace != CMYKColorspace))
cristye941a752011-10-15 01:52:48 +00001448 (void) TransformImageColorspace(image,RGBColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +00001449 delta.x=DefaultResolution;
1450 delta.y=DefaultResolution;
cristy2a11bef2011-10-28 18:33:11 +00001451 resolution.x=image->resolution.x;
1452 resolution.y=image->resolution.y;
cristy3ed852e2009-09-05 21:47:34 +00001453 if ((resolution.x == 0.0) || (resolution.y == 0.0))
1454 {
1455 flags=ParseGeometry(PSDensityGeometry,&geometry_info);
1456 resolution.x=geometry_info.rho;
1457 resolution.y=geometry_info.sigma;
1458 if ((flags & SigmaValue) == 0)
1459 resolution.y=resolution.x;
1460 }
1461 if (image_info->density != (char *) NULL)
1462 {
1463 flags=ParseGeometry(image_info->density,&geometry_info);
1464 resolution.x=geometry_info.rho;
1465 resolution.y=geometry_info.sigma;
1466 if ((flags & SigmaValue) == 0)
1467 resolution.y=resolution.x;
1468 }
1469 if (image->units == PixelsPerCentimeterResolution)
1470 {
cristya97426c2011-02-04 01:41:27 +00001471 resolution.x=(double) ((size_t) (100.0*2.54*resolution.x+0.5)/100.0);
1472 resolution.y=(double) ((size_t) (100.0*2.54*resolution.y+0.5)/100.0);
cristy3ed852e2009-09-05 21:47:34 +00001473 }
1474 SetGeometry(image,&geometry);
cristyb51dff52011-05-19 16:55:47 +00001475 (void) FormatLocaleString(page_geometry,MaxTextExtent,"%.20gx%.20g",
cristye8c25f92010-06-03 00:53:06 +00001476 (double) image->columns,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001477 if (image_info->page != (char *) NULL)
1478 (void) CopyMagickString(page_geometry,image_info->page,MaxTextExtent);
1479 else
1480 if ((image->page.width != 0) && (image->page.height != 0))
cristyb51dff52011-05-19 16:55:47 +00001481 (void) FormatLocaleString(page_geometry,MaxTextExtent,
cristy6d8abba2010-06-03 01:10:47 +00001482 "%.20gx%.20g%+.20g%+.20g",(double) image->page.width,(double)
cristye8c25f92010-06-03 00:53:06 +00001483 image->page.height,(double) image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00001484 else
1485 if ((image->gravity != UndefinedGravity) &&
1486 (LocaleCompare(image_info->magick,"PS") == 0))
1487 (void) CopyMagickString(page_geometry,PSPageGeometry,MaxTextExtent);
1488 (void) ConcatenateMagickString(page_geometry,">",MaxTextExtent);
1489 (void) ParseMetaGeometry(page_geometry,&geometry.x,&geometry.y,
1490 &geometry.width,&geometry.height);
1491 scale.x=(double) (geometry.width*delta.x)/resolution.x;
cristybb503372010-05-27 20:51:26 +00001492 geometry.width=(size_t) floor(scale.x+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001493 scale.y=(double) (geometry.height*delta.y)/resolution.y;
cristybb503372010-05-27 20:51:26 +00001494 geometry.height=(size_t) floor(scale.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001495 (void) ParseAbsoluteGeometry(page_geometry,&media_info);
cristy1e178e72011-08-28 19:44:34 +00001496 (void) ParseGravityGeometry(image,page_geometry,&page_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00001497 if (image->gravity != UndefinedGravity)
1498 {
1499 geometry.x=(-page_info.x);
cristybb503372010-05-27 20:51:26 +00001500 geometry.y=(ssize_t) (media_info.height+page_info.y-image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001501 }
1502 pointsize=12.0;
1503 if (image_info->pointsize != 0.0)
1504 pointsize=image_info->pointsize;
1505 text_size=0;
cristyd15e6592011-10-15 00:13:06 +00001506 value=GetImageProperty(image,"label",exception);
cristy3ed852e2009-09-05 21:47:34 +00001507 if (value != (const char *) NULL)
cristybb503372010-05-27 20:51:26 +00001508 text_size=(size_t) (MultilineCensus(value)*pointsize+12);
cristy3ed852e2009-09-05 21:47:34 +00001509 if (page == 1)
1510 {
1511 /*
1512 Output Postscript header.
1513 */
1514 if (LocaleCompare(image_info->magick,"PS") == 0)
1515 (void) CopyMagickString(buffer,"%!PS-Adobe-3.0\n",MaxTextExtent);
1516 else
1517 (void) CopyMagickString(buffer,"%!PS-Adobe-3.0 EPSF-3.0\n",
1518 MaxTextExtent);
1519 (void) WriteBlobString(image,buffer);
1520 (void) WriteBlobString(image,"%%Creator: (ImageMagick)\n");
cristyb51dff52011-05-19 16:55:47 +00001521 (void) FormatLocaleString(buffer,MaxTextExtent,"%%%%Title: (%s)\n",
cristy3ed852e2009-09-05 21:47:34 +00001522 image->filename);
1523 (void) WriteBlobString(image,buffer);
1524 timer=time((time_t *) NULL);
1525 (void) FormatMagickTime(timer,MaxTextExtent,date);
cristyb51dff52011-05-19 16:55:47 +00001526 (void) FormatLocaleString(buffer,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00001527 "%%%%CreationDate: (%s)\n",date);
1528 (void) WriteBlobString(image,buffer);
1529 bounds.x1=(double) geometry.x;
1530 bounds.y1=(double) geometry.y;
1531 bounds.x2=(double) geometry.x+scale.x;
1532 bounds.y2=(double) geometry.y+(geometry.height+text_size);
1533 if ((image_info->adjoin != MagickFalse) &&
1534 (GetNextImageInList(image) != (Image *) NULL))
1535 (void) CopyMagickString(buffer,"%%%%BoundingBox: (atend)\n",
1536 MaxTextExtent);
1537 else
1538 {
cristyb51dff52011-05-19 16:55:47 +00001539 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001540 "%%%%BoundingBox: %.20g %.20g %.20g %.20g\n",ceil(bounds.x1-0.5),
1541 ceil(bounds.y1-0.5),floor(bounds.x2+0.5),floor(bounds.y2+0.5));
cristy3ed852e2009-09-05 21:47:34 +00001542 (void) WriteBlobString(image,buffer);
cristyb51dff52011-05-19 16:55:47 +00001543 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001544 "%%%%HiResBoundingBox: %g %g %g %g\n",bounds.x1,
cristy8cd5b312010-01-07 01:10:24 +00001545 bounds.y1,bounds.x2,bounds.y2);
cristy3ed852e2009-09-05 21:47:34 +00001546 }
1547 (void) WriteBlobString(image,buffer);
1548 profile=GetImageProfile(image,"8bim");
1549 if (profile != (StringInfo *) NULL)
1550 {
1551 /*
1552 Embed Photoshop profile.
1553 */
cristyb51dff52011-05-19 16:55:47 +00001554 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001555 "%%BeginPhotoshop: %.20g",(double) GetStringInfoLength(profile));
cristy3ed852e2009-09-05 21:47:34 +00001556 (void) WriteBlobString(image,buffer);
cristybb503372010-05-27 20:51:26 +00001557 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
cristy3ed852e2009-09-05 21:47:34 +00001558 {
1559 if ((i % 32) == 0)
1560 (void) WriteBlobString(image,"\n% ");
cristyb51dff52011-05-19 16:55:47 +00001561 (void) FormatLocaleString(buffer,MaxTextExtent,"%02X",
cristy3ed852e2009-09-05 21:47:34 +00001562 (unsigned int) (GetStringInfoDatum(profile)[i] & 0xff));
1563 (void) WriteBlobString(image,buffer);
1564 }
1565 (void) WriteBlobString(image,"\n%EndPhotoshop\n");
1566 }
1567 profile=GetImageProfile(image,"xmp");
cristy2b2eb912010-03-03 14:56:40 +00001568 if (0 && (profile != (StringInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001569 {
1570 /*
1571 Embed XML profile.
1572 */
1573 (void) WriteBlobString(image,"\n%begin_xml_code\n");
cristyb51dff52011-05-19 16:55:47 +00001574 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001575 "\n%%begin_xml_packet: %.20g\n",(double)
cristyeec18db2010-03-03 21:15:45 +00001576 GetStringInfoLength(profile));
cristy2b2eb912010-03-03 14:56:40 +00001577 (void) WriteBlobString(image,buffer);
cristybb503372010-05-27 20:51:26 +00001578 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
cristy3ed852e2009-09-05 21:47:34 +00001579 (void) WriteBlobByte(image,GetStringInfoDatum(profile)[i]);
cristy2b2eb912010-03-03 14:56:40 +00001580 (void) WriteBlobString(image,"\n%end_xml_packet\n%end_xml_code\n");
cristy3ed852e2009-09-05 21:47:34 +00001581 }
cristyd15e6592011-10-15 00:13:06 +00001582 value=GetImageProperty(image,"label",exception);
cristy3ed852e2009-09-05 21:47:34 +00001583 if (value != (const char *) NULL)
1584 (void) WriteBlobString(image,
1585 "%%DocumentNeededResources: font Times-Roman\n");
1586 (void) WriteBlobString(image,"%%DocumentData: Clean7Bit\n");
1587 (void) WriteBlobString(image,"%%LanguageLevel: 1\n");
1588 if (LocaleCompare(image_info->magick,"PS") != 0)
1589 (void) WriteBlobString(image,"%%Pages: 1\n");
1590 else
1591 {
1592 /*
1593 Compute the number of pages.
1594 */
1595 (void) WriteBlobString(image,"%%Orientation: Portrait\n");
1596 (void) WriteBlobString(image,"%%PageOrder: Ascend\n");
cristyb51dff52011-05-19 16:55:47 +00001597 (void) FormatLocaleString(buffer,MaxTextExtent,"%%%%Pages: %.20g\n",
cristye8c25f92010-06-03 00:53:06 +00001598 image_info->adjoin != MagickFalse ? (double)
1599 GetImageListLength(image) : 1.0);
cristy3ed852e2009-09-05 21:47:34 +00001600 (void) WriteBlobString(image,buffer);
1601 }
1602 (void) WriteBlobString(image,"%%EndComments\n");
1603 (void) WriteBlobString(image,"\n%%BeginDefaults\n");
1604 (void) WriteBlobString(image,"%%EndDefaults\n\n");
1605 if ((LocaleCompare(image_info->magick,"EPI") == 0) ||
1606 (LocaleCompare(image_info->magick,"EPSI") == 0) ||
1607 (LocaleCompare(image_info->magick,"EPT") == 0))
1608 {
1609 Image
1610 *preview_image;
1611
cristy3ed852e2009-09-05 21:47:34 +00001612 Quantum
1613 pixel;
1614
cristybb503372010-05-27 20:51:26 +00001615 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001616 x;
1617
cristy802d3642011-04-27 02:02:41 +00001618 ssize_t
1619 y;
1620
cristy3ed852e2009-09-05 21:47:34 +00001621 /*
1622 Create preview image.
1623 */
cristy1e178e72011-08-28 19:44:34 +00001624 preview_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00001625 if (preview_image == (Image *) NULL)
1626 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1627 /*
1628 Dump image as bitmap.
1629 */
cristyb51dff52011-05-19 16:55:47 +00001630 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001631 "%%%%BeginPreview: %.20g %.20g %.20g %.20g\n%% ",(double)
1632 preview_image->columns,(double) preview_image->rows,1.0,
1633 (double) ((((preview_image->columns+7) >> 3)*preview_image->rows+
1634 35)/36));
cristy3ed852e2009-09-05 21:47:34 +00001635 (void) WriteBlobString(image,buffer);
1636 q=pixels;
cristybb503372010-05-27 20:51:26 +00001637 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001638 {
1639 p=GetVirtualPixels(preview_image,0,y,preview_image->columns,1,
cristy1e178e72011-08-28 19:44:34 +00001640 exception);
cristy4c08aed2011-07-01 19:47:50 +00001641 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001642 break;
cristy3ed852e2009-09-05 21:47:34 +00001643 bit=0;
1644 byte=0;
cristybb503372010-05-27 20:51:26 +00001645 for (x=0; x < (ssize_t) preview_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001646 {
1647 byte<<=1;
cristy4c08aed2011-07-01 19:47:50 +00001648 pixel=GetPixelIntensity(preview_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001649 if (pixel >= (Quantum) (QuantumRange/2))
1650 byte|=0x01;
1651 bit++;
1652 if (bit == 8)
1653 {
1654 q=PopHexPixel(hex_digits,byte,q);
1655 if ((q-pixels+8) >= 80)
1656 {
1657 *q++='\n';
1658 (void) WriteBlob(image,q-pixels,pixels);
1659 q=pixels;
1660 (void) WriteBlobString(image,"% ");
1661 };
1662 bit=0;
1663 byte=0;
1664 }
1665 }
1666 if (bit != 0)
1667 {
1668 byte<<=(8-bit);
1669 q=PopHexPixel(hex_digits,byte,q);
1670 if ((q-pixels+8) >= 80)
1671 {
1672 *q++='\n';
1673 (void) WriteBlob(image,q-pixels,pixels);
1674 q=pixels;
1675 (void) WriteBlobString(image,"% ");
1676 };
1677 };
1678 }
1679 if (q != pixels)
1680 {
1681 *q++='\n';
1682 (void) WriteBlob(image,q-pixels,pixels);
1683 }
1684 (void) WriteBlobString(image,"\n%%EndPreview\n");
1685 preview_image=DestroyImage(preview_image);
1686 }
1687 /*
1688 Output Postscript commands.
1689 */
1690 for (s=PostscriptProlog; *s != (char *) NULL; s++)
1691 {
cristyb51dff52011-05-19 16:55:47 +00001692 (void) FormatLocaleString(buffer,MaxTextExtent,"%s\n",*s);
cristy3ed852e2009-09-05 21:47:34 +00001693 (void) WriteBlobString(image,buffer);
1694 }
cristyd15e6592011-10-15 00:13:06 +00001695 value=GetImageProperty(image,"label",exception);
cristy3ed852e2009-09-05 21:47:34 +00001696 if (value != (const char *) NULL)
cristybb503372010-05-27 20:51:26 +00001697 for (j=(ssize_t) MultilineCensus(value)-1; j >= 0; j--)
cristy3ed852e2009-09-05 21:47:34 +00001698 {
1699 (void) WriteBlobString(image," /label 512 string def\n");
1700 (void) WriteBlobString(image," currentfile label readline pop\n");
cristyb51dff52011-05-19 16:55:47 +00001701 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001702 " 0 y %g add moveto label show pop\n",j*pointsize+12);
cristy3ed852e2009-09-05 21:47:34 +00001703 (void) WriteBlobString(image,buffer);
1704 }
1705 for (s=PostscriptEpilog; *s != (char *) NULL; s++)
1706 {
cristyb51dff52011-05-19 16:55:47 +00001707 (void) FormatLocaleString(buffer,MaxTextExtent,"%s\n",*s);
cristy3ed852e2009-09-05 21:47:34 +00001708 (void) WriteBlobString(image,buffer);
1709 }
1710 if (LocaleCompare(image_info->magick,"PS") == 0)
1711 (void) WriteBlobString(image," showpage\n");
1712 (void) WriteBlobString(image,"} bind def\n");
1713 (void) WriteBlobString(image,"%%EndProlog\n");
1714 }
cristyb51dff52011-05-19 16:55:47 +00001715 (void) FormatLocaleString(buffer,MaxTextExtent,"%%%%Page: 1 %.20g\n",
cristye8c25f92010-06-03 00:53:06 +00001716 (double) (page++));
cristy3ed852e2009-09-05 21:47:34 +00001717 (void) WriteBlobString(image,buffer);
cristyb51dff52011-05-19 16:55:47 +00001718 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001719 "%%%%PageBoundingBox: %.20g %.20g %.20g %.20g\n",(double) geometry.x,
1720 (double) geometry.y,geometry.x+(double) geometry.width,geometry.y+(double)
cristyf2faecf2010-05-28 19:19:36 +00001721 (geometry.height+text_size));
cristy3ed852e2009-09-05 21:47:34 +00001722 (void) WriteBlobString(image,buffer);
1723 if ((double) geometry.x < bounds.x1)
1724 bounds.x1=(double) geometry.x;
1725 if ((double) geometry.y < bounds.y1)
1726 bounds.y1=(double) geometry.y;
1727 if ((double) (geometry.x+geometry.width-1) > bounds.x2)
1728 bounds.x2=(double) geometry.x+geometry.width-1;
1729 if ((double) (geometry.y+(geometry.height+text_size)-1) > bounds.y2)
1730 bounds.y2=(double) geometry.y+(geometry.height+text_size)-1;
cristyd15e6592011-10-15 00:13:06 +00001731 value=GetImageProperty(image,"label",exception);
cristy3ed852e2009-09-05 21:47:34 +00001732 if (value != (const char *) NULL)
1733 (void) WriteBlobString(image,"%%%%PageResources: font Times-Roman\n");
1734 if (LocaleCompare(image_info->magick,"PS") != 0)
1735 (void) WriteBlobString(image,"userdict begin\n");
1736 (void) WriteBlobString(image,"DisplayImage\n");
1737 /*
1738 Output image data.
1739 */
cristyb51dff52011-05-19 16:55:47 +00001740 (void) FormatLocaleString(buffer,MaxTextExtent,"%.20g %.20g\n%g %g\n%g\n",
cristye8c25f92010-06-03 00:53:06 +00001741 (double) geometry.x,(double) geometry.y,scale.x,scale.y,pointsize);
cristy3ed852e2009-09-05 21:47:34 +00001742 (void) WriteBlobString(image,buffer);
1743 labels=(char **) NULL;
cristyd15e6592011-10-15 00:13:06 +00001744 value=GetImageProperty(image,"label",exception);
cristy3ed852e2009-09-05 21:47:34 +00001745 if (value != (const char *) NULL)
1746 labels=StringToList(value);
1747 if (labels != (char **) NULL)
1748 {
1749 for (i=0; labels[i] != (char *) NULL; i++)
1750 {
cristyb51dff52011-05-19 16:55:47 +00001751 (void) FormatLocaleString(buffer,MaxTextExtent,"%s \n",
cristy3ed852e2009-09-05 21:47:34 +00001752 labels[i]);
1753 (void) WriteBlobString(image,buffer);
1754 labels[i]=DestroyString(labels[i]);
1755 }
1756 labels=(char **) RelinquishMagickMemory(labels);
1757 }
1758 (void) ResetMagickMemory(&pixel,0,sizeof(pixel));
cristy4c08aed2011-07-01 19:47:50 +00001759 pixel.alpha=(Quantum) TransparentAlpha;
1760 index=0;
cristy3ed852e2009-09-05 21:47:34 +00001761 x=0;
1762 if ((image_info->type != TrueColorType) &&
cristy1e178e72011-08-28 19:44:34 +00001763 (IsImageGray(image,exception) != MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +00001764 {
cristy1e178e72011-08-28 19:44:34 +00001765 if (IsImageMonochrome(image,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001766 {
1767 Quantum
1768 pixel;
1769
1770 /*
1771 Dump image as grayscale.
1772 */
cristyb51dff52011-05-19 16:55:47 +00001773 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001774 "%.20g %.20g\n1\n1\n1\n8\n",(double) image->columns,(double)
1775 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001776 (void) WriteBlobString(image,buffer);
1777 q=pixels;
cristybb503372010-05-27 20:51:26 +00001778 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001779 {
cristy1e178e72011-08-28 19:44:34 +00001780 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001781 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001782 break;
cristybb503372010-05-27 20:51:26 +00001783 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001784 {
cristy4c08aed2011-07-01 19:47:50 +00001785 pixel=(Quantum) ScaleQuantumToChar(GetPixelIntensity(image,p));
cristya97426c2011-02-04 01:41:27 +00001786 q=PopHexPixel(hex_digits,(size_t) pixel,q);
cristy3ed852e2009-09-05 21:47:34 +00001787 i++;
1788 if ((q-pixels+8) >= 80)
1789 {
1790 *q++='\n';
1791 (void) WriteBlob(image,q-pixels,pixels);
1792 q=pixels;
1793 }
cristyed231572011-07-14 02:18:59 +00001794 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001795 }
1796 if (image->previous == (Image *) NULL)
1797 {
cristya97426c2011-02-04 01:41:27 +00001798 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType)
1799 y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001800 if (status == MagickFalse)
1801 break;
1802 }
1803 }
1804 if (q != pixels)
1805 {
1806 *q++='\n';
1807 (void) WriteBlob(image,q-pixels,pixels);
1808 }
1809 }
1810 else
1811 {
cristybb503372010-05-27 20:51:26 +00001812 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001813 y;
1814
1815 Quantum
1816 pixel;
1817
1818 /*
1819 Dump image as bitmap.
1820 */
cristyb51dff52011-05-19 16:55:47 +00001821 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001822 "%.20g %.20g\n1\n1\n1\n1\n",(double) image->columns,(double)
1823 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001824 (void) WriteBlobString(image,buffer);
1825 q=pixels;
cristybb503372010-05-27 20:51:26 +00001826 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001827 {
cristy1e178e72011-08-28 19:44:34 +00001828 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001829 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001830 break;
cristy3ed852e2009-09-05 21:47:34 +00001831 bit=0;
1832 byte=0;
cristybb503372010-05-27 20:51:26 +00001833 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001834 {
1835 byte<<=1;
cristy4c08aed2011-07-01 19:47:50 +00001836 pixel=GetPixelIntensity(image,p);
cristy3ed852e2009-09-05 21:47:34 +00001837 if (pixel >= (Quantum) (QuantumRange/2))
1838 byte|=0x01;
1839 bit++;
1840 if (bit == 8)
1841 {
1842 q=PopHexPixel(hex_digits,byte,q);
1843 if ((q-pixels+2) >= 80)
1844 {
1845 *q++='\n';
1846 (void) WriteBlob(image,q-pixels,pixels);
1847 q=pixels;
1848 };
1849 bit=0;
1850 byte=0;
1851 }
cristyed231572011-07-14 02:18:59 +00001852 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001853 }
1854 if (bit != 0)
1855 {
1856 byte<<=(8-bit);
1857 q=PopHexPixel(hex_digits,byte,q);
1858 if ((q-pixels+2) >= 80)
1859 {
1860 *q++='\n';
1861 (void) WriteBlob(image,q-pixels,pixels);
1862 q=pixels;
1863 }
1864 };
1865 if (image->previous == (Image *) NULL)
1866 {
cristy6cde06a2011-11-24 00:08:43 +00001867 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType)
cristy802d3642011-04-27 02:02:41 +00001868 y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001869 if (status == MagickFalse)
1870 break;
1871 }
1872 }
1873 if (q != pixels)
1874 {
1875 *q++='\n';
1876 (void) WriteBlob(image,q-pixels,pixels);
1877 }
1878 }
1879 }
1880 else
1881 if ((image->storage_class == DirectClass) ||
1882 (image->colors > 256) || (image->matte != MagickFalse))
1883 {
1884 /*
1885 Dump DirectClass image.
1886 */
cristyb51dff52011-05-19 16:55:47 +00001887 (void) FormatLocaleString(buffer,MaxTextExtent,"%.20g %.20g\n0\n%d\n",
cristye8c25f92010-06-03 00:53:06 +00001888 (double) image->columns,(double) image->rows,
cristy3ed852e2009-09-05 21:47:34 +00001889 image_info->compression == RLECompression ? 1 : 0);
1890 (void) WriteBlobString(image,buffer);
1891 switch (image_info->compression)
1892 {
1893 case RLECompression:
1894 {
1895 /*
1896 Dump runlength-encoded DirectColor packets.
1897 */
1898 q=pixels;
cristybb503372010-05-27 20:51:26 +00001899 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001900 {
cristy1e178e72011-08-28 19:44:34 +00001901 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001902 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001903 break;
cristy101ab702011-10-13 13:06:32 +00001904 GetPixelInfoPixel(image,p,&pixel);
cristy3ed852e2009-09-05 21:47:34 +00001905 length=255;
cristybb503372010-05-27 20:51:26 +00001906 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001907 {
cristy4c08aed2011-07-01 19:47:50 +00001908 if ((GetPixelRed(image,p) == pixel.red) &&
1909 (GetPixelGreen(image,p) == pixel.green) &&
1910 (GetPixelBlue(image,p) == pixel.blue) &&
1911 (GetPixelAlpha(image,p) == pixel.alpha) &&
cristy802d3642011-04-27 02:02:41 +00001912 (length < 255) && (x < (ssize_t) (image->columns-1)))
cristy3ed852e2009-09-05 21:47:34 +00001913 length++;
1914 else
1915 {
1916 if (x > 0)
1917 {
1918 WriteRunlengthPacket(image,pixel,length,p);
1919 if ((q-pixels+10) >= 80)
1920 {
1921 *q++='\n';
1922 (void) WriteBlob(image,q-pixels,pixels);
1923 q=pixels;
1924 }
1925 }
1926 length=0;
1927 }
cristy101ab702011-10-13 13:06:32 +00001928 GetPixelInfoPixel(image,p,&pixel);
cristyed231572011-07-14 02:18:59 +00001929 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001930 }
1931 WriteRunlengthPacket(image,pixel,length,p);
1932 if ((q-pixels+10) >= 80)
1933 {
1934 *q++='\n';
1935 (void) WriteBlob(image,q-pixels,pixels);
1936 q=pixels;
1937 }
1938 if (image->previous == (Image *) NULL)
1939 {
cristy802d3642011-04-27 02:02:41 +00001940 status=SetImageProgress(image,SaveImageTag,
1941 (MagickOffsetType) y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001942 if (status == MagickFalse)
1943 break;
1944 }
1945 }
1946 if (q != pixels)
1947 {
1948 *q++='\n';
1949 (void) WriteBlob(image,q-pixels,pixels);
1950 }
1951 break;
1952 }
1953 case NoCompression:
1954 default:
1955 {
1956 /*
1957 Dump uncompressed DirectColor packets.
1958 */
1959 q=pixels;
cristybb503372010-05-27 20:51:26 +00001960 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001961 {
cristy1e178e72011-08-28 19:44:34 +00001962 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001963 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001964 break;
cristybb503372010-05-27 20:51:26 +00001965 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001966 {
1967 if ((image->matte != MagickFalse) &&
cristy4c08aed2011-07-01 19:47:50 +00001968 (GetPixelAlpha(image,p) == (Quantum) TransparentAlpha))
cristy3ed852e2009-09-05 21:47:34 +00001969 {
1970 q=PopHexPixel(hex_digits,0xff,q);
1971 q=PopHexPixel(hex_digits,0xff,q);
1972 q=PopHexPixel(hex_digits,0xff,q);
1973 }
1974 else
1975 {
cristy802d3642011-04-27 02:02:41 +00001976 q=PopHexPixel(hex_digits,ScaleQuantumToChar(
cristy4c08aed2011-07-01 19:47:50 +00001977 GetPixelRed(image,p)),q);
cristy802d3642011-04-27 02:02:41 +00001978 q=PopHexPixel(hex_digits,ScaleQuantumToChar(
cristy4c08aed2011-07-01 19:47:50 +00001979 GetPixelGreen(image,p)),q);
cristy802d3642011-04-27 02:02:41 +00001980 q=PopHexPixel(hex_digits,ScaleQuantumToChar(
cristy4c08aed2011-07-01 19:47:50 +00001981 GetPixelBlue(image,p)),q);
cristy3ed852e2009-09-05 21:47:34 +00001982 }
1983 if ((q-pixels+6) >= 80)
1984 {
1985 *q++='\n';
1986 (void) WriteBlob(image,q-pixels,pixels);
1987 q=pixels;
1988 }
cristyed231572011-07-14 02:18:59 +00001989 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001990 }
1991 if (image->previous == (Image *) NULL)
1992 {
cristy802d3642011-04-27 02:02:41 +00001993 status=SetImageProgress(image,SaveImageTag,
1994 (MagickOffsetType) y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001995 if (status == MagickFalse)
1996 break;
1997 }
1998 }
1999 if (q != pixels)
2000 {
2001 *q++='\n';
2002 (void) WriteBlob(image,q-pixels,pixels);
2003 }
2004 break;
2005 }
2006 }
2007 (void) WriteBlobByte(image,'\n');
2008 }
2009 else
2010 {
2011 /*
2012 Dump PseudoClass image.
2013 */
cristyb51dff52011-05-19 16:55:47 +00002014 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00002015 "%.20g %.20g\n%d\n%d\n0\n",(double) image->columns,(double)
2016 image->rows,image->storage_class == PseudoClass ? 1 : 0,
cristy3ed852e2009-09-05 21:47:34 +00002017 image_info->compression == RLECompression ? 1 : 0);
2018 (void) WriteBlobString(image,buffer);
2019 /*
2020 Dump number of colors and colormap.
2021 */
cristyb51dff52011-05-19 16:55:47 +00002022 (void) FormatLocaleString(buffer,MaxTextExtent,"%.20g\n",(double)
cristyf2faecf2010-05-28 19:19:36 +00002023 image->colors);
cristy3ed852e2009-09-05 21:47:34 +00002024 (void) WriteBlobString(image,buffer);
cristybb503372010-05-27 20:51:26 +00002025 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002026 {
cristyb51dff52011-05-19 16:55:47 +00002027 (void) FormatLocaleString(buffer,MaxTextExtent,"%02X%02X%02X\n",
cristy3ed852e2009-09-05 21:47:34 +00002028 ScaleQuantumToChar(image->colormap[i].red),
2029 ScaleQuantumToChar(image->colormap[i].green),
2030 ScaleQuantumToChar(image->colormap[i].blue));
2031 (void) WriteBlobString(image,buffer);
2032 }
2033 switch (image_info->compression)
2034 {
2035 case RLECompression:
2036 {
2037 /*
2038 Dump runlength-encoded PseudoColor packets.
2039 */
2040 q=pixels;
cristybb503372010-05-27 20:51:26 +00002041 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002042 {
cristy1e178e72011-08-28 19:44:34 +00002043 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002044 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002045 break;
cristy4c08aed2011-07-01 19:47:50 +00002046 index=GetPixelIndex(image,p);
cristy3ed852e2009-09-05 21:47:34 +00002047 length=255;
cristybb503372010-05-27 20:51:26 +00002048 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002049 {
cristy4c08aed2011-07-01 19:47:50 +00002050 if ((index == GetPixelIndex(image,p)) &&
cristy802d3642011-04-27 02:02:41 +00002051 (length < 255) && (x < ((ssize_t) image->columns-1)))
cristy3ed852e2009-09-05 21:47:34 +00002052 length++;
2053 else
2054 {
2055 if (x > 0)
2056 {
cristya97426c2011-02-04 01:41:27 +00002057 q=PopHexPixel(hex_digits,(size_t) index,q);
cristybb503372010-05-27 20:51:26 +00002058 q=PopHexPixel(hex_digits,(size_t)
cristy3ed852e2009-09-05 21:47:34 +00002059 MagickMin(length,0xff),q);
2060 i++;
2061 if ((q-pixels+6) >= 80)
2062 {
2063 *q++='\n';
2064 (void) WriteBlob(image,q-pixels,pixels);
2065 q=pixels;
2066 }
2067 }
2068 length=0;
2069 }
cristy4c08aed2011-07-01 19:47:50 +00002070 index=GetPixelIndex(image,p);
2071 pixel.red=GetPixelRed(image,p);
2072 pixel.green=GetPixelGreen(image,p);
2073 pixel.blue=GetPixelBlue(image,p);
2074 pixel.alpha=GetPixelAlpha(image,p);
cristyed231572011-07-14 02:18:59 +00002075 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002076 }
cristya97426c2011-02-04 01:41:27 +00002077 q=PopHexPixel(hex_digits,(size_t) index,q);
cristybb503372010-05-27 20:51:26 +00002078 q=PopHexPixel(hex_digits,(size_t)
cristy3ed852e2009-09-05 21:47:34 +00002079 MagickMin(length,0xff),q);
2080 if (image->previous == (Image *) NULL)
2081 {
cristya97426c2011-02-04 01:41:27 +00002082 status=SetImageProgress(image,SaveImageTag,
2083 (MagickOffsetType) y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002084 if (status == MagickFalse)
2085 break;
2086 }
2087 }
2088 if (q != pixels)
2089 {
2090 *q++='\n';
2091 (void) WriteBlob(image,q-pixels,pixels);
2092 }
2093 break;
2094 }
2095 case NoCompression:
2096 default:
2097 {
2098 /*
2099 Dump uncompressed PseudoColor packets.
2100 */
2101 q=pixels;
cristybb503372010-05-27 20:51:26 +00002102 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002103 {
cristy1e178e72011-08-28 19:44:34 +00002104 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002105 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002106 break;
cristybb503372010-05-27 20:51:26 +00002107 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002108 {
cristy4c08aed2011-07-01 19:47:50 +00002109 q=PopHexPixel(hex_digits,(size_t) GetPixelIndex(image,p),q);
cristy3ed852e2009-09-05 21:47:34 +00002110 if ((q-pixels+4) >= 80)
2111 {
2112 *q++='\n';
2113 (void) WriteBlob(image,q-pixels,pixels);
2114 q=pixels;
2115 }
cristyed231572011-07-14 02:18:59 +00002116 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00002117 }
2118 if (image->previous == (Image *) NULL)
2119 {
cristya97426c2011-02-04 01:41:27 +00002120 status=SetImageProgress(image,SaveImageTag,
2121 (MagickOffsetType) y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002122 if (status == MagickFalse)
2123 break;
2124 }
2125 }
2126 if (q != pixels)
2127 {
2128 *q++='\n';
2129 (void) WriteBlob(image,q-pixels,pixels);
2130 }
2131 break;
2132 }
2133 }
2134 (void) WriteBlobByte(image,'\n');
2135 }
2136 if (LocaleCompare(image_info->magick,"PS") != 0)
2137 (void) WriteBlobString(image,"end\n");
2138 (void) WriteBlobString(image,"%%PageTrailer\n");
2139 if (GetNextImageInList(image) == (Image *) NULL)
2140 break;
2141 image=SyncNextImageInList(image);
2142 status=SetImageProgress(image,SaveImagesTag,scene++,
2143 GetImageListLength(image));
2144 if (status == MagickFalse)
2145 break;
2146 } while (image_info->adjoin != MagickFalse);
2147 (void) WriteBlobString(image,"%%Trailer\n");
2148 if (page > 2)
2149 {
cristyb51dff52011-05-19 16:55:47 +00002150 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00002151 "%%%%BoundingBox: %.20g %.20g %.20g %.20g\n",ceil(bounds.x1-0.5),
2152 ceil(bounds.y1-0.5),floor(bounds.x2+0.5),floor(bounds.y2+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002153 (void) WriteBlobString(image,buffer);
cristyb51dff52011-05-19 16:55:47 +00002154 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00002155 "%%%%HiResBoundingBox: %g %g %g %g\n",bounds.x1,bounds.y1,
cristy8cd5b312010-01-07 01:10:24 +00002156 bounds.x2,bounds.y2);
cristy3ed852e2009-09-05 21:47:34 +00002157 (void) WriteBlobString(image,buffer);
2158 }
2159 (void) WriteBlobString(image,"%%EOF\n");
2160 (void) CloseBlob(image);
2161 return(MagickTrue);
2162}