blob: 83351d0c62f9f62f71ba47943c41aa3859f9b804 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% PPPP SSSSS 22222 %
7% P P SS 22 %
8% PPPP SSS 222 %
9% P SS 22 %
10% P SSSSS 22222 %
11% %
12% %
13% Write Postscript Level II Format %
14% %
15% Software Design %
16% John Cristy %
17% July 1992 %
18% %
19% %
cristy16af1cb2009-12-11 21:38:29 +000020% Copyright 1999-2010 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*/
42#include "magick/studio.h"
43#include "magick/blob.h"
44#include "magick/blob-private.h"
45#include "magick/cache.h"
46#include "magick/color.h"
47#include "magick/color-private.h"
48#include "magick/compress.h"
49#include "magick/constitute.h"
50#include "magick/draw.h"
51#include "magick/exception.h"
52#include "magick/exception-private.h"
53#include "magick/geometry.h"
54#include "magick/image.h"
55#include "magick/image-private.h"
56#include "magick/list.h"
57#include "magick/magick.h"
58#include "magick/memory_.h"
59#include "magick/monitor.h"
60#include "magick/monitor-private.h"
61#include "magick/monitor-private.h"
cristy47b838c2009-09-19 16:09:30 +000062#include "magick/option.h"
cristy3ed852e2009-09-05 21:47:34 +000063#include "magick/resource_.h"
64#include "magick/property.h"
65#include "magick/quantum-private.h"
66#include "magick/static.h"
67#include "magick/string_.h"
68#include "magick/module.h"
69#include "magick/utility.h"
cristy80975862009-09-25 14:34:31 +000070
71/*
72 Define declarations.
73*/
cristy3ed852e2009-09-05 21:47:34 +000074#if defined(MAGICKCORE_TIFF_DELEGATE)
75#define CCITTParam "-1"
76#else
77#define CCITTParam "0"
78#endif
79
80/*
81 Forward declarations.
82*/
83static MagickBooleanType
84 WritePS2Image(const ImageInfo *,Image *);
85
86/*
87%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
88% %
89% %
90% %
91% R e g i s t e r P S 2 I m a g e %
92% %
93% %
94% %
95%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
96%
97% RegisterPS2Image() adds properties for the PS2 image format to
98% the list of supported formats. The properties include the image format
99% tag, a method to read and/or write the format, whether the format
100% supports the saving of more than one frame to the same file or blob,
101% whether the format supports native in-memory I/O, and a brief
102% description of the format.
103%
104% The format of the RegisterPS2Image method is:
105%
cristybb503372010-05-27 20:51:26 +0000106% size_t RegisterPS2Image(void)
cristy3ed852e2009-09-05 21:47:34 +0000107%
108*/
cristybb503372010-05-27 20:51:26 +0000109ModuleExport size_t RegisterPS2Image(void)
cristy3ed852e2009-09-05 21:47:34 +0000110{
111 MagickInfo
112 *entry;
113
114 entry=SetMagickInfo("EPS2");
115 entry->encoder=(EncodeImageHandler *) WritePS2Image;
116 entry->adjoin=MagickFalse;
117 entry->seekable_stream=MagickTrue;
118 entry->description=ConstantString("Level II Encapsulated PostScript");
119 entry->module=ConstantString("PS2");
120 (void) RegisterMagickInfo(entry);
121 entry=SetMagickInfo("PS2");
122 entry->encoder=(EncodeImageHandler *) WritePS2Image;
123 entry->seekable_stream=MagickTrue;
124 entry->description=ConstantString("Level II PostScript");
125 entry->module=ConstantString("PS2");
126 (void) RegisterMagickInfo(entry);
127 return(MagickImageCoderSignature);
128}
129
130/*
131%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
132% %
133% %
134% %
135% U n r e g i s t e r P S 2 I m a g e %
136% %
137% %
138% %
139%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
140%
141% UnregisterPS2Image() removes format registrations made by the
142% PS2 module from the list of supported formats.
143%
144% The format of the UnregisterPS2Image method is:
145%
146% UnregisterPS2Image(void)
147%
148*/
149ModuleExport void UnregisterPS2Image(void)
150{
151 (void) UnregisterMagickInfo("EPS2");
152 (void) UnregisterMagickInfo("PS2");
153}
154
155/*
156%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
157% %
158% %
159% %
160% W r i t e P S 2 I m a g e %
161% %
162% %
163% %
164%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
165%
166% WritePS2Image translates an image to encapsulated Postscript
167% Level II for printing. If the supplied geometry is null, the image is
168% centered on the Postscript page. Otherwise, the image is positioned as
169% specified by the geometry.
170%
171% The format of the WritePS2Image method is:
172%
173% MagickBooleanType WritePS2Image(const ImageInfo *image_info,Image *image)
174%
175% A description of each parameter follows:
176%
177% o image_info: the image info.
178%
179% o image: the image.
180%
181*/
cristy47b838c2009-09-19 16:09:30 +0000182
183static MagickBooleanType Huffman2DEncodeImage(const ImageInfo *image_info,
184 Image *image,Image *inject_image)
185{
cristy47b838c2009-09-19 16:09:30 +0000186 Image
cristy80975862009-09-25 14:34:31 +0000187 *group4_image;
cristy47b838c2009-09-19 16:09:30 +0000188
189 ImageInfo
190 *write_info;
191
cristy47b838c2009-09-19 16:09:30 +0000192 MagickBooleanType
193 status;
194
cristy80975862009-09-25 14:34:31 +0000195 size_t
196 length;
cristy47b838c2009-09-19 16:09:30 +0000197
198 unsigned char
cristy80975862009-09-25 14:34:31 +0000199 *group4;
cristy47b838c2009-09-19 16:09:30 +0000200
cristy42751fe2009-10-05 00:15:50 +0000201 status=MagickTrue;
cristy47b838c2009-09-19 16:09:30 +0000202 write_info=CloneImageInfo(image_info);
cristy80975862009-09-25 14:34:31 +0000203 (void) CopyMagickString(write_info->filename,"GROUP4:",MaxTextExtent);
204 (void) CopyMagickString(write_info->magick,"GROUP4",MaxTextExtent);
205 group4_image=CloneImage(inject_image,0,0,MagickTrue,&image->exception);
206 if (group4_image == (Image *) NULL)
207 return(MagickFalse);
208 group4=(unsigned char *) ImageToBlob(write_info,group4_image,&length,
209 &image->exception);
210 group4_image=DestroyImage(group4_image);
211 if (group4 == (unsigned char *) NULL)
212 return(MagickFalse);
cristy47b838c2009-09-19 16:09:30 +0000213 write_info=DestroyImageInfo(write_info);
cristy80975862009-09-25 14:34:31 +0000214 if (WriteBlob(image,length,group4) != (ssize_t) length)
215 status=MagickFalse;
216 group4=(unsigned char *) RelinquishMagickMemory(group4);
217 return(status);
cristy47b838c2009-09-19 16:09:30 +0000218}
219
cristy3ed852e2009-09-05 21:47:34 +0000220static MagickBooleanType WritePS2Image(const ImageInfo *image_info,Image *image)
221{
222 static const char
223 *PostscriptProlog[]=
224 {
225 "%%%%BeginProlog",
226 "%%",
227 "%% Display a color image. The image is displayed in color on",
228 "%% Postscript viewers or printers that support color, otherwise",
229 "%% it is displayed as grayscale.",
230 "%%",
231 "/DirectClassImage",
232 "{",
233 " %%",
234 " %% Display a DirectClass image.",
235 " %%",
236 " colorspace 0 eq",
237 " {",
238 " /DeviceRGB setcolorspace",
239 " <<",
240 " /ImageType 1",
241 " /Width columns",
242 " /Height rows",
243 " /BitsPerComponent 8",
244 " /Decode [0 1 0 1 0 1]",
245 " /ImageMatrix [columns 0 0 rows neg 0 rows]",
246 " compression 0 gt",
cristy80975862009-09-25 14:34:31 +0000247 " { /DataSource pixel_stream %s }",
248 " { /DataSource pixel_stream %s } ifelse",
cristy3ed852e2009-09-05 21:47:34 +0000249 " >> image",
250 " }",
251 " {",
252 " /DeviceCMYK setcolorspace",
253 " <<",
254 " /ImageType 1",
255 " /Width columns",
256 " /Height rows",
257 " /BitsPerComponent 8",
258 " /Decode [1 0 1 0 1 0 1 0]",
259 " /ImageMatrix [columns 0 0 rows neg 0 rows]",
260 " compression 0 gt",
cristy80975862009-09-25 14:34:31 +0000261 " { /DataSource pixel_stream %s }",
262 " { /DataSource pixel_stream %s } ifelse",
cristy3ed852e2009-09-05 21:47:34 +0000263 " >> image",
264 " } ifelse",
265 "} bind def",
266 "",
267 "/PseudoClassImage",
268 "{",
269 " %%",
270 " %% Display a PseudoClass image.",
271 " %%",
272 " %% Parameters:",
273 " %% colors: number of colors in the colormap.",
274 " %%",
275 " currentfile buffer readline pop",
276 " token pop /colors exch def pop",
277 " colors 0 eq",
278 " {",
279 " %%",
280 " %% Image is grayscale.",
281 " %%",
282 " currentfile buffer readline pop",
283 " token pop /bits exch def pop",
284 " /DeviceGray setcolorspace",
285 " <<",
286 " /ImageType 1",
287 " /Width columns",
288 " /Height rows",
289 " /BitsPerComponent bits",
290 " /Decode [0 1]",
291 " /ImageMatrix [columns 0 0 rows neg 0 rows]",
292 " compression 0 gt",
cristy80975862009-09-25 14:34:31 +0000293 " { /DataSource pixel_stream %s }",
cristy3ed852e2009-09-05 21:47:34 +0000294 " {",
cristy80975862009-09-25 14:34:31 +0000295 " /DataSource pixel_stream %s",
cristy3ed852e2009-09-05 21:47:34 +0000296 " <<",
297 " /K "CCITTParam,
298 " /Columns columns",
299 " /Rows rows",
300 " >> /CCITTFaxDecode filter",
301 " } ifelse",
302 " >> image",
303 " }",
304 " {",
305 " %%",
306 " %% Parameters:",
307 " %% colormap: red, green, blue color packets.",
308 " %%",
309 " /colormap colors 3 mul string def",
310 " currentfile colormap readhexstring pop pop",
311 " currentfile buffer readline pop",
312 " [ /Indexed /DeviceRGB colors 1 sub colormap ] setcolorspace",
313 " <<",
314 " /ImageType 1",
315 " /Width columns",
316 " /Height rows",
317 " /BitsPerComponent 8",
318 " /Decode [0 255]",
319 " /ImageMatrix [columns 0 0 rows neg 0 rows]",
320 " compression 0 gt",
cristy80975862009-09-25 14:34:31 +0000321 " { /DataSource pixel_stream %s }",
322 " { /DataSource pixel_stream %s } ifelse",
cristy3ed852e2009-09-05 21:47:34 +0000323 " >> image",
324 " } ifelse",
325 "} bind def",
326 "",
327 "/DisplayImage",
328 "{",
329 " %%",
330 " %% Display a DirectClass or PseudoClass image.",
331 " %%",
332 " %% Parameters:",
333 " %% x & y translation.",
334 " %% x & y scale.",
335 " %% label pointsize.",
336 " %% image label.",
337 " %% image columns & rows.",
338 " %% class: 0-DirectClass or 1-PseudoClass.",
339 " %% colorspace: 0-RGB or 1-CMYK.",
340 " %% compression: 0-RLECompression or 1-NoCompression.",
341 " %% hex color packets.",
342 " %%",
343 " gsave",
344 " /buffer 512 string def",
345 " /pixel_stream currentfile def",
346 "",
347 " currentfile buffer readline pop",
348 " token pop /x exch def",
349 " token pop /y exch def pop",
350 " x y translate",
351 " currentfile buffer readline pop",
352 " token pop /x exch def",
353 " token pop /y exch def pop",
354 " currentfile buffer readline pop",
355 " token pop /pointsize exch def pop",
356 " /Helvetica findfont pointsize scalefont setfont",
357 (char *) NULL
358 },
359 *PostscriptEpilog[]=
360 {
361 " x y scale",
362 " currentfile buffer readline pop",
363 " token pop /columns exch def",
364 " token pop /rows exch def pop",
365 " currentfile buffer readline pop",
366 " token pop /class exch def pop",
367 " currentfile buffer readline pop",
368 " token pop /colorspace exch def pop",
369 " currentfile buffer readline pop",
370 " token pop /compression exch def pop",
371 " class 0 gt { PseudoClassImage } { DirectClassImage } ifelse",
372 (char *) NULL
373 };
374
375 char
376 buffer[MaxTextExtent],
377 date[MaxTextExtent],
378 page_geometry[MaxTextExtent],
379 **labels;
380
381 CompressionType
382 compression;
383
384 const char
385 **q,
386 *value;
387
388 double
389 pointsize;
390
391 GeometryInfo
392 geometry_info;
393
cristybb503372010-05-27 20:51:26 +0000394 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000395 j,
396 y;
397
398 MagickOffsetType
399 scene,
400 start,
401 stop;
402
403 MagickBooleanType
404 progress,
405 status;
406
407 MagickOffsetType
408 offset;
409
410 MagickSizeType
411 number_pixels;
412
413 MagickStatusType
414 flags;
415
416 PointInfo
417 delta,
418 resolution,
419 scale;
420
421 RectangleInfo
422 geometry,
423 media_info,
424 page_info;
425
426 register const IndexPacket
427 *indexes;
428
429 register const PixelPacket
430 *p;
431
cristybb503372010-05-27 20:51:26 +0000432 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000433 x;
434
cristybb503372010-05-27 20:51:26 +0000435 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000436 i;
437
438 SegmentInfo
439 bounds;
440
441 size_t
442 length;
443
444 time_t
445 timer;
446
447 unsigned char
448 *pixels;
449
cristybb503372010-05-27 20:51:26 +0000450 size_t
cristy3ed852e2009-09-05 21:47:34 +0000451 page,
452 text_size;
453
454 /*
455 Open output image file.
456 */
457 assert(image_info != (const ImageInfo *) NULL);
458 assert(image_info->signature == MagickSignature);
459 assert(image != (Image *) NULL);
460 assert(image->signature == MagickSignature);
461 if (image->debug != MagickFalse)
462 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
463 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
464 if (status == MagickFalse)
465 return(status);
466 compression=image->compression;
467 if (image_info->compression != UndefinedCompression)
468 compression=image_info->compression;
469 switch (compression)
470 {
471#if !defined(MAGICKCORE_JPEG_DELEGATE)
472 case JPEGCompression:
473 {
474 compression=RLECompression;
475 (void) ThrowMagickException(&image->exception,GetMagickModule(),
476 MissingDelegateError,"DelegateLibrarySupportNotBuiltIn","`%s' (JPEG)",
477 image->filename);
478 break;
479 }
480#endif
481 default:
482 break;
483 }
484 (void) ResetMagickMemory(&bounds,0,sizeof(bounds));
485 page=1;
486 scene=0;
487 do
488 {
489 /*
490 Scale relative to dots-per-inch.
491 */
492 delta.x=DefaultResolution;
493 delta.y=DefaultResolution;
494 resolution.x=image->x_resolution;
495 resolution.y=image->y_resolution;
496 if ((resolution.x == 0.0) || (resolution.y == 0.0))
497 {
498 flags=ParseGeometry(PSDensityGeometry,&geometry_info);
499 resolution.x=geometry_info.rho;
500 resolution.y=geometry_info.sigma;
501 if ((flags & SigmaValue) == 0)
502 resolution.y=resolution.x;
503 }
504 if (image_info->density != (char *) NULL)
505 {
506 flags=ParseGeometry(image_info->density,&geometry_info);
507 resolution.x=geometry_info.rho;
508 resolution.y=geometry_info.sigma;
509 if ((flags & SigmaValue) == 0)
510 resolution.y=resolution.x;
511 }
512 if (image->units == PixelsPerCentimeterResolution)
513 {
cristybb503372010-05-27 20:51:26 +0000514 resolution.x=(size_t) (100.0*2.54*resolution.x+0.5)/100.0;
515 resolution.y=(size_t) (100.0*2.54*resolution.y+0.5)/100.0;
cristy3ed852e2009-09-05 21:47:34 +0000516 }
517 SetGeometry(image,&geometry);
cristye8c25f92010-06-03 00:53:06 +0000518 (void) FormatMagickString(page_geometry,MaxTextExtent,"%.20gx%.20g",
519 (double) image->columns,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000520 if (image_info->page != (char *) NULL)
521 (void) CopyMagickString(page_geometry,image_info->page,MaxTextExtent);
522 else
523 if ((image->page.width != 0) && (image->page.height != 0))
cristye8c25f92010-06-03 00:53:06 +0000524 (void) FormatMagickString(page_geometry,MaxTextExtent,
525 "%.20gx%.20g%+.20g%+.20g",(double) image->page.width,(double)
526 image->page.height,(double) image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +0000527 else
528 if ((image->gravity != UndefinedGravity) &&
529 (LocaleCompare(image_info->magick,"PS") == 0))
530 (void) CopyMagickString(page_geometry,PSPageGeometry,MaxTextExtent);
531 (void) ConcatenateMagickString(page_geometry,">",MaxTextExtent);
532 (void) ParseMetaGeometry(page_geometry,&geometry.x,&geometry.y,
533 &geometry.width,&geometry.height);
534 scale.x=(double) (geometry.width*delta.x)/resolution.x;
cristybb503372010-05-27 20:51:26 +0000535 geometry.width=(size_t) floor(scale.x+0.5);
cristy3ed852e2009-09-05 21:47:34 +0000536 scale.y=(double) (geometry.height*delta.y)/resolution.y;
cristybb503372010-05-27 20:51:26 +0000537 geometry.height=(size_t) floor(scale.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +0000538 (void) ParseAbsoluteGeometry(page_geometry,&media_info);
539 (void) ParseGravityGeometry(image,page_geometry,&page_info,
540 &image->exception);
541 if (image->gravity != UndefinedGravity)
542 {
543 geometry.x=(-page_info.x);
cristybb503372010-05-27 20:51:26 +0000544 geometry.y=(ssize_t) (media_info.height+page_info.y-image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000545 }
546 pointsize=12.0;
547 if (image_info->pointsize != 0.0)
548 pointsize=image_info->pointsize;
549 text_size=0;
550 value=GetImageProperty(image,"label");
551 if (value != (const char *) NULL)
cristybb503372010-05-27 20:51:26 +0000552 text_size=(size_t) (MultilineCensus(value)*pointsize+12);
cristy3ed852e2009-09-05 21:47:34 +0000553 if (page == 1)
554 {
555 /*
556 Output Postscript header.
557 */
558 if (LocaleCompare(image_info->magick,"PS2") == 0)
559 (void) CopyMagickString(buffer,"%!PS-Adobe-3.0\n",MaxTextExtent);
560 else
561 (void) CopyMagickString(buffer,"%!PS-Adobe-3.0 EPSF-3.0\n",
562 MaxTextExtent);
563 (void) WriteBlobString(image,buffer);
564 (void) WriteBlobString(image,"%%Creator: (ImageMagick)\n");
565 (void) FormatMagickString(buffer,MaxTextExtent,"%%%%Title: (%s)\n",
566 image->filename);
567 (void) WriteBlobString(image,buffer);
568 timer=time((time_t *) NULL);
569 (void) FormatMagickTime(timer,MaxTextExtent,date);
570 (void) FormatMagickString(buffer,MaxTextExtent,
571 "%%%%CreationDate: (%s)\n",date);
572 (void) WriteBlobString(image,buffer);
573 bounds.x1=(double) geometry.x;
574 bounds.y1=(double) geometry.y;
575 bounds.x2=(double) geometry.x+geometry.width;
576 bounds.y2=(double) geometry.y+geometry.height+text_size;
577 if ((image_info->adjoin != MagickFalse) &&
578 (GetNextImageInList(image) != (Image *) NULL))
579 (void) CopyMagickString(buffer,"%%BoundingBox: (atend)\n",
580 MaxTextExtent);
581 else
582 {
583 (void) FormatMagickString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +0000584 "%%%%BoundingBox: %.20g %.20g %.20g %.20g\n",ceil(bounds.x1-0.5),
585 ceil(bounds.y1-0.5),floor(bounds.x2+0.5),floor(bounds.y2+0.5));
cristy3ed852e2009-09-05 21:47:34 +0000586 (void) WriteBlobString(image,buffer);
587 (void) FormatMagickString(buffer,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +0000588 "%%%%HiResBoundingBox: %g %g %g %g\n",bounds.x1,
cristy8cd5b312010-01-07 01:10:24 +0000589 bounds.y1,bounds.x2,bounds.y2);
cristy3ed852e2009-09-05 21:47:34 +0000590 }
591 (void) WriteBlobString(image,buffer);
592 value=GetImageProperty(image,"label");
593 if (value != (const char *) NULL)
594 (void) WriteBlobString(image,
595 "%%DocumentNeededResources: font Helvetica\n");
596 (void) WriteBlobString(image,"%%LanguageLevel: 2\n");
597 if (LocaleCompare(image_info->magick,"PS2") != 0)
598 (void) WriteBlobString(image,"%%Pages: 1\n");
599 else
600 {
601 (void) WriteBlobString(image,"%%Orientation: Portrait\n");
602 (void) WriteBlobString(image,"%%PageOrder: Ascend\n");
603 if (image_info->adjoin == MagickFalse)
604 (void) CopyMagickString(buffer,"%%Pages: 1\n",MaxTextExtent);
605 else
cristye8c25f92010-06-03 00:53:06 +0000606 (void) FormatMagickString(buffer,MaxTextExtent,
607 "%%%%Pages: %.20g\n",(double) GetImageListLength(image));
cristy3ed852e2009-09-05 21:47:34 +0000608 (void) WriteBlobString(image,buffer);
609 }
610 (void) WriteBlobString(image,"%%EndComments\n");
611 (void) WriteBlobString(image,"\n%%BeginDefaults\n");
612 (void) WriteBlobString(image,"%%EndDefaults\n\n");
613 /*
614 Output Postscript commands.
615 */
616 for (q=PostscriptProlog; *q; q++)
617 {
618 switch (compression)
619 {
620 case NoCompression:
621 {
622 (void) FormatMagickString(buffer,MaxTextExtent,*q,
cristy80975862009-09-25 14:34:31 +0000623 "/ASCII85Decode filter");
cristy3ed852e2009-09-05 21:47:34 +0000624 break;
625 }
626 case JPEGCompression:
627 {
cristy80975862009-09-25 14:34:31 +0000628 (void) FormatMagickString(buffer,MaxTextExtent,*q,
629 "/DCTDecode filter");
cristy3ed852e2009-09-05 21:47:34 +0000630 break;
631 }
632 case LZWCompression:
633 {
cristy80975862009-09-25 14:34:31 +0000634 (void) FormatMagickString(buffer,MaxTextExtent,*q,
635 "/LZWDecode filter");
cristy3ed852e2009-09-05 21:47:34 +0000636 break;
637 }
638 case FaxCompression:
639 case Group4Compression:
640 {
cristy80975862009-09-25 14:34:31 +0000641 (void) FormatMagickString(buffer,MaxTextExtent,*q," ");
cristy3ed852e2009-09-05 21:47:34 +0000642 break;
643 }
644 default:
645 {
646 (void) FormatMagickString(buffer,MaxTextExtent,*q,
cristy80975862009-09-25 14:34:31 +0000647 "/RunLengthDecode filter");
cristy3ed852e2009-09-05 21:47:34 +0000648 break;
649 }
650 }
651 (void) WriteBlobString(image,buffer);
652 (void) WriteBlobByte(image,'\n');
653 }
654 value=GetImageProperty(image,"label");
655 if (value != (const char *) NULL)
cristybb503372010-05-27 20:51:26 +0000656 for (j=(ssize_t) MultilineCensus(value)-1; j >= 0; j--)
cristy3ed852e2009-09-05 21:47:34 +0000657 {
658 (void) WriteBlobString(image," /label 512 string def\n");
659 (void) WriteBlobString(image," currentfile label readline pop\n");
660 (void) FormatMagickString(buffer,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +0000661 " 0 y %g add moveto label show pop\n",j*pointsize+12);
cristy3ed852e2009-09-05 21:47:34 +0000662 (void) WriteBlobString(image,buffer);
663 }
664 for (q=PostscriptEpilog; *q; q++)
665 {
666 (void) FormatMagickString(buffer,MaxTextExtent,"%s\n",*q);
667 (void) WriteBlobString(image,buffer);
668 }
669 if (LocaleCompare(image_info->magick,"PS2") == 0)
670 (void) WriteBlobString(image," showpage\n");
671 (void) WriteBlobString(image,"} bind def\n");
672 (void) WriteBlobString(image,"%%EndProlog\n");
673 }
cristye8c25f92010-06-03 00:53:06 +0000674 (void) FormatMagickString(buffer,MaxTextExtent,"%%%%Page: 1 %.20g\n",
675 (double) page++);
cristy3ed852e2009-09-05 21:47:34 +0000676 (void) WriteBlobString(image,buffer);
677 (void) FormatMagickString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +0000678 "%%%%PageBoundingBox: %.20g %.20g %.20g %.20g\n",(double) geometry.x,
679 (double) geometry.y,geometry.x+(double) geometry.width,geometry.y+(double)
cristy3ed852e2009-09-05 21:47:34 +0000680 (geometry.height+text_size));
681 (void) WriteBlobString(image,buffer);
682 if ((double) geometry.x < bounds.x1)
683 bounds.x1=(double) geometry.x;
684 if ((double) geometry.y < bounds.y1)
685 bounds.y1=(double) geometry.y;
686 if ((double) (geometry.x+geometry.width-1) > bounds.x2)
687 bounds.x2=(double) geometry.x+geometry.width-1;
688 if ((double) (geometry.y+(geometry.height+text_size)-1) > bounds.y2)
689 bounds.y2=(double) geometry.y+(geometry.height+text_size)-1;
690 value=GetImageProperty(image,"label");
691 if (value != (const char *) NULL)
692 (void) WriteBlobString(image,"%%PageResources: font Times-Roman\n");
693 if (LocaleCompare(image_info->magick,"PS2") != 0)
694 (void) WriteBlobString(image,"userdict begin\n");
695 start=TellBlob(image);
696 (void) FormatMagickString(buffer,MaxTextExtent,
697 "%%%%BeginData:%13ld %s Bytes\n",0L,
698 compression == NoCompression ? "ASCII" : "Binary");
699 (void) WriteBlobString(image,buffer);
700 stop=TellBlob(image);
701 (void) WriteBlobString(image,"DisplayImage\n");
702 /*
703 Output image data.
704 */
cristye8c25f92010-06-03 00:53:06 +0000705 (void) FormatMagickString(buffer,MaxTextExtent,"%.20g %.20g\n%g %g\n%g\n",
706 (double) geometry.x,(double) geometry.y,scale.x,scale.y,pointsize);
cristy3ed852e2009-09-05 21:47:34 +0000707 (void) WriteBlobString(image,buffer);
708 labels=(char **) NULL;
709 value=GetImageProperty(image,"label");
710 if (value != (const char *) NULL)
711 labels=StringToList(value);
712 if (labels != (char **) NULL)
713 {
714 for (i=0; labels[i] != (char *) NULL; i++)
715 {
716 (void) FormatMagickString(buffer,MaxTextExtent,"%s \n",
717 labels[i]);
718 (void) WriteBlobString(image,buffer);
719 labels[i]=DestroyString(labels[i]);
720 }
721 labels=(char **) RelinquishMagickMemory(labels);
722 }
723 number_pixels=(MagickSizeType) image->columns*image->rows;
724 if (number_pixels != (MagickSizeType) ((size_t) number_pixels))
725 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
726 if ((compression == FaxCompression) || (compression == Group4Compression) ||
727 ((image_info->type != TrueColorType) &&
728 (IsGrayImage(image,&image->exception) != MagickFalse)))
729 {
cristye8c25f92010-06-03 00:53:06 +0000730 (void) FormatMagickString(buffer,MaxTextExtent,"%.20g %.20g\n1\n%d\n",
731 (double) image->columns,(double) image->rows,(int)
cristy3ed852e2009-09-05 21:47:34 +0000732 (image->colorspace == CMYKColorspace));
733 (void) WriteBlobString(image,buffer);
734 (void) FormatMagickString(buffer,MaxTextExtent,"%d\n",
735 (int) ((compression != FaxCompression) &&
736 (compression != Group4Compression)));
737 (void) WriteBlobString(image,buffer);
738 (void) WriteBlobString(image,"0\n");
739 (void) FormatMagickString(buffer,MaxTextExtent,"%d\n",
740 (compression == FaxCompression) ||
741 (compression == Group4Compression) ? 1 : 8);
742 (void) WriteBlobString(image,buffer);
743 switch (compression)
744 {
745 case FaxCompression:
746 case Group4Compression:
747 {
748 if (LocaleCompare(CCITTParam,"0") == 0)
749 {
750 (void) HuffmanEncodeImage(image_info,image,image);
751 break;
752 }
cristy3ed852e2009-09-05 21:47:34 +0000753 (void) Huffman2DEncodeImage(image_info,image,image);
cristy3ed852e2009-09-05 21:47:34 +0000754 break;
755 }
756 case JPEGCompression:
757 {
758 status=InjectImageBlob(image_info,image,image,"jpeg",
759 &image->exception);
760 if (status == MagickFalse)
761 ThrowWriterException(CoderError,image->exception.reason);
762 break;
763 }
764 case RLECompression:
765 default:
766 {
767 register unsigned char
768 *q;
769
770 /*
771 Allocate pixel array.
772 */
773 length=(size_t) number_pixels;
774 pixels=(unsigned char *) AcquireQuantumMemory(length,
775 sizeof(*pixels));
776 if (pixels == (unsigned char *) NULL)
777 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
778 /*
779 Dump Runlength encoded pixels.
780 */
781 q=pixels;
cristybb503372010-05-27 20:51:26 +0000782 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000783 {
784 p=GetVirtualPixels(image,0,y,image->columns,1,
785 &image->exception);
786 if (p == (const PixelPacket *) NULL)
787 break;
cristybb503372010-05-27 20:51:26 +0000788 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000789 {
790 *q++=ScaleQuantumToChar(PixelIntensityToQuantum(p));
791 p++;
792 }
cristycee97112010-05-28 00:44:52 +0000793 progress=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000794 if (progress == MagickFalse)
795 break;
796 }
797 length=(size_t) (q-pixels);
798 if (compression == LZWCompression)
799 status=LZWEncodeImage(image,length,pixels);
800 else
801 status=PackbitsEncodeImage(image,length,pixels);
802 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
803 if (status == MagickFalse)
804 {
805 (void) CloseBlob(image);
806 return(MagickFalse);
807 }
808 break;
809 }
810 case NoCompression:
811 {
812 /*
813 Dump uncompressed PseudoColor packets.
814 */
815 Ascii85Initialize(image);
cristybb503372010-05-27 20:51:26 +0000816 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000817 {
818 p=GetVirtualPixels(image,0,y,image->columns,1,
819 &image->exception);
820 if (p == (const PixelPacket *) NULL)
821 break;
cristybb503372010-05-27 20:51:26 +0000822 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000823 {
824 Ascii85Encode(image,
825 ScaleQuantumToChar(PixelIntensityToQuantum(p)));
826 p++;
827 }
cristye8c25f92010-06-03 00:53:06 +0000828 progress=SetImageProgress(image,SaveImageTag,(MagickOffsetType)
829 y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000830 if (progress == MagickFalse)
831 break;
832 }
833 Ascii85Flush(image);
834 break;
835 }
836 }
837 }
838 else
839 if ((image->storage_class == DirectClass) || (image->colors > 256) ||
840 (compression == JPEGCompression) || (image->matte != MagickFalse))
841 {
cristye8c25f92010-06-03 00:53:06 +0000842 (void) FormatMagickString(buffer,MaxTextExtent,"%.20g %.20g\n0\n%d\n",
843 (double) image->columns,(double) image->rows,(int)
cristy3ed852e2009-09-05 21:47:34 +0000844 (image->colorspace == CMYKColorspace));
845 (void) WriteBlobString(image,buffer);
846 (void) FormatMagickString(buffer,MaxTextExtent,"%d\n",
847 (int) (compression == NoCompression));
848 (void) WriteBlobString(image,buffer);
849 switch (compression)
850 {
851 case JPEGCompression:
852 {
853 status=InjectImageBlob(image_info,image,image,"jpeg",
854 &image->exception);
855 if (status == MagickFalse)
856 ThrowWriterException(CoderError,image->exception.reason);
857 break;
858 }
859 case RLECompression:
860 default:
861 {
862 register unsigned char
863 *q;
864
865 /*
866 Allocate pixel array.
867 */
868 length=(size_t) number_pixels;
869 pixels=(unsigned char *) AcquireQuantumMemory(length,
870 4*sizeof(*pixels));
871 if (pixels == (unsigned char *) NULL)
872 ThrowWriterException(ResourceLimitError,
873 "MemoryAllocationFailed");
874 /*
875 Dump Packbit encoded pixels.
876 */
877 q=pixels;
cristybb503372010-05-27 20:51:26 +0000878 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000879 {
880 p=GetVirtualPixels(image,0,y,image->columns,1,
881 &image->exception);
882 if (p == (const PixelPacket *) NULL)
883 break;
884 indexes=GetVirtualIndexQueue(image);
cristybb503372010-05-27 20:51:26 +0000885 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000886 {
887 if ((image->matte != MagickFalse) &&
888 (p->opacity == (Quantum) TransparentOpacity))
889 {
890 *q++=ScaleQuantumToChar((Quantum) QuantumRange);
891 *q++=ScaleQuantumToChar((Quantum) QuantumRange);
892 *q++=ScaleQuantumToChar((Quantum) QuantumRange);
893 }
894 else
895 if (image->colorspace != CMYKColorspace)
896 {
cristyce70c172010-01-07 17:15:30 +0000897 *q++=ScaleQuantumToChar(GetRedPixelComponent(p));
898 *q++=ScaleQuantumToChar(GetGreenPixelComponent(p));
899 *q++=ScaleQuantumToChar(GetBluePixelComponent(p));
cristy3ed852e2009-09-05 21:47:34 +0000900 }
901 else
902 {
cristyce70c172010-01-07 17:15:30 +0000903 *q++=ScaleQuantumToChar(GetRedPixelComponent(p));
904 *q++=ScaleQuantumToChar(GetGreenPixelComponent(p));
905 *q++=ScaleQuantumToChar(GetBluePixelComponent(p));
cristy3ed852e2009-09-05 21:47:34 +0000906 *q++=ScaleQuantumToChar(indexes[x]);
907 }
908 p++;
909 }
cristycee97112010-05-28 00:44:52 +0000910 progress=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000911 if (progress == MagickFalse)
912 break;
913 }
914 length=(size_t) (q-pixels);
915 if (compression == LZWCompression)
916 status=LZWEncodeImage(image,length,pixels);
917 else
918 status=PackbitsEncodeImage(image,length,pixels);
919 if (status == MagickFalse)
920 {
921 (void) CloseBlob(image);
922 return(MagickFalse);
923 }
924 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
925 break;
926 }
927 case NoCompression:
928 {
929 /*
930 Dump uncompressed DirectColor packets.
931 */
932 Ascii85Initialize(image);
cristybb503372010-05-27 20:51:26 +0000933 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000934 {
935 p=GetVirtualPixels(image,0,y,image->columns,1,
936 &image->exception);
937 if (p == (const PixelPacket *) NULL)
938 break;
939 indexes=GetVirtualIndexQueue(image);
cristybb503372010-05-27 20:51:26 +0000940 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000941 {
942 if ((image->matte != MagickFalse) &&
943 (p->opacity == (Quantum) TransparentOpacity))
944 {
945 Ascii85Encode(image,ScaleQuantumToChar((Quantum)
946 QuantumRange));
947 Ascii85Encode(image,ScaleQuantumToChar((Quantum)
948 QuantumRange));
949 Ascii85Encode(image,ScaleQuantumToChar((Quantum)
950 QuantumRange));
951 }
952 else
953 if (image->colorspace != CMYKColorspace)
954 {
cristyce70c172010-01-07 17:15:30 +0000955 Ascii85Encode(image,ScaleQuantumToChar(GetRedPixelComponent(p)));
956 Ascii85Encode(image,ScaleQuantumToChar(GetGreenPixelComponent(p)));
957 Ascii85Encode(image,ScaleQuantumToChar(GetBluePixelComponent(p)));
cristy3ed852e2009-09-05 21:47:34 +0000958 }
959 else
960 {
cristyce70c172010-01-07 17:15:30 +0000961 Ascii85Encode(image,ScaleQuantumToChar(GetRedPixelComponent(p)));
962 Ascii85Encode(image,ScaleQuantumToChar(GetGreenPixelComponent(p)));
963 Ascii85Encode(image,ScaleQuantumToChar(GetBluePixelComponent(p)));
cristy3ed852e2009-09-05 21:47:34 +0000964 Ascii85Encode(image,ScaleQuantumToChar(indexes[x]));
965 }
966 p++;
967 }
cristycee97112010-05-28 00:44:52 +0000968 progress=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000969 if (progress == MagickFalse)
970 break;
971 }
972 Ascii85Flush(image);
973 break;
974 }
975 }
976 }
977 else
978 {
979 /*
980 Dump number of colors and colormap.
981 */
cristye8c25f92010-06-03 00:53:06 +0000982 (void) FormatMagickString(buffer,MaxTextExtent,"%.20g %.20g\n1\n%d\n",
983 (double) image->columns,(double) image->rows,(int)
cristy3ed852e2009-09-05 21:47:34 +0000984 (image->colorspace == CMYKColorspace));
985 (void) WriteBlobString(image,buffer);
986 (void) FormatMagickString(buffer,MaxTextExtent,"%d\n",
987 (int) (compression == NoCompression));
988 (void) WriteBlobString(image,buffer);
cristye8c25f92010-06-03 00:53:06 +0000989 (void) FormatMagickString(buffer,MaxTextExtent,"%.20g\n",(double)
cristyf2faecf2010-05-28 19:19:36 +0000990 image->colors);
cristy3ed852e2009-09-05 21:47:34 +0000991 (void) WriteBlobString(image,buffer);
cristybb503372010-05-27 20:51:26 +0000992 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +0000993 {
994 (void) FormatMagickString(buffer,MaxTextExtent,"%02X%02X%02X\n",
995 ScaleQuantumToChar(image->colormap[i].red),
996 ScaleQuantumToChar(image->colormap[i].green),
997 ScaleQuantumToChar(image->colormap[i].blue));
998 (void) WriteBlobString(image,buffer);
999 }
1000 switch (compression)
1001 {
1002 case RLECompression:
1003 default:
1004 {
1005 register unsigned char
1006 *q;
1007
1008 /*
1009 Allocate pixel array.
1010 */
1011 length=(size_t) number_pixels;
1012 pixels=(unsigned char *) AcquireQuantumMemory(length,
1013 sizeof(*pixels));
1014 if (pixels == (unsigned char *) NULL)
1015 ThrowWriterException(ResourceLimitError,
1016 "MemoryAllocationFailed");
1017 /*
1018 Dump Runlength encoded pixels.
1019 */
1020 q=pixels;
cristybb503372010-05-27 20:51:26 +00001021 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001022 {
1023 p=GetVirtualPixels(image,0,y,image->columns,1,
1024 &image->exception);
1025 if (p == (const PixelPacket *) NULL)
1026 break;
1027 indexes=GetVirtualIndexQueue(image);
cristybb503372010-05-27 20:51:26 +00001028 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001029 *q++=(unsigned char) indexes[x];
cristycee97112010-05-28 00:44:52 +00001030 progress=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001031 if (progress == MagickFalse)
1032 break;
1033 }
1034 length=(size_t) (q-pixels);
1035 if (compression == LZWCompression)
1036 status=LZWEncodeImage(image,length,pixels);
1037 else
1038 status=PackbitsEncodeImage(image,length,pixels);
1039 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1040 if (status == MagickFalse)
1041 {
1042 (void) CloseBlob(image);
1043 return(MagickFalse);
1044 }
1045 break;
1046 }
1047 case NoCompression:
1048 {
1049 /*
1050 Dump uncompressed PseudoColor packets.
1051 */
1052 Ascii85Initialize(image);
cristybb503372010-05-27 20:51:26 +00001053 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001054 {
1055 p=GetVirtualPixels(image,0,y,image->columns,1,
1056 &image->exception);
1057 if (p == (const PixelPacket *) NULL)
1058 break;
1059 indexes=GetVirtualIndexQueue(image);
cristybb503372010-05-27 20:51:26 +00001060 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001061 Ascii85Encode(image,(unsigned char) indexes[x]);
cristycee97112010-05-28 00:44:52 +00001062 progress=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001063 if (progress == MagickFalse)
1064 break;
1065 }
1066 Ascii85Flush(image);
1067 break;
1068 }
1069 }
1070 }
1071 (void) WriteBlobByte(image,'\n');
1072 length=(size_t) (TellBlob(image)-stop);
1073 stop=TellBlob(image);
1074 offset=SeekBlob(image,start,SEEK_SET);
1075 if (offset < 0)
1076 ThrowWriterException(CorruptImageError,"ImproperImageHeader");
1077 (void) FormatMagickString(buffer,MaxTextExtent,
cristyf2faecf2010-05-28 19:19:36 +00001078 "%%%%BeginData:%13ld %s Bytes\n",(long) length,
cristy3ed852e2009-09-05 21:47:34 +00001079 compression == NoCompression ? "ASCII" : "Binary");
1080 (void) WriteBlobString(image,buffer);
1081 offset=SeekBlob(image,stop,SEEK_SET);
1082 (void) WriteBlobString(image,"%%EndData\n");
1083 if (LocaleCompare(image_info->magick,"PS2") != 0)
1084 (void) WriteBlobString(image,"end\n");
1085 (void) WriteBlobString(image,"%%PageTrailer\n");
1086 if (GetNextImageInList(image) == (Image *) NULL)
1087 break;
1088 image=SyncNextImageInList(image);
1089 status=SetImageProgress(image,SaveImagesTag,scene++,
1090 GetImageListLength(image));
1091 if (status == MagickFalse)
1092 break;
1093 } while (image_info->adjoin != MagickFalse);
1094 (void) WriteBlobString(image,"%%Trailer\n");
1095 if (page > 1)
1096 {
1097 (void) FormatMagickString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001098 "%%%%BoundingBox: %.20g %.20g %.20g %.20g\n",ceil(bounds.x1-0.5),
1099 ceil(bounds.y1-0.5),floor(bounds.x2+0.5),floor(bounds.y2+0.5));
cristy3ed852e2009-09-05 21:47:34 +00001100 (void) WriteBlobString(image,buffer);
1101 (void) FormatMagickString(buffer,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001102 "%%%%HiResBoundingBox: %g %g %g %g\n",bounds.x1,bounds.y1,
cristy8cd5b312010-01-07 01:10:24 +00001103 bounds.x2,bounds.y2);
cristy3ed852e2009-09-05 21:47:34 +00001104 (void) WriteBlobString(image,buffer);
1105 }
1106 (void) WriteBlobString(image,"%%EOF\n");
1107 (void) CloseBlob(image);
1108 return(MagickTrue);
1109}