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