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