1 /*
2 % Copyright (C) 2003 - 2020 GraphicsMagick Group
3 % Copyright (C) 2002 ImageMagick Studio
4 % Copyright 1991-1999 E. I. du Pont de Nemours and Company
5 %
6 % This program is covered by multiple licenses, which are described in
7 % Copyright.txt. You should have received a copy of Copyright.txt with this
8 % package; otherwise see http://www.graphicsmagick.org/www/Copyright.html.
9 %
10 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11 %                                                                             %
12 %                                                                             %
13 %                                                                             %
14 %                            PPPP   SSSSS  33333                              %
15 %                            P   P  SS        33                              %
16 %                            PPPP    SSS    333                               %
17 %                            P         SS     33                              %
18 %                            P      SSSSS  33333                              %
19 %                                                                             %
20 %                                                                             %
21 %                    Write Postscript Level III Format.                       %
22 %                                                                             %
23 %                                                                             %
24 %                              Software Design                                %
25 %                                John Cristy                                  %
26 %                                 July 1992                                   %
27 %                                                                             %
28 %                                                                             %
29 %                                                                             %
30 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
31 %
32 %
33 */
34 
35 /*
36   Include declarations.
37 */
38 #include "magick/studio.h"
39 #include "magick/analyze.h"
40 #include "magick/attribute.h"
41 #include "magick/blob.h"
42 #include "magick/pixel_cache.h"
43 #include "magick/channel.h"
44 #include "magick/color.h"
45 #include "magick/compress.h"
46 #include "magick/constitute.h"
47 #include "magick/enum_strings.h"
48 #include "magick/magick.h"
49 #include "magick/map.h"
50 #include "magick/monitor.h"
51 #include "magick/tempfile.h"
52 #include "magick/utility.h"
53 #include "magick/version.h"
54 #if defined(HasTIFF)
55 #define CCITTParam  "-1"
56 #else
57 #define CCITTParam  "0"
58 #endif
59 
60 /* For consistent PS and C code usage */
61 #define PS3_NoCompression "0"
62 #define PS3_FaxCompression "1"
63 #define PS3_RLECompression "2"
64 #define PS3_LZWCompression "3"
65 #define PS3_ZipCompression "4"
66 #define PS3_JPEGCompression "5"
67 
68 #define PS3_RGBColorspace "0"
69 #define PS3_CMYKColorspace "1"
70 
71 #define PS3_DirectClass "0"
72 #define PS3_PseudoClass "1"
73 
74 /*
75   Forward declarations.
76 */
77 static unsigned int
78   WritePS3Image(const ImageInfo *,Image *),
79   ZLIBEncode2Image(Image *,const size_t,const unsigned long,unsigned char *,
80     WriteByteHook,void *);
81 
82 #if defined(HasTIFF)
83 #if defined(HAVE_TIFFCONF_H)
84 #include "tiffconf.h"
85 #endif
86 #include "tiffio.h"
87 /*
88 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
89 %                                                                             %
90 %                                                                             %
91 %                                                                             %
92 %   H u f f m a n 2 D E n c o d e I m a g e                                   %
93 %                                                                             %
94 %                                                                             %
95 %                                                                             %
96 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
97 %
98 %  Method Huffman2DEncodeImage compresses an image via two-dimensional
99 %  Huffman-coding.
100 %
101 %  The format of the Huffman2DEncodeImage method is:
102 %
103 %      unsigned int Huffman2DEncodeImage(const ImageInfo *image_info,
104 %        Image *image)
105 %
106 %  A description of each parameter follows:
107 %
108 %    o status:  Method Huffman2DEncodeImage returns True if all the pixels are
109 %      compressed without error, otherwise False.
110 %
111 %    o image_info: The image info..
112 %
113 %    o image: The image.
114 %
115 */
SerializeHuffman2DImage(const ImageInfo * image_info,Image * image,unsigned char ** pixels,size_t * length)116 static unsigned int SerializeHuffman2DImage(const ImageInfo *image_info,
117   Image *image, unsigned char **pixels, size_t *length)
118 {
119   char
120     filename[MaxTextExtent];
121 
122   Image
123     *huffman_image;
124 
125   ImageInfo
126     *clone_info;
127 
128   long
129     count,
130     j;
131 
132   register long
133     i;
134 
135   TIFF
136     *tiff;
137 
138   uint16
139     fillorder;
140 
141   unsigned char
142     *buffer,
143     *p;
144 
145   unsigned int
146     status;
147 
148   unsigned long
149     *byte_count,
150     strip_size;
151 
152   /*
153     Write image as CCITTFax4 TIFF image to a temporary file.
154   */
155   assert(image_info != (ImageInfo *) NULL);
156   assert(image_info->signature == MagickSignature);
157   assert(image != (Image *) NULL);
158   assert(image->signature == MagickSignature);
159   if(!AcquireTemporaryFileName(filename))
160     {
161       ThrowBinaryException(FileOpenError,UnableToCreateTemporaryFile,
162         filename)
163     }
164   huffman_image=CloneImage(image,0,0,True,&image->exception);
165   if (huffman_image == (Image *) NULL)
166     return(False);
167   /*
168     TODO: If (image, then) huffman_image->compression is JPEG, huffman_image
169     is changed to DirectClass in WriteTIFFImage and the huffman_image ends up
170     broken. Change WriteTIFFIMage to not alter bilevel image to directclass
171     when clone_info is G4 compression. Until then, set
172     huffman_image->compression here:
173   */
174   huffman_image->compression=Group4Compression;
175 
176   (void) SetImageType(huffman_image,BilevelType);
177   FormatString(huffman_image->filename,"tiff:%s",filename);
178   clone_info=CloneImageInfo(image_info);
179   clone_info->compression=Group4Compression;
180   clone_info->type=BilevelType;
181   (void) AddDefinitions(clone_info,"tiff:fill-order=msb2lsb",
182                         &image->exception);
183   status=WriteImage(clone_info,huffman_image);
184   DestroyImageInfo(clone_info);
185   DestroyImage(huffman_image);
186   if (status == False)
187     {
188       (void) LiberateTemporaryFile(filename);
189       return(False);
190     }
191   tiff=TIFFOpen(filename,"rb");
192   if (tiff == (TIFF *) NULL)
193     {
194       (void) LiberateTemporaryFile(filename);
195       ThrowBinaryException(FileOpenError,UnableToOpenFile,
196         image_info->filename)
197     }
198   /*
199     Allocate raw strip buffer.
200   */
201   (void) TIFFGetField(tiff,TIFFTAG_STRIPBYTECOUNTS,&byte_count);
202   *length=strip_size=byte_count[0];
203   for (i=1; i < (long) TIFFNumberOfStrips(tiff); i++)
204   {
205     if (byte_count[i] > strip_size)
206       strip_size=byte_count[i];
207     *length+=byte_count[i];
208   }
209   buffer=MagickAllocateResourceLimitedMemory(unsigned char *,strip_size);
210   if (buffer == (unsigned char *) NULL)
211     {
212       TIFFClose(tiff);
213       (void) LiberateTemporaryFile(filename);
214       ThrowBinaryException(ResourceLimitError,MemoryAllocationFailed,
215         (char *) NULL)
216     }
217   *pixels=MagickAllocateResourceLimitedMemory(unsigned char *,*length);
218   if (*pixels == (unsigned char *) NULL)
219     {
220       MagickFreeResourceLimitedMemory(buffer);
221       TIFFClose(tiff);
222       (void) LiberateTemporaryFile(filename);
223       ThrowBinaryException(ResourceLimitError,MemoryAllocationFailed,
224         (char *) NULL)
225     }
226 
227   (void) TIFFGetFieldDefaulted(tiff,TIFFTAG_FILLORDER,&fillorder);
228   p=(*pixels);
229   for (i=0; i < (long) TIFFNumberOfStrips(tiff); i++)
230   {
231     count=TIFFReadRawStrip(tiff,(uint32) i,buffer,(long) byte_count[i]);
232     if (fillorder == FILLORDER_LSB2MSB)
233       TIFFReverseBits(buffer,count);
234     for (j=0; j < count; j++)
235       *p++=buffer[j];
236   }
237   MagickFreeResourceLimitedMemory(buffer);
238   TIFFClose(tiff);
239   (void) LiberateTemporaryFile(filename);
240   return(True);
241 }
242 
Huffman2DEncodeImage(const ImageInfo * image_info,Image * image)243 static unsigned int Huffman2DEncodeImage(const ImageInfo *image_info,
244   Image *image)
245 {
246   size_t
247     length;
248 
249   register unsigned long
250     i;
251 
252   unsigned char
253     *pixels;
254 
255   unsigned int
256     status;
257 
258   assert(image_info != (ImageInfo *) NULL);
259   assert(image_info->signature == MagickSignature);
260   assert(image != (Image *) NULL);
261   assert(image->signature == MagickSignature);
262 
263   status=SerializeHuffman2DImage(image_info,image,&pixels,&length);
264   if (!status)
265     return False;
266 
267   Ascii85Initialize(image);
268   for (i=0; i < length; i++)
269     Ascii85Encode(image,(unsigned long) pixels[i]);
270   Ascii85Flush(image);
271   MagickFreeResourceLimitedMemory(pixels);
272   return(True);
273 }
274 
275 #else
276 
SerializeHuffman2DImage(const ImageInfo * image_info,Image * image,unsigned char ** pixels,size_t * length)277 static unsigned int SerializeHuffman2DImage(const ImageInfo *image_info,
278   Image *image, unsigned char **pixels, size_t *length)
279 {
280   ARG_NOT_USED(image_info);
281   ARG_NOT_USED(pixels);
282   ARG_NOT_USED(length);
283   assert(image != (Image *) NULL);
284   assert(image->signature == MagickSignature);
285   ThrowBinaryException(MissingDelegateError,TIFFLibraryIsNotAvailable,image->filename)
286 }
Huffman2DEncodeImage(const ImageInfo * image_info,Image * image)287 static unsigned int Huffman2DEncodeImage(const ImageInfo *image_info,
288   Image *image)
289 {
290   ARG_NOT_USED(image_info);
291   assert(image != (Image *) NULL);
292   assert(image->signature == MagickSignature);
293   ThrowBinaryException(MissingDelegateError,TIFFLibraryIsNotAvailable,image->filename)
294 }
295 #endif
296 
297 /*
298 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
299 %                                                                             %
300 %                                                                             %
301 %                                                                             %
302 %   J P E G E n c o d e I m a g e                                             %
303 %                                                                             %
304 %                                                                             %
305 %                                                                             %
306 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
307 %
308 %  Method JPEGEncodeImage compresses an image via JPEG compression.
309 %
310 %  The format of the JPEGEncodeImage method is:
311 %
312 %      unsigned int JPEGEncodeImage(const ImageInfo *image_info,
313 %        Image *image)
314 %
315 %  A description of each parameter follows:
316 %
317 %    o status:  Method JPEGEncodeImage returns True if all the pixels are
318 %      compressed without error, otherwise False.
319 %
320 %    o image_info: The image info.
321 %
322 %    o image: The image.
323 %
324 */
JPEGEncodeImage(const ImageInfo * image_info,Image * image)325 static MagickPassFail JPEGEncodeImage(const ImageInfo *image_info,
326   Image *image)
327 {
328   unsigned char
329     *blob;
330 
331   size_t
332     length;
333 
334   MagickPassFail
335     status=MagickFail;
336 
337   blob=ImageToJPEGBlob(image,image_info,&length,&image->exception);
338   if (blob != (unsigned char *) NULL)
339     {
340       register const unsigned char
341         *p;
342 
343       register size_t
344         i;
345 
346         Ascii85Initialize(image);
347       for (p=(const unsigned char*) blob,i=0; i < length; i++)
348         Ascii85Encode(image,(unsigned long) p[i]);
349       Ascii85Flush(image);
350       MagickFreeMemory(blob);
351       status=MagickPass;
352     }
353   return status;
354 }
355 
356 /*
357 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
358 %                                                                             %
359 %                                                                             %
360 %                                                                             %
361 %   R e g i s t e r P S 3 I m a g e                                           %
362 %                                                                             %
363 %                                                                             %
364 %                                                                             %
365 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
366 %
367 %  Method RegisterPS3Image adds attributes for the PS3 image format to
368 %  the list of supported formats.  The attributes include the image format
369 %  tag, a method to read and/or write the format, whether the format
370 %  supports the saving of more than one frame to the same file or blob,
371 %  whether the format supports native in-memory I/O, and a brief
372 %  description of the format.
373 %
374 %  The format of the RegisterPS3Image method is:
375 %
376 %      RegisterPS3Image(void)
377 %
378 */
RegisterPS3Image(void)379 ModuleExport void RegisterPS3Image(void)
380 {
381   MagickInfo
382     *entry;
383 
384   entry=SetMagickInfo("EPS3");
385   entry->encoder=(EncoderHandler) WritePS3Image;
386   entry->description="Adobe Level III Encapsulated PostScript";
387   entry->seekable_stream=MagickTrue;
388   entry->module="PS3";
389   entry->coder_class=PrimaryCoderClass;
390   (void) RegisterMagickInfo(entry);
391 
392   entry=SetMagickInfo("PS3");
393   entry->encoder=(EncoderHandler) WritePS3Image;
394   entry->description="Adobe Level III PostScript";
395   entry->seekable_stream=MagickTrue;
396   entry->module="PS3";
397   entry->coder_class=PrimaryCoderClass;
398   (void) RegisterMagickInfo(entry);
399 }
400 
401 /*
402 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
403 %                                                                             %
404 %                                                                             %
405 %                                                                             %
406 %   S e r i a l i z e P s e u d o C l a s s I m a g e                         %
407 %                                                                             %
408 %                                                                             %
409 %                                                                             %
410 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
411 %
412 %  Convert the indexes of a color mapped image to a stream of bytes.
413 %
414 %  The format of the SerializePseudoClassImage method is:
415 %
416 %       unsigned int SerializePseudoClassImage(const ImageInfo *image_info,
417 %         Image *image, unsigned char **pixels, size_t *length)
418 %
419 %  A description of each parameter follows:
420 %
421 %    o image_info: Specifies a pointer to a ImageInfo structure. required by
422 %                  exception handlers.
423 %
424 %    o image: The image for which the color map indexes should be serialized.
425 %
426 %    o pixels: the serialized indexes.
427 %
428 %    o length: the length of the pixels mamory area.
429 %
430 */
SerializePseudoClassImage(const ImageInfo * image_info,Image * image,unsigned char ** pixels,size_t * length)431 static unsigned int SerializePseudoClassImage(const ImageInfo *image_info,
432   Image *image, unsigned char **pixels, size_t *length)
433 {
434   long
435     x,
436     y;
437 
438   register unsigned char
439     *q;
440 
441   const PixelPacket
442     *p;
443 
444   int
445     status;
446 
447   register const IndexPacket
448     *indexes;
449 
450   assert(image != (Image *) NULL);
451   assert(image->signature == MagickSignature);
452   status=True;
453   *length=MagickArraySize(image->columns,image->rows);
454   *pixels=MagickAllocateResourceLimitedMemory(unsigned char *, *length);
455   if (*pixels == (unsigned char *) NULL)
456     ThrowWriterException(ResourceLimitError,MemoryAllocationFailed,image);
457   q=(*pixels);
458   for (y=0; y < (long) image->rows; y++)
459   {
460     p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception);
461     if (p == (const PixelPacket *) NULL)
462       break;
463     indexes=AccessImmutableIndexes(image);
464     for (x=0; x < (long) image->columns; x++)
465       *q++=indexes[x];
466     if (image->previous == (Image *) NULL)
467       if (QuantumTick(y,image->rows))
468         {
469           status=MagickMonitorFormatted(y,image->rows,&image->exception,
470                                         SaveImageText,image->filename,
471                                         image->columns,image->rows);
472           if (status == False)
473             break;
474         }
475   }
476   if (status == False)
477     MagickFreeResourceLimitedMemory(*pixels);
478   return(status);
479 }
480 
481 /*
482 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
483 %                                                                             %
484 %                                                                             %
485 %                                                                             %
486 %   S e r i a l i z e M u l t i C h a n n e l I m a g e                       %
487 %                                                                             %
488 %                                                                             %
489 %                                                                             %
490 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
491 %
492 %  Convert three and four channel images to a stream of bytes.
493 %
494 %  The format of the SerializeMultiChannelImage method is:
495 %
496 %       unsigned int SerializeMultiChannelImage(const ImageInfo *image_info,
497 %         Image *image, unsigned char **pixels, size_t *length)
498 %
499 %  A description of each parameter follows:
500 %
501 %    o image_info: Specifies a pointer to a ImageInfo structure. required by
502 %                  exception handlers.
503 %
504 %    o image: The image for which the RGB or CMYK channels should be
505 %             serialized.
506 %
507 %    o pixels: the serialized image channels.
508 %
509 %    o length: the length of the pixels mamory area.
510 %
511 */
SerializeMultiChannelImage(const ImageInfo * image_info,Image * image,unsigned char ** pixels,size_t * length)512 static unsigned int SerializeMultiChannelImage(const ImageInfo *image_info,
513   Image *image, unsigned char **pixels, size_t *length)
514 {
515   long
516     x,
517     y;
518 
519   register unsigned char
520     *q;
521 
522   const PixelPacket
523     *p;
524 
525   int
526     status;
527 
528   assert(image != (Image *) NULL);
529   assert(image->signature == MagickSignature);
530   status=True;
531   *length=(size_t) (image->colorspace == CMYKColorspace ? 4U : 3U)*MagickArraySize(image->columns,image->rows);
532   *pixels=MagickAllocateResourceLimitedMemory(unsigned char *, *length);
533   if (*pixels == (unsigned char *) NULL)
534     ThrowWriterException(ResourceLimitError,MemoryAllocationFailed,image);
535 
536   q=(*pixels);
537   for (y=0; y < (long) image->rows; y++)
538   {
539     p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception);
540     if (p == (const PixelPacket *) NULL)
541       break;
542     if (image->colorspace == CMYKColorspace)
543       for (x=0; x < (long) image->columns; x++)
544       {
545         *q++=ScaleQuantumToChar(p->red);
546         *q++=ScaleQuantumToChar(p->green);
547         *q++=ScaleQuantumToChar(p->blue);
548         *q++=ScaleQuantumToChar(p->opacity);
549         p++;
550       }
551     else
552       for (x=0; x < (long) image->columns; x++)
553       {
554         *q++=ScaleQuantumToChar(p->red);
555         *q++=ScaleQuantumToChar(p->green);
556         *q++=ScaleQuantumToChar(p->blue);
557         p++;
558       }
559     if (image->previous == (Image *) NULL)
560       if (QuantumTick(y,image->rows))
561         {
562           status=MagickMonitorFormatted(y,image->rows,&image->exception,
563                                         SaveImageText,image->filename,
564                                         image->columns,image->rows);
565           if (status == False)
566             break;
567         }
568   }
569   if (status == False)
570     MagickFreeResourceLimitedMemory(*pixels);
571   return(status);
572 }
573 
574 /*
575 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
576 %                                                                             %
577 %                                                                             %
578 %                                                                             %
579 %   S e r i a l i z e S i n g l e C h a n n e l I m a g e                     %
580 %                                                                             %
581 %                                                                             %
582 %                                                                             %
583 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
584 %
585 %  Convert 1 and 8 bit single channel images to a stream of bytes. Bilevel
586 %  image pixels are packed 8 pixels in a byte.
587 %
588 %  The format of the SerializeSingleChannelImage method is:
589 %
590 %       unsigned int SerializeSingleChannelImage(const ImageInfo *image_info,
591 %         Image *image, unsigned char **pixels, size_t *length)
592 %
593 %  A description of each parameter follows:
594 %
595 %    o image_info: Specifies a pointer to an ImageInfo structure. Required by
596 %                  exception handlers.
597 %
598 %    o image: The image for which the RGB or CMYK channels should be
599 %             serialized.
600 %
601 %    o characteristics: Already populated image characteristics.
602 %
603 %    o pixels: the serialized image channels.
604 %
605 %    o length: the length of the pixels mamory area.
606 %
607 */
SerializeSingleChannelImage(const ImageInfo * image_info,Image * image,unsigned char ** pixels,size_t * length)608 static unsigned int SerializeSingleChannelImage(const ImageInfo *image_info,
609   Image *image,unsigned char **pixels, size_t *length)
610 {
611   unsigned long
612     x,
613     y;
614 
615   register unsigned char
616     *q;
617 
618   const PixelPacket
619     *p;
620 
621   unsigned long
622     pack,
623     padded_columns;
624 
625   unsigned char
626     code,
627     bit;
628 
629   int
630     status;
631 
632   assert(image != (Image *) NULL);
633   assert(image->signature == MagickSignature);
634   status=True;
635   pack=IsMonochromeImage(image,&image->exception) ? 8 : 1;
636   /* Padded columns are padded to byte boundary */
637   padded_columns=((image->columns+pack-1)/pack)*pack;
638   *length=padded_columns*image->rows/pack;
639   *pixels=MagickAllocateResourceLimitedMemory(unsigned char *, *length);
640   if (*pixels == (unsigned char *) NULL)
641     ThrowWriterException(ResourceLimitError,MemoryAllocationFailed,
642       image);
643 
644   q=(*pixels);
645   for (y=0; y < image->rows; y++)
646   {
647     p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception);
648     if (p == (const PixelPacket *) NULL)
649       break;
650     if (pack == 1)
651     {
652       for (x=0; x < image->columns; x++)
653       {
654         *q++=ScaleQuantumToChar(PixelIntensityToQuantum(p));
655         p++;
656       }
657     }
658     else
659     {
660       code=0;
661       for (x=0; x < padded_columns; x++)
662       {
663         bit=0x00;
664         if (x < image->columns)
665           bit=PixelIntensityToQuantum(p) == TransparentOpacity ? 0x01 : 0x00;
666         code=(code << 1)+bit;
667         if ((x+1) % pack == 0)
668         {
669           *q++=code;
670           code=0;
671         }
672         p++;
673       }
674     }
675     if (image->previous == (Image *) NULL)
676       if (QuantumTick(y,image->rows))
677         {
678           status=MagickMonitorFormatted(y,image->rows,
679                                         &image->exception,SaveImageText,
680                                         image->filename,
681                                         image->columns,image->rows);
682           if (status == False)
683             break;
684         }
685   }
686   if (status == False)
687     MagickFreeResourceLimitedMemory(*pixels);
688   return(status);
689 }
690 /*
691 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
692 %                                                                             %
693 %                                                                             %
694 %                                                                             %
695 %   U n r e g i s t e r P S 3 I m a g e                                       %
696 %                                                                             %
697 %                                                                             %
698 %                                                                             %
699 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
700 %
701 %  Method UnregisterPS3Image removes format registrations made by the
702 %  PS3 module from the list of supported formats.
703 %
704 %  The format of the UnregisterPS3Image method is:
705 %
706 %      UnregisterPS3Image(void)
707 %
708 */
UnregisterPS3Image(void)709 ModuleExport void UnregisterPS3Image(void)
710 {
711   (void) UnregisterMagickInfo("EPS3");
712   (void) UnregisterMagickInfo("PS3");
713 }
714 
715 /*
716 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
717 %                                                                             %
718 %                                                                             %
719 %                                                                             %
720 %   W r i t e P S 3 M a s k I m a g e                                         %
721 %                                                                             %
722 %                                                                             %
723 %                                                                             %
724 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
725 %
726 %  Method WritePS3ImageMask writes the alpha channel of an image as an
727 %  Encapsulated Postscript Level III image mask. The mask is written as a
728 %  bilevel image, converted from the grayscale alpha channel using dithering
729 %  options supplied in image_info.
730 %
731 %  The format of the WritePS3MaskImage method is:
732 %
733 %      unsigned int WritePS3MaskImage(const ImageInfo *image_info,Image *image)
734 %
735 %  A description of each parameter follows:
736 %
737 %    o status: Method WritePS3MaskImage return True if the mask is printed.
738 %      False is returned on errors like out-of-memory.
739 %
740 %    o image_info: Specifies a pointer to a ImageInfo structure.
741 %
742 %    o image: The address of a structure of type Image.
743 %
744 %
745 */
746 typedef struct WriteByteHookInfo_
747 {
748   Image
749     *image;
750 } WriteByteHookInfo;
751 
MaskWriteByteHook(Image * mask_image,const magick_uint8_t code,void * info)752 static unsigned int MaskWriteByteHook(Image *mask_image,
753   const magick_uint8_t code, void *info)
754 {
755   ARG_NOT_USED(mask_image);
756   Ascii85Encode(((WriteByteHookInfo *)info)->image, (unsigned long)code);
757   return(True);
758 }
759 
WritePS3MaskImage(const ImageInfo * image_info,Image * image)760 static MagickPassFail WritePS3MaskImage(const ImageInfo *image_info,Image *image)
761 {
762   char
763     buffer[MaxTextExtent];
764 
765   Image
766     *mask_image;
767 
768   size_t
769     i,
770     length;
771 
772   CompressionType
773     compression;
774 
775   unsigned char
776     *pixels;
777 
778   MagickPassFail
779     status;
780 
781   magick_off_t
782     here,
783     start,
784     stop;
785 
786   assert(image_info != (ImageInfo *) NULL);
787   assert(image_info->signature == MagickSignature);
788   assert(image != (Image *) NULL);
789   assert(image->signature == MagickSignature);
790   assert(image->matte);
791   status=MagickPass;
792   compression=image->compression;
793   if (image_info->compression != UndefinedCompression)
794     compression=image_info->compression;
795 
796   /* Keep position of BeginData DSC comment for update later */
797   start=TellBlob(image);
798   if (start < 0)
799     return MagickFail;
800   FormatString(buffer,"%%%%BeginData:%13ld ASCII Bytes\n",0L);
801   (void) WriteBlobString(image,buffer);
802   stop=TellBlob(image);
803   if (stop < 0)
804     return MagickFail;
805 
806   /* Only lossless compressions for the mask */
807   switch (compression)
808   {
809     case NoCompression:
810       FormatString(buffer,
811         "currentfile %lu %lu "PS3_NoCompression" ByteStreamDecodeFilter\n",
812         image->columns, image->rows);
813       break;
814     case FaxCompression:
815     default:
816       FormatString(buffer,
817         "currentfile %lu %lu "PS3_FaxCompression" ByteStreamDecodeFilter\n",
818         image->columns, image->rows);
819       break;
820     case RLECompression:
821       FormatString(buffer,
822         "currentfile %lu %lu "PS3_RLECompression" ByteStreamDecodeFilter\n",
823         image->columns, image->rows);
824       break;
825     case LZWCompression:
826       FormatString(buffer,
827         "currentfile %lu %lu "PS3_LZWCompression" ByteStreamDecodeFilter\n",
828         image->columns, image->rows);
829       break;
830     case ZipCompression:
831       FormatString(buffer,
832         "currentfile %lu %lu "PS3_ZipCompression" ByteStreamDecodeFilter\n",
833         image->columns, image->rows);
834       break;
835   }
836   (void)WriteBlobString(image, buffer);
837   (void)WriteBlobString(image, "/ReusableStreamDecode filter\n");
838 
839   mask_image=CloneImage(image,0,0,True,&image->exception);
840   if (mask_image == (Image *) NULL)
841     return MagickFail;
842   status=ChannelImage(mask_image, OpacityChannel);
843   if (!status)
844   {
845     CopyException(&image->exception,&mask_image->exception);
846     DestroyImage(mask_image);
847     return(MagickFail);
848   }
849   (void) SetImageType(mask_image, BilevelType);
850   mask_image->matte=False;
851 
852   /* Only lossless compressions for the mask */
853   switch (compression)
854   {
855     case NoCompression:
856       status=SerializeSingleChannelImage(image_info,mask_image,&pixels,&length);
857       if (status)
858       {
859         Ascii85Initialize(image);
860         for (i=0; i < length; i++)
861           Ascii85Encode(image, (unsigned long)pixels[i]);
862         Ascii85Flush(image);
863         MagickFreeResourceLimitedMemory(pixels);
864       }
865       break;
866     case FaxCompression:
867     default:
868       if (LocaleCompare(CCITTParam,"0") == 0)
869       {
870         WriteByteHookInfo
871           info;
872         info.image=image;
873         /* Read bytes from mask and write them ASCII85 encoded to image */
874         Ascii85Initialize(image);
875         status=HuffmanEncode2Image(image_info,mask_image,MaskWriteByteHook,(void *)&info);
876         Ascii85Flush(image);
877       }
878       else
879       {
880         status=SerializeHuffman2DImage(image_info,mask_image,&pixels,&length);
881         if (status)
882         {
883           Ascii85Initialize(image);
884           for (i=0; i < length; i++)
885             Ascii85Encode(image,(unsigned long) pixels[i]);
886           Ascii85Flush(image);
887           MagickFreeResourceLimitedMemory(pixels);
888         }
889       }
890       break;
891     case RLECompression:
892       status=SerializeSingleChannelImage(image_info,mask_image,&pixels,&length);
893       if (status)
894       {
895         Ascii85Initialize(image);
896         status=PackbitsEncode2Image(image,length,pixels,
897           Ascii85WriteByteHook,(void *)NULL);
898         Ascii85Flush(image);
899         MagickFreeResourceLimitedMemory(pixels);
900       }
901       break;
902     case LZWCompression:
903       status=SerializeSingleChannelImage(image_info,mask_image,&pixels,&length);
904       if (status)
905       {
906         Ascii85Initialize(image);
907         status=LZWEncode2Image(image,length,pixels,Ascii85WriteByteHook,(void*)NULL);
908         Ascii85Flush(image);
909         MagickFreeResourceLimitedMemory(pixels);
910       }
911       break;
912     case ZipCompression:
913       status=SerializeSingleChannelImage(image_info,mask_image,&pixels,&length);
914       if (status)
915       {
916         Ascii85Initialize(image);
917         status=ZLIBEncode2Image(image,length,image_info->quality,pixels,
918           Ascii85WriteByteHook,(void*)NULL);
919         Ascii85Flush(image);
920         MagickFreeResourceLimitedMemory(pixels);
921       }
922       break;
923   }
924   DestroyImage(mask_image);
925   here=TellBlob(image);
926   if (here < 0)
927     return MagickFail;
928   length=here-stop;
929   stop=TellBlob(image);
930   if (stop < 0)
931     return MagickFail;
932   if (SeekBlob(image,start,SEEK_SET) != start)
933     ThrowWriterException(BlobError,UnableToSeekToOffset,image);
934   FormatString(buffer,"%%%%BeginData:%13ld ASCII Bytes\n",(long) length);
935   (void) WriteBlobString(image,buffer);
936   if (SeekBlob(image,stop,SEEK_SET) != stop)
937     ThrowWriterException(BlobError,UnableToSeekToOffset,image);
938   (void) WriteBlobString(image,"%%EndData\n");
939   (void)WriteBlobString(image, "/mask_stream exch def\n");
940   return(status);
941 }
942 
943 /*
944 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
945 %                                                                             %
946 %                                                                             %
947 %                                                                             %
948 %   W r i t e P S 3 I m a g e                                                 %
949 %                                                                             %
950 %                                                                             %
951 %                                                                             %
952 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
953 %
954 %  Method WritePS3Image translates an image to encapsulated Postscript
955 %  Level III for printing.  If the supplied geometry is null, the image is
956 %  centered on the Postscript page.  Otherwise, the image is positioned as
957 %  specified by the geometry.
958 %
959 %  The format of the WritePS3Image method is:
960 %
961 %      MagickPassFail WritePS3Image(const ImageInfo *image_info,Image *image)
962 %
963 %  A description of each parameter follows:
964 %
965 %    o status: Method WritePS3Image return MagickPass if the image is printed.
966 %      MagickFail is returned if the image file cannot be opened for printing.
967 %
968 %    o image_info: Specifies a pointer to a ImageInfo structure.
969 %
970 %    o image: The address of a structure of type Image;  returned from
971 %      ReadImage.
972 %
973 %
974 */
WritePS3Image(const ImageInfo * image_info,Image * image)975 static MagickPassFail WritePS3Image(const ImageInfo *image_info,Image *image)
976 {
977   static const char PostscriptProlog[]=
978     "/ByteStreamDecodeFilter\n"
979     "{\n"
980     "  /z exch def\n"
981     "  /r exch def\n"
982     "  /c exch def\n"
983     "  /ASCII85Decode filter\n"
984     "  z "PS3_FaxCompression" eq\n"
985     "  {\n"
986     "    <<\n"
987     "      /K "CCITTParam"\n"
988     "      /Columns c\n"
989     "      /Rows r\n"
990     "    >>\n"
991     "    /CCITTFaxDecode filter\n"
992     "  } if\n"
993     "  z "PS3_RLECompression" eq {/RunLengthDecode filter} if\n"
994     "  z "PS3_LZWCompression" eq {/LZWDecode filter} if\n"
995     "  z "PS3_ZipCompression" eq {/FlateDecode filter} if\n"
996     "  z "PS3_JPEGCompression" eq {/DCTDecode filter} if\n"
997     "} bind def\n"
998     "\n"
999     "/DirectClassImageDict\n"
1000     "{\n"
1001     "  colorspace "PS3_RGBColorspace" eq\n"
1002     "  {\n"
1003     "    /DeviceRGB setcolorspace\n"
1004     "    <<\n"
1005     "      /ImageType 1\n"
1006     "      /Width columns\n"
1007     "      /Height rows\n"
1008     "      /BitsPerComponent 8\n"
1009     "      /DataSource pixel_stream\n"
1010     "      /MultipleDataSources false\n"
1011     "      /ImageMatrix [columns 0 0 rows neg 0 rows]\n"
1012     "      /Decode [0 1 0 1 0 1]\n"
1013     "    >>\n"
1014     "  }\n"
1015     "  {\n"
1016     "    /DeviceCMYK setcolorspace\n"
1017     "    <<\n"
1018     "      /ImageType 1\n"
1019     "      /Width columns\n"
1020     "      /Height rows\n"
1021     "      /BitsPerComponent 8\n"
1022     "      /DataSource pixel_stream\n"
1023     "      /MultipleDataSources false\n"
1024     "      /ImageMatrix [columns 0 0 rows neg 0 rows]\n"
1025     "      /Decode\n"
1026     /*
1027       JPEG coder is used for JPEG compression. It compensates
1028       for inverted CMYK, so use an inverted decode matrix for
1029       JPEG compressed CMYK images.
1030     */
1031     "        compression "PS3_JPEGCompression" eq\n"
1032     "        {[1 0 1 0 1 0 1 0]}\n"
1033     "        {[0 1 0 1 0 1 0 1]}\n"
1034     "        ifelse\n"
1035     "    >>\n"
1036     "  }\n"
1037     "  ifelse\n"
1038     "} bind def\n"
1039     "\n"
1040     "/PseudoClassImageDict\n"
1041     "{\n"
1042     "  % COLORS IN PSEUDO CLASS IMAGE\n"
1043     "  currentfile buffer readline pop\n"
1044     "  token pop /colors exch def pop\n"
1045     "  colors 0 eq\n"
1046     "  {\n"
1047     "    % DEPTH OF GRAYSCALE\n"
1048     "    currentfile buffer readline pop\n"
1049     "    token pop /bits exch def pop\n"
1050     "    /DeviceGray setcolorspace\n"
1051     "    <<\n"
1052     "      /ImageType 1\n"
1053     "      /Width columns\n"
1054     "      /Height rows\n"
1055     "      /BitsPerComponent bits\n"
1056     "      /Decode [0 1]\n"
1057     "      /ImageMatrix [columns 0 0 rows neg 0 rows]\n"
1058     "      /DataSource pixel_stream\n"
1059     "    >>\n"
1060     "  }\n"
1061     "  {\n"
1062     "    % RGB COLORMAP\n"
1063     "    /colormap colors 3 mul string def\n"
1064     "    % INDEXES\n"
1065     "    currentfile /ASCII85Decode filter colormap readstring pop pop\n"
1066     "    [ /Indexed /DeviceRGB colors 1 sub colormap ] setcolorspace\n"
1067     "    <<\n"
1068     "      /ImageType 1\n"
1069     "      /Width columns\n"
1070     "      /Height rows\n"
1071     "      /BitsPerComponent 8\n"
1072     "      /Decode [0 255]\n"
1073     "      /ImageMatrix [columns 0 0 rows neg 0 rows]\n"
1074     "      /DataSource pixel_stream\n"
1075     "    >>\n"
1076     "  }\n"
1077     "  ifelse\n"
1078     "} bind def\n"
1079     "\n"
1080     "/NonMaskedImageDict\n"
1081     "{\n"
1082     "  class "PS3_PseudoClass" eq\n"
1083     "  { PseudoClassImageDict }\n"
1084     "  { DirectClassImageDict }\n"
1085     "  ifelse\n"
1086     "} bind def\n"
1087     "\n"
1088     "/MaskedImageDict\n"
1089     "{\n"
1090     "  <<\n"
1091     "    /ImageType 3\n"
1092     "    /InterleaveType 3\n"
1093     "    /DataDict NonMaskedImageDict\n"
1094     "    /MaskDict\n"
1095     "    <<\n"
1096     "      /ImageType 1\n"
1097     "      /Width columns\n"
1098     "      /Height rows\n"
1099     "      /BitsPerComponent 1\n"
1100     "      /DataSource mask_stream\n"
1101     "      /MultipleDataSources false\n"
1102     "      /ImageMatrix [ columns 0 0 rows neg 0 rows]\n"
1103     "      /Decode [ 0 1 ]\n"
1104     "    >>\n"
1105     "  >>\n"
1106     "} bind def\n"
1107     "\n"
1108     /*
1109       Default procedure for image clipping does nothing. Image
1110       will provide overriding ClipPath procdure if relevant.
1111     */
1112     "/ClipImage\n"
1113     "{} def\n"
1114     "\n"
1115     "/DisplayImage\n"
1116     "{\n"
1117     "  /buffer 512 string def\n"
1118     "\n"
1119     "  % TRANSLATION\n"
1120     "  currentfile buffer readline pop\n"
1121     "  token pop /x exch def\n"
1122     "  token pop /y exch def pop\n"
1123     "  x y translate\n"
1124     "\n"
1125     "  % IMAGE SIZE AND FONT SIZE\n"
1126     "  currentfile buffer readline pop\n"
1127     "  token pop /x exch def\n"
1128     "  token pop /y exch def pop\n"
1129     "  currentfile buffer readline pop\n"
1130     "  token pop /pointsize exch def pop\n";
1131 
1132   /*
1133     This hole in the PS prolog is for labels.
1134   */
1135   static const char PostscriptEpilog[]=
1136     "  x y scale\n"
1137     "\n"
1138     "  % CLIPPING PATH\n"
1139     "  currentfile buffer readline pop\n"
1140     "  token pop /clipped exch def pop\n"
1141     "\n"
1142     "  % EPS\n"
1143     "  currentfile buffer readline pop\n"
1144     "  token pop /sp exch def pop\n"
1145     "\n"
1146     "  % IMAGE PIXEL SIZE\n"
1147     "  currentfile buffer readline pop\n"
1148     "  token pop /columns exch def\n"
1149     "  token pop /rows exch def pop\n"
1150     "\n"
1151     "  % COLORSPACE (RGB/CMYK)\n"
1152     "  currentfile buffer readline pop\n"
1153     "  token pop /colorspace exch def pop\n"
1154     "\n"
1155     "  % TRANSPARENCY\n"
1156     "  currentfile buffer readline pop\n"
1157     "  token pop /alpha exch def pop\n"
1158     "\n"
1159     "  % STENCIL MASK?\n"
1160     "  currentfile buffer readline pop\n"
1161     "  token pop /stencil exch def pop\n"
1162     "\n"
1163     "  % IMAGE CLASS (DIRECT/PSEUDO)\n"
1164     "  currentfile buffer readline pop\n"
1165     "  token pop /class exch def pop\n"
1166     "\n"
1167     "  % COMPRESSION\n"
1168     "  currentfile buffer readline pop\n"
1169     "  token pop /compression exch def pop\n"
1170     "\n"
1171     "  % CLIP AND RENDER\n"
1172     "  /pixel_stream currentfile columns rows compression\n"
1173     "    ByteStreamDecodeFilter def\n"
1174     "  clipped {ClipImage} if\n"
1175     "  alpha stencil not and\n"
1176     "  { MaskedImageDict mask_stream resetfile }\n"
1177     "  { NonMaskedImageDict }\n"
1178     "  ifelse\n"
1179     "  stencil {0 setgray imagemask} {image} ifelse\n"
1180     "  grestore\n"
1181     "  sp {showpage} if\n"
1182     "} bind def\n";
1183 
1184   char
1185     buffer[MaxTextExtent],
1186     date[MaxTextExtent],
1187     density[MaxTextExtent],
1188     page_geometry[MaxTextExtent],
1189     **labels;
1190 
1191   CompressionType
1192     compression;
1193 
1194   const ImageAttribute
1195     *attribute;
1196 
1197   double
1198     dx_resolution,
1199     dy_resolution,
1200     x_resolution,
1201     x_scale,
1202     y_resolution,
1203     y_scale;
1204 
1205   magick_off_t
1206     current,
1207     start,
1208     stop;
1209 
1210   int
1211     count,
1212     status;
1213 
1214   RectangleInfo
1215     geometry;
1216 
1217   register unsigned int
1218     i,
1219     j;
1220 
1221   SegmentInfo
1222     bounds={0.0,0.0,0.0,0.0};
1223 
1224   size_t
1225     length;
1226 
1227   time_t
1228     timer;
1229 
1230   unsigned char
1231     *pixels;
1232 
1233   unsigned long
1234     page,
1235     scene,
1236     text_size;
1237 
1238   size_t
1239     image_list_length;
1240 
1241   Image
1242     *clip_mask;
1243 
1244   /*
1245     Open output image file.
1246   */
1247   assert(image_info != (const ImageInfo *) NULL);
1248   assert(image_info->signature == MagickSignature);
1249   assert(image != (Image *) NULL);
1250   assert(image->signature == MagickSignature);
1251   image_list_length=GetImageListLength(image);
1252   status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
1253   if (status == False)
1254     ThrowWriterException(FileOpenError,UnableToOpenFile,image);
1255   compression=image->compression;
1256   if (image_info->compression != UndefinedCompression)
1257     compression=image_info->compression;
1258   switch (compression)
1259   {
1260 #if !defined(HasJPEG)
1261     case JPEGCompression:
1262     {
1263       compression=RLECompression;
1264       ThrowException(&image->exception,MissingDelegateError,JPEGLibraryIsNotAvailable,image->filename);
1265       break;
1266     }
1267 #endif
1268 #if !defined(HasZLIB)
1269     case ZipCompression:
1270     {
1271       compression=RLECompression;
1272       ThrowException(&image->exception,MissingDelegateError,ZipLibraryIsNotAvailable,image->filename);
1273       break;
1274     }
1275 #endif
1276     default:
1277       break;
1278   }
1279   page=0;
1280   scene=0;
1281   clip_mask = *ImageGetClipMask(image);
1282   do
1283   {
1284     page++;
1285     /*
1286       Scale image to size of Postscript page.
1287     */
1288     text_size=0;
1289     attribute=GetImageAttribute(image,"label");
1290     if (attribute != (const ImageAttribute *) NULL)
1291       text_size=(unsigned int)(MultilineCensus(attribute->value)*
1292         image_info->pointsize+12);
1293     SetGeometry(image,&geometry);
1294     geometry.y=(long) text_size;
1295     FormatString(page_geometry,"%lux%lu",image->columns,image->rows);
1296     if (image_info->page != (char *) NULL)
1297       {
1298         (void) strlcpy(page_geometry,image_info->page,MaxTextExtent);
1299       }
1300     else
1301       {
1302         if ((image->page.width != 0) && (image->page.height != 0))
1303           (void) FormatString(page_geometry,"%lux%lu%+ld%+ld",
1304             image->page.width,image->page.height,image->page.x,
1305               image->page.y);
1306         else
1307           if (LocaleCompare(image_info->magick,"PS3") == 0)
1308             (void) strlcpy(page_geometry,PSPageGeometry,sizeof(page_geometry));
1309       }
1310     (void) GetMagickGeometry(page_geometry,&geometry.x,&geometry.y,
1311        &geometry.width,&geometry.height);
1312     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1313                           "Image Resolution: %gx%g %s",
1314                           image->x_resolution,
1315                           image->y_resolution,
1316                           ResolutionTypeToString(image->units));
1317     /*
1318       Scale relative to dots-per-inch. First whatever resolution passed
1319       in image_info. Then resolution from the image. Last default PS
1320       resolution.
1321     */
1322     dx_resolution=72.0;
1323     dy_resolution=72.0;
1324     x_resolution=72.0;
1325     (void) strlcpy(density,PSDensityGeometry,sizeof(density));
1326     count=GetMagickDimension(density,&x_resolution,&y_resolution,NULL,NULL);
1327     if (count != 2)
1328       y_resolution=x_resolution;
1329     /*
1330       Use override resolution information if it appears to be valid.
1331     */
1332     if ((image_info->density != (char *) NULL) &&
1333         ((image_info->units == PixelsPerInchResolution) ||
1334          (image_info->units == PixelsPerCentimeterResolution)))
1335       {
1336         count=GetMagickDimension(image_info->density,&x_resolution,
1337                                  &y_resolution,NULL,NULL);
1338         if (count != 2)
1339           y_resolution=x_resolution;
1340         if (image_info->units == PixelsPerCentimeterResolution)
1341           {
1342             x_resolution *= 2.54;
1343             y_resolution *= 2.54;
1344           }
1345       }
1346     /*
1347       Use image resolution information if it appears to be valid.
1348     */
1349     else if ((image->x_resolution > 0.0) && (image->y_resolution > 0.0) &&
1350              ((image->units == PixelsPerInchResolution) ||
1351               (image->units == PixelsPerCentimeterResolution)))
1352       {
1353         x_resolution = image->x_resolution;
1354         y_resolution = image->y_resolution;
1355         if (image->units == PixelsPerCentimeterResolution)
1356           {
1357             x_resolution *= 2.54;
1358             y_resolution *= 2.54;
1359           }
1360       }
1361     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1362                           "Postscript Resolution: %gx%g DPI",
1363                           x_resolution,y_resolution);
1364     x_scale=(geometry.width*dx_resolution)/x_resolution;
1365     geometry.width=(unsigned long) (x_scale+0.5);
1366     y_scale=(geometry.height*dy_resolution)/y_resolution;
1367     geometry.height=(unsigned long) (y_scale+0.5);
1368 
1369     /*
1370       Postscript header on the first page
1371     */
1372     if (page == 1)
1373       {
1374 #if defined(HAVE_CTIME_R)
1375         char time_buf[26];
1376 #endif /* defined(HAVE_CTIME_R) */
1377         /* Postscript magic */
1378         if (LocaleCompare(image_info->magick,"PS3") == 0)
1379           (void) strlcpy(buffer,"%!PS-Adobe-3.0\n", sizeof(buffer));
1380         else
1381           (void) strlcpy(buffer,"%!PS-Adobe-3.0 EPSF-3.0\n", sizeof(buffer));
1382         (void) WriteBlobString(image,buffer);
1383 
1384         /* Creator */
1385         FormatString(buffer,"%%%%Creator: GraphicsMagick %s\n",
1386           MagickLibVersionText);
1387         (void) WriteBlobString(image,buffer);
1388 
1389         /* Title */
1390         FormatString(buffer,"%%%%Title: %.1024s\n",image->filename);
1391         (void) WriteBlobString(image,buffer);
1392 
1393         /* File creation timestamp */
1394         timer=time((time_t *) NULL);
1395 #if defined(HAVE_CTIME_R)
1396         (void) strlcpy(date,ctime_r(&timer,time_buf),MaxTextExtent);
1397 #else
1398         (void) strlcpy(date,ctime(&timer),MaxTextExtent); /* Thread-unsafe version */
1399 #endif /* defined(HAVE_CTIME_R) */
1400         date[strlen(date)-1]='\0';
1401         FormatString(buffer,"%%%%CreationDate: %.1024s\n",date);
1402         (void) WriteBlobString(image,buffer);
1403 
1404         /* Bounding box and process colors if not RGB */
1405         bounds.x1=geometry.x;
1406         bounds.y1=geometry.y;
1407         bounds.x2=geometry.x+x_scale;
1408         bounds.y2=geometry.y+y_scale+text_size;
1409         if (image_info->adjoin && (image->next != (Image *) NULL))
1410           {
1411             (void) WriteBlobString(image,"%%BoundingBox: (atend)\n");
1412             (void) WriteBlobString(image,"%%HiResBoundingBox: (atend)\n");
1413             /* No document process colors if there's more than one page */
1414           }
1415         else
1416           {
1417             FormatString(buffer,"%%%%BoundingBox: %g %g %g %g\n",
1418               floor(bounds.x1+0.5),floor(bounds.y1+0.5),ceil(bounds.x2-0.5),
1419               ceil(bounds.y2-0.5));
1420             (void) WriteBlobString(image,buffer);
1421             FormatString(buffer,"%%%%HiResBoundingBox: %.7g %.7g %.7g %.7g\n",
1422               bounds.x1,bounds.y1,bounds.x2,bounds.y2);
1423             (void) WriteBlobString(image,buffer);
1424 
1425             if (image->colorspace == CMYKColorspace)
1426               (void) WriteBlobString(image,
1427                 "%%DocumentProcessColors: Cyan Magenta Yellow Black\n");
1428             else
1429               if (IsGrayImage(image,&image->exception))
1430                 (void) WriteBlobString(image,
1431                   "%%DocumentProcessColors: Black\n");
1432           }
1433 
1434         /* Font resources */
1435         attribute=GetImageAttribute(image,"label");
1436         if (attribute != (const ImageAttribute *) NULL)
1437           (void) WriteBlobString(image,
1438             "%%DocumentNeededResources: font Helvetica\n");
1439 
1440         /* Language level */
1441         (void) WriteBlobString(image,"%%LanguageLevel: 3\n");
1442 
1443         /* Pages, orientation, and page order */
1444         if (LocaleCompare(image_info->magick,"PS3") != 0)
1445           {
1446             (void) WriteBlobString(image,"%%Pages: 1\n");
1447           }
1448         else
1449           {
1450             (void) WriteBlobString(image,"%%Orientation: Portrait\n");
1451             (void) WriteBlobString(image,"%%PageOrder: Ascend\n");
1452             if (!image_info->adjoin)
1453               (void) strlcpy(buffer,"%%Pages: 1\n",sizeof(buffer));
1454             else
1455               FormatString(buffer,"%%%%Pages: %lu\n",(unsigned long)
1456                            image_list_length);
1457             (void) WriteBlobString(image,buffer);
1458           }
1459         (void) WriteBlobString(image,"%%EndComments\n");
1460 
1461         /* The static postscript procedures prolog. */
1462         (void)WriteBlobString(image,"%%BeginProlog\n");
1463         (void) WriteBlob(image,sizeof(PostscriptProlog)-1,PostscriptProlog);
1464 
1465         /* One label line for each line in label string */
1466         attribute=GetImageAttribute(image,"label");
1467         if (attribute != (const ImageAttribute *) NULL)
1468           {
1469             long
1470               si;
1471 
1472             (void) WriteBlobString(image,"\n  % LABELS\n  /Helvetica "
1473               "findfont pointsize scalefont setfont\n");
1474             for (si=(long) MultilineCensus(attribute->value)-1; si >= 0; si--)
1475               {
1476                 (void) WriteBlobString(image,
1477                   "  currentfile buffer readline pop token pop\n");
1478                 FormatString(buffer,"  0 y %g add moveto show pop\n",
1479                   si*image_info->pointsize+12);
1480                 (void) WriteBlobString(image,buffer);
1481               }
1482           }
1483 
1484         /* The static postscript procedures epilog. */
1485         (void) WriteBlob(image,sizeof(PostscriptEpilog)-1,PostscriptEpilog);
1486         (void)WriteBlobString(image,"%%EndProlog\n");
1487       }
1488 
1489     /* Page number */
1490     FormatString(buffer,"%%%%Page: 1 %lu\n",page);
1491     (void) WriteBlobString(image,buffer);
1492 
1493     /* Page bounding box */
1494     FormatString(buffer,"%%%%PageBoundingBox: %ld %ld %ld %ld\n",geometry.x,
1495       geometry.y,geometry.x+(long) geometry.width,geometry.y+(long)
1496       (geometry.height+text_size));
1497     (void) WriteBlobString(image,buffer);
1498 
1499     /* Page process colors if not RGB */
1500     if (image->colorspace == CMYKColorspace)
1501       (void) WriteBlobString(image,
1502         "%%PageProcessColors: Cyan Magenta Yellow Black\n");
1503     else
1504       if (IsGrayImage(image,&image->exception))
1505         (void) WriteBlobString(image,"%%PageProcessColors: Black\n");
1506 
1507     /*
1508       Adjust document bounding box to bound page bounding
1509       box if page bounding box is the larger
1510     */
1511     if (geometry.x < bounds.x1)
1512       bounds.x1=geometry.x;
1513     if (geometry.y < bounds.y1)
1514       bounds.y1=geometry.y;
1515     if ((geometry.x+x_scale) > bounds.x2)
1516       bounds.x2=geometry.x+x_scale;
1517     if ((geometry.y+y_scale+text_size) > bounds.y2)
1518       bounds.y2=geometry.y+y_scale+text_size;
1519 
1520     /* Page font resource if there's a label */
1521     attribute=GetImageAttribute(image,"label");
1522     if (attribute != (const ImageAttribute *) NULL)
1523       (void) WriteBlobString(image,"%%PageResources: font Helvetica\n");
1524 
1525     /* PS clipping path from Photoshop clipping path */
1526     if ((clip_mask != (Image *) NULL) &&
1527       (LocaleNCompare("8BIM:",clip_mask->magick_filename,5) == 0))
1528       {
1529         const ImageAttribute
1530           *attribute;
1531 
1532         attribute=GetImageAttribute(image,clip_mask->magick_filename);
1533         if (attribute == (const ImageAttribute *) NULL)
1534           return(False);
1535         (void) WriteBlobString(image,attribute->value);
1536         (void) WriteBlobByte(image,'\n');
1537       }
1538     else
1539       {
1540         /* Reset definition in case previous image had a clipping path.*/
1541         (void)WriteBlobString(image,"/ClipImage {} def\n");
1542       }
1543 
1544     /* Push a dictionary for our own def's if this is an EPS */
1545     if (LocaleCompare(image_info->magick,"PS3") != 0)
1546       (void) WriteBlobString(image,"userdict begin\n");
1547 
1548     /* Image mask */
1549     if (image->matte)
1550       if (!WritePS3MaskImage(image_info, image))
1551       {
1552         CloseBlob(image);
1553         return(False);
1554       }
1555 
1556     /*
1557       Remember position of BeginData comment so we can update
1558       it when we know how many ascii85 encoded bytes we have
1559       in the blob.
1560     */
1561     start=TellBlob(image);
1562     if (start < 0)
1563       ThrowWriterException(BlobError,UnableToObtainOffset,image);
1564     FormatString(buffer,"%%%%BeginData:%13ld ASCII Bytes\n",0L);
1565     (void) WriteBlobString(image,buffer);
1566     stop=TellBlob(image);
1567     if (stop < 0)
1568       ThrowWriterException(BlobError,UnableToObtainOffset,image);
1569 
1570     /* Call to image display procedure */
1571     (void) WriteBlobString(image,"DisplayImage\n");
1572 
1573     /* Translate, scale, and font point size */
1574     FormatString(buffer,"%ld %ld\n%.7g %.7g\n%f\n",geometry.x,geometry.y,
1575       x_scale,y_scale,image_info->pointsize);
1576     (void) WriteBlobString(image,buffer);
1577 
1578     /* Output labels */
1579     labels=(char **) NULL;
1580     attribute=GetImageAttribute(image,"label");
1581     if (attribute != (const ImageAttribute *) NULL)
1582       labels=StringToList(attribute->value);
1583     if (labels != (char **) NULL)
1584       {
1585         for (i=0; labels[i] != (char *) NULL; i++)
1586           {
1587             Ascii85Initialize(image);
1588             (void) WriteBlobString(image,"<~");
1589             for (j=0; labels[i][j] != '\0'; j++)
1590               Ascii85Encode(image, (unsigned long)labels[i][j]);
1591             Ascii85Flush(image);
1592             MagickFreeMemory(labels[i]);
1593           }
1594         MagickFreeMemory(labels);
1595       }
1596 
1597     /* Photoshop clipping path active? */
1598     if ((clip_mask != (Image *) NULL) &&
1599         (LocaleNCompare("8BIM:",clip_mask->magick_filename,5) == 0))
1600       (void) WriteBlobString(image,"true\n");
1601     else
1602       (void) WriteBlobString(image,"false\n");
1603 
1604     /* Compression seems to take precedence over anyting */
1605     if (compression == FaxCompression)
1606       (void) SetImageType(image, BilevelType);
1607 
1608     /* Showpage for non-EPS. */
1609     (void) WriteBlobString(image,
1610       LocaleCompare(image_info->magick,"PS3") == 0 ? "true\n" : "false\n");
1611 
1612     /* Image columns and rows; color space */
1613     FormatString(buffer,"%lu %lu\n%.1024s\n",
1614       image->columns,image->rows,image->colorspace == CMYKColorspace ?
1615         PS3_CMYKColorspace : PS3_RGBColorspace);
1616     (void) WriteBlobString(image,buffer);
1617 
1618     /* Masked image? */
1619     (void) WriteBlobString(image,image->matte ? "true\n" : "false\n");
1620 
1621     /* Render with imagemask operator? */
1622     if (((AccessDefinition(image_info,"ps","imagemask")) != 0) &&
1623         IsMonochromeImage(image,&image->exception))
1624       (void) WriteBlobString(image,"true\n");
1625     else
1626       (void) WriteBlobString(image,"false\n");
1627 
1628     /*
1629       Output data in one of three ways:
1630       1) 1 bit and 8 bit single channel direct color image data.
1631       2) 3 and 4 channel direct color image data
1632       3) 8 bit color mapped image data
1633     */
1634 
1635     /*
1636       Output 1 bit and 8 bit single channel image data. IsGray and IsMono
1637       may return true for both direct class RGB and CMYK colors, so we need
1638       to test that these were not requested explicitly.
1639     */
1640     if ((image_info->type != TrueColorType) &&
1641          (image_info->type != TrueColorMatteType) &&
1642          (image_info->type != ColorSeparationType) &&
1643          (image_info->type != ColorSeparationMatteType) &&
1644          (image->colorspace != CMYKColorspace) &&
1645          (IsGrayImage(image,&image->exception) ||
1646           IsMonochromeImage(image,&image->exception)))
1647     {
1648       /* Image class */
1649       (void) WriteBlobString(image,PS3_PseudoClass"\n");
1650 
1651       /* Compression scheme - any */
1652       switch (compression)
1653       {
1654         case NoCompression:
1655         default:
1656           (void) WriteBlobString(image,PS3_NoCompression"\n");
1657           break;
1658         case FaxCompression:
1659           (void) WriteBlobString(image,PS3_FaxCompression"\n");
1660           break;
1661         case RLECompression:
1662           (void) WriteBlobString(image,PS3_RLECompression"\n");
1663           break;
1664         case LZWCompression:
1665           (void) WriteBlobString(image,PS3_LZWCompression"\n");
1666           break;
1667         case ZipCompression:
1668           (void) WriteBlobString(image,PS3_ZipCompression"\n");
1669           break;
1670         case JPEGCompression:
1671           (void) WriteBlobString(image,PS3_JPEGCompression"\n");
1672           break;
1673       }
1674 
1675       /* Number of colors -- 0 for single component non-color mapped data */
1676       (void) WriteBlobString(image,"0\n");
1677 
1678       /* 1 bit or 8 bit components? */
1679       FormatString(buffer,"%d\n",
1680         IsMonochromeImage(image,&image->exception) ? 1 : 8);
1681       (void) WriteBlobString(image,buffer);
1682 
1683       /* Image data. Always ASCII85 encoded. */
1684       if (compression == JPEGCompression)
1685         {
1686           status=JPEGEncodeImage(image_info,image);
1687         }
1688       else
1689         if (compression == FaxCompression)
1690           {
1691             if (LocaleCompare(CCITTParam,"0") == 0)
1692               status=HuffmanEncodeImage(image_info,image);
1693             else
1694               status=Huffman2DEncodeImage(image_info,image);
1695           }
1696         else
1697           {
1698             status=SerializeSingleChannelImage(image_info,image,&pixels,
1699               &length);
1700             if (!status)
1701               {
1702                 CloseBlob(image);
1703                 return(False);
1704               }
1705             Ascii85Initialize(image);
1706             switch (compression)
1707             {
1708               case NoCompression:
1709               default:
1710                 for (i=0; i < length; i++)
1711                   Ascii85Encode(image, (unsigned long)pixels[i]);
1712                 break;
1713               case RLECompression:
1714                 status=PackbitsEncode2Image(image,length,pixels,
1715                   Ascii85WriteByteHook,(void*)NULL);
1716                 break;
1717               case LZWCompression:
1718                 status=LZWEncode2Image(image,length,pixels,
1719                   Ascii85WriteByteHook,(void*)NULL);
1720                 break;
1721               case ZipCompression:
1722                 status=ZLIBEncode2Image(image,length,image_info->quality,
1723                   pixels,Ascii85WriteByteHook,(void*)NULL);
1724                 break;
1725             }
1726             Ascii85Flush(image);
1727             MagickFreeResourceLimitedMemory(pixels);
1728           }
1729     }
1730     else
1731       if ((image->storage_class == DirectClass) || (image->colors > 256) ||
1732           (compression == JPEGCompression))
1733         {
1734           /* Image class */
1735           (void) WriteBlobString(image,PS3_DirectClass"\n");
1736 
1737           /* Compression scheme - fax is only for bilevel images */
1738           switch (compression)
1739           {
1740             case NoCompression:
1741             default:
1742               (void) WriteBlobString(image,PS3_NoCompression"\n");
1743               break;
1744             case RLECompression:
1745               (void) WriteBlobString(image,PS3_RLECompression"\n");
1746               break;
1747             case LZWCompression:
1748               (void) WriteBlobString(image,PS3_LZWCompression"\n");
1749               break;
1750             case ZipCompression:
1751               (void) WriteBlobString(image,PS3_ZipCompression"\n");
1752               break;
1753             case JPEGCompression:
1754               (void) WriteBlobString(image,PS3_JPEGCompression"\n");
1755               break;
1756           }
1757 
1758           /* Image data. Always ASCII85 encoded. */
1759           if (compression == JPEGCompression)
1760             {
1761                 status=JPEGEncodeImage(image_info,image);
1762             }
1763           else
1764             {
1765               /* Stream based compressions */
1766               status=SerializeMultiChannelImage(image_info,image,&pixels,
1767                 &length);
1768               if (!status)
1769                 {
1770                   CloseBlob(image);
1771                   return(False);
1772                 }
1773               Ascii85Initialize(image);
1774               switch (compression)
1775               {
1776                 case NoCompression:
1777                 default:
1778                   for (i=0; i < length; i++)
1779                     Ascii85Encode(image, (unsigned long)pixels[i]);
1780                   status=True;
1781                   break;
1782                 case RLECompression:
1783                   status=PackbitsEncode2Image(image,length,pixels,
1784                     Ascii85WriteByteHook,(void*)NULL);
1785                   break;
1786                 case LZWCompression:
1787                   status=LZWEncode2Image(image,length,pixels,
1788                     Ascii85WriteByteHook,(void*)NULL);
1789                   break;
1790                 case ZipCompression:
1791                   status=ZLIBEncode2Image(image,length,image_info->quality,
1792                     pixels,Ascii85WriteByteHook,(void*)NULL);
1793                   break;
1794               }
1795               Ascii85Flush(image);
1796               MagickFreeResourceLimitedMemory(pixels);
1797             }
1798         }
1799       else
1800         {
1801           /*
1802             Color mapped images.
1803 
1804             Image class.
1805           */
1806           (void) WriteBlobString(image,PS3_PseudoClass"\n");
1807 
1808           /*
1809             Compression scheme - fax is only for bilevel images,
1810             JPEG for true color (single channel) images.
1811           */
1812           switch (compression)
1813           {
1814             case NoCompression:
1815             default:
1816               (void) WriteBlobString(image,PS3_NoCompression"\n");
1817               break;
1818             case RLECompression:
1819               (void) WriteBlobString(image,PS3_RLECompression"\n");
1820               break;
1821             case LZWCompression:
1822               (void) WriteBlobString(image,PS3_LZWCompression"\n");
1823               break;
1824             case ZipCompression:
1825               (void) WriteBlobString(image,PS3_ZipCompression"\n");
1826               break;
1827           }
1828 
1829           /* Number of colors in color map */
1830           FormatString(buffer,"%u\n",image->colors);
1831           (void) WriteBlobString(image,buffer);
1832 
1833           /* Color map - uncompressed, ascii85 encoded */
1834           Ascii85Initialize(image);
1835           for (i=0; i < image->colors; i++)
1836           {
1837             Ascii85Encode(image, (unsigned long)image->colormap[i].red);
1838             Ascii85Encode(image, (unsigned long)image->colormap[i].green);
1839             Ascii85Encode(image, (unsigned long)image->colormap[i].blue);
1840           }
1841           Ascii85Flush(image);
1842 
1843           status=SerializePseudoClassImage(image_info,image,&pixels,&length);
1844           if (!status)
1845             {
1846               CloseBlob(image);
1847               return(False);
1848             }
1849           Ascii85Initialize(image);
1850           switch (compression)
1851           {
1852             case NoCompression:
1853             default:
1854               for (i=0; i < length; i++)
1855                 Ascii85Encode(image, (unsigned long)pixels[i]);
1856               status=True;
1857               break;
1858             case RLECompression:
1859               status=PackbitsEncode2Image(image,length,pixels,
1860                 Ascii85WriteByteHook,(void *)NULL);
1861               break;
1862             case LZWCompression:
1863               status=LZWEncode2Image(image,length,pixels,Ascii85WriteByteHook,
1864                 (void *)NULL);
1865               break;
1866             case ZipCompression:
1867               status=ZLIBEncode2Image(image,length,image_info->quality,pixels,
1868                 Ascii85WriteByteHook,(void *)NULL);
1869               break;
1870           }
1871           Ascii85Flush(image);
1872           MagickFreeResourceLimitedMemory(pixels);
1873         }
1874 
1875     if (!status)
1876       {
1877         CloseBlob(image);
1878         return(False);
1879       }
1880 
1881     /* Update BeginData now that we know the data size */
1882     current=TellBlob(image);
1883     if (current < 0)
1884       ThrowWriterException(BlobError,UnableToObtainOffset,image);
1885     length=current-stop;
1886     stop=TellBlob(image);
1887     if (stop < 0)
1888       ThrowWriterException(BlobError,UnableToObtainOffset,image);
1889     if (SeekBlob(image,start,SEEK_SET) != start)
1890       ThrowWriterException(BlobError,UnableToSeekToOffset,image);
1891     FormatString(buffer,"%%%%BeginData:%13ld ASCII Bytes\n",(long) length);
1892     (void) WriteBlobString(image,buffer);
1893     if (SeekBlob(image,stop,SEEK_SET) != stop)
1894       ThrowWriterException(BlobError,UnableToSeekToOffset,image);
1895     (void) WriteBlobString(image,"%%EndData\n");
1896 
1897     /* End private dictionary if this is an EPS */
1898     if (LocaleCompare(image_info->magick,"PS3") != 0)
1899       (void) WriteBlobString(image,"end\n");
1900 
1901     (void) WriteBlobString(image,"%%PageTrailer\n");
1902     if (image->next == (Image *) NULL)
1903       break;
1904     image=SyncNextImageInList(image);
1905     status=MagickMonitorFormatted(scene,image_list_length,
1906                                   &image->exception,SaveImagesText,
1907                                   image->filename);
1908     if (status == False)
1909       break;
1910     scene++;
1911   } while (image_info->adjoin);
1912   if (image_info->adjoin)
1913     while (image->previous != (Image *) NULL)
1914       image=image->previous;
1915   (void) WriteBlobString(image,"%%Trailer\n");
1916   if (page > 1)
1917     {
1918       FormatString(buffer,"%%%%BoundingBox: %g %g %g %g\n",
1919         floor(bounds.x1+0.5),floor(bounds.y1+0.5),ceil(bounds.x2-0.5),
1920         ceil(bounds.y2-0.5));
1921       (void) WriteBlobString(image,buffer);
1922       FormatString(buffer,"%%%%HiResBoundingBox: %.7g %.7g %.7g %.7g\n",
1923         bounds.x1,bounds.y1,bounds.x2,bounds.y2);
1924       (void) WriteBlobString(image,buffer);
1925     }
1926   (void) WriteBlobString(image,"%%EOF\n");
1927   CloseBlob(image);
1928   return(True);
1929 }
1930 
1931 #if defined(HasZLIB)
1932 #include "zlib.h"
1933 /*
1934 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1935 %                                                                             %
1936 %                                                                             %
1937 %                                                                             %
1938 %   Z L I B E n c o d e 2 I m a g e                                           %
1939 %                                                                             %
1940 %                                                                             %
1941 %                                                                             %
1942 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1943 %
1944 %  Method ZLIBEncode2Image compresses an image via ZLIB-coding specific to
1945 %  Postscript Level II or Portable Document Format.  To ensure portability, the
1946 %  binary ZLIB bytes are encoded as ASCII base-85.
1947 %
1948 %  The format of the ZLIBEncode2Image method is:
1949 %
1950 %      unsigned int ZLIBEncode2Image(Image *image,const size_t length,
1951 %        const unsigned long quality,unsigned char *pixels,
1952 %        WriteByteHook write_byte,void *info)
1953 %
1954 %  A description of each parameter follows:
1955 %
1956 %    o image: The address of a structure of type Image;  returned from
1957 %      ReadImage.
1958 %
1959 %    o length:  A value that specifies the number of pixels to compress.
1960 %
1961 %    o quality: the compression level (0-100).
1962 %
1963 %    o pixels: The address of an unsigned array of characters containing the
1964 %      pixels to compress.
1965 %
1966 %    o write_byte: function (hook) to call for writing each byte of compressed
1967 %      data.
1968 %
1969 %    o info: information block to pass along when calling write_byte
1970 %      function.
1971 %
1972 %
1973 */
1974 
1975 static voidpf ZLIBAllocFunc(voidpf opaque, uInt items, uInt size) MAGICK_FUNC_MALLOC;
ZLIBAllocFunc(voidpf opaque,uInt items,uInt size)1976 static voidpf ZLIBAllocFunc(voidpf opaque, uInt items, uInt size)
1977 {
1978   ARG_NOT_USED(opaque);
1979   return MagickMallocCleared(MagickArraySize(items,size));
1980 }
ZLIBFreeFunc(voidpf opaque,voidpf address)1981 static void ZLIBFreeFunc(voidpf opaque, voidpf address)
1982 {
1983   ARG_NOT_USED(opaque);
1984   MagickFree(address);
1985 }
1986 
ZLIBEncode2Image(Image * image,const size_t length,const unsigned long quality,unsigned char * pixels,WriteByteHook write_byte,void * info)1987 static unsigned int ZLIBEncode2Image(Image *image,const size_t length,
1988   const unsigned long quality,unsigned char *pixels,WriteByteHook write_byte,
1989   void *info)
1990 {
1991   int
1992     status;
1993 
1994   register long
1995     i;
1996 
1997   unsigned char
1998     *compressed_pixels;
1999 
2000   unsigned long
2001     compressed_packets;
2002 
2003   z_stream
2004     stream;
2005 
2006   assert(image != (Image *) NULL);
2007   assert(image->signature == MagickSignature);
2008   compressed_packets=(unsigned long) (1.001*length+12);
2009   compressed_pixels=MagickAllocateResourceLimitedMemory(unsigned char *,compressed_packets);
2010   if (compressed_pixels == (unsigned char *) NULL)
2011     ThrowBinaryException(ResourceLimitError,MemoryAllocationFailed,
2012       (char *) NULL);
2013   stream.next_in=pixels;
2014   stream.avail_in=(unsigned int) length;
2015   stream.next_out=compressed_pixels;
2016   stream.avail_out=(unsigned int) compressed_packets;
2017   stream.zalloc=ZLIBAllocFunc;
2018   stream.zfree=ZLIBFreeFunc;
2019   stream.opaque=(voidpf) NULL;
2020   status=deflateInit(&stream,(int) Min(quality/10,9));
2021   if (status == Z_OK)
2022     {
2023       status=deflate(&stream,Z_FINISH);
2024       if (status == Z_STREAM_END)
2025         status=deflateEnd(&stream);
2026       else
2027         (void) deflateEnd(&stream);
2028       compressed_packets=stream.total_out;
2029     }
2030   if (status)
2031     ThrowBinaryException(CoderError,UnableToZipCompressImage,(char *) NULL)
2032   else
2033     for (i=0; i < (long) compressed_packets; i++)
2034       (void) (*write_byte)(image,(magick_uint8_t)compressed_pixels[i],info);
2035   MagickFreeResourceLimitedMemory(compressed_pixels);
2036   return(!status);
2037 }
2038 #else
ZLIBEncode2Image(Image * image,const size_t length,const unsigned long quality,unsigned char * pixels,WriteByteHook write_byte,void * info)2039 static unsigned int ZLIBEncode2Image(Image *image,const size_t length,
2040   const unsigned long quality,unsigned char *pixels,WriteByteHook write_byte,
2041   void *info)
2042 {
2043   ARG_NOT_USED(length);
2044   ARG_NOT_USED(quality);
2045   ARG_NOT_USED(pixels);
2046   ARG_NOT_USED(write_byte);
2047   ARG_NOT_USED(info);
2048 
2049   assert(image != (Image *) NULL);
2050   assert(image->signature == MagickSignature);
2051   ThrowBinaryException(MissingDelegateError,ZipLibraryIsNotAvailable,image->filename);
2052   return(False);
2053 }
2054 #endif
2055