pcl.c revision 4dd7567a1a42170a7eb263b67a933bec7c431353
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-2014 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[MaxTextExtent],
155    density[MaxTextExtent],
156    filename[MaxTextExtent],
157    geometry[MaxTextExtent],
158    options[MaxTextExtent],
159    input_filename[MaxTextExtent];
160
161  const char
162    *option;
163
164  const DelegateInfo
165    *delegate_info;
166
167  Image
168    *image,
169    *next_image;
170
171  ImageInfo
172    *read_info;
173
174  MagickBooleanType
175    cmyk,
176    status;
177
178  PointInfo
179    delta;
180
181  RectangleInfo
182    bounding_box,
183    page;
184
185  register char
186    *p;
187
188  register ssize_t
189    c;
190
191  SegmentInfo
192    bounds;
193
194  size_t
195    height,
196    width;
197
198  ssize_t
199    count;
200
201  assert(image_info != (const ImageInfo *) NULL);
202  assert(image_info->signature == MagickSignature);
203  if (image_info->debug != MagickFalse)
204    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
205      image_info->filename);
206  assert(exception != (ExceptionInfo *) NULL);
207  assert(exception->signature == MagickSignature);
208  /*
209    Open image file.
210  */
211  image=AcquireImage(image_info,exception);
212  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
213  if (status == MagickFalse)
214    {
215      image=DestroyImageList(image);
216      return((Image *) NULL);
217    }
218  status=AcquireUniqueSymbolicLink(image_info->filename,input_filename);
219  if (status == MagickFalse)
220    {
221      ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
222        image_info->filename);
223      image=DestroyImageList(image);
224      return((Image *) NULL);
225    }
226  /*
227    Set the page density.
228  */
229  delta.x=DefaultResolution;
230  delta.y=DefaultResolution;
231  if ((image->resolution.x == 0.0) || (image->resolution.y == 0.0))
232    {
233      GeometryInfo
234        geometry_info;
235
236      MagickStatusType
237        flags;
238
239      flags=ParseGeometry(PSDensityGeometry,&geometry_info);
240      image->resolution.x=geometry_info.rho;
241      image->resolution.y=geometry_info.sigma;
242      if ((flags & SigmaValue) == 0)
243        image->resolution.y=image->resolution.x;
244    }
245  /*
246    Determine page geometry from the PCL media box.
247  */
248  cmyk=image->colorspace == CMYKColorspace ? MagickTrue : MagickFalse;
249  count=0;
250  (void) ResetMagickMemory(&bounding_box,0,sizeof(bounding_box));
251  (void) ResetMagickMemory(&bounds,0,sizeof(bounds));
252  (void) ResetMagickMemory(&page,0,sizeof(page));
253  (void) ResetMagickMemory(command,0,sizeof(command));
254  p=command;
255  for (c=ReadBlobByte(image); c != EOF; c=ReadBlobByte(image))
256  {
257    if (image_info->page != (char *) NULL)
258      continue;
259    /*
260      Note PCL elements.
261    */
262    *p++=(char) c;
263    if ((c != (int) '/') && (c != '\n') &&
264        ((size_t) (p-command) < (MaxTextExtent-1)))
265      continue;
266    *p='\0';
267    p=command;
268    /*
269      Is this a CMYK document?
270    */
271    if (LocaleNCompare(DeviceCMYK,command,strlen(DeviceCMYK)) == 0)
272      cmyk=MagickTrue;
273    if (LocaleNCompare(CropBox,command,strlen(CropBox)) == 0)
274      {
275        /*
276          Note region defined by crop box.
277        */
278        count=(ssize_t) sscanf(command,"CropBox [%lf %lf %lf %lf",
279          &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
280        if (count != 4)
281          count=(ssize_t) sscanf(command,"CropBox[%lf %lf %lf %lf",
282            &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
283      }
284    if (LocaleNCompare(MediaBox,command,strlen(MediaBox)) == 0)
285      {
286        /*
287          Note region defined by media box.
288        */
289        count=(ssize_t) sscanf(command,"MediaBox [%lf %lf %lf %lf",
290          &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
291        if (count != 4)
292          count=(ssize_t) sscanf(command,"MediaBox[%lf %lf %lf %lf",
293            &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
294      }
295    if (count != 4)
296      continue;
297    /*
298      Set PCL render geometry.
299    */
300    width=(size_t) floor(bounds.x2-bounds.x1+0.5);
301    height=(size_t) floor(bounds.y2-bounds.y1+0.5);
302    if (width > page.width)
303      page.width=width;
304    if (height > page.height)
305      page.height=height;
306  }
307  (void) CloseBlob(image);
308  /*
309    Render PCL with the GhostPCL delegate.
310  */
311  if ((page.width == 0) || (page.height == 0))
312    (void) ParseAbsoluteGeometry(PSPageGeometry,&page);
313  if (image_info->page != (char *) NULL)
314    (void) ParseAbsoluteGeometry(image_info->page,&page);
315  (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g",(double)
316    page.width,(double) page.height);
317  if (image_info->monochrome != MagickFalse)
318    delegate_info=GetDelegateInfo("pcl:mono",(char *) NULL,exception);
319  else
320     if (cmyk != MagickFalse)
321       delegate_info=GetDelegateInfo("pcl:cmyk",(char *) NULL,exception);
322     else
323       delegate_info=GetDelegateInfo("pcl:color",(char *) NULL,exception);
324  if (delegate_info == (const DelegateInfo *) NULL)
325    return((Image *) NULL);
326  *options='\0';
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  (void) FormatLocaleString(density,MaxTextExtent,"%gx%g",
332    image->resolution.x,image->resolution.y);
333  page.width=(size_t) floor(page.width*image->resolution.x/delta.x+0.5);
334  page.height=(size_t) floor(page.height*image->resolution.y/delta.y+
335    0.5);
336  (void) FormatLocaleString(options,MaxTextExtent,"-g%.20gx%.20g ",(double)
337     page.width,(double) page.height);
338  image=DestroyImage(image);
339  read_info=CloneImageInfo(image_info);
340  *read_info->magick='\0';
341  if (read_info->number_scenes != 0)
342    {
343      if (read_info->number_scenes != 1)
344        (void) FormatLocaleString(options,MaxTextExtent,"-dLastPage=%.20g",
345          (double) (read_info->scene+read_info->number_scenes));
346      else
347        (void) FormatLocaleString(options,MaxTextExtent,
348          "-dFirstPage=%.20g -dLastPage=%.20g",(double) read_info->scene+1,
349          (double) (read_info->scene+read_info->number_scenes));
350      read_info->number_scenes=0;
351      if (read_info->scenes != (char *) NULL)
352        *read_info->scenes='\0';
353    }
354  option=GetImageOption(image_info,"authenticate");
355  if (option != (const char *) NULL)
356    (void) FormatLocaleString(options+strlen(options),MaxTextExtent,
357      " -sPCLPassword=%s",option);
358  (void) CopyMagickString(filename,read_info->filename,MaxTextExtent);
359  (void) AcquireUniqueFilename(read_info->filename);
360  (void) FormatLocaleString(command,MaxTextExtent,
361    GetDelegateCommands(delegate_info),
362    read_info->antialias != MagickFalse ? 4 : 1,
363    read_info->antialias != MagickFalse ? 4 : 1,density,options,
364    read_info->filename,input_filename);
365  status=SystemCommand(MagickFalse,read_info->verbose,command,(char *) NULL,
366    exception) != 0 ? MagickTrue : MagickFalse;
367  image=ReadImage(read_info,exception);
368  (void) RelinquishUniqueFileResource(read_info->filename);
369  (void) RelinquishUniqueFileResource(input_filename);
370  read_info=DestroyImageInfo(read_info);
371  if (image == (Image *) NULL)
372    ThrowReaderException(DelegateError,"PCLDelegateFailed");
373  if (LocaleCompare(image->magick,"BMP") == 0)
374    {
375      Image
376        *cmyk_image;
377
378      cmyk_image=ConsolidateCMYKImages(image,exception);
379      if (cmyk_image != (Image *) NULL)
380        {
381          image=DestroyImageList(image);
382          image=cmyk_image;
383        }
384    }
385  do
386  {
387    (void) CopyMagickString(image->filename,filename,MaxTextExtent);
388    image->page=page;
389    next_image=SyncNextImageInList(image);
390    if (next_image != (Image *) NULL)
391      image=next_image;
392  } while (next_image != (Image *) NULL);
393  return(GetFirstImageInList(image));
394}
395
396/*
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*/
419ModuleExport size_t RegisterPCLImage(void)
420{
421  MagickInfo
422    *entry;
423
424  entry=SetMagickInfo("PCL");
425  entry->decoder=(DecodeImageHandler *) ReadPCLImage;
426  entry->encoder=(EncodeImageHandler *) WritePCLImage;
427  entry->magick=(IsImageFormatHandler *) IsPCL;
428  entry->blob_support=MagickFalse;
429  entry->seekable_stream=MagickTrue;
430  entry->thread_support=EncoderThreadSupport;
431  entry->description=ConstantString("Printer Control Language");
432  entry->module=ConstantString("PCL");
433  (void) RegisterMagickInfo(entry);
434  return(MagickImageCoderSignature);
435}
436
437/*
438%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
439%                                                                             %
440%                                                                             %
441%                                                                             %
442%   U n r e g i s t e r P C L I m a g e                                       %
443%                                                                             %
444%                                                                             %
445%                                                                             %
446%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
447%
448%  UnregisterPCLImage() removes format registrations made by the PCL module
449%  from the list of supported formats.
450%
451%  The format of the UnregisterPCLImage method is:
452%
453%      UnregisterPCLImage(void)
454%
455*/
456ModuleExport void UnregisterPCLImage(void)
457{
458  (void) UnregisterMagickInfo("PCL");
459}
460
461/*
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
490static 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
561static 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
662static MagickBooleanType WritePCLImage(const ImageInfo *image_info,Image *image,
663  ExceptionInfo *exception)
664{
665  char
666    buffer[MaxTextExtent];
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 == MagickSignature);
706  assert(image != (Image *) NULL);
707  assert(image->signature == MagickSignature);
708  if (image->debug != MagickFalse)
709    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
710  assert(exception != (ExceptionInfo *) NULL);
711  assert(exception->signature == MagickSignature);
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,MaxTextExtent,"\033*r%.20gs%.20gT",
735      (double) image->columns,(double) image->rows);
736    (void) WriteBlobString(image,buffer);
737    (void) FormatLocaleString(buffer,MaxTextExtent,"\033*t%.20gR",(double)
738      density);
739    (void) WriteBlobString(image,buffer);
740    (void) WriteBlobString(image,"\033&l0E");  /* top margin 0 */
741    if (IsImageMonochrome(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,MaxTextExtent,
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,MaxTextExtent,"\033*v%.20gI",
788              (double) i);
789            (void) WriteBlobString(image,buffer);
790          }
791        }
792    option=GetImageOption(image_info,"pcl:fit-to-page");
793    if (IfMagickTrue(IsStringTrue(option)))
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,MaxTextExtent,"\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,MaxTextExtent,"\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,MaxTextExtent,"\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,MaxTextExtent,"\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,MaxTextExtent,"\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,MaxTextExtent,"\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