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