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-2021 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    https://imagemagick.org/script/license.php                               %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38 
39 /*
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 */
81 static 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 */
IsPCL(const unsigned char * magick,const size_t length)109 static 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 */
ReadPCLImage(const ImageInfo * image_info,ExceptionInfo * exception)146 static 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   char
183     *p;
184 
185   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) memset(&bounding_box,0,sizeof(bounding_box));
248   (void) memset(&bounds,0,sizeof(bounds));
249   (void) memset(&page,0,sizeof(page));
250   (void) memset(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     {
323       image=DestroyImage(image);
324       return((Image *) NULL);
325     }
326   if ((page.width == 0) || (page.height == 0))
327     (void) ParseAbsoluteGeometry(PSPageGeometry,&page);
328   if (image_info->page != (char *) NULL)
329     (void) ParseAbsoluteGeometry(image_info->page,&page);
330   density=AcquireString("");
331   options=AcquireString("");
332   (void) FormatLocaleString(density,MagickPathExtent,"%gx%g",
333     image->resolution.x,image->resolution.y);
334   if (image_info->ping != MagickFalse)
335     (void) FormatLocaleString(density,MagickPathExtent,"2.0x2.0");
336   page.width=(size_t) floor(page.width*image->resolution.x/delta.x+0.5);
337   page.height=(size_t) floor(page.height*image->resolution.y/delta.y+0.5);
338   (void) FormatLocaleString(options,MagickPathExtent,"-g%.20gx%.20g ",(double)
339     page.width,(double) page.height);
340   image=DestroyImage(image);
341   read_info=CloneImageInfo(image_info);
342   *read_info->magick='\0';
343   if (read_info->number_scenes != 0)
344     {
345       if (read_info->number_scenes != 1)
346         (void) FormatLocaleString(options,MagickPathExtent,"-dLastPage=%.20g",
347           (double) (read_info->scene+read_info->number_scenes));
348       else
349         (void) FormatLocaleString(options,MagickPathExtent,
350           "-dFirstPage=%.20g -dLastPage=%.20g",(double) read_info->scene+1,
351           (double) (read_info->scene+read_info->number_scenes));
352       read_info->number_scenes=0;
353       if (read_info->scenes != (char *) NULL)
354         *read_info->scenes='\0';
355     }
356   (void) CopyMagickString(filename,read_info->filename,MagickPathExtent);
357   (void) AcquireUniqueFilename(read_info->filename);
358   (void) FormatLocaleString(command,MagickPathExtent,
359     GetDelegateCommands(delegate_info),
360     read_info->antialias != MagickFalse ? 4 : 1,
361     read_info->antialias != MagickFalse ? 4 : 1,density,options,
362     read_info->filename,input_filename);
363   options=DestroyString(options);
364   density=DestroyString(density);
365   status=ExternalDelegateCommand(MagickFalse,read_info->verbose,command,
366     (char *) NULL,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,MagickPathExtent);
388     image->page=page;
389     if (image_info->ping != MagickFalse)
390       {
391         image->magick_columns*=image->resolution.x/2.0;
392         image->magick_rows*=image->resolution.y/2.0;
393         image->columns*=image->resolution.x/2.0;
394         image->rows*=image->resolution.y/2.0;
395       }
396     next_image=SyncNextImageInList(image);
397     if (next_image != (Image *) NULL)
398       image=next_image;
399   } while (next_image != (Image *) NULL);
400   return(GetFirstImageInList(image));
401 }
402 
403 /*
404 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
405 %                                                                             %
406 %                                                                             %
407 %                                                                             %
408 %   R e g i s t e r P C L I m a g e                                           %
409 %                                                                             %
410 %                                                                             %
411 %                                                                             %
412 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
413 %
414 %  RegisterPCLImage() adds attributes for the PCL image format to
415 %  the list of supported formats.  The attributes include the image format
416 %  tag, a method to read and/or write the format, whether the format
417 %  supports the saving of more than one frame to the i file or blob,
418 %  whether the format supports native in-memory I/O, and a brief
419 %  description of the format.
420 %
421 %  The format of the RegisterPCLImage method is:
422 %
423 %      size_t RegisterPCLImage(void)
424 %
425 */
RegisterPCLImage(void)426 ModuleExport size_t RegisterPCLImage(void)
427 {
428   MagickInfo
429     *entry;
430 
431   entry=AcquireMagickInfo("PCL","PCL","Printer Control Language");
432   entry->decoder=(DecodeImageHandler *) ReadPCLImage;
433   entry->encoder=(EncodeImageHandler *) WritePCLImage;
434   entry->magick=(IsImageFormatHandler *) IsPCL;
435   entry->flags^=CoderBlobSupportFlag;
436   entry->flags^=CoderDecoderThreadSupportFlag;
437   (void) RegisterMagickInfo(entry);
438   return(MagickImageCoderSignature);
439 }
440 
441 /*
442 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
443 %                                                                             %
444 %                                                                             %
445 %                                                                             %
446 %   U n r e g i s t e r P C L I m a g e                                       %
447 %                                                                             %
448 %                                                                             %
449 %                                                                             %
450 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
451 %
452 %  UnregisterPCLImage() removes format registrations made by the PCL module
453 %  from the list of supported formats.
454 %
455 %  The format of the UnregisterPCLImage method is:
456 %
457 %      UnregisterPCLImage(void)
458 %
459 */
UnregisterPCLImage(void)460 ModuleExport void UnregisterPCLImage(void)
461 {
462   (void) UnregisterMagickInfo("PCL");
463 }
464 
465 /*
466 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
467 %                                                                             %
468 %                                                                             %
469 %                                                                             %
470 %   W r i t e P C L I m a g e                                                 %
471 %                                                                             %
472 %                                                                             %
473 %                                                                             %
474 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
475 %
476 %  WritePCLImage() writes an image in the Page Control Language encoded
477 %  image format.
478 %
479 %  The format of the WritePCLImage method is:
480 %
481 %      MagickBooleanType WritePCLImage(const ImageInfo *image_info,
482 %        Image *image,ExceptionInfo *exception)
483 %
484 %  A description of each parameter follows.
485 %
486 %    o image_info: the image info.
487 %
488 %    o image:  The image.
489 %
490 %    o exception: return any errors or warnings in this structure.
491 %
492 */
493 
PCLDeltaCompressImage(const size_t length,const unsigned char * previous_pixels,const unsigned char * pixels,unsigned char * compress_pixels)494 static size_t PCLDeltaCompressImage(const size_t length,
495   const unsigned char *previous_pixels,const unsigned char *pixels,
496   unsigned char *compress_pixels)
497 {
498   int
499     delta,
500     j,
501     replacement;
502 
503   ssize_t
504     i,
505     x;
506 
507   unsigned char
508     *q;
509 
510   q=compress_pixels;
511   for (x=0; x < (ssize_t) length; )
512   {
513     j=0;
514     for (i=0; x < (ssize_t) length; x++)
515     {
516       if (*pixels++ != *previous_pixels++)
517         {
518           i=1;
519           break;
520         }
521       j++;
522     }
523     while (x < (ssize_t) length)
524     {
525       x++;
526       if (*pixels == *previous_pixels)
527         break;
528       i++;
529       previous_pixels++;
530       pixels++;
531     }
532     if (i == 0)
533       break;
534     replacement=j >= 31 ? 31 : j;
535     j-=replacement;
536     delta=i >= 8 ? 8 : i;
537     *q++=(unsigned char) (((delta-1) << 5) | replacement);
538     if (replacement == 31)
539       {
540         for (replacement=255; j != 0; )
541         {
542           if (replacement > j)
543             replacement=j;
544           *q++=(unsigned char) replacement;
545           j-=replacement;
546         }
547         if (replacement == 255)
548           *q++='\0';
549       }
550     for (pixels-=i; i != 0; )
551     {
552       for (i-=delta; delta != 0; delta--)
553         *q++=(*pixels++);
554       if (i == 0)
555         break;
556       delta=i;
557       if (i >= 8)
558         delta=8;
559       *q++=(unsigned char) ((delta-1) << 5);
560     }
561   }
562   return((size_t) (q-compress_pixels));
563 }
564 
PCLPackbitsCompressImage(const size_t length,const unsigned char * pixels,unsigned char * compress_pixels)565 static size_t PCLPackbitsCompressImage(const size_t length,
566   const unsigned char *pixels,unsigned char *compress_pixels)
567 {
568   int
569     count;
570 
571   ssize_t
572     x;
573 
574   unsigned char
575     *q;
576 
577   ssize_t
578     j;
579 
580   unsigned char
581     packbits[128];
582 
583   /*
584     Compress pixels with Packbits encoding.
585   */
586   q=compress_pixels;
587   for (x=(ssize_t) length; x != 0; )
588   {
589     switch (x)
590     {
591       case 1:
592       {
593         x--;
594         *q++=0;
595         *q++=(*pixels);
596         break;
597       }
598       case 2:
599       {
600         x-=2;
601         *q++=1;
602         *q++=(*pixels);
603         *q++=pixels[1];
604         break;
605       }
606       case 3:
607       {
608         x-=3;
609         if ((*pixels == *(pixels+1)) && (*(pixels+1) == *(pixels+2)))
610           {
611             *q++=(unsigned char) ((256-3)+1);
612             *q++=(*pixels);
613             break;
614           }
615         *q++=2;
616         *q++=(*pixels);
617         *q++=pixels[1];
618         *q++=pixels[2];
619         break;
620       }
621       default:
622       {
623         if ((*pixels == *(pixels+1)) && (*(pixels+1) == *(pixels+2)))
624           {
625             /*
626               Packed run.
627             */
628             count=3;
629             while (((ssize_t) count < x) && (*pixels == *(pixels+count)))
630             {
631               count++;
632               if (count >= 127)
633                 break;
634             }
635             x-=count;
636             *q++=(unsigned char) ((256-count)+1);
637             *q++=(*pixels);
638             pixels+=count;
639             break;
640           }
641         /*
642           Literal run.
643         */
644         count=0;
645         while ((*(pixels+count) != *(pixels+count+1)) ||
646                (*(pixels+count+1) != *(pixels+count+2)))
647         {
648           packbits[count+1]=pixels[count];
649           count++;
650           if (((ssize_t) count >= (x-3)) || (count >= 127))
651             break;
652         }
653         x-=count;
654         *packbits=(unsigned char) (count-1);
655         for (j=0; j <= (ssize_t) count; j++)
656           *q++=packbits[j];
657         pixels+=count;
658         break;
659       }
660     }
661   }
662   *q++=128; /* EOD marker */
663   return((size_t) (q-compress_pixels));
664 }
665 
WritePCLImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)666 static MagickBooleanType WritePCLImage(const ImageInfo *image_info,Image *image,
667   ExceptionInfo *exception)
668 {
669   char
670     buffer[MagickPathExtent];
671 
672   CompressionType
673     compression;
674 
675   const char
676     *option;
677 
678   MagickBooleanType
679     status;
680 
681   MagickOffsetType
682     scene;
683 
684   const Quantum *p;
685 
686   ssize_t i, x;
687 
688   unsigned char *q;
689 
690   size_t
691     density,
692     imageListLength,
693     length,
694     one,
695     packets;
696 
697   ssize_t
698     y;
699 
700   unsigned char
701     bits_per_pixel,
702     *compress_pixels,
703     *pixels,
704     *previous_pixels;
705 
706   /*
707     Open output image file.
708   */
709   assert(image_info != (const ImageInfo *) NULL);
710   assert(image_info->signature == MagickCoreSignature);
711   assert(image != (Image *) NULL);
712   assert(image->signature == MagickCoreSignature);
713   if (image->debug != MagickFalse)
714     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
715   assert(exception != (ExceptionInfo *) NULL);
716   assert(exception->signature == MagickCoreSignature);
717   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
718   if (status == MagickFalse)
719     return(status);
720   density=75;
721   if (image_info->density != (char *) NULL)
722     {
723       GeometryInfo
724         geometry;
725 
726       (void) ParseGeometry(image_info->density,&geometry);
727       density=(size_t) geometry.rho;
728     }
729   scene=0;
730   one=1;
731   imageListLength=GetImageListLength(image);
732   do
733   {
734     /*
735       Initialize the printer.
736     */
737     (void) TransformImageColorspace(image,sRGBColorspace,exception);
738     (void) WriteBlobString(image,"\033E");  /* printer reset */
739     (void) WriteBlobString(image,"\033*r3F");  /* set presentation mode */
740     (void) FormatLocaleString(buffer,MagickPathExtent,"\033*r%.20gs%.20gT",
741       (double) image->columns,(double) image->rows);
742     (void) WriteBlobString(image,buffer);
743     (void) FormatLocaleString(buffer,MagickPathExtent,"\033*t%.20gR",(double)
744       density);
745     (void) WriteBlobString(image,buffer);
746     (void) WriteBlobString(image,"\033&l0E");  /* top margin 0 */
747     if (SetImageMonochrome(image,exception) != MagickFalse)
748       {
749         /*
750           Monochrome image: use default printer monochrome setup.
751         */
752         bits_per_pixel=1;
753       }
754     else
755       if (image->storage_class == DirectClass)
756         {
757           /*
758             DirectClass image.
759           */
760           bits_per_pixel=24;
761           (void) WriteBlobString(image,"\033*v6W"); /* set color mode */
762           (void) WriteBlobByte(image,0); /* RGB */
763           (void) WriteBlobByte(image,3); /* direct by pixel */
764           (void) WriteBlobByte(image,0); /* bits per index (ignored) */
765           (void) WriteBlobByte(image,8); /* bits per red component */
766           (void) WriteBlobByte(image,8); /* bits per green component */
767           (void) WriteBlobByte(image,8); /* bits per blue component */
768         }
769       else
770         {
771           /*
772             Colormapped image.
773           */
774           bits_per_pixel=8;
775           (void) WriteBlobString(image,"\033*v6W"); /* set color mode... */
776           (void) WriteBlobByte(image,0); /* RGB */
777           (void) WriteBlobByte(image,1); /* indexed by pixel */
778           (void) WriteBlobByte(image,bits_per_pixel); /* bits per index */
779           (void) WriteBlobByte(image,8); /* bits per red component */
780           (void) WriteBlobByte(image,8); /* bits per green component */
781           (void) WriteBlobByte(image,8); /* bits per blue component */
782           for (i=0; i < (ssize_t) image->colors; i++)
783           {
784             (void) FormatLocaleString(buffer,MagickPathExtent,
785               "\033*v%da%db%dc%.20gI",
786               ScaleQuantumToChar(image->colormap[i].red),
787               ScaleQuantumToChar(image->colormap[i].green),
788               ScaleQuantumToChar(image->colormap[i].blue),(double) i);
789             (void) WriteBlobString(image,buffer);
790           }
791           for (one=1; i < (ssize_t) (one << bits_per_pixel); i++)
792           {
793             (void) FormatLocaleString(buffer,MagickPathExtent,"\033*v%.20gI",
794               (double) i);
795             (void) WriteBlobString(image,buffer);
796           }
797         }
798     option=GetImageOption(image_info,"pcl:fit-to-page");
799     if (IsStringTrue(option) != MagickFalse)
800       (void) WriteBlobString(image,"\033*r3A");
801     else
802       (void) WriteBlobString(image,"\033*r1A");  /* start raster graphics */
803     (void) WriteBlobString(image,"\033*b0Y");  /* set y offset */
804     length=(image->columns*bits_per_pixel+7)/8;
805     pixels=(unsigned char *) AcquireQuantumMemory(length+1,sizeof(*pixels));
806     if (pixels == (unsigned char *) NULL)
807       ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
808     (void) memset(pixels,0,(length+1)*sizeof(*pixels));
809     compress_pixels=(unsigned char *) NULL;
810     previous_pixels=(unsigned char *) NULL;
811 
812     compression=UndefinedCompression;
813     if (image_info->compression != UndefinedCompression)
814       compression=image_info->compression;
815     switch (compression)
816     {
817       case NoCompression:
818       {
819         (void) FormatLocaleString(buffer,MagickPathExtent,"\033*b0M");
820         (void) WriteBlobString(image,buffer);
821         break;
822       }
823       case RLECompression:
824       {
825         compress_pixels=(unsigned char *) AcquireQuantumMemory(length+256,
826           sizeof(*compress_pixels));
827         if (compress_pixels == (unsigned char *) NULL)
828           {
829             pixels=(unsigned char *) RelinquishMagickMemory(pixels);
830             ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
831           }
832         (void) memset(compress_pixels,0,(length+256)*
833           sizeof(*compress_pixels));
834         (void) FormatLocaleString(buffer,MagickPathExtent,"\033*b2M");
835         (void) WriteBlobString(image,buffer);
836         break;
837       }
838       default:
839       {
840         compress_pixels=(unsigned char *) AcquireQuantumMemory(3*length+256,
841           sizeof(*compress_pixels));
842         if (compress_pixels == (unsigned char *) NULL)
843           {
844             pixels=(unsigned char *) RelinquishMagickMemory(pixels);
845             ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
846           }
847         (void) memset(compress_pixels,0,(3*length+256)*
848           sizeof(*compress_pixels));
849         previous_pixels=(unsigned char *) AcquireQuantumMemory(length+1,
850           sizeof(*previous_pixels));
851         if (previous_pixels == (unsigned char *) NULL)
852           {
853             compress_pixels=(unsigned char *) RelinquishMagickMemory(
854               compress_pixels);
855             pixels=(unsigned char *) RelinquishMagickMemory(pixels);
856             ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
857           }
858         (void) memset(previous_pixels,0,(length+1)*
859           sizeof(*previous_pixels));
860         (void) FormatLocaleString(buffer,MagickPathExtent,"\033*b3M");
861         (void) WriteBlobString(image,buffer);
862         break;
863       }
864     }
865     for (y=0; y < (ssize_t) image->rows; y++)
866     {
867       p=GetVirtualPixels(image,0,y,image->columns,1,exception);
868       if (p == (const Quantum *) NULL)
869         break;
870       q=pixels;
871       switch (bits_per_pixel)
872       {
873         case 1:
874         {
875           unsigned char
876             bit,
877             byte;
878 
879           /*
880             Monochrome image.
881           */
882           bit=0;
883           byte=0;
884           for (x=0; x < (ssize_t) image->columns; x++)
885           {
886             byte<<=1;
887             if (GetPixelLuma(image,p) < (QuantumRange/2.0))
888               byte|=0x01;
889             bit++;
890             if (bit == 8)
891               {
892                 *q++=byte;
893                 bit=0;
894                 byte=0;
895               }
896             p+=GetPixelChannels(image);
897           }
898           if (bit != 0)
899             *q++=byte << (8-bit);
900           break;
901         }
902         case 8:
903         {
904           /*
905             Colormapped image.
906           */
907           for (x=0; x < (ssize_t) image->columns; x++)
908           {
909             *q++=(unsigned char) ((ssize_t) GetPixelIndex(image,p));
910             p+=GetPixelChannels(image);
911           }
912           break;
913         }
914         case 24:
915         case 32:
916         {
917           /*
918             Truecolor image.
919           */
920           for (x=0; x < (ssize_t) image->columns; x++)
921           {
922             *q++=ScaleQuantumToChar(GetPixelRed(image,p));
923             *q++=ScaleQuantumToChar(GetPixelGreen(image,p));
924             *q++=ScaleQuantumToChar(GetPixelBlue(image,p));
925             p+=GetPixelChannels(image);
926           }
927           break;
928         }
929       }
930       switch (compression)
931       {
932         case NoCompression:
933         {
934           (void) FormatLocaleString(buffer,MagickPathExtent,"\033*b%.20gW",
935             (double) length);
936           (void) WriteBlobString(image,buffer);
937           (void) WriteBlob(image,length,pixels);
938           break;
939         }
940         case RLECompression:
941         {
942           packets=PCLPackbitsCompressImage(length,pixels,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           break;
948         }
949         default:
950         {
951           if (y == 0)
952             for (i=0; i < (ssize_t) length; i++)
953               previous_pixels[i]=(~pixels[i]);
954           packets=PCLDeltaCompressImage(length,previous_pixels,pixels,
955             compress_pixels);
956           (void) FormatLocaleString(buffer,MagickPathExtent,"\033*b%.20gW",
957             (double) packets);
958           (void) WriteBlobString(image,buffer);
959           (void) WriteBlob(image,packets,compress_pixels);
960           (void) memcpy(previous_pixels,pixels,length*
961             sizeof(*pixels));
962           break;
963         }
964       }
965     }
966     (void) WriteBlobString(image,"\033*rB");  /* end graphics */
967     switch (compression)
968     {
969       case NoCompression:
970         break;
971       case RLECompression:
972       {
973         compress_pixels=(unsigned char *) RelinquishMagickMemory(
974           compress_pixels);
975         break;
976       }
977       default:
978       {
979         previous_pixels=(unsigned char *) RelinquishMagickMemory(
980           previous_pixels);
981         compress_pixels=(unsigned char *) RelinquishMagickMemory(
982           compress_pixels);
983         break;
984       }
985     }
986     pixels=(unsigned char *) RelinquishMagickMemory(pixels);
987     if (GetNextImageInList(image) == (Image *) NULL)
988       break;
989     image=SyncNextImageInList(image);
990     status=SetImageProgress(image,SaveImagesTag,scene++,imageListLength);
991     if (status == MagickFalse)
992       break;
993   } while (image_info->adjoin != MagickFalse);
994   (void) WriteBlobString(image,"\033E");
995   (void) CloseBlob(image);
996   return(MagickTrue);
997 }
998