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