ps2.c revision 8071c47b0ca8f471cbd6175e0f2e45739983bf4e
1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3%                                                                             %
4%                                                                             %
5%                                                                             %
6%                            PPPP   SSSSS  22222                              %
7%                            P   P  SS        22                              %
8%                            PPPP    SSS    222                               %
9%                            P         SS  22                                 %
10%                            P      SSSSS  22222                              %
11%                                                                             %
12%                                                                             %
13%                      Write Postscript Level II Format                       %
14%                                                                             %
15%                              Software Design                                %
16%                                John Cristy                                  %
17%                                 July 1992                                   %
18%                                                                             %
19%                                                                             %
20%  Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization      %
21%  dedicated to making software imaging solutions freely available.           %
22%                                                                             %
23%  You may not use this file except in compliance with the License.  You may  %
24%  obtain a copy of the License at                                            %
25%                                                                             %
26%    http://www.imagemagick.org/script/license.php                            %
27%                                                                             %
28%  Unless required by applicable law or agreed to in writing, software        %
29%  distributed under the License is distributed on an "AS IS" BASIS,          %
30%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31%  See the License for the specific language governing permissions and        %
32%  limitations under the License.                                             %
33%                                                                             %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
40  Include declarations.
41*/
42#include "MagickCore/studio.h"
43#include "MagickCore/attribute.h"
44#include "MagickCore/blob.h"
45#include "MagickCore/blob-private.h"
46#include "MagickCore/cache.h"
47#include "MagickCore/color.h"
48#include "MagickCore/color-private.h"
49#include "MagickCore/compress.h"
50#include "MagickCore/constitute.h"
51#include "MagickCore/draw.h"
52#include "MagickCore/exception.h"
53#include "MagickCore/exception-private.h"
54#include "MagickCore/geometry.h"
55#include "MagickCore/image.h"
56#include "MagickCore/image-private.h"
57#include "MagickCore/list.h"
58#include "MagickCore/magick.h"
59#include "MagickCore/memory_.h"
60#include "MagickCore/monitor.h"
61#include "MagickCore/monitor-private.h"
62#include "MagickCore/monitor-private.h"
63#include "MagickCore/option.h"
64#include "MagickCore/pixel-accessor.h"
65#include "MagickCore/property.h"
66#include "MagickCore/quantum-private.h"
67#include "MagickCore/resource_.h"
68#include "MagickCore/static.h"
69#include "MagickCore/string_.h"
70#include "MagickCore/module.h"
71#include "MagickCore/utility.h"
72
73/*
74  Define declarations.
75*/
76#if defined(MAGICKCORE_TIFF_DELEGATE)
77#define CCITTParam  "-1"
78#else
79#define CCITTParam  "0"
80#endif
81
82/*
83  Forward declarations.
84*/
85static MagickBooleanType
86  WritePS2Image(const ImageInfo *,Image *,ExceptionInfo *);
87
88/*
89%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
90%                                                                             %
91%                                                                             %
92%                                                                             %
93%   R e g i s t e r P S 2 I m a g e                                           %
94%                                                                             %
95%                                                                             %
96%                                                                             %
97%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
98%
99%  RegisterPS2Image() adds properties for the PS2 image format to
100%  the list of supported formats.  The properties include the image format
101%  tag, a method to read and/or write the format, whether the format
102%  supports the saving of more than one frame to the same file or blob,
103%  whether the format supports native in-memory I/O, and a brief
104%  description of the format.
105%
106%  The format of the RegisterPS2Image method is:
107%
108%      size_t RegisterPS2Image(void)
109%
110*/
111ModuleExport size_t RegisterPS2Image(void)
112{
113  MagickInfo
114    *entry;
115
116  entry=SetMagickInfo("EPS2");
117  entry->encoder=(EncodeImageHandler *) WritePS2Image;
118  entry->adjoin=MagickFalse;
119  entry->seekable_stream=MagickTrue;
120  entry->description=ConstantString("Level II Encapsulated PostScript");
121  entry->module=ConstantString("PS2");
122  (void) RegisterMagickInfo(entry);
123  entry=SetMagickInfo("PS2");
124  entry->encoder=(EncodeImageHandler *) WritePS2Image;
125  entry->seekable_stream=MagickTrue;
126  entry->description=ConstantString("Level II PostScript");
127  entry->module=ConstantString("PS2");
128  (void) RegisterMagickInfo(entry);
129  return(MagickImageCoderSignature);
130}
131
132/*
133%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
134%                                                                             %
135%                                                                             %
136%                                                                             %
137%   U n r e g i s t e r P S 2 I m a g e                                       %
138%                                                                             %
139%                                                                             %
140%                                                                             %
141%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
142%
143%  UnregisterPS2Image() removes format registrations made by the
144%  PS2 module from the list of supported formats.
145%
146%  The format of the UnregisterPS2Image method is:
147%
148%      UnregisterPS2Image(void)
149%
150*/
151ModuleExport void UnregisterPS2Image(void)
152{
153  (void) UnregisterMagickInfo("EPS2");
154  (void) UnregisterMagickInfo("PS2");
155}
156
157/*
158%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
159%                                                                             %
160%                                                                             %
161%                                                                             %
162%   W r i t e P S 2 I m a g e                                                 %
163%                                                                             %
164%                                                                             %
165%                                                                             %
166%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
167%
168%  WritePS2Image translates an image to encapsulated Postscript
169%  Level II for printing.  If the supplied geometry is null, the image is
170%  centered on the Postscript page.  Otherwise, the image is positioned as
171%  specified by the geometry.
172%
173%  The format of the WritePS2Image method is:
174%
175%      MagickBooleanType WritePS2Image(const ImageInfo *image_info,
176%        Image *image,ExceptionInfo *exception)
177%
178%  A description of each parameter follows:
179%
180%    o image_info: the image info.
181%
182%    o image: the image.
183%
184%    o exception: return any errors or warnings in this structure.
185%
186*/
187
188static MagickBooleanType Huffman2DEncodeImage(const ImageInfo *image_info,
189  Image *image,Image *inject_image,ExceptionInfo *exception)
190{
191  Image
192    *group4_image;
193
194  ImageInfo
195    *write_info;
196
197  MagickBooleanType
198    status;
199
200  size_t
201    length;
202
203  unsigned char
204    *group4;
205
206  status=MagickTrue;
207  write_info=CloneImageInfo(image_info);
208  (void) CopyMagickString(write_info->filename,"GROUP4:",MaxTextExtent);
209  (void) CopyMagickString(write_info->magick,"GROUP4",MaxTextExtent);
210  group4_image=CloneImage(inject_image,0,0,MagickTrue,exception);
211  if (group4_image == (Image *) NULL)
212    return(MagickFalse);
213  group4=(unsigned char *) ImageToBlob(write_info,group4_image,&length,
214    exception);
215  group4_image=DestroyImage(group4_image);
216  if (group4 == (unsigned char *) NULL)
217    return(MagickFalse);
218  write_info=DestroyImageInfo(write_info);
219  if (WriteBlob(image,length,group4) != (ssize_t) length)
220    status=MagickFalse;
221  group4=(unsigned char *) RelinquishMagickMemory(group4);
222  return(status);
223}
224
225static MagickBooleanType WritePS2Image(const ImageInfo *image_info,Image *image,
226  ExceptionInfo *exception)
227{
228  static const char
229    *PostscriptProlog[]=
230    {
231      "%%%%BeginProlog",
232      "%%",
233      "%% Display a color image.  The image is displayed in color on",
234      "%% Postscript viewers or printers that support color, otherwise",
235      "%% it is displayed as grayscale.",
236      "%%",
237      "/DirectClassImage",
238      "{",
239      "  %%",
240      "  %% Display a DirectClass image.",
241      "  %%",
242      "  colorspace 0 eq",
243      "  {",
244      "    /DeviceRGB setcolorspace",
245      "    <<",
246      "      /ImageType 1",
247      "      /Width columns",
248      "      /Height rows",
249      "      /BitsPerComponent 8",
250      "      /Decode [0 1 0 1 0 1]",
251      "      /ImageMatrix [columns 0 0 rows neg 0 rows]",
252      "      compression 0 gt",
253      "      { /DataSource pixel_stream %s }",
254      "      { /DataSource pixel_stream %s } ifelse",
255      "    >> image",
256      "  }",
257      "  {",
258      "    /DeviceCMYK setcolorspace",
259      "    <<",
260      "      /ImageType 1",
261      "      /Width columns",
262      "      /Height rows",
263      "      /BitsPerComponent 8",
264      "      /Decode [1 0 1 0 1 0 1 0]",
265      "      /ImageMatrix [columns 0 0 rows neg 0 rows]",
266      "      compression 0 gt",
267      "      { /DataSource pixel_stream %s }",
268      "      { /DataSource pixel_stream %s } ifelse",
269      "    >> image",
270      "  } ifelse",
271      "} bind def",
272      "",
273      "/PseudoClassImage",
274      "{",
275      "  %%",
276      "  %% Display a PseudoClass image.",
277      "  %%",
278      "  %% Parameters:",
279      "  %%   colors: number of colors in the colormap.",
280      "  %%",
281      "  currentfile buffer readline pop",
282      "  token pop /colors exch def pop",
283      "  colors 0 eq",
284      "  {",
285      "    %%",
286      "    %% Image is grayscale.",
287      "    %%",
288      "    currentfile buffer readline pop",
289      "    token pop /bits exch def pop",
290      "    /DeviceGray setcolorspace",
291      "    <<",
292      "      /ImageType 1",
293      "      /Width columns",
294      "      /Height rows",
295      "      /BitsPerComponent bits",
296      "      /Decode [0 1]",
297      "      /ImageMatrix [columns 0 0 rows neg 0 rows]",
298      "      compression 0 gt",
299      "      { /DataSource pixel_stream %s }",
300      "      {",
301      "        /DataSource pixel_stream %s",
302      "        <<",
303      "           /K "CCITTParam,
304      "           /Columns columns",
305      "           /Rows rows",
306      "        >> /CCITTFaxDecode filter",
307      "      } ifelse",
308      "    >> image",
309      "  }",
310      "  {",
311      "    %%",
312      "    %% Parameters:",
313      "    %%   colormap: red, green, blue color packets.",
314      "    %%",
315      "    /colormap colors 3 mul string def",
316      "    currentfile colormap readhexstring pop pop",
317      "    currentfile buffer readline pop",
318      "    [ /Indexed /DeviceRGB colors 1 sub colormap ] setcolorspace",
319      "    <<",
320      "      /ImageType 1",
321      "      /Width columns",
322      "      /Height rows",
323      "      /BitsPerComponent 8",
324      "      /Decode [0 255]",
325      "      /ImageMatrix [columns 0 0 rows neg 0 rows]",
326      "      compression 0 gt",
327      "      { /DataSource pixel_stream %s }",
328      "      { /DataSource pixel_stream %s } ifelse",
329      "    >> image",
330      "  } ifelse",
331      "} bind def",
332      "",
333      "/DisplayImage",
334      "{",
335      "  %%",
336      "  %% Display a DirectClass or PseudoClass image.",
337      "  %%",
338      "  %% Parameters:",
339      "  %%   x & y translation.",
340      "  %%   x & y scale.",
341      "  %%   label pointsize.",
342      "  %%   image label.",
343      "  %%   image columns & rows.",
344      "  %%   class: 0-DirectClass or 1-PseudoClass.",
345      "  %%   colorspace: 0-RGB or 1-CMYK.",
346      "  %%   compression: 0-RLECompression or 1-NoCompression.",
347      "  %%   hex color packets.",
348      "  %%",
349      "  gsave",
350      "  /buffer 512 string def",
351      "  /pixel_stream currentfile def",
352      "",
353      "  currentfile buffer readline pop",
354      "  token pop /x exch def",
355      "  token pop /y exch def pop",
356      "  x y translate",
357      "  currentfile buffer readline pop",
358      "  token pop /x exch def",
359      "  token pop /y exch def pop",
360      "  currentfile buffer readline pop",
361      "  token pop /pointsize exch def pop",
362      "  /Helvetica findfont pointsize scalefont setfont",
363      (char *) NULL
364    },
365    *PostscriptEpilog[]=
366    {
367      "  x y scale",
368      "  currentfile buffer readline pop",
369      "  token pop /columns exch def",
370      "  token pop /rows exch def pop",
371      "  currentfile buffer readline pop",
372      "  token pop /class exch def pop",
373      "  currentfile buffer readline pop",
374      "  token pop /colorspace exch def pop",
375      "  currentfile buffer readline pop",
376      "  token pop /compression exch def pop",
377      "  class 0 gt { PseudoClassImage } { DirectClassImage } ifelse",
378      "  grestore",
379      (char *) NULL
380    };
381
382  char
383    buffer[MaxTextExtent],
384    date[MaxTextExtent],
385    page_geometry[MaxTextExtent],
386    **labels;
387
388  CompressionType
389    compression;
390
391  const char
392    **q,
393    *value;
394
395  double
396    pointsize;
397
398  GeometryInfo
399    geometry_info;
400
401  MagickOffsetType
402    scene,
403    start,
404    stop;
405
406  MagickBooleanType
407    progress,
408    status;
409
410  MagickOffsetType
411    offset;
412
413  MagickSizeType
414    number_pixels;
415
416  MagickStatusType
417    flags;
418
419  PointInfo
420    delta,
421    resolution,
422    scale;
423
424  RectangleInfo
425    geometry,
426    media_info,
427    page_info;
428
429  register const Quantum
430    *p;
431
432  register ssize_t
433    x;
434
435  register ssize_t
436    i;
437
438  SegmentInfo
439    bounds;
440
441  size_t
442    length,
443    page,
444    text_size;
445
446  ssize_t
447    j,
448    y;
449
450  time_t
451    timer;
452
453  unsigned char
454    *pixels;
455
456  /*
457    Open output image file.
458  */
459  assert(image_info != (const ImageInfo *) NULL);
460  assert(image_info->signature == MagickSignature);
461  assert(image != (Image *) NULL);
462  assert(image->signature == MagickSignature);
463  if (image->debug != MagickFalse)
464    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
465  assert(exception != (ExceptionInfo *) NULL);
466  assert(exception->signature == MagickSignature);
467  status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
468  if (status == MagickFalse)
469    return(status);
470  compression=image->compression;
471  if (image_info->compression != UndefinedCompression)
472    compression=image_info->compression;
473  switch (compression)
474  {
475#if !defined(MAGICKCORE_JPEG_DELEGATE)
476    case JPEGCompression:
477    {
478      compression=RLECompression;
479      (void) ThrowMagickException(exception,GetMagickModule(),
480        MissingDelegateError,"DelegateLibrarySupportNotBuiltIn","`%s' (JPEG)",
481        image->filename);
482      break;
483    }
484#endif
485    default:
486      break;
487  }
488  (void) ResetMagickMemory(&bounds,0,sizeof(bounds));
489  page=1;
490  scene=0;
491  do
492  {
493    /*
494      Scale relative to dots-per-inch.
495    */
496    delta.x=DefaultResolution;
497    delta.y=DefaultResolution;
498    resolution.x=image->resolution.x;
499    resolution.y=image->resolution.y;
500    if ((resolution.x == 0.0) || (resolution.y == 0.0))
501      {
502        flags=ParseGeometry(PSDensityGeometry,&geometry_info);
503        resolution.x=geometry_info.rho;
504        resolution.y=geometry_info.sigma;
505        if ((flags & SigmaValue) == 0)
506          resolution.y=resolution.x;
507      }
508    if (image_info->density != (char *) NULL)
509      {
510        flags=ParseGeometry(image_info->density,&geometry_info);
511        resolution.x=geometry_info.rho;
512        resolution.y=geometry_info.sigma;
513        if ((flags & SigmaValue) == 0)
514          resolution.y=resolution.x;
515      }
516    if (image->units == PixelsPerCentimeterResolution)
517      {
518        resolution.x=(size_t) (100.0*2.54*resolution.x+0.5)/100.0;
519        resolution.y=(size_t) (100.0*2.54*resolution.y+0.5)/100.0;
520      }
521    SetGeometry(image,&geometry);
522    (void) FormatLocaleString(page_geometry,MaxTextExtent,"%.20gx%.20g",
523      (double) image->columns,(double) image->rows);
524    if (image_info->page != (char *) NULL)
525      (void) CopyMagickString(page_geometry,image_info->page,MaxTextExtent);
526    else
527      if ((image->page.width != 0) && (image->page.height != 0))
528        (void) FormatLocaleString(page_geometry,MaxTextExtent,
529          "%.20gx%.20g%+.20g%+.20g",(double) image->page.width,(double)
530          image->page.height,(double) image->page.x,(double) image->page.y);
531      else
532        if ((image->gravity != UndefinedGravity) &&
533            (LocaleCompare(image_info->magick,"PS") == 0))
534          (void) CopyMagickString(page_geometry,PSPageGeometry,MaxTextExtent);
535    (void) ConcatenateMagickString(page_geometry,">",MaxTextExtent);
536    (void) ParseMetaGeometry(page_geometry,&geometry.x,&geometry.y,
537      &geometry.width,&geometry.height);
538    scale.x=(double) (geometry.width*delta.x)/resolution.x;
539    geometry.width=(size_t) floor(scale.x+0.5);
540    scale.y=(double) (geometry.height*delta.y)/resolution.y;
541    geometry.height=(size_t) floor(scale.y+0.5);
542    (void) ParseAbsoluteGeometry(page_geometry,&media_info);
543    (void) ParseGravityGeometry(image,page_geometry,&page_info,exception);
544    if (image->gravity != UndefinedGravity)
545      {
546        geometry.x=(-page_info.x);
547        geometry.y=(ssize_t) (media_info.height+page_info.y-image->rows);
548      }
549    pointsize=12.0;
550    if (image_info->pointsize != 0.0)
551      pointsize=image_info->pointsize;
552    text_size=0;
553    value=GetImageProperty(image,"label",exception);
554    if (value != (const char *) NULL)
555      text_size=(size_t) (MultilineCensus(value)*pointsize+12);
556    if (page == 1)
557      {
558        /*
559          Output Postscript header.
560        */
561        if (LocaleCompare(image_info->magick,"PS2") == 0)
562          (void) CopyMagickString(buffer,"%!PS-Adobe-3.0\n",MaxTextExtent);
563        else
564          (void) CopyMagickString(buffer,"%!PS-Adobe-3.0 EPSF-3.0\n",
565            MaxTextExtent);
566        (void) WriteBlobString(image,buffer);
567        (void) WriteBlobString(image,"%%Creator: (ImageMagick)\n");
568        (void) FormatLocaleString(buffer,MaxTextExtent,"%%%%Title: (%s)\n",
569          image->filename);
570        (void) WriteBlobString(image,buffer);
571        timer=time((time_t *) NULL);
572        (void) FormatMagickTime(timer,MaxTextExtent,date);
573        (void) FormatLocaleString(buffer,MaxTextExtent,
574          "%%%%CreationDate: (%s)\n",date);
575        (void) WriteBlobString(image,buffer);
576        bounds.x1=(double) geometry.x;
577        bounds.y1=(double) geometry.y;
578        bounds.x2=(double) geometry.x+geometry.width;
579        bounds.y2=(double) geometry.y+geometry.height+text_size;
580        if ((image_info->adjoin != MagickFalse) &&
581            (GetNextImageInList(image) != (Image *) NULL))
582          (void) CopyMagickString(buffer,"%%BoundingBox: (atend)\n",
583            MaxTextExtent);
584        else
585          {
586            (void) FormatLocaleString(buffer,MaxTextExtent,
587              "%%%%BoundingBox: %.20g %.20g %.20g %.20g\n",ceil(bounds.x1-0.5),
588              ceil(bounds.y1-0.5),floor(bounds.x2+0.5),floor(bounds.y2+0.5));
589            (void) WriteBlobString(image,buffer);
590            (void) FormatLocaleString(buffer,MaxTextExtent,
591              "%%%%HiResBoundingBox: %g %g %g %g\n",bounds.x1,
592              bounds.y1,bounds.x2,bounds.y2);
593          }
594        (void) WriteBlobString(image,buffer);
595        value=GetImageProperty(image,"label",exception);
596        if (value != (const char *) NULL)
597          (void) WriteBlobString(image,
598            "%%DocumentNeededResources: font Helvetica\n");
599        (void) WriteBlobString(image,"%%LanguageLevel: 2\n");
600        if (LocaleCompare(image_info->magick,"PS2") != 0)
601          (void) WriteBlobString(image,"%%Pages: 1\n");
602        else
603          {
604            (void) WriteBlobString(image,"%%Orientation: Portrait\n");
605            (void) WriteBlobString(image,"%%PageOrder: Ascend\n");
606            if (image_info->adjoin == MagickFalse)
607              (void) CopyMagickString(buffer,"%%Pages: 1\n",MaxTextExtent);
608            else
609              (void) FormatLocaleString(buffer,MaxTextExtent,
610                "%%%%Pages: %.20g\n",(double) GetImageListLength(image));
611            (void) WriteBlobString(image,buffer);
612          }
613        (void) WriteBlobString(image,"%%EndComments\n");
614        (void) WriteBlobString(image,"\n%%BeginDefaults\n");
615        (void) WriteBlobString(image,"%%EndDefaults\n\n");
616        /*
617          Output Postscript commands.
618        */
619        for (q=PostscriptProlog; *q; q++)
620        {
621          switch (compression)
622          {
623            case NoCompression:
624            {
625              (void) FormatLocaleString(buffer,MaxTextExtent,*q,
626                "/ASCII85Decode filter");
627              break;
628            }
629            case JPEGCompression:
630            {
631              (void) FormatLocaleString(buffer,MaxTextExtent,*q,
632                "/DCTDecode filter");
633              break;
634            }
635            case LZWCompression:
636            {
637              (void) FormatLocaleString(buffer,MaxTextExtent,*q,
638                "/LZWDecode filter");
639              break;
640            }
641            case FaxCompression:
642            case Group4Compression:
643            {
644              (void) FormatLocaleString(buffer,MaxTextExtent,*q," ");
645              break;
646            }
647            default:
648            {
649              (void) FormatLocaleString(buffer,MaxTextExtent,*q,
650                "/RunLengthDecode filter");
651              break;
652            }
653          }
654          (void) WriteBlobString(image,buffer);
655          (void) WriteBlobByte(image,'\n');
656        }
657        value=GetImageProperty(image,"label",exception);
658        if (value != (const char *) NULL)
659          for (j=(ssize_t) MultilineCensus(value)-1; j >= 0; j--)
660          {
661            (void) WriteBlobString(image,"  /label 512 string def\n");
662            (void) WriteBlobString(image,"  currentfile label readline pop\n");
663            (void) FormatLocaleString(buffer,MaxTextExtent,
664              "  0 y %g add moveto label show pop\n",j*pointsize+12);
665            (void) WriteBlobString(image,buffer);
666          }
667        for (q=PostscriptEpilog; *q; q++)
668        {
669          (void) FormatLocaleString(buffer,MaxTextExtent,"%s\n",*q);
670          (void) WriteBlobString(image,buffer);
671        }
672        if (LocaleCompare(image_info->magick,"PS2") == 0)
673          (void) WriteBlobString(image,"  showpage\n");
674        (void) WriteBlobString(image,"} bind def\n");
675        (void) WriteBlobString(image,"%%EndProlog\n");
676      }
677    (void) FormatLocaleString(buffer,MaxTextExtent,"%%%%Page:  1 %.20g\n",
678      (double) page++);
679    (void) WriteBlobString(image,buffer);
680    (void) FormatLocaleString(buffer,MaxTextExtent,
681      "%%%%PageBoundingBox: %.20g %.20g %.20g %.20g\n",(double) geometry.x,
682      (double) geometry.y,geometry.x+(double) geometry.width,geometry.y+(double)
683      (geometry.height+text_size));
684    (void) WriteBlobString(image,buffer);
685    if ((double) geometry.x < bounds.x1)
686      bounds.x1=(double) geometry.x;
687    if ((double) geometry.y < bounds.y1)
688      bounds.y1=(double) geometry.y;
689    if ((double) (geometry.x+geometry.width-1) > bounds.x2)
690      bounds.x2=(double) geometry.x+geometry.width-1;
691    if ((double) (geometry.y+(geometry.height+text_size)-1) > bounds.y2)
692      bounds.y2=(double) geometry.y+(geometry.height+text_size)-1;
693    value=GetImageProperty(image,"label",exception);
694    if (value != (const char *) NULL)
695      (void) WriteBlobString(image,"%%PageResources: font Times-Roman\n");
696    if (LocaleCompare(image_info->magick,"PS2") != 0)
697      (void) WriteBlobString(image,"userdict begin\n");
698    start=TellBlob(image);
699    (void) FormatLocaleString(buffer,MaxTextExtent,
700      "%%%%BeginData:%13ld %s Bytes\n",0L,
701      compression == NoCompression ? "ASCII" : "Binary");
702    (void) WriteBlobString(image,buffer);
703    stop=TellBlob(image);
704    (void) WriteBlobString(image,"DisplayImage\n");
705    /*
706      Output image data.
707    */
708    (void) FormatLocaleString(buffer,MaxTextExtent,"%.20g %.20g\n%g %g\n%g\n",
709      (double) geometry.x,(double) geometry.y,scale.x,scale.y,pointsize);
710    (void) WriteBlobString(image,buffer);
711    labels=(char **) NULL;
712    value=GetImageProperty(image,"label",exception);
713    if (value != (const char *) NULL)
714      labels=StringToList(value);
715    if (labels != (char **) NULL)
716      {
717        for (i=0; labels[i] != (char *) NULL; i++)
718        {
719          (void) FormatLocaleString(buffer,MaxTextExtent,"%s \n",
720            labels[i]);
721          (void) WriteBlobString(image,buffer);
722          labels[i]=DestroyString(labels[i]);
723        }
724        labels=(char **) RelinquishMagickMemory(labels);
725      }
726    number_pixels=(MagickSizeType) image->columns*image->rows;
727    if (number_pixels != (MagickSizeType) ((size_t) number_pixels))
728      ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
729    if ((compression == FaxCompression) || (compression == Group4Compression) ||
730        ((image_info->type != TrueColorType) &&
731         (IsImageGray(image,exception) != MagickFalse)))
732      {
733        (void) FormatLocaleString(buffer,MaxTextExtent,"%.20g %.20g\n1\n%d\n",
734          (double) image->columns,(double) image->rows,(int)
735          (image->colorspace == CMYKColorspace));
736        (void) WriteBlobString(image,buffer);
737        (void) FormatLocaleString(buffer,MaxTextExtent,"%d\n",
738          (int) ((compression != FaxCompression) &&
739           (compression != Group4Compression)));
740        (void) WriteBlobString(image,buffer);
741        (void) WriteBlobString(image,"0\n");
742        (void) FormatLocaleString(buffer,MaxTextExtent,"%d\n",
743           (compression == FaxCompression) ||
744           (compression == Group4Compression) ? 1 : 8);
745        (void) WriteBlobString(image,buffer);
746        switch (compression)
747        {
748          case FaxCompression:
749          case Group4Compression:
750          {
751            if (LocaleCompare(CCITTParam,"0") == 0)
752              {
753                (void) HuffmanEncodeImage(image_info,image,image,exception);
754                break;
755              }
756            (void) Huffman2DEncodeImage(image_info,image,image,exception);
757            break;
758          }
759          case JPEGCompression:
760          {
761            status=InjectImageBlob(image_info,image,image,"jpeg",exception);
762            if (status == MagickFalse)
763              {
764                (void) CloseBlob(image);
765                return(MagickFalse);
766              }
767            break;
768          }
769          case RLECompression:
770          default:
771          {
772            register unsigned char
773              *q;
774
775            /*
776              Allocate pixel array.
777            */
778            length=(size_t) number_pixels;
779            pixels=(unsigned char *) AcquireQuantumMemory(length,
780              sizeof(*pixels));
781            if (pixels == (unsigned char *) NULL)
782              ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
783            /*
784              Dump Runlength encoded pixels.
785            */
786            q=pixels;
787            for (y=0; y < (ssize_t) image->rows; y++)
788            {
789              p=GetVirtualPixels(image,0,y,image->columns,1,exception);
790              if (p == (const Quantum *) NULL)
791                break;
792              for (x=0; x < (ssize_t) image->columns; x++)
793              {
794                *q++=ScaleQuantumToChar(GetPixelIntensity(image,p));
795                p+=GetPixelChannels(image);
796              }
797              progress=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
798                image->rows);
799              if (progress == MagickFalse)
800                break;
801            }
802            length=(size_t) (q-pixels);
803            if (compression == LZWCompression)
804              status=LZWEncodeImage(image,length,pixels,exception);
805            else
806              status=PackbitsEncodeImage(image,length,pixels,exception);
807            pixels=(unsigned char *) RelinquishMagickMemory(pixels);
808            if (status == MagickFalse)
809              {
810                (void) CloseBlob(image);
811                return(MagickFalse);
812              }
813            break;
814          }
815          case NoCompression:
816          {
817            /*
818              Dump uncompressed PseudoColor packets.
819            */
820            Ascii85Initialize(image);
821            for (y=0; y < (ssize_t) image->rows; y++)
822            {
823              p=GetVirtualPixels(image,0,y,image->columns,1,exception);
824              if (p == (const Quantum *) NULL)
825                break;
826              for (x=0; x < (ssize_t) image->columns; x++)
827              {
828                Ascii85Encode(image,ScaleQuantumToChar(
829                  GetPixelIntensity(image,p)));
830                p+=GetPixelChannels(image);
831              }
832              progress=SetImageProgress(image,SaveImageTag,(MagickOffsetType)
833                y,image->rows);
834              if (progress == MagickFalse)
835                break;
836            }
837            Ascii85Flush(image);
838            break;
839          }
840        }
841      }
842    else
843      if ((image->storage_class == DirectClass) || (image->colors > 256) ||
844          (compression == JPEGCompression) || (image->alpha_trait == BlendPixelTrait))
845        {
846          (void) FormatLocaleString(buffer,MaxTextExtent,"%.20g %.20g\n0\n%d\n",
847            (double) image->columns,(double) image->rows,(int)
848            (image->colorspace == CMYKColorspace));
849          (void) WriteBlobString(image,buffer);
850          (void) FormatLocaleString(buffer,MaxTextExtent,"%d\n",
851            (int) (compression == NoCompression));
852          (void) WriteBlobString(image,buffer);
853          switch (compression)
854          {
855            case JPEGCompression:
856            {
857              status=InjectImageBlob(image_info,image,image,"jpeg",exception);
858              if (status == MagickFalse)
859                {
860                  (void) CloseBlob(image);
861                  return(MagickFalse);
862                }
863              break;
864            }
865            case RLECompression:
866            default:
867            {
868              register unsigned char
869                *q;
870
871              /*
872                Allocate pixel array.
873              */
874              length=(size_t) number_pixels;
875              pixels=(unsigned char *) AcquireQuantumMemory(length,
876                4*sizeof(*pixels));
877              if (pixels == (unsigned char *) NULL)
878                ThrowWriterException(ResourceLimitError,
879                  "MemoryAllocationFailed");
880              /*
881                Dump Packbit encoded pixels.
882              */
883              q=pixels;
884              for (y=0; y < (ssize_t) image->rows; y++)
885              {
886                p=GetVirtualPixels(image,0,y,image->columns,1,exception);
887                if (p == (const Quantum *) NULL)
888                  break;
889                for (x=0; x < (ssize_t) image->columns; x++)
890                {
891                  if ((image->alpha_trait == BlendPixelTrait) &&
892                      (GetPixelAlpha(image,p) == (Quantum) TransparentAlpha))
893                    {
894                      *q++=ScaleQuantumToChar(QuantumRange);
895                      *q++=ScaleQuantumToChar(QuantumRange);
896                      *q++=ScaleQuantumToChar(QuantumRange);
897                    }
898                  else
899                    if (image->colorspace != CMYKColorspace)
900                      {
901                        *q++=ScaleQuantumToChar(GetPixelRed(image,p));
902                        *q++=ScaleQuantumToChar(GetPixelGreen(image,p));
903                        *q++=ScaleQuantumToChar(GetPixelBlue(image,p));
904                      }
905                    else
906                      {
907                        *q++=ScaleQuantumToChar(GetPixelRed(image,p));
908                        *q++=ScaleQuantumToChar(GetPixelGreen(image,p));
909                        *q++=ScaleQuantumToChar(GetPixelBlue(image,p));
910                        *q++=ScaleQuantumToChar(GetPixelBlack(image,p));
911                      }
912                  p+=GetPixelChannels(image);
913                }
914                progress=SetImageProgress(image,SaveImageTag,(MagickOffsetType)
915                  y,image->rows);
916                if (progress == MagickFalse)
917                  break;
918              }
919              length=(size_t) (q-pixels);
920              if (compression == LZWCompression)
921                status=LZWEncodeImage(image,length,pixels,exception);
922              else
923                status=PackbitsEncodeImage(image,length,pixels,exception);
924              if (status == MagickFalse)
925                {
926                  (void) CloseBlob(image);
927                  return(MagickFalse);
928                }
929              pixels=(unsigned char *) RelinquishMagickMemory(pixels);
930              break;
931            }
932            case NoCompression:
933            {
934              /*
935                Dump uncompressed DirectColor packets.
936              */
937              Ascii85Initialize(image);
938              for (y=0; y < (ssize_t) image->rows; y++)
939              {
940                p=GetVirtualPixels(image,0,y,image->columns,1,exception);
941                if (p == (const Quantum *) NULL)
942                  break;
943                for (x=0; x < (ssize_t) image->columns; x++)
944                {
945                  if ((image->alpha_trait == BlendPixelTrait) &&
946                      (GetPixelAlpha(image,p) == (Quantum) TransparentAlpha))
947                    {
948                      Ascii85Encode(image,ScaleQuantumToChar((Quantum)
949                        QuantumRange));
950                      Ascii85Encode(image,ScaleQuantumToChar((Quantum)
951                        QuantumRange));
952                      Ascii85Encode(image,ScaleQuantumToChar((Quantum)
953                        QuantumRange));
954                    }
955                  else
956                    if (image->colorspace != CMYKColorspace)
957                      {
958                        Ascii85Encode(image,ScaleQuantumToChar(
959                          GetPixelRed(image,p)));
960                        Ascii85Encode(image,ScaleQuantumToChar(
961                          GetPixelGreen(image,p)));
962                        Ascii85Encode(image,ScaleQuantumToChar(
963                          GetPixelBlue(image,p)));
964                      }
965                    else
966                      {
967                        Ascii85Encode(image,ScaleQuantumToChar(
968                          GetPixelRed(image,p)));
969                        Ascii85Encode(image,ScaleQuantumToChar(
970                          GetPixelGreen(image,p)));
971                        Ascii85Encode(image,ScaleQuantumToChar(
972                          GetPixelBlue(image,p)));
973                        Ascii85Encode(image,ScaleQuantumToChar(
974                          GetPixelBlack(image,p)));
975                      }
976                  p+=GetPixelChannels(image);
977                }
978                progress=SetImageProgress(image,SaveImageTag,(MagickOffsetType)
979                  y,image->rows);
980                if (progress == MagickFalse)
981                  break;
982              }
983              Ascii85Flush(image);
984              break;
985            }
986          }
987        }
988      else
989        {
990          /*
991            Dump number of colors and colormap.
992          */
993          (void) FormatLocaleString(buffer,MaxTextExtent,"%.20g %.20g\n1\n%d\n",
994            (double) image->columns,(double) image->rows,(int)
995            (image->colorspace == CMYKColorspace));
996          (void) WriteBlobString(image,buffer);
997          (void) FormatLocaleString(buffer,MaxTextExtent,"%d\n",
998            (int) (compression == NoCompression));
999          (void) WriteBlobString(image,buffer);
1000          (void) FormatLocaleString(buffer,MaxTextExtent,"%.20g\n",(double)
1001            image->colors);
1002          (void) WriteBlobString(image,buffer);
1003          for (i=0; i < (ssize_t) image->colors; i++)
1004          {
1005            (void) FormatLocaleString(buffer,MaxTextExtent,"%02X%02X%02X\n",
1006              ScaleQuantumToChar(image->colormap[i].red),
1007              ScaleQuantumToChar(image->colormap[i].green),
1008              ScaleQuantumToChar(image->colormap[i].blue));
1009            (void) WriteBlobString(image,buffer);
1010          }
1011          switch (compression)
1012          {
1013            case RLECompression:
1014            default:
1015            {
1016              register unsigned char
1017                *q;
1018
1019              /*
1020                Allocate pixel array.
1021              */
1022              length=(size_t) number_pixels;
1023              pixels=(unsigned char *) AcquireQuantumMemory(length,
1024                sizeof(*pixels));
1025              if (pixels == (unsigned char *) NULL)
1026                ThrowWriterException(ResourceLimitError,
1027                  "MemoryAllocationFailed");
1028              /*
1029                Dump Runlength encoded pixels.
1030              */
1031              q=pixels;
1032              for (y=0; y < (ssize_t) image->rows; y++)
1033              {
1034                p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1035                if (p == (const Quantum *) NULL)
1036                  break;
1037                for (x=0; x < (ssize_t) image->columns; x++)
1038                {
1039                  *q++=(unsigned char) GetPixelIndex(image,p);
1040                  p+=GetPixelChannels(image);
1041                }
1042                progress=SetImageProgress(image,SaveImageTag,(MagickOffsetType)
1043                  y,image->rows);
1044                if (progress == MagickFalse)
1045                  break;
1046              }
1047              length=(size_t) (q-pixels);
1048              if (compression == LZWCompression)
1049                status=LZWEncodeImage(image,length,pixels,exception);
1050              else
1051                status=PackbitsEncodeImage(image,length,pixels,exception);
1052              pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1053              if (status == MagickFalse)
1054                {
1055                  (void) CloseBlob(image);
1056                  return(MagickFalse);
1057                }
1058              break;
1059            }
1060            case NoCompression:
1061            {
1062              /*
1063                Dump uncompressed PseudoColor packets.
1064              */
1065              Ascii85Initialize(image);
1066              for (y=0; y < (ssize_t) image->rows; y++)
1067              {
1068                p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1069                if (p == (const Quantum *) NULL)
1070                  break;
1071                for (x=0; x < (ssize_t) image->columns; x++)
1072                {
1073                  Ascii85Encode(image,(unsigned char) GetPixelIndex(image,p));
1074                  p+=GetPixelChannels(image);
1075                }
1076                progress=SetImageProgress(image,SaveImageTag,(MagickOffsetType)
1077                  y,image->rows);
1078                if (progress == MagickFalse)
1079                  break;
1080              }
1081              Ascii85Flush(image);
1082              break;
1083            }
1084          }
1085        }
1086    (void) WriteBlobByte(image,'\n');
1087    length=(size_t) (TellBlob(image)-stop);
1088    stop=TellBlob(image);
1089    offset=SeekBlob(image,start,SEEK_SET);
1090    if (offset < 0)
1091      ThrowWriterException(CorruptImageError,"ImproperImageHeader");
1092    (void) FormatLocaleString(buffer,MaxTextExtent,
1093      "%%%%BeginData:%13ld %s Bytes\n",(long) length,
1094      compression == NoCompression ? "ASCII" : "Binary");
1095    (void) WriteBlobString(image,buffer);
1096    offset=SeekBlob(image,stop,SEEK_SET);
1097    (void) WriteBlobString(image,"%%EndData\n");
1098    if (LocaleCompare(image_info->magick,"PS2") != 0)
1099      (void) WriteBlobString(image,"end\n");
1100    (void) WriteBlobString(image,"%%PageTrailer\n");
1101    if (GetNextImageInList(image) == (Image *) NULL)
1102      break;
1103    image=SyncNextImageInList(image);
1104    status=SetImageProgress(image,SaveImagesTag,scene++,
1105      GetImageListLength(image));
1106    if (status == MagickFalse)
1107      break;
1108  } while (image_info->adjoin != MagickFalse);
1109  (void) WriteBlobString(image,"%%Trailer\n");
1110  if (page > 1)
1111    {
1112      (void) FormatLocaleString(buffer,MaxTextExtent,
1113        "%%%%BoundingBox: %.20g %.20g %.20g %.20g\n",ceil(bounds.x1-0.5),
1114        ceil(bounds.y1-0.5),floor(bounds.x2+0.5),floor(bounds.y2+0.5));
1115      (void) WriteBlobString(image,buffer);
1116      (void) FormatLocaleString(buffer,MaxTextExtent,
1117        "%%%%HiResBoundingBox: %g %g %g %g\n",bounds.x1,bounds.y1,
1118        bounds.x2,bounds.y2);
1119      (void) WriteBlobString(image,buffer);
1120    }
1121  (void) WriteBlobString(image,"%%EOF\n");
1122  (void) CloseBlob(image);
1123  return(MagickTrue);
1124}
1125