root / ImageMagick / trunk / coders / ps3.c

Revision 12035, 50.0 kB (checked in by cristy, 6 days ago)
Line 
1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3%                                                                             %
4%                                                                             %
5%                                                                             %
6%                            PPPP   SSSSS  33333                              %
7%                            P   P  SS        33                              %
8%                            PPPP    SSS    333                               %
9%                            P         SS     33                              %
10%                            P      SSSSS  33333                              %
11%                                                                             %
12%                                                                             %
13%                     Write Postscript Level III Format.                      %
14%                                                                             %
15%                              Software Design                                %
16%                                John Cristy                                  %
17%                              Lars Ruben Skyum                               %
18%                                 July 1992                                   %
19%                                                                             %
20%                                                                             %
21%  Copyright 1999-2008 ImageMagick Studio LLC, a non-profit organization      %
22%  dedicated to making software imaging solutions freely available.           %
23%                                                                             %
24%  You may not use this file except in compliance with the License.  You may  %
25%  obtain a copy of the License at                                            %
26%                                                                             %
27%    http://www.imagemagick.org/script/license.php                            %
28%                                                                             %
29%  Unless required by applicable law or agreed to in writing, software        %
30%  distributed under the License is distributed on an "AS IS" BASIS,          %
31%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
32%  See the License for the specific language governing permissions and        %
33%  limitations under the License.                                             %
34%                                                                             %
35%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36%
37%
38*/
39
40/*
41  Include declarations.
42*/
43#include "magick/studio.h"
44#include "magick/blob.h"
45#include "magick/blob-private.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/option.h"
62#include "magick/property.h"
63#include "magick/quantum-private.h"
64#include "magick/resource_.h"
65#include "magick/static.h"
66#include "magick/string_.h"
67#include "magick/module.h"
68#include "magick/token.h"
69#include "magick/utility.h"
70#include "magick/module.h"
71#if defined(MAGICKCORE_TIFF_DELEGATE)
72#define CCITTParam  "-1"
73#else
74#define CCITTParam  "0"
75#endif
76
77/*
78  Define declarations.
79*/
80#define PS3_NoCompression "0"
81#define PS3_FaxCompression "1"
82#define PS3_JPEGCompression "2"
83#define PS3_LZWCompression "3"
84#define PS3_RLECompression "4"
85#define PS3_ZipCompression "5"
86
87#define PS3_RGBColorspace "0"
88#define PS3_CMYKColorspace "1"
89
90#define PS3_DirectClass "0"
91#define PS3_PseudoClass "1"
92
93/*
94  Forward declarations.
95*/
96static MagickBooleanType
97  WritePS3Image(const ImageInfo *,Image *);
98
99/*
100%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
101%                                                                             %
102%                                                                             %
103%                                                                             %
104%   R e g i s t e r P S 3 I m a g e                                           %
105%                                                                             %
106%                                                                             %
107%                                                                             %
108%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
109%
110%  RegisterPS3Image() adds properties for the PS3 image format to the list of
111%  supported formats.  The properties include the image format tag, a method to
112%  read and/or write the format, whether the format supports the saving of more
113%  than one frame to the same file or blob, whether the format supports native
114%  in-memory I/O, and a brief description of the format.
115%
116%  The format of the RegisterPS3Image method is:
117%
118%      unsigned long RegisterPS3Image(void)
119%
120*/
121ModuleExport unsigned long RegisterPS3Image(void)
122{
123  MagickInfo
124    *entry;
125
126  entry=SetMagickInfo("EPS3");
127  entry->encoder=(EncodeImageHandler *) WritePS3Image;
128  entry->description=ConstantString("Level III Encapsulated PostScript");
129  entry->module=ConstantString("PS3");
130  (void) RegisterMagickInfo(entry);
131  entry=SetMagickInfo("PS3");
132  entry->encoder=(EncodeImageHandler *) WritePS3Image;
133  entry->description=ConstantString("Level III PostScript");
134  entry->module=ConstantString("PS3");
135  (void) RegisterMagickInfo(entry);
136  return(MagickImageCoderSignature);
137}
138
139/*
140%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
141%                                                                             %
142%                                                                             %
143%                                                                             %
144%   U n r e g i s t e r P S 3 I m a g e                                       %
145%                                                                             %
146%                                                                             %
147%                                                                             %
148%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
149%
150%  UnregisterPS3Image() removes format registrations made by the PS3 module
151%  from the list of supported formats.
152%
153%  The format of the UnregisterPS3Image method is:
154%
155%      UnregisterPS3Image(void)
156%
157*/
158ModuleExport void UnregisterPS3Image(void)
159{
160  (void) UnregisterMagickInfo("EPS3");
161  (void) UnregisterMagickInfo("PS3");
162}
163
164/*
165%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
166%                                                                             %
167%                                                                             %
168%                                                                             %
169%   W r i t e P S 3 I m a g e                                                 %
170%                                                                             %
171%                                                                             %
172%                                                                             %
173%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
174%
175%  WritePS3Image() translates an image to encapsulated Postscript Level III
176%  for printing.  If the supplied geometry is null, the image is centered on
177%  the Postscript page.  Otherwise, the image is positioned as specified by the
178%  geometry.
179%
180%  The format of the WritePS3Image method is:
181%
182%      MagickBooleanType WritePS3Image(const ImageInfo *image_info,Image *image)
183%
184%  A description of each parameter follows:
185%
186%    o image_info: Specifies a pointer to a ImageInfo structure.
187%
188%    o image: the image.
189%
190*/
191
192static MagickBooleanType SerializeImage(const ImageInfo *image_info,
193  Image *image,unsigned char **pixels,size_t *length)
194{
195  long
196    y;
197
198  MagickBooleanType
199    status;
200
201  register const IndexPacket
202    *indexes;
203
204  register const PixelPacket
205    *p;
206
207  register long
208    x;
209
210  register unsigned char
211    *q;
212
213  assert(image != (Image *) NULL);
214  assert(image->signature == MagickSignature);
215  if (image->debug != MagickFalse)
216    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
217  status=MagickTrue;
218  *length=(image->colorspace == CMYKColorspace ? 4 : 3)*
219    (size_t) image->columns*image->rows;
220  *pixels=(unsigned char *) AcquireQuantumMemory(*length,sizeof(**pixels));
221  if (*pixels == (unsigned char *) NULL)
222    ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
223  q=(*pixels);
224  for (y=0; y < (long) image->rows; y++)
225  {
226    p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception);
227    if (p == (const PixelPacket *) NULL)
228      break;
229    indexes=AcquireIndexes(image);
230    if (image->colorspace != CMYKColorspace)
231      for (x=0; x < (long) image->columns; x++)
232      {
233        *q++=ScaleQuantumToChar(p->red);
234        *q++=ScaleQuantumToChar(p->green);
235        *q++=ScaleQuantumToChar(p->blue);
236        p++;
237      }
238    else
239      for (x=0; x < (long) image->columns; x++)
240      {
241        *q++=ScaleQuantumToChar(p->red);
242        *q++=ScaleQuantumToChar(p->green);
243        *q++=ScaleQuantumToChar(p->blue);
244        *q++=ScaleQuantumToChar(indexes[x]);
245        p++;
246      }
247    if (image->previous == (Image *) NULL)
248      {
249        status=SetImageProgress(image,SaveImageTag,y,image->rows);
250        if (status == MagickFalse)
251          break;
252      }
253  }
254  if (status == MagickFalse)
255    *pixels=(unsigned char *) RelinquishMagickMemory(*pixels);
256  return(status);
257}
258
259static MagickBooleanType SerializeImageChannel(const ImageInfo *image_info,
260  Image *image,unsigned char **pixels,size_t *length)
261{
262  long
263    y;
264
265  MagickBooleanType
266    status;
267
268  register const PixelPacket
269    *p;
270
271  register long
272    x;
273
274  register unsigned char
275    *q;
276
277  unsigned char
278    code,
279    bit;
280
281  unsigned long
282    pack,
283    padded_columns;
284
285  assert(image != (Image *) NULL);
286  assert(image->signature == MagickSignature);
287  if (image->debug != MagickFalse)
288    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
289  status=MagickTrue;
290  pack=IsMonochromeImage(image,&image->exception) == MagickFalse ? 1UL : 8UL;
291  padded_columns=((image->columns+pack-1)/pack)*pack;
292  *length=(size_t) padded_columns*image->rows/pack;
293  *pixels=(unsigned char *) AcquireQuantumMemory(*length,sizeof(**pixels));
294  if (*pixels == (unsigned char *) NULL)
295    ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
296  q=(*pixels);
297  for (y=0; y < (long) image->rows; y++)
298  {
299    p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception);
300    if (p == (const PixelPacket *) NULL)
301      break;
302    if (pack == 1)
303      for (x=0; x < (long) image->columns; x++)
304      {
305        *q++=ScaleQuantumToChar(PixelIntensityToQuantum(p));
306        p++;
307      }
308    else
309      {
310        code='\0';
311        for (x=0; x < (long) padded_columns; x++)
312        {
313          bit=(unsigned char) 0x00;
314          if (x < (long) image->columns)
315            bit=(unsigned char) (PixelIntensityToQuantum(p) ==
316              (Quantum) TransparentOpacity ? 0x01 : 0x00);
317          code=(code << 1)+bit;
318          if (((x+1) % pack) == 0)
319            {
320              *q++=code;
321              code='\0';
322            }
323          p++;
324        }
325      }
326    status=SetImageProgress(image,SaveImageTag,y,image->rows);
327    if (status == MagickFalse)
328      break;
329  }
330  if (status == MagickFalse)
331    *pixels=(unsigned char *) RelinquishMagickMemory(*pixels);
332  return(status);
333}
334
335static MagickBooleanType SerializeImageIndexes(const ImageInfo *image_info,
336  Image *image,unsigned char **pixels,size_t *length)
337{
338  long
339    y;
340
341  MagickBooleanType
342    status;
343
344  register const IndexPacket
345    *indexes;
346
347  register const PixelPacket
348    *p;
349
350  register long
351    x;
352
353  register unsigned char
354    *q;
355
356  assert(image != (Image *) NULL);
357  assert(image->signature == MagickSignature);
358  if (image->debug != MagickFalse)
359    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
360  status=MagickTrue;
361  *length=(size_t) image->columns*image->rows;
362  *pixels=(unsigned char *) AcquireQuantumMemory(*length,sizeof(**pixels));
363  if (*pixels == (unsigned char *) NULL)
364    ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
365  q=(*pixels);
366  for (y=0; y < (long) image->rows; y++)
367  {
368    p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception);
369    if (p == (const PixelPacket *) NULL)
370      break;
371    indexes=AcquireIndexes(image);
372    for (x=0; x < (long) image->columns; x++)
373      *q++=(unsigned char) indexes[x];
374    if (image->previous == (Image *) NULL)
375      {
376        status=SetImageProgress(image,SaveImageTag,y,image->rows);
377        if (status == MagickFalse)
378          break;
379      }
380  }
381  if (status == MagickFalse)
382    *pixels=(unsigned char *) RelinquishMagickMemory(*pixels);
383  return(status);
384}
385
386static MagickBooleanType WritePS3MaskImage(const ImageInfo *image_info,
387  Image *image,const CompressionType compression)
388{
389  char
390    buffer[MaxTextExtent];
391
392  Image
393    *mask_image;
394
395  MagickBooleanType
396    status;
397
398  MagickOffsetType
399    offset,
400    start,
401    stop;
402
403  register long
404    i;
405
406  size_t
407    length;
408
409  unsigned char
410    *pixels;
411
412  assert(image_info != (ImageInfo *) NULL);
413  assert(image_info->signature == MagickSignature);
414  assert(image != (Image *) NULL);
415  assert(image->signature == MagickSignature);
416  if (image->debug != MagickFalse)
417    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
418  assert(image->matte != MagickFalse);
419  status=MagickTrue;
420  /*
421    Note BeginData DSC comment for update later.
422  */
423  start=TellBlob(image);
424  (void) FormatMagickString(buffer,MaxTextExtent,
425    "%%%%BeginData:%13ld %s Bytes\n",0L,
426    compression == NoCompression ? "ASCII" : "BINARY");
427  (void) WriteBlobString(image,buffer);
428  stop=TellBlob(image);
429  /*
430    Only lossless compressions for the mask.
431  */
432  switch (compression)
433  {
434    case NoCompression:
435    default:
436    {
437      (void) FormatMagickString(buffer,MaxTextExtent,
438        "currentfile %lu %lu "PS3_NoCompression" ByteStreamDecodeFilter\n",
439        image->columns,image->rows);
440      break;
441    }
442    case FaxCompression:
443    case Group4Compression:
444    {
445      (void) FormatMagickString(buffer,MaxTextExtent,
446        "currentfile %lu %lu "PS3_FaxCompression" ByteStreamDecodeFilter\n",
447        image->columns,image->rows);
448      break;
449    }
450    case JPEGCompression:
451    {
452      (void) FormatMagickString(buffer,MaxTextExtent,
453        "currentfile %lu %lu "PS3_JPEGCompression" ByteStreamDecodeFilter\n",
454        image->columns,image->rows);
455      break;
456    }
457    case LZWCompression:
458    {
459      (void) FormatMagickString(buffer,MaxTextExtent,
460        "currentfile %lu %lu "PS3_LZWCompression" ByteStreamDecodeFilter\n",
461        image->columns,image->rows);
462      break;
463    }
464    case RLECompression:
465    {
466      (void) FormatMagickString(buffer,MaxTextExtent,
467        "currentfile %lu %lu "PS3_RLECompression" ByteStreamDecodeFilter\n",
468        image->columns,image->rows);
469      break;
470    }
471    case ZipCompression:
472    {
473      (void) FormatMagickString(buffer,MaxTextExtent,
474        "currentfile %lu %lu "PS3_ZipCompression" ByteStreamDecodeFilter\n",
475        image->columns,image->rows);
476      break;
477    }
478  }
479  (void) WriteBlobString(image,buffer);
480  (void) WriteBlobString(image,"/ReusableStreamDecode filter\n");
481  mask_image=CloneImage(image,0,0,MagickTrue,&image->exception);
482  if (mask_image == (Image *) NULL)
483    ThrowWriterException(CoderError,image->exception.reason);
484  status=SeparateImageChannel(mask_image,OpacityChannel);
485  if (status == MagickFalse)
486    {
487      mask_image=DestroyImage(mask_image);
488      return(MagickFalse);
489    }
490  (void) SetImageType(mask_image,BilevelType);
491  (void) SetImageType(mask_image,PaletteType);
492  mask_image->matte=MagickFalse;
493  pixels=(unsigned char *) NULL;
494  length=0;
495  switch (compression)
496  {
497    case NoCompression:
498    {
499      status=SerializeImageChannel(image_info,mask_image,&pixels,&length);
500      if (status == MagickFalse)
501        break;
502      Ascii85Initialize(image);
503      for (i=0; i < (long) length; i++)
504        Ascii85Encode(image,pixels[i]);
505      Ascii85Flush(image);
506      pixels=(unsigned char *) RelinquishMagickMemory(pixels);
507      break;
508    }
509    case FaxCompression:
510    case Group4Compression:
511    default:
512    {
513      if ((compression == FaxCompression) ||
514          (LocaleCompare(CCITTParam,"0") == 0))
515        status=HuffmanEncodeImage(image_info,mask_image);
516      else
517        status=Huffman2DEncodeImage(image_info,mask_image);
518      break;
519    }
520    case JPEGCompression:
521    {
522      status=InjectImageBlob(image_info,mask_image,"jpeg");
523      break;
524    }
525    case LZWCompression:
526    {
527      status=SerializeImageChannel(image_info,mask_image,&pixels,&length);
528      if (status == MagickFalse)
529        break;
530      status=LZWEncodeImage(image,length,pixels);
531      pixels=(unsigned char *) RelinquishMagickMemory(pixels);
532      break;
533    }
534    case RLECompression:
535    {
536      status=SerializeImageChannel(image_info,mask_image,&pixels,&length);
537      if (status == MagickFalse)
538        break;
539      status=PackbitsEncodeImage(image,length,pixels);
540      pixels=(unsigned char *) RelinquishMagickMemory(pixels);
541      break;
542    }
543    case ZipCompression:
544    {
545      status=SerializeImageChannel(image_info,mask_image,&pixels,&length);
546      if (status == MagickFalse)
547        break;
548      status=ZLIBEncodeImage(image,length,pixels);
549      pixels=(unsigned char *) RelinquishMagickMemory(pixels);
550      break;
551    }
552  }
553  mask_image=DestroyImage(mask_image);
554  (void) WriteBlobByte(image,'\n');
555  length=(size_t) (TellBlob(image)-stop);
556  stop=TellBlob(image);
557  offset=SeekBlob(image,start,SEEK_SET);
558  if (offset < 0)
559    ThrowWriterException(CorruptImageError,"ImproperImageHeader");
560  (void) FormatMagickString(buffer,MaxTextExtent,
561    "%%%%BeginData:%13ld %s Bytes\n",(long) length,
562    compression == NoCompression ? "ASCII" : "BINARY");
563  (void) WriteBlobString(image,buffer);
564  offset=SeekBlob(image,stop,SEEK_SET);
565  if (offset < 0)
566    ThrowWriterException(CorruptImageError,"ImproperImageHeader");
567  (void) WriteBlobString(image,"%%EndData\n");
568  (void) WriteBlobString(image, "/mask_stream exch def\n");
569  return(status);
570}
571
572static MagickBooleanType WritePS3Image(const ImageInfo *image_info,Image *image)
573{
574  static const char
575    *PostscriptProlog[]=
576    {
577      "/ByteStreamDecodeFilter",
578      "{",
579      "  /z exch def",
580      "  /r exch def",
581      "  /c exch def",
582      "  z "PS3_NoCompression" eq { /ASCII85Decode filter } if",
583      "  z "PS3_FaxCompression" eq",
584      "  {",
585      "    <<",
586      "      /K "CCITTParam,
587      "      /Columns c",
588      "      /Rows r",
589      "    >>",
590      "    /CCITTFaxDecode filter",
591      "  } if",
592      "  z "PS3_JPEGCompression" eq { /DCTDecode filter } if",
593      "  z "PS3_LZWCompression" eq { /LZWDecode filter } if",
594      "  z "PS3_RLECompression" eq { /RunLengthDecode filter } if",
595      "  z "PS3_ZipCompression" eq { /FlateDecode filter } if",
596      "} bind def",
597      "",
598      "/DirectClassImageDict",
599      "{",
600      "  colorspace "PS3_RGBColorspace" eq",
601      "  {",
602      "    /DeviceRGB setcolorspace",
603      "    <<",
604      "      /ImageType 1",
605      "      /Width columns",
606      "      /Height rows",
607      "      /BitsPerComponent 8",
608      "      /DataSource pixel_stream",
609      "      /MultipleDataSources false",
610      "      /ImageMatrix [columns 0 0 rows neg 0 rows]",
611      "      /Decode [0 1 0 1 0 1]",
612      "    >>",
613      "  }",
614      "  {",
615      "    /DeviceCMYK setcolorspace",
616      "    <<",
617      "      /ImageType 1",
618      "      /Width columns",
619      "      /Height rows",
620      "      /BitsPerComponent 8",
621      "      /DataSource pixel_stream",
622      "      /MultipleDataSources false",
623      "      /ImageMatrix [columns 0 0 rows neg 0 rows]",
624      "      /Decode",
625      "        compression "PS3_JPEGCompression" eq",
626      "        { [1 0 1 0 1 0 1 0] }",
627      "        { [0 1 0 1 0 1 0 1] }",
628      "        ifelse",
629      "    >>",
630      "  }",
631      "  ifelse",
632      "} bind def",
633      "",
634      "/PseudoClassImageDict",
635      "{",
636      "  % Colors in colormap image.",
637      "  currentfile buffer readline pop",
638      "  token pop /colors exch def pop",
639      "  colors 0 eq",
640      "  {",
641      "    % Depth of grayscale image.",
642      "    currentfile buffer readline pop",
643      "    token pop /bits exch def pop",
644      "    /DeviceGray setcolorspace",
645      "    <<",
646      "      /ImageType 1",
647      "      /Width columns",
648      "      /Height rows",
649      "      /BitsPerComponent bits",
650      "      /Decode [0 1]",
651      "      /ImageMatrix [columns 0 0 rows neg 0 rows]",
652      "      /DataSource pixel_stream",
653      "    >>",
654      "  }",
655      "  {",
656      "    % RGB colormap.",
657      "    /colormap colors 3 mul string def",
658      "    compression "PS3_NoCompression" eq",
659      "    { currentfile /ASCII85Decode filter colormap readstring pop pop }",
660      "    { currentfile colormap readstring pop pop }",
661      "    ifelse",
662      "    [ /Indexed /DeviceRGB colors 1 sub colormap ] setcolorspace",
663      "    <<",
664      "      /ImageType 1",
665      "      /Width columns",
666      "      /Height rows",
667      "      /BitsPerComponent 8",
668      "      /Decode [0 255]",
669      "      /ImageMatrix [columns 0 0 rows neg 0 rows]",
670      "      /DataSource pixel_stream",
671      "    >>",
672      "  }",
673      "  ifelse",
674      "} bind def",
675      "",
676      "/NonMaskedImageDict",
677      "{",
678      "  class "PS3_PseudoClass" eq",
679      "  { PseudoClassImageDict }",
680      "  { DirectClassImageDict }",
681      "  ifelse",
682      "} bind def",
683      "",
684      "/MaskedImageDict",
685      "{",
686      "  <<",
687      "    /ImageType 3",
688      "    /InterleaveType 3",
689      "    /DataDict NonMaskedImageDict",
690      "    /MaskDict",
691      "    <<",
692      "      /ImageType 1",
693      "      /Width columns",
694      "      /Height rows",
695      "      /BitsPerComponent 1",
696      "      /DataSource mask_stream",
697      "      /MultipleDataSources false",
698      "      /ImageMatrix [ columns 0 0 rows neg 0 rows]",
699      "      /Decode [ 0 1 ]",
700      "    >>",
701      "  >>",
702      "} bind def",
703      "",
704      "/ClipImage",
705      "{} def",
706      "",
707      "/DisplayImage",
708      "{",
709      "  /buffer 512 string def",
710      "  % Translation.",
711      "  currentfile buffer readline pop",
712      "  token pop /x exch def",
713      "  token pop /y exch def pop",
714      "  x y translate",
715      "  % Image size and font size.",
716      "  currentfile buffer readline pop",
717      "  token pop /x exch def",
718      "  token pop /y exch def pop",
719      "  currentfile buffer readline pop",
720      "  token pop /pointsize exch def pop",
721      (char *) NULL
722    },
723    *PostscriptEpilog[]=
724    {
725      "  x y scale",
726      "  % Clipping path.",
727      "  currentfile buffer readline pop",
728      "  token pop /clipped exch def pop",
729      "  % Showpage.",
730      "  currentfile buffer readline pop",
731      "  token pop /sp exch def pop",
732      "  % Image pixel size.",
733      "  currentfile buffer readline pop",
734      "  token pop /columns exch def",
735      "  token pop /rows exch def pop",
736      "  % Colorspace (RGB/CMYK).",
737      "  currentfile buffer readline pop",
738      "  token pop /colorspace exch def pop",
739      "  % Transparency.",
740      "  currentfile buffer readline pop",
741      "  token pop /alpha exch def pop",
742      "  % Stencil mask?",
743      "  currentfile buffer readline pop",
744      "  token pop /stencil exch def pop",
745      "  % Image class (direct/pseudo).",
746      "  currentfile buffer readline pop",
747      "  token pop /class exch def pop",
748      "  % Compression type.",
749      "  currentfile buffer readline pop",
750      "  token pop /compression exch def pop",
751      "  % Clip and render.",
752      "  /pixel_stream currentfile columns rows compression ByteStreamDecodeFilter def",
753      "  clipped { ClipImage } if",
754      "  alpha stencil not and",
755      "  { MaskedImageDict mask_stream resetfile }",
756      "  { NonMaskedImageDict }",
757      "  ifelse",
758      "  stencil { 0 setgray imagemask } { image } ifelse",
759      "  grestore",
760      "  sp { showpage } if",
761      "} bind def",
762      (char *) NULL
763    };
764
765  char
766    buffer[MaxTextExtent],
767    date[MaxTextExtent],
768    **labels,
769    page_geometry[MaxTextExtent];
770
771  CompressionType
772    compression;
773
774  const char
775    *option,
776    **q,
777    *value;
778
779  double
780    pointsize;
781
782  GeometryInfo
783    geometry_info;
784
785  long
786    j;
787
788  MagickBooleanType
789    status;
790
791  MagickOffsetType
792    offset,
793    scene,
794    start,
795    stop;
796
797  MagickStatusType
798    flags;
799
800  PointInfo
801    delta,
802    resolution,
803    scale;
804
805  RectangleInfo
806    geometry,
807    media_info,
808    page_info;
809
810  register long
811    i;
812
813  SegmentInfo
814    bounds;
815
816  size_t
817    length;
818
819  time_t
820    timer;
821
822  unsigned char
823    *pixels;
824
825  unsigned long
826    page,
827    pixel,
828    text_size;
829
830  /*
831    Open output image file.
832  */
833  assert(image_info != (const ImageInfo *) NULL);
834  assert(image_info->signature == MagickSignature);
835  assert(image != (Image *) NULL);
836  assert(image->signature == MagickSignature);
837  if (image->debug != MagickFalse)
838    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
839  status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
840  if (status == MagickFalse)
841    return(MagickFalse);
842  compression=image_info->compression;
843  switch (compression)
844  {
845    case FaxCompression:
846    case Group4Compression:
847    {
848      if ((IsMonochromeImage(image,&image->exception) == MagickFalse) ||
849          (image->matte != MagickFalse))
850        compression=RLECompression;
851      break;
852    }
853#if !defined(MAGICKCORE_JPEG_DELEGATE)
854    case JPEGCompression:
855    {
856      compression=RLECompression;
857      (void) ThrowMagickException(&image->exception,GetMagickModule(),
858        MissingDelegateError,"DelegateLibrarySupportNotBuiltIn","`%s' (JPEG)",
859        image->filename);
860      break;
861    }
862#endif
863#if !defined(MAGICKCORE_ZLIB_DELEGATE)
864    case ZipCompression:
865    {
866      compression=RLECompression;
867      (void) ThrowMagickException(&image->exception,GetMagickModule(),
868        MissingDelegateError,"DelegateLibrarySupportNotBuiltIn","`%s' (ZLIB)",
869        image->filename);
870      break;
871    }
872#endif
873    default:
874      break;
875  }
876  (void) ResetMagickMemory(&bounds,0,sizeof(bounds));
877  page=0;
878  scene=0;
879  do
880  {
881    /*
882      Scale relative to dots-per-inch.
883    */
884    delta.x=DefaultResolution;
885    delta.y=DefaultResolution;
886    flags=ParseGeometry(PSDensityGeometry,&geometry_info);
887    resolution.x=geometry_info.rho;
888    resolution.y=geometry_info.sigma;
889    if ((flags & SigmaValue) == 0)
890      resolution.y=resolution.x;
891    if (image->x_resolution != 0.0)
892      resolution.x=image->x_resolution;
893    if (image->y_resolution != 0.0)
894      resolution.y=image->y_resolution;
895    if (image_info->density != (char *) NULL)
896      {
897        flags=ParseGeometry(image_info->density,&geometry_info);
898        resolution.x=geometry_info.rho;
899        resolution.y=geometry_info.sigma;
900        if ((flags & SigmaValue) == 0)
901          resolution.y=resolution.x;
902      }
903    if (image->units == PixelsPerCentimeterResolution)
904      {
905        resolution.x*=2.54;
906        resolution.y*=2.54;
907      }
908    SetGeometry(image,&geometry);
909    (void) FormatMagickString(page_geometry,MaxTextExtent,"%lux%lu",
910      image->columns,image->rows);
911    if (image_info->page != (char *) NULL)
912      (void) CopyMagickString(page_geometry,image_info->page,MaxTextExtent);
913    else
914      if ((image->page.width != 0) && (image->page.height != 0))
915        (void) FormatMagickString(page_geometry,MaxTextExtent,"%lux%lu%+ld%+ld",
916          image->page.width,image->page.height,image->page.x,image->page.y);
917      else
918        if ((image->gravity != UndefinedGravity) &&
919            (LocaleCompare(image_info->magick,"PS") == 0))
920          (void) CopyMagickString(page_geometry,PSPageGeometry,MaxTextExtent);
921    (void) ConcatenateMagickString(page_geometry,">",MaxTextExtent);
922    (void) ParseMetaGeometry(page_geometry,&geometry.x,&geometry.y,
923      &geometry.width,&geometry.height);
924    scale.x=(double) (geometry.width*delta.x)/resolution.x;
925    geometry.width=(unsigned long) (scale.x+0.5);
926    scale.y=(double) (geometry.height*delta.y)/resolution.y;
927    geometry.height=(unsigned long) (scale.y+0.5);
928    (void) ParseAbsoluteGeometry(page_geometry,&media_info);
929    (void) ParseGravityGeometry(image,page_geometry,&page_info);
930    if (image->gravity != UndefinedGravity)
931      {
932        geometry.x=(-page_info.x);
933        geometry.y=(long) (media_info.height+page_info.y-image->rows);
934      }
935    pointsize=12.0;
936    if (image_info->pointsize != 0.0)
937      pointsize=image_info->pointsize;
938    text_size=0;
939    value=GetImageProperty(image,"label");
940    if (value != (const char *) NULL)
941      text_size=(unsigned long) (MultilineCensus(value)*pointsize+12);
942    page++;
943    if (page == 1)
944      {
945        /*
946          Postscript header on the first page.
947        */
948        if (LocaleCompare(image_info->magick,"PS3") == 0)
949          (void) CopyMagickString(buffer,"%!PS-Adobe-3.0\n",MaxTextExtent);
950        else
951          (void) CopyMagickString(buffer,"%!PS-Adobe-3.0 EPSF-3.0\n",
952            MaxTextExtent);
953        (void) WriteBlobString(image,buffer);
954        (void) FormatMagickString(buffer,MaxTextExtent,
955          "%%%%Creator: ImageMagick %s\n",MagickLibVersionText);
956        (void) WriteBlobString(image,buffer);
957        (void) FormatMagickString(buffer,MaxTextExtent,"%%%%Title: %s\n",
958          image->filename);
959        (void) WriteBlobString(image,buffer);
960        timer=time((time_t *) NULL);
961        (void) localtime(&timer);
962        (void) CopyMagickString(date,ctime(&timer),MaxTextExtent);
963        date[strlen(date)-1]='\0';
964        (void) FormatMagickString(buffer,MaxTextExtent,
965          "%%%%CreationDate: %s\n",date);
966        (void) WriteBlobString(image,buffer);
967        bounds.x1=(double) geometry.x;
968        bounds.y1=(double) geometry.y;
969        bounds.x2=(double) geometry.x+scale.x;
970        bounds.y2=(double) geometry.y+scale.y+text_size;
971        if ((image_info->adjoin != MagickFalse) &&
972            (GetNextImageInList(image) != (Image *) NULL))
973          {
974            (void) WriteBlobString(image,"%%BoundingBox: (atend)\n");
975            (void) WriteBlobString(image,"%%HiResBoundingBox: (atend)\n");
976</