Home | History | Annotate | Download | only in coders
      1 /*
      2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      3 %                                                                             %
      4 %                                                                             %
      5 %                                                                             %
      6 %                            PPPP    CCCC  L                                  %
      7 %                            P   P  C      L                                  %
      8 %                            PPPP   C      L                                  %
      9 %                            P      C      L                                  %
     10 %                            P       CCCC  LLLLL                              %
     11 %                                                                             %
     12 %                                                                             %
     13 %                      Read/Write HP PCL Printer Format                       %
     14 %                                                                             %
     15 %                              Software Design                                %
     16 %                                   Cristy                                    %
     17 %                                 July 1992                                   %
     18 %                                                                             %
     19 %                                                                             %
     20 %  Copyright 1999-2016 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 /*
     41   Include declarations.
     42 */
     43 #include "MagickCore/studio.h"
     44 #include "MagickCore/artifact.h"
     45 #include "MagickCore/attribute.h"
     46 #include "MagickCore/blob.h"
     47 #include "MagickCore/blob-private.h"
     48 #include "MagickCore/cache.h"
     49 #include "MagickCore/color.h"
     50 #include "MagickCore/color-private.h"
     51 #include "MagickCore/colorspace.h"
     52 #include "MagickCore/colorspace-private.h"
     53 #include "MagickCore/constitute.h"
     54 #include "MagickCore/delegate.h"
     55 #include "MagickCore/draw.h"
     56 #include "MagickCore/exception.h"
     57 #include "MagickCore/exception-private.h"
     58 #include "MagickCore/geometry.h"
     59 #include "MagickCore/image.h"
     60 #include "MagickCore/image-private.h"
     61 #include "MagickCore/list.h"
     62 #include "MagickCore/magick.h"
     63 #include "MagickCore/memory_.h"
     64 #include "MagickCore/monitor.h"
     65 #include "MagickCore/monitor-private.h"
     66 #include "MagickCore/option.h"
     67 #include "MagickCore/pixel-accessor.h"
     68 #include "MagickCore/profile.h"
     69 #include "MagickCore/property.h"
     70 #include "MagickCore/resource_.h"
     71 #include "MagickCore/quantum-private.h"
     72 #include "MagickCore/static.h"
     73 #include "MagickCore/string_.h"
     74 #include "MagickCore/module.h"
     75 #include "MagickCore/token.h"
     76 #include "MagickCore/transform.h"
     77 #include "MagickCore/utility.h"
     78 
     79 /*
     81   Forward declarations.
     82 */
     83 static MagickBooleanType
     84   WritePCLImage(const ImageInfo *,Image *,ExceptionInfo *);
     85 
     86 /*
     88 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     89 %                                                                             %
     90 %                                                                             %
     91 %                                                                             %
     92 %   I s P C L                                                                 %
     93 %                                                                             %
     94 %                                                                             %
     95 %                                                                             %
     96 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     97 %
     98 %  IsPCL() returns MagickTrue if the image format type, identified by the
     99 %  magick string, is PCL.
    100 %
    101 %  The format of the IsPCL method is:
    102 %
    103 %      MagickBooleanType IsPCL(const unsigned char *magick,const size_t length)
    104 %
    105 %  A description of each parameter follows:
    106 %
    107 %    o magick: compare image format pattern against these bytes.
    108 %
    109 %    o length: Specifies the length of the magick string.
    110 %
    111 */
    112 static MagickBooleanType IsPCL(const unsigned char *magick,const size_t length)
    113 {
    114   if (length < 4)
    115     return(MagickFalse);
    116   if (memcmp(magick,"\033E\033&",4) == 0)
    117     return(MagickFalse);
    118   if (memcmp(magick,"\033E\033",3) == 0)
    119     return(MagickTrue);
    120   return(MagickFalse);
    121 }
    122 
    123 /*
    125 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    126 %                                                                             %
    127 %                                                                             %
    128 %                                                                             %
    129 %   R e a d P C L I m a g e                                                   %
    130 %                                                                             %
    131 %                                                                             %
    132 %                                                                             %
    133 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    134 %
    135 %  ReadPCLImage() reads a Printer Control Language image file and returns it.
    136 %  It allocates the memory necessary for the new Image structure and returns a
    137 %  pointer to the new image.
    138 %
    139 %  The format of the ReadPCLImage method is:
    140 %
    141 %      Image *ReadPCLImage(const ImageInfo *image_info,ExceptionInfo *exception)
    142 %
    143 %  A description of each parameter follows:
    144 %
    145 %    o image_info: the image info.
    146 %
    147 %    o exception: return any errors or warnings in this structure.
    148 %
    149 */
    150 static Image *ReadPCLImage(const ImageInfo *image_info,ExceptionInfo *exception)
    151 {
    152 #define CropBox  "CropBox"
    153 #define DeviceCMYK  "DeviceCMYK"
    154 #define MediaBox  "MediaBox"
    155 #define RenderPCLText  "  Rendering PCL...  "
    156 
    157   char
    158     command[MagickPathExtent],
    159     *density,
    160     filename[MagickPathExtent],
    161     geometry[MagickPathExtent],
    162     *options,
    163     input_filename[MagickPathExtent];
    164 
    165   const DelegateInfo
    166     *delegate_info;
    167 
    168   Image
    169     *image,
    170     *next_image;
    171 
    172   ImageInfo
    173     *read_info;
    174 
    175   MagickBooleanType
    176     cmyk,
    177     status;
    178 
    179   PointInfo
    180     delta;
    181 
    182   RectangleInfo
    183     bounding_box,
    184     page;
    185 
    186   register char
    187     *p;
    188 
    189   register ssize_t
    190     c;
    191 
    192   SegmentInfo
    193     bounds;
    194 
    195   size_t
    196     height,
    197     width;
    198 
    199   ssize_t
    200     count;
    201 
    202   assert(image_info != (const ImageInfo *) NULL);
    203   assert(image_info->signature == MagickCoreSignature);
    204   if (image_info->debug != MagickFalse)
    205     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
    206       image_info->filename);
    207   assert(exception != (ExceptionInfo *) NULL);
    208   assert(exception->signature == MagickCoreSignature);
    209   /*
    210     Open image file.
    211   */
    212   image=AcquireImage(image_info,exception);
    213   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
    214   if (status == MagickFalse)
    215     {
    216       image=DestroyImageList(image);
    217       return((Image *) NULL);
    218     }
    219   status=AcquireUniqueSymbolicLink(image_info->filename,input_filename);
    220   if (status == MagickFalse)
    221     {
    222       ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
    223         image_info->filename);
    224       image=DestroyImageList(image);
    225       return((Image *) NULL);
    226     }
    227   /*
    228     Set the page density.
    229   */
    230   delta.x=DefaultResolution;
    231   delta.y=DefaultResolution;
    232   if ((image->resolution.x == 0.0) || (image->resolution.y == 0.0))
    233     {
    234       GeometryInfo
    235         geometry_info;
    236 
    237       MagickStatusType
    238         flags;
    239 
    240       flags=ParseGeometry(PSDensityGeometry,&geometry_info);
    241       image->resolution.x=geometry_info.rho;
    242       image->resolution.y=geometry_info.sigma;
    243       if ((flags & SigmaValue) == 0)
    244         image->resolution.y=image->resolution.x;
    245     }
    246   /*
    247     Determine page geometry from the PCL media box.
    248   */
    249   cmyk=image->colorspace == CMYKColorspace ? MagickTrue : MagickFalse;
    250   count=0;
    251   (void) ResetMagickMemory(&bounding_box,0,sizeof(bounding_box));
    252   (void) ResetMagickMemory(&bounds,0,sizeof(bounds));
    253   (void) ResetMagickMemory(&page,0,sizeof(page));
    254   (void) ResetMagickMemory(command,0,sizeof(command));
    255   p=command;
    256   for (c=ReadBlobByte(image); c != EOF; c=ReadBlobByte(image))
    257   {
    258     if (image_info->page != (char *) NULL)
    259       continue;
    260     /*
    261       Note PCL elements.
    262     */
    263     *p++=(char) c;
    264     if ((c != (int) '/') && (c != '\n') &&
    265         ((size_t) (p-command) < (MagickPathExtent-1)))
    266       continue;
    267     *p='\0';
    268     p=command;
    269     /*
    270       Is this a CMYK document?
    271     */
    272     if (LocaleNCompare(DeviceCMYK,command,strlen(DeviceCMYK)) == 0)
    273       cmyk=MagickTrue;
    274     if (LocaleNCompare(CropBox,command,strlen(CropBox)) == 0)
    275       {
    276         /*
    277           Note region defined by crop box.
    278         */
    279         count=(ssize_t) sscanf(command,"CropBox [%lf %lf %lf %lf",
    280           &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
    281         if (count != 4)
    282           count=(ssize_t) sscanf(command,"CropBox[%lf %lf %lf %lf",
    283             &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
    284       }
    285     if (LocaleNCompare(MediaBox,command,strlen(MediaBox)) == 0)
    286       {
    287         /*
    288           Note region defined by media box.
    289         */
    290         count=(ssize_t) sscanf(command,"MediaBox [%lf %lf %lf %lf",
    291           &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
    292         if (count != 4)
    293           count=(ssize_t) sscanf(command,"MediaBox[%lf %lf %lf %lf",
    294             &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
    295       }
    296     if (count != 4)
    297       continue;
    298     /*
    299       Set PCL render geometry.
    300     */
    301     width=(size_t) floor(bounds.x2-bounds.x1+0.5);
    302     height=(size_t) floor(bounds.y2-bounds.y1+0.5);
    303     if (width > page.width)
    304       page.width=width;
    305     if (height > page.height)
    306       page.height=height;
    307   }
    308   (void) CloseBlob(image);
    309   /*
    310     Render PCL with the GhostPCL delegate.
    311   */
    312   if ((page.width == 0) || (page.height == 0))
    313     (void) ParseAbsoluteGeometry(PSPageGeometry,&page);
    314   if (image_info->page != (char *) NULL)
    315     (void) ParseAbsoluteGeometry(image_info->page,&page);
    316   (void) FormatLocaleString(geometry,MagickPathExtent,"%.20gx%.20g",(double)
    317     page.width,(double) page.height);
    318   if (image_info->monochrome != MagickFalse)
    319     delegate_info=GetDelegateInfo("pcl:mono",(char *) NULL,exception);
    320   else
    321      if (cmyk != MagickFalse)
    322        delegate_info=GetDelegateInfo("pcl:cmyk",(char *) NULL,exception);
    323      else
    324        delegate_info=GetDelegateInfo("pcl:color",(char *) NULL,exception);
    325   if (delegate_info == (const DelegateInfo *) NULL)
    326     return((Image *) NULL);
    327   if ((page.width == 0) || (page.height == 0))
    328     (void) ParseAbsoluteGeometry(PSPageGeometry,&page);
    329   if (image_info->page != (char *) NULL)
    330     (void) ParseAbsoluteGeometry(image_info->page,&page);
    331   density=AcquireString("");
    332   options=AcquireString("");
    333   (void) FormatLocaleString(density,MagickPathExtent,"%gx%g",image->resolution.x,
    334     image->resolution.y);
    335   page.width=(size_t) floor(page.width*image->resolution.x/delta.x+0.5);
    336   page.height=(size_t) floor(page.height*image->resolution.y/delta.y+0.5);
    337   (void) FormatLocaleString(options,MagickPathExtent,"-g%.20gx%.20g ",(double)
    338      page.width,(double) page.height);
    339   image=DestroyImage(image);
    340   read_info=CloneImageInfo(image_info);
    341   *read_info->magick='\0';
    342   if (read_info->number_scenes != 0)
    343     {
    344       if (read_info->number_scenes != 1)
    345         (void) FormatLocaleString(options,MagickPathExtent,"-dLastPage=%.20g",
    346           (double) (read_info->scene+read_info->number_scenes));
    347       else
    348         (void) FormatLocaleString(options,MagickPathExtent,
    349           "-dFirstPage=%.20g -dLastPage=%.20g",(double) read_info->scene+1,
    350           (double) (read_info->scene+read_info->number_scenes));
    351       read_info->number_scenes=0;
    352       if (read_info->scenes != (char *) NULL)
    353         *read_info->scenes='\0';
    354     }
    355   (void) CopyMagickString(filename,read_info->filename,MagickPathExtent);
    356   (void) AcquireUniqueFilename(read_info->filename);
    357   (void) FormatLocaleString(command,MagickPathExtent,
    358     GetDelegateCommands(delegate_info),
    359     read_info->antialias != MagickFalse ? 4 : 1,
    360     read_info->antialias != MagickFalse ? 4 : 1,density,options,
    361     read_info->filename,input_filename);
    362   options=DestroyString(options);
    363   density=DestroyString(density);
    364   status=ExternalDelegateCommand(MagickFalse,read_info->verbose,command,
    365     (char *) NULL,exception) != 0 ? MagickTrue : MagickFalse;
    366   image=ReadImage(read_info,exception);
    367   (void) RelinquishUniqueFileResource(read_info->filename);
    368   (void) RelinquishUniqueFileResource(input_filename);
    369   read_info=DestroyImageInfo(read_info);
    370   if (image == (Image *) NULL)
    371     ThrowReaderException(DelegateError,"PCLDelegateFailed");
    372   if (LocaleCompare(image->magick,"BMP") == 0)
    373     {
    374       Image
    375         *cmyk_image;
    376 
    377       cmyk_image=ConsolidateCMYKImages(image,exception);
    378       if (cmyk_image != (Image *) NULL)
    379         {
    380           image=DestroyImageList(image);
    381           image=cmyk_image;
    382         }
    383     }
    384   do
    385   {
    386     (void) CopyMagickString(image->filename,filename,MagickPathExtent);
    387     image->page=page;
    388     next_image=SyncNextImageInList(image);
    389     if (next_image != (Image *) NULL)
    390       image=next_image;
    391   } while (next_image != (Image *) NULL);
    392   return(GetFirstImageInList(image));
    393 }
    394 
    395 /*
    397 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    398 %                                                                             %
    399 %                                                                             %
    400 %                                                                             %
    401 %   R e g i s t e r P C L I m a g e                                           %
    402 %                                                                             %
    403 %                                                                             %
    404 %                                                                             %
    405 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    406 %
    407 %  RegisterPCLImage() adds attributes for the PCL image format to
    408 %  the list of supported formats.  The attributes include the image format
    409 %  tag, a method to read and/or write the format, whether the format
    410 %  supports the saving of more than one frame to the i file or blob,
    411 %  whether the format supports native in-memory I/O, and a brief
    412 %  description of the format.
    413 %
    414 %  The format of the RegisterPCLImage method is:
    415 %
    416 %      size_t RegisterPCLImage(void)
    417 %
    418 */
    419 ModuleExport size_t RegisterPCLImage(void)
    420 {
    421   MagickInfo
    422     *entry;
    423 
    424   entry=AcquireMagickInfo("PCL","PCL","Printer Control Language");
    425   entry->decoder=(DecodeImageHandler *) ReadPCLImage;
    426   entry->encoder=(EncodeImageHandler *) WritePCLImage;
    427   entry->magick=(IsImageFormatHandler *) IsPCL;
    428   entry->flags^=CoderBlobSupportFlag;
    429   entry->flags^=CoderDecoderThreadSupportFlag;
    430   entry->flags|=CoderSeekableStreamFlag;
    431   (void) RegisterMagickInfo(entry);
    432   return(MagickImageCoderSignature);
    433 }
    434 
    435 /*
    437 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    438 %                                                                             %
    439 %                                                                             %
    440 %                                                                             %
    441 %   U n r e g i s t e r P C L I m a g e                                       %
    442 %                                                                             %
    443 %                                                                             %
    444 %                                                                             %
    445 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    446 %
    447 %  UnregisterPCLImage() removes format registrations made by the PCL module
    448 %  from the list of supported formats.
    449 %
    450 %  The format of the UnregisterPCLImage method is:
    451 %
    452 %      UnregisterPCLImage(void)
    453 %
    454 */
    455 ModuleExport void UnregisterPCLImage(void)
    456 {
    457   (void) UnregisterMagickInfo("PCL");
    458 }
    459 
    460 /*
    462 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    463 %                                                                             %
    464 %                                                                             %
    465 %                                                                             %
    466 %   W r i t e P C L I m a g e                                                 %
    467 %                                                                             %
    468 %                                                                             %
    469 %                                                                             %
    470 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    471 %
    472 %  WritePCLImage() writes an image in the Page Control Language encoded
    473 %  image format.
    474 %
    475 %  The format of the WritePCLImage method is:
    476 %
    477 %      MagickBooleanType WritePCLImage(const ImageInfo *image_info,
    478 %        Image *image,ExceptionInfo *exception)
    479 %
    480 %  A description of each parameter follows.
    481 %
    482 %    o image_info: the image info.
    483 %
    484 %    o image:  The image.
    485 %
    486 %    o exception: return any errors or warnings in this structure.
    487 %
    488 */
    489 
    490 static size_t PCLDeltaCompressImage(const size_t length,
    491   const unsigned char *previous_pixels,const unsigned char *pixels,
    492   unsigned char *compress_pixels)
    493 {
    494   int
    495     delta,
    496     j,
    497     replacement;
    498 
    499   register ssize_t
    500     i,
    501     x;
    502 
    503   register unsigned char
    504     *q;
    505 
    506   q=compress_pixels;
    507   for (x=0; x < (ssize_t) length; )
    508   {
    509     j=0;
    510     for (i=0; x < (ssize_t) length; x++)
    511     {
    512       if (*pixels++ != *previous_pixels++)
    513         {
    514           i=1;
    515           break;
    516         }
    517       j++;
    518     }
    519     while (x < (ssize_t) length)
    520     {
    521       x++;
    522       if (*pixels == *previous_pixels)
    523         break;
    524       i++;
    525       previous_pixels++;
    526       pixels++;
    527     }
    528     if (i == 0)
    529       break;
    530     replacement=j >= 31 ? 31 : j;
    531     j-=replacement;
    532     delta=i >= 8 ? 8 : i;
    533     *q++=(unsigned char) (((delta-1) << 5) | replacement);
    534     if (replacement == 31)
    535       {
    536         for (replacement=255; j != 0; )
    537         {
    538           if (replacement > j)
    539             replacement=j;
    540           *q++=(unsigned char) replacement;
    541           j-=replacement;
    542         }
    543         if (replacement == 255)
    544           *q++='\0';
    545       }
    546     for (pixels-=i; i != 0; )
    547     {
    548       for (i-=delta; delta != 0; delta--)
    549         *q++=(*pixels++);
    550       if (i == 0)
    551         break;
    552       delta=i;
    553       if (i >= 8)
    554         delta=8;
    555       *q++=(unsigned char) ((delta-1) << 5);
    556     }
    557   }
    558   return((size_t) (q-compress_pixels));
    559 }
    560 
    561 static size_t PCLPackbitsCompressImage(const size_t length,
    562   const unsigned char *pixels,unsigned char *compress_pixels)
    563 {
    564   int
    565     count;
    566 
    567   register ssize_t
    568     x;
    569 
    570   register unsigned char
    571     *q;
    572 
    573   ssize_t
    574     j;
    575 
    576   unsigned char
    577     packbits[128];
    578 
    579   /*
    580     Compress pixels with Packbits encoding.
    581   */
    582   q=compress_pixels;
    583   for (x=(ssize_t) length; x != 0; )
    584   {
    585     switch (x)
    586     {
    587       case 1:
    588       {
    589         x--;
    590         *q++=0;
    591         *q++=(*pixels);
    592         break;
    593       }
    594       case 2:
    595       {
    596         x-=2;
    597         *q++=1;
    598         *q++=(*pixels);
    599         *q++=pixels[1];
    600         break;
    601       }
    602       case 3:
    603       {
    604         x-=3;
    605         if ((*pixels == *(pixels+1)) && (*(pixels+1) == *(pixels+2)))
    606           {
    607             *q++=(unsigned char) ((256-3)+1);
    608             *q++=(*pixels);
    609             break;
    610           }
    611         *q++=2;
    612         *q++=(*pixels);
    613         *q++=pixels[1];
    614         *q++=pixels[2];
    615         break;
    616       }
    617       default:
    618       {
    619         if ((*pixels == *(pixels+1)) && (*(pixels+1) == *(pixels+2)))
    620           {
    621             /*
    622               Packed run.
    623             */
    624             count=3;
    625             while (((ssize_t) count < x) && (*pixels == *(pixels+count)))
    626             {
    627               count++;
    628               if (count >= 127)
    629                 break;
    630             }
    631             x-=count;
    632             *q++=(unsigned char) ((256-count)+1);
    633             *q++=(*pixels);
    634             pixels+=count;
    635             break;
    636           }
    637         /*
    638           Literal run.
    639         */
    640         count=0;
    641         while ((*(pixels+count) != *(pixels+count+1)) ||
    642                (*(pixels+count+1) != *(pixels+count+2)))
    643         {
    644           packbits[count+1]=pixels[count];
    645           count++;
    646           if (((ssize_t) count >= (x-3)) || (count >= 127))
    647             break;
    648         }
    649         x-=count;
    650         *packbits=(unsigned char) (count-1);
    651         for (j=0; j <= (ssize_t) count; j++)
    652           *q++=packbits[j];
    653         pixels+=count;
    654         break;
    655       }
    656     }
    657   }
    658   *q++=128; /* EOD marker */
    659   return((size_t) (q-compress_pixels));
    660 }
    661 
    662 static MagickBooleanType WritePCLImage(const ImageInfo *image_info,Image *image,
    663   ExceptionInfo *exception)
    664 {
    665   char
    666     buffer[MagickPathExtent];
    667 
    668   CompressionType
    669     compression;
    670 
    671   const char
    672     *option;
    673 
    674   MagickBooleanType
    675     status;
    676 
    677   MagickOffsetType
    678     scene;
    679 
    680   register const Quantum *p;
    681 
    682   register ssize_t i, x;
    683 
    684   register unsigned char *q;
    685 
    686   size_t
    687     density,
    688     length,
    689     one,
    690     packets;
    691 
    692   ssize_t
    693     y;
    694 
    695   unsigned char
    696     bits_per_pixel,
    697     *compress_pixels,
    698     *pixels,
    699     *previous_pixels;
    700 
    701   /*
    702     Open output image file.
    703   */
    704   assert(image_info != (const ImageInfo *) NULL);
    705   assert(image_info->signature == MagickCoreSignature);
    706   assert(image != (Image *) NULL);
    707   assert(image->signature == MagickCoreSignature);
    708   if (image->debug != MagickFalse)
    709     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
    710   assert(exception != (ExceptionInfo *) NULL);
    711   assert(exception->signature == MagickCoreSignature);
    712   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
    713   if (status == MagickFalse)
    714     return(status);
    715   density=75;
    716   if (image_info->density != (char *) NULL)
    717     {
    718       GeometryInfo
    719         geometry;
    720 
    721       (void) ParseGeometry(image_info->density,&geometry);
    722       density=(size_t) geometry.rho;
    723     }
    724   scene=0;
    725   one=1;
    726   do
    727   {
    728     /*
    729       Initialize the printer.
    730     */
    731     (void) TransformImageColorspace(image,sRGBColorspace,exception);
    732     (void) WriteBlobString(image,"\033E");  /* printer reset */
    733     (void) WriteBlobString(image,"\033*r3F");  /* set presentation mode */
    734     (void) FormatLocaleString(buffer,MagickPathExtent,"\033*r%.20gs%.20gT",
    735       (double) image->columns,(double) image->rows);
    736     (void) WriteBlobString(image,buffer);
    737     (void) FormatLocaleString(buffer,MagickPathExtent,"\033*t%.20gR",(double)
    738       density);
    739     (void) WriteBlobString(image,buffer);
    740     (void) WriteBlobString(image,"\033&l0E");  /* top margin 0 */
    741     if (SetImageMonochrome(image,exception) != MagickFalse)
    742       {
    743         /*
    744           Monochrome image: use default printer monochrome setup.
    745         */
    746         bits_per_pixel=1;
    747       }
    748     else
    749       if (image->storage_class == DirectClass)
    750         {
    751           /*
    752             DirectClass image.
    753           */
    754           bits_per_pixel=24;
    755           (void) WriteBlobString(image,"\033*v6W"); /* set color mode */
    756           (void) WriteBlobByte(image,0); /* RGB */
    757           (void) WriteBlobByte(image,3); /* direct by pixel */
    758           (void) WriteBlobByte(image,0); /* bits per index (ignored) */
    759           (void) WriteBlobByte(image,8); /* bits per red component */
    760           (void) WriteBlobByte(image,8); /* bits per green component */
    761           (void) WriteBlobByte(image,8); /* bits per blue component */
    762         }
    763       else
    764         {
    765           /*
    766             Colormapped image.
    767           */
    768           bits_per_pixel=8;
    769           (void) WriteBlobString(image,"\033*v6W"); /* set color mode... */
    770           (void) WriteBlobByte(image,0); /* RGB */
    771           (void) WriteBlobByte(image,1); /* indexed by pixel */
    772           (void) WriteBlobByte(image,bits_per_pixel); /* bits per index */
    773           (void) WriteBlobByte(image,8); /* bits per red component */
    774           (void) WriteBlobByte(image,8); /* bits per green component */
    775           (void) WriteBlobByte(image,8); /* bits per blue component */
    776           for (i=0; i < (ssize_t) image->colors; i++)
    777           {
    778             (void) FormatLocaleString(buffer,MagickPathExtent,
    779               "\033*v%da%db%dc%.20gI",
    780               ScaleQuantumToChar(image->colormap[i].red),
    781               ScaleQuantumToChar(image->colormap[i].green),
    782               ScaleQuantumToChar(image->colormap[i].blue),(double) i);
    783             (void) WriteBlobString(image,buffer);
    784           }
    785           for (one=1; i < (ssize_t) (one << bits_per_pixel); i++)
    786           {
    787             (void) FormatLocaleString(buffer,MagickPathExtent,"\033*v%.20gI",
    788               (double) i);
    789             (void) WriteBlobString(image,buffer);
    790           }
    791         }
    792     option=GetImageOption(image_info,"pcl:fit-to-page");
    793     if (IsStringTrue(option) != MagickFalse)
    794       (void) WriteBlobString(image,"\033*r3A");
    795     else
    796       (void) WriteBlobString(image,"\033*r1A");  /* start raster graphics */
    797     (void) WriteBlobString(image,"\033*b0Y");  /* set y offset */
    798     length=(image->columns*bits_per_pixel+7)/8;
    799     pixels=(unsigned char *) AcquireQuantumMemory(length+1,sizeof(*pixels));
    800     if (pixels == (unsigned char *) NULL)
    801       ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
    802     (void) ResetMagickMemory(pixels,0,(length+1)*sizeof(*pixels));
    803     compress_pixels=(unsigned char *) NULL;
    804     previous_pixels=(unsigned char *) NULL;
    805 
    806     compression=UndefinedCompression;
    807     if (image_info->compression != UndefinedCompression)
    808       compression=image_info->compression;
    809     switch (compression)
    810     {
    811       case NoCompression:
    812       {
    813         (void) FormatLocaleString(buffer,MagickPathExtent,"\033*b0M");
    814         (void) WriteBlobString(image,buffer);
    815         break;
    816       }
    817       case RLECompression:
    818       {
    819         compress_pixels=(unsigned char *) AcquireQuantumMemory(length+256,
    820           sizeof(*compress_pixels));
    821         if (compress_pixels == (unsigned char *) NULL)
    822           {
    823             pixels=(unsigned char *) RelinquishMagickMemory(pixels);
    824             ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
    825           }
    826         (void) ResetMagickMemory(compress_pixels,0,(length+256)*
    827           sizeof(*compress_pixels));
    828         (void) FormatLocaleString(buffer,MagickPathExtent,"\033*b2M");
    829         (void) WriteBlobString(image,buffer);
    830         break;
    831       }
    832       default:
    833       {
    834         compress_pixels=(unsigned char *) AcquireQuantumMemory(3*length+256,
    835           sizeof(*compress_pixels));
    836         if (compress_pixels == (unsigned char *) NULL)
    837           {
    838             pixels=(unsigned char *) RelinquishMagickMemory(pixels);
    839             ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
    840           }
    841         (void) ResetMagickMemory(compress_pixels,0,(3*length+256)*
    842           sizeof(*compress_pixels));
    843         previous_pixels=(unsigned char *) AcquireQuantumMemory(length+1,
    844           sizeof(*previous_pixels));
    845         if (previous_pixels == (unsigned char *) NULL)
    846           {
    847             compress_pixels=(unsigned char *) RelinquishMagickMemory(
    848               compress_pixels);
    849             pixels=(unsigned char *) RelinquishMagickMemory(pixels);
    850             ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
    851           }
    852         (void) ResetMagickMemory(previous_pixels,0,(length+1)*
    853           sizeof(*previous_pixels));
    854         (void) FormatLocaleString(buffer,MagickPathExtent,"\033*b3M");
    855         (void) WriteBlobString(image,buffer);
    856         break;
    857       }
    858     }
    859     for (y=0; y < (ssize_t) image->rows; y++)
    860     {
    861       p=GetVirtualPixels(image,0,y,image->columns,1,exception);
    862       if (p == (const Quantum *) NULL)
    863         break;
    864       q=pixels;
    865       switch (bits_per_pixel)
    866       {
    867         case 1:
    868         {
    869           register unsigned char
    870             bit,
    871             byte;
    872 
    873           /*
    874             Monochrome image.
    875           */
    876           bit=0;
    877           byte=0;
    878           for (x=0; x < (ssize_t) image->columns; x++)
    879           {
    880             byte<<=1;
    881             if (GetPixelLuma(image,p) < (QuantumRange/2.0))
    882               byte|=0x01;
    883             bit++;
    884             if (bit == 8)
    885               {
    886                 *q++=byte;
    887                 bit=0;
    888                 byte=0;
    889               }
    890             p+=GetPixelChannels(image);
    891           }
    892           if (bit != 0)
    893             *q++=byte << (8-bit);
    894           break;
    895         }
    896         case 8:
    897         {
    898           /*
    899             Colormapped image.
    900           */
    901           for (x=0; x < (ssize_t) image->columns; x++)
    902           {
    903             *q++=(unsigned char) GetPixelIndex(image,p);
    904             p+=GetPixelChannels(image);
    905           }
    906           break;
    907         }
    908         case 24:
    909         case 32:
    910         {
    911           /*
    912             Truecolor image.
    913           */
    914           for (x=0; x < (ssize_t) image->columns; x++)
    915           {
    916             *q++=ScaleQuantumToChar(GetPixelRed(image,p));
    917             *q++=ScaleQuantumToChar(GetPixelGreen(image,p));
    918             *q++=ScaleQuantumToChar(GetPixelBlue(image,p));
    919             p+=GetPixelChannels(image);
    920           }
    921           break;
    922         }
    923       }
    924       switch (compression)
    925       {
    926         case NoCompression:
    927         {
    928           (void) FormatLocaleString(buffer,MagickPathExtent,"\033*b%.20gW",
    929             (double) length);
    930           (void) WriteBlobString(image,buffer);
    931           (void) WriteBlob(image,length,pixels);
    932           break;
    933         }
    934         case RLECompression:
    935         {
    936           packets=PCLPackbitsCompressImage(length,pixels,compress_pixels);
    937           (void) FormatLocaleString(buffer,MagickPathExtent,"\033*b%.20gW",
    938             (double) packets);
    939           (void) WriteBlobString(image,buffer);
    940           (void) WriteBlob(image,packets,compress_pixels);
    941           break;
    942         }
    943         default:
    944         {
    945           if (y == 0)
    946             for (i=0; i < (ssize_t) length; i++)
    947               previous_pixels[i]=(~pixels[i]);
    948           packets=PCLDeltaCompressImage(length,previous_pixels,pixels,
    949             compress_pixels);
    950           (void) FormatLocaleString(buffer,MagickPathExtent,"\033*b%.20gW",
    951             (double) packets);
    952           (void) WriteBlobString(image,buffer);
    953           (void) WriteBlob(image,packets,compress_pixels);
    954           (void) CopyMagickMemory(previous_pixels,pixels,length*
    955             sizeof(*pixels));
    956           break;
    957         }
    958       }
    959     }
    960     (void) WriteBlobString(image,"\033*rB");  /* end graphics */
    961     switch (compression)
    962     {
    963       case NoCompression:
    964         break;
    965       case RLECompression:
    966       {
    967         compress_pixels=(unsigned char *) RelinquishMagickMemory(
    968           compress_pixels);
    969         break;
    970       }
    971       default:
    972       {
    973         previous_pixels=(unsigned char *) RelinquishMagickMemory(
    974           previous_pixels);
    975         compress_pixels=(unsigned char *) RelinquishMagickMemory(
    976           compress_pixels);
    977         break;
    978       }
    979     }
    980     pixels=(unsigned char *) RelinquishMagickMemory(pixels);
    981     if (GetNextImageInList(image) == (Image *) NULL)
    982       break;
    983     image=SyncNextImageInList(image);
    984     status=SetImageProgress(image,SaveImagesTag,scene++,
    985       GetImageListLength(image));
    986     if (status == MagickFalse)
    987       break;
    988   } while (image_info->adjoin != MagickFalse);
    989   (void) WriteBlobString(image,"\033E");
    990   (void) CloseBlob(image);
    991   return(MagickTrue);
    992 }
    993