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